autoproj 2.0.0.rc4 → 2.0.0.rc5

Sign up to get free protection for your applications and to get access to all the features.
data/lib/autoproj.rb CHANGED
@@ -8,6 +8,7 @@
8
8
  require 'autoproj/reporter'
9
9
  require 'autoproj/environment'
10
10
  require 'autoproj/variable_expansion'
11
+ require 'autoproj/find_workspace'
11
12
  require 'autoproj/vcs_definition'
12
13
  require 'autoproj/package_set'
13
14
  require 'autoproj/package_definition'
data/lib/autoproj/cli.rb CHANGED
@@ -1,7 +1,6 @@
1
- require 'autobuild'
2
1
  module Autoproj
3
2
  module CLI
4
- class InvalidArguments < Autobuild::Exception
3
+ class InvalidArguments < Exception
5
4
  end
6
5
 
7
6
  def self.load_plugins
@@ -1,7 +1,6 @@
1
- require 'autoproj'
1
+ require 'autoproj/cli/inspection_tool'
2
2
  require 'autoproj/cli/versions'
3
3
  require 'autoproj/ops/snapshot'
4
- require 'autoproj/cli/base'
5
4
 
6
5
  module Autoproj
7
6
  module CLI
@@ -1,4 +1,3 @@
1
- require 'autoproj'
2
1
  require 'autoproj/cli/base'
3
2
 
4
3
  module Autoproj
@@ -13,10 +12,12 @@ def initialize(ws = nil)
13
12
  super
14
13
  self.ws.load_config
15
14
 
16
- @installation_manifest = Autoproj::InstallationManifest.new(self.ws.root_dir)
17
- if !File.file?(installation_manifest.default_manifest_path)
15
+ path = InstallationManifest.path_for_root(self.ws.root_dir)
16
+ if !File.file?(path)
18
17
  raise ConfigError, "the installation manifest is not present, please run autoproj envsh to generate it"
19
18
  end
19
+
20
+ @installation_manifest = Autoproj::InstallationManifest.new(path)
20
21
  installation_manifest.load
21
22
  end
22
23
 
@@ -1,4 +1,3 @@
1
- require 'autoproj'
2
1
  require 'autoproj/cli/base'
3
2
 
4
3
  module Autoproj
@@ -1,5 +1,4 @@
1
1
  require 'thor'
2
- require 'autoproj'
3
2
  require 'autoproj/cli/main_test'
4
3
 
5
4
  module Autoproj
@@ -7,9 +6,6 @@ module CLI
7
6
  def self.basic_setup
8
7
  Encoding.default_internal = Encoding::UTF_8
9
8
  Encoding.default_external = Encoding::UTF_8
10
-
11
- Autobuild::Reporting << Autoproj::Reporter.new
12
- Autobuild::Package.clear
13
9
  end
14
10
 
15
11
  class Main < Thor
@@ -67,6 +63,8 @@ def envsh
67
63
  desc: "compare to the given baseline. if 'true', the comparison will ignore any override, otherwise it will take into account overrides only up to the given package set"
68
64
  option :snapshot, type: :boolean, default: false,
69
65
  desc: "use the VCS information as 'versions --no-local' would detect it instead of the one in the configuration"
66
+ option :parallel, aliases: :p, type: :numeric,
67
+ desc: 'maximum number of parallel jobs'
70
68
  def status(*packages)
71
69
  run_autoproj_cli(:status, :Status, Hash[], *packages)
72
70
  end
@@ -312,9 +310,29 @@ def query(query_string = nil)
312
310
  run_autoproj_cli(:query, :Query, Hash[], *Array(query_string))
313
311
  end
314
312
 
315
- desc "upgrade autoproj itself"
313
+ desc 'upgrade', "upgrade autoproj itself, and the workspace layout"
314
+ option :local, type: :boolean, default: false,
315
+ desc: 'do not access the network (will fail if some gems are missing)'
316
+ option :gemfile, type: :string,
317
+ desc: 'path to a gemfile that should be used to install autoproj'
318
+ option :private_bundler, type: :boolean,
319
+ desc: 'install bundler inside the workspace instead of using the default Gem location'
320
+ option :private_gems, type: :boolean,
321
+ desc: 'install gems inside the workspace instead of using the default Gem location'
322
+ option :private_autoproj, type: :boolean,
323
+ desc: 'install bundler inside the workspace instead of using the default Gem location'
324
+ option :private, type: :boolean,
325
+ desc: 'equivalent to --private-bundler --private-autoproj --private-gems'
316
326
  def upgrade
317
- run_autoproj_cli(:upgrade, :Upgrade, Hash[])
327
+ require 'autoproj/cli/upgrade'
328
+ Autoproj::CLI::Upgrade.new.run(options)
329
+ end
330
+
331
+ desc 'install_stage2 ROOT_DIR', 'used by autoproj_install to finalize the installation',
332
+ hide: true
333
+ def install_stage2(root_dir)
334
+ require 'autoproj/ops/install'
335
+ Autoproj::Ops::Install.new(root_dir).run(stage2: true)
318
336
  end
319
337
  end
320
338
  end
@@ -1,4 +1,5 @@
1
1
  require 'autoproj/cli'
2
+ require 'autoproj/cli/inspection_tool'
2
3
  require 'autoproj/cli/update'
3
4
  require 'autoproj/cli/versions'
4
5
 
@@ -31,7 +31,7 @@ def run(user_selection, options = Hash.new)
31
31
  pkg_sets = ws.manifest.each_package_set.to_a
32
32
  if !pkg_sets.empty?
33
33
  Autoproj.message("autoproj: displaying status of configuration", :bold)
34
- display_status(pkg_sets, snapshot: options[:snapshot], only_local: options[:only_local])
34
+ display_status(pkg_sets, parallel: options[:parallel], snapshot: options[:snapshot], only_local: options[:only_local])
35
35
  STDERR.puts
36
36
  end
37
37
  end
@@ -133,7 +133,8 @@ def status_of_package(package_description, options = Hash.new)
133
133
  def display_status(packages, options = Hash.new)
134
134
  result = StatusResult.new
135
135
 
136
- executor = Concurrent::FixedThreadPool.new(ws.config.parallel_import_level, max_length: 0)
136
+ parallel = options[:parallel] || ws.config.parallel_import_level
137
+ executor = Concurrent::FixedThreadPool.new(parallel, max_length: 0)
137
138
  interactive, noninteractive = packages.partition do |pkg|
138
139
  pkg.autobuild.importer && pkg.autobuild.importer.interactive?
139
140
  end
@@ -6,8 +6,12 @@
6
6
  module Autoproj
7
7
  module CLI
8
8
  class Tag < Base
9
- def run(tag_name, *user_selection, options = Hash.new)
10
- pkg = manifest.main_package_set.create_autobuild_package
9
+ def run(arguments, options = Hash.new)
10
+ tag_name, *user_selection = *arguments
11
+ ws.load_config
12
+ main_package_set = LocalPackageSet.new(ws.manifest, ws.config_dir)
13
+
14
+ pkg = main_package_set.create_autobuild_package
11
15
  importer = pkg.importer
12
16
  if !importer || !importer.kind_of?(Autobuild::Git)
13
17
  raise ConfigError, "cannot use autoproj tag if the main configuration is not managed by git"
@@ -21,6 +25,7 @@ def run(tag_name, *user_selection, options = Hash.new)
21
25
  importer = pkg.importer
22
26
  all_tags = importer.run_git_bare(pkg, 'tag')
23
27
  all_tags.sort.each do |tag|
28
+ next if tag =~ /\^/
24
29
  begin importer.show(pkg, "refs/tags/#{tag}", versions_file)
25
30
  puts tag
26
31
  rescue Autobuild::PackageException
@@ -43,7 +48,7 @@ def run(tag_name, *user_selection, options = Hash.new)
43
48
  Autoproj.message "creating versions file, this may take a while"
44
49
  versions.run(user_selection,
45
50
  package_sets: options[:package_sets],
46
- output_file: io.path,
51
+ save: io.path,
47
52
  replace: true,
48
53
  keep_going: options[:keep_going])
49
54
  end
@@ -1,47 +1,31 @@
1
+ require 'autoproj/ops/install'
2
+ require 'autoproj/find_workspace'
1
3
  module Autoproj
2
4
  module CLI
3
5
  class Upgrade
4
- def initialize(root_dir)
5
- @root_dir = root_dir
6
- end
7
-
8
- def upgrade_from_v2
9
- require 'autoproj/ops/install'
10
- installer = Autoproj::Ops::Install.new(root_dir)
6
+ def upgrade_from_v2(installer, options = Hash.new)
11
7
  installer.install
12
8
  end
13
9
 
14
- def find_v1_root_dir(base_dir = ENV['AUTOPROJ_CURRENT_ROOT'] || Dir.pwd)
15
- path = Pathname.new(base_dir)
16
- while !path.root?
17
- if (path + "autoproj").exist?
18
- break
19
- end
20
- path = path.parent
21
- end
10
+ def upgrade_from_v1(installer, options = Hash.new)
11
+ root_dir = installer.root_dir
22
12
 
23
- if path.root?
24
- return
13
+ # Save a backup of the existing env.sh (if the backup does not
14
+ # already exist) to make it easier for a user to downgrade
15
+ env_backup = File.join(root_dir, 'env.sh-autoproj-v1')
16
+ if !File.file?(env_backup)
17
+ FileUtils.cp File.join(root_dir, 'env.sh'), env_backup
25
18
  end
26
19
 
27
- # I don't know if this is still useful or not ... but it does not hurt
28
- #
29
- # Preventing backslashed in path, that might be confusing on some path compares
30
- if Autobuild.windows?
31
- result = result.gsub(/\\/,'/')
32
- end
33
- result
34
- end
35
-
36
- def upgrade_from_v1
37
20
  # Do an install
38
- require 'autoproj/ops/install'
39
- installer = Autoproj::Ops::Install.new(root_dir)
40
21
  installer.run
41
- # Copy the current configuration
22
+
23
+ # Copy the current configuration, merging it with the autoproj
24
+ # 2.x attributes
42
25
  current_config = File.open(File.join(root_dir, 'autoproj', 'config.yml')) do |io|
43
26
  YAML.load(io)
44
27
  end
28
+ config_file_path = File.join(root_dir, '.autoproj', 'config.yml')
45
29
  new_config = File.open(config_file_path) do |io|
46
30
  YAML.load(io)
47
31
  end
@@ -49,20 +33,40 @@ def upgrade_from_v1
49
33
  io.write YAML.dump(current_config.merge(new_config))
50
34
  end
51
35
 
52
- Autoproj.message "now, open a new console, source env.sh and run"
53
- Autoproj.message " autoproj osdeps"
54
- Autoproj.message " autoproj envsh"
36
+ # Copy the remotes symlinks
37
+ FileUtils.cp_r File.join(root_dir, '.remotes'), File.join(root_dir, '.autoproj', 'remotes')
38
+ # Copy the installation manifest
39
+ FileUtils.cp File.join(root_dir, '.autoproj-installation-manifest'),
40
+ File.join(root_dir, '.autoproj', 'installation-manifest')
41
+
42
+ puts "now, open a new console, source env.sh and run"
43
+ puts " autoproj osdeps"
44
+ puts " autoproj envsh"
45
+ end
46
+
47
+ def create_installer(root_dir, options = Hash.new)
48
+ installer = Autoproj::Ops::Install.new(root_dir)
49
+ installer.local = options[:local]
50
+ installer.private_bundler = options[:private_bundler] || options[:private]
51
+ installer.private_autoproj = options[:private_autoproj] || options[:private]
52
+ installer.private_gems = options[:private_gems] || options[:private]
53
+ if gemfile_path = options[:gemfile]
54
+ installer.gemfile = File.read(gemfile_path)
55
+ end
56
+ installer
55
57
  end
56
58
 
57
- def run(*args)
58
- root_dir = Workspace.find_root_dir
59
+ def run(options = Hash.new)
60
+ root_dir = Autoproj.find_v2_workspace_dir
59
61
  if root_dir && File.directory?(File.join(root_dir, '.autoproj'))
60
- return upgrade_from_v2
62
+ installer = create_installer(root_dir, options)
63
+ return upgrade_from_v2(installer, options)
61
64
  end
62
65
 
63
- root_dir = find_v1_root_dir
64
- if root_dir && File.directory?(root_dir, '.gems')
65
- return upgrade_from_v1
66
+ root_dir = Autoproj.find_v1_workspace_dir
67
+ if root_dir && File.directory?(File.join(root_dir, '.gems'))
68
+ installer = create_installer(root_dir, options)
69
+ return upgrade_from_v1(installer, options)
66
70
  end
67
71
  end
68
72
  end
@@ -8,8 +8,16 @@ class Environment < Autobuild::Environment
8
8
  attr_reader :root_dir
9
9
 
10
10
  def prepare(root_dir)
11
+ super()
12
+
11
13
  @root_dir = root_dir
12
14
  set 'AUTOPROJ_CURRENT_ROOT', root_dir
15
+
16
+ @original_env = original_env.map_value do |name, value|
17
+ filtered = value.split(File::PATH_SEPARATOR).
18
+ find_all { |p| !Workspace.in_autoproj_project?(p) }
19
+ filtered.join(File::PATH_SEPARATOR)
20
+ end
13
21
  end
14
22
 
15
23
  def expand(value)
@@ -49,6 +49,10 @@ class WorkspaceAlreadyCreated < RuntimeError; end
49
49
  # Exception raised when looking for a workspace and it cannot be found
50
50
  class NotWorkspace < RuntimeError; end
51
51
 
52
+ # Exception raised when the autoproj workspace changes and the current
53
+ # workspace is outdated
54
+ class OutdatedWorkspace < RuntimeError; end
55
+
52
56
  # Exception raised when initializing on a workspace that is not the current
53
57
  # one
54
58
  class MismatchingWorkspace < RuntimeError; end
@@ -0,0 +1,83 @@
1
+ require 'pathname'
2
+ require 'yaml'
3
+
4
+ module Autoproj
5
+ # The base path from which we search for workspaces
6
+ def self.defaulT_find_base_dir
7
+ ENV['AUTOPROJ_CURRENT_ROOT'] || Dir.pwd
8
+ end
9
+
10
+ # Looks for the autoproj workspace that is related to a given directory
11
+ #
12
+ # @return [String,nil]
13
+ def self.find_workspace_dir(base_dir = defaulT_find_base_dir)
14
+ find_v2_workspace_dir(base_dir)
15
+ end
16
+
17
+ # Looks for the autoproj prefix that contains a given directory
18
+ #
19
+ # @return [String,nil]
20
+ def self.find_prefix_dir(base_dir = defaulT_find_base_dir)
21
+ find_v2_prefix_dir(base_dir)
22
+ end
23
+
24
+ # @private
25
+ #
26
+ # Finds an autoproj "root directory" that contains a given directory. It
27
+ # can either be the root of a workspace or the root of an install
28
+ # directory
29
+ #
30
+ # @param [String] base_dir the start of the search
31
+ # @param [String] config_field_name the name of a field in the root's
32
+ # configuration file, that should be returned instead of the root
33
+ # itself
34
+ # @return [String,nil] the root of the workspace directory, or nil if
35
+ # there's none
36
+ def self.find_v2_root_dir(base_dir, config_field_name)
37
+ path = Pathname.new(base_dir)
38
+ while !path.root?
39
+ if (path + ".autoproj").exist?
40
+ break
41
+ end
42
+ path = path.parent
43
+ end
44
+
45
+ if path.root?
46
+ return
47
+ end
48
+
49
+ config_path = path + ".autoproj" + "config.yml"
50
+ if config_path.exist?
51
+ config = YAML.load(config_path.read) || Hash.new
52
+ result = config[config_field_name] || path.to_s
53
+ File.expand_path(result, path.to_s)
54
+ else
55
+ path.to_s
56
+ end
57
+ end
58
+
59
+ # {#find_workspace_dir} for v2 workspaces
60
+ def self.find_v2_workspace_dir(base_dir = defaulT_find_base_dir)
61
+ find_v2_root_dir(base_dir, 'workspace')
62
+ end
63
+
64
+ # {#find_prefix_dir} for v2 workspaces
65
+ def self.find_v2_prefix_dir(base_dir = defaulT_find_base_dir)
66
+ find_v2_root_dir(base_dir, 'prefix')
67
+ end
68
+
69
+ # {#find_workspace_dir} for v1 workspaces
70
+ #
71
+ # Note that for v1 workspaces {#find_prefix_dir} cannot be implemented
72
+ def self.find_v1_workspace_dir(base_dir = defaulT_find_base_dir)
73
+ path = Pathname.new(base_dir)
74
+ while !path.root?
75
+ if (path + "autoproj").exist?
76
+ return path.to_s
77
+ end
78
+ path = path.parent
79
+ end
80
+ nil
81
+ end
82
+ end
83
+
@@ -1,3 +1,4 @@
1
+ require 'pathname'
1
2
  require 'optparse'
2
3
  require 'fileutils'
3
4
  require 'yaml'
@@ -10,19 +11,65 @@ module Ops
10
11
  # It can be required standalone (i.e. does not depend on anything else than
11
12
  # ruby and the ruby standard library)
12
13
  class Install
14
+ class UnexpectedBinstub < RuntimeError; end
15
+
13
16
  # The directory in which to install autoproj
14
17
  attr_reader :root_dir
15
18
  # Content of the Gemfile generated to install autoproj itself
16
19
  attr_accessor :gemfile
20
+ # The environment that is passed to the bundler installs
21
+ attr_reader :env
17
22
 
18
23
  def initialize(root_dir)
19
24
  @root_dir = root_dir
20
- @gemfile = default_gemfile_contents
25
+ if File.file?(autoproj_gemfile_path)
26
+ @gemfile = File.read(autoproj_gemfile_path)
27
+ else
28
+ @gemfile = default_gemfile_contents
29
+ end
30
+
21
31
  @private_bundler = false
22
32
  @private_autoproj = false
23
33
  @private_gems = false
34
+ @local = false
35
+ @env = self.class.clean_env
36
+ end
37
+
38
+ def self.clean_env
39
+ env = Hash.new
40
+ env['RUBYLIB'] = []
41
+ env['GEM_PATH'] = []
42
+ %w{PATH GEM_HOME}.each do |name|
43
+ env[name] = sanitize_env(ENV[name] || "")
44
+ end
45
+ env['BUNDLE_GEMFILE'] = nil
46
+ env
24
47
  end
25
48
 
49
+ def env_for_child
50
+ env.inject(Hash.new) do |h, (k, v)|
51
+ h[k] = (v.join(File::PATH_SEPARATOR) if v && !v.empty?)
52
+ h
53
+ end
54
+ end
55
+
56
+ def self.sanitize_env(value)
57
+ value.split(File::PATH_SEPARATOR).
58
+ find_all { |p| !in_workspace?(p) }
59
+ end
60
+
61
+ def self.in_workspace?(base_dir)
62
+ path = Pathname.new(base_dir)
63
+ while !path.root?
64
+ if (path + ".autoproj").exist? || (path + "autoproj").exist?
65
+ return true
66
+ end
67
+ path = path.parent
68
+ end
69
+ return false
70
+ end
71
+
72
+
26
73
  def dot_autoproj; File.join(root_dir, '.autoproj') end
27
74
  def bin_dir; File.join(dot_autoproj, 'bin') end
28
75
  def bundler_install_dir; File.join(dot_autoproj, 'bundler') end
@@ -31,13 +78,24 @@ def autoproj_install_dir; File.join(dot_autoproj, 'autoproj') end
31
78
  def autoproj_gemfile_path; File.join(autoproj_install_dir, 'Gemfile') end
32
79
  def autoproj_config_path; File.join(dot_autoproj, 'config.yml') end
33
80
 
81
+ # Whether we can access the network while installing
82
+ def local?; !!@local end
83
+ # (see #local?)
84
+ def local=(flag); @local = flag end
85
+
34
86
  # Whether bundler should be installed locally in {#dot_autoproj}
35
87
  def private_bundler?; @private_bundler end
88
+ # (see #private_bundler?)
89
+ def private_bundler=(flag); @private_bundler = flag end
36
90
  # Whether autoproj should be installed locally in {#dot_autoproj}
37
91
  def private_autoproj?; @private_autoproj end
92
+ # (see #private_autoproj?)
93
+ def private_autoproj=(flag); @private_autoproj = flag end
38
94
  # Whether bundler should be installed locally in the workspace
39
95
  # prefix directory
40
96
  def private_gems?; @private_gems end
97
+ # (see #private_gems?)
98
+ def private_gems=(flag); @private_gems = flag end
41
99
 
42
100
  def guess_gem_program
43
101
  ruby_bin = RbConfig::CONFIG['RUBY_INSTALL_NAME']
@@ -61,14 +119,18 @@ def guess_gem_program
61
119
  # @param [String] autoproj_version a constraint on the autoproj version
62
120
  # that should be used
63
121
  # @return [String]
64
- def default_gemfile_contents(autoproj_version = ">= 0")
122
+ def default_gemfile_contents(autoproj_version = ">= 2.0.0.a")
65
123
  ["source \"https://rubygems.org\"",
66
- "gem \"autoproj\", \"#{autoproj_version}\""].join("\n")
124
+ "gem \"autoproj\", \"#{autoproj_version}\"",
125
+ "gem \"utilrb\", \">= 3.0.0.a\""].join("\n")
67
126
  end
68
127
 
69
128
  # Parse the provided command line options and returns the non-options
70
129
  def parse_options(args = ARGV)
71
130
  options = OptionParser.new do |opt|
131
+ opt.on '--local', 'do not access the network (may fail)' do
132
+ @local = true
133
+ end
72
134
  opt.on '--private-bundler', 'install bundler locally in the workspace' do
73
135
  @private_bundler = true
74
136
  end
@@ -97,28 +159,37 @@ def install_bundler
97
159
  gem_program = guess_gem_program
98
160
  puts "Detected 'gem' to be #{gem_program}"
99
161
 
162
+ local = ['--local'] if local?
163
+
100
164
  result = system(
101
- Hash['GEM_PATH' => nil,
102
- 'GEM_HOME' => bundler_install_dir],
165
+ env_for_child.merge('GEM_PATH' => nil, 'GEM_HOME' => bundler_install_dir),
103
166
  gem_program, 'install', '--no-document', '--no-user-install', '--no-format-executable',
167
+ *local,
104
168
  "--bindir=#{File.join(bundler_install_dir, 'bin')}", 'bundler')
105
169
 
106
170
  if !result
107
171
  STDERR.puts "FATAL: failed to install bundler in #{dot_autoproj}"
108
172
  exit 1
109
173
  end
174
+ env['GEM_PATH'] << bundler_install_dir
175
+ env['PATH'] << File.join(bundler_install_dir, 'bin')
110
176
  File.join(bin_dir, 'bundler')
111
177
  end
112
178
 
113
179
  def save_env_sh
114
180
  env = Autobuild::Environment.new
115
- path = []
116
- if private_bundler?
117
- env.push_path 'PATH', File.join(bundler_install_dir, 'bin')
118
- env.push_path 'GEM_PATH', bundler_install_dir
181
+ env.prepare
182
+
183
+ %w{GEM_HOME GEM_PATH}.each do |name|
184
+ value = self.env[name]
185
+ if value.empty?
186
+ env.unset name
187
+ else
188
+ env.set name, *value
189
+ end
119
190
  end
120
191
  env.push_path 'PATH', File.join(autoproj_install_dir, 'bin')
121
- env.inherit 'PATH'
192
+
122
193
  if private_autoproj?
123
194
  env.push_path 'GEM_PATH', autoproj_install_dir
124
195
  end
@@ -142,7 +213,9 @@ def save_gemfile
142
213
  io.write gemfile
143
214
  end
144
215
  end
145
-
216
+
217
+ ENV_BUNDLE_GEMFILE_RX = /^(\s*ENV\[['"]BUNDLE_GEMFILE['"]\]\s*)(?:\|\|)?=/
218
+
146
219
  def install_autoproj(bundler)
147
220
  # Force bundler to update. If the user does not want this, let him specify a
148
221
  # Gemfile with tighter version constraints
@@ -151,24 +224,57 @@ def install_autoproj(bundler)
151
224
  FileUtils.rm lockfile
152
225
  end
153
226
 
154
- env = Hash['BUNDLE_GEMFILE' => nil, 'RUBYLIB' => nil]
155
227
  opts = Array.new
228
+ opts << '--local' if local?
156
229
 
230
+ env = env_for_child
157
231
  if private_autoproj?
158
- env = Hash['GEM_PATH' => bundler_install_dir,
159
- 'GEM_HOME' => nil]
232
+ env = env.merge(
233
+ 'GEM_PATH' => bundler_install_dir,
234
+ 'GEM_HOME' => nil)
160
235
  opts << "--clean" << "--path=#{autoproj_install_dir}"
161
236
  end
162
237
 
238
+ binstubs_path = File.join(autoproj_install_dir, 'bin')
239
+
163
240
  result = system(env,
164
- bundler, 'install',
241
+ Gem.ruby, bundler, 'install',
165
242
  "--gemfile=#{autoproj_gemfile_path}",
166
- "--binstubs=#{File.join(autoproj_install_dir, 'bin')}",
243
+ "--binstubs=#{binstubs_path}",
167
244
  *opts)
168
245
  if !result
169
246
  STDERR.puts "FATAL: failed to install autoproj in #{dot_autoproj}"
170
247
  exit 1
171
248
  end
249
+
250
+ # Now tune the binstubs to force the usage of the autoproj
251
+ # gemfile. Otherwise, they get BUNDLE_GEMFILE from the
252
+ # environment by default
253
+ Dir.glob(File.join(binstubs_path, '*')) do |path|
254
+ next if !File.file?(path)
255
+ # Do NOT do that for bundler, otherwise it will fail with an
256
+ # "already loaded gemfile" message once we e.g. try to do
257
+ # 'bundler install --gemfile=NEW_GEMFILE'
258
+ next if File.basename(path) == 'bundler'
259
+
260
+ lines = File.readlines(path)
261
+ matched = false
262
+ filtered = lines.map do |l|
263
+ matched ||= (ENV_BUNDLE_GEMFILE_RX === l)
264
+ l.gsub(ENV_BUNDLE_GEMFILE_RX, '\\1=')
265
+ end
266
+ if !matched
267
+ raise UnexpectedBinstub, "expected #{path} to contain a line looking like ENV['BUNDLE_GEMFILE'] ||= but could not find one"
268
+ end
269
+ File.open(path, 'w') do |io|
270
+ io.write filtered.join("")
271
+ end
272
+ end
273
+
274
+ env['PATH'] << File.join(autoproj_install_dir, 'bin')
275
+ if private_autoproj?
276
+ env['GEM_PATH'] << autoproj_install_dir
277
+ end
172
278
  end
173
279
 
174
280
  def update_configuration
@@ -185,25 +291,92 @@ def update_configuration
185
291
  end
186
292
  end
187
293
 
294
+ def find_in_clean_path(command)
295
+ clean_path = env_for_child['PATH'].split(File::PATH_SEPARATOR)
296
+ clean_path.each do |p|
297
+ full_path = File.join(p, command)
298
+ if File.file?(full_path)
299
+ return full_path
300
+ end
301
+ end
302
+ nil
303
+ end
304
+
305
+ def find_bundler
306
+ clean_env = env_for_child
307
+ Gem.paths = Hash[
308
+ 'GEM_HOME' => clean_env['GEM_HOME'] || Gem.default_dir,
309
+ 'GEM_PATH' => clean_env['GEM_PATH'] || nil
310
+ ]
311
+ # Here, we're getting into the esotheric
312
+ #
313
+ # The problem is that e.g. Ubuntu and Debian install an
314
+ # operating_system.rb file that sets proper OS defaults. Some
315
+ # autoproj installs have it in their RUBYLIB but should not
316
+ # because of limitations of autoproj 1.x. This leads to
317
+ # Gem.bindir being *not* valid for subprocesses
318
+ #
319
+ # So, we're calling 'gem' as a subcommand to discovery the
320
+ # actual bindir
321
+ bindir = IO.popen(clean_env, [Gem.ruby, '-e', 'puts Gem.bindir']).read
322
+ if bindir
323
+ env['PATH'].unshift bindir.chomp
324
+ else
325
+ STDERR.puts "FATAL: cannot run #{Gem.ruby} -e 'puts Gem.bindir'"
326
+ exit 1
327
+ end
328
+
329
+ bundler = find_in_clean_path('bundler')
330
+ if !bundler
331
+ clean_path = env_for_child['PATH']
332
+ STDERR.puts "FATAL: cannot find 'bundler' in PATH=#{clean_path}"
333
+ if ENV['PATH'] != clean_path
334
+ STDERR.puts " it appears that you already have some autoproj-generated env.sh loaded"
335
+ STDERR.puts " - if you are running 'autoproj upgrade', please contact the autoproj author at https://github.com/rock-core/autoproj/issues/new"
336
+ STDERR.puts " - if you are running an install, try again in a console where the env.sh is not loaded"
337
+ exit 1
338
+ else
339
+ STDERR.puts " the recommended action is to install it manually first by running 'gem install bundler'"
340
+ STDERR.puts " or call this command again with --private-bundler to have it installed in the workspace"
341
+ exit 1
342
+ end
343
+ end
344
+ bundler
345
+ end
346
+
188
347
  def install
189
348
  if private_bundler?
190
349
  puts "Installing bundler in #{bundler_install_dir}"
191
350
  bundler = install_bundler
351
+ else
352
+ bundler = find_bundler
353
+ puts "Detected bundler at #{bundler}"
192
354
  end
193
355
  save_gemfile
194
356
  puts "Installing autoproj in #{dot_autoproj}"
195
- install_autoproj(bundler || 'bundler')
357
+ install_autoproj(bundler)
196
358
  end
197
359
 
198
360
  # Actually perform the install
199
- def run
200
- install
201
- ENV['BUNDLE_GEMFILE'] = autoproj_gemfile_path
202
- require 'bundler'
203
- Bundler.setup
204
- require 'autobuild'
205
- save_env_sh
206
- update_configuration
361
+ def run(stage2: false)
362
+ if stage2
363
+ require 'autobuild'
364
+ save_env_sh
365
+ else
366
+ install
367
+
368
+ env_for_child.each do |k, v|
369
+ if v
370
+ ENV[k] = v
371
+ else
372
+ ENV.delete(k)
373
+ end
374
+ end
375
+ ENV['BUNDLE_GEMFILE'] = autoproj_gemfile_path
376
+ update_configuration
377
+ exec Gem.ruby, File.join(autoproj_install_dir, 'bin', 'autoproj'),
378
+ 'install-stage2', root_dir
379
+ end
207
380
  end
208
381
  end
209
382
  end