#!/usr/bin/env python3
# Yes, this is a Python program writing a Ruby program.
import argparse
import hashlib
import sys
import os
parser = argparse.ArgumentParser()
parser.add_argument("--url-base", default = "https://brew.cryptech.is/tarballs/")
parser.add_argument("--tarball", required = True)
parser.add_argument("--package", required = True)
parser.add_argument("--version", required = True)
parser.add_argument("--formula", type = argparse.FileType("w"), nargs = "?", default = sys.stdout)
parser.add_argument("--conflicts", default = "")
args = parser.parse_args()
template = '''\
# This Homebrew forumula was automatically generated by a script.
# You might not want to edit it manually.
#
# Installation is a bit complex due to the way Homebrew handles Python
# library dependencies and due to our stuff being a mixture of Python
# and C. It's also painfully slow, because we're not using bottles,
# due to lack of a MacOS build farm. Sorry.
#
# Per Homebrew expectations, we install copies of external Python
# libraries ("resources") in our own private library directory, using
# Homebrew's hack to run our executable Python scripts with PYTHONPATH
# pointing to our private library directory. Our own Python library
# code, however, is what Homebrew considers "bindings", so we install
# those where user scripts as well as our own can find them...then we
# add a symlink so that our scripts can find our bindings regardless
# of which copy of Python Homebrew decides we should use this week.
#
# We have to build our own software before installing our Python code,
# because at least one of the Python modules we install
# (cryptech.py11.attribute_map) is generated during the build.
#
# Reference for all the documented Python Homebrew voodoo:
#
# http://docs.brew.sh/Python-for-Formula-Authors.html
#
# Reference for undocumented Python Homebrew voodoo:
#
# /usr/local/Homebrew/Library/Homebrew/language/python.rb
class {classname} < Formula
desc "Software for working with Cryptech Alpha board HSM"
homepage "https://cryptech.is/"
version "{version}"
url "{url}"
sha256 "{sha256}"
depends_on "python@3.8"
{conflicts}
resource "pyserial" do
url "https://files.pythonhosted.org/packages/cc/74/11b04703ec416717b247d789103277269d567db575d2fd88f25d9767fe3d/pyserial-3.4.tar.gz"
sha256 "6e2d401fdee0eab996cf734e67773a0143b932772ca8b42451440cfed942c627"
end
resource "PyYAML" do
url "http://pyyaml.org/download/pyyaml/PyYAML-5.3.1.tar.gz"
sha256 "b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"
end
resource "tornado" do
url "https://files.pythonhosted.org/packages/source/t/tornado/tornado-4.4.3.tar.gz"
sha256 "f267acc96d5cf3df0fd8a7bfb5a91c2eb4ec81d5962d1a7386ceb34c655634a8"
end
resource "pycrypto" do
url "https://pypi.python.org/packages/source/p/pycrypto/pycrypto-2.6.1.tar.gz"
sha256 "f2ce1e989b272cfcb677616763e0a2e7ec659effa67a88aa92b3a65528f60a3c"
end
def install
xy = Language::Python.major_minor_version "python3"
ENV.prepend_create_path "PYTHONPATH", libexec/"vendor/lib/python#{{xy}}/site-packages"
resources.each do |r|
ohai "Building resource #{{r.url}}"
r.stage do
system "python3", *Language::Python.setup_install_args(libexec/"vendor")
end
end
ohai "Building PKCS #11 code (including crypto and bignum libraries) from source, this is slow, please be patient..."
ENV.deparallelize
system "make", "-C", "sw/pkcs11"
system "python3", *Language::Python.setup_install_args(prefix)
bin.env_script_all_files(libexec/"bin", :PYTHONPATH => ENV["PYTHONPATH"])
ln_s lib/"python#{{xy}}/site-packages/cryptech", libexec/"vendor/lib/python#{{xy}}/site-packages/cryptech"
share.install "cryptech-alpha-firmware.tar.gz"
lib.install "sw/pkcs11/libcryptech-pkcs11.dylib"
end
end
'''
with open(args.tarball, "rb") as f:
digest = hashlib.sha256(f.read()).hexdigest()
classname = "".join(word.capitalize() for word in args.package.split("-"))
conflicts = "\n".join(" conflicts_with \"{}\", :because => \"HSM firmware and PKCS #11 library must match\"".format(conflict)
for conflict in args.conflicts.split())
url = os.path.join(args.url_base, os.path.basename(args.tarball))
args.formula.write(template.format(
version = args.version,
url = url,
sha256 = digest,
classname = classname,
conflicts = conflicts))