# Compiling Go with python

I've made a small script to compile Go code with Python.

The result is not something I'm happy with, because:

• It seems overly complicated
• I'm worried the nested if-else with f string are unreadable
• Am I using the subprocess module the correct way?

Any review is welcome.

#!/usr/bin/env python3

import argparse
import subprocess
import os

ARCHS = {
"32": "386",
"64": "amd64"
}

BUILDTYPES = ["exe", "dll"]

def parse_arguments():
parser = argparse.ArgumentParser(usage='%(prog)s [options] <buildtype> <architecture>',
description='Go builder',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog='''
Python program to build go payloads

Examples:
./builder exe 32 --stripped
./builder dll 64 --upx --stripped
./builder dll 32
''')
arguments = parser.parse_args()

if not arguments.buildtype.lower() in BUILDTYPES:
parser.error(f"{arguments.buildtype} is not a valid buildtype can only be {', '.join(BUILDTYPES)}")

if not arguments.architecture.lower() in ARCHS:
parser.error(f"{arguments.architecture} is not a valid architecture can only be {', '.join(ARCHS.keys())}")
arguments.architecture = ARCHS[arguments.architecture]

return arguments

def build_go(**kwargs):
enviroment = {
**os.environ,
"GOOS": "windows",
"GOARCH": f"{kwargs['architecture']}"
}
if kwargs['buildtype'].lower() == "dll":
enviroment["CGO_ENABLED"] = "1"
enviroment["CC"] = f"{'i686' if kwargs['architecture'] == '386' else 'x86_64'}-w64-mingw32-gcc"

build_type = f"go build -buildmode=c-shared" if kwargs["buildtype"].lower() == "dll" else "go build -tags exe"
stripped = "-s -w" if kwargs['stripped'] else ""
builder = f'''{build_type} -o GOLoader.{kwargs['buildtype']} -ldflags "-H=windowsgui {stripped}"'''
subprocess.check_output(builder, shell=True, env=enviroment)

if kwargs["stripped"]:
subprocess.check_output(stripped, shell=True)

if kwargs["upx"]:
subprocess.check_output(upx, shell=True)

if __name__ == '__main__':
args = parse_arguments()
build_go(**vars(args))

• I would write it using go, so it does not depend upon any runtime dependencies. – mh-cbon Nov 19 '19 at 9:50

Some minor notes.

### I love the ternary operator and f strings, but...

Here are three ways to express the same thing. There is the original way, a DRY version of the original way, and a more traditional if/else. Which is easier to read and understand what is going on?

# variant 1
build_type = f"go build -buildmode=c-shared" if kwargs["buildtype"].lower() == "dll" else "go build -tags exe"

# variant 2
build_type = f"go build {'-buildmode=c-shared' if kwargs['buildtype'].lower() == 'dll' else '-tags exe'}"

# variant 3
if kwargs["buildtype"].lower() == "dll":
build_type = "go build -buildmode=c-shared"
else:
build_type = "go build -tags exe"


In this case I would argue that the third variant is MUCH easier to understand what the distinction is between the two build types. EG: What question is being asked (buildtype) and what is the difference in the result based on the answer.

### Environment has a "n" after the "o"

This:

enviroment["CGO_ENABLED"] = "1"


should likely be:

environment["CGO_ENABLED"] = "1"


### Click is the thing.

Not sure this is immediately relevant as a code review, but...

I much prefer click to argparse. In this simple case there is not likely a huge advantage for Click, but there is little downside. And if you get comfortable with click and the size of your project increases, I think Click can be quite advantageous.