def _get_default_cmake_generator(self): if 'DEFAULT_CMAKE_GENERATOR' in os.environ: generator_alias = os.environ['DEFAULT_CMAKE_GENERATOR'] if generator_alias not in self._dict_to_cmake_generator: raise Exception( "CMake generator " + generator_alias + " defined by environment variable DEFAULT_CMAKE_GENERATOR is unsupported." ) return generator_alias if self._sys_info.get_platform() == 'linux': generator_alias = 'umake' elif self._sys_info.get_platform() == 'macosx': generator_alias = 'xcode' elif self._sys_info.get_platform() == 'windows': # e.g. 14.2, 14.1, 14.0, 12.0 etc. bb_vs_latest_version = self._msvc_registry.get_latest_version() if ver.version_compare(bb_vs_latest_version, (14, 2)) == 0: generator_alias = 'vs16' elif ver.version_compare(bb_vs_latest_version, (14, 1)) == 0: generator_alias = 'vs15' else: generator_alias = 'vs' + str(bb_vs_latest_version[0]) else: assert False return generator_alias
def _add_cmake_build_jobs_option(self, cmake_argv, generator_alias, build_jobs): cmake_version = self._cmake_finder.get_cmake_version() if ver.version_compare(cmake_version, (3, 12)) >= 0: assert len(cmake_argv) >= 2 if build_jobs >= 2: if generator_alias.startswith('vs'): self._add_cmake_build_tool_options( cmake_argv, ['/maxcpucount:' + str(build_jobs)]) else: cmake_argv.insert(2, str(build_jobs)) cmake_argv.insert(2, '--parallel') elif build_jobs == 0: # Use the build engine's native number of jobs. cmake_argv.insert(2, '--parallel') elif build_jobs >= 2: if generator_alias in ['umake', 'ninja']: self._add_cmake_build_tool_options(cmake_argv, ['-j' + str(build_jobs)]) elif generator_alias.startswith('vs'): self._add_cmake_build_tool_options( cmake_argv, ['/maxcpucount:' + str(build_jobs)]) elif generator_alias == 'xcode': self._add_cmake_build_tool_options( cmake_argv, ['-parallelizeTargets', '-jobs', str(build_jobs)])
def get_subprocess_devnull(self): if ver.version_compare(self._python_version, (3, 3)) >= 0: devnull = subprocess.DEVNULL else: self._logger.debug( "attribute subprocess.DEVNULL not available (python < 3.3), using os.devnull instead" ) devnull = self._get_devnull() return devnull
def test_version_compare(self): self.assertEqual( 0, ver.version_compare(ver.version_tuple_from_str('10.7.4'), ver.version_tuple_from_str('10.7.4'))) self.assertEqual( 1, ver.version_compare(ver.version_tuple_from_str('10.7.4'), ver.version_tuple_from_str('10.6.4'))) self.assertEqual( -1, ver.version_compare(ver.version_tuple_from_str('10.6.4'), ver.version_tuple_from_str('10.7.4'))) self.assertEqual( -1, ver.version_compare(ver.version_tuple_from_str('10.7'), ver.version_tuple_from_str('10.7.4'))) self.assertEqual( 1, ver.version_compare(ver.version_tuple_from_str('10.7.4'), ver.version_tuple_from_str('10.7'))) self.assertEqual( 0, ver.version_compare(ver.version_tuple_from_str('10.7.0'), ver.version_tuple_from_str('10.7')))
def _get_default_cmake_generator(self): if self._sys_info.get_platform() == 'linux': generator_alias = 'umake' elif self._sys_info.get_platform() == 'macosx': generator_alias = 'xcode' elif self._sys_info.get_platform() == 'windows': # e.g. 14.1, 14.0, 12.0 etc. bb_vs_latest_version = self._msvc_registry.get_latest_version() if ver.version_compare(bb_vs_latest_version, (14, 1)) == 0: generator_alias = 'vs15' else: generator_alias = 'vs' + str(bb_vs_latest_version[0]) else: assert False return generator_alias
def _is_vs_64bit_native_toolset_supported(self, generator_alias): re_vs_generator = re.compile(r'vs(\d+)$') re_match = re_vs_generator.match(generator_alias) if not re_match: return False if not self._prefer_vs_native_toolsets: # Visual Studio native 64bit toolchains are disabled return False if self._sys_info.get_os_arch() != 'x86_64': return False if int(re_match.group(1), 10) < 12: # Visual Studio 11 2012 or earlier don't have native 64 bit toolchains. return False if ver.version_compare(self._cmake_finder.get_cmake_version(), (3, 8, 0)) < 0: # cmake too old to support vs native toolchains. return False return True
def _add_cmake_build_verbosity_option(self, cmake_argv, generator_alias, verbosity_level): if verbosity_level == 'cmake': cmake_version = self._cmake_finder.get_cmake_version() if ver.version_compare(cmake_version, (3, 14)) >= 0: # self._add_cmake_build_tool_options(cmake_argv, ['-v']) # -v is a cmake option and not a build tool option and therefore # it has to be inserted left of '--' if '--' in cmake_argv: index = cmake_argv.index('--') cmake_argv.insert(index, '-v') else: cmake_argv.append('-v') else: if generator_alias.startswith('vs'): self._add_cmake_build_tool_options( cmake_argv, ['/verbosity:' + verbosity_level])
def _create_mk_toolchain_script(self, ndk_root, hash_bang_missing): if ver.version_compare(self._ndk_version, (12, 0)) >= 0: mk_toolchain_script_basename = 'make_standalone_toolchain.py' else: mk_toolchain_script_basename = 'make-standalone-toolchain.sh' mk_toolchain_script = os.path.join(ndk_root, 'build', 'tools', mk_toolchain_script_basename) # print("mk_toolchain_script: " + mk_toolchain_script) assert os.path.exists(mk_toolchain_script) if hash_bang_missing: mk_toolchain_script_tmp = os.path.join( ndk_root, 'build', 'tools', 'make-standalone-toolchain.bash') self._add_bash_hash_bang(mk_toolchain_script, mk_toolchain_script_tmp) else: mk_toolchain_script_tmp = mk_toolchain_script return mk_toolchain_script_tmp
def _update_build_params(self, toolset, build_params): platform_info = toolset.get_platform_info(build_params.platform_index) # modify the build parameters based on the toolset properties if platform_info.get_target_os() == 'android': if (toolset.get_toolset() == 'gcc') and (ver.version_compare(toolset.get_version(), (4, 8)) < 0): raise Exception( "This gcc version is not supported on android, use gcc 4.8 or higher to get decent c++11 support." ) build_params.cxx_std = 'c++11' if not build_params.boost_with_list: # use the standard boost library set for android build_params.boost_with_list = self._boost_libs_android # TODO: evaluate boost version and add some more libraries elif (platform_info.get_target_os() == 'iphone') or (platform_info.get_target_os() == 'iphonesimulator'): build_params.cxx_std = 'c++11' if not build_params.boost_with_list: # use the standard boost library set for ios build_params.boost_with_list = self._boost_libs_ios
def main(self, argv): _description = """ This script creates a standalone NDK toolchain to be used with Boost.Build or another build system outside of the Android SDK/NDK build environment. """ _epilog = """ Examples: # create a default set of standalone toolchains %(prog)s # override the default API level %(prog)s --api-level=21 """ parser = argparse.ArgumentParser( description=_description, epilog=_epilog, formatter_class=argparse.RawDescriptionHelpFormatter) util.app_args_add_log_level(parser) parser.add_argument( "--unified-headers", action="store_true", dest="unified_headers", default=False, help= "enable unified headers [valid for NDK r14 and higher, enabled for NDK r15]." ) parser.add_argument( "--api-level", action="store", dest="api_level", type=int, help="override the default Android API level [default: latest].") parser.add_argument( "--stl", action="store", dest="stl", choices=['gnustl', 'libc++'], default='gnustl', help= "--stl=libc++ overrides the default c++ runtime, [default: gnustl to be compatible with Qt]." ) parser.add_argument( "--inst-dir", action="store", dest="inst_dir", help= "specify the directory to save the toolchain, [default: $HOME/bin/ndksa]." ) parser.add_argument( "toolchain", action="store", nargs='?', help= "specify the NDK toolchain to be converted into a standalone toolchain." " Use default to create toolchains for all supported architectures." ) args = parser.parse_args(argv) # configure the python logger util.app_configure_logging(args.log_level) # create the NDK finder ndk_finder = pyhhi.build.common.android.NdkFinder() ndk_version = ndk_finder.get_ndk_version() if ver.version_compare(ndk_version, (15, 0)) >= 0: args.unified_headers = True if (args.toolchain is None) or (args.toolchain == 'default'): for toolchain in [ 'arm-linux-androideabi-4.9', 'aarch64-linux-android-4.9', 'x86-4.9', 'x86_64-4.9' ]: ndk_finder.create_ndksa_toolchain( toolchain, api_level=args.api_level, ndk_stl=args.stl, inst_dir=args.inst_dir, unified_headers=args.unified_headers) else: # create the NDK standalone toolchain ndk_finder.create_ndksa_toolchain( args.toolchain, api_level=args.api_level, ndk_stl=args.stl, inst_dir=args.inst_dir, unified_headers=args.unified_headers) # list available toolchains print('%-42s %s' % (' ', 'Summary')) if args.inst_dir is None: print('%-42s %s' % ('NDK-SA root:', ndk_finder.get_ndksa_root(None))) ndksa_toolchains = ndk_finder.get_ndksa_toolchains(args.stl) if ndksa_toolchains: print('%-42s %s' % ('Avail. standalone toolchains using ' + args.stl + ':', ' '.join(ndksa_toolchains))) if ver.version_compare(ndk_version, (11, 0)) >= 0: compiler_flavor = 'clang++' else: compiler_flavor = 'g++' if args.inst_dir is None: print('\nExample: boost_install.py toolset=' + ndksa_toolchains[0] + '-' + compiler_flavor) else: print('\nExample: boost_install.py toolset=' + os.path.join(args.inst_dir, 'bin', ndksa_toolchains[0] + '-' + compiler_flavor)) print('\n')
def cmakebuild_update(self, params): initial_work_dir = os.getcwd() # (Re)create GitHelper in case the log level has been changed. self._git_helper = vcsutil.GitHelper() params = self._check_params(params) if params.top_dir is None: params.top_dir = util.get_top_dir() self._summary_lines = [] if params.update_prepare: print("{}: preparing CMakeBuild update ...".format(self._prog_name)) cmakebuild_version_current = self._get_current_cmakebuild_version(params.top_dir) if cmakebuild_version_current is None: print("{}: no existing CMakeBuild version found.".format(params.top_dir)) if not self._is_workspace_clean(params.top_dir): return if params.cmakebuild_tag is None: cmakebuild_tag = self._git_helper.get_latest_cmakebuild_tag(params.cmakebuild_repo) if cmakebuild_version_current and (ver.version_compare(cmakebuild_version_current, ver.version_tuple_from_str(cmakebuild_tag)) == 0): print("{}: CMakeBuild is up to date, nothing to be done.".format(params.top_dir)) return else: # Validate CMakeBuild tag cmakebuild_tags = self._git_helper.get_remote_tags(params.cmakebuild_repo) if params.cmakebuild_tag not in cmakebuild_tags: raise InvalidInputParameterError("requested tag {0} does not exists in {1}.".format(params.cmakebuild_tag, params.cmakebuild_repo)) cmakebuild_tag = params.cmakebuild_tag if cmakebuild_version_current and (ver.version_compare(ver.version_tuple_from_str(cmakebuild_tag), cmakebuild_version_current) <= 0): print("{}: CMakeBuild is up to date, nothing to be done.".format(params.top_dir)) return install_dir = os.path.join(params.top_dir, 'build', 'cmakebuild_update') self._install_self(install_dir) update_script = os.path.join(install_dir, self._prog_name) # Prepare execv argument vector to call self with different arguments using the current python executable. if self._sys_info.is_windows(): child_args = [self._sys_info.get_short_path(self._sys_info.get_python_executable())] else: child_args = [self._sys_info.get_python_executable()] child_args.extend([update_script, '--update']) if cmakebuild_tag: child_args.extend(['-t', cmakebuild_tag]) if params.cmakebuild_repo: child_args.extend(['--cmakebuild-repo', params.cmakebuild_repo]) # Add currrent log option to child_args[] to propagate the current log option to the child process. log_level = self._logger.getEffectiveLevel() log_level_str = None if log_level == logging.DEBUG: log_level_str = 'debug' elif log_level == logging.INFO: log_level_str = 'info' elif log_level == logging.WARNING: log_level_str = 'warning' elif log_level == logging.ERROR: log_level_str = 'error' elif log_level == logging.CRITICAL: log_level_str = 'critical' if log_level_str is not None: child_args.append('--log={}'.format(log_level_str)) child_args.append(params.top_dir) os.chdir(params.top_dir) if self._sys_info.is_windows(): # Unfortunately, there are issues with os.execv() on Windows and therefore a subprocess call is used instead. util.subproc_check_call_flushed(child_args) else: # execv() is preferred as the child is likely to remove python files just being executed # by the current python process. os.execv(child_args[0], child_args) else: try: print("{}: updating CMakeBuild in {} to {}".format(self._prog_name, params.top_dir, params.cmakebuild_tag)) os.chdir(params.top_dir) if not self._is_workspace_clean(): return self._append_item_to_summary("Git workspace:", params.top_dir) if self._list_summary: cmakebuild_version_current = self._get_current_cmakebuild_version(params.top_dir) if cmakebuild_version_current is not None: self._append_item_to_summary("Current CMakeBuild version:", "{}-{:d}".format(ver.version_tuple_to_str(cmakebuild_version_current[:3]), cmakebuild_version_current[-1])) self._append_item_to_summary("New CMakeBuild version:", params.cmakebuild_tag) # Inventory of CMakeBuild to remember existing directories to be removed later on. (git_cmakebuild_dirs, cmakebuild_dirs) = self._get_git_cmakebuild_dirs(params.top_dir) if params.cmakebuild_tag in git_cmakebuild_dirs: git_cmakebuild_dirs.remove(params.cmakebuild_tag) if params.cmakebuild_tag in cmakebuild_dirs: cmakebuild_dirs.remove(params.cmakebuild_tag) # print("found existing Git CMakeBuild subdirs:", git_cmakebuild_dirs) # print("found existing CMakeBuild subdirs:", cmakebuild_dirs) # Add new CMakeBuild subtree self._add_cmakebuild_to_git_repo(params.cmakebuild_tag, params.cmakebuild_repo) # Update .svnimportprops in case is does exist. svnimportprops_file = os.path.join(params.top_dir, '.svnimportprops') svnimportprops_modified = self._update_svnimportprops(svnimportprops_file, params.cmakebuild_tag) for dname in git_cmakebuild_dirs: git_argv = [self._git_executable, 'rm', '-r', os.path.join('CMakeBuild', dname)] util.subproc_check_call_flushed(git_argv) if svnimportprops_modified or git_cmakebuild_dirs: # Need a git commit if '.svnimportprops' has been updated or git rm -r has been launched. git_comment = [] if git_cmakebuild_dirs: git_comment.append("rm -r CMakeBuild/{}".format(','.join(git_cmakebuild_dirs))) if svnimportprops_modified: git_comment.append("modifying {}".format('.svnimportprops')) git_argv = [self._git_executable, 'commit', '-am', '{}: {}'.format(self._prog_name, ', '.join(git_comment))] util.subproc_check_call_flushed(git_argv) # Check again to get rid of any python cache files cmakebuild_dirs.extend(git_cmakebuild_dirs) for dname in cmakebuild_dirs: dname_path = os.path.join(params.top_dir, 'CMakeBuild', dname) if os.path.exists(dname_path): util.rmtree(dname_path) print("{}: finished updating CMakeBuild in {} to {}".format(self._prog_name, params.top_dir, params.cmakebuild_tag)) if self._list_summary: print("\n{0:^80}".format("Summary")) print("{0:^80}".format("=======")) for line in self._summary_lines: print(line) finally: pass os.chdir(initial_work_dir)
def build_boost(self, build_params): #print("BoostBuilder.build_boost() starting ...") # extract the boost version boost_version = get_boost_version(build_params.boost_dir) self._boost_version = boost_version # print("BoostBuilder: boost version:", boost_version) # check the boost version against the minimal version we support if ver.version_compare(boost_version, self._min_boost_version) < 0: raise Exception( "The boost version " + ver.version_tuple_to_str(boost_version) + " is not supported anymore, please contact technical support.") # construct a new toolset toolset = bldtools.Toolset(self._sys_info, build_params.toolset, build_params.cxx_runtime) self._toolset = toolset if toolset.get_toolset() == 'intel': if self._sys_info.is_windows(): # Update MSVC environment to be used with the Intel compiler if build_params.msvc_rt is None: # Use latest MSVC by default msvc_registry = bldtools.MsvcRegistry() msvc_version = msvc_registry.get_latest_version() build_params.msvc_rt = "msvc-%d.%d" % (msvc_version[0], msvc_version[1]) # self._intel_msvc_suffix = self._intel_msvc_suffix_dict[ build_params.msvc_rt] elif self._sys_info.is_macosx(): if ver.version_compare(boost_version, (1, 66, 0)) == 0: # Is intel-darwin.jam patched? intel_darwin_boost = os.path.join(build_params.boost_dir, 'tools', 'build', 'src', 'tools', 'intel-darwin.jam') intel_darwin_patched = os.path.join( util.get_top_dir(), 'CMakeBuild', 'patches', 'Boost', ver.version_tuple_to_str(boost_version), 'tools', 'build', 'src', 'tools', 'intel-darwin.jam') assert os.path.exists(intel_darwin_boost) assert os.path.exists(intel_darwin_patched) if not filecmp.cmp(intel_darwin_boost, intel_darwin_patched, shallow=False): raise Exception( """intel-darwin.jam requires a patch. Consider a manual update: cp %s %s or contact technical support. """ % (intel_darwin_patched, intel_darwin_boost)) platform_info = toolset.get_platform_info(build_params.platform_index) if (platform_info.get_target_os() == 'macosx') and (build_params.macosx_version_min is None): # At times Xcode ships with OSX SDK version > macosx version, this is not permitted by default. if ver.version_compare(platform_info.get_sdk_version(), self._sys_info.get_os_version()) > 0: build_params.macosx_version_min = self._sys_info.get_os_version( )[:2] # check the optional target if build_params.targets: for target in build_params.targets: if target not in platform_info.get_target_arch(): raise Exception( "The target " + target + " is not supported. Please check target and toolset.") else: # no target(s) specified, use the defaults if toolset.get_toolset() == 'msvc': build_params.targets = list(platform_info.get_target_arch()) elif platform_info.get_target_os() in [ 'iphone', 'iphonesimulator' ]: if build_params.macosx_version_min: target_os_version = build_params.macosx_version_min else: target_os_version = platform_info.get_target_os_version() if ver.version_compare(target_os_version, (11, 0)) >= 0: # No support for 32 bit IOS targets anymore. if platform_info.get_target_os() == 'iphone': build_params.targets = ['arm64'] elif platform_info.get_target_os() == 'iphonesimulator': build_params.targets = ['x86_64'] else: assert False else: if platform_info.get_target_os() == 'iphone': build_params.targets = ['arm64', 'armv7'] elif platform_info.get_target_os() == 'iphonesimulator': build_params.targets = ['x86_64', 'x86'] else: assert False else: build_params.targets = [platform_info.get_target_arch(0)] # update the build parameters given the attributes of the toolset. self._update_build_params(toolset, build_params) #print(build_params) #return boost_lib_files_missing = False lib_dir_list = [] if build_params.rebuild_all: boost_lib_files_missing = True if not build_params.dry_run: for target in build_params.targets: if platform_info.get_target_os() == 'windows': bin_dir = self.get_boost_bin_dir( build_params.boost_dir, toolset, build_params.platform_index, target) if os.path.exists(bin_dir): shutil.rmtree(bin_dir) lib_dir = self.get_boost_lib_dir( build_params.boost_dir, toolset, build_params.platform_index, target) lib_dir_list.append(lib_dir) if os.path.exists(lib_dir): shutil.rmtree(lib_dir) else: for target in build_params.targets: lib_dir = self.get_boost_lib_dir(build_params.boost_dir, toolset, build_params.platform_index, target) lib_dir_list.append(lib_dir) if not os.path.exists(lib_dir): boost_lib_files_missing = True if boost_lib_files_missing: self._build_libs(boost_version, toolset, build_params) if not build_params.dry_run: if self._sys_info.is_macosx(): if len(build_params.targets) > 1: # create a fat binary/universal library file fat_binary_tool = bldtools.FatBinaryTool() platform_info = toolset.get_platform_info( build_params.platform_index) assert ((platform_info.get_target_os() == 'iphone') or (platform_info.get_target_os() == 'iphonesimulator')) src_lib_dirs = [] for target in build_params.targets: lib_dir = self.get_boost_lib_dir( build_params.boost_dir, toolset, build_params.platform_index, target) src_lib_dirs.append(lib_dir) dst_lib_dir = self.get_boost_lib_dir( build_params.boost_dir, toolset, build_params.platform_index, 'combined') if os.path.exists(dst_lib_dir): shutil.rmtree(dst_lib_dir) fat_binary_tool.createLibs(src_lib_dirs, dst_lib_dir) # if ver.version_compare( (1, 59, 0), self._boost_version) > 0: platform_info = toolset.get_platform_info( build_params.platform_index) if platform_info.get_target_os() == 'macosx': lib_dir = self.get_boost_lib_dir( build_params.boost_dir, toolset, build_params.platform_index, build_params.targets[0]) print( "Checking install names and dependencies in " + lib_dir) dylib_list = glob.glob(lib_dir + '/*.dylib') inst_name_inspector = bldtools.DyLibInstallNameInfoInspector( ) for dylib in dylib_list: inst_name_info = inst_name_inspector.create_install_name_info( dylib) if not inst_name_info.inst_name.startswith( '@rpath/'): inst_name_inspector.modify_install_name( dylib, os.path.join('@rpath', inst_name_info.basename)) # Walk the dependency list and change all boost libraries from libboost_NNN.dylib to @rpath/libboost_NNN.dylib. depends_dict = {} for libname in inst_name_info.depends_list: if libname.startswith('libboost_'): depends_dict[ libname] = os.path.join( '@rpath', libname) if depends_dict: print("Changing dependencies of " + os.path.basename(dylib)) inst_name_inspector.modify_depends( dylib, depends_dict) print("\n") print( "-------------------- Post build phase --------------------" ) for lib_dir in lib_dir_list: self._list_boost_components(lib_dir) print( "----------------------------------------------------------" ) else: print( "No boost library directories are missing, skipping library build step." ) print("Use --rebuild if you intend to rebuild all libraries.")
def launch_config(self, compiler_info, generator_alias, build_configs, lnk_variant, cmake_argv_optional): cur_dir = os.getcwd() cmake_argv = [] if self._is_multi_configuration_generator(): tmp_build_configs = [build_configs[0]] else: tmp_build_configs = build_configs for cfg in tmp_build_configs: b_dir = self._build_tree_info.get_build_dir(cfg, lnk_variant) if not os.path.exists(b_dir): os.makedirs(b_dir) if generator_alias.startswith('vs'): if compiler_info.compiler_family == 'msvc': if self._is_vs_64bit_native_toolset_supported( generator_alias): vs_toolset = self._dict_to_vs_platform_toolset[ 'msvc-' + ver.version_tuple_to_str( compiler_info.version_major_minor )] + ',host=x64' else: vs_toolset = self._dict_to_vs_platform_toolset[ 'msvc-' + ver.version_tuple_to_str( compiler_info.version_major_minor)] elif compiler_info.compiler_family == 'intel': vs_toolset = "Intel C++ Compiler %d.%d" % ( compiler_info.version_major_minor[0], compiler_info.version_major_minor[1]) else: assert False cmake_argv = [ '-G', self._dict_to_cmake_generator[generator_alias] ] if generator_alias == 'vs16': if ver.version_compare(compiler_info.version_major_minor, (19, 20)) < 0: cmake_argv.extend([ '-T', self._dict_to_vs_platform_toolset[ 'msvc-' + ver.version_tuple_to_str( compiler_info.version_major_minor)] ]) if compiler_info.target_arch != 'x86_64': cmake_argv.extend([ '-A', self._dict_to_vs_platform_name[ compiler_info.target_arch] ]) else: cmake_argv.extend([ '-T', vs_toolset, '-A', self._dict_to_vs_platform_name[ compiler_info.target_arch] ]) elif generator_alias == 'xcode': cmake_argv = [ '-G', self._dict_to_cmake_generator[generator_alias] ] elif generator_alias in ['umake', 'mgwmake', 'ninja']: cmake_argv = [ '-G', self._dict_to_cmake_generator[generator_alias], '-DCMAKE_BUILD_TYPE:STRING=' + self._dict_to_cmake_config[cfg] ] if compiler_info.is_cross_compiler(): cmake_argv.append('-DCMAKE_TOOLCHAIN_FILE:FILEPATH=' + compiler_info.cmake_toolchain_file) else: if compiler_info.cmake_cxx_compiler: cmake_argv.append('-DCMAKE_CXX_COMPILER:FILEPATH=' + compiler_info.cmake_cxx_compiler) if compiler_info.cmake_c_compiler: cmake_argv.append('-DCMAKE_C_COMPILER:FILEPATH=' + compiler_info.cmake_c_compiler) if cmake_argv_optional: # Add any additional arguments to the cmake command line. cmake_argv.extend(cmake_argv_optional) if lnk_variant == 'shared': cmake_argv.append('-DBUILD_SHARED_LIBS:BOOL=ON') if self._is_multi_configuration_generator(): cmake_config_types = [ self._dict_to_cmake_config[x] for x in self._default_config_types ] for b_cfg in build_configs: if b_cfg not in self._default_config_types: cmake_config_types.append( self._dict_to_cmake_config[b_cfg]) cmake_argv.append('-DCMAKE_CONFIGURATION_TYPES:STRING=' + ';'.join(cmake_config_types)) # cmake_argv.append(self._top_dir) # print("launch_config(): cmake_args", cmake_argv) # print("build dir:", b_dir) # print("top dir:", self._top_dir) if ver.version_compare(self._cmake_finder.get_cmake_version(), (3, 13, 0)) >= 0: cmake_argv.extend(['-S', self._top_dir, '-B', b_dir]) retv = self.launch_cmake(cmake_argv) else: os.chdir(b_dir) cmake_argv.append(os.path.relpath(self._top_dir)) retv = self.launch_cmake(cmake_argv) os.chdir(cur_dir) if retv != 0: sys.exit(1)
def create_ndksa_toolchain(self, ndk_toolchain, api_level=None, unified_headers=False, ndk_stl='gnustl', ndk_llvm_version=None, inst_dir=None): # whether bash scripts have proper hash bang lines or not. hash_bang_missing = False if ver.version_compare(self._ndk_version, (11, 0)) < 0: hash_bang_missing = True # ndk_toolchain := arm-linux-androideabi-4.9, arm-linux-androideabi-clang3.6, arm-linux-androideabi # := arm-linux-androideabi-4.9 aarch64-linux-android-4.9 x86-4.9 x86_64-4.9 ndk_toolchain_parts = ndk_toolchain.split('-') if re.match('(clang)?[0-9.]+$', ndk_toolchain_parts[-1]): joiner = '-' ndk_toolchain_prefix = joiner.join(ndk_toolchain_parts[:-1]) else: ndk_toolchain_prefix = ndk_toolchain if ndk_toolchain_prefix in self._ndk_toolchain_dict: if inst_dir is None: ndksa_inst_dir = os.path.join( self.get_ndksa_root(ndk_stl), self._ndk_toolchain_dict[ndk_toolchain_prefix]) else: ndksa_inst_dir = os.path.join( inst_dir, ndk_stl, self._ndk_toolchain_dict[ndk_toolchain_prefix]) else: raise Exception("The NDK toolchain " + ndk_toolchain + " is not supported.") # Create a possibly patched version of the original script which can be invoked from python. mk_toolchain_script = self._create_mk_toolchain_script( self._ndk_root, hash_bang_missing) # build the argument vector mk_toolchain_args = [mk_toolchain_script] if api_level is None: api_level = self.get_api_level_from_platform( self._ndk_platforms[-1]) if (ver.version_compare(self._ndk_version, (14, 0)) >= 0) and (ver.version_compare( self._ndk_version, (16, 0)) < 0): if unified_headers: mk_toolchain_args.append('--unified-headers') if ver.version_compare(self._ndk_version, (12, 0)) >= 0: # NDK 12 comes with a new python script expecting different command line options if ndk_toolchain_parts[0] == 'aarch64': ndk_arch = 'arm64' else: ndk_arch = ndk_toolchain_parts[0] mk_toolchain_args.append('--arch=' + ndk_arch) mk_toolchain_args.append('--api=' + str(api_level)) # --force -> remove destination directory if it exists. mk_toolchain_args.append('--force') else: ndk_platform = 'android-%d' % api_level mk_toolchain_args.append('--platform=' + ndk_platform) mk_toolchain_args.append('--toolchain=' + ndk_toolchain) if ver.version_compare(self._ndk_version, (11, 0)) < 0: if ndk_llvm_version is not None: mk_toolchain_args.append('--llvm-version=' + ndk_llvm_version) else: # enable clang by default mk_toolchain_args.append('--use-llvm') if ndk_stl is not None: mk_toolchain_args.append('--stl=' + ndk_stl) mk_toolchain_args.append('--install-dir=' + ndksa_inst_dir) #print("create_ndksa_toolchain:", mk_toolchain_args) #return # prepare the destination directory if os.path.exists(ndksa_inst_dir): shutil.rmtree(ndksa_inst_dir) if not os.path.exists(self._ndksa_root): os.makedirs(self._ndksa_root) print("Launching: " + ' '.join(mk_toolchain_args)) # and launch the script to create the new toolchain subprocess.check_call(mk_toolchain_args) if hash_bang_missing: if os.path.exists(mk_toolchain_script): os.remove(mk_toolchain_script) # patch the clang++ shell wrapper to have a hash bang self._patch_clang_shell_script( os.path.join( ndksa_inst_dir, 'bin', self._ndk_toolchain_dict[ndk_toolchain_prefix] + '-clang++')) self._patch_clang_shell_script( os.path.join( ndksa_inst_dir, 'bin', self._ndk_toolchain_dict[ndk_toolchain_prefix] + '-clang')) # update the list of available toolchains. self._ndksa_toolchains[ndk_stl] = self._query_ndksa_toolchains( os.path.join(ndksa_inst_dir, '..'))
def _build_libs(self, boost_version, toolset, build_params): bjam_builder = None tmp_dirs = [] remove_user_config = True user_config = os.path.join(self._sys_info.get_home_dir(), 'user-config.jam') # check for $HOME/user-config.jam if build_params.custom_user_config: remove_user_config = False else: if os.path.exists(user_config): raise Exception( "$HOME/user-config.jam exists and may conflict with the current configured toolset to be used to compile the boost libraries." ) platform_info = toolset.get_platform_info(build_params.platform_index) try: #print("Listing toolset attributes ...") #print(toolset) # If we don't have a bjam program at this point, it's clear we must build it from the boost source. # On windows the boost version matters because vc11 can only be used to build bjam for 1.50.0 or higher. bjam_builder = bjambld.BjamBuilder(self._sys_info, top_dir=build_params.boost_dir, bb_version=self._boost_version) if build_params.dry_run: print("dry run: a temporary bjam has to be built from source.") # just for the message summary and command lines bjam_prog = "b2" else: bjam_target_arch = 'x86_64' if self._sys_info.is_windows() and (ver.version_compare( boost_version, (1, 64, 0)) >= 0): # Boost 1.64.0 and higher comes with build.bat which does not support building a 64 bit b2. bjam_target_arch = 'x86' bjam_prog = bjam_builder.build(bjam_target_arch) # print("created temporary bjam:", bjam_prog) tmp_dirs.append(os.path.join(build_params.boost_dir, 'bin.v2')) # The next temporary directory exists in 1.58.0 but not in earlier versions. tmp_dirs.append( os.path.join(build_params.boost_dir, 'libs', 'config', 'checks', 'architecture', 'bin')) tmp_dirs.append(os.path.join(build_params.boost_dir, 'tmp')) # Remove temporary build trees, they might exist if a previous build was terminated by ctrl-c or the user tried a manual build. for dir in tmp_dirs[:2]: if os.path.exists(dir): print("Removing '" + dir + "' ...") shutil.rmtree(dir) bjam_launcher = bjambld.BjamLauncher(self._sys_info) if build_params.bjam_jobs is None: build_params.bjam_jobs = bjam_launcher.get_optimal_number_bjam_jobs( ) # save the bjam command lines as a single string for logging self._bjam_cmd_lines = [] os.chdir(build_params.boost_dir) for target in build_params.targets: (bjam_arg_list, user_config_content, setup_cmd_file, setup_cmd_content) = self._compose_bjam_arg_list( bjam_prog, toolset, build_params, target) # Remember the complete command line to be included in the final summary message. self._bjam_cmd_lines.append(' '.join(bjam_arg_list)) if user_config_content: if not build_params.dry_run: self._write_tmp_user_config(user_config, user_config_content, setup_cmd_file, setup_cmd_content) self._bjam_cmd_lines.append('<user-config.jam>') self._bjam_cmd_lines.extend(user_config_content) self._bjam_cmd_lines.append('</user-config.jam>') if setup_cmd_file: self._bjam_cmd_lines.append('<' + setup_cmd_file + '>') self._bjam_cmd_lines.extend(setup_cmd_content) self._bjam_cmd_lines.append('</' + setup_cmd_file + '>') if not build_params.dry_run: bjam_launcher.launch(bjam_arg_list) if platform_info.get_target_os() == 'windows': bin_dir = self.get_boost_bin_dir( build_params.boost_dir, toolset, build_params.platform_index, target) lib_dir = self.get_boost_lib_dir( build_params.boost_dir, toolset, build_params.platform_index, target) self._move_boost_dlls(lib_dir, bin_dir, tmp_dirs[0]) # Get rid of the binaries in case multiple targets are selected which may use the same build tree and config check tree. for d in tmp_dirs[:2]: if os.path.exists(d): print("Removing '" + d + "' ...") shutil.rmtree(d) if os.path.exists(user_config) and remove_user_config: os.remove(user_config) finally: if remove_user_config: if os.path.exists(user_config): os.remove(user_config) if setup_cmd_file and os.path.exists(setup_cmd_file): os.remove(setup_cmd_file) if not build_params.debug_configuration: for dir in tmp_dirs: if os.path.exists(dir): print("Removing '" + dir + "' ...") shutil.rmtree(dir) if bjam_builder is not None: # get rid of the temporary bjam executable bjam_builder.remove_tmp_files()
def _compose_bjam_arg_list(self, bjam_prog, toolset, build_params, target_arch): platform_info = toolset.get_platform_info(build_params.platform_index) if platform_info.get_target_os() == 'android': is_android = True else: is_android = False cflags_list = [] cxxflags_list = [] linkflags_list = [] bjam_common_args = [bjam_prog] if build_params.bjam_jobs > 1: bjam_common_args.append('-j' + str(build_params.bjam_jobs)) if build_params.debug_configuration: bjam_common_args.append('--debug-configuration') bjam_common_args.append('--layout=versioned') bjam_common_args.append('--prefix=tmp/dist') #if toolset.get_toolset() == 'clang': # if ver.version_compare(toolset.get_version(), (3, 2) ) >= 0: # cxxflags_list.append('-ftemplate-depth=256') if platform_info.get_target_os() in [ 'macosx', 'iphone', 'iphonesimulator' ]: if build_params.max_install_names: # macosx specific flag to help the install_name_tool linkflags_list.append('-headerpad_max_install_names') if build_params.macosx_version_min: version_str = ver.version_tuple_to_str( build_params.macosx_version_min) else: version_str = None target_os_ver = platform_info.get_target_os_version() if platform_info.get_target_os() == 'macosx': # deployment target cannot be larger than the macosx SDK version. if ver.version_compare( target_os_ver, self._sys_info.get_os_version()) < 0: version_str = ver.version_tuple_to_str(target_os_ver) else: # a version hint is required for all portable platforms. version_str = ver.version_tuple_to_str(target_os_ver) if version_str: if platform_info.get_target_os() == 'macosx': cflags_list.append('-mmacosx-version-min=' + version_str) linkflags_list.append('-mmacosx-version-min=' + version_str) elif platform_info.get_target_os() == 'iphone': cflags_list.append('-miphoneos-version-min=' + version_str) linkflags_list.append('-miphoneos-version-min=' + version_str) elif platform_info.get_target_os() == 'iphonesimulator': cflags_list.append('-mios-simulator-version-min=' + version_str) linkflags_list.append('-mios-simulator-version-min=' + version_str) else: assert False if toolset.get_toolset() == 'clang': # For backward compatibility the user may have overridden the default c++ runtime on macosx which is only # sensible in c++03 mode. # On linux libc++ overrides the default c++ runtime. if self._sys_info.is_linux() and build_params.cxx_runtime in [ 'libc++' ]: cxxflags_list.append('-stdlib=' + build_params.cxx_runtime) linkflags_list.append('-stdlib=' + build_params.cxx_runtime) elif self._sys_info.is_macosx() and build_params.cxx_runtime in [ 'libstdc++' ]: cxxflags_list.append('-stdlib=' + build_params.cxx_runtime) linkflags_list.append('-stdlib=' + build_params.cxx_runtime) if toolset.is_mingw(): if build_params.cxx_runtime == 'shared': linkflags_list.append('-shared-libstdc++') linkflags_list.append('-shared-libgcc') elif build_params.cxx_runtime == 'static': linkflags_list.append('-static-libstdc++') linkflags_list.append('-static-libgcc') bjam_common_args.append('variant=release,debug') if is_android: bjam_common_args.append('link=static') elif platform_info.get_target_os() in ['iphone', 'iphonesimulator']: bjam_common_args.append('link=static') else: if (toolset.get_toolset() == 'msvc') and (build_params.cxx_runtime == 'static'): # Experimental feature to build the static boost libraries with dependency on the static MSVC runtime. bjam_common_args.append('link=static') bjam_common_args.append('runtime-link=static') else: bjam_common_args.append('link=static,shared') bjam_common_args.append('install') if build_params.boost_with_list: boost_with_list = list(build_params.boost_with_list) else: boost_with_list = [] bjam_arg_list = [] bjam_arg_list.extend(bjam_common_args) if toolset.get_toolset() == 'msvc': # On windows msvc-x.y selects the VS compiler. bjam_arg_list.append('toolset=' + toolset.get_toolset_versioned()) else: bjam_arg_list.append('toolset=' + toolset.get_toolset()) boost_lib_dir = self.get_boost_lib_dir(build_params.boost_dir, toolset, build_params.platform_index, target_arch) boost_lib_dir = os.path.relpath(boost_lib_dir, build_params.boost_dir) bjam_arg_list.append('--libdir=' + boost_lib_dir) if is_android: # The NDK cross compilers generate position independent code by default, some do -fpic and others -fPIC. bjam_arg_list.append('target-os=android') elif self._sys_info.is_linux(): if toolset.get_toolset() == 'gcc': if toolset.is_mingw(): if ver.version_compare(self.get_boost_version(), (1, 66, 0)) >= 0: # Boost 1.66.0 and higher if target_arch == 'x86_64': bjam_arg_list.append('address-model=64') elif target_arch == 'x86': bjam_arg_list.append('address-model=32') bjam_arg_list.append('target-os=windows') bjam_arg_list.append('threadapi=win32') elif platform_info.get_target_arch(0) in ['arm', 'aarch64']: # is this really supported by every arm compiler? cflags_list.append('-fPIC') else: cflags_list.append('-fPIC') assert self._sys_info.get_os_arch() == target_arch elif toolset.get_toolset() == 'clang': cflags_list.append('-fPIC') elif toolset.get_toolset() == 'intel': cflags_list.append('-fPIC') else: assert False elif self._sys_info.is_windows(): if ver.version_compare(self.get_boost_version(), (1, 66, 0)) >= 0: # Boost 1.66.0 and higher if target_arch == 'x86_64': bjam_arg_list.append('address-model=64') elif target_arch == 'x86': bjam_arg_list.append('address-model=32') else: if toolset.is_mingw(): if (target_arch == 'x86') and ( target_arch != platform_info.get_target_arch(0)): bjam_arg_list.append('address-model=32') elif toolset.get_toolset() == 'msvc': # Assume this compiler is a msvc-x.y variant. if target_arch == 'x86_64': bjam_arg_list.append('address-model=64') elif self._sys_info.is_macosx(): # target-os=iphonesimulator is undefined in Boost.Build inside the BOOST SDK tree. # target-os=iphone does not define dylib as the shared library suffix inside the BOOST SDK tree. #if platform_info.get_target_os() == 'iphone': # bjam_arg_list.append('target-os=' + platform_info.get_target_os()) pass else: assert False if build_params.cxx_std is not None: re_std = re.compile(r'c\+\+(\d+)$') re_match = re_std.match(build_params.cxx_std) if not re_match: raise Exception("C++ standard specification " + build_params.cxx_std + " is not supported.") if toolset.get_toolset() in ['gcc', 'clang', 'intel']: if ver.version_compare(self._boost_version, (1, 66, 0)) >= 0: bjam_arg_list.append('cxxstd=' + re_match.group(1)) else: cxxflags_list.append('-std=' + build_params.cxx_std) build_boost_python = False if build_params.build_python: build_boost_python = self._is_boost_python_enabled( toolset, build_params.platform_index, target_arch) if boost_with_list: for item in boost_with_list: bjam_arg_list.append('--with-' + item) else: # add --without-xxx arguments which cannot be combined with --with-xxx if toolset.is_mingw(): bjam_arg_list.append('--without-locale') if not build_boost_python: bjam_arg_list.append('--without-python') if not build_params.build_mpi: bjam_arg_list.append('--without-mpi') if build_params.custom_user_config: user_config_content = [] setup_cmd_file = None setup_cmd_content = [] else: (user_config_content, setup_cmd_file, setup_cmd_content) = self._create_user_config_content( toolset, build_params.platform_index, target_arch, build_params.build_mpi, build_boost_python, cppflags=cxxflags_list, cflags=cflags_list, lflags=linkflags_list, msvc_rt=build_params.msvc_rt) return bjam_arg_list, user_config_content, setup_cmd_file, setup_cmd_content
def build(self, target_arch='x86_64'): """Builds the b2 executable from source and returns the full path to the executable.""" assert self._bjam_src_dir is not None if self._sys_info.is_windows() and (ver.version_compare( self._bb_version, (1, 66, 0)) >= 0): target_arch = 'x86' # create a new list of temporary directories to be removed after the bjam executable has been installed. self._tmp_dirs = [] bjam_bin_dir = os.path.join(self._bjam_src_dir, self._get_bjam_bin_dir_folder(target_arch)) self._tmp_dirs.append(bjam_bin_dir) b2_prog_path = os.path.join(bjam_bin_dir, self._bjam_names[0]) bjam_prog_path = os.path.join(bjam_bin_dir, self._bjam_names[1]) bootstrap_dir = os.path.join(self._bjam_src_dir, 'bootstrap') self._tmp_dirs.append(bootstrap_dir) if os.path.exists(bootstrap_dir): # in case a previous build failed to remove the temporary files, remove bootstrap completely. shutil.rmtree(bootstrap_dir) cur_dir = os.getcwd() os.chdir(self._bjam_src_dir) print("========================================================") print("Start building bjam in", self._bjam_src_dir, "...") print("========================================================") build_script_args = [] if self._sys_info.is_windows(): build_script = os.path.join(self._bjam_src_dir, 'build.bat') build_script_args.append(build_script) bjam_toolset_arg = self._toolset.get_bjam_toolset( build_script_format=True) build_script_args.append(bjam_toolset_arg) if target_arch == 'x86_64': # build.bat builds a 32 bit b2 executable by default but we prefer a native b2. if bjam_toolset_arg in ['vc141', 'vc14']: build_script_args.append('amd64') else: build_script_args.append('x86_amd64') else: build_script = os.path.join(self._bjam_src_dir, 'build.sh') build_script_args.append(build_script) retv = subprocess.call(build_script_args) if retv != 0: raise Exception( "Building bjam failed. Please contact technical support.") # restore the previous current working directory os.chdir(cur_dir) if os.path.exists(b2_prog_path): return b2_prog_path elif os.path.exists(bjam_prog_path): return bjam_prog_path else: assert False return None
def is_python3(self): return ver.version_compare(self._python_version, (3, 0)) >= 0