Comments, grooming, new phases (src-split, pre- and post-)
parent
5f2e4d603c
commit
e218282594
|
@ -1,4 +1,27 @@
|
|||
|
||||
enum Package::BuildStatus
|
||||
Success
|
||||
Failed
|
||||
Pass
|
||||
end
|
||||
|
||||
# Building instructions
|
||||
# phases: src-split, (pre-)(configure|build|install) and post-install
|
||||
# name examples: autotools, cmake, make
|
||||
# build(context, recipe)
|
||||
|
||||
class Package::Backend::Building
|
||||
getter phase : String
|
||||
getter name : String
|
||||
getter callback : Proc(Context, Recipe, BuildStatus)
|
||||
def initialize(@phase, @name, &block : Proc(Context, Recipe, BuildStatus))
|
||||
@callback = block
|
||||
end
|
||||
def build(context : Context, recipe : Recipe)
|
||||
@callback.call context, recipe
|
||||
end
|
||||
end
|
||||
|
||||
abstract class Package::Backend::Packaging
|
||||
getter name : String
|
||||
|
||||
|
|
|
@ -2,13 +2,21 @@ require "openssl"
|
|||
|
||||
require "../backends.cr"
|
||||
|
||||
# Baguette package backend
|
||||
#
|
||||
# Packages contain:
|
||||
# - data.tar.zst: compressed tarball containing all installed data
|
||||
# - manifest: list of directories, files and their checksum
|
||||
# - control.spec: meta data about the package
|
||||
#
|
||||
# TODO:
|
||||
# - security: add a signature file
|
||||
|
||||
class Package::Backend::Packaging::Baguette < Package::Backend::Packaging
|
||||
def initialize
|
||||
@name = "package"
|
||||
end
|
||||
|
||||
# TODO: checksums + security
|
||||
def package(pkgdir : String, architecture : String, package : ::Package::Package) : Bool
|
||||
# Fake root example: /tmp/packages/<uuid>/root-xz-dev
|
||||
fake_root = package.fake_root_directory
|
||||
|
@ -23,7 +31,7 @@ class Package::Backend::Packaging::Baguette < Package::Backend::Packaging
|
|||
package_target = "#{pkgdir}/"
|
||||
package_target += "#{architecture}/"
|
||||
package_target += "#{package.name}-#{package.version}-#{package.release}.baguette"
|
||||
Dir.mkdir_p File.dirname package_target
|
||||
Do.mkdir_p File.dirname package_target
|
||||
|
||||
::Baguette::Log.detail "Archiving package content"
|
||||
Do.run fake_root, "tar", ["cvf", data_archive_path, "."]
|
||||
|
@ -75,7 +83,7 @@ class Package::Backend::Packaging::Baguette < Package::Backend::Packaging
|
|||
|
||||
manifest = File.open(file_name, "w").not_nil!
|
||||
|
||||
Dir.cd package.fake_root_directory
|
||||
Do.cd package.fake_root_directory
|
||||
|
||||
FileUtils.find "." do |path|
|
||||
file = path.lchop
|
||||
|
@ -94,7 +102,7 @@ class Package::Backend::Packaging::Baguette < Package::Backend::Packaging
|
|||
|
||||
manifest.close
|
||||
ensure
|
||||
Dir.cd old_pwd.not_nil!
|
||||
Do.cd old_pwd.not_nil!
|
||||
end
|
||||
|
||||
def self.install(packages : Array(String))
|
||||
|
|
|
@ -1,16 +1,33 @@
|
|||
|
||||
# Actual compilation instructions
|
||||
#
|
||||
# Backends:
|
||||
# - make
|
||||
# pass if no Makefile
|
||||
|
||||
class Package::Backend::Build
|
||||
|
||||
def self.make : Backend::Building
|
||||
Backend::Building.new "build", "make" do |context, recipe|
|
||||
next BuildStatus::Pass unless Dir.exists? recipe.dirname
|
||||
Dir.cd recipe.dirname
|
||||
unless Dir.exists? recipe.dirname
|
||||
Baguette::Log.detail "no '#{recipe.dirname}' directory: pass"
|
||||
next BuildStatus::Pass
|
||||
end
|
||||
Do.cd recipe.dirname
|
||||
|
||||
unless File.exists? "Makefile"
|
||||
Baguette::Log.detail "no Makefile: pass"
|
||||
next BuildStatus::Pass
|
||||
end
|
||||
|
||||
child = Do.sh "make -j#{context.build_cores} #{recipe.options["make"]? || ""}"
|
||||
ncores = recipe.options["build-cores"]? || context.build_cores
|
||||
|
||||
options = [
|
||||
"-j#{ncores}",
|
||||
recipe.options["make"]? || ""
|
||||
]
|
||||
|
||||
child = Do.sh "make #{options.join " "}"
|
||||
|
||||
if child.exit_status == 0
|
||||
BuildStatus::Success
|
||||
|
|
|
@ -1,18 +1,35 @@
|
|||
|
||||
# Configuration phase
|
||||
#
|
||||
# Backends:
|
||||
# - autotools
|
||||
# pass if no "configure" file
|
||||
# - cmake
|
||||
# pass if no "CMakeLists.txt" file
|
||||
# Both backends quit if recipe.dirname doesn't exist.
|
||||
|
||||
class Package::Backend::Configure
|
||||
|
||||
def self.autotools : Backend::Building
|
||||
Backend::Building.new "configure", "autotools" do |context, recipe|
|
||||
next BuildStatus::Pass unless Dir.exists? recipe.dirname
|
||||
|
||||
Dir.cd recipe.dirname
|
||||
|
||||
unless File.exists? "configure"
|
||||
unless Dir.exists? recipe.dirname
|
||||
Baguette::Log.detail "no '#{recipe.dirname}' directory: pass"
|
||||
next BuildStatus::Pass
|
||||
end
|
||||
|
||||
child = Do.sh "./configure --prefix=#{recipe.prefix} #{recipe.options["configure"]? || ""}"
|
||||
Do.cd recipe.dirname
|
||||
|
||||
unless File.exists? "configure"
|
||||
Baguette::Log.detail "no 'configure' file: pass"
|
||||
next BuildStatus::Pass
|
||||
end
|
||||
|
||||
options = [
|
||||
"--prefix=#{recipe.prefix}",
|
||||
recipe.options["configure"]? || ""
|
||||
]
|
||||
|
||||
child = Do.sh "./configure #{options.join " "}"
|
||||
if child.exit_status == 0
|
||||
BuildStatus::Success
|
||||
else
|
||||
|
@ -23,16 +40,23 @@ class Package::Backend::Configure
|
|||
|
||||
def self.cmake : Backend::Building
|
||||
Backend::Building.new "configure", "cmake" do |context, recipe|
|
||||
next BuildStatus::Pass unless Dir.exists? recipe.dirname
|
||||
unless Dir.exists? recipe.dirname
|
||||
Baguette::Log.detail "no '#{recipe.dirname}' directory: pass"
|
||||
next BuildStatus::Pass
|
||||
end
|
||||
|
||||
Dir.cd recipe.dirname
|
||||
Do.cd recipe.dirname
|
||||
|
||||
next BuildStatus::Pass unless File.exists? "CMakeLists.txt"
|
||||
unless File.exists? "CMakeLists.txt"
|
||||
Baguette::Log.detail "no 'CMakeLists.txt' file: pass"
|
||||
next BuildStatus::Pass
|
||||
end
|
||||
|
||||
ncores = recipe.options["build-cores"]? || context.build_cores
|
||||
options = [
|
||||
"-DCMAKE_INSTALL_PREFIX='#{recipe.prefix}'",
|
||||
"-DCMAKE_BUILD_TYPE=Release #{recipe.options["cmake"]}",
|
||||
"-- -j#{context.build_cores}"
|
||||
"-- -j#{ncores}"
|
||||
]
|
||||
|
||||
child = Do.sh "cmake . #{options.join " "}"
|
||||
|
|
|
@ -1,15 +1,30 @@
|
|||
|
||||
# Installation process
|
||||
# Files to package are put in recipe.fake_root_directory
|
||||
#
|
||||
# Backends:
|
||||
# - make
|
||||
# pass if no Makefile
|
||||
|
||||
class Package::Backend::Install
|
||||
def self.make : Backend::Building
|
||||
Backend::Building.new "install", "make" do |context, recipe|
|
||||
next BuildStatus::Pass unless Dir.exists? recipe.dirname
|
||||
Dir.cd recipe.dirname
|
||||
unless Dir.exists? recipe.dirname
|
||||
Baguette::Log.detail "no '#{recipe.dirname}' directory: pass"
|
||||
next BuildStatus::Pass
|
||||
end
|
||||
Do.cd recipe.dirname
|
||||
|
||||
unless File.exists? "Makefile"
|
||||
next BuildStatus::Pass
|
||||
end
|
||||
|
||||
child = Do.sh "make install 'DESTDIR=#{recipe.fake_root_directory}' #{recipe.options["make install"]? || ""}"
|
||||
options = [
|
||||
"'DESTDIR=#{recipe.fake_root_directory}'",
|
||||
recipe.options["make install"]? || ""
|
||||
]
|
||||
|
||||
child = Do.sh "make install #{options.join " "}"
|
||||
|
||||
if child.exit_status == 0
|
||||
BuildStatus::Success
|
||||
|
|
|
@ -53,23 +53,23 @@ class Package::Backend::Splitter
|
|||
end
|
||||
end
|
||||
|
||||
# Source files: prefix containing "src".
|
||||
# NOTE: src is not a regular split, _all_ files should be included,
|
||||
# as well as the patches used for the build.
|
||||
# Files are copied before build.
|
||||
|
||||
def self.src(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}-src"
|
||||
split.files = prefixes.map do |prefix|
|
||||
[
|
||||
"#{prefix}/lib/share/src",
|
||||
"#{prefix}/src",
|
||||
"#{prefix}/usr/src",
|
||||
"#{prefix}/usr/local/src"
|
||||
]
|
||||
end.flatten
|
||||
# No need, files are already in the package fake root.
|
||||
# split.files = prefixes.map do |prefix|
|
||||
# [ "#{prefix}/src" ]
|
||||
# end.flatten
|
||||
split.recipe.require_stripping = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -28,9 +28,6 @@ class Package::Context
|
|||
# By default, building a package only uses one core
|
||||
property build_cores = 1
|
||||
|
||||
# list of environment variables we want to have when building
|
||||
#property environment = {} of String => String
|
||||
|
||||
property recipe : Recipe? = nil
|
||||
|
||||
|
||||
|
@ -53,6 +50,7 @@ class Package::Context
|
|||
# Split = new package from a recipe, each one with different files being included.
|
||||
@splitter_backends << Backend::Splitter.man @prefixes
|
||||
@splitter_backends << Backend::Splitter.dev @prefixes
|
||||
# src isn't really a split, files already are copied before build.
|
||||
@splitter_backends << Backend::Splitter.src @prefixes
|
||||
end
|
||||
|
||||
|
|
10
src/do.cr
10
src/do.cr
|
@ -5,6 +5,7 @@ class Baguette::Context
|
|||
end
|
||||
|
||||
class Do < Process
|
||||
# list of environment variables we want to have when building
|
||||
class_property environment = {} of String => String
|
||||
|
||||
def self.require_cmd(cmd : String)
|
||||
|
@ -14,6 +15,11 @@ class Do < Process
|
|||
end
|
||||
end
|
||||
|
||||
def self.cd(chdir)
|
||||
Baguette::Log.detail "cd " + chdir.colorize(:light_magenta).to_s
|
||||
Dir.cd chdir
|
||||
end
|
||||
|
||||
def self.run(chdir, command, args)
|
||||
output = Process::Redirect::Inherit
|
||||
|
||||
|
@ -21,9 +27,9 @@ class Do < Process
|
|||
output = Process::Redirect::Close
|
||||
else
|
||||
# log sub-commands outputs
|
||||
STDOUT.puts "logging command " +
|
||||
Baguette::Log.info "logging command " +
|
||||
"#{command} #{args}".colorize(:light_magenta).to_s
|
||||
STDOUT.puts "in " + Baguette::Context.logfile_path.colorize(:blue).mode(:bright).to_s
|
||||
Baguette::Log.info "in " + Baguette::Context.logfile_path.colorize(:blue).mode(:bright).to_s
|
||||
|
||||
File.open Baguette::Context.logfile_path, "a" do |file|
|
||||
file.puts ""
|
||||
|
|
|
@ -1,21 +1,11 @@
|
|||
|
||||
enum Package::BuildStatus
|
||||
Success
|
||||
Failed
|
||||
Pass
|
||||
end
|
||||
|
||||
class Package::Backend::Building
|
||||
getter phase : String
|
||||
getter name : String
|
||||
getter callback : Proc(Context, Recipe, BuildStatus)
|
||||
def initialize(@phase, @name, &block : Proc(Context, Recipe, BuildStatus))
|
||||
@callback = block
|
||||
end
|
||||
def build(context : Context, recipe : Recipe)
|
||||
@callback.call context, recipe
|
||||
end
|
||||
end
|
||||
# Instructions for src-split, [pre-](configure|build|install) and post-install
|
||||
# Simple array of strings with a name.
|
||||
#
|
||||
# Methods:
|
||||
# - run(context, recipe) : BuildStatus
|
||||
# execute each instruction
|
||||
# instructions are provided by the recipe or by a backend if not specified
|
||||
|
||||
class Package::Instructions
|
||||
class Set < Array(String)
|
||||
|
@ -26,6 +16,7 @@ class Package::Instructions
|
|||
# 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]
|
||||
|
@ -38,8 +29,14 @@ class Package::Instructions
|
|||
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|
|
||||
Dir.cd recipe.building_directory
|
||||
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
|
||||
|
||||
|
@ -53,18 +50,29 @@ class Package::Instructions
|
|||
BuildStatus::Pass
|
||||
rescue e
|
||||
# Possible TODO: print the origin of the exception (backend, user-provided code, other/unknown).
|
||||
STDERR << "Exception caught: " << e.message << "\n"
|
||||
Baguette::Log.error "Exception during phase '#{@phase}', backend '#{last_backend}'"
|
||||
Baguette::Log.error "Exception caught: #{e.message}"
|
||||
BuildStatus::Failed
|
||||
end
|
||||
end
|
||||
|
||||
# FIXME: Switch to an enum at some point?
|
||||
getter configure = Set.new "configure"
|
||||
getter build = Set.new "build"
|
||||
getter install = Set.new "install"
|
||||
getter src_split = Set.new "src-split"
|
||||
getter configure = Set.new "configure"
|
||||
getter pre_configure = Set.new "pre-configure"
|
||||
getter build = Set.new "build"
|
||||
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
|
||||
[configure, build, install]
|
||||
[
|
||||
src_split,
|
||||
pre_configure, configure,
|
||||
pre_build, build,
|
||||
pre_install, install, post_install
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,7 +2,13 @@ require "specparser"
|
|||
|
||||
class Package::Package
|
||||
getter recipe : Recipe
|
||||
getter automatic : Bool
|
||||
getter automatic : Bool # Was the package created by automatic splitting?
|
||||
|
||||
# Reference for splits. Recipe#packages[0] should keep this set to `nil`.
|
||||
property files : Array(String)?
|
||||
property file_patterns : Array(Regex)?
|
||||
|
||||
property fake_root_directory : String?
|
||||
|
||||
def initialize(@recipe, @automatic = false, @fake_root_directory = nil)
|
||||
end
|
||||
|
@ -38,6 +44,8 @@ class Package::Package
|
|||
end
|
||||
end
|
||||
|
||||
# Most of the attributes are inherited from recipe.
|
||||
|
||||
macro inherit(attribute)
|
||||
@{{attribute.var.id}} : {{attribute.type.id}}?
|
||||
|
||||
|
@ -61,14 +69,8 @@ class Package::Package
|
|||
inherit conflicts : Array(String)
|
||||
inherit provides : Array(String)
|
||||
|
||||
# Reference for splits. Recipe#packages[0] should keep this set to `nil`.
|
||||
property files : Array(String)?
|
||||
property file_patterns : Array(Regex)?
|
||||
|
||||
inherit prefix : String
|
||||
|
||||
property fake_root_directory : String?
|
||||
|
||||
def dependencies
|
||||
@dependencies || @recipe.run_dependencies
|
||||
end
|
||||
|
|
|
@ -182,6 +182,9 @@ class Package::Recipe
|
|||
# Packages can only be created once @name and @version exist!
|
||||
@packages << Package.new self, false, fake_root_directory
|
||||
|
||||
# Spec sections are like this:
|
||||
# %split my-application
|
||||
# split = name of the section
|
||||
specs.sections.each do |section|
|
||||
case section.name
|
||||
when "split"
|
||||
|
@ -275,12 +278,9 @@ class Package::Recipe
|
|||
end
|
||||
|
||||
# TODO:
|
||||
# - Export packaging directory in $PKG.
|
||||
# - Add (pre|post)-(configure|build|install) instructions.
|
||||
# - 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
|
||||
# goes somehow wrong.
|
||||
# - Make things thread-safe. (those ENV[]= calls are definitely not)
|
||||
def build
|
||||
Do.mkdir_p fake_root_directory
|
||||
|
||||
|
@ -289,8 +289,13 @@ class Package::Recipe
|
|||
# Safety precautions.
|
||||
old_dir = Dir.current
|
||||
|
||||
# TODO: skip sequences
|
||||
instructions.to_a.each do |instruction|
|
||||
Baguette::Log.info "Building ('#{instruction.phase}' phase)"
|
||||
# TODO: finish this following lines
|
||||
#if Package::Context.any? &.phase==instruction.phase
|
||||
# next
|
||||
#end
|
||||
|
||||
if instruction.run(@context, self).failed?
|
||||
raise BuildError.new self, "Building (#{instruction.phase} phase) failed."
|
||||
|
@ -298,7 +303,7 @@ class Package::Recipe
|
|||
end
|
||||
end
|
||||
|
||||
Dir.cd old_dir
|
||||
Do.cd old_dir
|
||||
|
||||
ENV["PKG"] = nil
|
||||
|
||||
|
@ -493,4 +498,3 @@ class Package::Recipe
|
|||
spec
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Reference in New Issue