1#!/usr/bin/python 2# 3# Protocol Buffers - Google's data interchange format 4# Copyright 2023 Google LLC. All rights reserved. 5# https://developers.google.com/protocol-buffers/ 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions are 9# met: 10# 11# * Redistributions of source code must retain the above copyright 12# notice, this list of conditions and the following disclaimer. 13# * Redistributions in binary form must reproduce the above 14# copyright notice, this list of conditions and the following disclaimer 15# in the documentation and/or other materials provided with the 16# distribution. 17# * Neither the name of Google LLC nor the names of its 18# contributors may be used to endorse or promote products derived from 19# this software without specific prior written permission. 20# 21# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 33"""A tool to convert BUILD -> CMakeLists.txt. 34 35This tool is very upb-specific at the moment, and should not be seen as a 36generic Bazel -> CMake converter. 37""" 38 39from __future__ import absolute_import 40from __future__ import division 41from __future__ import print_function 42 43import sys 44import textwrap 45import os 46 47def StripFirstChar(deps): 48 return [dep[1:] for dep in deps] 49 50def IsSourceFile(name): 51 return name.endswith(".c") or name.endswith(".cc") 52 53 54ADD_LIBRARY_FORMAT = """ 55add_library(%(name)s %(type)s 56 %(sources)s 57) 58target_include_directories(%(name)s %(keyword)s 59 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..> 60 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../cmake> 61 $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}> 62) 63""" 64 65 66class BuildFileFunctions(object): 67 def __init__(self, converter): 68 self.converter = converter 69 70 def _add_deps(self, kwargs, keyword=""): 71 if "deps" not in kwargs: 72 return 73 self.converter.toplevel += "target_link_libraries(%s%s\n %s)\n" % ( 74 kwargs["name"], 75 keyword, 76 "\n ".join(StripFirstChar(kwargs["deps"])) 77 ) 78 79 def load(self, *args): 80 pass 81 82 def cc_library(self, **kwargs): 83 if kwargs["name"].endswith("amalgamation"): 84 return 85 if kwargs["name"] == "upbc_generator": 86 return 87 if kwargs["name"] == "lupb": 88 return 89 if "testonly" in kwargs: 90 return 91 files = kwargs.get("srcs", []) + kwargs.get("hdrs", []) 92 found_files = [] 93 pregenerated_files = [ 94 "CMakeLists.txt", "descriptor.upb.h", "descriptor.upb.c" 95 ] 96 for file in files: 97 if os.path.basename(file) in pregenerated_files: 98 found_files.append("../cmake/" + file) 99 else: 100 found_files.append("../" + file) 101 102 if list(filter(IsSourceFile, files)): 103 # Has sources, make this a normal library. 104 self.converter.toplevel += ADD_LIBRARY_FORMAT % { 105 "name": kwargs["name"], 106 "type": "", 107 "keyword": "PUBLIC", 108 "sources": "\n ".join(found_files), 109 } 110 self._add_deps(kwargs) 111 else: 112 # Header-only library, have to do a couple things differently. 113 # For some info, see: 114 # http://mariobadr.com/creating-a-header-only-library-with-cmake.html 115 self.converter.toplevel += ADD_LIBRARY_FORMAT % { 116 "name": kwargs["name"], 117 "type": "INTERFACE", 118 "keyword": "INTERFACE", 119 "sources": "", 120 } 121 self._add_deps(kwargs, " INTERFACE") 122 123 def cc_binary(self, **kwargs): 124 pass 125 126 def cc_test(self, **kwargs): 127 # Disable this until we properly support upb_proto_library(). 128 # self.converter.toplevel += "add_executable(%s\n %s)\n" % ( 129 # kwargs["name"], 130 # "\n ".join(kwargs["srcs"]) 131 # ) 132 # self.converter.toplevel += "add_test(NAME %s COMMAND %s)\n" % ( 133 # kwargs["name"], 134 # kwargs["name"], 135 # ) 136 137 # if "data" in kwargs: 138 # for data_dep in kwargs["data"]: 139 # self.converter.toplevel += textwrap.dedent("""\ 140 # add_custom_command( 141 # TARGET %s POST_BUILD 142 # COMMAND ${CMAKE_COMMAND} -E copy 143 # ${CMAKE_SOURCE_DIR}/%s 144 # ${CMAKE_CURRENT_BINARY_DIR}/%s)\n""" % ( 145 # kwargs["name"], data_dep, data_dep 146 # )) 147 148 # self._add_deps(kwargs) 149 pass 150 151 def cc_fuzz_test(self, **kwargs): 152 pass 153 154 def pkg_files(self, **kwargs): 155 pass 156 157 def py_library(self, **kwargs): 158 pass 159 160 def py_binary(self, **kwargs): 161 pass 162 163 def lua_proto_library(self, **kwargs): 164 pass 165 166 def sh_test(self, **kwargs): 167 pass 168 169 def make_shell_script(self, **kwargs): 170 pass 171 172 def exports_files(self, files, **kwargs): 173 pass 174 175 def proto_library(self, **kwargs): 176 pass 177 178 def cc_proto_library(self, **kwargs): 179 pass 180 181 def staleness_test(self, **kwargs): 182 pass 183 184 def upb_amalgamation(self, **kwargs): 185 pass 186 187 def upb_proto_library(self, **kwargs): 188 pass 189 190 def upb_minitable_proto_library(self, **kwargs): 191 pass 192 193 def upb_proto_library_copts(self, **kwargs): 194 pass 195 196 def upb_proto_reflection_library(self, **kwargs): 197 pass 198 199 def upb_proto_srcs(self, **kwargs): 200 pass 201 202 def genrule(self, **kwargs): 203 pass 204 205 def config_setting(self, **kwargs): 206 pass 207 208 def upb_fasttable_enabled(self, **kwargs): 209 pass 210 211 def select(self, arg_dict): 212 return [] 213 214 def glob(self, *args, **kwargs): 215 return [] 216 217 def licenses(self, *args): 218 pass 219 220 def filegroup(self, **kwargs): 221 pass 222 223 def map_dep(self, arg): 224 return arg 225 226 def package_group(self, **kwargs): 227 pass 228 229 def bool_flag(self, **kwargs): 230 pass 231 232 def bootstrap_upb_proto_library(self, **kwargs): 233 pass 234 235 def bootstrap_cc_library(self, **kwargs): 236 pass 237 238 def alias(self, **kwargs): 239 pass 240 241 def package(self, **kwargs): 242 pass 243 244class Converter(object): 245 def __init__(self): 246 self.toplevel = "" 247 self.if_lua = "" 248 249 def convert(self): 250 return self.template % { 251 "toplevel": converter.toplevel, 252 } 253 254 template = textwrap.dedent("""\ 255 # This file was generated from BUILD using tools/make_cmakelists.py. 256 257 cmake_minimum_required(VERSION 3.10...3.24) 258 259 project(upb) 260 set(CMAKE_C_STANDARD 99) 261 262 # Prevent CMake from setting -rdynamic on Linux (!!). 263 SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") 264 SET(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "") 265 266 # Set default build type. 267 if(NOT CMAKE_BUILD_TYPE) 268 message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.") 269 set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING 270 "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." 271 FORCE) 272 endif() 273 274 # When using Ninja, compiler output won't be colorized without this. 275 include(CheckCXXCompilerFlag) 276 CHECK_CXX_COMPILER_FLAG(-fdiagnostics-color=always SUPPORTS_COLOR_ALWAYS) 277 if(SUPPORTS_COLOR_ALWAYS) 278 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") 279 endif() 280 281 # Implement ASAN/UBSAN options 282 if(UPB_ENABLE_ASAN) 283 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") 284 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") 285 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") 286 set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address") 287 endif() 288 289 if(UPB_ENABLE_UBSAN) 290 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") 291 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") 292 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address") 293 set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address") 294 endif() 295 296 if(NOT TARGET utf8_range) 297 if(EXISTS ../../third_party/utf8_range) 298 # utf8_range is already installed 299 include_directories(../../third_party/utf8_range) 300 else() 301 include(FetchContent) 302 FetchContent_Declare( 303 utf8_range 304 GIT_REPOSITORY "https://github.com/protocolbuffers/utf8_range.git" 305 GIT_TAG "d863bc33e15cba6d873c878dcca9e6fe52b2f8cb" 306 ) 307 FetchContent_GetProperties(utf8_range) 308 if(NOT utf8_range_POPULATED) 309 FetchContent_Populate(utf8_range) 310 include_directories(${utf8_range_SOURCE_DIR}) 311 endif() 312 endif() 313 endif() 314 315 if(APPLE) 316 set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -undefined dynamic_lookup -flat_namespace") 317 elseif(UNIX) 318 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--build-id") 319 endif() 320 321 enable_testing() 322 323 %(toplevel)s 324 325 """) 326 327data = {} 328converter = Converter() 329 330def GetDict(obj): 331 ret = {} 332 ret["UPB_DEFAULT_COPTS"] = [] # HACK 333 ret["UPB_DEFAULT_CPPOPTS"] = [] # HACK 334 for k in dir(obj): 335 if not k.startswith("_"): 336 ret[k] = getattr(obj, k); 337 return ret 338 339globs = GetDict(converter) 340 341# We take the BUILD path as a command-line argument to ensure that we can find 342# it regardless of how exactly Bazel was invoked. 343exec(open(sys.argv[1]).read(), GetDict(BuildFileFunctions(converter))) # BUILD 344 345with open(sys.argv[2], "w") as f: 346 f.write(converter.convert()) 347