Skip to content

Instantly share code, notes, and snippets.

@Coronon
Created July 25, 2023 00:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Coronon/bfae90a5299c9a7eb191687d0acc8be6 to your computer and use it in GitHub Desktop.
Save Coronon/bfae90a5299c9a7eb191687d0acc8be6 to your computer and use it in GitHub Desktop.
Easily cross compile and package GO binaries
import argparse
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
from typing import List
parser = argparse.ArgumentParser(
prog='GoCrossCompile',
description='Automatically cross compile and package up go binaries',
epilog='©Rubin Raithel - https://rubinraithel.de',
)
parser.add_argument(
'-n',
'--name',
help = 'Name of the application without any extensions',
required = True
)
parser.add_argument(
'-v',
'--version',
help = 'Version of the application',
required = True,
)
parser.add_argument(
'-b',
'--builddir',
help = 'Directory containing the main package',
required = True
)
parser.add_argument(
'-d',
'--distdir',
help = 'Directory to store built archives in',
required = True
)
parser.add_argument(
'-a',
'--arch',
action = 'append',
help = '{GOOS}/{GOARCH} pairs to compile for',
)
parser.add_argument(
'-i',
'--include',
action = 'append',
help = 'Additional files to include',
required = False,
)
def build_binary(name: str, build_dir: Path, dist_dir: Path, goos: str, goarch: str) -> Path:
'''Compile a binary at `build_dir` for `{goos}/{goarch}` called `name`
Arguments:
name {str} -- Name of the binary
build_dir {Path} -- Directory of main package to compile
goos {str} -- Value for the GOOS environment variable
goarch {str} -- Value for the GOARCH environment variable
Returns:
Path -- Path to the compiled binary
'''
name_extension = '.exe' if goos == 'windows' else ''
binary_path = dist_dir.resolve().joinpath(f'{name}{name_extension}')
subprocess.run([
'env',
f'GOOS={goos}',
f'GOARCH={goarch}',
'go',
'build',
'-ldflags',
'-w -s',
'-trimpath',
'-o',
str(binary_path),
str(build_dir.resolve()),
])
return binary_path
def package_binary(
name: str,
version: str,
goos: str,
goarch: str,
binary_path: Path,
includes: List[Path],
dist_dir: Path,
) -> Path:
'''Package up a built binary into a zip archive for distribution
Arguments:
name {str} -- Name of the built binary (basename for the archive)
binary_path {Path} -- Path to the built binary
includes {List[Path]} -- Extra files to include in the archive
dist_dir {Path} -- Directory to place archive in
goos {str} -- GOOS used to built the binary
goarch {str} -- GOARCH used to built the binary
Returns:
Path -- Path to the built archive
'''
archive_name = f'{name}-{version}-{goos}_{goarch}'
archive_format = 'zip' if goos == 'windows' else 'gztar'
with tempfile.TemporaryDirectory(
f'{goos}_{goarch}',
'archive',
dist_dir,
) as tmp_dir:
# Copy files
for include in includes:
shutil.copyfile(include.resolve(), f'{tmp_dir}/{os.path.basename(include)}')
shutil.copyfile(binary_path.resolve(), f'{tmp_dir}/{os.path.basename(binary_path)}')
# Make archive
archive_path = Path(os.path.join(dist_dir.resolve(), archive_name))
archive = shutil.make_archive(archive_path.resolve(), archive_format, tmp_dir)
return Path(archive)
def main() -> None:
args = parser.parse_args()
name = args.name
version = args.version
build_dir = Path(args.builddir)
dist_dir = Path(args.distdir)
includes: List[str] = [Path(i) for i in args.include] if args.include is not None else []
# Clear old dist
try:
shutil.rmtree(dist_dir.resolve())
except:
pass
# Build binaries and package them
arch: str
for arch in args.arch:
goos, goarch = arch.lower().split('/')
print(f'Building {name} for {goos}/{goarch}...')
binary_path = build_binary(name, build_dir, dist_dir, goos, goarch)
archive_path = package_binary(
name,
version,
goos,
goarch,
binary_path,
includes,
dist_dir
)
os.remove(binary_path.resolve())
print(f'Built {archive_path.resolve()}')
print('Done!')
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment