1# Copyright (c) 2013 Google Inc. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5# Notes: 6# 7# This is all roughly based on the Makefile system used by the Linux 8# kernel, but is a non-recursive make -- we put the entire dependency 9# graph in front of make and let it figure it out. 10# 11# The code below generates a separate .mk file for each target, but 12# all are sourced by the top-level Makefile. This means that all 13# variables in .mk-files clobber one another. Be careful to use := 14# where appropriate for immediate evaluation, and similarly to watch 15# that you're not relying on a variable value to last between different 16# .mk files. 17# 18# TODOs: 19# 20# Global settings and utility functions are currently stuffed in the 21# toplevel Makefile. It may make sense to generate some .mk files on 22# the side to keep the files readable. 23 24from __future__ import print_function 25 26import os 27import re 28import sys 29import subprocess 30import gyp 31import gyp.common 32import gyp.xcode_emulation 33from gyp.common import GetEnvironFallback 34from gyp.common import GypError 35 36import hashlib 37 38generator_default_variables = { 39 'EXECUTABLE_PREFIX': '', 40 'EXECUTABLE_SUFFIX': '', 41 'STATIC_LIB_PREFIX': 'lib', 42 'SHARED_LIB_PREFIX': 'lib', 43 'STATIC_LIB_SUFFIX': '.a', 44 'INTERMEDIATE_DIR': '$(obj).$(TOOLSET)/$(TARGET)/geni', 45 'SHARED_INTERMEDIATE_DIR': '$(obj)/gen', 46 'PRODUCT_DIR': '$(builddir)', 47 'RULE_INPUT_ROOT': '%(INPUT_ROOT)s', # This gets expanded by Python. 48 'RULE_INPUT_DIRNAME': '%(INPUT_DIRNAME)s', # This gets expanded by Python. 49 'RULE_INPUT_PATH': '$(abspath $<)', 50 'RULE_INPUT_EXT': '$(suffix $<)', 51 'RULE_INPUT_NAME': '$(notdir $<)', 52 'CONFIGURATION_NAME': '$(BUILDTYPE)', 53} 54 55# Make supports multiple toolsets 56generator_supports_multiple_toolsets = True 57 58# Request sorted dependencies in the order from dependents to dependencies. 59generator_wants_sorted_dependencies = False 60 61# Placates pylint. 62generator_additional_non_configuration_keys = [] 63generator_additional_path_sections = [] 64generator_extra_sources_for_rules = [] 65generator_filelist_paths = None 66 67 68def CalculateVariables(default_variables, params): 69 """Calculate additional variables for use in the build (called by gyp).""" 70 flavor = gyp.common.GetFlavor(params) 71 if flavor == 'mac': 72 default_variables.setdefault('OS', 'mac') 73 default_variables.setdefault('SHARED_LIB_SUFFIX', '.dylib') 74 default_variables.setdefault('SHARED_LIB_DIR', 75 generator_default_variables['PRODUCT_DIR']) 76 default_variables.setdefault('LIB_DIR', 77 generator_default_variables['PRODUCT_DIR']) 78 79 # Copy additional generator configuration data from Xcode, which is shared 80 # by the Mac Make generator. 81 import gyp.generator.xcode as xcode_generator 82 global generator_additional_non_configuration_keys 83 generator_additional_non_configuration_keys = getattr(xcode_generator, 84 'generator_additional_non_configuration_keys', []) 85 global generator_additional_path_sections 86 generator_additional_path_sections = getattr(xcode_generator, 87 'generator_additional_path_sections', []) 88 global generator_extra_sources_for_rules 89 generator_extra_sources_for_rules = getattr(xcode_generator, 90 'generator_extra_sources_for_rules', []) 91 COMPILABLE_EXTENSIONS.update({'.m': 'objc', '.mm' : 'objcxx'}) 92 else: 93 operating_system = flavor 94 if flavor == 'android': 95 operating_system = 'linux' # Keep this legacy behavior for now. 96 default_variables.setdefault('OS', operating_system) 97 if flavor == 'aix': 98 default_variables.setdefault('SHARED_LIB_SUFFIX', '.a') 99 else: 100 default_variables.setdefault('SHARED_LIB_SUFFIX', '.so') 101 default_variables.setdefault('SHARED_LIB_DIR','$(builddir)/lib.$(TOOLSET)') 102 default_variables.setdefault('LIB_DIR', '$(obj).$(TOOLSET)') 103 104 105def CalculateGeneratorInputInfo(params): 106 """Calculate the generator specific info that gets fed to input (called by 107 gyp).""" 108 generator_flags = params.get('generator_flags', {}) 109 android_ndk_version = generator_flags.get('android_ndk_version', None) 110 # Android NDK requires a strict link order. 111 if android_ndk_version: 112 global generator_wants_sorted_dependencies 113 generator_wants_sorted_dependencies = True 114 115 output_dir = params['options'].generator_output or \ 116 params['options'].toplevel_dir 117 builddir_name = generator_flags.get('output_dir', 'out') 118 qualified_out_dir = os.path.normpath(os.path.join( 119 output_dir, builddir_name, 'gypfiles')) 120 121 global generator_filelist_paths 122 generator_filelist_paths = { 123 'toplevel': params['options'].toplevel_dir, 124 'qualified_out_dir': qualified_out_dir, 125 } 126 127 128# The .d checking code below uses these functions: 129# wildcard, sort, foreach, shell, wordlist 130# wildcard can handle spaces, the rest can't. 131# Since I could find no way to make foreach work with spaces in filenames 132# correctly, the .d files have spaces replaced with another character. The .d 133# file for 134# Chromium\ Framework.framework/foo 135# is for example 136# out/Release/.deps/out/Release/Chromium?Framework.framework/foo 137# This is the replacement character. 138SPACE_REPLACEMENT = '?' 139 140 141LINK_COMMANDS_LINUX = """\ 142quiet_cmd_alink = AR($(TOOLSET)) $@ 143cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) crs $@ $(filter %.o,$^) 144 145quiet_cmd_alink_thin = AR($(TOOLSET)) $@ 146cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) crsT $@ $(filter %.o,$^) 147 148# Due to circular dependencies between libraries :(, we wrap the 149# special "figure out circular dependencies" flags around the entire 150# input list during linking. 151quiet_cmd_link = LINK($(TOOLSET)) $@ 152cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ -Wl,--start-group $(LD_INPUTS) $(LIBS) -Wl,--end-group 153 154# We support two kinds of shared objects (.so): 155# 1) shared_library, which is just bundling together many dependent libraries 156# into a link line. 157# 2) loadable_module, which is generating a module intended for dlopen(). 158# 159# They differ only slightly: 160# In the former case, we want to package all dependent code into the .so. 161# In the latter case, we want to package just the API exposed by the 162# outermost module. 163# This means shared_library uses --whole-archive, while loadable_module doesn't. 164# (Note that --whole-archive is incompatible with the --start-group used in 165# normal linking.) 166 167# Other shared-object link notes: 168# - Set SONAME to the library filename so our binaries don't reference 169# the local, absolute paths used on the link command-line. 170quiet_cmd_solink = SOLINK($(TOOLSET)) $@ 171cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS) 172 173quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ 174cmd_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) 175""" 176 177LINK_COMMANDS_MAC = """\ 178quiet_cmd_alink = LIBTOOL-STATIC $@ 179cmd_alink = rm -f $@ && ./gyp-mac-tool filter-libtool libtool $(GYP_LIBTOOLFLAGS) -static -o $@ $(filter %.o,$^) 180 181quiet_cmd_link = LINK($(TOOLSET)) $@ 182cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) 183 184quiet_cmd_solink = SOLINK($(TOOLSET)) $@ 185cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) 186 187quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ 188cmd_solink_module = $(LINK.$(TOOLSET)) -bundle $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) 189""" 190 191LINK_COMMANDS_ANDROID = """\ 192quiet_cmd_alink = AR($(TOOLSET)) $@ 193cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) crs $@ $(filter %.o,$^) 194 195quiet_cmd_alink_thin = AR($(TOOLSET)) $@ 196cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) crsT $@ $(filter %.o,$^) 197 198# Due to circular dependencies between libraries :(, we wrap the 199# special "figure out circular dependencies" flags around the entire 200# input list during linking. 201quiet_cmd_link = LINK($(TOOLSET)) $@ 202quiet_cmd_link_host = LINK($(TOOLSET)) $@ 203cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ -Wl,--start-group $(LD_INPUTS) -Wl,--end-group $(LIBS) 204cmd_link_host = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS) 205 206# Other shared-object link notes: 207# - Set SONAME to the library filename so our binaries don't reference 208# the local, absolute paths used on the link command-line. 209quiet_cmd_solink = SOLINK($(TOOLSET)) $@ 210cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS) 211 212quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ 213cmd_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) 214quiet_cmd_solink_module_host = SOLINK_MODULE($(TOOLSET)) $@ 215cmd_solink_module_host = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) 216""" 217 218 219LINK_COMMANDS_AIX = """\ 220quiet_cmd_alink = AR($(TOOLSET)) $@ 221cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) -X32_64 crs $@ $(filter %.o,$^) 222 223quiet_cmd_alink_thin = AR($(TOOLSET)) $@ 224cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) -X32_64 crs $@ $(filter %.o,$^) 225 226quiet_cmd_link = LINK($(TOOLSET)) $@ 227cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS) 228 229quiet_cmd_solink = SOLINK($(TOOLSET)) $@ 230cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS) 231 232quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ 233cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) 234""" 235 236 237LINK_COMMANDS_OS390 = """\ 238quiet_cmd_alink = AR($(TOOLSET)) $@ 239cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) crs $@ $(filter %.o,$^) 240 241quiet_cmd_alink_thin = AR($(TOOLSET)) $@ 242cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) crsT $@ $(filter %.o,$^) 243 244quiet_cmd_link = LINK($(TOOLSET)) $@ 245cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS) 246 247quiet_cmd_solink = SOLINK($(TOOLSET)) $@ 248cmd_solink = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS) -Wl,DLL 249 250quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ 251cmd_solink_module = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) -Wl,DLL 252 253""" 254 255 256# Header of toplevel Makefile. 257# This should go into the build tree, but it's easier to keep it here for now. 258SHARED_HEADER = ("""\ 259# We borrow heavily from the kernel build setup, though we are simpler since 260# we don't have Kconfig tweaking settings on us. 261 262# The implicit make rules have it looking for RCS files, among other things. 263# We instead explicitly write all the rules we care about. 264# It's even quicker (saves ~200ms) to pass -r on the command line. 265MAKEFLAGS=-r 266 267# The source directory tree. 268srcdir := %(srcdir)s 269abs_srcdir := $(abspath $(srcdir)) 270 271# The name of the builddir. 272builddir_name ?= %(builddir)s 273 274# The V=1 flag on command line makes us verbosely print command lines. 275ifdef V 276 quiet= 277else 278 quiet=quiet_ 279endif 280 281# Specify BUILDTYPE=Release on the command line for a release build. 282BUILDTYPE ?= %(default_configuration)s 283 284# Directory all our build output goes into. 285# Note that this must be two directories beneath src/ for unit tests to pass, 286# as they reach into the src/ directory for data with relative paths. 287builddir ?= $(builddir_name)/$(BUILDTYPE) 288abs_builddir := $(abspath $(builddir)) 289depsdir := $(builddir)/.deps 290 291# Object output directory. 292obj := $(builddir)/obj 293abs_obj := $(abspath $(obj)) 294 295# We build up a list of every single one of the targets so we can slurp in the 296# generated dependency rule Makefiles in one pass. 297all_deps := 298 299%(make_global_settings)s 300 301CC.target ?= %(CC.target)s 302CFLAGS.target ?= $(CPPFLAGS) $(CFLAGS) 303CXX.target ?= %(CXX.target)s 304CXXFLAGS.target ?= $(CPPFLAGS) $(CXXFLAGS) 305LINK.target ?= %(LINK.target)s 306LDFLAGS.target ?= $(LDFLAGS) 307AR.target ?= $(AR) 308 309# C++ apps need to be linked with g++. 310LINK ?= $(CXX.target) 311 312# TODO(evan): move all cross-compilation logic to gyp-time so we don't need 313# to replicate this environment fallback in make as well. 314CC.host ?= %(CC.host)s 315CFLAGS.host ?= $(CPPFLAGS_host) $(CFLAGS_host) 316CXX.host ?= %(CXX.host)s 317CXXFLAGS.host ?= $(CPPFLAGS_host) $(CXXFLAGS_host) 318LINK.host ?= %(LINK.host)s 319LDFLAGS.host ?= 320AR.host ?= %(AR.host)s 321 322# Define a dir function that can handle spaces. 323# http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions 324# "leading spaces cannot appear in the text of the first argument as written. 325# These characters can be put into the argument value by variable substitution." 326empty := 327space := $(empty) $(empty) 328 329# http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces 330replace_spaces = $(subst $(space),""" + SPACE_REPLACEMENT + """,$1) 331unreplace_spaces = $(subst """ + SPACE_REPLACEMENT + """,$(space),$1) 332dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1))) 333 334# Flags to make gcc output dependency info. Note that you need to be 335# careful here to use the flags that ccache and distcc can understand. 336# We write to a dep file on the side first and then rename at the end 337# so we can't end up with a broken dep file. 338depfile = $(depsdir)/$(call replace_spaces,$@).d 339DEPFLAGS = %(makedep_args)s -MF $(depfile).raw 340 341# We have to fixup the deps output in a few ways. 342# (1) the file output should mention the proper .o file. 343# ccache or distcc lose the path to the target, so we convert a rule of 344# the form: 345# foobar.o: DEP1 DEP2 346# into 347# path/to/foobar.o: DEP1 DEP2 348# (2) we want missing files not to cause us to fail to build. 349# We want to rewrite 350# foobar.o: DEP1 DEP2 \\ 351# DEP3 352# to 353# DEP1: 354# DEP2: 355# DEP3: 356# so if the files are missing, they're just considered phony rules. 357# We have to do some pretty insane escaping to get those backslashes 358# and dollar signs past make, the shell, and sed at the same time. 359# Doesn't work with spaces, but that's fine: .d files have spaces in 360# their names replaced with other characters.""" 361r""" 362define fixup_dep 363# The depfile may not exist if the input file didn't have any #includes. 364touch $(depfile).raw 365# Fixup path as in (1). 366sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile) 367# Add extra rules as in (2). 368# We remove slashes and replace spaces with new lines; 369# remove blank lines; 370# delete the first line and append a colon to the remaining lines. 371sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ 372 grep -v '^$$' |\ 373 sed -e 1d -e 's|$$|:|' \ 374 >> $(depfile) 375rm $(depfile).raw 376endef 377""" 378""" 379# Command definitions: 380# - cmd_foo is the actual command to run; 381# - quiet_cmd_foo is the brief-output summary of the command. 382 383quiet_cmd_cc = CC($(TOOLSET)) $@ 384cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< 385 386quiet_cmd_cxx = CXX($(TOOLSET)) $@ 387cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< 388%(extra_commands)s 389quiet_cmd_touch = TOUCH $@ 390cmd_touch = touch $@ 391 392quiet_cmd_copy = COPY $@ 393# send stderr to /dev/null to ignore messages when linking directories. 394cmd_copy = rm -rf "$@" && cp %(copy_archive_args)s "$<" "$@" 395 396%(link_commands)s 397""" 398 399r""" 400# Define an escape_quotes function to escape single quotes. 401# This allows us to handle quotes properly as long as we always use 402# use single quotes and escape_quotes. 403escape_quotes = $(subst ','\'',$(1)) 404# This comment is here just to include a ' to unconfuse syntax highlighting. 405# Define an escape_vars function to escape '$' variable syntax. 406# This allows us to read/write command lines with shell variables (e.g. 407# $LD_LIBRARY_PATH), without triggering make substitution. 408escape_vars = $(subst $$,$$$$,$(1)) 409# Helper that expands to a shell command to echo a string exactly as it is in 410# make. This uses printf instead of echo because printf's behaviour with respect 411# to escape sequences is more portable than echo's across different shells 412# (e.g., dash, bash). 413exact_echo = printf '%%s\n' '$(call escape_quotes,$(1))' 414""" 415""" 416# Helper to compare the command we're about to run against the command 417# we logged the last time we ran the command. Produces an empty 418# string (false) when the commands match. 419# Tricky point: Make has no string-equality test function. 420# The kernel uses the following, but it seems like it would have false 421# positives, where one string reordered its arguments. 422# arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \\ 423# $(filter-out $(cmd_$@), $(cmd_$(1)))) 424# We instead substitute each for the empty string into the other, and 425# say they're equal if both substitutions produce the empty string. 426# .d files contain """ + SPACE_REPLACEMENT + \ 427 """ instead of spaces, take that into account. 428command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\\ 429 $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) 430 431# Helper that is non-empty when a prerequisite changes. 432# Normally make does this implicitly, but we force rules to always run 433# so we can check their command lines. 434# $? -- new prerequisites 435# $| -- order-only dependencies 436prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) 437 438# Helper that executes all postbuilds until one fails. 439define do_postbuilds 440 @E=0;\\ 441 for p in $(POSTBUILDS); do\\ 442 eval $$p;\\ 443 E=$$?;\\ 444 if [ $$E -ne 0 ]; then\\ 445 break;\\ 446 fi;\\ 447 done;\\ 448 if [ $$E -ne 0 ]; then\\ 449 rm -rf "$@";\\ 450 exit $$E;\\ 451 fi 452endef 453 454# do_cmd: run a command via the above cmd_foo names, if necessary. 455# Should always run for a given target to handle command-line changes. 456# Second argument, if non-zero, makes it do asm/C/C++ dependency munging. 457# Third argument, if non-zero, makes it do POSTBUILDS processing. 458# Note: We intentionally do NOT call dirx for depfile, since it contains """ + \ 459 SPACE_REPLACEMENT + """ for 460# spaces already and dirx strips the """ + SPACE_REPLACEMENT + \ 461 """ characters. 462define do_cmd 463$(if $(or $(command_changed),$(prereq_changed)), 464 @$(call exact_echo, $($(quiet)cmd_$(1))) 465 @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" 466 $(if $(findstring flock,$(word %(flock_index)d,$(cmd_$1))), 467 @$(cmd_$(1)) 468 @echo " $(quiet_cmd_$(1)): Finished", 469 @$(cmd_$(1)) 470 ) 471 @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) 472 @$(if $(2),$(fixup_dep)) 473 $(if $(and $(3), $(POSTBUILDS)), 474 $(call do_postbuilds) 475 ) 476) 477endef 478 479# Declare the "%(default_target)s" target first so it is the default, 480# even though we don't have the deps yet. 481.PHONY: %(default_target)s 482%(default_target)s: 483 484# make looks for ways to re-generate included makefiles, but in our case, we 485# don't have a direct way. Explicitly telling make that it has nothing to do 486# for them makes it go faster. 487%%.d: ; 488 489# Use FORCE_DO_CMD to force a target to run. Should be coupled with 490# do_cmd. 491.PHONY: FORCE_DO_CMD 492FORCE_DO_CMD: 493 494""") 495 496SHARED_HEADER_MAC_COMMANDS = """ 497quiet_cmd_objc = CXX($(TOOLSET)) $@ 498cmd_objc = $(CC.$(TOOLSET)) $(GYP_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< 499 500quiet_cmd_objcxx = CXX($(TOOLSET)) $@ 501cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< 502 503# Commands for precompiled header files. 504quiet_cmd_pch_c = CXX($(TOOLSET)) $@ 505cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< 506quiet_cmd_pch_cc = CXX($(TOOLSET)) $@ 507cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< 508quiet_cmd_pch_m = CXX($(TOOLSET)) $@ 509cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< 510quiet_cmd_pch_mm = CXX($(TOOLSET)) $@ 511cmd_pch_mm = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< 512 513# gyp-mac-tool is written next to the root Makefile by gyp. 514# Use $(4) for the command, since $(2) and $(3) are used as flag by do_cmd 515# already. 516quiet_cmd_mac_tool = MACTOOL $(4) $< 517cmd_mac_tool = ./gyp-mac-tool $(4) $< "$@" 518 519quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@ 520cmd_mac_package_framework = ./gyp-mac-tool package-framework "$@" $(4) 521 522quiet_cmd_infoplist = INFOPLIST $@ 523cmd_infoplist = $(CC.$(TOOLSET)) -E -P -Wno-trigraphs -x c $(INFOPLIST_DEFINES) "$<" -o "$@" 524""" 525 526 527def WriteRootHeaderSuffixRules(writer): 528 extensions = sorted(COMPILABLE_EXTENSIONS.keys(), key=str.lower) 529 530 writer.write('# Suffix rules, putting all outputs into $(obj).\n') 531 for ext in extensions: 532 writer.write('$(obj).$(TOOLSET)/%%.o: $(srcdir)/%%%s FORCE_DO_CMD\n' % ext) 533 writer.write('\t@$(call do_cmd,%s,1)\n' % COMPILABLE_EXTENSIONS[ext]) 534 535 writer.write('\n# Try building from generated source, too.\n') 536 for ext in extensions: 537 writer.write( 538 '$(obj).$(TOOLSET)/%%.o: $(obj).$(TOOLSET)/%%%s FORCE_DO_CMD\n' % ext) 539 writer.write('\t@$(call do_cmd,%s,1)\n' % COMPILABLE_EXTENSIONS[ext]) 540 writer.write('\n') 541 for ext in extensions: 542 writer.write('$(obj).$(TOOLSET)/%%.o: $(obj)/%%%s FORCE_DO_CMD\n' % ext) 543 writer.write('\t@$(call do_cmd,%s,1)\n' % COMPILABLE_EXTENSIONS[ext]) 544 writer.write('\n') 545 546 547SHARED_HEADER_SUFFIX_RULES_COMMENT1 = ("""\ 548# Suffix rules, putting all outputs into $(obj). 549""") 550 551 552SHARED_HEADER_SUFFIX_RULES_COMMENT2 = ("""\ 553# Try building from generated source, too. 554""") 555 556 557SHARED_FOOTER = """\ 558# "all" is a concatenation of the "all" targets from all the included 559# sub-makefiles. This is just here to clarify. 560all: 561 562# Add in dependency-tracking rules. $(all_deps) is the list of every single 563# target in our tree. Only consider the ones with .d (dependency) info: 564d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) 565ifneq ($(d_files),) 566 include $(d_files) 567endif 568""" 569 570header = """\ 571# This file is generated by gyp; do not edit. 572 573""" 574 575# Maps every compilable file extension to the do_cmd that compiles it. 576COMPILABLE_EXTENSIONS = { 577 '.c': 'cc', 578 '.cc': 'cxx', 579 '.cpp': 'cxx', 580 '.cxx': 'cxx', 581 '.s': 'cc', 582 '.S': 'cc', 583} 584 585def Compilable(filename): 586 """Return true if the file is compilable (should be in OBJS).""" 587 for res in (filename.endswith(e) for e in COMPILABLE_EXTENSIONS): 588 if res: 589 return True 590 return False 591 592 593def Linkable(filename): 594 """Return true if the file is linkable (should be on the link line).""" 595 return filename.endswith('.o') 596 597 598def Target(filename): 599 """Translate a compilable filename to its .o target.""" 600 return os.path.splitext(filename)[0] + '.o' 601 602 603def EscapeShellArgument(s): 604 """Quotes an argument so that it will be interpreted literally by a POSIX 605 shell. Taken from 606 http://stackoverflow.com/questions/35817/whats-the-best-way-to-escape-ossystem-calls-in-python 607 """ 608 return "'" + s.replace("'", "'\\''") + "'" 609 610 611def EscapeMakeVariableExpansion(s): 612 """Make has its own variable expansion syntax using $. We must escape it for 613 string to be interpreted literally.""" 614 return s.replace('$', '$$') 615 616 617def EscapeCppDefine(s): 618 """Escapes a CPP define so that it will reach the compiler unaltered.""" 619 s = EscapeShellArgument(s) 620 s = EscapeMakeVariableExpansion(s) 621 # '#' characters must be escaped even embedded in a string, else Make will 622 # treat it as the start of a comment. 623 return s.replace('#', r'\#') 624 625 626def QuoteIfNecessary(string): 627 """TODO: Should this ideally be replaced with one or more of the above 628 functions?""" 629 if '"' in string: 630 string = '"' + string.replace('"', '\\"') + '"' 631 return string 632 633 634def StringToMakefileVariable(string): 635 """Convert a string to a value that is acceptable as a make variable name.""" 636 return re.sub('[^a-zA-Z0-9_]', '_', string) 637 638 639srcdir_prefix = '' 640def Sourceify(path): 641 """Convert a path to its source directory form.""" 642 if '$(' in path: 643 return path 644 if os.path.isabs(path): 645 return path 646 return srcdir_prefix + path 647 648 649def QuoteSpaces(s, quote=r'\ '): 650 return s.replace(' ', quote) 651 652def SourceifyAndQuoteSpaces(path): 653 """Convert a path to its source directory form and quote spaces.""" 654 return QuoteSpaces(Sourceify(path)) 655 656# TODO: Avoid code duplication with _ValidateSourcesForMSVSProject in msvs.py. 657def _ValidateSourcesForOSX(spec, all_sources): 658 """Makes sure if duplicate basenames are not specified in the source list. 659 660 Arguments: 661 spec: The target dictionary containing the properties of the target. 662 """ 663 if spec.get('type', None) != 'static_library': 664 return 665 666 basenames = {} 667 for source in all_sources: 668 name, ext = os.path.splitext(source) 669 is_compiled_file = ext in [ 670 '.c', '.cc', '.cpp', '.cxx', '.m', '.mm', '.s', '.S'] 671 if not is_compiled_file: 672 continue 673 basename = os.path.basename(name) # Don't include extension. 674 basenames.setdefault(basename, []).append(source) 675 676 error = '' 677 for basename, files in basenames.items(): 678 if len(files) > 1: 679 error += ' %s: %s\n' % (basename, ' '.join(files)) 680 681 if error: 682 print(('static library %s has several files with the same basename:\n' % spec['target_name']) 683 + error + 'libtool on OS X will generate' + ' warnings for them.') 684 raise GypError('Duplicate basenames in sources section, see list above') 685 686 687# Map from qualified target to path to output. 688target_outputs = {} 689# Map from qualified target to any linkable output. A subset 690# of target_outputs. E.g. when mybinary depends on liba, we want to 691# include liba in the linker line; when otherbinary depends on 692# mybinary, we just want to build mybinary first. 693target_link_deps = {} 694 695 696class MakefileWriter(object): 697 """MakefileWriter packages up the writing of one target-specific foobar.mk. 698 699 Its only real entry point is Write(), and is mostly used for namespacing. 700 """ 701 702 def __init__(self, generator_flags, flavor): 703 self.generator_flags = generator_flags 704 self.flavor = flavor 705 706 self.suffix_rules_srcdir = {} 707 self.suffix_rules_objdir1 = {} 708 self.suffix_rules_objdir2 = {} 709 710 # Generate suffix rules for all compilable extensions. 711 for ext in COMPILABLE_EXTENSIONS.keys(): 712 # Suffix rules for source folder. 713 self.suffix_rules_srcdir.update({ext: ("""\ 714$(obj).$(TOOLSET)/$(TARGET)/%%.o: $(srcdir)/%%%s FORCE_DO_CMD 715 @$(call do_cmd,%s,1) 716""" % (ext, COMPILABLE_EXTENSIONS[ext]))}) 717 718 # Suffix rules for generated source files. 719 self.suffix_rules_objdir1.update({ext: ("""\ 720$(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj).$(TOOLSET)/%%%s FORCE_DO_CMD 721 @$(call do_cmd,%s,1) 722""" % (ext, COMPILABLE_EXTENSIONS[ext]))}) 723 self.suffix_rules_objdir2.update({ext: ("""\ 724$(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD 725 @$(call do_cmd,%s,1) 726""" % (ext, COMPILABLE_EXTENSIONS[ext]))}) 727 728 729 def Write(self, qualified_target, base_path, output_filename, spec, configs, 730 part_of_all): 731 """The main entry point: writes a .mk file for a single target. 732 733 Arguments: 734 qualified_target: target we're generating 735 base_path: path relative to source root we're building in, used to resolve 736 target-relative paths 737 output_filename: output .mk file name to write 738 spec, configs: gyp info 739 part_of_all: flag indicating this target is part of 'all' 740 """ 741 gyp.common.EnsureDirExists(output_filename) 742 743 self.fp = open(output_filename, 'w') 744 745 self.fp.write(header) 746 747 self.qualified_target = qualified_target 748 self.path = base_path 749 self.target = spec['target_name'] 750 self.type = spec['type'] 751 self.toolset = spec['toolset'] 752 753 self.is_mac_bundle = gyp.xcode_emulation.IsMacBundle(self.flavor, spec) 754 if self.flavor == 'mac': 755 self.xcode_settings = gyp.xcode_emulation.XcodeSettings(spec) 756 else: 757 self.xcode_settings = None 758 759 deps, link_deps = self.ComputeDeps(spec) 760 761 # Some of the generation below can add extra output, sources, or 762 # link dependencies. All of the out params of the functions that 763 # follow use names like extra_foo. 764 extra_outputs = [] 765 extra_sources = [] 766 extra_link_deps = [] 767 extra_mac_bundle_resources = [] 768 mac_bundle_deps = [] 769 770 if self.is_mac_bundle: 771 self.output = self.ComputeMacBundleOutput(spec) 772 self.output_binary = self.ComputeMacBundleBinaryOutput(spec) 773 else: 774 self.output = self.output_binary = self.ComputeOutput(spec) 775 776 self.is_standalone_static_library = bool( 777 spec.get('standalone_static_library', 0)) 778 self._INSTALLABLE_TARGETS = ('executable', 'loadable_module', 779 'shared_library') 780 if (self.is_standalone_static_library or 781 self.type in self._INSTALLABLE_TARGETS): 782 self.alias = os.path.basename(self.output) 783 install_path = self._InstallableTargetInstallPath() 784 else: 785 self.alias = self.output 786 install_path = self.output 787 788 self.WriteLn("TOOLSET := " + self.toolset) 789 self.WriteLn("TARGET := " + self.target) 790 791 # Actions must come first, since they can generate more OBJs for use below. 792 if 'actions' in spec: 793 self.WriteActions(spec['actions'], extra_sources, extra_outputs, 794 extra_mac_bundle_resources, part_of_all) 795 796 # Rules must be early like actions. 797 if 'rules' in spec: 798 self.WriteRules(spec['rules'], extra_sources, extra_outputs, 799 extra_mac_bundle_resources, part_of_all) 800 801 if 'copies' in spec: 802 self.WriteCopies(spec['copies'], extra_outputs, part_of_all) 803 804 # Bundle resources. 805 if self.is_mac_bundle: 806 all_mac_bundle_resources = ( 807 spec.get('mac_bundle_resources', []) + extra_mac_bundle_resources) 808 self.WriteMacBundleResources(all_mac_bundle_resources, mac_bundle_deps) 809 self.WriteMacInfoPlist(mac_bundle_deps) 810 811 # Sources. 812 all_sources = spec.get('sources', []) + extra_sources 813 if all_sources: 814 if self.flavor == 'mac': 815 # libtool on OS X generates warnings for duplicate basenames in the same 816 # target. 817 _ValidateSourcesForOSX(spec, all_sources) 818 self.WriteSources( 819 configs, deps, all_sources, extra_outputs, 820 extra_link_deps, part_of_all, 821 gyp.xcode_emulation.MacPrefixHeader( 822 self.xcode_settings, lambda p: Sourceify(self.Absolutify(p)), 823 self.Pchify)) 824 sources = list(filter(Compilable, all_sources)) 825 if sources: 826 self.WriteLn(SHARED_HEADER_SUFFIX_RULES_COMMENT1) 827 extensions = set([os.path.splitext(s)[1] for s in sources]) 828 for ext in extensions: 829 if ext in self.suffix_rules_srcdir: 830 self.WriteLn(self.suffix_rules_srcdir[ext]) 831 self.WriteLn(SHARED_HEADER_SUFFIX_RULES_COMMENT2) 832 for ext in extensions: 833 if ext in self.suffix_rules_objdir1: 834 self.WriteLn(self.suffix_rules_objdir1[ext]) 835 for ext in extensions: 836 if ext in self.suffix_rules_objdir2: 837 self.WriteLn(self.suffix_rules_objdir2[ext]) 838 self.WriteLn('# End of this set of suffix rules') 839 840 # Add dependency from bundle to bundle binary. 841 if self.is_mac_bundle: 842 mac_bundle_deps.append(self.output_binary) 843 844 self.WriteTarget(spec, configs, deps, extra_link_deps + link_deps, 845 mac_bundle_deps, extra_outputs, part_of_all) 846 847 # Update global list of target outputs, used in dependency tracking. 848 target_outputs[qualified_target] = install_path 849 850 # Update global list of link dependencies. 851 if self.type in ('static_library', 'shared_library'): 852 target_link_deps[qualified_target] = self.output_binary 853 854 # Currently any versions have the same effect, but in future the behavior 855 # could be different. 856 if self.generator_flags.get('android_ndk_version', None): 857 self.WriteAndroidNdkModuleRule(self.target, all_sources, link_deps) 858 859 self.fp.close() 860 861 862 def WriteSubMake(self, output_filename, makefile_path, targets, build_dir): 863 """Write a "sub-project" Makefile. 864 865 This is a small, wrapper Makefile that calls the top-level Makefile to build 866 the targets from a single gyp file (i.e. a sub-project). 867 868 Arguments: 869 output_filename: sub-project Makefile name to write 870 makefile_path: path to the top-level Makefile 871 targets: list of "all" targets for this sub-project 872 build_dir: build output directory, relative to the sub-project 873 """ 874 gyp.common.EnsureDirExists(output_filename) 875 self.fp = open(output_filename, 'w') 876 self.fp.write(header) 877 # For consistency with other builders, put sub-project build output in the 878 # sub-project dir (see test/subdirectory/gyptest-subdir-all.py). 879 self.WriteLn('export builddir_name ?= %s' % 880 os.path.join(os.path.dirname(output_filename), build_dir)) 881 self.WriteLn('.PHONY: all') 882 self.WriteLn('all:') 883 if makefile_path: 884 makefile_path = ' -C ' + makefile_path 885 self.WriteLn('\t$(MAKE)%s %s' % (makefile_path, ' '.join(targets))) 886 self.fp.close() 887 888 889 def WriteActions(self, actions, extra_sources, extra_outputs, 890 extra_mac_bundle_resources, part_of_all): 891 """Write Makefile code for any 'actions' from the gyp input. 892 893 extra_sources: a list that will be filled in with newly generated source 894 files, if any 895 extra_outputs: a list that will be filled in with any outputs of these 896 actions (used to make other pieces dependent on these 897 actions) 898 part_of_all: flag indicating this target is part of 'all' 899 """ 900 env = self.GetSortedXcodeEnv() 901 for action in actions: 902 name = StringToMakefileVariable('%s_%s' % (self.qualified_target, 903 action['action_name'])) 904 self.WriteLn('### Rules for action "%s":' % action['action_name']) 905 inputs = action['inputs'] 906 outputs = action['outputs'] 907 908 # Build up a list of outputs. 909 # Collect the output dirs we'll need. 910 dirs = set() 911 for out in outputs: 912 dir = os.path.split(out)[0] 913 if dir: 914 dirs.add(dir) 915 if int(action.get('process_outputs_as_sources', False)): 916 extra_sources += outputs 917 if int(action.get('process_outputs_as_mac_bundle_resources', False)): 918 extra_mac_bundle_resources += outputs 919 920 # Write the actual command. 921 action_commands = action['action'] 922 if self.flavor == 'mac': 923 action_commands = [gyp.xcode_emulation.ExpandEnvVars(command, env) 924 for command in action_commands] 925 command = gyp.common.EncodePOSIXShellList(action_commands) 926 if 'message' in action: 927 self.WriteLn('quiet_cmd_%s = ACTION %s $@' % (name, action['message'])) 928 else: 929 self.WriteLn('quiet_cmd_%s = ACTION %s $@' % (name, name)) 930 if len(dirs) > 0: 931 command = 'mkdir -p %s' % ' '.join(dirs) + '; ' + command 932 933 cd_action = 'cd %s; ' % Sourceify(self.path or '.') 934 935 # command and cd_action get written to a toplevel variable called 936 # cmd_foo. Toplevel variables can't handle things that change per 937 # makefile like $(TARGET), so hardcode the target. 938 command = command.replace('$(TARGET)', self.target) 939 cd_action = cd_action.replace('$(TARGET)', self.target) 940 941 # Set LD_LIBRARY_PATH in case the action runs an executable from this 942 # build which links to shared libs from this build. 943 # actions run on the host, so they should in theory only use host 944 # libraries, but until everything is made cross-compile safe, also use 945 # target libraries. 946 # TODO(piman): when everything is cross-compile safe, remove lib.target 947 self.WriteLn('cmd_%s = LD_LIBRARY_PATH=$(builddir)/lib.host:' 948 '$(builddir)/lib.target:$$LD_LIBRARY_PATH; ' 949 'export LD_LIBRARY_PATH; ' 950 '%s%s' 951 % (name, cd_action, command)) 952 self.WriteLn() 953 outputs = [self.Absolutify(output) for output in outputs] 954 # The makefile rules are all relative to the top dir, but the gyp actions 955 # are defined relative to their containing dir. This replaces the obj 956 # variable for the action rule with an absolute version so that the output 957 # goes in the right place. 958 # Only write the 'obj' and 'builddir' rules for the "primary" output (:1); 959 # it's superfluous for the "extra outputs", and this avoids accidentally 960 # writing duplicate dummy rules for those outputs. 961 # Same for environment. 962 self.WriteLn("%s: obj := $(abs_obj)" % QuoteSpaces(outputs[0])) 963 self.WriteLn("%s: builddir := $(abs_builddir)" % QuoteSpaces(outputs[0])) 964 self.WriteSortedXcodeEnv(outputs[0], self.GetSortedXcodeEnv()) 965 966 for input in inputs: 967 assert ' ' not in input, ( 968 "Spaces in action input filenames not supported (%s)" % input) 969 for output in outputs: 970 assert ' ' not in output, ( 971 "Spaces in action output filenames not supported (%s)" % output) 972 973 # See the comment in WriteCopies about expanding env vars. 974 outputs = [gyp.xcode_emulation.ExpandEnvVars(o, env) for o in outputs] 975 inputs = [gyp.xcode_emulation.ExpandEnvVars(i, env) for i in inputs] 976 977 self.WriteDoCmd(outputs, [Sourceify(self.Absolutify(i)) for i in inputs], 978 part_of_all=part_of_all, command=name) 979 980 # Stuff the outputs in a variable so we can refer to them later. 981 outputs_variable = 'action_%s_outputs' % name 982 self.WriteLn('%s := %s' % (outputs_variable, ' '.join(outputs))) 983 extra_outputs.append('$(%s)' % outputs_variable) 984 self.WriteLn() 985 986 self.WriteLn() 987 988 989 def WriteRules(self, rules, extra_sources, extra_outputs, 990 extra_mac_bundle_resources, part_of_all): 991 """Write Makefile code for any 'rules' from the gyp input. 992 993 extra_sources: a list that will be filled in with newly generated source 994 files, if any 995 extra_outputs: a list that will be filled in with any outputs of these 996 rules (used to make other pieces dependent on these rules) 997 part_of_all: flag indicating this target is part of 'all' 998 """ 999 env = self.GetSortedXcodeEnv() 1000 for rule in rules: 1001 name = StringToMakefileVariable('%s_%s' % (self.qualified_target, 1002 rule['rule_name'])) 1003 count = 0 1004 self.WriteLn('### Generated for rule %s:' % name) 1005 1006 all_outputs = [] 1007 1008 for rule_source in rule.get('rule_sources', []): 1009 dirs = set() 1010 (rule_source_dirname, rule_source_basename) = os.path.split(rule_source) 1011 (rule_source_root, rule_source_ext) = \ 1012 os.path.splitext(rule_source_basename) 1013 1014 outputs = [self.ExpandInputRoot(out, rule_source_root, 1015 rule_source_dirname) 1016 for out in rule['outputs']] 1017 1018 for out in outputs: 1019 dir = os.path.dirname(out) 1020 if dir: 1021 dirs.add(dir) 1022 if int(rule.get('process_outputs_as_sources', False)): 1023 extra_sources += outputs 1024 if int(rule.get('process_outputs_as_mac_bundle_resources', False)): 1025 extra_mac_bundle_resources += outputs 1026 inputs = [Sourceify(self.Absolutify(i)) for i 1027 in [rule_source] + rule.get('inputs', [])] 1028 actions = ['$(call do_cmd,%s_%d)' % (name, count)] 1029 1030 if name == 'resources_grit': 1031 # HACK: This is ugly. Grit intentionally doesn't touch the 1032 # timestamp of its output file when the file doesn't change, 1033 # which is fine in hash-based dependency systems like scons 1034 # and forge, but not kosher in the make world. After some 1035 # discussion, hacking around it here seems like the least 1036 # amount of pain. 1037 actions += ['@touch --no-create $@'] 1038 1039 # See the comment in WriteCopies about expanding env vars. 1040 outputs = [gyp.xcode_emulation.ExpandEnvVars(o, env) for o in outputs] 1041 inputs = [gyp.xcode_emulation.ExpandEnvVars(i, env) for i in inputs] 1042 1043 outputs = [self.Absolutify(output) for output in outputs] 1044 all_outputs += outputs 1045 # Only write the 'obj' and 'builddir' rules for the "primary" output 1046 # (:1); it's superfluous for the "extra outputs", and this avoids 1047 # accidentally writing duplicate dummy rules for those outputs. 1048 self.WriteLn('%s: obj := $(abs_obj)' % outputs[0]) 1049 self.WriteLn('%s: builddir := $(abs_builddir)' % outputs[0]) 1050 self.WriteMakeRule(outputs, inputs, actions, 1051 command="%s_%d" % (name, count)) 1052 # Spaces in rule filenames are not supported, but rule variables have 1053 # spaces in them (e.g. RULE_INPUT_PATH expands to '$(abspath $<)'). 1054 # The spaces within the variables are valid, so remove the variables 1055 # before checking. 1056 variables_with_spaces = re.compile(r'\$\([^ ]* \$<\)') 1057 for output in outputs: 1058 output = re.sub(variables_with_spaces, '', output) 1059 assert ' ' not in output, ( 1060 "Spaces in rule filenames not yet supported (%s)" % output) 1061 self.WriteLn('all_deps += %s' % ' '.join(outputs)) 1062 1063 action = [self.ExpandInputRoot(ac, rule_source_root, 1064 rule_source_dirname) 1065 for ac in rule['action']] 1066 mkdirs = '' 1067 if len(dirs) > 0: 1068 mkdirs = 'mkdir -p %s; ' % ' '.join(dirs) 1069 cd_action = 'cd %s; ' % Sourceify(self.path or '.') 1070 1071 # action, cd_action, and mkdirs get written to a toplevel variable 1072 # called cmd_foo. Toplevel variables can't handle things that change 1073 # per makefile like $(TARGET), so hardcode the target. 1074 if self.flavor == 'mac': 1075 action = [gyp.xcode_emulation.ExpandEnvVars(command, env) 1076 for command in action] 1077 action = gyp.common.EncodePOSIXShellList(action) 1078 action = action.replace('$(TARGET)', self.target) 1079 cd_action = cd_action.replace('$(TARGET)', self.target) 1080 mkdirs = mkdirs.replace('$(TARGET)', self.target) 1081 1082 # Set LD_LIBRARY_PATH in case the rule runs an executable from this 1083 # build which links to shared libs from this build. 1084 # rules run on the host, so they should in theory only use host 1085 # libraries, but until everything is made cross-compile safe, also use 1086 # target libraries. 1087 # TODO(piman): when everything is cross-compile safe, remove lib.target 1088 self.WriteLn( 1089 "cmd_%(name)s_%(count)d = LD_LIBRARY_PATH=" 1090 "$(builddir)/lib.host:$(builddir)/lib.target:$$LD_LIBRARY_PATH; " 1091 "export LD_LIBRARY_PATH; " 1092 "%(cd_action)s%(mkdirs)s%(action)s" % { 1093 'action': action, 1094 'cd_action': cd_action, 1095 'count': count, 1096 'mkdirs': mkdirs, 1097 'name': name, 1098 }) 1099 self.WriteLn( 1100 'quiet_cmd_%(name)s_%(count)d = RULE %(name)s_%(count)d $@' % { 1101 'count': count, 1102 'name': name, 1103 }) 1104 self.WriteLn() 1105 count += 1 1106 1107 outputs_variable = 'rule_%s_outputs' % name 1108 self.WriteList(all_outputs, outputs_variable) 1109 extra_outputs.append('$(%s)' % outputs_variable) 1110 1111 self.WriteLn('### Finished generating for rule: %s' % name) 1112 self.WriteLn() 1113 self.WriteLn('### Finished generating for all rules') 1114 self.WriteLn('') 1115 1116 1117 def WriteCopies(self, copies, extra_outputs, part_of_all): 1118 """Write Makefile code for any 'copies' from the gyp input. 1119 1120 extra_outputs: a list that will be filled in with any outputs of this action 1121 (used to make other pieces dependent on this action) 1122 part_of_all: flag indicating this target is part of 'all' 1123 """ 1124 self.WriteLn('### Generated for copy rule.') 1125 1126 variable = StringToMakefileVariable(self.qualified_target + '_copies') 1127 outputs = [] 1128 for copy in copies: 1129 for path in copy['files']: 1130 # Absolutify() may call normpath, and will strip trailing slashes. 1131 path = Sourceify(self.Absolutify(path)) 1132 filename = os.path.split(path)[1] 1133 output = Sourceify(self.Absolutify(os.path.join(copy['destination'], 1134 filename))) 1135 1136 # If the output path has variables in it, which happens in practice for 1137 # 'copies', writing the environment as target-local doesn't work, 1138 # because the variables are already needed for the target name. 1139 # Copying the environment variables into global make variables doesn't 1140 # work either, because then the .d files will potentially contain spaces 1141 # after variable expansion, and .d file handling cannot handle spaces. 1142 # As a workaround, manually expand variables at gyp time. Since 'copies' 1143 # can't run scripts, there's no need to write the env then. 1144 # WriteDoCmd() will escape spaces for .d files. 1145 env = self.GetSortedXcodeEnv() 1146 output = gyp.xcode_emulation.ExpandEnvVars(output, env) 1147 path = gyp.xcode_emulation.ExpandEnvVars(path, env) 1148 self.WriteDoCmd([output], [path], 'copy', part_of_all) 1149 outputs.append(output) 1150 self.WriteLn('%s = %s' % (variable, ' '.join(QuoteSpaces(o) for o in outputs))) 1151 extra_outputs.append('$(%s)' % variable) 1152 self.WriteLn() 1153 1154 1155 def WriteMacBundleResources(self, resources, bundle_deps): 1156 """Writes Makefile code for 'mac_bundle_resources'.""" 1157 self.WriteLn('### Generated for mac_bundle_resources') 1158 1159 for output, res in gyp.xcode_emulation.GetMacBundleResources( 1160 generator_default_variables['PRODUCT_DIR'], self.xcode_settings, 1161 [Sourceify(self.Absolutify(r)) for r in resources]): 1162 _, ext = os.path.splitext(output) 1163 if ext != '.xcassets': 1164 # Make does not supports '.xcassets' emulation. 1165 self.WriteDoCmd([output], [res], 'mac_tool,,,copy-bundle-resource', 1166 part_of_all=True) 1167 bundle_deps.append(output) 1168 1169 1170 def WriteMacInfoPlist(self, bundle_deps): 1171 """Write Makefile code for bundle Info.plist files.""" 1172 info_plist, out, defines, extra_env = gyp.xcode_emulation.GetMacInfoPlist( 1173 generator_default_variables['PRODUCT_DIR'], self.xcode_settings, 1174 lambda p: Sourceify(self.Absolutify(p))) 1175 if not info_plist: 1176 return 1177 if defines: 1178 # Create an intermediate file to store preprocessed results. 1179 intermediate_plist = ('$(obj).$(TOOLSET)/$(TARGET)/' + 1180 os.path.basename(info_plist)) 1181 self.WriteList(defines, intermediate_plist + ': INFOPLIST_DEFINES', '-D', 1182 quoter=EscapeCppDefine) 1183 self.WriteMakeRule([intermediate_plist], [info_plist], 1184 ['$(call do_cmd,infoplist)', 1185 # "Convert" the plist so that any weird whitespace changes from the 1186 # preprocessor do not affect the XML parser in mac_tool. 1187 '@plutil -convert xml1 $@ $@']) 1188 info_plist = intermediate_plist 1189 # plists can contain envvars and substitute them into the file. 1190 self.WriteSortedXcodeEnv( 1191 out, self.GetSortedXcodeEnv(additional_settings=extra_env)) 1192 self.WriteDoCmd([out], [info_plist], 'mac_tool,,,copy-info-plist', 1193 part_of_all=True) 1194 bundle_deps.append(out) 1195 1196 1197 def WriteSources(self, configs, deps, sources, 1198 extra_outputs, extra_link_deps, 1199 part_of_all, precompiled_header): 1200 """Write Makefile code for any 'sources' from the gyp input. 1201 These are source files necessary to build the current target. 1202 1203 configs, deps, sources: input from gyp. 1204 extra_outputs: a list of extra outputs this action should be dependent on; 1205 used to serialize action/rules before compilation 1206 extra_link_deps: a list that will be filled in with any outputs of 1207 compilation (to be used in link lines) 1208 part_of_all: flag indicating this target is part of 'all' 1209 """ 1210 1211 # Write configuration-specific variables for CFLAGS, etc. 1212 for configname in sorted(configs.keys()): 1213 config = configs[configname] 1214 self.WriteList(config.get('defines'), 'DEFS_%s' % configname, prefix='-D', 1215 quoter=EscapeCppDefine) 1216 1217 if self.flavor == 'mac': 1218 cflags = self.xcode_settings.GetCflags(configname) 1219 cflags_c = self.xcode_settings.GetCflagsC(configname) 1220 cflags_cc = self.xcode_settings.GetCflagsCC(configname) 1221 cflags_objc = self.xcode_settings.GetCflagsObjC(configname) 1222 cflags_objcc = self.xcode_settings.GetCflagsObjCC(configname) 1223 else: 1224 cflags = config.get('cflags') 1225 cflags_c = config.get('cflags_c') 1226 cflags_cc = config.get('cflags_cc') 1227 1228 self.WriteLn("# Flags passed to all source files.") 1229 self.WriteList(cflags, 'CFLAGS_%s' % configname) 1230 self.WriteLn("# Flags passed to only C files.") 1231 self.WriteList(cflags_c, 'CFLAGS_C_%s' % configname) 1232 self.WriteLn("# Flags passed to only C++ files.") 1233 self.WriteList(cflags_cc, 'CFLAGS_CC_%s' % configname) 1234 if self.flavor == 'mac': 1235 self.WriteLn("# Flags passed to only ObjC files.") 1236 self.WriteList(cflags_objc, 'CFLAGS_OBJC_%s' % configname) 1237 self.WriteLn("# Flags passed to only ObjC++ files.") 1238 self.WriteList(cflags_objcc, 'CFLAGS_OBJCC_%s' % configname) 1239 includes = config.get('include_dirs') 1240 if includes: 1241 includes = [Sourceify(self.Absolutify(i)) for i in includes] 1242 self.WriteList(includes, 'INCS_%s' % configname, prefix='-I') 1243 1244 compilable = list(filter(Compilable, sources)) 1245 objs = [self.Objectify(self.Absolutify(Target(c))) for c in compilable] 1246 self.WriteList(objs, 'OBJS') 1247 1248 for obj in objs: 1249 assert ' ' not in obj, ( 1250 "Spaces in object filenames not supported (%s)" % obj) 1251 self.WriteLn('# Add to the list of files we specially track ' 1252 'dependencies for.') 1253 self.WriteLn('all_deps += $(OBJS)') 1254 self.WriteLn() 1255 1256 # Make sure our dependencies are built first. 1257 if deps: 1258 self.WriteMakeRule(['$(OBJS)'], deps, 1259 comment = 'Make sure our dependencies are built ' 1260 'before any of us.', 1261 order_only = True) 1262 1263 # Make sure the actions and rules run first. 1264 # If they generate any extra headers etc., the per-.o file dep tracking 1265 # will catch the proper rebuilds, so order only is still ok here. 1266 if extra_outputs: 1267 self.WriteMakeRule(['$(OBJS)'], extra_outputs, 1268 comment = 'Make sure our actions/rules run ' 1269 'before any of us.', 1270 order_only = True) 1271 1272 pchdeps = precompiled_header.GetObjDependencies(compilable, objs ) 1273 if pchdeps: 1274 self.WriteLn('# Dependencies from obj files to their precompiled headers') 1275 for source, obj, gch in pchdeps: 1276 self.WriteLn('%s: %s' % (obj, gch)) 1277 self.WriteLn('# End precompiled header dependencies') 1278 1279 if objs: 1280 extra_link_deps.append('$(OBJS)') 1281 self.WriteLn("""\ 1282# CFLAGS et al overrides must be target-local. 1283# See "Target-specific Variable Values" in the GNU Make manual.""") 1284 self.WriteLn("$(OBJS): TOOLSET := $(TOOLSET)") 1285 self.WriteLn("$(OBJS): GYP_CFLAGS := " 1286 "$(DEFS_$(BUILDTYPE)) " 1287 "$(INCS_$(BUILDTYPE)) " 1288 "%s " % precompiled_header.GetInclude('c') + 1289 "$(CFLAGS_$(BUILDTYPE)) " 1290 "$(CFLAGS_C_$(BUILDTYPE))") 1291 self.WriteLn("$(OBJS): GYP_CXXFLAGS := " 1292 "$(DEFS_$(BUILDTYPE)) " 1293 "$(INCS_$(BUILDTYPE)) " 1294 "%s " % precompiled_header.GetInclude('cc') + 1295 "$(CFLAGS_$(BUILDTYPE)) " 1296 "$(CFLAGS_CC_$(BUILDTYPE))") 1297 if self.flavor == 'mac': 1298 self.WriteLn("$(OBJS): GYP_OBJCFLAGS := " 1299 "$(DEFS_$(BUILDTYPE)) " 1300 "$(INCS_$(BUILDTYPE)) " 1301 "%s " % precompiled_header.GetInclude('m') + 1302 "$(CFLAGS_$(BUILDTYPE)) " 1303 "$(CFLAGS_C_$(BUILDTYPE)) " 1304 "$(CFLAGS_OBJC_$(BUILDTYPE))") 1305 self.WriteLn("$(OBJS): GYP_OBJCXXFLAGS := " 1306 "$(DEFS_$(BUILDTYPE)) " 1307 "$(INCS_$(BUILDTYPE)) " 1308 "%s " % precompiled_header.GetInclude('mm') + 1309 "$(CFLAGS_$(BUILDTYPE)) " 1310 "$(CFLAGS_CC_$(BUILDTYPE)) " 1311 "$(CFLAGS_OBJCC_$(BUILDTYPE))") 1312 1313 self.WritePchTargets(precompiled_header.GetPchBuildCommands()) 1314 1315 # If there are any object files in our input file list, link them into our 1316 # output. 1317 extra_link_deps += list(filter(Linkable, sources)) 1318 1319 self.WriteLn() 1320 1321 def WritePchTargets(self, pch_commands): 1322 """Writes make rules to compile prefix headers.""" 1323 if not pch_commands: 1324 return 1325 1326 for gch, lang_flag, lang, input in pch_commands: 1327 extra_flags = { 1328 'c': '$(CFLAGS_C_$(BUILDTYPE))', 1329 'cc': '$(CFLAGS_CC_$(BUILDTYPE))', 1330 'm': '$(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE))', 1331 'mm': '$(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE))', 1332 }[lang] 1333 var_name = { 1334 'c': 'GYP_PCH_CFLAGS', 1335 'cc': 'GYP_PCH_CXXFLAGS', 1336 'm': 'GYP_PCH_OBJCFLAGS', 1337 'mm': 'GYP_PCH_OBJCXXFLAGS', 1338 }[lang] 1339 self.WriteLn("%s: %s := %s " % (gch, var_name, lang_flag) + 1340 "$(DEFS_$(BUILDTYPE)) " 1341 "$(INCS_$(BUILDTYPE)) " 1342 "$(CFLAGS_$(BUILDTYPE)) " + 1343 extra_flags) 1344 1345 self.WriteLn('%s: %s FORCE_DO_CMD' % (gch, input)) 1346 self.WriteLn('\t@$(call do_cmd,pch_%s,1)' % lang) 1347 self.WriteLn('') 1348 assert ' ' not in gch, ( 1349 "Spaces in gch filenames not supported (%s)" % gch) 1350 self.WriteLn('all_deps += %s' % gch) 1351 self.WriteLn('') 1352 1353 1354 def ComputeOutputBasename(self, spec): 1355 """Return the 'output basename' of a gyp spec. 1356 1357 E.g., the loadable module 'foobar' in directory 'baz' will produce 1358 'libfoobar.so' 1359 """ 1360 assert not self.is_mac_bundle 1361 1362 if self.flavor == 'mac' and self.type in ( 1363 'static_library', 'executable', 'shared_library', 'loadable_module'): 1364 return self.xcode_settings.GetExecutablePath() 1365 1366 target = spec['target_name'] 1367 target_prefix = '' 1368 target_ext = '' 1369 if self.type == 'static_library': 1370 if target[:3] == 'lib': 1371 target = target[3:] 1372 target_prefix = 'lib' 1373 target_ext = '.a' 1374 elif self.type in ('loadable_module', 'shared_library'): 1375 if target[:3] == 'lib': 1376 target = target[3:] 1377 target_prefix = 'lib' 1378 if self.flavor == 'aix': 1379 target_ext = '.a' 1380 else: 1381 target_ext = '.so' 1382 elif self.type == 'none': 1383 target = '%s.stamp' % target 1384 elif self.type != 'executable': 1385 print("ERROR: What output file should be generated?", 1386 "type", self.type, "target", target) 1387 1388 target_prefix = spec.get('product_prefix', target_prefix) 1389 target = spec.get('product_name', target) 1390 product_ext = spec.get('product_extension') 1391 if product_ext: 1392 target_ext = '.' + product_ext 1393 1394 return target_prefix + target + target_ext 1395 1396 1397 def _InstallImmediately(self): 1398 return self.toolset == 'target' and self.flavor == 'mac' and self.type in ( 1399 'static_library', 'executable', 'shared_library', 'loadable_module') 1400 1401 1402 def ComputeOutput(self, spec): 1403 """Return the 'output' (full output path) of a gyp spec. 1404 1405 E.g., the loadable module 'foobar' in directory 'baz' will produce 1406 '$(obj)/baz/libfoobar.so' 1407 """ 1408 assert not self.is_mac_bundle 1409 1410 path = os.path.join('$(obj).' + self.toolset, self.path) 1411 if self.type == 'executable' or self._InstallImmediately(): 1412 path = '$(builddir)' 1413 path = spec.get('product_dir', path) 1414 return os.path.join(path, self.ComputeOutputBasename(spec)) 1415 1416 1417 def ComputeMacBundleOutput(self, spec): 1418 """Return the 'output' (full output path) to a bundle output directory.""" 1419 assert self.is_mac_bundle 1420 path = generator_default_variables['PRODUCT_DIR'] 1421 return os.path.join(path, self.xcode_settings.GetWrapperName()) 1422 1423 1424 def ComputeMacBundleBinaryOutput(self, spec): 1425 """Return the 'output' (full output path) to the binary in a bundle.""" 1426 path = generator_default_variables['PRODUCT_DIR'] 1427 return os.path.join(path, self.xcode_settings.GetExecutablePath()) 1428 1429 1430 def ComputeDeps(self, spec): 1431 """Compute the dependencies of a gyp spec. 1432 1433 Returns a tuple (deps, link_deps), where each is a list of 1434 filenames that will need to be put in front of make for either 1435 building (deps) or linking (link_deps). 1436 """ 1437 deps = [] 1438 link_deps = [] 1439 if 'dependencies' in spec: 1440 deps.extend([target_outputs[dep] for dep in spec['dependencies'] 1441 if target_outputs[dep]]) 1442 for dep in spec['dependencies']: 1443 if dep in target_link_deps: 1444 link_deps.append(target_link_deps[dep]) 1445 deps.extend(link_deps) 1446 # TODO: It seems we need to transitively link in libraries (e.g. -lfoo)? 1447 # This hack makes it work: 1448 # link_deps.extend(spec.get('libraries', [])) 1449 return (gyp.common.uniquer(deps), gyp.common.uniquer(link_deps)) 1450 1451 1452 def WriteDependencyOnExtraOutputs(self, target, extra_outputs): 1453 self.WriteMakeRule([self.output_binary], extra_outputs, 1454 comment = 'Build our special outputs first.', 1455 order_only = True) 1456 1457 1458 def WriteTarget(self, spec, configs, deps, link_deps, bundle_deps, 1459 extra_outputs, part_of_all): 1460 """Write Makefile code to produce the final target of the gyp spec. 1461 1462 spec, configs: input from gyp. 1463 deps, link_deps: dependency lists; see ComputeDeps() 1464 extra_outputs: any extra outputs that our target should depend on 1465 part_of_all: flag indicating this target is part of 'all' 1466 """ 1467 1468 self.WriteLn('### Rules for final target.') 1469 1470 if extra_outputs: 1471 self.WriteDependencyOnExtraOutputs(self.output_binary, extra_outputs) 1472 self.WriteMakeRule(extra_outputs, deps, 1473 comment=('Preserve order dependency of ' 1474 'special output on deps.'), 1475 order_only = True) 1476 1477 target_postbuilds = {} 1478 if self.type != 'none': 1479 for configname in sorted(configs.keys()): 1480 config = configs[configname] 1481 if self.flavor == 'mac': 1482 ldflags = self.xcode_settings.GetLdflags(configname, 1483 generator_default_variables['PRODUCT_DIR'], 1484 lambda p: Sourceify(self.Absolutify(p))) 1485 1486 # TARGET_POSTBUILDS_$(BUILDTYPE) is added to postbuilds later on. 1487 gyp_to_build = gyp.common.InvertRelativePath(self.path) 1488 target_postbuild = self.xcode_settings.AddImplicitPostbuilds( 1489 configname, 1490 QuoteSpaces(os.path.normpath(os.path.join(gyp_to_build, 1491 self.output))), 1492 QuoteSpaces(os.path.normpath(os.path.join(gyp_to_build, 1493 self.output_binary)))) 1494 if target_postbuild: 1495 target_postbuilds[configname] = target_postbuild 1496 else: 1497 ldflags = config.get('ldflags', []) 1498 # Compute an rpath for this output if needed. 1499 if any(dep.endswith('.so') or '.so.' in dep for dep in deps): 1500 # We want to get the literal string "$ORIGIN" into the link command, 1501 # so we need lots of escaping. 1502 ldflags.append(r'-Wl,-rpath=\$$ORIGIN/lib.%s/' % self.toolset) 1503 ldflags.append(r'-Wl,-rpath-link=\$(builddir)/lib.%s/' % 1504 self.toolset) 1505 library_dirs = config.get('library_dirs', []) 1506 ldflags += [('-L%s' % library_dir) for library_dir in library_dirs] 1507 self.WriteList(ldflags, 'LDFLAGS_%s' % configname) 1508 if self.flavor == 'mac': 1509 self.WriteList(self.xcode_settings.GetLibtoolflags(configname), 1510 'LIBTOOLFLAGS_%s' % configname) 1511 libraries = spec.get('libraries') 1512 if libraries: 1513 # Remove duplicate entries 1514 libraries = gyp.common.uniquer(libraries) 1515 if self.flavor == 'mac': 1516 libraries = self.xcode_settings.AdjustLibraries(libraries) 1517 self.WriteList(libraries, 'LIBS') 1518 self.WriteLn('%s: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))' % 1519 QuoteSpaces(self.output_binary)) 1520 self.WriteLn('%s: LIBS := $(LIBS)' % QuoteSpaces(self.output_binary)) 1521 1522 if self.flavor == 'mac': 1523 self.WriteLn('%s: GYP_LIBTOOLFLAGS := $(LIBTOOLFLAGS_$(BUILDTYPE))' % 1524 QuoteSpaces(self.output_binary)) 1525 1526 # Postbuild actions. Like actions, but implicitly depend on the target's 1527 # output. 1528 postbuilds = [] 1529 if self.flavor == 'mac': 1530 if target_postbuilds: 1531 postbuilds.append('$(TARGET_POSTBUILDS_$(BUILDTYPE))') 1532 postbuilds.extend( 1533 gyp.xcode_emulation.GetSpecPostbuildCommands(spec)) 1534 1535 if postbuilds: 1536 # Envvars may be referenced by TARGET_POSTBUILDS_$(BUILDTYPE), 1537 # so we must output its definition first, since we declare variables 1538 # using ":=". 1539 self.WriteSortedXcodeEnv(self.output, self.GetSortedXcodePostbuildEnv()) 1540 1541 for configname in target_postbuilds: 1542 self.WriteLn('%s: TARGET_POSTBUILDS_%s := %s' % 1543 (QuoteSpaces(self.output), 1544 configname, 1545 gyp.common.EncodePOSIXShellList(target_postbuilds[configname]))) 1546 1547 # Postbuilds expect to be run in the gyp file's directory, so insert an 1548 # implicit postbuild to cd to there. 1549 postbuilds.insert(0, gyp.common.EncodePOSIXShellList(['cd', self.path])) 1550 for i in range(len(postbuilds)): 1551 if not postbuilds[i].startswith('$'): 1552 postbuilds[i] = EscapeShellArgument(postbuilds[i]) 1553 self.WriteLn('%s: builddir := $(abs_builddir)' % QuoteSpaces(self.output)) 1554 self.WriteLn('%s: POSTBUILDS := %s' % ( 1555 QuoteSpaces(self.output), ' '.join(postbuilds))) 1556 1557 # A bundle directory depends on its dependencies such as bundle resources 1558 # and bundle binary. When all dependencies have been built, the bundle 1559 # needs to be packaged. 1560 if self.is_mac_bundle: 1561 # If the framework doesn't contain a binary, then nothing depends 1562 # on the actions -- make the framework depend on them directly too. 1563 self.WriteDependencyOnExtraOutputs(self.output, extra_outputs) 1564 1565 # Bundle dependencies. Note that the code below adds actions to this 1566 # target, so if you move these two lines, move the lines below as well. 1567 self.WriteList([QuoteSpaces(dep) for dep in bundle_deps], 'BUNDLE_DEPS') 1568 self.WriteLn('%s: $(BUNDLE_DEPS)' % QuoteSpaces(self.output)) 1569 1570 # After the framework is built, package it. Needs to happen before 1571 # postbuilds, since postbuilds depend on this. 1572 if self.type in ('shared_library', 'loadable_module'): 1573 self.WriteLn('\t@$(call do_cmd,mac_package_framework,,,%s)' % 1574 self.xcode_settings.GetFrameworkVersion()) 1575 1576 # Bundle postbuilds can depend on the whole bundle, so run them after 1577 # the bundle is packaged, not already after the bundle binary is done. 1578 if postbuilds: 1579 self.WriteLn('\t@$(call do_postbuilds)') 1580 postbuilds = [] # Don't write postbuilds for target's output. 1581 1582 # Needed by test/mac/gyptest-rebuild.py. 1583 self.WriteLn('\t@true # No-op, used by tests') 1584 1585 # Since this target depends on binary and resources which are in 1586 # nested subfolders, the framework directory will be older than 1587 # its dependencies usually. To prevent this rule from executing 1588 # on every build (expensive, especially with postbuilds), expliclity 1589 # update the time on the framework directory. 1590 self.WriteLn('\t@touch -c %s' % QuoteSpaces(self.output)) 1591 1592 if postbuilds: 1593 assert not self.is_mac_bundle, ('Postbuilds for bundles should be done ' 1594 'on the bundle, not the binary (target \'%s\')' % self.target) 1595 assert 'product_dir' not in spec, ('Postbuilds do not work with ' 1596 'custom product_dir') 1597 1598 if self.type == 'executable': 1599 self.WriteLn('%s: LD_INPUTS := %s' % ( 1600 QuoteSpaces(self.output_binary), 1601 ' '.join(QuoteSpaces(dep) for dep in link_deps))) 1602 if self.toolset == 'host' and self.flavor == 'android': 1603 self.WriteDoCmd([self.output_binary], link_deps, 'link_host', 1604 part_of_all, postbuilds=postbuilds) 1605 else: 1606 self.WriteDoCmd([self.output_binary], link_deps, 'link', part_of_all, 1607 postbuilds=postbuilds) 1608 1609 elif self.type == 'static_library': 1610 for link_dep in link_deps: 1611 assert ' ' not in link_dep, ( 1612 "Spaces in alink input filenames not supported (%s)" % link_dep) 1613 if (self.flavor not in ('mac', 'openbsd', 'netbsd', 'win') and not 1614 self.is_standalone_static_library): 1615 self.WriteDoCmd([self.output_binary], link_deps, 'alink_thin', 1616 part_of_all, postbuilds=postbuilds) 1617 else: 1618 self.WriteDoCmd([self.output_binary], link_deps, 'alink', part_of_all, 1619 postbuilds=postbuilds) 1620 elif self.type == 'shared_library': 1621 self.WriteLn('%s: LD_INPUTS := %s' % ( 1622 QuoteSpaces(self.output_binary), 1623 ' '.join(QuoteSpaces(dep) for dep in link_deps))) 1624 self.WriteDoCmd([self.output_binary], link_deps, 'solink', part_of_all, 1625 postbuilds=postbuilds) 1626 elif self.type == 'loadable_module': 1627 for link_dep in link_deps: 1628 assert ' ' not in link_dep, ( 1629 "Spaces in module input filenames not supported (%s)" % link_dep) 1630 if self.toolset == 'host' and self.flavor == 'android': 1631 self.WriteDoCmd([self.output_binary], link_deps, 'solink_module_host', 1632 part_of_all, postbuilds=postbuilds) 1633 else: 1634 self.WriteDoCmd( 1635 [self.output_binary], link_deps, 'solink_module', part_of_all, 1636 postbuilds=postbuilds) 1637 elif self.type == 'none': 1638 # Write a stamp line. 1639 self.WriteDoCmd([self.output_binary], deps, 'touch', part_of_all, 1640 postbuilds=postbuilds) 1641 else: 1642 print("WARNING: no output for", self.type, self.target) 1643 1644 # Add an alias for each target (if there are any outputs). 1645 # Installable target aliases are created below. 1646 if ((self.output and self.output != self.target) and 1647 (self.type not in self._INSTALLABLE_TARGETS)): 1648 self.WriteMakeRule([self.target], [self.output], 1649 comment='Add target alias', phony = True) 1650 if part_of_all: 1651 self.WriteMakeRule(['all'], [self.target], 1652 comment = 'Add target alias to "all" target.', 1653 phony = True) 1654 1655 # Add special-case rules for our installable targets. 1656 # 1) They need to install to the build dir or "product" dir. 1657 # 2) They get shortcuts for building (e.g. "make chrome"). 1658 # 3) They are part of "make all". 1659 if (self.type in self._INSTALLABLE_TARGETS or 1660 self.is_standalone_static_library): 1661 if self.type == 'shared_library': 1662 file_desc = 'shared library' 1663 elif self.type == 'static_library': 1664 file_desc = 'static library' 1665 else: 1666 file_desc = 'executable' 1667 install_path = self._InstallableTargetInstallPath() 1668 installable_deps = [self.output] 1669 if (self.flavor == 'mac' and not 'product_dir' in spec and 1670 self.toolset == 'target'): 1671 # On mac, products are created in install_path immediately. 1672 assert install_path == self.output, '%s != %s' % ( 1673 install_path, self.output) 1674 1675 # Point the target alias to the final binary output. 1676 self.WriteMakeRule([self.target], [install_path], 1677 comment='Add target alias', phony = True) 1678 if install_path != self.output: 1679 assert not self.is_mac_bundle # See comment a few lines above. 1680 self.WriteDoCmd([install_path], [self.output], 'copy', 1681 comment = 'Copy this to the %s output path.' % 1682 file_desc, part_of_all=part_of_all) 1683 installable_deps.append(install_path) 1684 if self.output != self.alias and self.alias != self.target: 1685 self.WriteMakeRule([self.alias], installable_deps, 1686 comment = 'Short alias for building this %s.' % 1687 file_desc, phony = True) 1688 if part_of_all: 1689 self.WriteMakeRule(['all'], [install_path], 1690 comment = 'Add %s to "all" target.' % file_desc, 1691 phony = True) 1692 1693 1694 def WriteList(self, value_list, variable=None, prefix='', 1695 quoter=QuoteIfNecessary): 1696 """Write a variable definition that is a list of values. 1697 1698 E.g. WriteList(['a','b'], 'foo', prefix='blah') writes out 1699 foo = blaha blahb 1700 but in a pretty-printed style. 1701 """ 1702 values = '' 1703 if value_list: 1704 value_list = [quoter(prefix + l) for l in value_list] 1705 values = ' \\\n\t' + ' \\\n\t'.join(value_list) 1706 self.fp.write('%s :=%s\n\n' % (variable, values)) 1707 1708 1709 def WriteDoCmd(self, outputs, inputs, command, part_of_all, comment=None, 1710 postbuilds=False): 1711 """Write a Makefile rule that uses do_cmd. 1712 1713 This makes the outputs dependent on the command line that was run, 1714 as well as support the V= make command line flag. 1715 """ 1716 suffix = '' 1717 if postbuilds: 1718 assert ',' not in command 1719 suffix = ',,1' # Tell do_cmd to honor $POSTBUILDS 1720 self.WriteMakeRule(outputs, inputs, 1721 actions = ['$(call do_cmd,%s%s)' % (command, suffix)], 1722 comment = comment, 1723 command = command, 1724 force = True) 1725 # Add our outputs to the list of targets we read depfiles from. 1726 # all_deps is only used for deps file reading, and for deps files we replace 1727 # spaces with ? because escaping doesn't work with make's $(sort) and 1728 # other functions. 1729 outputs = [QuoteSpaces(o, SPACE_REPLACEMENT) for o in outputs] 1730 self.WriteLn('all_deps += %s' % ' '.join(outputs)) 1731 1732 1733 def WriteMakeRule(self, outputs, inputs, actions=None, comment=None, 1734 order_only=False, force=False, phony=False, command=None): 1735 """Write a Makefile rule, with some extra tricks. 1736 1737 outputs: a list of outputs for the rule (note: this is not directly 1738 supported by make; see comments below) 1739 inputs: a list of inputs for the rule 1740 actions: a list of shell commands to run for the rule 1741 comment: a comment to put in the Makefile above the rule (also useful 1742 for making this Python script's code self-documenting) 1743 order_only: if true, makes the dependency order-only 1744 force: if true, include FORCE_DO_CMD as an order-only dep 1745 phony: if true, the rule does not actually generate the named output, the 1746 output is just a name to run the rule 1747 command: (optional) command name to generate unambiguous labels 1748 """ 1749 outputs = [QuoteSpaces(o) for o in outputs] 1750 inputs = [QuoteSpaces(i) for i in inputs] 1751 1752 if comment: 1753 self.WriteLn('# ' + comment) 1754 if phony: 1755 self.WriteLn('.PHONY: ' + ' '.join(outputs)) 1756 if actions: 1757 self.WriteLn("%s: TOOLSET := $(TOOLSET)" % outputs[0]) 1758 force_append = ' FORCE_DO_CMD' if force else '' 1759 1760 if order_only: 1761 # Order only rule: Just write a simple rule. 1762 # TODO(evanm): just make order_only a list of deps instead of this hack. 1763 self.WriteLn('%s: | %s%s' % 1764 (' '.join(outputs), ' '.join(inputs), force_append)) 1765 elif len(outputs) == 1: 1766 # Regular rule, one output: Just write a simple rule. 1767 self.WriteLn('%s: %s%s' % (outputs[0], ' '.join(inputs), force_append)) 1768 else: 1769 # Regular rule, more than one output: Multiple outputs are tricky in 1770 # make. We will write three rules: 1771 # - All outputs depend on an intermediate file. 1772 # - Make .INTERMEDIATE depend on the intermediate. 1773 # - The intermediate file depends on the inputs and executes the 1774 # actual command. 1775 # - The intermediate recipe will 'touch' the intermediate file. 1776 # - The multi-output rule will have an do-nothing recipe. 1777 1778 # Hash the target name to avoid generating overlong filenames. 1779 cmddigest = hashlib.sha1((command or self.target).encode('utf-8')).hexdigest() 1780 intermediate = "%s.intermediate" % cmddigest 1781 self.WriteLn('%s: %s' % (' '.join(outputs), intermediate)) 1782 self.WriteLn('\t%s' % '@:') 1783 self.WriteLn('%s: %s' % ('.INTERMEDIATE', intermediate)) 1784 self.WriteLn('%s: %s%s' % 1785 (intermediate, ' '.join(inputs), force_append)) 1786 actions.insert(0, '$(call do_cmd,touch)') 1787 1788 if actions: 1789 for action in actions: 1790 self.WriteLn('\t%s' % action) 1791 self.WriteLn() 1792 1793 1794 def WriteAndroidNdkModuleRule(self, module_name, all_sources, link_deps): 1795 """Write a set of LOCAL_XXX definitions for Android NDK. 1796 1797 These variable definitions will be used by Android NDK but do nothing for 1798 non-Android applications. 1799 1800 Arguments: 1801 module_name: Android NDK module name, which must be unique among all 1802 module names. 1803 all_sources: A list of source files (will be filtered by Compilable). 1804 link_deps: A list of link dependencies, which must be sorted in 1805 the order from dependencies to dependents. 1806 """ 1807 if self.type not in ('executable', 'shared_library', 'static_library'): 1808 return 1809 1810 self.WriteLn('# Variable definitions for Android applications') 1811 self.WriteLn('include $(CLEAR_VARS)') 1812 self.WriteLn('LOCAL_MODULE := ' + module_name) 1813 self.WriteLn('LOCAL_CFLAGS := $(CFLAGS_$(BUILDTYPE)) ' 1814 '$(DEFS_$(BUILDTYPE)) ' 1815 # LOCAL_CFLAGS is applied to both of C and C++. There is 1816 # no way to specify $(CFLAGS_C_$(BUILDTYPE)) only for C 1817 # sources. 1818 '$(CFLAGS_C_$(BUILDTYPE)) ' 1819 # $(INCS_$(BUILDTYPE)) includes the prefix '-I' while 1820 # LOCAL_C_INCLUDES does not expect it. So put it in 1821 # LOCAL_CFLAGS. 1822 '$(INCS_$(BUILDTYPE))') 1823 # LOCAL_CXXFLAGS is obsolete and LOCAL_CPPFLAGS is preferred. 1824 self.WriteLn('LOCAL_CPPFLAGS := $(CFLAGS_CC_$(BUILDTYPE))') 1825 self.WriteLn('LOCAL_C_INCLUDES :=') 1826 self.WriteLn('LOCAL_LDLIBS := $(LDFLAGS_$(BUILDTYPE)) $(LIBS)') 1827 1828 # Detect the C++ extension. 1829 cpp_ext = {'.cc': 0, '.cpp': 0, '.cxx': 0} 1830 default_cpp_ext = '.cpp' 1831 for filename in all_sources: 1832 ext = os.path.splitext(filename)[1] 1833 if ext in cpp_ext: 1834 cpp_ext[ext] += 1 1835 if cpp_ext[ext] > cpp_ext[default_cpp_ext]: 1836 default_cpp_ext = ext 1837 self.WriteLn('LOCAL_CPP_EXTENSION := ' + default_cpp_ext) 1838 1839 self.WriteList(list(map(self.Absolutify, filter(Compilable, all_sources))), 1840 'LOCAL_SRC_FILES') 1841 1842 # Filter out those which do not match prefix and suffix and produce 1843 # the resulting list without prefix and suffix. 1844 def DepsToModules(deps, prefix, suffix): 1845 modules = [] 1846 for filepath in deps: 1847 filename = os.path.basename(filepath) 1848 if filename.startswith(prefix) and filename.endswith(suffix): 1849 modules.append(filename[len(prefix):-len(suffix)]) 1850 return modules 1851 1852 # Retrieve the default value of 'SHARED_LIB_SUFFIX' 1853 params = {'flavor': 'linux'} 1854 default_variables = {} 1855 CalculateVariables(default_variables, params) 1856 1857 self.WriteList( 1858 DepsToModules(link_deps, 1859 generator_default_variables['SHARED_LIB_PREFIX'], 1860 default_variables['SHARED_LIB_SUFFIX']), 1861 'LOCAL_SHARED_LIBRARIES') 1862 self.WriteList( 1863 DepsToModules(link_deps, 1864 generator_default_variables['STATIC_LIB_PREFIX'], 1865 generator_default_variables['STATIC_LIB_SUFFIX']), 1866 'LOCAL_STATIC_LIBRARIES') 1867 1868 if self.type == 'executable': 1869 self.WriteLn('include $(BUILD_EXECUTABLE)') 1870 elif self.type == 'shared_library': 1871 self.WriteLn('include $(BUILD_SHARED_LIBRARY)') 1872 elif self.type == 'static_library': 1873 self.WriteLn('include $(BUILD_STATIC_LIBRARY)') 1874 self.WriteLn() 1875 1876 1877 def WriteLn(self, text=''): 1878 self.fp.write(text + '\n') 1879 1880 1881 def GetSortedXcodeEnv(self, additional_settings=None): 1882 return gyp.xcode_emulation.GetSortedXcodeEnv( 1883 self.xcode_settings, "$(abs_builddir)", 1884 os.path.join("$(abs_srcdir)", self.path), "$(BUILDTYPE)", 1885 additional_settings) 1886 1887 1888 def GetSortedXcodePostbuildEnv(self): 1889 # CHROMIUM_STRIP_SAVE_FILE is a chromium-specific hack. 1890 # TODO(thakis): It would be nice to have some general mechanism instead. 1891 strip_save_file = self.xcode_settings.GetPerTargetSetting( 1892 'CHROMIUM_STRIP_SAVE_FILE', '') 1893 # Even if strip_save_file is empty, explicitly write it. Else a postbuild 1894 # might pick up an export from an earlier target. 1895 return self.GetSortedXcodeEnv( 1896 additional_settings={'CHROMIUM_STRIP_SAVE_FILE': strip_save_file}) 1897 1898 1899 def WriteSortedXcodeEnv(self, target, env): 1900 for k, v in env: 1901 # For 1902 # foo := a\ b 1903 # the escaped space does the right thing. For 1904 # export foo := a\ b 1905 # it does not -- the backslash is written to the env as literal character. 1906 # So don't escape spaces in |env[k]|. 1907 self.WriteLn('%s: export %s := %s' % (QuoteSpaces(target), k, v)) 1908 1909 1910 def Objectify(self, path): 1911 """Convert a path to its output directory form.""" 1912 if '$(' in path: 1913 path = path.replace('$(obj)/', '$(obj).%s/$(TARGET)/' % self.toolset) 1914 if not '$(obj)' in path: 1915 path = '$(obj).%s/$(TARGET)/%s' % (self.toolset, path) 1916 return path 1917 1918 1919 def Pchify(self, path, lang): 1920 """Convert a prefix header path to its output directory form.""" 1921 path = self.Absolutify(path) 1922 if '$(' in path: 1923 path = path.replace('$(obj)/', '$(obj).%s/$(TARGET)/pch-%s' % 1924 (self.toolset, lang)) 1925 return path 1926 return '$(obj).%s/$(TARGET)/pch-%s/%s' % (self.toolset, lang, path) 1927 1928 1929 def Absolutify(self, path): 1930 """Convert a subdirectory-relative path into a base-relative path. 1931 Skips over paths that contain variables.""" 1932 if '$(' in path: 1933 # Don't call normpath in this case, as it might collapse the 1934 # path too aggressively if it features '..'. However it's still 1935 # important to strip trailing slashes. 1936 return path.rstrip('/') 1937 return os.path.normpath(os.path.join(self.path, path)) 1938 1939 1940 def ExpandInputRoot(self, template, expansion, dirname): 1941 if '%(INPUT_ROOT)s' not in template and '%(INPUT_DIRNAME)s' not in template: 1942 return template 1943 path = template % { 1944 'INPUT_ROOT': expansion, 1945 'INPUT_DIRNAME': dirname, 1946 } 1947 return path 1948 1949 1950 def _InstallableTargetInstallPath(self): 1951 """Returns the location of the final output for an installable target.""" 1952 # Xcode puts shared_library results into PRODUCT_DIR, and some gyp files 1953 # rely on this. Emulate this behavior for mac. 1954 1955 # XXX(TooTallNate): disabling this code since we don't want this behavior... 1956 #if (self.type == 'shared_library' and 1957 # (self.flavor != 'mac' or self.toolset != 'target')): 1958 # # Install all shared libs into a common directory (per toolset) for 1959 # # convenient access with LD_LIBRARY_PATH. 1960 # return '$(builddir)/lib.%s/%s' % (self.toolset, self.alias) 1961 return '$(builddir)/' + self.alias 1962 1963 1964def WriteAutoRegenerationRule(params, root_makefile, makefile_name, 1965 build_files): 1966 """Write the target to regenerate the Makefile.""" 1967 options = params['options'] 1968 build_files_args = [gyp.common.RelativePath(filename, options.toplevel_dir) 1969 for filename in params['build_files_arg']] 1970 1971 gyp_binary = gyp.common.FixIfRelativePath(params['gyp_binary'], 1972 options.toplevel_dir) 1973 if not gyp_binary.startswith(os.sep): 1974 gyp_binary = os.path.join('.', gyp_binary) 1975 1976 root_makefile.write( 1977 "quiet_cmd_regen_makefile = ACTION Regenerating $@\n" 1978 "cmd_regen_makefile = cd $(srcdir); %(cmd)s\n" 1979 "%(makefile_name)s: %(deps)s\n" 1980 "\t$(call do_cmd,regen_makefile)\n\n" % { 1981 'makefile_name': makefile_name, 1982 'deps': ' '.join(SourceifyAndQuoteSpaces(bf) for bf in build_files), 1983 'cmd': gyp.common.EncodePOSIXShellList( 1984 [gyp_binary, '-fmake'] + 1985 gyp.RegenerateFlags(options) + 1986 build_files_args)}) 1987 1988 1989def PerformBuild(data, configurations, params): 1990 options = params['options'] 1991 for config in configurations: 1992 arguments = ['make'] 1993 if options.toplevel_dir and options.toplevel_dir != '.': 1994 arguments += '-C', options.toplevel_dir 1995 arguments.append('BUILDTYPE=' + config) 1996 print('Building [%s]: %s' % (config, arguments)) 1997 subprocess.check_call(arguments) 1998 1999 2000def GenerateOutput(target_list, target_dicts, data, params): 2001 options = params['options'] 2002 flavor = gyp.common.GetFlavor(params) 2003 generator_flags = params.get('generator_flags', {}) 2004 builddir_name = generator_flags.get('output_dir', 'out') 2005 android_ndk_version = generator_flags.get('android_ndk_version', None) 2006 default_target = generator_flags.get('default_target', 'all') 2007 2008 def CalculateMakefilePath(build_file, base_name): 2009 """Determine where to write a Makefile for a given gyp file.""" 2010 # Paths in gyp files are relative to the .gyp file, but we want 2011 # paths relative to the source root for the master makefile. Grab 2012 # the path of the .gyp file as the base to relativize against. 2013 # E.g. "foo/bar" when we're constructing targets for "foo/bar/baz.gyp". 2014 base_path = gyp.common.RelativePath(os.path.dirname(build_file), 2015 options.depth) 2016 # We write the file in the base_path directory. 2017 output_file = os.path.join(options.depth, base_path, base_name) 2018 if options.generator_output: 2019 output_file = os.path.join( 2020 options.depth, options.generator_output, base_path, base_name) 2021 base_path = gyp.common.RelativePath(os.path.dirname(build_file), 2022 options.toplevel_dir) 2023 return base_path, output_file 2024 2025 # TODO: search for the first non-'Default' target. This can go 2026 # away when we add verification that all targets have the 2027 # necessary configurations. 2028 default_configuration = None 2029 toolsets = set([target_dicts[target]['toolset'] for target in target_list]) 2030 for target in target_list: 2031 spec = target_dicts[target] 2032 if spec['default_configuration'] != 'Default': 2033 default_configuration = spec['default_configuration'] 2034 break 2035 if not default_configuration: 2036 default_configuration = 'Default' 2037 2038 srcdir = '.' 2039 makefile_name = 'Makefile' + options.suffix 2040 makefile_path = os.path.join(options.toplevel_dir, makefile_name) 2041 if options.generator_output: 2042 global srcdir_prefix 2043 makefile_path = os.path.join( 2044 options.toplevel_dir, options.generator_output, makefile_name) 2045 srcdir = gyp.common.RelativePath(srcdir, options.generator_output) 2046 srcdir_prefix = '$(srcdir)/' 2047 2048 flock_command= 'flock' 2049 copy_archive_arguments = '-af' 2050 makedep_arguments = '-MMD' 2051 header_params = { 2052 'default_target': default_target, 2053 'builddir': builddir_name, 2054 'default_configuration': default_configuration, 2055 'flock': flock_command, 2056 'flock_index': 1, 2057 'link_commands': LINK_COMMANDS_LINUX, 2058 'extra_commands': '', 2059 'srcdir': srcdir, 2060 'copy_archive_args': copy_archive_arguments, 2061 'makedep_args': makedep_arguments, 2062 'CC.target': GetEnvironFallback(('CC_target', 'CC'), '$(CC)'), 2063 'AR.target': GetEnvironFallback(('AR_target', 'AR'), '$(AR)'), 2064 'CXX.target': GetEnvironFallback(('CXX_target', 'CXX'), '$(CXX)'), 2065 'LINK.target': GetEnvironFallback(('LINK_target', 'LINK'), '$(LINK)'), 2066 'CC.host': GetEnvironFallback(('CC_host', 'CC'), 'gcc'), 2067 'AR.host': GetEnvironFallback(('AR_host', 'AR'), 'ar'), 2068 'CXX.host': GetEnvironFallback(('CXX_host', 'CXX'), 'g++'), 2069 'LINK.host': GetEnvironFallback(('LINK_host', 'LINK'), '$(CXX.host)'), 2070 } 2071 if flavor == 'mac': 2072 flock_command = './gyp-mac-tool flock' 2073 header_params.update({ 2074 'flock': flock_command, 2075 'flock_index': 2, 2076 'link_commands': LINK_COMMANDS_MAC, 2077 'extra_commands': SHARED_HEADER_MAC_COMMANDS, 2078 }) 2079 elif flavor == 'android': 2080 header_params.update({ 2081 'link_commands': LINK_COMMANDS_ANDROID, 2082 }) 2083 elif flavor == 'zos': 2084 copy_archive_arguments = '-fPR' 2085 makedep_arguments = '-qmakedep=gcc' 2086 header_params.update({ 2087 'copy_archive_args': copy_archive_arguments, 2088 'makedep_args': makedep_arguments, 2089 'link_commands': LINK_COMMANDS_OS390, 2090 'CC.target': GetEnvironFallback(('CC_target', 'CC'), 'njsc'), 2091 'CXX.target': GetEnvironFallback(('CXX_target', 'CXX'), 'njsc++'), 2092 'CC.host': GetEnvironFallback(('CC_host', 'CC'), 'njsc'), 2093 'CXX.host': GetEnvironFallback(('CXX_host', 'CXX'), 'njsc++'), 2094 }) 2095 elif flavor == 'solaris': 2096 header_params.update({ 2097 'flock': './gyp-flock-tool flock', 2098 'flock_index': 2, 2099 }) 2100 elif flavor == 'freebsd': 2101 # Note: OpenBSD has sysutils/flock. lockf seems to be FreeBSD specific. 2102 header_params.update({ 2103 'flock': 'lockf', 2104 }) 2105 elif flavor == 'openbsd': 2106 copy_archive_arguments = '-pPRf' 2107 header_params.update({ 2108 'copy_archive_args': copy_archive_arguments, 2109 }) 2110 elif flavor == 'aix': 2111 copy_archive_arguments = '-pPRf' 2112 header_params.update({ 2113 'copy_archive_args': copy_archive_arguments, 2114 'link_commands': LINK_COMMANDS_AIX, 2115 'flock': './gyp-flock-tool flock', 2116 'flock_index': 2, 2117 }) 2118 2119 build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0]) 2120 make_global_settings_array = data[build_file].get('make_global_settings', []) 2121 wrappers = {} 2122 for key, value in make_global_settings_array: 2123 if key.endswith('_wrapper'): 2124 wrappers[key[:-len('_wrapper')]] = '$(abspath %s)' % value 2125 make_global_settings = '' 2126 for key, value in make_global_settings_array: 2127 if re.match('.*_wrapper', key): 2128 continue 2129 if value[0] != '$': 2130 value = '$(abspath %s)' % value 2131 wrapper = wrappers.get(key) 2132 if wrapper: 2133 value = '%s %s' % (wrapper, value) 2134 del wrappers[key] 2135 if key in ('CC', 'CC.host', 'CXX', 'CXX.host'): 2136 make_global_settings += ( 2137 'ifneq (,$(filter $(origin %s), undefined default))\n' % key) 2138 # Let gyp-time envvars win over global settings. 2139 env_key = key.replace('.', '_') # CC.host -> CC_host 2140 if env_key in os.environ: 2141 value = os.environ[env_key] 2142 make_global_settings += ' %s = %s\n' % (key, value) 2143 make_global_settings += 'endif\n' 2144 else: 2145 make_global_settings += '%s ?= %s\n' % (key, value) 2146 # TODO(ukai): define cmd when only wrapper is specified in 2147 # make_global_settings. 2148 2149 header_params['make_global_settings'] = make_global_settings 2150 2151 gyp.common.EnsureDirExists(makefile_path) 2152 root_makefile = open(makefile_path, 'w') 2153 root_makefile.write(SHARED_HEADER % header_params) 2154 # Currently any versions have the same effect, but in future the behavior 2155 # could be different. 2156 if android_ndk_version: 2157 root_makefile.write( 2158 '# Define LOCAL_PATH for build of Android applications.\n' 2159 'LOCAL_PATH := $(call my-dir)\n' 2160 '\n') 2161 for toolset in toolsets: 2162 root_makefile.write('TOOLSET := %s\n' % toolset) 2163 WriteRootHeaderSuffixRules(root_makefile) 2164 2165 # Put build-time support tools next to the root Makefile. 2166 dest_path = os.path.dirname(makefile_path) 2167 gyp.common.CopyTool(flavor, dest_path) 2168 2169 # Find the list of targets that derive from the gyp file(s) being built. 2170 needed_targets = set() 2171 for build_file in params['build_files']: 2172 for target in gyp.common.AllTargets(target_list, target_dicts, build_file): 2173 needed_targets.add(target) 2174 2175 build_files = set() 2176 include_list = set() 2177 for qualified_target in target_list: 2178 build_file, target, toolset = gyp.common.ParseQualifiedTarget( 2179 qualified_target) 2180 2181 this_make_global_settings = data[build_file].get('make_global_settings', []) 2182 assert make_global_settings_array == this_make_global_settings, ( 2183 "make_global_settings needs to be the same for all targets. %s vs. %s" % 2184 (this_make_global_settings, make_global_settings)) 2185 2186 build_files.add(gyp.common.RelativePath(build_file, options.toplevel_dir)) 2187 included_files = data[build_file]['included_files'] 2188 for included_file in included_files: 2189 # The included_files entries are relative to the dir of the build file 2190 # that included them, so we have to undo that and then make them relative 2191 # to the root dir. 2192 relative_include_file = gyp.common.RelativePath( 2193 gyp.common.UnrelativePath(included_file, build_file), 2194 options.toplevel_dir) 2195 abs_include_file = os.path.abspath(relative_include_file) 2196 # If the include file is from the ~/.gyp dir, we should use absolute path 2197 # so that relocating the src dir doesn't break the path. 2198 if (params['home_dot_gyp'] and 2199 abs_include_file.startswith(params['home_dot_gyp'])): 2200 build_files.add(abs_include_file) 2201 else: 2202 build_files.add(relative_include_file) 2203 2204 base_path, output_file = CalculateMakefilePath(build_file, 2205 target + '.' + toolset + options.suffix + '.mk') 2206 2207 spec = target_dicts[qualified_target] 2208 configs = spec['configurations'] 2209 2210 if flavor == 'mac': 2211 gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec) 2212 2213 writer = MakefileWriter(generator_flags, flavor) 2214 writer.Write(qualified_target, base_path, output_file, spec, configs, 2215 part_of_all=qualified_target in needed_targets) 2216 2217 # Our root_makefile lives at the source root. Compute the relative path 2218 # from there to the output_file for including. 2219 mkfile_rel_path = gyp.common.RelativePath(output_file, 2220 os.path.dirname(makefile_path)) 2221 include_list.add(mkfile_rel_path) 2222 2223 # Write out per-gyp (sub-project) Makefiles. 2224 depth_rel_path = gyp.common.RelativePath(options.depth, os.getcwd()) 2225 for build_file in build_files: 2226 # The paths in build_files were relativized above, so undo that before 2227 # testing against the non-relativized items in target_list and before 2228 # calculating the Makefile path. 2229 build_file = os.path.join(depth_rel_path, build_file) 2230 gyp_targets = [target_dicts[target]['target_name'] for target in target_list 2231 if target.startswith(build_file) and 2232 target in needed_targets] 2233 # Only generate Makefiles for gyp files with targets. 2234 if not gyp_targets: 2235 continue 2236 base_path, output_file = CalculateMakefilePath(build_file, 2237 os.path.splitext(os.path.basename(build_file))[0] + '.Makefile') 2238 makefile_rel_path = gyp.common.RelativePath(os.path.dirname(makefile_path), 2239 os.path.dirname(output_file)) 2240 writer.WriteSubMake(output_file, makefile_rel_path, gyp_targets, 2241 builddir_name) 2242 2243 2244 # Write out the sorted list of includes. 2245 root_makefile.write('\n') 2246 for include_file in sorted(include_list): 2247 # We wrap each .mk include in an if statement so users can tell make to 2248 # not load a file by setting NO_LOAD. The below make code says, only 2249 # load the .mk file if the .mk filename doesn't start with a token in 2250 # NO_LOAD. 2251 root_makefile.write( 2252 "ifeq ($(strip $(foreach prefix,$(NO_LOAD),\\\n" 2253 " $(findstring $(join ^,$(prefix)),\\\n" 2254 " $(join ^," + include_file + ")))),)\n") 2255 root_makefile.write(" include " + include_file + "\n") 2256 root_makefile.write("endif\n") 2257 root_makefile.write('\n') 2258 2259 if (not generator_flags.get('standalone') 2260 and generator_flags.get('auto_regeneration', True)): 2261 WriteAutoRegenerationRule(params, root_makefile, makefile_name, build_files) 2262 2263 root_makefile.write(SHARED_FOOTER) 2264 2265 root_makefile.close() 2266