def test_terminal_colors(self): test = '@_This is underlined@|' rslt = '\033[4mThis is underlined\033[0m\033[0m' assert fmt(test) == rslt test = 'This has bad stuff @! @/ @_ @| OK!' test = sanitize(test) rslt = 'This has bad stuff @! @/ @_ @| OK!\033[0m' assert fmt(test) == rslt test = char(2018) test = sanitize(test) rslt = char(2018) assert test == rslt
def colorize_line(line): cline = sanitize(line) cline = cline.replace( '-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', '-- @{pf}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@|' ) if line.startswith('-- ~~'): # -- ~~ - cline = cline.replace('~~ ', '@{pf}~~ @|') cline = cline.replace(' - ', ' - @!@{bf}') cline = cline.replace('(', '@|(') cline = cline.replace('(plain cmake)', '@|(@{rf}plain cmake@|)') cline = cline.replace('(unknown)', '@|(@{yf}unknown@|)') if line.startswith('-- +++'): # -- +++ add_subdirectory(package) cline = cline.replace('+++', '@!@{gf}+++@|') cline = cline.replace('kin package: \'', 'kin package: \'@!@{bf}') cline = cline.replace(')', '@|)') cline = cline.replace('\'\n', '@|\'\n') cline = cline.replace('cmake package: \'', 'cmake package: \'@!@{bf}') cline = cline.replace('\'\n', '@|\'\n') if line.startswith('-- ==>'): cline = cline.replace('-- ==>', '-- @!@{bf}==>@|') if line.lower().startswith('warning'): # WARNING cline = ansi('yf') + cline if line.startswith('CMake Warning'): # CMake Warning... cline = cline.replace('CMake Warning', '@{yf}@!CMake Warning@|') if line.startswith('ERROR:'): # ERROR: cline = cline.replace('ERROR:', '@!@{rf}ERROR:@|') if line.startswith('CMake Error'): # CMake Error... cline = cline.replace('CMake Error', '@{rf}@!CMake Error@|') if line.startswith('Call Stack (most recent call first):'): # CMake Call Stack cline = cline.replace('Call Stack (most recent call first):', '@{cf}@_Call Stack (most recent call first):@|') return fmt(cline)
def build_workspace_isolated( workspace='.', sourcespace=None, buildspace=None, develspace=None, installspace=None, merge=False, install=False, force_cmake=False, colorize=True, build_packages=None, quiet=False, cmake_args=None, make_args=None, alpine_make_args=None, continue_from_pkg=False, only_pkg_with_deps=None, destdir=None ): ''' Runs ``cmake``, ``make`` and optionally ``make install`` for all alpine projects in sourcespace_dir. It creates several folders in the current working directory. For non-alpine projects it runs ``cmake``, ``make`` and ``make install`` for each, installing it to the devel space or install space if the ``install`` option is specified. :param workspace: path to the current workspace, ``str`` :param sourcespace: workspace folder containing alpine projects, ``str`` :param buildspace: path to build space location, ``str`` :param develspace: path to devel space location, ``str`` :param installspace: path to install space (CMAKE_INSTALL_PREFIX), ``str`` :param merge: if True, build each alpine project into the same devel space (not affecting plain cmake packages), ``bool`` :param install: if True, install all packages to the install space, ``bool`` :param force_cmake: (optional), if True calls cmake explicitly for each package, ``bool`` :param colorize: if True, colorize cmake output and other messages, ``bool`` :param build_packages: specific packages to build (all parent packages in the topological order must have been built before), ``str`` :param quiet: if True, hides some build output, ``bool`` :param cmake_args: additional arguments for cmake, ``[str]`` :param make_args: additional arguments for make, ``[str]`` :param alpine_make_args: additional arguments for make but only for alpine packages, ``[str]`` :param continue_from_pkg: indicates whether or not cmi should continue when a package is reached, ``bool`` :param only_pkg_with_deps: only consider the specific packages and their recursive dependencies and ignore all other packages in the workspace, ``[str]`` :param destdir: define DESTDIR for cmake/invocation, ``string`` ''' if not colorize: disable_ANSI_colors() # Check workspace existance if not os.path.exists(workspace): sys.exit("Workspace path '{0}' does not exist.".format(workspace)) workspace = os.path.abspath(workspace) # Check source space existance if sourcespace is None: ws_sourcespace = os.path.join(workspace, 'src') if not os.path.exists(ws_sourcespace): sys.exit("Could not find source space: {0}".format(sourcespace)) sourcespace = ws_sourcespace sourcespace = os.path.abspath(sourcespace) print('Base path: ' + str(workspace)) print('Source space: ' + str(sourcespace)) # Check build space if buildspace is None: buildspace = os.path.join(workspace, 'build_isolated') buildspace = os.path.abspath(buildspace) if not os.path.exists(buildspace): os.mkdir(buildspace) print('Build space: ' + str(buildspace)) # Check devel space if develspace is None: develspace = os.path.join(workspace, 'devel_isolated') develspace = os.path.abspath(develspace) print('Devel space: ' + str(develspace)) # Check install space if installspace is None: installspace = os.path.join(workspace, 'install_isolated') installspace = os.path.abspath(installspace) print('Install space: ' + str(installspace)) if cmake_args: print("Additional CMake Arguments: " + " ".join(cmake_args)) else: cmake_args = [] if make_args: print("Additional make Arguments: " + " ".join(make_args)) else: make_args = [] if alpine_make_args: print("Additional make Arguments for alpine projects: " + " ".join(alpine_make_args)) else: alpine_make_args = [] # Find packages packages = find_projects(sourcespace, exclude_subspaces=True) if not packages: print(fmt("@{yf}No packages found in source space: %s@|" % sourcespace)) # whitelist packages and their dependencies in workspace if only_pkg_with_deps: project_names = [p.name for p in packages.values()] unknown_packages = [name for name in only_pkg_with_deps if name not in project_names] if unknown_packages: sys.exit('Projects not found in the workspace: %s' % ', '.join(unknown_packages)) whitelist_pkg_names = get_project_names_with_recursive_dependencies(packages, only_pkg_with_deps) print('Whitelisted packages: %s' % ', '.join(sorted(whitelist_pkg_names))) packages = {path: p for path, p in packages.items() if p.name in whitelist_pkg_names} # verify that specified package exists in workspace if build_packages: packages_by_name = {p.name: path for path, p in packages.items()} unknown_packages = [p for p in build_packages if p not in packages_by_name] if unknown_packages: sys.exit('Projects not found in the workspace: %s' % ', '.join(unknown_packages)) # Report topological ordering ordered_projects = topological_order_projects(packages) unknown_build_types = [] msg = [] msg.append('@{pf}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' + ('~' * len(str(len(ordered_projects))))) msg.append('@{pf}~~@| traversing %d projects in topological order:' % len(ordered_projects)) for path, package in ordered_projects: export_tags = [e.tagname for e in package.exports] if 'build_type' in export_tags: build_type_tag = [e.content for e in package.exports if e.tagname == 'build_type'][0] else: build_type_tag = 'alpine' if build_type_tag == 'alpine': msg.append('@{pf}~~@| - @!@{bf}' + package.name + '@|') elif build_type_tag == 'cmake': msg.append( '@{pf}~~@| - @!@{bf}' + package.name + '@|' + ' (@!@{cf}plain cmake@|)' ) else: msg.append( '@{pf}~~@| - @!@{bf}' + package.name + '@|' + ' (@{rf}unknown@|)' ) unknown_build_types.append(package) msg.append('@{pf}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' + ('~' * len(str(len(ordered_projects))))) for index in range(len(msg)): msg[index] = fmt(msg[index]) print('\n'.join(msg)) # Error if there are packages with unknown build_types if unknown_build_types: print(colorize_line('Error: Projects with unknown build types exist')) sys.exit('Can not build workspace with projects of unknown build_type') # Check to see if the workspace has changed if not force_cmake and cmake_input_changed(packages, buildspace, cmake_args=cmake_args, filename='alpine_make_isolated'): print('The packages or cmake arguments have changed, forcing cmake invocation') force_cmake = True # Build packages pkg_develspace = None last_env = None for index, path_package in enumerate(ordered_projects): path, package = path_package if merge: pkg_develspace = develspace else: pkg_develspace = os.path.join(develspace, package.name) if not build_packages or package.name in build_packages: if continue_from_pkg and build_packages and package.name in build_packages: build_packages = None try: print() last_env = build_package( path, package, workspace, buildspace, pkg_develspace, installspace, install, force_cmake, quiet, last_env, cmake_args, make_args, alpine_make_args, destdir=destdir, number=index + 1, of=len(ordered_projects) ) except subprocess.CalledProcessError as e: _print_build_error(package, e) # Let users know how to reproduce # First add the cd to the build folder of the package cmd = 'cd ' + os.path.join(buildspace, package.name) + ' && ' # Then reproduce the command called cmd += ' '.join(e.cmd) if isinstance(e.cmd, list) else e.cmd print(fmt("\n@{rf}Reproduce this error by running:")) print(fmt("@{gf}@!==> @|") + cmd + "\n") sys.exit('Command failed, exiting.') except Exception as e: print("Unhandled exception of type '{0}':".format(type(e).__name__)) import traceback traceback.print_exc() _print_build_error(package, e) sys.exit('Command failed, exiting.') else: cprint("Skipping project: '@!@{bf}" + package.name + "@|'") last_env = get_new_env(package, pkg_develspace, installspace, install, last_env, destdir) # Provide a top level devel space environment setup script if not os.path.exists(develspace): os.makedirs(develspace) if not build_packages: generated_env_sh = os.path.join(develspace, 'env.sh') generated_setup_util_py = os.path.join(develspace, '_setup_util.py') if not merge and pkg_develspace: # generate env.sh and setup.sh|bash|zsh which relay to last devel space with open(generated_env_sh, 'w') as f: f.write("""\ #!/usr/bin/env sh # generated from alpine.builder module {0} "$@" """.format(os.path.join(pkg_develspace, 'env.sh'))) os.chmod(generated_env_sh, stat.S_IXUSR | stat.S_IWUSR | stat.S_IRUSR) for shell in ['sh', 'bash', 'zsh']: with open(os.path.join(develspace, 'setup.%s' % shell), 'w') as f: f.write("""\ #!/usr/bin/env {1} # generated from alpine.builder module . "{0}/setup.{1}" """.format(pkg_develspace, shell)) # remove _setup_util.py file which might have been generated for an empty devel space before if os.path.exists(generated_setup_util_py): os.remove(generated_setup_util_py) elif not pkg_develspace: # generate env.sh and setup.sh|bash|zsh for an empty devel space if 'CMAKE_PREFIX_PATH' in os.environ.keys(): variables = { 'ALPINE_GLOBAL_BIN_DESTINATION': 'bin', 'ALPINE_GLOBAL_LIB_DESTINATION': 'lib', 'CMAKE_PREFIX_PATH_AS_IS': ';'.join(os.environ['CMAKE_PREFIX_PATH'].split(os.pathsep)), 'PYTHON_INSTALL_DIR': get_python_install_dir(), } with open(generated_setup_util_py, 'w') as f: f.write(configure_file(os.path.join(get_cmake_path(), 'templates', '_setup_util.py.in'), variables)) os.chmod(generated_setup_util_py, stat.S_IXUSR | stat.S_IWUSR | stat.S_IRUSR) else: sys.exit("Unable to process CMAKE_PREFIX_PATH from environment. Cannot generate environment files.") variables = {'SETUP_FILENAME': 'setup'} with open(generated_env_sh, 'w') as f: f.write(configure_file(os.path.join(get_cmake_path(), 'templates', 'env.sh.in'), variables)) os.chmod(generated_env_sh, stat.S_IXUSR | stat.S_IWUSR | stat.S_IRUSR) variables = {'SETUP_DIR': develspace} for shell in ['sh', 'bash', 'zsh']: with open(os.path.join(develspace, 'setup.%s' % shell), 'w') as f: f.write(configure_file(os.path.join(get_cmake_path(), 'templates', 'setup.%s.in' % shell), variables))
def cprint(msg, end=None): print(fmt(msg), end=end)
def build_alpine_project( path, package, workspace, buildspace, develspace, installspace, install, force_cmake, quiet, last_env, cmake_args, make_args, destdir=None ): cprint( "Processing @{cf}alpine@| project: '@!@{bf}" + package.name + "@|'" ) # Make the build dir build_dir = _check_build_dir(package.name, workspace, buildspace) # Check last_env if last_env is not None: cprint( blue_arrow + " Building with env: " + "'{0}'".format(last_env) ) # Check for Makefile and maybe call cmake makefile = os.path.join(build_dir, 'Makefile') if not os.path.exists(makefile) or force_cmake: package_dir = os.path.dirname(package.filename) if not os.path.exists(os.path.join(package_dir, 'CMakeLists.txt')): export_tags = [e.tagname for e in package.exports] if 'metaproject' not in export_tags: print(colorize_line('Error: Project "%s" does not have a CMakeLists.txt file' % package.name)) sys.exit('Can not build alpine project without CMakeLists.txt file') # generate CMakeLists.txt for metpackages without one print(colorize_line('Warning: metaproject "%s" should have a CMakeLists.txt file' % package.name)) cmake_code = configure_file( get_metaproject_cmake_template_path(), {'name': package.name, 'metaproject_arguments': 'DIRECTORY "%s"' % package_dir}) cmakelists_txt = os.path.join(build_dir, 'CMakeLists.txt') with open(cmakelists_txt, 'w') as f: f.write(cmake_code) package_dir = build_dir # Run cmake cmake_cmd = [ 'cmake', package_dir, '-DALPINE_DEVEL_PREFIX=' + develspace, '-DCMAKE_INSTALL_PREFIX=' + installspace ] cmake_cmd.extend(cmake_args) add_env = get_additional_environment(install, destdir, installspace) isolation_print_command(' '.join(cmake_cmd), build_dir, add_env=add_env) if last_env is not None: cmake_cmd = [last_env] + cmake_cmd try: run_command_colorized(cmake_cmd, build_dir, quiet, add_env=add_env) except subprocess.CalledProcessError as e: if os.path.exists(makefile): # remove Makefile to force CMake invocation next time os.remove(makefile) raise else: print('Makefile exists, skipping explicit cmake invocation...') # Check to see if cmake needs to be run via make make_check_cmake_cmd = ['make', 'cmake_check_build_system'] add_env = get_additional_environment(install, destdir, installspace) isolation_print_command(' '.join(make_check_cmake_cmd), build_dir, add_env=add_env) if last_env is not None: make_check_cmake_cmd = [last_env] + make_check_cmake_cmd run_command_colorized( make_check_cmake_cmd, build_dir, quiet, add_env=add_env ) # Run make make_cmd = ['make'] make_cmd.extend(handle_make_arguments(make_args, force_single_threaded_when_running_tests=True)) isolation_print_command(' '.join(make_cmd), build_dir) if last_env is not None: make_cmd = [last_env] + make_cmd run_command(make_cmd, build_dir, quiet) # Make install if install: if has_make_target(build_dir, 'install'): make_install_cmd = ['make', 'install'] isolation_print_command(' '.join(make_install_cmd), build_dir) if last_env is not None: make_install_cmd = [last_env] + make_install_cmd run_command(make_install_cmd, build_dir, quiet) else: print(fmt('@{yf}Project has no "@{boldon}install@{boldoff}" target, skipping "make install" invocation...'))