1*bb86c7edSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*bb86c7edSAndroid Build Coastguard Worker# Copyright 2021 Google LLC 3*bb86c7edSAndroid Build Coastguard Worker# 4*bb86c7edSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 5*bb86c7edSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 6*bb86c7edSAndroid Build Coastguard Worker# You may obtain a copy of the License at 7*bb86c7edSAndroid Build Coastguard Worker# 8*bb86c7edSAndroid Build Coastguard Worker# https://www.apache.org/licenses/LICENSE-2.0 9*bb86c7edSAndroid Build Coastguard Worker# 10*bb86c7edSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 11*bb86c7edSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 12*bb86c7edSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*bb86c7edSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 14*bb86c7edSAndroid Build Coastguard Worker# limitations under the License. 15*bb86c7edSAndroid Build Coastguard Worker 16*bb86c7edSAndroid Build Coastguard Worker"""This is yet another bazel-to-cmake converter. It's independently written from 17*bb86c7edSAndroid Build Coastguard Workerscratch but relies on the same basic idea as others (including IREE's), namely: 18*bb86c7edSAndroid Build Coastguard Workerto let the python interpreter do the bulk of the work, exploiting the fact that 19*bb86c7edSAndroid Build Coastguard Workerboth Bazel's BUILD syntax and Starlark (".bzl") languages are more or less 20*bb86c7edSAndroid Build Coastguard Workersubsets of Python. 21*bb86c7edSAndroid Build Coastguard Worker 22*bb86c7edSAndroid Build Coastguard WorkerThe main features that this converter supports and that others don't, justifying 23*bb86c7edSAndroid Build Coastguard Workerits existence as of early 2021, are: 24*bb86c7edSAndroid Build Coastguard Worker 1. Ad-hoc support for select(), generating CMake if()...elseif()... chains 25*bb86c7edSAndroid Build Coastguard Worker parsing the condition keys (e.g. anything ending in ":windows" is 26*bb86c7edSAndroid Build Coastguard Worker interpreted as the condition "the target platform is Windows"). This allows 27*bb86c7edSAndroid Build Coastguard Worker to just ignore config_setting, as we only care about the config_setting 28*bb86c7edSAndroid Build Coastguard Worker names, not their actual implementation, as well as all the variants from 29*bb86c7edSAndroid Build Coastguard Worker the Bazel 'selects' library. 30*bb86c7edSAndroid Build Coastguard Worker 2. Support for load(), loading macros from Starlark files. 31*bb86c7edSAndroid Build Coastguard Worker""" 32*bb86c7edSAndroid Build Coastguard Worker 33*bb86c7edSAndroid Build Coastguard Workerimport re 34*bb86c7edSAndroid Build Coastguard Workerimport os 35*bb86c7edSAndroid Build Coastguard Workerimport os.path 36*bb86c7edSAndroid Build Coastguard Workerimport pickle 37*bb86c7edSAndroid Build Coastguard Workerimport sys 38*bb86c7edSAndroid Build Coastguard Workerimport datetime 39*bb86c7edSAndroid Build Coastguard Workerimport itertools 40*bb86c7edSAndroid Build Coastguard Worker 41*bb86c7edSAndroid Build Coastguard Worker# Ruy's dependencies. 42*bb86c7edSAndroid Build Coastguard Workerexternal_targets = ['gtest', 'gtest_main', 'cpuinfo'] 43*bb86c7edSAndroid Build Coastguard Worker 44*bb86c7edSAndroid Build Coastguard Worker# Text replacements [oldstring, newstring] pairs, applied on all BUILD and 45*bb86c7edSAndroid Build Coastguard Worker# Starlark files that we load. Only used by preprocess_input_text. 46*bb86c7edSAndroid Build Coastguard Workerreplacements = [ 47*bb86c7edSAndroid Build Coastguard Worker ['$(STACK_FRAME_UNLIMITED)', ''], 48*bb86c7edSAndroid Build Coastguard Worker ['native.cc_', 'cc_'], 49*bb86c7edSAndroid Build Coastguard Worker ['selects.config_setting_group', 'config_setting_group'], 50*bb86c7edSAndroid Build Coastguard Worker ['@com_google_googletest//:gtest', 'gtest'], 51*bb86c7edSAndroid Build Coastguard Worker ['@com_google_googletest//:gtest_main', 'gtest_main'], 52*bb86c7edSAndroid Build Coastguard Worker ['@cpuinfo', 'cpuinfo::cpuinfo'], 53*bb86c7edSAndroid Build Coastguard Worker] 54*bb86c7edSAndroid Build Coastguard Worker 55*bb86c7edSAndroid Build Coastguard Worker 56*bb86c7edSAndroid Build Coastguard Workerdef preprocess_input_text(text): 57*bb86c7edSAndroid Build Coastguard Worker result = text 58*bb86c7edSAndroid Build Coastguard Worker for replacement in replacements: 59*bb86c7edSAndroid Build Coastguard Worker result = result.replace(replacement[0], replacement[1]) 60*bb86c7edSAndroid Build Coastguard Worker return result 61*bb86c7edSAndroid Build Coastguard Worker 62*bb86c7edSAndroid Build Coastguard Worker 63*bb86c7edSAndroid Build Coastguard Workerdef set_cmake_list(list_name, values, indent): 64*bb86c7edSAndroid Build Coastguard Worker semicolon_separated = ';'.join(values) 65*bb86c7edSAndroid Build Coastguard Worker print(f'{indent}set({list_name} "{semicolon_separated}")') 66*bb86c7edSAndroid Build Coastguard Worker 67*bb86c7edSAndroid Build Coastguard Worker 68*bb86c7edSAndroid Build Coastguard Workerdef generate_cmake_select(select_name, dict): 69*bb86c7edSAndroid Build Coastguard Worker new_if_branch_keyword = 'if' 70*bb86c7edSAndroid Build Coastguard Worker default_value = [] 71*bb86c7edSAndroid Build Coastguard Worker for key in dict: 72*bb86c7edSAndroid Build Coastguard Worker condition = '' 73*bb86c7edSAndroid Build Coastguard Worker if key == '//conditions:default': 74*bb86c7edSAndroid Build Coastguard Worker default_value = dict[key] 75*bb86c7edSAndroid Build Coastguard Worker continue 76*bb86c7edSAndroid Build Coastguard Worker elif re.search(r':windows$', key): 77*bb86c7edSAndroid Build Coastguard Worker condition = 'CMAKE_SYSTEM_NAME STREQUAL Windows' 78*bb86c7edSAndroid Build Coastguard Worker elif re.search(r':ppc$', key): 79*bb86c7edSAndroid Build Coastguard Worker condition = ('CMAKE_SYSTEM_PROCESSOR STREQUAL ppc64 OR ' 80*bb86c7edSAndroid Build Coastguard Worker 'CMAKE_SYSTEM_PROCESSOR STREQUAL ppc64le') 81*bb86c7edSAndroid Build Coastguard Worker elif re.search(r':s390x$', key): 82*bb86c7edSAndroid Build Coastguard Worker condition = ('CMAKE_SYSTEM_PROCESSOR STREQUAL s390 OR ' 83*bb86c7edSAndroid Build Coastguard Worker 'CMAKE_SYSTEM_PROCESSOR STREQUAL s390x') 84*bb86c7edSAndroid Build Coastguard Worker elif re.search(r':fuchsia$', key): 85*bb86c7edSAndroid Build Coastguard Worker condition = 'CMAKE_SYSTEM_NAME STREQUAL Fuchsia' 86*bb86c7edSAndroid Build Coastguard Worker elif re.search(r':arm32_assuming_neon$', key): 87*bb86c7edSAndroid Build Coastguard Worker condition = 'CMAKE_SYSTEM_PROCESSOR STREQUAL arm' 88*bb86c7edSAndroid Build Coastguard Worker elif re.search(r':do_not_want_O3$', key): 89*bb86c7edSAndroid Build Coastguard Worker # Ruy is a specialist library: we always want code to be compiled 90*bb86c7edSAndroid Build Coastguard Worker # with -O3 unless the build type is Debug or the compiler does not 91*bb86c7edSAndroid Build Coastguard Worker # support that flag syntax. 92*bb86c7edSAndroid Build Coastguard Worker condition = '(CMAKE_BUILD_TYPE STREQUAL Debug) OR MSVC' 93*bb86c7edSAndroid Build Coastguard Worker elif re.search(r':x86_64_and_not_msvc$', key): 94*bb86c7edSAndroid Build Coastguard Worker condition = ('(CMAKE_SYSTEM_PROCESSOR STREQUAL x86_64 OR ' 95*bb86c7edSAndroid Build Coastguard Worker 'CMAKE_SYSTEM_PROCESSOR STREQUAL amd64) AND NOT MSVC') 96*bb86c7edSAndroid Build Coastguard Worker elif re.search(r':windows_msvc$', key): 97*bb86c7edSAndroid Build Coastguard Worker condition = 'MSVC' 98*bb86c7edSAndroid Build Coastguard Worker elif re.search(r':ruy_profiler$', key): 99*bb86c7edSAndroid Build Coastguard Worker condition = '${RUY_PROFILER}' 100*bb86c7edSAndroid Build Coastguard Worker else: 101*bb86c7edSAndroid Build Coastguard Worker raise ValueError(f'Unhandled key in select: {key}') 102*bb86c7edSAndroid Build Coastguard Worker 103*bb86c7edSAndroid Build Coastguard Worker print(f'{new_if_branch_keyword}({condition})') 104*bb86c7edSAndroid Build Coastguard Worker set_cmake_list(select_name, dict[key], ' ') 105*bb86c7edSAndroid Build Coastguard Worker new_if_branch_keyword = 'elseif' 106*bb86c7edSAndroid Build Coastguard Worker 107*bb86c7edSAndroid Build Coastguard Worker print('else()') 108*bb86c7edSAndroid Build Coastguard Worker set_cmake_list(select_name, default_value, ' ') 109*bb86c7edSAndroid Build Coastguard Worker 110*bb86c7edSAndroid Build Coastguard Worker print('endif()\n') 111*bb86c7edSAndroid Build Coastguard Worker 112*bb86c7edSAndroid Build Coastguard Worker 113*bb86c7edSAndroid Build Coastguard Workerdef trim_multiple_ruy_prefixes(name): 114*bb86c7edSAndroid Build Coastguard Worker return re.sub(r'(ruy_)+ruy', 'ruy', name) 115*bb86c7edSAndroid Build Coastguard Worker 116*bb86c7edSAndroid Build Coastguard Worker 117*bb86c7edSAndroid Build Coastguard Workerdef get_cmake_local_target_name(name): 118*bb86c7edSAndroid Build Coastguard Worker global package_prefix 119*bb86c7edSAndroid Build Coastguard Worker return trim_multiple_ruy_prefixes(f'ruy_{package_prefix}_{name}') 120*bb86c7edSAndroid Build Coastguard Worker 121*bb86c7edSAndroid Build Coastguard Worker 122*bb86c7edSAndroid Build Coastguard Workerdef get_cmake_dep_target_name(name): 123*bb86c7edSAndroid Build Coastguard Worker if name in external_targets: 124*bb86c7edSAndroid Build Coastguard Worker return name 125*bb86c7edSAndroid Build Coastguard Worker if name.startswith('$'): 126*bb86c7edSAndroid Build Coastguard Worker # Happens for deps that are the result of expanding a select() that we 127*bb86c7edSAndroid Build Coastguard Worker # have compiled to expanding a variable. 128*bb86c7edSAndroid Build Coastguard Worker return name 129*bb86c7edSAndroid Build Coastguard Worker if name.startswith('//'): 130*bb86c7edSAndroid Build Coastguard Worker after_last_slash = name.split('/')[-1] 131*bb86c7edSAndroid Build Coastguard Worker if ':' not in after_last_slash: 132*bb86c7edSAndroid Build Coastguard Worker name = f'{name}:{after_last_slash}' 133*bb86c7edSAndroid Build Coastguard Worker raw = name[2:].replace('/', '_').replace(':', '_') 134*bb86c7edSAndroid Build Coastguard Worker return trim_multiple_ruy_prefixes(raw) 135*bb86c7edSAndroid Build Coastguard Worker if name.startswith(':'): 136*bb86c7edSAndroid Build Coastguard Worker name = name[1:] 137*bb86c7edSAndroid Build Coastguard Worker return get_cmake_local_target_name(name) 138*bb86c7edSAndroid Build Coastguard Worker 139*bb86c7edSAndroid Build Coastguard Worker 140*bb86c7edSAndroid Build Coastguard Worker# 141*bb86c7edSAndroid Build Coastguard Worker# Functions implementing BUILD functions 142*bb86c7edSAndroid Build Coastguard Worker# 143*bb86c7edSAndroid Build Coastguard Worker 144*bb86c7edSAndroid Build Coastguard Worker 145*bb86c7edSAndroid Build Coastguard Workerdef package(**kwargs): 146*bb86c7edSAndroid Build Coastguard Worker pass 147*bb86c7edSAndroid Build Coastguard Worker 148*bb86c7edSAndroid Build Coastguard Worker 149*bb86c7edSAndroid Build Coastguard Workerdef exports_files(*args): 150*bb86c7edSAndroid Build Coastguard Worker pass 151*bb86c7edSAndroid Build Coastguard Worker 152*bb86c7edSAndroid Build Coastguard Worker 153*bb86c7edSAndroid Build Coastguard Workerdef load(filename, *args): 154*bb86c7edSAndroid Build Coastguard Worker if filename.startswith('@'): 155*bb86c7edSAndroid Build Coastguard Worker return 156*bb86c7edSAndroid Build Coastguard Worker elif filename.startswith(':'): 157*bb86c7edSAndroid Build Coastguard Worker filename = os.path.join(bazel_package_dir, filename[1:]) 158*bb86c7edSAndroid Build Coastguard Worker elif filename.startswith('//'): 159*bb86c7edSAndroid Build Coastguard Worker split = filename[2:].split(':') 160*bb86c7edSAndroid Build Coastguard Worker filename = os.path.join(bazel_workspace_dir, split[0], split[1]) 161*bb86c7edSAndroid Build Coastguard Worker 162*bb86c7edSAndroid Build Coastguard Worker src_file_content = open(filename).read() 163*bb86c7edSAndroid Build Coastguard Worker processed_file_content = preprocess_input_text(src_file_content) 164*bb86c7edSAndroid Build Coastguard Worker exec(processed_file_content, globals(), globals()) 165*bb86c7edSAndroid Build Coastguard Worker 166*bb86c7edSAndroid Build Coastguard Worker 167*bb86c7edSAndroid Build Coastguard Workerdef config_setting(**kwargs): 168*bb86c7edSAndroid Build Coastguard Worker # Nothing to do since our implementation of select() is based on parsing 169*bb86c7edSAndroid Build Coastguard Worker # the names of config_settings, not looking deep into their actual 170*bb86c7edSAndroid Build Coastguard Worker # implementation. 171*bb86c7edSAndroid Build Coastguard Worker pass 172*bb86c7edSAndroid Build Coastguard Worker 173*bb86c7edSAndroid Build Coastguard Worker 174*bb86c7edSAndroid Build Coastguard Workerdef filegroup(**kwargs): 175*bb86c7edSAndroid Build Coastguard Worker pass 176*bb86c7edSAndroid Build Coastguard Worker 177*bb86c7edSAndroid Build Coastguard Worker 178*bb86c7edSAndroid Build Coastguard Workerdef config_setting_group(**kwargs): 179*bb86c7edSAndroid Build Coastguard Worker # See config_setting. 180*bb86c7edSAndroid Build Coastguard Worker pass 181*bb86c7edSAndroid Build Coastguard Worker 182*bb86c7edSAndroid Build Coastguard Worker 183*bb86c7edSAndroid Build Coastguard Workerdef bzl_library(**kwargs): 184*bb86c7edSAndroid Build Coastguard Worker pass 185*bb86c7edSAndroid Build Coastguard Worker 186*bb86c7edSAndroid Build Coastguard Worker 187*bb86c7edSAndroid Build Coastguard Workerselect_index = 0 188*bb86c7edSAndroid Build Coastguard Workerselect_cache = {} 189*bb86c7edSAndroid Build Coastguard Worker 190*bb86c7edSAndroid Build Coastguard Worker 191*bb86c7edSAndroid Build Coastguard Workerdef select(select_dict): 192*bb86c7edSAndroid Build Coastguard Worker global select_index 193*bb86c7edSAndroid Build Coastguard Worker global select_cache 194*bb86c7edSAndroid Build Coastguard Worker global package_prefix 195*bb86c7edSAndroid Build Coastguard Worker key = pickle.dumps(sorted(select_dict.items())) 196*bb86c7edSAndroid Build Coastguard Worker if key in select_cache: 197*bb86c7edSAndroid Build Coastguard Worker select_name = select_cache[key] 198*bb86c7edSAndroid Build Coastguard Worker else: 199*bb86c7edSAndroid Build Coastguard Worker unique_values = sorted( 200*bb86c7edSAndroid Build Coastguard Worker set(itertools.chain.from_iterable(select_dict.values())) 201*bb86c7edSAndroid Build Coastguard Worker ) # sorting ensures determinism, no spurious diffs 202*bb86c7edSAndroid Build Coastguard Worker description = '_'.join(unique_values) 203*bb86c7edSAndroid Build Coastguard Worker select_name = f'{package_prefix}_{select_index}_{description}' 204*bb86c7edSAndroid Build Coastguard Worker select_name = select_name.replace('c++', 'cxx') 205*bb86c7edSAndroid Build Coastguard Worker select_name = re.sub(r'[^a-zA-Z0-9]+', '_', select_name) 206*bb86c7edSAndroid Build Coastguard Worker select_index = select_index + 1 207*bb86c7edSAndroid Build Coastguard Worker select_cache[key] = select_name 208*bb86c7edSAndroid Build Coastguard Worker generate_cmake_select(select_name, select_dict) 209*bb86c7edSAndroid Build Coastguard Worker 210*bb86c7edSAndroid Build Coastguard Worker return [f'${{{select_name}}}'] 211*bb86c7edSAndroid Build Coastguard Worker 212*bb86c7edSAndroid Build Coastguard Worker 213*bb86c7edSAndroid Build Coastguard Workerdef generic_rule(rule_name, **kwargs): 214*bb86c7edSAndroid Build Coastguard Worker print(f'{rule_name}(') 215*bb86c7edSAndroid Build Coastguard Worker for key in kwargs.keys(): 216*bb86c7edSAndroid Build Coastguard Worker values = kwargs[key] 217*bb86c7edSAndroid Build Coastguard Worker if type(values) is bool: 218*bb86c7edSAndroid Build Coastguard Worker if values: 219*bb86c7edSAndroid Build Coastguard Worker print(f' {key.upper()}') 220*bb86c7edSAndroid Build Coastguard Worker continue 221*bb86c7edSAndroid Build Coastguard Worker else: 222*bb86c7edSAndroid Build Coastguard Worker raise ValueError('Cannot specify FALSE boolean args in CMake') 223*bb86c7edSAndroid Build Coastguard Worker if key == 'visibility': 224*bb86c7edSAndroid Build Coastguard Worker if values == ['//visibility:public']: 225*bb86c7edSAndroid Build Coastguard Worker print(f' PUBLIC') 226*bb86c7edSAndroid Build Coastguard Worker continue 227*bb86c7edSAndroid Build Coastguard Worker if key == 'tags': 228*bb86c7edSAndroid Build Coastguard Worker values = list(filter(lambda x: not x.startswith('req_dep'), values)) 229*bb86c7edSAndroid Build Coastguard Worker if not values: 230*bb86c7edSAndroid Build Coastguard Worker continue 231*bb86c7edSAndroid Build Coastguard Worker print(f' {key.upper()}') 232*bb86c7edSAndroid Build Coastguard Worker if type(values) is list: 233*bb86c7edSAndroid Build Coastguard Worker for value in values: 234*bb86c7edSAndroid Build Coastguard Worker if key == 'deps': 235*bb86c7edSAndroid Build Coastguard Worker target_name = get_cmake_dep_target_name(value) 236*bb86c7edSAndroid Build Coastguard Worker print(f' {target_name}') 237*bb86c7edSAndroid Build Coastguard Worker else: 238*bb86c7edSAndroid Build Coastguard Worker print(f' {value}') 239*bb86c7edSAndroid Build Coastguard Worker else: 240*bb86c7edSAndroid Build Coastguard Worker if key == 'name': 241*bb86c7edSAndroid Build Coastguard Worker target_name = get_cmake_local_target_name(values) 242*bb86c7edSAndroid Build Coastguard Worker print(f' {target_name}') 243*bb86c7edSAndroid Build Coastguard Worker else: 244*bb86c7edSAndroid Build Coastguard Worker print(f' {values}') 245*bb86c7edSAndroid Build Coastguard Worker print(')\n') 246*bb86c7edSAndroid Build Coastguard Worker 247*bb86c7edSAndroid Build Coastguard Worker 248*bb86c7edSAndroid Build Coastguard Workerdef cc_library(**kwargs): 249*bb86c7edSAndroid Build Coastguard Worker generic_rule('ruy_cc_library', **kwargs) 250*bb86c7edSAndroid Build Coastguard Worker 251*bb86c7edSAndroid Build Coastguard Worker 252*bb86c7edSAndroid Build Coastguard Workerdef cc_test(**kwargs): 253*bb86c7edSAndroid Build Coastguard Worker generic_rule('ruy_cc_test', **kwargs) 254*bb86c7edSAndroid Build Coastguard Worker 255*bb86c7edSAndroid Build Coastguard Worker 256*bb86c7edSAndroid Build Coastguard Workerdef cc_binary(**kwargs): 257*bb86c7edSAndroid Build Coastguard Worker generic_rule('ruy_cc_binary', **kwargs) 258*bb86c7edSAndroid Build Coastguard Worker 259*bb86c7edSAndroid Build Coastguard Worker 260*bb86c7edSAndroid Build Coastguard Worker# 261*bb86c7edSAndroid Build Coastguard Worker# Program entry point. 262*bb86c7edSAndroid Build Coastguard Worker# 263*bb86c7edSAndroid Build Coastguard Workerif __name__ == "__main__": 264*bb86c7edSAndroid Build Coastguard Worker if len(sys.argv) != 3: 265*bb86c7edSAndroid Build Coastguard Worker print('Usage: bazel_to_cmake.py bazel_workspace_dir bazel_package_dir') 266*bb86c7edSAndroid Build Coastguard Worker sys.exit(1) 267*bb86c7edSAndroid Build Coastguard Worker 268*bb86c7edSAndroid Build Coastguard Worker bazel_workspace_dir = sys.argv[1] 269*bb86c7edSAndroid Build Coastguard Worker bazel_package_dir = sys.argv[2] 270*bb86c7edSAndroid Build Coastguard Worker bazel_package_relative_dir = os.path.relpath(bazel_package_dir, 271*bb86c7edSAndroid Build Coastguard Worker bazel_workspace_dir) 272*bb86c7edSAndroid Build Coastguard Worker package_prefix = bazel_package_relative_dir.replace(os.path.sep, '_') 273*bb86c7edSAndroid Build Coastguard Worker 274*bb86c7edSAndroid Build Coastguard Worker print("""# This file is generated (whence no license header). Do not edit! 275*bb86c7edSAndroid Build Coastguard Worker# To regenerate, run: 276*bb86c7edSAndroid Build Coastguard Worker# cmake/bazel_to_cmake.sh 277*bb86c7edSAndroid Build Coastguard Worker""") 278*bb86c7edSAndroid Build Coastguard Worker 279*bb86c7edSAndroid Build Coastguard Worker src_build_file = os.path.join(bazel_package_dir, 'BUILD') 280*bb86c7edSAndroid Build Coastguard Worker src_build_content = open(src_build_file).read() 281*bb86c7edSAndroid Build Coastguard Worker processed_build_content = preprocess_input_text(src_build_content) 282*bb86c7edSAndroid Build Coastguard Worker exec(processed_build_content) 283*bb86c7edSAndroid Build Coastguard Worker 284*bb86c7edSAndroid Build Coastguard Worker print('ruy_add_all_subdirs()') 285