10Usage: gn_to_cmake.py <json_file_name>
12gn gen out/config --ide=json --json-ide-script=../../gn/gn_to_cmake.py
16gn gen out/config --ide=json
17python3 gn/gn_to_cmake.py out/config/project.json
19The first is recommended, as it will auto-update.
33 """Escapes the string 'a' for use inside a CMake string.
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
40 The following do not need to be escaped
41 '#' when the lexer is in string state, this does not start a comment
43 return a.replace(
'\\',
'\\\\').replace(
';',
'\\;').replace(
'"',
'\\"')
47 """Escapes the string 'a' for use as a CMake target name.
49 CMP0037 in CMake 3.0 restricts target names to "^[A-Za-z0-9_.:+-]+$"
50 The ':' is only allowed for imported targets.
53 if c
in string.ascii_letters
or c
in string.digits
or c
in '_.+-':
57 return ''.join(map(Escape, a))
61 """Sets a CMake variable."""
70 """Sets a CMake variable to a list."""
75 out.write(
'list(APPEND "')
83 """Given a set of source files, sets the given property on them."""
84 output.write(
'set_source_files_properties(')
86 output.write(
' PROPERTIES ')
87 output.write(property_name)
96 """Given a target, sets the given property."""
97 out.write(
'set_target_properties("${target}" PROPERTIES ')
98 out.write(property_name)
108 output.write(prepend)
110 output.write(variable_name)
131 def __init__(self, command, modifier, property_modifier, is_linkable):
140cmake_target_types = {
141 'unknown': CMakeTargetType.custom,
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),
148 'copy': CMakeTargetType.custom,
149 'action': CMakeTargetType.custom,
150 'action_foreach': CMakeTargetType.custom,
151 'bundle_data': CMakeTargetType.custom,
152 'create_bundle': CMakeTargetType.custom,
157 return min(s.find(i)
for i
in a
if i
in s)
163 build_settings = project_json[
'build_settings']
168 if path.startswith(
'//'):
169 return posixpath.join(self.
root_path, path[2:])
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:
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)
195 path_separator =
FindFirstOf(gn_target_name, (
':',
'('))
199 if not path_separator:
200 location = gn_target_name[2:]
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:]
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
213 cmake_target_name =
None
214 if location.endswith(
'/' + name):
215 cmake_target_name = location
217 cmake_target_name = location +
'_' + name
219 cmake_target_name = name
221 cmake_target_name +=
'--' + toolchain
234def WriteAction(out, target, project, sources, synthetic_dependencies):
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)
242 output_directories.add(output_directory)
243 outputs_name =
'${target}__output'
246 out.write(
'add_custom_command(OUTPUT ')
250 if output_directories:
251 out.write(
' COMMAND ${CMAKE_COMMAND} -E make_directory "')
252 out.write(
'" "'.join(map(CMakeStringEscape, output_directories)))
255 script = target.properties[
'script']
256 arguments = target.properties[
'args']
257 out.write(
' COMMAND python3 "')
262 out.write(
'"\n "'.join(map(CMakeStringEscape, arguments)))
266 out.write(
' DEPENDS ')
267 for sources_type_name
in sources.values():
273 out.write(
' WORKING_DIRECTORY "')
277 out.write(
' COMMENT "Action: ${target}"\n')
279 out.write(
' VERBATIM)\n')
281 synthetic_dependencies.add(outputs_name)
285 source_dir, source_file_part = posixpath.split(source)
286 source_name_part, _ = posixpath.splitext(source_file_part)
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)
296 all_outputs = target.properties.get(
'outputs', [])
297 inputs = target.properties.get(
'sources', [])
299 outputs_per_input =
int(len(all_outputs) / len(inputs))
300 for count, source
in enumerate(inputs):
301 source_abs_path = project.GetAbsolutePath(source)
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)
311 output_directories.add(output_directory)
312 outputs_name =
'${target}__output_' + str(count)
315 out.write(
'add_custom_command(OUTPUT ')
319 if output_directories:
320 out.write(
' COMMAND ${CMAKE_COMMAND} -E make_directory "')
321 out.write(
'" "'.join(map(CMakeStringEscape, output_directories)))
324 script = target.properties[
'script']
326 arguments = target.properties[
'args']
327 out.write(
' COMMAND python3 "')
332 expand = functools.partial(ExpandPlaceholders, source_abs_path)
333 out.write(
'"\n "'.join(map(CMakeStringEscape, map(expand,arguments))))
337 out.write(
' DEPENDS')
338 if 'input' in sources:
346 out.write(
' WORKING_DIRECTORY "')
350 out.write(
' COMMENT "Action ${target} on ')
354 out.write(
' VERBATIM)\n')
356 synthetic_dependencies.add(outputs_name)
359def WriteCopy(out, target, project, sources, synthetic_dependencies):
360 inputs = target.properties.get(
'sources', [])
361 raw_outputs = target.properties.get(
'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'
371 out.write(
'add_custom_command(OUTPUT ')
375 for src, dst
in zip(inputs, outputs):
380 if "." in os.path.basename(abs_src_path):
381 out.write(
' COMMAND ${CMAKE_COMMAND} -E copy "')
383 out.write(
' COMMAND ${CMAKE_COMMAND} -E copy_directory "')
384 out.write(abs_src_path)
389 out.write(
' DEPENDS ')
390 for sources_type_name
in sources.values():
394 out.write(
' WORKING_DIRECTORY "')
398 out.write(
' COMMENT "Copy ${target}"\n')
400 out.write(
' VERBATIM)\n')
402 synthetic_dependencies.add(outputs_name)
408 if not 'c' in sources
and not 'cxx' in sources
and not target.cmake_type.modifier ==
"INTERFACE":
412 if 'input' in sources:
414 if 'other' in sources:
425 includes = target.properties.get(
'include_dirs', [])
427 out.write(
'set_property(TARGET "${target}" ')
428 out.write(
'APPEND PROPERTY INCLUDE_DIRECTORIES')
429 for include_dir
in includes:
431 out.write(project.GetAbsolutePath(include_dir))
436 defines = target.properties.get(
'defines', [])
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', []))
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)
467 if 'asm' in sources
and cflags_asm:
469 if 'c' in sources
and cflags_c:
471 if 'cxx' in sources
and cflags_cxx:
473 if 'objc' in sources
and cflags_objc:
475 if 'objcc' in sources
and cflags_objcc:
481 ldflags = target.properties.get(
'ldflags', [])
486gn_target_types_that_absorb_objects = (
497 source_types = {
'cxx':[],
'c':[],
'asm':[],
'objc':[],
'objcc':[],
498 'obj':[],
'obj_target':[],
'input':[],
'other':[]}
500 all_sources = target.properties.get(
'sources', [])
505 if len(all_sources) == 0
and not target.cmake_type.modifier ==
"INTERFACE":
506 all_sources.append(posixpath.join(project.build_path,
'empty.cpp'))
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)
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)
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)
530 for source_type, sources_of_type
in source_types.items():
532 sources[source_type] =
'${target}__' + source_type +
'_srcs'
539 out.write(target.gn_name)
542 if target.cmake_type
is None:
543 print (
'Target %s has unknown target type %s, skipping.' %
544 ( target.gn_name, target.gn_type ) )
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':
556 if target.gn_type ==
'copy':
557 WriteCopy(out, target, project, sources, synthetic_dependencies)
559 out.write(target.cmake_type.command)
560 out.write(
'("${target}"')
561 if target.cmake_type.modifier
is not None:
563 out.write(target.cmake_type.modifier)
564 for sources_type_name
in sources.values():
566 if synthetic_dependencies:
567 out.write(
' DEPENDS')
568 for synthetic_dependencie
in synthetic_dependencies:
572 if target.cmake_type.command !=
'add_custom_target':
578 dependencies = set(target.properties.get(
'deps', []))
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', []))
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)
598 nonlibraries.add(cmake_dependency_name)
602 out.write(
'add_dependencies("${target}"')
603 for nonlibrary
in nonlibraries:
605 out.write(nonlibrary)
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', [])
617 system_libraries = []
618 for external_library
in external_libraries:
619 if '/' in external_library:
620 libraries.add(project.GetAbsolutePath(external_library))
622 if external_library.endswith(
'.framework'):
623 external_library = external_library[:-len(
'.framework')]
624 system_library =
'library__' + external_library
626 system_library = system_library +
'__for_${target}'
627 out.write(
'find_library("')
633 out.write(
' PATHS "')
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:
645 for system_library
in system_libraries:
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')
659 out.write(
'file(WRITE "')
667 out.write(
'execute_process(COMMAND\n')
668 out.write(
' ninja -C "')
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')
677 out.write(
'include("')
681 out.write(
'include_directories(SYSTEM $ENV{EMSDK}/upstream/emscripten/system/include/)\n')
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')
691 out.write(
'enable_language(ASM)\n\n')
701 out.write(
'file(READ "')
702 gn_deps_file = posixpath.join(project.build_path,
'build.ninja.d')
704 out.write(
'" "gn_deps_file_content")\n')
706 out.write(
'string(REGEX REPLACE "^[^:]*: " "" ')
707 out.write(
'gn_deps_string ${gn_deps_file_content})\n')
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("')
715 out.write(
'${gn_dep}" "CMakeLists.devnull" COPYONLY)\n')
716 out.write(
'endforeach("gn_dep")\n')
718 out.write(
'list(APPEND other_deps "')
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')
725 for target_name
in project.targets.keys():
731 if len(sys.argv) != 2:
732 print(
'Usage: ' + sys.argv[0] +
' <json_file_name>')
735 json_path = sys.argv[1]
737 with open(json_path,
'r')
as json_file:
738 project = json.loads(json_file.read())
743if __name__ ==
"__main__":
Type::kYUV Type::kRGBA() int(0.7 *637)
__init__(self, command, modifier, property_modifier, is_linkable)
GetCMakeTargetName(self, gn_target_name)
GetObjectSourceDependencies(self, gn_target_name, object_dependencies)
__init__(self, project_json)
GetAbsolutePath(self, path)
GetObjectLibraryDependencies(self, gn_target_name, object_dependencies)
__init__(self, gn_target_name, project)
static void append(char **dst, size_t *count, const char *src, size_t n)
static float min(float r, float g, float b)
WriteTarget(out, target, project)
SetFilesProperty(output, variable, property_name, values, sep)
WriteSourceVariables(out, target, project)
SetVariableList(out, variable_name, values)
WriteActionForEach(out, target, project, sources, synthetic_dependencies)
WriteCopy(out, target, project, sources, synthetic_dependencies)
ExpandPlaceholders(source, a)
SetVariable(out, variable_name, value)
WriteCompilerFlags(out, target, project, sources)
SetCurrentTargetProperty(out, property_name, values, sep='')
WriteVariable(output, variable_name, prepend=None)
WriteAction(out, target, project, sources, synthetic_dependencies)