redsnow 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (174) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +34 -0
  3. data/.gitmodules +3 -0
  4. data/.travis.yml +20 -0
  5. data/CHANGELOG.md +4 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +21 -0
  8. data/README.md +62 -0
  9. data/Rakefile +36 -0
  10. data/Vagrantfile +20 -0
  11. data/ext/snowcrash/Makefile +64 -0
  12. data/ext/snowcrash/Vagrantfile +20 -0
  13. data/ext/snowcrash/bin/snowcrash +0 -0
  14. data/ext/snowcrash/common.gypi +163 -0
  15. data/ext/snowcrash/config.gypi +10 -0
  16. data/ext/snowcrash/config.mk +5 -0
  17. data/ext/snowcrash/configure +213 -0
  18. data/ext/snowcrash/provisioning.sh +15 -0
  19. data/ext/snowcrash/snowcrash.gyp +141 -0
  20. data/ext/snowcrash/src/ActionParser.h +503 -0
  21. data/ext/snowcrash/src/AssetParser.h +215 -0
  22. data/ext/snowcrash/src/BlockUtility.h +186 -0
  23. data/ext/snowcrash/src/Blueprint.h +283 -0
  24. data/ext/snowcrash/src/BlueprintParser.h +347 -0
  25. data/ext/snowcrash/src/BlueprintParserCore.h +190 -0
  26. data/ext/snowcrash/src/BlueprintSection.h +140 -0
  27. data/ext/snowcrash/src/BlueprintUtility.h +126 -0
  28. data/ext/snowcrash/src/CBlueprint.cc +600 -0
  29. data/ext/snowcrash/src/CBlueprint.h +354 -0
  30. data/ext/snowcrash/src/CSourceAnnotation.cc +140 -0
  31. data/ext/snowcrash/src/CSourceAnnotation.h +106 -0
  32. data/ext/snowcrash/src/CodeBlockUtility.h +189 -0
  33. data/ext/snowcrash/src/DescriptionSectionUtility.h +156 -0
  34. data/ext/snowcrash/src/HTTP.cc +46 -0
  35. data/ext/snowcrash/src/HTTP.h +105 -0
  36. data/ext/snowcrash/src/HeaderParser.h +289 -0
  37. data/ext/snowcrash/src/ListBlockUtility.h +273 -0
  38. data/ext/snowcrash/src/ListUtility.h +95 -0
  39. data/ext/snowcrash/src/MarkdownBlock.cc +176 -0
  40. data/ext/snowcrash/src/MarkdownBlock.h +93 -0
  41. data/ext/snowcrash/src/MarkdownParser.cc +266 -0
  42. data/ext/snowcrash/src/MarkdownParser.h +88 -0
  43. data/ext/snowcrash/src/ParameterDefinitonParser.h +570 -0
  44. data/ext/snowcrash/src/ParametersParser.h +252 -0
  45. data/ext/snowcrash/src/Parser.cc +71 -0
  46. data/ext/snowcrash/src/Parser.h +29 -0
  47. data/ext/snowcrash/src/ParserCore.cc +120 -0
  48. data/ext/snowcrash/src/ParserCore.h +82 -0
  49. data/ext/snowcrash/src/PayloadParser.h +672 -0
  50. data/ext/snowcrash/src/Platform.h +54 -0
  51. data/ext/snowcrash/src/RegexMatch.h +32 -0
  52. data/ext/snowcrash/src/ResourceGroupParser.h +195 -0
  53. data/ext/snowcrash/src/ResourceParser.h +584 -0
  54. data/ext/snowcrash/src/SectionUtility.h +142 -0
  55. data/ext/snowcrash/src/Serialize.cc +52 -0
  56. data/ext/snowcrash/src/Serialize.h +69 -0
  57. data/ext/snowcrash/src/SerializeJSON.cc +601 -0
  58. data/ext/snowcrash/src/SerializeJSON.h +21 -0
  59. data/ext/snowcrash/src/SerializeYAML.cc +336 -0
  60. data/ext/snowcrash/src/SerializeYAML.h +21 -0
  61. data/ext/snowcrash/src/SourceAnnotation.h +177 -0
  62. data/ext/snowcrash/src/StringUtility.h +109 -0
  63. data/ext/snowcrash/src/SymbolTable.h +83 -0
  64. data/ext/snowcrash/src/UriTemplateParser.cc +195 -0
  65. data/ext/snowcrash/src/UriTemplateParser.h +243 -0
  66. data/ext/snowcrash/src/Version.h +39 -0
  67. data/ext/snowcrash/src/csnowcrash.cc +23 -0
  68. data/ext/snowcrash/src/csnowcrash.h +38 -0
  69. data/ext/snowcrash/src/posix/RegexMatch.cc +99 -0
  70. data/ext/snowcrash/src/snowcrash.cc +18 -0
  71. data/ext/snowcrash/src/snowcrash.h +41 -0
  72. data/ext/snowcrash/src/snowcrash/snowcrash.cc +170 -0
  73. data/ext/snowcrash/src/win/RegexMatch.cc +78 -0
  74. data/ext/snowcrash/sundown/CONTRIBUTING.md +10 -0
  75. data/ext/snowcrash/sundown/Makefile +83 -0
  76. data/ext/snowcrash/sundown/Makefile.win +33 -0
  77. data/ext/snowcrash/sundown/examples/smartypants.c +72 -0
  78. data/ext/snowcrash/sundown/examples/sundown.c +80 -0
  79. data/ext/snowcrash/sundown/html/houdini.h +37 -0
  80. data/ext/snowcrash/sundown/html/houdini_href_e.c +108 -0
  81. data/ext/snowcrash/sundown/html/houdini_html_e.c +84 -0
  82. data/ext/snowcrash/sundown/html/html.c +647 -0
  83. data/ext/snowcrash/sundown/html/html.h +77 -0
  84. data/ext/snowcrash/sundown/html/html_smartypants.c +389 -0
  85. data/ext/snowcrash/sundown/html_block_names.txt +25 -0
  86. data/ext/snowcrash/sundown/src/autolink.c +297 -0
  87. data/ext/snowcrash/sundown/src/autolink.h +51 -0
  88. data/ext/snowcrash/sundown/src/buffer.c +225 -0
  89. data/ext/snowcrash/sundown/src/buffer.h +96 -0
  90. data/ext/snowcrash/sundown/src/html_blocks.h +206 -0
  91. data/ext/snowcrash/sundown/src/markdown.c +2701 -0
  92. data/ext/snowcrash/sundown/src/markdown.h +147 -0
  93. data/ext/snowcrash/sundown/src/src_map.c +200 -0
  94. data/ext/snowcrash/sundown/src/src_map.h +58 -0
  95. data/ext/snowcrash/sundown/src/stack.c +81 -0
  96. data/ext/snowcrash/sundown/src/stack.h +29 -0
  97. data/ext/snowcrash/sundown/sundown.def +20 -0
  98. data/ext/snowcrash/tools/gyp/AUTHORS +11 -0
  99. data/ext/snowcrash/tools/gyp/DEPS +24 -0
  100. data/ext/snowcrash/tools/gyp/OWNERS +1 -0
  101. data/ext/snowcrash/tools/gyp/PRESUBMIT.py +120 -0
  102. data/ext/snowcrash/tools/gyp/buildbot/buildbot_run.py +190 -0
  103. data/ext/snowcrash/tools/gyp/codereview.settings +10 -0
  104. data/ext/snowcrash/tools/gyp/data/win/large-pdb-shim.cc +12 -0
  105. data/ext/snowcrash/tools/gyp/gyp +8 -0
  106. data/ext/snowcrash/tools/gyp/gyp.bat +5 -0
  107. data/ext/snowcrash/tools/gyp/gyp_main.py +18 -0
  108. data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSNew.py +340 -0
  109. data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSProject.py +208 -0
  110. data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSSettings.py +1063 -0
  111. data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSToolFile.py +58 -0
  112. data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSUserFile.py +147 -0
  113. data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSUtil.py +267 -0
  114. data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSVersion.py +409 -0
  115. data/ext/snowcrash/tools/gyp/pylib/gyp/__init__.py +537 -0
  116. data/ext/snowcrash/tools/gyp/pylib/gyp/__init__.pyc +0 -0
  117. data/ext/snowcrash/tools/gyp/pylib/gyp/common.py +521 -0
  118. data/ext/snowcrash/tools/gyp/pylib/gyp/common.pyc +0 -0
  119. data/ext/snowcrash/tools/gyp/pylib/gyp/easy_xml.py +157 -0
  120. data/ext/snowcrash/tools/gyp/pylib/gyp/flock_tool.py +49 -0
  121. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/__init__.py +0 -0
  122. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/__init__.pyc +0 -0
  123. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/android.py +1069 -0
  124. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/cmake.py +1143 -0
  125. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/dump_dependency_json.py +81 -0
  126. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/eclipse.py +335 -0
  127. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/gypd.py +87 -0
  128. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/gypsh.py +56 -0
  129. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/make.py +2181 -0
  130. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/make.pyc +0 -0
  131. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/msvs.py +3335 -0
  132. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/ninja.py +2156 -0
  133. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/xcode.py +1224 -0
  134. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/xcode.pyc +0 -0
  135. data/ext/snowcrash/tools/gyp/pylib/gyp/input.py +2809 -0
  136. data/ext/snowcrash/tools/gyp/pylib/gyp/input.pyc +0 -0
  137. data/ext/snowcrash/tools/gyp/pylib/gyp/mac_tool.py +510 -0
  138. data/ext/snowcrash/tools/gyp/pylib/gyp/msvs_emulation.py +972 -0
  139. data/ext/snowcrash/tools/gyp/pylib/gyp/ninja_syntax.py +160 -0
  140. data/ext/snowcrash/tools/gyp/pylib/gyp/ordered_dict.py +289 -0
  141. data/ext/snowcrash/tools/gyp/pylib/gyp/win_tool.py +292 -0
  142. data/ext/snowcrash/tools/gyp/pylib/gyp/xcode_emulation.py +1440 -0
  143. data/ext/snowcrash/tools/gyp/pylib/gyp/xcode_emulation.pyc +0 -0
  144. data/ext/snowcrash/tools/gyp/pylib/gyp/xcodeproj_file.py +2889 -0
  145. data/ext/snowcrash/tools/gyp/pylib/gyp/xcodeproj_file.pyc +0 -0
  146. data/ext/snowcrash/tools/gyp/pylib/gyp/xml_fix.py +69 -0
  147. data/ext/snowcrash/tools/gyp/pylintrc +307 -0
  148. data/ext/snowcrash/tools/gyp/samples/samples +81 -0
  149. data/ext/snowcrash/tools/gyp/samples/samples.bat +5 -0
  150. data/ext/snowcrash/tools/gyp/setup.py +19 -0
  151. data/ext/snowcrash/tools/gyp/tools/Xcode/Specifications/gyp.pbfilespec +27 -0
  152. data/ext/snowcrash/tools/gyp/tools/Xcode/Specifications/gyp.xclangspec +226 -0
  153. data/ext/snowcrash/tools/gyp/tools/emacs/gyp.el +252 -0
  154. data/ext/snowcrash/tools/gyp/tools/graphviz.py +100 -0
  155. data/ext/snowcrash/tools/gyp/tools/pretty_gyp.py +155 -0
  156. data/ext/snowcrash/tools/gyp/tools/pretty_sln.py +168 -0
  157. data/ext/snowcrash/tools/gyp/tools/pretty_vcproj.py +329 -0
  158. data/ext/snowcrash/tools/homebrew/snowcrash.rb +11 -0
  159. data/ext/snowcrash/vcbuild.bat +184 -0
  160. data/lib/redsnow.rb +31 -0
  161. data/lib/redsnow/binding.rb +132 -0
  162. data/lib/redsnow/blueprint.rb +365 -0
  163. data/lib/redsnow/object.rb +18 -0
  164. data/lib/redsnow/parseresult.rb +107 -0
  165. data/lib/redsnow/version.rb +4 -0
  166. data/provisioning.sh +20 -0
  167. data/redsnow.gemspec +35 -0
  168. data/test/_helper.rb +15 -0
  169. data/test/fixtures/sample-api-ast.json +97 -0
  170. data/test/fixtures/sample-api.apib +20 -0
  171. data/test/redsnow_binding_test.rb +35 -0
  172. data/test/redsnow_parseresult_test.rb +50 -0
  173. data/test/redsnow_test.rb +285 -0
  174. metadata +358 -0
@@ -0,0 +1,2156 @@
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
+ import copy
6
+ import hashlib
7
+ import json
8
+ import multiprocessing
9
+ import os.path
10
+ import re
11
+ import signal
12
+ import subprocess
13
+ import sys
14
+ import gyp
15
+ import gyp.common
16
+ import gyp.msvs_emulation
17
+ import gyp.MSVSUtil as MSVSUtil
18
+ import gyp.xcode_emulation
19
+ from cStringIO import StringIO
20
+
21
+ from gyp.common import GetEnvironFallback
22
+ import gyp.ninja_syntax as ninja_syntax
23
+
24
+ generator_default_variables = {
25
+ 'EXECUTABLE_PREFIX': '',
26
+ 'EXECUTABLE_SUFFIX': '',
27
+ 'STATIC_LIB_PREFIX': 'lib',
28
+ 'STATIC_LIB_SUFFIX': '.a',
29
+ 'SHARED_LIB_PREFIX': 'lib',
30
+
31
+ # Gyp expects the following variables to be expandable by the build
32
+ # system to the appropriate locations. Ninja prefers paths to be
33
+ # known at gyp time. To resolve this, introduce special
34
+ # variables starting with $! and $| (which begin with a $ so gyp knows it
35
+ # should be treated specially, but is otherwise an invalid
36
+ # ninja/shell variable) that are passed to gyp here but expanded
37
+ # before writing out into the target .ninja files; see
38
+ # ExpandSpecial.
39
+ # $! is used for variables that represent a path and that can only appear at
40
+ # the start of a string, while $| is used for variables that can appear
41
+ # anywhere in a string.
42
+ 'INTERMEDIATE_DIR': '$!INTERMEDIATE_DIR',
43
+ 'SHARED_INTERMEDIATE_DIR': '$!PRODUCT_DIR/gen',
44
+ 'PRODUCT_DIR': '$!PRODUCT_DIR',
45
+ 'CONFIGURATION_NAME': '$|CONFIGURATION_NAME',
46
+
47
+ # Special variables that may be used by gyp 'rule' targets.
48
+ # We generate definitions for these variables on the fly when processing a
49
+ # rule.
50
+ 'RULE_INPUT_ROOT': '${root}',
51
+ 'RULE_INPUT_DIRNAME': '${dirname}',
52
+ 'RULE_INPUT_PATH': '${source}',
53
+ 'RULE_INPUT_EXT': '${ext}',
54
+ 'RULE_INPUT_NAME': '${name}',
55
+ }
56
+
57
+ # Placates pylint.
58
+ generator_additional_non_configuration_keys = []
59
+ generator_additional_path_sections = []
60
+ generator_extra_sources_for_rules = []
61
+ generator_filelist_paths = None
62
+
63
+ # TODO: figure out how to not build extra host objects in the non-cross-compile
64
+ # case when this is enabled, and enable unconditionally.
65
+ generator_supports_multiple_toolsets = (
66
+ os.environ.get('GYP_CROSSCOMPILE') or
67
+ os.environ.get('AR_host') or
68
+ os.environ.get('CC_host') or
69
+ os.environ.get('CXX_host') or
70
+ os.environ.get('AR_target') or
71
+ os.environ.get('CC_target') or
72
+ os.environ.get('CXX_target'))
73
+
74
+
75
+ def StripPrefix(arg, prefix):
76
+ if arg.startswith(prefix):
77
+ return arg[len(prefix):]
78
+ return arg
79
+
80
+
81
+ def QuoteShellArgument(arg, flavor):
82
+ """Quote a string such that it will be interpreted as a single argument
83
+ by the shell."""
84
+ # Rather than attempting to enumerate the bad shell characters, just
85
+ # whitelist common OK ones and quote anything else.
86
+ if re.match(r'^[a-zA-Z0-9_=.\\/-]+$', arg):
87
+ return arg # No quoting necessary.
88
+ if flavor == 'win':
89
+ return gyp.msvs_emulation.QuoteForRspFile(arg)
90
+ return "'" + arg.replace("'", "'" + '"\'"' + "'") + "'"
91
+
92
+
93
+ def Define(d, flavor):
94
+ """Takes a preprocessor define and returns a -D parameter that's ninja- and
95
+ shell-escaped."""
96
+ if flavor == 'win':
97
+ # cl.exe replaces literal # characters with = in preprocesor definitions for
98
+ # some reason. Octal-encode to work around that.
99
+ d = d.replace('#', '\\%03o' % ord('#'))
100
+ return QuoteShellArgument(ninja_syntax.escape('-D' + d), flavor)
101
+
102
+
103
+ def AddArch(output, arch):
104
+ """Adds an arch string to an output path."""
105
+ output, extension = os.path.splitext(output)
106
+ return '%s.%s%s' % (output, arch, extension)
107
+
108
+
109
+ class Target:
110
+ """Target represents the paths used within a single gyp target.
111
+
112
+ Conceptually, building a single target A is a series of steps:
113
+
114
+ 1) actions/rules/copies generates source/resources/etc.
115
+ 2) compiles generates .o files
116
+ 3) link generates a binary (library/executable)
117
+ 4) bundle merges the above in a mac bundle
118
+
119
+ (Any of these steps can be optional.)
120
+
121
+ From a build ordering perspective, a dependent target B could just
122
+ depend on the last output of this series of steps.
123
+
124
+ But some dependent commands sometimes need to reach inside the box.
125
+ For example, when linking B it needs to get the path to the static
126
+ library generated by A.
127
+
128
+ This object stores those paths. To keep things simple, member
129
+ variables only store concrete paths to single files, while methods
130
+ compute derived values like "the last output of the target".
131
+ """
132
+ def __init__(self, type):
133
+ # Gyp type ("static_library", etc.) of this target.
134
+ self.type = type
135
+ # File representing whether any input dependencies necessary for
136
+ # dependent actions have completed.
137
+ self.preaction_stamp = None
138
+ # File representing whether any input dependencies necessary for
139
+ # dependent compiles have completed.
140
+ self.precompile_stamp = None
141
+ # File representing the completion of actions/rules/copies, if any.
142
+ self.actions_stamp = None
143
+ # Path to the output of the link step, if any.
144
+ self.binary = None
145
+ # Path to the file representing the completion of building the bundle,
146
+ # if any.
147
+ self.bundle = None
148
+ # On Windows, incremental linking requires linking against all the .objs
149
+ # that compose a .lib (rather than the .lib itself). That list is stored
150
+ # here.
151
+ self.component_objs = None
152
+ # Windows only. The import .lib is the output of a build step, but
153
+ # because dependents only link against the lib (not both the lib and the
154
+ # dll) we keep track of the import library here.
155
+ self.import_lib = None
156
+
157
+ def Linkable(self):
158
+ """Return true if this is a target that can be linked against."""
159
+ return self.type in ('static_library', 'shared_library')
160
+
161
+ def UsesToc(self, flavor):
162
+ """Return true if the target should produce a restat rule based on a TOC
163
+ file."""
164
+ # For bundles, the .TOC should be produced for the binary, not for
165
+ # FinalOutput(). But the naive approach would put the TOC file into the
166
+ # bundle, so don't do this for bundles for now.
167
+ if flavor == 'win' or self.bundle:
168
+ return False
169
+ return self.type in ('shared_library', 'loadable_module')
170
+
171
+ def PreActionInput(self, flavor):
172
+ """Return the path, if any, that should be used as a dependency of
173
+ any dependent action step."""
174
+ if self.UsesToc(flavor):
175
+ return self.FinalOutput() + '.TOC'
176
+ return self.FinalOutput() or self.preaction_stamp
177
+
178
+ def PreCompileInput(self):
179
+ """Return the path, if any, that should be used as a dependency of
180
+ any dependent compile step."""
181
+ return self.actions_stamp or self.precompile_stamp
182
+
183
+ def FinalOutput(self):
184
+ """Return the last output of the target, which depends on all prior
185
+ steps."""
186
+ return self.bundle or self.binary or self.actions_stamp
187
+
188
+
189
+ # A small discourse on paths as used within the Ninja build:
190
+ # All files we produce (both at gyp and at build time) appear in the
191
+ # build directory (e.g. out/Debug).
192
+ #
193
+ # Paths within a given .gyp file are always relative to the directory
194
+ # containing the .gyp file. Call these "gyp paths". This includes
195
+ # sources as well as the starting directory a given gyp rule/action
196
+ # expects to be run from. We call the path from the source root to
197
+ # the gyp file the "base directory" within the per-.gyp-file
198
+ # NinjaWriter code.
199
+ #
200
+ # All paths as written into the .ninja files are relative to the build
201
+ # directory. Call these paths "ninja paths".
202
+ #
203
+ # We translate between these two notions of paths with two helper
204
+ # functions:
205
+ #
206
+ # - GypPathToNinja translates a gyp path (i.e. relative to the .gyp file)
207
+ # into the equivalent ninja path.
208
+ #
209
+ # - GypPathToUniqueOutput translates a gyp path into a ninja path to write
210
+ # an output file; the result can be namespaced such that it is unique
211
+ # to the input file name as well as the output target name.
212
+
213
+ class NinjaWriter:
214
+ def __init__(self, qualified_target, target_outputs, base_dir, build_dir,
215
+ output_file, toplevel_build, output_file_name, flavor,
216
+ toplevel_dir=None):
217
+ """
218
+ base_dir: path from source root to directory containing this gyp file,
219
+ by gyp semantics, all input paths are relative to this
220
+ build_dir: path from source root to build output
221
+ toplevel_dir: path to the toplevel directory
222
+ """
223
+
224
+ self.qualified_target = qualified_target
225
+ self.target_outputs = target_outputs
226
+ self.base_dir = base_dir
227
+ self.build_dir = build_dir
228
+ self.ninja = ninja_syntax.Writer(output_file)
229
+ self.toplevel_build = toplevel_build
230
+ self.output_file_name = output_file_name
231
+
232
+ self.flavor = flavor
233
+ self.abs_build_dir = None
234
+ if toplevel_dir is not None:
235
+ self.abs_build_dir = os.path.abspath(os.path.join(toplevel_dir,
236
+ build_dir))
237
+ self.obj_ext = '.obj' if flavor == 'win' else '.o'
238
+ if flavor == 'win':
239
+ # See docstring of msvs_emulation.GenerateEnvironmentFiles().
240
+ self.win_env = {}
241
+ for arch in ('x86', 'x64'):
242
+ self.win_env[arch] = 'environment.' + arch
243
+
244
+ # Relative path from build output dir to base dir.
245
+ build_to_top = gyp.common.InvertRelativePath(build_dir, toplevel_dir)
246
+ self.build_to_base = os.path.join(build_to_top, base_dir)
247
+ # Relative path from base dir to build dir.
248
+ base_to_top = gyp.common.InvertRelativePath(base_dir, toplevel_dir)
249
+ self.base_to_build = os.path.join(base_to_top, build_dir)
250
+
251
+ def ExpandSpecial(self, path, product_dir=None):
252
+ """Expand specials like $!PRODUCT_DIR in |path|.
253
+
254
+ If |product_dir| is None, assumes the cwd is already the product
255
+ dir. Otherwise, |product_dir| is the relative path to the product
256
+ dir.
257
+ """
258
+
259
+ PRODUCT_DIR = '$!PRODUCT_DIR'
260
+ if PRODUCT_DIR in path:
261
+ if product_dir:
262
+ path = path.replace(PRODUCT_DIR, product_dir)
263
+ else:
264
+ path = path.replace(PRODUCT_DIR + '/', '')
265
+ path = path.replace(PRODUCT_DIR + '\\', '')
266
+ path = path.replace(PRODUCT_DIR, '.')
267
+
268
+ INTERMEDIATE_DIR = '$!INTERMEDIATE_DIR'
269
+ if INTERMEDIATE_DIR in path:
270
+ int_dir = self.GypPathToUniqueOutput('gen')
271
+ # GypPathToUniqueOutput generates a path relative to the product dir,
272
+ # so insert product_dir in front if it is provided.
273
+ path = path.replace(INTERMEDIATE_DIR,
274
+ os.path.join(product_dir or '', int_dir))
275
+
276
+ CONFIGURATION_NAME = '$|CONFIGURATION_NAME'
277
+ path = path.replace(CONFIGURATION_NAME, self.config_name)
278
+
279
+ return path
280
+
281
+ def ExpandRuleVariables(self, path, root, dirname, source, ext, name):
282
+ if self.flavor == 'win':
283
+ path = self.msvs_settings.ConvertVSMacros(
284
+ path, config=self.config_name)
285
+ path = path.replace(generator_default_variables['RULE_INPUT_ROOT'], root)
286
+ path = path.replace(generator_default_variables['RULE_INPUT_DIRNAME'],
287
+ dirname)
288
+ path = path.replace(generator_default_variables['RULE_INPUT_PATH'], source)
289
+ path = path.replace(generator_default_variables['RULE_INPUT_EXT'], ext)
290
+ path = path.replace(generator_default_variables['RULE_INPUT_NAME'], name)
291
+ return path
292
+
293
+ def GypPathToNinja(self, path, env=None):
294
+ """Translate a gyp path to a ninja path, optionally expanding environment
295
+ variable references in |path| with |env|.
296
+
297
+ See the above discourse on path conversions."""
298
+ if env:
299
+ if self.flavor == 'mac':
300
+ path = gyp.xcode_emulation.ExpandEnvVars(path, env)
301
+ elif self.flavor == 'win':
302
+ path = gyp.msvs_emulation.ExpandMacros(path, env)
303
+ if path.startswith('$!'):
304
+ expanded = self.ExpandSpecial(path)
305
+ if self.flavor == 'win':
306
+ expanded = os.path.normpath(expanded)
307
+ return expanded
308
+ if '$|' in path:
309
+ path = self.ExpandSpecial(path)
310
+ assert '$' not in path, path
311
+ return os.path.normpath(os.path.join(self.build_to_base, path))
312
+
313
+ def GypPathToUniqueOutput(self, path, qualified=True):
314
+ """Translate a gyp path to a ninja path for writing output.
315
+
316
+ If qualified is True, qualify the resulting filename with the name
317
+ of the target. This is necessary when e.g. compiling the same
318
+ path twice for two separate output targets.
319
+
320
+ See the above discourse on path conversions."""
321
+
322
+ path = self.ExpandSpecial(path)
323
+ assert not path.startswith('$'), path
324
+
325
+ # Translate the path following this scheme:
326
+ # Input: foo/bar.gyp, target targ, references baz/out.o
327
+ # Output: obj/foo/baz/targ.out.o (if qualified)
328
+ # obj/foo/baz/out.o (otherwise)
329
+ # (and obj.host instead of obj for cross-compiles)
330
+ #
331
+ # Why this scheme and not some other one?
332
+ # 1) for a given input, you can compute all derived outputs by matching
333
+ # its path, even if the input is brought via a gyp file with '..'.
334
+ # 2) simple files like libraries and stamps have a simple filename.
335
+
336
+ obj = 'obj'
337
+ if self.toolset != 'target':
338
+ obj += '.' + self.toolset
339
+
340
+ path_dir, path_basename = os.path.split(path)
341
+ if qualified:
342
+ path_basename = self.name + '.' + path_basename
343
+ return os.path.normpath(os.path.join(obj, self.base_dir, path_dir,
344
+ path_basename))
345
+
346
+ def WriteCollapsedDependencies(self, name, targets):
347
+ """Given a list of targets, return a path for a single file
348
+ representing the result of building all the targets or None.
349
+
350
+ Uses a stamp file if necessary."""
351
+
352
+ assert targets == filter(None, targets), targets
353
+ if len(targets) == 0:
354
+ return None
355
+ if len(targets) > 1:
356
+ stamp = self.GypPathToUniqueOutput(name + '.stamp')
357
+ targets = self.ninja.build(stamp, 'stamp', targets)
358
+ self.ninja.newline()
359
+ return targets[0]
360
+
361
+ def _SubninjaNameForArch(self, arch):
362
+ output_file_base = os.path.splitext(self.output_file_name)[0]
363
+ return '%s.%s.ninja' % (output_file_base, arch)
364
+
365
+ def WriteSpec(self, spec, config_name, generator_flags):
366
+ """The main entry point for NinjaWriter: write the build rules for a spec.
367
+
368
+ Returns a Target object, which represents the output paths for this spec.
369
+ Returns None if there are no outputs (e.g. a settings-only 'none' type
370
+ target)."""
371
+
372
+ self.config_name = config_name
373
+ self.name = spec['target_name']
374
+ self.toolset = spec['toolset']
375
+ config = spec['configurations'][config_name]
376
+ self.target = Target(spec['type'])
377
+ self.is_standalone_static_library = bool(
378
+ spec.get('standalone_static_library', 0))
379
+ # Track if this target contains any C++ files, to decide if gcc or g++
380
+ # should be used for linking.
381
+ self.uses_cpp = False
382
+
383
+ self.is_mac_bundle = gyp.xcode_emulation.IsMacBundle(self.flavor, spec)
384
+ self.xcode_settings = self.msvs_settings = None
385
+ if self.flavor == 'mac':
386
+ self.xcode_settings = gyp.xcode_emulation.XcodeSettings(spec)
387
+ if self.flavor == 'win':
388
+ self.msvs_settings = gyp.msvs_emulation.MsvsSettings(spec,
389
+ generator_flags)
390
+ arch = self.msvs_settings.GetArch(config_name)
391
+ self.ninja.variable('arch', self.win_env[arch])
392
+ self.ninja.variable('cc', '$cl_' + arch)
393
+ self.ninja.variable('cxx', '$cl_' + arch)
394
+
395
+ if self.flavor == 'mac':
396
+ self.archs = self.xcode_settings.GetActiveArchs(config_name)
397
+ if len(self.archs) > 1:
398
+ self.arch_subninjas = dict(
399
+ (arch, ninja_syntax.Writer(
400
+ OpenOutput(os.path.join(self.toplevel_build,
401
+ self._SubninjaNameForArch(arch)),
402
+ 'w')))
403
+ for arch in self.archs)
404
+
405
+ # Compute predepends for all rules.
406
+ # actions_depends is the dependencies this target depends on before running
407
+ # any of its action/rule/copy steps.
408
+ # compile_depends is the dependencies this target depends on before running
409
+ # any of its compile steps.
410
+ actions_depends = []
411
+ compile_depends = []
412
+ # TODO(evan): it is rather confusing which things are lists and which
413
+ # are strings. Fix these.
414
+ if 'dependencies' in spec:
415
+ for dep in spec['dependencies']:
416
+ if dep in self.target_outputs:
417
+ target = self.target_outputs[dep]
418
+ actions_depends.append(target.PreActionInput(self.flavor))
419
+ compile_depends.append(target.PreCompileInput())
420
+ actions_depends = filter(None, actions_depends)
421
+ compile_depends = filter(None, compile_depends)
422
+ actions_depends = self.WriteCollapsedDependencies('actions_depends',
423
+ actions_depends)
424
+ compile_depends = self.WriteCollapsedDependencies('compile_depends',
425
+ compile_depends)
426
+ self.target.preaction_stamp = actions_depends
427
+ self.target.precompile_stamp = compile_depends
428
+
429
+ # Write out actions, rules, and copies. These must happen before we
430
+ # compile any sources, so compute a list of predependencies for sources
431
+ # while we do it.
432
+ extra_sources = []
433
+ mac_bundle_depends = []
434
+ self.target.actions_stamp = self.WriteActionsRulesCopies(
435
+ spec, extra_sources, actions_depends, mac_bundle_depends)
436
+
437
+ # If we have actions/rules/copies, we depend directly on those, but
438
+ # otherwise we depend on dependent target's actions/rules/copies etc.
439
+ # We never need to explicitly depend on previous target's link steps,
440
+ # because no compile ever depends on them.
441
+ compile_depends_stamp = (self.target.actions_stamp or compile_depends)
442
+
443
+ # Write out the compilation steps, if any.
444
+ link_deps = []
445
+ sources = extra_sources + spec.get('sources', [])
446
+ if sources:
447
+ if self.flavor == 'mac' and len(self.archs) > 1:
448
+ # Write subninja file containing compile and link commands scoped to
449
+ # a single arch if a fat binary is being built.
450
+ for arch in self.archs:
451
+ self.ninja.subninja(self._SubninjaNameForArch(arch))
452
+
453
+ pch = None
454
+ if self.flavor == 'win':
455
+ gyp.msvs_emulation.VerifyMissingSources(
456
+ sources, self.abs_build_dir, generator_flags, self.GypPathToNinja)
457
+ pch = gyp.msvs_emulation.PrecompiledHeader(
458
+ self.msvs_settings, config_name, self.GypPathToNinja,
459
+ self.GypPathToUniqueOutput, self.obj_ext)
460
+ else:
461
+ pch = gyp.xcode_emulation.MacPrefixHeader(
462
+ self.xcode_settings, self.GypPathToNinja,
463
+ lambda path, lang: self.GypPathToUniqueOutput(path + '-' + lang))
464
+ link_deps = self.WriteSources(
465
+ self.ninja, config_name, config, sources, compile_depends_stamp, pch,
466
+ spec)
467
+ # Some actions/rules output 'sources' that are already object files.
468
+ obj_outputs = [f for f in sources if f.endswith(self.obj_ext)]
469
+ if obj_outputs:
470
+ if self.flavor != 'mac' or len(self.archs) == 1:
471
+ link_deps += [self.GypPathToNinja(o) for o in obj_outputs]
472
+ else:
473
+ print "Warning: Actions/rules writing object files don't work with " \
474
+ "multiarch targets, dropping. (target %s)" % spec['target_name']
475
+
476
+
477
+ if self.flavor == 'win' and self.target.type == 'static_library':
478
+ self.target.component_objs = link_deps
479
+
480
+ # Write out a link step, if needed.
481
+ output = None
482
+ is_empty_bundle = not link_deps and not mac_bundle_depends
483
+ if link_deps or self.target.actions_stamp or actions_depends:
484
+ output = self.WriteTarget(spec, config_name, config, link_deps,
485
+ self.target.actions_stamp or actions_depends)
486
+ if self.is_mac_bundle:
487
+ mac_bundle_depends.append(output)
488
+
489
+ # Bundle all of the above together, if needed.
490
+ if self.is_mac_bundle:
491
+ output = self.WriteMacBundle(spec, mac_bundle_depends, is_empty_bundle)
492
+
493
+ if not output:
494
+ return None
495
+
496
+ assert self.target.FinalOutput(), output
497
+ return self.target
498
+
499
+ def _WinIdlRule(self, source, prebuild, outputs):
500
+ """Handle the implicit VS .idl rule for one source file. Fills |outputs|
501
+ with files that are generated."""
502
+ outdir, output, vars, flags = self.msvs_settings.GetIdlBuildData(
503
+ source, self.config_name)
504
+ outdir = self.GypPathToNinja(outdir)
505
+ def fix_path(path, rel=None):
506
+ path = os.path.join(outdir, path)
507
+ dirname, basename = os.path.split(source)
508
+ root, ext = os.path.splitext(basename)
509
+ path = self.ExpandRuleVariables(
510
+ path, root, dirname, source, ext, basename)
511
+ if rel:
512
+ path = os.path.relpath(path, rel)
513
+ return path
514
+ vars = [(name, fix_path(value, outdir)) for name, value in vars]
515
+ output = [fix_path(p) for p in output]
516
+ vars.append(('outdir', outdir))
517
+ vars.append(('idlflags', flags))
518
+ input = self.GypPathToNinja(source)
519
+ self.ninja.build(output, 'idl', input,
520
+ variables=vars, order_only=prebuild)
521
+ outputs.extend(output)
522
+
523
+ def WriteWinIdlFiles(self, spec, prebuild):
524
+ """Writes rules to match MSVS's implicit idl handling."""
525
+ assert self.flavor == 'win'
526
+ if self.msvs_settings.HasExplicitIdlRules(spec):
527
+ return []
528
+ outputs = []
529
+ for source in filter(lambda x: x.endswith('.idl'), spec['sources']):
530
+ self._WinIdlRule(source, prebuild, outputs)
531
+ return outputs
532
+
533
+ def WriteActionsRulesCopies(self, spec, extra_sources, prebuild,
534
+ mac_bundle_depends):
535
+ """Write out the Actions, Rules, and Copies steps. Return a path
536
+ representing the outputs of these steps."""
537
+ outputs = []
538
+ if self.is_mac_bundle:
539
+ mac_bundle_resources = spec.get('mac_bundle_resources', [])[:]
540
+ else:
541
+ mac_bundle_resources = []
542
+ extra_mac_bundle_resources = []
543
+
544
+ if 'actions' in spec:
545
+ outputs += self.WriteActions(spec['actions'], extra_sources, prebuild,
546
+ extra_mac_bundle_resources)
547
+ if 'rules' in spec:
548
+ outputs += self.WriteRules(spec['rules'], extra_sources, prebuild,
549
+ mac_bundle_resources,
550
+ extra_mac_bundle_resources)
551
+ if 'copies' in spec:
552
+ outputs += self.WriteCopies(spec['copies'], prebuild, mac_bundle_depends)
553
+
554
+ if 'sources' in spec and self.flavor == 'win':
555
+ outputs += self.WriteWinIdlFiles(spec, prebuild)
556
+
557
+ stamp = self.WriteCollapsedDependencies('actions_rules_copies', outputs)
558
+
559
+ if self.is_mac_bundle:
560
+ self.WriteMacBundleResources(
561
+ extra_mac_bundle_resources + mac_bundle_resources, mac_bundle_depends)
562
+ self.WriteMacInfoPlist(mac_bundle_depends)
563
+
564
+ return stamp
565
+
566
+ def GenerateDescription(self, verb, message, fallback):
567
+ """Generate and return a description of a build step.
568
+
569
+ |verb| is the short summary, e.g. ACTION or RULE.
570
+ |message| is a hand-written description, or None if not available.
571
+ |fallback| is the gyp-level name of the step, usable as a fallback.
572
+ """
573
+ if self.toolset != 'target':
574
+ verb += '(%s)' % self.toolset
575
+ if message:
576
+ return '%s %s' % (verb, self.ExpandSpecial(message))
577
+ else:
578
+ return '%s %s: %s' % (verb, self.name, fallback)
579
+
580
+ def WriteActions(self, actions, extra_sources, prebuild,
581
+ extra_mac_bundle_resources):
582
+ # Actions cd into the base directory.
583
+ env = self.GetSortedXcodeEnv()
584
+ if self.flavor == 'win':
585
+ env = self.msvs_settings.GetVSMacroEnv(
586
+ '$!PRODUCT_DIR', config=self.config_name)
587
+ all_outputs = []
588
+ for action in actions:
589
+ # First write out a rule for the action.
590
+ name = '%s_%s' % (action['action_name'],
591
+ hashlib.md5(self.qualified_target).hexdigest())
592
+ description = self.GenerateDescription('ACTION',
593
+ action.get('message', None),
594
+ name)
595
+ is_cygwin = (self.msvs_settings.IsRuleRunUnderCygwin(action)
596
+ if self.flavor == 'win' else False)
597
+ args = action['action']
598
+ rule_name, _ = self.WriteNewNinjaRule(name, args, description,
599
+ is_cygwin, env=env)
600
+
601
+ inputs = [self.GypPathToNinja(i, env) for i in action['inputs']]
602
+ if int(action.get('process_outputs_as_sources', False)):
603
+ extra_sources += action['outputs']
604
+ if int(action.get('process_outputs_as_mac_bundle_resources', False)):
605
+ extra_mac_bundle_resources += action['outputs']
606
+ outputs = [self.GypPathToNinja(o, env) for o in action['outputs']]
607
+
608
+ # Then write out an edge using the rule.
609
+ self.ninja.build(outputs, rule_name, inputs,
610
+ order_only=prebuild)
611
+ all_outputs += outputs
612
+
613
+ self.ninja.newline()
614
+
615
+ return all_outputs
616
+
617
+ def WriteRules(self, rules, extra_sources, prebuild,
618
+ mac_bundle_resources, extra_mac_bundle_resources):
619
+ env = self.GetSortedXcodeEnv()
620
+ all_outputs = []
621
+ for rule in rules:
622
+ # First write out a rule for the rule action.
623
+ name = '%s_%s' % (rule['rule_name'],
624
+ hashlib.md5(self.qualified_target).hexdigest())
625
+ # Skip a rule with no action and no inputs.
626
+ if 'action' not in rule and not rule.get('rule_sources', []):
627
+ continue
628
+ args = rule['action']
629
+ description = self.GenerateDescription(
630
+ 'RULE',
631
+ rule.get('message', None),
632
+ ('%s ' + generator_default_variables['RULE_INPUT_PATH']) % name)
633
+ is_cygwin = (self.msvs_settings.IsRuleRunUnderCygwin(rule)
634
+ if self.flavor == 'win' else False)
635
+ rule_name, args = self.WriteNewNinjaRule(
636
+ name, args, description, is_cygwin, env=env)
637
+
638
+ # TODO: if the command references the outputs directly, we should
639
+ # simplify it to just use $out.
640
+
641
+ # Rules can potentially make use of some special variables which
642
+ # must vary per source file.
643
+ # Compute the list of variables we'll need to provide.
644
+ special_locals = ('source', 'root', 'dirname', 'ext', 'name')
645
+ needed_variables = set(['source'])
646
+ for argument in args:
647
+ for var in special_locals:
648
+ if ('${%s}' % var) in argument:
649
+ needed_variables.add(var)
650
+
651
+ def cygwin_munge(path):
652
+ if is_cygwin:
653
+ return path.replace('\\', '/')
654
+ return path
655
+
656
+ # For each source file, write an edge that generates all the outputs.
657
+ for source in rule.get('rule_sources', []):
658
+ source = os.path.normpath(source)
659
+ dirname, basename = os.path.split(source)
660
+ root, ext = os.path.splitext(basename)
661
+
662
+ # Gather the list of inputs and outputs, expanding $vars if possible.
663
+ outputs = [self.ExpandRuleVariables(o, root, dirname,
664
+ source, ext, basename)
665
+ for o in rule['outputs']]
666
+ inputs = [self.ExpandRuleVariables(i, root, dirname,
667
+ source, ext, basename)
668
+ for i in rule.get('inputs', [])]
669
+
670
+ if int(rule.get('process_outputs_as_sources', False)):
671
+ extra_sources += outputs
672
+
673
+ was_mac_bundle_resource = source in mac_bundle_resources
674
+ if was_mac_bundle_resource or \
675
+ int(rule.get('process_outputs_as_mac_bundle_resources', False)):
676
+ extra_mac_bundle_resources += outputs
677
+ # Note: This is n_resources * n_outputs_in_rule. Put to-be-removed
678
+ # items in a set and remove them all in a single pass if this becomes
679
+ # a performance issue.
680
+ if was_mac_bundle_resource:
681
+ mac_bundle_resources.remove(source)
682
+
683
+ extra_bindings = []
684
+ for var in needed_variables:
685
+ if var == 'root':
686
+ extra_bindings.append(('root', cygwin_munge(root)))
687
+ elif var == 'dirname':
688
+ # '$dirname' is a parameter to the rule action, which means
689
+ # it shouldn't be converted to a Ninja path. But we don't
690
+ # want $!PRODUCT_DIR in there either.
691
+ dirname_expanded = self.ExpandSpecial(dirname, self.base_to_build)
692
+ extra_bindings.append(('dirname', cygwin_munge(dirname_expanded)))
693
+ elif var == 'source':
694
+ # '$source' is a parameter to the rule action, which means
695
+ # it shouldn't be converted to a Ninja path. But we don't
696
+ # want $!PRODUCT_DIR in there either.
697
+ source_expanded = self.ExpandSpecial(source, self.base_to_build)
698
+ extra_bindings.append(('source', cygwin_munge(source_expanded)))
699
+ elif var == 'ext':
700
+ extra_bindings.append(('ext', ext))
701
+ elif var == 'name':
702
+ extra_bindings.append(('name', cygwin_munge(basename)))
703
+ else:
704
+ assert var == None, repr(var)
705
+
706
+ inputs = [self.GypPathToNinja(i, env) for i in inputs]
707
+ outputs = [self.GypPathToNinja(o, env) for o in outputs]
708
+ extra_bindings.append(('unique_name',
709
+ hashlib.md5(outputs[0]).hexdigest()))
710
+ self.ninja.build(outputs, rule_name, self.GypPathToNinja(source),
711
+ implicit=inputs,
712
+ order_only=prebuild,
713
+ variables=extra_bindings)
714
+
715
+ all_outputs.extend(outputs)
716
+
717
+ return all_outputs
718
+
719
+ def WriteCopies(self, copies, prebuild, mac_bundle_depends):
720
+ outputs = []
721
+ env = self.GetSortedXcodeEnv()
722
+ for copy in copies:
723
+ for path in copy['files']:
724
+ # Normalize the path so trailing slashes don't confuse us.
725
+ path = os.path.normpath(path)
726
+ basename = os.path.split(path)[1]
727
+ src = self.GypPathToNinja(path, env)
728
+ dst = self.GypPathToNinja(os.path.join(copy['destination'], basename),
729
+ env)
730
+ outputs += self.ninja.build(dst, 'copy', src, order_only=prebuild)
731
+ if self.is_mac_bundle:
732
+ # gyp has mac_bundle_resources to copy things into a bundle's
733
+ # Resources folder, but there's no built-in way to copy files to other
734
+ # places in the bundle. Hence, some targets use copies for this. Check
735
+ # if this file is copied into the current bundle, and if so add it to
736
+ # the bundle depends so that dependent targets get rebuilt if the copy
737
+ # input changes.
738
+ if dst.startswith(self.xcode_settings.GetBundleContentsFolderPath()):
739
+ mac_bundle_depends.append(dst)
740
+
741
+ return outputs
742
+
743
+ def WriteMacBundleResources(self, resources, bundle_depends):
744
+ """Writes ninja edges for 'mac_bundle_resources'."""
745
+ for output, res in gyp.xcode_emulation.GetMacBundleResources(
746
+ generator_default_variables['PRODUCT_DIR'],
747
+ self.xcode_settings, map(self.GypPathToNinja, resources)):
748
+ output = self.ExpandSpecial(output)
749
+ self.ninja.build(output, 'mac_tool', res,
750
+ variables=[('mactool_cmd', 'copy-bundle-resource')])
751
+ bundle_depends.append(output)
752
+
753
+ def WriteMacInfoPlist(self, bundle_depends):
754
+ """Write build rules for bundle Info.plist files."""
755
+ info_plist, out, defines, extra_env = gyp.xcode_emulation.GetMacInfoPlist(
756
+ generator_default_variables['PRODUCT_DIR'],
757
+ self.xcode_settings, self.GypPathToNinja)
758
+ if not info_plist:
759
+ return
760
+ out = self.ExpandSpecial(out)
761
+ if defines:
762
+ # Create an intermediate file to store preprocessed results.
763
+ intermediate_plist = self.GypPathToUniqueOutput(
764
+ os.path.basename(info_plist))
765
+ defines = ' '.join([Define(d, self.flavor) for d in defines])
766
+ info_plist = self.ninja.build(
767
+ intermediate_plist, 'preprocess_infoplist', info_plist,
768
+ variables=[('defines',defines)])
769
+
770
+ env = self.GetSortedXcodeEnv(additional_settings=extra_env)
771
+ env = self.ComputeExportEnvString(env)
772
+
773
+ keys = self.xcode_settings.GetExtraPlistItems(self.config_name)
774
+ keys = QuoteShellArgument(json.dumps(keys), self.flavor)
775
+ self.ninja.build(out, 'copy_infoplist', info_plist,
776
+ variables=[('env', env), ('keys', keys)])
777
+ bundle_depends.append(out)
778
+
779
+ def WriteSources(self, ninja_file, config_name, config, sources, predepends,
780
+ precompiled_header, spec):
781
+ """Write build rules to compile all of |sources|."""
782
+ if self.toolset == 'host':
783
+ self.ninja.variable('ar', '$ar_host')
784
+ self.ninja.variable('cc', '$cc_host')
785
+ self.ninja.variable('cxx', '$cxx_host')
786
+ self.ninja.variable('ld', '$ld_host')
787
+ self.ninja.variable('ldxx', '$ldxx_host')
788
+
789
+ if self.flavor != 'mac' or len(self.archs) == 1:
790
+ return self.WriteSourcesForArch(
791
+ self.ninja, config_name, config, sources, predepends,
792
+ precompiled_header, spec)
793
+ else:
794
+ return dict((arch, self.WriteSourcesForArch(
795
+ self.arch_subninjas[arch], config_name, config, sources, predepends,
796
+ precompiled_header, spec, arch=arch))
797
+ for arch in self.archs)
798
+
799
+ def WriteSourcesForArch(self, ninja_file, config_name, config, sources,
800
+ predepends, precompiled_header, spec, arch=None):
801
+ """Write build rules to compile all of |sources|."""
802
+
803
+ extra_defines = []
804
+ if self.flavor == 'mac':
805
+ cflags = self.xcode_settings.GetCflags(config_name, arch=arch)
806
+ cflags_c = self.xcode_settings.GetCflagsC(config_name)
807
+ cflags_cc = self.xcode_settings.GetCflagsCC(config_name)
808
+ cflags_objc = ['$cflags_c'] + \
809
+ self.xcode_settings.GetCflagsObjC(config_name)
810
+ cflags_objcc = ['$cflags_cc'] + \
811
+ self.xcode_settings.GetCflagsObjCC(config_name)
812
+ elif self.flavor == 'win':
813
+ cflags = self.msvs_settings.GetCflags(config_name)
814
+ cflags_c = self.msvs_settings.GetCflagsC(config_name)
815
+ cflags_cc = self.msvs_settings.GetCflagsCC(config_name)
816
+ extra_defines = self.msvs_settings.GetComputedDefines(config_name)
817
+ # See comment at cc_command for why there's two .pdb files.
818
+ pdbpath_c = pdbpath_cc = self.msvs_settings.GetCompilerPdbName(
819
+ config_name, self.ExpandSpecial)
820
+ if not pdbpath_c:
821
+ obj = 'obj'
822
+ if self.toolset != 'target':
823
+ obj += '.' + self.toolset
824
+ pdbpath = os.path.normpath(os.path.join(obj, self.base_dir, self.name))
825
+ pdbpath_c = pdbpath + '.c.pdb'
826
+ pdbpath_cc = pdbpath + '.cc.pdb'
827
+ self.WriteVariableList(ninja_file, 'pdbname_c', [pdbpath_c])
828
+ self.WriteVariableList(ninja_file, 'pdbname_cc', [pdbpath_cc])
829
+ self.WriteVariableList(ninja_file, 'pchprefix', [self.name])
830
+ else:
831
+ cflags = config.get('cflags', [])
832
+ cflags_c = config.get('cflags_c', [])
833
+ cflags_cc = config.get('cflags_cc', [])
834
+
835
+ # Respect environment variables related to build, but target-specific
836
+ # flags can still override them.
837
+ if self.toolset == 'target':
838
+ cflags_c = (os.environ.get('CPPFLAGS', '').split() +
839
+ os.environ.get('CFLAGS', '').split() + cflags_c)
840
+ cflags_cc = (os.environ.get('CPPFLAGS', '').split() +
841
+ os.environ.get('CXXFLAGS', '').split() + cflags_cc)
842
+
843
+ defines = config.get('defines', []) + extra_defines
844
+ self.WriteVariableList(ninja_file, 'defines',
845
+ [Define(d, self.flavor) for d in defines])
846
+ if self.flavor == 'win':
847
+ self.WriteVariableList(ninja_file, 'rcflags',
848
+ [QuoteShellArgument(self.ExpandSpecial(f), self.flavor)
849
+ for f in self.msvs_settings.GetRcflags(config_name,
850
+ self.GypPathToNinja)])
851
+
852
+ include_dirs = config.get('include_dirs', [])
853
+ env = self.GetSortedXcodeEnv()
854
+ if self.flavor == 'win':
855
+ env = self.msvs_settings.GetVSMacroEnv('$!PRODUCT_DIR',
856
+ config=config_name)
857
+ include_dirs = self.msvs_settings.AdjustIncludeDirs(include_dirs,
858
+ config_name)
859
+ self.WriteVariableList(ninja_file, 'includes',
860
+ [QuoteShellArgument('-I' + self.GypPathToNinja(i, env), self.flavor)
861
+ for i in include_dirs])
862
+
863
+ pch_commands = precompiled_header.GetPchBuildCommands(arch)
864
+ if self.flavor == 'mac':
865
+ # Most targets use no precompiled headers, so only write these if needed.
866
+ for ext, var in [('c', 'cflags_pch_c'), ('cc', 'cflags_pch_cc'),
867
+ ('m', 'cflags_pch_objc'), ('mm', 'cflags_pch_objcc')]:
868
+ include = precompiled_header.GetInclude(ext, arch)
869
+ if include: ninja_file.variable(var, include)
870
+
871
+ self.WriteVariableList(ninja_file, 'cflags',
872
+ map(self.ExpandSpecial, cflags))
873
+ self.WriteVariableList(ninja_file, 'cflags_c',
874
+ map(self.ExpandSpecial, cflags_c))
875
+ self.WriteVariableList(ninja_file, 'cflags_cc',
876
+ map(self.ExpandSpecial, cflags_cc))
877
+ if self.flavor == 'mac':
878
+ self.WriteVariableList(ninja_file, 'cflags_objc',
879
+ map(self.ExpandSpecial, cflags_objc))
880
+ self.WriteVariableList(ninja_file, 'cflags_objcc',
881
+ map(self.ExpandSpecial, cflags_objcc))
882
+ ninja_file.newline()
883
+ outputs = []
884
+ has_rc_source = False
885
+ for source in sources:
886
+ filename, ext = os.path.splitext(source)
887
+ ext = ext[1:]
888
+ obj_ext = self.obj_ext
889
+ if ext in ('cc', 'cpp', 'cxx'):
890
+ command = 'cxx'
891
+ self.uses_cpp = True
892
+ elif ext == 'c' or (ext == 'S' and self.flavor != 'win'):
893
+ command = 'cc'
894
+ elif ext == 's' and self.flavor != 'win': # Doesn't generate .o.d files.
895
+ command = 'cc_s'
896
+ elif (self.flavor == 'win' and ext == 'asm' and
897
+ self.msvs_settings.GetArch(config_name) == 'x86' and
898
+ not self.msvs_settings.HasExplicitAsmRules(spec)):
899
+ # Asm files only get auto assembled for x86 (not x64).
900
+ command = 'asm'
901
+ # Add the _asm suffix as msvs is capable of handling .cc and
902
+ # .asm files of the same name without collision.
903
+ obj_ext = '_asm.obj'
904
+ elif self.flavor == 'mac' and ext == 'm':
905
+ command = 'objc'
906
+ elif self.flavor == 'mac' and ext == 'mm':
907
+ command = 'objcxx'
908
+ self.uses_cpp = True
909
+ elif self.flavor == 'win' and ext == 'rc':
910
+ command = 'rc'
911
+ obj_ext = '.res'
912
+ has_rc_source = True
913
+ else:
914
+ # Ignore unhandled extensions.
915
+ continue
916
+ input = self.GypPathToNinja(source)
917
+ output = self.GypPathToUniqueOutput(filename + obj_ext)
918
+ if arch is not None:
919
+ output = AddArch(output, arch)
920
+ implicit = precompiled_header.GetObjDependencies([input], [output], arch)
921
+ variables = []
922
+ if self.flavor == 'win':
923
+ variables, output, implicit = precompiled_header.GetFlagsModifications(
924
+ input, output, implicit, command, cflags_c, cflags_cc,
925
+ self.ExpandSpecial)
926
+ ninja_file.build(output, command, input,
927
+ implicit=[gch for _, _, gch in implicit],
928
+ order_only=predepends, variables=variables)
929
+ outputs.append(output)
930
+
931
+ if has_rc_source:
932
+ resource_include_dirs = config.get('resource_include_dirs', include_dirs)
933
+ self.WriteVariableList(ninja_file, 'resource_includes',
934
+ [QuoteShellArgument('-I' + self.GypPathToNinja(i, env), self.flavor)
935
+ for i in resource_include_dirs])
936
+
937
+ self.WritePchTargets(ninja_file, pch_commands)
938
+
939
+ ninja_file.newline()
940
+ return outputs
941
+
942
+ def WritePchTargets(self, ninja_file, pch_commands):
943
+ """Writes ninja rules to compile prefix headers."""
944
+ if not pch_commands:
945
+ return
946
+
947
+ for gch, lang_flag, lang, input in pch_commands:
948
+ var_name = {
949
+ 'c': 'cflags_pch_c',
950
+ 'cc': 'cflags_pch_cc',
951
+ 'm': 'cflags_pch_objc',
952
+ 'mm': 'cflags_pch_objcc',
953
+ }[lang]
954
+
955
+ map = { 'c': 'cc', 'cc': 'cxx', 'm': 'objc', 'mm': 'objcxx', }
956
+ cmd = map.get(lang)
957
+ ninja_file.build(gch, cmd, input, variables=[(var_name, lang_flag)])
958
+
959
+ def WriteLink(self, spec, config_name, config, link_deps):
960
+ """Write out a link step. Fills out target.binary. """
961
+ if self.flavor != 'mac' or len(self.archs) == 1:
962
+ return self.WriteLinkForArch(
963
+ self.ninja, spec, config_name, config, link_deps)
964
+ else:
965
+ output = self.ComputeOutput(spec)
966
+ inputs = [self.WriteLinkForArch(self.arch_subninjas[arch], spec,
967
+ config_name, config, link_deps[arch],
968
+ arch=arch)
969
+ for arch in self.archs]
970
+ extra_bindings = []
971
+ if not self.is_mac_bundle:
972
+ self.AppendPostbuildVariable(extra_bindings, spec, output, output)
973
+ self.ninja.build(output, 'lipo', inputs, variables=extra_bindings)
974
+ return output
975
+
976
+ def WriteLinkForArch(self, ninja_file, spec, config_name, config,
977
+ link_deps, arch=None):
978
+ """Write out a link step. Fills out target.binary. """
979
+ command = {
980
+ 'executable': 'link',
981
+ 'loadable_module': 'solink_module',
982
+ 'shared_library': 'solink',
983
+ }[spec['type']]
984
+ command_suffix = ''
985
+
986
+ implicit_deps = set()
987
+ solibs = set()
988
+
989
+ if 'dependencies' in spec:
990
+ # Two kinds of dependencies:
991
+ # - Linkable dependencies (like a .a or a .so): add them to the link line.
992
+ # - Non-linkable dependencies (like a rule that generates a file
993
+ # and writes a stamp file): add them to implicit_deps
994
+ extra_link_deps = set()
995
+ for dep in spec['dependencies']:
996
+ target = self.target_outputs.get(dep)
997
+ if not target:
998
+ continue
999
+ linkable = target.Linkable()
1000
+ if linkable:
1001
+ new_deps = []
1002
+ if (self.flavor == 'win' and
1003
+ target.component_objs and
1004
+ self.msvs_settings.IsUseLibraryDependencyInputs(config_name)):
1005
+ new_deps = target.component_objs
1006
+ elif self.flavor == 'win' and target.import_lib:
1007
+ new_deps = [target.import_lib]
1008
+ elif target.UsesToc(self.flavor):
1009
+ solibs.add(target.binary)
1010
+ implicit_deps.add(target.binary + '.TOC')
1011
+ else:
1012
+ new_deps = [target.binary]
1013
+ for new_dep in new_deps:
1014
+ if new_dep not in extra_link_deps:
1015
+ extra_link_deps.add(new_dep)
1016
+ link_deps.append(new_dep)
1017
+
1018
+ final_output = target.FinalOutput()
1019
+ if not linkable or final_output != target.binary:
1020
+ implicit_deps.add(final_output)
1021
+
1022
+ extra_bindings = []
1023
+ if self.uses_cpp and self.flavor != 'win':
1024
+ extra_bindings.append(('ld', '$ldxx'))
1025
+
1026
+ output = self.ComputeOutput(spec, arch)
1027
+ if arch is None and not self.is_mac_bundle:
1028
+ self.AppendPostbuildVariable(extra_bindings, spec, output, output)
1029
+
1030
+ is_executable = spec['type'] == 'executable'
1031
+ # The ldflags config key is not used on mac or win. On those platforms
1032
+ # linker flags are set via xcode_settings and msvs_settings, respectively.
1033
+ env_ldflags = os.environ.get('LDFLAGS', '').split()
1034
+ if self.flavor == 'mac':
1035
+ ldflags = self.xcode_settings.GetLdflags(config_name,
1036
+ self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']),
1037
+ self.GypPathToNinja, arch)
1038
+ ldflags = env_ldflags + ldflags
1039
+ elif self.flavor == 'win':
1040
+ manifest_base_name = self.GypPathToUniqueOutput(
1041
+ self.ComputeOutputFileName(spec))
1042
+ ldflags, intermediate_manifest, manifest_files = \
1043
+ self.msvs_settings.GetLdflags(config_name, self.GypPathToNinja,
1044
+ self.ExpandSpecial, manifest_base_name,
1045
+ output, is_executable,
1046
+ self.toplevel_build)
1047
+ ldflags = env_ldflags + ldflags
1048
+ self.WriteVariableList(ninja_file, 'manifests', manifest_files)
1049
+ implicit_deps = implicit_deps.union(manifest_files)
1050
+ if intermediate_manifest:
1051
+ self.WriteVariableList(
1052
+ ninja_file, 'intermediatemanifest', [intermediate_manifest])
1053
+ command_suffix = _GetWinLinkRuleNameSuffix(
1054
+ self.msvs_settings.IsEmbedManifest(config_name))
1055
+ def_file = self.msvs_settings.GetDefFile(self.GypPathToNinja)
1056
+ if def_file:
1057
+ implicit_deps.add(def_file)
1058
+ else:
1059
+ # Respect environment variables related to build, but target-specific
1060
+ # flags can still override them.
1061
+ ldflags = env_ldflags + config.get('ldflags', [])
1062
+ if is_executable and len(solibs):
1063
+ rpath = 'lib/'
1064
+ if self.toolset != 'target':
1065
+ rpath += self.toolset
1066
+ ldflags.append('-Wl,-rpath=\$$ORIGIN/%s' % rpath)
1067
+ ldflags.append('-Wl,-rpath-link=%s' % rpath)
1068
+ self.WriteVariableList(ninja_file, 'ldflags',
1069
+ gyp.common.uniquer(map(self.ExpandSpecial, ldflags)))
1070
+
1071
+ library_dirs = config.get('library_dirs', [])
1072
+ if self.flavor == 'win':
1073
+ library_dirs = [self.msvs_settings.ConvertVSMacros(l, config_name)
1074
+ for l in library_dirs]
1075
+ library_dirs = ['/LIBPATH:' + QuoteShellArgument(self.GypPathToNinja(l),
1076
+ self.flavor)
1077
+ for l in library_dirs]
1078
+ else:
1079
+ library_dirs = [QuoteShellArgument('-L' + self.GypPathToNinja(l),
1080
+ self.flavor)
1081
+ for l in library_dirs]
1082
+
1083
+ libraries = gyp.common.uniquer(map(self.ExpandSpecial,
1084
+ spec.get('libraries', [])))
1085
+ if self.flavor == 'mac':
1086
+ libraries = self.xcode_settings.AdjustLibraries(libraries, config_name)
1087
+ elif self.flavor == 'win':
1088
+ libraries = self.msvs_settings.AdjustLibraries(libraries)
1089
+
1090
+ self.WriteVariableList(ninja_file, 'libs', library_dirs + libraries)
1091
+
1092
+ linked_binary = output
1093
+
1094
+ if command in ('solink', 'solink_module'):
1095
+ extra_bindings.append(('soname', os.path.split(output)[1]))
1096
+ extra_bindings.append(('lib',
1097
+ gyp.common.EncodePOSIXShellArgument(output)))
1098
+ if self.flavor == 'win':
1099
+ extra_bindings.append(('binary', output))
1100
+ if '/NOENTRY' not in ldflags:
1101
+ self.target.import_lib = output + '.lib'
1102
+ extra_bindings.append(('implibflag',
1103
+ '/IMPLIB:%s' % self.target.import_lib))
1104
+ pdbname = self.msvs_settings.GetPDBName(
1105
+ config_name, self.ExpandSpecial, output + '.pdb')
1106
+ output = [output, self.target.import_lib]
1107
+ if pdbname:
1108
+ output.append(pdbname)
1109
+ elif not self.is_mac_bundle:
1110
+ output = [output, output + '.TOC']
1111
+ else:
1112
+ command = command + '_notoc'
1113
+ elif self.flavor == 'win':
1114
+ extra_bindings.append(('binary', output))
1115
+ pdbname = self.msvs_settings.GetPDBName(
1116
+ config_name, self.ExpandSpecial, output + '.pdb')
1117
+ if pdbname:
1118
+ output = [output, pdbname]
1119
+
1120
+
1121
+ if len(solibs):
1122
+ extra_bindings.append(('solibs', gyp.common.EncodePOSIXShellList(solibs)))
1123
+
1124
+ ninja_file.build(output, command + command_suffix, link_deps,
1125
+ implicit=list(implicit_deps),
1126
+ variables=extra_bindings)
1127
+ return linked_binary
1128
+
1129
+ def WriteTarget(self, spec, config_name, config, link_deps, compile_deps):
1130
+ extra_link_deps = any(self.target_outputs.get(dep).Linkable()
1131
+ for dep in spec.get('dependencies', [])
1132
+ if dep in self.target_outputs)
1133
+ if spec['type'] == 'none' or (not link_deps and not extra_link_deps):
1134
+ # TODO(evan): don't call this function for 'none' target types, as
1135
+ # it doesn't do anything, and we fake out a 'binary' with a stamp file.
1136
+ self.target.binary = compile_deps
1137
+ self.target.type = 'none'
1138
+ elif spec['type'] == 'static_library':
1139
+ self.target.binary = self.ComputeOutput(spec)
1140
+ if (self.flavor not in ('mac', 'openbsd', 'win') and not
1141
+ self.is_standalone_static_library):
1142
+ self.ninja.build(self.target.binary, 'alink_thin', link_deps,
1143
+ order_only=compile_deps)
1144
+ else:
1145
+ variables = []
1146
+ if self.xcode_settings:
1147
+ libtool_flags = self.xcode_settings.GetLibtoolflags(config_name)
1148
+ if libtool_flags:
1149
+ variables.append(('libtool_flags', libtool_flags))
1150
+ if self.msvs_settings:
1151
+ libflags = self.msvs_settings.GetLibFlags(config_name,
1152
+ self.GypPathToNinja)
1153
+ variables.append(('libflags', libflags))
1154
+
1155
+ if self.flavor != 'mac' or len(self.archs) == 1:
1156
+ self.AppendPostbuildVariable(variables, spec,
1157
+ self.target.binary, self.target.binary)
1158
+ self.ninja.build(self.target.binary, 'alink', link_deps,
1159
+ order_only=compile_deps, variables=variables)
1160
+ else:
1161
+ inputs = []
1162
+ for arch in self.archs:
1163
+ output = self.ComputeOutput(spec, arch)
1164
+ self.arch_subninjas[arch].build(output, 'alink', link_deps[arch],
1165
+ order_only=compile_deps,
1166
+ variables=variables)
1167
+ inputs.append(output)
1168
+ # TODO: It's not clear if libtool_flags should be passed to the alink
1169
+ # call that combines single-arch .a files into a fat .a file.
1170
+ self.AppendPostbuildVariable(variables, spec,
1171
+ self.target.binary, self.target.binary)
1172
+ self.ninja.build(self.target.binary, 'alink', inputs,
1173
+ # FIXME: test proving order_only=compile_deps isn't
1174
+ # needed.
1175
+ variables=variables)
1176
+ else:
1177
+ self.target.binary = self.WriteLink(spec, config_name, config, link_deps)
1178
+ return self.target.binary
1179
+
1180
+ def WriteMacBundle(self, spec, mac_bundle_depends, is_empty):
1181
+ assert self.is_mac_bundle
1182
+ package_framework = spec['type'] in ('shared_library', 'loadable_module')
1183
+ output = self.ComputeMacBundleOutput()
1184
+ if is_empty:
1185
+ output += '.stamp'
1186
+ variables = []
1187
+ self.AppendPostbuildVariable(variables, spec, output, self.target.binary,
1188
+ is_command_start=not package_framework)
1189
+ if package_framework and not is_empty:
1190
+ variables.append(('version', self.xcode_settings.GetFrameworkVersion()))
1191
+ self.ninja.build(output, 'package_framework', mac_bundle_depends,
1192
+ variables=variables)
1193
+ else:
1194
+ self.ninja.build(output, 'stamp', mac_bundle_depends,
1195
+ variables=variables)
1196
+ self.target.bundle = output
1197
+ return output
1198
+
1199
+ def GetSortedXcodeEnv(self, additional_settings=None):
1200
+ """Returns the variables Xcode would set for build steps."""
1201
+ assert self.abs_build_dir
1202
+ abs_build_dir = self.abs_build_dir
1203
+ return gyp.xcode_emulation.GetSortedXcodeEnv(
1204
+ self.xcode_settings, abs_build_dir,
1205
+ os.path.join(abs_build_dir, self.build_to_base), self.config_name,
1206
+ additional_settings)
1207
+
1208
+ def GetSortedXcodePostbuildEnv(self):
1209
+ """Returns the variables Xcode would set for postbuild steps."""
1210
+ postbuild_settings = {}
1211
+ # CHROMIUM_STRIP_SAVE_FILE is a chromium-specific hack.
1212
+ # TODO(thakis): It would be nice to have some general mechanism instead.
1213
+ strip_save_file = self.xcode_settings.GetPerTargetSetting(
1214
+ 'CHROMIUM_STRIP_SAVE_FILE')
1215
+ if strip_save_file:
1216
+ postbuild_settings['CHROMIUM_STRIP_SAVE_FILE'] = strip_save_file
1217
+ return self.GetSortedXcodeEnv(additional_settings=postbuild_settings)
1218
+
1219
+ def AppendPostbuildVariable(self, variables, spec, output, binary,
1220
+ is_command_start=False):
1221
+ """Adds a 'postbuild' variable if there is a postbuild for |output|."""
1222
+ postbuild = self.GetPostbuildCommand(spec, output, binary, is_command_start)
1223
+ if postbuild:
1224
+ variables.append(('postbuilds', postbuild))
1225
+
1226
+ def GetPostbuildCommand(self, spec, output, output_binary, is_command_start):
1227
+ """Returns a shell command that runs all the postbuilds, and removes
1228
+ |output| if any of them fails. If |is_command_start| is False, then the
1229
+ returned string will start with ' && '."""
1230
+ if not self.xcode_settings or spec['type'] == 'none' or not output:
1231
+ return ''
1232
+ output = QuoteShellArgument(output, self.flavor)
1233
+ postbuilds = gyp.xcode_emulation.GetSpecPostbuildCommands(spec, quiet=True)
1234
+ if output_binary is not None:
1235
+ postbuilds = self.xcode_settings.AddImplicitPostbuilds(
1236
+ self.config_name,
1237
+ os.path.normpath(os.path.join(self.base_to_build, output)),
1238
+ QuoteShellArgument(
1239
+ os.path.normpath(os.path.join(self.base_to_build, output_binary)),
1240
+ self.flavor),
1241
+ postbuilds, quiet=True)
1242
+
1243
+ if not postbuilds:
1244
+ return ''
1245
+ # Postbuilds expect to be run in the gyp file's directory, so insert an
1246
+ # implicit postbuild to cd to there.
1247
+ postbuilds.insert(0, gyp.common.EncodePOSIXShellList(
1248
+ ['cd', self.build_to_base]))
1249
+ env = self.ComputeExportEnvString(self.GetSortedXcodePostbuildEnv())
1250
+ # G will be non-null if any postbuild fails. Run all postbuilds in a
1251
+ # subshell.
1252
+ commands = env + ' (' + \
1253
+ ' && '.join([ninja_syntax.escape(command) for command in postbuilds])
1254
+ command_string = (commands + '); G=$$?; '
1255
+ # Remove the final output if any postbuild failed.
1256
+ '((exit $$G) || rm -rf %s) ' % output + '&& exit $$G)')
1257
+ if is_command_start:
1258
+ return '(' + command_string + ' && '
1259
+ else:
1260
+ return '$ && (' + command_string
1261
+
1262
+ def ComputeExportEnvString(self, env):
1263
+ """Given an environment, returns a string looking like
1264
+ 'export FOO=foo; export BAR="${FOO} bar;'
1265
+ that exports |env| to the shell."""
1266
+ export_str = []
1267
+ for k, v in env:
1268
+ export_str.append('export %s=%s;' %
1269
+ (k, ninja_syntax.escape(gyp.common.EncodePOSIXShellArgument(v))))
1270
+ return ' '.join(export_str)
1271
+
1272
+ def ComputeMacBundleOutput(self):
1273
+ """Return the 'output' (full output path) to a bundle output directory."""
1274
+ assert self.is_mac_bundle
1275
+ path = generator_default_variables['PRODUCT_DIR']
1276
+ return self.ExpandSpecial(
1277
+ os.path.join(path, self.xcode_settings.GetWrapperName()))
1278
+
1279
+ def ComputeOutputFileName(self, spec, type=None):
1280
+ """Compute the filename of the final output for the current target."""
1281
+ if not type:
1282
+ type = spec['type']
1283
+
1284
+ default_variables = copy.copy(generator_default_variables)
1285
+ CalculateVariables(default_variables, {'flavor': self.flavor})
1286
+
1287
+ # Compute filename prefix: the product prefix, or a default for
1288
+ # the product type.
1289
+ DEFAULT_PREFIX = {
1290
+ 'loadable_module': default_variables['SHARED_LIB_PREFIX'],
1291
+ 'shared_library': default_variables['SHARED_LIB_PREFIX'],
1292
+ 'static_library': default_variables['STATIC_LIB_PREFIX'],
1293
+ 'executable': default_variables['EXECUTABLE_PREFIX'],
1294
+ }
1295
+ prefix = spec.get('product_prefix', DEFAULT_PREFIX.get(type, ''))
1296
+
1297
+ # Compute filename extension: the product extension, or a default
1298
+ # for the product type.
1299
+ DEFAULT_EXTENSION = {
1300
+ 'loadable_module': default_variables['SHARED_LIB_SUFFIX'],
1301
+ 'shared_library': default_variables['SHARED_LIB_SUFFIX'],
1302
+ 'static_library': default_variables['STATIC_LIB_SUFFIX'],
1303
+ 'executable': default_variables['EXECUTABLE_SUFFIX'],
1304
+ }
1305
+ extension = spec.get('product_extension')
1306
+ if extension:
1307
+ extension = '.' + extension
1308
+ else:
1309
+ extension = DEFAULT_EXTENSION.get(type, '')
1310
+
1311
+ if 'product_name' in spec:
1312
+ # If we were given an explicit name, use that.
1313
+ target = spec['product_name']
1314
+ else:
1315
+ # Otherwise, derive a name from the target name.
1316
+ target = spec['target_name']
1317
+ if prefix == 'lib':
1318
+ # Snip out an extra 'lib' from libs if appropriate.
1319
+ target = StripPrefix(target, 'lib')
1320
+
1321
+ if type in ('static_library', 'loadable_module', 'shared_library',
1322
+ 'executable'):
1323
+ return '%s%s%s' % (prefix, target, extension)
1324
+ elif type == 'none':
1325
+ return '%s.stamp' % target
1326
+ else:
1327
+ raise Exception('Unhandled output type %s' % type)
1328
+
1329
+ def ComputeOutput(self, spec, arch=None):
1330
+ """Compute the path for the final output of the spec."""
1331
+ type = spec['type']
1332
+
1333
+ if self.flavor == 'win':
1334
+ override = self.msvs_settings.GetOutputName(self.config_name,
1335
+ self.ExpandSpecial)
1336
+ if override:
1337
+ return override
1338
+
1339
+ if arch is None and self.flavor == 'mac' and type in (
1340
+ 'static_library', 'executable', 'shared_library', 'loadable_module'):
1341
+ filename = self.xcode_settings.GetExecutablePath()
1342
+ else:
1343
+ filename = self.ComputeOutputFileName(spec, type)
1344
+
1345
+ if arch is None and 'product_dir' in spec:
1346
+ path = os.path.join(spec['product_dir'], filename)
1347
+ return self.ExpandSpecial(path)
1348
+
1349
+ # Some products go into the output root, libraries go into shared library
1350
+ # dir, and everything else goes into the normal place.
1351
+ type_in_output_root = ['executable', 'loadable_module']
1352
+ if self.flavor == 'mac' and self.toolset == 'target':
1353
+ type_in_output_root += ['shared_library', 'static_library']
1354
+ elif self.flavor == 'win' and self.toolset == 'target':
1355
+ type_in_output_root += ['shared_library']
1356
+
1357
+ if arch is not None:
1358
+ # Make sure partial executables don't end up in a bundle or the regular
1359
+ # output directory.
1360
+ archdir = 'arch'
1361
+ if self.toolset != 'target':
1362
+ archdir = os.path.join('arch', '%s' % self.toolset)
1363
+ return os.path.join(archdir, AddArch(filename, arch))
1364
+ elif type in type_in_output_root or self.is_standalone_static_library:
1365
+ return filename
1366
+ elif type == 'shared_library':
1367
+ libdir = 'lib'
1368
+ if self.toolset != 'target':
1369
+ libdir = os.path.join('lib', '%s' % self.toolset)
1370
+ return os.path.join(libdir, filename)
1371
+ else:
1372
+ return self.GypPathToUniqueOutput(filename, qualified=False)
1373
+
1374
+ def WriteVariableList(self, ninja_file, var, values):
1375
+ assert not isinstance(values, str)
1376
+ if values is None:
1377
+ values = []
1378
+ ninja_file.variable(var, ' '.join(values))
1379
+
1380
+ def WriteNewNinjaRule(self, name, args, description, is_cygwin, env):
1381
+ """Write out a new ninja "rule" statement for a given command.
1382
+
1383
+ Returns the name of the new rule, and a copy of |args| with variables
1384
+ expanded."""
1385
+
1386
+ if self.flavor == 'win':
1387
+ args = [self.msvs_settings.ConvertVSMacros(
1388
+ arg, self.base_to_build, config=self.config_name)
1389
+ for arg in args]
1390
+ description = self.msvs_settings.ConvertVSMacros(
1391
+ description, config=self.config_name)
1392
+ elif self.flavor == 'mac':
1393
+ # |env| is an empty list on non-mac.
1394
+ args = [gyp.xcode_emulation.ExpandEnvVars(arg, env) for arg in args]
1395
+ description = gyp.xcode_emulation.ExpandEnvVars(description, env)
1396
+
1397
+ # TODO: we shouldn't need to qualify names; we do it because
1398
+ # currently the ninja rule namespace is global, but it really
1399
+ # should be scoped to the subninja.
1400
+ rule_name = self.name
1401
+ if self.toolset == 'target':
1402
+ rule_name += '.' + self.toolset
1403
+ rule_name += '.' + name
1404
+ rule_name = re.sub('[^a-zA-Z0-9_]', '_', rule_name)
1405
+
1406
+ # Remove variable references, but not if they refer to the magic rule
1407
+ # variables. This is not quite right, as it also protects these for
1408
+ # actions, not just for rules where they are valid. Good enough.
1409
+ protect = [ '${root}', '${dirname}', '${source}', '${ext}', '${name}' ]
1410
+ protect = '(?!' + '|'.join(map(re.escape, protect)) + ')'
1411
+ description = re.sub(protect + r'\$', '_', description)
1412
+
1413
+ # gyp dictates that commands are run from the base directory.
1414
+ # cd into the directory before running, and adjust paths in
1415
+ # the arguments to point to the proper locations.
1416
+ rspfile = None
1417
+ rspfile_content = None
1418
+ args = [self.ExpandSpecial(arg, self.base_to_build) for arg in args]
1419
+ if self.flavor == 'win':
1420
+ rspfile = rule_name + '.$unique_name.rsp'
1421
+ # The cygwin case handles this inside the bash sub-shell.
1422
+ run_in = '' if is_cygwin else ' ' + self.build_to_base
1423
+ if is_cygwin:
1424
+ rspfile_content = self.msvs_settings.BuildCygwinBashCommandLine(
1425
+ args, self.build_to_base)
1426
+ else:
1427
+ rspfile_content = gyp.msvs_emulation.EncodeRspFileList(args)
1428
+ command = ('%s gyp-win-tool action-wrapper $arch ' % sys.executable +
1429
+ rspfile + run_in)
1430
+ else:
1431
+ env = self.ComputeExportEnvString(env)
1432
+ command = gyp.common.EncodePOSIXShellList(args)
1433
+ command = 'cd %s; ' % self.build_to_base + env + command
1434
+
1435
+ # GYP rules/actions express being no-ops by not touching their outputs.
1436
+ # Avoid executing downstream dependencies in this case by specifying
1437
+ # restat=1 to ninja.
1438
+ self.ninja.rule(rule_name, command, description, restat=True,
1439
+ rspfile=rspfile, rspfile_content=rspfile_content)
1440
+ self.ninja.newline()
1441
+
1442
+ return rule_name, args
1443
+
1444
+
1445
+ def CalculateVariables(default_variables, params):
1446
+ """Calculate additional variables for use in the build (called by gyp)."""
1447
+ global generator_additional_non_configuration_keys
1448
+ global generator_additional_path_sections
1449
+ flavor = gyp.common.GetFlavor(params)
1450
+ if flavor == 'mac':
1451
+ default_variables.setdefault('OS', 'mac')
1452
+ default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib')
1453
+ default_variables.setdefault('SHARED_LIB_DIR',
1454
+ generator_default_variables['PRODUCT_DIR'])
1455
+ default_variables.setdefault('LIB_DIR',
1456
+ generator_default_variables['PRODUCT_DIR'])
1457
+
1458
+ # Copy additional generator configuration data from Xcode, which is shared
1459
+ # by the Mac Ninja generator.
1460
+ import gyp.generator.xcode as xcode_generator
1461
+ generator_additional_non_configuration_keys = getattr(xcode_generator,
1462
+ 'generator_additional_non_configuration_keys', [])
1463
+ generator_additional_path_sections = getattr(xcode_generator,
1464
+ 'generator_additional_path_sections', [])
1465
+ global generator_extra_sources_for_rules
1466
+ generator_extra_sources_for_rules = getattr(xcode_generator,
1467
+ 'generator_extra_sources_for_rules', [])
1468
+ elif flavor == 'win':
1469
+ default_variables.setdefault('OS', 'win')
1470
+ default_variables['EXECUTABLE_SUFFIX'] = '.exe'
1471
+ default_variables['STATIC_LIB_PREFIX'] = ''
1472
+ default_variables['STATIC_LIB_SUFFIX'] = '.lib'
1473
+ default_variables['SHARED_LIB_PREFIX'] = ''
1474
+ default_variables['SHARED_LIB_SUFFIX'] = '.dll'
1475
+
1476
+ # Copy additional generator configuration data from VS, which is shared
1477
+ # by the Windows Ninja generator.
1478
+ import gyp.generator.msvs as msvs_generator
1479
+ generator_additional_non_configuration_keys = getattr(msvs_generator,
1480
+ 'generator_additional_non_configuration_keys', [])
1481
+ generator_additional_path_sections = getattr(msvs_generator,
1482
+ 'generator_additional_path_sections', [])
1483
+
1484
+ gyp.msvs_emulation.CalculateCommonVariables(default_variables, params)
1485
+ else:
1486
+ operating_system = flavor
1487
+ if flavor == 'android':
1488
+ operating_system = 'linux' # Keep this legacy behavior for now.
1489
+ default_variables.setdefault('OS', operating_system)
1490
+ default_variables.setdefault('SHARED_LIB_SUFFIX', '.so')
1491
+ default_variables.setdefault('SHARED_LIB_DIR',
1492
+ os.path.join('$!PRODUCT_DIR', 'lib'))
1493
+ default_variables.setdefault('LIB_DIR',
1494
+ os.path.join('$!PRODUCT_DIR', 'obj'))
1495
+
1496
+ def ComputeOutputDir(params):
1497
+ """Returns the path from the toplevel_dir to the build output directory."""
1498
+ # generator_dir: relative path from pwd to where make puts build files.
1499
+ # Makes migrating from make to ninja easier, ninja doesn't put anything here.
1500
+ generator_dir = os.path.relpath(params['options'].generator_output or '.')
1501
+
1502
+ # output_dir: relative path from generator_dir to the build directory.
1503
+ output_dir = params.get('generator_flags', {}).get('output_dir', 'out')
1504
+
1505
+ # Relative path from source root to our output files. e.g. "out"
1506
+ return os.path.normpath(os.path.join(generator_dir, output_dir))
1507
+
1508
+
1509
+ def CalculateGeneratorInputInfo(params):
1510
+ """Called by __init__ to initialize generator values based on params."""
1511
+ # E.g. "out/gypfiles"
1512
+ toplevel = params['options'].toplevel_dir
1513
+ qualified_out_dir = os.path.normpath(os.path.join(
1514
+ toplevel, ComputeOutputDir(params), 'gypfiles'))
1515
+
1516
+ global generator_filelist_paths
1517
+ generator_filelist_paths = {
1518
+ 'toplevel': toplevel,
1519
+ 'qualified_out_dir': qualified_out_dir,
1520
+ }
1521
+
1522
+
1523
+ def OpenOutput(path, mode='w'):
1524
+ """Open |path| for writing, creating directories if necessary."""
1525
+ gyp.common.EnsureDirExists(path)
1526
+ return open(path, mode)
1527
+
1528
+
1529
+ def CommandWithWrapper(cmd, wrappers, prog):
1530
+ wrapper = wrappers.get(cmd, '')
1531
+ if wrapper:
1532
+ return wrapper + ' ' + prog
1533
+ return prog
1534
+
1535
+
1536
+ def GetDefaultConcurrentLinks():
1537
+ """Returns a best-guess for a number of concurrent links."""
1538
+ if sys.platform in ('win32', 'cygwin'):
1539
+ import ctypes
1540
+
1541
+ class MEMORYSTATUSEX(ctypes.Structure):
1542
+ _fields_ = [
1543
+ ("dwLength", ctypes.c_ulong),
1544
+ ("dwMemoryLoad", ctypes.c_ulong),
1545
+ ("ullTotalPhys", ctypes.c_ulonglong),
1546
+ ("ullAvailPhys", ctypes.c_ulonglong),
1547
+ ("ullTotalPageFile", ctypes.c_ulonglong),
1548
+ ("ullAvailPageFile", ctypes.c_ulonglong),
1549
+ ("ullTotalVirtual", ctypes.c_ulonglong),
1550
+ ("ullAvailVirtual", ctypes.c_ulonglong),
1551
+ ("sullAvailExtendedVirtual", ctypes.c_ulonglong),
1552
+ ]
1553
+
1554
+ stat = MEMORYSTATUSEX()
1555
+ stat.dwLength = ctypes.sizeof(stat)
1556
+ ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
1557
+
1558
+ mem_limit = max(1, stat.ullTotalPhys / (4 * (2 ** 30))) # total / 4GB
1559
+ hard_cap = max(1, int(os.getenv('GYP_LINK_CONCURRENCY_MAX', 2**32)))
1560
+ return min(mem_limit, hard_cap)
1561
+ elif sys.platform.startswith('linux'):
1562
+ with open("/proc/meminfo") as meminfo:
1563
+ memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB')
1564
+ for line in meminfo:
1565
+ match = memtotal_re.match(line)
1566
+ if not match:
1567
+ continue
1568
+ # Allow 8Gb per link on Linux because Gold is quite memory hungry
1569
+ return max(1, int(match.group(1)) / (8 * (2 ** 20)))
1570
+ return 1
1571
+ elif sys.platform == 'darwin':
1572
+ try:
1573
+ avail_bytes = int(subprocess.check_output(['sysctl', '-n', 'hw.memsize']))
1574
+ # A static library debug build of Chromium's unit_tests takes ~2.7GB, so
1575
+ # 4GB per ld process allows for some more bloat.
1576
+ return max(1, avail_bytes / (4 * (2 ** 30))) # total / 4GB
1577
+ except:
1578
+ return 1
1579
+ else:
1580
+ # TODO(scottmg): Implement this for other platforms.
1581
+ return 1
1582
+
1583
+
1584
+ def _GetWinLinkRuleNameSuffix(embed_manifest):
1585
+ """Returns the suffix used to select an appropriate linking rule depending on
1586
+ whether the manifest embedding is enabled."""
1587
+ return '_embed' if embed_manifest else ''
1588
+
1589
+
1590
+ def _AddWinLinkRules(master_ninja, embed_manifest):
1591
+ """Adds link rules for Windows platform to |master_ninja|."""
1592
+ def FullLinkCommand(ldcmd, out, binary_type):
1593
+ resource_name = {
1594
+ 'exe': '1',
1595
+ 'dll': '2',
1596
+ }[binary_type]
1597
+ return '%(python)s gyp-win-tool link-with-manifests $arch %(embed)s ' \
1598
+ '%(out)s "%(ldcmd)s" %(resname)s $mt $rc "$intermediatemanifest" ' \
1599
+ '$manifests' % {
1600
+ 'python': sys.executable,
1601
+ 'out': out,
1602
+ 'ldcmd': ldcmd,
1603
+ 'resname': resource_name,
1604
+ 'embed': embed_manifest }
1605
+ rule_name_suffix = _GetWinLinkRuleNameSuffix(embed_manifest)
1606
+ use_separate_mspdbsrv = (
1607
+ int(os.environ.get('GYP_USE_SEPARATE_MSPDBSRV', '0')) != 0)
1608
+ dlldesc = 'LINK%s(DLL) $binary' % rule_name_suffix.upper()
1609
+ dllcmd = ('%s gyp-win-tool link-wrapper $arch %s '
1610
+ '$ld /nologo $implibflag /DLL /OUT:$binary '
1611
+ '@$binary.rsp' % (sys.executable, use_separate_mspdbsrv))
1612
+ dllcmd = FullLinkCommand(dllcmd, '$binary', 'dll')
1613
+ master_ninja.rule('solink' + rule_name_suffix,
1614
+ description=dlldesc, command=dllcmd,
1615
+ rspfile='$binary.rsp',
1616
+ rspfile_content='$libs $in_newline $ldflags',
1617
+ restat=True,
1618
+ pool='link_pool')
1619
+ master_ninja.rule('solink_module' + rule_name_suffix,
1620
+ description=dlldesc, command=dllcmd,
1621
+ rspfile='$binary.rsp',
1622
+ rspfile_content='$libs $in_newline $ldflags',
1623
+ restat=True,
1624
+ pool='link_pool')
1625
+ # Note that ldflags goes at the end so that it has the option of
1626
+ # overriding default settings earlier in the command line.
1627
+ exe_cmd = ('%s gyp-win-tool link-wrapper $arch %s '
1628
+ '$ld /nologo /OUT:$binary @$binary.rsp' %
1629
+ (sys.executable, use_separate_mspdbsrv))
1630
+ exe_cmd = FullLinkCommand(exe_cmd, '$binary', 'exe')
1631
+ master_ninja.rule('link' + rule_name_suffix,
1632
+ description='LINK%s $binary' % rule_name_suffix.upper(),
1633
+ command=exe_cmd,
1634
+ rspfile='$binary.rsp',
1635
+ rspfile_content='$in_newline $libs $ldflags',
1636
+ pool='link_pool')
1637
+
1638
+
1639
+ def GenerateOutputForConfig(target_list, target_dicts, data, params,
1640
+ config_name):
1641
+ options = params['options']
1642
+ flavor = gyp.common.GetFlavor(params)
1643
+ generator_flags = params.get('generator_flags', {})
1644
+
1645
+ # build_dir: relative path from source root to our output files.
1646
+ # e.g. "out/Debug"
1647
+ build_dir = os.path.normpath(
1648
+ os.path.join(ComputeOutputDir(params), config_name))
1649
+
1650
+ toplevel_build = os.path.join(options.toplevel_dir, build_dir)
1651
+
1652
+ master_ninja_file = OpenOutput(os.path.join(toplevel_build, 'build.ninja'))
1653
+ master_ninja = ninja_syntax.Writer(master_ninja_file, width=120)
1654
+
1655
+ # Put build-time support tools in out/{config_name}.
1656
+ gyp.common.CopyTool(flavor, toplevel_build)
1657
+
1658
+ # Grab make settings for CC/CXX.
1659
+ # The rules are
1660
+ # - The priority from low to high is gcc/g++, the 'make_global_settings' in
1661
+ # gyp, the environment variable.
1662
+ # - If there is no 'make_global_settings' for CC.host/CXX.host or
1663
+ # 'CC_host'/'CXX_host' enviroment variable, cc_host/cxx_host should be set
1664
+ # to cc/cxx.
1665
+ if flavor == 'win':
1666
+ # Overridden by local arch choice in the use_deps case.
1667
+ # Chromium's ffmpeg c99conv.py currently looks for a 'cc =' line in
1668
+ # build.ninja so needs something valid here. http://crbug.com/233985
1669
+ cc = 'cl.exe'
1670
+ cxx = 'cl.exe'
1671
+ ld = 'link.exe'
1672
+ ld_host = '$ld'
1673
+ else:
1674
+ cc = 'cc'
1675
+ cxx = 'c++'
1676
+ ld = '$cc'
1677
+ ldxx = '$cxx'
1678
+ ld_host = '$cc_host'
1679
+ ldxx_host = '$cxx_host'
1680
+
1681
+ cc_host = None
1682
+ cxx_host = None
1683
+ cc_host_global_setting = None
1684
+ cxx_host_global_setting = None
1685
+ clang_cl = None
1686
+
1687
+ build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
1688
+ make_global_settings = data[build_file].get('make_global_settings', [])
1689
+ build_to_root = gyp.common.InvertRelativePath(build_dir,
1690
+ options.toplevel_dir)
1691
+ wrappers = {}
1692
+ for key, value in make_global_settings:
1693
+ if key == 'CC':
1694
+ cc = os.path.join(build_to_root, value)
1695
+ if cc.endswith('clang-cl'):
1696
+ clang_cl = cc
1697
+ if key == 'CXX':
1698
+ cxx = os.path.join(build_to_root, value)
1699
+ if key == 'CC.host':
1700
+ cc_host = os.path.join(build_to_root, value)
1701
+ cc_host_global_setting = value
1702
+ if key == 'CXX.host':
1703
+ cxx_host = os.path.join(build_to_root, value)
1704
+ cxx_host_global_setting = value
1705
+ if key.endswith('_wrapper'):
1706
+ wrappers[key[:-len('_wrapper')]] = os.path.join(build_to_root, value)
1707
+
1708
+ # Support wrappers from environment variables too.
1709
+ for key, value in os.environ.iteritems():
1710
+ if key.lower().endswith('_wrapper'):
1711
+ key_prefix = key[:-len('_wrapper')]
1712
+ key_prefix = re.sub(r'\.HOST$', '.host', key_prefix)
1713
+ wrappers[key_prefix] = os.path.join(build_to_root, value)
1714
+
1715
+ if flavor == 'win':
1716
+ cl_paths = gyp.msvs_emulation.GenerateEnvironmentFiles(
1717
+ toplevel_build, generator_flags, OpenOutput)
1718
+ for arch, path in cl_paths.iteritems():
1719
+ if clang_cl:
1720
+ # If we have selected clang-cl, use that instead.
1721
+ path = clang_cl
1722
+ command = CommandWithWrapper('CC', wrappers,
1723
+ QuoteShellArgument(path, 'win'))
1724
+ if clang_cl:
1725
+ # Use clang-cl to cross-compile for x86 or x86_64.
1726
+ command += (' -m32' if arch == 'x86' else ' -m64')
1727
+ master_ninja.variable('cl_' + arch, command)
1728
+
1729
+ cc = GetEnvironFallback(['CC_target', 'CC'], cc)
1730
+ master_ninja.variable('cc', CommandWithWrapper('CC', wrappers, cc))
1731
+ cxx = GetEnvironFallback(['CXX_target', 'CXX'], cxx)
1732
+ master_ninja.variable('cxx', CommandWithWrapper('CXX', wrappers, cxx))
1733
+
1734
+ if flavor == 'win':
1735
+ master_ninja.variable('ld', ld)
1736
+ master_ninja.variable('idl', 'midl.exe')
1737
+ master_ninja.variable('ar', 'lib.exe')
1738
+ master_ninja.variable('rc', 'rc.exe')
1739
+ master_ninja.variable('asm', 'ml.exe')
1740
+ master_ninja.variable('mt', 'mt.exe')
1741
+ else:
1742
+ master_ninja.variable('ld', CommandWithWrapper('LINK', wrappers, ld))
1743
+ master_ninja.variable('ldxx', CommandWithWrapper('LINK', wrappers, ldxx))
1744
+ master_ninja.variable('ar', GetEnvironFallback(['AR_target', 'AR'], 'ar'))
1745
+
1746
+ if generator_supports_multiple_toolsets:
1747
+ if not cc_host:
1748
+ cc_host = cc
1749
+ if not cxx_host:
1750
+ cxx_host = cxx
1751
+
1752
+ master_ninja.variable('ar_host', GetEnvironFallback(['AR_host'], 'ar'))
1753
+ cc_host = GetEnvironFallback(['CC_host'], cc_host)
1754
+ cxx_host = GetEnvironFallback(['CXX_host'], cxx_host)
1755
+
1756
+ # The environment variable could be used in 'make_global_settings', like
1757
+ # ['CC.host', '$(CC)'] or ['CXX.host', '$(CXX)'], transform them here.
1758
+ if '$(CC)' in cc_host and cc_host_global_setting:
1759
+ cc_host = cc_host_global_setting.replace('$(CC)', cc)
1760
+ if '$(CXX)' in cxx_host and cxx_host_global_setting:
1761
+ cxx_host = cxx_host_global_setting.replace('$(CXX)', cxx)
1762
+ master_ninja.variable('cc_host',
1763
+ CommandWithWrapper('CC.host', wrappers, cc_host))
1764
+ master_ninja.variable('cxx_host',
1765
+ CommandWithWrapper('CXX.host', wrappers, cxx_host))
1766
+ if flavor == 'win':
1767
+ master_ninja.variable('ld_host', ld_host)
1768
+ else:
1769
+ master_ninja.variable('ld_host', CommandWithWrapper(
1770
+ 'LINK', wrappers, ld_host))
1771
+ master_ninja.variable('ldxx_host', CommandWithWrapper(
1772
+ 'LINK', wrappers, ldxx_host))
1773
+
1774
+ master_ninja.newline()
1775
+
1776
+ master_ninja.pool('link_pool', depth=GetDefaultConcurrentLinks())
1777
+ master_ninja.newline()
1778
+
1779
+ deps = 'msvc' if flavor == 'win' else 'gcc'
1780
+
1781
+ if flavor != 'win':
1782
+ master_ninja.rule(
1783
+ 'cc',
1784
+ description='CC $out',
1785
+ command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c '
1786
+ '$cflags_pch_c -c $in -o $out'),
1787
+ depfile='$out.d',
1788
+ deps=deps)
1789
+ master_ninja.rule(
1790
+ 'cc_s',
1791
+ description='CC $out',
1792
+ command=('$cc $defines $includes $cflags $cflags_c '
1793
+ '$cflags_pch_c -c $in -o $out'))
1794
+ master_ninja.rule(
1795
+ 'cxx',
1796
+ description='CXX $out',
1797
+ command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
1798
+ '$cflags_pch_cc -c $in -o $out'),
1799
+ depfile='$out.d',
1800
+ deps=deps)
1801
+ else:
1802
+ # TODO(scottmg) Separate pdb names is a test to see if it works around
1803
+ # http://crbug.com/142362. It seems there's a race between the creation of
1804
+ # the .pdb by the precompiled header step for .cc and the compilation of
1805
+ # .c files. This should be handled by mspdbsrv, but rarely errors out with
1806
+ # c1xx : fatal error C1033: cannot open program database
1807
+ # By making the rules target separate pdb files this might be avoided.
1808
+ cc_command = ('ninja -t msvc -e $arch ' +
1809
+ '-- '
1810
+ '$cc /nologo /showIncludes /FC '
1811
+ '@$out.rsp /c $in /Fo$out /Fd$pdbname_c ')
1812
+ cxx_command = ('ninja -t msvc -e $arch ' +
1813
+ '-- '
1814
+ '$cxx /nologo /showIncludes /FC '
1815
+ '@$out.rsp /c $in /Fo$out /Fd$pdbname_cc ')
1816
+ master_ninja.rule(
1817
+ 'cc',
1818
+ description='CC $out',
1819
+ command=cc_command,
1820
+ rspfile='$out.rsp',
1821
+ rspfile_content='$defines $includes $cflags $cflags_c',
1822
+ deps=deps)
1823
+ master_ninja.rule(
1824
+ 'cxx',
1825
+ description='CXX $out',
1826
+ command=cxx_command,
1827
+ rspfile='$out.rsp',
1828
+ rspfile_content='$defines $includes $cflags $cflags_cc',
1829
+ deps=deps)
1830
+ master_ninja.rule(
1831
+ 'idl',
1832
+ description='IDL $in',
1833
+ command=('%s gyp-win-tool midl-wrapper $arch $outdir '
1834
+ '$tlb $h $dlldata $iid $proxy $in '
1835
+ '$idlflags' % sys.executable))
1836
+ master_ninja.rule(
1837
+ 'rc',
1838
+ description='RC $in',
1839
+ # Note: $in must be last otherwise rc.exe complains.
1840
+ command=('%s gyp-win-tool rc-wrapper '
1841
+ '$arch $rc $defines $resource_includes $rcflags /fo$out $in' %
1842
+ sys.executable))
1843
+ master_ninja.rule(
1844
+ 'asm',
1845
+ description='ASM $in',
1846
+ command=('%s gyp-win-tool asm-wrapper '
1847
+ '$arch $asm $defines $includes /c /Fo $out $in' %
1848
+ sys.executable))
1849
+
1850
+ if flavor != 'mac' and flavor != 'win':
1851
+ master_ninja.rule(
1852
+ 'alink',
1853
+ description='AR $out',
1854
+ command='rm -f $out && $ar rcs $out $in')
1855
+ master_ninja.rule(
1856
+ 'alink_thin',
1857
+ description='AR $out',
1858
+ command='rm -f $out && $ar rcsT $out $in')
1859
+
1860
+ # This allows targets that only need to depend on $lib's API to declare an
1861
+ # order-only dependency on $lib.TOC and avoid relinking such downstream
1862
+ # dependencies when $lib changes only in non-public ways.
1863
+ # The resulting string leaves an uninterpolated %{suffix} which
1864
+ # is used in the final substitution below.
1865
+ mtime_preserving_solink_base = (
1866
+ 'if [ ! -e $lib -o ! -e ${lib}.TOC ]; then '
1867
+ '%(solink)s && %(extract_toc)s > ${lib}.TOC; else '
1868
+ '%(solink)s && %(extract_toc)s > ${lib}.tmp && '
1869
+ 'if ! cmp -s ${lib}.tmp ${lib}.TOC; then mv ${lib}.tmp ${lib}.TOC ; '
1870
+ 'fi; fi'
1871
+ % { 'solink':
1872
+ '$ld -shared $ldflags -o $lib -Wl,-soname=$soname %(suffix)s',
1873
+ 'extract_toc':
1874
+ ('{ readelf -d ${lib} | grep SONAME ; '
1875
+ 'nm -gD -f p ${lib} | cut -f1-2 -d\' \'; }')})
1876
+
1877
+ master_ninja.rule(
1878
+ 'solink',
1879
+ description='SOLINK $lib',
1880
+ restat=True,
1881
+ command=(mtime_preserving_solink_base % {
1882
+ 'suffix': '-Wl,--whole-archive $in $solibs -Wl,--no-whole-archive '
1883
+ '$libs'}),
1884
+ pool='link_pool')
1885
+ master_ninja.rule(
1886
+ 'solink_module',
1887
+ description='SOLINK(module) $lib',
1888
+ restat=True,
1889
+ command=(mtime_preserving_solink_base % {
1890
+ 'suffix': '-Wl,--start-group $in $solibs -Wl,--end-group '
1891
+ '$libs'}),
1892
+ pool='link_pool')
1893
+ master_ninja.rule(
1894
+ 'link',
1895
+ description='LINK $out',
1896
+ command=('$ld $ldflags -o $out '
1897
+ '-Wl,--start-group $in $solibs -Wl,--end-group $libs'),
1898
+ pool='link_pool')
1899
+ elif flavor == 'win':
1900
+ master_ninja.rule(
1901
+ 'alink',
1902
+ description='LIB $out',
1903
+ command=('%s gyp-win-tool link-wrapper $arch False '
1904
+ '$ar /nologo /ignore:4221 /OUT:$out @$out.rsp' %
1905
+ sys.executable),
1906
+ rspfile='$out.rsp',
1907
+ rspfile_content='$in_newline $libflags')
1908
+ _AddWinLinkRules(master_ninja, embed_manifest=True)
1909
+ _AddWinLinkRules(master_ninja, embed_manifest=False)
1910
+ else:
1911
+ master_ninja.rule(
1912
+ 'objc',
1913
+ description='OBJC $out',
1914
+ command=('$cc -MMD -MF $out.d $defines $includes $cflags $cflags_objc '
1915
+ '$cflags_pch_objc -c $in -o $out'),
1916
+ depfile='$out.d',
1917
+ deps=deps)
1918
+ master_ninja.rule(
1919
+ 'objcxx',
1920
+ description='OBJCXX $out',
1921
+ command=('$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_objcc '
1922
+ '$cflags_pch_objcc -c $in -o $out'),
1923
+ depfile='$out.d',
1924
+ deps=deps)
1925
+ master_ninja.rule(
1926
+ 'alink',
1927
+ description='LIBTOOL-STATIC $out, POSTBUILDS',
1928
+ command='rm -f $out && '
1929
+ './gyp-mac-tool filter-libtool libtool $libtool_flags '
1930
+ '-static -o $out $in'
1931
+ '$postbuilds')
1932
+ master_ninja.rule(
1933
+ 'lipo',
1934
+ description='LIPO $out, POSTBUILDS',
1935
+ command='rm -f $out && lipo -create $in -output $out$postbuilds')
1936
+
1937
+ # Record the public interface of $lib in $lib.TOC. See the corresponding
1938
+ # comment in the posix section above for details.
1939
+ solink_base = '$ld %(type)s $ldflags -o $lib %(suffix)s'
1940
+ mtime_preserving_solink_base = (
1941
+ 'if [ ! -e $lib -o ! -e ${lib}.TOC ] || '
1942
+ # Always force dependent targets to relink if this library
1943
+ # reexports something. Handling this correctly would require
1944
+ # recursive TOC dumping but this is rare in practice, so punt.
1945
+ 'otool -l $lib | grep -q LC_REEXPORT_DYLIB ; then '
1946
+ '%(solink)s && %(extract_toc)s > ${lib}.TOC; '
1947
+ 'else '
1948
+ '%(solink)s && %(extract_toc)s > ${lib}.tmp && '
1949
+ 'if ! cmp -s ${lib}.tmp ${lib}.TOC; then '
1950
+ 'mv ${lib}.tmp ${lib}.TOC ; '
1951
+ 'fi; '
1952
+ 'fi'
1953
+ % { 'solink': solink_base,
1954
+ 'extract_toc':
1955
+ '{ otool -l $lib | grep LC_ID_DYLIB -A 5; '
1956
+ 'nm -gP $lib | cut -f1-2 -d\' \' | grep -v U$$; true; }'})
1957
+
1958
+ solink_suffix = '$in $solibs $libs$postbuilds'
1959
+ master_ninja.rule(
1960
+ 'solink',
1961
+ description='SOLINK $lib, POSTBUILDS',
1962
+ restat=True,
1963
+ command=mtime_preserving_solink_base % {'suffix': solink_suffix,
1964
+ 'type': '-shared'},
1965
+ pool='link_pool')
1966
+ master_ninja.rule(
1967
+ 'solink_notoc',
1968
+ description='SOLINK $lib, POSTBUILDS',
1969
+ restat=True,
1970
+ command=solink_base % {'suffix':solink_suffix, 'type': '-shared'},
1971
+ pool='link_pool')
1972
+
1973
+ solink_module_suffix = '$in $solibs $libs$postbuilds'
1974
+ master_ninja.rule(
1975
+ 'solink_module',
1976
+ description='SOLINK(module) $lib, POSTBUILDS',
1977
+ restat=True,
1978
+ command=mtime_preserving_solink_base % {'suffix': solink_module_suffix,
1979
+ 'type': '-bundle'},
1980
+ pool='link_pool')
1981
+ master_ninja.rule(
1982
+ 'solink_module_notoc',
1983
+ description='SOLINK(module) $lib, POSTBUILDS',
1984
+ restat=True,
1985
+ command=solink_base % {'suffix': solink_module_suffix, 'type': '-bundle'},
1986
+ pool='link_pool')
1987
+
1988
+ master_ninja.rule(
1989
+ 'link',
1990
+ description='LINK $out, POSTBUILDS',
1991
+ command=('$ld $ldflags -o $out '
1992
+ '$in $solibs $libs$postbuilds'),
1993
+ pool='link_pool')
1994
+ master_ninja.rule(
1995
+ 'preprocess_infoplist',
1996
+ description='PREPROCESS INFOPLIST $out',
1997
+ command=('$cc -E -P -Wno-trigraphs -x c $defines $in -o $out && '
1998
+ 'plutil -convert xml1 $out $out'))
1999
+ master_ninja.rule(
2000
+ 'copy_infoplist',
2001
+ description='COPY INFOPLIST $in',
2002
+ command='$env ./gyp-mac-tool copy-info-plist $in $out $keys')
2003
+ master_ninja.rule(
2004
+ 'mac_tool',
2005
+ description='MACTOOL $mactool_cmd $in',
2006
+ command='$env ./gyp-mac-tool $mactool_cmd $in $out')
2007
+ master_ninja.rule(
2008
+ 'package_framework',
2009
+ description='PACKAGE FRAMEWORK $out, POSTBUILDS',
2010
+ command='./gyp-mac-tool package-framework $out $version$postbuilds '
2011
+ '&& touch $out')
2012
+ if flavor == 'win':
2013
+ master_ninja.rule(
2014
+ 'stamp',
2015
+ description='STAMP $out',
2016
+ command='%s gyp-win-tool stamp $out' % sys.executable)
2017
+ master_ninja.rule(
2018
+ 'copy',
2019
+ description='COPY $in $out',
2020
+ command='%s gyp-win-tool recursive-mirror $in $out' % sys.executable)
2021
+ else:
2022
+ master_ninja.rule(
2023
+ 'stamp',
2024
+ description='STAMP $out',
2025
+ command='${postbuilds}touch $out')
2026
+ master_ninja.rule(
2027
+ 'copy',
2028
+ description='COPY $in $out',
2029
+ command='ln -f $in $out 2>/dev/null || (rm -rf $out && cp -af $in $out)')
2030
+ master_ninja.newline()
2031
+
2032
+ all_targets = set()
2033
+ for build_file in params['build_files']:
2034
+ for target in gyp.common.AllTargets(target_list,
2035
+ target_dicts,
2036
+ os.path.normpath(build_file)):
2037
+ all_targets.add(target)
2038
+ all_outputs = set()
2039
+
2040
+ # target_outputs is a map from qualified target name to a Target object.
2041
+ target_outputs = {}
2042
+ # target_short_names is a map from target short name to a list of Target
2043
+ # objects.
2044
+ target_short_names = {}
2045
+
2046
+ for qualified_target in target_list:
2047
+ # qualified_target is like: third_party/icu/icu.gyp:icui18n#target
2048
+ build_file, name, toolset = \
2049
+ gyp.common.ParseQualifiedTarget(qualified_target)
2050
+
2051
+ this_make_global_settings = data[build_file].get('make_global_settings', [])
2052
+ assert make_global_settings == this_make_global_settings, (
2053
+ "make_global_settings needs to be the same for all targets. %s vs. %s" %
2054
+ (this_make_global_settings, make_global_settings))
2055
+
2056
+ spec = target_dicts[qualified_target]
2057
+ if flavor == 'mac':
2058
+ gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec)
2059
+
2060
+ build_file = gyp.common.RelativePath(build_file, options.toplevel_dir)
2061
+
2062
+ base_path = os.path.dirname(build_file)
2063
+ obj = 'obj'
2064
+ if toolset != 'target':
2065
+ obj += '.' + toolset
2066
+ output_file = os.path.join(obj, base_path, name + '.ninja')
2067
+
2068
+ ninja_output = StringIO()
2069
+ writer = NinjaWriter(qualified_target, target_outputs, base_path, build_dir,
2070
+ ninja_output,
2071
+ toplevel_build, output_file,
2072
+ flavor, toplevel_dir=options.toplevel_dir)
2073
+
2074
+ target = writer.WriteSpec(spec, config_name, generator_flags)
2075
+
2076
+ if ninja_output.tell() > 0:
2077
+ # Only create files for ninja files that actually have contents.
2078
+ with OpenOutput(os.path.join(toplevel_build, output_file)) as ninja_file:
2079
+ ninja_file.write(ninja_output.getvalue())
2080
+ ninja_output.close()
2081
+ master_ninja.subninja(output_file)
2082
+
2083
+ if target:
2084
+ if name != target.FinalOutput() and spec['toolset'] == 'target':
2085
+ target_short_names.setdefault(name, []).append(target)
2086
+ target_outputs[qualified_target] = target
2087
+ if qualified_target in all_targets:
2088
+ all_outputs.add(target.FinalOutput())
2089
+
2090
+ if target_short_names:
2091
+ # Write a short name to build this target. This benefits both the
2092
+ # "build chrome" case as well as the gyp tests, which expect to be
2093
+ # able to run actions and build libraries by their short name.
2094
+ master_ninja.newline()
2095
+ master_ninja.comment('Short names for targets.')
2096
+ for short_name in target_short_names:
2097
+ master_ninja.build(short_name, 'phony', [x.FinalOutput() for x in
2098
+ target_short_names[short_name]])
2099
+
2100
+ if all_outputs:
2101
+ master_ninja.newline()
2102
+ master_ninja.build('all', 'phony', list(all_outputs))
2103
+ master_ninja.default(generator_flags.get('default_target', 'all'))
2104
+
2105
+ master_ninja_file.close()
2106
+
2107
+
2108
+ def PerformBuild(data, configurations, params):
2109
+ options = params['options']
2110
+ for config in configurations:
2111
+ builddir = os.path.join(options.toplevel_dir, 'out', config)
2112
+ arguments = ['ninja', '-C', builddir]
2113
+ print 'Building [%s]: %s' % (config, arguments)
2114
+ subprocess.check_call(arguments)
2115
+
2116
+
2117
+ def CallGenerateOutputForConfig(arglist):
2118
+ # Ignore the interrupt signal so that the parent process catches it and
2119
+ # kills all multiprocessing children.
2120
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
2121
+
2122
+ (target_list, target_dicts, data, params, config_name) = arglist
2123
+ GenerateOutputForConfig(target_list, target_dicts, data, params, config_name)
2124
+
2125
+
2126
+ def GenerateOutput(target_list, target_dicts, data, params):
2127
+ # Update target_dicts for iOS device builds.
2128
+ target_dicts = gyp.xcode_emulation.CloneConfigurationForDeviceAndEmulator(
2129
+ target_dicts)
2130
+
2131
+ user_config = params.get('generator_flags', {}).get('config', None)
2132
+ if gyp.common.GetFlavor(params) == 'win':
2133
+ target_list, target_dicts = MSVSUtil.ShardTargets(target_list, target_dicts)
2134
+ target_list, target_dicts = MSVSUtil.InsertLargePdbShims(
2135
+ target_list, target_dicts, generator_default_variables)
2136
+
2137
+ if user_config:
2138
+ GenerateOutputForConfig(target_list, target_dicts, data, params,
2139
+ user_config)
2140
+ else:
2141
+ config_names = target_dicts[target_list[0]]['configurations'].keys()
2142
+ if params['parallel']:
2143
+ try:
2144
+ pool = multiprocessing.Pool(len(config_names))
2145
+ arglists = []
2146
+ for config_name in config_names:
2147
+ arglists.append(
2148
+ (target_list, target_dicts, data, params, config_name))
2149
+ pool.map(CallGenerateOutputForConfig, arglists)
2150
+ except KeyboardInterrupt, e:
2151
+ pool.terminate()
2152
+ raise e
2153
+ else:
2154
+ for config_name in config_names:
2155
+ GenerateOutputForConfig(target_list, target_dicts, data, params,
2156
+ config_name)