autoproj 2.4.0 → 2.5.0.pre1

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.
@@ -53,7 +53,7 @@ def export_env_sh(subdir = nil, options = Hash.new)
53
53
  end
54
54
 
55
55
  if new_content != existing_content
56
- File.open(filename, 'w') { |io| io.write new_content }
56
+ Ops.atomic_write(filename) { |io| io.write new_content }
57
57
  true
58
58
  end
59
59
  end
@@ -1,3 +1,5 @@
1
+ require 'autobuild/exceptions'
2
+
1
3
  module Autoproj
2
4
  class ConfigError < RuntimeError
3
5
  attr_accessor :file
@@ -84,6 +86,9 @@ class OutdatedWorkspace < InvalidWorkspace; end
84
86
  # Exception raised when initializing on a workspace that is not the current
85
87
  # one
86
88
  class MismatchingWorkspace < InvalidWorkspace; end
89
+
90
+ # Raised by 'which' when an executable cannot be found
91
+ class ExecutableNotFound < ArgumentError; end
87
92
  end
88
93
 
89
94
 
@@ -61,7 +61,7 @@ def load
61
61
 
62
62
  # Save the installation manifest
63
63
  def save(path = self.path)
64
- File.open(path, 'w') do |io|
64
+ Ops.atomic_write(path) do |io|
65
65
  marshalled_package_sets = each_package_set.map do |v|
66
66
  Hash['package_set' => v.name,
67
67
  'vcs' => v.vcs.to_hash,
@@ -79,7 +79,7 @@ def save(path = self.path)
79
79
  'prefix' => v.prefix,
80
80
  'dependencies' => v.dependencies]
81
81
  end
82
- YAML.dump(marshalled_package_sets + marshalled_packages, io)
82
+ io.write YAML.dump(marshalled_package_sets + marshalled_packages)
83
83
  end
84
84
  end
85
85
 
@@ -0,0 +1,36 @@
1
+ module Autoproj
2
+ module Ops
3
+ # Shamelessly stolen from ActiveSupport
4
+ def self.atomic_write(file_name, temp_dir = Dir.tmpdir)
5
+ require 'tempfile' unless defined?(Tempfile)
6
+ require 'fileutils' unless defined?(FileUtils)
7
+
8
+ temp_file = Tempfile.new(File.basename(file_name), temp_dir)
9
+ yield temp_file
10
+ temp_file.flush
11
+ begin temp_file.fsync
12
+ rescue NotImplementedError
13
+ end
14
+ temp_file.close
15
+
16
+ begin
17
+ # Get original file permissions
18
+ old_stat = File.stat(file_name)
19
+ rescue Errno::ENOENT
20
+ # No old permissions, write a temp file to determine the defaults
21
+ check_name = File.join(
22
+ File.dirname(file_name), ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}")
23
+ File.open(check_name, "w") { }
24
+ old_stat = File.stat(check_name)
25
+ File.unlink(check_name)
26
+ end
27
+
28
+ # Overwrite original file with temp file
29
+ FileUtils.mv(temp_file.path, file_name)
30
+
31
+ # Set correct permissions on new file
32
+ File.chown(old_stat.uid, old_stat.gid, file_name)
33
+ File.chmod(old_stat.mode, file_name)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,36 @@
1
+ require 'autobuild/environment'
2
+
3
+ module Autoproj
4
+ module Ops
5
+ def self.cached_env_path(root_dir)
6
+ File.join(root_dir, '.autoproj', 'env.yml')
7
+ end
8
+
9
+ def self.load_cached_env(root_dir)
10
+ path = cached_env_path(root_dir)
11
+ if File.file?(path)
12
+ env = YAML.load(File.read(path))
13
+ Autobuild::Environment::ExportedEnvironment.new(
14
+ env['set'], env['unset'], env['update'])
15
+ end
16
+ end
17
+
18
+ def self.save_cached_env(root_dir, env)
19
+ env = env.exported_environment
20
+ path = cached_env_path(root_dir)
21
+ existing =
22
+ begin
23
+ YAML.load(File.read(path))
24
+ rescue Exception
25
+ end
26
+
27
+ env = Hash['set' => env.set, 'unset' => env.unset, 'update' => env.update]
28
+ if env != existing
29
+ Ops.atomic_write(path) do |io|
30
+ io.write YAML.dump(env)
31
+ end
32
+ true
33
+ end
34
+ end
35
+ end
36
+ end
@@ -181,7 +181,7 @@ def import_selected_packages(selection,
181
181
 
182
182
  # This is used in the ensure block, initialize as early as
183
183
  # possible
184
- executor = Concurrent::FixedThreadPool.new(parallel, max_length: 0)
184
+ executor = Concurrent::FixedThreadPool.new(parallel)
185
185
  manifest = ws.manifest
186
186
 
187
187
  selected_packages = selection.each_source_package_name.map do |pkg_name|
@@ -13,7 +13,7 @@ module Ops
13
13
  class Install
14
14
  class UnexpectedBinstub < RuntimeError; end
15
15
 
16
- # The directory in which to install autoproj
16
+ # The created workspace's root directory
17
17
  attr_reader :root_dir
18
18
  # Content of the Gemfile generated to install autoproj itself
19
19
  attr_accessor :gemfile
@@ -306,12 +306,12 @@ def install_autoproj(bundler)
306
306
  exit 1
307
307
  end
308
308
  ensure
309
- self.class.rewrite_shims(shims_path, ruby_executable, autoproj_gemfile_path, gems_gem_home)
309
+ self.class.rewrite_shims(shims_path, ruby_executable, root_dir, autoproj_gemfile_path, gems_gem_home)
310
310
  end
311
311
 
312
312
  EXCLUDED_FROM_SHIMS = %w{rake thor}
313
313
 
314
- def self.rewrite_shims(shim_path, ruby_executable, autoproj_gemfile_path, gems_gem_home)
314
+ def self.rewrite_shims(shim_path, ruby_executable, root_dir, autoproj_gemfile_path, gems_gem_home)
315
315
  FileUtils.mkdir_p shim_path
316
316
  File.open(File.join(shim_path, 'ruby'), 'w') do |io|
317
317
  io.puts "#! /bin/sh"
@@ -337,7 +337,7 @@ def self.rewrite_shims(shim_path, ruby_executable, autoproj_gemfile_path, gems_g
337
337
  io.puts shim_bundler(ruby_executable, autoproj_gemfile_path, gems_gem_home)
338
338
  else
339
339
  load_line = bin_script_lines.grep(/load Gem.bin_path/).first
340
- io.puts shim_script(ruby_executable, autoproj_gemfile_path, gems_gem_home, load_line)
340
+ io.puts shim_script(ruby_executable, root_dir, autoproj_gemfile_path, gems_gem_home, load_line)
341
341
  end
342
342
  end
343
343
  FileUtils.chmod 0755, bin_shim
@@ -361,7 +361,7 @@ def self.shim_bundler(ruby_executable, autoproj_gemfile_path, gems_gem_home)
361
361
  load Gem.bin_path('bundler', 'bundler')"
362
362
  end
363
363
 
364
- def self.shim_script(ruby_executable, autoproj_gemfile_path, gems_gem_home, load_line)
364
+ def self.shim_script(ruby_executable, root_dir, autoproj_gemfile_path, gems_gem_home, load_line)
365
365
  "#! #{ruby_executable}
366
366
 
367
367
  if defined?(Bundler)
@@ -373,6 +373,7 @@ def self.shim_script(ruby_executable, autoproj_gemfile_path, gems_gem_home, load
373
373
  end
374
374
 
375
375
  ENV['BUNDLE_GEMFILE'] = '#{autoproj_gemfile_path}'
376
+ ENV['AUTOPROJ_CURRENT_ROOT'] = '#{root_dir}'
376
377
  require 'rubygems'
377
378
  Gem.paths = Hash['GEM_HOME' => '#{gems_gem_home}', 'GEM_PATH' => '']
378
379
  require 'bundler/setup'
@@ -421,7 +422,7 @@ def save_gemfile
421
422
  "config_path = File.join(__dir__, 'config.yml')",
422
423
  "if File.file?(config_path)",
423
424
  " require 'yaml'",
424
- " config = YAML.load(File.read(config_path))",
425
+ " config = YAML.load(File.read(config_path)) || Hash.new",
425
426
  " (config['plugins'] || Hash.new).each do |plugin_name, (version, options)|",
426
427
  " gem plugin_name, version, **options",
427
428
  " end",
@@ -490,6 +491,7 @@ def install
490
491
  self.class.rewrite_shims(
491
492
  File.join(dot_autoproj, 'bin'),
492
493
  ruby_executable,
494
+ root_dir,
493
495
  autoproj_gemfile_path,
494
496
  gems_gem_home)
495
497
  env['PATH'].unshift File.join(dot_autoproj, 'bin')
@@ -504,10 +506,10 @@ def load_config
504
506
 
505
507
  config = Hash.new
506
508
  if File.file?(v1_config_path)
507
- config.merge!(YAML.load(File.read(v1_config_path)))
509
+ config.merge!(YAML.load(File.read(v1_config_path)) || Hash.new)
508
510
  end
509
511
  if File.file?(autoproj_config_path)
510
- config.merge!(YAML.load(File.read(autoproj_config_path)))
512
+ config.merge!(YAML.load(File.read(autoproj_config_path)) || Hash.new)
511
513
  end
512
514
 
513
515
  ruby = RbConfig::CONFIG['RUBY_INSTALL_NAME']
@@ -0,0 +1,37 @@
1
+ module Autoproj
2
+ module Ops
3
+ def self.watch_marker_path(root_dir)
4
+ File.join(root_dir, '.autoproj', 'watch')
5
+ end
6
+
7
+ def self.watch_running?(root_dir)
8
+ io = File.open(watch_marker_path(root_dir))
9
+ !io.flock(File::LOCK_EX | File::LOCK_NB)
10
+ rescue Errno::ENOENT
11
+ false
12
+ ensure
13
+ io.close if io
14
+ end
15
+
16
+ class WatchAlreadyRunning < RuntimeError; end
17
+
18
+ def self.watch_create_marker(root_dir)
19
+ io = File.open(watch_marker_path(root_dir), 'a+')
20
+ if !io.flock(File::LOCK_EX | File::LOCK_NB)
21
+ raise WatchAlreadyRunning, "autoproj watch is already running as PID #{io.read.strip}"
22
+ end
23
+ io.truncate(0)
24
+ io.puts Process.pid
25
+ io.flush
26
+
27
+ rescue Exception
28
+ io.close if io
29
+ raise
30
+ end
31
+
32
+ def self.watch_cleanup_marker(io)
33
+ FileUtils.rm_f io.path
34
+ io.close
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,45 @@
1
+ require 'pathname'
2
+ require 'autoproj/exceptions'
3
+ require 'autobuild/environment'
4
+
5
+ module Autoproj
6
+ module Ops
7
+ # Find the given executable file in PATH
8
+ #
9
+ # If `cmd` is an absolute path, it will either return it or raise if
10
+ # `cmd` is not executable. Otherwise, looks for an executable named
11
+ # `cmd` in PATH and returns it, or raises if it cannot be found. The
12
+ # exception contains a more detailed reason for failure
13
+ #
14
+ #
15
+ # @param [String] cmd
16
+ # @return [String] the resolved program
17
+ # @raise [ExecutableNotFound] if an executable file named `cmd` cannot
18
+ # be found
19
+ def self.which(cmd, path_entries: nil)
20
+ path = Pathname.new(cmd)
21
+ if path.absolute?
22
+ if path.file? && path.executable?
23
+ return cmd
24
+ elsif path.exist?
25
+ raise ExecutableNotFound.new(cmd), "given command `#{cmd}` exists but is not an executable file"
26
+ else
27
+ raise ExecutableNotFound.new(cmd), "given command `#{cmd}` does not exist, an executable file was expected"
28
+ end
29
+ else
30
+ if path_entries.respond_to?(:call)
31
+ path_entries = path_entries.call
32
+ end
33
+ absolute = Autobuild::Environment.find_executable_in_path(cmd, path_entries)
34
+
35
+ if absolute
36
+ return absolute
37
+ elsif file = Autobuild::Environment.find_in_path(cmd, path_entries)
38
+ raise ExecutableNotFound.new(cmd), "`#{cmd}` resolves to #{file} which is not executable"
39
+ else
40
+ raise ExecutableNotFound.new(cmd), "cannot resolve `#{cmd}` to an executable in the workspace"
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -82,7 +82,7 @@ def initialize_environment
82
82
  FileUtils.mkdir_p prefix_gems
83
83
  gemfile = File.join(prefix_gems, 'Gemfile')
84
84
  if !File.exist?(gemfile)
85
- File.open(gemfile, 'w') do |io|
85
+ Ops.atomic_write(gemfile) do |io|
86
86
  io.puts "eval_gemfile \"#{File.join(ws.dot_autoproj_dir, 'Gemfile')}\""
87
87
  end
88
88
  end
@@ -314,7 +314,7 @@ def install(gems, filter_uptodate_packages: false, install_only: false)
314
314
  # wrong to avoid leaving bundler in an inconsistent state
315
315
  backup_files(backups)
316
316
  if !File.file?("#{gemfile_path}.orig")
317
- File.open("#{gemfile_path}.orig", 'w') do |io|
317
+ Ops.atomic_write("#{gemfile_path}.orig") do |io|
318
318
  io.puts "eval_gemfile \"#{File.join(ws.dot_autoproj_dir, 'Gemfile')}\""
319
319
  end
320
320
  end
@@ -338,7 +338,7 @@ def install(gems, filter_uptodate_packages: false, install_only: false)
338
338
 
339
339
  FileUtils.mkdir_p root_dir
340
340
  if updated = (!File.exist?(gemfile_path) || File.read(gemfile_path) != gemfile_contents)
341
- File.open(gemfile_path, 'w') do |io|
341
+ Ops.atomic_write(gemfile_path) do |io|
342
342
  io.puts "ruby \"#{RUBY_VERSION}\" if respond_to?(:ruby)"
343
343
  io.puts gemfile_contents
344
344
  end
@@ -43,7 +43,8 @@ def install(pips, filter_uptodate_packages: false, install_only: false)
43
43
  "#{pips.sort.join(", ")}"
44
44
 
45
45
  cmdlines.each do |c|
46
- Autobuild::Subprocess.run 'autoproj', 'osdeps', *c
46
+ Autobuild::Subprocess.run 'autoproj', 'osdeps', *c,
47
+ env: ws.env.resolved_env
47
48
  end
48
49
 
49
50
  pips.each do |p|
@@ -271,8 +271,7 @@ def ws_create_os_package_resolver
271
271
  os_package_manager: 'os')
272
272
  end
273
273
 
274
- def ws_create
275
- dir = make_tmpdir
274
+ def ws_create(dir = make_tmpdir)
276
275
  require 'autoproj/ops/main_config_switcher'
277
276
  FileUtils.cp_r Ops::MainConfigSwitcher::MAIN_CONFIGURATION_TEMPLATE, File.join(dir, 'autoproj')
278
277
  FileUtils.mkdir_p File.join(dir, '.autoproj')
@@ -1,3 +1,3 @@
1
1
  module Autoproj
2
- VERSION = "2.4.0"
2
+ VERSION = "2.5.0.pre1"
3
3
  end
@@ -1,6 +1,3 @@
1
- require 'autoproj/ops/import'
2
- require 'autoproj/ops/install'
3
-
4
1
  module Autoproj
5
2
  class Workspace < Ops::Loader
6
3
  attr_reader :root_dir
@@ -273,6 +270,7 @@ def setup
273
270
  rewrite_shims
274
271
  autodetect_operating_system
275
272
  config.validate_ruby_executable
273
+ Autobuild.programs['ruby'] = config.ruby_executable
276
274
  config.apply_autobuild_configuration
277
275
  load_autoprojrc
278
276
  load_main_initrb
@@ -285,7 +283,7 @@ def setup
285
283
 
286
284
  Autobuild.prefix = prefix_dir
287
285
  FileUtils.mkdir_p File.join(prefix_dir, '.autoproj')
288
- File.open(File.join(prefix_dir, '.autoproj', 'config.yml'), 'w') do |io|
286
+ Ops.atomic_write(File.join(prefix_dir, '.autoproj', 'config.yml')) do |io|
289
287
  io.puts "workspace: \"#{root_dir}\""
290
288
  end
291
289
 
@@ -308,7 +306,7 @@ def install_ruby_shims
308
306
  FileUtils.mkdir_p bindir
309
307
  env.add 'PATH', bindir
310
308
 
311
- File.open(File.join(bindir, 'ruby'), 'w') do |io|
309
+ Ops.atomic_write(File.join(bindir, 'ruby')) do |io|
312
310
  io.puts "#! /bin/sh"
313
311
  io.puts "exec #{config.ruby_executable} \"$@\""
314
312
  end
@@ -318,7 +316,7 @@ def install_ruby_shims
318
316
  # Look for the corresponding gem program
319
317
  prg_name = "#{name}#{install_suffix}"
320
318
  if File.file?(prg_path = File.join(RbConfig::CONFIG['bindir'], prg_name))
321
- File.open(File.join(bindir, name), 'w') do |io|
319
+ Ops.atomic_write(File.join(bindir, name)) do |io|
322
320
  io.puts "#! #{config.ruby_executable}"
323
321
  io.puts "exec \"#{prg_path}\", *ARGV"
324
322
  end
@@ -330,7 +328,7 @@ def install_ruby_shims
330
328
  def rewrite_shims
331
329
  gemfile = File.join(dot_autoproj_dir, 'Gemfile')
332
330
  binstubs = File.join(dot_autoproj_dir, 'bin')
333
- Ops::Install.rewrite_shims(binstubs, config.ruby_executable, gemfile, config.gems_gem_home)
331
+ Ops::Install.rewrite_shims(binstubs, config.ruby_executable, root_dir, gemfile, config.gems_gem_home)
334
332
  end
335
333
 
336
334
  def update_bundler
@@ -361,7 +359,7 @@ def update_autoproj(restart_on_update: true)
361
359
  PackageManagers::BundlerManager.run_bundler_install(
362
360
  self, gemfile, binstubs: binstubs)
363
361
  ensure
364
- Ops::Install.rewrite_shims(binstubs, config.ruby_executable, gemfile, config.gems_gem_home)
362
+ rewrite_shims
365
363
  end
366
364
  if restart_on_update
367
365
  new_autoproj_path = PackageManagers::BundlerManager.bundle_gem_path(
@@ -635,9 +633,19 @@ def full_env
635
633
 
636
634
  # Export the workspace's env.sh file
637
635
  def export_env_sh(package_names = nil, shell_helpers: true)
636
+ full_env = self.full_env
637
+ save_cached_env(full_env)
638
638
  full_env.export_env_sh(shell_helpers: shell_helpers)
639
639
  end
640
640
 
641
+ def save_cached_env(env = self.full_env)
642
+ Ops.save_cached_env(root_dir, env)
643
+ end
644
+
645
+ def load_cached_env
646
+ Ops.load_cached_env(root_dir)
647
+ end
648
+
641
649
  def pristine_os_packages(packages, options = Hash.new)
642
650
  os_package_installer.pristine(packages, options)
643
651
  end
@@ -702,9 +710,6 @@ def register_package(package, block = nil, package_set = manifest.main_package_s
702
710
  pkg
703
711
  end
704
712
 
705
- class ExecutableNotFound < ArgumentError
706
- end
707
-
708
713
  # Find the given executable file in PATH
709
714
  #
710
715
  # If `cmd` is an absolute path, it will either return it or raise if
@@ -717,27 +722,8 @@ class ExecutableNotFound < ArgumentError
717
722
  # @return [String] the resolved program
718
723
  # @raise [ExecutableNotFound] if an executable file named `cmd` cannot
719
724
  # be found
720
- def which(cmd)
721
- path = Pathname.new(cmd)
722
- if path.absolute?
723
- if path.file? && path.executable?
724
- return cmd
725
- elsif path.exist?
726
- raise ExecutableNotFound.new(cmd), "given command `#{cmd}` exists but is not an executable file"
727
- else
728
- raise ExecutableNotFound.new(cmd), "given command `#{cmd}` does not exist, an executable file was expected"
729
- end
730
- else
731
- env = full_env
732
- absolute = env.find_executable_in_path(cmd)
733
- if absolute
734
- return absolute
735
- elsif file = env.find_in_path(cmd)
736
- raise ExecutableNotFound.new(cmd), "`#{cmd}` resolves to #{file} which is not executable"
737
- else
738
- raise ExecutableNotFound.new(cmd), "cannot resolve `#{cmd}` to an executable in the workspace"
739
- end
740
- end
725
+ def which(cmd, path_entries: nil)
726
+ Ops.which(cmd, path_entries: -> { full_env.value('PATH') || Array.new })
741
727
  end
742
728
  end
743
729