Ejemplo n.º 1
0
    def do(cls, ws, args):
        '''Executes the build subcmd.'''
        ws_config = get_ws_config(get_ws_dir(args.root, ws))
        d = parse_manifest(args.root)

        # Validate.
        for project in args.projects:
            if project not in d:
                raise WSError('unknown project %s' % project)

        if len(args.projects) == 0:
            projects = d.keys()
        else:
            projects = args.projects

        # Build in reverse-dependency order.
        order = dependency_closure(d, projects)

        # Get all checksums; since this is a nop build bottle-neck, do it in
        # parallel. On my machine, this produces a ~20% speedup on a nop
        # "build-all".
        pool = multiprocessing.Pool(multiprocessing.cpu_count())
        src_dirs = [get_source_dir(args.root, d, proj) for proj in order]
        checksums = pool.map(calculate_checksum, src_dirs)

        for i, proj in enumerate(order):
            log('building %s' % proj)
            checksum = checksums[i]
            success = _build(args.root, ws, proj, d, checksum, ws_config,
                             args.force)
            if not success:
                raise WSError('%s build failed' % proj)
Ejemplo n.º 2
0
    def do(cls, ws, args):
        '''Executes the test subcmd.'''
        d = parse_manifest(args.root)

        # Validate.
        for project in args.projects:
            if project not in d:
                raise WSError('unknown project %s' % project)

        if len(args.projects) == 0:
            projects = d.keys()
        else:
            projects = args.projects

        for proj in projects:
            build_dir = get_build_dir(ws, proj)
            if not os.path.isdir(build_dir):
                raise WSError('build directory for %s doesn\'t exist; have '
                              'you built it yet?' % proj)

            if 'tests' not in d[proj]:
                raise WSError('no test configured for %s' % proj)

        for proj in projects:
            log('testing %s' % proj)
            build_env = get_build_env(ws, d, proj)
            cmds = d[proj]['tests']
            _test(args.root, ws, proj, cmds, build_env)
Ejemplo n.º 3
0
def rmtree(path, fail_ok=False):
    '''Removes the given file or directory, recursively.'''
    log('recursively removing %s' % path)
    if not dry_run():
        try:
            shutil.rmtree(path)
        except FileNotFoundError:
            if not fail_ok:
                raise
Ejemplo n.º 4
0
def remove(path, fail_ok=False):
    '''Removes the given path.'''
    log('removing %s' % path)
    if not dry_run():
        try:
            os.unlink(path)
        except FileNotFoundError:
            if not fail_ok:
                raise
Ejemplo n.º 5
0
def invalidate_checksum(ws, proj):
    '''Invalidates the current project checksum. This can be used to force a
    project to rebuild, for example if one of its dependencies rebuilds.'''
    log('invalidating checksum for %s' % proj)
    if dry_run():
        return

    try:
        remove(get_checksum_file(ws, proj))
    except OSError as e:
        if e.errno != errno.ENOENT:
            raise
Ejemplo n.º 6
0
def sync_config(ws):
    '''Writes out the config if and only if it changed since we first read
       it.'''
    if _WS_CONFIG == _ORIG_WS_CONFIG:
        log('ws config did not change, so not updating')
        return
    log('updating config at %s' % ws)

    if dry_run():
        return

    write_config(ws, _WS_CONFIG)
Ejemplo n.º 7
0
def get_ws_config(ws):  # noqa: E302
    '''Parses the current workspace config, returning a dictionary of the
    state.'''
    global _WS_CONFIG
    if _WS_CONFIG is None:
        config_path = get_ws_config_path(ws)
        with open(config_path, 'r') as f:
            _WS_CONFIG = yaml.safe_load(f)
            global _ORIG_WS_CONFIG
            # Save a copy of the config so we know later whether or not to
            # write it out when someone asks to sync the config.
            _ORIG_WS_CONFIG = copy.deepcopy(_WS_CONFIG)

    # Check if projects were added or removed from the manifest. If so, the
    # config needs to be updated accordingly.
    d = parse_manifest(get_ws_root(ws))
    deletions = []
    for proj in _WS_CONFIG['projects']:
        if proj not in d:
            deletions.append(proj)
    for proj in deletions:
        del _WS_CONFIG['projects'][proj]
        checksum_file = get_checksum_file(ws, proj)
        proj_dir = get_proj_dir(ws, proj)
        log('removing project %s, which is not in the manifest' % proj,
            logging.INFO)
        remove(checksum_file, True)
        rmtree(proj_dir, True)

    for proj in d:
        if proj in _WS_CONFIG['projects']:
            continue
        # Project is not in the config, so add it in.
        _WS_CONFIG['projects'][proj] = get_new_config(proj)

    # Split all project arguments by spaces so that args like '-D something'
    # turn into ['-D', 'something'], which is what exec requires. We do this
    # at parse time rather than in the "config" command to allow the user to
    # hand-edit in a natural way and have the config still work properly. The
    # next time the config is saved, it will be split by spaces.
    for proj, proj_map in _WS_CONFIG['projects'].items():
        parsed_args = []
        args = proj_map['args']
        for arg in args:
            parsed_args.extend(arg.split())
        proj_map['args'] = parsed_args

    return _WS_CONFIG
Ejemplo n.º 8
0
def merge_includes(root, d, parent_manifest):
    '''Recursively merge all the include lines from the manifest into the given
    dictionary. Include paths are relative to the including manifest's parent
    directory.'''
    included = set(include_paths(d, parent_manifest))
    queue = collections.deque(included)
    while len(queue) > 0:
        manifest = queue.popleft()
        d_include = parse_yaml(root, manifest)
        log('merging manifest %s into %s' % (manifest, parent_manifest))
        merge_manifest(d, parent_manifest, d_include, manifest)
        for path in include_paths(d_include, manifest):
            # Prevent double-inclusion.
            if path in included:
                continue
            queue.append(path)
            included.add(path)
Ejemplo n.º 9
0
def _force_clean(ws, proj):
    '''Performs a force-clean of a project, removing all files instead of
    politely calling the clean function of the underlying build system.'''
    build_dir = get_build_dir(ws, proj)
    log('removing %s' % build_dir)
    if dry_run():
        return
    try:
        rmtree(build_dir)
    except OSError as e:
        if e.errno == errno.ENOENT:
            log('%s already removed' % build_dir)
        else:
            raise

    config = get_ws_config(ws)
    config['projects'][proj]['taint'] = False
Ejemplo n.º 10
0
Archivo: env.py Proyecto: pinklite34/ws
    def do(cls, ws, args):
        '''Executes the env command.'''
        build_dir = get_build_dir(ws, args.project)
        if not os.path.isdir(build_dir):
            raise WSError('build directory for %s doesn\'t exist; have you '
                          'built it yet?' % args.project)

        d = parse_manifest(args.root)
        build_env = get_build_env(ws, d, args.project, True)

        # Add the build directory to the path for convenience of running
        # non-installed binaries, such as unit tests.
        merge_var(build_env, 'PATH', [build_dir])

        if len(args.command) > 0:
            cmd = args.command
        else:
            cmd = [get_shell()]

        exe = os.path.basename(cmd[0])
        if exe == 'bash':
            # Tweak the prompt to make it obvious we're in a special env.
            prompt = (
                '\\[\033[1;32m\\][ws:%s env]\\[\033[m\\] \\u@\\h:\\w\\$ '  # noqa: E501
                % args.project)
            build_env['PS1'] = prompt
            cmd.insert(1, '--norc')

        # Set an env var so the user can easily cd $WSBUILD and run tests or
        # similar inside the build directory.
        build_env['WSBUILD'] = build_dir

        log('execing with %s build environment: %s' % (args.project, cmd))

        if args.build_dir:
            args.current_dir = build_dir

        if args.current_dir is not None:
            os.chdir(args.current_dir)

        os.execvpe(cmd[0], cmd, build_env)
Ejemplo n.º 11
0
def rename(old, new):
    '''Renames the given path.'''
    log('renaming %s --> %s' % (old, new))
    if not dry_run():
        os.rename(old, new)
Ejemplo n.º 12
0
def symlink(dest, src):
    '''Makes a symlink from src to dest.'''
    log('making symlink %s --> %s' % (src, dest))
    if not dry_run():
        os.symlink(dest, src)
Ejemplo n.º 13
0
def mkdir(path):
    '''Makes a directory.'''
    log('making directory %s' % path)
    if not dry_run():
        os.mkdir(path)
Ejemplo n.º 14
0
def _build(root, ws, proj, d, current, ws_config, force):
    '''Builds a given project.'''
    if not ws_config['projects'][proj]['enable']:
        log('not building manually disabled project %s' % proj,
            logging.WARNING)
        return True

    if ws_config['projects'][proj]['taint']:
        log('force-cleaning tainted project %s' % proj, logging.WARNING)
        clean(root, ws, proj, d, True)

    source_dir = get_source_dir(root, d, proj)
    if not force:
        stored = get_stored_checksum(ws, proj)
        if current == stored:
            log('checksum for %s is current; skipping' % proj)
            return True
    else:
        log('forcing a build of %s' % proj)

    # Make the project directory if needed.
    proj_dir = get_proj_dir(ws, proj)
    try:
        mkdir(proj_dir)
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise

    # Make the build directory if needed.
    build_dir = get_build_dir(ws, proj)
    try:
        mkdir(build_dir)
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise
        needs_configure = False
    else:
        needs_configure = True

    # Populate the convenience source link.
    source_link = get_source_link(ws, proj)
    if not os.path.exists(source_link):
        source_dir = get_source_dir(root, d, proj)
        symlink(source_dir, source_link)

    # Invalidate the checksums for any downstream projects.
    for downstream_dep in d[proj]['downstream']:
        invalidate_checksum(ws, downstream_dep)

    # Add envs to find all projects on which this project is dependent.
    build_env = get_build_env(ws, d, proj)

    # Configure.
    builder = get_builder(d, proj)
    prefix = get_install_dir(ws, proj)
    extra_args = d[proj]['args'] + ws_config['projects'][proj]['args']
    if needs_configure:
        try:
            success = builder.conf(proj, prefix, source_dir, build_dir,
                                   build_env, ws_config['type'],
                                   d[proj]['builder-args'], extra_args)
        except Exception as _e:
            success = False
            e = _e
        else:
            e = None
        if not success:
            # Remove the build directory if we failed so that we are forced to
            # re-run configure next time.
            rmtree(build_dir)
            if e is not None:
                raise e
            else:
                return False

    # Build.
    success = builder.build(proj, prefix, source_dir, build_dir, build_env,
                            d[proj]['targets'], d[proj]['builder-args'],
                            d[proj]['args'])
    if success:
        set_stored_checksum(ws, proj, current)

    return success
Ejemplo n.º 15
0
Archivo: config.py Proyecto: rfrowe/ws
    def do(cls, ws, args):
        '''Executes the config command.'''
        config = get_ws_config(ws)
        if args.list:
            print(yaml.dump(config, default_flow_style=False), end='')
            return

        for arg in args.options:
            split = arg.split('=')
            split_len = len(split)
            key = split[0]
            if split_len == 1:
                val = None
            elif split_len == 2:
                val = split[1]
            else:
                val = '='.join(split[1:])

            project = args.project
            if project is not None:
                # Project-specific option.
                d = parse_manifest(args.root)
                if project not in d:
                    raise WSError('project "%s" not found' % project)

                can_taint = False
                if key == 'enable':
                    val = parse_bool_val(val)
                    can_taint = True
                elif key == 'args':
                    val = parse_build_args(val)
                    if val is None:
                        raise WSError('build args are not in the right format '
                                      '("args=key=val")')
                    can_taint = True
                else:
                    raise WSError('project key "%s" not found' % key)

                try:
                    proj_config = config['projects'][project]
                except KeyError:
                    proj_config = {}
                    config['projects'][project] = proj_config

                if can_taint and proj_config[key] != val:
                    log('tainting project %s' % project)
                    proj_config['taint'] = True

                proj_config[key] = val

            else:
                # Global option.
                if key == 'type':
                    if val is None or val not in BUILD_TYPES:
                        raise WSError('"type" key must be one of %s' %
                                      str(BUILD_TYPES))
                    if config[key] != val:
                        for proj_config in config['projects'].values():
                            proj_config['taint'] = True

                config[key] = val