blob: 30a039a54f50cd104b01c7e1fa48f0580bd54ad8 [file] [log] [blame]
bungeman623ef922016-09-23 08:16:04 -07001#!/usr/bin/env python
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
bungemane95ea082016-09-28 16:45:35 -04008
bungeman623ef922016-09-23 08:16:04 -07009"""
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
17python gn/gn_to_cmake.py out/config/project.json
18"""
19
bungemane95ea082016-09-28 16:45:35 -040020
Ben Wagnerdc69dc72016-10-04 16:44:44 -040021import itertools
22import functools
bungeman623ef922016-09-23 08:16:04 -070023import json
24import posixpath
25import os
Ben Wagnerdc69dc72016-10-04 16:44:44 -040026import string
bungeman623ef922016-09-23 08:16:04 -070027import sys
28
bungeman623ef922016-09-23 08:16:04 -070029
bungemane95ea082016-09-28 16:45:35 -040030def CMakeStringEscape(a):
31 """Escapes the string 'a' for use inside a CMake string.
bungeman623ef922016-09-23 08:16:04 -070032
bungemane95ea082016-09-28 16:45:35 -040033 This means escaping
34 '\' otherwise it may be seen as modifying the next character
35 '"' otherwise it will end the string
36 ';' otherwise the string becomes a list
bungeman623ef922016-09-23 08:16:04 -070037
bungemane95ea082016-09-28 16:45:35 -040038 The following do not need to be escaped
39 '#' when the lexer is in string state, this does not start a comment
40 """
41 return a.replace('\\', '\\\\').replace(';', '\\;').replace('"', '\\"')
bungeman623ef922016-09-23 08:16:04 -070042
bungeman623ef922016-09-23 08:16:04 -070043
Ben Wagnerdc69dc72016-10-04 16:44:44 -040044def CMakeTargetEscape(a):
45 """Escapes the string 'a' for use as a CMake target name.
46
47 CMP0037 in CMake 3.0 restricts target names to "^[A-Za-z0-9_.:+-]+$"
48 The ':' is only allowed for imported targets.
49 """
50 def Escape(c):
51 if c in string.ascii_letters or c in string.digits or c in '_.+-':
52 return c
53 else:
54 return '__'
55 return ''.join(map(Escape, a))
56
57
bungemane95ea082016-09-28 16:45:35 -040058def SetVariable(out, variable_name, value):
59 """Sets a CMake variable."""
Ben Wagnerdc69dc72016-10-04 16:44:44 -040060 out.write('set("')
61 out.write(CMakeStringEscape(variable_name))
62 out.write('" "')
bungemane95ea082016-09-28 16:45:35 -040063 out.write(CMakeStringEscape(value))
64 out.write('")\n')
bungeman623ef922016-09-23 08:16:04 -070065
bungemane95ea082016-09-28 16:45:35 -040066
67def SetVariableList(out, variable_name, values):
68 """Sets a CMake variable to a list."""
69 if not values:
70 return SetVariable(out, variable_name, "")
71 if len(values) == 1:
72 return SetVariable(out, variable_name, values[0])
Ben Wagnerdc69dc72016-10-04 16:44:44 -040073 out.write('list(APPEND "')
74 out.write(CMakeStringEscape(variable_name))
75 out.write('"\n "')
bungemane95ea082016-09-28 16:45:35 -040076 out.write('"\n "'.join([CMakeStringEscape(value) for value in values]))
77 out.write('")\n')
78
79
80def SetFilesProperty(output, variable, property_name, values, sep):
81 """Given a set of source files, sets the given property on them."""
82 output.write('set_source_files_properties(')
83 WriteVariable(output, variable)
84 output.write(' PROPERTIES ')
85 output.write(property_name)
86 output.write(' "')
87 for value in values:
88 output.write(CMakeStringEscape(value))
89 output.write(sep)
90 output.write('")\n')
91
92
Ben Wagnerdc69dc72016-10-04 16:44:44 -040093def SetCurrentTargetProperty(out, property_name, values, sep=''):
bungemane95ea082016-09-28 16:45:35 -040094 """Given a target, sets the given property."""
Ben Wagnerdc69dc72016-10-04 16:44:44 -040095 out.write('set_target_properties("${target}" PROPERTIES ')
bungemane95ea082016-09-28 16:45:35 -040096 out.write(property_name)
97 out.write(' "')
98 for value in values:
99 out.write(CMakeStringEscape(value))
100 out.write(sep)
101 out.write('")\n')
102
103
104def WriteVariable(output, variable_name, prepend=None):
105 if prepend:
106 output.write(prepend)
107 output.write('${')
108 output.write(variable_name)
109 output.write('}')
110
111
bungemane95ea082016-09-28 16:45:35 -0400112# See GetSourceFileType in gn
113source_file_types = {
114 '.cc': 'cxx',
115 '.cpp': 'cxx',
116 '.cxx': 'cxx',
117 '.c': 'c',
118 '.s': 'asm',
119 '.S': 'asm',
120 '.asm': 'asm',
121 '.o': 'obj',
122 '.obj': 'obj',
123}
124
125
126class CMakeTargetType(object):
127 def __init__(self, command, modifier, property_modifier, is_linkable):
128 self.command = command
129 self.modifier = modifier
130 self.property_modifier = property_modifier
131 self.is_linkable = is_linkable
132CMakeTargetType.custom = CMakeTargetType('add_custom_target', 'SOURCES',
133 None, False)
134
135# See GetStringForOutputType in gn
136cmake_target_types = {
137 'unknown': CMakeTargetType.custom,
138 'group': CMakeTargetType.custom,
139 'executable': CMakeTargetType('add_executable', None, 'RUNTIME', True),
140 'loadable_module': CMakeTargetType('add_library', 'MODULE', 'LIBRARY', True),
141 'shared_library': CMakeTargetType('add_library', 'SHARED', 'LIBRARY', True),
142 'static_library': CMakeTargetType('add_library', 'STATIC', 'ARCHIVE', False),
143 'source_set': CMakeTargetType('add_library', 'OBJECT', None, False),
Ben Wagnerbc344042016-09-29 15:41:53 -0400144 'copy': CMakeTargetType.custom,
bungemane95ea082016-09-28 16:45:35 -0400145 'action': CMakeTargetType.custom,
146 'action_foreach': CMakeTargetType.custom,
147 'bundle_data': CMakeTargetType.custom,
148 'create_bundle': CMakeTargetType.custom,
149}
150
151
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400152def FindFirstOf(s, a):
153 return min(s.find(i) for i in a if i in s)
Ben Wagnerbc344042016-09-29 15:41:53 -0400154
155
156class Project(object):
157 def __init__(self, project_json):
158 self.targets = project_json['targets']
159 build_settings = project_json['build_settings']
160 self.root_path = build_settings['root_path']
161 self.build_path = posixpath.join(self.root_path,
162 build_settings['build_dir'][2:])
163
164 def GetAbsolutePath(self, path):
165 if path.startswith("//"):
166 return self.root_path + "/" + path[2:]
167 else:
168 return path
169
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400170 def GetObjectSourceDependencies(self, gn_target_name, object_dependencies):
171 """All OBJECT libraries whose sources have not been absorbed."""
Ben Wagnerbc344042016-09-29 15:41:53 -0400172 dependencies = self.targets[gn_target_name].get('deps', [])
173 for dependency in dependencies:
174 dependency_type = self.targets[dependency].get('type', None)
175 if dependency_type == 'source_set':
176 object_dependencies.add(dependency)
177 if dependency_type not in gn_target_types_that_absorb_objects:
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400178 self.GetObjectSourceDependencies(dependency, object_dependencies)
179
180 def GetObjectLibraryDependencies(self, gn_target_name, object_dependencies):
181 """All OBJECT libraries whose libraries have not been absorbed."""
182 dependencies = self.targets[gn_target_name].get('deps', [])
183 for dependency in dependencies:
184 dependency_type = self.targets[dependency].get('type', None)
185 if dependency_type == 'source_set':
186 object_dependencies.add(dependency)
187 self.GetObjectLibraryDependencies(dependency, object_dependencies)
Ben Wagnerbc344042016-09-29 15:41:53 -0400188
189 def GetCMakeTargetName(self, gn_target_name):
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400190 # See <chromium>/src/tools/gn/label.cc#Resolve
191 # //base/test:test_support(//build/toolchain/win:msvc)
192 path_separator = FindFirstOf(gn_target_name, (':', '('))
193 location = None
194 name = None
195 toolchain = None
196 if not path_separator:
197 location = gn_target_name[2:]
198 else:
199 location = gn_target_name[2:path_separator]
200 toolchain_separator = gn_target_name.find('(', path_separator)
201 if toolchain_separator == -1:
202 name = gn_target_name[path_separator + 1:]
203 else:
204 if toolchain_separator > path_separator:
205 name = gn_target_name[path_separator + 1:toolchain_separator]
206 assert gn_target_name.endswith(')')
207 toolchain = gn_target_name[toolchain_separator + 1:-1]
208 assert location or name
209
210 cmake_target_name = None
211 if location.endswith('/' + name):
212 cmake_target_name = location
213 elif location:
214 cmake_target_name = location + '_' + name
215 else:
216 cmake_target_name = name
217 if toolchain:
218 cmake_target_name += '--' + toolchain
219 return CMakeTargetEscape(cmake_target_name)
Ben Wagnerbc344042016-09-29 15:41:53 -0400220
221
bungemane95ea082016-09-28 16:45:35 -0400222class Target(object):
Ben Wagnerbc344042016-09-29 15:41:53 -0400223 def __init__(self, gn_target_name, project):
224 self.gn_name = gn_target_name
225 self.properties = project.targets[self.gn_name]
226 self.cmake_name = project.GetCMakeTargetName(self.gn_name)
bungemane95ea082016-09-28 16:45:35 -0400227 self.gn_type = self.properties.get('type', None)
228 self.cmake_type = cmake_target_types.get(self.gn_type, None)
229
230
Ben Wagnerbc344042016-09-29 15:41:53 -0400231def WriteAction(out, target, project, sources, synthetic_dependencies):
232 outputs = []
233 output_directories = set()
234 for output in target.properties.get('outputs', []):
235 output_abs_path = project.GetAbsolutePath(output)
236 outputs.append(output_abs_path)
237 output_directory = posixpath.dirname(output_abs_path)
238 if output_directory:
239 output_directories.add(output_directory)
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400240 outputs_name = '${target}__output'
Ben Wagnerbc344042016-09-29 15:41:53 -0400241 SetVariableList(out, outputs_name, outputs)
242
243 out.write('add_custom_command(OUTPUT ')
244 WriteVariable(out, outputs_name)
245 out.write('\n')
246
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400247 if output_directories:
248 out.write(' COMMAND ${CMAKE_COMMAND} -E make_directory "')
249 out.write('" "'.join(map(CMakeStringEscape, output_directories)))
250 out.write('"\n')
Ben Wagnerbc344042016-09-29 15:41:53 -0400251
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400252 script = target.properties['script']
253 arguments = target.properties['args']
254 out.write(' COMMAND python "')
255 out.write(CMakeStringEscape(project.GetAbsolutePath(script)))
256 out.write('"')
257 if arguments:
258 out.write('\n "')
259 out.write('"\n "'.join(map(CMakeStringEscape, arguments)))
260 out.write('"')
Ben Wagnerbc344042016-09-29 15:41:53 -0400261 out.write('\n')
262
263 out.write(' DEPENDS ')
264 for sources_type_name in sources.values():
265 WriteVariable(out, sources_type_name, ' ')
266 out.write('\n')
267
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400268 #TODO: CMake 3.7 is introducing DEPFILE
269
270 out.write(' WORKING_DIRECTORY "')
271 out.write(CMakeStringEscape(project.build_path))
272 out.write('"\n')
273
274 out.write(' COMMENT "Action: ${target}"\n')
275
276 out.write(' VERBATIM)\n')
277
278 synthetic_dependencies.add(outputs_name)
279
280
281def ExpandPlaceholders(source, a):
282 source_dir, source_file_part = posixpath.split(source)
283 source_name_part, _ = posixpath.splitext(source_file_part)
284 #TODO: {{source_gen_dir}}, {{source_out_dir}}, {{response_file_name}}
285 return a.replace('{{source}}', source) \
286 .replace('{{source_file_part}}', source_file_part) \
287 .replace('{{source_name_part}}', source_name_part) \
288 .replace('{{source_dir}}', source_dir) \
289 .replace('{{source_root_relative_dir}}', source_dir)
290
291
292def WriteActionForEach(out, target, project, sources, synthetic_dependencies):
293 all_outputs = target.properties.get('outputs', [])
294 inputs = target.properties.get('sources', [])
295 # TODO: consider expanding 'output_patterns' instead.
296 outputs_per_input = len(all_outputs) / len(inputs)
297 for count, source in enumerate(inputs):
298 source_abs_path = project.GetAbsolutePath(source)
299
300 outputs = []
301 output_directories = set()
302 for output in all_outputs[outputs_per_input * count:
303 outputs_per_input * (count+1)]:
304 output_abs_path = project.GetAbsolutePath(output)
305 outputs.append(output_abs_path)
306 output_directory = posixpath.dirname(output_abs_path)
307 if output_directory:
308 output_directories.add(output_directory)
309 outputs_name = '${target}__output_' + str(count)
310 SetVariableList(out, outputs_name, outputs)
311
312 out.write('add_custom_command(OUTPUT ')
313 WriteVariable(out, outputs_name)
314 out.write('\n')
315
316 if output_directories:
317 out.write(' COMMAND ${CMAKE_COMMAND} -E make_directory "')
318 out.write('" "'.join(map(CMakeStringEscape, output_directories)))
319 out.write('"\n')
320
321 script = target.properties['script']
322 # TODO: need to expand {{xxx}} in arguments
323 arguments = target.properties['args']
324 out.write(' COMMAND python "')
325 out.write(CMakeStringEscape(project.GetAbsolutePath(script)))
326 out.write('"')
327 if arguments:
328 out.write('\n "')
329 expand = functools.partial(ExpandPlaceholders, source_abs_path)
330 out.write('"\n "'.join(map(CMakeStringEscape, map(expand,arguments))))
331 out.write('"')
332 out.write('\n')
333
334 out.write(' DEPENDS')
335 if 'input' in sources:
336 WriteVariable(out, sources['input'], ' ')
337 out.write(' "')
338 out.write(CMakeStringEscape(source_abs_path))
339 out.write('"\n')
340
341 #TODO: CMake 3.7 is introducing DEPFILE
342
343 out.write(' WORKING_DIRECTORY "')
344 out.write(CMakeStringEscape(project.build_path))
345 out.write('"\n')
346
347 out.write(' COMMENT "Action ${target} on ')
348 out.write(CMakeStringEscape(source_abs_path))
349 out.write('"\n')
350
351 out.write(' VERBATIM)\n')
352
353 synthetic_dependencies.add(outputs_name)
354
355
356def WriteCopy(out, target, project, sources, synthetic_dependencies):
357 inputs = target.properties.get('sources', [])
358 raw_outputs = target.properties.get('outputs', [])
359
360 # TODO: consider expanding 'output_patterns' instead.
361 outputs = []
362 for output in raw_outputs:
363 output_abs_path = project.GetAbsolutePath(output)
364 outputs.append(output_abs_path)
365 outputs_name = '${target}__output'
366 SetVariableList(out, outputs_name, outputs)
367
368 out.write('add_custom_command(OUTPUT ')
369 WriteVariable(out, outputs_name)
Ben Wagnerbc344042016-09-29 15:41:53 -0400370 out.write('\n')
371
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400372 for src, dst in zip(inputs, outputs):
373 out.write(' COMMAND ${CMAKE_COMMAND} -E copy "')
374 out.write(CMakeStringEscape(project.GetAbsolutePath(src)))
375 out.write('" "')
376 out.write(CMakeStringEscape(dst))
377 out.write('"\n')
378
379 out.write(' DEPENDS ')
380 for sources_type_name in sources.values():
381 WriteVariable(out, sources_type_name, ' ')
Ben Wagnerbc344042016-09-29 15:41:53 -0400382 out.write('\n')
383
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400384 out.write(' WORKING_DIRECTORY "')
385 out.write(CMakeStringEscape(project.build_path))
386 out.write('"\n')
387
388 out.write(' COMMENT "Copy ${target}"\n')
389
Ben Wagnerbc344042016-09-29 15:41:53 -0400390 out.write(' VERBATIM)\n')
391
392 synthetic_dependencies.add(outputs_name)
393
394
395def WriteCompilerFlags(out, target, project, sources):
bungemane95ea082016-09-28 16:45:35 -0400396 # Hack, set linker language to c if no c or cxx files present.
397 if not 'c' in sources and not 'cxx' in sources:
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400398 SetCurrentTargetProperty(out, 'LINKER_LANGUAGE', ['C'])
bungemane95ea082016-09-28 16:45:35 -0400399
400 # Mark uncompiled sources as uncompiled.
Ben Wagnerbc344042016-09-29 15:41:53 -0400401 if 'input' in sources:
402 SetFilesProperty(out, sources['input'], 'HEADER_FILE_ONLY', ('True',), '')
bungemane95ea082016-09-28 16:45:35 -0400403 if 'other' in sources:
Ben Wagnerbc344042016-09-29 15:41:53 -0400404 SetFilesProperty(out, sources['other'], 'HEADER_FILE_ONLY', ('True',), '')
bungemane95ea082016-09-28 16:45:35 -0400405
406 # Mark object sources as linkable.
407 if 'obj' in sources:
Ben Wagnerbc344042016-09-29 15:41:53 -0400408 SetFilesProperty(out, sources['obj'], 'EXTERNAL_OBJECT', ('True',), '')
bungemane95ea082016-09-28 16:45:35 -0400409
410 # TODO: 'output_name', 'output_dir', 'output_extension'
Ben Wagnerbc344042016-09-29 15:41:53 -0400411 # This includes using 'source_outputs' to direct compiler output.
bungemane95ea082016-09-28 16:45:35 -0400412
413 # Includes
414 includes = target.properties.get('include_dirs', [])
415 if includes:
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400416 out.write('set_property(TARGET "${target}" ')
417 out.write('APPEND PROPERTY INCLUDE_DIRECTORIES')
bungemane95ea082016-09-28 16:45:35 -0400418 for include_dir in includes:
419 out.write('\n "')
Ben Wagnerbc344042016-09-29 15:41:53 -0400420 out.write(project.GetAbsolutePath(include_dir))
bungemane95ea082016-09-28 16:45:35 -0400421 out.write('"')
422 out.write(')\n')
423
424 # Defines
425 defines = target.properties.get('defines', [])
426 if defines:
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400427 SetCurrentTargetProperty(out, 'COMPILE_DEFINITIONS', defines, ';')
bungemane95ea082016-09-28 16:45:35 -0400428
429 # Compile flags
430 # "arflags", "asmflags", "cflags",
431 # "cflags_c", "clfags_cc", "cflags_objc", "clfags_objcc"
432 # CMake does not have per target lang compile flags.
433 # TODO: $<$<COMPILE_LANGUAGE:CXX>:cflags_cc style generator expression.
434 # http://public.kitware.com/Bug/view.php?id=14857
435 flags = []
436 flags.extend(target.properties.get('cflags', []))
437 cflags_asm = target.properties.get('asmflags', [])
438 cflags_c = target.properties.get('cflags_c', [])
439 cflags_cxx = target.properties.get('cflags_cc', [])
440 if 'c' in sources and not any(k in sources for k in ('asm', 'cxx')):
441 flags.extend(cflags_c)
442 elif 'cxx' in sources and not any(k in sources for k in ('asm', 'c')):
443 flags.extend(cflags_cxx)
444 else:
445 # TODO: This is broken, one cannot generally set properties on files,
446 # as other targets may require different properties on the same files.
447 if 'asm' in sources and cflags_asm:
448 SetFilesProperty(out, sources['asm'], 'COMPILE_FLAGS', cflags_asm, ' ')
449 if 'c' in sources and cflags_c:
450 SetFilesProperty(out, sources['c'], 'COMPILE_FLAGS', cflags_c, ' ')
451 if 'cxx' in sources and cflags_cxx:
452 SetFilesProperty(out, sources['cxx'], 'COMPILE_FLAGS', cflags_cxx, ' ')
453 if flags:
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400454 SetCurrentTargetProperty(out, 'COMPILE_FLAGS', flags, ' ')
bungemane95ea082016-09-28 16:45:35 -0400455
456 # Linker flags
457 ldflags = target.properties.get('ldflags', [])
458 if ldflags:
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400459 SetCurrentTargetProperty(out, 'LINK_FLAGS', ldflags, ' ')
bungemane95ea082016-09-28 16:45:35 -0400460
461
Ben Wagnerbc344042016-09-29 15:41:53 -0400462gn_target_types_that_absorb_objects = (
463 'executable',
464 'loadable_module',
465 'shared_library',
466 'static_library'
467)
bungemane95ea082016-09-28 16:45:35 -0400468
469
Ben Wagnerbc344042016-09-29 15:41:53 -0400470def WriteSourceVariables(out, target, project):
bungemane95ea082016-09-28 16:45:35 -0400471 # gn separates the sheep from the goats based on file extensions.
472 # A full separation is done here because of flag handing (see Compile flags).
473 source_types = {'cxx':[], 'c':[], 'asm':[],
Ben Wagnerbc344042016-09-29 15:41:53 -0400474 'obj':[], 'obj_target':[], 'input':[], 'other':[]}
475
476 # TODO .def files on Windows
477 for source in target.properties.get('sources', []):
bungemane95ea082016-09-28 16:45:35 -0400478 _, ext = posixpath.splitext(source)
Ben Wagnerbc344042016-09-29 15:41:53 -0400479 source_abs_path = project.GetAbsolutePath(source)
bungemane95ea082016-09-28 16:45:35 -0400480 source_types[source_file_types.get(ext, 'other')].append(source_abs_path)
481
Ben Wagnerbc344042016-09-29 15:41:53 -0400482 for input_path in target.properties.get('inputs', []):
483 input_abs_path = project.GetAbsolutePath(input_path)
484 source_types['input'].append(input_abs_path)
485
bungemane95ea082016-09-28 16:45:35 -0400486 # OBJECT library dependencies need to be listed as sources.
487 # Only executables and non-OBJECT libraries may reference an OBJECT library.
488 # https://gitlab.kitware.com/cmake/cmake/issues/14778
Ben Wagnerbc344042016-09-29 15:41:53 -0400489 if target.gn_type in gn_target_types_that_absorb_objects:
bungemane95ea082016-09-28 16:45:35 -0400490 object_dependencies = set()
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400491 project.GetObjectSourceDependencies(target.gn_name, object_dependencies)
bungemane95ea082016-09-28 16:45:35 -0400492 for dependency in object_dependencies:
Ben Wagnerbc344042016-09-29 15:41:53 -0400493 cmake_dependency_name = project.GetCMakeTargetName(dependency)
bungemane95ea082016-09-28 16:45:35 -0400494 obj_target_sources = '$<TARGET_OBJECTS:' + cmake_dependency_name + '>'
495 source_types['obj_target'].append(obj_target_sources)
496
497 sources = {}
498 for source_type, sources_of_type in source_types.items():
499 if sources_of_type:
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400500 sources[source_type] = '${target}__' + source_type + '_srcs'
bungemane95ea082016-09-28 16:45:35 -0400501 SetVariableList(out, sources[source_type], sources_of_type)
502 return sources
503
504
Ben Wagnerbc344042016-09-29 15:41:53 -0400505def WriteTarget(out, target, project):
bungemane95ea082016-09-28 16:45:35 -0400506 out.write('\n#')
Ben Wagnerbc344042016-09-29 15:41:53 -0400507 out.write(target.gn_name)
bungemane95ea082016-09-28 16:45:35 -0400508 out.write('\n')
509
bungemane95ea082016-09-28 16:45:35 -0400510 if target.cmake_type is None:
511 print ('Target %s has unknown target type %s, skipping.' %
Ben Wagnerbc344042016-09-29 15:41:53 -0400512 ( target.gn_name, target.gn_type ) )
bungemane95ea082016-09-28 16:45:35 -0400513 return
514
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400515 SetVariable(out, 'target', target.cmake_name)
516
Ben Wagnerbc344042016-09-29 15:41:53 -0400517 sources = WriteSourceVariables(out, target, project)
518
519 synthetic_dependencies = set()
520 if target.gn_type == 'action':
521 WriteAction(out, target, project, sources, synthetic_dependencies)
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400522 if target.gn_type == 'action_foreach':
523 WriteActionForEach(out, target, project, sources, synthetic_dependencies)
524 if target.gn_type == 'copy':
525 WriteCopy(out, target, project, sources, synthetic_dependencies)
bungemane95ea082016-09-28 16:45:35 -0400526
527 out.write(target.cmake_type.command)
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400528 out.write('("${target}"')
bungemane95ea082016-09-28 16:45:35 -0400529 if target.cmake_type.modifier is not None:
530 out.write(' ')
531 out.write(target.cmake_type.modifier)
532 for sources_type_name in sources.values():
533 WriteVariable(out, sources_type_name, ' ')
Ben Wagnerbc344042016-09-29 15:41:53 -0400534 if synthetic_dependencies:
535 out.write(' DEPENDS')
536 for synthetic_dependencie in synthetic_dependencies:
537 WriteVariable(out, synthetic_dependencie, ' ')
bungemane95ea082016-09-28 16:45:35 -0400538 out.write(')\n')
539
540 if target.cmake_type.command != 'add_custom_target':
Ben Wagnerbc344042016-09-29 15:41:53 -0400541 WriteCompilerFlags(out, target, project, sources)
bungemane95ea082016-09-28 16:45:35 -0400542
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400543 libraries = set()
544 nonlibraries = set()
545
546 dependencies = set(target.properties.get('deps', []))
547 # Transitive OBJECT libraries are in sources.
548 # Those sources are dependent on the OBJECT library dependencies.
549 # Those sources cannot bring in library dependencies.
550 object_dependencies = set()
551 if target.gn_type != 'source_set':
552 project.GetObjectLibraryDependencies(target.gn_name, object_dependencies)
553 for object_dependency in object_dependencies:
554 dependencies.update(project.targets.get(object_dependency).get('deps', []))
555
bungemane95ea082016-09-28 16:45:35 -0400556 for dependency in dependencies:
Ben Wagnerbc344042016-09-29 15:41:53 -0400557 gn_dependency_type = project.targets.get(dependency, {}).get('type', None)
bungemane95ea082016-09-28 16:45:35 -0400558 cmake_dependency_type = cmake_target_types.get(gn_dependency_type, None)
Ben Wagnerbc344042016-09-29 15:41:53 -0400559 cmake_dependency_name = project.GetCMakeTargetName(dependency)
bungemane95ea082016-09-28 16:45:35 -0400560 if cmake_dependency_type.command != 'add_library':
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400561 nonlibraries.add(cmake_dependency_name)
bungemane95ea082016-09-28 16:45:35 -0400562 elif cmake_dependency_type.modifier != 'OBJECT':
Ben Wagnerbc344042016-09-29 15:41:53 -0400563 if target.cmake_type.is_linkable:
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400564 libraries.add(cmake_dependency_name)
Ben Wagnerbc344042016-09-29 15:41:53 -0400565 else:
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400566 nonlibraries.add(cmake_dependency_name)
bungemane95ea082016-09-28 16:45:35 -0400567
568 # Non-library dependencies.
569 if nonlibraries:
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400570 out.write('add_dependencies("${target}"')
bungemane95ea082016-09-28 16:45:35 -0400571 for nonlibrary in nonlibraries:
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400572 out.write('\n "')
Ben Wagnerbc344042016-09-29 15:41:53 -0400573 out.write(nonlibrary)
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400574 out.write('"')
bungemane95ea082016-09-28 16:45:35 -0400575 out.write(')\n')
576
577 # Non-OBJECT library dependencies.
578 external_libraries = target.properties.get('libs', [])
579 if target.cmake_type.is_linkable and (external_libraries or libraries):
580 system_libraries = []
581 for external_library in external_libraries:
582 if '/' in external_library:
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400583 libraries.add(project.GetAbsolutePath(external_library))
bungemane95ea082016-09-28 16:45:35 -0400584 else:
585 if external_library.endswith('.framework'):
586 external_library = external_library[:-len('.framework')]
587 system_library = external_library + '__library'
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400588 out.write('find_library("')
589 out.write(CMakeStringEscape(system_library))
590 out.write('" "')
591 out.write(CMakeStringEscape(external_library))
592 out.write('")\n')
bungemane95ea082016-09-28 16:45:35 -0400593 system_libraries.append(system_library)
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400594 out.write('target_link_libraries("${target}"')
bungemane95ea082016-09-28 16:45:35 -0400595 for library in libraries:
596 out.write('\n "')
597 out.write(CMakeStringEscape(library))
598 out.write('"')
599 for system_library in system_libraries:
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400600 WriteVariable(out, system_library, '\n "')
601 out.write('"')
bungemane95ea082016-09-28 16:45:35 -0400602 out.write(')\n')
603
604
605def WriteProject(project):
Ben Wagnerbc344042016-09-29 15:41:53 -0400606 out = open(posixpath.join(project.build_path, 'CMakeLists.txt'), 'w+')
Ben Wagnerdc69dc72016-10-04 16:44:44 -0400607 out.write('# Generated by gn_to_cmake.py.\n')
bungeman623ef922016-09-23 08:16:04 -0700608 out.write('cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR)\n')
609 out.write('cmake_policy(VERSION 2.8.8)\n')
610
bungemane95ea082016-09-28 16:45:35 -0400611 # The following appears to be as-yet undocumented.
612 # http://public.kitware.com/Bug/view.php?id=8392
613 out.write('enable_language(ASM)\n')
614 # ASM-ATT does not support .S files.
615 # output.write('enable_language(ASM-ATT)\n')
bungeman623ef922016-09-23 08:16:04 -0700616
Ben Wagnerbc344042016-09-29 15:41:53 -0400617 for target_name in project.targets.keys():
bungeman623ef922016-09-23 08:16:04 -0700618 out.write('\n')
Ben Wagnerbc344042016-09-29 15:41:53 -0400619 WriteTarget(out, Target(target_name, project), project)
bungeman623ef922016-09-23 08:16:04 -0700620
bungeman623ef922016-09-23 08:16:04 -0700621
622def main():
623 if len(sys.argv) != 2:
624 print('Usage: ' + sys.argv[0] + ' <json_file_name>')
625 exit(1)
626
627 json_path = sys.argv[1]
628 project = None
629 with open(json_path, 'r') as json_file:
630 project = json.loads(json_file.read())
631
Ben Wagnerbc344042016-09-29 15:41:53 -0400632 WriteProject(Project(project))
bungemane95ea082016-09-28 16:45:35 -0400633
bungeman623ef922016-09-23 08:16:04 -0700634
635if __name__ == "__main__":
636 main()