2011-01-30 21:12:02 +00:00
|
|
|
#!/usr/bin/ruby
|
|
|
|
|
|
|
|
require 'optparse'
|
|
|
|
require 'yaml'
|
|
|
|
require 'fileutils'
|
|
|
|
require 'pathname'
|
|
|
|
|
2011-07-15 16:41:20 +00:00
|
|
|
@options = {:num_procs => 2, :memory => 2000}
|
2011-01-30 21:12:02 +00:00
|
|
|
|
2011-02-01 07:50:59 +00:00
|
|
|
@bitness = {
|
|
|
|
'i386' => 32,
|
|
|
|
'amd64' => 64,
|
|
|
|
}
|
|
|
|
|
2011-01-30 21:12:02 +00:00
|
|
|
def system!(cmd)
|
|
|
|
system(cmd) or raise "failed to run #{cmd}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def sanitize(str, where)
|
|
|
|
raise "unsanitary string in #{where}" if (str =~ /[^\w.-]/)
|
|
|
|
str
|
|
|
|
end
|
|
|
|
|
2011-02-01 07:50:59 +00:00
|
|
|
def sanitize_path(str, where)
|
|
|
|
raise "unsanitary string in #{where}" if (str =~ /[^\w\/.-]/)
|
|
|
|
str
|
|
|
|
end
|
|
|
|
|
2011-01-30 21:12:02 +00:00
|
|
|
def info(str)
|
|
|
|
puts str unless @options[:quiet]
|
|
|
|
end
|
|
|
|
|
2011-02-01 07:50:59 +00:00
|
|
|
def build_one_configuration(suite, arch, build_desc, reference_datetime)
|
|
|
|
bits = @bitness[arch] or raise "unknown architecture ${arch}"
|
|
|
|
suitearch = "#{suite}-#{arch}"
|
|
|
|
|
|
|
|
info "Stopping target if it is up"
|
|
|
|
system "stop-target"
|
|
|
|
|
|
|
|
sleep 1
|
|
|
|
|
|
|
|
unless @options[:skip_image]
|
|
|
|
info "Making a new image copy"
|
|
|
|
system! "cp base-#{suitearch}.qcow2 target-#{suitearch}.qcow2"
|
|
|
|
end
|
|
|
|
|
|
|
|
info "Starting target"
|
|
|
|
system! "start-target #{bits} #{suitearch}&"
|
|
|
|
|
|
|
|
$stdout.write "Checking if target is up"
|
|
|
|
|
|
|
|
(1..10).each do
|
|
|
|
system "on-target true 2> /dev/null" and break
|
|
|
|
sleep 2
|
|
|
|
$stdout.write '.'
|
|
|
|
end
|
|
|
|
|
|
|
|
info ''
|
|
|
|
|
|
|
|
system! "on-target true"
|
|
|
|
|
|
|
|
info "Preparing build environment"
|
|
|
|
system! "on-target bash < target-bin/init-build.sh"
|
|
|
|
|
|
|
|
build_desc["files"].each do |filename|
|
|
|
|
filename = sanitize(filename, "files section")
|
2011-07-17 01:27:23 +00:00
|
|
|
system! "cd inputs && copy-to-target #{@quiet_flag} #{filename} build/"
|
2011-02-01 07:50:59 +00:00
|
|
|
end
|
|
|
|
|
2011-03-22 16:02:53 +00:00
|
|
|
info "Updating apt-get repository (log in var/install.log)"
|
|
|
|
system! "on-target -u root apt-get update > var/install.log 2>&1"
|
|
|
|
|
2011-02-01 07:50:59 +00:00
|
|
|
info "Installing additional packages (log in var/install.log)"
|
|
|
|
system! "on-target -u root apt-get -y install #{build_desc["packages"].join(" ")} > var/install.log 2>&1"
|
|
|
|
|
|
|
|
info "Grabbing package manifest"
|
|
|
|
system! "on-target -u root bash < target-bin/grab-packages.sh > var/base-#{suitearch}.manifest"
|
|
|
|
|
|
|
|
info "Creating build script (var/build-script)"
|
|
|
|
|
|
|
|
File.open("var/build-script", "w") do |script|
|
|
|
|
script.puts "#!/bin/bash"
|
|
|
|
script.puts "set -e"
|
|
|
|
script.puts "export OUTDIR=$HOME/out"
|
|
|
|
script.puts "GBUILD_BITS=#{bits}"
|
2011-07-15 16:41:20 +00:00
|
|
|
script.puts "MAKEOPTS=(-j#{@options[:num_procs]})"
|
2011-02-01 07:50:59 +00:00
|
|
|
(ref_date, ref_time) = reference_datetime.split
|
|
|
|
script.puts "REFERENCE_DATETIME='#{reference_datetime}'"
|
|
|
|
script.puts "REFERENCE_DATE='#{ref_date}'"
|
|
|
|
script.puts "REFERENCE_TIME='#{ref_time}'"
|
|
|
|
script.puts
|
|
|
|
build_desc["remotes"].each do |remote|
|
|
|
|
script.puts "git clone -q #{remote["url"]} build/#{remote["dir"]}"
|
|
|
|
script.puts "(cd build/#{remote["dir"]} && git checkout -q #{remote["commit"]})"
|
|
|
|
end
|
|
|
|
script.puts "cd build"
|
|
|
|
script.puts build_desc["script"]
|
|
|
|
end
|
|
|
|
|
|
|
|
info "Running build script (log in var/build.log)"
|
|
|
|
system! "on-target bash < var/build-script > var/build.log 2>&1"
|
|
|
|
end
|
|
|
|
|
|
|
|
################################
|
|
|
|
|
2011-01-30 21:12:02 +00:00
|
|
|
OptionParser.new do |opts|
|
|
|
|
opts.banner = "Usage: build [options] <build-description>.yml"
|
|
|
|
|
|
|
|
opts.on("-i", "--skip-image", "reuse current target image") do |v|
|
|
|
|
@options[:skip_image] = v
|
|
|
|
end
|
|
|
|
opts.on("-q", "--quiet", "be quiet") do |v|
|
2011-03-20 01:09:36 +00:00
|
|
|
@options[:quiet] = v
|
2011-01-30 21:12:02 +00:00
|
|
|
end
|
2011-07-15 16:41:20 +00:00
|
|
|
opts.on("-j PROCS", "--num-make PROCS", "number of processes to use") do |v|
|
|
|
|
@options[:num_procs] = v
|
|
|
|
end
|
|
|
|
opts.on("-m MEM", "--memory MEM", "memory to allocate in MiB") do |v|
|
|
|
|
@options[:memory] = v
|
|
|
|
end
|
2011-02-01 17:48:33 +00:00
|
|
|
opts.on("-c PAIRS", "--commit PAIRS", "comma separated list of DIRECTORY=COMMIT pairs") do |v|
|
|
|
|
@options[:commit] = v
|
|
|
|
end
|
2011-01-30 21:12:02 +00:00
|
|
|
end.parse!
|
|
|
|
|
2011-05-30 20:55:38 +00:00
|
|
|
if !File.exist?("/dev/kvm")
|
|
|
|
$stderr.puts "\n************* WARNING: kvm not loaded, this will probably not work out\n\n"
|
|
|
|
end
|
|
|
|
|
2011-01-30 21:12:02 +00:00
|
|
|
base_dir = Pathname.new(__FILE__).expand_path.dirname.parent
|
|
|
|
libexec_dir = base_dir + 'libexec'
|
|
|
|
|
|
|
|
ENV['PATH'] = libexec_dir.to_s + ":" + ENV['PATH']
|
|
|
|
ENV['GITIAN_BASE'] = base_dir.to_s
|
2011-07-15 16:41:20 +00:00
|
|
|
ENV['NPROCS'] = @options[:num_procs].to_s
|
|
|
|
ENV['VMEM'] = @options[:memory].to_s
|
2011-07-17 01:27:23 +00:00
|
|
|
@quiet_flag = @options[:quiet] ? "-q" : ""
|
2011-01-30 21:12:02 +00:00
|
|
|
|
|
|
|
build_desc_file = ARGV.shift or raise "must supply YAML build description file"
|
|
|
|
|
|
|
|
build_desc = YAML.load_file(build_desc_file)
|
|
|
|
|
2011-02-01 07:50:59 +00:00
|
|
|
in_sums = []
|
2011-01-30 21:12:02 +00:00
|
|
|
|
|
|
|
build_dir = 'build'
|
|
|
|
result_dir = 'result'
|
|
|
|
|
|
|
|
FileUtils.rm_rf(build_dir)
|
|
|
|
FileUtils.mkdir(build_dir)
|
|
|
|
FileUtils.mkdir_p(result_dir)
|
|
|
|
|
2011-02-01 07:50:59 +00:00
|
|
|
package_name = build_desc["name"] or raise "must supply name"
|
|
|
|
package_name = sanitize(package_name, "package name")
|
2011-01-30 21:12:02 +00:00
|
|
|
|
2011-02-01 07:50:59 +00:00
|
|
|
suites = build_desc["suites"] or raise "must supply suites"
|
|
|
|
archs = build_desc["architectures"] or raise "must supply architectures"
|
|
|
|
reference_datetime = build_desc["reference_datetime"] or raise "must supply reference_datetime"
|
2011-01-30 21:12:02 +00:00
|
|
|
|
2011-02-01 07:50:59 +00:00
|
|
|
desc_sum = `sha256sum #{build_desc_file}`
|
|
|
|
desc_sum = desc_sum.sub(build_desc_file, "#{package_name}-desc.yml")
|
|
|
|
in_sums << desc_sum
|
2011-01-30 21:12:02 +00:00
|
|
|
|
2011-02-01 07:50:59 +00:00
|
|
|
build_desc["files"].each do |filename|
|
|
|
|
filename = sanitize(filename, "files section")
|
|
|
|
in_sums << `cd inputs && sha256sum #{filename}`
|
2011-01-30 21:12:02 +00:00
|
|
|
end
|
|
|
|
|
2011-02-01 17:48:33 +00:00
|
|
|
commits = {}
|
|
|
|
|
|
|
|
if @options[:commit]
|
|
|
|
@options[:commit].split(',').each do |pair|
|
|
|
|
(dir, commit) = pair.split('=')
|
|
|
|
commits[dir] = commit
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
build_desc["remotes"].each do |remote|
|
|
|
|
if !remote["commit"]
|
|
|
|
remote["commit"] = commits[remote["dir"]]
|
|
|
|
raise "must specify a commit for directory #{remote["dir"]}" unless remote["commit"]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2011-02-01 07:50:59 +00:00
|
|
|
base_manifests = YAML::Omap.new
|
2011-01-30 21:12:02 +00:00
|
|
|
|
2011-02-01 07:50:59 +00:00
|
|
|
suites.each do |suite|
|
|
|
|
suite = sanitize(suite, "suite")
|
|
|
|
archs.each do |arch|
|
|
|
|
info "--- Building for #{suite} #{arch} ---"
|
|
|
|
arch = sanitize(arch, "architecture")
|
2011-01-30 21:12:02 +00:00
|
|
|
|
2011-02-01 07:50:59 +00:00
|
|
|
# Build!
|
|
|
|
build_one_configuration(suite, arch, build_desc, reference_datetime)
|
2011-01-30 21:12:02 +00:00
|
|
|
|
2011-02-01 07:50:59 +00:00
|
|
|
info "Grabbing results"
|
2011-07-17 01:27:23 +00:00
|
|
|
system! "copy-from-target #{@quiet_flag} out #{build_dir}"
|
2011-01-30 21:12:02 +00:00
|
|
|
|
2011-02-01 07:50:59 +00:00
|
|
|
base_manifest = File.read("var/base-#{suite}-#{arch}.manifest")
|
|
|
|
base_manifests["#{suite}-#{arch}"] = base_manifest
|
2011-01-30 21:12:02 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
out_dir = File.join(build_dir, "out")
|
|
|
|
out_sums = {}
|
|
|
|
|
|
|
|
info "Generating report"
|
2011-05-21 02:40:15 +00:00
|
|
|
Dir.glob(File.join(out_dir, '**', '*'), File::FNM_DOTMATCH).sort.each do |file_in_out|
|
2011-02-01 07:50:59 +00:00
|
|
|
next if File.directory?(file_in_out)
|
|
|
|
file = file_in_out.sub(out_dir + File::SEPARATOR, '')
|
|
|
|
file = sanitize_path(file, file_in_out)
|
2011-01-30 23:31:33 +00:00
|
|
|
out_sums[file] = `cd #{out_dir} && sha256sum #{file}`
|
2011-01-30 21:12:02 +00:00
|
|
|
raise "failed to sum #{file}" unless $? == 0
|
|
|
|
puts out_sums[file] unless @options[:quiet]
|
|
|
|
end
|
|
|
|
|
|
|
|
out_manifest = out_sums.keys.sort.map { |key| out_sums[key] }.join('')
|
|
|
|
|
|
|
|
in_manifest = in_sums.join('')
|
|
|
|
|
|
|
|
# Use Omap to keep result deterministic
|
|
|
|
report = YAML::Omap[
|
|
|
|
'out_manifest', out_manifest,
|
|
|
|
'in_manifest', in_manifest,
|
2011-02-01 07:50:59 +00:00
|
|
|
'base_manifests', base_manifests,
|
2011-01-30 21:12:02 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
result_file = "#{package_name}-res.yml"
|
|
|
|
File.open(File.join(result_dir, result_file), "w") do |io|
|
|
|
|
io.write report.to_yaml
|
|
|
|
end
|
|
|
|
|
2011-01-30 23:31:33 +00:00
|
|
|
system!("cd #{result_dir} && sha256sum #{result_file}") unless @options[:quiet]
|
2011-01-30 21:12:02 +00:00
|
|
|
|
|
|
|
info "Done."
|