autoproj 2.10.1 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -8
- data/.travis.yml +5 -3
- data/autoproj.gemspec +7 -6
- data/bin/alog +1 -0
- data/bin/autoproj +1 -1
- data/bin/autoproj_bootstrap +149 -86
- data/bin/autoproj_bootstrap.in +9 -7
- data/bin/autoproj_install +148 -82
- data/bin/autoproj_install.in +8 -3
- data/lib/autoproj.rb +3 -0
- data/lib/autoproj/aruba_minitest.rb +15 -0
- data/lib/autoproj/autobuild_extensions/dsl.rb +61 -27
- data/lib/autoproj/base.rb +35 -6
- data/lib/autoproj/cli/base.rb +1 -1
- data/lib/autoproj/cli/build.rb +9 -3
- data/lib/autoproj/cli/cache.rb +79 -7
- data/lib/autoproj/cli/doc.rb +4 -18
- data/lib/autoproj/cli/inspection_tool.rb +5 -6
- data/lib/autoproj/cli/main.rb +41 -18
- data/lib/autoproj/cli/main_doc.rb +86 -0
- data/lib/autoproj/cli/main_plugin.rb +3 -0
- data/lib/autoproj/cli/main_test.rb +15 -0
- data/lib/autoproj/cli/show.rb +12 -18
- data/lib/autoproj/cli/status.rb +15 -9
- data/lib/autoproj/cli/test.rb +13 -84
- data/lib/autoproj/cli/update.rb +77 -19
- data/lib/autoproj/cli/utility.rb +136 -0
- data/lib/autoproj/configuration.rb +28 -4
- data/lib/autoproj/default.osdeps +18 -0
- data/lib/autoproj/installation_manifest.rb +7 -5
- data/lib/autoproj/manifest.rb +15 -21
- data/lib/autoproj/ops/build.rb +23 -27
- data/lib/autoproj/ops/cache.rb +151 -33
- data/lib/autoproj/ops/cached_env.rb +2 -2
- data/lib/autoproj/ops/import.rb +146 -80
- data/lib/autoproj/ops/install.rb +140 -79
- data/lib/autoproj/ops/phase_reporting.rb +49 -0
- data/lib/autoproj/ops/snapshot.rb +2 -1
- data/lib/autoproj/ops/tools.rb +2 -2
- data/lib/autoproj/os_package_installer.rb +19 -11
- data/lib/autoproj/package_definition.rb +29 -10
- data/lib/autoproj/package_managers/apt_dpkg_manager.rb +49 -28
- data/lib/autoproj/package_managers/bundler_manager.rb +257 -87
- data/lib/autoproj/package_managers/homebrew_manager.rb +2 -2
- data/lib/autoproj/package_managers/shell_script_manager.rb +44 -24
- data/lib/autoproj/package_manifest.rb +49 -34
- data/lib/autoproj/package_set.rb +48 -29
- data/lib/autoproj/repository_managers/apt.rb +0 -1
- data/lib/autoproj/test.rb +29 -10
- data/lib/autoproj/variable_expansion.rb +3 -1
- data/lib/autoproj/vcs_definition.rb +30 -15
- data/lib/autoproj/version.rb +1 -1
- data/lib/autoproj/workspace.rb +55 -13
- metadata +32 -28
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'autoproj/package_managers/manager'
|
2
4
|
require 'autoproj/package_managers/unknown_os_manager'
|
3
5
|
require 'autoproj/package_managers/shell_script_manager'
|
@@ -37,7 +39,9 @@ class OSPackageInstaller
|
|
37
39
|
attr_reader :installed_resolved_packages
|
38
40
|
|
39
41
|
attr_writer :silent
|
40
|
-
def silent
|
42
|
+
def silent?
|
43
|
+
@silent
|
44
|
+
end
|
41
45
|
|
42
46
|
class << self
|
43
47
|
attr_accessor :force_osdeps
|
@@ -60,12 +64,12 @@ def initialize(ws, os_package_resolver, package_managers: PACKAGE_MANAGERS)
|
|
60
64
|
|
61
65
|
# Returns the package manager object for the current OS
|
62
66
|
def os_package_manager
|
63
|
-
|
67
|
+
unless @os_package_manager
|
64
68
|
name = os_package_resolver.os_package_manager
|
65
69
|
@os_package_manager = package_managers[name] ||
|
66
|
-
|
70
|
+
PackageManagers::UnknownOSManager.new(ws)
|
67
71
|
end
|
68
|
-
|
72
|
+
@os_package_manager
|
69
73
|
end
|
70
74
|
|
71
75
|
# Returns the set of package managers
|
@@ -75,13 +79,17 @@ def each_manager(&block)
|
|
75
79
|
package_managers.each_value(&block)
|
76
80
|
end
|
77
81
|
|
82
|
+
def each_manager_with_name(&block)
|
83
|
+
package_managers.each(&block)
|
84
|
+
end
|
85
|
+
|
78
86
|
HANDLE_ALL = 'all'
|
79
87
|
HANDLE_RUBY = 'ruby'
|
80
88
|
HANDLE_OS = 'os'
|
81
89
|
HANDLE_NONE = 'none'
|
82
90
|
|
83
91
|
def osdeps_mode_option_unsupported_os(config)
|
84
|
-
long_doc
|
92
|
+
long_doc = <<-EOT
|
85
93
|
The software packages that autoproj will have to build may require other
|
86
94
|
prepackaged softwares (a.k.a. OS dependencies) to be installed (RubyGems
|
87
95
|
packages, packages from your operating system/distribution, ...). Autoproj is
|
@@ -153,9 +161,9 @@ def osdeps_mode_option_supported_os(config)
|
|
153
161
|
message = [ "Which prepackaged software (a.k.a. 'osdeps') should autoproj install automatically (all, none or a comma-separated list of: os gem pip) ?", long_doc.strip ]
|
154
162
|
|
155
163
|
config.declare 'osdeps_mode', 'string',
|
156
|
-
|
157
|
-
|
158
|
-
|
164
|
+
default: 'all',
|
165
|
+
doc: message,
|
166
|
+
lowercase: true
|
159
167
|
end
|
160
168
|
|
161
169
|
def define_osdeps_mode_option
|
@@ -171,14 +179,14 @@ def osdeps_mode_string_to_value(string)
|
|
171
179
|
modes = []
|
172
180
|
user_modes.each do |str|
|
173
181
|
case str
|
174
|
-
when 'all' then modes.concat([
|
182
|
+
when 'all' then modes.concat(%w[os gem pip])
|
175
183
|
when 'ruby' then modes << 'gem'
|
176
184
|
when 'gem' then modes << 'gem'
|
177
185
|
when 'pip' then modes << 'pip'
|
178
186
|
when 'os' then modes << 'os'
|
179
187
|
when 'none' then
|
180
188
|
else
|
181
|
-
if package_managers.
|
189
|
+
if package_managers.key?(str)
|
182
190
|
modes << str
|
183
191
|
else
|
184
192
|
raise ArgumentError, "#{str} is not a known package handler, known handlers are #{package_managers.keys.sort.join(", ")}"
|
@@ -442,5 +450,5 @@ def install_manager_packages(manager, package_list, install_only: false, run_pac
|
|
442
450
|
end
|
443
451
|
end
|
444
452
|
end
|
445
|
-
end
|
453
|
+
end
|
446
454
|
|
@@ -19,7 +19,9 @@ class PackageDefinition
|
|
19
19
|
#
|
20
20
|
# If the package is set up, its importer as well as all target
|
21
21
|
# directories are properly set, and all {user_blocks} have been called.
|
22
|
-
def setup
|
22
|
+
def setup?
|
23
|
+
@setup
|
24
|
+
end
|
23
25
|
|
24
26
|
# Sets the {setup?} flag
|
25
27
|
attr_writer :setup
|
@@ -29,10 +31,11 @@ def setup?; !!@setup end
|
|
29
31
|
attr_accessor :vcs
|
30
32
|
|
31
33
|
def initialize(autobuild, package_set, file)
|
32
|
-
@autobuild
|
33
|
-
|
34
|
+
@autobuild = autobuild
|
35
|
+
@package_set = package_set
|
36
|
+
@file = file
|
34
37
|
@user_blocks = []
|
35
|
-
@modes = [
|
38
|
+
@modes = %w[import build]
|
36
39
|
@setup = false
|
37
40
|
@vcs = VCSDefinition.none
|
38
41
|
end
|
@@ -44,9 +47,8 @@ def initialize(autobuild, package_set, file)
|
|
44
47
|
#
|
45
48
|
# @return [Array<String>]
|
46
49
|
def modes
|
47
|
-
@modes + autobuild.utilities
|
48
|
-
|
49
|
-
map(&:name)
|
50
|
+
@modes + autobuild.utilities
|
51
|
+
.values.find_all(&:enabled?).map(&:name)
|
50
52
|
end
|
51
53
|
|
52
54
|
# The package name
|
@@ -65,9 +67,7 @@ def name
|
|
65
67
|
# @see {user_blocks}
|
66
68
|
def add_setup_block(block)
|
67
69
|
user_blocks << block
|
68
|
-
if setup?
|
69
|
-
block.call(autobuild)
|
70
|
-
end
|
70
|
+
block.call(autobuild) if setup?
|
71
71
|
end
|
72
72
|
|
73
73
|
# Whether this package is already checked out
|
@@ -79,5 +79,24 @@ def checked_out?
|
|
79
79
|
def depends_on(pkg)
|
80
80
|
autobuild.depends_on(pkg.autobuild)
|
81
81
|
end
|
82
|
+
|
83
|
+
def apply_dependencies_from_manifest
|
84
|
+
manifest = autobuild.description
|
85
|
+
manifest.each_dependency(modes) do |name, is_optional|
|
86
|
+
begin
|
87
|
+
if is_optional
|
88
|
+
autobuild.optional_dependency name
|
89
|
+
else
|
90
|
+
autobuild.depends_on name
|
91
|
+
end
|
92
|
+
rescue ConfigError => e
|
93
|
+
raise PackageNotFound.new(manifest.path),
|
94
|
+
"manifest #{manifest.path} of #{self.name} from "\
|
95
|
+
"#{package_set.name} lists '#{name}' as dependency, "\
|
96
|
+
'but it is neither a normal package nor an osdeps '\
|
97
|
+
"package. osdeps reports: #{e.message}", e.backtrace
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
82
101
|
end
|
83
102
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'autoproj/package_managers/debian_version'
|
2
4
|
|
3
5
|
module Autoproj
|
@@ -12,15 +14,17 @@ def initialize(ws, status_file = "/var/lib/dpkg/status")
|
|
12
14
|
@installed_packages = nil
|
13
15
|
@installed_versions = nil
|
14
16
|
super(ws, true,
|
15
|
-
%w
|
16
|
-
%w
|
17
|
+
%w[apt-get install],
|
18
|
+
%w[DEBIAN_FRONTEND=noninteractive apt-get install -y])
|
17
19
|
end
|
18
20
|
|
19
21
|
def configure_manager
|
20
22
|
super
|
21
|
-
ws.config.declare
|
23
|
+
ws.config.declare(
|
24
|
+
'apt_dpkg_update', 'boolean',
|
22
25
|
default: 'yes',
|
23
26
|
doc: ['Would you like autoproj to keep apt packages up-to-date?']
|
27
|
+
)
|
24
28
|
keep_uptodate?
|
25
29
|
end
|
26
30
|
|
@@ -32,7 +36,9 @@ def keep_uptodate=(flag)
|
|
32
36
|
ws.config.set('apt_dpkg_update', flag, true)
|
33
37
|
end
|
34
38
|
|
35
|
-
def self.parse_package_status(
|
39
|
+
def self.parse_package_status(
|
40
|
+
installed_packages, installed_versions, paragraph, virtual: true
|
41
|
+
)
|
36
42
|
if paragraph =~ /^Status: install ok installed$/
|
37
43
|
if paragraph =~ /^Package: (.*)$/
|
38
44
|
package_name = $1
|
@@ -41,38 +47,40 @@ def self.parse_package_status(installed_packages, installed_versions, paragraph)
|
|
41
47
|
installed_versions[package_name] = DebianVersion.new($1)
|
42
48
|
end
|
43
49
|
end
|
44
|
-
if paragraph =~ /^Provides: (.*)$/
|
50
|
+
if virtual && paragraph =~ /^Provides: (.*)$/
|
45
51
|
installed_packages.merge($1.split(',').map(&:strip))
|
46
52
|
end
|
47
53
|
end
|
48
54
|
end
|
49
55
|
|
50
|
-
def self.parse_dpkg_status(status_file)
|
56
|
+
def self.parse_dpkg_status(status_file, virtual: true)
|
51
57
|
installed_packages = Set.new
|
52
58
|
installed_versions = {}
|
53
59
|
dpkg_status = File.read(status_file)
|
54
60
|
dpkg_status << "\n"
|
55
61
|
|
56
62
|
dpkg_status = StringScanner.new(dpkg_status)
|
57
|
-
|
58
|
-
raise ArgumentError, "expected #{status_file} to have Package:
|
63
|
+
unless dpkg_status.scan(/Package: /)
|
64
|
+
raise ArgumentError, "expected #{status_file} to have Package: "\
|
65
|
+
"lines but found none"
|
59
66
|
end
|
60
67
|
|
61
|
-
while paragraph_end = dpkg_status.scan_until(/Package: /)
|
68
|
+
while (paragraph_end = dpkg_status.scan_until(/Package: /))
|
62
69
|
paragraph = "Package: #{paragraph_end[0..-10]}"
|
63
|
-
parse_package_status(installed_packages, installed_versions,
|
70
|
+
parse_package_status(installed_packages, installed_versions,
|
71
|
+
paragraph, virtual: virtual)
|
64
72
|
end
|
65
|
-
parse_package_status(installed_packages, installed_versions,
|
73
|
+
parse_package_status(installed_packages, installed_versions,
|
74
|
+
"Package: #{dpkg_status.rest}", virtual: virtual)
|
66
75
|
[installed_packages, installed_versions]
|
67
76
|
end
|
68
77
|
|
69
78
|
def self.parse_apt_cache_paragraph(paragraph)
|
70
79
|
version = '0'
|
71
|
-
if
|
72
|
-
package_name =
|
73
|
-
|
74
|
-
|
75
|
-
end
|
80
|
+
if (paragraph_m = /^Package: (.*)$/.match(paragraph))
|
81
|
+
package_name = paragraph_m[1]
|
82
|
+
version_m = /^Version: (.*)$/.match(paragraph)
|
83
|
+
version = version_m[1] if version_m
|
76
84
|
end
|
77
85
|
[package_name, version]
|
78
86
|
end
|
@@ -81,23 +89,24 @@ def self.parse_packages_versions(packages)
|
|
81
89
|
packages_versions = {}
|
82
90
|
apt_cache_show = `apt-cache show --no-all-versions #{packages.join(' ')}`
|
83
91
|
apt_cache_show = StringScanner.new(apt_cache_show)
|
84
|
-
|
85
|
-
return packages_versions
|
86
|
-
end
|
92
|
+
return packages_versions unless apt_cache_show.scan(/Package: /)
|
87
93
|
|
88
|
-
while paragraph_end = apt_cache_show.scan_until(/Package: /)
|
94
|
+
while (paragraph_end = apt_cache_show.scan_until(/Package: /))
|
89
95
|
paragraph = "Package: #{paragraph_end[0..-10]}"
|
90
96
|
package_name, version = parse_apt_cache_paragraph(paragraph)
|
91
97
|
packages_versions[package_name] = DebianVersion.new(version)
|
92
98
|
end
|
93
|
-
package_name, version = parse_apt_cache_paragraph(
|
99
|
+
package_name, version = parse_apt_cache_paragraph(
|
100
|
+
"Package: #{apt_cache_show.rest}"
|
101
|
+
)
|
94
102
|
packages_versions[package_name] = DebianVersion.new(version)
|
95
103
|
packages_versions
|
96
104
|
end
|
97
105
|
|
98
106
|
def updated?(package, available_version)
|
99
|
-
# Consider up-to-date if the package is provided by another
|
100
|
-
# Ideally, we should check the version
|
107
|
+
# Consider up-to-date if the package is provided by another
|
108
|
+
# package (purely virtual) Ideally, we should check the version
|
109
|
+
# of the package that provides it
|
101
110
|
return true unless available_version && @installed_versions[package]
|
102
111
|
|
103
112
|
(available_version <= @installed_versions[package])
|
@@ -105,8 +114,13 @@ def updated?(package, available_version)
|
|
105
114
|
|
106
115
|
# On a dpkg-enabled system, checks if the provided package is installed
|
107
116
|
# and returns true if it is the case
|
108
|
-
def installed?(package_name, filter_uptodate_packages: false,
|
109
|
-
|
117
|
+
def installed?(package_name, filter_uptodate_packages: false,
|
118
|
+
install_only: false)
|
119
|
+
unless @installed_packages && @installed_versions
|
120
|
+
@installed_packages, @installed_versions =
|
121
|
+
self.class.parse_dpkg_status(status_file)
|
122
|
+
end
|
123
|
+
|
110
124
|
if package_name =~ /^(\w[a-z0-9+-.]+)/
|
111
125
|
@installed_packages.include?($1)
|
112
126
|
else
|
@@ -116,11 +130,18 @@ def installed?(package_name, filter_uptodate_packages: false, install_only: fals
|
|
116
130
|
end
|
117
131
|
|
118
132
|
def install(packages, filter_uptodate_packages: false, install_only: false)
|
119
|
-
packages_versions = self.class.parse_packages_versions(packages)
|
120
133
|
if filter_uptodate_packages || install_only
|
121
|
-
|
122
|
-
|
134
|
+
already_installed, missing = packages.partition do |package_name|
|
135
|
+
installed?(package_name)
|
136
|
+
end
|
137
|
+
|
138
|
+
if keep_uptodate? && !install_only
|
139
|
+
packages_versions = self.class.parse_packages_versions(already_installed)
|
140
|
+
need_update = already_installed.find_all do |package_name|
|
141
|
+
!updated?(package_name, packages_versions[package_name])
|
142
|
+
end
|
123
143
|
end
|
144
|
+
packages = missing + (need_update || [])
|
124
145
|
end
|
125
146
|
|
126
147
|
if super(packages)
|
@@ -24,6 +24,14 @@ def self.with_prerelease(*value)
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
+
# Directory with cached .gem packages
|
28
|
+
#
|
29
|
+
# The directory must exist, but may be empty.
|
30
|
+
# It is initialized with {BundlerManager.cache_dir}
|
31
|
+
#
|
32
|
+
# @return [String]
|
33
|
+
attr_accessor :cache_dir
|
34
|
+
|
27
35
|
# (see Manager#call_while_empty?)
|
28
36
|
def call_while_empty?
|
29
37
|
!workspace_configuration_gemfiles.empty?
|
@@ -45,14 +53,31 @@ def initialize_environment
|
|
45
53
|
env.add_path 'PATH', File.join(ws.dot_autoproj_dir, 'bin')
|
46
54
|
env.set 'GEM_HOME', config.gems_gem_home
|
47
55
|
env.clear 'GEM_PATH'
|
56
|
+
if (bundler_version = config.bundler_version)
|
57
|
+
env.set 'BUNDLER_VERSION', bundler_version
|
58
|
+
else
|
59
|
+
env.clear 'BUNDLER_VERSION'
|
60
|
+
end
|
48
61
|
|
49
62
|
gemfile_path = File.join(ws.prefix_dir, 'gems', 'Gemfile')
|
50
|
-
if File.file?(gemfile_path)
|
51
|
-
|
63
|
+
env.set('BUNDLE_GEMFILE', gemfile_path) if File.file?(gemfile_path)
|
64
|
+
|
65
|
+
if cache_dir && File.exist?(cache_dir)
|
66
|
+
vendor_dir = File.join(File.dirname(gemfile_path), 'vendor')
|
67
|
+
FileUtils.mkdir_p vendor_dir
|
68
|
+
bundler_cache_dir = File.join(vendor_dir, 'cache')
|
69
|
+
if File.writable?(cache_dir)
|
70
|
+
create_cache_symlink(cache_dir, bundler_cache_dir)
|
71
|
+
else
|
72
|
+
Autoproj.warn "BundlerManager: #{cache_dir} is read-only "\
|
73
|
+
"copying the cache instead of symlinking it"
|
74
|
+
create_cache_copy(cache_dir, bundler_cache_dir)
|
75
|
+
end
|
52
76
|
end
|
53
77
|
|
54
|
-
Autobuild.programs['bundler'] =
|
55
|
-
|
78
|
+
Autobuild.programs['bundler'] =
|
79
|
+
Autobuild.programs['bundle'] =
|
80
|
+
File.join(ws.dot_autoproj_dir, 'bin', 'bundle')
|
56
81
|
|
57
82
|
env.init_from_env 'RUBYLIB'
|
58
83
|
env.inherit 'RUBYLIB'
|
@@ -61,17 +86,20 @@ def initialize_environment
|
|
61
86
|
original_rubylib =
|
62
87
|
(env['RUBYLIB'] || "").split(File::PATH_SEPARATOR).find_all do |p|
|
63
88
|
!p.start_with?(Bundler.rubygems.gem_dir) &&
|
64
|
-
|
89
|
+
Bundler.rubygems.gem_path
|
90
|
+
.none? { |gem_p| p.start_with?(gem_p) }
|
65
91
|
end
|
92
|
+
|
66
93
|
# And discover the system's rubylib
|
67
|
-
if system_rubylib = discover_rubylib
|
94
|
+
if (system_rubylib = discover_rubylib)
|
68
95
|
# Do not explicitely add the system rubylib to the
|
69
96
|
# environment, the interpreter will do it for us.
|
70
97
|
#
|
71
98
|
# This allows to use a binstub generated for one of ruby
|
72
99
|
# interpreter version on our workspace
|
73
100
|
env.system_env['RUBYLIB'] = []
|
74
|
-
env.original_env['RUBYLIB'] = (original_rubylib - system_rubylib)
|
101
|
+
env.original_env['RUBYLIB'] = (original_rubylib - system_rubylib)
|
102
|
+
.join(File::PATH_SEPARATOR)
|
75
103
|
end
|
76
104
|
|
77
105
|
ws.config.each_reused_autoproj_installation do |p|
|
@@ -82,17 +110,135 @@ def initialize_environment
|
|
82
110
|
prefix_gems = File.join(ws.prefix_dir, "gems")
|
83
111
|
FileUtils.mkdir_p prefix_gems
|
84
112
|
gemfile = File.join(prefix_gems, 'Gemfile')
|
85
|
-
|
113
|
+
unless File.exist?(gemfile)
|
86
114
|
Ops.atomic_write(gemfile) do |io|
|
87
|
-
|
115
|
+
dot_autoproj_gemfile = File.join(ws.dot_autoproj_dir, 'Gemfile')
|
116
|
+
io.puts "eval_gemfile \"#{dot_autoproj_gemfile}\""
|
88
117
|
end
|
89
118
|
end
|
90
119
|
|
91
|
-
if bundle_rubylib = discover_bundle_rubylib(silent_errors: true)
|
120
|
+
if (bundle_rubylib = discover_bundle_rubylib(silent_errors: true))
|
92
121
|
update_env_rubylib(bundle_rubylib, system_rubylib)
|
93
122
|
end
|
94
123
|
end
|
95
124
|
|
125
|
+
def create_cache_symlink(cache_dir, bundler_cache_dir)
|
126
|
+
valid = !File.exist?(bundler_cache_dir) ||
|
127
|
+
File.symlink?(bundler_cache_dir)
|
128
|
+
|
129
|
+
unless valid
|
130
|
+
Autoproj.warn "cannot use #{cache_dir} as gem cache as "\
|
131
|
+
"#{bundler_cache_dir} already exists"
|
132
|
+
return
|
133
|
+
end
|
134
|
+
|
135
|
+
FileUtils.rm_f bundler_cache_dir
|
136
|
+
FileUtils.ln_s cache_dir, bundler_cache_dir
|
137
|
+
end
|
138
|
+
|
139
|
+
def create_cache_copy(cache_dir, bundler_cache_dir)
|
140
|
+
valid = !File.exist?(bundler_cache_dir) ||
|
141
|
+
File.directory?(bundler_cache_dir) ||
|
142
|
+
File.symlink?(bundler_cache_dir)
|
143
|
+
|
144
|
+
unless valid
|
145
|
+
Autoproj.warn "cannot use #{cache_dir} as gem cache as "\
|
146
|
+
"#{bundler_cache_dir} already exists"
|
147
|
+
return
|
148
|
+
end
|
149
|
+
|
150
|
+
# Gracefully upgrade from the symlinks
|
151
|
+
FileUtils.rm_f bundler_cache_dir if File.symlink?(bundler_cache_dir)
|
152
|
+
FileUtils.mkdir_p bundler_cache_dir
|
153
|
+
|
154
|
+
Dir.glob(File.join(cache_dir, '*.gem')) do |path_src|
|
155
|
+
path_dest = File.join(bundler_cache_dir, File.basename(path_src))
|
156
|
+
next if File.exist?(path_dest)
|
157
|
+
|
158
|
+
FileUtils.cp path_src, path_dest
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Enumerate the per-gem build configurations
|
163
|
+
def self.per_gem_build_config(ws)
|
164
|
+
ws.config.get('bundler.build', {})
|
165
|
+
end
|
166
|
+
|
167
|
+
# Add new build configuration arguments for a given gem
|
168
|
+
#
|
169
|
+
# This is meant to be used from the Autoproj configuration files,
|
170
|
+
# e.g. overrides.rb or package configuration
|
171
|
+
def self.add_build_configuration_for(gem_name, build_config, ws: Autoproj.workspace)
|
172
|
+
c = ws.config.get('bundler.build', {})
|
173
|
+
c[gem_name] = [c[gem_name], build_config].compact.join(" ")
|
174
|
+
ws.config.set('bundler.build', c)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Set the build configuration for the given gem
|
178
|
+
#
|
179
|
+
# This is meant to be used from the Autoproj configuration files,
|
180
|
+
# e.g. overrides.rb or package configuration
|
181
|
+
def self.configure_build_for(gem_name, build_config, ws: Autoproj.workspace)
|
182
|
+
c = ws.config.get('bundler.build', {})
|
183
|
+
c[gem_name] = build_config
|
184
|
+
ws.config.set('bundler.build', c)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Removes build configuration flags for the given gem
|
188
|
+
#
|
189
|
+
# This is meant to be used from the Autoproj configuration files,
|
190
|
+
# e.g. overrides.rb or package configuration
|
191
|
+
def self.remove_build_configuration_for(gem_name, ws: Autoproj.workspace)
|
192
|
+
c = ws.config.get('bundler.build', {})
|
193
|
+
c.delete(gem_name)
|
194
|
+
ws.config.set('bundler.build', c)
|
195
|
+
end
|
196
|
+
|
197
|
+
# @api private
|
198
|
+
#
|
199
|
+
# Apply configured per-gem build configuration options
|
200
|
+
#
|
201
|
+
# @param [Workspace] ws the workspace whose bundler configuration
|
202
|
+
# should be updated
|
203
|
+
# @return [void]
|
204
|
+
def self.apply_build_config(ws)
|
205
|
+
root_dir = File.join(ws.prefix_dir, 'gems')
|
206
|
+
current_config_path = File.join(root_dir, ".bundle", "config")
|
207
|
+
current_config =
|
208
|
+
if File.file?(current_config_path)
|
209
|
+
File.readlines(current_config_path)
|
210
|
+
else
|
211
|
+
[]
|
212
|
+
end
|
213
|
+
|
214
|
+
build_config = {}
|
215
|
+
per_gem_build_config(ws).each do |name, conf|
|
216
|
+
build_config[name.upcase] = conf
|
217
|
+
end
|
218
|
+
|
219
|
+
new_config = current_config.map do |line|
|
220
|
+
next(line) unless (m = line.match(/BUNDLE_BUILD__(.*): "(.*)"$/))
|
221
|
+
next unless (desired_config = build_config.delete(m[1]))
|
222
|
+
|
223
|
+
if m[2] != desired_config
|
224
|
+
"BUNDLE_BUILD__#{m[1]}: \"#{desired_config}\""
|
225
|
+
else
|
226
|
+
line
|
227
|
+
end
|
228
|
+
end.compact
|
229
|
+
|
230
|
+
build_config.each do |name, config|
|
231
|
+
new_config << "BUNDLE_BUILD__#{name}: \"#{config}\""
|
232
|
+
end
|
233
|
+
|
234
|
+
if new_config != current_config
|
235
|
+
FileUtils.mkdir_p File.dirname(current_config_path)
|
236
|
+
File.open(current_config_path, 'w') do |io|
|
237
|
+
io.write new_config.join
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
96
242
|
# @api private
|
97
243
|
#
|
98
244
|
# Update RUBYLIB to add the gems that are part of the bundler
|
@@ -103,7 +249,8 @@ def initialize_environment
|
|
103
249
|
# @param [Array<String>] system_rubylib the rubylib entries that are
|
104
250
|
# set by the underlying ruby interpreter itself
|
105
251
|
def update_env_rubylib(bundle_rubylib, system_rubylib = discover_rubylib)
|
106
|
-
current = (ws.env.resolved_env['RUBYLIB'] || '')
|
252
|
+
current = (ws.env.resolved_env['RUBYLIB'] || '')
|
253
|
+
.split(File::PATH_SEPARATOR) + system_rubylib
|
107
254
|
(bundle_rubylib - current).each do |p|
|
108
255
|
ws.env.add_path('RUBYLIB', p)
|
109
256
|
end
|
@@ -128,7 +275,7 @@ def parse_package_entry(entry)
|
|
128
275
|
end
|
129
276
|
|
130
277
|
class NotCleanState < RuntimeError; end
|
131
|
-
|
278
|
+
|
132
279
|
# @api private
|
133
280
|
#
|
134
281
|
# Create backup files matching a certain file mapping
|
@@ -138,9 +285,7 @@ class NotCleanState < RuntimeError; end
|
|
138
285
|
# file might not exist.
|
139
286
|
def backup_files(mapping)
|
140
287
|
mapping.each do |file, backup_file|
|
141
|
-
if File.file?(file)
|
142
|
-
FileUtils.cp file, backup_file
|
143
|
-
end
|
288
|
+
FileUtils.cp file, backup_file if File.file?(file)
|
144
289
|
end
|
145
290
|
end
|
146
291
|
|
@@ -151,9 +296,7 @@ def backup_files(mapping)
|
|
151
296
|
# @param (see #backup_file)
|
152
297
|
def backup_restore(mapping)
|
153
298
|
mapping.each do |file, backup_file|
|
154
|
-
if File.file?(backup_file)
|
155
|
-
FileUtils.cp backup_file, file
|
156
|
-
end
|
299
|
+
FileUtils.cp backup_file, file if File.file?(backup_file)
|
157
300
|
end
|
158
301
|
end
|
159
302
|
|
@@ -163,34 +306,36 @@ def backup_restore(mapping)
|
|
163
306
|
#
|
164
307
|
# @param (see #backup_file)
|
165
308
|
def backup_clean(mapping)
|
166
|
-
mapping.each do |
|
167
|
-
if File.file?(backup_file)
|
168
|
-
FileUtils.rm backup_file
|
169
|
-
end
|
309
|
+
mapping.each do |_file, backup_file|
|
310
|
+
FileUtils.rm backup_file if File.file?(backup_file)
|
170
311
|
end
|
171
312
|
end
|
172
313
|
|
173
|
-
def self.run_bundler_install(
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
314
|
+
def self.run_bundler_install(
|
315
|
+
ws, gemfile, *options,
|
316
|
+
update: true, binstubs: nil,
|
317
|
+
bundler_version: ws.config.bundler_version,
|
318
|
+
gem_home: ws.config.gems_gem_home,
|
319
|
+
gem_path: ws.config.gems_install_path
|
320
|
+
)
|
321
|
+
FileUtils.rm "#{gemfile}.lock" if update && File.file?("#{gemfile}.lock")
|
179
322
|
|
180
323
|
options << '--path' << gem_path
|
181
324
|
options << "--shebang" << Gem.ruby
|
182
|
-
if binstubs
|
183
|
-
|
184
|
-
|
325
|
+
options << "--binstubs" << binstubs if binstubs
|
326
|
+
|
327
|
+
apply_build_config(ws)
|
185
328
|
|
186
329
|
connections = Set.new
|
187
|
-
run_bundler(ws, 'install', *options,
|
330
|
+
run_bundler(ws, 'install', *options,
|
331
|
+
bundler_version: bundler_version,
|
332
|
+
gem_home: gem_home, gemfile: gemfile) do |line|
|
188
333
|
case line
|
189
334
|
when /Installing (.*)/
|
190
335
|
Autobuild.message " bundler: installing #{$1}"
|
191
336
|
when /Fetching.*from (.*)/
|
192
337
|
host = $1.gsub(/\.+$/, '')
|
193
|
-
|
338
|
+
unless connections.include?(host)
|
194
339
|
Autobuild.message " bundler: connected to #{host}"
|
195
340
|
connections << host
|
196
341
|
end
|
@@ -198,32 +343,49 @@ def self.run_bundler_install(ws, gemfile, *options, update: true, binstubs: nil,
|
|
198
343
|
end
|
199
344
|
end
|
200
345
|
|
201
|
-
def self.bundle_gem_path(ws, gem_name,
|
346
|
+
def self.bundle_gem_path(ws, gem_name,
|
347
|
+
bundler_version: ws.config.bundler_version,
|
348
|
+
gem_home: nil, gemfile: nil)
|
202
349
|
path = String.new
|
203
|
-
|
204
|
-
|
205
|
-
|
350
|
+
run_bundler(
|
351
|
+
ws, 'show', gem_name,
|
352
|
+
bundler_version: bundler_version, gem_home: gem_home,
|
353
|
+
gemfile: gemfile) { |line| path << line }
|
206
354
|
path.chomp
|
207
355
|
end
|
208
356
|
|
209
|
-
def self.
|
210
|
-
|
357
|
+
def self.default_bundler(ws)
|
358
|
+
File.join(ws.dot_autoproj_dir, 'bin', 'bundle')
|
359
|
+
end
|
360
|
+
|
361
|
+
def self.run_bundler(ws, *commandline,
|
362
|
+
bundler_version: ws.config.bundler_version,
|
363
|
+
gem_home: ws.config.gems_gem_home,
|
364
|
+
gemfile: default_gemfile_path(ws))
|
365
|
+
bundle = Autobuild.programs['bundle'] || default_bundler(ws)
|
366
|
+
|
367
|
+
Autoproj.bundler_with_unbundled_env do
|
368
|
+
bundler_version_env =
|
369
|
+
if bundler_version
|
370
|
+
{ 'BUNDLER_VERSION' => bundler_version }
|
371
|
+
else
|
372
|
+
{}
|
373
|
+
end
|
211
374
|
target_env = Hash[
|
212
375
|
'GEM_HOME' => gem_home,
|
213
376
|
'GEM_PATH' => nil,
|
214
377
|
'BUNDLE_GEMFILE' => gemfile,
|
215
378
|
'RUBYOPT' => nil,
|
216
|
-
'RUBYLIB' => rubylib_for_bundler
|
217
|
-
]
|
218
|
-
ws.run
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
end
|
379
|
+
'RUBYLIB' => rubylib_for_bundler,
|
380
|
+
].merge(bundler_version_env)
|
381
|
+
ws.run('autoproj', 'osdeps',
|
382
|
+
bundle, *commandline,
|
383
|
+
working_directory: File.dirname(gemfile),
|
384
|
+
env: target_env) { |line| yield(line) if block_given? }
|
223
385
|
end
|
224
386
|
end
|
225
387
|
|
226
|
-
# Parse the contents of a gemfile into a set of
|
388
|
+
# Parse the contents of a gemfile into a set of
|
227
389
|
def merge_gemfiles(*path, unlock: [])
|
228
390
|
gems_remotes = Set.new
|
229
391
|
dependencies = Hash.new do |h, k|
|
@@ -237,9 +399,11 @@ def merge_gemfiles(*path, unlock: [])
|
|
237
399
|
bundler_def =
|
238
400
|
begin Bundler::Dsl.evaluate(gemfile, nil, [])
|
239
401
|
rescue Exception => e
|
240
|
-
cleaned_message = e
|
241
|
-
|
242
|
-
gsub(
|
402
|
+
cleaned_message = e
|
403
|
+
.message
|
404
|
+
.gsub(/There was an error parsing([^:]+)/,
|
405
|
+
"Error in gem definitions")
|
406
|
+
.gsub(/# from.*/, '')
|
243
407
|
raise ConfigError, cleaned_message
|
244
408
|
end
|
245
409
|
gems_remotes |= bundler_def.send(:sources).rubygems_remotes.to_set
|
@@ -259,17 +423,17 @@ def merge_gemfiles(*path, unlock: [])
|
|
259
423
|
contents = []
|
260
424
|
gems_remotes.each do |g|
|
261
425
|
g = g.to_s
|
262
|
-
if g.end_with?('/')
|
263
|
-
|
264
|
-
end
|
265
|
-
contents << "source '#{g.to_s}'"
|
426
|
+
g = g[0..-2] if g.end_with?('/')
|
427
|
+
contents << "source '#{g}'"
|
266
428
|
end
|
267
|
-
valid_keys = %w
|
429
|
+
valid_keys = %w[group groups git path glob name branch ref tag
|
430
|
+
require submodules platform platforms type
|
431
|
+
source install_if]
|
268
432
|
dependencies.each do |group_name, by_platform|
|
269
433
|
contents << "group :#{group_name} do"
|
270
434
|
by_platform.each do |platform_name, deps|
|
271
435
|
deps = deps.values.sort_by(&:name)
|
272
|
-
|
436
|
+
unless platform_name.empty?
|
273
437
|
contents << " platform :#{platform_name} do"
|
274
438
|
platform_indent = " "
|
275
439
|
end
|
@@ -279,13 +443,12 @@ def merge_gemfiles(*path, unlock: [])
|
|
279
443
|
options.delete_if { |k, _| !valid_keys.include?(k) }
|
280
444
|
options = options.map { |k, v| "#{k}: \"#{v}\"" }
|
281
445
|
end
|
282
|
-
contents << [" #{platform_indent}gem \"#{d.name}\",
|
283
|
-
|
284
|
-
if !platform_name.empty?
|
285
|
-
contents << " end"
|
446
|
+
contents << [" #{platform_indent}gem \"#{d.name}\",
|
447
|
+
\"#{d.requirement}\"", *options].join(', ')
|
286
448
|
end
|
449
|
+
contents << ' end' unless platform_name.empty?
|
287
450
|
end
|
288
|
-
contents <<
|
451
|
+
contents << 'end'
|
289
452
|
end
|
290
453
|
contents.join("\n")
|
291
454
|
end
|
@@ -293,20 +456,25 @@ def merge_gemfiles(*path, unlock: [])
|
|
293
456
|
def workspace_configuration_gemfiles
|
294
457
|
gemfiles = []
|
295
458
|
ws.manifest.each_package_set do |source|
|
296
|
-
|
459
|
+
pkg_set_gemfile = File.join(source.local_dir, 'Gemfile')
|
460
|
+
if source.local_dir && File.file?(pkg_set_gemfile)
|
297
461
|
gemfiles << pkg_set_gemfile
|
298
462
|
end
|
299
463
|
end
|
300
464
|
# In addition, look into overrides.d
|
301
|
-
Dir.glob(File.join(ws.overrides_dir, "*.gemfile")) do |
|
302
|
-
gemfiles <<
|
465
|
+
Dir.glob(File.join(ws.overrides_dir, "*.gemfile")) do |overrides_gemfile|
|
466
|
+
gemfiles << overrides_gemfile
|
303
467
|
end
|
304
468
|
gemfiles
|
305
469
|
end
|
306
470
|
|
471
|
+
def self.default_gemfile_path(ws)
|
472
|
+
File.join(ws.prefix_dir, 'gems', 'Gemfile')
|
473
|
+
end
|
474
|
+
|
307
475
|
def install(gems, filter_uptodate_packages: false, install_only: false)
|
308
|
-
|
309
|
-
|
476
|
+
gemfile_path = self.class.default_gemfile_path(ws)
|
477
|
+
root_dir = File.dirname(gemfile_path)
|
310
478
|
gemfile_lock_path = "#{gemfile_path}.lock"
|
311
479
|
backups = Hash[
|
312
480
|
gemfile_path => "#{gemfile_path}.orig",
|
@@ -316,9 +484,10 @@ def install(gems, filter_uptodate_packages: false, install_only: false)
|
|
316
484
|
# Back up the existing gemfile, we'll restore it if something is
|
317
485
|
# wrong to avoid leaving bundler in an inconsistent state
|
318
486
|
backup_files(backups)
|
319
|
-
|
487
|
+
unless File.file?("#{gemfile_path}.orig")
|
320
488
|
Ops.atomic_write("#{gemfile_path}.orig") do |io|
|
321
|
-
|
489
|
+
dot_autoproj_gemfile = File.join(ws.dot_autoproj_dir, 'Gemfile')
|
490
|
+
io.puts "eval_gemfile \"#{dot_autoproj_gemfile}\""
|
322
491
|
end
|
323
492
|
end
|
324
493
|
|
@@ -330,7 +499,7 @@ def install(gems, filter_uptodate_packages: false, install_only: false)
|
|
330
499
|
gemfile_contents = Tempfile.open 'autoproj-gemfile' do |io|
|
331
500
|
gems.sort.each do |name|
|
332
501
|
name, version = parse_package_entry(name)
|
333
|
-
io.puts "gem \"#{name}\", \"#{version ||
|
502
|
+
io.puts "gem \"#{name}\", \"#{version || '>= 0'}\""
|
334
503
|
end
|
335
504
|
io.flush
|
336
505
|
gemfiles.unshift io.path
|
@@ -340,7 +509,9 @@ def install(gems, filter_uptodate_packages: false, install_only: false)
|
|
340
509
|
end
|
341
510
|
|
342
511
|
FileUtils.mkdir_p root_dir
|
343
|
-
|
512
|
+
updated = (!File.exist?(gemfile_path) ||
|
513
|
+
File.read(gemfile_path) != gemfile_contents)
|
514
|
+
if updated
|
344
515
|
Ops.atomic_write(gemfile_path) do |io|
|
345
516
|
io.puts "ruby \"#{RUBY_VERSION}\" if respond_to?(:ruby)"
|
346
517
|
io.puts gemfile_contents
|
@@ -350,24 +521,25 @@ def install(gems, filter_uptodate_packages: false, install_only: false)
|
|
350
521
|
options = Array.new
|
351
522
|
binstubs_path = File.join(root_dir, 'bin')
|
352
523
|
if updated || !install_only || !File.file?("#{gemfile_path}.lock")
|
353
|
-
self.class.run_bundler_install
|
354
|
-
|
524
|
+
self.class.run_bundler_install(ws, gemfile_path, *options,
|
525
|
+
binstubs: binstubs_path)
|
355
526
|
end
|
356
527
|
|
357
|
-
if bundle_rubylib = discover_bundle_rubylib
|
528
|
+
if (bundle_rubylib = discover_bundle_rubylib)
|
358
529
|
update_env_rubylib(bundle_rubylib)
|
359
530
|
else
|
360
|
-
raise NotCleanState, "bundler executed successfully,
|
531
|
+
raise NotCleanState, "bundler executed successfully, "\
|
532
|
+
"but the result was not in a clean state"
|
361
533
|
end
|
362
|
-
|
363
534
|
rescue Exception
|
364
|
-
Autoproj.warn "saved the new Gemfile in #{gemfile_path}.FAILED
|
535
|
+
Autoproj.warn "saved the new Gemfile in #{gemfile_path}.FAILED "\
|
536
|
+
"and restored the last Gemfile version"
|
365
537
|
FileUtils.cp gemfile_path, "#{gemfile_path}.FAILED"
|
366
538
|
backup_restore(backups)
|
367
539
|
raise
|
368
540
|
ensure
|
369
541
|
if binstubs_path
|
370
|
-
FileUtils.rm_f File.join(binstubs_path, 'bundle')
|
542
|
+
FileUtils.rm_f File.join(binstubs_path, 'bundle')
|
371
543
|
FileUtils.rm_f File.join(binstubs_path, 'bundler')
|
372
544
|
end
|
373
545
|
backup_clean(backups)
|
@@ -376,14 +548,14 @@ def install(gems, filter_uptodate_packages: false, install_only: false)
|
|
376
548
|
def discover_rubylib
|
377
549
|
require 'bundler'
|
378
550
|
Tempfile.open 'autoproj-rubylib' do |io|
|
379
|
-
result =
|
551
|
+
result = Autoproj.bundler_unbundled_system(
|
380
552
|
Hash['RUBYLIB' => nil],
|
381
553
|
Autobuild.tool('ruby'), '-e', 'puts $LOAD_PATH',
|
382
554
|
out: io,
|
383
555
|
err: '/dev/null')
|
384
556
|
if result
|
385
557
|
io.rewind
|
386
|
-
io.readlines.map
|
558
|
+
io.readlines.map(&:chomp).find_all { |l| !l.empty? }
|
387
559
|
end
|
388
560
|
end
|
389
561
|
end
|
@@ -397,25 +569,23 @@ def discover_bundle_rubylib(silent_errors: false)
|
|
397
569
|
require 'bundler'
|
398
570
|
gemfile = File.join(ws.prefix_dir, 'gems', 'Gemfile')
|
399
571
|
silent_redirect = Hash.new
|
400
|
-
if silent_errors
|
401
|
-
silent_redirect[:err] = :close
|
402
|
-
end
|
572
|
+
silent_redirect[:err] = :close if silent_errors
|
403
573
|
env = ws.env.resolved_env
|
404
574
|
Tempfile.open 'autoproj-rubylib' do |io|
|
405
|
-
result =
|
575
|
+
result = Autoproj.bundler_unbundled_system(
|
406
576
|
Hash['GEM_HOME' => env['GEM_HOME'], 'GEM_PATH' => env['GEM_PATH'],
|
407
577
|
'BUNDLE_GEMFILE' => gemfile, 'RUBYOPT' => nil,
|
408
578
|
'RUBYLIB' => self.class.rubylib_for_bundler],
|
409
|
-
Autobuild.tool('ruby'), '-rbundler/setup',
|
579
|
+
Autobuild.tool('ruby'), '-rbundler/setup',
|
580
|
+
'-e', 'puts $LOAD_PATH',
|
410
581
|
out: io, **silent_redirect)
|
411
|
-
|
582
|
+
|
412
583
|
if result
|
413
584
|
io.rewind
|
414
|
-
io.readlines.map
|
585
|
+
io.readlines.map(&:chomp).find_all { |l| !l.empty? }
|
415
586
|
end
|
416
587
|
end
|
417
588
|
end
|
418
589
|
end
|
419
590
|
end
|
420
591
|
end
|
421
|
-
|