autoproj 2.3.1 → 2.4.0

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