Flutter Engine
The Flutter Engine
gn_to_cmake.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2#
3# Copyright 2016 Google Inc.
4#
5# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
8
9"""
10Usage: gn_to_cmake.py <json_file_name>
11
12gn gen out/config --ide=json --json-ide-script=../../gn/gn_to_cmake.py
13
14or
15
16gn gen out/config --ide=json
17python3 gn/gn_to_cmake.py out/config/project.json
18
19The first is recommended, as it will auto-update.
20"""
21
22
23import itertools
24import functools
25import json
26import posixpath
27import os
28import string
29import sys
30
31
33 """Escapes the string 'a' for use inside a CMake string.
34
35 This means escaping
36 '\' otherwise it may be seen as modifying the next character
37 '"' otherwise it will end the string
38 ';' otherwise the string becomes a list
39
40 The following do not need to be escaped
41 '#' when the lexer is in string state, this does not start a comment
42 """
43 return a.replace('\\', '\\\\').replace(';', '\\;').replace('"', '\\"')
44
45
47 """Escapes the string 'a' for use as a CMake target name.
48
49 CMP0037 in CMake 3.0 restricts target names to "^[A-Za-z0-9_.:+-]+$"
50 The ':' is only allowed for imported targets.
51 """
52 def Escape(c):
53 if c in string.ascii_letters or c in string.digits or c in '_.+-':
54 return c
55 else:
56 return '__'
57 return ''.join(map(Escape, a))
58
59
60def SetVariable(out, variable_name, value):
61 """Sets a CMake variable."""
62 out.write('set("')
63 out.write(CMakeStringEscape(variable_name))
64 out.write('" "')
65 out.write(CMakeStringEscape(value))
66 out.write('")\n')
67
68
69def SetVariableList(out, variable_name, values):
70 """Sets a CMake variable to a list."""
71 if not values:
72 return SetVariable(out, variable_name, "")
73 if len(values) == 1:
74 return SetVariable(out, variable_name, values[0])
75 out.write('list(APPEND "')
76 out.write(CMakeStringEscape(variable_name))
77 out.write('"\n "')
78 out.write('"\n "'.join([CMakeStringEscape(value) for value in values]))
79 out.write('")\n')
80
81
82def SetFilesProperty(output, variable, property_name, values, sep):
83 """Given a set of source files, sets the given property on them."""
84 output.write('set_source_files_properties(')
85 WriteVariable(output, variable)
86 output.write(' PROPERTIES ')
87 output.write(property_name)
88 output.write(' "')
89 for value in values:
90 output.write(CMakeStringEscape(value))
91 output.write(sep)
92 output.write('")\n')
93
94
95def SetCurrentTargetProperty(out, property_name, values, sep=''):
96 """Given a target, sets the given property."""
97 out.write('set_target_properties("${target}" PROPERTIES ')
98 out.write(property_name)
99 out.write(' "')
100 for value in values:
101 out.write(CMakeStringEscape(value))
102 out.write(sep)
103 out.write('")\n')
104
105
106def WriteVariable(output, variable_name, prepend=None):
107 if prepend:
108 output.write(prepend)
109 output.write('${')
110 output.write(variable_name)
111 output.write('}')
112
113
114# See GetSourceFileType in gn
115source_file_types = {
116 '.cc': 'cxx',
117 '.cpp': 'cxx',
118 '.cxx': 'cxx',
119 '.m': 'objc',
120 '.mm': 'objcc',
121 '.c': 'c',
122 '.s': 'asm',
123 '.S': 'asm',
124 '.asm': 'asm',
125 '.o': 'obj',
126 '.obj': 'obj',
127}
128
129
130class CMakeTargetType(object):
131 def __init__(self, command, modifier, property_modifier, is_linkable):
132 self.command = command
133 self.modifier = modifier
134 self.property_modifier = property_modifier
135 self.is_linkable = is_linkable
136CMakeTargetType.custom = CMakeTargetType('add_custom_target', 'SOURCES',
137 None, False)
138
139# See GetStringForOutputType in gn
140cmake_target_types = {
141 'unknown': CMakeTargetType.custom,
142 'group': CMakeTargetType('add_library', 'INTERFACE', None, True),
143 'executable': CMakeTargetType('add_executable', None, 'RUNTIME', True),
144 'loadable_module': CMakeTargetType('add_library', 'MODULE', 'LIBRARY', True),
145 'shared_library': CMakeTargetType('add_library', 'SHARED', 'LIBRARY', True),
146 'static_library': CMakeTargetType('add_library', 'STATIC', 'ARCHIVE', True),
147 'source_set': CMakeTargetType('add_library', 'OBJECT', None, False),
148 'copy': CMakeTargetType.custom,
149 'action': CMakeTargetType.custom,
150 'action_foreach': CMakeTargetType.custom,
151 'bundle_data': CMakeTargetType.custom,
152 'create_bundle': CMakeTargetType.custom,
153}
154
155
156def FindFirstOf(s, a):
157 return min(s.find(i) for i in a if i in s)
158
159
160class Project(object):
161 def __init__(self, project_json):
162 self.targets = project_json['targets']
163 build_settings = project_json['build_settings']
164 self.root_path = build_settings['root_path']
165 self.build_path = self.GetAbsolutePath(build_settings['build_dir'])
166
167 def GetAbsolutePath(self, path):
168 if path.startswith('//'):
169 return posixpath.join(self.root_path, path[2:])
170 else:
171 return path
172
173 def GetObjectSourceDependencies(self, gn_target_name, object_dependencies):
174 """All OBJECT libraries whose sources have not been absorbed."""
175 dependencies = self.targets[gn_target_name].get('deps', [])
176 for dependency in dependencies:
177 dependency_type = self.targets[dependency].get('type', None)
178 if dependency_type == 'source_set':
179 object_dependencies.add(dependency)
180 if dependency_type not in gn_target_types_that_absorb_objects:
181 self.GetObjectSourceDependencies(dependency, object_dependencies)
182
183 def GetObjectLibraryDependencies(self, gn_target_name, object_dependencies):
184 """All OBJECT libraries whose libraries have not been absorbed."""
185 dependencies = self.targets[gn_target_name].get('deps', [])
186 for dependency in dependencies:
187 dependency_type = self.targets[dependency].get('type', None)
188 if dependency_type == 'source_set':
189 object_dependencies.add(dependency)
190 self.GetObjectLibraryDependencies(dependency, object_dependencies)
191
192 def GetCMakeTargetName(self, gn_target_name):
193 # See <chromium>/src/tools/gn/label.cc#Resolve
194 # //base/test:test_support(//build/toolchain/win:msvc)
195 path_separator = FindFirstOf(gn_target_name, (':', '('))
196 location = None
197 name = None
198 toolchain = None
199 if not path_separator:
200 location = gn_target_name[2:]
201 else:
202 location = gn_target_name[2:path_separator]
203 toolchain_separator = gn_target_name.find('(', path_separator)
204 if toolchain_separator == -1:
205 name = gn_target_name[path_separator + 1:]
206 else:
207 if toolchain_separator > path_separator:
208 name = gn_target_name[path_separator + 1:toolchain_separator]
209 assert gn_target_name.endswith(')')
210 toolchain = gn_target_name[toolchain_separator + 1:-1]
211 assert location or name
212
213 cmake_target_name = None
214 if location.endswith('/' + name):
215 cmake_target_name = location
216 elif location:
217 cmake_target_name = location + '_' + name
218 else:
219 cmake_target_name = name
220 if toolchain:
221 cmake_target_name += '--' + toolchain
222 return CMakeTargetEscape(cmake_target_name)
223
224
225class Target(object):
226 def __init__(self, gn_target_name, project):
227 self.gn_name = gn_target_name
228 self.properties = project.targets[self.gn_name]
229 self.cmake_name = project.GetCMakeTargetName(self.gn_name)
230 self.gn_type = self.properties.get('type', None)
231 self.cmake_type = cmake_target_types.get(self.gn_type, None)
232
233
234def WriteAction(out, target, project, sources, synthetic_dependencies):
235 outputs = []
236 output_directories = set()
237 for output in target.properties.get('outputs', []):
238 output_abs_path = project.GetAbsolutePath(output)
239 outputs.append(output_abs_path)
240 output_directory = posixpath.dirname(output_abs_path)
241 if output_directory:
242 output_directories.add(output_directory)
243 outputs_name = '${target}__output'
244 SetVariableList(out, outputs_name, outputs)
245
246 out.write('add_custom_command(OUTPUT ')
247 WriteVariable(out, outputs_name)
248 out.write('\n')
249
250 if output_directories:
251 out.write(' COMMAND ${CMAKE_COMMAND} -E make_directory "')
252 out.write('" "'.join(map(CMakeStringEscape, output_directories)))
253 out.write('"\n')
254
255 script = target.properties['script']
256 arguments = target.properties['args']
257 out.write(' COMMAND python3 "')
258 out.write(CMakeStringEscape(project.GetAbsolutePath(script)))
259 out.write('"')
260 if arguments:
261 out.write('\n "')
262 out.write('"\n "'.join(map(CMakeStringEscape, arguments)))
263 out.write('"')
264 out.write('\n')
265
266 out.write(' DEPENDS ')
267 for sources_type_name in sources.values():
268 WriteVariable(out, sources_type_name, ' ')
269 out.write('\n')
270
271 #TODO: CMake 3.7 is introducing DEPFILE
272
273 out.write(' WORKING_DIRECTORY "')
274 out.write(CMakeStringEscape(project.build_path))
275 out.write('"\n')
276
277 out.write(' COMMENT "Action: ${target}"\n')
278
279 out.write(' VERBATIM)\n')
280
281 synthetic_dependencies.add(outputs_name)
282
283
284def ExpandPlaceholders(source, a):
285 source_dir, source_file_part = posixpath.split(source)
286 source_name_part, _ = posixpath.splitext(source_file_part)
287 #TODO: {{source_gen_dir}}, {{source_out_dir}}, {{response_file_name}}
288 return a.replace('{{source}}', source) \
289 .replace('{{source_file_part}}', source_file_part) \
290 .replace('{{source_name_part}}', source_name_part) \
291 .replace('{{source_dir}}', source_dir) \
292 .replace('{{source_root_relative_dir}}', source_dir)
293
294
295def WriteActionForEach(out, target, project, sources, synthetic_dependencies):
296 all_outputs = target.properties.get('outputs', [])
297 inputs = target.properties.get('sources', [])
298 # TODO: consider expanding 'output_patterns' instead.
299 outputs_per_input = int(len(all_outputs) / len(inputs))
300 for count, source in enumerate(inputs):
301 source_abs_path = project.GetAbsolutePath(source)
302
303 outputs = []
304 output_directories = set()
305 for output in all_outputs[outputs_per_input * count:
306 outputs_per_input * (count+1)]:
307 output_abs_path = project.GetAbsolutePath(output)
308 outputs.append(output_abs_path)
309 output_directory = posixpath.dirname(output_abs_path)
310 if output_directory:
311 output_directories.add(output_directory)
312 outputs_name = '${target}__output_' + str(count)
313 SetVariableList(out, outputs_name, outputs)
314
315 out.write('add_custom_command(OUTPUT ')
316 WriteVariable(out, outputs_name)
317 out.write('\n')
318
319 if output_directories:
320 out.write(' COMMAND ${CMAKE_COMMAND} -E make_directory "')
321 out.write('" "'.join(map(CMakeStringEscape, output_directories)))
322 out.write('"\n')
323
324 script = target.properties['script']
325 # TODO: need to expand {{xxx}} in arguments
326 arguments = target.properties['args']
327 out.write(' COMMAND python3 "')
328 out.write(CMakeStringEscape(project.GetAbsolutePath(script)))
329 out.write('"')
330 if arguments:
331 out.write('\n "')
332 expand = functools.partial(ExpandPlaceholders, source_abs_path)
333 out.write('"\n "'.join(map(CMakeStringEscape, map(expand,arguments))))
334 out.write('"')
335 out.write('\n')
336
337 out.write(' DEPENDS')
338 if 'input' in sources:
339 WriteVariable(out, sources['input'], ' ')
340 out.write(' "')
341 out.write(CMakeStringEscape(source_abs_path))
342 out.write('"\n')
343
344 #TODO: CMake 3.7 is introducing DEPFILE
345
346 out.write(' WORKING_DIRECTORY "')
347 out.write(CMakeStringEscape(project.build_path))
348 out.write('"\n')
349
350 out.write(' COMMENT "Action ${target} on ')
351 out.write(CMakeStringEscape(source_abs_path))
352 out.write('"\n')
353
354 out.write(' VERBATIM)\n')
355
356 synthetic_dependencies.add(outputs_name)
357
358
359def WriteCopy(out, target, project, sources, synthetic_dependencies):
360 inputs = target.properties.get('sources', [])
361 raw_outputs = target.properties.get('outputs', [])
362
363 # TODO: consider expanding 'output_patterns' instead.
364 outputs = []
365 for output in raw_outputs:
366 output_abs_path = project.GetAbsolutePath(output)
367 outputs.append(output_abs_path)
368 outputs_name = '${target}__output'
369 SetVariableList(out, outputs_name, outputs)
370
371 out.write('add_custom_command(OUTPUT ')
372 WriteVariable(out, outputs_name)
373 out.write('\n')
374
375 for src, dst in zip(inputs, outputs):
376 abs_src_path = CMakeStringEscape(project.GetAbsolutePath(src))
377 # CMake distinguishes between copying files and copying directories but
378 # gn does not. We assume if the src has a period in its name then it is
379 # a file and otherwise a directory.
380 if "." in os.path.basename(abs_src_path):
381 out.write(' COMMAND ${CMAKE_COMMAND} -E copy "')
382 else:
383 out.write(' COMMAND ${CMAKE_COMMAND} -E copy_directory "')
384 out.write(abs_src_path)
385 out.write('" "')
386 out.write(CMakeStringEscape(dst))
387 out.write('"\n')
388
389 out.write(' DEPENDS ')
390 for sources_type_name in sources.values():
391 WriteVariable(out, sources_type_name, ' ')
392 out.write('\n')
393
394 out.write(' WORKING_DIRECTORY "')
395 out.write(CMakeStringEscape(project.build_path))
396 out.write('"\n')
397
398 out.write(' COMMENT "Copy ${target}"\n')
399
400 out.write(' VERBATIM)\n')
401
402 synthetic_dependencies.add(outputs_name)
403
404
405def WriteCompilerFlags(out, target, project, sources):
406 # Hack, set linker language to c if no c or cxx files present.
407 # However, cannot set LINKER_LANGUAGE on INTERFACE (with source files) until 3.19.
408 if not 'c' in sources and not 'cxx' in sources and not target.cmake_type.modifier == "INTERFACE":
409 SetCurrentTargetProperty(out, 'LINKER_LANGUAGE', ['C'])
410
411 # Mark uncompiled sources as uncompiled.
412 if 'input' in sources:
413 SetFilesProperty(out, sources['input'], 'HEADER_FILE_ONLY', ('True',), '')
414 if 'other' in sources:
415 SetFilesProperty(out, sources['other'], 'HEADER_FILE_ONLY', ('True',), '')
416
417 # Mark object sources as linkable.
418 if 'obj' in sources:
419 SetFilesProperty(out, sources['obj'], 'EXTERNAL_OBJECT', ('True',), '')
420
421 # TODO: 'output_name', 'output_dir', 'output_extension'
422 # This includes using 'source_outputs' to direct compiler output.
423
424 # Includes
425 includes = target.properties.get('include_dirs', [])
426 if includes:
427 out.write('set_property(TARGET "${target}" ')
428 out.write('APPEND PROPERTY INCLUDE_DIRECTORIES')
429 for include_dir in includes:
430 out.write('\n "')
431 out.write(project.GetAbsolutePath(include_dir))
432 out.write('"')
433 out.write(')\n')
434
435 # Defines
436 defines = target.properties.get('defines', [])
437 if defines:
438 SetCurrentTargetProperty(out, 'COMPILE_DEFINITIONS', defines, ';')
439
440 # Compile flags
441 # "arflags", "asmflags", "cflags",
442 # "cflags_c", "clfags_cc", "cflags_objc", "clfags_objcc"
443 # CMake does not have per target lang compile flags.
444 # TODO: $<$<COMPILE_LANGUAGE:CXX>:cflags_cc style generator expression.
445 # http://public.kitware.com/Bug/view.php?id=14857
446 flags = []
447 flags.extend(target.properties.get('cflags', []))
448 cflags_asm = target.properties.get('asmflags', [])
449 cflags_c = target.properties.get('cflags_c', [])
450 cflags_cxx = target.properties.get('cflags_cc', [])
451 cflags_objc = cflags_c[:]
452 cflags_objc.extend(target.properties.get('cflags_objc', []))
453 cflags_objcc = cflags_cxx[:]
454 cflags_objcc.extend(target.properties.get('cflags_objcc', []))
455
456 if 'c' in sources and not any(k in sources for k in ('asm', 'cxx', 'objc', 'objcc')):
457 flags.extend(cflags_c)
458 elif 'cxx' in sources and not any(k in sources for k in ('asm', 'c', 'objc', 'objcc')):
459 flags.extend(cflags_cxx)
460 elif 'objc' in sources and not any(k in sources for k in ('asm', 'c', 'cxx', 'objcc')):
461 flags.extend(cflags_objc)
462 elif 'objcc' in sources and not any(k in sources for k in ('asm', 'c', 'cxx', 'objc')):
463 flags.extend(cflags_objcc)
464 else:
465 # TODO: This is broken, one cannot generally set properties on files,
466 # as other targets may require different properties on the same files.
467 if 'asm' in sources and cflags_asm:
468 SetFilesProperty(out, sources['asm'], 'COMPILE_FLAGS', cflags_asm, ' ')
469 if 'c' in sources and cflags_c:
470 SetFilesProperty(out, sources['c'], 'COMPILE_FLAGS', cflags_c, ' ')
471 if 'cxx' in sources and cflags_cxx:
472 SetFilesProperty(out, sources['cxx'], 'COMPILE_FLAGS', cflags_cxx, ' ')
473 if 'objc' in sources and cflags_objc:
474 SetFilesProperty(out, sources['objc'], 'COMPILE_FLAGS', cflags_objc, ' ')
475 if 'objcc' in sources and cflags_objcc:
476 SetFilesProperty(out, sources['objcc'], 'COMPILE_FLAGS', cflags_objcc, ' ')
477 if flags:
478 SetCurrentTargetProperty(out, 'COMPILE_FLAGS', flags, ' ')
479
480 # Linker flags
481 ldflags = target.properties.get('ldflags', [])
482 if ldflags:
483 SetCurrentTargetProperty(out, 'LINK_FLAGS', ldflags, ' ')
484
485
486gn_target_types_that_absorb_objects = (
487 'executable',
488 'loadable_module',
489 'shared_library',
490 'static_library'
491)
492
493
494def WriteSourceVariables(out, target, project):
495 # gn separates the sheep from the goats based on file extensions.
496 # A full separation is done here because of flag handing (see Compile flags).
497 source_types = {'cxx':[], 'c':[], 'asm':[], 'objc':[], 'objcc':[],
498 'obj':[], 'obj_target':[], 'input':[], 'other':[]}
499
500 all_sources = target.properties.get('sources', [])
501
502 # As of cmake 3.11 add_library must have sources.
503 # If there are no sources, add empty.cpp as the file to compile.
504 # Unless it's an INTERFACE, which must not have sources until 3.19.
505 if len(all_sources) == 0 and not target.cmake_type.modifier == "INTERFACE":
506 all_sources.append(posixpath.join(project.build_path, 'empty.cpp'))
507
508 # TODO .def files on Windows
509 for source in all_sources:
510 _, ext = posixpath.splitext(source)
511 source_abs_path = project.GetAbsolutePath(source)
512 source_types[source_file_types.get(ext, 'other')].append(source_abs_path)
513
514 for input_path in target.properties.get('inputs', []):
515 input_abs_path = project.GetAbsolutePath(input_path)
516 source_types['input'].append(input_abs_path)
517
518 # OBJECT library dependencies need to be listed as sources.
519 # Only executables and non-OBJECT libraries may reference an OBJECT library.
520 # https://gitlab.kitware.com/cmake/cmake/issues/14778
521 if target.gn_type in gn_target_types_that_absorb_objects:
522 object_dependencies = set()
523 project.GetObjectSourceDependencies(target.gn_name, object_dependencies)
524 for dependency in object_dependencies:
525 cmake_dependency_name = project.GetCMakeTargetName(dependency)
526 obj_target_sources = '$<TARGET_OBJECTS:' + cmake_dependency_name + '>'
527 source_types['obj_target'].append(obj_target_sources)
528
529 sources = {}
530 for source_type, sources_of_type in source_types.items():
531 if sources_of_type:
532 sources[source_type] = '${target}__' + source_type + '_srcs'
533 SetVariableList(out, sources[source_type], sources_of_type)
534 return sources
535
536
537def WriteTarget(out, target, project):
538 out.write('\n#')
539 out.write(target.gn_name)
540 out.write('\n')
541
542 if target.cmake_type is None:
543 print ('Target %s has unknown target type %s, skipping.' %
544 ( target.gn_name, target.gn_type ) )
545 return
546
547 SetVariable(out, 'target', target.cmake_name)
548
549 sources = WriteSourceVariables(out, target, project)
550
551 synthetic_dependencies = set()
552 if target.gn_type == 'action':
553 WriteAction(out, target, project, sources, synthetic_dependencies)
554 if target.gn_type == 'action_foreach':
555 WriteActionForEach(out, target, project, sources, synthetic_dependencies)
556 if target.gn_type == 'copy':
557 WriteCopy(out, target, project, sources, synthetic_dependencies)
558
559 out.write(target.cmake_type.command)
560 out.write('("${target}"')
561 if target.cmake_type.modifier is not None:
562 out.write(' ')
563 out.write(target.cmake_type.modifier)
564 for sources_type_name in sources.values():
565 WriteVariable(out, sources_type_name, ' ')
566 if synthetic_dependencies:
567 out.write(' DEPENDS')
568 for synthetic_dependencie in synthetic_dependencies:
569 WriteVariable(out, synthetic_dependencie, ' ')
570 out.write(')\n')
571
572 if target.cmake_type.command != 'add_custom_target':
573 WriteCompilerFlags(out, target, project, sources)
574
575 libraries = set()
576 nonlibraries = set()
577
578 dependencies = set(target.properties.get('deps', []))
579 # Transitive OBJECT libraries are in sources.
580 # Those sources are dependent on the OBJECT library dependencies.
581 # Those sources cannot bring in library dependencies.
582 object_dependencies = set()
583 if target.gn_type != 'source_set':
584 project.GetObjectLibraryDependencies(target.gn_name, object_dependencies)
585 for object_dependency in object_dependencies:
586 dependencies.update(project.targets.get(object_dependency).get('deps', []))
587
588 for dependency in dependencies:
589 gn_dependency_type = project.targets.get(dependency, {}).get('type', None)
590 cmake_dependency_type = cmake_target_types.get(gn_dependency_type, None)
591 cmake_dependency_name = project.GetCMakeTargetName(dependency)
592 if cmake_dependency_type.command != 'add_library':
593 nonlibraries.add(cmake_dependency_name)
594 elif cmake_dependency_type.modifier != 'OBJECT':
595 if target.cmake_type.is_linkable:
596 libraries.add(cmake_dependency_name)
597 else:
598 nonlibraries.add(cmake_dependency_name)
599
600 # Non-library dependencies.
601 if nonlibraries:
602 out.write('add_dependencies("${target}"')
603 for nonlibrary in nonlibraries:
604 out.write('\n "')
605 out.write(nonlibrary)
606 out.write('"')
607 out.write(')\n')
608
609 # Non-OBJECT library dependencies.
610 combined_library_lists = [target.properties.get(key, []) for key in ['libs', 'frameworks']]
611 external_libraries = list(itertools.chain(*combined_library_lists))
612 if target.cmake_type.is_linkable and (external_libraries or libraries):
613 library_dirs = target.properties.get('lib_dirs', [])
614 if library_dirs:
615 SetVariableList(out, '${target}__library_directories', library_dirs)
616
617 system_libraries = []
618 for external_library in external_libraries:
619 if '/' in external_library:
620 libraries.add(project.GetAbsolutePath(external_library))
621 else:
622 if external_library.endswith('.framework'):
623 external_library = external_library[:-len('.framework')]
624 system_library = 'library__' + external_library
625 if library_dirs:
626 system_library = system_library + '__for_${target}'
627 out.write('find_library("')
628 out.write(CMakeStringEscape(system_library))
629 out.write('" "')
630 out.write(CMakeStringEscape(external_library))
631 out.write('"')
632 if library_dirs:
633 out.write(' PATHS "')
634 WriteVariable(out, '${target}__library_directories')
635 out.write('"')
636 out.write(')\n')
637 system_libraries.append(system_library)
638 out.write('target_link_libraries("${target}"')
639 if (target.cmake_type.modifier == "INTERFACE"):
640 out.write(' INTERFACE')
641 for library in libraries:
642 out.write('\n "')
643 out.write(CMakeStringEscape(library))
644 out.write('"')
645 for system_library in system_libraries:
646 WriteVariable(out, system_library, '\n "')
647 out.write('"')
648 out.write(')\n')
649
650
651def WriteProject(project):
652 out = open(posixpath.join(project.build_path, 'CMakeLists.txt'), 'w+')
653 extName = posixpath.join(project.build_path, 'CMakeLists.ext')
654 out.write('# Generated by gn_to_cmake.py.\n')
655 out.write('cmake_minimum_required(VERSION 3.7 FATAL_ERROR)\n')
656 out.write('cmake_policy(VERSION 3.7)\n')
657 out.write('project(Skia)\n\n')
658
659 out.write('file(WRITE "')
660 out.write(CMakeStringEscape(posixpath.join(project.build_path, "empty.cpp")))
661 out.write('")\n')
662
663 # Update the gn generated ninja build.
664 # If a build file has changed, this will update CMakeLists.ext if
665 # gn gen out/config --ide=json --json-ide-script=../../gn/gn_to_cmake.py
666 # style was used to create this config.
667 out.write('execute_process(COMMAND\n')
668 out.write(' ninja -C "')
669 out.write(CMakeStringEscape(project.build_path))
670 out.write('" build.ninja\n')
671 out.write(' RESULT_VARIABLE ninja_result)\n')
672 out.write('if (ninja_result)\n')
673 out.write(' message(WARNING ')
674 out.write('"Regeneration failed running ninja: ${ninja_result}")\n')
675 out.write('endif()\n')
676
677 out.write('include("')
678 out.write(CMakeStringEscape(extName))
679 out.write('")\n')
680 # This lets Clion find the emscripten header files when working with CanvasKit.
681 out.write('include_directories(SYSTEM $ENV{EMSDK}/upstream/emscripten/system/include/)\n')
682 out.close()
683
684 out = open(extName, 'w+')
685 out.write('# Generated by gn_to_cmake.py.\n')
686 out.write('cmake_minimum_required(VERSION 3.7 FATAL_ERROR)\n')
687 out.write('cmake_policy(VERSION 3.7)\n')
688
689 # The following appears to be as-yet undocumented.
690 # http://public.kitware.com/Bug/view.php?id=8392
691 out.write('enable_language(ASM)\n\n')
692 # ASM-ATT does not support .S files.
693 # output.write('enable_language(ASM-ATT)\n')
694
695 # Current issues with automatic re-generation:
696 # The gn generated build.ninja target uses build.ninja.d
697 # but build.ninja.d does not contain the ide or gn.
698 # Currently the ide is not run if the project.json file is not changed
699 # but the ide needs to be run anyway if it has itself changed.
700 # This can be worked around by deleting the project.json file.
701 out.write('file(READ "')
702 gn_deps_file = posixpath.join(project.build_path, 'build.ninja.d')
703 out.write(CMakeStringEscape(gn_deps_file))
704 out.write('" "gn_deps_file_content")\n')
705
706 out.write('string(REGEX REPLACE "^[^:]*: " "" ')
707 out.write('gn_deps_string ${gn_deps_file_content})\n')
708
709 # One would think this would need to worry about escaped spaces
710 # but gn doesn't escape spaces here (it generates invalid .d files).
711 out.write('string(REPLACE " " ";" "gn_deps" ${gn_deps_string})\n')
712 out.write('foreach("gn_dep" ${gn_deps})\n')
713 out.write(' configure_file("')
714 out.write(CMakeStringEscape(project.build_path))
715 out.write('${gn_dep}" "CMakeLists.devnull" COPYONLY)\n')
716 out.write('endforeach("gn_dep")\n')
717
718 out.write('list(APPEND other_deps "')
719 out.write(CMakeStringEscape(os.path.abspath(__file__)))
720 out.write('")\n')
721 out.write('foreach("other_dep" ${other_deps})\n')
722 out.write(' configure_file("${other_dep}" "CMakeLists.devnull" COPYONLY)\n')
723 out.write('endforeach("other_dep")\n')
724
725 for target_name in project.targets.keys():
726 out.write('\n')
727 WriteTarget(out, Target(target_name, project), project)
728
729
730def main():
731 if len(sys.argv) != 2:
732 print('Usage: ' + sys.argv[0] + ' <json_file_name>')
733 exit(1)
734
735 json_path = sys.argv[1]
736 project = None
737 with open(json_path, 'r') as json_file:
738 project = json.loads(json_file.read())
739
740 WriteProject(Project(project))
741
742
743if __name__ == "__main__":
744 main()
def __init__(self, command, modifier, property_modifier, is_linkable)
Definition: gn_to_cmake.py:131
def GetObjectLibraryDependencies(self, gn_target_name, object_dependencies)
Definition: gn_to_cmake.py:183
def GetCMakeTargetName(self, gn_target_name)
Definition: gn_to_cmake.py:192
def GetAbsolutePath(self, path)
Definition: gn_to_cmake.py:167
def __init__(self, project_json)
Definition: gn_to_cmake.py:161
def GetObjectSourceDependencies(self, gn_target_name, object_dependencies)
Definition: gn_to_cmake.py:173
def __init__(self, gn_target_name, project)
Definition: gn_to_cmake.py:226
static void append(char **dst, size_t *count, const char *src, size_t n)
Definition: editor.cpp:211
static float min(float r, float g, float b)
Definition: hsl.cpp:48
exit(kErrorExitCode)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not set
Definition: switches.h:76
def WriteActionForEach(out, target, project, sources, synthetic_dependencies)
Definition: gn_to_cmake.py:295
def CMakeStringEscape(a)
Definition: gn_to_cmake.py:32
def SetVariable(out, variable_name, value)
Definition: gn_to_cmake.py:60
def FindFirstOf(s, a)
Definition: gn_to_cmake.py:156
def WriteTarget(out, target, project)
Definition: gn_to_cmake.py:537
def WriteProject(project)
Definition: gn_to_cmake.py:651
def ExpandPlaceholders(source, a)
Definition: gn_to_cmake.py:284
def SetFilesProperty(output, variable, property_name, values, sep)
Definition: gn_to_cmake.py:82
def WriteCompilerFlags(out, target, project, sources)
Definition: gn_to_cmake.py:405
def SetVariableList(out, variable_name, values)
Definition: gn_to_cmake.py:69
def WriteVariable(output, variable_name, prepend=None)
Definition: gn_to_cmake.py:106
def CMakeTargetEscape(a)
Definition: gn_to_cmake.py:46
def WriteCopy(out, target, project, sources, synthetic_dependencies)
Definition: gn_to_cmake.py:359
def WriteSourceVariables(out, target, project)
Definition: gn_to_cmake.py:494
def SetCurrentTargetProperty(out, property_name, values, sep='')
Definition: gn_to_cmake.py:95
def WriteAction(out, target, project, sources, synthetic_dependencies)
Definition: gn_to_cmake.py:234
Definition: main.py:1
const myers::Point & get(const myers::Segment &)
def print(*args, **kwargs)
Definition: run_tests.py:49
SI auto map(std::index_sequence< I... >, Fn &&fn, const Args &... args) -> skvx::Vec< sizeof...(I), decltype(fn(args[0]...))>
Definition: SkVx.h:680
SIT bool any(const Vec< 1, T > &x)
Definition: SkVx.h:530
Definition: zip.py:1
static SkString join(const CommandLineFlags::StringArray &)
Definition: skpbench.cpp:741