autoproj 2.0.0.rc37 → 2.0.0.rc38
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.
- checksums.yaml +4 -4
- data/.travis.yml +4 -2
- data/Rakefile +1 -1
- data/bin/autoproj_bootstrap +34 -2
- data/bin/autoproj_bootstrap.in +4 -2
- data/bin/autoproj_install +34 -2
- data/bin/autoproj_install.in +4 -2
- data/lib/autoproj.rb +9 -2
- data/lib/autoproj/autobuild.rb +13 -742
- data/lib/autoproj/autobuild_extensions/archive_importer.rb +44 -0
- data/lib/autoproj/autobuild_extensions/dsl.rb +439 -0
- data/lib/autoproj/autobuild_extensions/git.rb +116 -0
- data/lib/autoproj/autobuild_extensions/package.rb +159 -0
- data/lib/autoproj/autobuild_extensions/svn.rb +11 -0
- data/lib/autoproj/cli/base.rb +17 -18
- data/lib/autoproj/cli/clean.rb +1 -2
- data/lib/autoproj/cli/envsh.rb +1 -2
- data/lib/autoproj/cli/inspection_tool.rb +12 -21
- data/lib/autoproj/cli/locate.rb +130 -73
- data/lib/autoproj/cli/main.rb +31 -5
- data/lib/autoproj/cli/main_plugin.rb +79 -0
- data/lib/autoproj/cli/main_test.rb +19 -5
- data/lib/autoproj/cli/osdeps.rb +1 -2
- data/lib/autoproj/cli/patcher.rb +21 -0
- data/lib/autoproj/cli/query.rb +34 -41
- data/lib/autoproj/cli/show.rb +121 -52
- data/lib/autoproj/cli/status.rb +4 -5
- data/lib/autoproj/cli/tag.rb +1 -1
- data/lib/autoproj/cli/test.rb +7 -6
- data/lib/autoproj/cli/update.rb +8 -22
- data/lib/autoproj/cli/versions.rb +1 -2
- data/lib/autoproj/configuration.rb +1 -1
- data/lib/autoproj/environment.rb +2 -7
- data/lib/autoproj/exceptions.rb +10 -8
- data/lib/autoproj/find_workspace.rb +46 -12
- data/lib/autoproj/installation_manifest.rb +34 -25
- data/lib/autoproj/local_package_set.rb +86 -0
- data/lib/autoproj/manifest.rb +448 -503
- data/lib/autoproj/metapackage.rb +31 -5
- data/lib/autoproj/ops/configuration.rb +46 -45
- data/lib/autoproj/ops/import.rb +150 -60
- data/lib/autoproj/ops/install.rb +25 -1
- data/lib/autoproj/ops/loader.rb +4 -1
- data/lib/autoproj/ops/main_config_switcher.rb +4 -4
- data/lib/autoproj/ops/snapshot.rb +4 -3
- data/lib/autoproj/os_package_installer.rb +105 -46
- data/lib/autoproj/os_package_resolver.rb +63 -36
- data/lib/autoproj/package_definition.rb +1 -0
- data/lib/autoproj/package_managers/apt_dpkg_manager.rb +30 -27
- data/lib/autoproj/package_managers/bundler_manager.rb +64 -18
- data/lib/autoproj/package_managers/gem_manager.rb +4 -2
- data/lib/autoproj/package_managers/manager.rb +26 -7
- data/lib/autoproj/package_managers/shell_script_manager.rb +4 -4
- data/lib/autoproj/package_managers/zypper_manager.rb +1 -1
- data/lib/autoproj/package_manifest.rb +154 -137
- data/lib/autoproj/package_selection.rb +16 -2
- data/lib/autoproj/package_set.rb +352 -309
- data/lib/autoproj/query.rb +13 -1
- data/lib/autoproj/system.rb +2 -2
- data/lib/autoproj/test.rb +164 -11
- data/lib/autoproj/variable_expansion.rb +15 -42
- data/lib/autoproj/vcs_definition.rb +93 -76
- data/lib/autoproj/version.rb +1 -1
- data/lib/autoproj/workspace.rb +116 -80
- metadata +10 -2
@@ -13,36 +13,39 @@ def initialize(ws, status_file = "/var/lib/dpkg/status")
|
|
13
13
|
%w{DEBIAN_FRONTEND=noninteractive apt-get install -y})
|
14
14
|
end
|
15
15
|
|
16
|
+
def self.parse_package_status(installed_packages, paragraph)
|
17
|
+
if paragraph =~ /^Status: install ok installed$/
|
18
|
+
if paragraph =~ /^Package: (.*)$/
|
19
|
+
installed_packages << $1
|
20
|
+
end
|
21
|
+
if paragraph =~ /^Provides: (.*)$/
|
22
|
+
installed_packages.merge($1.split(',').map(&:strip))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.parse_dpkg_status(status_file)
|
28
|
+
installed_packages = Set.new
|
29
|
+
dpkg_status = File.read(status_file)
|
30
|
+
dpkg_status << "\n"
|
31
|
+
|
32
|
+
dpkg_status = StringScanner.new(dpkg_status)
|
33
|
+
if !dpkg_status.scan(/Package: /)
|
34
|
+
raise ArgumentError, "expected #{status_file} to have Package: lines but found none"
|
35
|
+
end
|
36
|
+
|
37
|
+
while paragraph_end = dpkg_status.scan_until(/Package: /)
|
38
|
+
paragraph = "Package: #{paragraph_end[0..-10]}"
|
39
|
+
parse_package_status(installed_packages, paragraph)
|
40
|
+
end
|
41
|
+
parse_package_status(installed_packages, "Package: #{dpkg_status.rest}")
|
42
|
+
installed_packages
|
43
|
+
end
|
44
|
+
|
16
45
|
# On a dpkg-enabled system, checks if the provided package is installed
|
17
46
|
# and returns true if it is the case
|
18
47
|
def installed?(package_name, filter_uptodate_packages: false, install_only: false)
|
19
|
-
|
20
|
-
@installed_packages = Set.new
|
21
|
-
dpkg_status = File.readlines(status_file)
|
22
|
-
dpkg_status << ""
|
23
|
-
|
24
|
-
current_packages = []
|
25
|
-
is_installed = false
|
26
|
-
dpkg_status.each do |line|
|
27
|
-
line = line.chomp
|
28
|
-
line = line.encode( "UTF-8", "binary", :invalid => :replace, :undef => :replace)
|
29
|
-
if line == ""
|
30
|
-
if is_installed
|
31
|
-
current_packages.each do |pkg|
|
32
|
-
@installed_packages << pkg
|
33
|
-
end
|
34
|
-
is_installed = false
|
35
|
-
end
|
36
|
-
current_packages.clear
|
37
|
-
elsif line =~ /Package: (.*)$/
|
38
|
-
current_packages << $1
|
39
|
-
elsif line =~ /Provides: (.*)$/
|
40
|
-
current_packages.concat($1.split(',').map(&:strip))
|
41
|
-
elsif line == "Status: install ok installed"
|
42
|
-
is_installed = true
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
48
|
+
@installed_packages ||= AptDpkgManager.parse_dpkg_status(status_file)
|
46
49
|
|
47
50
|
if package_name =~ /^(\w[a-z0-9+-.]+)/
|
48
51
|
@installed_packages.include?($1)
|
@@ -24,13 +24,18 @@ def self.with_prerelease(*value)
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
# (see Manager#call_while_empty?)
|
28
|
+
def call_while_empty?
|
29
|
+
!workspace_configuration_gemfiles.empty?
|
30
30
|
end
|
31
31
|
|
32
|
-
#
|
33
|
-
|
32
|
+
# (see Manager#strict?)
|
33
|
+
def strict?
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
# Set up the workspace environment to work with the bundler-managed
|
38
|
+
# gems
|
34
39
|
def initialize_environment
|
35
40
|
env = ws.env
|
36
41
|
|
@@ -87,6 +92,15 @@ def initialize_environment
|
|
87
92
|
end
|
88
93
|
end
|
89
94
|
|
95
|
+
# @api private
|
96
|
+
#
|
97
|
+
# Update RUBYLIB to add the gems that are part of the bundler
|
98
|
+
# install
|
99
|
+
#
|
100
|
+
# @param [Array<String>] bundle_rubylib the rubylib entries reported
|
101
|
+
# by bundler
|
102
|
+
# @param [Array<String>] system_rubylib the rubylib entries that are
|
103
|
+
# set by the underlying ruby interpreter itself
|
90
104
|
def update_env_rubylib(bundle_rubylib, system_rubylib = discover_rubylib)
|
91
105
|
current = (ws.env.resolved_env['RUBYLIB'] || '').split(File::PATH_SEPARATOR) + system_rubylib
|
92
106
|
(bundle_rubylib - current).each do |p|
|
@@ -94,6 +108,16 @@ def update_env_rubylib(bundle_rubylib, system_rubylib = discover_rubylib)
|
|
94
108
|
end
|
95
109
|
end
|
96
110
|
|
111
|
+
# @api private
|
112
|
+
#
|
113
|
+
# Parse an osdep entry into a gem name and gem version
|
114
|
+
#
|
115
|
+
# The 'gem' entries in the osdep files can contain a version
|
116
|
+
# specification. This method parses the two parts and return them
|
117
|
+
#
|
118
|
+
# @param [String] entry the osdep entry
|
119
|
+
# @return [(String,String),(String,nil)] the gem name, and an
|
120
|
+
# optional version specification
|
97
121
|
def parse_package_entry(entry)
|
98
122
|
if entry =~ /^([^><=~]*)([><=~]+.*)$/
|
99
123
|
[$1.strip, $2.strip]
|
@@ -103,7 +127,14 @@ def parse_package_entry(entry)
|
|
103
127
|
end
|
104
128
|
|
105
129
|
class NotCleanState < RuntimeError; end
|
106
|
-
|
130
|
+
|
131
|
+
# @api private
|
132
|
+
#
|
133
|
+
# Create backup files matching a certain file mapping
|
134
|
+
#
|
135
|
+
# @param [Hash<String,String>] mapping a mapping from the original
|
136
|
+
# file to the file into which it should be backed up. The source
|
137
|
+
# file might not exist.
|
107
138
|
def backup_files(mapping)
|
108
139
|
mapping.each do |file, backup_file|
|
109
140
|
if File.file?(file)
|
@@ -112,6 +143,11 @@ def backup_files(mapping)
|
|
112
143
|
end
|
113
144
|
end
|
114
145
|
|
146
|
+
# @api private
|
147
|
+
#
|
148
|
+
# Restore backups saved by {#backup_file}
|
149
|
+
#
|
150
|
+
# @param (see #backup_file)
|
115
151
|
def backup_restore(mapping)
|
116
152
|
mapping.each do |file, backup_file|
|
117
153
|
if File.file?(backup_file)
|
@@ -120,6 +156,11 @@ def backup_restore(mapping)
|
|
120
156
|
end
|
121
157
|
end
|
122
158
|
|
159
|
+
# @api private
|
160
|
+
#
|
161
|
+
# Remove backups created by {#backup_files}
|
162
|
+
#
|
163
|
+
# @param (see #backup_file)
|
123
164
|
def backup_clean(mapping)
|
124
165
|
mapping.each do |file, backup_file|
|
125
166
|
if File.file?(backup_file)
|
@@ -183,8 +224,8 @@ def self.run_bundler(ws, *commandline, gemfile: nil)
|
|
183
224
|
def merge_gemfiles(*path, unlock: [])
|
184
225
|
gems_remotes = Set.new
|
185
226
|
dependencies = Hash.new do |h, k|
|
186
|
-
h[k] = Hash.new do |
|
187
|
-
|
227
|
+
h[k] = Hash.new do |i, j|
|
228
|
+
i[j] = Hash.new do |a, b|
|
188
229
|
a[b] = Array.new
|
189
230
|
end
|
190
231
|
end
|
@@ -239,6 +280,20 @@ def merge_gemfiles(*path, unlock: [])
|
|
239
280
|
contents.join("\n")
|
240
281
|
end
|
241
282
|
|
283
|
+
def workspace_configuration_gemfiles
|
284
|
+
gemfiles = []
|
285
|
+
ws.manifest.each_package_set do |source|
|
286
|
+
if source.local_dir && File.file?(pkg_set_gemfile = File.join(source.local_dir, 'Gemfile'))
|
287
|
+
gemfiles << pkg_set_gemfile
|
288
|
+
end
|
289
|
+
end
|
290
|
+
# In addition, look into overrides.d
|
291
|
+
Dir.glob(File.join(ws.overrides_dir, "*.gemfile")) do |overrides_gemfile_path|
|
292
|
+
gemfiles << overrides_gemfile_path
|
293
|
+
end
|
294
|
+
gemfiles
|
295
|
+
end
|
296
|
+
|
242
297
|
def install(gems, filter_uptodate_packages: false, install_only: false)
|
243
298
|
root_dir = File.join(ws.prefix_dir, 'gems')
|
244
299
|
gemfile_path = File.join(root_dir, 'Gemfile')
|
@@ -257,16 +312,7 @@ def install(gems, filter_uptodate_packages: false, install_only: false)
|
|
257
312
|
end
|
258
313
|
end
|
259
314
|
|
260
|
-
gemfiles =
|
261
|
-
ws.manifest.each_package_set do |source|
|
262
|
-
if source.local_dir && File.file?(pkg_set_gemfile = File.join(source.local_dir, 'Gemfile'))
|
263
|
-
gemfiles << pkg_set_gemfile
|
264
|
-
end
|
265
|
-
end
|
266
|
-
# In addition, look into overrides.d
|
267
|
-
Dir.glob(File.join(ws.overrides_dir, "*.gemfile")) do |gemfile_path|
|
268
|
-
gemfiles << gemfile_path
|
269
|
-
end
|
315
|
+
gemfiles = workspace_configuration_gemfiles
|
270
316
|
gemfiles << File.join(ws.dot_autoproj_dir, 'Gemfile')
|
271
317
|
|
272
318
|
# Save the osdeps entries in a temporary gemfile and finally
|
@@ -90,6 +90,8 @@ def self.use_cache_dir
|
|
90
90
|
def self.gem_home
|
91
91
|
@gem_home
|
92
92
|
end
|
93
|
+
|
94
|
+
@gem_home = nil
|
93
95
|
|
94
96
|
# Returns the set of default options that are added to gem
|
95
97
|
#
|
@@ -158,7 +160,7 @@ def build_gem_cmdlines(gems)
|
|
158
160
|
|
159
161
|
def pristine(gems)
|
160
162
|
guess_gem_program
|
161
|
-
base_cmdline = [Autobuild.tool_in_path('ruby'), '-S', Autobuild.tool('gem')]
|
163
|
+
base_cmdline = [Autobuild.tool_in_path('ruby', env: ws.env), '-S', Autobuild.tool('gem')]
|
162
164
|
cmdlines = [
|
163
165
|
[*base_cmdline, 'clean'],
|
164
166
|
]
|
@@ -176,7 +178,7 @@ def pristine(gems)
|
|
176
178
|
def install(gems)
|
177
179
|
guess_gem_program
|
178
180
|
|
179
|
-
base_cmdline = [Autobuild.tool_in_path('ruby'), '-S', Autobuild.tool('gem'), 'install', *GemManager.default_install_options]
|
181
|
+
base_cmdline = [Autobuild.tool_in_path('ruby', env: ws.env), '-S', Autobuild.tool('gem'), 'install', *GemManager.default_install_options]
|
180
182
|
if !GemManager.with_doc
|
181
183
|
base_cmdline << '--no-rdoc' << '--no-ri'
|
182
184
|
end
|
@@ -16,19 +16,38 @@ def enabled?; !!@enabled end
|
|
16
16
|
attr_writer :silent
|
17
17
|
def silent?; !!@silent end
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
# Whether this package manager should be called even if no packages
|
20
|
+
# should be installed
|
21
|
+
#
|
22
|
+
# This is needed if the installer has ways to get specifications of
|
23
|
+
# packages to install through other means than the osdep system, as
|
24
|
+
# e.g. {BundlerManager} that would install gems listed in
|
25
|
+
# autoproj/Gemfile
|
26
|
+
def call_while_empty?
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
# Whether this package manager needs to maintain a list of all the
|
31
|
+
# packages that are needed for the whole installation (true), or
|
32
|
+
# needs only to be called with packages to install
|
33
|
+
#
|
34
|
+
# OS package managers are generally non-strict (once a package is
|
35
|
+
# installed, it's available to all). Package managers like
|
36
|
+
# {BundlerManager} are strict as they maintain a list of gems that
|
37
|
+
# are then made available to the whole installation
|
38
|
+
#
|
39
|
+
# The default is false, reimplement in subclasses to return true
|
40
|
+
def strict?
|
41
|
+
false
|
42
|
+
end
|
21
43
|
|
22
|
-
# Create a package manager
|
44
|
+
# Create a package manager
|
23
45
|
#
|
24
|
-
# @param [
|
25
|
-
# different from the OS names that autoproj uses. See the comment
|
26
|
-
# for OS_PACKAGE_HANDLERS for an explanation
|
46
|
+
# @param [Workspace] ws the underlying workspace
|
27
47
|
def initialize(ws)
|
28
48
|
@ws = ws
|
29
49
|
@enabled = true
|
30
50
|
@silent = true
|
31
|
-
@call_while_empty = false
|
32
51
|
end
|
33
52
|
|
34
53
|
# Overload to perform initialization of environment variables in
|
@@ -3,7 +3,7 @@ module PackageManagers
|
|
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, env: Autobuild.env)
|
7
7
|
if with_locking
|
8
8
|
File.open('/tmp/autoproj_osdeps_lock', 'w') do |lock_io|
|
9
9
|
begin
|
@@ -11,7 +11,7 @@ def self.execute(command_line, with_locking, with_root)
|
|
11
11
|
Autoproj.message " waiting for other autoproj instances to finish their osdeps installation"
|
12
12
|
sleep 5
|
13
13
|
end
|
14
|
-
return execute(command_line, false, with_root)
|
14
|
+
return execute(command_line, false, with_root, env: env)
|
15
15
|
ensure
|
16
16
|
lock_io.flock(File::LOCK_UN)
|
17
17
|
end
|
@@ -19,7 +19,7 @@ def self.execute(command_line, with_locking, with_root)
|
|
19
19
|
end
|
20
20
|
|
21
21
|
if with_root
|
22
|
-
sudo = Autobuild.tool_in_path('sudo')
|
22
|
+
sudo = Autobuild.tool_in_path('sudo', env: env)
|
23
23
|
command_line = [sudo, *command_line]
|
24
24
|
end
|
25
25
|
|
@@ -188,7 +188,7 @@ def install(packages, filter_uptodate_packages: false, install_only: false,
|
|
188
188
|
Autoproj.message shell_script
|
189
189
|
end
|
190
190
|
|
191
|
-
ShellScriptManager.execute([*auto_install_cmd, *packages], needs_locking?, needs_root
|
191
|
+
ShellScriptManager.execute([*auto_install_cmd, *packages], needs_locking?, needs_root?, env: ws.env)
|
192
192
|
return true
|
193
193
|
end
|
194
194
|
false
|
@@ -3,201 +3,218 @@ module Autoproj
|
|
3
3
|
#
|
4
4
|
# Use PackageManifest.load to create
|
5
5
|
class PackageManifest
|
6
|
-
#
|
7
|
-
|
6
|
+
# Create a null manifest for the given package
|
7
|
+
def self.null(package)
|
8
|
+
new(package, null: true)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Load a manifest.xml file and returns the corresponding PackageManifest
|
12
|
+
# object
|
13
|
+
#
|
14
|
+
# @param [PackageDescription] the package we're loading it for
|
15
|
+
# @param [String] file the path to the manifest.xml file
|
16
|
+
# @return [PackageManifest]
|
17
|
+
# @see parse
|
8
18
|
def self.load(package, file)
|
9
|
-
|
10
|
-
|
11
|
-
rescue REXML::ParseException => e
|
12
|
-
raise Autobuild::PackageException.new(package.name, 'prepare'), "invalid #{file}: #{e.message}"
|
13
|
-
end
|
19
|
+
parse(package, File.read(file), path: file)
|
20
|
+
end
|
14
21
|
|
15
|
-
|
22
|
+
# Create a PackageManifest object from the XML content provided as a
|
23
|
+
# string
|
24
|
+
#
|
25
|
+
# @param [PackageDescription] the package we're loading it for
|
26
|
+
# @param [String] contents the manifest.xml contents as a string
|
27
|
+
# @param [String] path the file path, used for error reporting
|
28
|
+
# @return [PackageManifest]
|
29
|
+
# @see load
|
30
|
+
def self.parse(package, contents, path: '<loaded from string>')
|
31
|
+
manifest = PackageManifest.new(package)
|
32
|
+
loader = Loader.new(path, manifest)
|
33
|
+
begin
|
34
|
+
REXML::Document.parse_stream(contents, loader)
|
35
|
+
rescue REXML::ParseException => e
|
36
|
+
raise Autobuild::PackageException.new(package.name, 'prepare'), "invalid #{file}: #{e.message}"
|
37
|
+
end
|
38
|
+
manifest
|
16
39
|
end
|
17
40
|
|
41
|
+
ContactInfo = Struct.new :name, :email
|
42
|
+
Dependency = Struct.new :name, :optional, :modes
|
43
|
+
|
18
44
|
# The Autobuild::Package instance this manifest applies on
|
19
45
|
attr_reader :package
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
46
|
+
attr_accessor :description
|
47
|
+
attr_accessor :brief_description
|
48
|
+
attr_reader :dependencies
|
49
|
+
attr_accessor :tags
|
50
|
+
attr_accessor :url
|
51
|
+
attr_accessor :license
|
52
|
+
attr_accessor :version
|
53
|
+
attr_accessor :authors
|
54
|
+
attr_accessor :maintainers
|
55
|
+
attr_accessor :rock_maintainers
|
56
|
+
|
57
|
+
# Add a declared dependency to this package
|
58
|
+
def add_dependency(name, optional: false, modes: [])
|
59
|
+
dependencies << Dependency.new(name, optional, modes)
|
33
60
|
end
|
34
61
|
|
35
62
|
def has_documentation?
|
36
|
-
|
37
|
-
doc = (node.text || "").strip
|
38
|
-
if !doc.empty?
|
39
|
-
return true
|
40
|
-
end
|
41
|
-
end
|
42
|
-
return false
|
63
|
+
!!description
|
43
64
|
end
|
44
65
|
|
45
66
|
def documentation
|
46
|
-
|
47
|
-
doc = (node.text || "").strip
|
48
|
-
if !doc.empty?
|
49
|
-
return doc
|
50
|
-
end
|
51
|
-
end
|
52
|
-
return short_documentation
|
67
|
+
description || short_documentation
|
53
68
|
end
|
54
69
|
|
55
70
|
def has_short_documentation?
|
56
|
-
|
57
|
-
doc = node.attributes['brief']
|
58
|
-
if doc
|
59
|
-
doc = doc.to_s.strip
|
60
|
-
end
|
61
|
-
if doc && !doc.empty?
|
62
|
-
return true
|
63
|
-
end
|
64
|
-
end
|
65
|
-
false
|
71
|
+
!!brief_description
|
66
72
|
end
|
67
73
|
|
68
74
|
def short_documentation
|
69
|
-
|
70
|
-
|
71
|
-
if doc
|
72
|
-
doc = doc.to_s.strip
|
73
|
-
end
|
74
|
-
if doc && !doc.empty?
|
75
|
-
return doc.to_s
|
76
|
-
end
|
77
|
-
end
|
78
|
-
"no documentation available for #{package.name} in its manifest.xml file"
|
75
|
+
brief_description ||
|
76
|
+
"no documentation available for package '#{package.name}' in its manifest.xml file"
|
79
77
|
end
|
80
78
|
|
81
|
-
def initialize(package,
|
79
|
+
def initialize(package, null: false)
|
82
80
|
@package = package
|
83
|
-
@
|
81
|
+
@dependencies = []
|
82
|
+
@authors = []
|
83
|
+
@maintainers = []
|
84
|
+
@rock_maintainers = []
|
85
|
+
@tags = []
|
86
|
+
@null = null
|
84
87
|
end
|
85
88
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
xml.elements.to_a('package/rosdep')
|
92
|
-
in_modes.each do |m|
|
93
|
-
depend_nodes += xml.elements.to_a("package/#{m}_depend")
|
94
|
-
end
|
95
|
-
|
96
|
-
depend_nodes.each do |node|
|
97
|
-
dependency = node.attributes['package'] || node.attributes['name']
|
98
|
-
optional = (node.attributes['optional'].to_s == '1' || node.name == "depend_optional")
|
99
|
-
modes = node.attributes['modes'].to_s.split(',')
|
100
|
-
if node.name =~ /^(\w+)_depend$/
|
101
|
-
modes << $1
|
102
|
-
end
|
103
|
-
if !modes.empty? && modes.none? { |m| in_modes.include?(m) }
|
104
|
-
next
|
105
|
-
end
|
89
|
+
# Whether this is a null manifest (used for packages that have actually
|
90
|
+
# no manifest) or not
|
91
|
+
def null?
|
92
|
+
!!@null
|
93
|
+
end
|
106
94
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
raise ConfigError.new, "manifest of #{package.name} has a <#{node.name}> tag without a 'package' attribute"
|
95
|
+
def each_dependency(in_modes = Array.new, &block)
|
96
|
+
return enum_for(__method__, in_modes) if !block_given?
|
97
|
+
dependencies.each do |dep|
|
98
|
+
if dep.modes.empty? || in_modes.any? { |m| dep.modes.include?(m) }
|
99
|
+
yield(dep.name, dep.optional)
|
113
100
|
end
|
114
101
|
end
|
115
|
-
|
116
|
-
package.os_packages.each do |name|
|
117
|
-
yield(name, false)
|
118
|
-
end
|
119
102
|
end
|
120
103
|
|
121
104
|
def each_os_dependency(modes = Array.new, &block)
|
122
|
-
Autoproj.
|
105
|
+
Autoproj.warn_deprecated "#{self.class}##{__method__}", "call #each_dependency instead"
|
123
106
|
return each_dependency(modes, &block)
|
124
107
|
end
|
125
108
|
|
126
109
|
def each_package_dependency(modes = Array.new, &block)
|
127
|
-
Autoproj.
|
110
|
+
Autoproj.warn_deprecated "#{self.class}##{__method__}", "call #each_dependency instead"
|
128
111
|
return each_dependency(modes, &block)
|
129
112
|
end
|
130
113
|
|
131
114
|
def each_rock_maintainer
|
132
115
|
return enum_for(__method__) if !block_given?
|
133
|
-
|
134
|
-
(
|
135
|
-
name, email = str.split('/').map(&:strip)
|
136
|
-
email = nil if email && email.empty?
|
137
|
-
yield(name, email)
|
138
|
-
end
|
116
|
+
rock_maintainers.each do |m|
|
117
|
+
yield(m.name, m.email)
|
139
118
|
end
|
140
119
|
end
|
141
120
|
|
142
121
|
def each_maintainer
|
143
122
|
return enum_for(__method__) if !block_given?
|
144
|
-
|
145
|
-
(
|
146
|
-
name, email = str.split('/').map(&:strip)
|
147
|
-
email = nil if email && email.empty?
|
148
|
-
yield(name, email)
|
149
|
-
end
|
123
|
+
maintainers.each do |m|
|
124
|
+
yield(m.name, m.email)
|
150
125
|
end
|
151
126
|
end
|
152
127
|
|
153
128
|
# Enumerates the name and email of each author. If no email is present,
|
154
129
|
# yields (name, nil)
|
155
130
|
def each_author
|
156
|
-
if !block_given?
|
157
|
-
|
131
|
+
return enum_for(__method__) if !block_given?
|
132
|
+
authors.each do |m|
|
133
|
+
yield(m.name, m.email)
|
158
134
|
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# @api private
|
138
|
+
#
|
139
|
+
# REXML stream parser object used to load the XML contents into a
|
140
|
+
# {PackageManifest} object
|
141
|
+
class Loader
|
142
|
+
include REXML::StreamListener
|
159
143
|
|
160
|
-
|
161
|
-
|
144
|
+
attr_reader :path, :manifest
|
145
|
+
|
146
|
+
def initialize(path, manifest)
|
147
|
+
@path = path
|
148
|
+
@manifest = manifest
|
149
|
+
end
|
150
|
+
|
151
|
+
def parse_depend_tag(tag_name, attributes, modes: [], optional: false)
|
152
|
+
package = attributes['package'] || attributes['name']
|
153
|
+
if !package
|
154
|
+
raise InvalidPackageManifest, "found '#{tag_name}' tag in #{path} without a 'package' attribute"
|
155
|
+
end
|
156
|
+
|
157
|
+
if tag_modes = attributes['modes']
|
158
|
+
modes += tag_modes.split(',')
|
159
|
+
end
|
160
|
+
|
161
|
+
manifest.add_dependency(
|
162
|
+
package,
|
163
|
+
optional: optional || (attributes['optional'] == '1'),
|
164
|
+
modes: modes)
|
165
|
+
end
|
166
|
+
|
167
|
+
def parse_contact_field(text)
|
168
|
+
text.strip.split(',').map do |str|
|
162
169
|
name, email = str.split('/').map(&:strip)
|
163
170
|
email = nil if email && email.empty?
|
164
|
-
|
171
|
+
ContactInfo.new(name, email)
|
165
172
|
end
|
166
173
|
end
|
167
|
-
end
|
168
174
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
175
|
+
TEXT_FIELDS = Set['url', 'license', 'version', 'description']
|
176
|
+
AUTHOR_FIELDS = Set['author', 'maintainer', 'rock_maintainer']
|
177
|
+
|
178
|
+
def tag_start(name, attributes)
|
179
|
+
if name == 'depend'
|
180
|
+
parse_depend_tag(name, attributes)
|
181
|
+
elsif name == 'depend_optional'
|
182
|
+
parse_depend_tag(name, attributes, optional: true)
|
183
|
+
elsif name == 'rosdep'
|
184
|
+
parse_depend_tag(name, attributes)
|
185
|
+
elsif name =~ /^(\w+)_depend$/
|
186
|
+
parse_depend_tag(name, attributes, modes: [$1])
|
187
|
+
elsif name == 'description'
|
188
|
+
if brief = attributes['brief']
|
189
|
+
manifest.brief_description = brief
|
190
|
+
end
|
191
|
+
@tag_text = ''
|
192
|
+
elsif TEXT_FIELDS.include?(name) || AUTHOR_FIELDS.include?(name)
|
193
|
+
@tag_text = ''
|
194
|
+
elsif name == 'tags'
|
195
|
+
@tag_text = ''
|
196
|
+
else
|
197
|
+
@tag_text = nil
|
177
198
|
end
|
178
199
|
end
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
#
|
198
|
-
# Returns 0 if none is declared
|
199
|
-
def version
|
200
|
-
return text_node("version")
|
200
|
+
def text(text)
|
201
|
+
if @tag_text
|
202
|
+
@tag_text << text
|
203
|
+
end
|
204
|
+
end
|
205
|
+
def tag_end(name)
|
206
|
+
if AUTHOR_FIELDS.include?(name)
|
207
|
+
manifest.send("#{name}s").concat(parse_contact_field(@tag_text))
|
208
|
+
elsif TEXT_FIELDS.include?(name)
|
209
|
+
field = @tag_text.strip
|
210
|
+
if !field.empty?
|
211
|
+
manifest.send("#{name}=", field)
|
212
|
+
end
|
213
|
+
elsif name == 'tags'
|
214
|
+
manifest.tags.concat(@tag_text.strip.split(',').map(&:strip))
|
215
|
+
end
|
216
|
+
@tag_text = nil
|
217
|
+
end
|
201
218
|
end
|
202
219
|
end
|
203
220
|
end
|