New package-create version. See more in git log.

This new version has a new code structure:
- context now has (configure|building|install)_backends attributes
- Package::Instructions now only is a wrapper around Array(String)
  with a phase name and a 'run' method
- new phases, with total user control through recipes:
  source-split, (pre-)?(configure|build|install) and post-install
- Do.cp

Misc:
- baguette backend removes the data.tar before processing the manifest
- new '-doc' split
- check for `bsdtar` in the PATH before doing anything
- logs are way more verbose
master
Philippe Pittoli 2021-03-09 17:51:51 +01:00
parent e218282594
commit c8ff92b508
8 changed files with 180 additions and 130 deletions

View File

@ -37,4 +37,22 @@ abstract class Package::Backend::Packaging
end end
end end
# Package::Backend::Splitter = create new package from a recipe
# takes (then stores) the given block
# this block takes a recipe as a parameter and create a new package
# the new package:
# keep prefixes
# new name = split name (-man, -src, ...)
# split files
class Package::Backend::Splitter
def initialize(&block : Proc(Recipe, Package))
@callback = block
end
def create_split(recipe : Recipe) : Package
@callback.call recipe
end
end
require "./backends/*" require "./backends/*"

View File

@ -40,6 +40,9 @@ class Package::Backend::Packaging::Baguette < Package::Backend::Packaging
# produces data.tar.zst # produces data.tar.zst
Do.run fake_root, "zstd", ["--ultra", data_archive_path] Do.run fake_root, "zstd", ["--ultra", data_archive_path]
::Baguette::Log.detail "Removing the uncompressed archive"
Do.run fake_root, "rm", [data_archive_path]
::Baguette::Log.detail "Generating control.spec" ::Baguette::Log.detail "Generating control.spec"
generate_spec package, control_spec_file_path generate_spec package, control_spec_file_path

View File

@ -1,20 +1,4 @@
class Package::Backend::Splitter class Package::Backend::Splitter
def initialize(&block : Proc(Recipe, Package))
@callback = block
end
def create_split(recipe : Recipe) : Package
@callback.call recipe
end
# Package::Backend::Splitter = create new package
# takes (then stores) the given block
# this block takes a recipe as a parameter and create a new package
# the new package:
# keep prefixes
# new name = split name (-man, -src, ...)
# split files
# Man-pages and documentation # Man-pages and documentation
def self.man(prefixes : Array(String)) : Splitter def self.man(prefixes : Array(String)) : Splitter
@ -53,6 +37,26 @@ class Package::Backend::Splitter
end end
end end
# Documentation files: /share/doc/, *.html
def self.doc(prefixes : Array(String)) : Splitter
Backend::Splitter.new do |recipe|
Package.new(recipe, true).tap do |split|
prefixes = (prefixes + [recipe.prefix]).uniq
split.name = "#{recipe.name}-doc"
split.files = prefixes.map do |prefix|
[
"#{prefix}/share/doc",
]
end.flatten
split.file_patterns = prefixes.map do |prefix|
Regex.new("^" + prefix + ".*\\.html$")
end
end
end
end
# NOTE: src is not a regular split, _all_ files should be included, # NOTE: src is not a regular split, _all_ files should be included,
# as well as the patches used for the build. # as well as the patches used for the build.
# Files are copied before build. # Files are copied before build.

View File

@ -10,9 +10,22 @@ class Package::Context
property sources_directory = Dir.current property sources_directory = Dir.current
property packages_directory = Dir.current property packages_directory = Dir.current
getter packaging_backends = [] of Backend::Packaging #alias Backends = Backend::Packaging | Backend::Building | Backend::Splitter
getter building_backends = [] of Backend::Building property user_instructions = Hash(String, Instructions).new
getter splitter_backends = [] of Backend::Splitter
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
getter all_phase_names = [
"sources-split",
"pre-configure", "configure",
"pre-build", "build",
"pre-install", "install", "post-install",
]
# Directories where ports can be found. # Directories where ports can be found.
getter repositories = [] of String getter repositories = [] of String
@ -35,27 +48,28 @@ class Package::Context
# Add package backends: baguette (package-tools) and apk. # Add package backends: baguette (package-tools) and apk.
# They implement the Backend::Packaging abstract class: # They implement the Backend::Packaging abstract class:
# init (@name) + package (context, package) method # init (@name) + package (context, package) method
@packaging_backends << Backend::Packaging::Baguette.new @packaging_backends["baguette"] = Backend::Packaging::Baguette.new
@packaging_backends << Backend::Packaging::APK.new @packaging_backends["apk"] = Backend::Packaging::APK.new
@packaging_backends << Backend::Packaging::Pkgutils.new @packaging_backends["pkgutils"] = Backend::Packaging::Pkgutils.new
@selected_packaging_backend = @packaging_backends[0] @selected_packaging_backend = @packaging_backends["baguette"]
# Add building backends: configuration, build and install. # Add building backends: configuration, build and install.
@building_backends << Backend::Configure.autotools @configure_backends["autotools"] = Backend::Configure.autotools
@building_backends << Backend::Configure.cmake @configure_backends["cmake"] = Backend::Configure.cmake
@building_backends << Backend::Build.make @building_backends["make"] = Backend::Build.make
@building_backends << Backend::Install.make @install_backends["make"] = Backend::Install.make
# Split = new package from a recipe, each one with different files being included. # Split = new package from a recipe, each one with different files being included.
@splitter_backends << Backend::Splitter.man @prefixes @splitter_backends["man"] = Backend::Splitter.man @prefixes
@splitter_backends << Backend::Splitter.dev @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. # src isn't really a split, files already are copied before build.
@splitter_backends << Backend::Splitter.src @prefixes #@splitter_backends << Backend::Splitter.src @prefixes
end end
def packaging_backend=(name : String) def packaging_backend=(name : String)
@selected_packaging_backend = @packaging_backends.find(&.name.==(name)).not_nil! @selected_packaging_backend = @packaging_backends[name].not_nil!
end end
def packaging_backend=(backend : Backend::Packaging) def packaging_backend=(backend : Backend::Packaging)
@ -130,8 +144,8 @@ class Package::Context
STDERR.puts "Error during selecting packaging backend: #{e}" STDERR.puts "Error during selecting packaging backend: #{e}"
STDERR.puts "#{value.as_s} seems not to be a valid backend" STDERR.puts "#{value.as_s} seems not to be a valid backend"
STDERR.puts "valid backends:" STDERR.puts "valid backends:"
@packaging_backends.each do |backend| @packaging_backends.each do |name, backend|
STDERR.puts "- #{backend.name}" STDERR.puts "- #{name}"
end end
exit 1 exit 1
end end

View File

@ -20,6 +20,13 @@ class Do < Process
Dir.cd chdir Dir.cd chdir
end end
def self.cp(origin : String, destination : String)
corig = origin.colorize(:light_magenta).to_s
cdest = destination.colorize(:light_orange).to_s
Baguette::Log.detail "cp #{corig} #{cdest}"
FileUtils.cp origin, destination
end
def self.run(chdir, command, args) def self.run(chdir, command, args)
output = Process::Redirect::Inherit output = Process::Redirect::Inherit
@ -37,6 +44,8 @@ class Do < Process
file.puts "logging command $ #{command}" file.puts "logging command $ #{command}"
file.puts " parameters $ #{args}" file.puts " parameters $ #{args}"
end end
output = File.open Baguette::Context.logfile_path, "a"
end end
c = Process.run command, args, chdir: chdir, output: output, error: output, env: @@environment c = Process.run command, args, chdir: chdir, output: output, error: output, env: @@environment

View File

@ -3,76 +3,30 @@
# Simple array of strings with a name. # Simple array of strings with a name.
# #
# Methods: # Methods:
# - run(context, recipe) : BuildStatus # - run(recipe) : BuildStatus
# execute each instruction # execute each instruction
# instructions are provided by the recipe or by a backend if not specified # instructions are provided by the recipe
class Package::Instructions class Package::Instructions < Array(String)
class Set < Array(String) getter phase : String
getter phase : String def initialize(@phase)
def initialize(@phase) super()
super()
end
# FIXME: def execute
def run(context : Context, recipe : Recipe) : BuildStatus
# Does the recipe provided instructions for this phase?
if size > 0
each do |command|
child = Do.run recipe.building_directory, "sh", ["-x", "-c", command]
if child.exit_status != 0
return BuildStatus::Failed
end
end
return BuildStatus::Success
end
last_backend = "not-known"
# In case the recipe didn't provide instructions: checking backends.
context.building_backends.select(&.phase.==(@phase)).each do |backend|
last_backend = backend.name
Baguette::Log.info "Doing phase '#{@phase}' :: '" +
backend.name.colorize(:light_magenta).to_s +
"'"
Do.cd recipe.building_directory
rvalue = backend.build context, recipe
if rvalue == BuildStatus::Pass
next
end
return rvalue
end
BuildStatus::Pass
rescue e
# Possible TODO: print the origin of the exception (backend, user-provided code, other/unknown).
Baguette::Log.error "Exception during phase '#{@phase}', backend '#{last_backend}'"
Baguette::Log.error "Exception caught: #{e.message}"
BuildStatus::Failed
end
end end
# FIXME: Switch to an enum at some point? def run(directory : String) : BuildStatus
getter src_split = Set.new "src-split" if size == 0
getter configure = Set.new "configure" Baguette::Log.error "Empty instructions for phase #{@phase}"
getter pre_configure = Set.new "pre-configure" return BuildStatus::Pass
getter build = Set.new "build" end
getter pre_build = Set.new "pre-build"
getter install = Set.new "install"
getter pre_install = Set.new "pre-install"
getter post_install = Set.new "post-install"
def to_a each do |command|
[ child = Do.run directory, "sh", ["-x", "-c", command]
src_split,
pre_configure, configure, if child.exit_status != 0
pre_build, build, return BuildStatus::Failed
pre_install, install, post_install end
] end
BuildStatus::Success
end end
end end

View File

@ -99,7 +99,7 @@ end
# Verify package-create dependencies: tar, strip # Verify package-create dependencies: tar, strip
Do.require_cmd "tar" Do.require_cmd "bsdtar"
Do.require_cmd "strip" Do.require_cmd "strip"
if File.exists? CLIOpts.configuration_file if File.exists? CLIOpts.configuration_file
@ -184,7 +184,10 @@ begin
recipe.extract recipe.extract
recipe.build # Perform every building operation:
# sources-split, (pre-)(configure|build|install) and post-install
recipe.run
recipe.package recipe.package
recipe.clean unless CLIOpts.do_not_clean recipe.clean unless CLIOpts.do_not_clean

View File

@ -65,7 +65,6 @@ class Package::Recipe
# Build instructions, helpers and other build-only data. # Build instructions, helpers and other build-only data.
setter dirname : String? # matching getter defined manually later. setter dirname : String? # matching getter defined manually later.
getter instructions = Instructions.new
getter options = Hash(String, String).new getter options = Hash(String, String).new
getter watch_script : String? getter watch_script : String?
@ -122,16 +121,17 @@ class Package::Recipe
value.as_a_or_s.each do |source| value.as_a_or_s.each do |source|
@sources << source @sources << source
end end
when "configure"
@instructions.configure << value.as_s_or_ls # User instructions.
when "build" when /(?<phase>(pre-)?(configure|build|install)|post-install)/
@instructions.build << value.as_s_or_ls phase = $~["phase"]
when "install" @context.user_instructions[phase] = Instructions.new(phase).<<(value.as_s_or_ls)
@instructions.install << value.as_s_or_ls
when "dirname" when "dirname"
@dirname = value.as_s @dirname = value.as_s
when "prefix" when "prefix"
@prefix = value.as_s @prefix = value.as_s
when "dependencies" when "dependencies"
value.as_a_or_s.each do |atom| value.as_a_or_s.each do |atom|
@run_dependencies << atom @run_dependencies << atom
@ -145,6 +145,7 @@ class Package::Recipe
value.as_a_or_s.each do |atom| value.as_a_or_s.each do |atom|
@run_dependencies << atom @run_dependencies << atom
end end
when "conflicts" when "conflicts"
value.as_a_or_s.each do |atom| value.as_a_or_s.each do |atom|
@conflicts << atom @conflicts << atom
@ -156,8 +157,7 @@ class Package::Recipe
when "options" when "options"
value.as_a_or_s.each do |option| value.as_a_or_s.each do |option|
match = option.split(':').map( match = option.split(':').map(&.strip)
&.gsub(/^[ \t]*/, "").gsub(/[ \t]*$/, ""))
if match.size != 2 if match.size != 2
STDERR.puts "WARNING: misformed option: #{option}" STDERR.puts "WARNING: misformed option: #{option}"
@ -221,7 +221,7 @@ class Package::Recipe
if url.scheme == "file" if url.scheme == "file"
Baguette::Log.info "Copying '#{url.filename}'" Baguette::Log.info "Copying '#{url.filename}'"
FileUtils.cp "#{recipe_directory}/#{url.filename}", filename Do.cp "#{recipe_directory}/#{url.filename}", filename
else else
Baguette::Log.info "Downloading '#{url.filename}'" Baguette::Log.info "Downloading '#{url.filename}'"
@ -271,17 +271,44 @@ class Package::Recipe
@context.sources_directory @context.sources_directory
end end
FileUtils.cp "#{directory}/#{url.filename}", Do.cp "#{directory}/#{url.filename}",
"#{building_directory}/#{url.filename}" "#{building_directory}/#{url.filename}"
end end
end end
end end
def execute_backend(backends : Hash(String, Backend::Building)) : BuildStatus
last_backend = "not-known"
# In case the recipe didn't provide instructions: checking backends.
backends.each do |name, backend|
last_backend = name
cname = name.colorize(:light_green).to_s
Baguette::Log.info "Backend :: '#{cname}'"
Do.cd building_directory
rvalue = backend.build @context, self
if rvalue == BuildStatus::Pass
Baguette::Log.info "Pass…"
next
end
return rvalue
end
BuildStatus::Pass
rescue e
Baguette::Log.error "Exception backend '#{last_backend}'"
Baguette::Log.error "Exception caught: #{e.message}"
BuildStatus::Failed
end
# TODO: # TODO:
# - Have some instructions be non-critical, like the (pre|post) ones. # - Have some instructions be non-critical, like the (pre|post) ones.
# - Be careful about return values, flee from everything if something # - Be careful about return values, flee from everything if something
# goes somehow wrong. # goes somehow wrong.
def build def run
Do.mkdir_p fake_root_directory Do.mkdir_p fake_root_directory
ENV["PKG"] = fake_root_directory ENV["PKG"] = fake_root_directory
@ -290,16 +317,35 @@ class Package::Recipe
old_dir = Dir.current old_dir = Dir.current
# TODO: skip sequences # TODO: skip sequences
instructions.to_a.each do |instruction| @context.all_phase_names.each do |phase|
Baguette::Log.info "Building ('#{instruction.phase}' phase)" cphase = phase.colorize(:light_magenta).to_s
# TODO: finish this following lines Baguette::Log.info "Building ('#{cphase}')"
#if Package::Context.any? &.phase==instruction.phase
# next
#end
if instruction.run(@context, self).failed? ret = if instructions = @context.user_instructions[phase]?
raise BuildError.new self, "Building (#{instruction.phase} phase) failed." instructions.run building_directory
break else
# Some phases have available backends.
case phase
when "configure"
cphase = "configure".colorize(:light_blue).to_s
Baguette::Log.info "Executing phase :: #{cphase}"
execute_backend @context.configure_backends
when "build"
cphase = "build".colorize(:light_blue).to_s
Baguette::Log.info "Executing phase :: #{cphase}"
execute_backend @context.building_backends
when "install"
cphase = "install".colorize(:light_blue).to_s
Baguette::Log.info "Executing phase :: #{cphase}"
execute_backend @context.install_backends
else
# Not available backend and no user instructions.
BuildStatus::Pass
end
end
if ret.failed?
raise BuildError.new self, "Building ('#{phase}') failed."
end end
end end
@ -383,7 +429,9 @@ class Package::Recipe
end end
def auto_splits : Array(Package) def auto_splits : Array(Package)
@context.splitter_backends.compact_map do |backend| @context.splitter_backends.compact_map do |name, backend|
csplit = name.colorize(:light_green).to_s
Baguette::Log.info "Creating split for #{csplit}"
backend.create_split self backend.create_split self
end end
end end
@ -473,13 +521,10 @@ class Package::Recipe
spec += "dirname: #{@dirname}\n" spec += "dirname: #{@dirname}\n"
end end
[ @context.all_phase_names.each do |phase|
{"configure", @instructions.configure}, if instructions = @context.user_instructions[phase]
{"build", @instructions.build}, next unless instructions.size > 0
{"install", @instructions.install} spec += "@#{phase}\n"
].each do |name, instructions|
if instructions.size > 0
spec += "@#{name}\n"
instructions.each do |line| instructions.each do |line|
line = line.gsub /^\t* *\n/, "" line = line.gsub /^\t* *\n/, ""
line = line.gsub /\n *\t*\n/, "\n" line = line.gsub /\n *\t*\n/, "\n"