module Autoproj
# Access to the information contained in a package's manifest.xml file
#
# Use PackageManifest.load to create
class PackageManifest
# Load a manifest.xml file and returns the corresponding
# PackageManifest object
def self.load(package, file)
doc =
begin REXML::Document.new(File.read(file))
rescue REXML::ParseException => e
raise Autobuild::PackageException.new(package.name, 'prepare'), "invalid #{file}: #{e.message}"
end
PackageManifest.new(package, doc)
end
# The Autobuild::Package instance this manifest applies on
attr_reader :package
# The raw XML data as a Nokogiri document
attr_reader :xml
# The list of tags defined for this package
#
# Tags are defined as multiple blocks, each of which can
# contain multiple comma-separated tags
def tags
result = []
xml.elements.each('package/tags') do |node|
result.concat((node.text || "").strip.split(','))
end
result
end
def documentation
xml.elements.each('package/description') do |node|
doc = (node.text || "").strip
if !doc.empty?
return doc
end
end
return short_documentation
end
def short_documentation
xml.elements.each('package/description') do |node|
doc = node.attributes['brief']
if doc
doc = doc.to_s.strip
end
if doc && !doc.empty?
return doc.to_s
end
end
"no documentation available for #{package.name} in its manifest.xml file"
end
def initialize(package, doc = REXML::Document.new)
@package = package
@xml = doc
end
def each_dependency(&block)
if block_given?
each_os_dependency(&block)
each_package_dependency(&block)
else
enum_for(:each_dependency, &block)
end
end
def each_os_dependency
if block_given?
xml.elements.each('package/rosdep') do |node|
yield(node.attributes['name'], false)
end
package.os_packages.each do |name|
yield(name, false)
end
else
enum_for :each_os_dependency
end
end
def each_package_dependency
if block_given?
depend_nodes = xml.elements.to_a('package/depend') +
xml.elements.to_a('package/depend_optional')
depend_nodes.each do |node|
dependency = node.attributes['package']
optional = (node.attributes['optional'].to_s == '1' || node.name == "depend_optional")
if dependency
yield(dependency, optional)
else
raise ConfigError.new, "manifest of #{package.name} has a tag without a 'package' attribute"
end
end
else
enum_for :each_package_dependency
end
end
# Enumerates the name and email of each author. If no email is present,
# yields (name, nil)
def each_author
if !block_given?
return enum_for(:each_author)
end
xml.elements.each('package/author') do |author|
(author.text || "").strip.split(',').each do |str|
name, email = str.split('/').map(&:strip)
email = nil if email && email.empty?
yield(name, email)
end
end
end
# If +name+ points to a text element in the XML document, returns the
# content of that element. If no element matches +name+, or if the
# content is empty, returns nil
def text_node(name)
xml.elements.each(name) do |str|
str = (str.text || "").strip
if !str.empty?
return str
end
end
nil
end
# The package associated URL, usually meant to direct to a website
#
# Returns nil if there is none
def url
return text_node('package/url')
end
# The package license name
#
# Returns nil if there is none
def license
return text_node('package/license')
end
# The package version number
#
# Returns 0 if none is declared
def version
return text_node("version")
end
end
end