require "colorize" require "specparser" require "./exception.cr" require "./config.cr" require "./backends.cr" class Package::Context property working_directory = "/tmp/packaging" property sources_directory = Dir.current property packages_directory = Dir.current #alias Backends = Backend::Packaging | Backend::Building | Backend::Splitter property user_instructions = Hash(String, Instructions).new property configure_backends = Hash(String, Backend::Building).new property building_backends = Hash(String, Backend::Building).new property install_backends = Hash(String, Backend::Building).new getter packaging_backends = Hash(String, Backend::Packaging).new getter splitter_backends = Hash(String, Backend::Splitter).new # General options, to add to the recipe before reading it. getter options = Hash(String, String).new getter all_phase_names = [ "sources-split", "pre-configure", "configure", "pre-build", "build", "pre-install", "install", "post-install", ] # Directories where ports can be found. getter repositories = [] of String # Well, this will need configuration, auto-detection and conversion, # but it’ll be kind of enough for now. property architecture = "x86_64" # prefixes for `packaging` running environment and child processes # = where to search for binaries and libraries for the build property prefixes = ["/usr/baguette", "/usr", "/"] # By default, building a package only uses one core property build_cores = 1 property recipe : Recipe? = nil def initialize # Add package backends: baguette (package-tools) and apk. # They implement the Backend::Packaging abstract class: # init (@name) + package (context, package) method @packaging_backends["baguette"] = Backend::Packaging::Baguette.new @packaging_backends["apk"] = Backend::Packaging::APK.new @packaging_backends["pkgutils"] = Backend::Packaging::Pkgutils.new @selected_packaging_backend = @packaging_backends["baguette"] # Add building backends: configuration, build and install. @configure_backends["autotools"] = Backend::Configure.autotools @configure_backends["cmake"] = Backend::Configure.cmake @building_backends["make"] = Backend::Build.make @install_backends["make"] = Backend::Install.make # Split = new package from a recipe, each one with different files being included. @splitter_backends["man"] = Backend::Splitter.man @prefixes @splitter_backends["dev"] = Backend::Splitter.dev @prefixes @splitter_backends["doc"] = Backend::Splitter.doc @prefixes # src isn't really a split, files already are copied before build. #@splitter_backends << Backend::Splitter.src @prefixes end def packaging_backend=(name : String) @selected_packaging_backend = @packaging_backends[name].not_nil! end def packaging_backend=(backend : Backend::Packaging) @selected_packaging_backend = backend end def package(package : Package) : Bool @selected_packaging_backend.package @packages_directory, @architecture, package end def read_recipe(filename : String) @recipe = Recipe.new self, filename @recipe.not_nil! end def find_recipe(name : String) : Recipe? recipe_file_name = "" repo = @repositories.find do |repo| repo_dir_name = "#{repo}/#{name}" recipe_file_name = "#{repo_dir_name}/recipe.spec" if Dir.exists?(repo_dir_name) && File.exists?(recipe_file_name) next true end end read_recipe recipe_file_name if repo end def read_configuration(filename : String) specs = SpecParser.parse(File.read(filename)) specs.assignments.each do |key, value| case key when "packages-directory" @packages_directory = value.as_s when "sources-directory" @sources_directory = value.as_s when "working-directory" @working_directory = value.as_s when "prefixes" # Prefixes during the build process. @prefixes = value.as_a_or_s when "build-cores" # NB of cores used to compile applications. @build_cores = value.as_s.to_i when "environment" # Environment variables during the build process. value.as_a_or_s.each do |entry| match = entry.split(':').map( &.gsub(/^[ \t]*/, "").gsub(/[ \t]*$/, "")) if match.size != 2 STDERR.puts "WARNING: misformed environment definition: #{entry}" next end key, value = match Do.environment[key] = value if Baguette::Context.verbosity > 2 STDOUT.puts "environment: #{key} => #{value}" end end when "options" value.as_a_or_s.each do |option| match = option.split(':').map(&.strip) if match.size != 2 STDERR.puts "WARNING: misformed option: #{option}" next end key, value = match @options[key] = value end # (default) user instructions (for testing). when /(?(pre-)?(configure|build|install)|post-install)/ phase = $~["phase"] @user_instructions[phase] = Instructions.new(phase).<<(value.as_s_or_ls) when "package-manager" # Targeted package manager (default: package, for BaguetteOS). begin self.packaging_backend = value.as_s rescue e STDERR.puts "Error during selecting packaging backend: #{e}" STDERR.puts "#{value.as_s} seems not to be a valid backend" STDERR.puts "valid backends:" @packaging_backends.each do |name, backend| STDERR.puts "- #{name}" end exit 1 end end end end def install(packages : Array(String)) @selected_packaging_backend.install packages end end