Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # Copyright 2020 Google LLC |
| 3 | # |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | # you may not use this file except in compliance with the License. |
| 6 | # You may obtain a copy of the License at |
| 7 | # |
| 8 | # https://www.apache.org/licenses/LICENSE-2.0 |
| 9 | # |
| 10 | # Unless required by applicable law or agreed to in writing, software |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. |
Geoffrey Martin-Noble | bdeb1ab | 2020-03-31 13:27:14 -0700 | [diff] [blame] | 15 | """This script assists with converting from Bazel BUILD files to CMakeLists.txt. |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 16 | |
Geoffrey Martin-Noble | bdeb1ab | 2020-03-31 13:27:14 -0700 | [diff] [blame] | 17 | Bazel BUILD files should, where possible, be written to use simple features |
| 18 | that can be directly evaluated and avoid more advanced features like |
| 19 | variables, list comprehensions, etc. |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 20 | |
Geoffrey Martin-Noble | bdeb1ab | 2020-03-31 13:27:14 -0700 | [diff] [blame] | 21 | Generated CMake files will be similar in structure to their source BUILD |
| 22 | files by using the functions in build_tools/cmake/ that imitate corresponding |
| 23 | Bazel rules (e.g. cc_library -> iree_cc_library.cmake). |
| 24 | |
| 25 | For usage, see: |
| 26 | python3 build_tools/bazel_to_cmake/bazel_to_cmake.py --help |
| 27 | """ |
Geoffrey Martin-Noble | ea9c683 | 2020-02-29 20:26:35 -0800 | [diff] [blame] | 28 | # pylint: disable=missing-docstring |
Geoffrey Martin-Noble | ea9c683 | 2020-02-29 20:26:35 -0800 | [diff] [blame] | 29 | |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 30 | import argparse |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 31 | import datetime |
| 32 | import os |
Geoffrey Martin-Noble | 2a36c38 | 2020-01-28 15:04:30 -0800 | [diff] [blame] | 33 | import re |
Geoffrey Martin-Noble | bdeb1ab | 2020-03-31 13:27:14 -0700 | [diff] [blame] | 34 | import sys |
Geoffrey Martin-Noble | d2cb996 | 2021-02-19 17:30:26 -0800 | [diff] [blame^] | 35 | import textwrap |
| 36 | from enum import Enum |
Geoffrey Martin-Noble | ea9c683 | 2020-02-29 20:26:35 -0800 | [diff] [blame] | 37 | |
Geoffrey Martin-Noble | bdeb1ab | 2020-03-31 13:27:14 -0700 | [diff] [blame] | 38 | import bazel_to_cmake_converter |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 39 | |
| 40 | repo_root = None |
| 41 | |
Geoffrey Martin-Noble | 2a36c38 | 2020-01-28 15:04:30 -0800 | [diff] [blame] | 42 | EDIT_BLOCKING_PATTERN = re.compile( |
Geoffrey Martin-Noble | c9784d6 | 2020-02-18 13:49:49 -0800 | [diff] [blame] | 43 | r"bazel[\s_]*to[\s_]*cmake[\s_]*:?[\s_]*do[\s_]*not[\s_]*edit", |
Geoffrey Martin-Noble | 2a36c38 | 2020-01-28 15:04:30 -0800 | [diff] [blame] | 44 | flags=re.IGNORECASE) |
| 45 | |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 46 | |
Geoffrey Martin-Noble | d2cb996 | 2021-02-19 17:30:26 -0800 | [diff] [blame^] | 47 | class Status(Enum): |
| 48 | SUCCEEDED = 1 |
| 49 | FAILED = 2 |
| 50 | SKIPPED = 3 |
| 51 | NO_BUILD_FILE = 4 |
| 52 | |
| 53 | |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 54 | def parse_arguments(): |
| 55 | global repo_root |
| 56 | |
| 57 | parser = argparse.ArgumentParser( |
| 58 | description="Bazel to CMake conversion helper.") |
Phoenix Meadowlark | 9e63d75 | 2020-10-16 16:28:33 -0700 | [diff] [blame] | 59 | parser.add_argument("--preview", |
| 60 | help="Prints results instead of writing files", |
| 61 | action="store_true", |
| 62 | default=False) |
Scott Todd | bb79717 | 2020-04-15 17:20:33 -0700 | [diff] [blame] | 63 | parser.add_argument( |
| 64 | "--allow_partial_conversion", |
| 65 | help="Generates partial files, ignoring errors during conversion", |
Geoffrey Martin-Noble | f7be55a | 2020-01-28 17:47:42 -0800 | [diff] [blame] | 66 | action="store_true", |
| 67 | default=False) |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 68 | |
| 69 | # Specify only one of these (defaults to --root_dir=iree). |
| 70 | group = parser.add_mutually_exclusive_group() |
Phoenix Meadowlark | 9e63d75 | 2020-10-16 16:28:33 -0700 | [diff] [blame] | 71 | group.add_argument("--dir", |
| 72 | help="Converts the BUILD file in the given directory", |
| 73 | default=None) |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 74 | group.add_argument( |
| 75 | "--root_dir", |
| 76 | help="Converts all BUILD files under a root directory (defaults to iree/)", |
| 77 | default="iree") |
| 78 | |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 79 | args = parser.parse_args() |
| 80 | |
| 81 | # --dir takes precedence over --root_dir. |
| 82 | # They are mutually exclusive, but the default value is still set. |
| 83 | if args.dir: |
| 84 | args.root_dir = None |
| 85 | |
| 86 | return args |
| 87 | |
| 88 | |
| 89 | def setup_environment(): |
| 90 | """Sets up some environment globals.""" |
| 91 | global repo_root |
| 92 | |
| 93 | # Determine the repository root (two dir-levels up). |
| 94 | repo_root = os.path.dirname( |
| 95 | os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| 96 | |
| 97 | |
Geoffrey Martin-Noble | d2cb996 | 2021-02-19 17:30:26 -0800 | [diff] [blame^] | 98 | def repo_relpath(path): |
| 99 | return os.path.relpath(path, repo_root) |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 100 | |
| 101 | |
Geoffrey Martin-Noble | d2cb996 | 2021-02-19 17:30:26 -0800 | [diff] [blame^] | 102 | def log(string, *args, indent=0, **kwargs): |
| 103 | print(textwrap.indent(string, prefix=(indent * " ")), |
| 104 | *args, |
| 105 | **kwargs, |
| 106 | file=sys.stderr) |
| 107 | |
| 108 | |
| 109 | def convert_directories(directories, write_files, allow_partial_conversion): |
| 110 | failure_dirs = [] |
| 111 | skip_count = 0 |
| 112 | success_count = 0 |
| 113 | for directory in directories: |
| 114 | status = convert_directory(directory, write_files, allow_partial_conversion) |
| 115 | if status == Status.FAILED: |
| 116 | failure_dirs.append(repo_relpath(directory)) |
| 117 | elif status == Status.SKIPPED: |
| 118 | skip_count += 1 |
| 119 | elif status == Status.SUCCEEDED: |
| 120 | success_count += 1 |
| 121 | |
| 122 | log(f"Updated {success_count} and skipped {skip_count} CMakeLists.txt files") |
| 123 | if failure_dirs: |
| 124 | log(f"ERROR: Encountered unexpected errors converting {len(failure_dirs)}" |
| 125 | " directories:") |
| 126 | log("\n".join(failure_dirs), indent=2) |
| 127 | sys.exit(1) |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 128 | |
| 129 | |
Scott Todd | bb79717 | 2020-04-15 17:20:33 -0700 | [diff] [blame] | 130 | def convert_directory(directory_path, write_files, allow_partial_conversion): |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 131 | if not os.path.isdir(directory_path): |
Phoenix Meadowlark | 01bbb6c | 2020-03-30 14:20:11 -0700 | [diff] [blame] | 132 | raise FileNotFoundError(f"Cannot find directory '{directory_path}'") |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 133 | |
Stella Laurenzo | 66578b0 | 2020-07-20 17:34:44 -0700 | [diff] [blame] | 134 | skip_file_path = os.path.join(directory_path, ".skip_bazel_to_cmake") |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 135 | build_file_path = os.path.join(directory_path, "BUILD") |
| 136 | cmakelists_file_path = os.path.join(directory_path, "CMakeLists.txt") |
| 137 | |
Geoffrey Martin-Noble | d2cb996 | 2021-02-19 17:30:26 -0800 | [diff] [blame^] | 138 | if os.path.isfile(skip_file_path): |
| 139 | return Status.SKIPPED |
| 140 | if not os.path.isfile(build_file_path): |
| 141 | return Status.NO_BUILD_FILE |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 142 | |
Geoffrey Martin-Noble | d2cb996 | 2021-02-19 17:30:26 -0800 | [diff] [blame^] | 143 | rel_cmakelists_file_path = repo_relpath(cmakelists_file_path) |
| 144 | log(f"{rel_cmakelists_file_path}...", end="") |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 145 | |
Geoffrey Martin-Noble | 2a36c38 | 2020-01-28 15:04:30 -0800 | [diff] [blame] | 146 | cmake_file_exists = os.path.isfile(cmakelists_file_path) |
Phoenix Meadowlark | 01bbb6c | 2020-03-30 14:20:11 -0700 | [diff] [blame] | 147 | copyright_line = f"# Copyright {datetime.date.today().year} Google LLC" |
Geoffrey Martin-Noble | 2a36c38 | 2020-01-28 15:04:30 -0800 | [diff] [blame] | 148 | write_allowed = write_files |
| 149 | if cmake_file_exists: |
| 150 | with open(cmakelists_file_path) as f: |
| 151 | for i, line in enumerate(f): |
| 152 | if line.startswith("# Copyright"): |
| 153 | copyright_line = line.rstrip() |
| 154 | if EDIT_BLOCKING_PATTERN.search(line): |
Geoffrey Martin-Noble | d2cb996 | 2021-02-19 17:30:26 -0800 | [diff] [blame^] | 155 | log(f"\n Skipped. line {i + 1}: '{line.strip()}' prevents edits. ") |
| 156 | return Status.SKIPPED |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 157 | |
| 158 | with open(build_file_path, "rt") as build_file: |
| 159 | build_file_code = compile(build_file.read(), build_file_path, "exec") |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 160 | try: |
Geoffrey Martin-Noble | bdeb1ab | 2020-03-31 13:27:14 -0700 | [diff] [blame] | 161 | converted_text = bazel_to_cmake_converter.convert_build_file( |
Scott Todd | bb79717 | 2020-04-15 17:20:33 -0700 | [diff] [blame] | 162 | build_file_code, |
| 163 | copyright_line, |
| 164 | allow_partial_conversion=allow_partial_conversion) |
Geoffrey Martin-Noble | 2a36c38 | 2020-01-28 15:04:30 -0800 | [diff] [blame] | 165 | if write_allowed: |
Ben Vanik | 679424c | 2020-07-03 17:31:22 -0700 | [diff] [blame] | 166 | with open(cmakelists_file_path, "wt") as cmakelists_file: |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 167 | cmakelists_file.write(converted_text) |
| 168 | else: |
Geoffrey Martin-Noble | bdeb1ab | 2020-03-31 13:27:14 -0700 | [diff] [blame] | 169 | print(converted_text, end="") |
Geoffrey Martin-Noble | f7be55a | 2020-01-28 17:47:42 -0800 | [diff] [blame] | 170 | except (NameError, NotImplementedError) as e: |
Geoffrey Martin-Noble | d2cb996 | 2021-02-19 17:30:26 -0800 | [diff] [blame^] | 171 | log( |
| 172 | f"\nERROR.\n" |
| 173 | f"Missing a rule handler in bazel_to_cmake_converter.py?\n" |
| 174 | f"Reason: `{type(e).__name__}: {e}`", |
| 175 | indent=2) |
| 176 | return Status.FAILED |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 177 | except KeyError as e: |
Geoffrey Martin-Noble | d2cb996 | 2021-02-19 17:30:26 -0800 | [diff] [blame^] | 178 | log( |
| 179 | f"\nERROR.\n" |
| 180 | f"Missing a conversion in bazel_to_cmake_targets.py?\n" |
| 181 | f"Reason: `{type(e).__name__}: {e}`", |
| 182 | indent=2) |
| 183 | return Status.FAILED |
| 184 | log(f"Success") |
| 185 | return Status.SUCCEEDED |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 186 | |
| 187 | |
| 188 | def main(args): |
| 189 | """Runs Bazel to CMake conversion.""" |
| 190 | global repo_root |
| 191 | |
| 192 | write_files = not args.preview |
| 193 | |
| 194 | if args.root_dir: |
Geoffrey Martin-Noble | d2cb996 | 2021-02-19 17:30:26 -0800 | [diff] [blame^] | 195 | root_directory_path = os.path.join(repo_root, args.root_dir) |
| 196 | log(f"Converting directory tree rooted at: {root_directory_path}") |
| 197 | convert_directories((root for root, _, _ in os.walk(root_directory_path)), |
| 198 | write_files, args.allow_partial_conversion) |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 199 | elif args.dir: |
Geoffrey Martin-Noble | d2cb996 | 2021-02-19 17:30:26 -0800 | [diff] [blame^] | 200 | convert_directories([os.path.join(repo_root, args.dir)], write_files, |
| 201 | args.allow_partial_conversion) |
Scott Todd | d0a0629 | 2020-01-24 14:41:50 -0800 | [diff] [blame] | 202 | |
| 203 | |
| 204 | if __name__ == "__main__": |
| 205 | setup_environment() |
| 206 | main(parse_arguments()) |