PageRenderTime 53ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/deps/v8/tools/cppgc/gen_cmake.py

https://github.com/isaacs/node
Python | 518 lines | 510 code | 2 blank | 6 comment | 7 complexity | 8bef4c38b4e474463f40979fde6c1421 MD5 | raw file
  1. #!/usr/bin/env python3
  2. #
  3. # Copyright 2020 the V8 project authors. All rights reserved.
  4. # Use of this source code is governed by a BSD-style license that can be
  5. # found in the LICENSE file.
  6. import sys
  7. import lark
  8. import argparse
  9. from contextlib import suppress
  10. from collections import namedtuple
  11. from datetime import datetime
  12. # GN grammar from https://gn.googlesource.com/gn/+/master/src/gn/parser.cc.
  13. GN_GRAMMAR = """
  14. ?file : statement_list
  15. ?statement : assignment | call | condition
  16. ?lvalue : IDENTIFIER | array_access | scope_access
  17. assignment : lvalue assign_op expr
  18. call : IDENTIFIER "(" [ expr_list ] ")" [ block ]
  19. condition : "if" "(" expr ")" block [ "else" ( condition | block ) ]
  20. ?block : "{" statement_list "}"
  21. statement_list : statement*
  22. array_access : IDENTIFIER "[" expr "]"
  23. scope_access : IDENTIFIER "." IDENTIFIER
  24. ?primary_expr : IDENTIFIER | INTEGER | STRING | call
  25. | array_access | scope_access | block
  26. | "(" expr ")" -> par_expr
  27. | array
  28. array : "[" [ expr ( "," expr? )* ] "]"
  29. expr_list : expr ( "," expr )*
  30. ?assign_op : "=" -> asgn_op
  31. | "+=" -> asgn_add_op
  32. | "-=" -> asgn_sub_op
  33. ?expr : expr1
  34. ?expr1 : expr1 "||" expr2 -> or
  35. | expr2
  36. ?expr2 : expr2 "&&" expr3 -> and
  37. | expr3
  38. ?expr3 : expr3 "==" expr4 -> eq
  39. | expr3 "!=" expr4 -> ne
  40. | expr4
  41. ?expr4 : expr4 "<=" expr5 -> le
  42. | expr4 "<" expr5 -> lt
  43. | expr4 ">=" expr5 -> ge
  44. | expr4 ">" expr5 -> gt
  45. | expr5
  46. ?expr5 : expr5 "+" expr6 -> add
  47. | expr5 "-" expr6 -> sub
  48. | expr6
  49. ?expr6 : "!" primary_expr -> neg
  50. | primary_expr
  51. COMMENT : /#.*/
  52. %import common.ESCAPED_STRING -> STRING
  53. %import common.SIGNED_INT -> INTEGER
  54. %import common.CNAME -> IDENTIFIER
  55. %import common.WS
  56. %ignore WS
  57. %ignore COMMENT
  58. """
  59. V8_TARGET_TYPES = (
  60. 'v8_component',
  61. 'v8_source_set',
  62. 'v8_executable',
  63. )
  64. OPS = (
  65. 'neg',
  66. 'eq',
  67. 'ne',
  68. 'le',
  69. 'lt',
  70. 'ge',
  71. 'gt',
  72. 'and',
  73. 'or',
  74. )
  75. class UnsupportedOperation(Exception):
  76. pass
  77. class V8GNTransformer(object):
  78. """
  79. Traverse GN parse-tree and build resulting object.
  80. """
  81. def __init__(self, builder, filtered_targets):
  82. self.builder = builder
  83. self.filtered_targets = filtered_targets
  84. self.current_target = None
  85. def Traverse(self, tree):
  86. self.builder.BuildPrologue()
  87. self.TraverseTargets(tree)
  88. self.builder.BuildEpilogue()
  89. def TraverseTargets(self, tree):
  90. 'Traverse top level GN targets and call the builder functions'
  91. for stmt in tree.children:
  92. if stmt.data != 'call':
  93. continue
  94. target_type = stmt.children[0]
  95. if target_type not in V8_TARGET_TYPES:
  96. continue
  97. target = stmt.children[1].children[0].strip('\"')
  98. if target not in self.filtered_targets:
  99. continue
  100. self.current_target = target
  101. self._Target(target_type, target, stmt.children[2].children)
  102. def _Target(self, target_type, target, stmts):
  103. stmts = self._StatementList(stmts)
  104. return self.builder.BuildTarget(target_type, target, stmts)
  105. def _StatementList(self, stmts):
  106. built_stmts = []
  107. for stmt in stmts:
  108. built_stmts.append(self._Statement(stmt))
  109. return [stmt for stmt in built_stmts if stmt]
  110. def _Statement(self, stmt):
  111. # Handle only interesting gn statements.
  112. with suppress(KeyError):
  113. return self.STATEMENTS[stmt.data](self, *stmt.children)
  114. def _Assignment(self, left, op, right):
  115. return self.ASSIGN_TYPES[op.data](self, left, right)
  116. def _AssignEq(self, left, right):
  117. if left == 'sources':
  118. return self.builder.BuildSourcesList(
  119. self.current_target, [str(token) for token in right.children])
  120. def _AssignAdd(self, left, right):
  121. if left == 'sources':
  122. return self.builder.BuildAppendSources(
  123. self.current_target, [str(token) for token in right.children])
  124. def _AssignSub(self, left, right):
  125. if left == 'sources':
  126. return self.builder.BuildRemoveSources(
  127. self.current_target, [str(token) for token in right.children])
  128. def _Condition(self, cond_expr, then_stmts, else_stmts=None):
  129. 'Visit GN condition: if (cond) {then_stmts} else {else_stmts}'
  130. cond_expr = self._Expr(cond_expr)
  131. then_stmts = self._StatementList(then_stmts.children)
  132. if not then_stmts:
  133. # Ignore conditions with empty then stmts.
  134. return
  135. if else_stmts is None:
  136. return self.builder.BuildCondition(cond_expr, then_stmts)
  137. elif else_stmts.data == 'condition':
  138. else_cond = self._Condition(*else_stmts.children)
  139. return self.builder.BuildConditionWithElseCond(
  140. cond_expr, then_stmts, else_cond)
  141. else:
  142. assert 'statement_list' == else_stmts.data
  143. else_stmts = self._StatementList(else_stmts.children)
  144. return self.builder.BuildConditionWithElseStmts(
  145. cond_expr, then_stmts, else_stmts)
  146. def _Expr(self, expr):
  147. 'Post-order traverse expression trees'
  148. if isinstance(expr, lark.Token):
  149. if expr.type == 'IDENTIFIER':
  150. return self.builder.BuildIdentifier(str(expr))
  151. elif expr.type == 'INTEGER':
  152. return self.builder.BuildInteger(str(expr))
  153. else:
  154. return self.builder.BuildString(str(expr))
  155. if expr.data == 'par_expr':
  156. return self.builder.BuildParenthesizedOperation(
  157. self._Expr(*expr.children))
  158. if expr.data not in OPS:
  159. raise UnsupportedOperation(
  160. f'The operator "{expr.data}" is not supported')
  161. if len(expr.children) == 1:
  162. return self._UnaryExpr(expr.data, *expr.children)
  163. if len(expr.children) == 2:
  164. return self._BinaryExpr(expr.data, *expr.children)
  165. raise UnsupportedOperation(f'Unsupported arity {len(expr.children)}')
  166. def _UnaryExpr(self, op, right):
  167. right = self._Expr(right)
  168. return self.builder.BuildUnaryOperation(op, right)
  169. def _BinaryExpr(self, op, left, right):
  170. left = self._Expr(left)
  171. right = self._Expr(right)
  172. return self.builder.BuildBinaryOperation(left, op, right)
  173. STATEMENTS = {
  174. 'assignment': _Assignment,
  175. 'condition': _Condition,
  176. }
  177. ASSIGN_TYPES = {
  178. 'asgn_op': _AssignEq,
  179. 'asgn_add_op': _AssignAdd,
  180. 'asgn_sub_op': _AssignSub,
  181. }
  182. TARGETS = {
  183. 'v8_libbase': 'lib',
  184. 'v8_cppgc_shared': 'lib',
  185. 'cppgc_base': 'lib',
  186. 'cppgc_standalone': 'sample',
  187. 'cppgc_unittests_sources': 'tests',
  188. 'cppgc_unittests': 'tests',
  189. }
  190. class CMakeBuilder(object):
  191. """
  192. Builder that produces the main CMakeLists.txt.
  193. """
  194. def __init__(self):
  195. self.result = []
  196. self.source_sets = {}
  197. def BuildPrologue(self):
  198. self.result.append(f"""
  199. # Copyright {datetime.now().year} the V8 project authors. All rights reserved.
  200. # Use of this source code is governed by a BSD-style license that can be
  201. # found in the LICENSE file.
  202. #
  203. # This file is automatically generated by {__file__}. Do NOT edit it.
  204. cmake_minimum_required(VERSION 3.11)
  205. project(cppgc CXX)
  206. set(CMAKE_CXX_STANDARD 14)
  207. set(CMAKE_CXX_STANDARD_REQUIRED ON)
  208. option(CPPGC_ENABLE_OBJECT_NAMES "Enable object names in cppgc for debug purposes" OFF)
  209. option(CPPGC_ENABLE_CAGED_HEAP "Enable heap reservation of size 4GB, only possible for 64bit archs" OFF)
  210. option(CPPGC_ENABLE_YOUNG_GENERATION "Enable young generation in cppgc" OFF)
  211. set(CPPGC_TARGET_ARCH "x64" CACHE STRING "Target architecture, possible options: x64, x86, arm, arm64, ppc64, s390x, mipsel, mips64el")
  212. set(IS_POSIX ${{UNIX}})
  213. set(IS_MAC ${{APPLE}})
  214. set(IS_WIN ${{WIN32}})
  215. if("${{CMAKE_SYSTEM_NAME}}" STREQUAL "Linux")
  216. set(IS_LINUX 1)
  217. elseif("${{CMAKE_SYSTEM_NAME}}" STREQUAL "Fuchsia")
  218. set(IS_FUCHSIA 1)
  219. endif()
  220. set(CURRENT_CPU ${{CPPGC_TARGET_ARCH}})
  221. if("${{CPPGC_TARGET_ARCH}}" STREQUAL "x64" OR
  222. "${{CPPGC_TARGET_ARCH}}" STREQUAL "arm64" OR
  223. "${{CPPGC_TARGET_ARCH}}" STREQUAL "ppc64" OR
  224. "${{CPPGC_TARGET_ARCH}}" STREQUAL "mips64el")
  225. if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
  226. message(FATAL_ERROR "64-bit arch specified for 32-bit compiler")
  227. endif()
  228. set(CPPGC_64_BITS ON)
  229. endif()
  230. if(CPPGC_ENABLE_CAGED_HEAP AND NOT CPPGC_64_BITS)
  231. message(FATAL_ERROR "Caged heap is only supported for 64bit archs")
  232. endif()
  233. if(CPPGC_64_BITS)
  234. # Always enable caged heap for 64bits archs.
  235. set(CPPGC_ENABLE_CAGED_HEAP ON CACHE BOOL "Enable caged heap for 64bit" FORCE)
  236. endif()
  237. if(CPPGC_ENABLE_YOUNG_GENERATION AND NOT CPPGC_ENABLE_CAGED_HEAP)
  238. message(FATAL_ERROR "Young generation is only supported for caged heap configuration")
  239. endif()
  240. if(NOT CPPGC_64_BITS)
  241. if(NOT MSVC)
  242. set(CMAKE_CXX_FLAGS "${{CMAKE_CXX_FLAGS}} -m32")
  243. set(CMAKE_C_FLAGS "${{CMAKE_C_FLAGS}} -m32")
  244. set(CMAKE_EXE_LINKER_FLAGS "${{CMAKE_EXE_LINKER_FLAGS}} -m32")
  245. set(CMAKE_SHARED_LINKER_FLAGS "${{CMAKE_SHARED_LINKER_FLAGS}} -m32")
  246. set(CMAKE_MODULE_LINKER_FLAGS "${{CMAKE_MODULE_LINKER_FLAGS}} -m32")
  247. endif()
  248. endif()
  249. find_package(Threads REQUIRED)
  250. include(FetchContent)
  251. FetchContent_Declare(
  252. googletest
  253. GIT_REPOSITORY "https://chromium.googlesource.com/external/github.com/google/googletest.git"
  254. GIT_TAG "4fe018038f87675c083d0cfb6a6b57c274fb1753"
  255. SOURCE_DIR "${{CMAKE_BINARY_DIR}}/third_party/googletest/src"
  256. )
  257. FetchContent_GetProperties(googletest)
  258. if(NOT googletest_POPULATED)
  259. FetchContent_Populate(googletest)
  260. message("Fetched googletest into ${{googletest_SOURCE_DIR}}")
  261. add_subdirectory(${{googletest_SOURCE_DIR}} ${{googletest_BINARY_DIR}} EXCLUDE_FROM_ALL)
  262. include_directories("${{CMAKE_BINARY_DIR}}")
  263. endif()
  264. """)
  265. def BuildEpilogue(self):
  266. self.result.extend(
  267. self._GenTargetString(target, sets)
  268. for target, sets in self.source_sets.items())
  269. self.result.append("\ninstall(TARGETS cppgc)")
  270. def BuildTarget(self, target_type, target, rules):
  271. # Don't generate CMake targets yet, defer it to build_epilogue.
  272. comment = f"""
  273. #===============================================================================
  274. # {self._CMakeTarget(target)} sources.
  275. #==============================================================================="""
  276. self.result.append(comment)
  277. self.result.extend(rules)
  278. self.source_sets.setdefault(
  279. TARGETS[target], []).append('${' + self._SourceVar(target) + '}')
  280. def BuildSourcesList(self, target, sources):
  281. sources = self._ExpandSources(target, sources)
  282. return f'set({self._SourceVar(target)} {sources})'
  283. def BuildAppendSources(self, target, sources):
  284. sources = self._ExpandSources(target, sources)
  285. return f'list(APPEND {self._SourceVar(target)} {sources})'
  286. def BuildRemoveSources(self, target, sources):
  287. sources = self._ExpandSources(target, sources)
  288. return f'list(REMOVE_ITEM {self._SourceVar(target)} {sources})'
  289. def BuildCondition(self, cond, then_stmts):
  290. return f"""
  291. if({cond})
  292. {' '.join(then_stmts)}
  293. endif()
  294. """.strip()
  295. def BuildConditionWithElseStmts(self, cond, then_stmts, else_stmts):
  296. return f"""
  297. if({cond})
  298. {' '.join(then_stmts)}
  299. {'else() ' + ' '.join(else_stmts)}
  300. endif()
  301. """.strip()
  302. def BuildConditionWithElseCond(self, cond, then_stmts, else_cond):
  303. return f"""
  304. if({cond})
  305. {' '.join(then_stmts)}
  306. else{else_cond}
  307. """.strip()
  308. def BuildParenthesizedOperation(self, operation):
  309. return ''.join(['(', operation, ')'])
  310. def BuildUnaryOperation(self, op, right):
  311. OPS = {
  312. 'neg': 'NOT',
  313. }
  314. return ' '.join([OPS[op], right])
  315. def BuildBinaryOperation(self, left, op, right):
  316. if op == 'ne':
  317. neg_result = self.BuildBinaryOperation(left, 'eq', right)
  318. return self.BuildUnaryOperation('neg', neg_result)
  319. OPS = {
  320. 'eq': 'STREQUAL',
  321. 'le': 'LESS_EQUAL',
  322. 'lt': 'LESS',
  323. 'ge': 'GREATER_EQUAL',
  324. 'gt': 'GREATER',
  325. 'and': 'AND',
  326. 'or': 'OR',
  327. }
  328. return ' '.join([left, OPS[op], right])
  329. def BuildIdentifier(self, token):
  330. return self._CMakeVarRef(token)
  331. def BuildInteger(self, integer):
  332. return integer
  333. def BuildString(self, string):
  334. return string
  335. def GetResult(self):
  336. return '\n'.join(self.result)
  337. @staticmethod
  338. def _GenTargetString(target_type, source_sets):
  339. Target = namedtuple('Target', 'name cmake deps desc')
  340. CMAKE_TARGETS = {
  341. 'lib':
  342. Target(name='cppgc',
  343. cmake='add_library',
  344. deps=['Threads::Threads'],
  345. desc='Main library'),
  346. 'sample':
  347. Target(name='cppgc_sample',
  348. cmake='add_executable',
  349. deps=['cppgc'],
  350. desc='Example'),
  351. 'tests':
  352. Target(name='cppgc_unittests',
  353. cmake='add_executable',
  354. deps=['cppgc', 'gtest', 'gmock'],
  355. desc='Unittests')
  356. }
  357. target = CMAKE_TARGETS[target_type]
  358. return f"""
  359. # {target.desc} target.
  360. {target.cmake}({target.name} {' '.join(source_sets)})
  361. {'target_link_libraries(' + target.name + ' ' + ' '.join(target.deps) + ')' if target.deps else ''}
  362. target_include_directories({target.name} PRIVATE "${{CMAKE_SOURCE_DIR}}"
  363. PRIVATE "${{CMAKE_SOURCE_DIR}}/include")
  364. if(CPPGC_ENABLE_OBJECT_NAMES)
  365. target_compile_definitions({target.name} PRIVATE "-DCPPGC_SUPPORTS_OBJECT_NAMES")
  366. endif()
  367. if(CPPGC_ENABLE_CAGED_HEAP)
  368. target_compile_definitions({target.name} PRIVATE "-DCPPGC_CAGED_HEAP")
  369. endif()
  370. if(CPPGC_ENABLE_YOUNG_GENERATION)
  371. target_compile_definitions({target.name} PRIVATE "-DCPPGC_YOUNG_GENERATION")
  372. endif()"""
  373. @staticmethod
  374. def _ExpandSources(target, sources):
  375. if TARGETS[target] == 'tests':
  376. sources = ['\"test/unittests/' + s[1:] for s in sources]
  377. return ' '.join(sources)
  378. @staticmethod
  379. def _SourceVar(target):
  380. return CMakeBuilder._CMakeVar(target) + '_SOURCES'
  381. @staticmethod
  382. def _CMakeVar(var):
  383. return var.replace('v8_', '').upper()
  384. @staticmethod
  385. def _CMakeTarget(var):
  386. return var.replace('v8_', '')
  387. @staticmethod
  388. def _CMakeVarRef(var):
  389. return '\"${' + CMakeBuilder._CMakeVar(var) + '}"'
  390. def FormatCMake(contents):
  391. from cmake_format import configuration, lexer, parse, formatter
  392. cfg = configuration.Configuration()
  393. tokens = lexer.tokenize(contents)
  394. parse_tree = parse.parse(tokens)
  395. box_tree = formatter.layout_tree(parse_tree, cfg)
  396. return formatter.write_tree(box_tree, cfg, contents)
  397. def SaveContents(contents, outfile):
  398. if outfile == '-':
  399. return print(contents)
  400. with open(outfile, 'w+') as ofile:
  401. ofile.write(contents)
  402. def ParseGN(contents):
  403. parser = lark.Lark(GN_GRAMMAR, parser='lalr', start='file')
  404. return parser.parse(contents)
  405. def ParseGNFile(filename):
  406. with open(filename, 'r') as file:
  407. contents = file.read()
  408. return ParseGN(contents)
  409. def GenCMake(main_gn, test_gn, outfile):
  410. tree = ParseGNFile(main_gn)
  411. tree.children.extend(ParseGNFile(test_gn).children)
  412. builder = CMakeBuilder()
  413. V8GNTransformer(builder, TARGETS.keys()).Traverse(tree)
  414. result = FormatCMake(builder.GetResult())
  415. SaveContents(result, outfile)
  416. def Main():
  417. arg_parser = argparse.ArgumentParser(
  418. description=
  419. 'Generate CMake from the main GN file for targets needed to build CppGC.'
  420. )
  421. arg_parser.add_argument('--out', help='output CMake filename', default='-')
  422. arg_parser.add_argument('--main-gn',
  423. help='main BUILD.gn input file',
  424. default='BUILD.gn')
  425. arg_parser.add_argument('--test-gn',
  426. help='unittest BUILD.gn input file',
  427. default='test/unittests/BUILD.gn')
  428. args = arg_parser.parse_args()
  429. GenCMake(args.main_gn, args.test_gn, args.out)
  430. return 0
  431. if __name__ == '__main__':
  432. sys.exit(Main())