autoproj 2.10.1 → 2.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
|