require 'pathname' require 'autobuild/timestamps' require 'autobuild/environment' require 'autobuild/package' require 'autobuild/subcommand' require 'shellwords' module Autobuild def self.autotools(opts, &proc) Autotools.new(opts, &proc) end # # ==== Handles autotools-based packages # # == Used programs (see Autobuild.programs) # Autotools will use the 'aclocal', 'autoheader', 'autoconf' and 'automake' # programs defined on Autobuild.programs. autoheader is disabled by default, # aclocal, autoconf and automake use are autodetected. # # To override this default behaviour on a per-package basis, use Autotools#use # class Autotools < Package attr_accessor :using attr_accessor :configureflags class << self attr_reader :builddir def builddir=(new) raise ConfigException, "absolute builddirs are not supported" if (Pathname.new(new).absolute?) raise ConfigException, "builddir must be non-nil and non-empty" if (new.nil? || new.empty?) @builddir = new end end @builddir = 'build' def builddir=(new) raise ConfigException, "absolute builddirs are not supported" if (Pathname.new(new).absolute?) raise ConfigException, "builddir must be non-empty" if new.empty? @builddir = new end # Returns the absolute builddir def builddir; File.expand_path(@builddir || Autotools.builddir, srcdir) end # Build stamp # This returns the name of the file which marks when the package has been # successfully built for the last time. The path is absolute def buildstamp; "#{builddir}/#{name}-#{STAMPFILE}" end def initialize(options) @using = Hash.new @configureflags = [] super Autobuild.update_environment(prefix) end # Overrides the default behaviour w.r.t. autotools script generation # # Use it like that: # * to force a generation step (skipping autodetection), do # pkg.use => true # For instance, for aclocal # pkg.use :aclocal => true # # * to force a generation step, overriding the program defined on Autobuild # pkg.use => true # For instance, for autoconf # pkg.use :autoconf => 'my_autoconf_program' # # * to disable a generation step, do # pkg.use => false # For instance, for automake # pkg.use :automake => false # # * to restore autodetection, do # pkg.use => nil # For instance, for automake # pkg.use :automake => nil # def use(*programs) programs = *programs if !programs.kind_of?(Hash) programs = Array[*programs].inject({}) do |programs, spec| programs[spec.first] = spec.last programs end end programs.each do |name, opt| using[name.to_sym] = opt end nil end def depends_on(*packages) super stamps = packages.collect { |p| Package[p.to_s].installstamp } #file "#{builddir}/config.status" => stamps file buildstamp => stamps end def ensure_dependencies_installed dependencies.each do |pkg| Rake::Task[Package[pkg].installstamp].invoke end end def prepare configureflags.flatten! # Check if config.status has been generated with the # same options than the ones in configureflags config_status = "#{builddir}/config.status" force_reconfigure = false if File.exists?(config_status) output = IO.popen("#{config_status} --version").readlines.grep(/with options/).first.chomp raise "invalid output of config.status --version" unless output =~ /with options "(.*)"$/ options = Shellwords.shellwords($1) # Add the --prefix option to the configureflags array testflags = ["--prefix=#{prefix}"] + Array[*configureflags] old_opt = options.find { |o| !testflags.include?(o) } new_opt = testflags.find { |o| !options.include?(o) } if old_opt || new_opt File.rm_f config_status # to force reconfiguration end end file config_status => regen do ensure_dependencies_installed configure end source_tree srcdir, /^#{Regexp.quote(builddir)}/ file buildstamp => [ srcdir, "#{builddir}/config.status" ] do ensure_dependencies_installed build end file installstamp => buildstamp do install Autobuild.update_environment(prefix) end end private # Adds a target to rebuild the autotools environment def regen(confsource = nil) conffile = "#{srcdir}/configure" if confsource file conffile => confsource elsif confext = %w{.ac .in}.find { |ext| File.exists?("#{conffile}#{ext}") } file conffile => "#{conffile}#{confext}" else raise PackageException.new(name), "neither configure.ac nor configure.in present in #{srcdir}" end file conffile do Dir.chdir(srcdir) { if using[:autogen].nil? using[:autogen] = %w{autogen autogen.sh}.find { |f| File.exists?(f) } end if using[:autogen] Subprocess.run(name, 'configure', File.expand_path(using[:autogen])) else # Autodetect autoconf/aclocal/automake # # Let the user disable the use of autoconf explicitely by using 'false'. # 'nil' means autodetection using[:autoconf] = true if using[:autoconf].nil? using[:aclocal] = using[:autoconf] if using[:aclocal].nil? if using[:automake].nil? using[:automake] = File.exists?(File.join(srcdir, 'Makefile.am')) end [ :aclocal, :autoconf, :autoheader, :automake ].each do |tool| if tool_flag = using[tool] tool_program = if tool_flag.respond_to?(:to_str) tool_flag.to_str else; Autobuild.tool(tool) end Subprocess.run(name, 'configure', tool_program) end end end } end return conffile end # Configure the builddir directory before starting make def configure if File.exists?(builddir) && !File.directory?(builddir) raise ConfigException, "#{builddir} already exists but is not a directory" end FileUtils.mkdir_p builddir if !File.directory?(builddir) Dir.chdir(builddir) { command = [ "#{srcdir}/configure", "--no-create", "--prefix=#{prefix}" ] command += Array[*configureflags] Subprocess.run(name, 'configure', *command) } end # Do the build in builddir def build Dir.chdir(builddir) { Subprocess.run(name, 'build', './config.status') Subprocess.run(name, 'build', Autobuild.tool(:make)) } touch_stamp(buildstamp) end # Install the result in prefix def install Dir.chdir(builddir) { Subprocess.run(name, 'install', Autobuild.tool(:make), 'install') } touch_stamp(installstamp) end end end