autoproj 2.0.0.rc37 → 2.0.0.rc38

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -2
  3. data/Rakefile +1 -1
  4. data/bin/autoproj_bootstrap +34 -2
  5. data/bin/autoproj_bootstrap.in +4 -2
  6. data/bin/autoproj_install +34 -2
  7. data/bin/autoproj_install.in +4 -2
  8. data/lib/autoproj.rb +9 -2
  9. data/lib/autoproj/autobuild.rb +13 -742
  10. data/lib/autoproj/autobuild_extensions/archive_importer.rb +44 -0
  11. data/lib/autoproj/autobuild_extensions/dsl.rb +439 -0
  12. data/lib/autoproj/autobuild_extensions/git.rb +116 -0
  13. data/lib/autoproj/autobuild_extensions/package.rb +159 -0
  14. data/lib/autoproj/autobuild_extensions/svn.rb +11 -0
  15. data/lib/autoproj/cli/base.rb +17 -18
  16. data/lib/autoproj/cli/clean.rb +1 -2
  17. data/lib/autoproj/cli/envsh.rb +1 -2
  18. data/lib/autoproj/cli/inspection_tool.rb +12 -21
  19. data/lib/autoproj/cli/locate.rb +130 -73
  20. data/lib/autoproj/cli/main.rb +31 -5
  21. data/lib/autoproj/cli/main_plugin.rb +79 -0
  22. data/lib/autoproj/cli/main_test.rb +19 -5
  23. data/lib/autoproj/cli/osdeps.rb +1 -2
  24. data/lib/autoproj/cli/patcher.rb +21 -0
  25. data/lib/autoproj/cli/query.rb +34 -41
  26. data/lib/autoproj/cli/show.rb +121 -52
  27. data/lib/autoproj/cli/status.rb +4 -5
  28. data/lib/autoproj/cli/tag.rb +1 -1
  29. data/lib/autoproj/cli/test.rb +7 -6
  30. data/lib/autoproj/cli/update.rb +8 -22
  31. data/lib/autoproj/cli/versions.rb +1 -2
  32. data/lib/autoproj/configuration.rb +1 -1
  33. data/lib/autoproj/environment.rb +2 -7
  34. data/lib/autoproj/exceptions.rb +10 -8
  35. data/lib/autoproj/find_workspace.rb +46 -12
  36. data/lib/autoproj/installation_manifest.rb +34 -25
  37. data/lib/autoproj/local_package_set.rb +86 -0
  38. data/lib/autoproj/manifest.rb +448 -503
  39. data/lib/autoproj/metapackage.rb +31 -5
  40. data/lib/autoproj/ops/configuration.rb +46 -45
  41. data/lib/autoproj/ops/import.rb +150 -60
  42. data/lib/autoproj/ops/install.rb +25 -1
  43. data/lib/autoproj/ops/loader.rb +4 -1
  44. data/lib/autoproj/ops/main_config_switcher.rb +4 -4
  45. data/lib/autoproj/ops/snapshot.rb +4 -3
  46. data/lib/autoproj/os_package_installer.rb +105 -46
  47. data/lib/autoproj/os_package_resolver.rb +63 -36
  48. data/lib/autoproj/package_definition.rb +1 -0
  49. data/lib/autoproj/package_managers/apt_dpkg_manager.rb +30 -27
  50. data/lib/autoproj/package_managers/bundler_manager.rb +64 -18
  51. data/lib/autoproj/package_managers/gem_manager.rb +4 -2
  52. data/lib/autoproj/package_managers/manager.rb +26 -7
  53. data/lib/autoproj/package_managers/shell_script_manager.rb +4 -4
  54. data/lib/autoproj/package_managers/zypper_manager.rb +1 -1
  55. data/lib/autoproj/package_manifest.rb +154 -137
  56. data/lib/autoproj/package_selection.rb +16 -2
  57. data/lib/autoproj/package_set.rb +352 -309
  58. data/lib/autoproj/query.rb +13 -1
  59. data/lib/autoproj/system.rb +2 -2
  60. data/lib/autoproj/test.rb +164 -11
  61. data/lib/autoproj/variable_expansion.rb +15 -42
  62. data/lib/autoproj/vcs_definition.rb +93 -76
  63. data/lib/autoproj/version.rb +1 -1
  64. data/lib/autoproj/workspace.rb +116 -80
  65. metadata +10 -2
@@ -31,8 +31,7 @@ def run(user_selection, options)
31
31
  initialize_and_load
32
32
  packages, *, config_selected =
33
33
  finalize_setup(user_selection,
34
- recursive: options[:deps],
35
- ignore_non_imported_packages: true)
34
+ recursive: options[:deps])
36
35
 
37
36
  ops = Ops::Snapshot.new(ws.manifest, ignore_errors: options[:keep_going])
38
37
 
@@ -115,7 +115,7 @@ def validated_values
115
115
  # should be converted to lowercase before it gets validated / saved.
116
116
  # @option options [Boolean] :uppercase (false) whether the user's input
117
117
  # should be converted to uppercase before it gets validated / saved.
118
- def declare(name, type, options, &validator)
118
+ def declare(name, type, **options, &validator)
119
119
  declared_options[name] = BuildOption.new(name, type, options, validator)
120
120
  end
121
121
 
@@ -8,18 +8,13 @@ class Environment < Autobuild::Environment
8
8
  attr_reader :root_dir
9
9
 
10
10
  def prepare(root_dir)
11
- super()
12
-
13
11
  @root_dir = root_dir
14
12
  set 'AUTOPROJ_CURRENT_ROOT', root_dir
13
+ super()
15
14
  end
16
15
 
17
16
  def filter_original_env(name, env)
18
- env.find_all { |p| !Workspace.in_autoproj_project?(p) }
19
- end
20
-
21
- def expand(value)
22
- Autoproj.expand_environment(value)
17
+ Autoproj.filter_out_paths_in_workspace(env)
23
18
  end
24
19
 
25
20
  def export_env_sh(subdir = nil, options = Hash.new)
@@ -12,6 +12,11 @@ class InternalError < RuntimeError; end
12
12
  class PackageNotFound < ConfigError
13
13
  end
14
14
 
15
+ class UnregisteredPackage < ArgumentError
16
+ end
17
+
18
+ class InvalidPackageManifest < RuntimeError; end
19
+
15
20
  class InputError < RuntimeError; end
16
21
 
17
22
  # Exception raised when a caller requires to use an excluded package
@@ -22,16 +27,13 @@ def initialize(name)
22
27
  end
23
28
  end
24
29
 
25
- # Exception raised when an unknown package is encountered
26
- class UnknownPackage < ConfigError
27
- attr_reader :name
28
- def initialize(name)
29
- @name = name
30
- end
31
- end
32
-
33
30
  class MissingOSDep < ConfigError; end
34
31
 
32
+ # Exception raised when finding unexpected objects in a YAML file
33
+ #
34
+ # E.g. having a hash instead of an array
35
+ class InvalidYAMLFormatting < ConfigError; end
36
+
35
37
  # Exception raised by
36
38
  # PackageSelection#filter_excluded_and_ignored_packages when a given
37
39
  # selection is completely excluded
@@ -21,19 +21,13 @@ def self.find_prefix_dir(base_dir = default_find_base_dir)
21
21
  find_v2_prefix_dir(base_dir)
22
22
  end
23
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
24
+ # Find a workspace configuration file in the parent tree of a given
28
25
  # directory
29
26
  #
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)
27
+ # @param [String] base_dir the directory to start from
28
+ # @return [(String,Hash),nil] the root path, and the configuration, or nil
29
+ # if base_dir is not part of a workspace
30
+ def self.find_v2_workspace_config(base_dir)
37
31
  path = Pathname.new(base_dir).expand_path
38
32
  while !path.root?
39
33
  if (path + ".autoproj" + "config.yml").exist?
@@ -47,8 +41,24 @@ def self.find_v2_root_dir(base_dir, config_field_name)
47
41
  end
48
42
 
49
43
  config_path = path + ".autoproj" + "config.yml"
44
+ return path.to_s, (YAML.load(config_path.read) || Hash.new)
45
+ end
50
46
 
51
- config = YAML.load(config_path.read) || Hash.new
47
+ # @private
48
+ #
49
+ # Finds an autoproj "root directory" that contains a given directory. It
50
+ # can either be the root of a workspace or the root of an install
51
+ # directory
52
+ #
53
+ # @param [String] base_dir the start of the search
54
+ # @param [String] config_field_name the name of a field in the root's
55
+ # configuration file, that should be returned instead of the root
56
+ # itself
57
+ # @return [String,nil] the root of the workspace directory, or nil if
58
+ # there's none
59
+ def self.find_v2_root_dir(base_dir, config_field_name)
60
+ path, config = find_v2_workspace_config(base_dir)
61
+ return if !path
52
62
  result = config[config_field_name] || path.to_s
53
63
  result = File.expand_path(result, path.to_s)
54
64
  if result == path.to_s
@@ -62,6 +72,30 @@ def self.find_v2_root_dir(base_dir, config_field_name)
62
72
  resolved
63
73
  end
64
74
 
75
+ # Filters in the given list of paths the paths that are within a workspace
76
+ def self.filter_out_paths_in_workspace(paths)
77
+ known_valid_dirs = Set.new
78
+ known_workspace_dirs = Array.new
79
+ paths.find_all do |p|
80
+ if !File.directory?(p)
81
+ true
82
+ elsif known_workspace_dirs.any? { |ws_root| "#{p}/".start_with?(ws_root) }
83
+ false
84
+ else
85
+ ws_path, ws_config = find_v2_workspace_config(p)
86
+ if ws_path
87
+ known_workspace_dirs << "#{ws_path}/"
88
+ if ws_dir = ws_config['workspace']
89
+ known_workspace_dirs << "#{ws_dir}/"
90
+ end
91
+ false
92
+ else
93
+ true
94
+ end
95
+ end
96
+ end
97
+ end
98
+
65
99
  # {#find_workspace_dir} for v2 workspaces
66
100
  def self.find_v2_workspace_dir(base_dir = default_find_base_dir)
67
101
  find_v2_root_dir(base_dir, 'workspace')
@@ -2,31 +2,35 @@ module Autoproj
2
2
  # Manifest of installed packages imported from another autoproj installation
3
3
  class InstallationManifest
4
4
  Package = Struct.new :name, :srcdir, :prefix, :builddir, :dependencies
5
+ PackageSet = Struct.new :name, :raw_local_dir, :user_local_dir
5
6
 
6
7
  attr_reader :path
7
8
  attr_reader :packages
9
+ attr_reader :package_sets
8
10
  def initialize(path)
9
11
  @path = path
10
12
  @packages = Hash.new
13
+ @package_sets = Hash.new
11
14
  end
12
15
 
13
16
  def exist?
14
17
  File.exist?(path)
15
18
  end
16
19
 
17
- # Returns information about a given package
18
- #
19
- # @return [Package]
20
- def [](name)
21
- packages[name]
20
+ def add_package(pkg)
21
+ packages[pkg.name] = pkg
22
22
  end
23
23
 
24
- def []=(name, pkg)
25
- packages[name] = pkg
24
+ def add_package_set(pkg_set)
25
+ package_sets[pkg_set.name] = pkg_set
26
26
  end
27
27
 
28
- def delete_if
29
- packages.delete_if { |_, pkg| yield(pkg) }
28
+ def each_package_set(&block)
29
+ package_sets.each_value(&block)
30
+ end
31
+
32
+ def each_package(&block)
33
+ packages.each_value(&block)
30
34
  end
31
35
 
32
36
  def load
@@ -41,10 +45,16 @@ def load
41
45
  save(path)
42
46
  else
43
47
  raw.each do |entry|
44
- pkg = Package.new(
45
- entry['name'], entry['srcdir'], entry['prefix'],
46
- entry['builddir'], entry['dependencies'])
47
- packages[pkg.name] = pkg
48
+ if entry['package_set']
49
+ pkg_set = PackageSet.new(
50
+ entry['package_set'], entry['raw_local_dir'], entry['user_local_dir'])
51
+ package_sets[pkg_set.name] = pkg_set
52
+ else
53
+ pkg = Package.new(
54
+ entry['name'], entry['srcdir'], entry['prefix'],
55
+ entry['builddir'], entry['dependencies'])
56
+ packages[pkg.name] = pkg
57
+ end
48
58
  end
49
59
  end
50
60
  end
@@ -52,35 +62,34 @@ def load
52
62
  # Save the installation manifest
53
63
  def save(path = self.path)
54
64
  File.open(path, 'w') do |io|
55
- marshalled_packages = packages.values.map do |v|
65
+ marshalled_package_sets = each_package_set.map do |v|
66
+ Hash['package_set' => v.name,
67
+ 'raw_local_dir' => v.raw_local_dir,
68
+ 'user_local_dir' => v.user_local_dir]
69
+ end
70
+ marshalled_packages = each_package.map do |v|
71
+ v = v.autobuild
56
72
  Hash['name' => v.name,
57
73
  'srcdir' => v.srcdir,
58
74
  'builddir' => (v.builddir if v.respond_to?(:builddir)),
59
75
  'prefix' => v.prefix,
60
76
  'dependencies' => v.dependencies]
61
77
  end
62
- YAML.dump(marshalled_packages, io)
78
+ YAML.dump(marshalled_package_sets + marshalled_packages, io)
63
79
  end
64
80
  end
65
81
 
66
- # Enumerate the packages from this manifest
67
- #
68
- # @yieldparam [Package]
69
- def each(&block)
70
- packages.each_value(&block)
71
- end
72
-
73
82
  # Returns the default Autoproj installation manifest path for a given
74
83
  # autoproj workspace root
75
84
  #
76
85
  # @param [String] root_dir
77
86
  # @return [String]
78
- def self.path_for_root(root_dir)
87
+ def self.path_for_workspace_root(root_dir)
79
88
  File.join(root_dir, '.autoproj', 'installation-manifest')
80
89
  end
81
90
 
82
- def self.from_root(root_dir)
83
- path = path_for_root(root_dir)
91
+ def self.from_workspace_root(root_dir)
92
+ path = path_for_workspace_root(root_dir)
84
93
  manifest = InstallationManifest.new(path)
85
94
  if !manifest.exist?
86
95
  raise ConfigError.new, "no #{path} file found. You should probably rerun autoproj envsh in that folder first"
@@ -0,0 +1,86 @@
1
+ module Autoproj
2
+ # Specialization of the PackageSet class to handle the "master" package set
3
+ # in autoproj/
4
+ class LocalPackageSet < PackageSet
5
+ def initialize(ws, local_dir: ws.config_dir)
6
+ super(ws, VCSDefinition.none, name: 'main configuration', raw_local_dir: local_dir)
7
+ @local_dir = local_dir
8
+ end
9
+
10
+ def vcs
11
+ ws.manifest.vcs
12
+ end
13
+
14
+ def main?
15
+ true
16
+ end
17
+
18
+ def local?
19
+ true
20
+ end
21
+
22
+ def local_dir
23
+ raw_local_dir
24
+ end
25
+
26
+ def manifest_path
27
+ manifest.file
28
+ end
29
+
30
+ def overrides_file_path
31
+ if d = local_dir
32
+ File.join(d, "overrides.yml")
33
+ end
34
+ end
35
+
36
+ def source_file
37
+ overrides_file_path
38
+ end
39
+
40
+ # Reimplemented from {PackageSet#load_description_file} to remove the
41
+ # name validation
42
+ def load_description_file
43
+ source_definition = raw_description_file
44
+ parse_source_definition(source_definition)
45
+ end
46
+
47
+ # Load the files in overrides.d in addition to the overrides: field in
48
+ # the yaml file
49
+ def load_overrides(source_definition)
50
+ files = Dir.glob(File.join( ws.overrides_dir, "*.yml" ) ).sort
51
+ overrides = files.map do |file|
52
+ source_data = Autoproj.in_file(file, Autoproj::YAML_LOAD_ERROR) do
53
+ YAML.load(File.read(file)) || Array.new
54
+ end
55
+ source_data =
56
+ if source_data.respond_to?(:to_ary)
57
+ source_data
58
+ else source_data['overrides'] || Hash.new
59
+ end
60
+ [file, source_data]
61
+ end
62
+ overrides + super
63
+ end
64
+
65
+ def raw_description_file
66
+ description = Hash[
67
+ 'imports' => Array.new,
68
+ 'version_control' => Array.new,
69
+ 'overrides' => Array.new]
70
+ if File.file?(overrides_file_path)
71
+ overrides_data = Autoproj.in_file(overrides_file_path, Autoproj::YAML_LOAD_ERROR) do
72
+ YAML.load(File.read(overrides_file_path)) || Hash.new
73
+ end
74
+ description = description.merge(overrides_data)
75
+ end
76
+
77
+ manifest_data = Autoproj.in_file(manifest_path, Autoproj::YAML_LOAD_ERROR) do
78
+ YAML.load(File.read(manifest_path)) || Hash.new
79
+ end
80
+ description['imports'] = description['imports'].
81
+ concat(manifest_data['package_sets'] || Array.new)
82
+ description['name'] = name
83
+ description
84
+ end
85
+ end
86
+ end
@@ -2,7 +2,6 @@
2
2
  require 'csv'
3
3
  require 'utilrb/kernel/options'
4
4
  require 'set'
5
- require 'rexml/document'
6
5
 
7
6
  require 'win32/dir' if RbConfig::CONFIG["host_os"] =~%r!(msdos|mswin|djgpp|mingw|[Ww]indows)!
8
7
 
@@ -26,6 +25,29 @@ def self.load(file)
26
25
  manifest
27
26
  end
28
27
 
28
+ # A normalized version of the layout as represented in the manifest file
29
+ #
30
+ # It is a mapping from a selection name into the layout level it is
31
+ # defined in. For instance:
32
+ #
33
+ # layout:
34
+ # - subdir:
35
+ # - pkg/in/subdir
36
+ # - pkg/in/root
37
+ #
38
+ # Would be normalized as
39
+ #
40
+ # 'pkg/in/subdir' => '/subdir/',
41
+ # 'pkg/in/root' => '/'
42
+ #
43
+ # Note that these are only strings. There is no normalization against
44
+ # package names or metapackages.
45
+ #
46
+ # This is computed by {#compute_normalized_layout}
47
+ #
48
+ # @return [Hash]
49
+ attr_reader :normalized_layout
50
+
29
51
  # Load the manifest data contained in +file+
30
52
  def load(file)
31
53
  if !File.exist?(file)
@@ -37,21 +59,76 @@ def load(file)
37
59
  end
38
60
 
39
61
  @file = file
62
+ initialize_from_hash(data)
63
+ end
64
+
65
+ # @api private
66
+ #
67
+ # Initialize the manifest from a hash, as loaded from a manifest file
68
+ def initialize_from_hash(data)
40
69
  @data = data
41
70
  @ignored_packages |= (data['ignored_packages'] || Set.new).to_set
71
+ @ignored_packages |= (data['ignore_packages'] || Set.new).to_set
72
+ invalidate_ignored_package_names
42
73
  @manifest_exclusions |= (data['exclude_packages'] || Set.new).to_set
43
74
 
44
- @normalized_layout = Hash.new
75
+ normalized_layout = Hash.new
45
76
  compute_normalized_layout(
46
77
  normalized_layout,
47
78
  '/',
48
79
  data['layout'] || Hash.new)
80
+ @normalized_layout = normalized_layout
81
+ @has_layout = !!data['layout']
49
82
 
50
83
  if data['constants']
51
84
  @constant_definitions = Autoproj.resolve_constant_definitions(data['constants'])
52
85
  end
53
86
  end
54
87
 
88
+ # Make an empty layout
89
+ #
90
+ # Unless the default layout (that you can get with {#remove_layout}), this
91
+ # means that no package is selected by default
92
+ def clear_layout
93
+ @has_layout = true
94
+ normalized_layout.clear
95
+ end
96
+
97
+ # Remove the layout
98
+ #
99
+ # Unlike {#clear_layout}, this means that all defined source packages
100
+ # will be selected by default
101
+ def remove_layout
102
+ @has_layout = false
103
+ normalized_layout.clear
104
+ end
105
+
106
+ # Add a package into the layout
107
+ def add_package_to_layout(package)
108
+ package_name = validate_package_name_argument(package)
109
+ @has_layout = true
110
+ normalized_layout[package_name] = '/'
111
+ end
112
+
113
+ # Add a package into the layout
114
+ def add_package_set_to_layout(package_set)
115
+ validate_package_set_in_self(package_set)
116
+ add_metapackage_to_layout(package_set.metapackage)
117
+ end
118
+
119
+ # Add a metapackage into the layout
120
+ def add_metapackage_to_layout(metapackage)
121
+ validate_metapackage_in_self(metapackage)
122
+ @has_layout = true
123
+ normalized_layout[metapackage.name] = '/'
124
+ end
125
+
126
+ # Add a constant definition, used when resolving $CONSTANT in loaded
127
+ # files
128
+ def add_constant_definition(key, value)
129
+ constant_definitions[key] = value
130
+ end
131
+
55
132
  # The manifest data as a Hash
56
133
  attr_reader :data
57
134
 
@@ -59,9 +136,6 @@ def load(file)
59
136
  # [Autobuild::Package, package_set, file] tuple
60
137
  attr_reader :packages
61
138
 
62
- # A mapping from package names into PackageManifest objects
63
- attr_reader :package_manifests
64
-
65
139
  # The path to the manifest file that has been loaded
66
140
  attr_reader :file
67
141
 
@@ -80,15 +154,6 @@ def accept_unavailable_osdeps?; !!@accept_unavailable_osdeps end
80
154
  # Sets {#accept_unavailable_osdeps?}
81
155
  def accept_unavailable_osdeps=(flag); @accept_unavailable_osdeps = flag end
82
156
 
83
- # True if osdeps should be handled in update and build, or left to the
84
- # osdeps command
85
- def auto_osdeps?
86
- if data.has_key?('auto_osdeps')
87
- !!data['auto_osdeps']
88
- else true
89
- end
90
- end
91
-
92
157
  attr_reader :constant_definitions
93
158
 
94
159
  attr_reader :metapackages
@@ -101,17 +166,24 @@ def auto_osdeps?
101
166
  # The definition of all OS packages available on this installation
102
167
  attr_reader :os_package_resolver
103
168
 
104
- def initialize
169
+ # Whether there is a layout specified
170
+ #
171
+ # This is used to disambiguate between an empty layout (which would
172
+ # build nothing) and no layout at all
173
+ attr_predicate :has_layout?
174
+
175
+ def initialize(ws, os_package_resolver: OSPackageResolver.new)
176
+ @ws = ws
105
177
  @file = nil
106
178
  @data = Hash.new
179
+ @has_layout = false
180
+ @normalized_layout = Hash.new
107
181
  @packages = Hash.new
108
- @package_manifests = Hash.new
109
182
  @package_sets = []
110
- @os_package_resolver = OSPackageResolver.new
183
+ @os_package_resolver = os_package_resolver
111
184
 
112
185
  @automatic_exclusions = Hash.new
113
186
  @constants_definitions = Hash.new
114
- @disabled_imports = Set.new
115
187
  @moved_packages = Hash.new
116
188
  @osdeps_overrides = Hash.new
117
189
  @metapackages = Hash.new
@@ -122,75 +194,172 @@ def initialize
122
194
  @accept_unavailable_osdeps = false
123
195
 
124
196
  @constant_definitions = Hash.new
125
- @package_sets << LocalPackageSet.new(self)
197
+ @package_sets << LocalPackageSet.new(ws)
126
198
  end
127
199
 
200
+ # @api private
201
+ #
202
+ # Validate that the given metapackage object is defined in self
203
+ def validate_metapackage_in_self(metapackage)
204
+ if find_metapackage(metapackage.name) != metapackage
205
+ raise UnregisteredPackage, "#{metapackage.name} is not registered on #{self}"
206
+ end
207
+ end
208
+
209
+ # @api private
210
+ #
211
+ # Validate that the given package object is defined in self
212
+ def validate_package_in_self(package)
213
+ if !package.respond_to?(:autobuild)
214
+ raise ArgumentError, "expected a PackageDefinition object but got an Autobuild package"
215
+ elsif find_package_definition(package.name) != package
216
+ raise UnregisteredPackage, "#{package.name} is not registered on #{self}"
217
+ end
218
+ end
219
+
220
+ # @api private
221
+ #
222
+ # Massage an argument that should be interpreted as a package name
223
+ def validate_package_name_argument(package, require_existing: true)
224
+ if package.respond_to?(:name)
225
+ validate_package_in_self(package)
226
+ package.name
227
+ else
228
+ package = package.to_str
229
+ if require_existing && !has_package?(package)
230
+ raise PackageNotFound, "no package named #{package} in #{self}"
231
+ end
232
+ package
233
+ end
234
+ end
235
+
236
+ # @api private
237
+ #
238
+ # Validate that the given package object is defined in self
239
+ def validate_package_set_in_self(package_set)
240
+ if find_package_set(package.name) != package_set
241
+ raise UnregisteredPackageSet, "#{package_set.name} is not registered on #{self}"
242
+ end
243
+ end
128
244
 
129
245
  # Call this method to ignore a specific package. It must not be used in
130
246
  # init.rb, as the manifest is not yet loaded then
131
- def ignore_package(package_name)
132
- @ignored_packages << package_name.to_str
247
+ def ignore_package(package)
248
+ invalidate_ignored_package_names
249
+ @ignored_packages << validate_package_name_argument(package, require_existing: false)
133
250
  end
134
251
 
135
252
  # True if the given package should not be built, with the packages that
136
253
  # depend on him have this dependency met.
137
254
  #
138
255
  # This is useful if the packages are already installed on this system.
139
- def ignored?(package_name)
140
- ignored_packages.any? do |l|
141
- if package_name == l
142
- true
143
- elsif (pkg_set = metapackages[l]) && pkg_set.include?(package_name)
144
- true
145
- else
146
- false
147
- end
256
+ def ignored?(package)
257
+ package_name = validate_package_name_argument(package)
258
+ cache_ignored_package_names.include?(package_name)
259
+ end
260
+
261
+ # @api private
262
+ #
263
+ # The list of packages that are ignored
264
+ #
265
+ # Do not use directly, use {#ignored?} instead
266
+ def cache_ignored_package_names
267
+ if @ignored_package_names
268
+ return @ignored_package_names
148
269
  end
270
+
271
+ @ignored_package_names = each_package_definition.find_all do |pkg|
272
+ ignored_packages.any? do |l|
273
+ (pkg.name == l) ||
274
+ ((pkg_set = metapackages[l]) && pkg_set.include?(pkg))
275
+ end
276
+ end.map(&:name).to_set
277
+ end
278
+
279
+ # @api private
280
+ #
281
+ # Invalidate the cache computed by {#cache_ignored_package_names}
282
+ def invalidate_ignored_package_names
283
+ @ignored_package_names = nil
149
284
  end
150
285
 
151
286
  # Enumerates the package names of all ignored packages
152
- def each_excluded_package
153
- each_autobuild_package do |pkg|
154
- yield(pkg) if excluded?(pkg.name)
287
+ #
288
+ # @yieldparam [Autobuild::Package]
289
+ def each_ignored_package
290
+ return enum_for(__method__) if !block_given?
291
+ cache_ignored_package_names.each do |pkg_name|
292
+ yield(find_autobuild_package(pkg_name))
293
+ end
294
+ end
295
+
296
+ # Removes all registered ignored packages
297
+ def clear_ignored
298
+ invalidate_ignored_package_names
299
+ ignored_packages.clear
300
+ end
301
+
302
+ # True if the given package should not be built and its dependencies
303
+ # should be considered as met.
304
+ #
305
+ # This is useful to avoid building packages that are of no use for the
306
+ # user.
307
+ def excluded?(package_name)
308
+ package_name = validate_package_name_argument(package_name)
309
+
310
+ if excluded_in_manifest?(package_name)
311
+ true
312
+ elsif automatic_exclusions.any? { |pkg_name, | pkg_name == package_name }
313
+ true
314
+ else
315
+ false
155
316
  end
156
317
  end
157
318
 
158
319
  # Enumerates the package names of all ignored packages
159
- def each_ignored_package
320
+ def each_excluded_package
321
+ return enum_for(__method__) if !block_given?
160
322
  each_autobuild_package do |pkg|
161
- yield(pkg) if ignored?(pkg.name)
323
+ yield(pkg) if excluded?(pkg.name)
162
324
  end
163
325
  end
164
326
 
165
327
  # Removes all registered exclusions
166
328
  def clear_exclusions
167
329
  automatic_exclusions.clear
168
- data['exclude_packages'].clear
169
- end
170
-
171
- # Removes all registered ignored packages
172
- def clear_ignored
173
- ignored_packages.clear
330
+ manifest_exclusions.clear
174
331
  end
175
332
 
176
333
  # The set of package names that are listed in the excluded_packages
177
334
  # section of the manifest
178
- def manifest_exclusions
179
- @manifest_exclusions
180
- end
335
+ attr_reader :manifest_exclusions
181
336
 
182
- # A package_name => reason map of the exclusions added with #add_exclusion.
337
+ # A package_name => reason map of the exclusions added with {#exclude_package}
183
338
  # Exclusions listed in the manifest file are returned by #manifest_exclusions
184
339
  attr_reader :automatic_exclusions
185
340
 
341
+ # @deprecated use {#exclude_package} instead
342
+ def add_exclusion(package_name, reason)
343
+ Autoproj.warn_deprecated __method__, "use #exclude_package instead"
344
+ exclude_package(package_name, reason)
345
+ end
346
+
186
347
  # Exclude +package_name+ from the build. +reason+ is a string describing
187
348
  # why the package is to be excluded.
188
- def add_exclusion(package_name, reason)
189
- automatic_exclusions[package_name] = reason
349
+ def exclude_package(package_name, reason)
350
+ package = validate_package_name_argument(package_name, require_existing: false)
351
+ if meta = find_metapackage(package)
352
+ meta.each_package do |pkg|
353
+ automatic_exclusions[pkg.name] = "#{meta.name} is an excluded metapackage, and it includes #{pkg.name}: #{reason}"
354
+ end
355
+ else
356
+ automatic_exclusions[package] = reason
357
+ end
190
358
  end
191
359
 
192
360
  # Tests whether the given package is excluded in the manifest
193
361
  def excluded_in_manifest?(package_name)
362
+ package_name = validate_package_name_argument(package_name)
194
363
  manifest_exclusions.any? do |matcher|
195
364
  if (pkg_set = metapackages[matcher]) && pkg_set.include?(package_name)
196
365
  true
@@ -207,11 +376,15 @@ def excluded_in_manifest?(package_name)
207
376
  # exclude_packages section of the manifest, or because they are
208
377
  # disabled on this particular operating system.
209
378
  def exclusion_reason(package_name)
210
- if excluded_in_manifest?(package_name)
211
- "#{package_name} is listed in the exclude_packages section of the manifest"
212
- else
213
- automatic_exclusions[package_name]
379
+ package_name = validate_package_name_argument(package_name)
380
+ manifest_exclusions.any? do |matcher|
381
+ if (pkg_set = metapackages[matcher]) && pkg_set.include?(package_name)
382
+ return "#{pkg_set.name} is a metapackage listed in the exclude_packages section of the manifest, and it includes #{package_name}"
383
+ elsif Regexp.new(matcher) === package_name
384
+ return "#{package_name} is listed in the exclude_packages section of the manifest"
385
+ end
214
386
  end
387
+ automatic_exclusions[package_name]
215
388
  end
216
389
 
217
390
  # Returns true if the given package name has been explicitely added to
@@ -224,65 +397,9 @@ def explicitely_selected_in_layout?(package_name)
224
397
  normalized_layout.has_key?(package_name)
225
398
  end
226
399
 
227
- # True if the given package should not be built and its dependencies
228
- # should be considered as met.
229
- #
230
- # This is useful to avoid building packages that are of no use for the
231
- # user.
232
- def excluded?(package_name)
233
- package_name = package_name.to_str
234
-
235
- if excluded_in_manifest?(package_name)
236
- true
237
- elsif automatic_exclusions.any? { |pkg_name, | pkg_name == package_name }
238
- true
239
- else
240
- false
241
- end
242
- end
243
-
244
- # Lists the autobuild files that are in the package sets we know of
245
- def each_autobuild_file(source_name = nil, &block)
246
- if !block_given?
247
- return enum_for(__method__, source_name)
248
- end
249
-
250
- # This looks very inefficient, but it is because source names are
251
- # contained in source.yml and we must therefore load that file to
252
- # check the package set name ...
253
- #
254
- # And honestly I don't think someone will have 20 000 package sets
255
- done_something = false
256
- each_package_set do |source|
257
- next if source_name && source.name != source_name
258
- done_something = true
259
-
260
- Dir.glob(File.join(source.local_dir, "*.autobuild")).sort.each do |file|
261
- yield(source, file)
262
- end
263
- end
264
-
265
- if source_name && !done_something
266
- raise ConfigError.new(file), "in #{file}: package set '#{source_name}' does not exist"
267
- end
268
- end
269
-
270
- # Yields each osdeps definition files that are present in our sources
271
- def each_osdeps_file
272
- if !block_given?
273
- return enum_for(__method__)
274
- end
275
-
276
- each_package_set do |source|
277
- Dir.glob(File.join(source.local_dir, "*.osdeps")).each do |file|
278
- yield(source, file)
279
- end
280
- end
281
- end
282
-
283
400
  # True if some of the sources are remote sources
284
- def has_remote_sources?
285
- each_remote_source(false).any? { true }
401
+ def has_remote_package_sets?
402
+ each_remote_package_set.any? { true }
286
403
  end
287
404
 
288
405
  # Like #each_package_set, but filters out local package sets
@@ -296,25 +413,6 @@ def each_remote_package_set
296
413
  end
297
414
  end
298
415
 
299
- # Enumerates the version control information for all the package sets
300
- # listed directly in the manifest file
301
- #
302
- # @yieldparam [VCSDefinition] vcs the package set VCS object
303
- # @yieldparam [Hash] options additional import options
304
- # @options options [Boolean] :auto_update (true) if true, the set of
305
- # package sets declared as imports in package set's source.yml file
306
- # will be auto-imported by autoproj, otherwise they won't
307
- # @return [nil]
308
- def each_raw_explicit_package_set
309
- return enum_for(__method__) if !block_given?
310
- (data['package_sets'] || []).map do |spec|
311
- Autoproj.in_file(self.file) do
312
- yield(*PackageSet.resolve_definition(self, spec))
313
- end
314
- end
315
- nil
316
- end
317
-
318
416
  # Lists all package sets defined in this manifest, including the package
319
417
  # sets that are auto-imported
320
418
  #
@@ -326,20 +424,16 @@ def each_package_set(&block)
326
424
  @package_sets.each(&block)
327
425
  end
328
426
 
329
- # Load the package set information
330
- def load_and_update_package_sets
331
- Autoproj.warn_deprecated __method__,
332
- "use Ops::Configuration instead"
333
- Ops::Configuration.new(Autoproj.workspace).load_and_update_package_sets
334
- end
335
-
336
- # Returns a package set that is used by autoproj for its own purposes
337
- def local_package_set
338
- each_package_set.find { |s| s.kind_of?(LocalPackageSet) }
427
+ # Reset the list of package sets
428
+ def reset_package_sets
429
+ @package_sets.clear
339
430
  end
340
431
 
341
432
  # Registers a new package set
433
+ #
434
+ # @param [PackageSet] pkg_set the package set object
342
435
  def register_package_set(pkg_set)
436
+ invalidate_ignored_package_names
343
437
  metapackage(pkg_set.name)
344
438
  metapackage("#{pkg_set.name}.all")
345
439
  @package_sets << pkg_set
@@ -353,6 +447,7 @@ def register_package_set(pkg_set)
353
447
  # @param [String] file the file in which the package is defined
354
448
  # @return [PackageDefinition]
355
449
  def register_package(package, block = nil, package_set = main_package_set, file = nil)
450
+ invalidate_ignored_package_names
356
451
  pkg = PackageDefinition.new(package, package_set, file)
357
452
  if block
358
453
  pkg.add_setup_block(block)
@@ -363,52 +458,43 @@ def register_package(package, block = nil, package_set = main_package_set, file
363
458
  pkg
364
459
  end
365
460
 
366
- # Returns the package set that defines a package
461
+ # The autoproj description of a package by its name
367
462
  #
368
- # @param [String] package_name the package name
369
- # @return [PackageSet] the package set
370
- # @raise ArgumentError if package_name is not the name of a known
371
- # package
372
- def definition_package_set(package_name)
373
- if pkg_def = @packages[package_name]
374
- pkg_def.package_set
375
- else raise ArgumentError, "no package called #{package_name}"
376
- end
463
+ # @param [String,#name] name the package name
464
+ # @return [PackageDefinition,nil]
465
+ def find_package_definition(name)
466
+ packages[validate_package_name_argument(name, require_existing: false)]
377
467
  end
378
468
 
379
- # @deprecated use {definition_package_set} instead
380
- def definition_source(package_name)
381
- definition_package_set(package_name)
469
+ # @deprecated use {#find_package_definition}(pkg_name).package_set
470
+ # instead
471
+ def definition_source(name)
472
+ Autoproj.warn_deprecated __method__, "use #package_definition_by_name(name).package_set instead"
473
+ package_definition_by_name(name).package_set
382
474
  end
383
475
 
384
- # Returns the full path to the file that defines a package
385
- #
386
- # @param [String] package_name the package name
387
- # @return [String] the package set
388
- # @raise ArgumentError if package_name is not the name of a known
389
- # package
390
- def definition_file(package_name)
391
- if pkg_def = @packages[package_name]
392
- pkg_def.file
393
- else raise ArgumentError, "no package called #{package_name}"
394
- end
476
+ # @deprecated use {#find_package_definition}(pkg_name).package_set
477
+ # instead
478
+ def definition_package_set(name)
479
+ Autoproj.warn_deprecated __method__, "use #package_definition_by_name(name).package_set instead"
480
+ package_definition_by_name(name).package_set
395
481
  end
396
482
 
397
483
  # @deprecated use {#find_package_definition} instead
398
- def find_package(name)
484
+ def package(name)
485
+ Autoproj.warn_deprecated "Manifest#package is deprecated, use #package_definition_by_name instead"
399
486
  find_package_definition(name)
400
487
  end
401
488
 
402
- # The autoproj description of a package by its name
489
+ # Resolve a package definition by name
403
490
  #
404
- # @param [String,#name] name the package name
405
- # @return [PackageDefinition,nil]
406
- def find_package_definition(name)
407
- if name.respond_to?(:name)
408
- name = name.name
491
+ # Unlike {#find_package_definition}, raise if the package does not exist
492
+ def package_definition_by_name(name)
493
+ if pkg = find_package_definition(name)
494
+ pkg
495
+ else
496
+ raise ArgumentError, "no package defined named '#{name}'"
409
497
  end
410
-
411
- packages[name.to_str]
412
498
  end
413
499
 
414
500
  # The autobuild description of a package by its name
@@ -416,23 +502,11 @@ def find_package_definition(name)
416
502
  # @param [String,#name] name the package name
417
503
  # @return [Autobuild::Package,nil]
418
504
  def find_autobuild_package(name)
419
- if pkg = find_package(name)
505
+ if pkg = find_package_definition(name)
420
506
  pkg.autobuild
421
507
  end
422
508
  end
423
509
 
424
- # @deprecated use {#find_package} instead
425
- def package(name)
426
- find_package(name)
427
- end
428
-
429
- # @deprecated use {each_autobuild_package} instead
430
- def each_package(&block)
431
- Autoproj.warn "Manifest#each_package is deprecated, use each_autobuild_package instead"
432
- Autoproj.warn " " + caller.join("\n ")
433
- each_autobuild_package(&block)
434
- end
435
-
436
510
  # Lists all defined packages
437
511
  #
438
512
  # @yieldparam [PackageDefinition] pkg
@@ -449,58 +523,41 @@ def each_autobuild_package
449
523
  each_package_definition { |pkg| yield(pkg.autobuild) }
450
524
  end
451
525
 
452
- # @deprecated use Ops::Tools.create_autobuild_package or include
453
- # Ops::Tools into your class to get it as instance method
454
- def self.create_autobuild_package(vcs, text_name, into)
455
- Autoproj.warn_deprecated __method__, "use Ops::Tools.create_autobuild_package instead"
456
- Ops::Tools.create_autobuild_package(vcs, text_name, into)
457
- end
458
-
459
- # @deprecated use Ops::Configuration#update_main_configuration
460
- def update_yourself(only_local = false)
461
- Autoproj.warn_deprecated __method__, "use Ops::Configuration instead"
462
- Ops::Configuration.new(Autoproj.workspace).update_main_configuration(only_local)
463
- end
464
-
465
- # @deprecated use Ops::Configuration.update_remote_package_set
466
- def update_remote_set(vcs, only_local = false)
467
- Autoproj.warn_deprecated __method__, "use Ops::Configuration instead"
468
- Ops::Configuration.update_remote_package_set(vcs, only_local)
469
- end
470
-
471
- # Compute the VCS definition for a given package
526
+ # @overload importer_definition_for(package, mainline: nil, require_existing: true, package_set: nil)
472
527
  #
473
- # @param [String] package_name the name of the package to be resolved
474
- # @param [PackageSet,nil] package_source the package set that defines the
475
- # given package, defaults to the package's definition source (as
476
- # returned by {definition_package_set}) if not given
528
+ # @param [PackageDefinition] package the name of the package to be resolved
529
+ # @param [PackageSet,nil] mainline the reference package set for which
530
+ # we want to compute the importer definition. Pass package.package_set
531
+ # if you want to avoid applying any override
477
532
  # @return [VCSDefinition] the VCS definition object
478
- def importer_definition_for(package_name, package_set = definition_package_set(package_name),
479
- options = Hash.new)
480
- options = validate_options options, mainline: nil
481
- mainline =
482
- if options[:mainline] == true
483
- package_set
484
- else
485
- options[:mainline]
486
- end
533
+ def importer_definition_for(package, _package_set = nil, mainline: nil, require_existing: true, package_set: nil)
534
+ if _package_set
535
+ Autoproj.warn_deprecated "calling #importer_definition_for with the package set as second argument is deprecated, use the package_set: keyword argument instead"
536
+ require_existing = false
537
+ end
538
+ package_name = validate_package_name_argument(package, require_existing: require_existing)
539
+ package_set = _package_set || package_set || package.package_set
540
+ mainline = if mainline == true
541
+ package_set
542
+ else mainline
543
+ end
487
544
 
488
- vcs = package_set.importer_definition_for(package_name)
489
- return if !vcs
545
+ # package_name is already validated, do not re-validate
546
+ vcs = package_set.importer_definition_for(package_name, require_existing: false)
490
547
 
491
- # Get the sets that come *after* the one that defines the package to
492
- # apply the overrides
493
548
  package_sets = each_package_set.to_a.dup
494
- while !package_sets.empty? && package_sets.first != package_set
495
- set = package_sets.shift
496
- return vcs if set == mainline
549
+ index = package_sets.find_index(package_set)
550
+ if !index
551
+ raise RuntimeError, "found inconsistency: package #{package_name} is not in a package set of #{self}"
552
+ end
553
+
554
+ if package_sets[0, index + 1].include?(mainline)
555
+ return vcs
497
556
  end
498
- set = package_sets.shift
499
- return vcs if set == mainline
500
557
 
501
558
  # Then apply the overrides
502
- package_sets.inject(vcs) do |updated_vcs, pkg_set|
503
- updated_vcs = pkg_set.overrides_for(package_name, updated_vcs)
559
+ package_sets[(index + 1)..-1].inject(vcs) do |updated_vcs, pkg_set|
560
+ updated_vcs = pkg_set.overrides_for(package_name, updated_vcs, require_existing: false)
504
561
  return updated_vcs if pkg_set == mainline
505
562
  updated_vcs
506
563
  end
@@ -522,45 +579,59 @@ def importer_definition_for(package_name, package_set = definition_package_set(p
522
579
  # * S1 must have a VCS line for P
523
580
  # * S0 can have a VCS line for P, which would override the one defined
524
581
  # by S1
525
- def load_importers(options = Hash.new)
582
+ def load_importers(mainline: nil)
526
583
  packages.each_value do |pkg|
527
- vcs = importer_definition_for(pkg.autobuild.name, pkg.package_set, options) ||
528
- pkg.package_set.default_importer
529
-
584
+ package_mainline =
585
+ if mainline == true
586
+ pkg.package_set
587
+ else mainline
588
+ end
589
+ vcs = importer_definition_for(pkg, mainline: package_mainline)
530
590
 
531
- if vcs
532
- pkg.vcs = vcs
533
- pkg.autobuild.importer = vcs.create_autobuild_importer
534
- else
535
- raise ConfigError.new, "source #{pkg.package_set.name} defines #{pkg.autobuild.name}, but does not provide a version control definition for it"
591
+ if vcs.none?
592
+ if pkg.package_set.importer_definition_for(pkg).none?
593
+ if (pkg.package_set != main_package_set) || !File.exist?(pkg.autobuild.srcdir)
594
+ raise ConfigError.new, "package set #{pkg.package_set.name} defines the package '#{pkg.name}', but does not provide a version control definition for it"
595
+ end
596
+ end
536
597
  end
598
+
599
+ pkg.vcs = vcs
600
+ pkg.autobuild.importer = vcs.create_autobuild_importer
537
601
  end
538
602
  end
539
603
 
540
604
  # Checks if there is a package with a given name
541
605
  #
542
- # @param [String] name
606
+ # @param [String] name the name of a source or osdep package
543
607
  # @return [Boolean]
544
608
  def has_package?(name)
545
- packages.has_key?(name)
609
+ packages.has_key?(name) || os_package_resolver.include?(name)
546
610
  end
547
611
 
548
- # Returns true if +name+ is the name of a package set known to this
549
- # autoproj installation
612
+ # Checks if there is a package set with a given name
550
613
  def has_package_set?(name)
614
+ !!find_package_set(name)
615
+ end
616
+
617
+ # Returns a package set from its name
618
+ def find_package_set(name)
551
619
  each_package_set.find { |set| set.name == name }
552
620
  end
553
621
 
554
- # Returns the PackageSet object for the given package set, or raises
555
- # ArgumentError if none exists with that name
622
+ # The PackageSet object for the given package set
623
+ #
624
+ # @return [PackageSet] the package set
625
+ # @raise [ArgumentError] if none exists with that name
556
626
  def package_set(name)
557
- set = each_package_set.find { |set| set.name == name }
558
- if !set
627
+ if set = find_package_set(name)
628
+ set
629
+ else
559
630
  raise ArgumentError, "no package set called #{name} exists"
560
631
  end
561
- set
562
632
  end
563
633
 
634
+ # The root package set, which represents the workspace itself
564
635
  def main_package_set
565
636
  each_package_set.find(&:main?)
566
637
  end
@@ -572,7 +643,7 @@ def main_package_set
572
643
  # package set).
573
644
  #
574
645
  # @return [nil,Array] either nil if there is no such osdep, or a list of
575
- # (type, package_name) pairs where type is either :package or :osdep and
646
+ # (type, package_name) pairs where type is either :package or :osdeps and
576
647
  # package_name the corresponding package name
577
648
  # @raise [PackageNotFound] if the given package name cannot be resolved
578
649
  # into a package. If {#accept_unavailable_osdeps?} is false (the
@@ -599,27 +670,6 @@ def resolve_package_name(name)
599
670
  result
600
671
  end
601
672
 
602
- # Resolves all the source package dependencies for given packages
603
- #
604
- # @param [Set<String>] the set of package names of which we want to
605
- # discover the dependencies
606
- # @return [Set<String>] the set of all package names that the packages designed
607
- # by root_names depend on
608
- def resolve_packages_dependencies(*root_names)
609
- result = Set.new
610
- queue = root_names.dup
611
- while pkg_name = queue.shift
612
- next if result.include?(pkg_name)
613
- result << pkg_name
614
-
615
- pkg = find_autobuild_package(pkg_name)
616
- pkg.dependencies.each do |dep_name|
617
- queue << dep_name
618
- end
619
- end
620
- result
621
- end
622
-
623
673
  # @api private
624
674
  #
625
675
  # Resolves a package name, where +name+ cannot be resolved as a
@@ -629,7 +679,7 @@ def resolve_packages_dependencies(*root_names)
629
679
  # directly
630
680
  #
631
681
  # @return [nil,Array] either nil if there is no such osdep, or a list of
632
- # (type, package_name) pairs where type is either :package or :osdep and
682
+ # (type, package_name) pairs where type is either :package or :osdeps and
633
683
  # package_name the corresponding package name
634
684
  def resolve_single_package_name(name)
635
685
  resolve_package_name_as_osdep(name)
@@ -695,28 +745,11 @@ def resolve_package_name_as_osdep(name)
695
745
  elsif osdeps_available || accept_unavailable_osdeps?
696
746
  return [[:osdeps, name]]
697
747
  elsif osdeps_availability == OSPackageResolver::WRONG_OS
698
- raise PackageNotFound, "#{name} is an osdep, but it is not available for this operating system"
748
+ raise PackageNotFound, "#{name} is an osdep, but it is not available for this operating system (#{os_package_resolver.operating_system})"
699
749
  elsif osdeps_availability == OSPackageResolver::UNKNOWN_OS
700
750
  raise PackageNotFound, "#{name} is an osdep, but the local operating system is unavailable"
701
751
  elsif osdeps_availability == OSPackageResolver::NONEXISTENT
702
- raise PackageNotFound, "#{name} is an osdep, but it is explicitely marked as 'nonexistent' for this operating system"
703
- end
704
- end
705
-
706
- # +name+ can either be the name of a source or the name of a package. In
707
- # the first case, we return all packages defined by that source. In the
708
- # latter case, we return the singleton array [name]
709
- def resolve_package_set(name)
710
- if find_autobuild_package(name)
711
- [name]
712
- else
713
- pkg_set = find_metapackage(name)
714
- if !pkg_set
715
- raise UnknownPackage.new(name), "#{name} is neither a package nor a package set name. Packages in autoproj must be declared in an autobuild file."
716
- end
717
- pkg_set.each_package.
718
- map(&:name).
719
- find_all { |pkg_name| !os_package_resolver.has?(pkg_name) }
752
+ raise PackageNotFound, "#{name} is an osdep, but it is explicitely marked as 'nonexistent' for this operating system (#{os_package_resolver.operating_system})"
720
753
  end
721
754
  end
722
755
 
@@ -743,14 +776,19 @@ def find_metapackage(name)
743
776
  #
744
777
  def metapackage(name, *packages, &block)
745
778
  meta = (@metapackages[name.to_s] ||= Metapackage.new(name))
746
- packages.each do |pkg|
747
- if pkg.respond_to?(:to_str)
748
- package_names = resolve_package_set(pkg)
749
- package_names.each do |pkg_name|
750
- meta.add(find_autobuild_package(pkg_name))
779
+ packages.each do |arg|
780
+ if !arg.respond_to?(:to_str)
781
+ meta.add(arg)
782
+ elsif pkg = find_autobuild_package(arg)
783
+ meta.add(pkg)
784
+ elsif pkg_set = find_metapackage(arg)
785
+ pkg_set.each_package do |pkg_in_set|
786
+ meta.add(pkg_in_set)
751
787
  end
788
+ elsif os_package_resolver.has?(arg)
789
+ raise ArgumentError, "cannot specify the osdep #{arg} as an element of a metapackage"
752
790
  else
753
- meta.add(pkg)
791
+ raise PackageNotFound, "cannot find a package called #{arg}"
754
792
  end
755
793
  end
756
794
 
@@ -771,7 +809,7 @@ def each_metapackage(&block)
771
809
  # Returns the packages selected in this manifest's layout
772
810
  #
773
811
  # @return [PackageSelection]
774
- def layout_packages(validate)
812
+ def layout_packages(validate = true)
775
813
  result = PackageSelection.new
776
814
  Autoproj.in_file(self.file) do
777
815
  normalized_layout.each_key do |pkg_or_set|
@@ -780,10 +818,11 @@ def layout_packages(validate)
780
818
  meta.weak_dependencies?
781
819
  end
782
820
 
783
-
784
- result.select(pkg_or_set, resolve_package_set(pkg_or_set), weak: weak)
785
- rescue UnknownPackage => e
786
- raise e, "#{e.name}, which is selected in the layout, is unknown: #{e.message}", e.backtrace
821
+ resolve_package_name(pkg_or_set).each do |pkg_type, pkg_name|
822
+ result.select(pkg_or_set, pkg_name, osdep: (pkg_type == :osdeps), weak: weak)
823
+ end
824
+ rescue PackageNotFound => e
825
+ raise e, "#{pkg_or_set}, which is selected in the layout, is unknown: #{e.message}", e.backtrace
787
826
  end
788
827
  end
789
828
  end
@@ -798,37 +837,17 @@ def layout_packages(validate)
798
837
  result
799
838
  end
800
839
 
801
- # Enumerates the sublayouts defined in +layout_def+.
802
- def each_sublayout(layout_def)
803
- layout_def.each do |value|
804
- if value.kind_of?(Hash)
805
- name, layout = value.find { true }
806
- yield(name, layout)
807
- end
808
- end
809
- end
810
-
811
840
  # Returns the set of package names that are explicitely listed in the
812
841
  # layout, minus the excluded and ignored ones
813
842
  def all_layout_packages(validate = true)
814
843
  default_packages(validate)
815
844
  end
816
845
 
817
- # Returns all defined package names, minus the excluded and ignored ones
846
+ # Returns all defined package names
818
847
  def all_package_names
819
848
  each_autobuild_package.map(&:name)
820
849
  end
821
850
 
822
- # Returns all the packages that can be built in this installation
823
- def all_packages
824
- result = Set.new
825
- each_package_set do |pkg_set|
826
- result |= metapackage(pkg_set.name).packages.map(&:name).to_set
827
- end
828
- result.to_a.
829
- find_all { |pkg_name| !os_package_resolver.has?(pkg_name) }
830
- end
831
-
832
851
  # Returns true if +name+ is a valid package and is included in the build
833
852
  #
834
853
  # If +validate+ is true, the method will raise ArgumentError if the
@@ -837,14 +856,8 @@ def all_packages
837
856
  # If it is false, the method will simply return false on non-defined
838
857
  # packages
839
858
  def package_enabled?(name, validate = true)
840
- if !find_autobuild_package(name) && !os_package_resolver.has?(name)
841
- if validate
842
- raise ArgumentError, "package #{name} does not exist"
843
- end
844
- return false
845
- end
846
-
847
- !excluded?(name)
859
+ Autoproj.warn_deprecated "#package_enabled? and #package_selected? were broken in autoproj v1, and there are usually other ways to get the same effect (as e.g. splitting package sets). Feel free to contact the autoproj developers if you have a use case that demands this functionality. For now, this method returns true for backward compatibility reasons."
860
+ true
848
861
  end
849
862
 
850
863
  # Returns true if +name+ is a valid package and is neither excluded from
@@ -856,48 +869,68 @@ def package_enabled?(name, validate = true)
856
869
  # If it is false, the method will simply return false on non-defined
857
870
  # packages
858
871
  def package_selected?(name, validate = true)
859
- if package_enabled?(name)
860
- !ignored?(name)
872
+ Autoproj.warn_deprecated "#package_enabled? and #package_selected? were broken in autoproj v1, and there are usually other ways to get the same effect (as e.g. splitting package sets). Feel free to contact the autoproj developers if you have a use case that demands this functionality. For now, this method returns true for backward compatibility reasons."
873
+ true
874
+ end
875
+
876
+ # Returns the set of source packages that are selected by the layout
877
+ #
878
+ # @return [Array<PackageDefinition>]
879
+ def all_selected_source_packages(validate = true)
880
+ result = Set.new
881
+ selection = default_packages(validate)
882
+
883
+ root_sources = selection.each_source_package_name.to_set
884
+ root_sources.each do |pkg_name|
885
+ find_autobuild_package(pkg_name).all_dependencies(result)
886
+ end
887
+ result.merge(root_sources).map do |pkg_name|
888
+ find_package_definition(pkg_name)
861
889
  end
862
890
  end
863
891
 
864
892
  # Returns the set of packages that are selected by the layout
893
+ #
894
+ # Unless {#default_packages}, it returns both the selected packages and
895
+ # the dependencies (resolved recursively)
896
+ #
897
+ # @return [Array<String>] a list of source and osdep package names
865
898
  def all_selected_packages(validate = true)
866
899
  result = Set.new
867
- root = default_packages(validate).source_packages.to_set
868
- root.each do |pkg_name|
869
- find_autobuild_package(pkg_name).all_dependencies(result)
900
+ selection = default_packages(validate)
901
+
902
+ root_sources = selection.each_source_package_name.to_set
903
+ root_sources.each do |pkg_name|
904
+ find_autobuild_package(pkg_name).all_dependencies_with_osdeps(result)
870
905
  end
871
- result | root
906
+ result | root_sources | selection.each_osdep_package_name.to_set
872
907
  end
873
908
 
874
909
  # Returns the set of packages that should be built if the user does not
875
910
  # specify any on the command line
876
911
  def default_packages(validate = true)
877
- if data['layout']
878
- return layout_packages(validate)
912
+ if has_layout?
913
+ layout_packages(validate)
879
914
  else
880
915
  result = PackageSelection.new
881
- # No layout, all packages are selected
882
- names = all_packages
883
- names.delete_if { |pkg_name| excluded?(pkg_name) || ignored?(pkg_name) }
884
- names.each do |pkg_name|
885
- result.select(pkg_name, pkg_name)
916
+ all_package_names.each do |pkg_name|
917
+ package_type, package_name = resolve_single_package_name(pkg_name).first
918
+ next if excluded?(package_name) || ignored?(package_name)
919
+ result.select(package_name, package_name, osdep: (package_type == :osdeps))
886
920
  end
887
921
  result
888
922
  end
889
923
  end
890
924
 
891
- # A mapping from names to layout placement, as found in the layout
892
- # section of the manifest
893
- attr_reader :normalized_layout
894
-
925
+ # @api private
926
+ #
927
+ # Compute a layout structure that is normalized
895
928
  def compute_normalized_layout(result, layout_level, layout_data)
896
929
  layout_data.each do |value|
897
930
  if value.kind_of?(Hash)
898
931
  subname, subdef = value.find { true }
899
932
  if subdef
900
- normalized_layout(result, "#{layout_level}#{subname}/", subdef)
933
+ compute_normalized_layout(result, "#{layout_level}#{subname}/", subdef)
901
934
  end
902
935
  else
903
936
  result[value] = layout_level
@@ -906,19 +939,28 @@ def compute_normalized_layout(result, layout_level, layout_data)
906
939
  result
907
940
  end
908
941
 
909
- # Returns the package directory for the given package name
942
+ # Returns the level of the layout into which of a certain package
943
+ # would be selected
944
+ #
945
+ # @return [String]
910
946
  def whereis(package_name)
911
- Autoproj.in_file(self.file) do
912
- set_name = definition_package_set(package_name).name
913
- actual_layout = normalized_layout
914
- return actual_layout[package_name] || actual_layout[set_name] || '/'
947
+ package_name = validate_package_name_argument(package_name)
948
+
949
+ matches = [package_name]
950
+ if source_package = find_package_definition(package_name)
951
+ each_metapackage do |meta|
952
+ if meta.include?(source_package)
953
+ matches << meta.name
954
+ end
955
+ end
915
956
  end
916
- end
917
957
 
918
- def resolve_optional_dependencies
919
- packages.each_value do |pkg|
920
- pkg.autobuild.resolve_optional_dependencies
958
+ matches.each do |name|
959
+ if place = normalized_layout[name]
960
+ return place
961
+ end
921
962
  end
963
+ '/'
922
964
  end
923
965
 
924
966
  # Loads the package's manifest.xml file for the current package
@@ -927,37 +969,31 @@ def resolve_optional_dependencies
927
969
  # warning. This will later be changed into an error.
928
970
  def load_package_manifest(pkg)
929
971
  if pkg.respond_to?(:to_str)
930
- pkg = packages.values.
931
- find { |p| p.autobuild.name == pkg }
972
+ pkg_definition = find_package_definition(pkg)
973
+ if !pkg_definition
974
+ raise ArgumentError, "#{pkg} is not a known package in #{self}"
975
+ end
976
+ pkg = pkg_definition
932
977
  end
933
- package, package_set, file = pkg.autobuild, pkg.package_set, pkg.file
978
+ package, package_set = pkg.autobuild, pkg.package_set
934
979
 
935
- if !pkg
936
- raise ArgumentError, "package #{pkg} is not defined"
980
+ # Look for the package's manifest.xml, but fallback to a manifest in
981
+ # the package set if present
982
+ manifest_paths = [File.join(package.srcdir, "manifest.xml")]
983
+ if package_set.local_dir
984
+ manifest_paths << File.join(package_set.local_dir, "manifests", package.name + ".xml")
937
985
  end
938
-
939
- manifest_paths =
940
- [File.join(package_set.local_dir, "manifests", package.name + ".xml"), File.join(package.srcdir, "manifest.xml")]
941
986
  manifest_path = manifest_paths.find do |path|
942
- File.directory?(File.dirname(path)) &&
943
- File.file?(path)
987
+ File.file?(path)
944
988
  end
945
989
 
946
- manifest =
947
- if !manifest_path
948
- if !pkg.autobuild.description
949
- Autoproj.warn "#{package.name} from #{package_set.name} does not have a manifest"
950
- PackageManifest.new(package)
951
- else
952
- pkg.autobuild.description
953
- end
954
- else
955
- PackageManifest.load(package, manifest_path)
956
- end
957
-
958
- pkg.autobuild.description = manifest
959
- package_manifests[package.name] = manifest
990
+ if manifest_path
991
+ pkg.autobuild.description = PackageManifest.load(package, manifest_path)
992
+ elsif pkg.autobuild.description.null?
993
+ Autoproj.warn "#{package.name} from #{package_set.name} does not have a manifest"
994
+ end
960
995
 
996
+ manifest = pkg.autobuild.description
961
997
  manifest.each_dependency(pkg.modes) do |name, is_optional|
962
998
  begin
963
999
  if is_optional
@@ -965,9 +1001,6 @@ def load_package_manifest(pkg)
965
1001
  else
966
1002
  package.depends_on name
967
1003
  end
968
- rescue Autobuild::ConfigException => e
969
- raise ConfigError.new(manifest_path),
970
- "manifest #{manifest_path} of #{package.name} from #{package_set.name} lists '#{name}' as dependency, which is listed in the layout of #{file} but has no autobuild definition", e.backtrace
971
1004
  rescue ConfigError => e
972
1005
  raise ConfigError.new(manifest_path),
973
1006
  "manifest #{manifest_path} of #{package.name} from #{package_set.name} lists '#{name}' as dependency, but it is neither a normal package nor an osdeps package. osdeps reports: #{e.message}", e.backtrace
@@ -976,59 +1009,6 @@ def load_package_manifest(pkg)
976
1009
  manifest
977
1010
  end
978
1011
 
979
- # Loads the manifests for all packages known to this project.
980
- #
981
- # See #load_package_manifest
982
- def load_package_manifests(selected_packages)
983
- selected_packages.each(&:load_package_manifest)
984
- end
985
-
986
- # Disable all automatic imports from the given package set name
987
- def disable_imports_from(pkg_set_name)
988
- @disabled_imports << pkg_set_name
989
- end
990
-
991
- # call-seq:
992
- # list_os_packages(packages) => required_packages, ospkg_to_pkg
993
- #
994
- # Returns the set of dependencies required by the listed packages.
995
- #
996
- # +required_packages+ is the set of osdeps names that are required for
997
- # +packages+ and +ospkg_to_pkg+ a mapping from the osdeps name to the
998
- # set of packages that require this OS package.
999
- def list_os_packages(packages)
1000
- required_os_packages = Set.new
1001
- package_os_deps = Hash.new { |h, k| h[k] = Array.new }
1002
- packages.each do |pkg_name|
1003
- pkg = find_autobuild_package(pkg_name)
1004
- if !pkg
1005
- raise InternalError, "internal error: #{pkg_name} is not a package"
1006
- end
1007
-
1008
- pkg.os_packages.each do |osdep_name|
1009
- package_os_deps[osdep_name] << pkg_name
1010
- required_os_packages << osdep_name
1011
- end
1012
- end
1013
-
1014
- return required_os_packages, package_os_deps
1015
- end
1016
-
1017
- def filter_os_packages(required_os_packages, package_os_deps)
1018
- required_os_packages.find_all do |pkg|
1019
- if excluded?(pkg)
1020
- raise ConfigError.new, "the osdeps package #{pkg} is excluded from the build in #{file}. It is required by #{package_os_deps[pkg].join(", ")}"
1021
- end
1022
- if ignored?(pkg)
1023
- if Autoproj.verbose
1024
- Autoproj.message "ignoring osdeps package #{pkg}"
1025
- end
1026
- false
1027
- else true
1028
- end
1029
- end
1030
- end
1031
-
1032
1012
  # The set of overrides added with #add_osdeps_overrides
1033
1013
  attr_reader :osdeps_overrides
1034
1014
 
@@ -1089,38 +1069,30 @@ def update_selection(selection, user_selection_string, name, weak)
1089
1069
  # name, or a prefix of the package's source directory. For osdeps, it
1090
1070
  # has to be the plain package name
1091
1071
  # @return [PackageSelection, Array<String>]
1092
- def expand_package_selection(selection, options = Hash.new)
1093
- options = Kernel.validate_options options, filter: true
1072
+ def expand_package_selection(selection, filter: true)
1094
1073
  result = PackageSelection.new
1095
1074
 
1096
- # First, remove packages that are directly referenced by name or by
1097
- # package set names. When it comes to packages (NOT package sets),
1098
- # we prefer the ones selected in the layout
1099
- all_selected_packages = self.all_selected_packages
1100
- candidates = all_selected_packages.to_a +
1101
- each_metapackage.map { |metapkg| [metapkg.name, metapkg.weak_dependencies?] }
1102
- selection.each do |sel|
1103
- match_pkg_name = Regexp.new(Regexp.quote(sel))
1104
- candidates.each do |name, weak|
1105
- next if name !~ match_pkg_name
1106
- update_selection(result, sel, name, true)
1107
- end
1108
- end
1109
-
1110
- pending_selections = Hash.new { |h, k| h[k] = Array.new }
1111
-
1112
- # Finally, check for partial matches
1075
+ all_selected_packages = self.all_selected_packages.to_set
1113
1076
  all_source_package_names = self.all_package_names
1114
1077
  all_osdeps_package_names = os_package_resolver.all_package_names
1115
1078
  selection.each do |sel|
1116
1079
  match_pkg_name = Regexp.new(Regexp.quote(sel))
1117
1080
  all_matches = Array.new
1081
+ each_metapackage do |meta_pkg|
1082
+ if meta_pkg.name =~ match_pkg_name
1083
+ all_matches << [meta_pkg.name, meta_pkg.name == sel]
1084
+ end
1085
+ end
1118
1086
  all_source_package_names.each do |pkg_name|
1119
1087
  pkg = find_autobuild_package(pkg_name)
1120
- if pkg_name =~ match_pkg_name ||
1121
- "#{sel}/" =~ Regexp.new("^#{Regexp.quote(pkg.srcdir)}/") ||
1122
- pkg.srcdir =~ Regexp.new("^#{Regexp.quote(sel)}")
1123
- all_matches << [pkg_name, (pkg_name == sel || pkg.srcdir == sel)]
1088
+ if pkg.name =~ match_pkg_name
1089
+ all_matches << [pkg.name, pkg.name == sel]
1090
+ elsif "#{sel}/".start_with?("#{pkg.srcdir}/")
1091
+ all_matches << [pkg.name, true]
1092
+ elsif pkg.respond_to?(:builddir) && "#{sel}/".start_with?("#{pkg.builddir}/")
1093
+ all_matches << [pkg.name, true]
1094
+ elsif pkg.srcdir.start_with?(sel) && all_selected_packages.include?(pkg.name)
1095
+ all_matches << [pkg.name, false]
1124
1096
  end
1125
1097
  end
1126
1098
  all_osdeps_package_names.each do |pkg_name|
@@ -1129,34 +1101,28 @@ def expand_package_selection(selection, options = Hash.new)
1129
1101
  end
1130
1102
  end
1131
1103
 
1132
- all_matches.each do |pkg_name, exact_match|
1133
- # Select packages that are not in the manifest only
1134
- # if they are explicitely selected. However, we do store
1135
- # them as "possible resolutions" for the user selection,
1136
- # and add them if -- at the end of the method -- nothing
1137
- # has been found for this particular selection
1138
- if !all_selected_packages.include?(pkg_name) && !exact_match
1139
- pending_selections[sel] << pkg_name
1140
- else
1104
+ exact_matches, partial_matches =
1105
+ all_matches.partition { |_, exact_match| exact_match }
1106
+ selected_partial_matches, not_selected_partial_matches =
1107
+ partial_matches.partition { |pkg_name, _| all_selected_packages.include?(pkg_name) }
1108
+ if result.has_match_for?(sel)
1109
+ not_selected_partial_matches.clear
1110
+ end
1111
+
1112
+ matches = [exact_matches, selected_partial_matches, not_selected_partial_matches].
1113
+ find { |m| !m.empty? }
1114
+ if matches
1115
+ matches.each do |pkg_name, _|
1141
1116
  update_selection(result, sel, pkg_name, true)
1142
1117
  end
1143
1118
  end
1144
1119
  end
1145
1120
 
1146
- if options[:filter]
1121
+ if filter
1147
1122
  result.filter_excluded_and_ignored_packages(self)
1148
1123
  end
1149
1124
 
1150
1125
  nonresolved = selection - result.matches.keys
1151
- nonresolved.delete_if do |sel|
1152
- if pending = pending_selections.fetch(sel, nil)
1153
- pending.each do |name|
1154
- update_selection(result, sel, name, true)
1155
- end
1156
- true
1157
- end
1158
- end
1159
-
1160
1126
  return result, nonresolved
1161
1127
  end
1162
1128
 
@@ -1197,38 +1163,20 @@ def compute_revdeps
1197
1163
  result
1198
1164
  end
1199
1165
 
1200
- # @deprecated use Autoproj.config.each_reused_autoproj_installation
1201
- def each_reused_autoproj_installation
1202
- Autoproj.config.each_reused_autoproj_installation(&proc)
1203
- end
1204
-
1205
- def reuse(*dir)
1206
- dir = File.expand_path(File.join(*dir), Autoproj.root_dir)
1207
- if reused_installations.any? { |mnf| mnf.path == dir }
1166
+ # Declare that we should reuse the autoproj installation present at the
1167
+ # given path
1168
+ def reuse(workspace_root)
1169
+ if reused_installations.any? { |mnf| mnf.path == workspace_root }
1208
1170
  return
1209
1171
  end
1210
1172
 
1211
- manifest = InstallationManifest.new(dir)
1212
- if !File.file?(manifest.default_manifest_path)
1213
- raise ConfigError.new, "while setting up reuse of #{dir}, the .autoproj-installation-manifest file does not exist. You should probably rerun autoproj envsh in that folder first"
1214
- end
1173
+ manifest = InstallationManifest.from_workspace_root(workspace_root)
1215
1174
  manifest.load
1216
1175
  @reused_installations << manifest
1217
1176
  manifest.each do |pkg|
1218
1177
  ignore_package pkg.name
1219
1178
  end
1220
1179
  end
1221
-
1222
- # Load OS dependency information contained in our registered package
1223
- # sets into the provided osdep object
1224
- #
1225
- # @param [OSPackageResolver] osdeps the osdep handling object
1226
- # @return [void]
1227
- def load_osdeps_from_package_sets(osdeps)
1228
- each_package_set do |pkg_set, file|
1229
- osdeps.merge(pkg_set.load_osdeps(file))
1230
- end
1231
- end
1232
1180
  end
1233
1181
 
1234
1182
  def self.manifest
@@ -1246,9 +1194,6 @@ def self.osdeps
1246
1194
  end
1247
1195
 
1248
1196
  def self.config
1249
- Autoproj.warn_deprecated(
1250
- __method__, "use workspace.config instead")
1251
-
1252
1197
  workspace.config
1253
1198
  end
1254
1199