blob: 41a5123486ff4ced0237dec289f5a9936db8ba9e [file] [log] [blame]
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Library module for building go binaries used by build_mojom_tool and
build_generators.
"""
import argparse
import imp
import os
import shutil
import subprocess
import sys
class GoBinaryBuilder(object):
_TARGET_ARCHITECTURES = [
('darwin', 'amd64'),
('linux', 'amd64'),
]
def __init__(self,
target_dir,
binary_name,
src_path,
src_root=None,
go_tool=None,
go_root=None,
out_dir=None,
upload=False,
friendly_name=None,
quiet=False,
keep_going=False):
"""Builds a go binary building object.
Args:
target_dir: Relative path from the architecture directory to the
directory which contains the sha1 file.
binary_name: File name for the binary to be built.
src_path: Path from src_root to the directory which contains the main
package for the binary to be built.
src_root: Path to the root of the source tree. (If None, the path of the
directory 3 levels above the one containing this script is used.)
go_tool: Path to the go tool.
(If None, uses {src_root}/third_party/go/tool/linux_amd64/bin/go)
go_root: Path to the root of the go installation.
(If None, uses the directory containing the go tool.)
out_dir: Path to the directory in which the built binary should be placed.
(If None, uses the current working directory.)
upload: If True, get the path to the depot tools as those are needed to
upload only. If False, the upload method will fail.
quiet: If True, do not print any messages on success.
keep_going: If True, move on to the next step when experiencing a failure.
"""
self._target_dir = target_dir
self._binary_name = binary_name
self._src_path = src_path
if src_root:
self._src_root = os.path.abspath(src_root)
else:
self._src_root = os.path.abspath(
os.path.join(os.path.dirname(__file__), *([os.pardir] * 2)))
assert os.path.exists(self._src_root)
if go_tool:
self._go_tool = os.path.abspath(go_tool)
else:
self._go_tool = os.path.join(self._src_root,
'third_party', 'go', 'tool', 'linux_amd64', 'bin', 'go')
assert os.path.exists(self._go_tool)
if go_root:
self._go_root = os.path.abspath(go_root)
else:
self._go_root = os.path.dirname(os.path.dirname(self._go_tool))
assert os.path.exists(self._go_root)
if not out_dir:
out_dir = os.getcwd()
self._out_dir = os.path.abspath(out_dir)
self._depot_tools_path = None
if upload:
self._depot_tools_path = self._get_depot_tools_path(self._src_root)
self._quiet = quiet
self._keep_going = keep_going
self._friendly_name = friendly_name
if self._friendly_name is None:
self._friendly_name = binary_name
def build_and_upload(self):
"""Builds and then uploads the binary for every target architecture.
Returns:
0 if everything went well. The return code of the last command to fail
otherwise.
"""
final_result = 0
# First we build the binary for every architecture.
result = self.build()
if result != 0:
final_result = result
if not self._keep_going:
return result
# Then we upload the binary for every architecture.
result = self.upload()
if result != 0:
final_result = result
if not self._keep_going:
return result
return final_result
def build(self):
"""Builds the binary for every target architectures.
Returns:
0 if everything went well. The return code of the last command to fail
otherwise.
"""
final_result = 0
for target_os, target_arch in self._TARGET_ARCHITECTURES:
result = self._build_for_arch(target_os, target_arch)
if result != 0:
final_result = result
if not self._keep_going:
return result
return final_result
def upload(self):
"""Uploads the binary that was just created for every target arch.
Returns:
0 if everything went well. The return code of the last command to fail
otherwise.
"""
final_result = 0
for target_os, target_arch in self._TARGET_ARCHITECTURES:
result = self._upload_for_arch(target_os, target_arch)
if result != 0:
final_result = result
if not self._keep_going:
return result
return final_result
def _upload_for_arch(self, target_os, target_arch):
"""Uploads the binary that was just created for the given target.
upload must have been set to True when creating this class.
Args:
target_os: Any value valid for GOOS.
target_arch: Any value valid for GOARCH.
Returns:
0 if everything went well, something else otherwise.
"""
assert self._depot_tools_path
upload_result = self._upload_binary(target_os, target_arch)
if upload_result != 0:
return upload_result
# Copy the generated sha1 file to the stamp_file location.
target_dir = os.path.join(
self._get_dir_name_for_arch(target_os, target_arch),
self._target_dir)
sha1_file = '%s.sha1' % self._get_output_path(target_os, target_arch)
assert os.path.exists(sha1_file)
stamp_file = os.path.join(self._src_root, 'mojo', 'public', 'tools',
'bindings', 'mojom_tool', 'bin', target_dir,
'%s.sha1' % self._binary_name)
shutil.move(sha1_file, stamp_file)
self._info_print(
"Wrote stamp file %s. You probably want to commit this." % stamp_file)
return 0
def _build_for_arch(self, target_os, target_arch):
"""Builds the mojom parser for target_os and target_arch.
Args:
target_os: Any value valid for GOOS.
target_arch: Any value valid for GOARCH.
Returns:
The go tool's return value.
"""
out_path = self._get_output_path(target_os, target_arch)
environ = {
'GOROOT': self._go_root,
'GOPATH': os.path.dirname(self._src_root),
'GOOS': target_os,
'GOARCH': target_arch,
}
save_cwd = os.getcwd()
os.chdir(os.path.join(self._src_root, self._src_path))
self._info_print(
"Building the Mojom parser for %s %s..." % (target_os, target_arch))
result = subprocess.call(
[self._go_tool, 'build', '-o', out_path] , env=environ)
if result == 0:
self._info_print("Success! Built %s" % out_path)
else:
print >> sys.stderr, "Failure building Mojom parser for %s %s!" % (
target_os, target_arch)
os.chdir(save_cwd)
return result
def _upload_binary(self, target_os, target_arch):
"""Computes the sha1 digest of the contents of the specified binary, then
uploads the contents of the file at that path to the Google Cloud Storage
bucket "mojo" using the filename "mojom_tool/$%target/$sha1." $target
is computed based upon the target_os and target_arch. A file whose name is
the name of the binary with .sha1 appended is created containing the sha1.
This method will only work if self._depot_tools_path has been set.
"""
assert self._depot_tools_path
upload_script = os.path.abspath(os.path.join(self._depot_tools_path,
"upload_to_google_storage.py"))
out_path = self._get_output_path(target_os, target_arch)
assert os.path.exists(out_path)
cloud_path = 'mojo/mojom_parser/%s' % self._get_dir_name_for_arch(
target_os, target_arch)
if self._target_dir:
cloud_path = '%s/%s' % (cloud_path, self._target_dir)
upload_cmd = ['python', upload_script, out_path, '-b', cloud_path]
stdout = None
if self._quiet:
stdout = open(os.devnull, 'w')
self._info_print(
"Uploading %s (%s,%s) to GCS..." %
(self._friendly_name, target_os, target_arch))
return subprocess.call(upload_cmd, stdout=stdout)
def _get_dir_name_for_arch(self, target_os, target_arch):
dir_names = {
('darwin', 'amd64'): 'mac64',
('linux', 'amd64'): 'linux64',
}
return dir_names[(target_os, target_arch)]
def _get_output_path(self, target_os, target_arch):
return os.path.join(
self._out_dir, '%s_%s_%s' % (self._binary_name, target_os, target_arch))
def _get_depot_tools_path(self, src_root):
name = 'find_depot_tools'
find_depot_tools = imp.load_source(name,
os.path.join(src_root, 'tools', name + '.py'))
return find_depot_tools.add_depot_tools_to_path()
def _info_print(self, message):
if self._quiet:
return
print message
def get_arg_parser(description):
"""Returns an ArgumentParser instance with the provided description and with
required arguments already added.
"""
parser = argparse.ArgumentParser(description=description)
parser.add_argument('--src-root', dest='src_root', action='store',
help='Path to the source tree. (optional)')
parser.add_argument('--go-tool', dest='go_tool', action='store',
help='Path to the go tool. (optional)')
parser.add_argument('--go-root', dest='go_root', action='store',
help='Path to the root of the go installation. '
'(optional)')
parser.add_argument('--out-dir', dest='out_dir', action='store',
help='Directory in which to place the built binary. '
'Defaults to the current working directory. '
'(optional)')
parser.add_argument('--upload', dest='upload', action='store_true',
default=False,
help='Upload the built binaries to Google Cloud Storage '
'and update the corresponding hash files locally.')
parser.add_argument('--keep-going', dest='keep_going', action='store_true',
default=False,
help='Instead of stopping when encountering a failure '
'move on to the next step.')
parser.add_argument('--quiet', dest='quiet', action='store_true',
default=False,
help='Do not output anything on success.')
return parser
def get_builder(args, target_dir, binary_name, src_path, friendly_name=None):
"""Return a GoBinaryBuilder object."""
return GoBinaryBuilder(
target_dir=target_dir,
binary_name=binary_name,
src_path=src_path,
src_root=args.src_root,
go_tool=args.go_tool,
go_root=args.go_root,
out_dir=args.out_dir,
upload=args.upload,
quiet=args.quiet,
keep_going=args.keep_going,
friendly_name=friendly_name)