nix

configuration files that power my machines
Log | Files | Refs | README | LICENSE

xbps-otterb-build (4973B)


      1 #!/usr/bin/env ruby
      2 # frozen_string_literal: true
      3 
      4 require "fileutils"
      5 require "open3"
      6 require "etc"
      7 
      8 class Builder
      9   REPO = "https://repo-default.voidlinux.org/current"
     10   DEPS = %w[
     11     cmake
     12     make
     13     gcc
     14     qt5-devel
     15     qt5-svg-devel
     16     qt5-multimedia-devel
     17     qt5-declarative-devel
     18     qt5-webkit-devel
     19     hunspell-devel
     20     gstreamer1-devel
     21     libxml2-devel
     22     qt5-tools
     23   ]
     24     .freeze
     25 
     26   def initialize(argv)
     27     @outdir = nil
     28     @chroot = "/var/chroot/otter-build"
     29     @keep = false
     30     @quiet = false
     31     parse(argv)
     32     validate
     33   end
     34 
     35   def run
     36     bootstrap
     37     mount_vfs
     38     prep_chroot
     39     install_deps
     40     build
     41     extract
     42   ensure
     43     unmount_vfs
     44     remove_chroot unless @keep
     45     msg("done.")
     46   end
     47 
     48   private
     49 
     50   def parse(argv)
     51     i = 0
     52     while i < argv.length
     53       case argv[i]
     54       when "-o"
     55         @outdir = argv[i += 1]
     56       when "-c"
     57         @chroot = argv[i += 1]
     58       when "-k"
     59         @keep = true
     60       when "-q"
     61         @quiet = true
     62       when "-h", "--help"
     63         usage(code: 0)
     64       else
     65         usage
     66       end
     67 
     68       i += 1
     69     end
     70   end
     71 
     72   def usage_lines
     73     [
     74       "Usage: #{File.basename($0)} [-o DIR] [-c DIR] [-k] [-q]",
     75       "  -o DIR   output directory (default: invoking user home)",
     76       "  -c DIR   chroot directory (default: /var/chroot/otter-build)",
     77       "  -k       keep chroot after build",
     78       "  -q       quiet"
     79     ]
     80   end
     81 
     82   def usage(code: 1)
     83     usage_lines.each { |l| puts(l) }
     84     exit(code)
     85   end
     86 
     87   def validate
     88     unless Process.uid.zero?
     89       usage_lines.each { |l| puts(l) }
     90       die("must run as root")
     91     end
     92 
     93     @src = Dir.pwd
     94     die("no CMakeLists.txt in #{@src}") unless File.file?(File.join(@src, "CMakeLists.txt"))
     95 
     96     inv = ENV["SUDO_USER"] || ENV["DOAS_USER"]
     97     if inv
     98       @pw = Etc.getpwnam(inv) rescue nil
     99       die("cannot resolve user #{inv}") unless @pw
    100       @outdir ||= @pw.dir
    101     else
    102       @outdir ||= File.expand_path("~")
    103     end
    104   end
    105 
    106   def msg(s) = puts(s) unless @quiet
    107 
    108   def die(s)
    109     $stderr.puts("#{File.basename($0)}: #{s}")
    110     exit(1)
    111   end
    112 
    113   def run_cmd(cmd, fatal: true)
    114     msg(cmd)
    115     _o, e, st = Open3.capture3(cmd)
    116     die("#{cmd}: #{e.strip}") if !st.success? && fatal
    117     st.success?
    118   end
    119 
    120   def chroot_cmd(script)
    121     msg("chroot #{@chroot} /bin/bash -c '#{script}'")
    122     die("chroot command failed") unless system("chroot #{@chroot} /bin/bash -c '#{script}'")
    123   end
    124 
    125   def bootstrap
    126     FileUtils.mkdir_p(@chroot)
    127     return if File.executable?(File.join(@chroot, "bin/bash"))
    128 
    129     msg("bootstrapping chroot...")
    130     kdir = File.join(@chroot, "var/db/xbps/keys")
    131     FileUtils.mkdir_p(kdir)
    132     Dir.glob("/var/db/xbps/keys/*").each { |f| FileUtils.cp(f, kdir) }
    133 
    134     run_cmd("xbps-install -Syu -R #{REPO} -r #{@chroot} base-voidstrap", fatal: false)
    135     die("bootstrap failed") unless File.executable?(File.join(@chroot, "bin/bash"))
    136     msg("bootstrap ok")
    137   end
    138 
    139   def mount_vfs
    140     mounts = {
    141       File.join(@chroot, "proc") => ["-t", "proc", "proc"],
    142       File.join(@chroot, "sys") => ["-t", "sysfs", "sys"],
    143       File.join(@chroot, "dev") => ["--bind", "/dev"]
    144     }
    145     mounts.each do |dst, opts|
    146       FileUtils.mkdir_p(dst)
    147       next if system("mountpoint -q #{dst} 2>/dev/null")
    148       system("mount #{opts.join(" ")} #{dst}")
    149     end
    150   end
    151 
    152   def unmount_vfs
    153     %w[dev sys proc].each { |n| system("umount #{File.join(@chroot, n)} 2>/dev/null") }
    154   end
    155 
    156   def prep_chroot
    157     FileUtils.cp("/etc/resolv.conf", File.join(@chroot, "etc/resolv.conf"))
    158     kdir = File.join(@chroot, "var/db/xbps/keys")
    159     FileUtils.mkdir_p(kdir)
    160     Dir.glob("/var/db/xbps/keys/*").each { |f| FileUtils.cp(f, kdir) }
    161   end
    162 
    163   def install_deps
    164     chroot_cmd("xbps-install -Syu")
    165     chroot_cmd("xbps-install -S #{DEPS.join(" ")}")
    166   end
    167 
    168   def build
    169     dst = File.join(@chroot, "src/otter-browser")
    170     FileUtils.rm_rf(dst)
    171     FileUtils.mkdir_p(File.dirname(dst))
    172     system("cp -a #{@src} #{dst}") || die("copy failed")
    173 
    174     cm = File.join(dst, "CMakeLists.txt")
    175     txt = File.read(cm)
    176     unless txt.include?("FATAL_ON_MISSING_REQUIRED_PACKAGES")
    177       die("CMakeLists.txt: FATAL_ON_MISSING_REQUIRED_PACKAGES not found")
    178     end
    179 
    180     txt.gsub!("FATAL_ON_MISSING_REQUIRED_PACKAGES", "")
    181     File.write(cm, txt)
    182 
    183     script = "set -e; cd /src/otter-browser; rm -rf build; mkdir build; cd build; cmake -DENABLE_QT5=ON ../; make -j$(nproc)"
    184     chroot_cmd(script)
    185   end
    186 
    187   def extract
    188     bin = File.join(@chroot, "src/otter-browser/build/otter-browser")
    189     die("binary not found") unless File.file?(bin)
    190 
    191     out = File.join(@outdir, "otter-browser")
    192     system("cp #{bin} #{out}") || die("copy binary failed")
    193     FileUtils.chmod("+x", out)
    194 
    195     if @pw
    196       begin
    197         File.chown(@pw.uid, @pw.gid, out)
    198       rescue => e
    199         msg("chown failed: #{e}")
    200       end
    201     end
    202 
    203     msg("binary: #{out}")
    204   end
    205 
    206   def remove_chroot
    207     msg("removing #{@chroot}...")
    208     FileUtils.rm_rf(@chroot)
    209   end
    210 end
    211 
    212 Builder.new(ARGV).run