185 lines
5.4 KiB
Crystal
185 lines
5.4 KiB
Crystal
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 /(?<phase>(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
|
||
|