123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745 |
- # Copyright (c) 2013 Google Inc. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- # Notes:
- #
- # This is all roughly based on the Makefile system used by the Linux
- # kernel, but is a non-recursive make -- we put the entire dependency
- # graph in front of make and let it figure it out.
- #
- # The code below generates a separate .mk file for each target, but
- # all are sourced by the top-level Makefile. This means that all
- # variables in .mk-files clobber one another. Be careful to use :=
- # where appropriate for immediate evaluation, and similarly to watch
- # that you're not relying on a variable value to last between different
- # .mk files.
- #
- # TODOs:
- #
- # Global settings and utility functions are currently stuffed in the
- # toplevel Makefile. It may make sense to generate some .mk files on
- # the side to keep the files readable.
- import os
- import re
- import subprocess
- import sys
- import gyp
- import gyp.common
- import gyp.xcode_emulation
- from gyp.common import GetEnvironFallback
- import hashlib
- generator_default_variables = {
- "EXECUTABLE_PREFIX": "",
- "EXECUTABLE_SUFFIX": "",
- "STATIC_LIB_PREFIX": "lib",
- "SHARED_LIB_PREFIX": "lib",
- "STATIC_LIB_SUFFIX": ".a",
- "INTERMEDIATE_DIR": "$(obj).$(TOOLSET)/$(TARGET)/geni",
- "SHARED_INTERMEDIATE_DIR": "$(obj)/gen",
- "PRODUCT_DIR": "$(builddir)",
- "RULE_INPUT_ROOT": "%(INPUT_ROOT)s", # This gets expanded by Python.
- "RULE_INPUT_DIRNAME": "%(INPUT_DIRNAME)s", # This gets expanded by Python.
- "RULE_INPUT_PATH": "$(abspath $<)",
- "RULE_INPUT_EXT": "$(suffix $<)",
- "RULE_INPUT_NAME": "$(notdir $<)",
- "CONFIGURATION_NAME": "$(BUILDTYPE)",
- }
- # Make supports multiple toolsets
- generator_supports_multiple_toolsets = gyp.common.CrossCompileRequested()
- # Request sorted dependencies in the order from dependents to dependencies.
- generator_wants_sorted_dependencies = False
- # Placates pylint.
- generator_additional_non_configuration_keys = []
- generator_additional_path_sections = []
- generator_extra_sources_for_rules = []
- generator_filelist_paths = None
- def CalculateVariables(default_variables, params):
- """Calculate additional variables for use in the build (called by gyp)."""
- flavor = gyp.common.GetFlavor(params)
- if flavor == "mac":
- default_variables.setdefault("OS", "mac")
- default_variables.setdefault("SHARED_LIB_SUFFIX", ".dylib")
- default_variables.setdefault(
- "SHARED_LIB_DIR", generator_default_variables["PRODUCT_DIR"]
- )
- default_variables.setdefault(
- "LIB_DIR", generator_default_variables["PRODUCT_DIR"]
- )
- # Copy additional generator configuration data from Xcode, which is shared
- # by the Mac Make generator.
- import gyp.generator.xcode as xcode_generator
- global generator_additional_non_configuration_keys
- generator_additional_non_configuration_keys = getattr(
- xcode_generator, "generator_additional_non_configuration_keys", []
- )
- global generator_additional_path_sections
- generator_additional_path_sections = getattr(
- xcode_generator, "generator_additional_path_sections", []
- )
- global generator_extra_sources_for_rules
- generator_extra_sources_for_rules = getattr(
- xcode_generator, "generator_extra_sources_for_rules", []
- )
- COMPILABLE_EXTENSIONS.update({".m": "objc", ".mm": "objcxx"})
- else:
- operating_system = flavor
- if flavor == "android":
- operating_system = "linux" # Keep this legacy behavior for now.
- default_variables.setdefault("OS", operating_system)
- if flavor == "aix":
- default_variables.setdefault("SHARED_LIB_SUFFIX", ".a")
- elif flavor == "zos":
- default_variables.setdefault("SHARED_LIB_SUFFIX", ".x")
- COMPILABLE_EXTENSIONS.update({".pli": "pli"})
- else:
- default_variables.setdefault("SHARED_LIB_SUFFIX", ".so")
- default_variables.setdefault("SHARED_LIB_DIR", "$(builddir)/lib.$(TOOLSET)")
- default_variables.setdefault("LIB_DIR", "$(obj).$(TOOLSET)")
- def CalculateGeneratorInputInfo(params):
- """Calculate the generator specific info that gets fed to input (called by
- gyp)."""
- generator_flags = params.get("generator_flags", {})
- android_ndk_version = generator_flags.get("android_ndk_version", None)
- # Android NDK requires a strict link order.
- if android_ndk_version:
- global generator_wants_sorted_dependencies
- generator_wants_sorted_dependencies = True
- output_dir = params["options"].generator_output or params["options"].toplevel_dir
- builddir_name = generator_flags.get("output_dir", "out")
- qualified_out_dir = os.path.normpath(
- os.path.join(output_dir, builddir_name, "gypfiles")
- )
- global generator_filelist_paths
- generator_filelist_paths = {
- "toplevel": params["options"].toplevel_dir,
- "qualified_out_dir": qualified_out_dir,
- }
- # The .d checking code below uses these functions:
- # wildcard, sort, foreach, shell, wordlist
- # wildcard can handle spaces, the rest can't.
- # Since I could find no way to make foreach work with spaces in filenames
- # correctly, the .d files have spaces replaced with another character. The .d
- # file for
- # Chromium\ Framework.framework/foo
- # is for example
- # out/Release/.deps/out/Release/Chromium?Framework.framework/foo
- # This is the replacement character.
- SPACE_REPLACEMENT = "?"
- LINK_COMMANDS_LINUX = """\
- quiet_cmd_alink = AR($(TOOLSET)) $@
- cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) crs $@ $(filter %.o,$^)
- quiet_cmd_alink_thin = AR($(TOOLSET)) $@
- cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) crsT $@ $(filter %.o,$^)
- # Due to circular dependencies between libraries :(, we wrap the
- # special "figure out circular dependencies" flags around the entire
- # input list during linking.
- quiet_cmd_link = LINK($(TOOLSET)) $@
- cmd_link = $(LINK.$(TOOLSET)) -o $@ $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,--start-group $(LD_INPUTS) $(LIBS) -Wl,--end-group
- # Note: this does not handle spaces in paths
- define xargs
- $(1) $(word 1,$(2))
- $(if $(word 2,$(2)),$(call xargs,$(1),$(wordlist 2,$(words $(2)),$(2))))
- endef
- define write-to-file
- @: >$(1)
- $(call xargs,@printf "%s\\n" >>$(1),$(2))
- endef
- OBJ_FILE_LIST := ar-file-list
- define create_archive
- rm -f $(1) $(1).$(OBJ_FILE_LIST); mkdir -p `dirname $(1)`
- $(call write-to-file,$(1).$(OBJ_FILE_LIST),$(filter %.o,$(2)))
- $(AR.$(TOOLSET)) crs $(1) @$(1).$(OBJ_FILE_LIST)
- endef
- define create_thin_archive
- rm -f $(1) $(OBJ_FILE_LIST); mkdir -p `dirname $(1)`
- $(call write-to-file,$(1).$(OBJ_FILE_LIST),$(filter %.o,$(2)))
- $(AR.$(TOOLSET)) crsT $(1) @$(1).$(OBJ_FILE_LIST)
- endef
- # We support two kinds of shared objects (.so):
- # 1) shared_library, which is just bundling together many dependent libraries
- # into a link line.
- # 2) loadable_module, which is generating a module intended for dlopen().
- #
- # They differ only slightly:
- # In the former case, we want to package all dependent code into the .so.
- # In the latter case, we want to package just the API exposed by the
- # outermost module.
- # This means shared_library uses --whole-archive, while loadable_module doesn't.
- # (Note that --whole-archive is incompatible with the --start-group used in
- # normal linking.)
- # Other shared-object link notes:
- # - Set SONAME to the library filename so our binaries don't reference
- # the local, absolute paths used on the link command-line.
- quiet_cmd_solink = SOLINK($(TOOLSET)) $@
- cmd_solink = $(LINK.$(TOOLSET)) -o $@ -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS)
- quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
- cmd_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)
- """ # noqa: E501
- LINK_COMMANDS_MAC = """\
- quiet_cmd_alink = LIBTOOL-STATIC $@
- cmd_alink = rm -f $@ && %(python)s gyp-mac-tool filter-libtool libtool $(GYP_LIBTOOLFLAGS) -static -o $@ $(filter %%.o,$^)
- quiet_cmd_link = LINK($(TOOLSET)) $@
- cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS)
- quiet_cmd_solink = SOLINK($(TOOLSET)) $@
- cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS)
- quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
- cmd_solink_module = $(LINK.$(TOOLSET)) -bundle $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS)
- """ % {'python': sys.executable} # noqa: E501
- LINK_COMMANDS_ANDROID = """\
- quiet_cmd_alink = AR($(TOOLSET)) $@
- cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) crs $@ $(filter %.o,$^)
- quiet_cmd_alink_thin = AR($(TOOLSET)) $@
- cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) crsT $@ $(filter %.o,$^)
- # Note: this does not handle spaces in paths
- define xargs
- $(1) $(word 1,$(2))
- $(if $(word 2,$(2)),$(call xargs,$(1),$(wordlist 2,$(words $(2)),$(2))))
- endef
- define write-to-file
- @: >$(1)
- $(call xargs,@printf "%s\\n" >>$(1),$(2))
- endef
- OBJ_FILE_LIST := ar-file-list
- define create_archive
- rm -f $(1) $(1).$(OBJ_FILE_LIST); mkdir -p `dirname $(1)`
- $(call write-to-file,$(1).$(OBJ_FILE_LIST),$(filter %.o,$(2)))
- $(AR.$(TOOLSET)) crs $(1) @$(1).$(OBJ_FILE_LIST)
- endef
- define create_thin_archive
- rm -f $(1) $(OBJ_FILE_LIST); mkdir -p `dirname $(1)`
- $(call write-to-file,$(1).$(OBJ_FILE_LIST),$(filter %.o,$(2)))
- $(AR.$(TOOLSET)) crsT $(1) @$(1).$(OBJ_FILE_LIST)
- endef
- # Due to circular dependencies between libraries :(, we wrap the
- # special "figure out circular dependencies" flags around the entire
- # input list during linking.
- quiet_cmd_link = LINK($(TOOLSET)) $@
- quiet_cmd_link_host = LINK($(TOOLSET)) $@
- cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ -Wl,--start-group $(LD_INPUTS) -Wl,--end-group $(LIBS)
- cmd_link_host = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ -Wl,--start-group $(LD_INPUTS) -Wl,--end-group $(LIBS)
- # Other shared-object link notes:
- # - Set SONAME to the library filename so our binaries don't reference
- # the local, absolute paths used on the link command-line.
- quiet_cmd_solink = SOLINK($(TOOLSET)) $@
- cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS)
- quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
- cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ -Wl,--start-group $(filter-out FORCE_DO_CMD, $^) -Wl,--end-group $(LIBS)
- quiet_cmd_solink_module_host = SOLINK_MODULE($(TOOLSET)) $@
- cmd_solink_module_host = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS)
- """ # noqa: E501
- LINK_COMMANDS_AIX = """\
- quiet_cmd_alink = AR($(TOOLSET)) $@
- cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) -X32_64 crs $@ $(filter %.o,$^)
- quiet_cmd_alink_thin = AR($(TOOLSET)) $@
- cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) -X32_64 crs $@ $(filter %.o,$^)
- quiet_cmd_link = LINK($(TOOLSET)) $@
- cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS)
- quiet_cmd_solink = SOLINK($(TOOLSET)) $@
- cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS)
- quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
- cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS)
- """ # noqa: E501
- LINK_COMMANDS_OS400 = """\
- quiet_cmd_alink = AR($(TOOLSET)) $@
- cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) -X64 crs $@ $(filter %.o,$^)
- quiet_cmd_alink_thin = AR($(TOOLSET)) $@
- cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) -X64 crs $@ $(filter %.o,$^)
- quiet_cmd_link = LINK($(TOOLSET)) $@
- cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS)
- quiet_cmd_solink = SOLINK($(TOOLSET)) $@
- cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS)
- quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
- cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS)
- """ # noqa: E501
- LINK_COMMANDS_OS390 = """\
- quiet_cmd_alink = AR($(TOOLSET)) $@
- cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) crs $@ $(filter %.o,$^)
- quiet_cmd_alink_thin = AR($(TOOLSET)) $@
- cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) crsT $@ $(filter %.o,$^)
- quiet_cmd_link = LINK($(TOOLSET)) $@
- cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS)
- quiet_cmd_solink = SOLINK($(TOOLSET)) $@
- cmd_solink = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS)
- quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
- cmd_solink_module = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS)
- """ # noqa: E501
- # Header of toplevel Makefile.
- # This should go into the build tree, but it's easier to keep it here for now.
- SHARED_HEADER = (
- """\
- # We borrow heavily from the kernel build setup, though we are simpler since
- # we don't have Kconfig tweaking settings on us.
- # The implicit make rules have it looking for RCS files, among other things.
- # We instead explicitly write all the rules we care about.
- # It's even quicker (saves ~200ms) to pass -r on the command line.
- MAKEFLAGS=-r
- # The source directory tree.
- srcdir := %(srcdir)s
- abs_srcdir := $(abspath $(srcdir))
- # The name of the builddir.
- builddir_name ?= %(builddir)s
- # The V=1 flag on command line makes us verbosely print command lines.
- ifdef V
- quiet=
- else
- quiet=quiet_
- endif
- # Specify BUILDTYPE=Release on the command line for a release build.
- BUILDTYPE ?= %(default_configuration)s
- # Directory all our build output goes into.
- # Note that this must be two directories beneath src/ for unit tests to pass,
- # as they reach into the src/ directory for data with relative paths.
- builddir ?= $(builddir_name)/$(BUILDTYPE)
- abs_builddir := $(abspath $(builddir))
- depsdir := $(builddir)/.deps
- # Object output directory.
- obj := $(builddir)/obj
- abs_obj := $(abspath $(obj))
- # We build up a list of every single one of the targets so we can slurp in the
- # generated dependency rule Makefiles in one pass.
- all_deps :=
- %(make_global_settings)s
- CC.target ?= %(CC.target)s
- CFLAGS.target ?= $(CPPFLAGS) $(CFLAGS)
- CXX.target ?= %(CXX.target)s
- CXXFLAGS.target ?= $(CPPFLAGS) $(CXXFLAGS)
- LINK.target ?= %(LINK.target)s
- LDFLAGS.target ?= $(LDFLAGS)
- AR.target ?= %(AR.target)s
- PLI.target ?= %(PLI.target)s
- # C++ apps need to be linked with g++.
- LINK ?= $(CXX.target)
- # TODO(evan): move all cross-compilation logic to gyp-time so we don't need
- # to replicate this environment fallback in make as well.
- CC.host ?= %(CC.host)s
- CFLAGS.host ?= $(CPPFLAGS_host) $(CFLAGS_host)
- CXX.host ?= %(CXX.host)s
- CXXFLAGS.host ?= $(CPPFLAGS_host) $(CXXFLAGS_host)
- LINK.host ?= %(LINK.host)s
- LDFLAGS.host ?= $(LDFLAGS_host)
- AR.host ?= %(AR.host)s
- PLI.host ?= %(PLI.host)s
- # Define a dir function that can handle spaces.
- # http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions
- # "leading spaces cannot appear in the text of the first argument as written.
- # These characters can be put into the argument value by variable substitution."
- empty :=
- space := $(empty) $(empty)
- # http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces
- replace_spaces = $(subst $(space),"""
- + SPACE_REPLACEMENT
- + """,$1)
- unreplace_spaces = $(subst """
- + SPACE_REPLACEMENT
- + """,$(space),$1)
- dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1)))
- # Flags to make gcc output dependency info. Note that you need to be
- # careful here to use the flags that ccache and distcc can understand.
- # We write to a dep file on the side first and then rename at the end
- # so we can't end up with a broken dep file.
- depfile = $(depsdir)/$(call replace_spaces,$@).d
- DEPFLAGS = %(makedep_args)s -MF $(depfile).raw
- # We have to fixup the deps output in a few ways.
- # (1) the file output should mention the proper .o file.
- # ccache or distcc lose the path to the target, so we convert a rule of
- # the form:
- # foobar.o: DEP1 DEP2
- # into
- # path/to/foobar.o: DEP1 DEP2
- # (2) we want missing files not to cause us to fail to build.
- # We want to rewrite
- # foobar.o: DEP1 DEP2 \\
- # DEP3
- # to
- # DEP1:
- # DEP2:
- # DEP3:
- # so if the files are missing, they're just considered phony rules.
- # We have to do some pretty insane escaping to get those backslashes
- # and dollar signs past make, the shell, and sed at the same time.
- # Doesn't work with spaces, but that's fine: .d files have spaces in
- # their names replaced with other characters."""
- r"""
- define fixup_dep
- # The depfile may not exist if the input file didn't have any #includes.
- touch $(depfile).raw
- # Fixup path as in (1).""" +
- (r"""
- sed -e "s|^$(notdir $@)|$@|" -re 's/\\\\([^$$])/\/\1/g' $(depfile).raw >> $(depfile)"""
- if sys.platform == 'win32' else r"""
- sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile)""") +
- r"""
- # Add extra rules as in (2).
- # We remove slashes and replace spaces with new lines;
- # remove blank lines;
- # delete the first line and append a colon to the remaining lines.""" +
- ("""
- sed -e 's/\\\\\\\\$$//' -e 's/\\\\\\\\/\\//g' -e 'y| |\\n|' $(depfile).raw |\\"""
- if sys.platform == 'win32' else """
- sed -e 's|\\\\||' -e 'y| |\\n|' $(depfile).raw |\\""") +
- r"""
- grep -v '^$$' |\
- sed -e 1d -e 's|$$|:|' \
- >> $(depfile)
- rm $(depfile).raw
- endef
- """
- """
- # Command definitions:
- # - cmd_foo is the actual command to run;
- # - quiet_cmd_foo is the brief-output summary of the command.
- quiet_cmd_cc = CC($(TOOLSET)) $@
- cmd_cc = $(CC.$(TOOLSET)) -o $@ $< $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c
- quiet_cmd_cxx = CXX($(TOOLSET)) $@
- cmd_cxx = $(CXX.$(TOOLSET)) -o $@ $< $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c
- %(extra_commands)s
- quiet_cmd_touch = TOUCH $@
- cmd_touch = touch $@
- quiet_cmd_copy = COPY $@
- # send stderr to /dev/null to ignore messages when linking directories.
- cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp %(copy_archive_args)s "$<" "$@")
- quiet_cmd_symlink = SYMLINK $@
- cmd_symlink = ln -sf "$<" "$@"
- %(link_commands)s
- """ # noqa: E501
- r"""
- # Define an escape_quotes function to escape single quotes.
- # This allows us to handle quotes properly as long as we always use
- # use single quotes and escape_quotes.
- escape_quotes = $(subst ','\'',$(1))
- # This comment is here just to include a ' to unconfuse syntax highlighting.
- # Define an escape_vars function to escape '$' variable syntax.
- # This allows us to read/write command lines with shell variables (e.g.
- # $LD_LIBRARY_PATH), without triggering make substitution.
- escape_vars = $(subst $$,$$$$,$(1))
- # Helper that expands to a shell command to echo a string exactly as it is in
- # make. This uses printf instead of echo because printf's behaviour with respect
- # to escape sequences is more portable than echo's across different shells
- # (e.g., dash, bash).
- exact_echo = printf '%%s\n' '$(call escape_quotes,$(1))'
- """
- """
- # Helper to compare the command we're about to run against the command
- # we logged the last time we ran the command. Produces an empty
- # string (false) when the commands match.
- # Tricky point: Make has no string-equality test function.
- # The kernel uses the following, but it seems like it would have false
- # positives, where one string reordered its arguments.
- # arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \\
- # $(filter-out $(cmd_$@), $(cmd_$(1))))
- # We instead substitute each for the empty string into the other, and
- # say they're equal if both substitutions produce the empty string.
- # .d files contain """
- + SPACE_REPLACEMENT
- + """ instead of spaces, take that into account.
- command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\\
- $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1))))
- # Helper that is non-empty when a prerequisite changes.
- # Normally make does this implicitly, but we force rules to always run
- # so we can check their command lines.
- # $? -- new prerequisites
- # $| -- order-only dependencies
- prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?))
- # Helper that executes all postbuilds until one fails.
- define do_postbuilds
- @E=0;\\
- for p in $(POSTBUILDS); do\\
- eval $$p;\\
- E=$$?;\\
- if [ $$E -ne 0 ]; then\\
- break;\\
- fi;\\
- done;\\
- if [ $$E -ne 0 ]; then\\
- rm -rf "$@";\\
- exit $$E;\\
- fi
- endef
- # do_cmd: run a command via the above cmd_foo names, if necessary.
- # Should always run for a given target to handle command-line changes.
- # Second argument, if non-zero, makes it do asm/C/C++ dependency munging.
- # Third argument, if non-zero, makes it do POSTBUILDS processing.
- # Note: We intentionally do NOT call dirx for depfile, since it contains """
- + SPACE_REPLACEMENT
- + """ for
- # spaces already and dirx strips the """
- + SPACE_REPLACEMENT
- + """ characters.
- define do_cmd
- $(if $(or $(command_changed),$(prereq_changed)),
- @$(call exact_echo, $($(quiet)cmd_$(1)))
- @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))"
- $(if $(findstring flock,$(word %(flock_index)d,$(cmd_$1))),
- @$(cmd_$(1))
- @echo " $(quiet_cmd_$(1)): Finished",
- @$(cmd_$(1))
- )
- @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile)
- @$(if $(2),$(fixup_dep))
- $(if $(and $(3), $(POSTBUILDS)),
- $(call do_postbuilds)
- )
- )
- endef
- # Declare the "%(default_target)s" target first so it is the default,
- # even though we don't have the deps yet.
- .PHONY: %(default_target)s
- %(default_target)s:
- # make looks for ways to re-generate included makefiles, but in our case, we
- # don't have a direct way. Explicitly telling make that it has nothing to do
- # for them makes it go faster.
- %%.d: ;
- # Use FORCE_DO_CMD to force a target to run. Should be coupled with
- # do_cmd.
- .PHONY: FORCE_DO_CMD
- FORCE_DO_CMD:
- """ # noqa: E501
- )
- SHARED_HEADER_MAC_COMMANDS = """
- quiet_cmd_objc = CXX($(TOOLSET)) $@
- cmd_objc = $(CC.$(TOOLSET)) $(GYP_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $<
- quiet_cmd_objcxx = CXX($(TOOLSET)) $@
- cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $<
- # Commands for precompiled header files.
- quiet_cmd_pch_c = CXX($(TOOLSET)) $@
- cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $<
- quiet_cmd_pch_cc = CXX($(TOOLSET)) $@
- cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $<
- quiet_cmd_pch_m = CXX($(TOOLSET)) $@
- cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $<
- quiet_cmd_pch_mm = CXX($(TOOLSET)) $@
- cmd_pch_mm = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $<
- # gyp-mac-tool is written next to the root Makefile by gyp.
- # Use $(4) for the command, since $(2) and $(3) are used as flag by do_cmd
- # already.
- quiet_cmd_mac_tool = MACTOOL $(4) $<
- cmd_mac_tool = %(python)s gyp-mac-tool $(4) $< "$@"
- quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@
- cmd_mac_package_framework = %(python)s gyp-mac-tool package-framework "$@" $(4)
- quiet_cmd_infoplist = INFOPLIST $@
- cmd_infoplist = $(CC.$(TOOLSET)) -E -P -Wno-trigraphs -x c $(INFOPLIST_DEFINES) "$<" -o "$@"
- """ % {'python': sys.executable} # noqa: E501
- def WriteRootHeaderSuffixRules(writer):
- extensions = sorted(COMPILABLE_EXTENSIONS.keys(), key=str.lower)
- writer.write("# Suffix rules, putting all outputs into $(obj).\n")
- for ext in extensions:
- writer.write("$(obj).$(TOOLSET)/%%.o: $(srcdir)/%%%s FORCE_DO_CMD\n" % ext)
- writer.write("\t@$(call do_cmd,%s,1)\n" % COMPILABLE_EXTENSIONS[ext])
- writer.write("\n# Try building from generated source, too.\n")
- for ext in extensions:
- writer.write(
- "$(obj).$(TOOLSET)/%%.o: $(obj).$(TOOLSET)/%%%s FORCE_DO_CMD\n" % ext
- )
- writer.write("\t@$(call do_cmd,%s,1)\n" % COMPILABLE_EXTENSIONS[ext])
- writer.write("\n")
- for ext in extensions:
- writer.write("$(obj).$(TOOLSET)/%%.o: $(obj)/%%%s FORCE_DO_CMD\n" % ext)
- writer.write("\t@$(call do_cmd,%s,1)\n" % COMPILABLE_EXTENSIONS[ext])
- writer.write("\n")
- SHARED_HEADER_OS390_COMMANDS = """
- PLIFLAGS.target ?= -qlp=64 -qlimits=extname=31 $(PLIFLAGS)
- PLIFLAGS.host ?= -qlp=64 -qlimits=extname=31 $(PLIFLAGS)
- quiet_cmd_pli = PLI($(TOOLSET)) $@
- cmd_pli = $(PLI.$(TOOLSET)) $(GYP_PLIFLAGS) $(PLIFLAGS.$(TOOLSET)) -c $< && \
- if [ -f $(notdir $@) ]; then /bin/cp $(notdir $@) $@; else true; fi
- """
- SHARED_HEADER_SUFFIX_RULES_COMMENT1 = """\
- # Suffix rules, putting all outputs into $(obj).
- """
- SHARED_HEADER_SUFFIX_RULES_COMMENT2 = """\
- # Try building from generated source, too.
- """
- SHARED_FOOTER = """\
- # "all" is a concatenation of the "all" targets from all the included
- # sub-makefiles. This is just here to clarify.
- all:
- # Add in dependency-tracking rules. $(all_deps) is the list of every single
- # target in our tree. Only consider the ones with .d (dependency) info:
- d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d))
- ifneq ($(d_files),)
- include $(d_files)
- endif
- """
- header = """\
- # This file is generated by gyp; do not edit.
- """
- # Maps every compilable file extension to the do_cmd that compiles it.
- COMPILABLE_EXTENSIONS = {
- ".c": "cc",
- ".cc": "cxx",
- ".cpp": "cxx",
- ".cxx": "cxx",
- ".s": "cc",
- ".S": "cc",
- }
- def Compilable(filename):
- """Return true if the file is compilable (should be in OBJS)."""
- return any(res for res in (filename.endswith(e) for e in COMPILABLE_EXTENSIONS))
- def Linkable(filename):
- """Return true if the file is linkable (should be on the link line)."""
- return filename.endswith(".o")
- def Target(filename):
- """Translate a compilable filename to its .o target."""
- return os.path.splitext(filename)[0] + ".o"
- def EscapeShellArgument(s):
- """Quotes an argument so that it will be interpreted literally by a POSIX
- shell. Taken from
- http://stackoverflow.com/questions/35817/whats-the-best-way-to-escape-ossystem-calls-in-python
- """
- return "'" + s.replace("'", "'\\''") + "'"
- def EscapeMakeVariableExpansion(s):
- """Make has its own variable expansion syntax using $. We must escape it for
- string to be interpreted literally."""
- return s.replace("$", "$$")
- def EscapeCppDefine(s):
- """Escapes a CPP define so that it will reach the compiler unaltered."""
- s = EscapeShellArgument(s)
- s = EscapeMakeVariableExpansion(s)
- # '#' characters must be escaped even embedded in a string, else Make will
- # treat it as the start of a comment.
- return s.replace("#", r"\#")
- def QuoteIfNecessary(string):
- """TODO: Should this ideally be replaced with one or more of the above
- functions?"""
- if '"' in string:
- string = '"' + string.replace('"', '\\"') + '"'
- return string
- def replace_sep(string):
- if sys.platform == 'win32':
- string = string.replace('\\\\', '/').replace('\\', '/')
- return string
- def StringToMakefileVariable(string):
- """Convert a string to a value that is acceptable as a make variable name."""
- return re.sub("[^a-zA-Z0-9_]", "_", string)
- srcdir_prefix = ""
- def Sourceify(path):
- """Convert a path to its source directory form."""
- if "$(" in path:
- return path
- if os.path.isabs(path):
- return path
- return srcdir_prefix + path
- def QuoteSpaces(s, quote=r"\ "):
- return s.replace(" ", quote)
- def SourceifyAndQuoteSpaces(path):
- """Convert a path to its source directory form and quote spaces."""
- return QuoteSpaces(Sourceify(path))
- # Map from qualified target to path to output.
- target_outputs = {}
- # Map from qualified target to any linkable output. A subset
- # of target_outputs. E.g. when mybinary depends on liba, we want to
- # include liba in the linker line; when otherbinary depends on
- # mybinary, we just want to build mybinary first.
- target_link_deps = {}
- class MakefileWriter:
- """MakefileWriter packages up the writing of one target-specific foobar.mk.
- Its only real entry point is Write(), and is mostly used for namespacing.
- """
- def __init__(self, generator_flags, flavor):
- self.generator_flags = generator_flags
- self.flavor = flavor
- self.suffix_rules_srcdir = {}
- self.suffix_rules_objdir1 = {}
- self.suffix_rules_objdir2 = {}
- # Generate suffix rules for all compilable extensions.
- for ext, value in COMPILABLE_EXTENSIONS.items():
- # Suffix rules for source folder.
- self.suffix_rules_srcdir.update(
- {
- ext: (
- """\
- $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(srcdir)/%%%s FORCE_DO_CMD
- \t@$(call do_cmd,%s,1)
- """
- % (ext, value)
- )
- }
- )
- # Suffix rules for generated source files.
- self.suffix_rules_objdir1.update(
- {
- ext: (
- """\
- $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj).$(TOOLSET)/%%%s FORCE_DO_CMD
- \t@$(call do_cmd,%s,1)
- """
- % (ext, value)
- )
- }
- )
- self.suffix_rules_objdir2.update(
- {
- ext: (
- """\
- $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
- \t@$(call do_cmd,%s,1)
- """
- % (ext, value)
- )
- }
- )
- def Write(
- self, qualified_target, base_path, output_filename, spec, configs, part_of_all
- ):
- """The main entry point: writes a .mk file for a single target.
- Arguments:
- qualified_target: target we're generating
- base_path: path relative to source root we're building in, used to resolve
- target-relative paths
- output_filename: output .mk file name to write
- spec, configs: gyp info
- part_of_all: flag indicating this target is part of 'all'
- """
- gyp.common.EnsureDirExists(output_filename)
- self.fp = open(output_filename, "w")
- self.fp.write(header)
- self.qualified_target = qualified_target
- self.path = base_path
- self.target = spec["target_name"]
- self.type = spec["type"]
- self.toolset = spec["toolset"]
- self.is_mac_bundle = gyp.xcode_emulation.IsMacBundle(self.flavor, spec)
- if self.flavor == "mac":
- self.xcode_settings = gyp.xcode_emulation.XcodeSettings(spec)
- else:
- self.xcode_settings = None
- deps, link_deps = self.ComputeDeps(spec)
- # Some of the generation below can add extra output, sources, or
- # link dependencies. All of the out params of the functions that
- # follow use names like extra_foo.
- extra_outputs = []
- extra_sources = []
- extra_link_deps = []
- extra_mac_bundle_resources = []
- mac_bundle_deps = []
- if self.is_mac_bundle:
- self.output = self.ComputeMacBundleOutput(spec)
- self.output_binary = self.ComputeMacBundleBinaryOutput(spec)
- else:
- self.output = self.output_binary = replace_sep(self.ComputeOutput(spec))
- self.is_standalone_static_library = bool(
- spec.get("standalone_static_library", 0)
- )
- self._INSTALLABLE_TARGETS = ("executable", "loadable_module", "shared_library")
- if self.is_standalone_static_library or self.type in self._INSTALLABLE_TARGETS:
- self.alias = os.path.basename(self.output)
- install_path = self._InstallableTargetInstallPath()
- else:
- self.alias = self.output
- install_path = self.output
- self.WriteLn("TOOLSET := " + self.toolset)
- self.WriteLn("TARGET := " + self.target)
- # Actions must come first, since they can generate more OBJs for use below.
- if "actions" in spec:
- self.WriteActions(
- spec["actions"],
- extra_sources,
- extra_outputs,
- extra_mac_bundle_resources,
- part_of_all,
- )
- # Rules must be early like actions.
- if "rules" in spec:
- self.WriteRules(
- spec["rules"],
- extra_sources,
- extra_outputs,
- extra_mac_bundle_resources,
- part_of_all,
- )
- if "copies" in spec:
- self.WriteCopies(spec["copies"], extra_outputs, part_of_all)
- # Bundle resources.
- if self.is_mac_bundle:
- all_mac_bundle_resources = (
- spec.get("mac_bundle_resources", []) + extra_mac_bundle_resources
- )
- self.WriteMacBundleResources(all_mac_bundle_resources, mac_bundle_deps)
- self.WriteMacInfoPlist(mac_bundle_deps)
- # Sources.
- all_sources = spec.get("sources", []) + extra_sources
- if all_sources:
- self.WriteSources(
- configs,
- deps,
- all_sources,
- extra_outputs,
- extra_link_deps,
- part_of_all,
- gyp.xcode_emulation.MacPrefixHeader(
- self.xcode_settings,
- lambda p: Sourceify(self.Absolutify(p)),
- self.Pchify,
- ),
- )
- sources = [x for x in all_sources if Compilable(x)]
- if sources:
- self.WriteLn(SHARED_HEADER_SUFFIX_RULES_COMMENT1)
- extensions = {os.path.splitext(s)[1] for s in sources}
- for ext in extensions:
- if ext in self.suffix_rules_srcdir:
- self.WriteLn(self.suffix_rules_srcdir[ext])
- self.WriteLn(SHARED_HEADER_SUFFIX_RULES_COMMENT2)
- for ext in extensions:
- if ext in self.suffix_rules_objdir1:
- self.WriteLn(self.suffix_rules_objdir1[ext])
- for ext in extensions:
- if ext in self.suffix_rules_objdir2:
- self.WriteLn(self.suffix_rules_objdir2[ext])
- self.WriteLn("# End of this set of suffix rules")
- # Add dependency from bundle to bundle binary.
- if self.is_mac_bundle:
- mac_bundle_deps.append(self.output_binary)
- self.WriteTarget(
- spec,
- configs,
- deps,
- extra_link_deps + link_deps,
- mac_bundle_deps,
- extra_outputs,
- part_of_all,
- )
- # Update global list of target outputs, used in dependency tracking.
- target_outputs[qualified_target] = install_path
- # Update global list of link dependencies.
- if self.type in ("static_library", "shared_library"):
- target_link_deps[qualified_target] = self.output_binary
- # Currently any versions have the same effect, but in future the behavior
- # could be different.
- if self.generator_flags.get("android_ndk_version", None):
- self.WriteAndroidNdkModuleRule(self.target, all_sources, link_deps)
- self.fp.close()
- def WriteSubMake(self, output_filename, makefile_path, targets, build_dir):
- """Write a "sub-project" Makefile.
- This is a small, wrapper Makefile that calls the top-level Makefile to build
- the targets from a single gyp file (i.e. a sub-project).
- Arguments:
- output_filename: sub-project Makefile name to write
- makefile_path: path to the top-level Makefile
- targets: list of "all" targets for this sub-project
- build_dir: build output directory, relative to the sub-project
- """
- gyp.common.EnsureDirExists(output_filename)
- self.fp = open(output_filename, "w")
- self.fp.write(header)
- # For consistency with other builders, put sub-project build output in the
- # sub-project dir (see test/subdirectory/gyptest-subdir-all.py).
- self.WriteLn(
- "export builddir_name ?= %s"
- % replace_sep(os.path.join(os.path.dirname(output_filename), build_dir))
- )
- self.WriteLn(".PHONY: all")
- self.WriteLn("all:")
- if makefile_path:
- makefile_path = " -C " + makefile_path
- self.WriteLn("\t$(MAKE){} {}".format(makefile_path, " ".join(targets)))
- self.fp.close()
- def WriteActions(
- self,
- actions,
- extra_sources,
- extra_outputs,
- extra_mac_bundle_resources,
- part_of_all,
- ):
- """Write Makefile code for any 'actions' from the gyp input.
- extra_sources: a list that will be filled in with newly generated source
- files, if any
- extra_outputs: a list that will be filled in with any outputs of these
- actions (used to make other pieces dependent on these
- actions)
- part_of_all: flag indicating this target is part of 'all'
- """
- env = self.GetSortedXcodeEnv()
- for action in actions:
- name = StringToMakefileVariable(
- "{}_{}".format(self.qualified_target, action["action_name"])
- )
- self.WriteLn('### Rules for action "%s":' % action["action_name"])
- inputs = action["inputs"]
- outputs = action["outputs"]
- # Build up a list of outputs.
- # Collect the output dirs we'll need.
- dirs = set()
- for out in outputs:
- dir = os.path.split(out)[0]
- if dir:
- dirs.add(dir)
- if int(action.get("process_outputs_as_sources", False)):
- extra_sources += outputs
- if int(action.get("process_outputs_as_mac_bundle_resources", False)):
- extra_mac_bundle_resources += outputs
- # Write the actual command.
- action_commands = action["action"]
- if self.flavor == "mac":
- action_commands = [
- gyp.xcode_emulation.ExpandEnvVars(command, env)
- for command in action_commands
- ]
- command = gyp.common.EncodePOSIXShellList(action_commands)
- if "message" in action:
- self.WriteLn(
- "quiet_cmd_{} = ACTION {} $@".format(name, action["message"])
- )
- else:
- self.WriteLn(f"quiet_cmd_{name} = ACTION {name} $@")
- if len(dirs) > 0:
- command = "mkdir -p %s" % " ".join(dirs) + "; " + command
- cd_action = "cd %s; " % Sourceify(self.path or ".")
- # command and cd_action get written to a toplevel variable called
- # cmd_foo. Toplevel variables can't handle things that change per
- # makefile like $(TARGET), so hardcode the target.
- command = command.replace("$(TARGET)", self.target)
- cd_action = cd_action.replace("$(TARGET)", self.target)
- # Set LD_LIBRARY_PATH in case the action runs an executable from this
- # build which links to shared libs from this build.
- # actions run on the host, so they should in theory only use host
- # libraries, but until everything is made cross-compile safe, also use
- # target libraries.
- # TODO(piman): when everything is cross-compile safe, remove lib.target
- if self.flavor in {"zos", "aix"}:
- self.WriteLn(
- "cmd_%s = LIBPATH=$(builddir)/lib.host:"
- "$(builddir)/lib.target:$$LIBPATH; "
- "export LIBPATH; "
- "%s%s" % (name, cd_action, command)
- )
- else:
- self.WriteLn(
- "cmd_%s = LD_LIBRARY_PATH=$(builddir)/lib.host:"
- "$(builddir)/lib.target:$$LD_LIBRARY_PATH; "
- "export LD_LIBRARY_PATH; "
- "%s%s" % (name, cd_action, command)
- )
- self.WriteLn()
- outputs = [self.Absolutify(o) for o in outputs]
- # The makefile rules are all relative to the top dir, but the gyp actions
- # are defined relative to their containing dir. This replaces the obj
- # variable for the action rule with an absolute version so that the output
- # goes in the right place.
- # Only write the 'obj' and 'builddir' rules for the "primary" output (:1);
- # it's superfluous for the "extra outputs", and this avoids accidentally
- # writing duplicate dummy rules for those outputs.
- # Same for environment.
- self.WriteLn("%s: obj := $(abs_obj)" % QuoteSpaces(outputs[0]))
- self.WriteLn("%s: builddir := $(abs_builddir)" % QuoteSpaces(outputs[0]))
- self.WriteSortedXcodeEnv(outputs[0], self.GetSortedXcodeEnv())
- for input in inputs:
- assert " " not in input, (
- "Spaces in action input filenames not supported (%s)" % input
- )
- for output in outputs:
- assert " " not in output, (
- "Spaces in action output filenames not supported (%s)" % output
- )
- # See the comment in WriteCopies about expanding env vars.
- outputs = [gyp.xcode_emulation.ExpandEnvVars(o, env) for o in outputs]
- inputs = [gyp.xcode_emulation.ExpandEnvVars(i, env) for i in inputs]
- self.WriteDoCmd(
- outputs,
- [Sourceify(self.Absolutify(i)) for i in inputs],
- part_of_all=part_of_all,
- command=name,
- )
- # Stuff the outputs in a variable so we can refer to them later.
- outputs_variable = "action_%s_outputs" % name
- self.WriteLn("{} := {}".format(outputs_variable, " ".join(outputs)))
- extra_outputs.append("$(%s)" % outputs_variable)
- self.WriteLn()
- self.WriteLn()
- def WriteRules(
- self,
- rules,
- extra_sources,
- extra_outputs,
- extra_mac_bundle_resources,
- part_of_all,
- ):
- """Write Makefile code for any 'rules' from the gyp input.
- extra_sources: a list that will be filled in with newly generated source
- files, if any
- extra_outputs: a list that will be filled in with any outputs of these
- rules (used to make other pieces dependent on these rules)
- part_of_all: flag indicating this target is part of 'all'
- """
- env = self.GetSortedXcodeEnv()
- for rule in rules:
- name = StringToMakefileVariable(
- "{}_{}".format(self.qualified_target, rule["rule_name"])
- )
- count = 0
- self.WriteLn("### Generated for rule %s:" % name)
- all_outputs = []
- for rule_source in rule.get("rule_sources", []):
- dirs = set()
- (rule_source_dirname, rule_source_basename) = os.path.split(rule_source)
- (rule_source_root, rule_source_ext) = os.path.splitext(
- rule_source_basename
- )
- outputs = [
- self.ExpandInputRoot(out, rule_source_root, rule_source_dirname)
- for out in rule["outputs"]
- ]
- for out in outputs:
- dir = os.path.dirname(out)
- if dir:
- dirs.add(dir)
- if int(rule.get("process_outputs_as_sources", False)):
- extra_sources += outputs
- if int(rule.get("process_outputs_as_mac_bundle_resources", False)):
- extra_mac_bundle_resources += outputs
- inputs = [
- Sourceify(self.Absolutify(i))
- for i in [rule_source] + rule.get("inputs", [])
- ]
- actions = ["$(call do_cmd,%s_%d)" % (name, count)]
- if name == "resources_grit":
- # HACK: This is ugly. Grit intentionally doesn't touch the
- # timestamp of its output file when the file doesn't change,
- # which is fine in hash-based dependency systems like scons
- # and forge, but not kosher in the make world. After some
- # discussion, hacking around it here seems like the least
- # amount of pain.
- actions += ["@touch --no-create $@"]
- # See the comment in WriteCopies about expanding env vars.
- outputs = [gyp.xcode_emulation.ExpandEnvVars(o, env) for o in outputs]
- inputs = [gyp.xcode_emulation.ExpandEnvVars(i, env) for i in inputs]
- outputs = [self.Absolutify(o) for o in outputs]
- all_outputs += outputs
- # Only write the 'obj' and 'builddir' rules for the "primary" output
- # (:1); it's superfluous for the "extra outputs", and this avoids
- # accidentally writing duplicate dummy rules for those outputs.
- self.WriteLn("%s: obj := $(abs_obj)" % outputs[0])
- self.WriteLn("%s: builddir := $(abs_builddir)" % outputs[0])
- self.WriteMakeRule(
- outputs, inputs, actions, command="%s_%d" % (name, count)
- )
- # Spaces in rule filenames are not supported, but rule variables have
- # spaces in them (e.g. RULE_INPUT_PATH expands to '$(abspath $<)').
- # The spaces within the variables are valid, so remove the variables
- # before checking.
- variables_with_spaces = re.compile(r"\$\([^ ]* \$<\)")
- for output in outputs:
- output = re.sub(variables_with_spaces, "", output)
- assert " " not in output, (
- "Spaces in rule filenames not yet supported (%s)" % output
- )
- self.WriteLn("all_deps += %s" % " ".join(outputs))
- action = [
- self.ExpandInputRoot(ac, rule_source_root, rule_source_dirname)
- for ac in rule["action"]
- ]
- mkdirs = ""
- if len(dirs) > 0:
- mkdirs = "mkdir -p %s; " % " ".join(dirs)
- cd_action = "cd %s; " % Sourceify(self.path or ".")
- # action, cd_action, and mkdirs get written to a toplevel variable
- # called cmd_foo. Toplevel variables can't handle things that change
- # per makefile like $(TARGET), so hardcode the target.
- if self.flavor == "mac":
- action = [
- gyp.xcode_emulation.ExpandEnvVars(command, env)
- for command in action
- ]
- action = gyp.common.EncodePOSIXShellList(action)
- action = action.replace("$(TARGET)", self.target)
- cd_action = cd_action.replace("$(TARGET)", self.target)
- mkdirs = mkdirs.replace("$(TARGET)", self.target)
- # Set LD_LIBRARY_PATH in case the rule runs an executable from this
- # build which links to shared libs from this build.
- # rules run on the host, so they should in theory only use host
- # libraries, but until everything is made cross-compile safe, also use
- # target libraries.
- # TODO(piman): when everything is cross-compile safe, remove lib.target
- self.WriteLn(
- "cmd_%(name)s_%(count)d = LD_LIBRARY_PATH="
- "$(builddir)/lib.host:$(builddir)/lib.target:$$LD_LIBRARY_PATH; "
- "export LD_LIBRARY_PATH; "
- "%(cd_action)s%(mkdirs)s%(action)s"
- % {
- "action": action,
- "cd_action": cd_action,
- "count": count,
- "mkdirs": mkdirs,
- "name": name,
- }
- )
- self.WriteLn(
- "quiet_cmd_%(name)s_%(count)d = RULE %(name)s_%(count)d $@"
- % {"count": count, "name": name}
- )
- self.WriteLn()
- count += 1
- outputs_variable = "rule_%s_outputs" % name
- self.WriteList(all_outputs, outputs_variable)
- extra_outputs.append("$(%s)" % outputs_variable)
- self.WriteLn("### Finished generating for rule: %s" % name)
- self.WriteLn()
- self.WriteLn("### Finished generating for all rules")
- self.WriteLn("")
- def WriteCopies(self, copies, extra_outputs, part_of_all):
- """Write Makefile code for any 'copies' from the gyp input.
- extra_outputs: a list that will be filled in with any outputs of this action
- (used to make other pieces dependent on this action)
- part_of_all: flag indicating this target is part of 'all'
- """
- self.WriteLn("### Generated for copy rule.")
- variable = StringToMakefileVariable(self.qualified_target + "_copies")
- outputs = []
- for copy in copies:
- for path in copy["files"]:
- # Absolutify() may call normpath, and will strip trailing slashes.
- path = Sourceify(self.Absolutify(path))
- filename = os.path.split(path)[1]
- output = Sourceify(
- self.Absolutify(os.path.join(copy["destination"], filename))
- )
- # If the output path has variables in it, which happens in practice for
- # 'copies', writing the environment as target-local doesn't work,
- # because the variables are already needed for the target name.
- # Copying the environment variables into global make variables doesn't
- # work either, because then the .d files will potentially contain spaces
- # after variable expansion, and .d file handling cannot handle spaces.
- # As a workaround, manually expand variables at gyp time. Since 'copies'
- # can't run scripts, there's no need to write the env then.
- # WriteDoCmd() will escape spaces for .d files.
- env = self.GetSortedXcodeEnv()
- output = gyp.xcode_emulation.ExpandEnvVars(output, env)
- path = gyp.xcode_emulation.ExpandEnvVars(path, env)
- self.WriteDoCmd([output], [path], "copy", part_of_all)
- outputs.append(output)
- self.WriteLn(
- "{} = {}".format(variable, " ".join(QuoteSpaces(o) for o in outputs))
- )
- extra_outputs.append("$(%s)" % variable)
- self.WriteLn()
- def WriteMacBundleResources(self, resources, bundle_deps):
- """Writes Makefile code for 'mac_bundle_resources'."""
- self.WriteLn("### Generated for mac_bundle_resources")
- for output, res in gyp.xcode_emulation.GetMacBundleResources(
- generator_default_variables["PRODUCT_DIR"],
- self.xcode_settings,
- [Sourceify(self.Absolutify(r)) for r in resources],
- ):
- _, ext = os.path.splitext(output)
- if ext != ".xcassets":
- # Make does not supports '.xcassets' emulation.
- self.WriteDoCmd(
- [output], [res], "mac_tool,,,copy-bundle-resource", part_of_all=True
- )
- bundle_deps.append(output)
- def WriteMacInfoPlist(self, bundle_deps):
- """Write Makefile code for bundle Info.plist files."""
- info_plist, out, defines, extra_env = gyp.xcode_emulation.GetMacInfoPlist(
- generator_default_variables["PRODUCT_DIR"],
- self.xcode_settings,
- lambda p: Sourceify(self.Absolutify(p)),
- )
- if not info_plist:
- return
- if defines:
- # Create an intermediate file to store preprocessed results.
- intermediate_plist = "$(obj).$(TOOLSET)/$(TARGET)/" + os.path.basename(
- info_plist
- )
- self.WriteList(
- defines,
- intermediate_plist + ": INFOPLIST_DEFINES",
- "-D",
- quoter=EscapeCppDefine,
- )
- self.WriteMakeRule(
- [intermediate_plist],
- [info_plist],
- [
- "$(call do_cmd,infoplist)",
- # "Convert" the plist so that any weird whitespace changes from the
- # preprocessor do not affect the XML parser in mac_tool.
- "@plutil -convert xml1 $@ $@",
- ],
- )
- info_plist = intermediate_plist
- # plists can contain envvars and substitute them into the file.
- self.WriteSortedXcodeEnv(
- out, self.GetSortedXcodeEnv(additional_settings=extra_env)
- )
- self.WriteDoCmd(
- [out], [info_plist], "mac_tool,,,copy-info-plist", part_of_all=True
- )
- bundle_deps.append(out)
- def WriteSources(
- self,
- configs,
- deps,
- sources,
- extra_outputs,
- extra_link_deps,
- part_of_all,
- precompiled_header,
- ):
- """Write Makefile code for any 'sources' from the gyp input.
- These are source files necessary to build the current target.
- configs, deps, sources: input from gyp.
- extra_outputs: a list of extra outputs this action should be dependent on;
- used to serialize action/rules before compilation
- extra_link_deps: a list that will be filled in with any outputs of
- compilation (to be used in link lines)
- part_of_all: flag indicating this target is part of 'all'
- """
- # Write configuration-specific variables for CFLAGS, etc.
- for configname in sorted(configs.keys()):
- config = configs[configname]
- self.WriteList(
- config.get("defines"),
- "DEFS_%s" % configname,
- prefix="-D",
- quoter=EscapeCppDefine,
- )
- if self.flavor == "mac":
- cflags = self.xcode_settings.GetCflags(
- configname, arch=config.get("xcode_configuration_platform")
- )
- cflags_c = self.xcode_settings.GetCflagsC(configname)
- cflags_cc = self.xcode_settings.GetCflagsCC(configname)
- cflags_objc = self.xcode_settings.GetCflagsObjC(configname)
- cflags_objcc = self.xcode_settings.GetCflagsObjCC(configname)
- else:
- cflags = config.get("cflags")
- cflags_c = config.get("cflags_c")
- cflags_cc = config.get("cflags_cc")
- self.WriteLn("# Flags passed to all source files.")
- self.WriteList(cflags, "CFLAGS_%s" % configname)
- self.WriteLn("# Flags passed to only C files.")
- self.WriteList(cflags_c, "CFLAGS_C_%s" % configname)
- self.WriteLn("# Flags passed to only C++ files.")
- self.WriteList(cflags_cc, "CFLAGS_CC_%s" % configname)
- if self.flavor == "mac":
- self.WriteLn("# Flags passed to only ObjC files.")
- self.WriteList(cflags_objc, "CFLAGS_OBJC_%s" % configname)
- self.WriteLn("# Flags passed to only ObjC++ files.")
- self.WriteList(cflags_objcc, "CFLAGS_OBJCC_%s" % configname)
- includes = config.get("include_dirs")
- if includes:
- includes = [Sourceify(self.Absolutify(i)) for i in includes]
- self.WriteList(includes, "INCS_%s" % configname, prefix="-I")
- compilable = list(filter(Compilable, sources))
- objs = [self.Objectify(self.Absolutify(Target(c))) for c in compilable]
- self.WriteList(objs, "OBJS")
- for obj in objs:
- assert " " not in obj, "Spaces in object filenames not supported (%s)" % obj
- self.WriteLn(
- "# Add to the list of files we specially track " "dependencies for."
- )
- self.WriteLn("all_deps += $(OBJS)")
- self.WriteLn()
- # Make sure our dependencies are built first.
- if deps:
- self.WriteMakeRule(
- ["$(OBJS)"],
- deps,
- comment="Make sure our dependencies are built " "before any of us.",
- order_only=True,
- )
- # Make sure the actions and rules run first.
- # If they generate any extra headers etc., the per-.o file dep tracking
- # will catch the proper rebuilds, so order only is still ok here.
- if extra_outputs:
- self.WriteMakeRule(
- ["$(OBJS)"],
- extra_outputs,
- comment="Make sure our actions/rules run " "before any of us.",
- order_only=True,
- )
- pchdeps = precompiled_header.GetObjDependencies(compilable, objs)
- if pchdeps:
- self.WriteLn("# Dependencies from obj files to their precompiled headers")
- for source, obj, gch in pchdeps:
- self.WriteLn(f"{obj}: {gch}")
- self.WriteLn("# End precompiled header dependencies")
- if objs:
- extra_link_deps.append("$(OBJS)")
- self.WriteLn(
- """\
- # CFLAGS et al overrides must be target-local.
- # See "Target-specific Variable Values" in the GNU Make manual."""
- )
- self.WriteLn("$(OBJS): TOOLSET := $(TOOLSET)")
- self.WriteLn(
- "$(OBJS): GYP_CFLAGS := "
- "$(DEFS_$(BUILDTYPE)) "
- "$(INCS_$(BUILDTYPE)) "
- "%s " % precompiled_header.GetInclude("c") + "$(CFLAGS_$(BUILDTYPE)) "
- "$(CFLAGS_C_$(BUILDTYPE))"
- )
- self.WriteLn(
- "$(OBJS): GYP_CXXFLAGS := "
- "$(DEFS_$(BUILDTYPE)) "
- "$(INCS_$(BUILDTYPE)) "
- "%s " % precompiled_header.GetInclude("cc") + "$(CFLAGS_$(BUILDTYPE)) "
- "$(CFLAGS_CC_$(BUILDTYPE))"
- )
- if self.flavor == "mac":
- self.WriteLn(
- "$(OBJS): GYP_OBJCFLAGS := "
- "$(DEFS_$(BUILDTYPE)) "
- "$(INCS_$(BUILDTYPE)) "
- "%s " % precompiled_header.GetInclude("m")
- + "$(CFLAGS_$(BUILDTYPE)) "
- "$(CFLAGS_C_$(BUILDTYPE)) "
- "$(CFLAGS_OBJC_$(BUILDTYPE))"
- )
- self.WriteLn(
- "$(OBJS): GYP_OBJCXXFLAGS := "
- "$(DEFS_$(BUILDTYPE)) "
- "$(INCS_$(BUILDTYPE)) "
- "%s " % precompiled_header.GetInclude("mm")
- + "$(CFLAGS_$(BUILDTYPE)) "
- "$(CFLAGS_CC_$(BUILDTYPE)) "
- "$(CFLAGS_OBJCC_$(BUILDTYPE))"
- )
- self.WritePchTargets(precompiled_header.GetPchBuildCommands())
- # If there are any object files in our input file list, link them into our
- # output.
- extra_link_deps += [source for source in sources if Linkable(source)]
- self.WriteLn()
- def WritePchTargets(self, pch_commands):
- """Writes make rules to compile prefix headers."""
- if not pch_commands:
- return
- for gch, lang_flag, lang, input in pch_commands:
- extra_flags = {
- "c": "$(CFLAGS_C_$(BUILDTYPE))",
- "cc": "$(CFLAGS_CC_$(BUILDTYPE))",
- "m": "$(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE))",
- "mm": "$(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE))",
- }[lang]
- var_name = {
- "c": "GYP_PCH_CFLAGS",
- "cc": "GYP_PCH_CXXFLAGS",
- "m": "GYP_PCH_OBJCFLAGS",
- "mm": "GYP_PCH_OBJCXXFLAGS",
- }[lang]
- self.WriteLn(
- f"{gch}: {var_name} := {lang_flag} " + "$(DEFS_$(BUILDTYPE)) "
- "$(INCS_$(BUILDTYPE)) "
- "$(CFLAGS_$(BUILDTYPE)) " + extra_flags
- )
- self.WriteLn(f"{gch}: {input} FORCE_DO_CMD")
- self.WriteLn("\t@$(call do_cmd,pch_%s,1)" % lang)
- self.WriteLn("")
- assert " " not in gch, "Spaces in gch filenames not supported (%s)" % gch
- self.WriteLn("all_deps += %s" % gch)
- self.WriteLn("")
- def ComputeOutputBasename(self, spec):
- """Return the 'output basename' of a gyp spec.
- E.g., the loadable module 'foobar' in directory 'baz' will produce
- 'libfoobar.so'
- """
- assert not self.is_mac_bundle
- if self.flavor == "mac" and self.type in (
- "static_library",
- "executable",
- "shared_library",
- "loadable_module",
- ):
- return self.xcode_settings.GetExecutablePath()
- target = spec["target_name"]
- target_prefix = ""
- target_ext = ""
- if self.type == "static_library":
- if target[:3] == "lib":
- target = target[3:]
- target_prefix = "lib"
- target_ext = ".a"
- elif self.type in ("loadable_module", "shared_library"):
- if target[:3] == "lib":
- target = target[3:]
- target_prefix = "lib"
- if self.flavor == "aix":
- target_ext = ".a"
- elif self.flavor == "zos":
- target_ext = ".x"
- else:
- target_ext = ".so"
- elif self.type == "none":
- target = "%s.stamp" % target
- elif self.type != "executable":
- print(
- "ERROR: What output file should be generated?",
- "type",
- self.type,
- "target",
- target,
- )
- target_prefix = spec.get("product_prefix", target_prefix)
- target = spec.get("product_name", target)
- product_ext = spec.get("product_extension")
- if product_ext:
- target_ext = "." + product_ext
- return target_prefix + target + target_ext
- def _InstallImmediately(self):
- return (
- self.toolset == "target"
- and self.flavor == "mac"
- and self.type
- in ("static_library", "executable", "shared_library", "loadable_module")
- )
- def ComputeOutput(self, spec):
- """Return the 'output' (full output path) of a gyp spec.
- E.g., the loadable module 'foobar' in directory 'baz' will produce
- '$(obj)/baz/libfoobar.so'
- """
- assert not self.is_mac_bundle
- path = os.path.join("$(obj)." + self.toolset, self.path)
- if self.type == "executable" or self._InstallImmediately():
- path = "$(builddir)"
- path = spec.get("product_dir", path)
- return os.path.join(path, self.ComputeOutputBasename(spec))
- def ComputeMacBundleOutput(self, spec):
- """Return the 'output' (full output path) to a bundle output directory."""
- assert self.is_mac_bundle
- path = generator_default_variables["PRODUCT_DIR"]
- return os.path.join(path, self.xcode_settings.GetWrapperName())
- def ComputeMacBundleBinaryOutput(self, spec):
- """Return the 'output' (full output path) to the binary in a bundle."""
- path = generator_default_variables["PRODUCT_DIR"]
- return os.path.join(path, self.xcode_settings.GetExecutablePath())
- def ComputeDeps(self, spec):
- """Compute the dependencies of a gyp spec.
- Returns a tuple (deps, link_deps), where each is a list of
- filenames that will need to be put in front of make for either
- building (deps) or linking (link_deps).
- """
- deps = []
- link_deps = []
- if "dependencies" in spec:
- deps.extend(
- [
- target_outputs[dep]
- for dep in spec["dependencies"]
- if target_outputs[dep]
- ]
- )
- for dep in spec["dependencies"]:
- if dep in target_link_deps:
- link_deps.append(target_link_deps[dep])
- deps.extend(link_deps)
- # TODO: It seems we need to transitively link in libraries (e.g. -lfoo)?
- # This hack makes it work:
- # link_deps.extend(spec.get('libraries', []))
- return (gyp.common.uniquer(deps), gyp.common.uniquer(link_deps))
- def GetSharedObjectFromSidedeck(self, sidedeck):
- """Return the shared object files based on sidedeck"""
- return re.sub(r"\.x$", ".so", sidedeck)
- def GetUnversionedSidedeckFromSidedeck(self, sidedeck):
- """Return the shared object files based on sidedeck"""
- return re.sub(r"\.\d+\.x$", ".x", sidedeck)
- def WriteDependencyOnExtraOutputs(self, target, extra_outputs):
- self.WriteMakeRule(
- [self.output_binary],
- extra_outputs,
- comment="Build our special outputs first.",
- order_only=True,
- )
- def WriteTarget(
- self, spec, configs, deps, link_deps, bundle_deps, extra_outputs, part_of_all
- ):
- """Write Makefile code to produce the final target of the gyp spec.
- spec, configs: input from gyp.
- deps, link_deps: dependency lists; see ComputeDeps()
- extra_outputs: any extra outputs that our target should depend on
- part_of_all: flag indicating this target is part of 'all'
- """
- self.WriteLn("### Rules for final target.")
- if extra_outputs:
- self.WriteDependencyOnExtraOutputs(self.output_binary, extra_outputs)
- self.WriteMakeRule(
- extra_outputs,
- deps,
- comment=("Preserve order dependency of " "special output on deps."),
- order_only=True,
- )
- target_postbuilds = {}
- if self.type != "none":
- for configname in sorted(configs.keys()):
- config = configs[configname]
- if self.flavor == "mac":
- ldflags = self.xcode_settings.GetLdflags(
- configname,
- generator_default_variables["PRODUCT_DIR"],
- lambda p: Sourceify(self.Absolutify(p)),
- arch=config.get("xcode_configuration_platform"),
- )
- # TARGET_POSTBUILDS_$(BUILDTYPE) is added to postbuilds later on.
- gyp_to_build = gyp.common.InvertRelativePath(self.path)
- target_postbuild = self.xcode_settings.AddImplicitPostbuilds(
- configname,
- QuoteSpaces(
- os.path.normpath(os.path.join(gyp_to_build, self.output))
- ),
- QuoteSpaces(
- os.path.normpath(
- os.path.join(gyp_to_build, self.output_binary)
- )
- ),
- )
- if target_postbuild:
- target_postbuilds[configname] = target_postbuild
- else:
- ldflags = config.get("ldflags", [])
- # Compute an rpath for this output if needed.
- if any(dep.endswith(".so") or ".so." in dep for dep in deps):
- # We want to get the literal string "$ORIGIN"
- # into the link command, so we need lots of escaping.
- ldflags.append(r"-Wl,-rpath=\$$ORIGIN/")
- ldflags.append(r"-Wl,-rpath-link=\$(builddir)/")
- library_dirs = config.get("library_dirs", [])
- ldflags += [("-L%s" % library_dir) for library_dir in library_dirs]
- self.WriteList(ldflags, "LDFLAGS_%s" % configname)
- if self.flavor == "mac":
- self.WriteList(
- self.xcode_settings.GetLibtoolflags(configname),
- "LIBTOOLFLAGS_%s" % configname,
- )
- libraries = spec.get("libraries")
- if libraries:
- # Remove duplicate entries
- libraries = gyp.common.uniquer(libraries)
- if self.flavor == "mac":
- libraries = self.xcode_settings.AdjustLibraries(libraries)
- self.WriteList(libraries, "LIBS")
- self.WriteLn(
- "%s: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))"
- % QuoteSpaces(self.output_binary)
- )
- self.WriteLn("%s: LIBS := $(LIBS)" % QuoteSpaces(self.output_binary))
- if self.flavor == "mac":
- self.WriteLn(
- "%s: GYP_LIBTOOLFLAGS := $(LIBTOOLFLAGS_$(BUILDTYPE))"
- % QuoteSpaces(self.output_binary)
- )
- # Postbuild actions. Like actions, but implicitly depend on the target's
- # output.
- postbuilds = []
- if self.flavor == "mac":
- if target_postbuilds:
- postbuilds.append("$(TARGET_POSTBUILDS_$(BUILDTYPE))")
- postbuilds.extend(gyp.xcode_emulation.GetSpecPostbuildCommands(spec))
- if postbuilds:
- # Envvars may be referenced by TARGET_POSTBUILDS_$(BUILDTYPE),
- # so we must output its definition first, since we declare variables
- # using ":=".
- self.WriteSortedXcodeEnv(self.output, self.GetSortedXcodePostbuildEnv())
- for configname, value in target_postbuilds.items():
- self.WriteLn(
- "%s: TARGET_POSTBUILDS_%s := %s"
- % (
- QuoteSpaces(self.output),
- configname,
- gyp.common.EncodePOSIXShellList(value),
- )
- )
- # Postbuilds expect to be run in the gyp file's directory, so insert an
- # implicit postbuild to cd to there.
- postbuilds.insert(0, gyp.common.EncodePOSIXShellList(["cd", self.path]))
- for i, postbuild in enumerate(postbuilds):
- if not postbuild.startswith("$"):
- postbuilds[i] = EscapeShellArgument(postbuild)
- self.WriteLn("%s: builddir := $(abs_builddir)" % QuoteSpaces(self.output))
- self.WriteLn(
- "%s: POSTBUILDS := %s"
- % (QuoteSpaces(self.output), " ".join(postbuilds))
- )
- # A bundle directory depends on its dependencies such as bundle resources
- # and bundle binary. When all dependencies have been built, the bundle
- # needs to be packaged.
- if self.is_mac_bundle:
- # If the framework doesn't contain a binary, then nothing depends
- # on the actions -- make the framework depend on them directly too.
- self.WriteDependencyOnExtraOutputs(self.output, extra_outputs)
- # Bundle dependencies. Note that the code below adds actions to this
- # target, so if you move these two lines, move the lines below as well.
- self.WriteList([QuoteSpaces(dep) for dep in bundle_deps], "BUNDLE_DEPS")
- self.WriteLn("%s: $(BUNDLE_DEPS)" % QuoteSpaces(self.output))
- # After the framework is built, package it. Needs to happen before
- # postbuilds, since postbuilds depend on this.
- if self.type in ("shared_library", "loadable_module"):
- self.WriteLn(
- "\t@$(call do_cmd,mac_package_framework,,,%s)"
- % self.xcode_settings.GetFrameworkVersion()
- )
- # Bundle postbuilds can depend on the whole bundle, so run them after
- # the bundle is packaged, not already after the bundle binary is done.
- if postbuilds:
- self.WriteLn("\t@$(call do_postbuilds)")
- postbuilds = [] # Don't write postbuilds for target's output.
- # Needed by test/mac/gyptest-rebuild.py.
- self.WriteLn("\t@true # No-op, used by tests")
- # Since this target depends on binary and resources which are in
- # nested subfolders, the framework directory will be older than
- # its dependencies usually. To prevent this rule from executing
- # on every build (expensive, especially with postbuilds), explicitly
- # update the time on the framework directory.
- self.WriteLn("\t@touch -c %s" % QuoteSpaces(self.output))
- if postbuilds:
- assert not self.is_mac_bundle, (
- "Postbuilds for bundles should be done "
- "on the bundle, not the binary (target '%s')" % self.target
- )
- assert "product_dir" not in spec, (
- "Postbuilds do not work with " "custom product_dir"
- )
- if self.type == "executable":
- self.WriteLn(
- "%s: LD_INPUTS := %s"
- % (
- QuoteSpaces(self.output_binary),
- " ".join(QuoteSpaces(dep) for dep in link_deps),
- )
- )
- if self.toolset == "host" and self.flavor == "android":
- self.WriteDoCmd(
- [self.output_binary],
- link_deps,
- "link_host",
- part_of_all,
- postbuilds=postbuilds,
- )
- else:
- self.WriteDoCmd(
- [self.output_binary],
- link_deps,
- "link",
- part_of_all,
- postbuilds=postbuilds,
- )
- elif self.type == "static_library":
- for link_dep in link_deps:
- assert " " not in link_dep, (
- "Spaces in alink input filenames not supported (%s)" % link_dep
- )
- if (
- self.flavor not in ("mac", "openbsd", "netbsd", "win")
- and not self.is_standalone_static_library
- ):
- if self.flavor in ("linux", "android"):
- self.WriteMakeRule(
- [self.output_binary],
- link_deps,
- actions=["$(call create_thin_archive,$@,$^)"],
- )
- else:
- self.WriteDoCmd(
- [self.output_binary],
- link_deps,
- "alink_thin",
- part_of_all,
- postbuilds=postbuilds,
- )
- else:
- if self.flavor in ("linux", "android"):
- self.WriteMakeRule(
- [self.output_binary],
- link_deps,
- actions=["$(call create_archive,$@,$^)"],
- )
- else:
- self.WriteDoCmd(
- [self.output_binary],
- link_deps,
- "alink",
- part_of_all,
- postbuilds=postbuilds,
- )
- elif self.type == "shared_library":
- self.WriteLn(
- "%s: LD_INPUTS := %s"
- % (
- QuoteSpaces(self.output_binary),
- " ".join(QuoteSpaces(dep) for dep in link_deps),
- )
- )
- self.WriteDoCmd(
- [self.output_binary],
- link_deps,
- "solink",
- part_of_all,
- postbuilds=postbuilds,
- )
- # z/OS has a .so target as well as a sidedeck .x target
- if self.flavor == "zos":
- self.WriteLn(
- "%s: %s"
- % (
- QuoteSpaces(
- self.GetSharedObjectFromSidedeck(self.output_binary)
- ),
- QuoteSpaces(self.output_binary),
- )
- )
- elif self.type == "loadable_module":
- for link_dep in link_deps:
- assert " " not in link_dep, (
- "Spaces in module input filenames not supported (%s)" % link_dep
- )
- if self.toolset == "host" and self.flavor == "android":
- self.WriteDoCmd(
- [self.output_binary],
- link_deps,
- "solink_module_host",
- part_of_all,
- postbuilds=postbuilds,
- )
- else:
- self.WriteDoCmd(
- [self.output_binary],
- link_deps,
- "solink_module",
- part_of_all,
- postbuilds=postbuilds,
- )
- elif self.type == "none":
- # Write a stamp line.
- self.WriteDoCmd(
- [self.output_binary], deps, "touch", part_of_all, postbuilds=postbuilds
- )
- else:
- print("WARNING: no output for", self.type, self.target)
- # Add an alias for each target (if there are any outputs).
- # Installable target aliases are created below.
- if (self.output and self.output != self.target) and (
- self.type not in self._INSTALLABLE_TARGETS
- ):
- self.WriteMakeRule(
- [self.target], [self.output], comment="Add target alias", phony=True
- )
- if part_of_all:
- self.WriteMakeRule(
- ["all"],
- [self.target],
- comment='Add target alias to "all" target.',
- phony=True,
- )
- # Add special-case rules for our installable targets.
- # 1) They need to install to the build dir or "product" dir.
- # 2) They get shortcuts for building (e.g. "make chrome").
- # 3) They are part of "make all".
- if self.type in self._INSTALLABLE_TARGETS or self.is_standalone_static_library:
- if self.type == "shared_library":
- file_desc = "shared library"
- elif self.type == "static_library":
- file_desc = "static library"
- else:
- file_desc = "executable"
- install_path = self._InstallableTargetInstallPath()
- installable_deps = []
- if self.flavor != "zos":
- installable_deps.append(self.output)
- if (
- self.flavor == "mac"
- and "product_dir" not in spec
- and self.toolset == "target"
- ):
- # On mac, products are created in install_path immediately.
- assert install_path == self.output, f"{install_path} != {self.output}"
- # Point the target alias to the final binary output.
- self.WriteMakeRule(
- [self.target], [install_path], comment="Add target alias", phony=True
- )
- if install_path != self.output:
- assert not self.is_mac_bundle # See comment a few lines above.
- self.WriteDoCmd(
- [install_path],
- [self.output],
- "copy",
- comment="Copy this to the %s output path." % file_desc,
- part_of_all=part_of_all,
- )
- if self.flavor != "zos":
- installable_deps.append(install_path)
- if self.flavor == "zos" and self.type == "shared_library":
- # lib.target/libnode.so has a dependency on $(obj).target/libnode.so
- self.WriteDoCmd(
- [self.GetSharedObjectFromSidedeck(install_path)],
- [self.GetSharedObjectFromSidedeck(self.output)],
- "copy",
- comment="Copy this to the %s output path." % file_desc,
- part_of_all=part_of_all,
- )
- # Create a symlink of libnode.x to libnode.version.x
- self.WriteDoCmd(
- [self.GetUnversionedSidedeckFromSidedeck(install_path)],
- [install_path],
- "symlink",
- comment="Symlnk this to the %s output path." % file_desc,
- part_of_all=part_of_all,
- )
- # Place libnode.version.so and libnode.x symlink in lib.target dir
- installable_deps.append(self.GetSharedObjectFromSidedeck(install_path))
- installable_deps.append(
- self.GetUnversionedSidedeckFromSidedeck(install_path)
- )
- if self.alias not in (self.output, self.target):
- self.WriteMakeRule(
- [self.alias],
- installable_deps,
- comment="Short alias for building this %s." % file_desc,
- phony=True,
- )
- if self.flavor == "zos" and self.type == "shared_library":
- # Make sure that .x symlink target is run
- self.WriteMakeRule(
- ["all"],
- [
- self.GetUnversionedSidedeckFromSidedeck(install_path),
- self.GetSharedObjectFromSidedeck(install_path),
- ],
- comment='Add %s to "all" target.' % file_desc,
- phony=True,
- )
- elif part_of_all:
- self.WriteMakeRule(
- ["all"],
- [install_path],
- comment='Add %s to "all" target.' % file_desc,
- phony=True,
- )
- def WriteList(self, value_list, variable=None, prefix="", quoter=QuoteIfNecessary):
- """Write a variable definition that is a list of values.
- E.g. WriteList(['a','b'], 'foo', prefix='blah') writes out
- foo = blaha blahb
- but in a pretty-printed style.
- """
- values = ""
- if value_list:
- value_list = [replace_sep(quoter(prefix + value)) for value in value_list]
- values = " \\\n\t" + " \\\n\t".join(value_list)
- self.fp.write(f"{variable} :={values}\n\n")
- def WriteDoCmd(
- self, outputs, inputs, command, part_of_all, comment=None, postbuilds=False
- ):
- """Write a Makefile rule that uses do_cmd.
- This makes the outputs dependent on the command line that was run,
- as well as support the V= make command line flag.
- """
- suffix = ""
- if postbuilds:
- assert "," not in command
- suffix = ",,1" # Tell do_cmd to honor $POSTBUILDS
- self.WriteMakeRule(
- outputs,
- inputs,
- actions=[f"$(call do_cmd,{command}{suffix})"],
- comment=comment,
- command=command,
- force=True,
- )
- # Add our outputs to the list of targets we read depfiles from.
- # all_deps is only used for deps file reading, and for deps files we replace
- # spaces with ? because escaping doesn't work with make's $(sort) and
- # other functions.
- outputs = [QuoteSpaces(o, SPACE_REPLACEMENT) for o in outputs]
- self.WriteLn("all_deps += %s" % " ".join(outputs))
- def WriteMakeRule(
- self,
- outputs,
- inputs,
- actions=None,
- comment=None,
- order_only=False,
- force=False,
- phony=False,
- command=None,
- ):
- """Write a Makefile rule, with some extra tricks.
- outputs: a list of outputs for the rule (note: this is not directly
- supported by make; see comments below)
- inputs: a list of inputs for the rule
- actions: a list of shell commands to run for the rule
- comment: a comment to put in the Makefile above the rule (also useful
- for making this Python script's code self-documenting)
- order_only: if true, makes the dependency order-only
- force: if true, include FORCE_DO_CMD as an order-only dep
- phony: if true, the rule does not actually generate the named output, the
- output is just a name to run the rule
- command: (optional) command name to generate unambiguous labels
- """
- outputs = [QuoteSpaces(o) for o in outputs]
- inputs = [QuoteSpaces(i) for i in inputs]
- if comment:
- self.WriteLn("# " + comment)
- if phony:
- self.WriteLn(".PHONY: " + " ".join(outputs))
- if actions:
- self.WriteLn("%s: TOOLSET := $(TOOLSET)" % outputs[0])
- force_append = " FORCE_DO_CMD" if force else ""
- if order_only:
- # Order only rule: Just write a simple rule.
- # TODO(evanm): just make order_only a list of deps instead of this hack.
- self.WriteLn(
- "{}: | {}{}".format(" ".join(outputs), " ".join(inputs), force_append)
- )
- elif len(outputs) == 1:
- # Regular rule, one output: Just write a simple rule.
- self.WriteLn("{}: {}{}".format(outputs[0], " ".join(inputs), force_append))
- else:
- # Regular rule, more than one output: Multiple outputs are tricky in
- # make. We will write three rules:
- # - All outputs depend on an intermediate file.
- # - Make .INTERMEDIATE depend on the intermediate.
- # - The intermediate file depends on the inputs and executes the
- # actual command.
- # - The intermediate recipe will 'touch' the intermediate file.
- # - The multi-output rule will have an do-nothing recipe.
- # Hash the target name to avoid generating overlong filenames.
- cmddigest = hashlib.sha1(
- (command or self.target).encode("utf-8")
- ).hexdigest()
- intermediate = "%s.intermediate" % cmddigest
- self.WriteLn("{}: {}".format(" ".join(outputs), intermediate))
- self.WriteLn("\t%s" % "@:")
- self.WriteLn("{}: {}".format(".INTERMEDIATE", intermediate))
- self.WriteLn(
- "{}: {}{}".format(intermediate, " ".join(inputs), force_append)
- )
- actions.insert(0, "$(call do_cmd,touch)")
- if actions:
- for action in actions:
- self.WriteLn("\t%s" % action)
- self.WriteLn()
- def WriteAndroidNdkModuleRule(self, module_name, all_sources, link_deps):
- """Write a set of LOCAL_XXX definitions for Android NDK.
- These variable definitions will be used by Android NDK but do nothing for
- non-Android applications.
- Arguments:
- module_name: Android NDK module name, which must be unique among all
- module names.
- all_sources: A list of source files (will be filtered by Compilable).
- link_deps: A list of link dependencies, which must be sorted in
- the order from dependencies to dependents.
- """
- if self.type not in ("executable", "shared_library", "static_library"):
- return
- self.WriteLn("# Variable definitions for Android applications")
- self.WriteLn("include $(CLEAR_VARS)")
- self.WriteLn("LOCAL_MODULE := " + module_name)
- self.WriteLn(
- "LOCAL_CFLAGS := $(CFLAGS_$(BUILDTYPE)) "
- "$(DEFS_$(BUILDTYPE)) "
- # LOCAL_CFLAGS is applied to both of C and C++. There is
- # no way to specify $(CFLAGS_C_$(BUILDTYPE)) only for C
- # sources.
- "$(CFLAGS_C_$(BUILDTYPE)) "
- # $(INCS_$(BUILDTYPE)) includes the prefix '-I' while
- # LOCAL_C_INCLUDES does not expect it. So put it in
- # LOCAL_CFLAGS.
- "$(INCS_$(BUILDTYPE))"
- )
- # LOCAL_CXXFLAGS is obsolete and LOCAL_CPPFLAGS is preferred.
- self.WriteLn("LOCAL_CPPFLAGS := $(CFLAGS_CC_$(BUILDTYPE))")
- self.WriteLn("LOCAL_C_INCLUDES :=")
- self.WriteLn("LOCAL_LDLIBS := $(LDFLAGS_$(BUILDTYPE)) $(LIBS)")
- # Detect the C++ extension.
- cpp_ext = {".cc": 0, ".cpp": 0, ".cxx": 0}
- default_cpp_ext = ".cpp"
- for filename in all_sources:
- ext = os.path.splitext(filename)[1]
- if ext in cpp_ext:
- cpp_ext[ext] += 1
- if cpp_ext[ext] > cpp_ext[default_cpp_ext]:
- default_cpp_ext = ext
- self.WriteLn("LOCAL_CPP_EXTENSION := " + default_cpp_ext)
- self.WriteList(
- list(map(self.Absolutify, filter(Compilable, all_sources))),
- "LOCAL_SRC_FILES",
- )
- # Filter out those which do not match prefix and suffix and produce
- # the resulting list without prefix and suffix.
- def DepsToModules(deps, prefix, suffix):
- modules = []
- for filepath in deps:
- filename = os.path.basename(filepath)
- if filename.startswith(prefix) and filename.endswith(suffix):
- modules.append(filename[len(prefix) : -len(suffix)])
- return modules
- # Retrieve the default value of 'SHARED_LIB_SUFFIX'
- params = {"flavor": "linux"}
- default_variables = {}
- CalculateVariables(default_variables, params)
- self.WriteList(
- DepsToModules(
- link_deps,
- generator_default_variables["SHARED_LIB_PREFIX"],
- default_variables["SHARED_LIB_SUFFIX"],
- ),
- "LOCAL_SHARED_LIBRARIES",
- )
- self.WriteList(
- DepsToModules(
- link_deps,
- generator_default_variables["STATIC_LIB_PREFIX"],
- generator_default_variables["STATIC_LIB_SUFFIX"],
- ),
- "LOCAL_STATIC_LIBRARIES",
- )
- if self.type == "executable":
- self.WriteLn("include $(BUILD_EXECUTABLE)")
- elif self.type == "shared_library":
- self.WriteLn("include $(BUILD_SHARED_LIBRARY)")
- elif self.type == "static_library":
- self.WriteLn("include $(BUILD_STATIC_LIBRARY)")
- self.WriteLn()
- def WriteLn(self, text=""):
- self.fp.write(text + "\n")
- def GetSortedXcodeEnv(self, additional_settings=None):
- return gyp.xcode_emulation.GetSortedXcodeEnv(
- self.xcode_settings,
- "$(abs_builddir)",
- os.path.join("$(abs_srcdir)", self.path),
- "$(BUILDTYPE)",
- additional_settings,
- )
- def GetSortedXcodePostbuildEnv(self):
- # CHROMIUM_STRIP_SAVE_FILE is a chromium-specific hack.
- # TODO(thakis): It would be nice to have some general mechanism instead.
- strip_save_file = self.xcode_settings.GetPerTargetSetting(
- "CHROMIUM_STRIP_SAVE_FILE", ""
- )
- # Even if strip_save_file is empty, explicitly write it. Else a postbuild
- # might pick up an export from an earlier target.
- return self.GetSortedXcodeEnv(
- additional_settings={"CHROMIUM_STRIP_SAVE_FILE": strip_save_file}
- )
- def WriteSortedXcodeEnv(self, target, env):
- for k, v in env:
- # For
- # foo := a\ b
- # the escaped space does the right thing. For
- # export foo := a\ b
- # it does not -- the backslash is written to the env as literal character.
- # So don't escape spaces in |env[k]|.
- self.WriteLn(f"{QuoteSpaces(target)}: export {k} := {v}")
- def Objectify(self, path):
- """Convert a path to its output directory form."""
- if "$(" in path:
- path = path.replace("$(obj)/", "$(obj).%s/$(TARGET)/" % self.toolset)
- if "$(obj)" not in path:
- path = f"$(obj).{self.toolset}/$(TARGET)/{path}"
- return path
- def Pchify(self, path, lang):
- """Convert a prefix header path to its output directory form."""
- path = self.Absolutify(path)
- if "$(" in path:
- path = path.replace(
- "$(obj)/", f"$(obj).{self.toolset}/$(TARGET)/pch-{lang}"
- )
- return path
- return f"$(obj).{self.toolset}/$(TARGET)/pch-{lang}/{path}"
- def Absolutify(self, path):
- """Convert a subdirectory-relative path into a base-relative path.
- Skips over paths that contain variables."""
- if "$(" in path:
- # Don't call normpath in this case, as it might collapse the
- # path too aggressively if it features '..'. However it's still
- # important to strip trailing slashes.
- return path.rstrip("/")
- return os.path.normpath(os.path.join(self.path, path))
- def ExpandInputRoot(self, template, expansion, dirname):
- if "%(INPUT_ROOT)s" not in template and "%(INPUT_DIRNAME)s" not in template:
- return template
- path = template % {
- "INPUT_ROOT": expansion,
- "INPUT_DIRNAME": dirname,
- }
- return path
- def _InstallableTargetInstallPath(self):
- """Returns the location of the final output for an installable target."""
- # Functionality removed for all platforms to match Xcode and hoist
- # shared libraries into PRODUCT_DIR for users:
- # Xcode puts shared_library results into PRODUCT_DIR, and some gyp files
- # rely on this. Emulate this behavior for mac.
- # if self.type == "shared_library" and (
- # self.flavor != "mac" or self.toolset != "target"
- # ):
- # # Install all shared libs into a common directory (per toolset) for
- # # convenient access with LD_LIBRARY_PATH.
- # return "$(builddir)/lib.%s/%s" % (self.toolset, self.alias)
- if self.flavor == "zos" and self.type == "shared_library":
- return "$(builddir)/lib.%s/%s" % (self.toolset, self.alias)
- return "$(builddir)/" + self.alias
- def WriteAutoRegenerationRule(params, root_makefile, makefile_name, build_files):
- """Write the target to regenerate the Makefile."""
- options = params["options"]
- build_files_args = [
- gyp.common.RelativePath(filename, options.toplevel_dir)
- for filename in params["build_files_arg"]
- ]
- gyp_binary = gyp.common.FixIfRelativePath(
- params["gyp_binary"], options.toplevel_dir
- )
- if not gyp_binary.startswith(os.sep):
- gyp_binary = os.path.join(".", gyp_binary)
- root_makefile.write(
- "quiet_cmd_regen_makefile = ACTION Regenerating $@\n"
- "cmd_regen_makefile = cd $(srcdir); %(cmd)s\n"
- "%(makefile_name)s: %(deps)s\n"
- "\t$(call do_cmd,regen_makefile)\n\n"
- % {
- "makefile_name": makefile_name,
- "deps": replace_sep(
- " ".join(SourceifyAndQuoteSpaces(bf) for bf in build_files)
- ),
- "cmd": replace_sep(gyp.common.EncodePOSIXShellList(
- [gyp_binary, "-fmake"] + gyp.RegenerateFlags(options) + build_files_args
- )),
- }
- )
- def PerformBuild(data, configurations, params):
- options = params["options"]
- for config in configurations:
- arguments = ["make"]
- if options.toplevel_dir and options.toplevel_dir != ".":
- arguments += "-C", options.toplevel_dir
- arguments.append("BUILDTYPE=" + config)
- print(f"Building [{config}]: {arguments}")
- subprocess.check_call(arguments)
- def GenerateOutput(target_list, target_dicts, data, params):
- options = params["options"]
- flavor = gyp.common.GetFlavor(params)
- generator_flags = params.get("generator_flags", {})
- builddir_name = generator_flags.get("output_dir", "out")
- android_ndk_version = generator_flags.get("android_ndk_version", None)
- default_target = generator_flags.get("default_target", "all")
- def CalculateMakefilePath(build_file, base_name):
- """Determine where to write a Makefile for a given gyp file."""
- # Paths in gyp files are relative to the .gyp file, but we want
- # paths relative to the source root for the master makefile. Grab
- # the path of the .gyp file as the base to relativize against.
- # E.g. "foo/bar" when we're constructing targets for "foo/bar/baz.gyp".
- base_path = gyp.common.RelativePath(os.path.dirname(build_file), options.depth)
- # We write the file in the base_path directory.
- output_file = os.path.join(options.depth, base_path, base_name)
- if options.generator_output:
- output_file = os.path.join(
- options.depth, options.generator_output, base_path, base_name
- )
- base_path = gyp.common.RelativePath(
- os.path.dirname(build_file), options.toplevel_dir
- )
- return base_path, output_file
- # TODO: search for the first non-'Default' target. This can go
- # away when we add verification that all targets have the
- # necessary configurations.
- default_configuration = None
- toolsets = {target_dicts[target]["toolset"] for target in target_list}
- for target in target_list:
- spec = target_dicts[target]
- if spec["default_configuration"] != "Default":
- default_configuration = spec["default_configuration"]
- break
- if not default_configuration:
- default_configuration = "Default"
- srcdir = "."
- makefile_name = "Makefile" + options.suffix
- makefile_path = os.path.join(options.toplevel_dir, makefile_name)
- if options.generator_output:
- global srcdir_prefix
- makefile_path = os.path.join(
- options.toplevel_dir, options.generator_output, makefile_name
- )
- srcdir = replace_sep(gyp.common.RelativePath(srcdir, options.generator_output))
- srcdir_prefix = "$(srcdir)/"
- flock_command = "flock"
- copy_archive_arguments = "-af"
- makedep_arguments = "-MMD"
- # wasm-ld doesn't support --start-group/--end-group
- link_commands = LINK_COMMANDS_LINUX
- if flavor in ["wasi", "wasm"]:
- link_commands = link_commands.replace(' -Wl,--start-group', '').replace(
- ' -Wl,--end-group', ''
- )
- CC_target = replace_sep(GetEnvironFallback(("CC_target", "CC"), "$(CC)"))
- AR_target = replace_sep(GetEnvironFallback(("AR_target", "AR"), "$(AR)"))
- CXX_target = replace_sep(GetEnvironFallback(("CXX_target", "CXX"), "$(CXX)"))
- LINK_target = replace_sep(GetEnvironFallback(("LINK_target", "LINK"), "$(LINK)"))
- PLI_target = replace_sep(GetEnvironFallback(("PLI_target", "PLI"), "pli"))
- CC_host = replace_sep(GetEnvironFallback(("CC_host", "CC"), "gcc"))
- AR_host = replace_sep(GetEnvironFallback(("AR_host", "AR"), "ar"))
- CXX_host = replace_sep(GetEnvironFallback(("CXX_host", "CXX"), "g++"))
- LINK_host = replace_sep(GetEnvironFallback(("LINK_host", "LINK"), "$(CXX.host)"))
- PLI_host = replace_sep(GetEnvironFallback(("PLI_host", "PLI"), "pli"))
- header_params = {
- "default_target": default_target,
- "builddir": builddir_name,
- "default_configuration": default_configuration,
- "flock": flock_command,
- "flock_index": 1,
- "link_commands": link_commands,
- "extra_commands": "",
- "srcdir": srcdir,
- "copy_archive_args": copy_archive_arguments,
- "makedep_args": makedep_arguments,
- "CC.target": CC_target,
- "AR.target": AR_target,
- "CXX.target": CXX_target,
- "LINK.target": LINK_target,
- "PLI.target": PLI_target,
- "CC.host": CC_host,
- "AR.host": AR_host,
- "CXX.host": CXX_host,
- "LINK.host": LINK_host,
- "PLI.host": PLI_host,
- }
- if flavor == "mac":
- flock_command = "%s gyp-mac-tool flock" % sys.executable
- header_params.update(
- {
- "flock": flock_command,
- "flock_index": 2,
- "link_commands": LINK_COMMANDS_MAC,
- "extra_commands": SHARED_HEADER_MAC_COMMANDS,
- }
- )
- elif flavor == "android":
- header_params.update({"link_commands": LINK_COMMANDS_ANDROID})
- elif flavor == "zos":
- copy_archive_arguments = "-fPR"
- CC_target = GetEnvironFallback(("CC_target", "CC"), "njsc")
- makedep_arguments = "-MMD"
- if CC_target == "clang":
- CC_host = GetEnvironFallback(("CC_host", "CC"), "clang")
- CXX_target = GetEnvironFallback(("CXX_target", "CXX"), "clang++")
- CXX_host = GetEnvironFallback(("CXX_host", "CXX"), "clang++")
- elif CC_target == "ibm-clang64":
- CC_host = GetEnvironFallback(("CC_host", "CC"), "ibm-clang64")
- CXX_target = GetEnvironFallback(("CXX_target", "CXX"), "ibm-clang++64")
- CXX_host = GetEnvironFallback(("CXX_host", "CXX"), "ibm-clang++64")
- elif CC_target == "ibm-clang":
- CC_host = GetEnvironFallback(("CC_host", "CC"), "ibm-clang")
- CXX_target = GetEnvironFallback(("CXX_target", "CXX"), "ibm-clang++")
- CXX_host = GetEnvironFallback(("CXX_host", "CXX"), "ibm-clang++")
- else:
- # Node.js versions prior to v18:
- makedep_arguments = "-qmakedep=gcc"
- CC_host = GetEnvironFallback(("CC_host", "CC"), "njsc")
- CXX_target = GetEnvironFallback(("CXX_target", "CXX"), "njsc++")
- CXX_host = GetEnvironFallback(("CXX_host", "CXX"), "njsc++")
- header_params.update(
- {
- "copy_archive_args": copy_archive_arguments,
- "makedep_args": makedep_arguments,
- "link_commands": LINK_COMMANDS_OS390,
- "extra_commands": SHARED_HEADER_OS390_COMMANDS,
- "CC.target": CC_target,
- "CXX.target": CXX_target,
- "CC.host": CC_host,
- "CXX.host": CXX_host,
- }
- )
- elif flavor == "solaris":
- copy_archive_arguments = "-pPRf@"
- header_params.update(
- {
- "copy_archive_args": copy_archive_arguments,
- "flock": "%s gyp-flock-tool flock" % sys.executable,
- "flock_index": 2,
- }
- )
- elif flavor == "freebsd":
- # Note: OpenBSD has sysutils/flock. lockf seems to be FreeBSD specific.
- header_params.update({"flock": "lockf"})
- elif flavor == "openbsd":
- copy_archive_arguments = "-pPRf"
- header_params.update({"copy_archive_args": copy_archive_arguments})
- elif flavor == "aix":
- copy_archive_arguments = "-pPRf"
- header_params.update(
- {
- "copy_archive_args": copy_archive_arguments,
- "link_commands": LINK_COMMANDS_AIX,
- "flock": "%s gyp-flock-tool flock" % sys.executable,
- "flock_index": 2,
- }
- )
- elif flavor == "os400":
- copy_archive_arguments = "-pPRf"
- header_params.update(
- {
- "copy_archive_args": copy_archive_arguments,
- "link_commands": LINK_COMMANDS_OS400,
- "flock": "%s gyp-flock-tool flock" % sys.executable,
- "flock_index": 2,
- }
- )
- build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
- make_global_settings_array = data[build_file].get("make_global_settings", [])
- wrappers = {}
- for key, value in make_global_settings_array:
- if key.endswith("_wrapper"):
- wrappers[key[: -len("_wrapper")]] = "$(abspath %s)" % value
- make_global_settings = ""
- for key, value in make_global_settings_array:
- if re.match(".*_wrapper", key):
- continue
- if value[0] != "$":
- value = "$(abspath %s)" % value
- wrapper = wrappers.get(key)
- if wrapper:
- value = f"{wrapper} {value}"
- del wrappers[key]
- if key in ("CC", "CC.host", "CXX", "CXX.host"):
- make_global_settings += (
- "ifneq (,$(filter $(origin %s), undefined default))\n" % key
- )
- # Let gyp-time envvars win over global settings.
- env_key = key.replace(".", "_") # CC.host -> CC_host
- if env_key in os.environ:
- value = os.environ[env_key]
- make_global_settings += f" {key} = {value}\n"
- make_global_settings += "endif\n"
- else:
- make_global_settings += f"{key} ?= {value}\n"
- # TODO(ukai): define cmd when only wrapper is specified in
- # make_global_settings.
- header_params["make_global_settings"] = make_global_settings
- gyp.common.EnsureDirExists(makefile_path)
- root_makefile = open(makefile_path, "w")
- root_makefile.write(SHARED_HEADER % header_params)
- # Currently any versions have the same effect, but in future the behavior
- # could be different.
- if android_ndk_version:
- root_makefile.write(
- "# Define LOCAL_PATH for build of Android applications.\n"
- "LOCAL_PATH := $(call my-dir)\n"
- "\n"
- )
- for toolset in toolsets:
- root_makefile.write("TOOLSET := %s\n" % toolset)
- WriteRootHeaderSuffixRules(root_makefile)
- # Put build-time support tools next to the root Makefile.
- dest_path = os.path.dirname(makefile_path)
- gyp.common.CopyTool(flavor, dest_path)
- # Find the list of targets that derive from the gyp file(s) being built.
- needed_targets = set()
- for build_file in params["build_files"]:
- for target in gyp.common.AllTargets(target_list, target_dicts, build_file):
- needed_targets.add(target)
- build_files = set()
- include_list = set()
- for qualified_target in target_list:
- build_file, target, toolset = gyp.common.ParseQualifiedTarget(qualified_target)
- this_make_global_settings = data[build_file].get("make_global_settings", [])
- assert make_global_settings_array == this_make_global_settings, (
- "make_global_settings needs to be the same for all targets "
- f"{this_make_global_settings} vs. {make_global_settings}"
- )
- build_files.add(gyp.common.RelativePath(build_file, options.toplevel_dir))
- included_files = data[build_file]["included_files"]
- for included_file in included_files:
- # The included_files entries are relative to the dir of the build file
- # that included them, so we have to undo that and then make them relative
- # to the root dir.
- relative_include_file = gyp.common.RelativePath(
- gyp.common.UnrelativePath(included_file, build_file),
- options.toplevel_dir,
- )
- abs_include_file = os.path.abspath(relative_include_file)
- # If the include file is from the ~/.gyp dir, we should use absolute path
- # so that relocating the src dir doesn't break the path.
- if params["home_dot_gyp"] and abs_include_file.startswith(
- params["home_dot_gyp"]
- ):
- build_files.add(abs_include_file)
- else:
- build_files.add(relative_include_file)
- base_path, output_file = CalculateMakefilePath(
- build_file, target + "." + toolset + options.suffix + ".mk"
- )
- spec = target_dicts[qualified_target]
- configs = spec["configurations"]
- if flavor == "mac":
- gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec)
- writer = MakefileWriter(generator_flags, flavor)
- writer.Write(
- qualified_target,
- base_path,
- output_file,
- spec,
- configs,
- part_of_all=qualified_target in needed_targets,
- )
- # Our root_makefile lives at the source root. Compute the relative path
- # from there to the output_file for including.
- mkfile_rel_path = gyp.common.RelativePath(
- output_file, os.path.dirname(makefile_path)
- )
- include_list.add(mkfile_rel_path)
- # Write out per-gyp (sub-project) Makefiles.
- depth_rel_path = gyp.common.RelativePath(options.depth, os.getcwd())
- for build_file in build_files:
- # The paths in build_files were relativized above, so undo that before
- # testing against the non-relativized items in target_list and before
- # calculating the Makefile path.
- build_file = os.path.join(depth_rel_path, build_file)
- gyp_targets = [
- target_dicts[qualified_target]["target_name"]
- for qualified_target in target_list
- if qualified_target.startswith(build_file)
- and qualified_target in needed_targets
- ]
- # Only generate Makefiles for gyp files with targets.
- if not gyp_targets:
- continue
- base_path, output_file = CalculateMakefilePath(
- build_file, os.path.splitext(os.path.basename(build_file))[0] + ".Makefile"
- )
- makefile_rel_path = gyp.common.RelativePath(
- os.path.dirname(makefile_path), os.path.dirname(output_file)
- )
- writer.WriteSubMake(output_file, makefile_rel_path, gyp_targets, builddir_name)
- # Write out the sorted list of includes.
- root_makefile.write("\n")
- for include_file in sorted(include_list):
- # We wrap each .mk include in an if statement so users can tell make to
- # not load a file by setting NO_LOAD. The below make code says, only
- # load the .mk file if the .mk filename doesn't start with a token in
- # NO_LOAD.
- root_makefile.write(
- "ifeq ($(strip $(foreach prefix,$(NO_LOAD),\\\n"
- " $(findstring $(join ^,$(prefix)),\\\n"
- " $(join ^," + include_file + ")))),)\n"
- )
- root_makefile.write(" include " + include_file + "\n")
- root_makefile.write("endif\n")
- root_makefile.write("\n")
- if not generator_flags.get("standalone") and generator_flags.get(
- "auto_regeneration", True
- ):
- WriteAutoRegenerationRule(params, root_makefile, makefile_name, build_files)
- root_makefile.write(SHARED_FOOTER)
- root_makefile.close()
|