From d717eb0a70823453575db1f807dbeaa842c823a2 Mon Sep 17 00:00:00 2001 From: Laramie Leavitt Date: Thu, 1 Dec 2022 10:14:20 -0800 Subject: [PATCH] Revise tests for bazel_to_cmake Introduces golden_test.py which validates cmake file generation. Minor CMake format updates in a few cases. Adds example testdata for the following: * native_rules * bazel_skylib * grpc_generate_cc * local_mirror * third_party_http_archive * rules_nasm PiperOrigin-RevId: 492229343 Change-Id: Id8e47ea2b54427958639073acfc3e42ffabd9019 --- .bazelignore | 15 + .../bzl_library/bazel_skylib.py | 17 +- .../bzl_library/bazel_skylib_test.py | 87 --- .../bzl_library/local_mirror.py | 2 +- .../bazel_to_cmake/bzl_library/rules_nasm.py | 7 +- .../bzl_library/third_party_http_archive.py | 8 +- tools/cmake/bazel_to_cmake/evaluation.py | 5 +- tools/cmake/bazel_to_cmake/golden_test.py | 218 +++++++ tools/cmake/bazel_to_cmake/native_rules.py | 5 +- tools/cmake/bazel_to_cmake/native_rules_cc.py | 4 +- .../testdata/bazel_skylib/BUILD.bazel | 59 ++ .../testdata/bazel_skylib/WORKSPACE.bazel | 1 + .../testdata/bazel_skylib/config.json | 17 + .../golden/_cmake_binary_dir_/config.h | 1 + .../bazel_skylib/golden/build_rules.cmake | 20 + .../testdata/grpc_generate_cc/BUILD.bazel | 32 + .../testdata/grpc_generate_cc/WORKSPACE.bazel | 1 + .../testdata/grpc_generate_cc/a.cc | 1 + .../testdata/grpc_generate_cc/c.proto | 5 + .../testdata/grpc_generate_cc/config.json | 8 + .../grpc_generate_cc/golden/build_rules.cmake | 45 ++ .../testdata/local_mirror/BUILD.bazel | 10 + .../testdata/local_mirror/WORKSPACE.bazel | 49 ++ .../bazel_to_cmake/testdata/local_mirror/a.cc | 1 + .../testdata/local_mirror/config.json | 12 + .../local_mirror/lpm/BUILD.bazel | 20 + .../local_mirror/lpm/CMakeLists.txt | 14 + .../_cmake_binary_dir_/local_mirror/lpm/b.cc | 1 + .../_cmake_binary_dir_/local_mirror/lpm/b.h | 1 + .../local_mirror/golden/build_rules.cmake | 22 + .../testdata/native_rules/BUILD.bazel | 40 ++ .../testdata/native_rules/WORKSPACE.bazel | 1 + .../bazel_to_cmake/testdata/native_rules/a.cc | 1 + .../testdata/native_rules/c.proto | 5 + .../testdata/native_rules/config.json | 5 + .../native_rules/golden/build_rules.cmake | 76 +++ .../bazel_to_cmake/testdata/native_rules/x.h | 1 + .../bazel_to_cmake/testdata/native_rules/y.h | 1 + .../testdata/rules_nasm/BUILD.bazel | 31 + .../testdata/rules_nasm/WORKSPACE.bazel | 1 + .../bazel_to_cmake/testdata/rules_nasm/a.asm | 1 + .../bazel_to_cmake/testdata/rules_nasm/a.cc | 1 + .../bazel_to_cmake/testdata/rules_nasm/a.h | 1 + .../testdata/rules_nasm/config.json | 8 + .../rules_nasm/golden/build_rules.cmake | 24 + .../testdata/rules_nasm/include/b.inc | 1 + .../third_party_http_archive/BUILD.bazel | 9 + .../third_party_http_archive/WORKSPACE.bazel | 31 + .../testdata/third_party_http_archive/a.cc | 1 + .../third_party_http_archive/config.json | 11 + .../third_party/CMakeLists.txt | 3 + .../third_party/half-proxy-CMakeLists.txt | 36 ++ .../_find_pkg_redirects_/half-extra.cmake | 2 + .../golden/build_rules.cmake | 25 + tools/cmake/bazel_to_cmake/util.py | 7 + tools/cmake/bazel_to_cmake/workspace.py | 5 +- tools/cmake/bazel_to_cmake_test.py | 608 ------------------ 57 files changed, 908 insertions(+), 716 deletions(-) create mode 100644 .bazelignore delete mode 100644 tools/cmake/bazel_to_cmake/bzl_library/bazel_skylib_test.py create mode 100644 tools/cmake/bazel_to_cmake/golden_test.py create mode 100644 tools/cmake/bazel_to_cmake/testdata/bazel_skylib/BUILD.bazel create mode 100644 tools/cmake/bazel_to_cmake/testdata/bazel_skylib/WORKSPACE.bazel create mode 100644 tools/cmake/bazel_to_cmake/testdata/bazel_skylib/config.json create mode 100644 tools/cmake/bazel_to_cmake/testdata/bazel_skylib/golden/_cmake_binary_dir_/config.h create mode 100644 tools/cmake/bazel_to_cmake/testdata/bazel_skylib/golden/build_rules.cmake create mode 100644 tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/BUILD.bazel create mode 100644 tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/WORKSPACE.bazel create mode 100644 tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/a.cc create mode 100644 tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/c.proto create mode 100644 tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/config.json create mode 100644 tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/golden/build_rules.cmake create mode 100644 tools/cmake/bazel_to_cmake/testdata/local_mirror/BUILD.bazel create mode 100644 tools/cmake/bazel_to_cmake/testdata/local_mirror/WORKSPACE.bazel create mode 100644 tools/cmake/bazel_to_cmake/testdata/local_mirror/a.cc create mode 100644 tools/cmake/bazel_to_cmake/testdata/local_mirror/config.json create mode 100644 tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/BUILD.bazel create mode 100644 tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/CMakeLists.txt create mode 100644 tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/b.cc create mode 100644 tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/b.h create mode 100644 tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/build_rules.cmake create mode 100644 tools/cmake/bazel_to_cmake/testdata/native_rules/BUILD.bazel create mode 100644 tools/cmake/bazel_to_cmake/testdata/native_rules/WORKSPACE.bazel create mode 100644 tools/cmake/bazel_to_cmake/testdata/native_rules/a.cc create mode 100644 tools/cmake/bazel_to_cmake/testdata/native_rules/c.proto create mode 100644 tools/cmake/bazel_to_cmake/testdata/native_rules/config.json create mode 100644 tools/cmake/bazel_to_cmake/testdata/native_rules/golden/build_rules.cmake create mode 100644 tools/cmake/bazel_to_cmake/testdata/native_rules/x.h create mode 100644 tools/cmake/bazel_to_cmake/testdata/native_rules/y.h create mode 100644 tools/cmake/bazel_to_cmake/testdata/rules_nasm/BUILD.bazel create mode 100644 tools/cmake/bazel_to_cmake/testdata/rules_nasm/WORKSPACE.bazel create mode 100644 tools/cmake/bazel_to_cmake/testdata/rules_nasm/a.asm create mode 100644 tools/cmake/bazel_to_cmake/testdata/rules_nasm/a.cc create mode 100644 tools/cmake/bazel_to_cmake/testdata/rules_nasm/a.h create mode 100644 tools/cmake/bazel_to_cmake/testdata/rules_nasm/config.json create mode 100644 tools/cmake/bazel_to_cmake/testdata/rules_nasm/golden/build_rules.cmake create mode 100644 tools/cmake/bazel_to_cmake/testdata/rules_nasm/include/b.inc create mode 100644 tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/BUILD.bazel create mode 100644 tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/WORKSPACE.bazel create mode 100644 tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/a.cc create mode 100644 tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/config.json create mode 100644 tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/_cmake_binary_dir_/third_party/CMakeLists.txt create mode 100644 tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/_cmake_binary_dir_/third_party/half-proxy-CMakeLists.txt create mode 100644 tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/_find_pkg_redirects_/half-extra.cmake create mode 100644 tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/build_rules.cmake delete mode 100644 tools/cmake/bazel_to_cmake_test.py diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 000000000..9959e87c0 --- /dev/null +++ b/.bazelignore @@ -0,0 +1,15 @@ +# Ignore .git folders +.git + +# See: https://github.com/bazelbuild/bazel/issues/7093 +tools/cmake/bazel_to_cmake +tools/cmake/bazel_to_cmake/bzl_library +tools/cmake/bazel_to_cmake/starlark +tools/cmake/bazel_to_cmake/testdata +tools/cmake/bazel_to_cmake/testdata/native_rules +tools/cmake/bazel_to_cmake/testdata/rules_nasm +tools/cmake/bazel_to_cmake/testdata/bazel_skylib +tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc +tools/cmake/bazel_to_cmake/testdata/local_mirror +tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm +tools/cmake/bazel_to_cmake/testdata/third_party_http_archive diff --git a/tools/cmake/bazel_to_cmake/bzl_library/bazel_skylib.py b/tools/cmake/bazel_to_cmake/bzl_library/bazel_skylib.py index 0faeed217..1d13e5763 100644 --- a/tools/cmake/bazel_to_cmake/bzl_library/bazel_skylib.py +++ b/tools/cmake/bazel_to_cmake/bzl_library/bazel_skylib.py @@ -39,6 +39,7 @@ from ..starlark.provider import TargetInfo from ..starlark.select import Configurable from ..util import cmake_is_true +from ..util import cmake_is_windows from ..util import write_file_if_not_already_equal @@ -142,11 +143,12 @@ def _expand_template_impl( CMakeDepsProvider([cmake_target_pair.dep]), FilesProvider([out_file]))) - template_target = _context.resolve_target_or_label( + resolved_template = _context.resolve_target_or_label( cast(RelativeLabel, _context.evaluate_configurable(template))) deps: List[CMakeTarget] = [] - template_paths = state.get_file_paths(template_target, deps) + template_paths = state.get_file_paths(resolved_template, deps) + assert len(template_paths) == 1 template_path = template_paths[0] script_path = os.path.join(os.path.dirname(__file__), "expand_template.py") @@ -224,7 +226,7 @@ def _write_file_impl( _target: TargetId, _out_target: TargetId, content: Configurable[List[str]], - newline: str, + newline: Configurable[str], **kwargs, ): del kwargs @@ -232,8 +234,13 @@ def _write_file_impl( _context.add_analyzed_target(_out_target, TargetInfo(FilesProvider([out_file]))) _context.add_analyzed_target(_target, TargetInfo()) - if newline == "unix" or (newline == "auto" and _context.access( - EvaluationState).workspace.cmake_vars["CMAKE_SYSTEM_NAME"] != "Windows"): + + resolved_newline = _context.evaluate_configurable(newline) + + if resolved_newline == "unix" or ( + resolved_newline == "auto" and not cmake_is_windows( + _context.access( + EvaluationState).workspace.cmake_vars["CMAKE_SYSTEM_NAME"])): nl = "\n" else: nl = "\r\n" diff --git a/tools/cmake/bazel_to_cmake/bzl_library/bazel_skylib_test.py b/tools/cmake/bazel_to_cmake/bzl_library/bazel_skylib_test.py deleted file mode 100644 index e83606913..000000000 --- a/tools/cmake/bazel_to_cmake/bzl_library/bazel_skylib_test.py +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright 2022 The TensorStore Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from typing import Any, Dict, List, Optional, Type, TypeVar -import unittest - -from ..starlark.bazel_globals import BuildFileGlobals -from ..starlark.bazel_globals import get_bazel_library -from ..starlark.bazel_target import RepositoryId -from ..starlark.bazel_target import TargetId -from ..starlark.ignored import IgnoredLibrary -from ..starlark.invocation_context import InvocationContext -from ..starlark.invocation_context import RuleImpl -from ..starlark.provider import TargetInfo - -T = TypeVar("T") - -BUILD_BAZEL = """ - -load("@bazel_skylib//lib:selects.bzl", "selects") -load("@bazel_skylib//rules:expand_template.bzl", "expand_template") -load("@bazel_skylib//rules:copy_file.bzl", "copy_file") -load("@bazel_skylib//rules:write_file.bzl", "write_file") -load("@bazel_skylib//rules:common_settings.bzl", "bool_flag", "string_flag") - -""" - - -class MyRuleContext(InvocationContext): - - def __init__(self): - self.build_target = TargetId("@bar//:BUILD.bazel") - self.build_scope = BuildFileGlobals(self, self.build_target, "") - self.rules: Dict[RuleImpl] = {} - self.ignored = IgnoredLibrary() - - def access(self, provider_type: Type[T]) -> T: - return self.ignored - - @property - def caller_package_id(self): - return self.build_target.package_id - - def resolve_repo_mapping( - self, target: TargetId, - mapping_repository_id: Optional[RepositoryId]) -> TargetId: - return target - - def resolve_workspace_root(self, repository_id: RepositoryId) -> str: - return f"external/{repository_id.repository_name}" - - def load_library(self, target: TargetId) -> Dict[str, Any]: - library_type = get_bazel_library((target, False)) - if library_type is not None: - return library_type(self, target, "builtin") - return IgnoredLibrary() - - def add_rule(self, - rule_id: TargetId, - impl: RuleImpl, - outs: Optional[List[TargetId]] = None, - **kwargs) -> None: - self.rules[rule_id] = impl - - def add_analyzed_target(self, target_id: TargetId, info: TargetInfo) -> None: - pass - - -class BazelSkylibTest(unittest.TestCase): - - def test_rule(self): - self.maxDiff = None # pylint: disable=invalid-name - ctx = MyRuleContext() - - # Compile the BUILD file. - exec(compile(BUILD_BAZEL, "build", "exec"), ctx.build_scope) # pylint: disable=exec-used diff --git a/tools/cmake/bazel_to_cmake/bzl_library/local_mirror.py b/tools/cmake/bazel_to_cmake/bzl_library/local_mirror.py index 1b7262c23..52f8dbff0 100644 --- a/tools/cmake/bazel_to_cmake/bzl_library/local_mirror.py +++ b/tools/cmake/bazel_to_cmake/bzl_library/local_mirror.py @@ -95,7 +95,7 @@ def _local_mirror_impl(_context: InvocationContext, **kwargs): if not sha256: raise ValueError( f"local_mirror requires SHA256 for downloaded file: {file}") - out.write(f"\n EXPECTED_HASH SHA256={sha256})\n\n") + out.write(f"""\n EXPECTED_HASH "SHA256={sha256}")\n\n""") cmaketxt_path = pathlib.Path(os.path.join(local_mirror_dir, "CMakeLists.txt")) diff --git a/tools/cmake/bazel_to_cmake/bzl_library/rules_nasm.py b/tools/cmake/bazel_to_cmake/bzl_library/rules_nasm.py index f740a6ffb..8a3a7259f 100644 --- a/tools/cmake/bazel_to_cmake/bzl_library/rules_nasm.py +++ b/tools/cmake/bazel_to_cmake/bzl_library/rules_nasm.py @@ -154,13 +154,12 @@ def _emit_nasm_library( if use_builtin_rule: _builder.addtext( f"""target_sources({target_name} PRIVATE {quote_list(all_srcs + dummy_sources)}) - target_include_directories({target_name} PRIVATE {quote_list(sorted(includes))}) - set_source_files_properties( +target_include_directories({target_name} PRIVATE {quote_list(sorted(includes))}) +set_source_files_properties( {quote_list(all_srcs)} PROPERTIES LANGUAGE ASM_NASM - COMPILE_OPTIONS {quote_string(";".join(flags))}) - """) + COMPILE_OPTIONS {quote_string(";".join(flags))})\n""") if cmake_deps: _builder.addtext( f"add_dependencies({target_name} {quote_list(sorted(cmake_deps))})\n") diff --git a/tools/cmake/bazel_to_cmake/bzl_library/third_party_http_archive.py b/tools/cmake/bazel_to_cmake/bzl_library/third_party_http_archive.py index 7476e3d0b..04dfd1ad4 100644 --- a/tools/cmake/bazel_to_cmake/bzl_library/third_party_http_archive.py +++ b/tools/cmake/bazel_to_cmake/bzl_library/third_party_http_archive.py @@ -267,10 +267,10 @@ def _get_fetch_content_invocation( out = io.StringIO() out.write(f"FetchContent_Declare({cmake_name}") if urls: - out.write(f" URL {quote_string(urls[0])}") + out.write(f"\n URL {quote_string(urls[0])}") if sha256: hash_str = f"SHA256={sha256}" - out.write(f" URL_HASH {quote_string(hash_str)}") + out.write(f"\n URL_HASH {quote_string(hash_str)}") patch_commands = [] for patch in patches or (): @@ -306,8 +306,8 @@ def _get_fetch_content_invocation( f"""{quote_path(cmake_command)} -E copy {quote_path(new_cmakelists_path)} CMakeLists.txt""" ) patch_command = " && ".join(patch_commands) - out.write(f" PATCH_COMMAND {patch_command}") - out.write(" OVERRIDE_FIND_PACKAGE)\n") + out.write(f"\n PATCH_COMMAND {patch_command}") + out.write("\n OVERRIDE_FIND_PACKAGE)\n") return out.getvalue() diff --git a/tools/cmake/bazel_to_cmake/evaluation.py b/tools/cmake/bazel_to_cmake/evaluation.py index 30c6ec905..4060076bd 100644 --- a/tools/cmake/bazel_to_cmake/evaluation.py +++ b/tools/cmake/bazel_to_cmake/evaluation.py @@ -129,7 +129,10 @@ class Phase(enum.Enum): def _get_kind(currentframe) -> Optional[str]: if not currentframe: return None - return currentframe.f_back.f_code.co_name + kind = currentframe.f_back.f_back.f_code.co_name + if kind.startswith("bazel_"): + kind = kind[len("bazel_"):] + return kind class EvaluationState: diff --git a/tools/cmake/bazel_to_cmake/golden_test.py b/tools/cmake/bazel_to_cmake/golden_test.py new file mode 100644 index 000000000..7e2655194 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/golden_test.py @@ -0,0 +1,218 @@ +# Copyright 2022 The TensorStore Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# pylint: disable=relative-beyond-top-level,wildcard-import + +import json +import os +import pathlib +import shutil +import sys +import tempfile +from typing import Any, Dict, List +import unittest + +from .cmake_target import CMakeTarget +from .evaluation import EvaluationState +from .native_rules import * # pylint: disable=unused-import,wildcard-import +from .native_rules_cc import * # pylint: disable=unused-import,wildcard-import +from parameterized import parameterized +from .platforms import add_platform_constraints +from .starlark import rule # pylint: disable=unused-import +from .workspace import Repository +from .workspace import Workspace + +# NOTE: Consider adding failure tests as well as the success tests. + +# Set to 1 to update the golden files. +UPDATE_GOLDENS = (os.getenv('UPDATE_GOLDENS') == '1') + +CMAKE_VARS = { + 'CMAKE_CXX_COMPILER_ID': 'clang', + 'CMAKE_SYSTEM_NAME': 'Linux', + 'CMAKE_SYSTEM_PROCESSOR': 'AMD64', + 'CMAKE_COMMAND': 'cmake', + 'PROJECT_IS_TOP_LEVEL': 'YES', + 'CMAKE_FIND_PACKAGE_REDIRECTS_DIR': '_find_pkg_redirects_', +} + + +def testdata_parameters(): + """Returns config tuples for the 'testdata' subdir.""" + testdata = pathlib.Path(__file__).resolve().with_name('testdata') + result = [] + for x in testdata.iterdir(): + if '__' in str(x): + continue + try: + with (x / 'config.json').open('r') as f: + config: Dict[str, Any] = json.load(f) + except FileNotFoundError as e: + raise FileNotFoundError(f'Failed to read {str(x)}/config.json') from e + config['source_directory'] = str(x) + result.append((x.name, config)) + return result + + +def write_config_json(path: str, config: Dict[str, Any]): + """Writes config.json.""" + with pathlib.Path(os.path.join(path, 'config.json')).open('w') as f: + json.dump(config, f, sort_keys=True, indent=4) + + +def get_files_list(source_directory: str) -> List[pathlib.Path]: + """Returns non-golden files under source directory.""" + files = [] + try: + include_goldens = ('golden' in source_directory) + p = pathlib.Path(source_directory) + for x in sorted(p.glob('**/*')): + if not x.is_file(): + continue + if 'golden/' in str(x) and not include_goldens: + continue + files.append(x.relative_to(p)) + except FileNotFoundError as e: + print(f'Failure to read {source_directory}: {e}') + return files + + +def copy_tree(source_dir: str, source_files: List[str], dest_dir: str): + """Copies source_files from source_dir to dest_dir.""" + for x in source_files: + dest_path = os.path.join(dest_dir, x) + os.makedirs(os.path.dirname(dest_path), exist_ok=True) + + shutil.copy(os.path.join(source_dir, x), dest_path) + + +class GoldenTest(unittest.TestCase): + + def compare_files(self, golden, generated): + with pathlib.Path(golden).open('r') as left: + with pathlib.Path(generated).open('r') as right: + self.assertListEqual(list(left), list(right)) + + @parameterized.expand(testdata_parameters()) + def test_golden(self, test_name, config): + self.maxDiff = None # pylint: disable=invalid-name + + # Start with the list of source files. + source_directory = config['source_directory'] + del config['source_directory'] + input_files = [str(x) for x in get_files_list(source_directory)] + + # Create the working directory as a snapshot of the source directory. + with tempfile.TemporaryDirectory(prefix='src') as directory: + os.chdir(directory) + copy_tree(source_directory, input_files, directory) + os.makedirs(CMAKE_VARS['CMAKE_FIND_PACKAGE_REDIRECTS_DIR'], exist_ok=True) + + # Workspace setup + workspace = Workspace(CMAKE_VARS) + workspace.save_workspace = '_workspace.pickle' + add_platform_constraints(workspace) + + # Add default mappings used in proto code. + workspace.set_bazel_target_mapping( + '@com_github_grpc_grpc//:grpc++_codegen_proto', + CMakeTarget('gRPC::gRPC_codegen'), 'gRPC') + + workspace.set_bazel_target_mapping( + '@com_github_grpc_grpc//src/compiler:grpc_cpp_plugin', + CMakeTarget('gRPC::grpc_cpp_plugin'), 'gRPC') + + workspace.set_bazel_target_mapping( # + '@com_google_protobuf//:protobuf', + CMakeTarget('protobuf::libprotobuf'), 'protobuf') + + # Load specified modules. + for x in config.get('modules', []): + workspace.add_module(x) + workspace.load_modules() + + repository = Repository( + workspace=workspace, + source_directory=directory, + bazel_repo_name='bazel_test_repo', + cmake_project_name='CMakeProject', + cmake_binary_dir='_cmake_binary_dir_', + top_level=True, + ) + + # Setup root workspace. + workspace.bazel_to_cmake_deps[ + repository.repository_id] = repository.cmake_project_name + workspace.exclude_repo_targets(repository.repository_id) + + # Setup repo mapping. + for x in config.get('repo_mapping', []): + repository.repo_mapping[x[0]] = x[1] + + # Evaluate the WORKSPACE and BUILD files + state = EvaluationState(repository) + state.process_workspace() + state.process_build_file(os.path.join(directory, 'BUILD.bazel')) + + # Analyze + if config.get('targets') is None: + targets_to_analyze = state.targets_to_analyze + else: + targets_to_analyze = sorted([ + repository.repository_id.parse_target(t) + for t in config.get('targets') + ]) + state.analyze(targets_to_analyze) + + # Write generated file + pathlib.Path('build_rules.cmake').write_text(state.builder.as_text()) + + # Collect the output files, excluding the input files, + # and normalize the contents. + excludes = config.get('excludes', []) + files = [] + for x in get_files_list('.'): + if str(x) in input_files: + continue + if str(x) in excludes: + continue + txt = x.read_text() + txt = txt.replace(directory, '${TEST_DIRECTORY}') + txt = txt.replace(os.path.dirname(__file__), '${SCRIPT_DIRECTORY}') + txt = txt.replace(sys.argv[0], 'bazel_to_cmake.py') + x.write_text(txt) + files.append(str(x)) + + golden_directory = os.path.join(source_directory, 'golden') + if UPDATE_GOLDENS: + print(f'Updating goldens for {test_name}') + try: + shutil.rmtree(golden_directory) + except FileNotFoundError: + pass + config['files'] = [] + for x in files: + dest_path = os.path.join(golden_directory, x) + os.makedirs(os.path.dirname(dest_path), exist_ok=True) + shutil.copyfile(x, dest_path) + config['files'].append(x) + write_config_json(source_directory, config) + + # Assert files exist. + golden_files = get_files_list(golden_directory) + self.assertGreater(len(golden_files), 0) + for x in golden_files: + self.compare_files( + os.path.join(golden_directory, str(x)), + os.path.join(directory, str(x))) diff --git a/tools/cmake/bazel_to_cmake/native_rules.py b/tools/cmake/bazel_to_cmake/native_rules.py index 1c00beaae..f27f1b723 100644 --- a/tools/cmake/bazel_to_cmake/native_rules.py +++ b/tools/cmake/bazel_to_cmake/native_rules.py @@ -245,7 +245,7 @@ def _emit_genrule( message: Optional[str] = None, ): if message: - optional_message_text = f"COMMENT {cmake_builder.quote_string(message)}" + optional_message_text = f"COMMENT {cmake_builder.quote_string(message)}\n " else: optional_message_text = "" builder.addtext(f""" @@ -253,8 +253,7 @@ def _emit_genrule( OUTPUT {cmake_builder.quote_list(out_files)} DEPENDS {cmake_builder.quote_list(cast(List[str], cmake_deps))} COMMAND {cmd_text} - {optional_message_text} - VERBATIM + {optional_message_text}VERBATIM WORKING_DIRECTORY "${{CMAKE_CURRENT_SOURCE_DIR}}" ) add_custom_target({cmake_target} DEPENDS {cmake_builder.quote_list(out_files)}) diff --git a/tools/cmake/bazel_to_cmake/native_rules_cc.py b/tools/cmake/bazel_to_cmake/native_rules_cc.py index 090d8c1a1..8380cd3b9 100644 --- a/tools/cmake/bazel_to_cmake/native_rules_cc.py +++ b/tools/cmake/bazel_to_cmake/native_rules_cc.py @@ -411,8 +411,8 @@ def _get_cc_proto_library_target(context: InvocationContext, if len(cc_deps) == 1: return cc_deps[0] - cc_library_target = proto_target.get_target_id(proto_target.target_name + - "__cc_proto_library") + cc_library_target = proto_target.get_target_id( + f"{proto_target.target_name}__cc_proto_library") state = context.access(EvaluationState) cmake_target_pair = state.generate_cmake_target_pair( diff --git a/tools/cmake/bazel_to_cmake/testdata/bazel_skylib/BUILD.bazel b/tools/cmake/bazel_to_cmake/testdata/bazel_skylib/BUILD.bazel new file mode 100644 index 000000000..dec887f9f --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/bazel_skylib/BUILD.bazel @@ -0,0 +1,59 @@ +load("@bazel_skylib//lib:selects.bzl", "selects") +load("@bazel_skylib//rules:expand_template.bzl", "expand_template") +load("@bazel_skylib//rules:copy_file.bzl", "copy_file") +load("@bazel_skylib//rules:write_file.bzl", "write_file") + +package(default_visibility = ["//visibility:public"]) + +# NOTE: The rule labels, such as ":config_h" should work as well as +# the rule outputs, I think. + +write_file( + name = "config_h", + out = "config.h", + content = ["// config.h"], + newline = select({ + ":any_os": "unix", + "//conditions:default": "unix", + }), +) + +expand_template( + name = "config2_h", + out = "config2.h", + substitutions = {}, + template = "config.h", +) + +copy_file( + name = "config_copy_rule", + src = "config2.h", + out = "config3.h", + allow_symlink = True, +) + +# Config settings + +config_setting( + name = "linux", + constraint_values = ["@platforms//os:linux"], +) + +config_setting( + name = "macos", + constraint_values = ["@platforms//os:macos"], +) + +config_setting( + name = "windows", + constraint_values = ["@platforms//os:windows"], +) + +selects.config_setting_group( + name = "any_os", + match_any = [ + ":windows", + ":macos", + ":linux", + ], +) diff --git a/tools/cmake/bazel_to_cmake/testdata/bazel_skylib/WORKSPACE.bazel b/tools/cmake/bazel_to_cmake/testdata/bazel_skylib/WORKSPACE.bazel new file mode 100644 index 000000000..ca87af9aa --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/bazel_skylib/WORKSPACE.bazel @@ -0,0 +1 @@ +workspace(name = "bazel_test_repo") diff --git a/tools/cmake/bazel_to_cmake/testdata/bazel_skylib/config.json b/tools/cmake/bazel_to_cmake/testdata/bazel_skylib/config.json new file mode 100644 index 000000000..7d3d58e21 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/bazel_skylib/config.json @@ -0,0 +1,17 @@ +{ + "excludes": [ + "_cmake_binary_dir_/CMakeProject_config2_h.subs.json" + ], + "files": [ + "_cmake_binary_dir_/config.h", + "build_rules.cmake" + ], + "modules": [ + ".bzl_library.bazel_skylib" + ], + "targets": [ + "//:config3.h", + "//:config2_h", + "//:config_h" + ] +} \ No newline at end of file diff --git a/tools/cmake/bazel_to_cmake/testdata/bazel_skylib/golden/_cmake_binary_dir_/config.h b/tools/cmake/bazel_to_cmake/testdata/bazel_skylib/golden/_cmake_binary_dir_/config.h new file mode 100644 index 000000000..05aa07580 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/bazel_skylib/golden/_cmake_binary_dir_/config.h @@ -0,0 +1 @@ +// config.h diff --git a/tools/cmake/bazel_to_cmake/testdata/bazel_skylib/golden/build_rules.cmake b/tools/cmake/bazel_to_cmake/testdata/bazel_skylib/golden/build_rules.cmake new file mode 100644 index 000000000..ab771fde1 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/bazel_skylib/golden/build_rules.cmake @@ -0,0 +1,20 @@ + +add_custom_command( +OUTPUT "_cmake_binary_dir_/config2.h" +COMMAND ${Python3_EXECUTABLE} "${SCRIPT_DIRECTORY}/bzl_library/expand_template.py" + "_cmake_binary_dir_/config.h" + "_cmake_binary_dir_/CMakeProject_config2_h.subs.json" + "_cmake_binary_dir_/config2.h" +DEPENDS "_cmake_binary_dir_/config.h" "${SCRIPT_DIRECTORY}/bzl_library/expand_template.py" "_cmake_binary_dir_/CMakeProject_config2_h.subs.json" +VERBATIM +) +add_custom_target(CMakeProject_config2_h DEPENDS "_cmake_binary_dir_/config2.h") + +add_custom_command( + OUTPUT "_cmake_binary_dir_/config3.h" + DEPENDS "CMakeProject_config2_h" "_cmake_binary_dir_/config2.h" + COMMAND "cmake" -E copy _cmake_binary_dir_/config2.h _cmake_binary_dir_/config3.h + VERBATIM + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" +) +add_custom_target(CMakeProject_config_copy_rule DEPENDS "_cmake_binary_dir_/config3.h") diff --git a/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/BUILD.bazel b/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/BUILD.bazel new file mode 100644 index 000000000..d6f7bb373 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/BUILD.bazel @@ -0,0 +1,32 @@ +load("@com_github_grpc_grpc//bazel:generate_cc.bzl", "generate_cc") + +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "a", + srcs = ["a.cc"], + deps = [":cc_grpc"], +) + +proto_library( + name = "c_proto", + srcs = ["c.proto"], + cc_api_version = 2, +) + +# Mimic cc_grpc_library + +generate_cc( + name = "cc__grpc_codegen", + srcs = [":c_proto"], + flags = ["services_namespace=grpc_gen"], + plugin = "@com_github_grpc_grpc//src/compiler:grpc_cpp_plugin", + visibility = ["//visibility:private"], +) + +cc_library( + name = "cc_grpc", + srcs = [":cc__grpc_codegen"], + hdrs = [":cc__grpc_codegen"], + deps = ["@com_github_grpc_grpc//:grpc++_codegen_proto"], +) diff --git a/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/WORKSPACE.bazel b/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/WORKSPACE.bazel new file mode 100644 index 000000000..ca87af9aa --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/WORKSPACE.bazel @@ -0,0 +1 @@ +workspace(name = "bazel_test_repo") diff --git a/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/a.cc b/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/a.cc new file mode 100644 index 000000000..c61a7250e --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/a.cc @@ -0,0 +1 @@ +#include "c.pb.h" diff --git a/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/c.proto b/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/c.proto new file mode 100644 index 000000000..efde33863 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/c.proto @@ -0,0 +1,5 @@ +syntax = "proto2"; + +package grpc_generate_cc; + +option java_multiple_files = true; diff --git a/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/config.json b/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/config.json new file mode 100644 index 000000000..723441f98 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/config.json @@ -0,0 +1,8 @@ +{ + "files": [ + "build_rules.cmake" + ], + "modules": [ + ".bzl_library.grpc_generate_cc" + ] +} \ No newline at end of file diff --git a/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/golden/build_rules.cmake b/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/golden/build_rules.cmake new file mode 100644 index 000000000..f65a45344 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/grpc_generate_cc/golden/build_rules.cmake @@ -0,0 +1,45 @@ +find_package(gRPC REQUIRED) + +add_custom_command( + OUTPUT "_cmake_binary_dir_/c.grpc.pb.h" "_cmake_binary_dir_/c.grpc.pb.cc" + COMMAND protobuf::protoc + ARGS --experimental_allow_proto3_optional + -I "${PROJECT_SOURCE_DIR}" + --plugin=protoc-gen-grpc="$" + --grpc_out=services_namespace=grpc_gen:${PROJECT_BINARY_DIR} + "${TEST_DIRECTORY}/c.proto" + DEPENDS "${TEST_DIRECTORY}/c.proto" "gRPC::grpc_cpp_plugin" "protobuf::protoc" + COMMENT "Running protoc (grpc) on c.proto" + VERBATIM) +add_custom_target(CMakeProject_cc__grpc_codegen DEPENDS "_cmake_binary_dir_/c.grpc.pb.h" "_cmake_binary_dir_/c.grpc.pb.cc") + + +add_library(CMakeProject_cc_grpc) +target_sources(CMakeProject_cc_grpc PRIVATE + "_cmake_binary_dir_/c.grpc.pb.cc") +set_property(TARGET CMakeProject_cc_grpc PROPERTY LINKER_LANGUAGE "CXX") +target_link_libraries(CMakeProject_cc_grpc PUBLIC + "Threads::Threads" + "gRPC::gRPC_codegen" + "m") +target_include_directories(CMakeProject_cc_grpc PUBLIC + "$" + "$") +target_compile_features(CMakeProject_cc_grpc PUBLIC cxx_std_17) +add_dependencies(CMakeProject_cc_grpc "CMakeProject_cc__grpc_codegen") +add_library(CMakeProject::cc_grpc ALIAS CMakeProject_cc_grpc) + + +add_library(CMakeProject_a) +target_sources(CMakeProject_a PRIVATE + "${TEST_DIRECTORY}/a.cc") +set_property(TARGET CMakeProject_a PROPERTY LINKER_LANGUAGE "CXX") +target_link_libraries(CMakeProject_a PUBLIC + "CMakeProject::cc_grpc" + "Threads::Threads" + "m") +target_include_directories(CMakeProject_a PUBLIC + "$" + "$") +target_compile_features(CMakeProject_a PUBLIC cxx_std_17) +add_library(CMakeProject::a ALIAS CMakeProject_a) diff --git a/tools/cmake/bazel_to_cmake/testdata/local_mirror/BUILD.bazel b/tools/cmake/bazel_to_cmake/testdata/local_mirror/BUILD.bazel new file mode 100644 index 000000000..a3cf6c303 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/local_mirror/BUILD.bazel @@ -0,0 +1,10 @@ +package(default_visibility = ["//visibility:public"]) + +cc_library( + name = "a", + srcs = ["a.cc"], + deps = [ + "@local_proto_mirror//:b", + "@local_proto_mirror//:validate_cc", + ], +) diff --git a/tools/cmake/bazel_to_cmake/testdata/local_mirror/WORKSPACE.bazel b/tools/cmake/bazel_to_cmake/testdata/local_mirror/WORKSPACE.bazel new file mode 100644 index 000000000..4d6f1c69e --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/local_mirror/WORKSPACE.bazel @@ -0,0 +1,49 @@ +workspace(name = "bazel_test_repo") + +load("@com_google_tensorstore//bazel:local_mirror.bzl", "local_mirror") + +_BUILD = ''' +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +proto_library( + name = "validate_proto", + srcs = ["validate.proto"], +) + +cc_proto_library( + name = "validate_cc", + deps = [":validate_proto"], +) + +cc_library( + name = "b", + srcs = ["b.cc"], + hdrs = ["b.h"], +) +''' + +local_mirror( + name = "local_proto_mirror", + + bazel_to_cmake = {}, + cmake_name = "lpm", + cmake_target_mapping = { + "@local_proto_mirror//:validate_cc": "lpm::validate_cc", + }, + files = [ "b.h", "b.cc", "validate.proto", "BUILD.bazel" ], + file_url = { + "validate.proto": [ + "https://raw.githubusercontent.com/bufbuild/protoc-gen-validate/2682ad06cca00550030e177834f58a2bc06eb61e/validate/validate.proto", + ], + }, + file_sha256 = { + "validate.proto": "bf7ca2ac45a75b8b9ff12f38efd7f48ee460ede1a7919d60c93fad3a64fc2eee", + }, + file_content = { + "b.h": "// b", + "b.cc": "// b.cc", + "BUILD.bazel": _BUILD, + } +) diff --git a/tools/cmake/bazel_to_cmake/testdata/local_mirror/a.cc b/tools/cmake/bazel_to_cmake/testdata/local_mirror/a.cc new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/local_mirror/a.cc @@ -0,0 +1 @@ + diff --git a/tools/cmake/bazel_to_cmake/testdata/local_mirror/config.json b/tools/cmake/bazel_to_cmake/testdata/local_mirror/config.json new file mode 100644 index 000000000..e08e2171d --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/local_mirror/config.json @@ -0,0 +1,12 @@ +{ + "files": [ + "_cmake_binary_dir_/local_mirror/lpm/BUILD.bazel", + "_cmake_binary_dir_/local_mirror/lpm/CMakeLists.txt", + "_cmake_binary_dir_/local_mirror/lpm/b.cc", + "_cmake_binary_dir_/local_mirror/lpm/b.h", + "build_rules.cmake" + ], + "modules": [ + ".bzl_library.local_mirror" + ] +} \ No newline at end of file diff --git a/tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/BUILD.bazel b/tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/BUILD.bazel new file mode 100644 index 000000000..fc84dcaaf --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/BUILD.bazel @@ -0,0 +1,20 @@ + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +proto_library( + name = "validate_proto", + srcs = ["validate.proto"], +) + +cc_proto_library( + name = "validate_cc", + deps = [":validate_proto"], +) + +cc_library( + name = "b", + srcs = ["b.cc"], + hdrs = ["b.h"], +) diff --git a/tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/CMakeLists.txt b/tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/CMakeLists.txt new file mode 100644 index 000000000..f34340546 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/CMakeLists.txt @@ -0,0 +1,14 @@ +set(CMAKE_MESSAGE_INDENT "[lpm] ") + +project("lpm") +execute_process( + COMMAND ${Python3_EXECUTABLE} "${TEST_DIRECTORY}/bazel_to_cmake.py" + --load-workspace "_workspace.pickle" + --cmake-project-name lpm + --cmake-binary-dir "${CMAKE_CURRENT_BINARY_DIR}" + --bazel-repo-name local_proto_mirror + --build-rules-output "${CMAKE_CURRENT_BINARY_DIR}/build_rules.cmake" + --target-alias "@local_proto_mirror//:validate_cc" "lpm::validate_cc" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMAND_ERROR_IS_FATAL ANY) +include("${CMAKE_CURRENT_BINARY_DIR}/build_rules.cmake") diff --git a/tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/b.cc b/tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/b.cc new file mode 100644 index 000000000..78215e7bb --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/b.cc @@ -0,0 +1 @@ +// b.cc \ No newline at end of file diff --git a/tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/b.h b/tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/b.h new file mode 100644 index 000000000..ec3277866 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/_cmake_binary_dir_/local_mirror/lpm/b.h @@ -0,0 +1 @@ +// b \ No newline at end of file diff --git a/tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/build_rules.cmake b/tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/build_rules.cmake new file mode 100644 index 000000000..7fb45196a --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/local_mirror/golden/build_rules.cmake @@ -0,0 +1,22 @@ +# Loading local_proto_mirror +file(DOWNLOAD "https://raw.githubusercontent.com/bufbuild/protoc-gen-validate/2682ad06cca00550030e177834f58a2bc06eb61e/validate/validate.proto" "_cmake_binary_dir_/local_mirror/lpm/validate.proto" + EXPECTED_HASH "SHA256=bf7ca2ac45a75b8b9ff12f38efd7f48ee460ede1a7919d60c93fad3a64fc2eee") + +add_subdirectory("_cmake_binary_dir_/local_mirror/lpm" _local_mirror_configs EXCLUDE_FROM_ALL) +find_package(lpm REQUIRED) + + +add_library(CMakeProject_a) +target_sources(CMakeProject_a PRIVATE + "${TEST_DIRECTORY}/a.cc") +set_property(TARGET CMakeProject_a PROPERTY LINKER_LANGUAGE "CXX") +target_link_libraries(CMakeProject_a PUBLIC + "Threads::Threads" + "lpm::b" + "lpm::validate_cc" + "m") +target_include_directories(CMakeProject_a PUBLIC + "$" + "$") +target_compile_features(CMakeProject_a PUBLIC cxx_std_17) +add_library(CMakeProject::a ALIAS CMakeProject_a) diff --git a/tools/cmake/bazel_to_cmake/testdata/native_rules/BUILD.bazel b/tools/cmake/bazel_to_cmake/testdata/native_rules/BUILD.bazel new file mode 100644 index 000000000..a166088f8 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/native_rules/BUILD.bazel @@ -0,0 +1,40 @@ +# NOTE: ":h_file" should work as well as "s.h", right? +cc_library( + name = "a", + srcs = ["a.cc"], + hdrs = ["a.h"], + visibility = ["//visibility:public"], +) + +genrule( + name = "h_file", + srcs = select({ + "//conditions:default": ["x.h"], + "//:other": ["y.h"], + }), + outs = ["a.h"], + cmd = "cp $< $@", + visibility = ["//visibility:public"], +) + +config_setting( + name = "other", + values = {"cpu": "darwin_arm64"}, +) + +proto_library( + name = "c_proto", + srcs = ["c.proto"], + cc_api_version = 2, +) + +cc_proto_library( + name = "c_proto_cc", + deps = [":c_proto"], +) + +cc_test( + name = "a_test", + srcs = ["a.cc"], + deps = [":c_proto_cc"], +) diff --git a/tools/cmake/bazel_to_cmake/testdata/native_rules/WORKSPACE.bazel b/tools/cmake/bazel_to_cmake/testdata/native_rules/WORKSPACE.bazel new file mode 100644 index 000000000..ca87af9aa --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/native_rules/WORKSPACE.bazel @@ -0,0 +1 @@ +workspace(name = "bazel_test_repo") diff --git a/tools/cmake/bazel_to_cmake/testdata/native_rules/a.cc b/tools/cmake/bazel_to_cmake/testdata/native_rules/a.cc new file mode 100644 index 000000000..2243de1ba --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/native_rules/a.cc @@ -0,0 +1 @@ +#include "a.h" diff --git a/tools/cmake/bazel_to_cmake/testdata/native_rules/c.proto b/tools/cmake/bazel_to_cmake/testdata/native_rules/c.proto new file mode 100644 index 000000000..efde33863 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/native_rules/c.proto @@ -0,0 +1,5 @@ +syntax = "proto2"; + +package grpc_generate_cc; + +option java_multiple_files = true; diff --git a/tools/cmake/bazel_to_cmake/testdata/native_rules/config.json b/tools/cmake/bazel_to_cmake/testdata/native_rules/config.json new file mode 100644 index 000000000..7f53f5d9b --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/native_rules/config.json @@ -0,0 +1,5 @@ +{ + "files": [ + "build_rules.cmake" + ] +} \ No newline at end of file diff --git a/tools/cmake/bazel_to_cmake/testdata/native_rules/golden/build_rules.cmake b/tools/cmake/bazel_to_cmake/testdata/native_rules/golden/build_rules.cmake new file mode 100644 index 000000000..c7236ac40 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/native_rules/golden/build_rules.cmake @@ -0,0 +1,76 @@ +find_package(protobuf REQUIRED) + +add_custom_command( + OUTPUT "_cmake_binary_dir_/a.h" + DEPENDS "${TEST_DIRECTORY}/x.h" + COMMAND cp x.h _cmake_binary_dir_/a.h + VERBATIM + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" +) +add_custom_target(CMakeProject_h_file DEPENDS "_cmake_binary_dir_/a.h") + + +add_library(CMakeProject_a) +target_sources(CMakeProject_a PRIVATE + "${TEST_DIRECTORY}/a.cc") +set_property(TARGET CMakeProject_a PROPERTY LINKER_LANGUAGE "CXX") +target_link_libraries(CMakeProject_a PUBLIC + "Threads::Threads" + "m") +target_include_directories(CMakeProject_a PUBLIC + "$" + "$") +target_compile_features(CMakeProject_a PUBLIC cxx_std_17) +add_dependencies(CMakeProject_a "CMakeProject_h_file") +add_library(CMakeProject::a ALIAS CMakeProject_a) + +add_custom_command( + OUTPUT "_cmake_binary_dir_/c.pb.h" "_cmake_binary_dir_/c.pb.cc" + COMMAND protobuf::protoc + ARGS --experimental_allow_proto3_optional + -I "${PROJECT_SOURCE_DIR}" + --cpp_out=${PROJECT_BINARY_DIR} + "${TEST_DIRECTORY}/c.proto" + DEPENDS "${TEST_DIRECTORY}/c.proto" "protobuf::protoc" + COMMENT "Running protoc (cpp) on c.proto" + VERBATIM) +add_custom_target(CMakeProject_c.proto__cc_protoc DEPENDS "_cmake_binary_dir_/c.pb.h" "_cmake_binary_dir_/c.pb.cc") + + +add_library(CMakeProject_c.proto__cc_proto) +target_sources(CMakeProject_c.proto__cc_proto PRIVATE + "_cmake_binary_dir_/c.pb.cc") +set_property(TARGET CMakeProject_c.proto__cc_proto PROPERTY LINKER_LANGUAGE "CXX") +target_link_libraries(CMakeProject_c.proto__cc_proto PUBLIC + "protobuf::libprotobuf") +target_include_directories(CMakeProject_c.proto__cc_proto PUBLIC + "$" + "$") +target_compile_features(CMakeProject_c.proto__cc_proto PUBLIC cxx_std_17) +add_library(CMakeProject::c.proto__cc_proto ALIAS CMakeProject_c.proto__cc_proto) +add_dependencies(CMakeProject_c.proto__cc_proto CMakeProject_c.proto__cc_protoc) + + +add_library(CMakeProject_c_proto_cc INTERFACE) +target_link_libraries(CMakeProject_c_proto_cc INTERFACE + "CMakeProject::c.proto__cc_proto") +target_include_directories(CMakeProject_c_proto_cc INTERFACE + "$" + "$") +target_compile_features(CMakeProject_c_proto_cc INTERFACE cxx_std_17) +add_library(CMakeProject::c_proto_cc ALIAS CMakeProject_c_proto_cc) + + +add_executable(CMakeProject_a_test "") +add_executable(CMakeProject::a_test ALIAS CMakeProject_a_test) +target_sources(CMakeProject_a_test PRIVATE + "${TEST_DIRECTORY}/a.cc") +target_link_libraries(CMakeProject_a_test PUBLIC + "CMakeProject::c_proto_cc" + "Threads::Threads" + "m") +target_include_directories(CMakeProject_a_test PUBLIC + "$" + "$") +target_compile_features(CMakeProject_a_test PUBLIC cxx_std_17) +add_test(NAME CMakeProject_a_test COMMAND CMakeProject_a_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/tools/cmake/bazel_to_cmake/testdata/native_rules/x.h b/tools/cmake/bazel_to_cmake/testdata/native_rules/x.h new file mode 100644 index 000000000..656425ac4 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/native_rules/x.h @@ -0,0 +1 @@ +// x.h diff --git a/tools/cmake/bazel_to_cmake/testdata/native_rules/y.h b/tools/cmake/bazel_to_cmake/testdata/native_rules/y.h new file mode 100644 index 000000000..a45a2a790 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/native_rules/y.h @@ -0,0 +1 @@ +// y.h diff --git a/tools/cmake/bazel_to_cmake/testdata/rules_nasm/BUILD.bazel b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/BUILD.bazel new file mode 100644 index 000000000..88e14ccb0 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/BUILD.bazel @@ -0,0 +1,31 @@ +load("@com_google_tensorstore//bazel:rules_nasm.bzl", "nasm_library") + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +NASM_FLAGS = [ + "-w+all", + "-D__x86_64__", + "-felf64", + "-DELF", + "-DPIC", +] + +nasm_library( + name = "asm_library", + srcs = [ + "a.asm", + ], + flags = NASM_FLAGS, + includes = [ + "include/b.inc", + ], +) + +cc_library( + name = "a", + srcs = ["a.cc"], + hdrs = ["a.h"], + deps = [":asm_library"], +) diff --git a/tools/cmake/bazel_to_cmake/testdata/rules_nasm/WORKSPACE.bazel b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/WORKSPACE.bazel new file mode 100644 index 000000000..ca87af9aa --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/WORKSPACE.bazel @@ -0,0 +1 @@ +workspace(name = "bazel_test_repo") diff --git a/tools/cmake/bazel_to_cmake/testdata/rules_nasm/a.asm b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/a.asm new file mode 100644 index 000000000..adbd3f706 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/a.asm @@ -0,0 +1 @@ +# a.asm \ No newline at end of file diff --git a/tools/cmake/bazel_to_cmake/testdata/rules_nasm/a.cc b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/a.cc new file mode 100644 index 000000000..2243de1ba --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/a.cc @@ -0,0 +1 @@ +#include "a.h" diff --git a/tools/cmake/bazel_to_cmake/testdata/rules_nasm/a.h b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/a.h new file mode 100644 index 000000000..17ab46c84 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/a.h @@ -0,0 +1 @@ +// a.h diff --git a/tools/cmake/bazel_to_cmake/testdata/rules_nasm/config.json b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/config.json new file mode 100644 index 000000000..32f261410 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/config.json @@ -0,0 +1,8 @@ +{ + "files": [ + "build_rules.cmake" + ], + "modules": [ + ".bzl_library.rules_nasm" + ] +} \ No newline at end of file diff --git a/tools/cmake/bazel_to_cmake/testdata/rules_nasm/golden/build_rules.cmake b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/golden/build_rules.cmake new file mode 100644 index 000000000..41a273181 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/golden/build_rules.cmake @@ -0,0 +1,24 @@ +add_library(CMakeProject_asm_library) +target_sources(CMakeProject_asm_library PRIVATE "${TEST_DIRECTORY}/a.asm") +target_include_directories(CMakeProject_asm_library PRIVATE "${TEST_DIRECTORY}" "${TEST_DIRECTORY}/include") +set_source_files_properties( + "${TEST_DIRECTORY}/a.asm" + PROPERTIES + LANGUAGE ASM_NASM + COMPILE_OPTIONS "-w+all;-D__x86_64__;-felf64;-DELF;-DPIC") +add_library(CMakeProject::asm_library ALIAS CMakeProject_asm_library) + + +add_library(CMakeProject_a) +target_sources(CMakeProject_a PRIVATE + "${TEST_DIRECTORY}/a.cc") +set_property(TARGET CMakeProject_a PROPERTY LINKER_LANGUAGE "CXX") +target_link_libraries(CMakeProject_a PUBLIC + "CMakeProject::asm_library" + "Threads::Threads" + "m") +target_include_directories(CMakeProject_a PUBLIC + "$" + "$") +target_compile_features(CMakeProject_a PUBLIC cxx_std_17) +add_library(CMakeProject::a ALIAS CMakeProject_a) diff --git a/tools/cmake/bazel_to_cmake/testdata/rules_nasm/include/b.inc b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/include/b.inc new file mode 100644 index 000000000..fef1c7246 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/rules_nasm/include/b.inc @@ -0,0 +1 @@ +#b.inc \ No newline at end of file diff --git a/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/BUILD.bazel b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/BUILD.bazel new file mode 100644 index 000000000..939773b07 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/BUILD.bazel @@ -0,0 +1,9 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +cc_library( + name = "a", + srcs = ["a.cc"], + deps = ["@net_sourceforge_half//:half"], +) diff --git a/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/WORKSPACE.bazel b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/WORKSPACE.bazel new file mode 100644 index 000000000..050be3581 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/WORKSPACE.bazel @@ -0,0 +1,31 @@ +workspace(name = "bazel_test_repo") + +load("@com_google_tensorstore//third_party:repo.bzl", "third_party_http_archive") + +_BUILD = """ +licenses(["notice"]) + +exports_files(["LICENSE.txt"]) + +cc_library( + name = "half", + hdrs = ["include/half.hpp"], + strip_include_prefix = "include", + visibility = ["//visibility:public"], +) +""" + +third_party_http_archive( + name = "net_sourceforge_half", + urls = [ + "https://storage.googleapis.com/tensorstore-bazel-mirror/sourceforge.net/projects/half/files/half/2.1.0/half-2.1.0.zip", + "https://sourceforge.net/projects/half/files/half/2.1.0/half-2.1.0.zip", + ], + sha256 = "ad1788afe0300fa2b02b0d1df128d857f021f92ccf7c8bddd07812685fa07a25", + build_file_content = _BUILD, + cmake_name = "half", + cmake_target_mapping = { + "@net_sourceforge_half//:half": "half::half", + }, + bazel_to_cmake = {}, +) diff --git a/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/a.cc b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/a.cc new file mode 100644 index 000000000..8de632f30 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/a.cc @@ -0,0 +1 @@ +// a.cc diff --git a/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/config.json b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/config.json new file mode 100644 index 000000000..844e5527a --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/config.json @@ -0,0 +1,11 @@ +{ + "files": [ + "_cmake_binary_dir_/third_party/CMakeLists.txt", + "_cmake_binary_dir_/third_party/half-proxy-CMakeLists.txt", + "_find_pkg_redirects_/half-extra.cmake", + "build_rules.cmake" + ], + "modules": [ + ".bzl_library.third_party_http_archive" + ] +} \ No newline at end of file diff --git a/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/_cmake_binary_dir_/third_party/CMakeLists.txt b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/_cmake_binary_dir_/third_party/CMakeLists.txt new file mode 100644 index 000000000..698e45dea --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/_cmake_binary_dir_/third_party/CMakeLists.txt @@ -0,0 +1,3 @@ + +include(FetchContent) +FetchContent_MakeAvailable("half") diff --git a/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/_cmake_binary_dir_/third_party/half-proxy-CMakeLists.txt b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/_cmake_binary_dir_/third_party/half-proxy-CMakeLists.txt new file mode 100644 index 000000000..346f93ad1 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/_cmake_binary_dir_/third_party/half-proxy-CMakeLists.txt @@ -0,0 +1,36 @@ +set(CMAKE_MESSAGE_INDENT "[half] ") + +get_property(_prop DIRECTORY "${TEST_DIRECTORY}" PROPERTY COMPILE_DEFINITIONS) +set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS "${_prop}") +unset(_prop) + +get_property(_prop DIRECTORY "${TEST_DIRECTORY}" PROPERTY COMPILE_OPTIONS) +set_property(DIRECTORY PROPERTY COMPILE_OPTIONS "${_prop}") +unset(_prop) + +get_property(_prop DIRECTORY "${TEST_DIRECTORY}" PROPERTY INCLUDE_DIRECTORIES) +set_property(DIRECTORY PROPERTY INCLUDE_DIRECTORIES "${_prop}") +unset(_prop) + +get_property(_prop DIRECTORY "${TEST_DIRECTORY}" PROPERTY LINK_DIRECTORIES) +set_property(DIRECTORY PROPERTY LINK_DIRECTORIES "${_prop}") +unset(_prop) + +get_property(_prop DIRECTORY "${TEST_DIRECTORY}" PROPERTY LINK_OPTIONS) +set_property(DIRECTORY PROPERTY LINK_OPTIONS "${_prop}") +unset(_prop) +unset(half_BINARY_DIR) +unset(half_SOURCE_DIR) + +project("half") +execute_process( + COMMAND ${Python3_EXECUTABLE} "${TEST_DIRECTORY}/bazel_to_cmake.py" + --load-workspace "_workspace.pickle" + --cmake-project-name half + --cmake-binary-dir "${CMAKE_CURRENT_BINARY_DIR}" + --bazel-repo-name net_sourceforge_half + --build-rules-output "${CMAKE_CURRENT_BINARY_DIR}/build_rules.cmake" + --target-alias "@net_sourceforge_half//:half" "half::half" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMAND_ERROR_IS_FATAL ANY) +include("${CMAKE_CURRENT_BINARY_DIR}/build_rules.cmake") diff --git a/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/_find_pkg_redirects_/half-extra.cmake b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/_find_pkg_redirects_/half-extra.cmake new file mode 100644 index 000000000..aeee77892 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/_find_pkg_redirects_/half-extra.cmake @@ -0,0 +1,2 @@ + +set(HALF_FOUND ON) diff --git a/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/build_rules.cmake b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/build_rules.cmake new file mode 100644 index 000000000..1ad7f4f20 --- /dev/null +++ b/tools/cmake/bazel_to_cmake/testdata/third_party_http_archive/golden/build_rules.cmake @@ -0,0 +1,25 @@ +include(FetchContent) +option(TENSORSTORE_USE_SYSTEM_HALF "Use an installed version of half") +# Loading net_sourceforge_half +FetchContent_Declare(half + URL "https://storage.googleapis.com/tensorstore-bazel-mirror/sourceforge.net/projects/half/files/half/2.1.0/half-2.1.0.zip" + URL_HASH "SHA256=ad1788afe0300fa2b02b0d1df128d857f021f92ccf7c8bddd07812685fa07a25" + PATCH_COMMAND "cmake" -E copy "_cmake_binary_dir_/third_party/half-proxy-CMakeLists.txt" CMakeLists.txt + OVERRIDE_FIND_PACKAGE) +add_subdirectory("_cmake_binary_dir_/third_party" _third_party_configs EXCLUDE_FROM_ALL) +find_package(half REQUIRED) + + +add_library(CMakeProject_a) +target_sources(CMakeProject_a PRIVATE + "${TEST_DIRECTORY}/a.cc") +set_property(TARGET CMakeProject_a PROPERTY LINKER_LANGUAGE "CXX") +target_link_libraries(CMakeProject_a PUBLIC + "Threads::Threads" + "half::half" + "m") +target_include_directories(CMakeProject_a PUBLIC + "$" + "$") +target_compile_features(CMakeProject_a PUBLIC cxx_std_17) +add_library(CMakeProject::a ALIAS CMakeProject_a) diff --git a/tools/cmake/bazel_to_cmake/util.py b/tools/cmake/bazel_to_cmake/util.py index 36b14add4..78ee3b76a 100644 --- a/tools/cmake/bazel_to_cmake/util.py +++ b/tools/cmake/bazel_to_cmake/util.py @@ -65,6 +65,13 @@ def cmake_is_true(value: Optional[str]) -> bool: return not _CMAKE_FALSE_PATTERN.fullmatch(value) +def cmake_is_windows(value: Optional[str]) -> bool: + """Determines if a string is considered by CMake to be TRUE.""" + if value is None: + return False + return value.startswith("Windows") + + def _get_build_patterns(package_patterns: List[str]): patterns = [] for package_pattern in package_patterns: diff --git a/tools/cmake/bazel_to_cmake/workspace.py b/tools/cmake/bazel_to_cmake/workspace.py index 3c3307008..bdfd71330 100644 --- a/tools/cmake/bazel_to_cmake/workspace.py +++ b/tools/cmake/bazel_to_cmake/workspace.py @@ -170,7 +170,10 @@ def add_module(self, module_name: str): def load_modules(self): """Load modules added by add_module.""" for module_name in self._modules: - importlib.import_module(module_name) + if module_name.startswith("."): + importlib.import_module(module_name, package=__package__) + else: + importlib.import_module(module_name) class Repository: diff --git a/tools/cmake/bazel_to_cmake_test.py b/tools/cmake/bazel_to_cmake_test.py deleted file mode 100644 index 4e2639dcb..000000000 --- a/tools/cmake/bazel_to_cmake_test.py +++ /dev/null @@ -1,608 +0,0 @@ -# Copyright 2022 The TensorStore Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Main entry point for bazel_to_cmake.""" - -# pylint: disable=relative-beyond-top-level - -import os -import pathlib -import sys -import tempfile -import unittest - -from bazel_to_cmake import native_rules # pylint: disable=unused-import -from bazel_to_cmake import native_rules_cc # pylint: disable=unused-import -from bazel_to_cmake.bzl_library import default as _ # pylint: disable=unused-import -from bazel_to_cmake.evaluation import EvaluationState -from bazel_to_cmake.platforms import add_platform_constraints -from bazel_to_cmake.starlark import rule # pylint: disable=unused-import -from bazel_to_cmake.workspace import Repository -from bazel_to_cmake.workspace import Workspace - -PROJECT_SOURCE_DIR = '{PROJECT_SOURCE_DIR}' -PROJECT_BINARY_DIR = '{PROJECT_BINARY_DIR}' - -CMAKE_VARS = { - 'CMAKE_CXX_COMPILER_ID': 'clang', - 'CMAKE_SYSTEM_NAME': 'Linux', - 'CMAKE_SYSTEM_PROCESSOR': 'AMD64', - 'CMAKE_COMMAND': 'cmake', - 'PROJECT_IS_TOP_LEVEL': 'YES', -} - - -class CMakeHelper: - - def __init__(self, directory, cmake_vars): - self.directory = directory - self.workspace = Workspace(cmake_vars) - workspace = self.workspace - workspace.save_workspace = '_workspace.pickle' - add_platform_constraints(workspace) - workspace.add_module('bazel_to_cmake.native_rules') - workspace.add_module('bazel_to_cmake.native_rules_cc') - workspace.add_module('bazel_to_cmake.bzl_library.third_party_http_archive') - workspace.add_module('bazel_to_cmake.bzl_library.local_mirror') - workspace.add_module('bazel_to_cmake.bzl_library.grpc_generate_cc') - - workspace.set_bazel_target_mapping( - '@com_github_grpc_grpc//:grpc++_codegen_proto', 'gRPC::gRPC_codegen', - 'gRPC') - workspace.set_bazel_target_mapping( - '@com_github_grpc_grpc//src/compiler:grpc_cpp_plugin', - 'gRPC::grpc_cpp_plugin', 'gRPC') - workspace.set_bazel_target_mapping('@com_google_protobuf//:protobuf', - 'protobuf::libprotobuf', 'protobuf') - workspace.load_modules() - - self.repository = Repository( - workspace=self.workspace, - source_directory=directory, - bazel_repo_name='com_google_tensorstore', - cmake_project_name='CMakeProject', - cmake_binary_dir='_cmake_binary_dir_', - top_level=True, - ) - repo = self.repository - - # Setup root workspace. - workspace.bazel_to_cmake_deps[repo.repository_id] = repo.cmake_project_name - workspace.exclude_repo_targets(repo.repository_id) - - # Setup repo mapping. - repo.repo_mapping['upb'] = 'com_google_upb' - - self.state = EvaluationState(repo) - self.builder = self.state.builder - - def get_text(self): - return self.builder.as_text() - - def analyze(self): - self.state.analyze(self.state.targets_to_analyze) - - -class CMakeBuilder(unittest.TestCase): - - def test_basic(self): - self.maxDiff = None # pylint: disable=invalid-name - # bazel_to_cmake checks file existence before returning a - with tempfile.TemporaryDirectory(prefix='src') as directory: - os.chdir(directory) - with open('a.h', 'wb') as f: - f.write(bytes('// a.h\n', 'utf-8')) - with open('a.cc', 'wb') as f: - f.write(bytes('// a.cc\n', 'utf-8')) - with open('a_test.cc', 'wb') as f: - f.write(bytes('// a_test.cc\n', 'utf-8')) - with open('c.proto', 'wb') as f: - f.write(bytes('// c.proto\n', 'utf-8')) - - cmake = CMakeHelper(directory, CMAKE_VARS) - - # Load the WORKSPACE - cmake.state.process_workspace_content( - os.path.join(directory, 'WORKSPACE'), """ -workspace(name = "com_google_tensorstore") -""") - - # Load the BUILD file - cmake.state.process_build_content( - os.path.join(directory, 'BUILD.bazel'), """ - -package(default_visibility = ["//visibility:public"]) - -cc_library( - name = "a", - srcs = ["a.cc"], - hdrs = ["a.h"], - deps = [ ":b" , ":cc_proto" ], - local_defines = [ "FOO" ], -) - -cc_library( - name = "b", - srcs = ["a.cc"], - hdrs = ["a.h"], - alwayslink = 1, -) - -proto_library( - name = "c_proto", - cc_api_version = 2, - srcs = [ "c.proto" ], -) - -cc_proto_library( - name = "cc_proto", - deps = [ ":c_proto" ], -) - -cc_test( - name = "a_test", - srcs = ["a_test.cc"], - deps = [ ":a" ], - visibility = [ "//visibility:private" ], -) - -""") - - cmake.analyze() - cmake_text = cmake.get_text() - - # There's output for A - self.assertIn( - f""" -add_library(CMakeProject_a) -target_sources(CMakeProject_a PRIVATE - "{directory}/a.cc") -set_property(TARGET CMakeProject_a PROPERTY LINKER_LANGUAGE "CXX") -target_compile_definitions(CMakeProject_a PRIVATE "FOO") -target_link_libraries(CMakeProject_a PUBLIC - "CMakeProject::b" - "CMakeProject::cc_proto" - "Threads::Threads" - "m") -target_include_directories(CMakeProject_a PUBLIC - "$" - "$") -target_compile_features(CMakeProject_a PUBLIC cxx_std_17) -add_library(CMakeProject::a ALIAS CMakeProject_a) -""", cmake_text) - - # There's protoc output - self.assertIn( - f""" -add_custom_command( - OUTPUT "_cmake_binary_dir_/c.pb.h" "_cmake_binary_dir_/c.pb.cc" - COMMAND protobuf::protoc - ARGS --experimental_allow_proto3_optional - -I "${PROJECT_SOURCE_DIR}" - --cpp_out=${PROJECT_BINARY_DIR} - "{directory}/c.proto" - DEPENDS "{directory}/c.proto" "protobuf::protoc" - COMMENT "Running protoc (cpp) on c.proto" - VERBATIM) -add_custom_target(CMakeProject_c.proto__cc_protoc DEPENDS "_cmake_binary_dir_/c.pb.h" "_cmake_binary_dir_/c.pb.cc") -""", cmake_text) - - # The test, even though private, is built because the repo is top-level. - self.assertIn('add_executable(CMakeProject_a_test', cmake_text) - - def test_starlark_primitives(self): - self.maxDiff = None # pylint: disable=invalid-name - # bazel_to_cmake checks file existence before returning a - with tempfile.TemporaryDirectory(prefix='src') as directory: - os.chdir(directory) - os.makedirs('bazel') - with open('bazel/a.bzl', 'wb') as f: - f.write( - bytes(""" -load(":b.bzl", "bfn") - -def afn(x): - return bfn(x) - -""", 'utf-8')) - with open('bazel/b.bzl', 'wb') as f: - f.write( - bytes( - """ -def bfn(x): - return 'b_'+x - -def test_fn(x): - print(x) - -# Tests -T=len([1,2,3]) -T=all([True, False]) -T=any([True, False]) -T=sorted([2,1, 3]) -T=reversed([1,2,3]) -T=dict([('one', 2), (3, 4)]) -T=range(0, 5, 10) -T=list(range(5)) -T=min(5, -2, 1, 7, 3) -T=max(5, -2, 1, 7, 3) -T=enumerate([False, True, None], 42) -T=zip([1, 2, 3]) -T=dir({}) -T=hasattr("", "find") -T=struct(x=1, y='a') -T=repr(T) -native.repository_name() -native.package_name() -print(Label("@foo//bar:baz")) - -""", 'utf-8')) - with open('b_r.h', 'wb') as f: - f.write(bytes('// a.h\n', 'utf-8')) - - with open('b_r.cc', 'wb') as f: - f.write(bytes('// a.cc\n', 'utf-8')) - - cmake = CMakeHelper(directory, CMAKE_VARS) - - # Load the WORKSPACE - cmake.state.process_workspace_content( - os.path.join(directory, 'WORKSPACE'), """ -workspace(name = "com_google_tensorstore") -""") - - # Load the BUILD file - cmake.state.process_build_content( - os.path.join(directory, 'BUILD.bazel'), """ - -package(default_visibility = ["//visibility:public"]) - -load("//bazel:a.bzl", "afn") - -alias( - name = "h_file", - actual = select({ - "//conditions:default": afn("r.h"), - "//:cpu_darwin_arm64": afn("r.h"), - }) -) - -cc_library( - name = "a", - hdrs = [ ":h_file" ], - srcs = select({ - "//conditions:default" : [ afn("r.cc") ], - "//:cpu_darwin_arm64" : [ afn("r.cc") ], - }) -) - -config_setting( - name = "cpu_darwin_arm64", - values = {"cpu": "darwin_arm64"}, -) - -""") - - cmake.analyze() - cmake_text = cmake.get_text() - - # There's output for A - self.assertIn( - f""" -add_library(CMakeProject_a) -target_sources(CMakeProject_a PRIVATE - "{directory}/b_r.cc") -set_property(TARGET CMakeProject_a PROPERTY LINKER_LANGUAGE "CXX") -target_link_libraries(CMakeProject_a PUBLIC - "Threads::Threads" - "m") -target_include_directories(CMakeProject_a PUBLIC - "$" - "$") -target_compile_features(CMakeProject_a PUBLIC cxx_std_17) -add_library(CMakeProject::a ALIAS CMakeProject_a) -""", cmake_text) - - def test_third_party_http_library(self): - # bazel_to_cmake checks file existence before returning a - with tempfile.TemporaryDirectory(prefix='src') as directory: - os.chdir(directory) - with open('a.h', 'wb') as f: - f.write(bytes('// a.h\n', 'utf-8')) - with open('a.cc', 'wb') as f: - f.write(bytes('// a.cc\n', 'utf-8')) - - cmake_vars = CMAKE_VARS.copy() - cmake_vars.update({ - 'CMAKE_FIND_PACKAGE_REDIRECTS_DIR': - os.path.join(directory, '_find_package'), - }) - os.makedirs(cmake_vars['CMAKE_FIND_PACKAGE_REDIRECTS_DIR'], exist_ok=True) - - cmake = CMakeHelper(directory, cmake_vars) - - # Load the WORKSPACE - cmake.state.process_workspace_content( - os.path.join(directory, 'WORKSPACE'), ''' -workspace(name = "com_google_tensorstore") - -load("//third_party:repo.bzl", "third_party_http_archive") - -third_party_http_archive( - name = "net_sourceforge_half", - urls = [ - "https://sourceforge.net/projects/half/files/half/2.1.0/half-2.1.0.zip", - ], - sha256 = "ad1788afe0300fa2b02b0d1df128d857f021f92ccf7c8bddd07812685fa07a25", - build_file_content = """ -licenses(["notice"]) -exports_files(["LICENSE.txt"]) -cc_library( - name = "half", - hdrs = ["include/half.hpp"], - strip_include_prefix = "include", - visibility = ["//visibility:public"], -) -""", - patches = [ - # https://sourceforge.net/p/half/discussion/general/thread/86298c105c/ - "//third_party:net_sourceforge_half/patches/detail_raise.patch", - ], - patch_args = ["-p1"], - cmake_name = "half", - cmake_target_mapping = { - "@net_sourceforge_half//:half": "half::half", - }, - bazel_to_cmake = {}, -) -''') - - # Load the BUILD file - cmake.state.process_build_content( - os.path.join(directory, 'BUILD.bazel'), """ - -package(default_visibility = ["//visibility:public"]) - -cc_library( - name = "a", - srcs = ["a.cc"], - hdrs = ["a.h"], - deps = [ - "@net_sourceforge_half//::half", - "@local_proto_mirror//:validate_cc", - ], -) -""") - - cmake.analyze() - cmake_text = cmake.get_text() - all_files = [str(x) for x in sorted(pathlib.Path('.').glob('**/*'))] - - self.assertIn('_cmake_binary_dir_/third_party/CMakeLists.txt', all_files) - self.assertIn('_cmake_binary_dir_/third_party/half-proxy-CMakeLists.txt', - all_files) - self.assertIn('FetchContent_Declare(half ', cmake_text) - - def test_local_mirror(self): - # bazel_to_cmake checks file existence before returning a - with tempfile.TemporaryDirectory(prefix='src') as directory: - os.chdir(directory) - with open('a.h', 'wb') as f: - f.write(bytes('// a.h\n', 'utf-8')) - with open('a.cc', 'wb') as f: - f.write(bytes('// a.cc\n', 'utf-8')) - - cmake_vars = CMAKE_VARS.copy() - cmake_vars.update({ - 'CMAKE_FIND_PACKAGE_REDIRECTS_DIR': - os.path.join(directory, '_find_package'), - }) - os.makedirs(cmake_vars['CMAKE_FIND_PACKAGE_REDIRECTS_DIR'], exist_ok=True) - - cmake = CMakeHelper(directory, cmake_vars) - - # Load the WORKSPACE - cmake.state.process_workspace_content( - os.path.join(directory, 'WORKSPACE'), ''' -workspace(name = "com_google_tensorstore") - -load("//bazel:local_mirror.bzl", "local_mirror") - -local_mirror( - name = "local_proto_mirror", - - cmake_name = "lpm", - cmake_target_mapping = { - "@local_proto_mirror//:validate_cc": "lpm::validate_cc", - }, - bazel_to_cmake = {}, - - files = [ - "b.h", "b.cc", - "validate.proto", - "BUILD.bazel", - ], - file_url = { - "validate.proto": [ - "https://raw.githubusercontent.com/bufbuild/protoc-gen-validate/2682ad06cca00550030e177834f58a2bc06eb61e/validate/validate.proto", - ], - }, - file_sha256 = { - "validate.proto": "bf7ca2ac45a75b8b9ff12f38efd7f48ee460ede1a7919d60c93fad3a64fc2eee", - }, - file_content = { - "b.h": "// b", - "b.cc": "// b.cc", - "BUILD.bazel": """ -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) - -proto_library( - name = "validate_proto", - srcs = ["validate.proto"], -) - -cc_proto_library( - name = "validate_cc", - deps = [ ":validate_proto" ], -) - -cc_library( - name = "b", - srcs = ["b.cc"], - hdrs = ["b.h"], -) -""", - } -) - -''') - - # Load the BUILD file - cmake.state.process_build_content( - os.path.join(directory, 'BUILD.bazel'), """ - -package(default_visibility = ["//visibility:public"]) - -cc_library( - name = "a", - srcs = ["a.cc"], - hdrs = ["a.h"], - deps = [ - "@local_proto_mirror//:b", - "@local_proto_mirror//:validate_cc", - ], -) -""") - - cmake.analyze() - cmake_text = cmake.get_text() - all_files = [str(x) for x in sorted(pathlib.Path('.').glob('**/*'))] - - self.assertIn('_cmake_binary_dir_/local_mirror/lpm/CMakeLists.txt', - all_files) - self.assertIn('_cmake_binary_dir_/local_mirror/lpm/BUILD.bazel', - all_files) - self.assertIn('_cmake_binary_dir_/local_mirror/lpm/b.cc', all_files) - self.assertIn('_cmake_binary_dir_/local_mirror/lpm/b.h', all_files) - self.assertIn( - """ -file(DOWNLOAD "https://raw.githubusercontent.com/bufbuild/protoc-gen-validate/2682ad06cca00550030e177834f58a2bc06eb61e/validate/validate.proto" "_cmake_binary_dir_/local_mirror/lpm/validate.proto" - EXPECTED_HASH SHA256=bf7ca2ac45a75b8b9ff12f38efd7f48ee460ede1a7919d60c93fad3a64fc2eee) -""", cmake_text) - - self.assertIn( - """add_subdirectory("_cmake_binary_dir_/local_mirror/lpm" _local_mirror_configs EXCLUDE_FROM_ALL)""", - cmake_text) - - def test_grpc_generate_cc(self): - self.maxDiff = None # pylint: disable=invalid-name - # bazel_to_cmake checks file existence before returning a - with tempfile.TemporaryDirectory(prefix='src') as directory: - os.chdir(directory) - with open('a.h', 'wb') as f: - f.write(bytes('// a.h\n', 'utf-8')) - with open('a.cc', 'wb') as f: - f.write(bytes('// a.cc\n', 'utf-8')) - with open('c.proto', 'wb') as f: - f.write(bytes('// c.proto\n', 'utf-8')) - - cmake = CMakeHelper(directory, CMAKE_VARS) - - # Load the WORKSPACE - cmake.state.process_workspace_content( - os.path.join(directory, 'WORKSPACE'), """ -workspace(name = "com_google_tensorstore") -""") - - # Load the BUILD file - cmake.state.process_build_content( - os.path.join(directory, 'BUILD.bazel'), """ -load("@com_github_grpc_grpc//bazel:generate_cc.bzl", "generate_cc") - -package(default_visibility = ["//visibility:public"]) - -cc_library( - name = "a", - srcs = ["a.cc"], - hdrs = ["a.h"], - deps = [":cc_grpc" ], -) - -proto_library( - name = "c_proto", - cc_api_version = 2, - srcs = [ "c.proto" ], -) - -# Mimic cc_grpc_library - -generate_cc( - name = "cc__grpc_codegen", - srcs = [ ":c_proto" ], - plugin = "@com_github_grpc_grpc//src/compiler:grpc_cpp_plugin", - flags = [ "services_namespace=grpc_gen" ], - visibility = ["//visibility:private"] -) - -cc_library( - name = "cc_grpc", - srcs = [":cc__grpc_codegen"], - hdrs = [":cc__grpc_codegen"], - deps = ["@com_github_grpc_grpc//:grpc++_codegen_proto"], -) - -""") - - cmake.analyze() - cmake_text = cmake.get_text() - - # There's output for A - self.assertIn( - f""" -add_library(CMakeProject_a) -target_sources(CMakeProject_a PRIVATE - "{directory}/a.cc") -set_property(TARGET CMakeProject_a PROPERTY LINKER_LANGUAGE "CXX") -target_link_libraries(CMakeProject_a PUBLIC - "CMakeProject::cc_grpc" - "Threads::Threads" - "m") -target_include_directories(CMakeProject_a PUBLIC - "$" - "$") -target_compile_features(CMakeProject_a PUBLIC cxx_std_17) -add_library(CMakeProject::a ALIAS CMakeProject_a) -""", cmake_text) - - # There's grpc output - self.assertIn( - f""" -add_custom_command( - OUTPUT "_cmake_binary_dir_/c.grpc.pb.h" "_cmake_binary_dir_/c.grpc.pb.cc" - COMMAND protobuf::protoc - ARGS --experimental_allow_proto3_optional - -I "${PROJECT_SOURCE_DIR}" - --plugin=protoc-gen-grpc="$" - --grpc_out=services_namespace=grpc_gen:${PROJECT_BINARY_DIR} - "{directory}/c.proto" - DEPENDS "{directory}/c.proto" "gRPC::grpc_cpp_plugin" "protobuf::protoc" - COMMENT "Running protoc (grpc) on c.proto" - VERBATIM) -add_custom_target(CMakeProject_cc__grpc_codegen DEPENDS "_cmake_binary_dir_/c.grpc.pb.h" "_cmake_binary_dir_/c.grpc.pb.cc") -""", cmake_text) - - -if __name__ == '__main__': - print(f'Invoke using python3 -m unittest {sys.argv[0]}')