def test_longest_dir_prefix(self) -> None: # Find the longest prefix (standard case). prefixes = ["hello", "hello_world", "hello/world", "helloworld"] assert longest_dir_prefix("hello/world/pants", prefixes) == "hello/world" assert longest_dir_prefix("hello/", prefixes) == "hello" assert longest_dir_prefix("hello", prefixes) == "hello" assert longest_dir_prefix("scoobydoobydoo", prefixes) is None
def test_longest_dir_prefix(self) -> None: # Find the longest prefix (standard case). prefixes = ["hello", "hello_world", "hello/world", "helloworld"] self.assertEqual(longest_dir_prefix("hello/world/pants", prefixes), "hello/world") self.assertEqual(longest_dir_prefix("hello/", prefixes), "hello") self.assertEqual(longest_dir_prefix("hello", prefixes), "hello") self.assertEqual(longest_dir_prefix("scoobydoobydoo", prefixes), None)
def test_longest_dir_prefix_special(self) -> None: # Ensure that something that is a longest prefix, but not a longest dir # prefix, is not tagged. prefixes = ["helloworldhowareyou", "helloworld"] self.assertEqual( longest_dir_prefix("helloworldhowareyoufine/", prefixes), None) self.assertEqual( longest_dir_prefix("helloworldhowareyoufine", prefixes), None)
def test_longest_dir_prefix_special(self): # Ensure that something that is a longest prefix, but not a longest dir # prefix, is not tagged. prefixes = ['helloworldhowareyou', 'helloworld'] self.assertEqual( longest_dir_prefix('helloworldhowareyoufine/', prefixes), None) self.assertEqual( longest_dir_prefix('helloworldhowareyoufine', prefixes), None)
def test_longest_dir_prefix(self): # Find the longest prefix (standard case). prefixes = ['hello', 'hello_world', 'hello/world', 'helloworld'] self.assertEqual(longest_dir_prefix('hello/world/pants', prefixes), 'hello/world') self.assertEqual(longest_dir_prefix('hello/', prefixes), 'hello') self.assertEqual(longest_dir_prefix('hello', prefixes), 'hello') self.assertEqual(longest_dir_prefix('scoobydoobydoo', prefixes), None)
def test_longest_dir_prefix_special(self): # Ensure that something that is a longest prefix, but not a longest dir # prefix, is not tagged. prefixes = ['helloworldhowareyou', 'helloworld'] self.assertEquals(longest_dir_prefix('helloworldhowareyoufine/', prefixes), None) self.assertEquals(longest_dir_prefix('helloworldhowareyoufine', prefixes), None)
def test_longest_dir_prefix(self): # Find the longest prefix (standard case). prefixes = ['hello', 'hello_world', 'hello/world', 'helloworld'] self.assertEquals(longest_dir_prefix('hello/world/pants', prefixes), 'hello/world') self.assertEquals(longest_dir_prefix('hello/', prefixes), 'hello') self.assertEquals(longest_dir_prefix('hello', prefixes), 'hello') self.assertEquals(longest_dir_prefix('scoobydoobydoo', prefixes), None)
def determine_subproject_spec(self, spec, relative_to): subproject_prefix = longest_dir_prefix(relative_to, self.subproject_roots) if subproject_prefix: spec = join_specs(subproject_prefix, spec) logger.debug('Determined that spec {} relative to {} belongs to ' 'subproject {}'.format(spec, relative_to, subproject_prefix)) return spec
def parse_spec( spec: str, relative_to: Optional[str] = None, subproject_roots: Optional[Sequence[str]] = None, ) -> Tuple[str, str]: """Parses a target address spec and returns the path from the root of the repo to this target and target name. :API: public :param spec: Target address spec. :param relative_to: path to use for sibling specs, ie: ':another_in_same_build_family', interprets the missing spec_path part as `relative_to`. :param subproject_roots: Paths that correspond with embedded build roots under the current build root. For example: some_target( name='mytarget', dependencies=['path/to/buildfile:targetname'], ) Where `path/to/buildfile:targetname` is the dependent target address spec. In case the target name is empty, it returns the last component of the path as target name, ie: spec_path, target_name = parse_spec('path/to/buildfile/foo') will return spec_path as 'path/to/buildfile/foo' and target_name as 'foo'. Optionally, specs can be prefixed with '//' to denote an absolute spec path. This is normally not significant except when a spec referring to a root level target is needed from deeper in the tree. For example, in `path/to/buildfile/BUILD`: some_target( name='mytarget', dependencies=[':targetname'], ) The `targetname` spec refers to a target defined in `path/to/buildfile/BUILD*`. If instead you want to reference `targetname` in a root level BUILD file, use the absolute form. For example: some_target( name='mytarget', dependencies=['//:targetname'], ) """ def normalize_absolute_refs(ref: str) -> str: return strip_prefix(ref, "//") subproject = (longest_dir_prefix(relative_to, subproject_roots) if relative_to and subproject_roots else None) def prefix_subproject(spec_path: str) -> str: if not subproject: return spec_path elif spec_path: return os.path.join(subproject, spec_path) else: return os.path.normpath(subproject) spec_parts = spec.rsplit(":", 1) if len(spec_parts) == 1: default_target_spec = spec_parts[0] spec_path = prefix_subproject( normalize_absolute_refs(default_target_spec)) target_name = os.path.basename(spec_path) else: spec_path, target_name = spec_parts if not spec_path and relative_to: spec_path = fast_relpath(relative_to, subproject) if subproject else relative_to spec_path = prefix_subproject(normalize_absolute_refs(spec_path)) return spec_path, target_name
def parse_spec(spec, relative_to=None, subproject_roots=None): """Parses a target address spec and returns the path from the root of the repo to this Target and Target name. :API: public :param string spec: Target address spec. :param string relative_to: path to use for sibling specs, ie: ':another_in_same_build_family', interprets the missing spec_path part as `relative_to`. :param list subproject_roots: Paths that correspond with embedded build roots under the current build root. For Example:: some_target(name='mytarget', dependencies=['path/to/buildfile:targetname'] ) Where ``path/to/buildfile:targetname`` is the dependent target address spec In case the target name is empty it returns the last component of the path as target name, ie:: spec_path, target_name = parse_spec('path/to/buildfile/foo') Will return spec_path as 'path/to/buildfile/foo' and target_name as 'foo'. Optionally, specs can be prefixed with '//' to denote an absolute spec path. This is normally not significant except when a spec referring to a root level target is needed from deeper in the tree. For example, in ``path/to/buildfile/BUILD``:: some_target(name='mytarget', dependencies=[':targetname'] ) The ``targetname`` spec refers to a target defined in ``path/to/buildfile/BUILD*``. If instead you want to reference ``targetname`` in a root level BUILD file, use the absolute form. For example:: some_target(name='mytarget', dependencies=['//:targetname'] ) """ def normalize_absolute_refs(ref): return strip_prefix(ref, '//') subproject = longest_dir_prefix(relative_to, subproject_roots) if subproject_roots else None def prefix_subproject(spec_path): if not subproject: return spec_path elif spec_path: return os.path.join(subproject, spec_path) else: return os.path.normpath(subproject) spec_parts = spec.rsplit(':', 1) if len(spec_parts) == 1: spec_path = prefix_subproject(normalize_absolute_refs(spec_parts[0])) target_name = os.path.basename(spec_path) else: spec_path, target_name = spec_parts if not spec_path and not subproject and relative_to: spec_path = relative_to spec_path = prefix_subproject(normalize_absolute_refs(spec_path)) return spec_path, target_name
def parse( cls, spec: str, relative_to: str | None = None, subproject_roots: Sequence[str] | None = None, ) -> AddressInput: """Parse a string into an AddressInput. :param spec: Target address spec. :param relative_to: path to use for sibling specs, ie: ':another_in_same_build_family', interprets the missing spec_path part as `relative_to`. :param subproject_roots: Paths that correspond with embedded build roots under the current build root. For example: some_target( name='mytarget', dependencies=['path/to/buildfile:targetname'], ) Where `path/to/buildfile:targetname` is the dependent target address spec. In there is no target name component, it defaults the default target in the resulting Address's spec_path. Optionally, specs can be prefixed with '//' to denote an absolute spec path. This is normally not significant except when a spec referring to a root level target is needed from deeper in the tree. For example, in `path/to/buildfile/BUILD`: some_target( name='mytarget', dependencies=[':targetname'], ) The `targetname` spec refers to a target defined in `path/to/buildfile/BUILD*`. If instead you want to reference `targetname` in a root level BUILD file, use the absolute form. For example: some_target( name='mytarget', dependencies=['//:targetname'], ) The spec may be for a generated target: `dir:generator#generated`. The spec may be a file, such as `a/b/c.txt`. It may include a relative address spec at the end, such as `a/b/c.txt:original` or `a/b/c.txt:../original`, to disambiguate which target the file comes from; otherwise, it will be assumed to come from the default target in the directory, i.e. a target which leaves off `name`. """ subproject = (longest_dir_prefix(relative_to, subproject_roots) if relative_to and subproject_roots else None) def prefix_subproject(spec_path: str) -> str: if not subproject: return spec_path if spec_path: return os.path.join(subproject, spec_path) return os.path.normpath(subproject) spec_parts = spec.split(":", maxsplit=1) path_component = spec_parts[0] if len(spec_parts) == 1: target_component = None generated_parts = path_component.split("#", maxsplit=1) if len(generated_parts) == 1: generated_component = None else: path_component, generated_component = generated_parts else: generated_parts = spec_parts[1].split("#", maxsplit=1) if len(generated_parts) == 1: target_component = generated_parts[0] generated_component = None else: target_component, generated_component = generated_parts normalized_relative_to = None if relative_to: normalized_relative_to = (fast_relpath(relative_to, subproject) if subproject else relative_to) if path_component.startswith("./") and normalized_relative_to: path_component = os.path.join(normalized_relative_to, path_component[2:]) if not path_component and normalized_relative_to: path_component = normalized_relative_to path_component = prefix_subproject(strip_prefix(path_component, "//")) return cls(path_component, target_component, generated_component)
def parse_spec(spec, relative_to=None, subproject_roots=None): """Parses a target address spec and returns the path from the root of the repo to this Target and Target name. :API: public :param string spec: Target address spec. :param string relative_to: path to use for sibling specs, ie: ':another_in_same_build_family', interprets the missing spec_path part as `relative_to`. :param list subproject_roots: Paths that correspond with embedded build roots under the current build root. For Example:: some_target(name='mytarget', dependencies=['path/to/buildfile:targetname'] ) Where ``path/to/buildfile:targetname`` is the dependent target address spec In case the target name is empty it returns the last component of the path as target name, ie:: spec_path, target_name = parse_spec('path/to/buildfile/foo') Will return spec_path as 'path/to/buildfile/foo' and target_name as 'foo'. Optionally, specs can be prefixed with '//' to denote an absolute spec path. This is normally not significant except when a spec referring to a root level target is needed from deeper in the tree. For example, in ``path/to/buildfile/BUILD``:: some_target(name='mytarget', dependencies=[':targetname'] ) The ``targetname`` spec refers to a target defined in ``path/to/buildfile/BUILD*``. If instead you want to reference ``targetname`` in a root level BUILD file, use the absolute form. For example:: some_target(name='mytarget', dependencies=['//:targetname'] ) """ def normalize_absolute_refs(ref): return strip_prefix(ref, '//') subproject = longest_dir_prefix( relative_to, subproject_roots) if subproject_roots else None def prefix_subproject(spec_path): if not subproject: return spec_path elif spec_path: return os.path.join(subproject, spec_path) else: return os.path.normpath(subproject) spec_parts = spec.rsplit(':', 1) if len(spec_parts) == 1: spec_path = prefix_subproject(normalize_absolute_refs(spec_parts[0])) target_name = os.path.basename(spec_path) else: spec_path, target_name = spec_parts if not spec_path and not subproject and relative_to: spec_path = relative_to spec_path = prefix_subproject(normalize_absolute_refs(spec_path)) return spec_path, target_name
def parse( cls, spec: str, *, relative_to: str | None = None, subproject_roots: Sequence[str] | None = None, description_of_origin: str, ) -> AddressInput: """Parse a string into an AddressInput. :param spec: Target address spec. :param relative_to: path to use for sibling specs, ie: ':another_in_same_build_family', interprets the missing spec_path part as `relative_to`. :param subproject_roots: Paths that correspond with embedded build roots under the current build root. :param description_of_origin: where the AddressInput comes from, e.g. "CLI arguments" or "the option `--paths-from`". This is used for better error messages. For example: some_target( name='mytarget', dependencies=['path/to/buildfile:targetname'], ) Where `path/to/buildfile:targetname` is the dependent target address spec. In there is no target name component, it defaults the default target in the resulting Address's spec_path. Optionally, specs can be prefixed with '//' to denote an absolute spec path. This is normally not significant except when a spec referring to a root level target is needed from deeper in the tree. For example, in `path/to/buildfile/BUILD`: some_target( name='mytarget', dependencies=[':targetname'], ) The `targetname` spec refers to a target defined in `path/to/buildfile/BUILD*`. If instead you want to reference `targetname` in a root level BUILD file, use the absolute form. For example: some_target( name='mytarget', dependencies=['//:targetname'], ) The spec may be for a generated target: `dir:generator#generated`. The spec may be a file, such as `a/b/c.txt`. It may include a relative address spec at the end, such as `a/b/c.txt:original` or `a/b/c.txt:../original`, to disambiguate which target the file comes from; otherwise, it will be assumed to come from the default target in the directory, i.e. a target which leaves off `name`. """ subproject = (longest_dir_prefix(relative_to, subproject_roots) if relative_to and subproject_roots else None) def prefix_subproject(spec_path: str) -> str: if not subproject: return spec_path if spec_path: return os.path.join(subproject, spec_path) return os.path.normpath(subproject) ( ( path_component, target_component, generated_component, parameters, ), wildcard, ) = native_engine.address_spec_parse(spec) if wildcard: raise UnsupportedWildcard( softwrap(f""" The address `{spec}` from {description_of_origin} ended in a wildcard (`{wildcard}`), which is not supported. """)) normalized_relative_to = None if relative_to: normalized_relative_to = (fast_relpath(relative_to, subproject) if subproject else relative_to) if path_component.startswith("./") and normalized_relative_to: path_component = os.path.join(normalized_relative_to, path_component[2:]) if not path_component and normalized_relative_to: path_component = normalized_relative_to path_component = prefix_subproject(strip_prefix(path_component, "//")) return cls( path_component, target_component, generated_component=generated_component, parameters=FrozenDict(sorted(parameters)), description_of_origin=description_of_origin, )