diff --git a/tools/ctexplain/bazel_api.py b/tools/ctexplain/bazel_api.py index e19203169f3ae6..d9ccec454ac52e 100644 --- a/tools/ctexplain/bazel_api.py +++ b/tools/ctexplain/bazel_api.py @@ -43,7 +43,7 @@ def run_bazel_in_client(args: List[str]) -> Tuple[int, List[str], List[str]]: Tuple of (return code, stdout, stderr) """ result = subprocess.run( - ["blaze"] + args, + ["bazel"] + args, cwd=os.getcwd(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -145,20 +145,27 @@ def _parse_cquery_result_line(line: str) -> ConfiguredTarget: Returns: Corresponding ConfiguredTarget if the line matches else None. """ - tokens = line.split(maxsplit=2) - label = tokens[0] - if tokens[1][0] != "(" or tokens[1][-1] != ")": - raise ValueError(f"{tokens[1]} in {line} not surrounded by parentheses") - config_hash = tokens[1][1:-1] + config_hash_start = line.find("(") + config_hash_end = line.find(")", config_hash_start) + fragments_start = line.find("[", config_hash_end) + fragments_end = line.find("]", fragments_start) + + label = line[0:config_hash_start].strip() + if config_hash_start == -1 or config_hash_end < config_hash_start: + raise ValueError(f'"{line}": no "()" suffix') + config_hash = line[config_hash_start + 1:config_hash_end] if config_hash == "null": fragments = () else: - if tokens[2][0] != "[" or tokens[2][-1] != "]": - raise ValueError(f"{tokens[2]} in {line} not surrounded by [] brackets") + if fragments_start == -1 or fragments_end < fragments_start: + raise ValueError(f'"{line}": no "[, ...]" suffix') # The fragments list looks like '[Fragment1, Fragment2, ...]'. Split the # whole line on ' [' to get just this list, then remove the final ']', then # split again on ', ' to convert it to a structured tuple. - fragments = tuple(line.split(" [")[1][0:-1].split(", ")) + if fragments_start == fragments_end - 1: + fragments = tuple() + else: + fragments = tuple(line[fragments_start + 1:fragments_end].split(", ")) return ConfiguredTarget( label=label, config=None, # Not yet available: we'll need `bazel config` to get this. diff --git a/tools/ctexplain/bazel_api_test.py b/tools/ctexplain/bazel_api_test.py index e3d404c4f329eb..af9c91ca4b6576 100644 --- a/tools/ctexplain/bazel_api_test.py +++ b/tools/ctexplain/bazel_api_test.py @@ -45,7 +45,7 @@ def testBasicCquery(self): self.assertEqual(len(cts), 1) self.assertEqual(cts[0].label, '//testapp:fg') self.assertIsNone(cts[0].config) - self.assertGreater(len(cts[0].config_hash), 10) + self.assertGreater(len(cts[0].config_hash), 6) self.assertIn('PlatformConfiguration', cts[0].transitive_fragments) def testFailedCquery(self): @@ -144,7 +144,7 @@ def testConfigWithStarlarkFlags(self): 'string_flag(name = "my_flag", build_setting_default = "nada")', 'filegroup(name = "fg", srcs = ["a.file"])', ]) - cquery_args = ['//testapp:fg', '--//testapp:my_flag', 'algo'] + cquery_args = ['//testapp:fg', '--//testapp:my_flag=algo'] cts = self._bazel_api.cquery(cquery_args)[2] config = self._bazel_api.get_config(cts[0].config_hash) user_defined_options = config.options['user-defined'] @@ -152,6 +152,30 @@ def testConfigWithStarlarkFlags(self): self.assertDictEqual(user_defined_options._dict, {'//testapp:my_flag': 'algo'}) + def testLabelsWithSpaces(self): + self.ScratchFile('testapp/BUILD', [ + 'filegroup(name = "labels can have spaces", srcs = ["a.file"])', + ]) + cquery_args = ['//testapp:labels can have spaces'] + res = self._bazel_api.cquery(['//testapp:all']) + success = res[0] + cts = res[2] + self.assertTrue(success) + self.assertEqual(len(cts), 1) + self.assertEqual(cts[0].label, '//testapp:labels can have spaces') + + def testEmptyFragmentRequirements(self): + self.ScratchFile('testapp/BUILD', [ + 'alias(name = "alias_to_src", actual = "source.file")', + ]) + cquery_args = ['//testapp:alias_to_src'] + res = self._bazel_api.cquery(['//testapp:all']) + success = res[0] + cts = res[2] + self.assertTrue(success) + self.assertEqual(len(cts), 1) + self.assertEqual(cts[0].label, '//testapp:alias_to_src') + self.assertEqual(len(cts[0].transitive_fragments), 0) if __name__ == '__main__': unittest.main()