class Autobuild::Package
Basic block for the autobuilder
The build is done in three phases:
- import - prepare - build & install
In the first stage checks the source out and/or updates it.
In the second stage, packages create their dependency structure to handle specific build systems. For instance, it is there that build systems like CMake
are handled so that reconfiguration happens if needed. In the same way, it is there that code generation will happen as well.
Finally, the build stage actually calls the package’s build targets (of the form “package_name-build”, which will trigger the build if needed.
Constants
- EnvOp
Attributes
The list of packages this one depends upon
List of environment values added by this package with {#env_add}, {#env_add_path} or {#env_set}
@return [Array<EnvOp>]
If something failed on this package, returns the corresponding exception object. Otherwise, returns nil
set the importdir, this can be different than the sourcedir if the source-root is in an subfolder of the package itself then the importdir will be the root
Sets an importer object for this package
Sets the log directory. If no value is set, the package will use Autobuild.logdir
the package name
set the installation directory. If a relative path is given, it is relative to Autobuild.prefix. Defaults to ”
set the source directory. If a relative path is given, it is relative to Autobuild.srcdir. Defaults to name
Some statistics about the commands that have been run
Sets whether this package should update itself or not. If false, the only importer operation that will be performed is checkout
If nil, the global setting Autobuild.do_update is used
The set of utilities attached to this package @return [{String=>Utility}]
Public Class Methods
Source
# File lib/autobuild/package.rb, line 793 def self.[](name) @@packages[name.to_s] || @@provides[name.to_s] end
Gets a package from its name
Source
# File lib/autobuild/package.rb, line 798 def self.clear @@packages.clear @@provides.clear end
Removes all package definitions
Source
# File lib/autobuild/package.rb, line 785 def self.each(with_provides = false, &block) return enum_for(:each, with_provides) unless block @@packages.each(&block) @@provides.each(&block) if with_provides end
Iterates on all available packages if with_provides is true, includes the list of package aliases
Source
# File lib/autobuild/package.rb, line 136 def initialize(spec = Hash.new) @srcdir = @importdir = @logdir = @prefix = nil @updated = false @update = nil @failed = nil @dependencies = Array.new @provides = Array.new @statistics = Hash.new @parallel_build_level = nil @failures = Array.new @post_install_blocks = Array.new @applied_post_install = false @in_dir_stack = Array.new @utilities = Hash.new @env = Array.new @imported = false @prepared = false @built = false @disabled = nil if Hash === spec name, depends = spec.to_a.first else name = spec depends = nil end name = name.to_s @name = name if Autobuild::Package[name] raise ConfigException, "package #{name} is already defined" end @@packages[name] = self # Call the config block (if any) yield(self) if block_given? doc_utility.source_dir ||= 'doc' doc_utility.target_dir ||= name # Define the default tasks task "#{name}-import" do isolate_errors { import } @imported = true end task :import => "#{name}-import" # Define the prepare task task "#{name}-prepare" => "#{name}-import" do isolate_errors { prepare } @prepared = true end task :prepare => "#{name}-prepare" task "#{name}-build" task :build => "#{name}-build" task(name) do Rake::Task["#{name}-import"].invoke Rake::Task["#{name}-prepare"].invoke Rake::Task["#{name}-build"].invoke Rake::Task["#{name}-doc"].invoke if has_doc? && Autobuild.do_doc end task :default => name # The dependencies will be declared in the import phase, so save # them there for now @spec_dependencies = depends end
Public Instance Methods
Source
# File lib/autobuild/package.rb, line 243 def add_env_op(envop) env << envop end
@api private
Adds a new operation to this package’s environment setup. This is a helper for the other env_* methods
@param [EnvOp] op @return [void]
Source
# File lib/autobuild/package.rb, line 75 def add_stat(phase, duration) @statistics[phase] ||= 0 @statistics[phase] += duration end
Source
# File lib/autobuild/package.rb, line 712 def all_dependencies(result = Set.new) dependencies.each do |pkg_name| pkg = Autobuild::Package[pkg_name] unless result.include?(pkg.name) result << pkg.name pkg.all_dependencies(result) end end result end
Returns the name of all the packages self
depends on
Source
# File lib/autobuild/package.rb, line 48 def applied_post_install? @applied_post_install end
Whether {#apply_post_install} has been called
Source
# File lib/autobuild/package.rb, line 319 def apply_env(env, set = Hash.new, ops = Array.new) self.env.each do |env_op| next if ops.last == env_op if env_op.type == :set if (last = set[env_op.name]) last_pkg, last_values = *last if last_values != env_op.values raise IncompatibleEnvironment, "trying to reset "\ "#{env_op.name} to #{env_op.values} in #{name} "\ "but this conflicts with #{last_pkg.name} "\ "already setting it to #{last_values}" end else set[env_op.name] = [self, env_op.values] end end if env_op.type == :source_after env.send(env_op.type, env_op.name, **env_op.values) else env.send(env_op.type, env_op.name, *env_op.values) end ops << env_op end ops end
@api private
Apply this package’s environment to the given {Environment} object
It does not apply the dependencies’ environment. Call {#resolved_env} for that.
@param [Environment] env the environment to be updated @param [Set] set a set of environment variable names which have
already been set by a {#env_set}. Autoproj will verify that only one package sets a variable as to avoid unexpected conflicts.
@return [Array<EnvOp>] list of environment-modifying operations
applied so far
Source
# File lib/autobuild/package.rb, line 555 def apply_post_install Autobuild.post_install_handlers.each do |b| Autobuild.apply_post_install(self, b) end @post_install_blocks.each do |b| Autobuild.apply_post_install(self, b) end @applied_post_install = true end
Source
# File lib/autobuild/package.rb, line 208 def checked_out? File.directory?(srcdir) end
Whether the package’s source directory is present on disk
Source
# File lib/autobuild/package.rb, line 732 def depends_on(*packages) packages.each do |p| p = p.name if p.respond_to?(:name) unless p.respond_to?(:to_str) raise ArgumentError, "#{p.inspect} should be a string" end p = p.to_str next if p == name unless (pkg = Package[p]) raise ConfigException.new(self), "package #{p}, "\ "listed as a dependency of #{name}, is not defined" end next if @dependencies.include?(pkg.name) Autobuild.message "#{name} depends on #{pkg.name}" if Autobuild.verbose task "#{name}-import" => "#{pkg.name}-import" task "#{name}-prepare" => "#{pkg.name}-prepare" task "#{name}-build" => "#{pkg.name}-build" @dependencies << pkg.name end end
This package depends on packages
. It means that its build will always be triggered after the packages listed in packages
are built and installed.
Source
# File lib/autobuild/package.rb, line 725 def depends_on?(package_name) @dependencies.include?(package_name) end
Returns true if this package depends on package_name
and false otherwise.
Source
# File lib/autobuild/package.rb, line 853 def disable(_phases = Autobuild.all_phases) @disabled = true end
Make sure that this package will be ignored in the build
Source
# File lib/autobuild/package.rb, line 652 def disable_doc doc_utility.enabled = false end
Source
# File lib/autobuild/package.rb, line 844 def disable_phases(*phases) phases.each do |phase| task "#{name}-#{phase}" t = Rake::Task["#{name}-#{phase}"] t.disable end end
Makes sure that the specified phases of this package will be no-ops
Source
# File lib/autobuild/package.rb, line 624 def doc_dir=(value) doc_utility.source_dir = value end
Source
# File lib/autobuild/package.rb, line 660 def doc_disabled doc_utility.disabled end
Source
# File lib/autobuild/package.rb, line 636 def doc_target_dir doc_utility.target_dir end
Source
# File lib/autobuild/package.rb, line 632 def doc_target_dir=(value) doc_utility.target_dir = value end
Source
# File lib/autobuild/package.rb, line 640 def doc_task(&block) doc_utility.task(&block) end
Source
# File lib/autobuild/package.rb, line 648 def enable_doc doc_utility.enabled = true end
Source
# File lib/autobuild/package.rb, line 255 def env_add(name, *values) add_env_op EnvOp.new(:add, name, values) end
Add value(s) to a list-based environment variable
This differs from {#env_add_path} in that a value can be added multiple times in the list.
@param [String] name the environment variable name @param [Array<String>] values list of values to be added @return [void]
Source
# File lib/autobuild/package.rb, line 269 def env_add_path(name, *values) add_env_op EnvOp.new(:add_path, name, values) end
Add a new path to a PATH-like environment variable
It differs from {#env_add} in its handling of duplicate values. Any value already existing will be removed, and re-appended to the value so that it takes priority.
@param [String] name the environment variable name @param [Array<String>] values list of values. They will be joined
using the platform's standard separator (e.g. : on Unices)
@return [void]
Source
# File lib/autobuild/package.rb, line 287 def env_add_prefix(prefix, includes = nil) add_env_op EnvOp.new(:add_prefix, prefix, [includes]) end
Add a prefix to be resolved into the environment
Autoproj will update all “standard” environment variables based on what it finds as subdirectories from the prefix
Source
# File lib/autobuild/package.rb, line 279 def env_set(name, *values) add_env_op EnvOp.new(:set, name, values) end
Set an environment variable to a list of values
@param [String] name the environment variable name @param [Array<String>] values list of values. They will be joined
using the platform's standard separator (e.g. : on Unices)
@return [void]
Source
# File lib/autobuild/package.rb, line 292 def env_source_after(file, shell: "sh") add_env_op EnvOp.new(:source_after, file, shell: shell) end
Add a file to be sourced at the end of the generated env file
Source
# File lib/autobuild/package.rb, line 527 def error(error_string) message(" ERROR: #{error_string}", :red, :bold) end
Display a progress message. %s in the string is replaced by the package name
Source
# File lib/autobuild/package.rb, line 398 def failed? @failed end
Returns true if one of the operations applied on this package failed
Source
# File lib/autobuild/package.rb, line 609 def file(*args, &block) task = super task.extend TaskExtension task.package = self task end
Calls Rake
to define a file task and then extends it with TaskExtension
Source
# File lib/autobuild/package.rb, line 369 def find_in_path(file, envvar = 'PATH') full_env.find_in_path(file, envvar) end
Find a file in a path-like environment variable
Source
# File lib/autobuild/package.rb, line 686 def fingerprint(recursive: true, memo: {}) return memo[name] if memo.key?(name) self_fingerprint = self.self_fingerprint return unless self_fingerprint if dependencies.empty? return (memo[name] = self_fingerprint) elsif !recursive return self_fingerprint end dependency_fingerprints = dependencies.sort.map do |pkg_name| pkg = Autobuild::Package[pkg_name] unless (fingerprint = memo[pkg.name]) fingerprint = pkg.fingerprint(recursive: true, memo: memo) break unless fingerprint end fingerprint end return unless dependency_fingerprints memo[name] = Digest::SHA1.hexdigest( self_fingerprint + dependency_fingerprints.join("")) end
Returns a unique hash representing a state of the package and its dependencies, if any dependency can’t calculate its own fingerprint the result will be nil @return [String]
Source
# File lib/autobuild/package.rb, line 359 def full_env(root = Autobuild.env) set = Hash.new env = root.dup ops = Array.new ops = resolve_dependency_env(env, set, ops) apply_env(env, set, ops) env end
This package’s environment
Source
# File lib/autobuild/package.rb, line 644 def generates_doc? doc_utility.enabled? end
Source
# File lib/autobuild/package.rb, line 462 def import(*old_boolean, **options) options = { only_local: old_boolean.first } unless old_boolean.empty? @import_invoked = true if @importer result = @importer.import(self, **options) elsif update? message "%s: no importer defined, doing nothing" end @imported = true # Add the dependencies declared in spec depends_on(*@spec_dependencies) if @spec_dependencies result end
Call the importer if there is one. Autodetection of “provides” should be done there as well.
(see Importer#import
)
Source
# File lib/autobuild/package.rb, line 54 def import=(value) @importer = value end
Sets importer object for this package. Defined for backwards compatibility. Use the importer
attribute instead
Source
# File lib/autobuild/package.rb, line 212 def import_invoked? @import_invoked end
Source
# File lib/autobuild/package.rb, line 86 def importdir File.expand_path(@importdir || srcdir, Autobuild.srcdir) end
Absolute path to the import directory. See importdir=
Source
# File lib/autobuild/package.rb, line 832 def in_dir(directory) @in_dir_stack << directory yield ensure @in_dir_stack.pop end
Source
# File lib/autobuild/package.rb, line 566 def install apply_post_install # Safety net for forgotten progress_done progress_done Autobuild.touch_stamp(installstamp) @installed = true end
Install the result in prefix
Source
# File lib/autobuild/package.rb, line 656 def install_doc doc_utility.install end
Source
# File lib/autobuild/package.rb, line 220 def install_invoked? @install_invoked end
Source
# File lib/autobuild/package.rb, line 108 def installstamp File.join(logdir, "#{name}-#{STAMPFILE}") end
The file which marks when the last sucessful install has finished. The path is absolute
A package is sucessfully built when it is installed
Source
# File lib/autobuild/package.rb, line 413 def isolate_errors(options = Hash.new) options = Hash[mark_as_failed: true] unless options.kind_of?(Hash) options = validate_options options, mark_as_failed: true, ignore_errors: Autobuild.ignore_errors # Don't do anything if we already have failed if failed? unless options[:ignore_errors] raise AlreadyFailedError, "attempting to do an operation "\ "on a failed package" end return end begin toplevel = !Thread.current[:isolate_errors] Thread.current[:isolate_errors] = true yield rescue InteractionRequired raise rescue Interrupt raise rescue ::Exception => e @failures << e @failed = true if options[:mark_as_failed] if options[:ignore_errors] lines = e.to_s.split("\n") lines = e.message.split("\n") if lines.empty? lines = ["unknown error"] if lines.empty? message(lines.shift, :red, :bold) lines.each do |line| message(line) end nil else raise end ensure Thread.current[:isolate_errors] = false if toplevel end end
If Autobuild.ignore_errors
is set, an exception raised from within the provided block will be filtered out, only displaying a message instead of stopping the build
Moreover, the package will be marked as “failed” and isolate_errors
will subsequently be a noop. I.e. if build
fails, install
will do nothing.
Source
# File lib/autobuild/package.rb, line 96 def logdir if @logdir File.expand_path(@logdir, prefix) else Autobuild.logdir end end
Absolute path to the log directory for this package. See logdir=
Source
# File lib/autobuild/package.rb, line 533 def message(*args) args[0] = " #{process_formatting_string(args[0])}" unless args.empty? Autobuild.message(*args) end
Display a progress message. %s in the string is replaced by the package name
Source
# File lib/autobuild/package.rb, line 865 def method_missing(name, *args, &block) case name.to_s when /(\w+)_utility$/ utility_name = $1 unless args.empty? raise ArgumentError, "expected 0 arguments and got #{args.size}" end begin return utility(utility_name) rescue ArgumentError => e raise NoMethodError.new(name), e.message, e.backtrace end end super end
Source
# File lib/autobuild/package.rb, line 818 def parallel_build_level if @parallel_build_level.nil? Autobuild.parallel_build_level elsif !@parallel_build_level || @parallel_build_level <= 0 1 else @parallel_build_level end end
Returns the level of parallelism authorized during the build for this particular package. If not set, defaults to the system-wide option (Autobuild.parallel_build_level
and Autobuild.parallel_build_level=
).
The default value is the number of CPUs on this system.
Source
# File lib/autobuild/package.rb, line 809 def parallel_build_level=(value) @parallel_build_level = Integer(value) end
Sets the level of parallelism authorized while building this package
See parallel_build_level
and Autobuild.parallel_build_level
for more information.
Note that not all package types use this value
Source
# File lib/autobuild/package.rb, line 668 def post_install(*args, &block) if args.empty? @post_install_blocks << block elsif !block @post_install_blocks << args else raise ArgumentError, "cannot set both arguments and block" end end
Source
# File lib/autobuild/package.rb, line 91 def prefix File.expand_path(@prefix || '', Autobuild.prefix) end
Absolute path to the installation directory. See prefix=
Source
# File lib/autobuild/package.rb, line 481 def prepare super if defined? super stamps = dependencies.map { |p| Package[p].installstamp } file installstamp => stamps do isolate_errors do @install_invoked = true install end end task "#{name}-build" => installstamp @prepared = true end
Create all the dependencies required to reconfigure and/or rebuild the package when required. The package’s build target is called “package_name-build”.
Source
# File lib/autobuild/package.rb, line 385 def prepare_for_forced_build FileUtils.rm_f installstamp if File.exist?(installstamp) end
Called before a forced build. It should remove all the timestamp and target files so that all the build phases of this package gets retriggered. However, it should not clean the build products.
Source
# File lib/autobuild/package.rb, line 391 def prepare_for_rebuild prepare_for_forced_build FileUtils.rm_f installstamp if File.exist?(installstamp) end
Called when the user asked for a full rebuild. It should delete the build products so that a full build is retriggered.
Source
# File lib/autobuild/package.rb, line 497 def process_formatting_string(msg, *prefix_style) prefix = [] suffix = [] msg.split(" ").each do |token| if token =~ /%s/ suffix << token.gsub(/%s/, name) elsif suffix.empty? prefix << token else suffix << token end end if suffix.empty? msg elsif prefix_style.empty? (prefix + suffix).join(" ") else colorized_prefix = Autobuild.color(prefix.join(" "), *prefix_style) [colorized_prefix, *suffix].join(" ") end end
Source
# File lib/autobuild/package.rb, line 545 def progress(*args) args[0] = process_formatting_string(args[0], :bold) Autobuild.progress(self, *args) end
Source
# File lib/autobuild/package.rb, line 550 def progress_done(done_message = nil) done_message = process_formatting_string(done_message) if done_message Autobuild.progress_done(self, message: done_message) end
Source
# File lib/autobuild/package.rb, line 538 def progress_start(*args, done_message: nil, **raw_options, &block) args[0] = process_formatting_string(args[0], :bold) done_message = process_formatting_string(done_message) if done_message Autobuild.progress_start(self, *args, done_message: done_message, **raw_options, &block) end
Source
# File lib/autobuild/package.rb, line 760 def provides(*packages) packages.each do |p| unless p.respond_to?(:to_str) raise ArgumentError, "#{p.inspect} should be a string" end p = p.to_str next if p == name next if @provides.include?(name) @@provides[p] = self Autobuild.message "#{name} provides #{p}" if Autobuild.verbose task p => name task "#{p}-import" => "#{name}-import" task "#{p}-prepare" => "#{name}-prepare" task "#{p}-build" => "#{name}-build" @provides << p end end
Declare that this package provides packages
. In effect, the names listed in packages
are aliases for this package.
Source
# File lib/autobuild/package.rb, line 350 def resolve_dependency_env(env, set, ops) all_dependencies.each do |pkg_name| pkg = Autobuild::Package[pkg_name] ops = pkg.apply_env(env, set, ops) end ops end
@api private
Updates an {Environment} object with the environment of the package’s dependencies
Source
# File lib/autobuild/package.rb, line 378 def resolved_env(root = Autobuild.env) full_env(root).resolved_env end
Resolves this package’s environment into Hash form
@param [Environment] root the base environment object to update @return [Hash<String,String>] the full environment @see Autobuild::Environment#resolved_env
Source
# File lib/autobuild/package.rb, line 861 def respond_to_missing?(name, _include_all) utilities.key?(name.to_s) end
Source
# File lib/autobuild/package.rb, line 577 def run(*args, &block) options = if args.last.kind_of?(Hash) args.pop else Hash.new end options[:env] = options.delete(:resolved_env) || (options[:env] || Hash.new).merge(resolved_env) Autobuild::Subprocess.run(self, *args, options, &block) end
Source
# File lib/autobuild/package.rb, line 678 def self_fingerprint importer.fingerprint(self) end
Source
# File lib/autobuild/package.rb, line 601 def source_tree(*args, &block) task = Autobuild.source_tree(*args, &block) task.extend TaskExtension task.package = self task end
Source
# File lib/autobuild/package.rb, line 81 def srcdir File.expand_path(@srcdir || name, Autobuild.srcdir) end
Absolute path to the source directory. See srcdir=
Source
# File lib/autobuild/package.rb, line 617 def task(*args, &block) task = super task.extend TaskExtension task.package = self task end
Calls Rake
to define a plain task and then extends it with TaskExtension
Source
# File lib/autobuild/package.rb, line 228 def to_s "#<#{self.class} name=#{name}>" end
Source
# File lib/autobuild/package.rb, line 119 def update? if @update.nil? Autobuild.do_update else @update end end
True if this package should update itself when import
is called
Source
# File lib/autobuild/package.rb, line 300 def update_environment env_add_prefix prefix end
Hook called by autoproj to set up the default environment for this package
By default, it calls {#env_add_prefix} with this package’s prefix
Source
# File lib/autobuild/package.rb, line 132 def updated? @updated end
Returns true if this package has already been updated. It will not be true if the importer has been called while Autobuild.do_update was false.
Source
# File lib/autobuild/package.rb, line 857 def utility(utility_name) utilities[utility_name.to_s] ||= Autobuild.create_utility(utility_name, self) end
Source
# File lib/autobuild/package.rb, line 521 def warn(warning_string) message(" WARN: #{warning_string}", :magenta) end
Display a progress message. %s in the string is replaced by the package name
Source
# File lib/autobuild/package.rb, line 828 def working_directory @in_dir_stack.last end