blob: db57f5af136a2e0f754bfdb7b208d8b459e83a7c [file] [log] [blame]
Scott Toddd0a06292020-01-24 14:41:50 -08001#!/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-Noblebdeb1ab2020-03-31 13:27:14 -070015"""This script assists with converting from Bazel BUILD files to CMakeLists.txt.
Scott Toddd0a06292020-01-24 14:41:50 -080016
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -070017Bazel BUILD files should, where possible, be written to use simple features
18that can be directly evaluated and avoid more advanced features like
19variables, list comprehensions, etc.
Scott Toddd0a06292020-01-24 14:41:50 -080020
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -070021Generated CMake files will be similar in structure to their source BUILD
22files by using the functions in build_tools/cmake/ that imitate corresponding
23Bazel rules (e.g. cc_library -> iree_cc_library.cmake).
24
25For usage, see:
26 python3 build_tools/bazel_to_cmake/bazel_to_cmake.py --help
27"""
Geoffrey Martin-Nobleea9c6832020-02-29 20:26:35 -080028# pylint: disable=missing-docstring
Geoffrey Martin-Nobleea9c6832020-02-29 20:26:35 -080029
Scott Toddd0a06292020-01-24 14:41:50 -080030import argparse
Scott Toddd0a06292020-01-24 14:41:50 -080031import datetime
32import os
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -080033import re
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -070034import sys
Geoffrey Martin-Nobled2cb9962021-02-19 17:30:26 -080035import textwrap
36from enum import Enum
Geoffrey Martin-Nobleea9c6832020-02-29 20:26:35 -080037
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -070038import bazel_to_cmake_converter
Scott Toddd0a06292020-01-24 14:41:50 -080039
40repo_root = None
41
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -080042EDIT_BLOCKING_PATTERN = re.compile(
Geoffrey Martin-Noblec9784d62020-02-18 13:49:49 -080043 r"bazel[\s_]*to[\s_]*cmake[\s_]*:?[\s_]*do[\s_]*not[\s_]*edit",
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -080044 flags=re.IGNORECASE)
45
Scott Toddd0a06292020-01-24 14:41:50 -080046
Geoffrey Martin-Nobled2cb9962021-02-19 17:30:26 -080047class Status(Enum):
48 SUCCEEDED = 1
49 FAILED = 2
50 SKIPPED = 3
51 NO_BUILD_FILE = 4
52
53
Scott Toddd0a06292020-01-24 14:41:50 -080054def parse_arguments():
55 global repo_root
56
57 parser = argparse.ArgumentParser(
58 description="Bazel to CMake conversion helper.")
Phoenix Meadowlark9e63d752020-10-16 16:28:33 -070059 parser.add_argument("--preview",
60 help="Prints results instead of writing files",
61 action="store_true",
62 default=False)
Scott Toddbb797172020-04-15 17:20:33 -070063 parser.add_argument(
64 "--allow_partial_conversion",
65 help="Generates partial files, ignoring errors during conversion",
Geoffrey Martin-Noblef7be55a2020-01-28 17:47:42 -080066 action="store_true",
67 default=False)
Scott Toddd0a06292020-01-24 14:41:50 -080068
69 # Specify only one of these (defaults to --root_dir=iree).
70 group = parser.add_mutually_exclusive_group()
Phoenix Meadowlark9e63d752020-10-16 16:28:33 -070071 group.add_argument("--dir",
72 help="Converts the BUILD file in the given directory",
73 default=None)
Scott Toddd0a06292020-01-24 14:41:50 -080074 group.add_argument(
75 "--root_dir",
76 help="Converts all BUILD files under a root directory (defaults to iree/)",
77 default="iree")
78
Scott Toddd0a06292020-01-24 14:41:50 -080079 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
89def 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-Nobled2cb9962021-02-19 17:30:26 -080098def repo_relpath(path):
99 return os.path.relpath(path, repo_root)
Scott Toddd0a06292020-01-24 14:41:50 -0800100
101
Geoffrey Martin-Nobled2cb9962021-02-19 17:30:26 -0800102def log(string, *args, indent=0, **kwargs):
103 print(textwrap.indent(string, prefix=(indent * " ")),
104 *args,
105 **kwargs,
106 file=sys.stderr)
107
108
109def 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 Toddd0a06292020-01-24 14:41:50 -0800128
129
Scott Toddbb797172020-04-15 17:20:33 -0700130def convert_directory(directory_path, write_files, allow_partial_conversion):
Scott Toddd0a06292020-01-24 14:41:50 -0800131 if not os.path.isdir(directory_path):
Phoenix Meadowlark01bbb6c2020-03-30 14:20:11 -0700132 raise FileNotFoundError(f"Cannot find directory '{directory_path}'")
Scott Toddd0a06292020-01-24 14:41:50 -0800133
Stella Laurenzo66578b02020-07-20 17:34:44 -0700134 skip_file_path = os.path.join(directory_path, ".skip_bazel_to_cmake")
Scott Toddd0a06292020-01-24 14:41:50 -0800135 build_file_path = os.path.join(directory_path, "BUILD")
136 cmakelists_file_path = os.path.join(directory_path, "CMakeLists.txt")
137
Geoffrey Martin-Nobled2cb9962021-02-19 17:30:26 -0800138 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 Toddd0a06292020-01-24 14:41:50 -0800142
Geoffrey Martin-Nobled2cb9962021-02-19 17:30:26 -0800143 rel_cmakelists_file_path = repo_relpath(cmakelists_file_path)
144 log(f"{rel_cmakelists_file_path}...", end="")
Scott Toddd0a06292020-01-24 14:41:50 -0800145
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -0800146 cmake_file_exists = os.path.isfile(cmakelists_file_path)
Phoenix Meadowlark01bbb6c2020-03-30 14:20:11 -0700147 copyright_line = f"# Copyright {datetime.date.today().year} Google LLC"
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -0800148 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-Nobled2cb9962021-02-19 17:30:26 -0800155 log(f"\n Skipped. line {i + 1}: '{line.strip()}' prevents edits. ")
156 return Status.SKIPPED
Scott Toddd0a06292020-01-24 14:41:50 -0800157
158 with open(build_file_path, "rt") as build_file:
159 build_file_code = compile(build_file.read(), build_file_path, "exec")
Scott Toddd0a06292020-01-24 14:41:50 -0800160 try:
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700161 converted_text = bazel_to_cmake_converter.convert_build_file(
Scott Toddbb797172020-04-15 17:20:33 -0700162 build_file_code,
163 copyright_line,
164 allow_partial_conversion=allow_partial_conversion)
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -0800165 if write_allowed:
Ben Vanik679424c2020-07-03 17:31:22 -0700166 with open(cmakelists_file_path, "wt") as cmakelists_file:
Scott Toddd0a06292020-01-24 14:41:50 -0800167 cmakelists_file.write(converted_text)
168 else:
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700169 print(converted_text, end="")
Geoffrey Martin-Noblef7be55a2020-01-28 17:47:42 -0800170 except (NameError, NotImplementedError) as e:
Geoffrey Martin-Nobled2cb9962021-02-19 17:30:26 -0800171 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 Toddd0a06292020-01-24 14:41:50 -0800177 except KeyError as e:
Geoffrey Martin-Nobled2cb9962021-02-19 17:30:26 -0800178 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 Toddd0a06292020-01-24 14:41:50 -0800186
187
188def 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-Nobled2cb9962021-02-19 17:30:26 -0800195 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 Toddd0a06292020-01-24 14:41:50 -0800199 elif args.dir:
Geoffrey Martin-Nobled2cb9962021-02-19 17:30:26 -0800200 convert_directories([os.path.join(repo_root, args.dir)], write_files,
201 args.allow_partial_conversion)
Scott Toddd0a06292020-01-24 14:41:50 -0800202
203
204if __name__ == "__main__":
205 setup_environment()
206 main(parse_arguments())