blob: 51e559c680c6b7e1b51adfe804010742c11bfff4 [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-Nobleea9c6832020-02-29 20:26:35 -080035
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -070036import bazel_to_cmake_converter
Scott Toddd0a06292020-01-24 14:41:50 -080037
38repo_root = None
39
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -080040EDIT_BLOCKING_PATTERN = re.compile(
Geoffrey Martin-Noblec9784d62020-02-18 13:49:49 -080041 r"bazel[\s_]*to[\s_]*cmake[\s_]*:?[\s_]*do[\s_]*not[\s_]*edit",
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -080042 flags=re.IGNORECASE)
43
Scott Toddd0a06292020-01-24 14:41:50 -080044
45def parse_arguments():
46 global repo_root
47
48 parser = argparse.ArgumentParser(
49 description="Bazel to CMake conversion helper.")
50 parser.add_argument(
51 "--preview",
52 help="Prints results instead of writing files",
53 action="store_true",
54 default=False)
Scott Toddbb797172020-04-15 17:20:33 -070055 parser.add_argument(
56 "--allow_partial_conversion",
57 help="Generates partial files, ignoring errors during conversion",
Geoffrey Martin-Noblef7be55a2020-01-28 17:47:42 -080058 action="store_true",
59 default=False)
Scott Toddd0a06292020-01-24 14:41:50 -080060
61 # Specify only one of these (defaults to --root_dir=iree).
62 group = parser.add_mutually_exclusive_group()
63 group.add_argument(
64 "--dir",
65 help="Converts the BUILD file in the given directory",
66 default=None)
67 group.add_argument(
68 "--root_dir",
69 help="Converts all BUILD files under a root directory (defaults to iree/)",
70 default="iree")
71
Scott Toddd0a06292020-01-24 14:41:50 -080072 args = parser.parse_args()
73
74 # --dir takes precedence over --root_dir.
75 # They are mutually exclusive, but the default value is still set.
76 if args.dir:
77 args.root_dir = None
78
79 return args
80
81
82def setup_environment():
83 """Sets up some environment globals."""
84 global repo_root
85
86 # Determine the repository root (two dir-levels up).
87 repo_root = os.path.dirname(
88 os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
89
90
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -070091def log(*args, **kwargs):
92 print(*args, **kwargs, file=sys.stderr)
Scott Toddd0a06292020-01-24 14:41:50 -080093
94
Scott Toddbb797172020-04-15 17:20:33 -070095def convert_directory_tree(root_directory_path, write_files,
96 allow_partial_conversion):
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -070097 log(f"convert_directory_tree: {root_directory_path}")
Geoffrey Martin-Noble1b4ac812020-03-09 11:21:03 -070098 for root, _, _ in os.walk(root_directory_path):
Scott Toddbb797172020-04-15 17:20:33 -070099 convert_directory(root, write_files, allow_partial_conversion)
Scott Toddd0a06292020-01-24 14:41:50 -0800100
101
Scott Toddbb797172020-04-15 17:20:33 -0700102def convert_directory(directory_path, write_files, allow_partial_conversion):
Scott Toddd0a06292020-01-24 14:41:50 -0800103 if not os.path.isdir(directory_path):
Phoenix Meadowlark01bbb6c2020-03-30 14:20:11 -0700104 raise FileNotFoundError(f"Cannot find directory '{directory_path}'")
Scott Toddd0a06292020-01-24 14:41:50 -0800105
Stella Laurenzo66578b02020-07-20 17:34:44 -0700106 skip_file_path = os.path.join(directory_path, ".skip_bazel_to_cmake")
Scott Toddd0a06292020-01-24 14:41:50 -0800107 build_file_path = os.path.join(directory_path, "BUILD")
108 cmakelists_file_path = os.path.join(directory_path, "CMakeLists.txt")
109
Stella Laurenzo66578b02020-07-20 17:34:44 -0700110 if os.path.isfile(skip_file_path) or not os.path.isfile(build_file_path):
111 # No Bazel BUILD file in this directory or explicit skip.
Scott Toddd0a06292020-01-24 14:41:50 -0800112 return
113
114 global repo_root
115 rel_build_file_path = os.path.relpath(build_file_path, repo_root)
116 rel_cmakelists_file_path = os.path.relpath(cmakelists_file_path, repo_root)
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700117 log(f"Converting {rel_build_file_path} to {rel_cmakelists_file_path}")
Scott Toddd0a06292020-01-24 14:41:50 -0800118
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -0800119 cmake_file_exists = os.path.isfile(cmakelists_file_path)
Phoenix Meadowlark01bbb6c2020-03-30 14:20:11 -0700120 copyright_line = f"# Copyright {datetime.date.today().year} Google LLC"
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -0800121 write_allowed = write_files
122 if cmake_file_exists:
123 with open(cmakelists_file_path) as f:
124 for i, line in enumerate(f):
125 if line.startswith("# Copyright"):
126 copyright_line = line.rstrip()
127 if EDIT_BLOCKING_PATTERN.search(line):
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700128 log(f" {rel_cmakelists_file_path} already exists, and "
129 f"line {i + 1}: '{line.strip()}' prevents edits. "
130 f"Falling back to preview")
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -0800131 write_allowed = False
132
133 if write_allowed:
Scott Toddd0a06292020-01-24 14:41:50 -0800134 # TODO(scotttodd): Attempt to merge instead of overwrite?
135 # Existing CMakeLists.txt may have special logic that should be preserved
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -0800136 if cmake_file_exists:
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700137 log(f" {rel_cmakelists_file_path} already exists; overwriting")
Scott Toddd0a06292020-01-24 14:41:50 -0800138 else:
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700139 log(f" {rel_cmakelists_file_path} does not exist yet; creating")
140 log("")
Scott Toddd0a06292020-01-24 14:41:50 -0800141
142 with open(build_file_path, "rt") as build_file:
143 build_file_code = compile(build_file.read(), build_file_path, "exec")
Scott Toddd0a06292020-01-24 14:41:50 -0800144 try:
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700145 converted_text = bazel_to_cmake_converter.convert_build_file(
Scott Toddbb797172020-04-15 17:20:33 -0700146 build_file_code,
147 copyright_line,
148 allow_partial_conversion=allow_partial_conversion)
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -0800149 if write_allowed:
Ben Vanik679424c2020-07-03 17:31:22 -0700150 with open(cmakelists_file_path, "wt") as cmakelists_file:
Scott Toddd0a06292020-01-24 14:41:50 -0800151 cmakelists_file.write(converted_text)
152 else:
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700153 print(converted_text, end="")
Geoffrey Martin-Noblef7be55a2020-01-28 17:47:42 -0800154 except (NameError, NotImplementedError) as e:
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700155 log(f"Failed to convert {rel_build_file_path}.", end=" ")
156 log("Missing a rule handler in bazel_to_cmake.py?")
157 log(f" Reason: `{type(e).__name__}: {e}`")
Scott Toddd0a06292020-01-24 14:41:50 -0800158 except KeyError as e:
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700159 log(f"Failed to convert {rel_build_file_path}.", end=" ")
160 log("Missing a conversion in bazel_to_cmake_targets.py?")
161 log(f" Reason: `{type(e).__name__}: {e}`")
Scott Toddd0a06292020-01-24 14:41:50 -0800162
163
164def main(args):
165 """Runs Bazel to CMake conversion."""
166 global repo_root
167
168 write_files = not args.preview
169
170 if args.root_dir:
Geoffrey Martin-Noblef7be55a2020-01-28 17:47:42 -0800171 convert_directory_tree(
Scott Toddbb797172020-04-15 17:20:33 -0700172 os.path.join(repo_root, args.root_dir), write_files,
173 args.allow_partial_conversion)
Scott Toddd0a06292020-01-24 14:41:50 -0800174 elif args.dir:
Geoffrey Martin-Noblef7be55a2020-01-28 17:47:42 -0800175 convert_directory(
Scott Toddbb797172020-04-15 17:20:33 -0700176 os.path.join(repo_root, args.dir), write_files,
177 args.allow_partial_conversion)
Scott Toddd0a06292020-01-24 14:41:50 -0800178
179
180if __name__ == "__main__":
181 setup_environment()
182 main(parse_arguments())