#!/usr/bin/env python3 import os import shlex import common import subprocess from shell_helpers import LF class Main(common.BuildCliFunction): def __init__(self): super().__init__( description='''\ Build our compiled userland examples. ''' ) self.add_argument( '--has-package', action='append', default=[], help='''\ Indicate that a given package is present in the root filesystem, which allows us to build examples that rely on it. ''', ) self.add_argument( 'targets', default=[], help='''\ Build only the given userland programs. Default: build all examples that have their package dependencies met. For example, an OpenBLAS example can only be built if the target root filesystem has the OpenBLAS libraries and headers installed. ''', nargs='*', ) def _build_one( self, in_path, out_path, ccflags, extra_deps=None, link=True, extra_objs=None, std=None, ccflags_after=None, ): if extra_deps is None: extra_deps = [] if extra_objs is None: extra_objs = [] if ccflags_after is None: ccflags_after = [] if self.need_rebuild([in_path] + extra_objs + extra_deps, out_path): ccflags = ccflags.copy() if not link: ccflags.extend(['-c', LF]) in_ext = os.path.splitext(in_path)[1] if in_ext == self.env['c_ext']: cc = self.env['gcc'] if std is None: std = 'c11' ccflags.extend([ '-fopenmp', LF, ]) elif in_ext == self.env['cxx_ext']: cc = self.env['gxx'] if std is None: std = 'c++17' else: return self.sh.run_cmd( [ cc, LF, ] + ccflags + [ '-std={}'.format(std), LF, '-o', out_path, LF, in_path, LF, ] + extra_objs + [ '-lm', LF, '-pthread', LF, ] + ccflags_after ) def build(self): build_dir = self.get_build_dir() os.makedirs(build_dir, exist_ok=True) has_packages = set(self.env['has_package']) ccflags = [ '-I', self.env['root_dir'], LF, '-I', self.env['userland_source_dir'], LF, '-O0', LF, '-Wall', LF, '-Werror', LF, '-Wextra', LF, '-Wno-unused-function', LF, '-pedantic', LF, '-ggdb3', LF, ] if self.env['static']: ccflags.extend(['-static', LF]) common_obj = os.path.join( build_dir, self.env['common_basename_noext'] + self.env['obj_ext'] ) self._build_one( in_path=self.env['common_c'], out_path=common_obj, ccflags=ccflags, extra_deps=[self.env['common_h']], link=False, ) pkgs = { 'eigen': { # TODO: was failing with: # fatal error: Eigen/Dense: No such file or directory as of # 975ce0723ee3fa1fea1766e6683e2f3acb8558d6 # http://lists.busybox.net/pipermail/buildroot/2018-June/222914.html 'ccflags': [ '-I', os.path.join( self.env['buildroot_staging_dir'], 'usr', 'include', 'eigen3' ), LF ], # Header only. 'ccflags_after': [], }, 'libdrm': {}, 'openblas': {}, } rootdir_abs_len = len(self.env['userland_source_dir']) for path, in_dirnames, in_filenames in os.walk(self.env['userland_source_dir']): in_dirnames.sort() dirpath_relative_root = path[rootdir_abs_len + 1:] dirpath_relative_root_components = dirpath_relative_root.split(os.sep) if ( len(dirpath_relative_root_components) < 2 or dirpath_relative_root_components[0] != 'arch' or dirpath_relative_root_components[1] == self.env['arch'] ): out_dir = os.path.join( self.env['userland_build_dir'], dirpath_relative_root ) os.makedirs(out_dir, exist_ok=True) for in_filename in in_filenames: in_path = os.path.join(path, in_filename) in_name, in_ext = os.path.splitext(in_filename) out_path = os.path.join( out_dir, in_name + self.env['userland_build_ext'] ) pkg_key = in_name.split('_')[0] ccflags_file = ccflags.copy() ccflags_after = [] if pkg_key in pkgs: if pkg_key not in has_packages: continue pkg = pkgs[pkg_key] if 'ccflags' in pkg: ccflags_file.extend(pkg['ccflags']) else: pkg_config_output = subprocess.check_output([ self.env['buildroot_pkg_config'], '--cflags', pkg_key ]).decode() ccflags_file.extend(self.sh.shlex_split(pkg_config_output)) if 'ccflags_after' in pkg: ccflags_file.extend(pkg['ccflags_after']) else: pkg_config_output = subprocess.check_output([ self.env['buildroot_pkg_config'], '--libs', pkg_key ]).decode() ccflags_after.extend(self.sh.shlex_split(pkg_config_output)) self._build_one( in_path=in_path, out_path=out_path, ccflags=ccflags_file, extra_objs=[common_obj], ccflags_after=ccflags_after, ) #self.sh.run_cmd( # ( # [ # 'make', LF, # '-j', str(self.env['nproc']), LF, # 'ARCH={}'.format(self.env['arch']), LF, # 'CCFLAGS_SCRIPT={} {}'.format('-I', self.env['userland_source_dir']), LF, # 'COMMON_DIR={}'.format(self.env['root_dir']), LF, # 'CC={}'.format(self.env['gcc']), LF, # 'CXX={}'.format(self.env['gxx']), LF, # 'PKG_CONFIG={}'.format(self.env['buildroot_pkg_config']), LF, # 'STAGING_DIR={}'.format(self.env['buildroot_staging_dir']), LF, # 'OUT_DIR={}'.format(build_dir), LF, # ] + # self.sh.add_newlines([ # 'HAS_{}=y'.format(package.upper()) # for package in # self.env['has_package'] # ]) + # make_args + # self.sh.add_newlines([ # os.path.join( # build_dir, # os.path.splitext(os.path.split(target)[1])[0] # ) + self.env['userland_build_ext'] # for target in self.env['targets'] # ]) # ), # cwd=self.env['userland_source_dir'], # extra_paths=[self.env['ccache_dir']], #) self.sh.copy_dir_if_update( srcdir=build_dir, destdir=self.env['out_rootfs_overlay_dir'], filter_ext=self.env['userland_build_ext'], ) def get_build_dir(self): return self.env['userland_build_dir'] if __name__ == '__main__': Main().cli()