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