Upgrade gyp to r1046
[platform/upstream/nodejs.git] / tools / gyp / pylib / gyp / generator / make.py
1 #!/usr/bin/python
2
3 # Copyright (c) 2011 Google Inc. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6
7 # Notes:
8 #
9 # This is all roughly based on the Makefile system used by the Linux
10 # kernel, but is a non-recursive make -- we put the entire dependency
11 # graph in front of make and let it figure it out.
12 #
13 # The code below generates a separate .mk file for each target, but
14 # all are sourced by the top-level Makefile.  This means that all
15 # variables in .mk-files clobber one another.  Be careful to use :=
16 # where appropriate for immediate evaluation, and similarly to watch
17 # that you're not relying on a variable value to last beween different
18 # .mk files.
19 #
20 # TODOs:
21 #
22 # Global settings and utility functions are currently stuffed in the
23 # toplevel Makefile.  It may make sense to generate some .mk files on
24 # the side to keep the the files readable.
25
26 import gyp
27 import gyp.common
28 import gyp.system_test
29 import os.path
30 import os
31 import sys
32
33 # Debugging-related imports -- remove me once we're solid.
34 import code
35 import pprint
36
37 generator_default_variables = {
38   'EXECUTABLE_PREFIX': '',
39   'EXECUTABLE_SUFFIX': '',
40   'STATIC_LIB_PREFIX': 'lib',
41   'SHARED_LIB_PREFIX': 'lib',
42   'STATIC_LIB_SUFFIX': '.a',
43   'INTERMEDIATE_DIR': '$(obj).$(TOOLSET)/geni',
44   'SHARED_INTERMEDIATE_DIR': '$(obj)/gen',
45   'PRODUCT_DIR': '$(builddir)',
46   'RULE_INPUT_ROOT': '%(INPUT_ROOT)s',  # This gets expanded by Python.
47   'RULE_INPUT_PATH': '$(abspath $<)',
48   'RULE_INPUT_EXT': '$(suffix $<)',
49   'RULE_INPUT_NAME': '$(notdir $<)',
50
51   # This appears unused --- ?
52   'CONFIGURATION_NAME': '$(BUILDTYPE)',
53 }
54
55 # Make supports multiple toolsets
56 generator_supports_multiple_toolsets = True
57
58 # Request sorted dependencies in the order from dependents to dependencies.
59 generator_wants_sorted_dependencies = False
60
61
62 def GetFlavor(params):
63   """Returns |params.flavor| if it's set, the system's default flavor else."""
64   return params.get('flavor', 'mac' if sys.platform == 'darwin' else 'linux')
65
66
67 def CalculateVariables(default_variables, params):
68   """Calculate additional variables for use in the build (called by gyp)."""
69   cc_target = os.environ.get('CC.target', os.environ.get('CC', 'cc'))
70   default_variables['LINKER_SUPPORTS_ICF'] = \
71       gyp.system_test.TestLinkerSupportsICF(cc_command=cc_target)
72
73   if GetFlavor(params) == 'mac':
74     default_variables.setdefault('OS', 'mac')
75     default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib')
76     default_variables.setdefault('SHARED_LIB_DIR',
77                                  generator_default_variables['PRODUCT_DIR'])
78     default_variables.setdefault('LIB_DIR',
79                                  generator_default_variables['PRODUCT_DIR'])
80
81     # Copy additional generator configuration data from Xcode, which is shared
82     # by the Mac Make generator.
83     import gyp.generator.xcode as xcode_generator
84     global generator_additional_non_configuration_keys
85     generator_additional_non_configuration_keys = getattr(xcode_generator,
86         'generator_additional_non_configuration_keys', [])
87     global generator_additional_path_sections
88     generator_additional_path_sections = getattr(xcode_generator,
89         'generator_additional_path_sections', [])
90     global generator_extra_sources_for_rules
91     generator_extra_sources_for_rules = getattr(xcode_generator,
92         'generator_extra_sources_for_rules', [])
93     global COMPILABLE_EXTENSIONS
94     COMPILABLE_EXTENSIONS.update({'.m': 'objc', '.mm' : 'objcxx'})
95   else:
96     default_variables.setdefault('OS', 'linux')
97     default_variables.setdefault('SHARED_LIB_SUFFIX', '.so')
98     default_variables.setdefault('SHARED_LIB_DIR','$(builddir)/lib.$(TOOLSET)')
99     default_variables.setdefault('LIB_DIR', '$(obj).$(TOOLSET)')
100
101
102 def CalculateGeneratorInputInfo(params):
103   """Calculate the generator specific info that gets fed to input (called by
104   gyp)."""
105   generator_flags = params.get('generator_flags', {})
106   android_ndk_version = generator_flags.get('android_ndk_version', None)
107   # Android NDK requires a strict link order.
108   if android_ndk_version:
109     global generator_wants_sorted_dependencies
110     generator_wants_sorted_dependencies = True
111
112
113 def ensure_directory_exists(path):
114   dir = os.path.dirname(path)
115   if dir and not os.path.exists(dir):
116     os.makedirs(dir)
117
118
119 # The .d checking code below uses these functions:
120 # wildcard, sort, foreach, shell, wordlist
121 # wildcard can handle spaces, the rest can't.
122 # Since I could find no way to make foreach work with spaces in filenames
123 # correctly, the .d files have spaces replaced with another character. The .d
124 # file for
125 #     Chromium\ Framework.framework/foo
126 # is for example
127 #     out/Release/.deps/out/Release/Chromium?Framework.framework/foo
128 # This is the replacement character.
129 SPACE_REPLACEMENT = '?'
130
131
132 LINK_COMMANDS_LINUX = """\
133 quiet_cmd_alink = AR($(TOOLSET)) $@
134 cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) $(ARFLAGS.$(TOOLSET)) $@ $(filter %.o,$^)
135
136 # Due to circular dependencies between libraries :(, we wrap the
137 # special "figure out circular dependencies" flags around the entire
138 # input list during linking.
139 quiet_cmd_link = LINK($(TOOLSET)) $@
140 cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ -Wl,--start-group $(LD_INPUTS) -Wl,--end-group $(LIBS)
141
142 # We support two kinds of shared objects (.so):
143 # 1) shared_library, which is just bundling together many dependent libraries
144 # into a link line.
145 # 2) loadable_module, which is generating a module intended for dlopen().
146 #
147 # They differ only slightly:
148 # In the former case, we want to package all dependent code into the .so.
149 # In the latter case, we want to package just the API exposed by the
150 # outermost module.
151 # This means shared_library uses --whole-archive, while loadable_module doesn't.
152 # (Note that --whole-archive is incompatible with the --start-group used in
153 # normal linking.)
154
155 # Other shared-object link notes:
156 # - Set SONAME to the library filename so our binaries don't reference
157 # the local, absolute paths used on the link command-line.
158 quiet_cmd_solink = SOLINK($(TOOLSET)) $@
159 cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS)
160
161 quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
162 cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--start-group $(filter-out FORCE_DO_CMD, $^) -Wl,--end-group $(LIBS)
163 """
164
165 LINK_COMMANDS_MAC = """\
166 quiet_cmd_alink = LIBTOOL-STATIC $@
167 cmd_alink = rm -f $@ && libtool -static -o $@ $(filter %.o,$^)
168
169 quiet_cmd_link = LINK($(TOOLSET)) $@
170 cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS)
171
172 # TODO(thakis): Find out and document the difference between shared_library and
173 # loadable_module on mac.
174 quiet_cmd_solink = SOLINK($(TOOLSET)) $@
175 cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS)
176
177 # TODO(thakis): The solink_module rule is likely wrong. Xcode seems to pass
178 # -bundle -single_module here (for osmesa.so).
179 quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
180 cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS)
181 """
182
183 LINK_COMMANDS_ANDROID = """\
184 quiet_cmd_alink = AR($(TOOLSET)) $@
185 cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) $(ARFLAGS.$(TOOLSET)) $@ $(filter %.o,$^)
186
187 # Due to circular dependencies between libraries :(, we wrap the
188 # special "figure out circular dependencies" flags around the entire
189 # input list during linking.
190 quiet_cmd_link = LINK($(TOOLSET)) $@
191 quiet_cmd_link_host = LINK($(TOOLSET)) $@
192 cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ -Wl,--start-group $(LD_INPUTS) -Wl,--end-group $(LIBS)
193 cmd_link_host = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS)
194
195 # Other shared-object link notes:
196 # - Set SONAME to the library filename so our binaries don't reference
197 # the local, absolute paths used on the link command-line.
198 quiet_cmd_solink = SOLINK($(TOOLSET)) $@
199 cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS)
200
201 quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
202 cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--start-group $(filter-out FORCE_DO_CMD, $^) -Wl,--end-group $(LIBS)
203 quiet_cmd_solink_module_host = SOLINK_MODULE($(TOOLSET)) $@
204 cmd_solink_module_host = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS)
205 """
206
207
208 # Header of toplevel Makefile.
209 # This should go into the build tree, but it's easier to keep it here for now.
210 SHARED_HEADER = ("""\
211 # We borrow heavily from the kernel build setup, though we are simpler since
212 # we don't have Kconfig tweaking settings on us.
213
214 # The implicit make rules have it looking for RCS files, among other things.
215 # We instead explicitly write all the rules we care about.
216 # It's even quicker (saves ~200ms) to pass -r on the command line.
217 MAKEFLAGS=-r
218
219 # The source directory tree.
220 srcdir := %(srcdir)s
221
222 # The name of the builddir.
223 builddir_name ?= %(builddir)s
224
225 # The V=1 flag on command line makes us verbosely print command lines.
226 ifdef V
227   quiet=
228 else
229   quiet=quiet_
230 endif
231
232 # Specify BUILDTYPE=Release on the command line for a release build.
233 BUILDTYPE ?= %(default_configuration)s
234
235 # Directory all our build output goes into.
236 # Note that this must be two directories beneath src/ for unit tests to pass,
237 # as they reach into the src/ directory for data with relative paths.
238 builddir ?= $(builddir_name)/$(BUILDTYPE)
239 abs_builddir := $(abspath $(builddir))
240 depsdir := $(builddir)/.deps
241
242 # Object output directory.
243 obj := $(builddir)/obj
244 abs_obj := $(abspath $(obj))
245
246 # We build up a list of every single one of the targets so we can slurp in the
247 # generated dependency rule Makefiles in one pass.
248 all_deps :=
249
250 # C++ apps need to be linked with g++.
251 #
252 # Note: flock is used to seralize linking. Linking is a memory-intensive
253 # process so running parallel links can often lead to thrashing.  To disable
254 # the serialization, override LINK via an envrionment variable as follows:
255 #
256 #   export LINK=g++
257 #
258 # This will allow make to invoke N linker processes as specified in -jN.
259 LINK ?= %(flock)s $(builddir)/linker.lock $(CXX)
260
261 %(make_global_settings)s
262
263 LINK ?= $(FLOCK) $(CXX)
264 CC.target ?= $(CC)
265 CFLAGS.target ?= $(CFLAGS)
266 CXX.target ?= $(CXX)
267 CXXFLAGS.target ?= $(CXXFLAGS)
268 LINK.target ?= $(LINK)
269 LDFLAGS.target ?= $(LDFLAGS) %(LINK_flags)s
270 AR.target ?= $(AR)
271 ARFLAGS.target ?= %(ARFLAGS.target)s
272
273 # N.B.: the logic of which commands to run should match the computation done
274 # in gyp's make.py where ARFLAGS.host etc. is computed.
275 # TODO(evan): move all cross-compilation logic to gyp-time so we don't need
276 # to replicate this environment fallback in make as well.
277 CC.host ?= gcc
278 CFLAGS.host ?=
279 CXX.host ?= g++
280 CXXFLAGS.host ?=
281 LINK.host ?= g++
282 LDFLAGS.host ?=
283 AR.host ?= ar
284 ARFLAGS.host := %(ARFLAGS.host)s
285
286 # Define a dir function that can handle spaces.
287 # http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions
288 # "leading spaces cannot appear in the text of the first argument as written.
289 # These characters can be put into the argument value by variable substitution."
290 empty :=
291 space := $(empty) $(empty)
292
293 # http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces
294 replace_spaces = $(subst $(space),""" + SPACE_REPLACEMENT + """,$1)
295 unreplace_spaces = $(subst """ + SPACE_REPLACEMENT + """,$(space),$1)
296 dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1)))
297
298 # Flags to make gcc output dependency info.  Note that you need to be
299 # careful here to use the flags that ccache and distcc can understand.
300 # We write to a dep file on the side first and then rename at the end
301 # so we can't end up with a broken dep file.
302 depfile = $(depsdir)/$(call replace_spaces,$@).d
303 DEPFLAGS = -MMD -MF $(depfile).raw
304
305 # We have to fixup the deps output in a few ways.
306 # (1) the file output should mention the proper .o file.
307 # ccache or distcc lose the path to the target, so we convert a rule of
308 # the form:
309 #   foobar.o: DEP1 DEP2
310 # into
311 #   path/to/foobar.o: DEP1 DEP2
312 # (2) we want missing files not to cause us to fail to build.
313 # We want to rewrite
314 #   foobar.o: DEP1 DEP2 \\
315 #               DEP3
316 # to
317 #   DEP1:
318 #   DEP2:
319 #   DEP3:
320 # so if the files are missing, they're just considered phony rules.
321 # We have to do some pretty insane escaping to get those backslashes
322 # and dollar signs past make, the shell, and sed at the same time.
323 # Doesn't work with spaces, but that's fine: .d files have spaces in
324 # their names replaced with other characters."""
325 r"""
326 define fixup_dep
327 # The depfile may not exist if the input file didn't have any #includes.
328 touch $(depfile).raw
329 # Fixup path as in (1).
330 sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile)
331 # Add extra rules as in (2).
332 # We remove slashes and replace spaces with new lines;
333 # remove blank lines;
334 # delete the first line and append a colon to the remaining lines.
335 sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\
336   grep -v '^$$'                             |\
337   sed -e 1d -e 's|$$|:|'                     \
338     >> $(depfile)
339 rm $(depfile).raw
340 endef
341 """
342 """
343 # Command definitions:
344 # - cmd_foo is the actual command to run;
345 # - quiet_cmd_foo is the brief-output summary of the command.
346
347 quiet_cmd_cc = CC($(TOOLSET)) $@
348 cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $<
349
350 quiet_cmd_cxx = CXX($(TOOLSET)) $@
351 cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $<
352 %(mac_commands)s
353 quiet_cmd_touch = TOUCH $@
354 cmd_touch = touch $@
355
356 quiet_cmd_copy = COPY $@
357 # send stderr to /dev/null to ignore messages when linking directories.
358 cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp -af "$<" "$@")
359
360 %(link_commands)s
361 """
362
363 r"""
364 # Define an escape_quotes function to escape single quotes.
365 # This allows us to handle quotes properly as long as we always use
366 # use single quotes and escape_quotes.
367 escape_quotes = $(subst ','\'',$(1))
368 # This comment is here just to include a ' to unconfuse syntax highlighting.
369 # Define an escape_vars function to escape '$' variable syntax.
370 # This allows us to read/write command lines with shell variables (e.g.
371 # $LD_LIBRARY_PATH), without triggering make substitution.
372 escape_vars = $(subst $$,$$$$,$(1))
373 # Helper that expands to a shell command to echo a string exactly as it is in
374 # make. This uses printf instead of echo because printf's behaviour with respect
375 # to escape sequences is more portable than echo's across different shells
376 # (e.g., dash, bash).
377 exact_echo = printf '%%s\n' '$(call escape_quotes,$(1))'
378 """
379 """
380 # Helper to compare the command we're about to run against the command
381 # we logged the last time we ran the command.  Produces an empty
382 # string (false) when the commands match.
383 # Tricky point: Make has no string-equality test function.
384 # The kernel uses the following, but it seems like it would have false
385 # positives, where one string reordered its arguments.
386 #   arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \\
387 #                       $(filter-out $(cmd_$@), $(cmd_$(1))))
388 # We instead substitute each for the empty string into the other, and
389 # say they're equal if both substitutions produce the empty string.
390 # .d files contain """ + SPACE_REPLACEMENT + \
391                    """ instead of spaces, take that into account.
392 command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\\
393                        $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1))))
394
395 # Helper that is non-empty when a prerequisite changes.
396 # Normally make does this implicitly, but we force rules to always run
397 # so we can check their command lines.
398 #   $? -- new prerequisites
399 #   $| -- order-only dependencies
400 prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?))
401
402 # do_cmd: run a command via the above cmd_foo names, if necessary.
403 # Should always run for a given target to handle command-line changes.
404 # Second argument, if non-zero, makes it do asm/C/C++ dependency munging.
405 # Third argument, if non-zero, makes it do POSTBUILDS processing.
406 # Note: We intentionally do NOT call dirx for depfile, since it contains """ + \
407                                                      SPACE_REPLACEMENT + """ for
408 # spaces already and dirx strips the """ + SPACE_REPLACEMENT + \
409                                      """ characters.
410 define do_cmd
411 $(if $(or $(command_changed),$(prereq_changed)),
412   @$(call exact_echo,  $($(quiet)cmd_$(1)))
413   @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))"
414   $(if $(findstring flock,$(word %(flock_index)d,$(cmd_$1))),
415     @$(cmd_$(1))
416     @echo "  $(quiet_cmd_$(1)): Finished",
417     @$(cmd_$(1))
418   )
419   @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile)
420   @$(if $(2),$(fixup_dep))
421   $(if $(and $(3), $(POSTBUILDS)),
422     @for p in $(POSTBUILDS); do eval $$p; done
423   )
424 )
425 endef
426
427 # Declare "all" target first so it is the default, even though we don't have the
428 # deps yet.
429 .PHONY: all
430 all:
431
432 # Use FORCE_DO_CMD to force a target to run.  Should be coupled with
433 # do_cmd.
434 .PHONY: FORCE_DO_CMD
435 FORCE_DO_CMD:
436
437 """)
438
439 SHARED_HEADER_MAC_COMMANDS = """
440 quiet_cmd_objc = CXX($(TOOLSET)) $@
441 cmd_objc = $(CC.$(TOOLSET)) $(GYP_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $<
442
443 quiet_cmd_objcxx = CXX($(TOOLSET)) $@
444 cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $<
445
446 # Commands for precompiled header files.
447 quiet_cmd_pch_c = CXX($(TOOLSET)) $@
448 cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $<
449 quiet_cmd_pch_cc = CXX($(TOOLSET)) $@
450 cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CCFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $<
451 quiet_cmd_pch_m = CXX($(TOOLSET)) $@
452 cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $<
453 quiet_cmd_pch_mm = CXX($(TOOLSET)) $@
454 cmd_pch_mm = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $<
455
456 # gyp-mac-tool is written next to the root Makefile by gyp.
457 # Use $(4) for the command, since $(2) and $(3) are used as flag by do_cmd
458 # already.
459 quiet_cmd_mac_tool = MACTOOL $(4) $<
460 cmd_mac_tool = ./gyp-mac-tool $(4) $< "$@"
461
462 quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@
463 cmd_mac_package_framework = ./gyp-mac-tool package-framework "$@" $(4)
464 """
465
466
467 def WriteRootHeaderSuffixRules(writer):
468   extensions = sorted(COMPILABLE_EXTENSIONS.keys(), key=str.lower)
469
470   writer.write('# Suffix rules, putting all outputs into $(obj).\n')
471   for ext in extensions:
472     writer.write('$(obj).$(TOOLSET)/%%.o: $(srcdir)/%%%s FORCE_DO_CMD\n' % ext)
473     writer.write('\t@$(call do_cmd,%s,1)\n' % COMPILABLE_EXTENSIONS[ext])
474
475   writer.write('\n# Try building from generated source, too.\n')
476   for ext in extensions:
477     writer.write(
478         '$(obj).$(TOOLSET)/%%.o: $(obj).$(TOOLSET)/%%%s FORCE_DO_CMD\n' % ext)
479     writer.write('\t@$(call do_cmd,%s,1)\n' % COMPILABLE_EXTENSIONS[ext])
480   writer.write('\n')
481   for ext in extensions:
482     writer.write('$(obj).$(TOOLSET)/%%.o: $(obj)/%%%s FORCE_DO_CMD\n' % ext)
483     writer.write('\t@$(call do_cmd,%s,1)\n' % COMPILABLE_EXTENSIONS[ext])
484   writer.write('\n')
485
486
487 SHARED_HEADER_SUFFIX_RULES_COMMENT1 = ("""\
488 # Suffix rules, putting all outputs into $(obj).
489 """)
490
491
492 SHARED_HEADER_SUFFIX_RULES_COMMENT2 = ("""\
493 # Try building from generated source, too.
494 """)
495
496
497 SHARED_FOOTER = """\
498 # "all" is a concatenation of the "all" targets from all the included
499 # sub-makefiles. This is just here to clarify.
500 all:
501
502 # Add in dependency-tracking rules.  $(all_deps) is the list of every single
503 # target in our tree. Only consider the ones with .d (dependency) info:
504 d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d))
505 ifneq ($(d_files),)
506   # Rather than include each individual .d file, concatenate them into a
507   # single file which make is able to load faster.  We split this into
508   # commands that take 1000 files at a time to avoid overflowing the
509   # command line.
510   $(shell cat $(wordlist 1,1000,$(d_files)) > $(depsdir)/all.deps)
511 %(generate_all_deps)s
512   # make looks for ways to re-generate included makefiles, but in our case, we
513   # don't have a direct way. Explicitly telling make that it has nothing to do
514   # for them makes it go faster.
515   $(depsdir)/all.deps: ;
516
517   include $(depsdir)/all.deps
518 endif
519 """
520
521 header = """\
522 # This file is generated by gyp; do not edit.
523
524 """
525
526 # Maps every compilable file extension to the do_cmd that compiles it.
527 COMPILABLE_EXTENSIONS = {
528   '.c': 'cc',
529   '.cc': 'cxx',
530   '.cpp': 'cxx',
531   '.cxx': 'cxx',
532   '.s': 'cc',
533   '.S': 'cc',
534 }
535
536 def Compilable(filename):
537   """Return true if the file is compilable (should be in OBJS)."""
538   for res in (filename.endswith(e) for e in COMPILABLE_EXTENSIONS):
539     if res:
540       return True
541   return False
542
543
544 def Linkable(filename):
545   """Return true if the file is linkable (should be on the link line)."""
546   return filename.endswith('.o')
547
548
549 def Target(filename):
550   """Translate a compilable filename to its .o target."""
551   return os.path.splitext(filename)[0] + '.o'
552
553
554 def EscapeShellArgument(s):
555   """Quotes an argument so that it will be interpreted literally by a POSIX
556      shell. Taken from
557      http://stackoverflow.com/questions/35817/whats-the-best-way-to-escape-ossystem-calls-in-python
558      """
559   return "'" + s.replace("'", "'\\''") + "'"
560
561
562 def EscapeMakeVariableExpansion(s):
563   """Make has its own variable expansion syntax using $. We must escape it for
564      string to be interpreted literally."""
565   return s.replace('$', '$$')
566
567
568 def EscapeCppDefine(s):
569   """Escapes a CPP define so that it will reach the compiler unaltered."""
570   s = EscapeShellArgument(s)
571   s = EscapeMakeVariableExpansion(s)
572   return s
573
574
575 def QuoteIfNecessary(string):
576   """TODO: Should this ideally be replaced with one or more of the above
577      functions?"""
578   if '"' in string:
579     string = '"' + string.replace('"', '\\"') + '"'
580   return string
581
582
583 def StringToMakefileVariable(string):
584   """Convert a string to a value that is acceptable as a make variable name."""
585   # TODO: replace other metacharacters that we encounter.
586   return string.replace(' ', '_')
587
588
589 srcdir_prefix = ''
590 def Sourceify(path):
591   """Convert a path to its source directory form."""
592   if '$(' in path:
593     return path
594   if os.path.isabs(path):
595     return path
596   return srcdir_prefix + path
597
598
599 def QuoteSpaces(s):
600   return s.replace(' ', r'\ ')
601
602
603 def ReplaceQuotedSpaces(s):
604   return s.replace(r'\ ', SPACE_REPLACEMENT)
605
606
607 # Map from qualified target to path to output.
608 target_outputs = {}
609 # Map from qualified target to any linkable output.  A subset
610 # of target_outputs.  E.g. when mybinary depends on liba, we want to
611 # include liba in the linker line; when otherbinary depends on
612 # mybinary, we just want to build mybinary first.
613 target_link_deps = {}
614
615
616 class XcodeSettings(object):
617   """A class that understands the gyp 'xcode_settings' object."""
618
619   def __init__(self, spec):
620     self.spec = spec
621
622     # Per-target 'xcode_settings' are pushed down into configs earlier by gyp.
623     # This means self.xcode_settings[config] always contains all settings
624     # for that config -- the per-target settings as well. Settings that are
625     # the same for all configs are implicitly per-target settings.
626     self.xcode_settings = {}
627     configs = spec['configurations']
628     for configname, config in configs.iteritems():
629       self.xcode_settings[configname] = config.get('xcode_settings', {})
630
631     # This is only non-None temporarily during the execution of some methods.
632     self.configname = None
633
634   def _Settings(self):
635     assert self.configname
636     return self.xcode_settings[self.configname]
637
638   def _Test(self, test_key, cond_key, default):
639     return self._Settings().get(test_key, default) == cond_key
640
641   def _Appendf(self, lst, test_key, format_str):
642     if test_key in self._Settings():
643       lst.append(format_str % str(self._Settings()[test_key]))
644
645   def _WarnUnimplemented(self, test_key):
646     if test_key in self._Settings():
647       print 'Warning: Ignoring not yet implemented key "%s".' % test_key
648
649   def _IsBundle(self):
650     return int(self.spec.get('mac_bundle', 0)) != 0
651
652   def GetFrameworkVersion(self):
653     """Returns the framework version of the current target. Only valid for
654     bundles."""
655     assert self._IsBundle()
656     return self.GetPerTargetSetting('FRAMEWORK_VERSION', default='A')
657
658   def GetWrapperExtension(self):
659     """Returns the bundle extension (.app, .framework, .plugin, etc).  Only
660     valid for bundles."""
661     assert self._IsBundle()
662     if self.spec['type'] in ('loadable_module', 'shared_library'):
663       wrapper_extension = self.GetPerTargetSetting(
664           'WRAPPER_EXTENSION', default='framework')
665       return '.' + self.spec.get('product_extension', wrapper_extension)
666     elif self.spec['type'] == 'executable':
667       return '.app'
668     else:
669       assert False, "Don't know extension for '%s', target '%s'" % (
670           self.spec['type'], self.spec['target_name'])
671
672   def GetProductName(self):
673     """Returns PRODUCT_NAME."""
674     return self.spec.get('product_name', self.spec['target_name'])
675
676   def GetWrapperName(self):
677     """Returns the directory name of the bundle represented by this target.
678     Only valid for bundles."""
679     assert self._IsBundle()
680     return self.GetProductName() + self.GetWrapperExtension()
681
682   def GetBundleContentsFolderPath(self):
683     """Returns the qualified path to the bundle's contents folder. E.g.
684     Chromium.app/Contents or Foo.bundle/Versions/A. Only valid for bundles."""
685     assert self._IsBundle()
686     if self.spec['type'] == 'shared_library':
687       return os.path.join(
688           self.GetWrapperName(), 'Versions', self.GetFrameworkVersion())
689     else:
690       # loadable_modules have a 'Contents' folder like executables.
691       return os.path.join(self.GetWrapperName(), 'Contents')
692
693   def GetBundleResourceFolder(self):
694     """Returns the qualified path to the bundle's resource folder. E.g.
695     Chromium.app/Contents/Resources. Only valid for bundles."""
696     assert self._IsBundle()
697     return os.path.join(self.GetBundleContentsFolderPath(), 'Resources')
698
699   def GetBundlePlistPath(self):
700     """Returns the qualified path to the bundle's plist file. E.g.
701     Chromium.app/Contents/Info.plist. Only valid for bundles."""
702     assert self._IsBundle()
703     assert self.spec['type'] != 'loadable_modules', (
704         "Info.plist files for loadable_modules not yet supported by the "
705         "make generator (target %s)" % self.spec['target_name'])  # Not tested.
706     if self.spec['type'] == 'executable':
707       return os.path.join(self.GetBundleContentsFolderPath(), 'Info.plist')
708     else:
709       return os.path.join(self.GetBundleContentsFolderPath(),
710                           'Resources', 'Info.plist')
711
712   def _GetBundleBinaryPath(self):
713     """Returns the name of the bundle binary of by this target.
714     E.g. Chromium.app/Contents/MacOS/Chromium. Only valid for bundles."""
715     assert self._IsBundle()
716     if self.spec['type'] in ('loadable_module', 'shared_library'):
717       path = self.GetBundleContentsFolderPath()
718     elif self.spec['type'] == 'executable':
719       path = os.path.join(self.GetBundleContentsFolderPath(), 'MacOS')
720     return os.path.join(path, self.spec.get('product_name',
721                                             self.spec['target_name']))
722
723   def _GetStandaloneExecutableSuffix(self):
724     if 'product_extension' in self.spec:
725       return '.' + self.spec['product_extension']
726     return {
727       'executable': '',
728       'static_library': '.a',
729       'shared_library': '.dylib',
730       'loadable_module': '.so',
731     }[self.spec['type']]
732
733   def _GetStandaloneExecutablePrefix(self):
734     return self.spec.get('product_prefix', {
735       'executable': '',
736       'static_library': 'lib',
737       'shared_library': 'lib',
738       # Non-bundled loadable_modules are called foo.so for some reason
739       # (that is, .so and no prefix) with the xcode build -- match that.
740       'loadable_module': '',
741     }[self.spec['type']])
742
743   def _GetStandaloneBinaryPath(self):
744     """Returns the name of the non-bundle binary represented by this target.
745     E.g. hello_world. Only valid for non-bundles."""
746     assert not self._IsBundle()
747     assert self.spec['type'] in (
748         'executable', 'shared_library', 'static_library', 'loadable_module')
749     target = self.spec['target_name']
750     if self.spec['type'] == 'static_library':
751       if target[:3] == 'lib':
752         target = target[3:]
753     elif self.spec['type'] in ('loadable_module', 'shared_library'):
754       if target[:3] == 'lib':
755         target = target[3:]
756
757     target_prefix = self._GetStandaloneExecutablePrefix()
758     target = self.spec.get('product_name', target)
759     target_ext = self._GetStandaloneExecutableSuffix()
760     return target_prefix + target + target_ext
761
762   def GetExecutablePath(self):
763     """Returns the directory name of the bundle represented by this target. E.g.
764     Chromium.app/Contents/MacOS/Chromium."""
765     if self._IsBundle():
766       return self._GetBundleBinaryPath()
767     else:
768       return self._GetStandaloneBinaryPath()
769
770   def _SdkPath(self):
771     sdk_root = 'macosx10.5'
772     if 'SDKROOT' in self._Settings():
773       sdk_root = self._Settings()['SDKROOT']
774     if sdk_root.startswith('macosx'):
775       sdk_root = 'MacOSX' + sdk_root[len('macosx'):]
776     return '/Developer/SDKs/%s.sdk' % sdk_root
777
778   def GetCflags(self, configname):
779     """Returns flags that need to be added to .c, .cc, .m, and .mm
780     compilations."""
781     # This functions (and the similar ones below) do not offer complete
782     # emulation of all xcode_settings keys. They're implemented on demand.
783
784     self.configname = configname
785     cflags = []
786
787     sdk_root = self._SdkPath()
788     if 'SDKROOT' in self._Settings():
789       cflags.append('-isysroot %s' % sdk_root)
790
791     if self._Test('GCC_CW_ASM_SYNTAX', 'YES', default='YES'):
792       cflags.append('-fasm-blocks')
793
794     if 'GCC_DYNAMIC_NO_PIC' in self._Settings():
795       if self._Settings()['GCC_DYNAMIC_NO_PIC'] == 'YES':
796         cflags.append('-mdynamic-no-pic')
797     else:
798       pass
799       # TODO: In this case, it depends on the target. xcode passes
800       # mdynamic-no-pic by default for executable and possibly static lib
801       # according to mento
802
803     if self._Test('GCC_ENABLE_PASCAL_STRINGS', 'YES', default='YES'):
804       cflags.append('-mpascal-strings')
805
806     self._Appendf(cflags, 'GCC_OPTIMIZATION_LEVEL', '-O%s')
807
808     if self._Test('GCC_GENERATE_DEBUGGING_SYMBOLS', 'YES', default='YES'):
809       dbg_format = self._Settings().get('DEBUG_INFORMATION_FORMAT', 'dwarf')
810       if dbg_format == 'dwarf':
811         cflags.append('-gdwarf-2')
812       elif dbg_format == 'stabs':
813         raise NotImplementedError('stabs debug format is not supported yet.')
814       elif dbg_format == 'dwarf-with-dsym':
815         # TODO(thakis): this is needed for mac_breakpad chromium builds, but not
816         # for regular chromium builds.
817         # -gdwarf-2 as well, but needs to invoke dsymutil after linking too:
818         #   dsymutil build/Default/TestAppGyp.app/Contents/MacOS/TestAppGyp \
819         #       -o build/Default/TestAppGyp.app.dSYM
820         raise NotImplementedError('dsym debug format is not supported yet.')
821       else:
822         raise NotImplementedError('Unknown debug format %s' % dbg_format)
823
824     if self._Test('GCC_SYMBOLS_PRIVATE_EXTERN', 'YES', default='NO'):
825       cflags.append('-fvisibility=hidden')
826
827     if self._Test('GCC_TREAT_WARNINGS_AS_ERRORS', 'YES', default='NO'):
828       cflags.append('-Werror')
829
830     if self._Test('GCC_WARN_ABOUT_MISSING_NEWLINE', 'YES', default='NO'):
831       cflags.append('-Wnewline-eof')
832
833     self._Appendf(cflags, 'MACOSX_DEPLOYMENT_TARGET', '-mmacosx-version-min=%s')
834
835     # TODO:
836     self._WarnUnimplemented('ARCHS')
837     self._WarnUnimplemented('COPY_PHASE_STRIP')
838     self._WarnUnimplemented('DEPLOYMENT_POSTPROCESSING')
839     self._WarnUnimplemented('GCC_DEBUGGING_SYMBOLS')
840     self._WarnUnimplemented('GCC_ENABLE_OBJC_EXCEPTIONS')
841     self._WarnUnimplemented('GCC_ENABLE_OBJC_GC')
842     self._WarnUnimplemented('INFOPLIST_PREPROCESS')
843     self._WarnUnimplemented('INFOPLIST_PREPROCESSOR_DEFINITIONS')
844     self._WarnUnimplemented('STRIPFLAGS')
845     self._WarnUnimplemented('STRIP_INSTALLED_PRODUCT')
846
847     # TODO: Do not hardcode arch. Supporting fat binaries will be annoying.
848     cflags.append('-arch i386')
849
850     cflags += self._Settings().get('OTHER_CFLAGS', [])
851     cflags += self._Settings().get('WARNING_CFLAGS', [])
852
853     config = self.spec['configurations'][self.configname]
854     framework_dirs = config.get('mac_framework_dirs', [])
855     for directory in framework_dirs:
856       cflags.append('-F ' + os.path.join(sdk_root, directory))
857
858     self.configname = None
859     return cflags
860
861   def GetCflagsC(self, configname):
862     """Returns flags that need to be added to .c, and .m compilations."""
863     self.configname = configname
864     cflags_c = []
865     self._Appendf(cflags_c, 'GCC_C_LANGUAGE_STANDARD', '-std=%s')
866     self.configname = None
867     return cflags_c
868
869   def GetCflagsCC(self, configname):
870     """Returns flags that need to be added to .cc, and .mm compilations."""
871     self.configname = configname
872     cflags_cc = []
873     if self._Test('GCC_ENABLE_CPP_RTTI', 'NO', default='YES'):
874       cflags_cc.append('-fno-rtti')
875     if self._Test('GCC_ENABLE_CPP_EXCEPTIONS', 'NO', default='YES'):
876       cflags_cc.append('-fno-exceptions')
877     if self._Test('GCC_INLINES_ARE_PRIVATE_EXTERN', 'YES', default='NO'):
878       cflags_cc.append('-fvisibility-inlines-hidden')
879     if self._Test('GCC_THREADSAFE_STATICS', 'NO', default='YES'):
880       cflags_cc.append('-fno-threadsafe-statics')
881     self.configname = None
882     return cflags_cc
883
884   def GetCflagsObjC(self, configname):
885     """Returns flags that need to be added to .m compilations."""
886     self.configname = configname
887     self.configname = None
888     return []
889
890   def GetCflagsObjCC(self, configname):
891     """Returns flags that need to be added to .mm compilations."""
892     self.configname = configname
893     cflags_objcc = []
894     if self._Test('GCC_OBJC_CALL_CXX_CDTORS', 'YES', default='NO'):
895       cflags_objcc.append('-fobjc-call-cxx-cdtors')
896     self.configname = None
897     return cflags_objcc
898
899   def GetLdflags(self, target, configname):
900     """Returns flags that need to be passed to the linker."""
901     self.configname = configname
902     ldflags = []
903
904     # The xcode build is relative to a gyp file's directory, and OTHER_LDFLAGS
905     # contains two entries that depend on this. Explicitly absolutify for these
906     # two cases.
907     def AbsolutifyPrefix(flag, prefix):
908       if flag.startswith(prefix):
909         flag = prefix + target.Absolutify(flag[len(prefix):])
910       return flag
911     for ldflag in self._Settings().get('OTHER_LDFLAGS', []):
912       # Required for ffmpeg (no idea why they don't use LIBRARY_SEARCH_PATHS,
913       # TODO(thakis): Update ffmpeg.gyp):
914       ldflag = AbsolutifyPrefix(ldflag, '-L')
915       # Required for the nacl plugin:
916       ldflag = AbsolutifyPrefix(ldflag, '-Wl,-exported_symbols_list ')
917       ldflags.append(ldflag)
918
919     if self._Test('DEAD_CODE_STRIPPING', 'YES', default='NO'):
920       ldflags.append('-Wl,-dead_strip')
921
922     if self._Test('PREBINDING', 'YES', default='NO'):
923       ldflags.append('-Wl,-prebind')
924
925     self._Appendf(
926         ldflags, 'DYLIB_COMPATIBILITY_VERSION', '-compatibility_version %s')
927     self._Appendf(
928         ldflags, 'DYLIB_CURRENT_VERSION', '-current_version %s')
929     self._Appendf(
930         ldflags, 'MACOSX_DEPLOYMENT_TARGET', '-mmacosx-version-min=%s')
931     if 'SDKROOT' in self._Settings():
932       ldflags.append('-isysroot ' + self._SdkPath())
933
934     for library_path in self._Settings().get('LIBRARY_SEARCH_PATHS', []):
935       ldflags.append('-L' + library_path)
936
937     if 'ORDER_FILE' in self._Settings():
938       ldflags.append('-Wl,-order_file ' +
939                      '-Wl,' + target.Absolutify(self._Settings()['ORDER_FILE']))
940
941     # TODO: Do not hardcode arch. Supporting fat binaries will be annoying.
942     ldflags.append('-arch i386')
943
944     # Xcode adds the product directory by default.
945     ldflags.append('-L' + generator_default_variables['PRODUCT_DIR'])
946
947     install_name = self.GetPerTargetSetting('LD_DYLIB_INSTALL_NAME')
948     install_base = self.GetPerTargetSetting('DYLIB_INSTALL_NAME_BASE')
949     default_install_name = \
950           '$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)'
951     if not install_name and install_base:
952       install_name = default_install_name
953
954     if install_name:
955       # Hardcode support for the variables used in chromium for now, to unblock
956       # people using the make build.
957       if '$' in install_name:
958         assert install_name in ('$(DYLIB_INSTALL_NAME_BASE:standardizepath)/'
959             '$(WRAPPER_NAME)/$(PRODUCT_NAME)', default_install_name), (
960             'Variables in LD_DYLIB_INSTALL_NAME are not generally supported yet'
961             ' in target \'%s\' (got \'%s\')' %
962                 (self.spec['target_name'], install_name))
963         # I'm not quite sure what :standardizepath does. Just call normpath(),
964         # but don't let @executable_path/../foo collapse to foo.
965         if '/' in install_base:
966           prefix, rest = '', install_base
967           if install_base.startswith('@'):
968             prefix, rest = install_base.split('/', 1)
969           rest = os.path.normpath(rest)  # :standardizepath
970           install_base = os.path.join(prefix, rest)
971
972         install_name = install_name.replace(
973             '$(DYLIB_INSTALL_NAME_BASE:standardizepath)', install_base)
974         if self._IsBundle():
975           # These are only valid for bundles, hence the |if|.
976           install_name = install_name.replace(
977               '$(WRAPPER_NAME)', self.GetWrapperName())
978           install_name = install_name.replace(
979               '$(PRODUCT_NAME)', self.GetProductName())
980         else:
981           assert '$(WRAPPER_NAME)' not in install_name
982           assert '$(PRODUCT_NAME)' not in install_name
983
984         install_name = install_name.replace(
985             '$(EXECUTABLE_PATH)', self.GetExecutablePath())
986
987       install_name = QuoteSpaces(install_name)
988       ldflags.append('-install_name ' + install_name)
989
990     self.configname = None
991     return ldflags
992
993   def GetPerTargetSettings(self):
994     """Gets a list of all the per-target settings. This will only fetch keys
995     whose values are the same across all configurations."""
996     first_pass = True
997     result = {}
998     for configname in sorted(self.xcode_settings.keys()):
999       if first_pass:
1000         result = dict(self.xcode_settings[configname])
1001         first_pass = False
1002       else:
1003         for key, value in self.xcode_settings[configname].iteritems():
1004           if key not in result:
1005             continue
1006           elif result[key] != value:
1007             del result[key]
1008     return result
1009
1010   def GetPerTargetSetting(self, setting, default=None):
1011     """Tries to get xcode_settings.setting from spec. Assumes that the setting
1012        has the same value in all configurations and throws otherwise."""
1013     first_pass = True
1014     result = None
1015     for configname in sorted(self.xcode_settings.keys()):
1016       if first_pass:
1017         result = self.xcode_settings[configname].get(setting, None)
1018         first_pass = False
1019       else:
1020         assert result == self.xcode_settings[configname].get(setting, None), (
1021             "Expected per-target setting for '%s', got per-config setting "
1022             "(target %s)" % (setting, spec['target_name']))
1023     if result is None:
1024       return default
1025     return result
1026
1027
1028 class MacPrefixHeader(object):
1029   """A class that helps with emulating Xcode's GCC_PREFIX_HEADER feature. If
1030   GCC_PREFIX_HEADER isn't present (in most gyp targets on mac, and always on
1031   non-mac systems), all methods of this class are no-ops."""
1032
1033   def __init__(self, path_provider):
1034     # This doesn't support per-configuration prefix headers. Good enough
1035     # for now.
1036     self.header = None
1037     self.compile_headers = False
1038     if path_provider.flavor == 'mac':
1039       self.header = path_provider.xcode_settings.GetPerTargetSetting(
1040           'GCC_PREFIX_HEADER')
1041       self.compile_headers = path_provider.xcode_settings.GetPerTargetSetting(
1042           'GCC_PRECOMPILE_PREFIX_HEADER', default='NO') != 'NO'
1043     self.compiled_headers = {}
1044     if self.header:
1045       self.header = path_provider.Absolutify(self.header)
1046       if self.compile_headers:
1047         for lang in ['c', 'cc', 'm', 'mm']:
1048           self.compiled_headers[lang] = path_provider.Pchify(self.header, lang)
1049
1050   def _Gch(self, lang):
1051     """Returns the actual file name of the prefix header for language |lang|."""
1052     assert self.compile_headers
1053     return self.compiled_headers[lang] + '.gch'
1054
1055   def WriteObjDependencies(self, compilable, objs, writer):
1056     """Writes dependencies from the object files in |objs| to the corresponding
1057     precompiled header file. |compilable[i]| has to be the source file belonging
1058     to |objs[i]|."""
1059     if not self.header or not self.compile_headers:
1060       return
1061
1062     writer.WriteLn('# Dependencies from obj files to their precompiled headers')
1063     for source, obj in zip(compilable, objs):
1064       ext = os.path.splitext(source)[1]
1065       lang = {
1066         '.c': 'c',
1067         '.cpp': 'cc', '.cc': 'cc', '.cxx': 'cc',
1068         '.m': 'm',
1069         '.mm': 'mm',
1070       }.get(ext, None)
1071       if lang:
1072         writer.WriteLn('%s: %s' % (obj, self._Gch(lang)))
1073     writer.WriteLn('# End precompiled header dependencies')
1074
1075   def GetInclude(self, lang):
1076     """Gets the cflags to include the prefix header for language |lang|."""
1077     if self.compile_headers and lang in self.compiled_headers:
1078       return '-include %s ' % self.compiled_headers[lang]
1079     elif self.header:
1080       return '-include %s ' % self.header
1081     else:
1082       return ''
1083
1084   def WritePchTargets(self, writer):
1085     """Writes make rules to compile the prefix headers."""
1086     if not self.header or not self.compile_headers:
1087       return
1088
1089     writer.WriteLn(self._Gch('c') + ": GYP_PCH_CFLAGS := "
1090                  "-x c-header "
1091                  "$(DEFS_$(BUILDTYPE)) "
1092                  "$(INCS_$(BUILDTYPE)) "
1093                  "$(CFLAGS_$(BUILDTYPE)) "
1094                  "$(CFLAGS_C_$(BUILDTYPE))")
1095
1096     writer.WriteLn(self._Gch('cc') + ": GYP_PCH_CCFLAGS := "
1097                  "-x c++-header "
1098                  "$(DEFS_$(BUILDTYPE)) "
1099                  "$(INCS_$(BUILDTYPE)) "
1100                  "$(CFLAGS_$(BUILDTYPE)) "
1101                  "$(CFLAGS_CC_$(BUILDTYPE))")
1102
1103     writer.WriteLn(self._Gch('m') + ": GYP_PCH_OBJCFLAGS := "
1104                  "-x objective-c-header "
1105                  "$(DEFS_$(BUILDTYPE)) "
1106                  "$(INCS_$(BUILDTYPE)) "
1107                  "$(CFLAGS_$(BUILDTYPE)) "
1108                  "$(CFLAGS_C_$(BUILDTYPE)) "
1109                  "$(CFLAGS_OBJC_$(BUILDTYPE))")
1110
1111     writer.WriteLn(self._Gch('mm') + ": GYP_PCH_OBJCXXFLAGS := "
1112                  "-x objective-c++-header "
1113                  "$(DEFS_$(BUILDTYPE)) "
1114                  "$(INCS_$(BUILDTYPE)) "
1115                  "$(CFLAGS_$(BUILDTYPE)) "
1116                  "$(CFLAGS_CC_$(BUILDTYPE)) "
1117                  "$(CFLAGS_OBJCC_$(BUILDTYPE))")
1118
1119     for lang in self.compiled_headers:
1120       writer.WriteLn('%s: %s FORCE_DO_CMD' % (self._Gch(lang), self.header))
1121       writer.WriteLn('\t@$(call do_cmd,pch_%s,1)' % lang)
1122       writer.WriteLn('')
1123       assert ' ' not in self._Gch(lang), (
1124           "Spaces in gch filenames not supported (%s)"  % self._Gch(lang))
1125       writer.WriteLn('all_deps += %s' % self._Gch(lang))
1126       writer.WriteLn('')
1127
1128
1129 class MakefileWriter:
1130   """MakefileWriter packages up the writing of one target-specific foobar.mk.
1131
1132   Its only real entry point is Write(), and is mostly used for namespacing.
1133   """
1134
1135   def __init__(self, generator_flags, flavor):
1136     self.generator_flags = generator_flags
1137     self.flavor = flavor
1138     # Keep track of the total number of outputs for this makefile.
1139     self._num_outputs = 0
1140
1141     self.suffix_rules_srcdir = {}
1142     self.suffix_rules_objdir1 = {}
1143     self.suffix_rules_objdir2 = {}
1144
1145     # Generate suffix rules for all compilable extensions.
1146     for ext in COMPILABLE_EXTENSIONS.keys():
1147       # Suffix rules for source folder.
1148       self.suffix_rules_srcdir.update({ext: ("""\
1149 $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(srcdir)/%%%s FORCE_DO_CMD
1150         @$(call do_cmd,%s,1)
1151 """ % (ext, COMPILABLE_EXTENSIONS[ext]))})
1152
1153       # Suffix rules for generated source files.
1154       self.suffix_rules_objdir1.update({ext: ("""\
1155 $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj).$(TOOLSET)/%%%s FORCE_DO_CMD
1156         @$(call do_cmd,%s,1)
1157 """ % (ext, COMPILABLE_EXTENSIONS[ext]))})
1158       self.suffix_rules_objdir2.update({ext: ("""\
1159 $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
1160         @$(call do_cmd,%s,1)
1161 """ % (ext, COMPILABLE_EXTENSIONS[ext]))})
1162
1163
1164   def NumOutputs(self):
1165     return self._num_outputs
1166
1167
1168   def Write(self, qualified_target, base_path, output_filename, spec, configs,
1169             part_of_all):
1170     """The main entry point: writes a .mk file for a single target.
1171
1172     Arguments:
1173       qualified_target: target we're generating
1174       base_path: path relative to source root we're building in, used to resolve
1175                  target-relative paths
1176       output_filename: output .mk file name to write
1177       spec, configs: gyp info
1178       part_of_all: flag indicating this target is part of 'all'
1179     """
1180     ensure_directory_exists(output_filename)
1181
1182     self.fp = open(output_filename, 'w')
1183
1184     self.fp.write(header)
1185
1186     self.path = base_path
1187     self.target = spec['target_name']
1188     self.type = spec['type']
1189     self.toolset = spec['toolset']
1190
1191     # Bundles are directories with a certain subdirectory structure, instead of
1192     # just a single file. Bundle rules do not produce a binary but also package
1193     # resources into that directory.
1194     self.is_mac_bundle = (int(spec.get('mac_bundle', 0)) != 0 and
1195         self.flavor == 'mac')
1196     if self.is_mac_bundle:
1197       assert self.type != 'none', (
1198           'mac_bundle targets cannot have type none (target "%s")' %
1199           self.target)
1200
1201     if self.flavor == 'mac':
1202       self.xcode_settings = XcodeSettings(spec)
1203
1204     deps, link_deps = self.ComputeDeps(spec)
1205
1206     # Some of the generation below can add extra output, sources, or
1207     # link dependencies.  All of the out params of the functions that
1208     # follow use names like extra_foo.
1209     extra_outputs = []
1210     extra_sources = []
1211     extra_link_deps = []
1212     extra_mac_bundle_resources = []
1213     mac_bundle_deps = []
1214
1215     if self.is_mac_bundle:
1216       self.output = self.ComputeMacBundleOutput(spec)
1217       self.output_binary = self.ComputeMacBundleBinaryOutput(spec)
1218     else:
1219       self.output = self.output_binary = self.ComputeOutput(spec)
1220
1221     self.output = QuoteSpaces(self.output)
1222     self.output_binary = QuoteSpaces(self.output_binary)
1223
1224     self._INSTALLABLE_TARGETS = ('executable', 'loadable_module',
1225                                  'shared_library')
1226     if self.type in self._INSTALLABLE_TARGETS:
1227       self.alias = os.path.basename(self.output)
1228       install_path = self._InstallableTargetInstallPath()
1229     else:
1230       self.alias = self.output
1231       install_path = self.output
1232
1233     self.WriteLn("TOOLSET := " + self.toolset)
1234     self.WriteLn("TARGET := " + self.target)
1235
1236     # Actions must come first, since they can generate more OBJs for use below.
1237     if 'actions' in spec:
1238       self.WriteActions(spec['actions'], extra_sources, extra_outputs,
1239                         extra_mac_bundle_resources, part_of_all, spec)
1240
1241     # Rules must be early like actions.
1242     if 'rules' in spec:
1243       self.WriteRules(spec['rules'], extra_sources, extra_outputs,
1244                       extra_mac_bundle_resources, part_of_all)
1245
1246     if 'copies' in spec:
1247       self.WriteCopies(spec['copies'], extra_outputs, part_of_all, spec)
1248
1249     # Bundle resources.
1250     if self.is_mac_bundle:
1251       all_mac_bundle_resources = (
1252           spec.get('mac_bundle_resources', []) + extra_mac_bundle_resources)
1253       if all_mac_bundle_resources:
1254         self.WriteMacBundleResources(
1255             all_mac_bundle_resources, mac_bundle_deps, spec)
1256       info_plist = self.xcode_settings.GetPerTargetSetting('INFOPLIST_FILE')
1257       if info_plist:
1258         self.WriteMacInfoPlist(info_plist, mac_bundle_deps, spec)
1259
1260     # Sources.
1261     all_sources = spec.get('sources', []) + extra_sources
1262     if all_sources:
1263       self.WriteSources(
1264           configs, deps, all_sources, extra_outputs,
1265           extra_link_deps, part_of_all, MacPrefixHeader(self))
1266       sources = filter(Compilable, all_sources)
1267       if sources:
1268         self.WriteLn(SHARED_HEADER_SUFFIX_RULES_COMMENT1)
1269         extensions = set([os.path.splitext(s)[1] for s in sources])
1270         for ext in extensions:
1271           if ext in self.suffix_rules_srcdir:
1272             self.WriteLn(self.suffix_rules_srcdir[ext])
1273         self.WriteLn(SHARED_HEADER_SUFFIX_RULES_COMMENT2)
1274         for ext in extensions:
1275           if ext in self.suffix_rules_objdir1:
1276             self.WriteLn(self.suffix_rules_objdir1[ext])
1277         for ext in extensions:
1278           if ext in self.suffix_rules_objdir2:
1279             self.WriteLn(self.suffix_rules_objdir2[ext])
1280         self.WriteLn('# End of this set of suffix rules')
1281
1282         # Add dependency from bundle to bundle binary.
1283         if self.is_mac_bundle:
1284           mac_bundle_deps.append(self.output_binary)
1285
1286     self.WriteTarget(spec, configs, deps, extra_link_deps + link_deps,
1287                      mac_bundle_deps, extra_outputs, part_of_all)
1288
1289     # Update global list of target outputs, used in dependency tracking.
1290     target_outputs[qualified_target] = install_path
1291
1292     # Update global list of link dependencies.
1293     if self.type in ('static_library', 'shared_library'):
1294       target_link_deps[qualified_target] = self.output_binary
1295
1296     # Currently any versions have the same effect, but in future the behavior
1297     # could be different.
1298     if self.generator_flags.get('android_ndk_version', None):
1299       self.WriteAndroidNdkModuleRule(self.target, all_sources, link_deps)
1300
1301     self.fp.close()
1302
1303
1304   def WriteSubMake(self, output_filename, makefile_path, targets, build_dir):
1305     """Write a "sub-project" Makefile.
1306
1307     This is a small, wrapper Makefile that calls the top-level Makefile to build
1308     the targets from a single gyp file (i.e. a sub-project).
1309
1310     Arguments:
1311       output_filename: sub-project Makefile name to write
1312       makefile_path: path to the top-level Makefile
1313       targets: list of "all" targets for this sub-project
1314       build_dir: build output directory, relative to the sub-project
1315     """
1316     ensure_directory_exists(output_filename)
1317     self.fp = open(output_filename, 'w')
1318     self.fp.write(header)
1319     # For consistency with other builders, put sub-project build output in the
1320     # sub-project dir (see test/subdirectory/gyptest-subdir-all.py).
1321     self.WriteLn('export builddir_name ?= %s' %
1322                  os.path.join(os.path.dirname(output_filename), build_dir))
1323     self.WriteLn('.PHONY: all')
1324     self.WriteLn('all:')
1325     if makefile_path:
1326       makefile_path = ' -C ' + makefile_path
1327     self.WriteLn('\t$(MAKE)%s %s' % (makefile_path, ' '.join(targets)))
1328     self.fp.close()
1329
1330
1331   def WriteActions(self, actions, extra_sources, extra_outputs,
1332                    extra_mac_bundle_resources, part_of_all, spec):
1333     """Write Makefile code for any 'actions' from the gyp input.
1334
1335     extra_sources: a list that will be filled in with newly generated source
1336                    files, if any
1337     extra_outputs: a list that will be filled in with any outputs of these
1338                    actions (used to make other pieces dependent on these
1339                    actions)
1340     part_of_all: flag indicating this target is part of 'all'
1341     """
1342     for action in actions:
1343       name = self.target + '_' + StringToMakefileVariable(action['action_name'])
1344       self.WriteLn('### Rules for action "%s":' % action['action_name'])
1345       inputs = action['inputs']
1346       outputs = action['outputs']
1347
1348       # Build up a list of outputs.
1349       # Collect the output dirs we'll need.
1350       dirs = set()
1351       for out in outputs:
1352         dir = os.path.split(out)[0]
1353         if dir:
1354           dirs.add(dir)
1355       if int(action.get('process_outputs_as_sources', False)):
1356         extra_sources += outputs
1357       if int(action.get('process_outputs_as_mac_bundle_resources', False)):
1358         extra_mac_bundle_resources += outputs
1359
1360       # Write the actual command.
1361       command = gyp.common.EncodePOSIXShellList(action['action'])
1362       if 'message' in action:
1363         self.WriteLn('quiet_cmd_%s = ACTION %s $@' % (name, action['message']))
1364       else:
1365         self.WriteLn('quiet_cmd_%s = ACTION %s $@' % (name, name))
1366       if len(dirs) > 0:
1367         command = 'mkdir -p %s' % ' '.join(dirs) + '; ' + command
1368
1369       cd_action = 'cd %s; ' % Sourceify(self.path or '.')
1370
1371       # Set LD_LIBRARY_PATH in case the action runs an executable from this
1372       # build which links to shared libs from this build.
1373       # actions run on the host, so they should in theory only use host
1374       # libraries, but until everything is made cross-compile safe, also use
1375       # target libraries.
1376       # TODO(piman): when everything is cross-compile safe, remove lib.target
1377       self.WriteLn('cmd_%s = export LD_LIBRARY_PATH=$(builddir)/lib.host:'
1378                    '$(builddir)/lib.target:$$LD_LIBRARY_PATH; %s%s'
1379                    % (name, cd_action, command))
1380       self.WriteLn()
1381       outputs = map(self.Absolutify, outputs)
1382       # The makefile rules are all relative to the top dir, but the gyp actions
1383       # are defined relative to their containing dir.  This replaces the obj
1384       # variable for the action rule with an absolute version so that the output
1385       # goes in the right place.
1386       # Only write the 'obj' and 'builddir' rules for the "primary" output (:1);
1387       # it's superfluous for the "extra outputs", and this avoids accidentally
1388       # writing duplicate dummy rules for those outputs.
1389       # Same for environment.
1390       self.WriteMakeRule(outputs[:1], ['obj := $(abs_obj)'])
1391       # Needs to be before builddir is redefined in the next line!
1392       self.WriteXcodeEnv(outputs[0], spec, target_relative_path=True)
1393       self.WriteMakeRule(outputs[:1], ['builddir := $(abs_builddir)'])
1394
1395       for input in inputs:
1396         assert ' ' not in input, (
1397             "Spaces in action input filenames not supported (%s)"  % input)
1398       for output in outputs:
1399         assert ' ' not in output, (
1400             "Spaces in action output filenames not supported (%s)"  % output)
1401
1402       self.WriteDoCmd(outputs, map(Sourceify, map(self.Absolutify, inputs)),
1403                       part_of_all=part_of_all, command=name)
1404
1405       # Stuff the outputs in a variable so we can refer to them later.
1406       outputs_variable = 'action_%s_outputs' % name
1407       self.WriteLn('%s := %s' % (outputs_variable, ' '.join(outputs)))
1408       extra_outputs.append('$(%s)' % outputs_variable)
1409       self.WriteLn()
1410
1411     self.WriteLn()
1412
1413
1414   def WriteRules(self, rules, extra_sources, extra_outputs,
1415                  extra_mac_bundle_resources, part_of_all):
1416     """Write Makefile code for any 'rules' from the gyp input.
1417
1418     extra_sources: a list that will be filled in with newly generated source
1419                    files, if any
1420     extra_outputs: a list that will be filled in with any outputs of these
1421                    rules (used to make other pieces dependent on these rules)
1422     part_of_all: flag indicating this target is part of 'all'
1423     """
1424     for rule in rules:
1425       name = self.target + '_' + StringToMakefileVariable(rule['rule_name'])
1426       count = 0
1427       self.WriteLn('### Generated for rule %s:' % name)
1428
1429       all_outputs = []
1430
1431       for rule_source in rule.get('rule_sources', []):
1432         dirs = set()
1433         rule_source_basename = os.path.basename(rule_source)
1434         (rule_source_root, rule_source_ext) = \
1435             os.path.splitext(rule_source_basename)
1436
1437         outputs = [self.ExpandInputRoot(out, rule_source_root)
1438                    for out in rule['outputs']]
1439         for out in outputs:
1440           dir = os.path.dirname(out)
1441           if dir:
1442             dirs.add(dir)
1443         if int(rule.get('process_outputs_as_sources', False)):
1444           extra_sources += outputs
1445         if int(rule.get('process_outputs_as_mac_bundle_resources', False)):
1446           extra_mac_bundle_resources += outputs
1447         all_outputs += outputs
1448         inputs = map(Sourceify, map(self.Absolutify, [rule_source] +
1449                                     rule.get('inputs', [])))
1450         actions = ['$(call do_cmd,%s_%d)' % (name, count)]
1451
1452         if name == 'resources_grit':
1453           # HACK: This is ugly.  Grit intentionally doesn't touch the
1454           # timestamp of its output file when the file doesn't change,
1455           # which is fine in hash-based dependency systems like scons
1456           # and forge, but not kosher in the make world.  After some
1457           # discussion, hacking around it here seems like the least
1458           # amount of pain.
1459           actions += ['@touch --no-create $@']
1460
1461         # Only write the 'obj' and 'builddir' rules for the "primary" output
1462         # (:1); it's superfluous for the "extra outputs", and this avoids
1463         # accidentally writing duplicate dummy rules for those outputs.
1464         self.WriteMakeRule(outputs[:1], ['obj := $(abs_obj)'])
1465         self.WriteMakeRule(outputs[:1], ['builddir := $(abs_builddir)'])
1466         self.WriteMakeRule(outputs, inputs + ['FORCE_DO_CMD'], actions)
1467         for output in outputs:
1468           assert ' ' not in output, (
1469               "Spaces in rule filenames not yet supported (%s)"  % output)
1470         self.WriteLn('all_deps += %s' % ' '.join(outputs))
1471         self._num_outputs += len(outputs)
1472
1473         action = [self.ExpandInputRoot(ac, rule_source_root)
1474                   for ac in rule['action']]
1475         mkdirs = ''
1476         if len(dirs) > 0:
1477           mkdirs = 'mkdir -p %s; ' % ' '.join(dirs)
1478         cd_action = 'cd %s; ' % Sourceify(self.path or '.')
1479         # Set LD_LIBRARY_PATH in case the rule runs an executable from this
1480         # build which links to shared libs from this build.
1481         # rules run on the host, so they should in theory only use host
1482         # libraries, but until everything is made cross-compile safe, also use
1483         # target libraries.
1484         # TODO(piman): when everything is cross-compile safe, remove lib.target
1485         self.WriteLn(
1486             "cmd_%(name)s_%(count)d = export LD_LIBRARY_PATH="
1487               "$(builddir)/lib.host:$(builddir)/lib.target:$$LD_LIBRARY_PATH; "
1488               "%(cd_action)s%(mkdirs)s%(action)s" % {
1489           'action': gyp.common.EncodePOSIXShellList(action),
1490           'cd_action': cd_action,
1491           'count': count,
1492           'mkdirs': mkdirs,
1493           'name': name,
1494         })
1495         self.WriteLn(
1496             'quiet_cmd_%(name)s_%(count)d = RULE %(name)s_%(count)d $@' % {
1497           'count': count,
1498           'name': name,
1499         })
1500         self.WriteLn()
1501         count += 1
1502
1503       outputs_variable = 'rule_%s_outputs' % name
1504       self.WriteList(all_outputs, outputs_variable)
1505       extra_outputs.append('$(%s)' % outputs_variable)
1506
1507       self.WriteLn('### Finished generating for rule: %s' % name)
1508       self.WriteLn()
1509     self.WriteLn('### Finished generating for all rules')
1510     self.WriteLn('')
1511
1512
1513   def WriteCopies(self, copies, extra_outputs, part_of_all, spec):
1514     """Write Makefile code for any 'copies' from the gyp input.
1515
1516     extra_outputs: a list that will be filled in with any outputs of this action
1517                    (used to make other pieces dependent on this action)
1518     part_of_all: flag indicating this target is part of 'all'
1519     """
1520     self.WriteLn('### Generated for copy rule.')
1521
1522     variable = self.target + '_copies'
1523     outputs = []
1524     for copy in copies:
1525       for path in copy['files']:
1526         path = Sourceify(self.Absolutify(path))
1527         filename = os.path.split(path)[1]
1528         output = Sourceify(self.Absolutify(os.path.join(copy['destination'],
1529                                                         filename)))
1530         path = QuoteSpaces(path)
1531         output = QuoteSpaces(output)
1532
1533         # If the output path has variables in it, which happens in practice for
1534         # 'copies', writing the environment as target-local doesn't work,
1535         # because the variables are already needed for the target name.
1536         # Copying the environment variables into global make variables doesn't
1537         # work either, because then the .d files will potentially contain spaces
1538         # after variable expansion, and .d file handling cannot handle spaces.
1539         # As a workaround, manually expand variables at gyp time. Since 'copies'
1540         # can't run scripts, there's no need to write the env then.
1541         # WriteDoCmd() will escape spaces for .d files.
1542         import gyp.generator.xcode as xcode_generator
1543         env = self.GetXcodeEnv(spec)
1544         output = xcode_generator.ExpandXcodeVariables(output, env)
1545         path = xcode_generator.ExpandXcodeVariables(path, env)
1546         self.WriteDoCmd([output], [path], 'copy', part_of_all)
1547         outputs.append(output)
1548     self.WriteLn('%s = %s' % (variable, ' '.join(outputs)))
1549     extra_outputs.append('$(%s)' % variable)
1550     self.WriteLn()
1551
1552
1553   def WriteMacBundleResources(self, resources, bundle_deps, spec):
1554     """Writes Makefile code for 'mac_bundle_resources'."""
1555     self.WriteLn('### Generated for mac_bundle_resources')
1556     variable = self.target + '_mac_bundle_resources'
1557     path = generator_default_variables['PRODUCT_DIR']
1558     dest = os.path.join(path, self.xcode_settings.GetBundleResourceFolder())
1559     dest = QuoteSpaces(dest)
1560     for res in resources:
1561       output = dest
1562
1563       assert ' ' not in res, (
1564         "Spaces in resource filenames not supported (%s)"  % res)
1565
1566       # Split into (path,file).
1567       path = Sourceify(self.Absolutify(res))
1568       path_parts = os.path.split(path)
1569
1570       # Now split the path into (prefix,maybe.lproj).
1571       lproj_parts = os.path.split(path_parts[0])
1572       # If the resource lives in a .lproj bundle, add that to the destination.
1573       if lproj_parts[1].endswith('.lproj'):
1574         output = os.path.join(output, lproj_parts[1])
1575
1576       output = Sourceify(self.Absolutify(os.path.join(output, path_parts[1])))
1577       # Compiled XIB files are referred to by .nib.
1578       if output.endswith('.xib'):
1579         output = output[0:-3] + 'nib'
1580
1581       self.WriteDoCmd([output], [path], 'mac_tool,,,copy-bundle-resource',
1582                       part_of_all=True)
1583       bundle_deps.append(output)
1584
1585
1586   def WriteMacInfoPlist(self, info_plist, bundle_deps, spec):
1587     """Write Makefile code for bundle Info.plist files."""
1588     assert ' ' not in info_plist, (
1589       "Spaces in resource filenames not supported (%s)"  % info_plist)
1590     info_plist = self.Absolutify(info_plist)
1591     path = generator_default_variables['PRODUCT_DIR']
1592     dest_plist = os.path.join(path, self.xcode_settings.GetBundlePlistPath())
1593     dest_plist = QuoteSpaces(dest_plist)
1594     extra_settings = self.xcode_settings.GetPerTargetSettings()
1595     # plists can contain envvars and substitute them into the file..
1596     self.WriteXcodeEnv(dest_plist, spec, additional_settings=extra_settings)
1597     self.WriteDoCmd([dest_plist], [info_plist], 'mac_tool,,,copy-info-plist',
1598                     part_of_all=True)
1599     bundle_deps.append(dest_plist)
1600
1601
1602   def WriteSources(self, configs, deps, sources,
1603                    extra_outputs, extra_link_deps,
1604                    part_of_all, precompiled_header):
1605     """Write Makefile code for any 'sources' from the gyp input.
1606     These are source files necessary to build the current target.
1607
1608     configs, deps, sources: input from gyp.
1609     extra_outputs: a list of extra outputs this action should be dependent on;
1610                    used to serialize action/rules before compilation
1611     extra_link_deps: a list that will be filled in with any outputs of
1612                      compilation (to be used in link lines)
1613     part_of_all: flag indicating this target is part of 'all'
1614     """
1615
1616     # Write configuration-specific variables for CFLAGS, etc.
1617     for configname in sorted(configs.keys()):
1618       config = configs[configname]
1619       self.WriteList(config.get('defines'), 'DEFS_%s' % configname, prefix='-D',
1620           quoter=EscapeCppDefine)
1621
1622       if self.flavor == 'mac':
1623         cflags = self.xcode_settings.GetCflags(configname)
1624         cflags_c = self.xcode_settings.GetCflagsC(configname)
1625         cflags_cc = self.xcode_settings.GetCflagsCC(configname)
1626         cflags_objc = self.xcode_settings.GetCflagsObjC(configname)
1627         cflags_objcc = self.xcode_settings.GetCflagsObjCC(configname)
1628       else:
1629         cflags = config.get('cflags')
1630         cflags_c = config.get('cflags_c')
1631         cflags_cc = config.get('cflags_cc')
1632
1633       self.WriteLn("# Flags passed to all source files.");
1634       self.WriteList(cflags, 'CFLAGS_%s' % configname)
1635       self.WriteLn("# Flags passed to only C files.");
1636       self.WriteList(cflags_c, 'CFLAGS_C_%s' % configname)
1637       self.WriteLn("# Flags passed to only C++ files.");
1638       self.WriteList(cflags_cc, 'CFLAGS_CC_%s' % configname)
1639       if self.flavor == 'mac':
1640         self.WriteLn("# Flags passed to only ObjC files.");
1641         self.WriteList(cflags_objc, 'CFLAGS_OBJC_%s' % configname)
1642         self.WriteLn("# Flags passed to only ObjC++ files.");
1643         self.WriteList(cflags_objcc, 'CFLAGS_OBJCC_%s' % configname)
1644       includes = config.get('include_dirs')
1645       if includes:
1646         includes = map(Sourceify, map(self.Absolutify, includes))
1647       self.WriteList(includes, 'INCS_%s' % configname, prefix='-I')
1648
1649     compilable = filter(Compilable, sources)
1650     objs = map(self.Objectify, map(self.Absolutify, map(Target, compilable)))
1651     self.WriteList(objs, 'OBJS')
1652
1653     for obj in objs:
1654       assert ' ' not in obj, (
1655           "Spaces in object filenames not supported (%s)"  % obj)
1656     self.WriteLn('# Add to the list of files we specially track '
1657                  'dependencies for.')
1658     self.WriteLn('all_deps += $(OBJS)')
1659     self._num_outputs += len(objs)
1660     self.WriteLn()
1661
1662     # Make sure our dependencies are built first.
1663     if deps:
1664       self.WriteMakeRule(['$(OBJS)'], deps,
1665                          comment = 'Make sure our dependencies are built '
1666                                    'before any of us.',
1667                          order_only = True)
1668
1669     # Make sure the actions and rules run first.
1670     # If they generate any extra headers etc., the per-.o file dep tracking
1671     # will catch the proper rebuilds, so order only is still ok here.
1672     if extra_outputs:
1673       self.WriteMakeRule(['$(OBJS)'], extra_outputs,
1674                          comment = 'Make sure our actions/rules run '
1675                                    'before any of us.',
1676                          order_only = True)
1677
1678     precompiled_header.WriteObjDependencies(compilable, objs, self)
1679
1680     if objs:
1681       extra_link_deps.append('$(OBJS)')
1682       self.WriteLn("""\
1683 # CFLAGS et al overrides must be target-local.
1684 # See "Target-specific Variable Values" in the GNU Make manual.""")
1685       self.WriteLn("$(OBJS): TOOLSET := $(TOOLSET)")
1686       self.WriteLn("$(OBJS): GYP_CFLAGS := "
1687                    "$(DEFS_$(BUILDTYPE)) "
1688                    "$(INCS_$(BUILDTYPE)) "
1689                    "%s" % precompiled_header.GetInclude('c') +
1690                    "$(CFLAGS_$(BUILDTYPE)) "
1691                    "$(CFLAGS_C_$(BUILDTYPE))")
1692       self.WriteLn("$(OBJS): GYP_CXXFLAGS := "
1693                    "$(DEFS_$(BUILDTYPE)) "
1694                    "$(INCS_$(BUILDTYPE)) "
1695                    "%s" % precompiled_header.GetInclude('cc') +
1696                    "$(CFLAGS_$(BUILDTYPE)) "
1697                    "$(CFLAGS_CC_$(BUILDTYPE))")
1698       if self.flavor == 'mac':
1699         self.WriteLn("$(OBJS): GYP_OBJCFLAGS := "
1700                      "$(DEFS_$(BUILDTYPE)) "
1701                      "$(INCS_$(BUILDTYPE)) "
1702                      "%s" % precompiled_header.GetInclude('m') +
1703                      "$(CFLAGS_$(BUILDTYPE)) "
1704                      "$(CFLAGS_C_$(BUILDTYPE)) "
1705                      "$(CFLAGS_OBJC_$(BUILDTYPE))")
1706         self.WriteLn("$(OBJS): GYP_OBJCXXFLAGS := "
1707                      "$(DEFS_$(BUILDTYPE)) "
1708                      "$(INCS_$(BUILDTYPE)) "
1709                      "%s" % precompiled_header.GetInclude('mm') +
1710                      "$(CFLAGS_$(BUILDTYPE)) "
1711                      "$(CFLAGS_CC_$(BUILDTYPE)) "
1712                      "$(CFLAGS_OBJCC_$(BUILDTYPE))")
1713
1714     precompiled_header.WritePchTargets(self)
1715
1716     # If there are any object files in our input file list, link them into our
1717     # output.
1718     extra_link_deps += filter(Linkable, sources)
1719
1720     self.WriteLn()
1721
1722
1723   def ComputeOutputBasename(self, spec):
1724     """Return the 'output basename' of a gyp spec.
1725
1726     E.g., the loadable module 'foobar' in directory 'baz' will produce
1727       'libfoobar.so'
1728     """
1729     assert not self.is_mac_bundle
1730
1731     if self.flavor == 'mac' and self.type in (
1732         'static_library', 'executable', 'shared_library', 'loadable_module'):
1733       return self.xcode_settings.GetExecutablePath()
1734
1735     target = spec['target_name']
1736     target_prefix = ''
1737     target_ext = ''
1738     if self.type == 'static_library':
1739       if target[:3] == 'lib':
1740         target = target[3:]
1741       target_prefix = 'lib'
1742       target_ext = '.a'
1743     elif self.type in ('loadable_module', 'shared_library'):
1744       if target[:3] == 'lib':
1745         target = target[3:]
1746       target_prefix = 'lib'
1747       target_ext = '.so'
1748     elif self.type == 'none':
1749       target = '%s.stamp' % target
1750     elif self.type != 'executable':
1751       print ("ERROR: What output file should be generated?",
1752              "type", self.type, "target", target)
1753
1754     target_prefix = spec.get('product_prefix', target_prefix)
1755     target = spec.get('product_name', target)
1756     product_ext = spec.get('product_extension')
1757     if product_ext:
1758       target_ext = '.' + product_ext
1759
1760     return target_prefix + target + target_ext
1761
1762
1763   def _InstallImmediately(self):
1764     return self.toolset == 'target' and self.flavor == 'mac' and self.type in (
1765           'static_library', 'executable', 'shared_library', 'loadable_module')
1766
1767
1768   def ComputeOutput(self, spec):
1769     """Return the 'output' (full output path) of a gyp spec.
1770
1771     E.g., the loadable module 'foobar' in directory 'baz' will produce
1772       '$(obj)/baz/libfoobar.so'
1773     """
1774     assert not self.is_mac_bundle
1775
1776     if self.type == 'settings':
1777       return ''  # Doesn't have any output.
1778
1779     path = os.path.join('$(obj).' + self.toolset, self.path)
1780     if self.type == 'executable' or self._InstallImmediately():
1781       path = '$(builddir)'
1782     path = spec.get('product_dir', path)
1783     return os.path.join(path, self.ComputeOutputBasename(spec))
1784
1785
1786   def ComputeMacBundleOutput(self, spec):
1787     """Return the 'output' (full output path) to a bundle output directory."""
1788     assert self.is_mac_bundle
1789     path = generator_default_variables['PRODUCT_DIR']
1790     return os.path.join(path, self.xcode_settings.GetWrapperName())
1791
1792
1793   def ComputeMacBundleBinaryOutput(self, spec):
1794     """Return the 'output' (full output path) to the binary in a bundle."""
1795     path = generator_default_variables['PRODUCT_DIR']
1796     return os.path.join(path, self.xcode_settings.GetExecutablePath())
1797
1798
1799   def ComputeDeps(self, spec):
1800     """Compute the dependencies of a gyp spec.
1801
1802     Returns a tuple (deps, link_deps), where each is a list of
1803     filenames that will need to be put in front of make for either
1804     building (deps) or linking (link_deps).
1805     """
1806     deps = []
1807     link_deps = []
1808     if 'dependencies' in spec:
1809       deps.extend([target_outputs[dep] for dep in spec['dependencies']
1810                    if target_outputs[dep]])
1811       for dep in spec['dependencies']:
1812         if dep in target_link_deps:
1813           link_deps.append(target_link_deps[dep])
1814       deps.extend(link_deps)
1815       # TODO: It seems we need to transitively link in libraries (e.g. -lfoo)?
1816       # This hack makes it work:
1817       # link_deps.extend(spec.get('libraries', []))
1818     return (gyp.common.uniquer(deps), gyp.common.uniquer(link_deps))
1819
1820
1821   def WriteDependencyOnExtraOutputs(self, target, extra_outputs):
1822     self.WriteMakeRule([self.output_binary], extra_outputs,
1823                        comment = 'Build our special outputs first.',
1824                        order_only = True)
1825
1826
1827   def WriteTarget(self, spec, configs, deps, link_deps, bundle_deps,
1828                   extra_outputs, part_of_all):
1829     """Write Makefile code to produce the final target of the gyp spec.
1830
1831     spec, configs: input from gyp.
1832     deps, link_deps: dependency lists; see ComputeDeps()
1833     extra_outputs: any extra outputs that our target should depend on
1834     part_of_all: flag indicating this target is part of 'all'
1835     """
1836
1837     self.WriteLn('### Rules for final target.')
1838
1839     if extra_outputs:
1840       self.WriteDependencyOnExtraOutputs(self.output_binary, extra_outputs)
1841       self.WriteMakeRule(extra_outputs, deps,
1842                          comment=('Preserve order dependency of '
1843                                   'special output on deps.'),
1844                          order_only = True,
1845                          multiple_output_trick = False)
1846
1847     if self.type not in ('settings', 'none'):
1848       for configname in sorted(configs.keys()):
1849         config = configs[configname]
1850         if self.flavor == 'mac':
1851           ldflags = self.xcode_settings.GetLdflags(self, configname)
1852         else:
1853           ldflags = config.get('ldflags', [])
1854           # Compute an rpath for this output if needed.
1855           if any(dep.endswith('.so') for dep in deps):
1856             # We want to get the literal string "$ORIGIN" into the link command,
1857             # so we need lots of escaping.
1858             ldflags.append(r'-Wl,-rpath=\$$ORIGIN/lib.%s/' % self.toolset)
1859         self.WriteList(ldflags, 'LDFLAGS_%s' % configname)
1860       libraries = spec.get('libraries')
1861       if libraries:
1862         # Remove duplicate entries
1863         libraries = gyp.common.uniquer(libraries)
1864         # On Mac, framework libraries need to be passed as '-framework Cocoa'.
1865         if self.flavor == 'mac':
1866           libraries = [
1867               '-framework ' + os.path.splitext(os.path.basename(library))[0]
1868               if library.endswith('.framework') else library
1869               for library in libraries]
1870       self.WriteList(libraries, 'LIBS')
1871       self.WriteLn(
1872           '%s: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))' % self.output_binary)
1873       self.WriteLn('%s: LIBS := $(LIBS)' % self.output_binary)
1874
1875     postbuilds = []
1876     if self.flavor == 'mac':
1877       # Postbuild actions. Like actions, but implicitly depend on the target's
1878       # output.
1879       for postbuild in spec.get('postbuilds', []):
1880         postbuilds.append('echo POSTBUILD\\(%s\\) %s' % (
1881               self.target, postbuild['postbuild_name']))
1882         shell_list = postbuild['action']
1883         # The first element is the command. If it's a relative path, it's
1884         # a script in the source tree relative to the gyp file and needs to be
1885         # absolutified. Else, it's in the PATH (e.g. install_name_tool, ln).
1886         if os.path.sep in shell_list[0]:
1887           shell_list[0] = self.Absolutify(shell_list[0])
1888         postbuilds.append('%s' % gyp.common.EncodePOSIXShellList(shell_list))
1889
1890     # A bundle directory depends on its dependencies such as bundle resources
1891     # and bundle binary. When all dependencies have been built, the bundle
1892     # needs to be packaged.
1893     if self.is_mac_bundle:
1894       self.WriteXcodeEnv(self.output, spec)  # For postbuilds
1895
1896       # If the framework doesn't contain a binary, then nothing depends
1897       # on the actions -- make the framework depend on them directly too.
1898       self.WriteDependencyOnExtraOutputs(self.output, extra_outputs)
1899
1900       # Bundle dependencies. Note that the code below adds actions to this
1901       # target, so if you move these two lines, move the lines below as well.
1902       self.WriteList(bundle_deps, 'BUNDLE_DEPS')
1903       self.WriteLn('%s: $(BUNDLE_DEPS)' % self.output)
1904
1905       # After the framework is built, package it. Needs to happen before
1906       # postbuilds, since postbuilds depend on this.
1907       if self.type in ('shared_library', 'loadable_module'):
1908         self.WriteLn('\t@$(call do_cmd,mac_package_framework,,,%s)' %
1909             self.xcode_settings.GetFrameworkVersion())
1910
1911       # Bundle postbuilds can depend on the whole bundle, so run them after
1912       # the bundle is packaged, not already after the bundle binary is done.
1913       for postbuild in postbuilds:
1914         self.WriteLn('\t@' + postbuild)
1915       postbuilds = []  # Don't write postbuilds for target's output.
1916
1917       # Needed by test/mac/gyptest-rebuild.py.
1918       self.WriteLn('\t@true  # No-op, used by tests')
1919
1920       # Since this target depends on binary and resources which are in
1921       # nested subfolders, the framework directory will be older than
1922       # its dependencies usually. To prevent this rule from executing
1923       # on every build (expensive, especially with postbuilds), expliclity
1924       # update the time on the framework directory.
1925       self.WriteLn('\t@touch -c %s' % self.output)
1926
1927     if postbuilds:
1928       assert not self.is_mac_bundle, ('Postbuilds for bundles should be done '
1929           'on the bundle, not the binary (target \'%s\')' % self.target)
1930       assert 'product_dir' not in spec, ('Postbuilds do not work with '
1931           'custom product_dir')
1932       self.WriteXcodeEnv(self.output_binary, spec)  # For postbuilds
1933       postbuilds = [EscapeShellArgument(p) for p in postbuilds]
1934       self.WriteLn('%s: builddir := $(abs_builddir)' % self.output_binary)
1935       self.WriteLn('%s: POSTBUILDS := %s' % (
1936           self.output_binary, ' '.join(postbuilds)))
1937
1938     if self.type == 'executable':
1939       self.WriteLn(
1940           '%s: LD_INPUTS := %s' % (self.output_binary, ' '.join(link_deps)))
1941       if self.toolset == 'host' and self.flavor == 'android':
1942         self.WriteDoCmd([self.output_binary], link_deps, 'link_host',
1943                         part_of_all, postbuilds=postbuilds)
1944       else:
1945         self.WriteDoCmd([self.output_binary], link_deps, 'link', part_of_all,
1946                         postbuilds=postbuilds)
1947
1948     elif self.type == 'static_library':
1949       for link_dep in link_deps:
1950         assert ' ' not in link_dep, (
1951             "Spaces in alink input filenames not supported (%s)"  % link_dep)
1952       self.WriteDoCmd([self.output_binary], link_deps, 'alink', part_of_all,
1953                       postbuilds=postbuilds)
1954     elif self.type == 'shared_library':
1955       self.WriteLn(
1956           '%s: LD_INPUTS := %s' % (self.output_binary, ' '.join(link_deps)))
1957       self.WriteDoCmd([self.output_binary], link_deps, 'solink', part_of_all,
1958                       postbuilds=postbuilds)
1959     elif self.type == 'loadable_module':
1960       for link_dep in link_deps:
1961         assert ' ' not in link_dep, (
1962             "Spaces in module input filenames not supported (%s)"  % link_dep)
1963       if self.toolset == 'host' and self.flavor == 'android':
1964         self.WriteDoCmd([self.output_binary], link_deps, 'solink_module_host',
1965                         part_of_all, postbuilds=postbuilds)
1966       else:
1967         self.WriteDoCmd(
1968             [self.output_binary], link_deps, 'solink_module', part_of_all,
1969             postbuilds=postbuilds)
1970     elif self.type == 'none':
1971       # Write a stamp line.
1972       self.WriteDoCmd([self.output_binary], deps, 'touch', part_of_all,
1973                       postbuilds=postbuilds)
1974     elif self.type == 'settings':
1975       # Only used for passing flags around.
1976       pass
1977     else:
1978       print "WARNING: no output for", self.type, target
1979
1980     # Add an alias for each target (if there are any outputs).
1981     # Installable target aliases are created below.
1982     if ((self.output and self.output != self.target) and
1983         (self.type not in self._INSTALLABLE_TARGETS)):
1984       self.WriteMakeRule([self.target], [self.output],
1985                          comment='Add target alias', phony = True)
1986       if part_of_all:
1987         self.WriteMakeRule(['all'], [self.target],
1988                            comment = 'Add target alias to "all" target.',
1989                            phony = True)
1990
1991     # Add special-case rules for our installable targets.
1992     # 1) They need to install to the build dir or "product" dir.
1993     # 2) They get shortcuts for building (e.g. "make chrome").
1994     # 3) They are part of "make all".
1995     if self.type in self._INSTALLABLE_TARGETS:
1996       if self.type == 'shared_library':
1997         file_desc = 'shared library'
1998       else:
1999         file_desc = 'executable'
2000       install_path = self._InstallableTargetInstallPath()
2001       installable_deps = [self.output]
2002       if self.flavor == 'mac' and not 'product_dir' in spec:
2003         # On mac, products are created in install_path immediately.
2004         assert install_path == self.output, '%s != %s' % (
2005             install_path, self.output)
2006
2007       # Point the target alias to the final binary output.
2008       self.WriteMakeRule([self.target], [install_path],
2009                          comment='Add target alias', phony = True)
2010       if install_path != self.output:
2011         assert not self.is_mac_bundle  # See comment a few lines above.
2012         self.WriteDoCmd([install_path], [self.output], 'copy',
2013                         comment = 'Copy this to the %s output path.' %
2014                         file_desc, part_of_all=part_of_all)
2015         installable_deps.append(install_path)
2016       if self.output != self.alias and self.alias != self.target:
2017         self.WriteMakeRule([self.alias], installable_deps,
2018                            comment = 'Short alias for building this %s.' %
2019                            file_desc, phony = True)
2020       if part_of_all:
2021         self.WriteMakeRule(['all'], [install_path],
2022                            comment = 'Add %s to "all" target.' % file_desc,
2023                            phony = True)
2024
2025
2026   def WriteList(self, list, variable=None, prefix='', quoter=QuoteIfNecessary):
2027     """Write a variable definition that is a list of values.
2028
2029     E.g. WriteList(['a','b'], 'foo', prefix='blah') writes out
2030          foo = blaha blahb
2031     but in a pretty-printed style.
2032     """
2033     self.fp.write(variable + " := ")
2034     if list:
2035       list = [quoter(prefix + l) for l in list]
2036       self.fp.write(" \\\n\t".join(list))
2037     self.fp.write("\n\n")
2038
2039
2040   def WriteDoCmd(self, outputs, inputs, command, part_of_all, comment=None,
2041                  postbuilds=False):
2042     """Write a Makefile rule that uses do_cmd.
2043
2044     This makes the outputs dependent on the command line that was run,
2045     as well as support the V= make command line flag.
2046     """
2047     suffix = ''
2048     if postbuilds:
2049       assert ',' not in command
2050       suffix = ',,1'  # Tell do_cmd to honor $POSTBUILDS
2051     self.WriteMakeRule(outputs, inputs,
2052                        actions = ['$(call do_cmd,%s%s)' % (command, suffix)],
2053                        comment = comment,
2054                        force = True)
2055     # Add our outputs to the list of targets we read depfiles from.
2056     # all_deps is only used for deps file reading, and for deps files we replace
2057     # spaces with ? because escaping doesn't work with make's $(sort) and
2058     # other functions.
2059     outputs = [ReplaceQuotedSpaces(o) for o in outputs]
2060     self.WriteLn('all_deps += %s' % ' '.join(outputs))
2061     self._num_outputs += len(outputs)
2062
2063
2064   def WriteMakeRule(self, outputs, inputs, actions=None, comment=None,
2065                     order_only=False, force=False, phony=False,
2066                     multiple_output_trick=True):
2067     """Write a Makefile rule, with some extra tricks.
2068
2069     outputs: a list of outputs for the rule (note: this is not directly
2070              supported by make; see comments below)
2071     inputs: a list of inputs for the rule
2072     actions: a list of shell commands to run for the rule
2073     comment: a comment to put in the Makefile above the rule (also useful
2074              for making this Python script's code self-documenting)
2075     order_only: if true, makes the dependency order-only
2076     force: if true, include FORCE_DO_CMD as an order-only dep
2077     phony: if true, the rule does not actually generate the named output, the
2078            output is just a name to run the rule
2079     multiple_output_trick: if true (the default), perform tricks such as dummy
2080            rules to avoid problems with multiple outputs.
2081     """
2082     if comment:
2083       self.WriteLn('# ' + comment)
2084     if phony:
2085       self.WriteLn('.PHONY: ' + ' '.join(outputs))
2086     # TODO(evanm): just make order_only a list of deps instead of these hacks.
2087     if order_only:
2088       order_insert = '| '
2089     else:
2090       order_insert = ''
2091     if force:
2092       force_append = ' FORCE_DO_CMD'
2093     else:
2094       force_append = ''
2095     if actions:
2096       self.WriteLn("%s: TOOLSET := $(TOOLSET)" % outputs[0])
2097     self.WriteLn('%s: %s%s%s' % (outputs[0], order_insert, ' '.join(inputs),
2098                                  force_append))
2099     if actions:
2100       for action in actions:
2101         self.WriteLn('\t%s' % action)
2102     if multiple_output_trick and len(outputs) > 1:
2103       # If we have more than one output, a rule like
2104       #   foo bar: baz
2105       # that for *each* output we must run the action, potentially
2106       # in parallel.  That is not what we're trying to write -- what
2107       # we want is that we run the action once and it generates all
2108       # the files.
2109       # http://www.gnu.org/software/hello/manual/automake/Multiple-Outputs.html
2110       # discusses this problem and has this solution:
2111       # 1) Write the naive rule that would produce parallel runs of
2112       # the action.
2113       # 2) Make the outputs seralized on each other, so we won't start
2114       # a parallel run until the first run finishes, at which point
2115       # we'll have generated all the outputs and we're done.
2116       self.WriteLn('%s: %s' % (' '.join(outputs[1:]), outputs[0]))
2117       # Add a dummy command to the "extra outputs" rule, otherwise make seems to
2118       # think these outputs haven't (couldn't have?) changed, and thus doesn't
2119       # flag them as changed (i.e. include in '$?') when evaluating dependent
2120       # rules, which in turn causes do_cmd() to skip running dependent commands.
2121       self.WriteLn('%s: ;' % (' '.join(outputs[1:])))
2122     self.WriteLn()
2123
2124
2125   def WriteAndroidNdkModuleRule(self, module_name, all_sources, link_deps):
2126     """Write a set of LOCAL_XXX definitions for Android NDK.
2127
2128     These variable definitions will be used by Android NDK but do nothing for
2129     non-Android applications.
2130
2131     Arguments:
2132       module_name: Android NDK module name, which must be unique among all
2133           module names.
2134       all_sources: A list of source files (will be filtered by Compilable).
2135       link_deps: A list of link dependencies, which must be sorted in
2136           the order from dependencies to dependents.
2137     """
2138     if self.type not in ('executable', 'shared_library', 'static_library'):
2139       return
2140
2141     self.WriteLn('# Variable definitions for Android applications')
2142     self.WriteLn('include $(CLEAR_VARS)')
2143     self.WriteLn('LOCAL_MODULE := ' + module_name)
2144     self.WriteLn('LOCAL_CFLAGS := $(CFLAGS_$(BUILDTYPE)) '
2145                  '$(DEFS_$(BUILDTYPE)) '
2146                  # LOCAL_CFLAGS is applied to both of C and C++.  There is
2147                  # no way to specify $(CFLAGS_C_$(BUILDTYPE)) only for C
2148                  # sources.
2149                  '$(CFLAGS_C_$(BUILDTYPE)) '
2150                  # $(INCS_$(BUILDTYPE)) includes the prefix '-I' while
2151                  # LOCAL_C_INCLUDES does not expect it.  So put it in
2152                  # LOCAL_CFLAGS.
2153                  '$(INCS_$(BUILDTYPE))')
2154     # LOCAL_CXXFLAGS is obsolete and LOCAL_CPPFLAGS is preferred.
2155     self.WriteLn('LOCAL_CPPFLAGS := $(CFLAGS_CC_$(BUILDTYPE))')
2156     self.WriteLn('LOCAL_C_INCLUDES :=')
2157     self.WriteLn('LOCAL_LDLIBS := $(LDFLAGS_$(BUILDTYPE)) $(LIBS)')
2158
2159     # Detect the C++ extension.
2160     cpp_ext = {'.cc': 0, '.cpp': 0, '.cxx': 0}
2161     default_cpp_ext = '.cpp'
2162     for filename in all_sources:
2163       ext = os.path.splitext(filename)[1]
2164       if ext in cpp_ext:
2165         cpp_ext[ext] += 1
2166         if cpp_ext[ext] > cpp_ext[default_cpp_ext]:
2167           default_cpp_ext = ext
2168     self.WriteLn('LOCAL_CPP_EXTENSION := ' + default_cpp_ext)
2169
2170     self.WriteList(map(self.Absolutify, filter(Compilable, all_sources)),
2171                    'LOCAL_SRC_FILES')
2172
2173     # Filter out those which do not match prefix and suffix and produce
2174     # the resulting list without prefix and suffix.
2175     def DepsToModules(deps, prefix, suffix):
2176       modules = []
2177       for filepath in deps:
2178         filename = os.path.basename(filepath)
2179         if filename.startswith(prefix) and filename.endswith(suffix):
2180           modules.append(filename[len(prefix):-len(suffix)])
2181       return modules
2182
2183     # Retrieve the default value of 'SHARED_LIB_SUFFIX'
2184     params = {'flavor': 'linux'}
2185     default_variables = {}
2186     CalculateVariables(default_variables, params)
2187
2188     self.WriteList(
2189         DepsToModules(link_deps,
2190                       generator_default_variables['SHARED_LIB_PREFIX'],
2191                       default_variables['SHARED_LIB_SUFFIX']),
2192         'LOCAL_SHARED_LIBRARIES')
2193     self.WriteList(
2194         DepsToModules(link_deps,
2195                       generator_default_variables['STATIC_LIB_PREFIX'],
2196                       generator_default_variables['STATIC_LIB_SUFFIX']),
2197         'LOCAL_STATIC_LIBRARIES')
2198
2199     if self.type == 'executable':
2200       self.WriteLn('include $(BUILD_EXECUTABLE)')
2201     elif self.type == 'shared_library':
2202       self.WriteLn('include $(BUILD_SHARED_LIBRARY)')
2203     elif self.type == 'static_library':
2204       self.WriteLn('include $(BUILD_STATIC_LIBRARY)')
2205     self.WriteLn()
2206
2207
2208   def WriteLn(self, text=''):
2209     self.fp.write(text + '\n')
2210
2211
2212   def GetXcodeEnv(self, spec, target_relative_path=False):
2213     """Return the environment variables that Xcode would set. See
2214     http://developer.apple.com/library/mac/#documentation/DeveloperTools/Reference/XcodeBuildSettingRef/1-Build_Setting_Reference/build_setting_ref.html#//apple_ref/doc/uid/TP40003931-CH3-SW153
2215     for a full list."""
2216     if self.flavor != 'mac': return {}
2217
2218     built_products_dir = generator_default_variables['PRODUCT_DIR']
2219     def StripProductDir(s):
2220       assert s.startswith(built_products_dir), s
2221       return s[len(built_products_dir) + 1:]
2222
2223     product_name = spec.get('product_name', self.output)
2224
2225     if self._InstallImmediately():
2226       if product_name.startswith(built_products_dir):
2227         product_name = StripProductDir(product_name)
2228
2229     srcroot = self.path
2230     if target_relative_path:
2231       built_products_dir = os.path.relpath(built_products_dir, srcroot)
2232       srcroot = '.'
2233     # These are filled in on a as-needed basis.
2234     env = {
2235       'BUILT_PRODUCTS_DIR' : built_products_dir,
2236       'CONFIGURATION' : '$(BUILDTYPE)',
2237       'PRODUCT_NAME' : product_name,
2238       # See /Developer/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications/MacOSX\ Product\ Types.xcspec for FULL_PRODUCT_NAME
2239       'FULL_PRODUCT_NAME' : product_name,
2240       'SRCROOT' : srcroot,
2241       # This is not true for static libraries, but currently the env is only
2242       # written for bundles:
2243       'TARGET_BUILD_DIR' : built_products_dir,
2244       'TEMP_DIR' : '$(TMPDIR)',
2245     }
2246     if self.type in ('executable', 'shared_library'):
2247       env['EXECUTABLE_NAME'] = os.path.basename(self.output_binary)
2248     if self.type in (
2249         'executable', 'static_library', 'shared_library', 'loadable_module'):
2250       env['EXECUTABLE_PATH'] = self.xcode_settings.GetExecutablePath()
2251     if self.is_mac_bundle:
2252       env['CONTENTS_FOLDER_PATH'] = \
2253         self.xcode_settings.GetBundleContentsFolderPath()
2254       env['UNLOCALIZED_RESOURCES_FOLDER_PATH'] = \
2255           self.xcode_settings.GetBundleResourceFolder()
2256       env['INFOPLIST_PATH'] = self.xcode_settings.GetBundlePlistPath()
2257
2258       # TODO(thakis): Remove this.
2259       env['EXECUTABLE_PATH'] = QuoteSpaces(env['EXECUTABLE_PATH'])
2260       env['CONTENTS_FOLDER_PATH'] = QuoteSpaces(env['CONTENTS_FOLDER_PATH'])
2261       env['INFOPLIST_PATH'] = QuoteSpaces(env['INFOPLIST_PATH'])
2262
2263     return env
2264
2265
2266   def WriteXcodeEnv(self,
2267                     target,
2268                     spec,
2269                     target_relative_path=False,
2270                     additional_settings={}):
2271     env = additional_settings
2272     env.update(self.GetXcodeEnv(spec, target_relative_path))
2273
2274     # Keys whose values will not have $(builddir) replaced with $(abs_builddir).
2275     # These have special substitution rules in some cases; see above in
2276     # GetXcodeEnv() for the full rationale.
2277     keys_to_not_absolutify = ('PRODUCT_NAME', 'FULL_PRODUCT_NAME')
2278
2279     # Perform some transformations that are required to mimic Xcode behavior.
2280     for k in env:
2281       # Values that are not strings but are, for example, lists or tuples such
2282       # as LDFLAGS or CFLAGS, should not be written out because they are
2283       # not needed and it's undefined how multi-valued keys should be written.
2284       if not isinstance(env[k], str):
2285         continue
2286
2287       # For
2288       #  foo := a\ b
2289       # the escaped space does the right thing. For
2290       #  export foo := a\ b
2291       # it does not -- the backslash is written to the env as literal character.
2292       # Hence, unescape all spaces here.
2293       v = env[k].replace(r'\ ', ' ')
2294
2295       # Xcode works purely with absolute paths. When writing env variables to
2296       # mimic its usage, replace $(builddir) with $(abs_builddir).
2297       if k not in keys_to_not_absolutify:
2298         v = v.replace('$(builddir)', '$(abs_builddir)')
2299
2300       self.WriteLn('%s: export %s := %s' % (target, k, v))
2301
2302
2303   def Objectify(self, path):
2304     """Convert a path to its output directory form."""
2305     if '$(' in path:
2306       path = path.replace('$(obj)/', '$(obj).%s/$(TARGET)/' % self.toolset)
2307       return path
2308     return '$(obj).%s/$(TARGET)/%s' % (self.toolset, path)
2309
2310
2311   def Pchify(self, path, lang):
2312     """Convert a prefix header path to its output directory form."""
2313     if '$(' in path:
2314       path = path.replace('$(obj)/', '$(obj).%s/$(TARGET)/pch-%s' %
2315                           (self.toolset, lang))
2316       return path
2317     return '$(obj).%s/$(TARGET)/pch-%s/%s' % (self.toolset, lang, path)
2318
2319
2320   def Absolutify(self, path):
2321     """Convert a subdirectory-relative path into a base-relative path.
2322     Skips over paths that contain variables."""
2323     if '$(' in path:
2324       return path
2325     return os.path.normpath(os.path.join(self.path, path))
2326
2327
2328   def FixupArgPath(self, arg):
2329     if '/' in arg or '.h.' in arg:
2330       return self.Absolutify(arg)
2331     return arg
2332
2333
2334   def ExpandInputRoot(self, template, expansion):
2335     if '%(INPUT_ROOT)s' not in template:
2336       return template
2337     path = template % { 'INPUT_ROOT': expansion }
2338     if not os.path.dirname(path):
2339       # If it's just the file name, turn it into a path so FixupArgPath()
2340       # will know to Absolutify() it.
2341       path = os.path.join('.', path)
2342     return path
2343
2344
2345   def _InstallableTargetInstallPath(self):
2346     """Returns the location of the final output for an installable target."""
2347     # Xcode puts shared_library results into PRODUCT_DIR, and some gyp files
2348     # rely on this. Emulate this behavior for mac.
2349     if self.type == 'shared_library' and self.flavor != 'mac':
2350       # Install all shared libs into a common directory (per toolset) for
2351       # convenient access with LD_LIBRARY_PATH.
2352       return '$(builddir)/lib.%s/%s' % (self.toolset, self.alias)
2353     return '$(builddir)/' + self.alias
2354
2355
2356 def WriteAutoRegenerationRule(params, root_makefile, makefile_name,
2357                               build_files):
2358   """Write the target to regenerate the Makefile."""
2359   options = params['options']
2360   build_files_args = [gyp.common.RelativePath(filename, options.toplevel_dir)
2361                       for filename in params['build_files_arg']]
2362   gyp_binary = gyp.common.FixIfRelativePath(params['gyp_binary'],
2363                                             options.toplevel_dir)
2364   if not gyp_binary.startswith(os.sep):
2365     gyp_binary = os.path.join('.', gyp_binary)
2366   root_makefile.write(
2367       "quiet_cmd_regen_makefile = ACTION Regenerating $@\n"
2368       "cmd_regen_makefile = %(cmd)s\n"
2369       "%(makefile_name)s: %(deps)s\n"
2370       "\t$(call do_cmd,regen_makefile)\n\n" % {
2371           'makefile_name': makefile_name,
2372           'deps': ' '.join(map(Sourceify, build_files)),
2373           'cmd': gyp.common.EncodePOSIXShellList(
2374                      [gyp_binary, '-fmake'] +
2375                      gyp.RegenerateFlags(options) +
2376                      build_files_args)})
2377
2378
2379 def RunSystemTests(flavor):
2380   """Run tests against the system to compute default settings for commands.
2381
2382   Returns:
2383     dictionary of settings matching the block of command-lines used in
2384     SHARED_HEADER.  E.g. the dictionary will contain a ARFLAGS.target
2385     key for the default ARFLAGS for the target ar command.
2386   """
2387   # Compute flags used for building static archives.
2388   # N.B.: this fallback logic should match the logic in SHARED_HEADER.
2389   # See comment there for more details.
2390   ar_target = os.environ.get('AR.target', os.environ.get('AR', 'ar'))
2391   cc_target = os.environ.get('CC.target', os.environ.get('CC', 'cc'))
2392   arflags_target = 'crs'
2393   # ar -T enables thin archives on Linux. OS X's ar supports a -T flag, but it
2394   # does something useless (it limits filenames in the archive to 15 chars).
2395   if flavor != 'mac' and gyp.system_test.TestArSupportsT(ar_command=ar_target,
2396                                                          cc_command=cc_target):
2397     arflags_target = 'crsT'
2398
2399   ar_host = os.environ.get('AR.host', 'ar')
2400   cc_host = os.environ.get('CC.host', 'gcc')
2401   arflags_host = 'crs'
2402   # It feels redundant to compute this again given that most builds aren't
2403   # cross-compiles, but due to quirks of history CC.host defaults to 'gcc'
2404   # while CC.target defaults to 'cc', so the commands really are different
2405   # even though they're nearly guaranteed to run the same code underneath.
2406   if flavor != 'mac' and gyp.system_test.TestArSupportsT(ar_command=ar_host,
2407                                                          cc_command=cc_host):
2408     arflags_host = 'crsT'
2409
2410   link_flags = ''
2411   if gyp.system_test.TestLinkerSupportsThreads(cc_command=cc_target):
2412     # N.B. we don't test for cross-compilation; as currently written, we
2413     # don't even use flock when linking in the cross-compile setup!
2414     # TODO(evan): refactor cross-compilation such that this code can
2415     # be reused.
2416     link_flags = '-Wl,--threads -Wl,--thread-count=4'
2417
2418   # TODO(evan): cache this output.  (But then we'll need to add extra
2419   # flags to gyp to flush the cache, yuk!  It's fast enough for now to
2420   # just run it every time.)
2421
2422   return { 'ARFLAGS.target': arflags_target,
2423            'ARFLAGS.host': arflags_host,
2424            'LINK_flags': link_flags }
2425
2426
2427 def CopyMacTool(out_path):
2428   """Finds mac_tool.gyp in the gyp directory and copies it to |out_path|."""
2429   source_path = os.path.join(
2430       os.path.dirname(os.path.abspath(__file__)), '..', 'mac_tool.py')
2431   source_file = open(source_path)
2432   source = source_file.readlines()
2433   source_file.close()
2434   mactool_file = open(out_path, 'w')
2435   mactool_file.write(
2436       ''.join([source[0], '# Generated by gyp. Do not edit.\n'] + source[1:]))
2437   mactool_file.close()
2438
2439
2440 def GenerateOutput(target_list, target_dicts, data, params):
2441   options = params['options']
2442   flavor = GetFlavor(params)
2443   generator_flags = params.get('generator_flags', {})
2444   builddir_name = generator_flags.get('output_dir', 'out')
2445   android_ndk_version = generator_flags.get('android_ndk_version', None)
2446
2447   def CalculateMakefilePath(build_file, base_name):
2448     """Determine where to write a Makefile for a given gyp file."""
2449     # Paths in gyp files are relative to the .gyp file, but we want
2450     # paths relative to the source root for the master makefile.  Grab
2451     # the path of the .gyp file as the base to relativize against.
2452     # E.g. "foo/bar" when we're constructing targets for "foo/bar/baz.gyp".
2453     base_path = gyp.common.RelativePath(os.path.dirname(build_file),
2454                                         options.depth)
2455     # We write the file in the base_path directory.
2456     output_file = os.path.join(options.depth, base_path, base_name)
2457     if options.generator_output:
2458       output_file = os.path.join(options.generator_output, output_file)
2459     base_path = gyp.common.RelativePath(os.path.dirname(build_file),
2460                                         options.toplevel_dir)
2461     return base_path, output_file
2462
2463   # TODO:  search for the first non-'Default' target.  This can go
2464   # away when we add verification that all targets have the
2465   # necessary configurations.
2466   default_configuration = None
2467   toolsets = set([target_dicts[target]['toolset'] for target in target_list])
2468   for target in target_list:
2469     spec = target_dicts[target]
2470     if spec['default_configuration'] != 'Default':
2471       default_configuration = spec['default_configuration']
2472       break
2473   if not default_configuration:
2474     default_configuration = 'Default'
2475
2476   srcdir = '.'
2477   makefile_name = 'Makefile' + options.suffix
2478   makefile_path = os.path.join(options.toplevel_dir, makefile_name)
2479   if options.generator_output:
2480     global srcdir_prefix
2481     makefile_path = os.path.join(options.generator_output, makefile_path)
2482     srcdir = gyp.common.RelativePath(srcdir, options.generator_output)
2483     srcdir_prefix = '$(srcdir)/'
2484
2485   header_params = {
2486       'builddir': builddir_name,
2487       'default_configuration': default_configuration,
2488       'flock': 'flock',
2489       'flock_index': 1,
2490       'link_commands': LINK_COMMANDS_LINUX,
2491       'mac_commands': '',
2492       'srcdir': srcdir,
2493     }
2494   if flavor == 'mac':
2495     header_params.update({
2496         'flock': './gyp-mac-tool flock',
2497         'flock_index': 2,
2498         'link_commands': LINK_COMMANDS_MAC,
2499         'mac_commands': SHARED_HEADER_MAC_COMMANDS,
2500     })
2501   if flavor == 'android':
2502     header_params.update({
2503         'link_commands': LINK_COMMANDS_ANDROID,
2504     })
2505   header_params.update(RunSystemTests(flavor))
2506
2507   build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
2508   make_global_settings_dict = data[build_file].get('make_global_settings', {})
2509   make_global_settings = ''
2510   for key, value in make_global_settings_dict:
2511     if value[0] != '$':
2512       value = '$(abspath %s)' % value
2513     if key == 'LINK':
2514       make_global_settings += '%s ?= $(FLOCK) %s\n' % (key, value)
2515     elif key in ['CC', 'CXX']:
2516       make_global_settings += (
2517           'ifneq (,$(filter $(origin %s), undefined default))\n' % key)
2518       # Let gyp-time envvars win over global settings.
2519       if key in os.environ:
2520         value = os.environ[key]
2521       make_global_settings += '  %s = %s\n' % (key, value)
2522       make_global_settings += 'endif\n'
2523     else:
2524       make_global_settings += '%s ?= %s\n' % (key, value)
2525   header_params['make_global_settings'] = make_global_settings
2526
2527   ensure_directory_exists(makefile_path)
2528   root_makefile = open(makefile_path, 'w')
2529   root_makefile.write(SHARED_HEADER % header_params)
2530   # Currently any versions have the same effect, but in future the behavior
2531   # could be different.
2532   if android_ndk_version:
2533     root_makefile.write(
2534         '# Define LOCAL_PATH for build of Android applications.\n'
2535         'LOCAL_PATH := $(call my-dir)\n'
2536         '\n')
2537   for toolset in toolsets:
2538     root_makefile.write('TOOLSET := %s\n' % toolset)
2539     WriteRootHeaderSuffixRules(root_makefile)
2540
2541   # Put mac_tool next to the root Makefile.
2542   if flavor == 'mac':
2543     mactool_path = os.path.join(os.path.dirname(makefile_path), 'gyp-mac-tool')
2544     if os.path.exists(mactool_path):
2545       os.remove(mactool_path)
2546     CopyMacTool(mactool_path)
2547     # Make file executable.
2548     os.chmod(mactool_path, 0755)
2549
2550   # Find the list of targets that derive from the gyp file(s) being built.
2551   needed_targets = set()
2552   for build_file in params['build_files']:
2553     for target in gyp.common.AllTargets(target_list, target_dicts, build_file):
2554       needed_targets.add(target)
2555
2556   num_outputs = 0
2557   build_files = set()
2558   include_list = set()
2559   for qualified_target in target_list:
2560     build_file, target, toolset = gyp.common.ParseQualifiedTarget(
2561         qualified_target)
2562
2563     this_make_global_settings = data[build_file].get('make_global_settings', {})
2564     assert make_global_settings_dict == this_make_global_settings, (
2565         "make_global_settings needs to be the same for all targets.")
2566
2567     build_files.add(gyp.common.RelativePath(build_file, options.toplevel_dir))
2568     included_files = data[build_file]['included_files']
2569     for included_file in included_files:
2570       # The included_files entries are relative to the dir of the build file
2571       # that included them, so we have to undo that and then make them relative
2572       # to the root dir.
2573       relative_include_file = gyp.common.RelativePath(
2574           gyp.common.UnrelativePath(included_file, build_file),
2575           options.toplevel_dir)
2576       abs_include_file = os.path.abspath(relative_include_file)
2577       # If the include file is from the ~/.gyp dir, we should use absolute path
2578       # so that relocating the src dir doesn't break the path.
2579       if (params['home_dot_gyp'] and
2580           abs_include_file.startswith(params['home_dot_gyp'])):
2581         build_files.add(abs_include_file)
2582       else:
2583         build_files.add(relative_include_file)
2584
2585     base_path, output_file = CalculateMakefilePath(build_file,
2586         target + '.' + toolset + options.suffix + '.mk')
2587
2588     spec = target_dicts[qualified_target]
2589     configs = spec['configurations']
2590
2591     # The xcode generator special-cases global xcode_settings and does something
2592     # that amounts to merging in the global xcode_settings into each local
2593     # xcode_settings dict.
2594     if flavor == 'mac':
2595       global_xcode_settings = data[build_file].get('xcode_settings', {})
2596       for configname in configs.keys():
2597         config = configs[configname]
2598         if 'xcode_settings' in config:
2599           new_settings = global_xcode_settings.copy()
2600           new_settings.update(config['xcode_settings'])
2601           config['xcode_settings'] = new_settings
2602
2603     writer = MakefileWriter(generator_flags, flavor)
2604     writer.Write(qualified_target, base_path, output_file, spec, configs,
2605                  part_of_all=qualified_target in needed_targets)
2606     num_outputs += writer.NumOutputs()
2607
2608     # Our root_makefile lives at the source root.  Compute the relative path
2609     # from there to the output_file for including.
2610     mkfile_rel_path = gyp.common.RelativePath(output_file,
2611                                               os.path.dirname(makefile_path))
2612     include_list.add(mkfile_rel_path)
2613
2614   # Write out per-gyp (sub-project) Makefiles.
2615   depth_rel_path = gyp.common.RelativePath(options.depth, os.getcwd())
2616   for build_file in build_files:
2617     # The paths in build_files were relativized above, so undo that before
2618     # testing against the non-relativized items in target_list and before
2619     # calculating the Makefile path.
2620     build_file = os.path.join(depth_rel_path, build_file)
2621     gyp_targets = [target_dicts[target]['target_name'] for target in target_list
2622                    if target.startswith(build_file) and
2623                    target in needed_targets]
2624     # Only generate Makefiles for gyp files with targets.
2625     if not gyp_targets:
2626       continue
2627     base_path, output_file = CalculateMakefilePath(build_file,
2628         os.path.splitext(os.path.basename(build_file))[0] + '.Makefile')
2629     makefile_rel_path = gyp.common.RelativePath(os.path.dirname(makefile_path),
2630                                                 os.path.dirname(output_file))
2631     writer.WriteSubMake(output_file, makefile_rel_path, gyp_targets,
2632                         builddir_name)
2633
2634
2635   # Write out the sorted list of includes.
2636   root_makefile.write('\n')
2637   for include_file in sorted(include_list):
2638     # We wrap each .mk include in an if statement so users can tell make to
2639     # not load a file by setting NO_LOAD.  The below make code says, only
2640     # load the .mk file if the .mk filename doesn't start with a token in
2641     # NO_LOAD.
2642     root_makefile.write(
2643         "ifeq ($(strip $(foreach prefix,$(NO_LOAD),\\\n"
2644         "    $(findstring $(join ^,$(prefix)),\\\n"
2645         "                 $(join ^," + include_file + ")))),)\n")
2646     root_makefile.write("  include " + include_file + "\n")
2647     root_makefile.write("endif\n")
2648   root_makefile.write('\n')
2649
2650   if generator_flags.get('auto_regeneration', True):
2651     WriteAutoRegenerationRule(params, root_makefile, makefile_name, build_files)
2652
2653   # Write the rule to load dependencies.  We batch 1000 files at a time to
2654   # avoid overflowing the command line.
2655   all_deps = ""
2656   for i in range(1001, num_outputs, 1000):
2657     all_deps += ("""
2658   ifneq ($(word %(start)d,$(d_files)),)
2659     $(shell cat $(wordlist %(start)d,%(end)d,$(d_files)) >> $(depsdir)/all.deps)
2660   endif""" % { 'start': i, 'end': i + 999 })
2661
2662   # Add a check to make sure we tried to process all the .d files.
2663   all_deps += """
2664   ifneq ($(word %(last)d,$(d_files)),)
2665     $(error Found unprocessed dependency files (gyp didn't generate enough rules!))
2666   endif
2667 """ % { 'last': ((num_outputs / 1000) + 1) * 1000 + 1 }
2668
2669   root_makefile.write(SHARED_FOOTER % { 'generate_all_deps': all_deps })
2670
2671   root_makefile.close()