blob: 1fd3dbc874258c8bdd64623d8633f64f79062a70 [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.
#
"""A class to assist with sysroots."""
import os
import shutil
def _FilterHeaders(filename):
return filename.endswith('.h')
class SysrootUtil(object):
"""A class to assist with setting up sysroots.
Attributes:
sysroot - the sysroot this instance helps with.
"""
# Includes to provide in the sysroot.
# (lib, recurse, filter)
DEFAULT_INCLUDES = [
('bionic/libc/include', True, _FilterHeaders),
('bionic/libc/kernel/uapi/asm-generic/..', True, _FilterHeaders),
('bionic/libc/kernel/uapi/linux/..', True, _FilterHeaders),
('external/zlib', False, _FilterHeaders),
('system/core/include', True, _FilterHeaders)
]
DEFAULT_ARCH_INCLUDES = {
'arm': [('bionic/libc/kernel/uapi/asm-arm', True, _FilterHeaders),
('bionic/libc/arch-arm/include', True, _FilterHeaders)],
'mips': [('bionic/libc/kernel/uapi/asm-mips', True, _FilterHeaders),
('bionic/libc/arch-mips/include', True, _FilterHeaders)],
'x86': [('bionic/libc/kernel/uapi/asm-x86', True, _FilterHeaders),
('bionic/libc/arch-x86/include', True, _FilterHeaders)],
}
# Libs to provide in the sysroot.
STATIC_PATH_FORMAT = '{0}_intermediates/{0}.a'
DEFAULT_STATIC_LIBS = ['libc', 'libc++_static', 'libm', 'libz']
DEFAULT_SHARED_LIBS = ['crtbegin_so.o',
'crtbegin_dynamic.o',
'crtbegin_static.o',
'crtend_android.o',
'crtend_so.o',
'libc.so',
'libc++.so',
'libdl.so',
'libm.so',
'libz.so']
DEFAULT_LIB_RENAMES = {
'libc++.so': 'libstdc++.so',
'libc++_static.a': 'libstdc++.a'
}
# Some defaults for where things go.
DEFAULT_INCLUDE_DIR = os.path.join('usr', 'include')
DEFAULT_LIB_DIR = os.path.join('usr', 'lib')
ADDITIONAL_INCLUDE_EXTENSION = 'brillo'
# Libcxx gets special treatment.
LIBCXX_HEADERS = 'external/libcxx/include'
LIBCXX_INCLUDE_DIR = os.path.join(DEFAULT_INCLUDE_DIR, 'c++')
# pkg-config constants.
PC_DIR = os.path.join('usr', 'share', 'pkgconfig')
# For this format:
# {0} - lib name
# {1} - version
# {2} - dependencies
# {3} - normalized lib name ('lib' prefix removed)
# Note that if this format changes where to expect things,
# also change the AddProvidedLib function.
PC_FILE_FORMAT = ('prefix=/usr\n'
'exec_prefix=${{prefix}}\n'
'libdir=${{exec_prefix}}/lib\n'
'includedir=${{exec_prefix}}/include/' +
ADDITIONAL_INCLUDE_EXTENSION + '/{0}\n'
'\n'
'Name: {0}\n'
'Description: {0} for Brillo\n'
'Version: {1}\n'
'Requires: {2}\n'
'Libs: -L${{libdir}} -l{3}\n'
'Cflags: -I${{includedir}}\n')
def __init__(self, sysroot, platform):
self.sysroot = sysroot
self.platform = platform
def _FormatForCopy(self, src, dest):
"""Helper to account for special formatting signals.
Given a path to copy from and path to copy to, returns a pair suitable
to input into sysroot.AddDir.
Special format A/B/.. designates wanting only B when A has several
subdirs.
Args:
src: the path to copy from. Relative to the OS.
dest: the path to copy to. Relative to the sysroot.
Returns:
(result_src, result_dest), src and dest transformed and ready
to be passed into sysroot.AddDir.
"""
result_src = self.platform.os.path(src)
result_dest = dest
if src.endswith('/..'):
basename = os.path.basename(src[:-3])
result_src = os.path.join(result_src, basename)
result_dest = os.path.join(result_dest, basename)
return (result_src, result_dest)
def _AddPkgConfigFile(self, lib, deps, version):
"""Writes a .pc file for a lib in the sysroot.
Args:
lib: the name of the library for this .pc file.
deps: a space separated string of library names this library
depends on.
version: the version of this library.
Raises:
IOError if the file write fails.
"""
dest_dir = self.PC_DIR
normalized_lib = lib
if lib.startswith('lib'):
normalized_lib = lib[3:]
self.sysroot.WriteFile(
os.path.join(dest_dir, lib + '.pc'),
self.PC_FILE_FORMAT.format(lib, version, deps, normalized_lib))
def AddSharedLib(self, lib_src, name, deps, includes, suffix=None):
"""Adds a library to the sysroot.
Copies in the .so file, copies in includes, and generates a pkg-config
file.
Args:
lib_src: path where prebuilt shared libs can be found.
name: the name of the library to add.
deps: a space-separated string of library names lib is dependent on.
includes: a list of directories with headers for lib.
suffix: (optional) a suffix to append to the lib name when added
to the sysroot.
Raises:
IOError: If there are any issues adding the necessary files
to the sysroot.
"""
suffixed_name = name
if suffix:
suffixed_name += '-' + suffix
# Set up destinations.
# NOTE: if these change, also update the PC_FILE_FORMAT.
lib_dest = self.DEFAULT_LIB_DIR
include_dest = os.path.join(self.DEFAULT_INCLUDE_DIR,
self.ADDITIONAL_INCLUDE_EXTENSION,
suffixed_name)
self.sysroot.Makedirs(lib_dest)
self.sysroot.Makedirs(include_dest)
errors = []
# Get the .so file.
try:
self.sysroot.AddFile(os.path.join(lib_src, name + '.so'),
os.path.join(lib_dest, suffixed_name + '.so'))
except IOError as e:
errors.append('.so file: {}'.format(e))
# Copy includes (.h files only) over to sysroot.
for include in includes:
(src, dest) = self._FormatForCopy(include, include_dest)
try:
self.sysroot.AddDir(src, dest, filter_func=_FilterHeaders)
except IOError as e:
errors.append('include {}: {}'.format(include, e))
# Write the .pc file.
try:
self._AddPkgConfigFile(suffixed_name, deps,
self.platform.os.version)
except IOError as e:
errors.append('.pc file: {}'.format(e))
if errors:
raise IOError('Failed to add components for {}: {}'.format(
suffixed_name, errors))
def AddSharedLibsFromCSV(self, lib_src, csv_file, suffix=None):
"""Add libraries read from a csv file to the sysroot.
CSV file format is one line header, followed by lines of the form
libname, deps, include_dirs
Args:
lib_src: path where prebuilt shared libs can be found.
csv_file: path to the file to add libs from.
suffix: (optional) a suffix to append to all the lib names when
added to the sysroot.
Raises:
IOError: if file can't be found or adding any lib fails.
ValueError: if any line in the file doesn't meet format
expectations.
"""
libs = []
try:
with open(csv_file) as pkg_list:
# Skip the header line
pkg_list.readline()
# Read in all the remaining lines
for line in pkg_list:
lib_details = [pkg.strip() for pkg in line.split(',')]
if len(lib_details) != 3:
raise ValueError(('Line in package csv file "{0}" has '
'the incorrect number of items '
'(expected 3): {1}').format(csv_file,
line))
libs.append(lib_details)
except IOError as e:
raise IOError('Failed to open lib list: {0}'.format(e))
errors = []
for (name, deps, include_str) in libs:
try:
self.AddSharedLib(lib_src, name, deps, include_str.split(),
suffix)
except IOError as e:
errors.append(e)
if errors:
raise IOError('Failed to add libs from csv {0}: {1}'.format(
csv_file, errors))
def SetupBaseSysroot(self, arch, shared_libs, static_libs,
base_sysroot=None):
"""Sets up a sysroot dir from defaults.
Standard includes and lib files are added to
<sysroot>/usr/{include, lib}.
Args:
arch: the architecture to setup a sysroot for.
shared_libs: path where prebuilt shared libs can be found.
static_libs: path where prebuilt static libs can be found.
base_sysroot: (optional). If provided, copies all files of the
sysroot found at this path, with priority over other files.
Raises:
IOError: if anything fails, specifying which things failed.
"""
errors = []
# Copy in includes.
self.sysroot.Makedirs(self.DEFAULT_INCLUDE_DIR)
for (include, recurse, filter_func) in (
self.DEFAULT_INCLUDES + self.DEFAULT_ARCH_INCLUDES[arch]):
(src, dest) = self._FormatForCopy(include, self.DEFAULT_INCLUDE_DIR)
try:
self.sysroot.AddDir(src, dest, recurse, filter_func)
except IOError as e:
errors.append('{}: {}'.format(include, e))
# Handle libcxx.
(src, dest) = self._FormatForCopy(self.LIBCXX_HEADERS,
self.LIBCXX_INCLUDE_DIR)
try:
self.sysroot.AddDir(src, dest)
except IOError as e:
errors.append(self.LIBCXX_HEADERS)
# Copy in libs.
self.sysroot.Makedirs(self.DEFAULT_LIB_DIR)
for lib in self.DEFAULT_SHARED_LIBS:
try:
self.sysroot.AddFile(os.path.join(shared_libs, lib),
self.DEFAULT_LIB_DIR)
except IOError as e:
errors.append(lib)
for lib in self.DEFAULT_STATIC_LIBS:
try:
self.sysroot.AddFile(
os.path.join(static_libs,
self.STATIC_PATH_FORMAT.format(lib)),
self.DEFAULT_LIB_DIR)
except IOError as e:
errors.append(lib)
# Do some renaming.
for (src, dest) in self.DEFAULT_LIB_RENAMES.iteritems():
try:
shutil.move(self.sysroot.Path(self.DEFAULT_LIB_DIR, src),
self.sysroot.Path(self.DEFAULT_LIB_DIR, dest))
except IOError as e:
errors.append('rename {0} --> {1}'.format(src, dest))
# Base our sysroot on another.
# We do this after copying in defaults to give this base priority.
if base_sysroot:
try:
self.sysroot.AddDir(base_sysroot)
except IOError as e:
errors.append(base_sysroot)
if errors:
raise IOError(
'Failed to add {0} during sysroot setup.'.format(errors))