Archived
3
0
This repository has been archived on 2022-01-17. You can view files and clone it, but cannot push or open issues or pull requests.
packaging/src/context.cr
Luka Vandervelden 24ff4358d3 Verbosity control.
Multiple -v or -q flags can be provided to alter verbosity more. No log
file with full-verbosity is currently stored, but that’s a planned
feature.
2019-09-03 13:03:04 +02:00

317 lines
7.8 KiB
Crystal
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

require "colorize"
require "specfileparser"
require "./exception.cr"
require "./config.cr"
# FIXME: Where should this go? We cant just leave it here. :(
def pkginfo(package)
du = `du -sk #{package.fake_root_directory}`
size = du.sub(/[ \t].*/, "").to_i * 1024
lines = [] of String
lines << "# Generated by `package`."
lines << "pkgname = #{package.name}"
lines << "pkgver = #{package.version}-r#{package.release}"
lines << "url = #{package.url || ""} "
lines << "size = #{size}"
lines << "origin = #{package.recipe.name}"
lines << "buildtype = host" # Thisll need to be imported from Context.
lines << "builddate = #{Time.utc.to_unix}"
package.dependencies.each do |atom|
lines << "depends = #{atom.to_s}"
end
package.provides.each do |atom|
lines << "provides = #{atom.to_s}"
end
package.conflicts.each do |atom|
lines << "conflicts = #{atom.to_s}"
end
lines.join("\n") + "\n"
end
class Package::Backend::Packaging
getter name : String
def initialize(@name, &block : Proc(Context, Package, Bool))
@callback = block
end
def package(context : Context, package : Package)
@callback.call context, package
end
end
class Package::Backend::Splitter
def initialize(&block : Proc(Recipe, Package))
@callback = block
end
def create_split(recipe : Recipe) : Package
@callback.call recipe
end
end
class Package::Context
property working_directory = "/tmp/package"
property sources_directory = Dir.current
property packages_directory = Dir.current
getter packaging_backends = [] of Backend::Packaging
getter building_backends = [] of Backend::Building
getter splitter_backends = [] of Backend::Splitter
# Directories where ports can be found.
getter repositories = [] of String
# Well, this will need configuration, auto-detection and conversion,
# but itll be kind of enough for now.
property architecture = "x86_64"
property prefixes = ["/usr", "/", "/usr/weirdos"]
property environment = {} of String => String
property verbosity = 0
def initialize
@packaging_backends << Backend::Packaging.new "pkgutils" do |context, package|
puts "#{package.fake_root_directory} -> #{packages_directory}/#{package.name}##{package.version}-#{package.release}.pkg.tar.xz"
pp! r = run package.fake_root_directory, "tar", ["cJf", "#{packages_directory}/#{package.name}##{package.version}.pkg.tar.xz", "."]
r.exit_status == 0
end
@packaging_backends << Backend::Packaging.new "apk" do |context, package|
# FIXME: This needs to have access to architecture (from Context?)
# to work properly.
old_cwd = Dir.current
File.write "#{package.fake_root_directory}/.PKGINFO", pkginfo package
# Create data.tar.gz here.
package_target = "#{packages_directory}/#{context.architecture}/#{package.name}-#{package.version}-r#{package.release}.apk"
Dir.mkdir_p File.dirname package_target
# FIXME: This shouldnt have to be in users PATH. libexec?
r = run package.fake_root_directory, "#{OWN_LIBEXEC_DIR}/assemble-apk.sh", [
package_target
]
r.exit_status == 0
end
@selected_packaging_backend = @packaging_backends[0]
@building_backends << Backend::Building.new "configure", "autotools" do |context, recipe|
next BuildStatus::Pass unless Dir.exists? recipe.dirname
Dir.cd recipe.dirname
unless File.exists? "configure"
next BuildStatus::Pass
end
child = context.sh "./configure --prefix=#{recipe.prefix} #{recipe.options["configure"]? || ""}"
if child.exit_status == 0
BuildStatus::Success
else
BuildStatus::Failed
end
end
@building_backends << Backend::Building.new "configure", "cmake" do |context, recipe|
next BuildStatus::Pass unless Dir.exists? recipe.dirname
Dir.cd recipe.dirname
next BuildStatus::Pass unless File.exists? "CMakeLists.txt"
options = [
"-DCMAKE_INSTALL_PREFIX='#{recipe.prefix}'",
"-DCMAKE_BUILD_TYPE=Release #{recipe.options["cmake"]}"
]
child = context.sh "cmake . #{options.join " "}"
if child.exit_status == 0
BuildStatus::Success
else
BuildStatus::Failed
end
end
@building_backends << Backend::Building.new "build", "make" do |context, recipe|
next BuildStatus::Pass unless Dir.exists? recipe.dirname
Dir.cd recipe.dirname
unless File.exists? "Makefile"
next BuildStatus::Pass
end
child = context.sh "make #{recipe.options["make"]? || ""}"
if child.exit_status == 0
BuildStatus::Success
else
BuildStatus::Failed
end
end
@building_backends << Backend::Building.new "install", "make" do |context, recipe|
next BuildStatus::Pass unless Dir.exists? recipe.dirname
Dir.cd recipe.dirname
unless File.exists? "Makefile"
next BuildStatus::Pass
end
child = context.sh "make install 'DESTDIR=#{recipe.fake_root_directory}' #{recipe.options["make install"]? || ""}"
if child.exit_status == 0
BuildStatus::Success
else
BuildStatus::Failed
end
end
@splitter_backends << Backend::Splitter.new do |recipe|
Package.new(recipe, true).tap do |split|
prefixes = (@prefixes + [recipe.prefix]).uniq
split.name = "#{recipe.name}-man"
split.files = prefixes.map do |prefix|
"#{prefix}/share/man"
end
end
end
@splitter_backends << Backend::Splitter.new do |recipe|
Package.new(recipe, true).tap do |split|
prefixes = (@prefixes + [recipe.prefix]).uniq
split.name = "#{recipe.name}-dev"
split.files = prefixes.map do |prefix|
[
"#{prefix}/include",
"#{prefix}/lib/pkgconfig"
]
end.flatten
split.file_patterns = prefixes.map do |prefix|
Regex.new("^" + prefix + ".*\\.a$")
end
end
end
end
def packaging_backend=(name : String)
@selected_packaging_backend = @packaging_backends.find(&.name.==(name)).not_nil!
end
def packaging_backend=(backend : Backend::Packaging)
@selected_packaging_backend = backend
end
def run(chdir, command, args)
output = Process::Redirect::Inherit
if @verbosity < -1
output = Process::Redirect::Close
end
Process.run command, args, chdir: chdir, output: output, error: output, env: @environment
end
def run(command, args)
run nil, command, args
end
def run(command)
run nil, command, nil
end
def sh(command)
run nil, "sh", ["-x", "-e", "-c", command]
end
def captured_sh(command)
output = IO::Memory.new
child = Process.run "sh", ["-x", "-e", "-c", command], output: output
{child, output}
end
# Output functions.
def title(s)
return if @verbosity < -3
puts ">> ".colorize(:green).mode(:bright).to_s +
s.colorize(:white).mode(:bright).to_s
end
def info(s)
return if @verbosity < -2
puts ":: ".colorize(:green).to_s + s.colorize(:white).to_s
end
def detail(s)
return if @verbosity < -1
puts ("+ " + s).colorize(:cyan)
end
def package(package : Package) : Bool
@selected_packaging_backend.package self, package
end
def read_recipe(filename : String)
Recipe.new self, filename
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 = SpecFileParser.parse(filename).not_nil!
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 "prefixes"
@prefixes = value.as_a_or_s
when "environment"
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
@environment[key] = value
end
end
end
end
end