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