Exemplo n.º 1
0
def load_isolate_for_config(isolate_dir, content, config_variables):
    """Loads the .isolate file and returns the information unprocessed but
  filtered for the specific OS.

  Returns:
    tuple of command, dependencies, touched, read_only flag, isolate_dir.
    The dependencies are fixed to use os.path.sep.
  """
    # Load the .isolate file, process its conditions, retrieve the command and
    # dependencies.
    isolate = load_isolate_as_config(isolate_dir, eval_content(content), None)
    try:
        config_name = tuple(config_variables[var]
                            for var in isolate.config_variables)
    except KeyError:
        raise isolateserver.ConfigError(
            'These configuration variables were missing from the command line: %s'
            % ', '.join(
                sorted(set(isolate.config_variables) - set(config_variables))))

    # A configuration is to be created with all the combinations of free
    # variables.
    config = isolate.get_config(config_name)
    # Merge tracked and untracked variables, isolate.py doesn't care about the
    # trackability of the variables, only the build tool does.
    dependencies = sorted(
        f.replace('/', os.path.sep) for f in config.tracked + config.untracked)
    touched = sorted(f.replace('/', os.path.sep) for f in config.touched)
    return (config.command, dependencies, touched, config.read_only,
            config.isolate_dir)
Exemplo n.º 2
0
def replace_variable(part, variables):
    m = re.match(r'<\((' + VALID_VARIABLE + ')\)', part)
    if m:
        if m.group(1) not in variables:
            raise isolateserver.ConfigError(
                'Variable "%s" was not found in %s.\nDid you forget to specify '
                '--path-variable?' % (m.group(1), variables))
        return variables[m.group(1)]
    return part
Exemplo n.º 3
0
def verify_condition(condition, variables_and_values):
    """Verifies the |condition| dictionary is in the expected format.
  See verify_ast() for the meaning of |variables_and_values|.
  """
    VALID_INSIDE_CONDITION = ['variables']
    assert isinstance(condition, list), condition
    assert len(condition) == 2, condition
    expr, then = condition

    test_ast = compile(expr, '<condition>', 'eval', ast.PyCF_ONLY_AST)
    verify_ast(test_ast.body, variables_and_values)

    assert isinstance(then, dict), then
    assert set(VALID_INSIDE_CONDITION).issuperset(set(then)), then.keys()
    if not 'variables' in then:
        raise isolateserver.ConfigError(
            'Missing \'variables\' in condition %s' % condition)
    verify_variables(then['variables'])
Exemplo n.º 4
0
def match_configs(expr, config_variables, all_configs):
    """Returns the list of values from |values| that match the condition |expr|.

  Arguments:
    expr: string that is evaluatable with eval(). It is a GYP condition.
    config_variables: list of the name of the variables.
    all_configs: list of the list of possible values.

  If a variable is not referenced at all, it is marked as unbounded (free) with
  a value set to None.
  """
    # It is more than just eval'ing the variable, it needs to be double checked to
    # see if the variable is referenced at all. If not, the variable is free
    # (unbounded).
    # TODO(maruel): Use the intelligent way by inspecting expr instead of doing
    # trial and error to figure out which variable is bound.
    combinations = []
    for bound_variables in itertools.product((True, False),
                                             repeat=len(config_variables)):
        # Add the combination of variables bound.
        combinations.append(
            ([c for c, b in zip(config_variables, bound_variables) if b],
             set(
                 tuple(v if b else None for v, b in zip(line, bound_variables))
                 for line in all_configs)))

    out = []
    for variables, configs in combinations:
        # Strip variables and see if expr can still be evaluated.
        for values in configs:
            globs = {'__builtins__': None}
            globs.update(zip(variables, (v for v in values if v is not None)))
            try:
                assertion = eval(expr, globs, {})
            except NameError:
                continue
            if not isinstance(assertion, bool):
                raise isolateserver.ConfigError('Invalid condition')
            if assertion:
                out.append(values)
    return out
Exemplo n.º 5
0
def load_isolate_as_config(isolate_dir, value, file_comment):
    """Parses one .isolate file and returns a Configs() instance.

  Arguments:
    isolate_dir: only used to load relative includes so it doesn't depend on
                 cwd.
    value: is the loaded dictionary that was defined in the gyp file.
    file_comment: comments found at the top of the file so it can be preserved.

  The expected format is strict, anything diverting from the format below will
  throw an assert:
  {
    'includes': [
      'foo.isolate',
    ],
    'conditions': [
      ['OS=="vms" and foo=42', {
        'variables': {
          'command': [
            ...
          ],
          'isolate_dependency_tracked': [
            ...
          ],
          'isolate_dependency_untracked': [
            ...
          ],
          'read_only': 0,
        },
      }],
      ...
    ],
    'variables': {
      ...
    },
  }
  """
    assert os.path.isabs(isolate_dir), isolate_dir
    if any(len(cond) == 3 for cond in value.get('conditions', [])):
        raise isolateserver.ConfigError(
            'Using \'else\' is not supported anymore.')
    variables_and_values = {}
    verify_root(value, variables_and_values)
    if variables_and_values:
        config_variables, config_values = zip(
            *sorted(variables_and_values.iteritems()))
        all_configs = list(itertools.product(*config_values))
    else:
        config_variables = ()
        all_configs = []

    isolate = Configs(file_comment, config_variables)

    # Add global variables. The global variables are on the empty tuple key.
    isolate.set_config((None, ) * len(config_variables),
                       ConfigSettings(value.get('variables', {}), isolate_dir))

    # Add configuration-specific variables.
    for expr, then in value.get('conditions', []):
        configs = match_configs(expr, config_variables, all_configs)
        new = Configs(None, config_variables)
        for config in configs:
            new.set_config(config,
                           ConfigSettings(then['variables'], isolate_dir))
        isolate = isolate.union(new)

    # Load the includes. Process them in reverse so the last one take precedence.
    for include in reversed(value.get('includes', [])):
        if os.path.isabs(include):
            raise isolateserver.ConfigError(
                'Failed to load configuration; absolute include path \'%s\'' %
                include)
        included_isolate = os.path.normpath(os.path.join(isolate_dir, include))
        if sys.platform == 'win32':
            if included_isolate[0].lower() != isolate_dir[0].lower():
                raise isolateserver.ConfigError(
                    'Can\'t reference a .isolate file from another drive')
        with open(included_isolate, 'r') as f:
            included_isolate = load_isolate_as_config(
                os.path.dirname(included_isolate), eval_content(f.read()),
                None)
        isolate = isolate.union(included_isolate)

    return isolate
Exemplo n.º 6
0
def convert_map_to_isolate_dict(values, config_variables):
    """Regenerates back a .isolate configuration dict from files and dirs
  mappings generated from reduce_inputs().
  """
    # Gather a list of configurations for set inversion later.
    all_mentioned_configs = set()
    for configs_by_item in values.itervalues():
        for configs in configs_by_item.itervalues():
            all_mentioned_configs.update(configs)

    # Invert the mapping to make it dict first.
    conditions = {}
    for key in values:
        for item, configs in values[key].iteritems():
            then = conditions.setdefault(frozenset(configs), {})
            variables = then.setdefault('variables', {})

            if key == 'read_only':
                if not isinstance(item, int):
                    raise isolateserver.ConfigError(
                        'Unexpected entry type %r for key %s' % (item, key))
                variables[key] = item
            elif key == 'command':
                if not isinstance(item, tuple):
                    raise isolateserver.ConfigError(
                        'Unexpected entry type %r for key %s' % (item, key))
                if key in variables:
                    raise isolateserver.ConfigError(
                        'Unexpected duplicate key %s' % key)
                if not item:
                    raise isolateserver.ConfigError(
                        'Expected non empty entry in %s' % key)
                variables[key] = list(item)
            elif key in (KEY_TOUCHED, KEY_TRACKED, KEY_UNTRACKED):
                if not isinstance(item, basestring):
                    raise isolateserver.ConfigError(
                        'Unexpected entry type %r' % item)
                if not item:
                    raise isolateserver.ConfigError(
                        'Expected non empty entry in %s' % key)
                # The list of items (files or dirs). Append the new item and keep
                # the list sorted.
                l = variables.setdefault(key, [])
                l.append(item)
                l.sort()
            else:
                raise isolateserver.ConfigError('Unexpected key %s' % key)

    if all_mentioned_configs:
        # Change [(1, 2), (3, 4)] to [set(1, 3), set(2, 4)]
        config_values = map(set, zip(*all_mentioned_configs))
        for i in config_values:
            i.discard(None)
        sef = short_expression_finder.ShortExpressionFinder(
            zip(config_variables, config_values))
        conditions = sorted([sef.get_expr(c), v]
                            for c, v in conditions.iteritems())
    else:
        conditions = []
    out = {'conditions': conditions}
    for c in conditions:
        if c[0] == '':
            # Extract the global.
            out.update(c[1])
            conditions.remove(c)
            break
    return out