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