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