blob: 6978e8ea9c62935868aef0d354d7fc3594acd333 [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2017 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Build script for a Go app.
import argparse
import os
import subprocess
import sys
import string
import shutil
import errno
from gen_library_metadata import get_sources
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--godepfile', help='Path to godepfile tool', required=True)
parser.add_argument('--root-out-dir', help='Path to root of build output',
required=True)
parser.add_argument('--cc', help='The C compiler to use',
required=False, default='cc')
parser.add_argument('--cxx', help='The C++ compiler to use',
required=False, default='c++')
parser.add_argument('--objcopy', help='The objcopy tool to use',
required=False, default='objcopy')
parser.add_argument('--sysroot', help='The sysroot to use',
required=False)
parser.add_argument('--target', help='The compiler target to use',
required=False)
parser.add_argument('--depfile', help='The path to the depfile',
required=True)
parser.add_argument('--current-cpu', help='Target architecture.',
choices=['x64', 'arm64'], required=True)
parser.add_argument('--current-os', help='Target operating system.',
choices=['fuchsia', 'linux', 'mac', 'win'], required=True)
parser.add_argument('--go-root', help='The go root to use for builds.', required=True)
parser.add_argument('--go-cache', help='Cache directory to use for builds.',
required=False)
parser.add_argument('--is-test', help='True if the target is a go test',
default=False)
parser.add_argument('--go-dep-files',
help='List of files describing library dependencies',
nargs='*',
default=[])
parser.add_argument('--binname', help='Output file', required=True)
parser.add_argument('--unstripped-binname', help='Unstripped output file')
parser.add_argument('--verbose', help='Tell the go tool to be verbose about what it is doing',
action='store_true')
parser.add_argument('--package', help='The package name', required=True)
parser.add_argument('--include-dir', help='-isystem path to add',
action='append', default=[])
parser.add_argument('--lib-dir', help='-L path to add',
action='append', default=[])
parser.add_argument('--vet', help='Run go vet',
action='store_true')
args = parser.parse_args()
try:
os.makedirs(args.go_cache)
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir(args.go_cache):
pass
else:
raise
goarch = {
'x64': 'amd64',
'arm64': 'arm64',
}[args.current_cpu]
goos = {
'fuchsia': 'fuchsia',
'linux': 'linux',
'mac': 'darwin',
'win': 'windows',
}[args.current_os]
output_name = os.path.join(args.root_out_dir, args.binname)
build_id_dir = os.path.join(args.root_out_dir, '.build-id')
depfile_output = output_name
if args.unstripped_binname:
stripped_output_name = output_name
output_name = os.path.join(args.root_out_dir, 'exe.unstripped',
args.binname)
# Project path is a package specific gopath, also known as a "project" in go parlance.
project_path = os.path.join(args.root_out_dir, 'gen', 'gopaths', args.binname)
# Clean up any old project path to avoid leaking old dependencies
shutil.rmtree(os.path.join(project_path, 'src'), ignore_errors=True)
os.makedirs(os.path.join(project_path, 'src'))
if args.go_dep_files:
# Create a gopath for the packages dependency tree
for dst, src in get_sources(args.go_dep_files).items():
dstdir = os.path.join(project_path, 'src', os.path.dirname(dst))
try:
os.makedirs(dstdir)
except OSError as e:
# EEXIST occurs if two gopath entries share the same parent name
if e.errno != errno.EEXIST:
raise
# TODO(BLD-228): the following check might not be necessary anymore.
tgt = os.path.join(dstdir, os.path.basename(dst))
# The source tree is effectively read-only once the build begins.
# Therefore it is an error if tgt is in the source tree. At first
# glance this may seem impossible, but it can happen if dst is foo/bar
# and foo is a symlink back to the source tree.
canon_root_out_dir = os.path.realpath(args.root_out_dir)
canon_tgt = os.path.realpath(tgt)
if not canon_tgt.startswith(canon_root_out_dir):
raise ValueError("Dependency destination not in --root-out-dir: provided=%s, path=%s, realpath=%s" % (dst, tgt, canon_tgt))
os.symlink(os.path.relpath(src, os.path.dirname(tgt)), tgt)
gopath = os.path.abspath(project_path)
build_goroot = os.path.abspath(args.go_root)
env = {}
env['GOARCH'] = goarch
env['GOOS'] = goos
env['GOPATH'] = gopath
# Some users have GOROOT set in their parent environment, which can break
# things, so it is always set explicitly here.
env['GOROOT'] = build_goroot
env['GOCACHE'] = args.go_cache
env['CC'] = args.cc
if args.target:
env['CC_FOR_TARGET'] = args.cc
env['CXX'] = args.cxx
if args.target:
env['CXX_FOR_TARGET'] = args.cxx
cflags = []
if args.sysroot:
cflags.append('--sysroot=' + args.sysroot)
if args.target:
cflags.append('--target=' + args.target)
ldflags = cflags[:]
cflags += ['-isystem' + dir for dir in args.include_dir]
ldflags += ['-L' + dir for dir in args.lib_dir]
env['CGO_CFLAGS'] = env['CGO_CPPFLAGS'] = env['CGO_CXXFLAGS'] = ' '.join(cflags)
env['CGO_LDFLAGS'] = ' '.join(ldflags)
env['CGO_ENABLED'] = '1'
# /usr/bin:/bin are required for basic things like bash(1) and env(1). Note
# that on Mac, ld is also found from /usr/bin.
env['PATH'] = "/usr/bin:/bin"
go_tool = os.path.join(build_goroot, 'bin/go')
if args.vet:
retcode = subprocess.call([go_tool, 'vet', args.package], env=env)
if retcode != 0:
return retcode
cmd = [go_tool]
if args.is_test:
cmd += ['test', '-c']
else:
cmd += ['build']
if args.verbose:
cmd += ['-x']
cmd += ['-pkgdir', os.path.join(project_path, 'pkg'), '-o',
output_name, args.package]
retcode = subprocess.call(cmd, env=env)
if retcode == 0 and args.unstripped_binname:
if args.current_os == 'mac':
retcode = subprocess.call(['xcrun', 'strip', '-x', output_name,
'-o', stripped_output_name],
env=env)
else:
retcode = subprocess.call([args.objcopy,
'--strip-sections',
'--build-id-link-dir=%s' % build_id_dir,
'--build-id-link-input=.debug',
'--build-id-link-output=',
output_name,
stripped_output_name],
env=env)
if retcode == 0:
if args.depfile is not None:
with open(args.depfile, "wb") as out:
godepfile_args = [args.godepfile, '-o', depfile_output]
if args.is_test:
godepfile_args += [ '-test']
godepfile_args += [args.package]
subprocess.Popen(godepfile_args, stdout=out, env=env)
return retcode
if __name__ == '__main__':
sys.exit(main())