autoproj 2.0.0.rc3 → 2.0.0.rc4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +9 -29
  3. data/bin/autoproj_bootstrap +159 -3150
  4. data/bin/autoproj_bootstrap.in +4 -256
  5. data/bin/autoproj_install +225 -0
  6. data/bin/autoproj_install.in +14 -0
  7. data/lib/autoproj.rb +2 -1
  8. data/lib/autoproj/autobuild.rb +2 -2
  9. data/lib/autoproj/cli/bootstrap.rb +0 -39
  10. data/lib/autoproj/cli/build.rb +0 -3
  11. data/lib/autoproj/cli/main.rb +13 -1
  12. data/lib/autoproj/cli/osdeps.rb +1 -1
  13. data/lib/autoproj/cli/show.rb +1 -1
  14. data/lib/autoproj/cli/update.rb +4 -4
  15. data/lib/autoproj/cli/upgrade.rb +71 -0
  16. data/lib/autoproj/configuration.rb +18 -1
  17. data/lib/autoproj/exceptions.rb +7 -0
  18. data/lib/autoproj/installation_manifest.rb +23 -12
  19. data/lib/autoproj/manifest.rb +22 -48
  20. data/lib/autoproj/ops/build.rb +2 -2
  21. data/lib/autoproj/ops/configuration.rb +1 -1
  22. data/lib/autoproj/ops/import.rb +1 -1
  23. data/lib/autoproj/ops/install.rb +211 -0
  24. data/lib/autoproj/ops/main_config_switcher.rb +1 -5
  25. data/lib/autoproj/os_package_installer.rb +348 -0
  26. data/lib/autoproj/{osdeps.rb → os_package_resolver.rb} +56 -392
  27. data/lib/autoproj/package_managers/apt_dpkg_manager.rb +2 -2
  28. data/lib/autoproj/package_managers/bundler_manager.rb +179 -0
  29. data/lib/autoproj/package_managers/emerge_manager.rb +2 -2
  30. data/lib/autoproj/package_managers/gem_manager.rb +7 -6
  31. data/lib/autoproj/package_managers/homebrew_manager.rb +2 -2
  32. data/lib/autoproj/package_managers/manager.rb +5 -6
  33. data/lib/autoproj/package_managers/pacman_manager.rb +2 -2
  34. data/lib/autoproj/package_managers/pip_manager.rb +8 -8
  35. data/lib/autoproj/package_managers/pkg_manager.rb +2 -2
  36. data/lib/autoproj/package_managers/port_manager.rb +2 -2
  37. data/lib/autoproj/package_managers/shell_script_manager.rb +4 -4
  38. data/lib/autoproj/package_managers/unknown_os_manager.rb +2 -2
  39. data/lib/autoproj/package_managers/yum_manager.rb +2 -2
  40. data/lib/autoproj/package_managers/zypper_manager.rb +2 -2
  41. data/lib/autoproj/package_set.rb +10 -10
  42. data/lib/autoproj/reporter.rb +3 -2
  43. data/lib/autoproj/system.rb +1 -4
  44. data/lib/autoproj/version.rb +1 -1
  45. data/lib/autoproj/workspace.rb +155 -32
  46. metadata +9 -3
@@ -5,9 +5,9 @@ module PackageManagers
5
5
  class AptDpkgManager < ShellScriptManager
6
6
  attr_accessor :status_file
7
7
 
8
- def initialize(status_file = "/var/lib/dpkg/status")
8
+ def initialize(ws, status_file = "/var/lib/dpkg/status")
9
9
  @status_file = status_file
10
- super(['apt-dpkg'], true,
10
+ super(ws, true,
11
11
  "apt-get install '%s'",
12
12
  "export DEBIAN_FRONTEND=noninteractive; apt-get install -y '%s'")
13
13
  end
@@ -0,0 +1,179 @@
1
+ module Autoproj
2
+ module PackageManagers
3
+ # Package manager interface for the RubyGems system
4
+ class BundlerManager < Manager
5
+ class << self
6
+ attr_writer :with_prerelease
7
+ attr_accessor :with_doc
8
+ end
9
+ @with_prerelease = false
10
+ @with_doc = false
11
+
12
+ def self.with_prerelease(*value)
13
+ if value.empty?
14
+ @with_prerelease
15
+ else
16
+ begin
17
+ saved_flag = @with_prerelease
18
+ @with_prerelease = value.first
19
+ yield
20
+ ensure
21
+ @with_prerelease = saved_flag
22
+ end
23
+ end
24
+ end
25
+
26
+ # Filters all paths that come from other autoproj installations out
27
+ # of GEM_PATH
28
+ def initialize_environment
29
+ env = ws.env
30
+
31
+ env.original_env['GEM_PATH'] =
32
+ (env['GEM_PATH'] || "").split(File::PATH_SEPARATOR).find_all do |p|
33
+ !Workspace.in_autoproj_project?(p)
34
+ end.join(File::PATH_SEPARATOR)
35
+ env.inherit 'GEM_PATH'
36
+ env.init_from_env 'GEM_PATH'
37
+ orig_gem_path = env.original_env['GEM_PATH'].split(File::PATH_SEPARATOR)
38
+ env.system_env['GEM_PATH'] = Gem.default_path
39
+ env.original_env['GEM_PATH'] = orig_gem_path.join(File::PATH_SEPARATOR)
40
+
41
+ env.init_from_env 'RUBYLIB'
42
+ env.inherit 'RUBYLIB'
43
+ original_rubylib =
44
+ (env['RUBYLIB'] || "").split(File::PATH_SEPARATOR).find_all do |p|
45
+ !Workspace.in_autoproj_project?(p)
46
+ !p.start_with?(Bundler.rubygems.gem_dir) &&
47
+ !Bundler.rubygems.gem_path.any? { |gem_p| p.start_with?(p) }
48
+ end
49
+ system_rubylib = discover_rubylib
50
+ env.system_env['RUBYLIB'] = []
51
+ env.original_env['RUBYLIB'] = (original_rubylib - system_rubylib).join(File::PATH_SEPARATOR)
52
+
53
+ dot_autoproj = ws.dot_autoproj_dir
54
+ if ws.config.private_bundler?
55
+ env.push_path 'GEM_PATH', File.join(dot_autoproj, 'bundler')
56
+ end
57
+ if ws.config.private_autoproj?
58
+ env.push_path 'GEM_PATH', File.join(dot_autoproj, 'autoproj')
59
+ end
60
+
61
+ ws.manifest.each_reused_autoproj_installation do |p|
62
+ reused_w = ws.new(p)
63
+ reused_c = reused_w.load_config
64
+ if reused_c.private_gems?
65
+ env.push_path 'GEM_PATH', File.join(reused_w.prefix_dir, 'gems')
66
+ end
67
+ env.push_path 'PATH', File.join(reused_w.prefix_dir, 'gems', 'bin')
68
+ end
69
+
70
+
71
+ gem_home = File.join(ws.prefix_dir, "gems")
72
+ if ws.config.private_gems?
73
+ env.set 'GEM_HOME', gem_home
74
+ env.push_path 'GEM_PATH', gem_home
75
+ end
76
+
77
+ FileUtils.mkdir_p gem_home
78
+ gemfile = File.join(gem_home, 'Gemfile')
79
+ if !File.exists?(gemfile)
80
+ File.open(gemfile, 'w') do |io|
81
+ io.puts "eval_gemfile \"#{File.join(ws.dot_autoproj_dir, 'autoproj', 'Gemfile')}\""
82
+ end
83
+ end
84
+
85
+ env.set 'BUNDLE_GEMFILE', File.join(gem_home, 'Gemfile')
86
+ env.push_path 'PATH', File.join(ws.dot_autoproj_dir, 'autoproj', 'bin')
87
+ env.push_path 'PATH', File.join(gem_home, 'bin')
88
+ Autobuild.programs['bundler'] = File.join(ws.dot_autoproj_dir, 'autoproj', 'bin', 'bundler')
89
+
90
+ update_env_rubylib(system_rubylib)
91
+ end
92
+
93
+ def update_env_rubylib(system_rubylib = discover_rubylib)
94
+ rubylib = discover_bundle_rubylib
95
+ current = ws.env.resolved_env['RUBYLIB'].split(File::PATH_SEPARATOR) + system_rubylib
96
+ (rubylib - current).each do |p|
97
+ ws.env.push_path('RUBYLIB', p)
98
+ end
99
+ end
100
+
101
+ def parse_package_entry(entry)
102
+ if entry =~ /^([^><=~]*)([><=~]+.*)$/
103
+ [$1.strip, $2.strip]
104
+ else
105
+ [entry]
106
+ end
107
+ end
108
+
109
+ def install(gems)
110
+ # Generate the gemfile
111
+ gems = gems.sort.map do |name|
112
+ name, version = parse_package_entry(name)
113
+ "gem \"#{name}\", \"#{version || ">= 0"}\""
114
+ end.join("\n")
115
+
116
+ root_dir = File.join(ws.prefix_dir, 'gems')
117
+ FileUtils.mkdir_p root_dir
118
+ gemfile = File.join(root_dir, 'Gemfile')
119
+ File.open(gemfile, 'w') do |io|
120
+ io.puts "eval_gemfile \"#{File.join(ws.dot_autoproj_dir, 'autoproj', 'Gemfile')}\""
121
+ io.puts gems
122
+ end
123
+
124
+ File.open(File.join(root_dir, 'Gemfile.lock'), 'w') do |io|
125
+ io.write File.read(File.join(ws.dot_autoproj_dir, 'autoproj', 'Gemfile.lock'))
126
+ end
127
+
128
+ if ws.config.private_gems?
129
+ options = ['--path', root_dir]
130
+ end
131
+
132
+ Bundler.with_clean_env do
133
+ connections = Set.new
134
+ Autobuild::Subprocess.run 'autoproj', 'osdeps',
135
+ Autobuild.tool('bundler'), 'install',
136
+ "--gemfile=#{gemfile}", *options,
137
+ "--binstubs", File.join(root_dir, 'bin'),
138
+ env: Hash['BUNDLE_GEMFILE' => gemfile] do |line|
139
+
140
+ case line
141
+ when /Installing (.*)/
142
+ Autobuild.message " bundler: installing #{$1}"
143
+ when /Fetching.*from (.*)/
144
+ host = $1.gsub(/\.+$/, '')
145
+ if !connections.include?(host)
146
+ Autobuild.message " bundler: connected to #{host}"
147
+ connections << host
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ update_env_rubylib
154
+ end
155
+
156
+ def discover_rubylib
157
+ r, w = IO.pipe
158
+ Bundler.clean_system(
159
+ Hash['RUBYLIB' => nil],
160
+ Autobuild.tool('ruby'), '-e', 'puts $LOAD_PATH',
161
+ out: w)
162
+ w.close
163
+ r.readlines.map { |l| l.chomp }.find_all { |l| !l.empty? }
164
+ end
165
+
166
+ def discover_bundle_rubylib
167
+ gemfile = File.join(ws.prefix_dir, 'gems', 'Gemfile')
168
+ r, w = IO.pipe
169
+ Bundler.clean_system(
170
+ Hash['BUNDLE_GEMFILE' => gemfile],
171
+ Autobuild.tool('bundler'), 'exec', 'ruby', '-e', 'puts $LOAD_PATH',
172
+ out: w)
173
+ w.close
174
+ r.readlines.map { |l| l.chomp }.find_all { |l| !l.empty? }
175
+ end
176
+ end
177
+ end
178
+ end
179
+
@@ -3,8 +3,8 @@ module PackageManagers
3
3
  # Package manager interface for systems that use emerge (i.e. gentoo) as
4
4
  # their package manager
5
5
  class EmergeManager < ShellScriptManager
6
- def initialize
7
- super(['emerge'], true,
6
+ def initialize(ws)
7
+ super(ws, true,
8
8
  "emerge '%s'",
9
9
  "emerge --noreplace '%s'")
10
10
  end
@@ -25,7 +25,8 @@ def self.with_prerelease(*value)
25
25
 
26
26
  # Filters all paths that come from other autoproj installations out
27
27
  # of GEM_PATH
28
- def self.initialize_environment(env = Autobuild.env, manifest = Autoproj.manifest, root_dir = Autoproj.root_dir)
28
+ def initialize_environment
29
+ env = ws.env
29
30
  env.original_env['GEM_PATH'] =
30
31
  (env['GEM_PATH'] || "").split(File::PATH_SEPARATOR).find_all do |p|
31
32
  !Autoproj.in_autoproj_installation?(p)
@@ -37,7 +38,7 @@ def self.initialize_environment(env = Autobuild.env, manifest = Autoproj.manifes
37
38
  env.system_env['GEM_PATH'] = Gem.default_path
38
39
  env.original_env['GEM_PATH'] = orig_gem_path.join(File::PATH_SEPARATOR)
39
40
 
40
- manifest.each_reused_autoproj_installation do |p|
41
+ ws.manifest.each_reused_autoproj_installation do |p|
41
42
  p_gems = File.join(p, '.gems')
42
43
  if File.directory?(p_gems)
43
44
  env.push_path 'GEM_PATH', p_gems
@@ -45,7 +46,7 @@ def self.initialize_environment(env = Autobuild.env, manifest = Autoproj.manifes
45
46
  end
46
47
  end
47
48
 
48
- @gem_home = (ENV['AUTOPROJ_GEM_HOME'] || File.join(root_dir, ".gems"))
49
+ @gem_home = (ENV['AUTOPROJ_GEM_HOME'] || File.join(ws.root_dir, ".gems"))
49
50
  env.push_path 'GEM_PATH', gem_home
50
51
  env.set 'GEM_HOME', gem_home
51
52
  env.push_path 'PATH', "#{gem_home}/bin"
@@ -99,8 +100,8 @@ def self.default_install_options
99
100
  @default_install_options ||= ['--no-user-install', '--no-format-executable']
100
101
  end
101
102
 
102
- def initialize
103
- super(['gem'])
103
+ def initialize(ws)
104
+ super(ws)
104
105
  @installed_gems = Set.new
105
106
  end
106
107
 
@@ -274,7 +275,7 @@ def parse_package_entry(entry)
274
275
  end
275
276
 
276
277
  def gems_interaction(gems, cmdlines)
277
- if OSDependencies.force_osdeps
278
+ if OSPackageInstaller.force_osdeps
278
279
  return true
279
280
  elsif enabled?
280
281
  return true
@@ -3,8 +3,8 @@ module PackageManagers
3
3
  # Package manager interface for Mac OS using homebrew as
4
4
  # its package manager
5
5
  class HomebrewManager < ShellScriptManager
6
- def initialize
7
- super(['brew'], true,
6
+ def initialize(ws)
7
+ super(ws, true,
8
8
  "brew install '%s'",
9
9
  "brew install '%s'",
10
10
  false)
@@ -7,9 +7,8 @@ module PackageManagers
7
7
  # Package managers must be registered in PACKAGE_HANDLERS and
8
8
  # (if applicable) OS_PACKAGE_HANDLERS.
9
9
  class Manager
10
- # @return [Array<String>] the various names this package manager is
11
- # known about
12
- attr_reader :names
10
+ # @return [Workspace] the workspace
11
+ attr_reader :ws
13
12
 
14
13
  attr_writer :enabled
15
14
  def enabled?; !!@enabled end
@@ -22,8 +21,8 @@ def silent?; !!@silent end
22
21
  # @param [Array<String>] names the package manager names. It MUST be
23
22
  # different from the OS names that autoproj uses. See the comment
24
23
  # for OS_PACKAGE_HANDLERS for an explanation
25
- def initialize(names = [])
26
- @names = names.dup
24
+ def initialize(ws)
25
+ @ws = ws
27
26
  @enabled = true
28
27
  @silent = true
29
28
  end
@@ -37,7 +36,7 @@ def name
37
36
  # order to have a properly functioning package manager
38
37
  #
39
38
  # This is e.g. needed for python pip or rubygems
40
- def self.initialize_environment(_env = nil, _manifest = nil, _root_dir = Autoproj.root_dir)
39
+ def initialize_environment
41
40
  end
42
41
  end
43
42
  end
@@ -3,8 +3,8 @@ module PackageManagers
3
3
  # Package manager interface for systems that use pacman (i.e. arch) as
4
4
  # their package manager
5
5
  class PacmanManager < ShellScriptManager
6
- def initialize
7
- super(['pacman'], true,
6
+ def initialize(ws)
7
+ super(ws, true,
8
8
  "pacman -Sy --needed '%s'",
9
9
  "pacman -Sy --needed --noconfirm '%s'")
10
10
  end
@@ -4,18 +4,18 @@ module PackageManagers
4
4
  class PipManager < Manager
5
5
  attr_reader :installed_pips
6
6
 
7
- def self.initialize_environment(env = Autobuild.env, _manifest = nil, root_dir = Autoproj.root_dir)
8
- env.set 'PYTHONUSERBASE', pip_home(env, root_dir)
7
+ def initialize_environment
8
+ ws.env.set 'PYTHONUSERBASE', pip_home
9
9
  end
10
10
 
11
11
  # Return the directory where python packages are installed to.
12
12
  # The actual path is pip_home/lib/pythonx.y/site-packages.
13
- def self.pip_home(env = Autobuild.env, root_dir = Autoproj.root_dir)
14
- env['AUTOPROJ_PYTHONUSERBASE'] || File.join(root_dir,".pip")
13
+ def pip_home
14
+ ws.env['AUTOPROJ_PYTHONUSERBASE'] || File.join(ws.prefix_dir, "pip")
15
15
  end
16
16
 
17
- def initialize
18
- super(['pip'])
17
+ def initialize(ws)
18
+ super(ws)
19
19
  @installed_pips = Set.new
20
20
  end
21
21
 
@@ -52,7 +52,7 @@ def install(pips)
52
52
  end
53
53
 
54
54
  def pips_interaction(pips, cmdlines)
55
- if OSDependencies.force_osdeps
55
+ if OSPackageInstaller.force_osdeps
56
56
  return true
57
57
  elsif enabled?
58
58
  return true
@@ -69,7 +69,7 @@ def pips_interaction(pips, cmdlines)
69
69
 
70
70
  #{cmdlines.map { |c| c.join(" ") }.join("\n ")}
71
71
 
72
- Autoproj expects these Python packages to be installed in #{PipManager.pip_home} This can
72
+ Autoproj expects these Python packages to be installed in #{pip_home} This can
73
73
  be overridden by setting the AUTOPROJ_PYTHONUSERBASE environment variable manually
74
74
 
75
75
  EOMSG
@@ -3,8 +3,8 @@ module PackageManagers
3
3
  # Package manager interface for systems that use pkg (i.e. FreeBSD) as
4
4
  # their package manager
5
5
  class PkgManager < ShellScriptManager
6
- def initialize
7
- super(['pkg'], true,
6
+ def initialize(ws)
7
+ super(ws, true,
8
8
  "pkg install -y '%s'",
9
9
  "pkg install -y '%s'")
10
10
  end
@@ -3,8 +3,8 @@ module PackageManagers
3
3
  # Package manager interface for systems that use port (i.e. MacPorts/Darwin) as
4
4
  # their package manager
5
5
  class PortManager < ShellScriptManager
6
- def initialize
7
- super(['macports'], true,
6
+ def initialize(ws)
7
+ super(ws, true,
8
8
  "port install '%s'",
9
9
  "port install '%s'")
10
10
  end
@@ -93,8 +93,8 @@ def needs_root?; !!@needs_root end
93
93
  # itself, see {#auto_install_cmd}.
94
94
  # @param [Boolean] needs_root if the command lines should be started
95
95
  # as root or not. See {#needs_root?}
96
- def initialize(names, needs_locking, user_install_cmd, auto_install_cmd,needs_root=true)
97
- super(names)
96
+ def initialize(ws, needs_locking, user_install_cmd, auto_install_cmd,needs_root=true)
97
+ super(ws)
98
98
  @needs_locking, @user_install_cmd, @auto_install_cmd,@needs_root =
99
99
  needs_locking, user_install_cmd, auto_install_cmd, needs_root
100
100
  end
@@ -139,7 +139,7 @@ def generate_auto_os_script(os_packages, options = Hash.new)
139
139
  # @return [Boolean] true if the packages should be installed
140
140
  # automatically, false otherwise
141
141
  def osdeps_interaction(os_packages, shell_script)
142
- if OSDependencies.force_osdeps
142
+ if OSPackageInstaller.force_osdeps
143
143
  return true
144
144
  elsif enabled?
145
145
  return true
@@ -184,7 +184,7 @@ def osdeps_interaction(os_packages, shell_script)
184
184
  # packages. See the option in {#generate_auto_os_script}
185
185
  # @return [Boolean] true if packages got installed, false otherwise
186
186
  def install(packages, options = Hash.new)
187
- handled_os = OSDependencies.supported_operating_system?
187
+ handled_os = OSPackageResolver.supported_operating_system?
188
188
  if handled_os
189
189
  shell_script = generate_auto_os_script(packages, options)
190
190
  user_shell_script = generate_user_os_script(packages, options)
@@ -3,8 +3,8 @@ module PackageManagers
3
3
  # Dummy package manager used for unknown OSes. It simply displays a
4
4
  # message to the user when packages are needed
5
5
  class UnknownOSManager < Manager
6
- def initialize
7
- super(['unknown'])
6
+ def initialize(ws)
7
+ super(ws)
8
8
  @installed_osdeps = Set.new
9
9
  end
10
10
 
@@ -2,8 +2,8 @@ module Autoproj
2
2
  module PackageManagers
3
3
  # Package manager interface for systems that use yum
4
4
  class YumManager < ShellScriptManager
5
- def initialize
6
- super(['yum'], true,
5
+ def initialize(ws)
6
+ super(ws, true,
7
7
  "yum install '%s'",
8
8
  "yum install -y '%s'")
9
9
  end
@@ -2,8 +2,8 @@ module Autoproj
2
2
  module PackageManagers
3
3
  #Package manger for OpenSuse and Suse (untested)
4
4
  class ZypperManager < ShellScriptManager
5
- def initialize
6
- super(['zypper'], true,
5
+ def initialize(ws)
6
+ super(ws, true,
7
7
  "zypper install '%s'",
8
8
  "zypper -n install '%s'")
9
9
  end
@@ -36,13 +36,13 @@ def add_source_file(name)
36
36
  # local? returns true.
37
37
  attr_accessor :vcs
38
38
 
39
- # The set of OSDependencies object that represent the osdeps files
39
+ # The set of OSPackageResolver object that represent the osdeps files
40
40
  # available in this package set
41
41
  attr_reader :all_osdeps
42
42
 
43
- # The OSDependencies which is a merged version of all OSdeps in
43
+ # The OSPackageResolver which is a merged version of all OSdeps in
44
44
  # #all_osdeps
45
- attr_reader :osdeps
45
+ attr_reader :os_package_resolver
46
46
 
47
47
  # If this package set has been imported from another package set, this
48
48
  # is the other package set object
@@ -90,7 +90,7 @@ def default_packages
90
90
  def initialize(manifest, vcs)
91
91
  @manifest = manifest
92
92
  @vcs = vcs
93
- @osdeps = OSDependencies.new
93
+ @os_package_resolver = OSPackageResolver.new
94
94
  @all_osdeps = []
95
95
  @overrides = Array.new
96
96
 
@@ -104,15 +104,15 @@ def initialize(manifest, vcs)
104
104
 
105
105
  # Load a new osdeps file for this package set
106
106
  def load_osdeps(file)
107
- new_osdeps = OSDependencies.load(file)
108
- @all_osdeps << new_osdeps
109
- @osdeps.merge(@all_osdeps.last)
107
+ new_osdeps = OSPackageResolver.load(file)
108
+ all_osdeps << new_osdeps
109
+ os_package_resolver.merge(all_osdeps.last)
110
110
  new_osdeps
111
111
  end
112
112
 
113
113
  # Enumerate all osdeps package names from this package set
114
114
  def each_osdep(&block)
115
- @osdeps.definitions.each_key(&block)
115
+ os_package_resolver.definitions.each_key(&block)
116
116
  end
117
117
 
118
118
  # True if this source has already been checked out on the local autoproj
@@ -222,7 +222,7 @@ def repository_id
222
222
  end
223
223
 
224
224
  # Remote sources can be accessed through a hidden directory in
225
- # $AUTOPROJ_ROOT/.remotes, or through a symbolic link in
225
+ # {Workspace#remotes_dir}, or through a symbolic link in
226
226
  # autoproj/remotes/
227
227
  #
228
228
  # This returns the former. See #user_local_dir for the latter.
@@ -233,7 +233,7 @@ def raw_local_dir
233
233
  end
234
234
 
235
235
  # Remote sources can be accessed through a hidden directory in
236
- # $AUTOPROJ_ROOT/.remotes, or through a symbolic link in
236
+ # {Workspace#remotes_dir}, or through a symbolic link in
237
237
  # autoproj/remotes/
238
238
  #
239
239
  # This returns the latter. See #raw_local_dir for the former.