Skip to main content
Glama
build_target_pattern.bzl7.19 kB
# Copyright (c) Meta Platforms, Inc. and affiliates. # # This source code is licensed under both the MIT license found in the # LICENSE-MIT file in the root directory of this source tree and the Apache # License, Version 2.0 found in the LICENSE-APACHE file in the root directory # of this source tree. ROOT_SYMBOL = "//" _TARGET_SYMBOL = ":" _RECURSIVE_SYMBOL = "..." _PATH_SYMBOL = "/" # https://www.internalfb.com/intern/staticdocs/buck2/docs/concepts/build_target/ _NAME_REGEX_PATTERN = "[A-Za-z0-9_/.=,@~+-]+" _NAME_REGEX = regex(_NAME_REGEX_PATTERN) _BuildTargetPatternKind = enum( "single", "package", "recursive", ) BuildTargetPattern = record( kind = field(_BuildTargetPatternKind), cell = field([str, None], None), path = field(str), name = field([str, None], None), matches = field(typing.Callable), as_string = field(typing.Callable), # Exists purely for optimisation purposes. # Matching pattern inside a loop for many targets creates huge amount of # unnecessary string allocations that we can avoid _path_with_path_symbol = field(str), ) BuildTargetPatternParseResult = record( build_target_pattern = field([BuildTargetPattern, None], None), error = field([str, None], default = None), ) def try_parse_build_target_pattern(pattern: str) -> BuildTargetPatternParseResult: """ This function try to parse build target pattern. If parse fails, it will return the error message. """ if not (len(pattern) >= len(ROOT_SYMBOL) + 1): err_msg = "Invalid build target pattern, pattern too short: {}".format(pattern) return BuildTargetPatternParseResult(error = err_msg) root_position = pattern.find(ROOT_SYMBOL) if not (root_position >= 0): err_msg = "Invalid build target pattern, pattern should started with `{}` or a cell name followed by `{}`: ".format(ROOT_SYMBOL, ROOT_SYMBOL, pattern) return BuildTargetPatternParseResult(error = err_msg) cell = None if root_position > 0: cell = pattern[0:root_position] name = None if pattern.endswith(_TARGET_SYMBOL): kind = _BuildTargetPatternKind("package") end_of_path_position = len(pattern) - 1 elif pattern.endswith(_RECURSIVE_SYMBOL): kind = _BuildTargetPatternKind("recursive") end_of_path_position = len(pattern) - len(_RECURSIVE_SYMBOL) - 1 if not (pattern[end_of_path_position] == _PATH_SYMBOL): err_msg = "Invalid build target pattern, `{}` should be preceded by a `{}`: {}".format(_RECURSIVE_SYMBOL, _PATH_SYMBOL, pattern) return BuildTargetPatternParseResult(error = err_msg) else: kind = _BuildTargetPatternKind("single") end_of_path_position = pattern.rfind(_TARGET_SYMBOL) if (end_of_path_position < 0): # Pattern does not have a target delimiter and thus a target name # Assume target name to be the same as the last component of the package end_of_path_position = len(pattern) start_of_package = pattern.rfind(_PATH_SYMBOL) name = pattern[start_of_package + len(_PATH_SYMBOL):] elif end_of_path_position < root_position: err_msg = "Invalid build target pattern, cell name should not contain `{}`: {}".format(_PATH_SYMBOL, pattern) return BuildTargetPatternParseResult(error = err_msg) else: name = pattern[end_of_path_position + len(_TARGET_SYMBOL):] valid_name = _NAME_REGEX.match(name) if not valid_name: err_msg = "Invalid build target pattern, target name `{}` contains invalid characters. Valid characters are `{}`.".format(name, _NAME_REGEX_PATTERN) return BuildTargetPatternParseResult(error = err_msg) start_of_path_position = root_position + len(ROOT_SYMBOL) if not (pattern[start_of_path_position] != _PATH_SYMBOL): err_msg = "Invalid build target pattern, path cannot start with `{}`: {}".format(_PATH_SYMBOL, pattern) return BuildTargetPatternParseResult(error = err_msg) path = pattern[start_of_path_position:end_of_path_position] if not (path.find(ROOT_SYMBOL) < 0): err_msg = "Invalid build target pattern, `{}` can only appear once: {}".format(ROOT_SYMBOL, pattern) return BuildTargetPatternParseResult(error = err_msg) if not (path.find(_RECURSIVE_SYMBOL) < 0): err_msg = "Invalid build target pattern, `{}` can only appear once: {}".format(_RECURSIVE_SYMBOL, pattern) return BuildTargetPatternParseResult(error = err_msg) if not (path.find(_TARGET_SYMBOL) < 0): err_msg = "Invalid build target pattern, `{}` can only appear once: {}".format(_TARGET_SYMBOL, pattern) return BuildTargetPatternParseResult(error = err_msg) if not (len(path) == 0 or path[-1:] != _PATH_SYMBOL): err_msg = "Invalid build target pattern, path cannot end with `{}`: {}".format(_PATH_SYMBOL, pattern) return BuildTargetPatternParseResult(error = err_msg) # buildifier: disable=uninitialized - self is initialized def matches(label: [Label, TargetLabel]) -> bool: if self.cell and self.cell != label.cell: return False if self.kind == _BuildTargetPatternKind("single"): return self.path == label.package and self.name == label.name elif self.kind == _BuildTargetPatternKind("package"): return self.path == label.package elif self.kind == _BuildTargetPatternKind("recursive"): path_pattern_length = len(self.path) if path_pattern_length == 0: # This is a recursive pattern of the cell: cell//... return True elif len(label.package) > path_pattern_length: # pattern cell//package/... matches label cell//package/subpackage:target return label.package.startswith(self._path_with_path_symbol) else: return self.path == label.package else: fail("Unknown build target pattern kind.") # buildifier: disable=uninitialized - self is initialized def as_string() -> str: normalized_cell = self.cell if self.cell else "" if self.kind == _BuildTargetPatternKind("single"): return "{}//{}:{}".format(normalized_cell, self.path, self.name) elif self.kind == _BuildTargetPatternKind("package"): return "{}//{}:".format(normalized_cell, self.path) elif self.kind == _BuildTargetPatternKind("recursive"): return "{}//{}...".format(normalized_cell, self._path_with_path_symbol) else: fail("Unknown build target pattern kind.") self = BuildTargetPattern(kind = kind, cell = cell, path = path, name = name, matches = matches, as_string = as_string, _path_with_path_symbol = path + _PATH_SYMBOL if path else "") return BuildTargetPatternParseResult(build_target_pattern = self) def parse_build_target_pattern(pattern: str) -> BuildTargetPattern: parse_res = try_parse_build_target_pattern(pattern) if parse_res.error != None: fail(parse_res.error) return parse_res.build_target_pattern

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server