~hjd/ubuntu/wily/gyp/debian-merged

« back to all changes in this revision

Viewing changes to pylib/gyp/generator/cmake.py

  • Committer: Hans Joachim Desserud
  • Date: 2015-10-31 12:46:59 UTC
  • mfrom: (6.2.6 sid)
  • Revision ID: hans_joachim_desserud-20151031124659-lzxekr6woskh4k0b
Merge latest Debian version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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_SOURCE_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_SOURCE_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_SOURCE_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_SOURCE_DIR is where the CMakeLists.txt lives.
 
402
      # The cwd is the current build directory.
 
403
      output.write('  WORKING_DIRECTORY ${CMAKE_SOURCE_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_SOURCE_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_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)