blob: 213df337a3586bc9b3aa9897f8eb886edc9995ed [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)
Geoffrey Martin-Noblef26a05a2020-03-06 13:09:25 -080055 # TODO(b/149926655): Invert the default to be strict and rename this flag.
Geoffrey Martin-Noblef7be55a2020-01-28 17:47:42 -080056 parser.add_argument(
57 "--strict",
58 help="Does not try to generate files where it cannot convert completely",
59 action="store_true",
60 default=False)
Scott Toddd0a06292020-01-24 14:41:50 -080061
62 # Specify only one of these (defaults to --root_dir=iree).
63 group = parser.add_mutually_exclusive_group()
64 group.add_argument(
65 "--dir",
66 help="Converts the BUILD file in the given directory",
67 default=None)
68 group.add_argument(
69 "--root_dir",
70 help="Converts all BUILD files under a root directory (defaults to iree/)",
71 default="iree")
72
73 # TODO(scotttodd): --check option that returns success/failure depending on
74 # if files match the converted versions
75
76 args = parser.parse_args()
77
78 # --dir takes precedence over --root_dir.
79 # They are mutually exclusive, but the default value is still set.
80 if args.dir:
81 args.root_dir = None
82
83 return args
84
85
86def setup_environment():
87 """Sets up some environment globals."""
88 global repo_root
89
90 # Determine the repository root (two dir-levels up).
91 repo_root = os.path.dirname(
92 os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
93
94
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -070095def log(*args, **kwargs):
96 print(*args, **kwargs, file=sys.stderr)
Scott Toddd0a06292020-01-24 14:41:50 -080097
98
Geoffrey Martin-Noblef7be55a2020-01-28 17:47:42 -080099def convert_directory_tree(root_directory_path, write_files, strict):
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700100 log(f"convert_directory_tree: {root_directory_path}")
Geoffrey Martin-Noble1b4ac812020-03-09 11:21:03 -0700101 for root, _, _ in os.walk(root_directory_path):
Geoffrey Martin-Noblef7be55a2020-01-28 17:47:42 -0800102 convert_directory(root, write_files, strict)
Scott Toddd0a06292020-01-24 14:41:50 -0800103
104
Geoffrey Martin-Noblef7be55a2020-01-28 17:47:42 -0800105def convert_directory(directory_path, write_files, strict):
Scott Toddd0a06292020-01-24 14:41:50 -0800106 if not os.path.isdir(directory_path):
Phoenix Meadowlark01bbb6c2020-03-30 14:20:11 -0700107 raise FileNotFoundError(f"Cannot find directory '{directory_path}'")
Scott Toddd0a06292020-01-24 14:41:50 -0800108
109 build_file_path = os.path.join(directory_path, "BUILD")
110 cmakelists_file_path = os.path.join(directory_path, "CMakeLists.txt")
111
112 if not os.path.isfile(build_file_path):
113 # No Bazel BUILD file in this directory to convert, skip.
114 return
115
116 global repo_root
117 rel_build_file_path = os.path.relpath(build_file_path, repo_root)
118 rel_cmakelists_file_path = os.path.relpath(cmakelists_file_path, repo_root)
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700119 log(f"Converting {rel_build_file_path} to {rel_cmakelists_file_path}")
Scott Toddd0a06292020-01-24 14:41:50 -0800120
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -0800121 cmake_file_exists = os.path.isfile(cmakelists_file_path)
Phoenix Meadowlark01bbb6c2020-03-30 14:20:11 -0700122 copyright_line = f"# Copyright {datetime.date.today().year} Google LLC"
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -0800123 write_allowed = write_files
124 if cmake_file_exists:
125 with open(cmakelists_file_path) as f:
126 for i, line in enumerate(f):
127 if line.startswith("# Copyright"):
128 copyright_line = line.rstrip()
129 if EDIT_BLOCKING_PATTERN.search(line):
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700130 log(f" {rel_cmakelists_file_path} already exists, and "
131 f"line {i + 1}: '{line.strip()}' prevents edits. "
132 f"Falling back to preview")
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -0800133 write_allowed = False
134
135 if write_allowed:
Scott Toddd0a06292020-01-24 14:41:50 -0800136 # TODO(scotttodd): Attempt to merge instead of overwrite?
137 # Existing CMakeLists.txt may have special logic that should be preserved
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -0800138 if cmake_file_exists:
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700139 log(f" {rel_cmakelists_file_path} already exists; overwriting")
Scott Toddd0a06292020-01-24 14:41:50 -0800140 else:
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700141 log(f" {rel_cmakelists_file_path} does not exist yet; creating")
142 log("")
Scott Toddd0a06292020-01-24 14:41:50 -0800143
144 with open(build_file_path, "rt") as build_file:
145 build_file_code = compile(build_file.read(), build_file_path, "exec")
Scott Toddd0a06292020-01-24 14:41:50 -0800146 try:
Geoffrey Martin-Noblebdeb1ab2020-03-31 13:27:14 -0700147 converted_text = bazel_to_cmake_converter.convert_build_file(
148 build_file_code, copyright_line, strict=strict)
Geoffrey Martin-Noble2a36c382020-01-28 15:04:30 -0800149 if write_allowed:
Scott Toddd0a06292020-01-24 14:41:50 -0800150 with open(cmakelists_file_path, "wt") as cmakelists_file:
151 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(
172 os.path.join(repo_root, args.root_dir), write_files, args.strict)
Scott Toddd0a06292020-01-24 14:41:50 -0800173 elif args.dir:
Geoffrey Martin-Noblef7be55a2020-01-28 17:47:42 -0800174 convert_directory(
175 os.path.join(repo_root, args.dir), write_files, args.strict)
Scott Toddd0a06292020-01-24 14:41:50 -0800176
177
178if __name__ == "__main__":
179 setup_environment()
180 main(parse_arguments())