#!/usr/bin/env python2
#
# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# pylint: disable=cros-logging-import
"""Script to build the benchmark locally with toolchain settings."""
from __future__ import print_function
import argparse
import config
import logging
import os
import subprocess
import sys
# Turn the logging level to INFO before importing other code, to avoid having
# failed import logging messages confuse the user.
logging.basicConfig(level=logging.INFO)
def _parse_arguments_internal(argv):
parser = argparse.ArgumentParser(description='Build benchmarks with '
'specified toolchain settings')
parser.add_argument(
'-b', '--bench', required=True, help='Select the benchmark to be built.')
parser.add_argument(
'-c',
'--compiler_dir',
metavar='DIR',
help='Specify the path to the compiler bin '
'directory.')
parser.add_argument(
'-o', '--build_os', help='Specify the host OS to build benchmark.')
parser.add_argument(
'-l',
'--llvm_prebuilts_version',
help='Specify the version of prebuilt LLVM.')
parser.add_argument(
'-f',
'--cflags',
help='Specify the optimization cflags for '
'the toolchain.')
parser.add_argument(
'--ldflags', help='Specify linker flags for the toolchain.')
return parser.parse_args(argv)
# Set flags for compiling benchmarks, by changing the local
# CFLAGS/LDFLAGS in the android makefile of each benchmark
def set_flags(bench, cflags, ldflags):
if not cflags:
logging.info('No CFLAGS specified, using default settings.')
cflags = ''
else:
logging.info('Cflags setting to "%s"...', cflags)
if not ldflags:
logging.info('No LDFLAGS specifed, using default settings.')
ldflags = ''
else:
logging.info('Ldflags setting to "%s"...', ldflags)
add_flags = config.bench_flags_dict[bench]
add_flags(cflags, ldflags)
logging.info('Flags set successfully!')
def set_build_os(build_os):
# Set $BUILD_OS variable for android makefile
if build_os:
os.environ['BUILD_OS'] = build_os
logging.info('BUILD_OS set to "%s"...', build_os)
else:
logging.info('No BUILD_OS specified, using linux as default...')
def set_llvm_prebuilts_version(llvm_prebuilts_version):
# Set $LLVM_PREBUILTS_VERSION for android makefile
if llvm_prebuilts_version:
os.environ['LLVM_PREBUILTS_VERSION'] = llvm_prebuilts_version
logging.info('LLVM_PREBUILTS_VERSION set to "%s"...',
llvm_prebuilts_version)
else:
logging.info('No LLVM_PREBUILTS_VERSION specified, using default one...')
def set_compiler(compiler):
# If compiler_dir has been specified, copy the binaries to
# a temporary location, set BUILD_OS and LLVM_PREBUILTS_VERSION
# variables to the location
if compiler:
# Report error if path not exits
if not os.path.isdir(compiler):
logging.error('Error while setting compiler: '
'Directory %s does not exist!', compiler)
raise OSError('Directory %s not exist.' % compiler)
# Specify temporary directory for compiler
tmp_dir = os.path.join(config.android_home,
'prebuilts/clang/host/linux-x86', 'clang-tmp')
compiler_content = os.path.join(compiler, '.')
# Copy compiler to new directory
try:
subprocess.check_call(['cp', '-rf', compiler_content, tmp_dir])
except subprocess.CalledProcessError:
logging.error('Error while copying the compiler to '
'temporary directory %s!', tmp_dir)
raise
# Set environment variable
os.environ['LLVM_PREBUILTS_VERSION'] = 'clang-tmp'
logging.info('Prebuilt Compiler set as %s.', os.path.abspath(compiler))
def set_compiler_env(bench, compiler, build_os, llvm_prebuilts_version, cflags,
ldflags):
logging.info('Setting compiler options for benchmark...')
# If no specific prebuilt compiler directory, use BUILD_OS and
# LLVM_PREBUILTS_VERSION to set the compiler version.
# Otherwise, use the new prebuilt compiler.
if not compiler:
set_build_os(build_os)
set_llvm_prebuilts_version(llvm_prebuilts_version)
else:
set_compiler(compiler)
set_flags(bench, cflags, ldflags)
return 0
def remove_tmp_dir():
tmp_dir = os.path.join(config.android_home, 'prebuilts/clang/host/linux-x86',
'clang-tmp')
try:
subprocess.check_call(['rm', '-r', tmp_dir])
except subprocess.CalledProcessError:
logging.error('Error while removing the temporary '
'compiler directory %s!', tmp_dir)
raise
# Recover the makefile/blueprint from our patch after building
def restore_makefile(bench):
pwd = os.path.join(config.android_home, config.bench_dict[bench])
mk_file = os.path.join(pwd, 'Android.mk')
if not os.path.exists(mk_file):
mk_file = os.path.join(pwd, 'Android.bp')
subprocess.check_call(['mv', os.path.join(pwd, 'tmp_makefile'), mk_file])
# Run script to build benchmark
def build_bench(bench, source_dir):
logging.info('Start building benchmark...')
raw_cmd = ('cd {android_home} '
'&& source build/envsetup.sh '
'&& lunch {product_combo} '
'&& mmma {source_dir} -j48'.format(
android_home=config.android_home,
product_combo=config.product_combo,
source_dir=source_dir))
log_file = os.path.join(config.bench_suite_dir, 'build_log')
with open(log_file, 'a') as logfile:
log_head = 'Log for building benchmark: %s\n' % (bench)
logfile.write(log_head)
try:
subprocess.check_call(
['bash', '-c', raw_cmd], stdout=logfile, stderr=logfile)
except subprocess.CalledProcessError:
logging.error('Error while running %s, please check '
'%s for more info.', raw_cmd, log_file)
restore_makefile(bench)
raise
logging.info('Logs for building benchmark %s are written to %s.', bench,
log_file)
logging.info('Benchmark built successfully!')
def main(argv):
arguments = _parse_arguments_internal(argv)
bench = arguments.bench
compiler = arguments.compiler_dir
build_os = arguments.build_os
llvm_version = arguments.llvm_prebuilts_version
cflags = arguments.cflags
ldflags = arguments.ldflags
try:
source_dir = config.bench_dict[bench]
except KeyError:
logging.error('Please select one benchmark from the list below:\n\t' +
'\n\t'.join(config.bench_list))
raise
set_compiler_env(bench, compiler, build_os, llvm_version, cflags, ldflags)
build_bench(bench, source_dir)
# If flags has been set, remember to restore the makefile/blueprint to
# original ones.
restore_makefile(bench)
# If a tmp directory is used for compiler path, remove it after building.
if compiler:
remove_tmp_dir()
if __name__ == '__main__':
main(sys.argv[1:])