autoproj 2.0.0.rc4 → 2.0.0.rc5

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.
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