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
예제 #2
0
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)
예제 #3
0
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))
예제 #4
0
def cprint(msg, end=None):
    print(fmt(msg), end=end)
예제 #5
0
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...'))