nix

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

otterb (5063B)


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