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 '_.+-':
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__":
def __init__(self, command, modifier, property_modifier, is_linkable)
def GetObjectLibraryDependencies(self, gn_target_name, object_dependencies)
def GetCMakeTargetName(self, gn_target_name)
def GetAbsolutePath(self, path)
def __init__(self, project_json)
def GetObjectSourceDependencies(self, gn_target_name, object_dependencies)
def __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)
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
def WriteActionForEach(out, target, project, sources, synthetic_dependencies)
def SetVariable(out, variable_name, value)
def WriteTarget(out, target, project)
def WriteProject(project)
def ExpandPlaceholders(source, a)
def SetFilesProperty(output, variable, property_name, values, sep)
def WriteCompilerFlags(out, target, project, sources)
def SetVariableList(out, variable_name, values)
def WriteVariable(output, variable_name, prepend=None)
def WriteCopy(out, target, project, sources, synthetic_dependencies)
def WriteSourceVariables(out, target, project)
def SetCurrentTargetProperty(out, property_name, values, sep='')
def WriteAction(out, target, project, sources, synthetic_dependencies)
const myers::Point & get(const myers::Segment &)
def print(*args, **kwargs)
SI auto map(std::index_sequence< I... >, Fn &&fn, const Args &... args) -> skvx::Vec< sizeof...(I), decltype(fn(args[0]...))>
SIT bool any(const Vec< 1, T > &x)
static SkString join(const CommandLineFlags::StringArray &)