autoproj 2.3.1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -3
  3. data/.vscode/launch.json +25 -0
  4. data/Gemfile +10 -3
  5. data/autoproj.gemspec +1 -1
  6. data/bin/autoproj_bootstrap +5 -3
  7. data/bin/autoproj_bootstrap.in +5 -3
  8. data/bin/autoproj_install +2 -2
  9. data/bin/autoproj_install.in +2 -2
  10. data/lib/autoproj.rb +3 -1
  11. data/lib/autoproj/build_option.rb +4 -4
  12. data/lib/autoproj/cli.rb +10 -1
  13. data/lib/autoproj/cli/base.rb +14 -4
  14. data/lib/autoproj/cli/bootstrap.rb +2 -2
  15. data/lib/autoproj/cli/cache.rb +4 -10
  16. data/lib/autoproj/cli/clean.rb +1 -1
  17. data/lib/autoproj/cli/commit.rb +1 -1
  18. data/lib/autoproj/cli/exec.rb +11 -5
  19. data/lib/autoproj/cli/locate.rb +12 -12
  20. data/lib/autoproj/cli/log.rb +1 -2
  21. data/lib/autoproj/cli/main.rb +23 -4
  22. data/lib/autoproj/cli/main_plugin.rb +2 -2
  23. data/lib/autoproj/cli/manifest.rb +6 -3
  24. data/lib/autoproj/cli/query.rb +71 -10
  25. data/lib/autoproj/cli/reset.rb +4 -4
  26. data/lib/autoproj/cli/switch_config.rb +2 -2
  27. data/lib/autoproj/cli/tag.rb +2 -2
  28. data/lib/autoproj/cli/update.rb +2 -0
  29. data/lib/autoproj/cli/which.rb +17 -0
  30. data/lib/autoproj/exceptions.rb +9 -4
  31. data/lib/autoproj/git_server_configuration.rb +80 -53
  32. data/lib/autoproj/manifest.rb +1 -1
  33. data/lib/autoproj/ops/cache.rb +21 -10
  34. data/lib/autoproj/ops/main_config_switcher.rb +6 -9
  35. data/lib/autoproj/os_package_query.rb +99 -0
  36. data/lib/autoproj/package_managers/bundler_manager.rb +8 -1
  37. data/lib/autoproj/package_managers/yum_manager.rb +2 -2
  38. data/lib/autoproj/package_managers/zypper_manager.rb +2 -2
  39. data/lib/autoproj/query_base.rb +128 -0
  40. data/lib/autoproj/reporter.rb +12 -0
  41. data/lib/autoproj/{query.rb → source_package_query.rb} +15 -81
  42. data/lib/autoproj/version.rb +1 -1
  43. data/lib/autoproj/workspace.rb +38 -0
  44. metadata +8 -5
  45. data/lib/autoproj/cli/snapshot.rb +0 -66
@@ -240,7 +240,7 @@ def validate_package_name_argument(package, require_existing: true)
240
240
  #
241
241
  # Validate that the given package object is defined in self
242
242
  def validate_package_set_in_self(package_set)
243
- if find_package_set(package.name) != package_set
243
+ if find_package_set(package_set.name) != package_set
244
244
  raise UnregisteredPackageSet, "#{package_set.name} is not registered on #{self}"
245
245
  end
246
246
  end
@@ -47,7 +47,7 @@ def cache_git(pkg, options = Hash.new)
47
47
  Autobuild::Subprocess.run('autoproj-cache', :import, Autobuild.tool('git'), '--git-dir', pkg.importdir, 'remote', 'update', 'autobuild')
48
48
  end
49
49
  with_retry(10) do
50
- Autobuild::Subprocess.run('autoproj-cache', :import, Autobuild.tool('git'), '--git-dir', pkg.importdir, 'fetch', '--tags')
50
+ Autobuild::Subprocess.run('autoproj-cache', :import, Autobuild.tool('git'), '--git-dir', pkg.importdir, 'fetch', 'autobuild', '--tags')
51
51
  end
52
52
  Autobuild::Subprocess.run('autoproj-cache', :import, Autobuild.tool('git'), '--git-dir', pkg.importdir, 'gc', '--prune=all')
53
53
  end
@@ -63,17 +63,28 @@ def cache_archive(pkg)
63
63
  end
64
64
  end
65
65
 
66
- def create_or_update(options = Hash.new)
67
- options = Kernel.validate_options options,
68
- keep_going: false,
69
- checkout_only: false
70
- keep_going = options[:keep_going]
71
- checkout_only = options[:checkout_only]
72
-
66
+ def create_or_update(*package_names, all: true, keep_going: false, checkout_only: false)
73
67
  FileUtils.mkdir_p cache_dir
74
68
 
75
- packages = manifest.each_autobuild_package.
76
- sort_by(&:name)
69
+ if package_names.empty?
70
+ packages =
71
+ if all
72
+ manifest.each_autobuild_package
73
+ else
74
+ manifest.all_selected_source_packages.map(&:autobuild)
75
+ end
76
+ else
77
+ packages = package_names.map do |name|
78
+ if pkg = manifest.find_autobuild_package(name)
79
+ pkg
80
+ else
81
+ raise PackageNotFound, "no package named #{name}"
82
+ end
83
+ end
84
+ end
85
+
86
+ packages = packages.sort_by(&:name)
87
+
77
88
  total = packages.size
78
89
  Autoproj.message "Handling #{total} packages"
79
90
  packages.each_with_index do |pkg, i|
@@ -73,15 +73,12 @@ def validate_autoproj_current_root(reuse)
73
73
 
74
74
  MAIN_CONFIGURATION_TEMPLATE = File.expand_path(File.join("..", "..", "..", "samples", 'autoproj'), File.dirname(__FILE__))
75
75
 
76
- def bootstrap(buildconf_info, options = Hash.new)
77
- options = validate_options options,
78
- reuse: Array.new
79
-
76
+ def bootstrap(buildconf_info, reuse: Array.new)
80
77
  check_root_dir_empty
81
- validate_autoproj_current_root(options[:reuse])
78
+ validate_autoproj_current_root(reuse)
82
79
 
83
80
  ws.config.validate_ruby_executable
84
- ws.config.set 'reused_autoproj_installations', options[:reuse], true
81
+ ws.config.set 'reused_autoproj_installations', reuse, true
85
82
  ws.env.export_env_sh(shell_helpers: ws.config.shell_helpers?)
86
83
 
87
84
  # If we are not getting the installation setup from a VCS, copy the template
@@ -101,14 +98,14 @@ def bootstrap(buildconf_info, options = Hash.new)
101
98
  raise ConfigError.new, "cannot read file / URL #{manifest_url}, did you mean 'autoproj bootstrap VCSTYPE #{manifest_url}' ?"
102
99
  end
103
100
 
104
- File.open(File.join(Autoproj.config_dir, "manifest"), "w") do |io|
101
+ File.open(File.join(ws.config_dir, "manifest"), "w") do |io|
105
102
  io.write(manifest_data)
106
103
  end
107
104
 
108
105
  elsif buildconf_info.size >= 2 # is a VCS definition for the manifest itself ...
109
- type, url, *options = *buildconf_info
106
+ type, url, *vcs_options = *buildconf_info
110
107
  url = VCSDefinition.to_absolute_url(url, ws.root_dir)
111
- do_switch_config(false, type, url, *options)
108
+ do_switch_config(false, type, url, *vcs_options)
112
109
  end
113
110
  ws.env.export_env_sh(shell_helpers: ws.config.shell_helpers?)
114
111
  ws.config.save
@@ -0,0 +1,99 @@
1
+ module Autoproj
2
+ # Match class to query OS packages
3
+ #
4
+ # This class allows to create a query object based on a textual
5
+ # representation, and then match osdeps packages using this query object.
6
+ #
7
+ # The queries are of the form
8
+ #
9
+ # FIELD=VALUE:FIELD~VALUE:FIELD=VALUE
10
+ #
11
+ # The F=V form requires an exact match while F~V allows partial
12
+ # matches. The different matches are combined with AND (i.e. only packages
13
+ # matching all criterias will be returned)
14
+ #
15
+ # The following fields are allowed:
16
+ # * name: the osdep name
17
+ # * real_package: a regexp that matches the name of the underlying package
18
+ # * package_manager: a regexp that matches the underlying package manager
19
+ #
20
+ class OSPackageQuery < QueryBase
21
+ ALLOWED_FIELDS = [
22
+ 'name',
23
+ 'real_package',
24
+ 'package_manager'
25
+ ]
26
+ DEFAULT_FIELDS = {
27
+ }
28
+
29
+ def initialize(fields, value, partial, os_package_resolver)
30
+ super(fields, value, partial)
31
+ @os_package_resolver = os_package_resolver
32
+ end
33
+
34
+ class Adapter
35
+ def initialize(pkg, os_package_resolver)
36
+ @pkg = pkg
37
+ @os_package_resolver = os_package_resolver
38
+ end
39
+
40
+ def name
41
+ [@pkg]
42
+ end
43
+
44
+ def real_package
45
+ packages = @os_package_resolver.resolve_os_packages([@pkg])
46
+ packages.flat_map do |handler, handler_packages|
47
+ handler_packages
48
+ end.uniq
49
+ end
50
+
51
+ def package_manager
52
+ packages = @os_package_resolver.resolve_os_packages([@pkg])
53
+ packages.flat_map do |handler, handler_packages|
54
+ handler
55
+ end.uniq
56
+ end
57
+ end
58
+
59
+ # Checks if a package matches against the query
60
+ #
61
+ # @param [String] pkg the osdep package name
62
+ # @return [Boolean] true if it does, false otherwise
63
+ #
64
+ # If the package matches, the returned value can be one of:
65
+ #
66
+ # EXACT:: this is an exact match
67
+ # PARTIAL::
68
+ # the expected value can be found in the package field. The
69
+ # match is done in a case-insensitive way
70
+ #
71
+ # If partial? is not set (i.e. if FIELD=VALUE was used), then only EXACT
72
+ # or false can be returned.
73
+ def match(pkg)
74
+ pkg = Adapter.new(pkg, @os_package_resolver)
75
+ pkg_value = fields.inject(pkg) do |v, field_name|
76
+ v.send(field_name)
77
+ end
78
+
79
+ if pkg_value.include?(value)
80
+ return EXACT
81
+ end
82
+
83
+ if !partial?
84
+ return
85
+ end
86
+
87
+ if pkg_value.any? { |v| @value_rx === v }
88
+ return PARTIAL
89
+ end
90
+ end
91
+
92
+ # Parse a single field in a query (i.e. a FIELD[=~]VALUE string)
93
+ def self.parse(str, os_package_resolver)
94
+ fields, value, partial =
95
+ super(str, allowed_fields: ALLOWED_FIELDS)
96
+ OSPackageQuery.new(fields, value, partial, os_package_resolver)
97
+ end
98
+ end
99
+ end
@@ -231,7 +231,14 @@ def merge_gemfiles(*path, unlock: [])
231
231
  end
232
232
  end
233
233
  path.each do |gemfile|
234
- bundler_def = Bundler::Dsl.evaluate(gemfile, nil, [])
234
+ bundler_def =
235
+ begin Bundler::Dsl.evaluate(gemfile, nil, [])
236
+ rescue Exception => e
237
+ cleaned_message = e.message.
238
+ gsub(/There was an error parsing([^:]+)/, "Error in gem definitions").
239
+ gsub(/# from.*/, '')
240
+ raise ConfigError, cleaned_message
241
+ end
235
242
  gems_remotes |= bundler_def.send(:sources).rubygems_remotes.to_set
236
243
  bundler_def.dependencies.each do |d|
237
244
  d.groups.each do |group_name|
@@ -42,8 +42,8 @@ def install(packages, filter_uptodate_packages: false, install_only: false)
42
42
  result = false
43
43
  if !patterns.empty?
44
44
  result |= super(patterns,
45
- auto_install_cmd: "yum groupinstall -y '%s'",
46
- user_install_cmd: "yum groupinstall '%s'")
45
+ auto_install_cmd: %w{yum groupinstall -y},
46
+ user_install_cmd: %w{yum groupinstall})
47
47
  end
48
48
  if !packages.empty?
49
49
  result |= super(packages)
@@ -29,8 +29,8 @@ def install(packages, filter_uptodate_packages: false, install_only: false)
29
29
  result = false
30
30
  if !patterns.empty?
31
31
  result |= super(patterns,
32
- auto_install_cmd: "zypper --non-interactive install --type pattern '%s'",
33
- user_install_cmd: "zypper install --type pattern '%s'")
32
+ auto_install_cmd: %w{zypper --non-interactive install --type pattern},
33
+ user_install_cmd: %w{zypper install --type pattern})
34
34
  end
35
35
  if !packages.empty?
36
36
  result |= super(packages)
@@ -0,0 +1,128 @@
1
+ module Autoproj
2
+ # Base implementation for the query classes {SourcePackageQuery} and {OSPackageQuery}
3
+ class QueryBase
4
+ # Match priorities
5
+ EXACT = 4
6
+ PARTIAL = 3
7
+
8
+ # The call chain to be matched (i.e. autobuild.name becomes
9
+ # ['autobuild', 'name']
10
+ attr_reader :fields
11
+ # The expected value
12
+ attr_reader :value
13
+ # Whether the match can be partial
14
+ attr_predicate :partial?, true
15
+
16
+ # @api private
17
+ #
18
+ # Match class that matches anything
19
+ #
20
+ # Use {.all}
21
+ class All
22
+ def match(pkg); true end
23
+ end
24
+
25
+ # Get a query that matches anything
26
+ #
27
+ # @return [All]
28
+ def self.all
29
+ All.new
30
+ end
31
+
32
+ def initialize(fields, value, partial)
33
+ @fields = fields
34
+ @value = value
35
+ @value_rx = Regexp.new(Regexp.quote(value), true)
36
+ @partial = partial
37
+ end
38
+
39
+ # Checks if a package matches against the query
40
+ #
41
+ # @param [String] pkg the osdep package name
42
+ # @return [Boolean] true if it does, false otherwise
43
+ #
44
+ # If the package matches, the returned value can be one of:
45
+ #
46
+ # EXACT:: this is an exact match
47
+ # PARTIAL::
48
+ # the expected value can be found in the package field. The
49
+ # match is done in a case-insensitive way
50
+ #
51
+ # If partial? is not set (i.e. if FIELD=VALUE was used), then only EXACT
52
+ # or false can be returned.
53
+ def match(pkg)
54
+ raise NotImplementedError
55
+ end
56
+
57
+ # @api private
58
+ #
59
+ # Parse a single field in a query (i.e. a FIELD[=~]VALUE string)
60
+ #
61
+ # This is NOT meant to be used directly. Subclasses are supposed to
62
+ # redefine .parse to create the relevant match object.
63
+ def self.parse(str, allowed_fields: [], default_fields: Hash.new)
64
+ if parsed = /[=~]/.match(str)
65
+ field, value = parsed.pre_match, parsed.post_match
66
+ partial = (parsed[0] == '~')
67
+ else
68
+ raise ArgumentError, "invalid query string '#{str}', expected FIELD and VALUE separated by either = or ~"
69
+ end
70
+
71
+ field = default_fields[field] || field
72
+
73
+ # Validate the query key
74
+ if !allowed_fields.include?(field)
75
+ raise ArgumentError, "'#{field}' is not a known query key"
76
+ end
77
+
78
+ fields = field.split('.')
79
+ return fields, value, partial
80
+ end
81
+
82
+ # Parse a complete query
83
+ def self.parse_query(query, *args)
84
+ query = query.split(':')
85
+ query = query.map do |str|
86
+ parse(str, *args)
87
+ end
88
+ if query.size == 1
89
+ query.first
90
+ else
91
+ And.new(query)
92
+ end
93
+ end
94
+
95
+ # Match object that combines multiple matches using a logical OR
96
+ class Or
97
+ def initialize(submatches)
98
+ @submatches = submatches
99
+ end
100
+ def each_subquery(&block)
101
+ @submatches.each(&block)
102
+ end
103
+ def match(pkg)
104
+ @submatches.map { |m| m.match(pkg) }.compact.max
105
+ end
106
+ end
107
+
108
+ # Match object that combines multiple matches using a logical AND
109
+ class And
110
+ def initialize(submatches)
111
+ @submatches = submatches
112
+ end
113
+ def each_subquery(&block)
114
+ @submatches.each(&block)
115
+ end
116
+ def match(pkg)
117
+ matches = @submatches.map do |m|
118
+ if p = m.match(pkg)
119
+ p
120
+ else return
121
+ end
122
+ end
123
+ matches.min
124
+ end
125
+ end
126
+ end
127
+ end
128
+
@@ -88,6 +88,18 @@ def self.report(root_dir: nil, silent: nil, debug: Autobuild.debug,
88
88
  package_failures, on_package_failures: on_package_failures, interrupted_by: interrupted)
89
89
  end
90
90
 
91
+ rescue CLI::CLIException, InvalidWorkspace, ConfigError => e
92
+ if silent_errors
93
+ return [e]
94
+ elsif on_package_failures == :raise
95
+ raise e
96
+ elsif on_package_failures == :report
97
+ Autoproj.error e.message
98
+ elsif on_package_failures == :exit
99
+ Autoproj.error e.message
100
+ exit 1
101
+ end
102
+
91
103
  rescue SystemExit
92
104
  raise
93
105
  ensure
@@ -27,7 +27,7 @@ module Autoproj
27
27
  # * 'vcs' can be used instead of 'vcs.url'
28
28
  # * 'package_set' can be used instead of 'package_set.name'
29
29
  #
30
- class Query
30
+ class SourcePackageQuery < QueryBase
31
31
  ALLOWED_FIELDS = [
32
32
  'autobuild.name',
33
33
  'autobuild.srcdir',
@@ -44,29 +44,13 @@ class Query
44
44
  }
45
45
 
46
46
  # Match priorities
47
- EXACT = 4
48
- PARTIAL = 3
49
47
  DIR_PREFIX_STRONG = 2
50
48
  DIR_PREFIX_WEAK = 1
51
49
 
52
- attr_reader :fields
53
- attr_reader :value
54
50
  attr_predicate :use_dir_prefix?
55
- attr_predicate :partial?
56
-
57
- class All
58
- def match(pkg); true end
59
- end
60
-
61
- def self.all
62
- All.new
63
- end
64
51
 
65
52
  def initialize(fields, value, partial)
66
- @fields = fields
67
- @value = value
68
- @value_rx = Regexp.new(Regexp.quote(value), true)
69
- @partial = partial
53
+ super(fields, value, partial)
70
54
 
71
55
  directories = value.split('/')
72
56
  if !directories.empty?
@@ -119,15 +103,15 @@ def match(pkg)
119
103
  return
120
104
  end
121
105
 
122
- if pkg_value =~ @value_rx
106
+ if @value_rx === pkg_value
123
107
  return PARTIAL
124
108
  end
125
109
 
126
110
  # Special match for directories: match directory prefixes
127
111
  if use_dir_prefix?
128
- if pkg_value =~ @dir_prefix_strong_rx
112
+ if @dir_prefix_strong_rx === pkg_value
129
113
  return DIR_PREFIX_STRONG
130
- elsif pkg_value =~ @dir_prefix_weak_rx
114
+ elsif @dir_prefix_weak_rx === pkg_value
131
115
  return DIR_PREFIX_WEAK
132
116
  end
133
117
  end
@@ -135,69 +119,19 @@ def match(pkg)
135
119
 
136
120
  # Parse a single field in a query (i.e. a FIELD[=~]VALUE string)
137
121
  def self.parse(str)
138
- field, value = str.split('=')
139
- if !value
140
- partial = true
141
- field, value = str.split('~')
142
- end
143
-
144
- if DEFAULT_FIELDS[field]
145
- field = DEFAULT_FIELDS[field]
146
- end
147
-
148
- # Validate the query key
149
- if !ALLOWED_FIELDS.include?(field)
150
- raise ArgumentError, "#{field} is not a known query key"
151
- end
152
-
153
- fields = field.split('.')
154
- new(fields, value, partial)
155
- end
156
-
157
- # Parse a complete query
158
- def self.parse_query(query)
159
- query = query.split(':')
160
- query = query.map do |str|
161
- if str !~ /[=~]/
162
- match_name = Query.parse("autobuild.name~#{str}")
163
- match_dir = Query.parse("autobuild.srcdir~#{str}")
164
- Or.new([match_name, match_dir])
165
- else
166
- Query.parse(str)
167
- end
168
- end
169
- if query.size == 1
170
- query.first
122
+ if str !~ /[=~]/
123
+ match_name = parse("autobuild.name~#{str}")
124
+ match_dir = parse("autobuild.srcdir~#{str}")
125
+ return Or.new([match_name, match_dir])
171
126
  else
172
- And.new(query)
173
- end
174
- end
175
-
176
- # Match object that combines multiple matches using a logical OR
177
- class Or
178
- def initialize(submatches)
179
- @submatches = submatches
180
- end
181
- def match(pkg)
182
- @submatches.map { |m| m.match(pkg) }.compact.max
183
- end
184
- end
185
-
186
- # Match object that combines multiple matches using a logical AND
187
- class And
188
- def initialize(submatches)
189
- @submatches = submatches
190
- end
191
- def match(pkg)
192
- matches = @submatches.map do |m|
193
- if p = m.match(pkg)
194
- p
195
- else return
196
- end
197
- end
198
- matches.min
127
+ fields, value, partial =
128
+ super(str, default_fields: DEFAULT_FIELDS, allowed_fields: ALLOWED_FIELDS)
129
+ return new(fields, value, partial)
199
130
  end
200
131
  end
201
132
  end
133
+
134
+ # For backward compatibility
135
+ Query = SourcePackageQuery
202
136
  end
203
137