| # |
| # 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)) |