blob: 79cd98d472aaa7016d2d891f3e73208c5943bf16 [file] [log] [blame]
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Toolchains util functions."""
import glob
import os
import stat
from environment import sysroot_util
import error
# TODO(b/27386504): Consider loading these from a cached envsetup.
# Where the prebuilt toolchain can be found (relative to OS path).
EXISTING_TOOLS_FORMAT = os.path.join('prebuilts', 'gcc', '{host_arch}')
# Where, under the existing tools dir, can tools be found for each arch.
ARCH_TOOL_PREFIX = {
'x86': 'x86/x86_64-linux-android-4.9/bin/x86_64-linux-android-',
'arm': 'arm/arm-linux-androideabi-4.9/bin/arm-linux-androideabi-'
}
# What default flags do we want tools to run with (default none).
TOOL_FLAGS = {
'g++': ['-std=gnu++11',
'-fstack-protector-strong',
'-Wformat -Werror=format-security',
'-D_FORTIFY_SOURCE=2',
'-Werror=implicit-function-declaration',
'-fpie -pie',
'-Wl,-z,now',
'-isystem=' + os.path.join(
os.path.sep, sysroot_util.SysrootUtil.LIBCXX_INCLUDE_DIR)]
}
# Any architecture specific flags we want to be defaults.
ARCH_TOOL_FLAGS = {
'x86': {
'g++': ['-m32']
},
'arm': {
'g++': ['-marm']
},
'mips': {
# TODO(b/27722530): get mips sysroot/toolchain working.
}
}
class Error(error.Error):
pass
class GenerationError(Error):
"""Raised when the toolchain fails to generate correctly."""
description = 'Failed to generate all tools'
def _ToolFlags(tool, arch):
"""Helper to combine general and arch-specific tool flags."""
result = []
if tool in TOOL_FLAGS:
result += TOOL_FLAGS[tool]
if arch in ARCH_TOOL_FLAGS and tool in ARCH_TOOL_FLAGS[arch]:
result += ARCH_TOOL_FLAGS[arch][tool]
return result
def _GenerateWrapper(src, dest, flags=None):
"""Write a simple wrapper for a tool.
dest will call src with flags. dest will be an executable file.
Args:
src: The original tool to wrap.
dest: The place to put the wrapper.
flags: (optional) Flags to include in the wrapper. Default empty list.
Raises:
OSError: There is an error opening or otherwise accessing dest.
IOError: There is an error writing the wrapper file.
"""
flags = flags or []
with open(dest, 'w') as f:
f.write('#!/bin/sh\n{0} {1} "$@"\n'.format(src, ' '.join(flags)))
# Make sure the file is executable.
st = os.stat(dest)
os.chmod(dest, st.st_mode | stat.S_IEXEC)
def GenerateToolchain(platform, host, output_dir):
"""Generate a toolchain.
Args:
platform: Platform to generate toolchain for.
host: Host architecture to generate toolchain for.
output_dir: Where to put generated tools.
Raises:
Error: Not all tools generated properly.
"""
# Make sure output dir exists.
if not os.path.isdir(output_dir):
os.makedirs(output_dir)
# Put together some variables based on host and target.
existing_tools = platform.os.path(
EXISTING_TOOLS_FORMAT.format(host_arch=host))
tool_prefix = os.path.join(
existing_tools, ARCH_TOOL_PREFIX[platform.device.arch])
prefix_len = len(tool_prefix)
# Walk the existing tools, wrapping them all.
errors = []
for path in glob.iglob(tool_prefix + '*'):
# Skip dirs, not that there should be any.
if not os.path.isfile(path):
continue
# Otherwise, assume it's a tool and wrap it.
tool = path[prefix_len:]
try:
output_tool = os.path.join(output_dir, tool)
tool_flags = _ToolFlags(tool, platform.device.arch)
# Write a simple wrapper.
_GenerateWrapper(path, output_tool, tool_flags)
except (IOError, OSError) as e:
errors.append((tool, e))
if errors:
raise GenerationError('Failed: {}'.format(errors))