autoproj 2.4.0 → 2.5.0.pre1

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