def initialize_parse_state(self, build_file): """Creates a fresh parse state for the given build file.""" type_aliases = self._exposed_objects.copy() registered_addressable_instances = [] def registration_callback(address, addressable): registered_addressable_instances.append((address, addressable)) for alias, addressable_type in self._addressable_alias_map.items(): call_proxy = AddressableCallProxy( addressable_type=addressable_type, build_file=build_file, registration_callback=registration_callback) type_aliases[alias] = call_proxy parse_context = ParseContext(rel_path=build_file.spec_path, type_aliases=type_aliases) parse_globals = type_aliases.copy() for alias, object_factory in self._exposed_context_aware_object_factories.items( ): parse_globals[alias] = object_factory(parse_context) return self.ParseState(registered_addressable_instances, parse_globals)
def initialize_parse_state(self, build_file): """Creates a fresh parse state for the given build file. :param build_file: The BUILD file to set up a new ParseState for. :type build_file: :class:`pants.base.build_file.BuildFile` :returns: A fresh ParseState for parsing the given `build_file` with. :rtype: :class:`BuildConfiguration.ParseState` """ # TODO(John Sirois): Introduce a factory method to seal the BuildConfiguration and add a check # there that all anonymous types are covered by context aware object factories that are # Macro instances. Without this, we could have non-Macro context aware object factories being # asked to be a BuildFileTargetFactory when they are not (in SourceRoot registration context). # See: https://github.com/pantsbuild/pants/issues/2125 type_aliases = self._exposed_object_by_alias.copy() parse_context = ParseContext(rel_path=build_file.spec_path, type_aliases=type_aliases) def create_call_proxy(tgt_type, tgt_alias=None): def registration_callback(address, addressable): parse_context._storage.add(addressable, name=address.target_name) addressable_factory = self._get_addressable_factory( tgt_type, tgt_alias) return AddressableCallProxy( addressable_factory=addressable_factory, build_file=build_file, registration_callback=registration_callback) # Expose all aliased Target types. for alias, target_type in self._target_by_alias.items(): proxy = create_call_proxy(target_type, alias) type_aliases[alias] = proxy # Expose aliases for exposed objects and targets in the BUILD file. parse_globals = type_aliases.copy() # Now its safe to add mappings from both the directly exposed and macro-created target types to # their call proxies for context awares and macros to use to manufacture targets by type # instead of by alias. for alias, target_type in self._target_by_alias.items(): proxy = type_aliases[alias] type_aliases[target_type] = proxy for target_macro_factory in self._target_macro_factory_by_alias.values( ): for target_type in target_macro_factory.target_types: proxy = create_call_proxy(target_type) type_aliases[target_type] = proxy for alias, object_factory in self._exposed_context_aware_object_factory_by_alias.items( ): parse_globals[alias] = object_factory(parse_context) for alias, target_macro_factory in self._target_macro_factory_by_alias.items( ): parse_globals[alias] = target_macro_factory.target_macro( parse_context) return self.ParseState(parse_context, parse_globals)
def __init__(self, address=None, payload=None, sources=None, **kwargs): if not sources: # We grab all files in the current directory except BUILD files for 2 reasons: # 1. cgo: If a file imports "C" then it may rely on sibling .c, .cc, etc files that `go build` # will compile. # 2. resources: Even though go does not support them; ie by providing a protocol to embed them # in binaries, it does allow them to be placed in a directory where a test might use them # for example via plain old filesystem access. globs = Globs( ParseContext(rel_path=address.spec_path, type_aliases={})) sources = globs( '*', exclude=[ globs('BUILD*'), # This skips subdir content. globs('*/**') ]) payload = payload or Payload() payload.add_fields({ 'sources': self.create_sources_field(sources=sources, sources_rel_path=address.spec_path, key_arg='sources'), }) super(GoLocalSource, self).__init__(address=address, payload=payload, **kwargs)
def _get_symbols(cls, symbol_table_cls): symbol_table = symbol_table_cls.table() # TODO: nasty escape hatch aliases = symbol_table_cls.aliases() class Registrar(BuildFileTargetFactory): def __init__(self, type_alias, object_type): self._type_alias = type_alias self._object_type = object_type self._serializable = Serializable.is_serializable_type( self._object_type) @memoized_property def target_types(self): return [self._object_type] def __call__(self, *args, **kwargs): name = kwargs.get('name') if name and self._serializable: obj = self._object_type(type_alias=self._type_alias, **kwargs) cls._objects.append(obj) return obj else: return self._object_type(*args, **kwargs) # Compute a single ParseContext for a default path, which we will mutate for each parsed path. symbols = {} for alias, target_macro_factory in aliases.target_macro_factories.items( ): for target_type in target_macro_factory.target_types: symbols[target_type] = TargetAdaptor parse_context = ParseContext(rel_path='', type_aliases=symbols) for alias, symbol in symbol_table.items(): registrar = Registrar(alias, symbol) symbols[alias] = registrar symbols[symbol] = registrar if aliases.objects: symbols.update(aliases.objects) # Compute "per path" symbols (which will all use the same mutable ParseContext). aliases = symbol_table_cls.aliases() for alias, object_factory in aliases.context_aware_object_factories.items( ): symbols[alias] = object_factory(parse_context) for alias, target_macro_factory in aliases.target_macro_factories.items( ): symbols[alias] = target_macro_factory.target_macro(parse_context) for target_type in target_macro_factory.target_types: symbols[target_type] = TargetAdaptor # TODO: Replace builtins for paths with objects that will create wrapped PathGlobs objects. symbols['globs'] = Globs symbols['rglobs'] = RGlobs symbols['zglobs'] = ZGlobs return symbols, parse_context
def per_path_symbol_factory(path, global_symbols): per_path_symbols = {} symbols = global_symbols.copy() for alias, target_macro_factory in aliases.target_macro_factories.items( ): for target_type in target_macro_factory.target_types: symbols[ target_type] = lambda *args, **kwargs: per_path_symbols[ alias](*args, **kwargs) parse_context = ParseContext(rel_path=os.path.relpath( os.path.dirname(path), build_root), type_aliases=symbols) for alias, object_factory in aliases.context_aware_object_factories.items( ): per_path_symbols[alias] = object_factory(parse_context) for alias, target_macro_factory in aliases.target_macro_factories.items( ): target_macro = target_macro_factory.target_macro(parse_context) per_path_symbols[alias] = target_macro for target_type in target_macro_factory.target_types: per_path_symbols[target_type] = target_macro return per_path_symbols
def initialize_parse_state(self, build_file): """Creates a fresh parse state for the given build file.""" type_aliases = self._exposed_objects.copy() registered_addressable_instances = [] def registration_callback(address, addressable): registered_addressable_instances.append((address, addressable)) for alias, addressable_type in self._addressable_alias_map.items(): call_proxy = AddressableCallProxy( addressable_type=addressable_type, build_file=build_file, registration_callback=registration_callback) type_aliases[alias] = call_proxy # Expose aliases for exposed objects and addressables in the BUILD file. parse_globals = type_aliases.copy() # Now its safe to add concrete addressable type to proxy mappings for context awares to use. for alias, addressable_type in self._addressable_alias_map.items(): target_type = addressable_type.get_target_type() proxy = type_aliases[alias] type_aliases[target_type] = proxy parse_context = ParseContext(rel_path=build_file.spec_path, type_aliases=type_aliases) for alias, object_factory in self._exposed_context_aware_object_factories.items( ): parse_globals[alias] = object_factory(parse_context) return self.ParseState(registered_addressable_instances, parse_globals)
def _get_symbols(cls, symbol_table_cls): symbol_table = symbol_table_cls.table() # TODO: Nasty escape hatch: see https://github.com/pantsbuild/pants/issues/3561 aliases = symbol_table_cls.aliases() class Registrar(BuildFileTargetFactory): def __init__(self, type_alias, object_type): self._type_alias = type_alias self._object_type = object_type self._serializable = Serializable.is_serializable_type(self._object_type) @memoized_property def target_types(self): return [self._object_type] def __call__(self, *args, **kwargs): name = kwargs.get('name') if name and self._serializable: kwargs['type_alias'] = self._type_alias obj = self._object_type(**kwargs) cls._objects.append(obj) return obj else: return self._object_type(*args, **kwargs) symbols = {} for alias, symbol in symbol_table.items(): registrar = Registrar(alias, symbol) symbols[alias] = registrar symbols[symbol] = registrar if aliases.objects: symbols.update(aliases.objects) # Compute "per path" symbols (which will all use the same mutable ParseContext). # TODO: See https://github.com/pantsbuild/pants/issues/3561 parse_context = ParseContext(rel_path='', type_aliases=symbols) for alias, object_factory in aliases.context_aware_object_factories.items(): symbols[alias] = object_factory(parse_context) for alias, target_macro_factory in aliases.target_macro_factories.items(): underlying_symbol = symbols.get(alias, TargetAdaptor) symbols[alias] = target_macro_factory.target_macro(parse_context) for target_type in target_macro_factory.target_types: symbols[target_type] = Registrar(alias, underlying_symbol) # TODO: Replace builtins for paths with objects that will create wrapped PathGlobs objects. # The strategy for https://github.com/pantsbuild/pants/issues/3560 should account for # migrating these additional captured arguments to typed Sources. symbols['globs'] = Globs symbols['rglobs'] = RGlobs symbols['zglobs'] = ZGlobs symbols['bundle'] = BundleAdaptor return symbols, parse_context
def _instantiate_jvm_app(self, kwargs): """For JvmApp target, convert BundleAdaptor to BundleProps.""" parse_context = ParseContext(kwargs['address'].spec_path, dict()) bundleprops_factory = Bundle(parse_context) kwargs['bundles'] = [ bundleprops_factory.create_bundle_props(bundle) for bundle in kwargs['bundles'] ] return JvmApp(build_graph=self, **kwargs)
def _instantiate_app(self, target_cls: Type[TargetV1], kwargs) -> TargetV1: """For App targets, convert BundleAdaptor to BundleProps.""" parse_context = ParseContext(kwargs["address"].spec_path, dict()) bundleprops_factory = Bundle(parse_context) kwargs["bundles"] = [ bundleprops_factory.create_bundle_props(bundle) for bundle in kwargs["bundles"] ] return target_cls(build_graph=self, **kwargs)
def _generate_symbols( build_root: str, target_type_aliases: Iterable[str], object_aliases: BuildFileAliases, use_deprecated_python_macros: bool, ) -> tuple[dict[str, Any], ParseState]: # N.B.: We re-use the thread local ParseState across symbols for performance reasons. # This allows a single construction of all symbols here that can be re-used for each BUILD # file parse with a reset of the ParseState for the calling thread. parse_state = ParseState() class Registrar: def __init__(self, type_alias: str) -> None: self._type_alias = type_alias def __call__(self, **kwargs: Any) -> TargetAdaptor: # Target names default to the name of the directory their BUILD file is in # (as long as it's not the root directory). if "name" not in kwargs: dirname = os.path.basename(parse_state.rel_path()) if not dirname: raise UnaddressableObjectError( "Targets in root-level BUILD files must be named explicitly." ) kwargs["name"] = dirname target_adaptor = TargetAdaptor(self._type_alias, **kwargs) parse_state.add(target_adaptor) return target_adaptor symbols: dict[str, Any] = { **object_aliases.objects, "build_file_dir": lambda: PurePath(parse_state.rel_path()), } symbols.update((alias, Registrar(alias)) for alias in target_type_aliases if not use_deprecated_python_macros or alias not in _AMBIGUOUS_PYTHON_MACRO_SYMBOLS) parse_context = ParseContext(build_root=build_root, type_aliases=symbols, rel_path_oracle=parse_state) for alias, object_factory in object_aliases.context_aware_object_factories.items( ): if use_deprecated_python_macros or alias not in _AMBIGUOUS_PYTHON_MACRO_SYMBOLS: symbols[alias] = object_factory(parse_context) return symbols, parse_state
def _generate_symbols( target_type_aliases: Iterable[str], object_aliases: BuildFileAliases, ) -> Tuple[Dict[str, Any], ParseContext]: symbols: Dict[str, Any] = {} # Compute "per path" symbols. For performance, we use the same ParseContext, which we # mutate to set the rel_path appropriately before it's actually used. This allows this # method to reuse the same symbols for all parses. Meanwhile, we set the rel_path to None, # so that we get a loud error if anything tries to use it before it's set. # TODO: See https://github.com/pantsbuild/pants/issues/3561 parse_context = ParseContext(rel_path=None, type_aliases=symbols) class Registrar: def __init__(self, parse_context: ParseContext, type_alias: str): self._parse_context = parse_context self._type_alias = type_alias def __call__(self, *args, **kwargs): # Target names default to the name of the directory their BUILD file is in # (as long as it's not the root directory). if "name" not in kwargs: dirname = os.path.basename(self._parse_context.rel_path) if not dirname: raise UnaddressableObjectError( "Targets in root-level BUILD files must be named explicitly." ) kwargs["name"] = dirname kwargs.setdefault("type_alias", self._type_alias) target_adaptor = TargetAdaptor(**kwargs) self._parse_context._storage.add(target_adaptor) return target_adaptor symbols.update({ alias: Registrar(parse_context, alias) for alias in target_type_aliases }) symbols.update(object_aliases.objects) for alias, object_factory in object_aliases.context_aware_object_factories.items( ): symbols[alias] = object_factory(parse_context) return symbols, parse_context
def _per_path_symbol_factory(cls, path, aliases, global_symbols): per_path_symbols = {} symbols = global_symbols.copy() for alias, target_macro_factory in aliases.target_macro_factories.items(): for target_type in target_macro_factory.target_types: symbols[target_type] = Target parse_context = ParseContext(rel_path=os.path.dirname(path), type_aliases=symbols) for alias, object_factory in aliases.context_aware_object_factories.items(): per_path_symbols[alias] = object_factory(parse_context) for alias, target_macro_factory in aliases.target_macro_factories.items(): target_macro = target_macro_factory.target_macro(parse_context) per_path_symbols[alias] = target_macro for target_type in target_macro_factory.target_types: per_path_symbols[target_type] = Target return per_path_symbols
def _bundle(rel_path): pc = ParseContext(rel_path=rel_path, type_aliases={}) return Bundle(pc)
def _generate_symbols(symbol_table, aliases): symbols = {} # Compute "per path" symbols. For performance, we use the same ParseContext, which we # mutate (in a critical section) to set the rel_path appropriately before it's actually used. # This allows this method to reuse the same symbols for all parses. Meanwhile we set the # rel_path to None, so that we get a loud error if anything tries to use it before it's set. # TODO: See https://github.com/pantsbuild/pants/issues/3561 parse_context = ParseContext(rel_path=None, type_aliases=symbols) class Registrar(BuildFileTargetFactory): def __init__(self, parse_context, type_alias, object_type): self._parse_context = parse_context self._type_alias = type_alias self._object_type = object_type self._serializable = Serializable.is_serializable_type( self._object_type) @memoized_property def target_types(self): return [self._object_type] def __call__(self, *args, **kwargs): # Target names default to the name of the directory their BUILD file is in # (as long as it's not the root directory). if 'name' not in kwargs and issubclass(self._object_type, TargetAdaptor): dirname = os.path.basename(self._parse_context.rel_path) if dirname: kwargs['name'] = dirname else: raise UnaddressableObjectError( 'Targets in root-level BUILD files must be named explicitly.' ) name = kwargs.get('name') if name and self._serializable: kwargs.setdefault('type_alias', self._type_alias) obj = self._object_type(**kwargs) self._parse_context._storage.add(obj) return obj else: return self._object_type(*args, **kwargs) for alias, symbol in symbol_table.table.items(): registrar = Registrar(parse_context, alias, symbol) symbols[alias] = registrar symbols[symbol] = registrar if aliases.objects: symbols.update(aliases.objects) for alias, object_factory in aliases.context_aware_object_factories.items( ): symbols[alias] = object_factory(parse_context) for alias, target_macro_factory in aliases.target_macro_factories.items( ): underlying_symbol = symbols.get(alias, TargetAdaptor) symbols[alias] = target_macro_factory.target_macro(parse_context) for target_type in target_macro_factory.target_types: symbols[target_type] = Registrar(parse_context, alias, underlying_symbol) # TODO: Replace builtins for paths with objects that will create wrapped PathGlobs objects. # The strategy for https://github.com/pantsbuild/pants/issues/3560 should account for # migrating these additional captured arguments to typed Sources. class GlobWrapper: def __init__(self, parse_context, glob_type): self._parse_context = parse_context self._glob_type = glob_type def __call__(self, *args, **kwargs): return self._glob_type(*args, spec_path=self._parse_context.rel_path, **kwargs) symbols['globs'] = GlobWrapper(parse_context, Globs) symbols['rglobs'] = GlobWrapper(parse_context, RGlobs) symbols['zglobs'] = GlobWrapper(parse_context, ZGlobs) symbols['bundle'] = BundleAdaptor return symbols, parse_context
def _globs(rel_path): pc = ParseContext(rel_path=rel_path, type_aliases={}) return Globs(pc)
def _generate_symbols( symbol_table: SymbolTable, aliases: BuildFileAliases, ) -> Tuple[Dict, ParseContext]: symbols: Dict = {} # Compute "per path" symbols. For performance, we use the same ParseContext, which we # mutate (in a critical section) to set the rel_path appropriately before it's actually used. # This allows this method to reuse the same symbols for all parses. Meanwhile we set the # rel_path to None, so that we get a loud error if anything tries to use it before it's set. # TODO: See https://github.com/pantsbuild/pants/issues/3561 parse_context = ParseContext(rel_path=None, type_aliases=symbols) class Registrar(BuildFileTargetFactory): def __init__(self, parse_context, type_alias, object_type): self._parse_context = parse_context self._type_alias = type_alias self._object_type = object_type self._serializable = Serializable.is_serializable_type( self._object_type) @memoized_property def target_types(self): return [self._object_type] def __call__(self, *args, **kwargs): # Target names default to the name of the directory their BUILD file is in # (as long as it's not the root directory). if "name" not in kwargs and issubclass(self._object_type, TargetAdaptor): dirname = os.path.basename(self._parse_context.rel_path) if dirname: kwargs["name"] = dirname else: raise UnaddressableObjectError( "Targets in root-level BUILD files must be named explicitly." ) name = kwargs.get("name") if name and self._serializable: kwargs.setdefault("type_alias", self._type_alias) obj = self._object_type(**kwargs) self._parse_context._storage.add(obj) return obj else: return self._object_type(*args, **kwargs) for alias, symbol in symbol_table.table.items(): registrar = Registrar(parse_context, alias, symbol) symbols[alias] = registrar symbols[symbol] = registrar if aliases.objects: symbols.update(aliases.objects) for alias, object_factory in aliases.context_aware_object_factories.items( ): symbols[alias] = object_factory(parse_context) for alias, target_macro_factory in aliases.target_macro_factories.items( ): underlying_symbol = symbols.get(alias, TargetAdaptor) symbols[alias] = target_macro_factory.target_macro(parse_context) for target_type in target_macro_factory.target_types: symbols[target_type] = Registrar(parse_context, alias, underlying_symbol) symbols["bundle"] = BundleAdaptor return symbols, parse_context