Initial commit
[yaffs-website] / node_modules / node-gyp / gyp / pylib / gyp / generator / cmake.py
1 # Copyright (c) 2013 Google Inc. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """cmake output module
6
7 This module is under development and should be considered experimental.
8
9 This module produces cmake (2.8.8+) input as its output. One CMakeLists.txt is
10 created for each configuration.
11
12 This module's original purpose was to support editing in IDEs like KDevelop
13 which use CMake for project management. It is also possible to use CMake to
14 generate projects for other IDEs such as eclipse cdt and code::blocks. QtCreator
15 will convert the CMakeLists.txt to a code::blocks cbp for the editor to read,
16 but build using CMake. As a result QtCreator editor is unaware of compiler
17 defines. The generated CMakeLists.txt can also be used to build on Linux. There
18 is currently no support for building on platforms other than Linux.
19
20 The generated CMakeLists.txt should properly compile all projects. However,
21 there is a mismatch between gyp and cmake with regard to linking. All attempts
22 are made to work around this, but CMake sometimes sees -Wl,--start-group as a
23 library and incorrectly repeats it. As a result the output of this generator
24 should not be relied on for building.
25
26 When using with kdevelop, use version 4.4+. Previous versions of kdevelop will
27 not be able to find the header file directories described in the generated
28 CMakeLists.txt file.
29 """
30
31 import multiprocessing
32 import os
33 import signal
34 import string
35 import subprocess
36 import gyp.common
37
38 generator_default_variables = {
39   'EXECUTABLE_PREFIX': '',
40   'EXECUTABLE_SUFFIX': '',
41   'STATIC_LIB_PREFIX': 'lib',
42   'STATIC_LIB_SUFFIX': '.a',
43   'SHARED_LIB_PREFIX': 'lib',
44   'SHARED_LIB_SUFFIX': '.so',
45   'SHARED_LIB_DIR': '${builddir}/lib.${TOOLSET}',
46   'LIB_DIR': '${obj}.${TOOLSET}',
47   'INTERMEDIATE_DIR': '${obj}.${TOOLSET}/${TARGET}/geni',
48   'SHARED_INTERMEDIATE_DIR': '${obj}/gen',
49   'PRODUCT_DIR': '${builddir}',
50   'RULE_INPUT_PATH': '${RULE_INPUT_PATH}',
51   'RULE_INPUT_DIRNAME': '${RULE_INPUT_DIRNAME}',
52   'RULE_INPUT_NAME': '${RULE_INPUT_NAME}',
53   'RULE_INPUT_ROOT': '${RULE_INPUT_ROOT}',
54   'RULE_INPUT_EXT': '${RULE_INPUT_EXT}',
55   'CONFIGURATION_NAME': '${configuration}',
56 }
57
58 FULL_PATH_VARS = ('${CMAKE_CURRENT_LIST_DIR}', '${builddir}', '${obj}')
59
60 generator_supports_multiple_toolsets = True
61 generator_wants_static_library_dependencies_adjusted = True
62
63 COMPILABLE_EXTENSIONS = {
64   '.c': 'cc',
65   '.cc': 'cxx',
66   '.cpp': 'cxx',
67   '.cxx': 'cxx',
68   '.s': 's', # cc
69   '.S': 's', # cc
70 }
71
72
73 def RemovePrefix(a, prefix):
74   """Returns 'a' without 'prefix' if it starts with 'prefix'."""
75   return a[len(prefix):] if a.startswith(prefix) else a
76
77
78 def CalculateVariables(default_variables, params):
79   """Calculate additional variables for use in the build (called by gyp)."""
80   default_variables.setdefault('OS', gyp.common.GetFlavor(params))
81
82
83 def Compilable(filename):
84   """Return true if the file is compilable (should be in OBJS)."""
85   return any(filename.endswith(e) for e in COMPILABLE_EXTENSIONS)
86
87
88 def Linkable(filename):
89   """Return true if the file is linkable (should be on the link line)."""
90   return filename.endswith('.o')
91
92
93 def NormjoinPathForceCMakeSource(base_path, rel_path):
94   """Resolves rel_path against base_path and returns the result.
95
96   If rel_path is an absolute path it is returned unchanged.
97   Otherwise it is resolved against base_path and normalized.
98   If the result is a relative path, it is forced to be relative to the
99   CMakeLists.txt.
100   """
101   if os.path.isabs(rel_path):
102     return rel_path
103   if any([rel_path.startswith(var) for var in FULL_PATH_VARS]):
104     return rel_path
105   # TODO: do we need to check base_path for absolute variables as well?
106   return os.path.join('${CMAKE_CURRENT_LIST_DIR}',
107                       os.path.normpath(os.path.join(base_path, rel_path)))
108
109
110 def NormjoinPath(base_path, rel_path):
111   """Resolves rel_path against base_path and returns the result.
112   TODO: what is this really used for?
113   If rel_path begins with '$' it is returned unchanged.
114   Otherwise it is resolved against base_path if relative, then normalized.
115   """
116   if rel_path.startswith('$') and not rel_path.startswith('${configuration}'):
117     return rel_path
118   return os.path.normpath(os.path.join(base_path, rel_path))
119
120
121 def CMakeStringEscape(a):
122   """Escapes the string 'a' for use inside a CMake string.
123
124   This means escaping
125   '\' otherwise it may be seen as modifying the next character
126   '"' otherwise it will end the string
127   ';' otherwise the string becomes a list
128
129   The following do not need to be escaped
130   '#' when the lexer is in string state, this does not start a comment
131
132   The following are yet unknown
133   '$' generator variables (like ${obj}) must not be escaped,
134       but text $ should be escaped
135       what is wanted is to know which $ come from generator variables
136   """
137   return a.replace('\\', '\\\\').replace(';', '\\;').replace('"', '\\"')
138
139
140 def SetFileProperty(output, source_name, property_name, values, sep):
141   """Given a set of source file, sets the given property on them."""
142   output.write('set_source_files_properties(')
143   output.write(source_name)
144   output.write(' PROPERTIES ')
145   output.write(property_name)
146   output.write(' "')
147   for value in values:
148     output.write(CMakeStringEscape(value))
149     output.write(sep)
150   output.write('")\n')
151
152
153 def SetFilesProperty(output, variable, property_name, values, sep):
154   """Given a set of source files, sets the given property on them."""
155   output.write('set_source_files_properties(')
156   WriteVariable(output, variable)
157   output.write(' PROPERTIES ')
158   output.write(property_name)
159   output.write(' "')
160   for value in values:
161     output.write(CMakeStringEscape(value))
162     output.write(sep)
163   output.write('")\n')
164
165
166 def SetTargetProperty(output, target_name, property_name, values, sep=''):
167   """Given a target, sets the given property."""
168   output.write('set_target_properties(')
169   output.write(target_name)
170   output.write(' PROPERTIES ')
171   output.write(property_name)
172   output.write(' "')
173   for value in values:
174     output.write(CMakeStringEscape(value))
175     output.write(sep)
176   output.write('")\n')
177
178
179 def SetVariable(output, variable_name, value):
180   """Sets a CMake variable."""
181   output.write('set(')
182   output.write(variable_name)
183   output.write(' "')
184   output.write(CMakeStringEscape(value))
185   output.write('")\n')
186
187
188 def SetVariableList(output, variable_name, values):
189   """Sets a CMake variable to a list."""
190   if not values:
191     return SetVariable(output, variable_name, "")
192   if len(values) == 1:
193     return SetVariable(output, variable_name, values[0])
194   output.write('list(APPEND ')
195   output.write(variable_name)
196   output.write('\n  "')
197   output.write('"\n  "'.join([CMakeStringEscape(value) for value in values]))
198   output.write('")\n')
199
200
201 def UnsetVariable(output, variable_name):
202   """Unsets a CMake variable."""
203   output.write('unset(')
204   output.write(variable_name)
205   output.write(')\n')
206
207
208 def WriteVariable(output, variable_name, prepend=None):
209   if prepend:
210     output.write(prepend)
211   output.write('${')
212   output.write(variable_name)
213   output.write('}')
214
215
216 class CMakeTargetType(object):
217   def __init__(self, command, modifier, property_modifier):
218     self.command = command
219     self.modifier = modifier
220     self.property_modifier = property_modifier
221
222
223 cmake_target_type_from_gyp_target_type = {
224   'executable': CMakeTargetType('add_executable', None, 'RUNTIME'),
225   'static_library': CMakeTargetType('add_library', 'STATIC', 'ARCHIVE'),
226   'shared_library': CMakeTargetType('add_library', 'SHARED', 'LIBRARY'),
227   'loadable_module': CMakeTargetType('add_library', 'MODULE', 'LIBRARY'),
228   'none': CMakeTargetType('add_custom_target', 'SOURCES', None),
229 }
230
231
232 def StringToCMakeTargetName(a):
233   """Converts the given string 'a' to a valid CMake target name.
234
235   All invalid characters are replaced by '_'.
236   Invalid for cmake: ' ', '/', '(', ')', '"'
237   Invalid for make: ':'
238   Invalid for unknown reasons but cause failures: '.'
239   """
240   return a.translate(string.maketrans(' /():."', '_______'))
241
242
243 def WriteActions(target_name, actions, extra_sources, extra_deps,
244                  path_to_gyp, output):
245   """Write CMake for the 'actions' in the target.
246
247   Args:
248     target_name: the name of the CMake target being generated.
249     actions: the Gyp 'actions' dict for this target.
250     extra_sources: [(<cmake_src>, <src>)] to append with generated source files.
251     extra_deps: [<cmake_taget>] to append with generated targets.
252     path_to_gyp: relative path from CMakeLists.txt being generated to
253         the Gyp file in which the target being generated is defined.
254   """
255   for action in actions:
256     action_name = StringToCMakeTargetName(action['action_name'])
257     action_target_name = '%s__%s' % (target_name, action_name)
258
259     inputs = action['inputs']
260     inputs_name = action_target_name + '__input'
261     SetVariableList(output, inputs_name,
262         [NormjoinPathForceCMakeSource(path_to_gyp, dep) for dep in inputs])
263
264     outputs = action['outputs']
265     cmake_outputs = [NormjoinPathForceCMakeSource(path_to_gyp, out)
266                      for out in outputs]
267     outputs_name = action_target_name + '__output'
268     SetVariableList(output, outputs_name, cmake_outputs)
269
270     # Build up a list of outputs.
271     # Collect the output dirs we'll need.
272     dirs = set(dir for dir in (os.path.dirname(o) for o in outputs) if dir)
273
274     if int(action.get('process_outputs_as_sources', False)):
275       extra_sources.extend(zip(cmake_outputs, outputs))
276
277     # add_custom_command
278     output.write('add_custom_command(OUTPUT ')
279     WriteVariable(output, outputs_name)
280     output.write('\n')
281
282     if len(dirs) > 0:
283       for directory in dirs:
284         output.write('  COMMAND ${CMAKE_COMMAND} -E make_directory ')
285         output.write(directory)
286         output.write('\n')
287
288     output.write('  COMMAND ')
289     output.write(gyp.common.EncodePOSIXShellList(action['action']))
290     output.write('\n')
291
292     output.write('  DEPENDS ')
293     WriteVariable(output, inputs_name)
294     output.write('\n')
295
296     output.write('  WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/')
297     output.write(path_to_gyp)
298     output.write('\n')
299
300     output.write('  COMMENT ')
301     if 'message' in action:
302       output.write(action['message'])
303     else:
304       output.write(action_target_name)
305     output.write('\n')
306
307     output.write('  VERBATIM\n')
308     output.write(')\n')
309
310     # add_custom_target
311     output.write('add_custom_target(')
312     output.write(action_target_name)
313     output.write('\n  DEPENDS ')
314     WriteVariable(output, outputs_name)
315     output.write('\n  SOURCES ')
316     WriteVariable(output, inputs_name)
317     output.write('\n)\n')
318
319     extra_deps.append(action_target_name)
320
321
322 def NormjoinRulePathForceCMakeSource(base_path, rel_path, rule_source):
323   if rel_path.startswith(("${RULE_INPUT_PATH}","${RULE_INPUT_DIRNAME}")):
324     if any([rule_source.startswith(var) for var in FULL_PATH_VARS]):
325       return rel_path
326   return NormjoinPathForceCMakeSource(base_path, rel_path)
327
328
329 def WriteRules(target_name, rules, extra_sources, extra_deps,
330                path_to_gyp, output):
331   """Write CMake for the 'rules' in the target.
332
333   Args:
334     target_name: the name of the CMake target being generated.
335     actions: the Gyp 'actions' dict for this target.
336     extra_sources: [(<cmake_src>, <src>)] to append with generated source files.
337     extra_deps: [<cmake_taget>] to append with generated targets.
338     path_to_gyp: relative path from CMakeLists.txt being generated to
339         the Gyp file in which the target being generated is defined.
340   """
341   for rule in rules:
342     rule_name = StringToCMakeTargetName(target_name + '__' + rule['rule_name'])
343
344     inputs = rule.get('inputs', [])
345     inputs_name = rule_name + '__input'
346     SetVariableList(output, inputs_name,
347         [NormjoinPathForceCMakeSource(path_to_gyp, dep) for dep in inputs])
348     outputs = rule['outputs']
349     var_outputs = []
350
351     for count, rule_source in enumerate(rule.get('rule_sources', [])):
352       action_name = rule_name + '_' + str(count)
353
354       rule_source_dirname, rule_source_basename = os.path.split(rule_source)
355       rule_source_root, rule_source_ext = os.path.splitext(rule_source_basename)
356
357       SetVariable(output, 'RULE_INPUT_PATH', rule_source)
358       SetVariable(output, 'RULE_INPUT_DIRNAME', rule_source_dirname)
359       SetVariable(output, 'RULE_INPUT_NAME', rule_source_basename)
360       SetVariable(output, 'RULE_INPUT_ROOT', rule_source_root)
361       SetVariable(output, 'RULE_INPUT_EXT', rule_source_ext)
362
363       # Build up a list of outputs.
364       # Collect the output dirs we'll need.
365       dirs = set(dir for dir in (os.path.dirname(o) for o in outputs) if dir)
366
367       # Create variables for the output, as 'local' variable will be unset.
368       these_outputs = []
369       for output_index, out in enumerate(outputs):
370         output_name = action_name + '_' + str(output_index)
371         SetVariable(output, output_name,
372                      NormjoinRulePathForceCMakeSource(path_to_gyp, out,
373                                                       rule_source))
374         if int(rule.get('process_outputs_as_sources', False)):
375           extra_sources.append(('${' + output_name + '}', out))
376         these_outputs.append('${' + output_name + '}')
377         var_outputs.append('${' + output_name + '}')
378
379       # add_custom_command
380       output.write('add_custom_command(OUTPUT\n')
381       for out in these_outputs:
382         output.write('  ')
383         output.write(out)
384         output.write('\n')
385
386       for directory in dirs:
387         output.write('  COMMAND ${CMAKE_COMMAND} -E make_directory ')
388         output.write(directory)
389         output.write('\n')
390
391       output.write('  COMMAND ')
392       output.write(gyp.common.EncodePOSIXShellList(rule['action']))
393       output.write('\n')
394
395       output.write('  DEPENDS ')
396       WriteVariable(output, inputs_name)
397       output.write(' ')
398       output.write(NormjoinPath(path_to_gyp, rule_source))
399       output.write('\n')
400
401       # CMAKE_CURRENT_LIST_DIR is where the CMakeLists.txt lives.
402       # The cwd is the current build directory.
403       output.write('  WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/')
404       output.write(path_to_gyp)
405       output.write('\n')
406
407       output.write('  COMMENT ')
408       if 'message' in rule:
409         output.write(rule['message'])
410       else:
411         output.write(action_name)
412       output.write('\n')
413
414       output.write('  VERBATIM\n')
415       output.write(')\n')
416
417       UnsetVariable(output, 'RULE_INPUT_PATH')
418       UnsetVariable(output, 'RULE_INPUT_DIRNAME')
419       UnsetVariable(output, 'RULE_INPUT_NAME')
420       UnsetVariable(output, 'RULE_INPUT_ROOT')
421       UnsetVariable(output, 'RULE_INPUT_EXT')
422
423     # add_custom_target
424     output.write('add_custom_target(')
425     output.write(rule_name)
426     output.write(' DEPENDS\n')
427     for out in var_outputs:
428       output.write('  ')
429       output.write(out)
430       output.write('\n')
431     output.write('SOURCES ')
432     WriteVariable(output, inputs_name)
433     output.write('\n')
434     for rule_source in rule.get('rule_sources', []):
435       output.write('  ')
436       output.write(NormjoinPath(path_to_gyp, rule_source))
437       output.write('\n')
438     output.write(')\n')
439
440     extra_deps.append(rule_name)
441
442
443 def WriteCopies(target_name, copies, extra_deps, path_to_gyp, output):
444   """Write CMake for the 'copies' in the target.
445
446   Args:
447     target_name: the name of the CMake target being generated.
448     actions: the Gyp 'actions' dict for this target.
449     extra_deps: [<cmake_taget>] to append with generated targets.
450     path_to_gyp: relative path from CMakeLists.txt being generated to
451         the Gyp file in which the target being generated is defined.
452   """
453   copy_name = target_name + '__copies'
454
455   # CMake gets upset with custom targets with OUTPUT which specify no output.
456   have_copies = any(copy['files'] for copy in copies)
457   if not have_copies:
458     output.write('add_custom_target(')
459     output.write(copy_name)
460     output.write(')\n')
461     extra_deps.append(copy_name)
462     return
463
464   class Copy(object):
465     def __init__(self, ext, command):
466       self.cmake_inputs = []
467       self.cmake_outputs = []
468       self.gyp_inputs = []
469       self.gyp_outputs = []
470       self.ext = ext
471       self.inputs_name = None
472       self.outputs_name = None
473       self.command = command
474
475   file_copy = Copy('', 'copy')
476   dir_copy = Copy('_dirs', 'copy_directory')
477
478   for copy in copies:
479     files = copy['files']
480     destination = copy['destination']
481     for src in files:
482       path = os.path.normpath(src)
483       basename = os.path.split(path)[1]
484       dst = os.path.join(destination, basename)
485
486       copy = file_copy if os.path.basename(src) else dir_copy
487
488       copy.cmake_inputs.append(NormjoinPathForceCMakeSource(path_to_gyp, src))
489       copy.cmake_outputs.append(NormjoinPathForceCMakeSource(path_to_gyp, dst))
490       copy.gyp_inputs.append(src)
491       copy.gyp_outputs.append(dst)
492
493   for copy in (file_copy, dir_copy):
494     if copy.cmake_inputs:
495       copy.inputs_name = copy_name + '__input' + copy.ext
496       SetVariableList(output, copy.inputs_name, copy.cmake_inputs)
497
498       copy.outputs_name = copy_name + '__output' + copy.ext
499       SetVariableList(output, copy.outputs_name, copy.cmake_outputs)
500
501   # add_custom_command
502   output.write('add_custom_command(\n')
503
504   output.write('OUTPUT')
505   for copy in (file_copy, dir_copy):
506     if copy.outputs_name:
507       WriteVariable(output, copy.outputs_name, ' ')
508   output.write('\n')
509
510   for copy in (file_copy, dir_copy):
511     for src, dst in zip(copy.gyp_inputs, copy.gyp_outputs):
512       # 'cmake -E copy src dst' will create the 'dst' directory if needed.
513       output.write('COMMAND ${CMAKE_COMMAND} -E %s ' % copy.command)
514       output.write(src)
515       output.write(' ')
516       output.write(dst)
517       output.write("\n")
518
519   output.write('DEPENDS')
520   for copy in (file_copy, dir_copy):
521     if copy.inputs_name:
522       WriteVariable(output, copy.inputs_name, ' ')
523   output.write('\n')
524
525   output.write('WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/')
526   output.write(path_to_gyp)
527   output.write('\n')
528
529   output.write('COMMENT Copying for ')
530   output.write(target_name)
531   output.write('\n')
532
533   output.write('VERBATIM\n')
534   output.write(')\n')
535
536   # add_custom_target
537   output.write('add_custom_target(')
538   output.write(copy_name)
539   output.write('\n  DEPENDS')
540   for copy in (file_copy, dir_copy):
541     if copy.outputs_name:
542       WriteVariable(output, copy.outputs_name, ' ')
543   output.write('\n  SOURCES')
544   if file_copy.inputs_name:
545     WriteVariable(output, file_copy.inputs_name, ' ')
546   output.write('\n)\n')
547
548   extra_deps.append(copy_name)
549
550
551 def CreateCMakeTargetBaseName(qualified_target):
552   """This is the name we would like the target to have."""
553   _, gyp_target_name, gyp_target_toolset = (
554       gyp.common.ParseQualifiedTarget(qualified_target))
555   cmake_target_base_name = gyp_target_name
556   if gyp_target_toolset and gyp_target_toolset != 'target':
557     cmake_target_base_name += '_' + gyp_target_toolset
558   return StringToCMakeTargetName(cmake_target_base_name)
559
560
561 def CreateCMakeTargetFullName(qualified_target):
562   """An unambiguous name for the target."""
563   gyp_file, gyp_target_name, gyp_target_toolset = (
564       gyp.common.ParseQualifiedTarget(qualified_target))
565   cmake_target_full_name = gyp_file + ':' + gyp_target_name
566   if gyp_target_toolset and gyp_target_toolset != 'target':
567     cmake_target_full_name += '_' + gyp_target_toolset
568   return StringToCMakeTargetName(cmake_target_full_name)
569
570
571 class CMakeNamer(object):
572   """Converts Gyp target names into CMake target names.
573
574   CMake requires that target names be globally unique. One way to ensure
575   this is to fully qualify the names of the targets. Unfortunatly, this
576   ends up with all targets looking like "chrome_chrome_gyp_chrome" instead
577   of just "chrome". If this generator were only interested in building, it
578   would be possible to fully qualify all target names, then create
579   unqualified target names which depend on all qualified targets which
580   should have had that name. This is more or less what the 'make' generator
581   does with aliases. However, one goal of this generator is to create CMake
582   files for use with IDEs, and fully qualified names are not as user
583   friendly.
584
585   Since target name collision is rare, we do the above only when required.
586
587   Toolset variants are always qualified from the base, as this is required for
588   building. However, it also makes sense for an IDE, as it is possible for
589   defines to be different.
590   """
591   def __init__(self, target_list):
592     self.cmake_target_base_names_conficting = set()
593
594     cmake_target_base_names_seen = set()
595     for qualified_target in target_list:
596       cmake_target_base_name = CreateCMakeTargetBaseName(qualified_target)
597
598       if cmake_target_base_name not in cmake_target_base_names_seen:
599         cmake_target_base_names_seen.add(cmake_target_base_name)
600       else:
601         self.cmake_target_base_names_conficting.add(cmake_target_base_name)
602
603   def CreateCMakeTargetName(self, qualified_target):
604     base_name = CreateCMakeTargetBaseName(qualified_target)
605     if base_name in self.cmake_target_base_names_conficting:
606       return CreateCMakeTargetFullName(qualified_target)
607     return base_name
608
609
610 def WriteTarget(namer, qualified_target, target_dicts, build_dir, config_to_use,
611                 options, generator_flags, all_qualified_targets, output):
612
613   # The make generator does this always.
614   # TODO: It would be nice to be able to tell CMake all dependencies.
615   circular_libs = generator_flags.get('circular', True)
616
617   if not generator_flags.get('standalone', False):
618     output.write('\n#')
619     output.write(qualified_target)
620     output.write('\n')
621
622   gyp_file, _, _ = gyp.common.ParseQualifiedTarget(qualified_target)
623   rel_gyp_file = gyp.common.RelativePath(gyp_file, options.toplevel_dir)
624   rel_gyp_dir = os.path.dirname(rel_gyp_file)
625
626   # Relative path from build dir to top dir.
627   build_to_top = gyp.common.InvertRelativePath(build_dir, options.toplevel_dir)
628   # Relative path from build dir to gyp dir.
629   build_to_gyp = os.path.join(build_to_top, rel_gyp_dir)
630
631   path_from_cmakelists_to_gyp = build_to_gyp
632
633   spec = target_dicts.get(qualified_target, {})
634   config = spec.get('configurations', {}).get(config_to_use, {})
635
636   target_name = spec.get('target_name', '<missing target name>')
637   target_type = spec.get('type', '<missing target type>')
638   target_toolset = spec.get('toolset')
639
640   cmake_target_type = cmake_target_type_from_gyp_target_type.get(target_type)
641   if cmake_target_type is None:
642     print ('Target %s has unknown target type %s, skipping.' %
643           (        target_name,               target_type  ) )
644     return
645
646   SetVariable(output, 'TARGET', target_name)
647   SetVariable(output, 'TOOLSET', target_toolset)
648
649   cmake_target_name = namer.CreateCMakeTargetName(qualified_target)
650
651   extra_sources = []
652   extra_deps = []
653
654   # Actions must come first, since they can generate more OBJs for use below.
655   if 'actions' in spec:
656     WriteActions(cmake_target_name, spec['actions'], extra_sources, extra_deps,
657                  path_from_cmakelists_to_gyp, output)
658
659   # Rules must be early like actions.
660   if 'rules' in spec:
661     WriteRules(cmake_target_name, spec['rules'], extra_sources, extra_deps,
662                path_from_cmakelists_to_gyp, output)
663
664   # Copies
665   if 'copies' in spec:
666     WriteCopies(cmake_target_name, spec['copies'], extra_deps,
667                 path_from_cmakelists_to_gyp, output)
668
669   # Target and sources
670   srcs = spec.get('sources', [])
671
672   # Gyp separates the sheep from the goats based on file extensions.
673   # A full separation is done here because of flag handing (see below).
674   s_sources = []
675   c_sources = []
676   cxx_sources = []
677   linkable_sources = []
678   other_sources = []
679   for src in srcs:
680     _, ext = os.path.splitext(src)
681     src_type = COMPILABLE_EXTENSIONS.get(ext, None)
682     src_norm_path = NormjoinPath(path_from_cmakelists_to_gyp, src);
683
684     if src_type == 's':
685       s_sources.append(src_norm_path)
686     elif src_type == 'cc':
687       c_sources.append(src_norm_path)
688     elif src_type == 'cxx':
689       cxx_sources.append(src_norm_path)
690     elif Linkable(ext):
691       linkable_sources.append(src_norm_path)
692     else:
693       other_sources.append(src_norm_path)
694
695   for extra_source in extra_sources:
696     src, real_source = extra_source
697     _, ext = os.path.splitext(real_source)
698     src_type = COMPILABLE_EXTENSIONS.get(ext, None)
699
700     if src_type == 's':
701       s_sources.append(src)
702     elif src_type == 'cc':
703       c_sources.append(src)
704     elif src_type == 'cxx':
705       cxx_sources.append(src)
706     elif Linkable(ext):
707       linkable_sources.append(src)
708     else:
709       other_sources.append(src)
710
711   s_sources_name = None
712   if s_sources:
713     s_sources_name = cmake_target_name + '__asm_srcs'
714     SetVariableList(output, s_sources_name, s_sources)
715
716   c_sources_name = None
717   if c_sources:
718     c_sources_name = cmake_target_name + '__c_srcs'
719     SetVariableList(output, c_sources_name, c_sources)
720
721   cxx_sources_name = None
722   if cxx_sources:
723     cxx_sources_name = cmake_target_name + '__cxx_srcs'
724     SetVariableList(output, cxx_sources_name, cxx_sources)
725
726   linkable_sources_name = None
727   if linkable_sources:
728     linkable_sources_name = cmake_target_name + '__linkable_srcs'
729     SetVariableList(output, linkable_sources_name, linkable_sources)
730
731   other_sources_name = None
732   if other_sources:
733     other_sources_name = cmake_target_name + '__other_srcs'
734     SetVariableList(output, other_sources_name, other_sources)
735
736   # CMake gets upset when executable targets provide no sources.
737   # http://www.cmake.org/pipermail/cmake/2010-July/038461.html
738   dummy_sources_name = None
739   has_sources = (s_sources_name or
740                  c_sources_name or
741                  cxx_sources_name or
742                  linkable_sources_name or
743                  other_sources_name)
744   if target_type == 'executable' and not has_sources:
745     dummy_sources_name = cmake_target_name + '__dummy_srcs'
746     SetVariable(output, dummy_sources_name,
747                 "${obj}.${TOOLSET}/${TARGET}/genc/dummy.c")
748     output.write('if(NOT EXISTS "')
749     WriteVariable(output, dummy_sources_name)
750     output.write('")\n')
751     output.write('  file(WRITE "')
752     WriteVariable(output, dummy_sources_name)
753     output.write('" "")\n')
754     output.write("endif()\n")
755
756
757   # CMake is opposed to setting linker directories and considers the practice
758   # of setting linker directories dangerous. Instead, it favors the use of
759   # find_library and passing absolute paths to target_link_libraries.
760   # However, CMake does provide the command link_directories, which adds
761   # link directories to targets defined after it is called.
762   # As a result, link_directories must come before the target definition.
763   # CMake unfortunately has no means of removing entries from LINK_DIRECTORIES.
764   library_dirs = config.get('library_dirs')
765   if library_dirs is not None:
766     output.write('link_directories(')
767     for library_dir in library_dirs:
768       output.write(' ')
769       output.write(NormjoinPath(path_from_cmakelists_to_gyp, library_dir))
770       output.write('\n')
771     output.write(')\n')
772
773   output.write(cmake_target_type.command)
774   output.write('(')
775   output.write(cmake_target_name)
776
777   if cmake_target_type.modifier is not None:
778     output.write(' ')
779     output.write(cmake_target_type.modifier)
780
781   if s_sources_name:
782     WriteVariable(output, s_sources_name, ' ')
783   if c_sources_name:
784     WriteVariable(output, c_sources_name, ' ')
785   if cxx_sources_name:
786     WriteVariable(output, cxx_sources_name, ' ')
787   if linkable_sources_name:
788     WriteVariable(output, linkable_sources_name, ' ')
789   if other_sources_name:
790     WriteVariable(output, other_sources_name, ' ')
791   if dummy_sources_name:
792     WriteVariable(output, dummy_sources_name, ' ')
793
794   output.write(')\n')
795
796   # Let CMake know if the 'all' target should depend on this target.
797   exclude_from_all = ('TRUE' if qualified_target not in all_qualified_targets
798                              else 'FALSE')
799   SetTargetProperty(output, cmake_target_name,
800                       'EXCLUDE_FROM_ALL', exclude_from_all)
801   for extra_target_name in extra_deps:
802     SetTargetProperty(output, extra_target_name,
803                         'EXCLUDE_FROM_ALL', exclude_from_all)
804
805   # Output name and location.
806   if target_type != 'none':
807     # Link as 'C' if there are no other files
808     if not c_sources and not cxx_sources:
809       SetTargetProperty(output, cmake_target_name, 'LINKER_LANGUAGE', ['C'])
810
811     # Mark uncompiled sources as uncompiled.
812     if other_sources_name:
813       output.write('set_source_files_properties(')
814       WriteVariable(output, other_sources_name, '')
815       output.write(' PROPERTIES HEADER_FILE_ONLY "TRUE")\n')
816
817     # Mark object sources as linkable.
818     if linkable_sources_name:
819       output.write('set_source_files_properties(')
820       WriteVariable(output, other_sources_name, '')
821       output.write(' PROPERTIES EXTERNAL_OBJECT "TRUE")\n')
822
823     # Output directory
824     target_output_directory = spec.get('product_dir')
825     if target_output_directory is None:
826       if target_type in ('executable', 'loadable_module'):
827         target_output_directory = generator_default_variables['PRODUCT_DIR']
828       elif target_type == 'shared_library':
829         target_output_directory = '${builddir}/lib.${TOOLSET}'
830       elif spec.get('standalone_static_library', False):
831         target_output_directory = generator_default_variables['PRODUCT_DIR']
832       else:
833         base_path = gyp.common.RelativePath(os.path.dirname(gyp_file),
834                                             options.toplevel_dir)
835         target_output_directory = '${obj}.${TOOLSET}'
836         target_output_directory = (
837             os.path.join(target_output_directory, base_path))
838
839     cmake_target_output_directory = NormjoinPathForceCMakeSource(
840                                         path_from_cmakelists_to_gyp,
841                                         target_output_directory)
842     SetTargetProperty(output,
843         cmake_target_name,
844         cmake_target_type.property_modifier + '_OUTPUT_DIRECTORY',
845         cmake_target_output_directory)
846
847     # Output name
848     default_product_prefix = ''
849     default_product_name = target_name
850     default_product_ext = ''
851     if target_type == 'static_library':
852       static_library_prefix = generator_default_variables['STATIC_LIB_PREFIX']
853       default_product_name = RemovePrefix(default_product_name,
854                                           static_library_prefix)
855       default_product_prefix = static_library_prefix
856       default_product_ext = generator_default_variables['STATIC_LIB_SUFFIX']
857
858     elif target_type in ('loadable_module', 'shared_library'):
859       shared_library_prefix = generator_default_variables['SHARED_LIB_PREFIX']
860       default_product_name = RemovePrefix(default_product_name,
861                                           shared_library_prefix)
862       default_product_prefix = shared_library_prefix
863       default_product_ext = generator_default_variables['SHARED_LIB_SUFFIX']
864
865     elif target_type != 'executable':
866       print ('ERROR: What output file should be generated?',
867               'type', target_type, 'target', target_name)
868
869     product_prefix = spec.get('product_prefix', default_product_prefix)
870     product_name = spec.get('product_name', default_product_name)
871     product_ext = spec.get('product_extension')
872     if product_ext:
873       product_ext = '.' + product_ext
874     else:
875       product_ext = default_product_ext
876
877     SetTargetProperty(output, cmake_target_name, 'PREFIX', product_prefix)
878     SetTargetProperty(output, cmake_target_name,
879                         cmake_target_type.property_modifier + '_OUTPUT_NAME',
880                         product_name)
881     SetTargetProperty(output, cmake_target_name, 'SUFFIX', product_ext)
882
883     # Make the output of this target referenceable as a source.
884     cmake_target_output_basename = product_prefix + product_name + product_ext
885     cmake_target_output = os.path.join(cmake_target_output_directory,
886                                        cmake_target_output_basename)
887     SetFileProperty(output, cmake_target_output, 'GENERATED', ['TRUE'], '')
888
889     # Includes
890     includes = config.get('include_dirs')
891     if includes:
892       # This (target include directories) is what requires CMake 2.8.8
893       includes_name = cmake_target_name + '__include_dirs'
894       SetVariableList(output, includes_name,
895           [NormjoinPathForceCMakeSource(path_from_cmakelists_to_gyp, include)
896            for include in includes])
897       output.write('set_property(TARGET ')
898       output.write(cmake_target_name)
899       output.write(' APPEND PROPERTY INCLUDE_DIRECTORIES ')
900       WriteVariable(output, includes_name, '')
901       output.write(')\n')
902
903     # Defines
904     defines = config.get('defines')
905     if defines is not None:
906       SetTargetProperty(output,
907                           cmake_target_name,
908                           'COMPILE_DEFINITIONS',
909                           defines,
910                           ';')
911
912     # Compile Flags - http://www.cmake.org/Bug/view.php?id=6493
913     # CMake currently does not have target C and CXX flags.
914     # So, instead of doing...
915
916     # cflags_c = config.get('cflags_c')
917     # if cflags_c is not None:
918     #   SetTargetProperty(output, cmake_target_name,
919     #                       'C_COMPILE_FLAGS', cflags_c, ' ')
920
921     # cflags_cc = config.get('cflags_cc')
922     # if cflags_cc is not None:
923     #   SetTargetProperty(output, cmake_target_name,
924     #                       'CXX_COMPILE_FLAGS', cflags_cc, ' ')
925
926     # Instead we must...
927     cflags = config.get('cflags', [])
928     cflags_c = config.get('cflags_c', [])
929     cflags_cxx = config.get('cflags_cc', [])
930     if (not cflags_c or not c_sources) and (not cflags_cxx or not cxx_sources):
931       SetTargetProperty(output, cmake_target_name, 'COMPILE_FLAGS', cflags, ' ')
932
933     elif c_sources and not (s_sources or cxx_sources):
934       flags = []
935       flags.extend(cflags)
936       flags.extend(cflags_c)
937       SetTargetProperty(output, cmake_target_name, 'COMPILE_FLAGS', flags, ' ')
938
939     elif cxx_sources and not (s_sources or c_sources):
940       flags = []
941       flags.extend(cflags)
942       flags.extend(cflags_cxx)
943       SetTargetProperty(output, cmake_target_name, 'COMPILE_FLAGS', flags, ' ')
944
945     else:
946       # TODO: This is broken, one cannot generally set properties on files,
947       # as other targets may require different properties on the same files.
948       if s_sources and cflags:
949         SetFilesProperty(output, s_sources_name, 'COMPILE_FLAGS', cflags, ' ')
950
951       if c_sources and (cflags or cflags_c):
952         flags = []
953         flags.extend(cflags)
954         flags.extend(cflags_c)
955         SetFilesProperty(output, c_sources_name, 'COMPILE_FLAGS', flags, ' ')
956
957       if cxx_sources and (cflags or cflags_cxx):
958         flags = []
959         flags.extend(cflags)
960         flags.extend(cflags_cxx)
961         SetFilesProperty(output, cxx_sources_name, 'COMPILE_FLAGS', flags, ' ')
962
963     # Linker flags
964     ldflags = config.get('ldflags')
965     if ldflags is not None:
966       SetTargetProperty(output, cmake_target_name, 'LINK_FLAGS', ldflags, ' ')
967
968   # Note on Dependencies and Libraries:
969   # CMake wants to handle link order, resolving the link line up front.
970   # Gyp does not retain or enforce specifying enough information to do so.
971   # So do as other gyp generators and use --start-group and --end-group.
972   # Give CMake as little information as possible so that it doesn't mess it up.
973
974   # Dependencies
975   rawDeps = spec.get('dependencies', [])
976
977   static_deps = []
978   shared_deps = []
979   other_deps = []
980   for rawDep in rawDeps:
981     dep_cmake_name = namer.CreateCMakeTargetName(rawDep)
982     dep_spec = target_dicts.get(rawDep, {})
983     dep_target_type = dep_spec.get('type', None)
984
985     if dep_target_type == 'static_library':
986       static_deps.append(dep_cmake_name)
987     elif dep_target_type ==  'shared_library':
988       shared_deps.append(dep_cmake_name)
989     else:
990       other_deps.append(dep_cmake_name)
991
992   # ensure all external dependencies are complete before internal dependencies
993   # extra_deps currently only depend on their own deps, so otherwise run early
994   if static_deps or shared_deps or other_deps:
995     for extra_dep in extra_deps:
996       output.write('add_dependencies(')
997       output.write(extra_dep)
998       output.write('\n')
999       for deps in (static_deps, shared_deps, other_deps):
1000         for dep in gyp.common.uniquer(deps):
1001           output.write('  ')
1002           output.write(dep)
1003           output.write('\n')
1004       output.write(')\n')
1005
1006   linkable = target_type in ('executable', 'loadable_module', 'shared_library')
1007   other_deps.extend(extra_deps)
1008   if other_deps or (not linkable and (static_deps or shared_deps)):
1009     output.write('add_dependencies(')
1010     output.write(cmake_target_name)
1011     output.write('\n')
1012     for dep in gyp.common.uniquer(other_deps):
1013       output.write('  ')
1014       output.write(dep)
1015       output.write('\n')
1016     if not linkable:
1017       for deps in (static_deps, shared_deps):
1018         for lib_dep in gyp.common.uniquer(deps):
1019           output.write('  ')
1020           output.write(lib_dep)
1021           output.write('\n')
1022     output.write(')\n')
1023
1024   # Libraries
1025   if linkable:
1026     external_libs = [lib for lib in spec.get('libraries', []) if len(lib) > 0]
1027     if external_libs or static_deps or shared_deps:
1028       output.write('target_link_libraries(')
1029       output.write(cmake_target_name)
1030       output.write('\n')
1031       if static_deps:
1032         write_group = circular_libs and len(static_deps) > 1
1033         if write_group:
1034           output.write('-Wl,--start-group\n')
1035         for dep in gyp.common.uniquer(static_deps):
1036           output.write('  ')
1037           output.write(dep)
1038           output.write('\n')
1039         if write_group:
1040           output.write('-Wl,--end-group\n')
1041       if shared_deps:
1042         for dep in gyp.common.uniquer(shared_deps):
1043           output.write('  ')
1044           output.write(dep)
1045           output.write('\n')
1046       if external_libs:
1047         for lib in gyp.common.uniquer(external_libs):
1048           output.write('  ')
1049           output.write(lib)
1050           output.write('\n')
1051
1052       output.write(')\n')
1053
1054   UnsetVariable(output, 'TOOLSET')
1055   UnsetVariable(output, 'TARGET')
1056
1057
1058 def GenerateOutputForConfig(target_list, target_dicts, data,
1059                             params, config_to_use):
1060   options = params['options']
1061   generator_flags = params['generator_flags']
1062
1063   # generator_dir: relative path from pwd to where make puts build files.
1064   # Makes migrating from make to cmake easier, cmake doesn't put anything here.
1065   # Each Gyp configuration creates a different CMakeLists.txt file
1066   # to avoid incompatibilities between Gyp and CMake configurations.
1067   generator_dir = os.path.relpath(options.generator_output or '.')
1068
1069   # output_dir: relative path from generator_dir to the build directory.
1070   output_dir = generator_flags.get('output_dir', 'out')
1071
1072   # build_dir: relative path from source root to our output files.
1073   # e.g. "out/Debug"
1074   build_dir = os.path.normpath(os.path.join(generator_dir,
1075                                             output_dir,
1076                                             config_to_use))
1077
1078   toplevel_build = os.path.join(options.toplevel_dir, build_dir)
1079
1080   output_file = os.path.join(toplevel_build, 'CMakeLists.txt')
1081   gyp.common.EnsureDirExists(output_file)
1082
1083   output = open(output_file, 'w')
1084   output.write('cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR)\n')
1085   output.write('cmake_policy(VERSION 2.8.8)\n')
1086
1087   gyp_file, project_target, _ = gyp.common.ParseQualifiedTarget(target_list[-1])
1088   output.write('project(')
1089   output.write(project_target)
1090   output.write(')\n')
1091
1092   SetVariable(output, 'configuration', config_to_use)
1093
1094   ar = None
1095   cc = None
1096   cxx = None
1097
1098   make_global_settings = data[gyp_file].get('make_global_settings', [])
1099   build_to_top = gyp.common.InvertRelativePath(build_dir,
1100                                                options.toplevel_dir)
1101   for key, value in make_global_settings:
1102     if key == 'AR':
1103       ar = os.path.join(build_to_top, value)
1104     if key == 'CC':
1105       cc = os.path.join(build_to_top, value)
1106     if key == 'CXX':
1107       cxx = os.path.join(build_to_top, value)
1108
1109   ar = gyp.common.GetEnvironFallback(['AR_target', 'AR'], ar)
1110   cc = gyp.common.GetEnvironFallback(['CC_target', 'CC'], cc)
1111   cxx = gyp.common.GetEnvironFallback(['CXX_target', 'CXX'], cxx)
1112
1113   if ar:
1114     SetVariable(output, 'CMAKE_AR', ar)
1115   if cc:
1116     SetVariable(output, 'CMAKE_C_COMPILER', cc)
1117   if cxx:
1118     SetVariable(output, 'CMAKE_CXX_COMPILER', cxx)
1119
1120   # The following appears to be as-yet undocumented.
1121   # http://public.kitware.com/Bug/view.php?id=8392
1122   output.write('enable_language(ASM)\n')
1123   # ASM-ATT does not support .S files.
1124   # output.write('enable_language(ASM-ATT)\n')
1125
1126   if cc:
1127     SetVariable(output, 'CMAKE_ASM_COMPILER', cc)
1128
1129   SetVariable(output, 'builddir', '${CMAKE_CURRENT_BINARY_DIR}')
1130   SetVariable(output, 'obj', '${builddir}/obj')
1131   output.write('\n')
1132
1133   # TODO: Undocumented/unsupported (the CMake Java generator depends on it).
1134   # CMake by default names the object resulting from foo.c to be foo.c.o.
1135   # Gyp traditionally names the object resulting from foo.c foo.o.
1136   # This should be irrelevant, but some targets extract .o files from .a
1137   # and depend on the name of the extracted .o files.
1138   output.write('set(CMAKE_C_OUTPUT_EXTENSION_REPLACE 1)\n')
1139   output.write('set(CMAKE_CXX_OUTPUT_EXTENSION_REPLACE 1)\n')
1140   output.write('\n')
1141
1142   # Force ninja to use rsp files. Otherwise link and ar lines can get too long,
1143   # resulting in 'Argument list too long' errors.
1144   output.write('set(CMAKE_NINJA_FORCE_RESPONSE_FILE 1)\n')
1145   output.write('\n')
1146
1147   namer = CMakeNamer(target_list)
1148
1149   # The list of targets upon which the 'all' target should depend.
1150   # CMake has it's own implicit 'all' target, one is not created explicitly.
1151   all_qualified_targets = set()
1152   for build_file in params['build_files']:
1153     for qualified_target in gyp.common.AllTargets(target_list,
1154                                                   target_dicts,
1155                                                   os.path.normpath(build_file)):
1156       all_qualified_targets.add(qualified_target)
1157
1158   for qualified_target in target_list:
1159     WriteTarget(namer, qualified_target, target_dicts, build_dir, config_to_use,
1160                 options, generator_flags, all_qualified_targets, output)
1161
1162   output.close()
1163
1164
1165 def PerformBuild(data, configurations, params):
1166   options = params['options']
1167   generator_flags = params['generator_flags']
1168
1169   # generator_dir: relative path from pwd to where make puts build files.
1170   # Makes migrating from make to cmake easier, cmake doesn't put anything here.
1171   generator_dir = os.path.relpath(options.generator_output or '.')
1172
1173   # output_dir: relative path from generator_dir to the build directory.
1174   output_dir = generator_flags.get('output_dir', 'out')
1175
1176   for config_name in configurations:
1177     # build_dir: relative path from source root to our output files.
1178     # e.g. "out/Debug"
1179     build_dir = os.path.normpath(os.path.join(generator_dir,
1180                                               output_dir,
1181                                               config_name))
1182     arguments = ['cmake', '-G', 'Ninja']
1183     print 'Generating [%s]: %s' % (config_name, arguments)
1184     subprocess.check_call(arguments, cwd=build_dir)
1185
1186     arguments = ['ninja', '-C', build_dir]
1187     print 'Building [%s]: %s' % (config_name, arguments)
1188     subprocess.check_call(arguments)
1189
1190
1191 def CallGenerateOutputForConfig(arglist):
1192   # Ignore the interrupt signal so that the parent process catches it and
1193   # kills all multiprocessing children.
1194   signal.signal(signal.SIGINT, signal.SIG_IGN)
1195
1196   target_list, target_dicts, data, params, config_name = arglist
1197   GenerateOutputForConfig(target_list, target_dicts, data, params, config_name)
1198
1199
1200 def GenerateOutput(target_list, target_dicts, data, params):
1201   user_config = params.get('generator_flags', {}).get('config', None)
1202   if user_config:
1203     GenerateOutputForConfig(target_list, target_dicts, data,
1204                             params, user_config)
1205   else:
1206     config_names = target_dicts[target_list[0]]['configurations'].keys()
1207     if params['parallel']:
1208       try:
1209         pool = multiprocessing.Pool(len(config_names))
1210         arglists = []
1211         for config_name in config_names:
1212           arglists.append((target_list, target_dicts, data,
1213                            params, config_name))
1214           pool.map(CallGenerateOutputForConfig, arglists)
1215       except KeyboardInterrupt, e:
1216         pool.terminate()
1217         raise e
1218     else:
1219       for config_name in config_names:
1220         GenerateOutputForConfig(target_list, target_dicts, data,
1221                                 params, config_name)