def create_cmakelists(package_template, rosdistro, meta=False): """ :param package_template: contains the required information :returns: file contents as string """ if meta: template_path = get_metapackage_cmake_template_path() temp_dict = { 'name': package_template.name, 'metapackage_arguments': '' } return configure_file(template_path, temp_dict) else: cmakelists_txt_template = read_template_file('CMakeLists.txt', rosdistro) ctemp = CatkinTemplate(cmakelists_txt_template) if package_template.catkin_deps == []: components = '' else: components = ' COMPONENTS\n %s\n' % '\n '.join( package_template.catkin_deps) boost_find_package = \ ('' if not package_template.boost_comps else ('find_package(Boost REQUIRED COMPONENTS %s)\n' % ' '.join(package_template.boost_comps))) system_find_package = '' for sysdep in package_template.system_deps: if sysdep == 'boost': continue if sysdep.startswith('python-'): system_find_package += '# ' system_find_package += 'find_package(%s REQUIRED)\n' % sysdep # provide dummy values catkin_depends = (''.join('# %s\n' % dep for dep in package_template.catkin_deps)[:-1] if package_template.catkin_deps else '# other_catkin_pkg') system_depends = (''.join( '# %s\n' % dep for dep in package_template.system_deps)[:-1] if package_template.system_deps else '# system_lib') message_pkgs = [ pkg for pkg in package_template.catkin_deps if pkg.endswith('_msgs') ] if message_pkgs: message_depends = '# %s' % '\n# '.join(message_pkgs) else: message_depends = '# packages containing msgs' temp_dict = { 'name': package_template.name, 'components': components, 'include_directories': _create_include_macro(package_template), 'boost_find': boost_find_package, 'systems_find': system_find_package, 'catkin_depends': catkin_depends, 'system_depends': system_depends, 'target_libraries': _create_targetlib_args(package_template), 'message_dependencies': message_depends } return ctemp.substitute(temp_dict)
def create_cmakelists(package_template, rosdistro, meta=False): """ :param package_template: contains the required information :returns: file contents as string """ if meta: template_path = get_metapackage_cmake_template_path() temp_dict = {"name": package_template.name, "metapackage_arguments": ""} return configure_file(template_path, temp_dict) else: cmakelists_txt_template = read_template_file("CMakeLists.txt", rosdistro) ctemp = CatkinTemplate(cmakelists_txt_template) if package_template.catkin_deps == []: components = "" else: components = " COMPONENTS\n %s\n" % "\n ".join(package_template.catkin_deps) boost_find_package = ( "" if not package_template.boost_comps else ("find_package(Boost REQUIRED COMPONENTS %s)\n" % " ".join(package_template.boost_comps)) ) system_find_package = "" for sysdep in package_template.system_deps: if sysdep == "boost": continue if sysdep.startswith("python-"): system_find_package += "# " system_find_package += "find_package(%s REQUIRED)\n" % sysdep # provide dummy values catkin_depends = ( "".join("# %s\n" % dep for dep in package_template.catkin_deps)[:-1] if package_template.catkin_deps else "# other_catkin_pkg" ) system_depends = ( "".join("# %s\n" % dep for dep in package_template.system_deps)[:-1] if package_template.system_deps else "# system_lib" ) message_pkgs = [pkg for pkg in package_template.catkin_deps if pkg.endswith("_msgs")] if message_pkgs: message_depends = "# %s" % "\n# ".join(message_pkgs) else: message_depends = "# packages containing msgs" temp_dict = { "name": package_template.name, "components": components, "include_directories": _create_include_macro(package_template), "boost_find": boost_find_package, "systems_find": system_find_package, "catkin_depends": catkin_depends, "system_depends": system_depends, "target_libraries": _create_targetlib_args(package_template), "message_dependencies": message_depends, } return ctemp.substitute(temp_dict)
def create_cmakelists(package_template, rosdistro, meta=False): """Create CMake file contents from the template. :param package_template: contains the required information :returns: file contents as string """ if meta: template_path = get_metapackage_cmake_template_path() temp_dict = { 'name': package_template.name, 'metapackage_arguments': '', } return configure_file(template_path, temp_dict) else: cmakelists_txt_template = read_template_file('CMakeLists.txt', rosdistro) ctemp = CatkinTemplate(cmakelists_txt_template) if package_template.catkin_deps == []: components = '' else: components = ' COMPONENTS\n %s\n' % '\n '.join(package_template.catkin_deps) boost_find_package = \ ('' if not package_template.boost_comps else ('find_package(Boost REQUIRED COMPONENTS %s)\n' % ' '.join(package_template.boost_comps))) system_find_package = '' for sysdep in package_template.system_deps: if sysdep == 'boost': continue if sysdep.startswith('python-'): system_find_package += '# ' system_find_package += 'find_package(%s REQUIRED)\n' % sysdep # provide dummy values catkin_depends = (' '.join(package_template.catkin_deps) if package_template.catkin_deps else 'other_catkin_pkg') system_depends = (' '.join(package_template.system_deps) if package_template.system_deps else 'system_lib') message_pkgs = [pkg for pkg in package_template.catkin_deps if pkg.endswith('_msgs')] if message_pkgs: message_depends = '# %s' % '# '.join(message_pkgs) else: message_depends = '# std_msgs # Or other packages containing msgs' temp_dict = {'name': package_template.name, 'components': components, 'include_directories': _create_include_macro(package_template), 'boost_find': boost_find_package, 'systems_find': system_find_package, 'catkin_depends': catkin_depends, 'system_depends': system_depends, 'target_libraries': _create_targetlib_args(package_template), 'message_dependencies': message_depends } return ctemp.substitute(temp_dict)
def get_expected_cmakelists_txt(metapackage_name): """ Return the expected boilerplate CMakeLists.txt file for a metapackage. :param metapackage_name: name of the metapackage :type metapackage_name: str :returns: expected CMakeLists.txt file :rtype: str """ env = {'name': metapackage_name, 'metapackage_arguments': ''} return configure_file(get_metapackage_cmake_template_path(), env)
def get_expected_cmakelists_txt(metapackage_name): """ Returns the expected boilerplate CMakeLists.txt file for a metapackage :param metapackage_name: name of the metapackage :type metapackage_name: str :returns: expected CMakeLists.txt file :rtype: str """ env = { 'name': metapackage_name, 'metapackage_arguments': '' } return configure_file(get_metapackage_cmake_template_path(), env)
def check_metapackage_for_valid_cmake(name): if not os.path.exists('CMakeLists.txt'): warning("See: http://ros.org/reps/rep-0127.html#metapackage") error("Metapackage '{0}' does not have a CMakeLists.txt, refusing to release." .format(name), exit=True) template_path = get_metapackage_cmake_template_path() env = {'name': name, 'metapackage_arguments': ''} expected_cmake = configure_file(template_path, env) with open('CMakeLists.txt', 'r') as f: actual_cmake = f.read() if expected_cmake != actual_cmake: error("Metapackage '{0}' has a non-compliant CMakeLists.txt, expected:" .format(name)) for line in expected_cmake.splitlines(): info(" " + line) error("But got instead:") for line in actual_cmake.splitlines(): info(" " + line) warning("See: http://ros.org/reps/rep-0127.html#metapackage") error("Metapackage '{0}' has a non-compliant CMakeLists.txt".format(name), exit=True)
import os import shutil import sys import tempfile import unittest from catkin_pkg.cmake import configure_file data = configure_file( os.path.join(os.path.dirname(__file__), '..', '..', 'cmake', 'templates', '_setup_util.py.in'), { 'CATKIN_LIB_ENVIRONMENT_PATHS': "'lib'", 'CATKIN_PKGCONFIG_ENVIRONMENT_PATHS': "os.path.join('lib', 'pkgconfig')", 'CATKIN_GLOBAL_BIN_DESTINATION': 'bin', 'PYTHON_EXECUTABLE': sys.executable, 'PYTHON_INSTALL_DIR': 'pythonX.Y/packages', 'CMAKE_PREFIX_PATH_AS_IS': '', }) with tempfile.NamedTemporaryFile('w+') as setup_util_file: setup_util_file.write(data) setup_util_file.seek(0) import imp imp.load_source('setup_util', setup_util_file.name, setup_util_file.file) from setup_util import CATKIN_MARKER_FILE # noqa: E402 from setup_util import _get_workspaces # noqa: E402 from setup_util import _prefix_env_variable # noqa: E402 from setup_util import _rollback_env_variable # noqa: E402
def build_catkin_package(path, package, workspace, buildspace, develspace, installspace, install, force_cmake, quiet, last_env, cmake_args, make_args, destdir=None, use_ninja=False): cprint("Processing @{cf}catkin@| package: '@!@{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 if not use_ninja: makefile_name = 'Makefile' else: makefile_name = 'build.ninja' makefile = os.path.join(build_dir, makefile_name) 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 'metapackage' not in export_tags: print( colorize_line( 'Error: Package "%s" does not have a CMakeLists.txt file' % package.name)) sys.exit( 'Can not build catkin package without CMakeLists.txt file') # generate CMakeLists.txt for metpackages without one print( colorize_line( 'Warning: metapackage "%s" should have a CMakeLists.txt file' % package.name)) cmake_code = configure_file( get_metapackage_cmake_template_path(), { 'name': package.name, 'metapackage_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, '-DCATKIN_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('%s exists, skipping explicit cmake invocation...' % makefile_name) # Check to see if cmake needs to be run via make if not use_ninja: make_check_cmake_cmd = ['make', 'cmake_check_build_system'] else: make_check_cmake_cmd = ['ninja', 'build.ninja'] 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 if not use_ninja: make_executable = 'make' else: make_executable = 'ninja' make_cmd = [make_executable] make_cmd.extend(handle_make_arguments(make_args)) 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', use_ninja=use_ninja): make_install_cmd = [make_executable, '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}Package has no "@{boldon}install@{boldoff}" target, skipping "%s install" invocation...' % make_executable))
def build_catkin_package(path, package, workspace, buildspace, develspace, installspace, install, jobs, force_cmake, quiet, cmake_args, make_args, catkin_python_path): cprint("Processing @{cf}catkin@| package: '@!@{bf}" + package.name + "@|'") # Make the build dir build_dir = _check_build_dir(package.name, workspace, buildspace) # Help find catkin cmake and python env = os.environ.copy() try: env['PYTHONPATH'] = env['PYTHONPATH'] + os.pathsep + catkin_python_path except KeyError: env['PYTHONPATH'] = catkin_python_path # Check for Makefile and maybe call cmake makefile = os.path.join(build_dir, 'Makefile') # check if toolchain.cmake, config.cmake exist toolchain_cmd = "-DCMAKE_TOOLCHAIN_FILE=%s" % os.path.join( workspace, 'toolchain.cmake') if os.path.isfile( os.path.join(workspace, 'toolchain.cmake')) else None config_cmd = "-C%s" % os.path.join( workspace, 'config.cmake') if os.path.isfile( os.path.join(workspace, 'config.cmake')) else None 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 'metapackage' not in export_tags: print( colorize_line( 'Error: Package "%s" does not have a CMakeLists.txt file' % package.name)) raise RuntimeError( 'Can not build catkin package without CMakeLists.txt file') # generate CMakeLists.txt for metpackages without one print( colorize_line( 'Warning: metapackage "%s" should have a CMakeLists.txt file' % package.name)) cmake_code = configure_file( get_metapackage_cmake_template_path(), { 'name': package.name, 'metapackage_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, ] if toolchain_cmd: cmake_cmd.append(toolchain_cmd) if config_cmd: cmake_cmd.append(config_cmd) cmake_cmd.extend(cmake_args) isolation_print_command(' '.join(cmake_cmd)) #if last_env is not None: # cmake_cmd = [last_env] + cmake_cmd try: run_command_colorized(cmake_cmd, build_dir, quiet, env=env) except subprocess.CalledProcessError as e: # 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'] isolation_print_command(' '.join(make_check_cmake_cmd), build_dir) #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, env=env) # Run make make_cmd = ['make', '-j' + str(jobs), '-l' + str(jobs)] make_cmd.extend(make_args) 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, env=env) # Make install if 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)
import os import shutil import tempfile import unittest from catkin_pkg.cmake import configure_file data = configure_file( os.path.join(os.path.dirname(__file__), '..', '..', 'cmake', 'templates', '_setup_util.py.in'), { 'CATKIN_GLOBAL_LIB_DESTINATION': 'lib', 'CATKIN_GLOBAL_BIN_DESTINATION': 'bin', 'PYTHON_INSTALL_DIR': 'pythonX.Y/packages', 'CMAKE_PREFIX_PATH_AS_IS': '', }) with tempfile.TemporaryFile() as setup_util_file: setup_util_file.write(data) setup_util_file.seek(0) import imp imp.load_source('setup_util', '/somewhere/_setup_util.py', setup_util_file) import setup_util from setup_util import _get_workspaces, _prefix_env_variable, _rollback_env_variable, CATKIN_MARKER_FILE class SetupUtilTest(unittest.TestCase): def test_get_reversed_workspaces(self): try: rootdir = tempfile.mkdtemp() mock_env = {}
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, catkin_make_args=None, continue_from_pkg=False, only_pkg_with_deps=None, destdir=None, use_ninja=False, use_nmake=False, override_build_tool_check=False ): ''' Runs ``cmake``, ``make`` and optionally ``make install`` for all catkin packages in sourcespace_dir. It creates several folders in the current working directory. For non-catkin packages 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 catkin packages, ``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 catkin package 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 catkin_make_args: additional arguments for make but only for catkin 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`` :param use_ninja: if True, use ninja instead of make, ``bool`` :param use_nmake: if True, use nmake instead of make, ``bool`` :param override_build_tool_check: if True, build even if a space was built by another tool previously. ''' 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: sourcespace = os.path.join(workspace, 'src') if not os.path.exists(sourcespace): sys.exit('Could not find source space: {0}'.format(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') if not os.path.exists(buildspace): os.mkdir(buildspace) print('Build space: ' + str(buildspace)) # ensure the build space was previously built by catkin_make_isolated previous_tool = get_previous_tool_used_on_the_space(buildspace) if previous_tool is not None and previous_tool != 'catkin_make_isolated': if override_build_tool_check: print(fmt( "@{yf}Warning: build space at '%s' was previously built by '%s', " "but --override-build-tool-check was passed so continuing anyways." % (buildspace, previous_tool))) else: sys.exit(fmt( "@{rf}The build space at '%s' was previously built by '%s'. " "Please remove the build space or pick a different build space." % (buildspace, previous_tool))) mark_space_as_built_by(buildspace, 'catkin_make_isolated') # Check devel space if develspace is None: develspace = os.path.join(workspace, 'devel_isolated') print('Devel space: ' + str(develspace)) # ensure the devel space was previously built by catkin_make_isolated previous_tool = get_previous_tool_used_on_the_space(develspace) if previous_tool is not None and previous_tool != 'catkin_make_isolated': if override_build_tool_check: print(fmt( "@{yf}Warning: devel space at '%s' was previously built by '%s', " "but --override-build-tool-check was passed so continuing anyways." % (develspace, previous_tool))) else: sys.exit(fmt( "@{rf}The devel space at '%s' was previously built by '%s'. " "Please remove the devel space or pick a different devel space." % (develspace, previous_tool))) mark_space_as_built_by(develspace, 'catkin_make_isolated') # Check install space if installspace is None: installspace = os.path.join(workspace, 'install_isolated') print('Install space: ' + str(installspace)) if cmake_args: print("Additional CMake Arguments: " + " ".join(cmake_args)) else: cmake_args = [] if not [arg for arg in cmake_args if arg.startswith('-G')]: if use_ninja: cmake_args += ['-G', 'Ninja'] elif use_nmake: cmake_args += ['-G', 'NMake Makefiles'] else: cmake_args += ['-G', 'Unix Makefiles'] elif use_ninja or use_nmake: print(colorize_line("Error: either specify a generator using '-G...' or '--use-[ninja|nmake]' but not both")) sys.exit(1) if make_args: print("Additional make Arguments: " + " ".join(make_args)) else: make_args = [] if catkin_make_args: print("Additional make Arguments for catkin packages: " + " ".join(catkin_make_args)) else: catkin_make_args = [] # Find packages packages = find_packages(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: package_names = [p.name for p in packages.values()] unknown_packages = [name for name in only_pkg_with_deps if name not in package_names] if unknown_packages: sys.exit('Packages not found in the workspace: %s' % ', '.join(unknown_packages)) whitelist_pkg_names = get_package_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('Packages not found in the workspace: %s' % ', '.join(unknown_packages)) # Report topological ordering ordered_packages = topological_order_packages(packages) unknown_build_types = [] msg = [] msg.append('@{pf}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' + ('~' * len(str(len(ordered_packages))))) msg.append('@{pf}~~@| traversing %d packages in topological order:' % len(ordered_packages)) for path, package in ordered_packages: if path is None: print(fmt('@{rf}Error: Circular dependency in subset of packages: @!%s@|' % package)) sys.exit('Can not build workspace with circular dependency') 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 = 'catkin' if build_type_tag == 'catkin': 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_packages))))) 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: Packages with unknown build types exist')) sys.exit('Can not build workspace with packages of unknown build_type') # Check to see if the workspace has changed cmake_args_with_spaces = list(cmake_args) if develspace: cmake_args_with_spaces.append('-DCATKIN_DEVEL_PREFIX=' + develspace) if installspace: cmake_args_with_spaces.append('-DCMAKE_INSTALL_PREFIX=' + installspace) if ( not force_cmake and cmake_input_changed(packages, buildspace, cmake_args=cmake_args_with_spaces, filename='catkin_make_isolated') ): print('The packages or cmake arguments have changed, forcing cmake invocation') force_cmake = True ensure_workspace_marker(workspace) # Build packages pkg_develspace = None last_env = None for index, path_package in enumerate(ordered_packages): 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, catkin_make_args, destdir=destdir, use_ninja=use_ninja, number=index + 1, of=len(ordered_packages) ) 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 ' + quote(os.path.join(buildspace, package.name)) + ' && ' # Then reproduce the command called if isinstance(e.cmd, list): # quote arguments to allow copy-n-paste of command cmd += ' '.join([quote(arg) for arg in e.cmd]) else: cmd += 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 package: '@!@{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 catkin.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 catkin.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 = { 'CATKIN_GLOBAL_BIN_DESTINATION': 'bin', 'CATKIN_LIB_ENVIRONMENT_PATHS': "'lib'", 'CATKIN_PKGCONFIG_ENVIRONMENT_PATHS': "os.path.join('lib', 'pkgconfig')", 'CMAKE_PREFIX_PATH_AS_IS': ';'.join(os.environ['CMAKE_PREFIX_PATH'].split(os.pathsep)), 'PYTHON_EXECUTABLE': sys.executable, '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 build_workspace_isolated( workspace='.', sourcespace=None, buildspace=None, develspace=None, installspace=None, merge=False, install=False, jobs=None, force_cmake=False, build_packages=None, quiet=False, cmake_args=[], make_args=[], catkin_cmake_path=None, catkin_python_path=None ): ''' Runs ``cmake``, ``make`` and optionally ``make install`` for all catkin packages in sourcespace_dir. It creates several folders in the current working directory. For non-catkin packages 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 catkin packages, ``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 catkin package into the same devel space. does not work with non-catkin packages, ``bool`` :param install: if True, install all packages to the install space, ``bool`` :param jobs: number of parallel build jobs to run (make -jN -lN), ``int`` :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]`` ''' # Should actually have alot of argument checks here, rather than # before feeding the function (makes for safe functions) console.pretty_print("Base Path: ", console.cyan) console.pretty_println("%s" % workspace, console.yellow) console.pretty_print("Build Path: ", console.cyan) console.pretty_println("%s" % buildspace, console.yellow) console.pretty_print("Source Path: ", console.cyan) console.pretty_println("%s" % sourcespace, console.yellow) console.pretty_print("Devel Path: ", console.cyan) console.pretty_println("%s" % develspace, console.yellow) console.pretty_print("Install Path: ", console.cyan) console.pretty_println("%s" % installspace, console.yellow) console.pretty_print("Catkin CMake Path: ", console.cyan) console.pretty_println("%s" % catkin_cmake_path, console.yellow) console.pretty_print("Catkin Python Path: ", console.cyan) console.pretty_println("%s" % catkin_python_path, console.yellow) # Find packages packages = find_packages(sourcespace, exclude_subspaces=True) if not packages: raise RuntimeError("No packages found in source space: %s" % sourcespace) # verify that specified package exists in workspace if build_packages: packages_by_name = {p.name: path for path, p in packages.iteritems()} unknown_packages = [p for p in build_packages if p not in packages_by_name] if unknown_packages: raise RuntimeError('Packages not found in the workspace: %s' % ', '.join(unknown_packages)) # Report topological ordering ordered_packages = topological_order_packages(packages) unknown_build_types = [] msg = [] msg.append('@{pf}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' + ('~' * len(str(len(ordered_packages))))) msg.append('@{pf}~~@| traversing %d packages in topological order:' % len(ordered_packages)) for path, package in ordered_packages: 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 = 'catkin' if build_type_tag == 'catkin': 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_packages))))) 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: raise RuntimeError('Can not build workspace with packages of unknown build_type.') # Check to see if the workspace has changed if not force_cmake: force_cmake, install_toggled = builder.cmake_input_changed( packages, buildspace, install=install, cmake_args=cmake_args, filename='catkin_make_isolated' ) if force_cmake: print('The packages or cmake arguments have changed, forcing cmake invocation') elif install_toggled: print('The install argument has been toggled, forcing cmake invocation on plain cmake package') # Build packages original_develspace = copy.deepcopy(develspace) for index, path_package in enumerate(ordered_packages): path, package = path_package if not merge: develspace = os.path.join(original_develspace, package.name) if not build_packages or package.name in build_packages: try: export_tags = [e.tagname for e in package.exports] is_cmake_package = 'cmake' in [e.content for e in package.exports if e.tagname == 'build_type'] builder.build_package( path, package, workspace, buildspace, develspace, installspace, install, jobs, force_cmake or (install_toggled and is_cmake_package), quiet, cmake_args, make_args, number=index + 1, of=len(ordered_packages), catkin_cmake_path=catkin_cmake_path, catkin_python_path=catkin_python_path ) except Exception as e: import traceback traceback.print_exc() builder.cprint( '@{rf}@!<==@| ' + 'Failed to process package \'@!@{bf}' + package.name + '@|\': \n ' + ('KeyboardInterrupt' if isinstance(e, KeyboardInterrupt) else str(e)) ) if isinstance(e, subprocess.CalledProcessError): 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.') else: builder.cprint("Skipping package: '@!@{bf}" + package.name + "@|'") # Provide a top level devel space environment setup script if not merge and not build_packages: # generate env.sh and setup.sh which relay to last devel space generated_env = os.path.join(original_develspace, 'env.sh') with open(generated_env, 'w') as f: f.write("""\ #!/usr/bin/env sh # generated from catkin.builder module {0} "$@" """.format(os.path.join(develspace, 'env.sh'))) os.chmod(generated_env, stat.S_IXUSR | stat.S_IWUSR | stat.S_IRUSR) with open(os.path.join(original_develspace, 'setup.sh'), 'w') as f: f.write("""\ #!/usr/bin/env sh # generated from catkin.builder module . "{0}/setup.sh" """.format(develspace)) # generate setup.bash and setup.zsh for convenience variables = {'SETUP_DIR': original_develspace} with open(os.path.join(original_develspace, 'setup.bash'), 'w') as f: f.write(configure_file(os.path.join(catkin_cmake_path, 'templates', 'setup.bash.in'), variables)) with open(os.path.join(original_develspace, 'setup.zsh'), 'w') as f: f.write(configure_file(os.path.join(catkin_cmake_path, 'templates', 'setup.zsh.in'), variables))
def build_cmake_package(path, package, workspace, buildspace, develspace, installspace, install, force_cmake, quiet, last_env, cmake_args, make_args, destdir=None): # Notify the user that we are processing a plain cmake package cprint("Processing @{cf}plain cmake@| package: '@!@{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') install_target = installspace if install else develspace if not os.path.exists(makefile) or force_cmake: # Call cmake cmake_cmd = [ 'cmake', os.path.dirname(package.filename), '-DCMAKE_INSTALL_PREFIX=' + install_target ] cmake_cmd.extend(cmake_args) isolation_print_command(' '.join(cmake_cmd), build_dir) if last_env is not None: cmake_cmd = [last_env] + cmake_cmd run_command_colorized(cmake_cmd, build_dir, quiet) 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'] isolation_print_command(' '.join(make_check_cmake_cmd), build_dir) 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) # Run make make_cmd = ['make'] make_cmd.extend(handle_make_arguments(make_args)) 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 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) # If we are installing, and a env.sh exists, don't overwrite it if install and os.path.exists(os.path.join(installspace, 'env.sh')): return cprint(blue_arrow + " Generating an env.sh") # Generate env.sh for chaining to catkin packages # except if using --merge which implies that new_env_path equals last_env new_env_path = os.path.join(install_target, 'env.sh') if new_env_path != last_env: variables = {'SETUP_DIR': install_target, 'SETUP_FILENAME': 'setup'} with open(os.path.join(new_env_path), 'w') as f: f.write( configure_file( os.path.join(get_cmake_path(), 'templates', 'env.sh.in'), variables)) os.chmod(new_env_path, stat.S_IXUSR | stat.S_IWUSR | stat.S_IRUSR) # Generate setup.sh for chaining to catkin packages # except if using --merge which implies that new_setup_path equals last_setup_env new_setup_path = os.path.join(install_target, 'setup.sh') last_setup_env = os.path.join(os.path.dirname(last_env), 'setup.sh') if last_env is not None else None if new_setup_path != last_setup_env: subs = {} subs['cmake_prefix_path'] = install_target + ":" subs['ld_path'] = os.path.join(install_target, 'lib') + ":" pythonpath = os.path.join(install_target, get_python_install_dir()) subs['pythonpath'] = pythonpath + ':' subs['pkgcfg_path'] = os.path.join(install_target, 'lib', 'pkgconfig') subs['pkgcfg_path'] += ":" subs['path'] = os.path.join(install_target, 'bin') + ":" if not os.path.exists(install_target): os.mkdir(install_target) with open(new_setup_path, 'w+') as file_handle: file_handle.write("""\ #!/usr/bin/env sh # generated from catkin.builder module """) if last_env is not None: file_handle.write('. %s\n\n' % last_setup_env) file_handle.write("""\ # detect if running on Darwin platform _UNAME=`uname -s` IS_DARWIN=0 if [ "$_UNAME" = "Darwin" ]; then IS_DARWIN=1 fi # Prepend to the environment export CMAKE_PREFIX_PATH="{cmake_prefix_path}$CMAKE_PREFIX_PATH" if [ $IS_DARWIN -eq 0 ]; then export LD_LIBRARY_PATH="{ld_path}$LD_LIBRARY_PATH" else export DYLD_LIBRARY_PATH="{ld_path}$DYLD_LIBRARY_PATH" fi export PATH="{path}$PATH" export PKG_CONFIG_PATH="{pkgcfg_path}$PKG_CONFIG_PATH" export PYTHONPATH="{pythonpath}$PYTHONPATH" """.format(**subs))
def build_catkin_package( path, package, workspace, buildspace, develspace, installspace, install, force_cmake, quiet, last_env, cmake_args, make_args, destdir=None, use_ninja=False, use_nmake=False ): cprint( "Processing @{cf}catkin@| package: '@!@{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 if not use_ninja: makefile_name = 'Makefile' else: makefile_name = 'build.ninja' makefile = os.path.join(build_dir, makefile_name) 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 'metapackage' not in export_tags: print(colorize_line('Error: Package "%s" does not have a CMakeLists.txt file' % package.name)) sys.exit('Can not build catkin package without CMakeLists.txt file') # generate CMakeLists.txt for metpackages without one print(colorize_line('Warning: metapackage "%s" should have a CMakeLists.txt file' % package.name)) cmake_code = configure_file( get_metapackage_cmake_template_path(), {'name': package.name, 'metapackage_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, '-DCATKIN_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('%s exists, skipping explicit cmake invocation...' % makefile_name) # Check to see if cmake needs to be run via make if use_ninja: make_check_cmake_cmd = ['ninja', 'build.ninja'] elif use_nmake: make_check_cmake_cmd = ['nmake', 'cmake_check_build_system'] else: 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 if use_ninja: make_executable = 'ninja' elif use_nmake: make_executable = 'nmake' else: make_executable = 'make' make_cmd = [make_executable] make_cmd.extend(handle_make_arguments(make_args)) 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 # NMake doesn't have an option to list target so try it anyway if install or use_nmake: if has_make_target(build_dir, 'install', use_ninja=use_ninja): make_install_cmd = [make_executable, '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}Package has no "@{boldon}install@{boldoff}" target, skipping "%s install" invocation...' % make_executable))
def build_catkin_package( path, package, workspace, buildspace, develspace, installspace, install, force_cmake, quiet, last_env, cmake_args, make_args, destdir=None, ): cprint("Processing @{cf}catkin@| package: '@!@{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 "metapackage" not in export_tags: print(colorize_line('Error: Package "%s" does not have a CMakeLists.txt file' % package.name)) sys.exit("Can not build catkin package without CMakeLists.txt file") # generate CMakeLists.txt for metpackages without one print(colorize_line('Warning: metapackage "%s" should have a CMakeLists.txt file' % package.name)) cmake_code = configure_file( get_metapackage_cmake_template_path(), {"name": package.name, "metapackage_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, "-DCATKIN_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}Package has no "@{boldon}install@{boldoff}" target, skipping "make install" invocation...'))
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, catkin_make_args=None, continue_from_pkg=False, only_pkg_with_deps=None, destdir=None, use_ninja=False): ''' Runs ``cmake``, ``make`` and optionally ``make install`` for all catkin packages in sourcespace_dir. It creates several folders in the current working directory. For non-catkin packages 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 catkin packages, ``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 catkin package 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 catkin_make_args: additional arguments for make but only for catkin 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`` :param use_ninja: if True, use ninja instead of make, ``bool`` ''' 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: sourcespace = os.path.join(workspace, 'src') if not os.path.exists(sourcespace): sys.exit('Could not find source space: {0}'.format(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') 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') print('Devel space: ' + str(develspace)) # Check install space if installspace is None: installspace = os.path.join(workspace, 'install_isolated') print('Install space: ' + str(installspace)) if cmake_args: print("Additional CMake Arguments: " + " ".join(cmake_args)) else: cmake_args = [] if not [arg for arg in cmake_args if arg.startswith('-G')]: if not use_ninja: cmake_args += ['-G', 'Unix Makefiles'] else: cmake_args += ['-G', 'Ninja'] elif use_ninja: print( colorize_line( "Error: either specify a generator using '-G...' or '--use-ninja' but not both" )) sys.exit(1) if make_args: print("Additional make Arguments: " + " ".join(make_args)) else: make_args = [] if catkin_make_args: print("Additional make Arguments for catkin packages: " + " ".join(catkin_make_args)) else: catkin_make_args = [] # Find packages packages = find_packages(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: package_names = [p.name for p in packages.values()] unknown_packages = [ name for name in only_pkg_with_deps if name not in package_names ] if unknown_packages: sys.exit('Packages not found in the workspace: %s' % ', '.join(unknown_packages)) whitelist_pkg_names = get_package_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('Packages not found in the workspace: %s' % ', '.join(unknown_packages)) # Report topological ordering ordered_packages = topological_order_packages(packages) unknown_build_types = [] msg = [] msg.append('@{pf}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' + ('~' * len(str(len(ordered_packages))))) msg.append('@{pf}~~@| traversing %d packages in topological order:' % len(ordered_packages)) for path, package in ordered_packages: if path is None: print( fmt('@{rf}Error: Circular dependency in subset of packages: @!%s@|' % package)) sys.exit('Can not build workspace with circular dependency') 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 = 'catkin' if build_type_tag == 'catkin': 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_packages))))) 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: Packages with unknown build types exist')) sys.exit('Can not build workspace with packages of unknown build_type') # Check to see if the workspace has changed cmake_args_with_spaces = list(cmake_args) if develspace: cmake_args_with_spaces.append('-DCATKIN_DEVEL_PREFIX=' + develspace) if installspace: cmake_args_with_spaces.append('-DCMAKE_INSTALL_PREFIX=' + installspace) if not force_cmake and cmake_input_changed( packages, buildspace, cmake_args=cmake_args_with_spaces, filename='catkin_make_isolated'): print( 'The packages or cmake arguments have changed, forcing cmake invocation' ) force_cmake = True ensure_workspace_marker(workspace) # Build packages pkg_develspace = None last_env = None for index, path_package in enumerate(ordered_packages): 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, catkin_make_args, destdir=destdir, use_ninja=use_ninja, number=index + 1, of=len(ordered_packages)) 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 ' + quote(os.path.join(buildspace, package.name)) + ' && ' # Then reproduce the command called if isinstance(e.cmd, list): # quote arguments to allow copy-n-paste of command cmd += ' '.join([quote(arg) for arg in e.cmd]) else: cmd += 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 package: '@!@{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 catkin.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 catkin.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 = { 'CATKIN_GLOBAL_BIN_DESTINATION': 'bin', 'CATKIN_LIB_ENVIRONMENT_PATHS': "'lib'", 'CATKIN_PKGCONFIG_ENVIRONMENT_PATHS': "os.path.join('lib', 'pkgconfig')", 'CMAKE_PREFIX_PATH_AS_IS': ';'.join(os.environ['CMAKE_PREFIX_PATH'].split( os.pathsep)), 'PYTHON_EXECUTABLE': sys.executable, '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 build_catkin_package( path, package, workspace, buildspace, develspace, installspace, install, jobs, force_cmake, quiet, cmake_args, make_args, catkin_python_path ): cprint( "Processing @{cf}catkin@| package: '@!@{bf}" + package.name + "@|'" ) # Make the build dir build_dir = _check_build_dir(package.name, workspace, buildspace) # Help find catkin cmake and python env = os.environ.copy() try: env['PYTHONPATH'] = env['PYTHONPATH'] + os.pathsep + catkin_python_path except KeyError: env['PYTHONPATH'] = catkin_python_path # Check for Makefile and maybe call cmake makefile = os.path.join(build_dir, 'Makefile') # check if toolchain.cmake, config.cmake exist toolchain_cmd = "-DCMAKE_TOOLCHAIN_FILE=%s" % os.path.join(workspace, 'toolchain.cmake') if os.path.isfile(os.path.join(workspace, 'toolchain.cmake')) else None config_cmd = "-C%s" % os.path.join(workspace, 'config.cmake') if os.path.isfile(os.path.join(workspace, 'config.cmake')) else None 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 'metapackage' not in export_tags: print(colorize_line('Error: Package "%s" does not have a CMakeLists.txt file' % package.name)) raise RuntimeError('Can not build catkin package without CMakeLists.txt file') # generate CMakeLists.txt for metpackages without one print(colorize_line('Warning: metapackage "%s" should have a CMakeLists.txt file' % package.name)) cmake_code = configure_file( get_metapackage_cmake_template_path(), {'name': package.name, 'metapackage_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, ] if toolchain_cmd: cmake_cmd.append(toolchain_cmd) if config_cmd: cmake_cmd.append(config_cmd) cmake_cmd.extend(cmake_args) isolation_print_command(' '.join(cmake_cmd)) #if last_env is not None: # cmake_cmd = [last_env] + cmake_cmd try: run_command_colorized(cmake_cmd, build_dir, quiet, env=env) except subprocess.CalledProcessError as e: # 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'] isolation_print_command(' '.join(make_check_cmake_cmd), build_dir) #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, env=env ) # Run make make_cmd = ['make', '-j' + str(jobs), '-l' + str(jobs)] make_cmd.extend(make_args) 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, env=env) # Make install if 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)
import os import shutil import sys import tempfile import unittest from catkin_pkg.cmake import configure_file data = configure_file(os.path.join(os.path.dirname(__file__), '..', '..', 'cmake', 'templates', '_setup_util.py.in'), { 'CATKIN_LIB_ENVIRONMENT_PATHS': "'lib'", 'CATKIN_PKGCONFIG_ENVIRONMENT_PATHS': "os.path.join('lib', 'pkgconfig')", 'CATKIN_GLOBAL_BIN_DESTINATION': 'bin', 'PYTHON_EXECUTABLE': sys.executable, 'PYTHON_INSTALL_DIR': 'pythonX.Y/packages', 'CMAKE_PREFIX_PATH_AS_IS': '', }) with tempfile.NamedTemporaryFile('w+') as setup_util_file: setup_util_file.write(data) setup_util_file.seek(0) import imp imp.load_source('setup_util', setup_util_file.name, setup_util_file.file) import setup_util from setup_util import _get_workspaces, _prefix_env_variable, _rollback_env_variable, CATKIN_MARKER_FILE class SetupUtilTest(unittest.TestCase): def test_get_reversed_workspaces(self):
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, catkin_make_args=None, continue_from_pkg=False, only_pkg_with_deps=None, destdir=None, ): """ Runs ``cmake``, ``make`` and optionally ``make install`` for all catkin packages in sourcespace_dir. It creates several folders in the current working directory. For non-catkin packages 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 catkin packages, ``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 catkin package 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 catkin_make_args: additional arguments for make but only for catkin 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: sourcespace = os.path.join(workspace, "src") if not os.path.exists(sourcespace): sys.exit("Could not find source space: {0}".format(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") 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") print("Devel space: " + str(develspace)) # Check install space if installspace is None: installspace = os.path.join(workspace, "install_isolated") 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 catkin_make_args: print("Additional make Arguments for catkin packages: " + " ".join(catkin_make_args)) else: catkin_make_args = [] # Find packages packages = find_packages(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: package_names = [p.name for p in packages.values()] unknown_packages = [name for name in only_pkg_with_deps if name not in package_names] if unknown_packages: sys.exit("Packages not found in the workspace: %s" % ", ".join(unknown_packages)) whitelist_pkg_names = get_package_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("Packages not found in the workspace: %s" % ", ".join(unknown_packages)) # Report topological ordering ordered_packages = topological_order_packages(packages) unknown_build_types = [] msg = [] msg.append("@{pf}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" + ("~" * len(str(len(ordered_packages))))) msg.append("@{pf}~~@| traversing %d packages in topological order:" % len(ordered_packages)) for path, package in ordered_packages: if path is None: print(fmt("@{rf}Error: Circular dependency in subset of packages: @!%s@|" % package)) sys.exit("Can not build workspace with circular dependency") 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 = "catkin" if build_type_tag == "catkin": 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_packages))))) 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: Packages with unknown build types exist")) sys.exit("Can not build workspace with packages of unknown build_type") # Check to see if the workspace has changed cmake_args_with_spaces = list(cmake_args) if develspace: cmake_args_with_spaces.append("-DCATKIN_DEVEL_PREFIX=" + develspace) if installspace: cmake_args_with_spaces.append("-DCMAKE_INSTALL_PREFIX=" + installspace) if not force_cmake and cmake_input_changed( packages, buildspace, cmake_args=cmake_args_with_spaces, filename="catkin_make_isolated" ): print("The packages or cmake arguments have changed, forcing cmake invocation") force_cmake = True ensure_workspace_marker(workspace) # Build packages pkg_develspace = None last_env = None for index, path_package in enumerate(ordered_packages): 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, catkin_make_args, destdir=destdir, number=index + 1, of=len(ordered_packages), ) 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 package: '@!@{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 catkin.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 catkin.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 = { "CATKIN_GLOBAL_BIN_DESTINATION": "bin", "CATKIN_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 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, catkin_make_args=None): ''' Runs ``cmake``, ``make`` and optionally ``make install`` for all catkin packages in sourcespace_dir. It creates several folders in the current working directory. For non-catkin packages 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 catkin packages, ``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 catkin package into the same devel space. does not work with non-catkin 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 catkin_make_args: additional arguments for make but only for catkin packages, ``[str]`` ''' 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 catkin_make_args: print("Additional make Arguments for catkin packages: " + " ".join(catkin_make_args)) else: catkin_make_args = [] # Find packages packages = find_packages(sourcespace, exclude_subspaces=True) if not packages: print(fmt("@{yf}No packages found in source space: %s@|" % sourcespace)) # verify that specified package exists in workspace if build_packages: packages_by_name = {p.name: path for path, p in packages.iteritems()} unknown_packages = [ p for p in build_packages if p not in packages_by_name ] if unknown_packages: sys.exit('Packages not found in the workspace: %s' % ', '.join(unknown_packages)) # Report topological ordering ordered_packages = topological_order_packages(packages) unknown_build_types = [] msg = [] msg.append('@{pf}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' + ('~' * len(str(len(ordered_packages))))) msg.append('@{pf}~~@| traversing %d packages in topological order:' % len(ordered_packages)) for path, package in ordered_packages: 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 = 'catkin' if build_type_tag == 'catkin': 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_packages))))) 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: Packages with unknown build types exist')) sys.exit('Can not build workspace with packages of unknown build_type') # Check to see if the workspace has changed if not force_cmake: force_cmake, install_toggled = cmake_input_changed( packages, buildspace, install=install, cmake_args=cmake_args, filename='catkin_make_isolated') if force_cmake: print( 'The packages or cmake arguments have changed, forcing cmake invocation' ) elif install_toggled: print( 'The install argument has been toggled, forcing cmake invocation on plain cmake package' ) # Build packages pkg_develspace = None last_env = None for index, path_package in enumerate(ordered_packages): 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: try: export_tags = [e.tagname for e in package.exports] is_cmake_package = 'cmake' in [ e.content for e in package.exports if e.tagname == 'build_type' ] last_env = build_package( path, package, workspace, buildspace, pkg_develspace, installspace, install, force_cmake or (install_toggled and is_cmake_package), quiet, last_env, cmake_args, make_args, catkin_make_args, number=index + 1, of=len(ordered_packages)) except Exception as e: import traceback traceback.print_exc() cprint('@{rf}@!<==@| ' + 'Failed to process package \'@!@{bf}' + package.name + '@|\': \n ' + ('KeyboardInterrupt' if isinstance( e, KeyboardInterrupt) else str(e))) if isinstance(e, subprocess.CalledProcessError): 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.') else: cprint("Skipping package: '@!@{bf}" + package.name + "@|'") last_env = get_new_env(package, pkg_develspace, installspace, install, last_env) # 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_sh = os.path.join(develspace, 'setup.sh') generated_setup_util_py = os.path.join(develspace, '_setup_util.py') if not merge and pkg_develspace: # generate env.sh and setup.sh which relay to last devel space with open(generated_env_sh, 'w') as f: f.write("""\ #!/usr/bin/env sh # generated from catkin.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) with open(generated_setup_sh, 'w') as f: f.write("""\ #!/usr/bin/env sh # generated from catkin.builder module . "{0}/setup.sh" """.format(pkg_develspace)) elif not pkg_develspace: # generate env.sh and setup.sh for an empty devel space if 'CMAKE_PREFIX_PATH' in os.environ.keys(): variables = { 'CATKIN_GLOBAL_BIN_DESTINATION': 'bin', 'CATKIN_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(), 'SETUP_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_DIR': develspace, '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} with open(generated_setup_sh, 'w') as f: f.write( configure_file( os.path.join(get_cmake_path(), 'templates', 'setup.sh.in'), variables)) if not merge and pkg_develspace: # remove _setup_util.py file which might have been generated for an empty if os.path.exists(generated_setup_util_py): os.remove(generated_setup_util_py) if not merge or not pkg_develspace: # generate setup.bash and setup.zsh for convenience variables = {'SETUP_DIR': develspace} with open(os.path.join(develspace, 'setup.bash'), 'w') as f: f.write( configure_file( os.path.join(get_cmake_path(), 'templates', 'setup.bash.in'), variables)) with open(os.path.join(develspace, 'setup.zsh'), 'w') as f: f.write( configure_file( os.path.join(get_cmake_path(), 'templates', 'setup.zsh.in'), variables))
import os import shutil import tempfile import unittest from catkin_pkg.cmake import configure_file data = configure_file(os.path.join(os.path.dirname(__file__), '..', '..', 'cmake', 'templates', '_setup_util.py.in'), { 'CATKIN_GLOBAL_LIB_DESTINATION': 'lib', 'CATKIN_GLOBAL_BIN_DESTINATION': 'bin', 'PYTHON_INSTALL_DIR': 'pythonX.Y/packages', 'CMAKE_PREFIX_PATH_AS_IS': '', }) with tempfile.TemporaryFile() as setup_util_file: setup_util_file.write(data) setup_util_file.seek(0) import imp imp.load_source('setup_util', '/somewhere/_setup_util.py', setup_util_file) import setup_util from setup_util import _get_workspaces, _prefix_env_variable, _rollback_env_variable, CATKIN_MARKER_FILE class SetupUtilTest(unittest.TestCase): def test_get_reversed_workspaces(self): try: rootdir = tempfile.mkdtemp() mock_env = {}