普通文本  |  236行  |  7.4 KB

#!/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:])