autoproj 2.15.0 → 2.15.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/autoproj.gemspec +2 -0
- data/bin/autoproj_bootstrap +44 -22
- data/bin/autoproj_install +44 -22
- data/lib/autoproj/autobuild.rb +4 -0
- data/lib/autoproj/autobuild_extensions/dsl.rb +26 -0
- data/lib/autoproj/autobuild_extensions/python.rb +18 -0
- data/lib/autoproj/configuration.rb +2 -3
- data/lib/autoproj/ops/configuration.rb +55 -48
- data/lib/autoproj/ops/import.rb +3 -1
- data/lib/autoproj/ops/install.rb +44 -22
- data/lib/autoproj/package_managers/apt_dpkg_manager.rb +9 -1
- data/lib/autoproj/package_managers/bundler_manager.rb +37 -17
- data/lib/autoproj/package_managers/pip_manager.rb +6 -14
- data/lib/autoproj/package_managers/shell_script_manager.rb +19 -7
- data/lib/autoproj/package_manifest.rb +5 -52
- data/lib/autoproj/package_set.rb +17 -0
- data/lib/autoproj/python.rb +26 -14
- data/lib/autoproj/repository_managers/apt.rb +1 -1
- data/lib/autoproj/ros_condition_parser.rb +84 -0
- data/lib/autoproj/ros_package_manifest.rb +125 -0
- data/lib/autoproj/version.rb +1 -1
- data/lib/autoproj.rb +2 -0
- metadata +34 -3
@@ -370,7 +370,7 @@ module Autoproj
|
|
370
370
|
end
|
371
371
|
|
372
372
|
# Parse the contents of a gemfile into a set of
|
373
|
-
def merge_gemfiles(*path, unlock: [])
|
373
|
+
def merge_gemfiles(*path, ruby_version: nil, unlock: [])
|
374
374
|
gems_remotes = Set.new
|
375
375
|
dependencies = Hash.new do |h, k|
|
376
376
|
h[k] = Hash.new do |i, j|
|
@@ -410,6 +410,9 @@ module Autoproj
|
|
410
410
|
g = g[0..-2] if g.end_with?("/")
|
411
411
|
contents << "source '#{g}'"
|
412
412
|
end
|
413
|
+
if ruby_version
|
414
|
+
contents << "ruby \"#{ruby_version}\" if respond_to?(:ruby)"
|
415
|
+
end
|
413
416
|
valid_keys = %w[group groups git path glob name branch ref tag
|
414
417
|
require submodules platform platforms type
|
415
418
|
source install_if]
|
@@ -526,34 +529,32 @@ module Autoproj
|
|
526
529
|
end
|
527
530
|
end
|
528
531
|
|
529
|
-
gemfiles =
|
530
|
-
gemfiles << File.join(ws.dot_autoproj_dir, "Gemfile")
|
531
|
-
|
532
|
-
# Save the osdeps entries in a temporary gemfile and finally
|
533
|
-
# merge the whole lot of it
|
534
|
-
gemfile_contents = Tempfile.open "autoproj-gemfile" do |io|
|
535
|
-
gems.map { |entry| GemEntry.parse(entry) }
|
536
|
-
.sort_by(&:name)
|
537
|
-
.each { |entry| io.puts entry.to_gemfile_line }
|
532
|
+
gemfiles = []
|
538
533
|
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
# want to mess it up
|
543
|
-
merge_gemfiles(*gemfiles)
|
534
|
+
unless gems.empty?
|
535
|
+
osdeps_gemfile_io = prepare_osdeps_gemfile(gems)
|
536
|
+
gemfiles << osdeps_gemfile_io.path
|
544
537
|
end
|
545
538
|
|
539
|
+
gemfiles += workspace_configuration_gemfiles
|
540
|
+
# The autoproj gemfile needs to be last, we really don't
|
541
|
+
# want to mess it up
|
542
|
+
gemfiles << File.join(ws.dot_autoproj_dir, "Gemfile")
|
543
|
+
|
544
|
+
ruby_version = RUBY_VERSION.gsub(/\.\d+$/, ".0")
|
545
|
+
gemfile_contents =
|
546
|
+
merge_gemfiles(*gemfiles, ruby_version: "~> #{ruby_version}")
|
547
|
+
|
546
548
|
FileUtils.mkdir_p root_dir
|
547
549
|
updated = (!File.exist?(gemfile_path) ||
|
548
550
|
File.read(gemfile_path) != gemfile_contents)
|
549
551
|
if updated
|
550
552
|
Ops.atomic_write(gemfile_path) do |io|
|
551
|
-
io.puts "ruby \"#{RUBY_VERSION}\" if respond_to?(:ruby)"
|
552
553
|
io.puts gemfile_contents
|
553
554
|
end
|
554
555
|
end
|
555
556
|
|
556
|
-
options =
|
557
|
+
options = []
|
557
558
|
binstubs_path = File.join(root_dir, "bin")
|
558
559
|
if updated || !install_only || !File.file?("#{gemfile_path}.lock")
|
559
560
|
self.class.run_bundler_install(ws, gemfile_path, *options,
|
@@ -580,6 +581,25 @@ module Autoproj
|
|
580
581
|
backup_clean(backups)
|
581
582
|
end
|
582
583
|
|
584
|
+
# Prepare a Gemfile that contains osdeps gem declarations
|
585
|
+
#
|
586
|
+
# @param [Array<String,Hash>] gems osdeps declarations as understood
|
587
|
+
# by {GemEntry.parse}
|
588
|
+
# @return [File]
|
589
|
+
def prepare_osdeps_gemfile(gems)
|
590
|
+
io = Tempfile.open "autoproj-gemfile"
|
591
|
+
io.puts "source \"https://rubygems.org\""
|
592
|
+
gems.map { |entry| GemEntry.parse(entry) }
|
593
|
+
.sort_by(&:name)
|
594
|
+
.each { |entry| io.puts entry.to_gemfile_line }
|
595
|
+
|
596
|
+
io.flush
|
597
|
+
io
|
598
|
+
rescue Exception
|
599
|
+
io&.close
|
600
|
+
raise
|
601
|
+
end
|
602
|
+
|
583
603
|
def discover_rubylib
|
584
604
|
require "bundler"
|
585
605
|
Tempfile.open "autoproj-rubylib" do |io|
|
@@ -27,24 +27,16 @@ module Autoproj
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def guess_pip_program
|
30
|
-
|
31
|
-
Autoproj::Python.setup_python_configuration_options(ws: ws)
|
32
|
-
end
|
33
|
-
unless ws.config.get("USE_PYTHON")
|
34
|
-
raise ConfigError,
|
35
|
-
"Your current package selection requires the use of pip, but" \
|
36
|
-
" the use of python is either unspecified or has been denied,"\
|
37
|
-
" see setting of USE_PYTHON in your workspace configuration." \
|
38
|
-
" Either remove all packages depending on pip packages " \
|
39
|
-
" from the workspace layout (manifest) or " \
|
40
|
-
" call 'autoproj reconfigure' to change the setting."
|
41
|
-
|
42
|
-
end
|
43
|
-
|
30
|
+
activate_python
|
44
31
|
Autobuild.programs["pip"] = "pip" unless Autobuild.programs["pip"]
|
45
32
|
Autobuild.programs["pip"]
|
46
33
|
end
|
47
34
|
|
35
|
+
def activate_python
|
36
|
+
Autoproj::Python.setup_python_configuration_options(ws: ws)
|
37
|
+
Autoproj::Python.assert_python_activated(ws: ws)
|
38
|
+
end
|
39
|
+
|
48
40
|
def install(pips, filter_uptodate_packages: false, install_only: false)
|
49
41
|
guess_pip_program
|
50
42
|
pips = [pips] if pips.is_a?(String)
|
@@ -3,7 +3,8 @@ module Autoproj
|
|
3
3
|
# Base class for all package managers that simply require the call of a
|
4
4
|
# shell script to install packages (e.g. yum, apt, ...)
|
5
5
|
class ShellScriptManager < Manager
|
6
|
-
def self.execute(command_line, with_locking, with_root,
|
6
|
+
def self.execute(command_line, with_locking, with_root,
|
7
|
+
env: Autoproj.workspace.env, inherit: Set.new)
|
7
8
|
if with_locking
|
8
9
|
File.open("/tmp/autoproj_osdeps_lock", "w") do |lock_io|
|
9
10
|
until lock_io.flock(File::LOCK_EX | File::LOCK_NB)
|
@@ -12,18 +13,28 @@ module Autoproj
|
|
12
13
|
"installation"
|
13
14
|
sleep 5
|
14
15
|
end
|
15
|
-
return execute(command_line, false, with_root,
|
16
|
+
return execute(command_line, false, with_root,
|
17
|
+
env: env, inherit: inherit)
|
16
18
|
ensure
|
17
19
|
lock_io.flock(File::LOCK_UN)
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
23
|
+
process_env = env
|
21
24
|
if with_root
|
22
|
-
|
23
|
-
|
25
|
+
process_env = Autobuild::Environment.new
|
26
|
+
process_env.isolate
|
27
|
+
process_env.add_path("PATH", "/usr/local/sbin",
|
28
|
+
"/usr/sbin", "/sbin")
|
29
|
+
|
30
|
+
inherit.each { |var| process_env.set(var, env[var]) }
|
31
|
+
sudo = Autobuild.tool_in_path("sudo", env: process_env)
|
32
|
+
command_line = [sudo, "--preserve-env", *command_line]
|
24
33
|
end
|
25
34
|
|
26
|
-
Autobuild::Subprocess.run "autoproj", "osdeps", *command_line
|
35
|
+
Autobuild::Subprocess.run "autoproj", "osdeps", *command_line,
|
36
|
+
env: process_env.resolved_env,
|
37
|
+
env_inherit: false
|
27
38
|
end
|
28
39
|
|
29
40
|
# Overrides the {#needs_locking?} flag
|
@@ -194,7 +205,8 @@ module Autoproj
|
|
194
205
|
# @return [Boolean] true if packages got installed, false otherwise
|
195
206
|
def install(packages, filter_uptodate_packages: false, install_only: false,
|
196
207
|
auto_install_cmd: self.auto_install_cmd,
|
197
|
-
user_install_cmd: self.user_install_cmd
|
208
|
+
user_install_cmd: self.user_install_cmd,
|
209
|
+
inherit: Set.new)
|
198
210
|
return if packages.empty?
|
199
211
|
|
200
212
|
handled_os = ws.supported_operating_system?
|
@@ -218,7 +230,7 @@ module Autoproj
|
|
218
230
|
|
219
231
|
ShellScriptManager.execute(
|
220
232
|
[*auto_install_cmd, *packages], needs_locking?,
|
221
|
-
needs_root?, env: ws.env
|
233
|
+
needs_root?, env: ws.env, inherit: inherit
|
222
234
|
)
|
223
235
|
return true
|
224
236
|
end
|
@@ -17,7 +17,7 @@ module Autoproj
|
|
17
17
|
# @return [PackageManifest]
|
18
18
|
# @see parse
|
19
19
|
def self.load(package, file, ros_manifest: false)
|
20
|
-
loader_class = ros_manifest ?
|
20
|
+
loader_class = ros_manifest ? RosPackageManifest::Loader : Loader
|
21
21
|
parse(package, File.read(file), path: file, loader_class: loader_class)
|
22
22
|
end
|
23
23
|
|
@@ -32,7 +32,7 @@ module Autoproj
|
|
32
32
|
# @see load
|
33
33
|
def self.parse(package, contents,
|
34
34
|
path: "<loaded from string>", loader_class: Loader)
|
35
|
-
manifest =
|
35
|
+
manifest = loader_class::MANIFEST_CLASS.new(package, path)
|
36
36
|
loader = loader_class.new(path, manifest)
|
37
37
|
begin
|
38
38
|
REXML::Document.parse_stream(contents, loader)
|
@@ -167,6 +167,7 @@ module Autoproj
|
|
167
167
|
end
|
168
168
|
|
169
169
|
def text(text)
|
170
|
+
@tag_text = @tag_text.dup
|
170
171
|
@tag_text << text if @tag_text
|
171
172
|
end
|
172
173
|
end
|
@@ -178,6 +179,8 @@ module Autoproj
|
|
178
179
|
class Loader < BaseLoader
|
179
180
|
attr_reader :path, :manifest
|
180
181
|
|
182
|
+
MANIFEST_CLASS = PackageManifest
|
183
|
+
|
181
184
|
def initialize(path, manifest)
|
182
185
|
super()
|
183
186
|
@path = path
|
@@ -249,55 +252,5 @@ module Autoproj
|
|
249
252
|
@tag_text = nil
|
250
253
|
end
|
251
254
|
end
|
252
|
-
|
253
|
-
# @api private
|
254
|
-
#
|
255
|
-
# REXML stream parser object used to load the XML contents into a
|
256
|
-
# {PackageManifest} object
|
257
|
-
class RosLoader < Loader
|
258
|
-
SUPPORTED_MODES = %w[test doc].freeze
|
259
|
-
DEPEND_TAGS = %w[depend build_depend build_export_depend
|
260
|
-
buildtool_depend buildtool_export_depend
|
261
|
-
exec_depend test_depend run_depend doc_depend].to_set.freeze
|
262
|
-
|
263
|
-
def toplevel_tag_start(name, attributes)
|
264
|
-
if DEPEND_TAGS.include?(name)
|
265
|
-
@tag_text = ""
|
266
|
-
elsif TEXT_FIELDS.include?(name)
|
267
|
-
@tag_text = ""
|
268
|
-
elsif AUTHOR_FIELDS.include?(name)
|
269
|
-
@author_email = attributes["email"]
|
270
|
-
@tag_text = ""
|
271
|
-
else
|
272
|
-
@tag_text = nil
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
def toplevel_tag_end(name)
|
277
|
-
if DEPEND_TAGS.include?(name)
|
278
|
-
if @tag_text.strip.empty?
|
279
|
-
raise InvalidPackageManifest, "found '#{name}' tag in #{path} "\
|
280
|
-
"without content"
|
281
|
-
end
|
282
|
-
|
283
|
-
mode = []
|
284
|
-
if (m = /^(\w+)_depend$/.match(name))
|
285
|
-
mode = SUPPORTED_MODES & [m[1]]
|
286
|
-
end
|
287
|
-
|
288
|
-
manifest.add_dependency(@tag_text, modes: mode)
|
289
|
-
elsif AUTHOR_FIELDS.include?(name)
|
290
|
-
author_name = @tag_text.strip
|
291
|
-
email = @author_email ? @author_email.strip : nil
|
292
|
-
email = nil if email&.empty?
|
293
|
-
contact = ContactInfo.new(author_name, email)
|
294
|
-
manifest.send("#{name}s").concat([contact])
|
295
|
-
elsif TEXT_FIELDS.include?(name)
|
296
|
-
field = @tag_text.strip
|
297
|
-
manifest.send("#{name}=", field) unless field.empty?
|
298
|
-
end
|
299
|
-
@tag_text = nil
|
300
|
-
end
|
301
|
-
end
|
302
255
|
end
|
303
256
|
end
|
data/lib/autoproj/package_set.rb
CHANGED
@@ -793,6 +793,23 @@ module Autoproj
|
|
793
793
|
vcs
|
794
794
|
end
|
795
795
|
|
796
|
+
# Recursively resolve imports for a given package set
|
797
|
+
def self.resolve_imports(pkg_set, parents = Set.new)
|
798
|
+
return Set.new if pkg_set.imports.empty?
|
799
|
+
|
800
|
+
updated_parents = parents | [pkg_set]
|
801
|
+
|
802
|
+
imports = pkg_set.imports.dup
|
803
|
+
pkg_set.imports.each do |p|
|
804
|
+
if parents.include?(p)
|
805
|
+
raise "Cycling dependency between package sets encountered:" \
|
806
|
+
"#{p.name} <--> #{pkg_set.name}"
|
807
|
+
end
|
808
|
+
imports.merge(resolve_imports(p, updated_parents))
|
809
|
+
end
|
810
|
+
imports
|
811
|
+
end
|
812
|
+
|
796
813
|
# Enumerates the Autobuild::Package instances that are defined in this
|
797
814
|
# source
|
798
815
|
def each_package
|
data/lib/autoproj/python.rb
CHANGED
@@ -168,18 +168,18 @@ module Autoproj
|
|
168
168
|
end
|
169
169
|
end
|
170
170
|
|
171
|
-
def self.remove_python_shims(
|
172
|
-
shim_path = File.join(
|
171
|
+
def self.remove_python_shims(prefix_dir)
|
172
|
+
shim_path = File.join(prefix_dir, "bin", "python")
|
173
173
|
FileUtils.rm shim_path if File.exist?(shim_path)
|
174
174
|
end
|
175
175
|
|
176
|
-
def self.remove_pip_shims(
|
177
|
-
shim_path = File.join(
|
176
|
+
def self.remove_pip_shims(prefix_dir)
|
177
|
+
shim_path = File.join(prefix_dir, "bin", "pip")
|
178
178
|
FileUtils.rm shim_path if File.exist?(shim_path)
|
179
179
|
end
|
180
180
|
|
181
|
-
def self.rewrite_python_shims(python_executable,
|
182
|
-
shim_path = File.join(
|
181
|
+
def self.rewrite_python_shims(python_executable, prefix_dir)
|
182
|
+
shim_path = File.join(prefix_dir, "bin")
|
183
183
|
unless File.exist?(shim_path)
|
184
184
|
FileUtils.mkdir_p shim_path
|
185
185
|
Autoproj.warn "Autoproj::Python.rewrite_python_shims: creating "\
|
@@ -196,8 +196,8 @@ module Autoproj
|
|
196
196
|
python_path
|
197
197
|
end
|
198
198
|
|
199
|
-
def self.rewrite_pip_shims(python_executable,
|
200
|
-
shim_path = File.join(
|
199
|
+
def self.rewrite_pip_shims(python_executable, prefix_dir)
|
200
|
+
shim_path = File.join(prefix_dir, "bin")
|
201
201
|
unless File.exist?(shim_path)
|
202
202
|
FileUtils.mkdir_p shim_path
|
203
203
|
Autoproj.warn "Autoproj::Python.rewrite_pip_shims: creating "\
|
@@ -224,14 +224,14 @@ module Autoproj
|
|
224
224
|
|
225
225
|
ws.osdep_suffixes << "python#{$1}" if version =~ /^([0-9]+)\./
|
226
226
|
|
227
|
-
rewrite_python_shims(bin, ws.
|
228
|
-
rewrite_pip_shims(bin, ws.
|
227
|
+
rewrite_python_shims(bin, ws.dot_autoproj_dir)
|
228
|
+
rewrite_pip_shims(bin, ws.dot_autoproj_dir)
|
229
229
|
[bin, version]
|
230
230
|
end
|
231
231
|
|
232
232
|
def self.deactivate_python(ws: Autoproj.workspace)
|
233
|
-
remove_python_shims(ws.
|
234
|
-
remove_pip_shims(ws.
|
233
|
+
remove_python_shims(ws.dot_autoproj_dir)
|
234
|
+
remove_pip_shims(ws.dot_autoproj_dir)
|
235
235
|
ws.config.reset("python_executable")
|
236
236
|
ws.config.reset("python_version")
|
237
237
|
end
|
@@ -260,6 +260,18 @@ module Autoproj
|
|
260
260
|
[bin, version, path]
|
261
261
|
end
|
262
262
|
|
263
|
+
def self.assert_python_activated(ws: Autoproj.workspace)
|
264
|
+
return true if ws.config.get("USE_PYTHON")
|
265
|
+
|
266
|
+
raise ConfigError,
|
267
|
+
"Your current package selection requires the use of python," \
|
268
|
+
" but this is either unspecified or has been denied,"\
|
269
|
+
" see setting of USE_PYTHON in your workspace configuration." \
|
270
|
+
" Either remove all packages depending on pip packages " \
|
271
|
+
" from the workspace layout (manifest) or " \
|
272
|
+
" call 'autoproj reconfigure' to change the setting."
|
273
|
+
end
|
274
|
+
|
263
275
|
def self.setup_python_configuration_options(ws: Autoproj.workspace)
|
264
276
|
ws.config.declare "USE_PYTHON", "boolean",
|
265
277
|
default: "no",
|
@@ -267,8 +279,8 @@ module Autoproj
|
|
267
279
|
|
268
280
|
if ws.config.get("USE_PYTHON")
|
269
281
|
unless ws.config.has_value_for?("python_executable")
|
270
|
-
remove_python_shims(ws.
|
271
|
-
remove_pip_shims(ws.
|
282
|
+
remove_python_shims(ws.dot_autoproj_dir)
|
283
|
+
remove_pip_shims(ws.dot_autoproj_dir)
|
272
284
|
python_bin, = auto_resolve_python(ws: ws)
|
273
285
|
end
|
274
286
|
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "parslet"
|
4
|
+
require "autoproj/exceptions"
|
5
|
+
require "autoproj/variable_expansion"
|
6
|
+
|
7
|
+
module Autoproj
|
8
|
+
# Parses a conditional expression
|
9
|
+
# Syntax and rules as defined in https://www.ros.org/reps/rep-0149.html#id20
|
10
|
+
class RosConditionParser < Parslet::Parser
|
11
|
+
def initialize(context)
|
12
|
+
@context = context
|
13
|
+
super()
|
14
|
+
end
|
15
|
+
|
16
|
+
def expand(var)
|
17
|
+
Autoproj.expand(var, @context)
|
18
|
+
rescue StandardError
|
19
|
+
""
|
20
|
+
end
|
21
|
+
|
22
|
+
# Evaluates the Abstract Syntax Tree generated by the parser
|
23
|
+
class Transform < Parslet::Transform
|
24
|
+
rule(literal: simple(:x)) { String(x) }
|
25
|
+
rule(variable: simple(:x)) { expander.call(String(x)) }
|
26
|
+
rule(op: simple(:o), lhs: simple(:l), rhs: simple(:r)) { l.send(o, r) }
|
27
|
+
rule(op: "and", lhs: simple(:l), rhs: simple(:r)) { l && r }
|
28
|
+
rule(op: "or", lhs: simple(:l), rhs: simple(:r)) { l || r }
|
29
|
+
end
|
30
|
+
|
31
|
+
def evaluate(expr)
|
32
|
+
Transform.new.apply(parse(expr.strip), expander: method(:expand))
|
33
|
+
rescue Parslet::ParseFailed => e
|
34
|
+
raise Autoproj::ConfigError, e.message
|
35
|
+
end
|
36
|
+
|
37
|
+
def any_of(strings)
|
38
|
+
strings = strings.dup
|
39
|
+
strings.reduce(str(strings.shift)) { |acc, s| acc | str(s) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def quoted_literal(quote)
|
43
|
+
str(quote) >> (
|
44
|
+
str(quote).absent? >> any
|
45
|
+
).repeat.maybe.as(:literal) >> str(quote)
|
46
|
+
end
|
47
|
+
|
48
|
+
def chain(lhs, operator, operation)
|
49
|
+
(lhs.as(:lhs) >> operator >> operation.as(:rhs)) | lhs
|
50
|
+
end
|
51
|
+
|
52
|
+
RESERVED = %w[and or].freeze
|
53
|
+
|
54
|
+
rule(:space) { match['\s\t'].repeat(1) }
|
55
|
+
rule(:space?) { space.maybe }
|
56
|
+
rule(:lparen) { str("(") >> space? }
|
57
|
+
rule(:rparen) { str(")") >> space? }
|
58
|
+
rule(:comp_operator) { any_of(%w[== != >= <= > <]).as(:op) >> space? }
|
59
|
+
rule(:and_operator) { str("and").as(:op) >> space? }
|
60
|
+
rule(:or_operator) { str("or").as(:op) >> space? }
|
61
|
+
rule(:literal) do
|
62
|
+
(any_of(RESERVED) >> space).absent? >>
|
63
|
+
(match('\w') | match["_-"]).repeat(1)
|
64
|
+
.as(:literal)
|
65
|
+
end
|
66
|
+
rule(:double_quote) { quoted_literal('"') }
|
67
|
+
rule(:single_quote) { quoted_literal("'") }
|
68
|
+
rule(:variable) do
|
69
|
+
(
|
70
|
+
str("$") >> match('\d').absent? >> (
|
71
|
+
match('\w') | match["_"]
|
72
|
+
).repeat(1)
|
73
|
+
).as(:variable)
|
74
|
+
end
|
75
|
+
|
76
|
+
rule(:term) { (literal | single_quote | double_quote | variable) >> space? }
|
77
|
+
rule(:primary) { (lparen >> or_operation >> rparen) | term }
|
78
|
+
rule(:comp_operation) { chain(primary, comp_operator, comp_operation) }
|
79
|
+
rule(:and_operation) { chain(comp_operation, and_operator, and_operation) }
|
80
|
+
rule(:or_operation) { chain(and_operation, or_operator, or_operation) }
|
81
|
+
|
82
|
+
root(:or_operation)
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Autoproj
|
4
|
+
# Access to the information contained in a package's package.xml file
|
5
|
+
#
|
6
|
+
# Use PackageManifest.load to create
|
7
|
+
class RosPackageManifest < PackageManifest
|
8
|
+
attr_accessor :name
|
9
|
+
attr_writer :build_type
|
10
|
+
|
11
|
+
def build_type
|
12
|
+
@build_type || "catkin"
|
13
|
+
end
|
14
|
+
|
15
|
+
# @api private
|
16
|
+
#
|
17
|
+
# REXML stream parser object used to load the XML contents into a
|
18
|
+
# {PackageManifest} object
|
19
|
+
class Loader < PackageManifest::Loader
|
20
|
+
MANIFEST_CLASS = RosPackageManifest
|
21
|
+
SUPPORTED_MODES = %w[test doc].freeze
|
22
|
+
DEPEND_TAGS = %w[depend build_depend build_export_depend
|
23
|
+
buildtool_depend buildtool_export_depend
|
24
|
+
exec_depend test_depend run_depend doc_depend].to_set.freeze
|
25
|
+
|
26
|
+
def initialize(path, manifest)
|
27
|
+
super
|
28
|
+
@env = manifest.package.ws.env
|
29
|
+
@condition_parser = RosConditionParser.new(@env)
|
30
|
+
end
|
31
|
+
|
32
|
+
def tag_start(name, attributes)
|
33
|
+
super
|
34
|
+
exportlevel_tag_start(name, attributes) if @export_level
|
35
|
+
end
|
36
|
+
|
37
|
+
def tag_end(name)
|
38
|
+
super
|
39
|
+
exportlevel_tag_end(name) if @export_level
|
40
|
+
if @tag_level == 0 && name == "package" &&
|
41
|
+
(!manifest.name || manifest.name.empty?)
|
42
|
+
raise InvalidPackageManifest, "Package name missiing in #{path}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def exportlevel_tag_start(name, attributes)
|
47
|
+
return unless name == "build_type"
|
48
|
+
|
49
|
+
@build_type_condition = attributes["condition"]
|
50
|
+
@tag_text = ""
|
51
|
+
end
|
52
|
+
|
53
|
+
def exportlevel_tag_end(name)
|
54
|
+
return unless name == "build_type"
|
55
|
+
return unless handle_condition(@build_type_condition)
|
56
|
+
|
57
|
+
manifest.build_type = @tag_text.strip
|
58
|
+
end
|
59
|
+
|
60
|
+
def toplevel_tag_start(name, attributes)
|
61
|
+
if DEPEND_TAGS.include?(name)
|
62
|
+
@depend_condition = attributes["condition"]
|
63
|
+
@tag_text = ""
|
64
|
+
elsif TEXT_FIELDS.include?(name)
|
65
|
+
@tag_text = ""
|
66
|
+
elsif AUTHOR_FIELDS.include?(name)
|
67
|
+
@author_email = attributes["email"]
|
68
|
+
@tag_text = ""
|
69
|
+
elsif name == "name"
|
70
|
+
@tag_text = ""
|
71
|
+
elsif name == "export"
|
72
|
+
@export_level = true
|
73
|
+
else
|
74
|
+
@tag_text = nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def handle_condition(expr)
|
79
|
+
return true unless expr && !expr.empty?
|
80
|
+
|
81
|
+
@condition_parser.evaluate(expr)
|
82
|
+
end
|
83
|
+
|
84
|
+
def depend_tag_end(name)
|
85
|
+
return unless handle_condition(@depend_condition)
|
86
|
+
|
87
|
+
if @tag_text.strip.empty?
|
88
|
+
raise InvalidPackageManifest, "found '#{name}' tag in #{path} "\
|
89
|
+
"without content"
|
90
|
+
end
|
91
|
+
|
92
|
+
mode = []
|
93
|
+
if (m = /^(\w+)_depend$/.match(name))
|
94
|
+
mode = SUPPORTED_MODES & [m[1]]
|
95
|
+
end
|
96
|
+
|
97
|
+
manifest.add_dependency(@tag_text, modes: mode)
|
98
|
+
end
|
99
|
+
|
100
|
+
def author_tag_end(name)
|
101
|
+
author_name = @tag_text.strip
|
102
|
+
email = @author_email ? @author_email.strip : nil
|
103
|
+
email = nil if email&.empty?
|
104
|
+
contact = PackageManifest::ContactInfo.new(author_name, email)
|
105
|
+
manifest.send("#{name}s").concat([contact])
|
106
|
+
end
|
107
|
+
|
108
|
+
def toplevel_tag_end(name)
|
109
|
+
if DEPEND_TAGS.include?(name)
|
110
|
+
depend_tag_end(name)
|
111
|
+
elsif AUTHOR_FIELDS.include?(name)
|
112
|
+
author_tag_end(name)
|
113
|
+
elsif TEXT_FIELDS.include?(name)
|
114
|
+
field = @tag_text.strip
|
115
|
+
manifest.send("#{name}=", field) unless field.empty?
|
116
|
+
elsif name == "name"
|
117
|
+
manifest.name = @tag_text.strip
|
118
|
+
elsif name == "export"
|
119
|
+
@export_level = false
|
120
|
+
end
|
121
|
+
@tag_text = nil
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/autoproj/version.rb
CHANGED
data/lib/autoproj.rb
CHANGED
@@ -27,7 +27,9 @@ require "autoproj/package_definition"
|
|
27
27
|
require "autoproj/package_selection"
|
28
28
|
require "autoproj/metapackage"
|
29
29
|
require "autoproj/manifest"
|
30
|
+
require "autoproj/ros_condition_parser"
|
30
31
|
require "autoproj/package_manifest"
|
32
|
+
require "autoproj/ros_package_manifest"
|
31
33
|
require "autoproj/installation_manifest"
|
32
34
|
require "autoproj/os_package_installer"
|
33
35
|
require "autoproj/os_package_resolver"
|