| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967 |
- # 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.
- import collections
- import copy
- import hashlib
- import json
- import multiprocessing
- import os.path
- import re
- import signal
- import shutil
- import subprocess
- import sys
- import gyp
- import gyp.common
- import gyp.msvs_emulation
- import gyp.MSVSUtil as MSVSUtil
- import gyp.xcode_emulation
- from io import StringIO
- from gyp.common import GetEnvironFallback
- import gyp.ninja_syntax as ninja_syntax
- generator_default_variables = {
- "EXECUTABLE_PREFIX": "",
- "EXECUTABLE_SUFFIX": "",
- "STATIC_LIB_PREFIX": "lib",
- "STATIC_LIB_SUFFIX": ".a",
- "SHARED_LIB_PREFIX": "lib",
- # Gyp expects the following variables to be expandable by the build
- # system to the appropriate locations. Ninja prefers paths to be
- # known at gyp time. To resolve this, introduce special
- # variables starting with $! and $| (which begin with a $ so gyp knows it
- # should be treated specially, but is otherwise an invalid
- # ninja/shell variable) that are passed to gyp here but expanded
- # before writing out into the target .ninja files; see
- # ExpandSpecial.
- # $! is used for variables that represent a path and that can only appear at
- # the start of a string, while $| is used for variables that can appear
- # anywhere in a string.
- "INTERMEDIATE_DIR": "$!INTERMEDIATE_DIR",
- "SHARED_INTERMEDIATE_DIR": "$!PRODUCT_DIR/gen",
- "PRODUCT_DIR": "$!PRODUCT_DIR",
- "CONFIGURATION_NAME": "$|CONFIGURATION_NAME",
- # Special variables that may be used by gyp 'rule' targets.
- # We generate definitions for these variables on the fly when processing a
- # rule.
- "RULE_INPUT_ROOT": "${root}",
- "RULE_INPUT_DIRNAME": "${dirname}",
- "RULE_INPUT_PATH": "${source}",
- "RULE_INPUT_EXT": "${ext}",
- "RULE_INPUT_NAME": "${name}",
- }
- # Placates pylint.
- generator_additional_non_configuration_keys = []
- generator_additional_path_sections = []
- generator_extra_sources_for_rules = []
- generator_filelist_paths = None
- generator_supports_multiple_toolsets = gyp.common.CrossCompileRequested()
- def StripPrefix(arg, prefix):
- if arg.startswith(prefix):
- return arg[len(prefix) :]
- return arg
- def QuoteShellArgument(arg, flavor):
- """Quote a string such that it will be interpreted as a single argument
- by the shell."""
- # Rather than attempting to enumerate the bad shell characters, just
- # allow common OK ones and quote anything else.
- if re.match(r"^[a-zA-Z0-9_=.\\/-]+$", arg):
- return arg # No quoting necessary.
- if flavor == "win":
- return gyp.msvs_emulation.QuoteForRspFile(arg)
- return "'" + arg.replace("'", "'" + '"\'"' + "'") + "'"
- def Define(d, flavor):
- """Takes a preprocessor define and returns a -D parameter that's ninja- and
- shell-escaped."""
- if flavor == "win":
- # cl.exe replaces literal # characters with = in preprocessor definitions for
- # some reason. Octal-encode to work around that.
- d = d.replace("#", "\\%03o" % ord("#"))
- return QuoteShellArgument(ninja_syntax.escape("-D" + d), flavor)
- def AddArch(output, arch):
- """Adds an arch string to an output path."""
- output, extension = os.path.splitext(output)
- return f"{output}.{arch}{extension}"
- class Target:
- """Target represents the paths used within a single gyp target.
- Conceptually, building a single target A is a series of steps:
- 1) actions/rules/copies generates source/resources/etc.
- 2) compiles generates .o files
- 3) link generates a binary (library/executable)
- 4) bundle merges the above in a mac bundle
- (Any of these steps can be optional.)
- From a build ordering perspective, a dependent target B could just
- depend on the last output of this series of steps.
- But some dependent commands sometimes need to reach inside the box.
- For example, when linking B it needs to get the path to the static
- library generated by A.
- This object stores those paths. To keep things simple, member
- variables only store concrete paths to single files, while methods
- compute derived values like "the last output of the target".
- """
- def __init__(self, type):
- # Gyp type ("static_library", etc.) of this target.
- self.type = type
- # File representing whether any input dependencies necessary for
- # dependent actions have completed.
- self.preaction_stamp = None
- # File representing whether any input dependencies necessary for
- # dependent compiles have completed.
- self.precompile_stamp = None
- # File representing the completion of actions/rules/copies, if any.
- self.actions_stamp = None
- # Path to the output of the link step, if any.
- self.binary = None
- # Path to the file representing the completion of building the bundle,
- # if any.
- self.bundle = None
- # On Windows, incremental linking requires linking against all the .objs
- # that compose a .lib (rather than the .lib itself). That list is stored
- # here. In this case, we also need to save the compile_deps for the target,
- # so that the target that directly depends on the .objs can also depend
- # on those.
- self.component_objs = None
- self.compile_deps = None
- # Windows only. The import .lib is the output of a build step, but
- # because dependents only link against the lib (not both the lib and the
- # dll) we keep track of the import library here.
- self.import_lib = None
- # Track if this target contains any C++ files, to decide if gcc or g++
- # should be used for linking.
- self.uses_cpp = False
- def Linkable(self):
- """Return true if this is a target that can be linked against."""
- return self.type in ("static_library", "shared_library")
- def UsesToc(self, flavor):
- """Return true if the target should produce a restat rule based on a TOC
- file."""
- # For bundles, the .TOC should be produced for the binary, not for
- # FinalOutput(). But the naive approach would put the TOC file into the
- # bundle, so don't do this for bundles for now.
- if flavor == "win" or self.bundle:
- return False
- return self.type in ("shared_library", "loadable_module")
- def PreActionInput(self, flavor):
- """Return the path, if any, that should be used as a dependency of
- any dependent action step."""
- if self.UsesToc(flavor):
- return self.FinalOutput() + ".TOC"
- return self.FinalOutput() or self.preaction_stamp
- def PreCompileInput(self):
- """Return the path, if any, that should be used as a dependency of
- any dependent compile step."""
- return self.actions_stamp or self.precompile_stamp
- def FinalOutput(self):
- """Return the last output of the target, which depends on all prior
- steps."""
- return self.bundle or self.binary or self.actions_stamp
- # A small discourse on paths as used within the Ninja build:
- # All files we produce (both at gyp and at build time) appear in the
- # build directory (e.g. out/Debug).
- #
- # Paths within a given .gyp file are always relative to the directory
- # containing the .gyp file. Call these "gyp paths". This includes
- # sources as well as the starting directory a given gyp rule/action
- # expects to be run from. We call the path from the source root to
- # the gyp file the "base directory" within the per-.gyp-file
- # NinjaWriter code.
- #
- # All paths as written into the .ninja files are relative to the build
- # directory. Call these paths "ninja paths".
- #
- # We translate between these two notions of paths with two helper
- # functions:
- #
- # - GypPathToNinja translates a gyp path (i.e. relative to the .gyp file)
- # into the equivalent ninja path.
- #
- # - GypPathToUniqueOutput translates a gyp path into a ninja path to write
- # an output file; the result can be namespaced such that it is unique
- # to the input file name as well as the output target name.
- class NinjaWriter:
- def __init__(
- self,
- hash_for_rules,
- target_outputs,
- base_dir,
- build_dir,
- output_file,
- toplevel_build,
- output_file_name,
- flavor,
- toplevel_dir=None,
- ):
- """
- base_dir: path from source root to directory containing this gyp file,
- by gyp semantics, all input paths are relative to this
- build_dir: path from source root to build output
- toplevel_dir: path to the toplevel directory
- """
- self.hash_for_rules = hash_for_rules
- self.target_outputs = target_outputs
- self.base_dir = base_dir
- self.build_dir = build_dir
- self.ninja = ninja_syntax.Writer(output_file)
- self.toplevel_build = toplevel_build
- self.output_file_name = output_file_name
- self.flavor = flavor
- self.abs_build_dir = None
- if toplevel_dir is not None:
- self.abs_build_dir = os.path.abspath(os.path.join(toplevel_dir, build_dir))
- self.obj_ext = ".obj" if flavor == "win" else ".o"
- if flavor == "win":
- # See docstring of msvs_emulation.GenerateEnvironmentFiles().
- self.win_env = {}
- for arch in ("x86", "x64"):
- self.win_env[arch] = "environment." + arch
- # Relative path from build output dir to base dir.
- build_to_top = gyp.common.InvertRelativePath(build_dir, toplevel_dir)
- self.build_to_base = os.path.join(build_to_top, base_dir)
- # Relative path from base dir to build dir.
- base_to_top = gyp.common.InvertRelativePath(base_dir, toplevel_dir)
- self.base_to_build = os.path.join(base_to_top, build_dir)
- def ExpandSpecial(self, path, product_dir=None):
- """Expand specials like $!PRODUCT_DIR in |path|.
- If |product_dir| is None, assumes the cwd is already the product
- dir. Otherwise, |product_dir| is the relative path to the product
- dir.
- """
- PRODUCT_DIR = "$!PRODUCT_DIR"
- if PRODUCT_DIR in path:
- if product_dir:
- path = path.replace(PRODUCT_DIR, product_dir)
- else:
- path = path.replace(PRODUCT_DIR + "/", "")
- path = path.replace(PRODUCT_DIR + "\\", "")
- path = path.replace(PRODUCT_DIR, ".")
- INTERMEDIATE_DIR = "$!INTERMEDIATE_DIR"
- if INTERMEDIATE_DIR in path:
- int_dir = self.GypPathToUniqueOutput("gen")
- # GypPathToUniqueOutput generates a path relative to the product dir,
- # so insert product_dir in front if it is provided.
- path = path.replace(
- INTERMEDIATE_DIR, os.path.join(product_dir or "", int_dir)
- )
- CONFIGURATION_NAME = "$|CONFIGURATION_NAME"
- path = path.replace(CONFIGURATION_NAME, self.config_name)
- return path
- def ExpandRuleVariables(self, path, root, dirname, source, ext, name):
- if self.flavor == "win":
- path = self.msvs_settings.ConvertVSMacros(path, config=self.config_name)
- path = path.replace(generator_default_variables["RULE_INPUT_ROOT"], root)
- path = path.replace(generator_default_variables["RULE_INPUT_DIRNAME"], dirname)
- path = path.replace(generator_default_variables["RULE_INPUT_PATH"], source)
- path = path.replace(generator_default_variables["RULE_INPUT_EXT"], ext)
- path = path.replace(generator_default_variables["RULE_INPUT_NAME"], name)
- return path
- def GypPathToNinja(self, path, env=None):
- """Translate a gyp path to a ninja path, optionally expanding environment
- variable references in |path| with |env|.
- See the above discourse on path conversions."""
- if env:
- if self.flavor == "mac":
- path = gyp.xcode_emulation.ExpandEnvVars(path, env)
- elif self.flavor == "win":
- path = gyp.msvs_emulation.ExpandMacros(path, env)
- if path.startswith("$!"):
- expanded = self.ExpandSpecial(path)
- if self.flavor == "win":
- expanded = os.path.normpath(expanded)
- return expanded
- if "$|" in path:
- path = self.ExpandSpecial(path)
- assert "$" not in path, path
- return os.path.normpath(os.path.join(self.build_to_base, path))
- def GypPathToUniqueOutput(self, path, qualified=True):
- """Translate a gyp path to a ninja path for writing output.
- If qualified is True, qualify the resulting filename with the name
- of the target. This is necessary when e.g. compiling the same
- path twice for two separate output targets.
- See the above discourse on path conversions."""
- path = self.ExpandSpecial(path)
- assert not path.startswith("$"), path
- # Translate the path following this scheme:
- # Input: foo/bar.gyp, target targ, references baz/out.o
- # Output: obj/foo/baz/targ.out.o (if qualified)
- # obj/foo/baz/out.o (otherwise)
- # (and obj.host instead of obj for cross-compiles)
- #
- # Why this scheme and not some other one?
- # 1) for a given input, you can compute all derived outputs by matching
- # its path, even if the input is brought via a gyp file with '..'.
- # 2) simple files like libraries and stamps have a simple filename.
- obj = "obj"
- if self.toolset != "target":
- obj += "." + self.toolset
- path_dir, path_basename = os.path.split(path)
- assert not os.path.isabs(path_dir), (
- "'%s' can not be absolute path (see crbug.com/462153)." % path_dir
- )
- if qualified:
- path_basename = self.name + "." + path_basename
- return os.path.normpath(
- os.path.join(obj, self.base_dir, path_dir, path_basename)
- )
- def WriteCollapsedDependencies(self, name, targets, order_only=None):
- """Given a list of targets, return a path for a single file
- representing the result of building all the targets or None.
- Uses a stamp file if necessary."""
- assert targets == [item for item in targets if item], targets
- if len(targets) == 0:
- assert not order_only
- return None
- if len(targets) > 1 or order_only:
- stamp = self.GypPathToUniqueOutput(name + ".stamp")
- targets = self.ninja.build(stamp, "stamp", targets, order_only=order_only)
- self.ninja.newline()
- return targets[0]
- def _SubninjaNameForArch(self, arch):
- output_file_base = os.path.splitext(self.output_file_name)[0]
- return f"{output_file_base}.{arch}.ninja"
- def WriteSpec(self, spec, config_name, generator_flags):
- """The main entry point for NinjaWriter: write the build rules for a spec.
- Returns a Target object, which represents the output paths for this spec.
- Returns None if there are no outputs (e.g. a settings-only 'none' type
- target)."""
- self.config_name = config_name
- self.name = spec["target_name"]
- self.toolset = spec["toolset"]
- config = spec["configurations"][config_name]
- self.target = Target(spec["type"])
- self.is_standalone_static_library = bool(
- spec.get("standalone_static_library", 0)
- )
- self.target_rpath = generator_flags.get("target_rpath", r"\$$ORIGIN/lib/")
- self.is_mac_bundle = gyp.xcode_emulation.IsMacBundle(self.flavor, spec)
- self.xcode_settings = self.msvs_settings = None
- if self.flavor == "mac":
- self.xcode_settings = gyp.xcode_emulation.XcodeSettings(spec)
- mac_toolchain_dir = generator_flags.get("mac_toolchain_dir", None)
- if mac_toolchain_dir:
- self.xcode_settings.mac_toolchain_dir = mac_toolchain_dir
- if self.flavor == "win":
- self.msvs_settings = gyp.msvs_emulation.MsvsSettings(spec, generator_flags)
- arch = self.msvs_settings.GetArch(config_name)
- self.ninja.variable("arch", self.win_env[arch])
- self.ninja.variable("cc", "$cl_" + arch)
- self.ninja.variable("cxx", "$cl_" + arch)
- self.ninja.variable("cc_host", "$cl_" + arch)
- self.ninja.variable("cxx_host", "$cl_" + arch)
- self.ninja.variable("asm", "$ml_" + arch)
- if self.flavor == "mac":
- self.archs = self.xcode_settings.GetActiveArchs(config_name)
- if len(self.archs) > 1:
- self.arch_subninjas = {
- arch: ninja_syntax.Writer(
- OpenOutput(
- os.path.join(
- self.toplevel_build, self._SubninjaNameForArch(arch)
- ),
- "w",
- )
- )
- for arch in self.archs
- }
- # Compute predepends for all rules.
- # actions_depends is the dependencies this target depends on before running
- # any of its action/rule/copy steps.
- # compile_depends is the dependencies this target depends on before running
- # any of its compile steps.
- actions_depends = []
- compile_depends = []
- # TODO(evan): it is rather confusing which things are lists and which
- # are strings. Fix these.
- if "dependencies" in spec:
- for dep in spec["dependencies"]:
- if dep in self.target_outputs:
- target = self.target_outputs[dep]
- actions_depends.append(target.PreActionInput(self.flavor))
- compile_depends.append(target.PreCompileInput())
- if target.uses_cpp:
- self.target.uses_cpp = True
- actions_depends = [item for item in actions_depends if item]
- compile_depends = [item for item in compile_depends if item]
- actions_depends = self.WriteCollapsedDependencies(
- "actions_depends", actions_depends
- )
- compile_depends = self.WriteCollapsedDependencies(
- "compile_depends", compile_depends
- )
- self.target.preaction_stamp = actions_depends
- self.target.precompile_stamp = compile_depends
- # Write out actions, rules, and copies. These must happen before we
- # compile any sources, so compute a list of predependencies for sources
- # while we do it.
- extra_sources = []
- mac_bundle_depends = []
- self.target.actions_stamp = self.WriteActionsRulesCopies(
- spec, extra_sources, actions_depends, mac_bundle_depends
- )
- # If we have actions/rules/copies, we depend directly on those, but
- # otherwise we depend on dependent target's actions/rules/copies etc.
- # We never need to explicitly depend on previous target's link steps,
- # because no compile ever depends on them.
- compile_depends_stamp = self.target.actions_stamp or compile_depends
- # Write out the compilation steps, if any.
- link_deps = []
- try:
- sources = extra_sources + spec.get("sources", [])
- except TypeError:
- print("extra_sources: ", str(extra_sources))
- print('spec.get("sources"): ', str(spec.get("sources")))
- raise
- if sources:
- if self.flavor == "mac" and len(self.archs) > 1:
- # Write subninja file containing compile and link commands scoped to
- # a single arch if a fat binary is being built.
- for arch in self.archs:
- self.ninja.subninja(self._SubninjaNameForArch(arch))
- pch = None
- if self.flavor == "win":
- gyp.msvs_emulation.VerifyMissingSources(
- sources, self.abs_build_dir, generator_flags, self.GypPathToNinja
- )
- pch = gyp.msvs_emulation.PrecompiledHeader(
- self.msvs_settings,
- config_name,
- self.GypPathToNinja,
- self.GypPathToUniqueOutput,
- self.obj_ext,
- )
- else:
- pch = gyp.xcode_emulation.MacPrefixHeader(
- self.xcode_settings,
- self.GypPathToNinja,
- lambda path, lang: self.GypPathToUniqueOutput(path + "-" + lang),
- )
- link_deps = self.WriteSources(
- self.ninja,
- config_name,
- config,
- sources,
- compile_depends_stamp,
- pch,
- spec,
- )
- # Some actions/rules output 'sources' that are already object files.
- obj_outputs = [f for f in sources if f.endswith(self.obj_ext)]
- if obj_outputs:
- if self.flavor != "mac" or len(self.archs) == 1:
- link_deps += [self.GypPathToNinja(o) for o in obj_outputs]
- else:
- print(
- "Warning: Actions/rules writing object files don't work with "
- "multiarch targets, dropping. (target %s)" % spec["target_name"]
- )
- elif self.flavor == "mac" and len(self.archs) > 1:
- link_deps = collections.defaultdict(list)
- compile_deps = self.target.actions_stamp or actions_depends
- if self.flavor == "win" and self.target.type == "static_library":
- self.target.component_objs = link_deps
- self.target.compile_deps = compile_deps
- # Write out a link step, if needed.
- output = None
- is_empty_bundle = not link_deps and not mac_bundle_depends
- if link_deps or self.target.actions_stamp or actions_depends:
- output = self.WriteTarget(
- spec, config_name, config, link_deps, compile_deps
- )
- if self.is_mac_bundle:
- mac_bundle_depends.append(output)
- # Bundle all of the above together, if needed.
- if self.is_mac_bundle:
- output = self.WriteMacBundle(spec, mac_bundle_depends, is_empty_bundle)
- if not output:
- return None
- assert self.target.FinalOutput(), output
- return self.target
- def _WinIdlRule(self, source, prebuild, outputs):
- """Handle the implicit VS .idl rule for one source file. Fills |outputs|
- with files that are generated."""
- outdir, output, vars, flags = self.msvs_settings.GetIdlBuildData(
- source, self.config_name
- )
- outdir = self.GypPathToNinja(outdir)
- def fix_path(path, rel=None):
- path = os.path.join(outdir, path)
- dirname, basename = os.path.split(source)
- root, ext = os.path.splitext(basename)
- path = self.ExpandRuleVariables(path, root, dirname, source, ext, basename)
- if rel:
- path = os.path.relpath(path, rel)
- return path
- vars = [(name, fix_path(value, outdir)) for name, value in vars]
- output = [fix_path(p) for p in output]
- vars.append(("outdir", outdir))
- vars.append(("idlflags", flags))
- input = self.GypPathToNinja(source)
- self.ninja.build(output, "idl", input, variables=vars, order_only=prebuild)
- outputs.extend(output)
- def WriteWinIdlFiles(self, spec, prebuild):
- """Writes rules to match MSVS's implicit idl handling."""
- assert self.flavor == "win"
- if self.msvs_settings.HasExplicitIdlRulesOrActions(spec):
- return []
- outputs = []
- for source in filter(lambda x: x.endswith(".idl"), spec["sources"]):
- self._WinIdlRule(source, prebuild, outputs)
- return outputs
- def WriteActionsRulesCopies(
- self, spec, extra_sources, prebuild, mac_bundle_depends
- ):
- """Write out the Actions, Rules, and Copies steps. Return a path
- representing the outputs of these steps."""
- outputs = []
- if self.is_mac_bundle:
- mac_bundle_resources = spec.get("mac_bundle_resources", [])[:]
- else:
- mac_bundle_resources = []
- extra_mac_bundle_resources = []
- if "actions" in spec:
- outputs += self.WriteActions(
- spec["actions"], extra_sources, prebuild, extra_mac_bundle_resources
- )
- if "rules" in spec:
- outputs += self.WriteRules(
- spec["rules"],
- extra_sources,
- prebuild,
- mac_bundle_resources,
- extra_mac_bundle_resources,
- )
- if "copies" in spec:
- outputs += self.WriteCopies(spec["copies"], prebuild, mac_bundle_depends)
- if "sources" in spec and self.flavor == "win":
- outputs += self.WriteWinIdlFiles(spec, prebuild)
- if self.xcode_settings and self.xcode_settings.IsIosFramework():
- self.WriteiOSFrameworkHeaders(spec, outputs, prebuild)
- stamp = self.WriteCollapsedDependencies("actions_rules_copies", outputs)
- if self.is_mac_bundle:
- xcassets = self.WriteMacBundleResources(
- extra_mac_bundle_resources + mac_bundle_resources, mac_bundle_depends
- )
- partial_info_plist = self.WriteMacXCassets(xcassets, mac_bundle_depends)
- self.WriteMacInfoPlist(partial_info_plist, mac_bundle_depends)
- return stamp
- def GenerateDescription(self, verb, message, fallback):
- """Generate and return a description of a build step.
- |verb| is the short summary, e.g. ACTION or RULE.
- |message| is a hand-written description, or None if not available.
- |fallback| is the gyp-level name of the step, usable as a fallback.
- """
- if self.toolset != "target":
- verb += "(%s)" % self.toolset
- if message:
- return f"{verb} {self.ExpandSpecial(message)}"
- else:
- return f"{verb} {self.name}: {fallback}"
- def WriteActions(
- self, actions, extra_sources, prebuild, extra_mac_bundle_resources
- ):
- # Actions cd into the base directory.
- env = self.GetToolchainEnv()
- all_outputs = []
- for action in actions:
- # First write out a rule for the action.
- name = "{}_{}".format(action["action_name"], self.hash_for_rules)
- description = self.GenerateDescription(
- "ACTION", action.get("message", None), name
- )
- win_shell_flags = (
- self.msvs_settings.GetRuleShellFlags(action)
- if self.flavor == "win"
- else None
- )
- args = action["action"]
- depfile = action.get("depfile", None)
- if depfile:
- depfile = self.ExpandSpecial(depfile, self.base_to_build)
- pool = "console" if int(action.get("ninja_use_console", 0)) else None
- rule_name, _ = self.WriteNewNinjaRule(
- name, args, description, win_shell_flags, env, pool, depfile=depfile
- )
- inputs = [self.GypPathToNinja(i, env) for i in action["inputs"]]
- if int(action.get("process_outputs_as_sources", False)):
- extra_sources += action["outputs"]
- if int(action.get("process_outputs_as_mac_bundle_resources", False)):
- extra_mac_bundle_resources += action["outputs"]
- outputs = [self.GypPathToNinja(o, env) for o in action["outputs"]]
- # Then write out an edge using the rule.
- self.ninja.build(outputs, rule_name, inputs, order_only=prebuild)
- all_outputs += outputs
- self.ninja.newline()
- return all_outputs
- def WriteRules(
- self,
- rules,
- extra_sources,
- prebuild,
- mac_bundle_resources,
- extra_mac_bundle_resources,
- ):
- env = self.GetToolchainEnv()
- all_outputs = []
- for rule in rules:
- # Skip a rule with no action and no inputs.
- if "action" not in rule and not rule.get("rule_sources", []):
- continue
- # First write out a rule for the rule action.
- name = "{}_{}".format(rule["rule_name"], self.hash_for_rules)
- args = rule["action"]
- description = self.GenerateDescription(
- "RULE",
- rule.get("message", None),
- ("%s " + generator_default_variables["RULE_INPUT_PATH"]) % name,
- )
- win_shell_flags = (
- self.msvs_settings.GetRuleShellFlags(rule)
- if self.flavor == "win"
- else None
- )
- pool = "console" if int(rule.get("ninja_use_console", 0)) else None
- rule_name, args = self.WriteNewNinjaRule(
- name, args, description, win_shell_flags, env, pool
- )
- # TODO: if the command references the outputs directly, we should
- # simplify it to just use $out.
- # Rules can potentially make use of some special variables which
- # must vary per source file.
- # Compute the list of variables we'll need to provide.
- special_locals = ("source", "root", "dirname", "ext", "name")
- needed_variables = {"source"}
- for argument in args:
- for var in special_locals:
- if "${%s}" % var in argument:
- needed_variables.add(var)
- needed_variables = sorted(needed_variables)
- def cygwin_munge(path):
- # pylint: disable=cell-var-from-loop
- if win_shell_flags and win_shell_flags.cygwin:
- return path.replace("\\", "/")
- return path
- inputs = [self.GypPathToNinja(i, env) for i in rule.get("inputs", [])]
- # If there are n source files matching the rule, and m additional rule
- # inputs, then adding 'inputs' to each build edge written below will
- # write m * n inputs. Collapsing reduces this to m + n.
- sources = rule.get("rule_sources", [])
- num_inputs = len(inputs)
- if prebuild:
- num_inputs += 1
- if num_inputs > 2 and len(sources) > 2:
- inputs = [
- self.WriteCollapsedDependencies(
- rule["rule_name"], inputs, order_only=prebuild
- )
- ]
- prebuild = []
- # For each source file, write an edge that generates all the outputs.
- for source in sources:
- source = os.path.normpath(source)
- dirname, basename = os.path.split(source)
- root, ext = os.path.splitext(basename)
- # Gather the list of inputs and outputs, expanding $vars if possible.
- outputs = [
- self.ExpandRuleVariables(o, root, dirname, source, ext, basename)
- for o in rule["outputs"]
- ]
- if int(rule.get("process_outputs_as_sources", False)):
- extra_sources += outputs
- was_mac_bundle_resource = source in mac_bundle_resources
- if was_mac_bundle_resource or int(
- rule.get("process_outputs_as_mac_bundle_resources", False)
- ):
- extra_mac_bundle_resources += outputs
- # Note: This is n_resources * n_outputs_in_rule.
- # Put to-be-removed items in a set and
- # remove them all in a single pass
- # if this becomes a performance issue.
- if was_mac_bundle_resource:
- mac_bundle_resources.remove(source)
- extra_bindings = []
- for var in needed_variables:
- if var == "root":
- extra_bindings.append(("root", cygwin_munge(root)))
- elif var == "dirname":
- # '$dirname' is a parameter to the rule action, which means
- # it shouldn't be converted to a Ninja path. But we don't
- # want $!PRODUCT_DIR in there either.
- dirname_expanded = self.ExpandSpecial(
- dirname, self.base_to_build
- )
- extra_bindings.append(
- ("dirname", cygwin_munge(dirname_expanded))
- )
- elif var == "source":
- # '$source' is a parameter to the rule action, which means
- # it shouldn't be converted to a Ninja path. But we don't
- # want $!PRODUCT_DIR in there either.
- source_expanded = self.ExpandSpecial(source, self.base_to_build)
- extra_bindings.append(("source", cygwin_munge(source_expanded)))
- elif var == "ext":
- extra_bindings.append(("ext", ext))
- elif var == "name":
- extra_bindings.append(("name", cygwin_munge(basename)))
- else:
- assert var is None, repr(var)
- outputs = [self.GypPathToNinja(o, env) for o in outputs]
- if self.flavor == "win":
- # WriteNewNinjaRule uses unique_name to create a rsp file on win.
- extra_bindings.append(
- ("unique_name", hashlib.md5(outputs[0]).hexdigest())
- )
- self.ninja.build(
- outputs,
- rule_name,
- self.GypPathToNinja(source),
- implicit=inputs,
- order_only=prebuild,
- variables=extra_bindings,
- )
- all_outputs.extend(outputs)
- return all_outputs
- def WriteCopies(self, copies, prebuild, mac_bundle_depends):
- outputs = []
- if self.xcode_settings:
- extra_env = self.xcode_settings.GetPerTargetSettings()
- env = self.GetToolchainEnv(additional_settings=extra_env)
- else:
- env = self.GetToolchainEnv()
- for to_copy in copies:
- for path in to_copy["files"]:
- # Normalize the path so trailing slashes don't confuse us.
- path = os.path.normpath(path)
- basename = os.path.split(path)[1]
- src = self.GypPathToNinja(path, env)
- dst = self.GypPathToNinja(
- os.path.join(to_copy["destination"], basename), env
- )
- outputs += self.ninja.build(dst, "copy", src, order_only=prebuild)
- if self.is_mac_bundle:
- # gyp has mac_bundle_resources to copy things into a bundle's
- # Resources folder, but there's no built-in way to copy files
- # to other places in the bundle.
- # Hence, some targets use copies for this.
- # Check if this file is copied into the current bundle,
- # and if so add it to the bundle depends so
- # that dependent targets get rebuilt if the copy input changes.
- if dst.startswith(
- self.xcode_settings.GetBundleContentsFolderPath()
- ):
- mac_bundle_depends.append(dst)
- return outputs
- def WriteiOSFrameworkHeaders(self, spec, outputs, prebuild):
- """Prebuild steps to generate hmap files and copy headers to destination."""
- framework = self.ComputeMacBundleOutput()
- all_sources = spec["sources"]
- copy_headers = spec["mac_framework_headers"]
- output = self.GypPathToUniqueOutput("headers.hmap")
- self.xcode_settings.header_map_path = output
- all_headers = map(
- self.GypPathToNinja, filter(lambda x: x.endswith(".h"), all_sources)
- )
- variables = [
- ("framework", framework),
- ("copy_headers", map(self.GypPathToNinja, copy_headers)),
- ]
- outputs.extend(
- self.ninja.build(
- output,
- "compile_ios_framework_headers",
- all_headers,
- variables=variables,
- order_only=prebuild,
- )
- )
- def WriteMacBundleResources(self, resources, bundle_depends):
- """Writes ninja edges for 'mac_bundle_resources'."""
- xcassets = []
- extra_env = self.xcode_settings.GetPerTargetSettings()
- env = self.GetSortedXcodeEnv(additional_settings=extra_env)
- env = self.ComputeExportEnvString(env)
- isBinary = self.xcode_settings.IsBinaryOutputFormat(self.config_name)
- for output, res in gyp.xcode_emulation.GetMacBundleResources(
- generator_default_variables["PRODUCT_DIR"],
- self.xcode_settings,
- map(self.GypPathToNinja, resources),
- ):
- output = self.ExpandSpecial(output)
- if os.path.splitext(output)[-1] != ".xcassets":
- self.ninja.build(
- output,
- "mac_tool",
- res,
- variables=[
- ("mactool_cmd", "copy-bundle-resource"),
- ("env", env),
- ("binary", isBinary),
- ],
- )
- bundle_depends.append(output)
- else:
- xcassets.append(res)
- return xcassets
- def WriteMacXCassets(self, xcassets, bundle_depends):
- """Writes ninja edges for 'mac_bundle_resources' .xcassets files.
- This add an invocation of 'actool' via the 'mac_tool.py' helper script.
- It assumes that the assets catalogs define at least one imageset and
- thus an Assets.car file will be generated in the application resources
- directory. If this is not the case, then the build will probably be done
- at each invocation of ninja."""
- if not xcassets:
- return
- extra_arguments = {}
- settings_to_arg = {
- "XCASSETS_APP_ICON": "app-icon",
- "XCASSETS_LAUNCH_IMAGE": "launch-image",
- }
- settings = self.xcode_settings.xcode_settings[self.config_name]
- for settings_key, arg_name in settings_to_arg.items():
- value = settings.get(settings_key)
- if value:
- extra_arguments[arg_name] = value
- partial_info_plist = None
- if extra_arguments:
- partial_info_plist = self.GypPathToUniqueOutput(
- "assetcatalog_generated_info.plist"
- )
- extra_arguments["output-partial-info-plist"] = partial_info_plist
- outputs = []
- outputs.append(
- os.path.join(self.xcode_settings.GetBundleResourceFolder(), "Assets.car")
- )
- if partial_info_plist:
- outputs.append(partial_info_plist)
- keys = QuoteShellArgument(json.dumps(extra_arguments), self.flavor)
- extra_env = self.xcode_settings.GetPerTargetSettings()
- env = self.GetSortedXcodeEnv(additional_settings=extra_env)
- env = self.ComputeExportEnvString(env)
- bundle_depends.extend(
- self.ninja.build(
- outputs,
- "compile_xcassets",
- xcassets,
- variables=[("env", env), ("keys", keys)],
- )
- )
- return partial_info_plist
- def WriteMacInfoPlist(self, partial_info_plist, bundle_depends):
- """Write build rules for bundle Info.plist files."""
- info_plist, out, defines, extra_env = gyp.xcode_emulation.GetMacInfoPlist(
- generator_default_variables["PRODUCT_DIR"],
- self.xcode_settings,
- self.GypPathToNinja,
- )
- if not info_plist:
- return
- out = self.ExpandSpecial(out)
- if defines:
- # Create an intermediate file to store preprocessed results.
- intermediate_plist = self.GypPathToUniqueOutput(
- os.path.basename(info_plist)
- )
- defines = " ".join([Define(d, self.flavor) for d in defines])
- info_plist = self.ninja.build(
- intermediate_plist,
- "preprocess_infoplist",
- info_plist,
- variables=[("defines", defines)],
- )
- env = self.GetSortedXcodeEnv(additional_settings=extra_env)
- env = self.ComputeExportEnvString(env)
- if partial_info_plist:
- intermediate_plist = self.GypPathToUniqueOutput("merged_info.plist")
- info_plist = self.ninja.build(
- intermediate_plist, "merge_infoplist", [partial_info_plist, info_plist]
- )
- keys = self.xcode_settings.GetExtraPlistItems(self.config_name)
- keys = QuoteShellArgument(json.dumps(keys), self.flavor)
- isBinary = self.xcode_settings.IsBinaryOutputFormat(self.config_name)
- self.ninja.build(
- out,
- "copy_infoplist",
- info_plist,
- variables=[("env", env), ("keys", keys), ("binary", isBinary)],
- )
- bundle_depends.append(out)
- def WriteSources(
- self,
- ninja_file,
- config_name,
- config,
- sources,
- predepends,
- precompiled_header,
- spec,
- ):
- """Write build rules to compile all of |sources|."""
- if self.toolset == "host":
- self.ninja.variable("ar", "$ar_host")
- self.ninja.variable("cc", "$cc_host")
- self.ninja.variable("cxx", "$cxx_host")
- self.ninja.variable("ld", "$ld_host")
- self.ninja.variable("ldxx", "$ldxx_host")
- self.ninja.variable("nm", "$nm_host")
- self.ninja.variable("readelf", "$readelf_host")
- if self.flavor != "mac" or len(self.archs) == 1:
- return self.WriteSourcesForArch(
- self.ninja,
- config_name,
- config,
- sources,
- predepends,
- precompiled_header,
- spec,
- )
- else:
- return {
- arch: self.WriteSourcesForArch(
- self.arch_subninjas[arch],
- config_name,
- config,
- sources,
- predepends,
- precompiled_header,
- spec,
- arch=arch,
- )
- for arch in self.archs
- }
- def WriteSourcesForArch(
- self,
- ninja_file,
- config_name,
- config,
- sources,
- predepends,
- precompiled_header,
- spec,
- arch=None,
- ):
- """Write build rules to compile all of |sources|."""
- extra_defines = []
- if self.flavor == "mac":
- cflags = self.xcode_settings.GetCflags(config_name, arch=arch)
- cflags_c = self.xcode_settings.GetCflagsC(config_name)
- cflags_cc = self.xcode_settings.GetCflagsCC(config_name)
- cflags_objc = ["$cflags_c"] + self.xcode_settings.GetCflagsObjC(config_name)
- cflags_objcc = ["$cflags_cc"] + self.xcode_settings.GetCflagsObjCC(
- config_name
- )
- elif self.flavor == "win":
- asmflags = self.msvs_settings.GetAsmflags(config_name)
- cflags = self.msvs_settings.GetCflags(config_name)
- cflags_c = self.msvs_settings.GetCflagsC(config_name)
- cflags_cc = self.msvs_settings.GetCflagsCC(config_name)
- extra_defines = self.msvs_settings.GetComputedDefines(config_name)
- # See comment at cc_command for why there's two .pdb files.
- pdbpath_c = pdbpath_cc = self.msvs_settings.GetCompilerPdbName(
- config_name, self.ExpandSpecial
- )
- if not pdbpath_c:
- obj = "obj"
- if self.toolset != "target":
- obj += "." + self.toolset
- pdbpath = os.path.normpath(os.path.join(obj, self.base_dir, self.name))
- pdbpath_c = pdbpath + ".c.pdb"
- pdbpath_cc = pdbpath + ".cc.pdb"
- self.WriteVariableList(ninja_file, "pdbname_c", [pdbpath_c])
- self.WriteVariableList(ninja_file, "pdbname_cc", [pdbpath_cc])
- self.WriteVariableList(ninja_file, "pchprefix", [self.name])
- else:
- cflags = config.get("cflags", [])
- cflags_c = config.get("cflags_c", [])
- cflags_cc = config.get("cflags_cc", [])
- # Respect environment variables related to build, but target-specific
- # flags can still override them.
- if self.toolset == "target":
- cflags_c = (
- os.environ.get("CPPFLAGS", "").split()
- + os.environ.get("CFLAGS", "").split()
- + cflags_c
- )
- cflags_cc = (
- os.environ.get("CPPFLAGS", "").split()
- + os.environ.get("CXXFLAGS", "").split()
- + cflags_cc
- )
- elif self.toolset == "host":
- cflags_c = (
- os.environ.get("CPPFLAGS_host", "").split()
- + os.environ.get("CFLAGS_host", "").split()
- + cflags_c
- )
- cflags_cc = (
- os.environ.get("CPPFLAGS_host", "").split()
- + os.environ.get("CXXFLAGS_host", "").split()
- + cflags_cc
- )
- defines = config.get("defines", []) + extra_defines
- self.WriteVariableList(
- ninja_file, "defines", [Define(d, self.flavor) for d in defines]
- )
- if self.flavor == "win":
- self.WriteVariableList(
- ninja_file, "asmflags", map(self.ExpandSpecial, asmflags)
- )
- self.WriteVariableList(
- ninja_file,
- "rcflags",
- [
- QuoteShellArgument(self.ExpandSpecial(f), self.flavor)
- for f in self.msvs_settings.GetRcflags(
- config_name, self.GypPathToNinja
- )
- ],
- )
- include_dirs = config.get("include_dirs", [])
- env = self.GetToolchainEnv()
- if self.flavor == "win":
- include_dirs = self.msvs_settings.AdjustIncludeDirs(
- include_dirs, config_name
- )
- self.WriteVariableList(
- ninja_file,
- "includes",
- [
- QuoteShellArgument("-I" + self.GypPathToNinja(i, env), self.flavor)
- for i in include_dirs
- ],
- )
- if self.flavor == "win":
- midl_include_dirs = config.get("midl_include_dirs", [])
- midl_include_dirs = self.msvs_settings.AdjustMidlIncludeDirs(
- midl_include_dirs, config_name
- )
- self.WriteVariableList(
- ninja_file,
- "midl_includes",
- [
- QuoteShellArgument("-I" + self.GypPathToNinja(i, env), self.flavor)
- for i in midl_include_dirs
- ],
- )
- pch_commands = precompiled_header.GetPchBuildCommands(arch)
- if self.flavor == "mac":
- # Most targets use no precompiled headers, so only write these if needed.
- for ext, var in [
- ("c", "cflags_pch_c"),
- ("cc", "cflags_pch_cc"),
- ("m", "cflags_pch_objc"),
- ("mm", "cflags_pch_objcc"),
- ]:
- include = precompiled_header.GetInclude(ext, arch)
- if include:
- ninja_file.variable(var, include)
- arflags = config.get("arflags", [])
- self.WriteVariableList(ninja_file, "cflags", map(self.ExpandSpecial, cflags))
- self.WriteVariableList(
- ninja_file, "cflags_c", map(self.ExpandSpecial, cflags_c)
- )
- self.WriteVariableList(
- ninja_file, "cflags_cc", map(self.ExpandSpecial, cflags_cc)
- )
- if self.flavor == "mac":
- self.WriteVariableList(
- ninja_file, "cflags_objc", map(self.ExpandSpecial, cflags_objc)
- )
- self.WriteVariableList(
- ninja_file, "cflags_objcc", map(self.ExpandSpecial, cflags_objcc)
- )
- self.WriteVariableList(ninja_file, "arflags", map(self.ExpandSpecial, arflags))
- ninja_file.newline()
- outputs = []
- has_rc_source = False
- for source in sources:
- filename, ext = os.path.splitext(source)
- ext = ext[1:]
- obj_ext = self.obj_ext
- if ext in ("cc", "cpp", "cxx"):
- command = "cxx"
- self.target.uses_cpp = True
- elif ext == "c" or (ext == "S" and self.flavor != "win"):
- command = "cc"
- elif ext == "s" and self.flavor != "win": # Doesn't generate .o.d files.
- command = "cc_s"
- elif (
- self.flavor == "win"
- and ext in ("asm", "S")
- and not self.msvs_settings.HasExplicitAsmRules(spec)
- ):
- command = "asm"
- # Add the _asm suffix as msvs is capable of handling .cc and
- # .asm files of the same name without collision.
- obj_ext = "_asm.obj"
- elif self.flavor == "mac" and ext == "m":
- command = "objc"
- elif self.flavor == "mac" and ext == "mm":
- command = "objcxx"
- self.target.uses_cpp = True
- elif self.flavor == "win" and ext == "rc":
- command = "rc"
- obj_ext = ".res"
- has_rc_source = True
- else:
- # Ignore unhandled extensions.
- continue
- input = self.GypPathToNinja(source)
- output = self.GypPathToUniqueOutput(filename + obj_ext)
- if arch is not None:
- output = AddArch(output, arch)
- implicit = precompiled_header.GetObjDependencies([input], [output], arch)
- variables = []
- if self.flavor == "win":
- variables, output, implicit = precompiled_header.GetFlagsModifications(
- input,
- output,
- implicit,
- command,
- cflags_c,
- cflags_cc,
- self.ExpandSpecial,
- )
- ninja_file.build(
- output,
- command,
- input,
- implicit=[gch for _, _, gch in implicit],
- order_only=predepends,
- variables=variables,
- )
- outputs.append(output)
- if has_rc_source:
- resource_include_dirs = config.get("resource_include_dirs", include_dirs)
- self.WriteVariableList(
- ninja_file,
- "resource_includes",
- [
- QuoteShellArgument("-I" + self.GypPathToNinja(i, env), self.flavor)
- for i in resource_include_dirs
- ],
- )
- self.WritePchTargets(ninja_file, pch_commands)
- ninja_file.newline()
- return outputs
- def WritePchTargets(self, ninja_file, pch_commands):
- """Writes ninja rules to compile prefix headers."""
- if not pch_commands:
- return
- for gch, lang_flag, lang, input in pch_commands:
- var_name = {
- "c": "cflags_pch_c",
- "cc": "cflags_pch_cc",
- "m": "cflags_pch_objc",
- "mm": "cflags_pch_objcc",
- }[lang]
- map = {
- "c": "cc",
- "cc": "cxx",
- "m": "objc",
- "mm": "objcxx",
- }
- cmd = map.get(lang)
- ninja_file.build(gch, cmd, input, variables=[(var_name, lang_flag)])
- def WriteLink(self, spec, config_name, config, link_deps, compile_deps):
- """Write out a link step. Fills out target.binary. """
- if self.flavor != "mac" or len(self.archs) == 1:
- return self.WriteLinkForArch(
- self.ninja, spec, config_name, config, link_deps, compile_deps
- )
- else:
- output = self.ComputeOutput(spec)
- inputs = [
- self.WriteLinkForArch(
- self.arch_subninjas[arch],
- spec,
- config_name,
- config,
- link_deps[arch],
- compile_deps,
- arch=arch,
- )
- for arch in self.archs
- ]
- extra_bindings = []
- build_output = output
- if not self.is_mac_bundle:
- self.AppendPostbuildVariable(extra_bindings, spec, output, output)
- # TODO(yyanagisawa): more work needed to fix:
- # https://code.google.com/p/gyp/issues/detail?id=411
- if (
- spec["type"] in ("shared_library", "loadable_module")
- and not self.is_mac_bundle
- ):
- extra_bindings.append(("lib", output))
- self.ninja.build(
- [output, output + ".TOC"],
- "solipo",
- inputs,
- variables=extra_bindings,
- )
- else:
- self.ninja.build(build_output, "lipo", inputs, variables=extra_bindings)
- return output
- def WriteLinkForArch(
- self, ninja_file, spec, config_name, config, link_deps, compile_deps, arch=None
- ):
- """Write out a link step. Fills out target.binary. """
- command = {
- "executable": "link",
- "loadable_module": "solink_module",
- "shared_library": "solink",
- }[spec["type"]]
- command_suffix = ""
- implicit_deps = set()
- solibs = set()
- order_deps = set()
- if compile_deps:
- # Normally, the compiles of the target already depend on compile_deps,
- # but a shared_library target might have no sources and only link together
- # a few static_library deps, so the link step also needs to depend
- # on compile_deps to make sure actions in the shared_library target
- # get run before the link.
- order_deps.add(compile_deps)
- if "dependencies" in spec:
- # Two kinds of dependencies:
- # - Linkable dependencies (like a .a or a .so): add them to the link line.
- # - Non-linkable dependencies (like a rule that generates a file
- # and writes a stamp file): add them to implicit_deps
- extra_link_deps = set()
- for dep in spec["dependencies"]:
- target = self.target_outputs.get(dep)
- if not target:
- continue
- linkable = target.Linkable()
- if linkable:
- new_deps = []
- if (
- self.flavor == "win"
- and target.component_objs
- and self.msvs_settings.IsUseLibraryDependencyInputs(config_name)
- ):
- new_deps = target.component_objs
- if target.compile_deps:
- order_deps.add(target.compile_deps)
- elif self.flavor == "win" and target.import_lib:
- new_deps = [target.import_lib]
- elif target.UsesToc(self.flavor):
- solibs.add(target.binary)
- implicit_deps.add(target.binary + ".TOC")
- else:
- new_deps = [target.binary]
- for new_dep in new_deps:
- if new_dep not in extra_link_deps:
- extra_link_deps.add(new_dep)
- link_deps.append(new_dep)
- final_output = target.FinalOutput()
- if not linkable or final_output != target.binary:
- implicit_deps.add(final_output)
- extra_bindings = []
- if self.target.uses_cpp and self.flavor != "win":
- extra_bindings.append(("ld", "$ldxx"))
- output = self.ComputeOutput(spec, arch)
- if arch is None and not self.is_mac_bundle:
- self.AppendPostbuildVariable(extra_bindings, spec, output, output)
- is_executable = spec["type"] == "executable"
- # The ldflags config key is not used on mac or win. On those platforms
- # linker flags are set via xcode_settings and msvs_settings, respectively.
- if self.toolset == "target":
- env_ldflags = os.environ.get("LDFLAGS", "").split()
- elif self.toolset == "host":
- env_ldflags = os.environ.get("LDFLAGS_host", "").split()
- if self.flavor == "mac":
- ldflags = self.xcode_settings.GetLdflags(
- config_name,
- self.ExpandSpecial(generator_default_variables["PRODUCT_DIR"]),
- self.GypPathToNinja,
- arch,
- )
- ldflags = env_ldflags + ldflags
- elif self.flavor == "win":
- manifest_base_name = self.GypPathToUniqueOutput(
- self.ComputeOutputFileName(spec)
- )
- (
- ldflags,
- intermediate_manifest,
- manifest_files,
- ) = self.msvs_settings.GetLdflags(
- config_name,
- self.GypPathToNinja,
- self.ExpandSpecial,
- manifest_base_name,
- output,
- is_executable,
- self.toplevel_build,
- )
- ldflags = env_ldflags + ldflags
- self.WriteVariableList(ninja_file, "manifests", manifest_files)
- implicit_deps = implicit_deps.union(manifest_files)
- if intermediate_manifest:
- self.WriteVariableList(
- ninja_file, "intermediatemanifest", [intermediate_manifest]
- )
- command_suffix = _GetWinLinkRuleNameSuffix(
- self.msvs_settings.IsEmbedManifest(config_name)
- )
- def_file = self.msvs_settings.GetDefFile(self.GypPathToNinja)
- if def_file:
- implicit_deps.add(def_file)
- else:
- # Respect environment variables related to build, but target-specific
- # flags can still override them.
- ldflags = env_ldflags + config.get("ldflags", [])
- if is_executable and len(solibs):
- rpath = "lib/"
- if self.toolset != "target":
- rpath += self.toolset
- ldflags.append(r"-Wl,-rpath=\$$ORIGIN/%s" % rpath)
- else:
- ldflags.append("-Wl,-rpath=%s" % self.target_rpath)
- ldflags.append("-Wl,-rpath-link=%s" % rpath)
- self.WriteVariableList(ninja_file, "ldflags", map(self.ExpandSpecial, ldflags))
- library_dirs = config.get("library_dirs", [])
- if self.flavor == "win":
- library_dirs = [
- self.msvs_settings.ConvertVSMacros(library_dir, config_name)
- for library_dir in library_dirs
- ]
- library_dirs = [
- "/LIBPATH:"
- + QuoteShellArgument(self.GypPathToNinja(library_dir), self.flavor)
- for library_dir in library_dirs
- ]
- else:
- library_dirs = [
- QuoteShellArgument("-L" + self.GypPathToNinja(library_dir), self.flavor)
- for library_dir in library_dirs
- ]
- libraries = gyp.common.uniquer(
- map(self.ExpandSpecial, spec.get("libraries", []))
- )
- if self.flavor == "mac":
- libraries = self.xcode_settings.AdjustLibraries(libraries, config_name)
- elif self.flavor == "win":
- libraries = self.msvs_settings.AdjustLibraries(libraries)
- self.WriteVariableList(ninja_file, "libs", library_dirs + libraries)
- linked_binary = output
- if command in ("solink", "solink_module"):
- extra_bindings.append(("soname", os.path.split(output)[1]))
- extra_bindings.append(("lib", gyp.common.EncodePOSIXShellArgument(output)))
- if self.flavor != "win":
- link_file_list = output
- if self.is_mac_bundle:
- # 'Dependency Framework.framework/Versions/A/Dependency Framework'
- # -> 'Dependency Framework.framework.rsp'
- link_file_list = self.xcode_settings.GetWrapperName()
- if arch:
- link_file_list += "." + arch
- link_file_list += ".rsp"
- # If an rspfile contains spaces, ninja surrounds the filename with
- # quotes around it and then passes it to open(), creating a file with
- # quotes in its name (and when looking for the rsp file, the name
- # makes it through bash which strips the quotes) :-/
- link_file_list = link_file_list.replace(" ", "_")
- extra_bindings.append(
- (
- "link_file_list",
- gyp.common.EncodePOSIXShellArgument(link_file_list),
- )
- )
- if self.flavor == "win":
- extra_bindings.append(("binary", output))
- if (
- "/NOENTRY" not in ldflags
- and not self.msvs_settings.GetNoImportLibrary(config_name)
- ):
- self.target.import_lib = output + ".lib"
- extra_bindings.append(
- ("implibflag", "/IMPLIB:%s" % self.target.import_lib)
- )
- pdbname = self.msvs_settings.GetPDBName(
- config_name, self.ExpandSpecial, output + ".pdb"
- )
- output = [output, self.target.import_lib]
- if pdbname:
- output.append(pdbname)
- elif not self.is_mac_bundle:
- output = [output, output + ".TOC"]
- else:
- command = command + "_notoc"
- elif self.flavor == "win":
- extra_bindings.append(("binary", output))
- pdbname = self.msvs_settings.GetPDBName(
- config_name, self.ExpandSpecial, output + ".pdb"
- )
- if pdbname:
- output = [output, pdbname]
- if len(solibs):
- extra_bindings.append(
- ("solibs", gyp.common.EncodePOSIXShellList(sorted(solibs)))
- )
- ninja_file.build(
- output,
- command + command_suffix,
- link_deps,
- implicit=sorted(implicit_deps),
- order_only=list(order_deps),
- variables=extra_bindings,
- )
- return linked_binary
- def WriteTarget(self, spec, config_name, config, link_deps, compile_deps):
- extra_link_deps = any(
- self.target_outputs.get(dep).Linkable()
- for dep in spec.get("dependencies", [])
- if dep in self.target_outputs
- )
- if spec["type"] == "none" or (not link_deps and not extra_link_deps):
- # TODO(evan): don't call this function for 'none' target types, as
- # it doesn't do anything, and we fake out a 'binary' with a stamp file.
- self.target.binary = compile_deps
- self.target.type = "none"
- elif spec["type"] == "static_library":
- self.target.binary = self.ComputeOutput(spec)
- if (
- self.flavor not in ("ios", "mac", "netbsd", "openbsd", "win")
- and not self.is_standalone_static_library
- ):
- self.ninja.build(
- self.target.binary, "alink_thin", link_deps, order_only=compile_deps
- )
- else:
- variables = []
- if self.xcode_settings:
- libtool_flags = self.xcode_settings.GetLibtoolflags(config_name)
- if libtool_flags:
- variables.append(("libtool_flags", libtool_flags))
- if self.msvs_settings:
- libflags = self.msvs_settings.GetLibFlags(
- config_name, self.GypPathToNinja
- )
- variables.append(("libflags", libflags))
- if self.flavor != "mac" or len(self.archs) == 1:
- self.AppendPostbuildVariable(
- variables, spec, self.target.binary, self.target.binary
- )
- self.ninja.build(
- self.target.binary,
- "alink",
- link_deps,
- order_only=compile_deps,
- variables=variables,
- )
- else:
- inputs = []
- for arch in self.archs:
- output = self.ComputeOutput(spec, arch)
- self.arch_subninjas[arch].build(
- output,
- "alink",
- link_deps[arch],
- order_only=compile_deps,
- variables=variables,
- )
- inputs.append(output)
- # TODO: It's not clear if
- # libtool_flags should be passed to the alink
- # call that combines single-arch .a files into a fat .a file.
- self.AppendPostbuildVariable(
- variables, spec, self.target.binary, self.target.binary
- )
- self.ninja.build(
- self.target.binary,
- "alink",
- inputs,
- # FIXME: test proving order_only=compile_deps isn't
- # needed.
- variables=variables,
- )
- else:
- self.target.binary = self.WriteLink(
- spec, config_name, config, link_deps, compile_deps
- )
- return self.target.binary
- def WriteMacBundle(self, spec, mac_bundle_depends, is_empty):
- assert self.is_mac_bundle
- package_framework = spec["type"] in ("shared_library", "loadable_module")
- output = self.ComputeMacBundleOutput()
- if is_empty:
- output += ".stamp"
- variables = []
- self.AppendPostbuildVariable(
- variables,
- spec,
- output,
- self.target.binary,
- is_command_start=not package_framework,
- )
- if package_framework and not is_empty:
- if spec["type"] == "shared_library" and self.xcode_settings.isIOS:
- self.ninja.build(
- output,
- "package_ios_framework",
- mac_bundle_depends,
- variables=variables,
- )
- else:
- variables.append(("version", self.xcode_settings.GetFrameworkVersion()))
- self.ninja.build(
- output, "package_framework", mac_bundle_depends, variables=variables
- )
- else:
- self.ninja.build(output, "stamp", mac_bundle_depends, variables=variables)
- self.target.bundle = output
- return output
- def GetToolchainEnv(self, additional_settings=None):
- """Returns the variables toolchain would set for build steps."""
- env = self.GetSortedXcodeEnv(additional_settings=additional_settings)
- if self.flavor == "win":
- env = self.GetMsvsToolchainEnv(additional_settings=additional_settings)
- return env
- def GetMsvsToolchainEnv(self, additional_settings=None):
- """Returns the variables Visual Studio would set for build steps."""
- return self.msvs_settings.GetVSMacroEnv(
- "$!PRODUCT_DIR", config=self.config_name
- )
- def GetSortedXcodeEnv(self, additional_settings=None):
- """Returns the variables Xcode would set for build steps."""
- assert self.abs_build_dir
- abs_build_dir = self.abs_build_dir
- return gyp.xcode_emulation.GetSortedXcodeEnv(
- self.xcode_settings,
- abs_build_dir,
- os.path.join(abs_build_dir, self.build_to_base),
- self.config_name,
- additional_settings,
- )
- def GetSortedXcodePostbuildEnv(self):
- """Returns the variables Xcode would set for postbuild steps."""
- postbuild_settings = {}
- # 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"
- )
- if strip_save_file:
- postbuild_settings["CHROMIUM_STRIP_SAVE_FILE"] = strip_save_file
- return self.GetSortedXcodeEnv(additional_settings=postbuild_settings)
- def AppendPostbuildVariable(
- self, variables, spec, output, binary, is_command_start=False
- ):
- """Adds a 'postbuild' variable if there is a postbuild for |output|."""
- postbuild = self.GetPostbuildCommand(spec, output, binary, is_command_start)
- if postbuild:
- variables.append(("postbuilds", postbuild))
- def GetPostbuildCommand(self, spec, output, output_binary, is_command_start):
- """Returns a shell command that runs all the postbuilds, and removes
- |output| if any of them fails. If |is_command_start| is False, then the
- returned string will start with ' && '."""
- if not self.xcode_settings or spec["type"] == "none" or not output:
- return ""
- output = QuoteShellArgument(output, self.flavor)
- postbuilds = gyp.xcode_emulation.GetSpecPostbuildCommands(spec, quiet=True)
- if output_binary is not None:
- postbuilds = self.xcode_settings.AddImplicitPostbuilds(
- self.config_name,
- os.path.normpath(os.path.join(self.base_to_build, output)),
- QuoteShellArgument(
- os.path.normpath(os.path.join(self.base_to_build, output_binary)),
- self.flavor,
- ),
- postbuilds,
- quiet=True,
- )
- if not postbuilds:
- return ""
- # 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.build_to_base])
- )
- env = self.ComputeExportEnvString(self.GetSortedXcodePostbuildEnv())
- # G will be non-null if any postbuild fails. Run all postbuilds in a
- # subshell.
- commands = (
- env
- + " ("
- + " && ".join([ninja_syntax.escape(command) for command in postbuilds])
- )
- command_string = (
- commands
- + "); G=$$?; "
- # Remove the final output if any postbuild failed.
- "((exit $$G) || rm -rf %s) " % output
- + "&& exit $$G)"
- )
- if is_command_start:
- return "(" + command_string + " && "
- else:
- return "$ && (" + command_string
- def ComputeExportEnvString(self, env):
- """Given an environment, returns a string looking like
- 'export FOO=foo; export BAR="${FOO} bar;'
- that exports |env| to the shell."""
- export_str = []
- for k, v in env:
- export_str.append(
- "export %s=%s;"
- % (k, ninja_syntax.escape(gyp.common.EncodePOSIXShellArgument(v)))
- )
- return " ".join(export_str)
- def ComputeMacBundleOutput(self):
- """Return the 'output' (full output path) to a bundle output directory."""
- assert self.is_mac_bundle
- path = generator_default_variables["PRODUCT_DIR"]
- return self.ExpandSpecial(
- os.path.join(path, self.xcode_settings.GetWrapperName())
- )
- def ComputeOutputFileName(self, spec, type=None):
- """Compute the filename of the final output for the current target."""
- if not type:
- type = spec["type"]
- default_variables = copy.copy(generator_default_variables)
- CalculateVariables(default_variables, {"flavor": self.flavor})
- # Compute filename prefix: the product prefix, or a default for
- # the product type.
- DEFAULT_PREFIX = {
- "loadable_module": default_variables["SHARED_LIB_PREFIX"],
- "shared_library": default_variables["SHARED_LIB_PREFIX"],
- "static_library": default_variables["STATIC_LIB_PREFIX"],
- "executable": default_variables["EXECUTABLE_PREFIX"],
- }
- prefix = spec.get("product_prefix", DEFAULT_PREFIX.get(type, ""))
- # Compute filename extension: the product extension, or a default
- # for the product type.
- DEFAULT_EXTENSION = {
- "loadable_module": default_variables["SHARED_LIB_SUFFIX"],
- "shared_library": default_variables["SHARED_LIB_SUFFIX"],
- "static_library": default_variables["STATIC_LIB_SUFFIX"],
- "executable": default_variables["EXECUTABLE_SUFFIX"],
- }
- extension = spec.get("product_extension")
- extension = "." + extension if extension else DEFAULT_EXTENSION.get(type, "")
- if "product_name" in spec:
- # If we were given an explicit name, use that.
- target = spec["product_name"]
- else:
- # Otherwise, derive a name from the target name.
- target = spec["target_name"]
- if prefix == "lib":
- # Snip out an extra 'lib' from libs if appropriate.
- target = StripPrefix(target, "lib")
- if type in (
- "static_library",
- "loadable_module",
- "shared_library",
- "executable",
- ):
- return f"{prefix}{target}{extension}"
- elif type == "none":
- return "%s.stamp" % target
- else:
- raise Exception("Unhandled output type %s" % type)
- def ComputeOutput(self, spec, arch=None):
- """Compute the path for the final output of the spec."""
- type = spec["type"]
- if self.flavor == "win":
- override = self.msvs_settings.GetOutputName(
- self.config_name, self.ExpandSpecial
- )
- if override:
- return override
- if (
- arch is None
- and self.flavor == "mac"
- and type
- in ("static_library", "executable", "shared_library", "loadable_module")
- ):
- filename = self.xcode_settings.GetExecutablePath()
- else:
- filename = self.ComputeOutputFileName(spec, type)
- if arch is None and "product_dir" in spec:
- path = os.path.join(spec["product_dir"], filename)
- return self.ExpandSpecial(path)
- # Some products go into the output root, libraries go into shared library
- # dir, and everything else goes into the normal place.
- type_in_output_root = ["executable", "loadable_module"]
- if self.flavor == "mac" and self.toolset == "target":
- type_in_output_root += ["shared_library", "static_library"]
- elif self.flavor == "win" and self.toolset == "target":
- type_in_output_root += ["shared_library"]
- if arch is not None:
- # Make sure partial executables don't end up in a bundle or the regular
- # output directory.
- archdir = "arch"
- if self.toolset != "target":
- archdir = os.path.join("arch", "%s" % self.toolset)
- return os.path.join(archdir, AddArch(filename, arch))
- elif type in type_in_output_root or self.is_standalone_static_library:
- return filename
- elif type == "shared_library":
- libdir = "lib"
- if self.toolset != "target":
- libdir = os.path.join("lib", "%s" % self.toolset)
- return os.path.join(libdir, filename)
- else:
- return self.GypPathToUniqueOutput(filename, qualified=False)
- def WriteVariableList(self, ninja_file, var, values):
- assert not isinstance(values, str)
- if values is None:
- values = []
- ninja_file.variable(var, " ".join(values))
- def WriteNewNinjaRule(
- self, name, args, description, win_shell_flags, env, pool, depfile=None
- ):
- """Write out a new ninja "rule" statement for a given command.
- Returns the name of the new rule, and a copy of |args| with variables
- expanded."""
- if self.flavor == "win":
- args = [
- self.msvs_settings.ConvertVSMacros(
- arg, self.base_to_build, config=self.config_name
- )
- for arg in args
- ]
- description = self.msvs_settings.ConvertVSMacros(
- description, config=self.config_name
- )
- elif self.flavor == "mac":
- # |env| is an empty list on non-mac.
- args = [gyp.xcode_emulation.ExpandEnvVars(arg, env) for arg in args]
- description = gyp.xcode_emulation.ExpandEnvVars(description, env)
- # TODO: we shouldn't need to qualify names; we do it because
- # currently the ninja rule namespace is global, but it really
- # should be scoped to the subninja.
- rule_name = self.name
- if self.toolset == "target":
- rule_name += "." + self.toolset
- rule_name += "." + name
- rule_name = re.sub("[^a-zA-Z0-9_]", "_", rule_name)
- # Remove variable references, but not if they refer to the magic rule
- # variables. This is not quite right, as it also protects these for
- # actions, not just for rules where they are valid. Good enough.
- protect = ["${root}", "${dirname}", "${source}", "${ext}", "${name}"]
- protect = "(?!" + "|".join(map(re.escape, protect)) + ")"
- description = re.sub(protect + r"\$", "_", description)
- # gyp dictates that commands are run from the base directory.
- # cd into the directory before running, and adjust paths in
- # the arguments to point to the proper locations.
- rspfile = None
- rspfile_content = None
- args = [self.ExpandSpecial(arg, self.base_to_build) for arg in args]
- if self.flavor == "win":
- rspfile = rule_name + ".$unique_name.rsp"
- # The cygwin case handles this inside the bash sub-shell.
- run_in = "" if win_shell_flags.cygwin else " " + self.build_to_base
- if win_shell_flags.cygwin:
- rspfile_content = self.msvs_settings.BuildCygwinBashCommandLine(
- args, self.build_to_base
- )
- else:
- rspfile_content = gyp.msvs_emulation.EncodeRspFileList(
- args, win_shell_flags.quote)
- command = (
- "%s gyp-win-tool action-wrapper $arch " % sys.executable
- + rspfile
- + run_in
- )
- else:
- env = self.ComputeExportEnvString(env)
- command = gyp.common.EncodePOSIXShellList(args)
- command = "cd %s; " % self.build_to_base + env + command
- # GYP rules/actions express being no-ops by not touching their outputs.
- # Avoid executing downstream dependencies in this case by specifying
- # restat=1 to ninja.
- self.ninja.rule(
- rule_name,
- command,
- description,
- depfile=depfile,
- restat=True,
- pool=pool,
- rspfile=rspfile,
- rspfile_content=rspfile_content,
- )
- self.ninja.newline()
- return rule_name, args
- def CalculateVariables(default_variables, params):
- """Calculate additional variables for use in the build (called by gyp)."""
- global generator_additional_non_configuration_keys
- global generator_additional_path_sections
- 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 Ninja generator.
- import gyp.generator.xcode as xcode_generator
- generator_additional_non_configuration_keys = getattr(
- xcode_generator, "generator_additional_non_configuration_keys", []
- )
- 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", []
- )
- elif flavor == "win":
- exts = gyp.MSVSUtil.TARGET_TYPE_EXT
- default_variables.setdefault("OS", "win")
- default_variables["EXECUTABLE_SUFFIX"] = "." + exts["executable"]
- default_variables["STATIC_LIB_PREFIX"] = ""
- default_variables["STATIC_LIB_SUFFIX"] = "." + exts["static_library"]
- default_variables["SHARED_LIB_PREFIX"] = ""
- default_variables["SHARED_LIB_SUFFIX"] = "." + exts["shared_library"]
- # Copy additional generator configuration data from VS, which is shared
- # by the Windows Ninja generator.
- import gyp.generator.msvs as msvs_generator
- generator_additional_non_configuration_keys = getattr(
- msvs_generator, "generator_additional_non_configuration_keys", []
- )
- generator_additional_path_sections = getattr(
- msvs_generator, "generator_additional_path_sections", []
- )
- gyp.msvs_emulation.CalculateCommonVariables(default_variables, params)
- else:
- operating_system = flavor
- if flavor == "android":
- operating_system = "linux" # Keep this legacy behavior for now.
- default_variables.setdefault("OS", operating_system)
- default_variables.setdefault("SHARED_LIB_SUFFIX", ".so")
- default_variables.setdefault(
- "SHARED_LIB_DIR", os.path.join("$!PRODUCT_DIR", "lib")
- )
- default_variables.setdefault("LIB_DIR", os.path.join("$!PRODUCT_DIR", "obj"))
- def ComputeOutputDir(params):
- """Returns the path from the toplevel_dir to the build output directory."""
- # generator_dir: relative path from pwd to where make puts build files.
- # Makes migrating from make to ninja easier, ninja doesn't put anything here.
- generator_dir = os.path.relpath(params["options"].generator_output or ".")
- # output_dir: relative path from generator_dir to the build directory.
- output_dir = params.get("generator_flags", {}).get("output_dir", "out")
- # Relative path from source root to our output files. e.g. "out"
- return os.path.normpath(os.path.join(generator_dir, output_dir))
- def CalculateGeneratorInputInfo(params):
- """Called by __init__ to initialize generator values based on params."""
- # E.g. "out/gypfiles"
- toplevel = params["options"].toplevel_dir
- qualified_out_dir = os.path.normpath(
- os.path.join(toplevel, ComputeOutputDir(params), "gypfiles")
- )
- global generator_filelist_paths
- generator_filelist_paths = {
- "toplevel": toplevel,
- "qualified_out_dir": qualified_out_dir,
- }
- def OpenOutput(path, mode="w"):
- """Open |path| for writing, creating directories if necessary."""
- gyp.common.EnsureDirExists(path)
- return open(path, mode)
- def CommandWithWrapper(cmd, wrappers, prog):
- wrapper = wrappers.get(cmd, "")
- if wrapper:
- return wrapper + " " + prog
- return prog
- def GetDefaultConcurrentLinks():
- """Returns a best-guess for a number of concurrent links."""
- pool_size = int(os.environ.get("GYP_LINK_CONCURRENCY", 0))
- if pool_size:
- return pool_size
- if sys.platform in ("win32", "cygwin"):
- import ctypes
- class MEMORYSTATUSEX(ctypes.Structure):
- _fields_ = [
- ("dwLength", ctypes.c_ulong),
- ("dwMemoryLoad", ctypes.c_ulong),
- ("ullTotalPhys", ctypes.c_ulonglong),
- ("ullAvailPhys", ctypes.c_ulonglong),
- ("ullTotalPageFile", ctypes.c_ulonglong),
- ("ullAvailPageFile", ctypes.c_ulonglong),
- ("ullTotalVirtual", ctypes.c_ulonglong),
- ("ullAvailVirtual", ctypes.c_ulonglong),
- ("sullAvailExtendedVirtual", ctypes.c_ulonglong),
- ]
- stat = MEMORYSTATUSEX()
- stat.dwLength = ctypes.sizeof(stat)
- ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
- # VS 2015 uses 20% more working set than VS 2013 and can consume all RAM
- # on a 64 GiB machine.
- mem_limit = max(1, stat.ullTotalPhys // (5 * (2 ** 30))) # total / 5GiB
- hard_cap = max(1, int(os.environ.get("GYP_LINK_CONCURRENCY_MAX", 2 ** 32)))
- return min(mem_limit, hard_cap)
- elif sys.platform.startswith("linux"):
- if os.path.exists("/proc/meminfo"):
- with open("/proc/meminfo") as meminfo:
- memtotal_re = re.compile(r"^MemTotal:\s*(\d*)\s*kB")
- for line in meminfo:
- match = memtotal_re.match(line)
- if not match:
- continue
- # Allow 8Gb per link on Linux because Gold is quite memory hungry
- return max(1, int(match.group(1)) // (8 * (2 ** 20)))
- return 1
- elif sys.platform == "darwin":
- try:
- avail_bytes = int(subprocess.check_output(["sysctl", "-n", "hw.memsize"]))
- # A static library debug build of Chromium's unit_tests takes ~2.7GB, so
- # 4GB per ld process allows for some more bloat.
- return max(1, avail_bytes // (4 * (2 ** 30))) # total / 4GB
- except subprocess.CalledProcessError:
- return 1
- else:
- # TODO(scottmg): Implement this for other platforms.
- return 1
- def _GetWinLinkRuleNameSuffix(embed_manifest):
- """Returns the suffix used to select an appropriate linking rule depending on
- whether the manifest embedding is enabled."""
- return "_embed" if embed_manifest else ""
- def _AddWinLinkRules(master_ninja, embed_manifest):
- """Adds link rules for Windows platform to |master_ninja|."""
- def FullLinkCommand(ldcmd, out, binary_type):
- resource_name = {"exe": "1", "dll": "2"}[binary_type]
- return (
- "%(python)s gyp-win-tool link-with-manifests $arch %(embed)s "
- '%(out)s "%(ldcmd)s" %(resname)s $mt $rc "$intermediatemanifest" '
- "$manifests"
- % {
- "python": sys.executable,
- "out": out,
- "ldcmd": ldcmd,
- "resname": resource_name,
- "embed": embed_manifest,
- }
- )
- rule_name_suffix = _GetWinLinkRuleNameSuffix(embed_manifest)
- use_separate_mspdbsrv = int(os.environ.get("GYP_USE_SEPARATE_MSPDBSRV", "0")) != 0
- dlldesc = "LINK%s(DLL) $binary" % rule_name_suffix.upper()
- dllcmd = (
- "%s gyp-win-tool link-wrapper $arch %s "
- "$ld /nologo $implibflag /DLL /OUT:$binary "
- "@$binary.rsp" % (sys.executable, use_separate_mspdbsrv)
- )
- dllcmd = FullLinkCommand(dllcmd, "$binary", "dll")
- master_ninja.rule(
- "solink" + rule_name_suffix,
- description=dlldesc,
- command=dllcmd,
- rspfile="$binary.rsp",
- rspfile_content="$libs $in_newline $ldflags",
- restat=True,
- pool="link_pool",
- )
- master_ninja.rule(
- "solink_module" + rule_name_suffix,
- description=dlldesc,
- command=dllcmd,
- rspfile="$binary.rsp",
- rspfile_content="$libs $in_newline $ldflags",
- restat=True,
- pool="link_pool",
- )
- # Note that ldflags goes at the end so that it has the option of
- # overriding default settings earlier in the command line.
- exe_cmd = (
- "%s gyp-win-tool link-wrapper $arch %s "
- "$ld /nologo /OUT:$binary @$binary.rsp"
- % (sys.executable, use_separate_mspdbsrv)
- )
- exe_cmd = FullLinkCommand(exe_cmd, "$binary", "exe")
- master_ninja.rule(
- "link" + rule_name_suffix,
- description="LINK%s $binary" % rule_name_suffix.upper(),
- command=exe_cmd,
- rspfile="$binary.rsp",
- rspfile_content="$in_newline $libs $ldflags",
- pool="link_pool",
- )
- def GenerateOutputForConfig(target_list, target_dicts, data, params, config_name):
- options = params["options"]
- flavor = gyp.common.GetFlavor(params)
- generator_flags = params.get("generator_flags", {})
- generate_compile_commands = generator_flags.get("compile_commands", False)
- # build_dir: relative path from source root to our output files.
- # e.g. "out/Debug"
- build_dir = os.path.normpath(os.path.join(ComputeOutputDir(params), config_name))
- toplevel_build = os.path.join(options.toplevel_dir, build_dir)
- master_ninja_file = OpenOutput(os.path.join(toplevel_build, "build.ninja"))
- master_ninja = ninja_syntax.Writer(master_ninja_file, width=120)
- # Put build-time support tools in out/{config_name}.
- gyp.common.CopyTool(flavor, toplevel_build, generator_flags)
- # Grab make settings for CC/CXX.
- # The rules are
- # - The priority from low to high is gcc/g++, the 'make_global_settings' in
- # gyp, the environment variable.
- # - If there is no 'make_global_settings' for CC.host/CXX.host or
- # 'CC_host'/'CXX_host' environment variable, cc_host/cxx_host should be set
- # to cc/cxx.
- if flavor == "win":
- ar = "lib.exe"
- # cc and cxx must be set to the correct architecture by overriding with one
- # of cl_x86 or cl_x64 below.
- cc = "UNSET"
- cxx = "UNSET"
- ld = "link.exe"
- ld_host = "$ld"
- else:
- ar = "ar"
- cc = "cc"
- cxx = "c++"
- ld = "$cc"
- ldxx = "$cxx"
- ld_host = "$cc_host"
- ldxx_host = "$cxx_host"
- ar_host = ar
- cc_host = None
- cxx_host = None
- cc_host_global_setting = None
- cxx_host_global_setting = None
- clang_cl = None
- nm = "nm"
- nm_host = "nm"
- readelf = "readelf"
- readelf_host = "readelf"
- build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
- make_global_settings = data[build_file].get("make_global_settings", [])
- build_to_root = gyp.common.InvertRelativePath(build_dir, options.toplevel_dir)
- wrappers = {}
- for key, value in make_global_settings:
- if key == "AR":
- ar = os.path.join(build_to_root, value)
- if key == "AR.host":
- ar_host = os.path.join(build_to_root, value)
- if key == "CC":
- cc = os.path.join(build_to_root, value)
- if cc.endswith("clang-cl"):
- clang_cl = cc
- if key == "CXX":
- cxx = os.path.join(build_to_root, value)
- if key == "CC.host":
- cc_host = os.path.join(build_to_root, value)
- cc_host_global_setting = value
- if key == "CXX.host":
- cxx_host = os.path.join(build_to_root, value)
- cxx_host_global_setting = value
- if key == "LD":
- ld = os.path.join(build_to_root, value)
- if key == "LD.host":
- ld_host = os.path.join(build_to_root, value)
- if key == "LDXX":
- ldxx = os.path.join(build_to_root, value)
- if key == "LDXX.host":
- ldxx_host = os.path.join(build_to_root, value)
- if key == "NM":
- nm = os.path.join(build_to_root, value)
- if key == "NM.host":
- nm_host = os.path.join(build_to_root, value)
- if key == "READELF":
- readelf = os.path.join(build_to_root, value)
- if key == "READELF.host":
- readelf_host = os.path.join(build_to_root, value)
- if key.endswith("_wrapper"):
- wrappers[key[: -len("_wrapper")]] = os.path.join(build_to_root, value)
- # Support wrappers from environment variables too.
- for key, value in os.environ.items():
- if key.lower().endswith("_wrapper"):
- key_prefix = key[: -len("_wrapper")]
- key_prefix = re.sub(r"\.HOST$", ".host", key_prefix)
- wrappers[key_prefix] = os.path.join(build_to_root, value)
- mac_toolchain_dir = generator_flags.get("mac_toolchain_dir", None)
- if mac_toolchain_dir:
- wrappers["LINK"] = "export DEVELOPER_DIR='%s' &&" % mac_toolchain_dir
- if flavor == "win":
- configs = [
- target_dicts[qualified_target]["configurations"][config_name]
- for qualified_target in target_list
- ]
- shared_system_includes = None
- if not generator_flags.get("ninja_use_custom_environment_files", 0):
- shared_system_includes = gyp.msvs_emulation.ExtractSharedMSVSSystemIncludes(
- configs, generator_flags
- )
- cl_paths = gyp.msvs_emulation.GenerateEnvironmentFiles(
- toplevel_build, generator_flags, shared_system_includes, OpenOutput
- )
- for arch, path in sorted(cl_paths.items()):
- if clang_cl:
- # If we have selected clang-cl, use that instead.
- path = clang_cl
- command = CommandWithWrapper(
- "CC", wrappers, QuoteShellArgument(path, "win")
- )
- if clang_cl:
- # Use clang-cl to cross-compile for x86 or x86_64.
- command += " -m32" if arch == "x86" else " -m64"
- master_ninja.variable("cl_" + arch, command)
- cc = GetEnvironFallback(["CC_target", "CC"], cc)
- master_ninja.variable("cc", CommandWithWrapper("CC", wrappers, cc))
- cxx = GetEnvironFallback(["CXX_target", "CXX"], cxx)
- master_ninja.variable("cxx", CommandWithWrapper("CXX", wrappers, cxx))
- if flavor == "win":
- master_ninja.variable("ld", ld)
- master_ninja.variable("idl", "midl.exe")
- master_ninja.variable("ar", ar)
- master_ninja.variable("rc", "rc.exe")
- master_ninja.variable("ml_x86", "ml.exe")
- master_ninja.variable("ml_x64", "ml64.exe")
- master_ninja.variable("mt", "mt.exe")
- else:
- master_ninja.variable("ld", CommandWithWrapper("LINK", wrappers, ld))
- master_ninja.variable("ldxx", CommandWithWrapper("LINK", wrappers, ldxx))
- master_ninja.variable("ar", GetEnvironFallback(["AR_target", "AR"], ar))
- if flavor != "mac":
- # Mac does not use readelf/nm for .TOC generation, so avoiding polluting
- # the master ninja with extra unused variables.
- master_ninja.variable("nm", GetEnvironFallback(["NM_target", "NM"], nm))
- master_ninja.variable(
- "readelf", GetEnvironFallback(["READELF_target", "READELF"], readelf)
- )
- if generator_supports_multiple_toolsets:
- if not cc_host:
- cc_host = cc
- if not cxx_host:
- cxx_host = cxx
- master_ninja.variable("ar_host", GetEnvironFallback(["AR_host"], ar_host))
- master_ninja.variable("nm_host", GetEnvironFallback(["NM_host"], nm_host))
- master_ninja.variable(
- "readelf_host", GetEnvironFallback(["READELF_host"], readelf_host)
- )
- cc_host = GetEnvironFallback(["CC_host"], cc_host)
- cxx_host = GetEnvironFallback(["CXX_host"], cxx_host)
- # The environment variable could be used in 'make_global_settings', like
- # ['CC.host', '$(CC)'] or ['CXX.host', '$(CXX)'], transform them here.
- if "$(CC)" in cc_host and cc_host_global_setting:
- cc_host = cc_host_global_setting.replace("$(CC)", cc)
- if "$(CXX)" in cxx_host and cxx_host_global_setting:
- cxx_host = cxx_host_global_setting.replace("$(CXX)", cxx)
- master_ninja.variable(
- "cc_host", CommandWithWrapper("CC.host", wrappers, cc_host)
- )
- master_ninja.variable(
- "cxx_host", CommandWithWrapper("CXX.host", wrappers, cxx_host)
- )
- if flavor == "win":
- master_ninja.variable("ld_host", ld_host)
- else:
- master_ninja.variable(
- "ld_host", CommandWithWrapper("LINK", wrappers, ld_host)
- )
- master_ninja.variable(
- "ldxx_host", CommandWithWrapper("LINK", wrappers, ldxx_host)
- )
- master_ninja.newline()
- master_ninja.pool("link_pool", depth=GetDefaultConcurrentLinks())
- master_ninja.newline()
- deps = "msvc" if flavor == "win" else "gcc"
- if flavor != "win":
- master_ninja.rule(
- "cc",
- description="CC $out",
- command=(
- "$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c "
- "$cflags_pch_c -c $in -o $out"
- ),
- depfile="$out.d",
- deps=deps,
- )
- master_ninja.rule(
- "cc_s",
- description="CC $out",
- command=(
- "$cc $defines $includes $cflags $cflags_c "
- "$cflags_pch_c -c $in -o $out"
- ),
- )
- master_ninja.rule(
- "cxx",
- description="CXX $out",
- command=(
- "$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc "
- "$cflags_pch_cc -c $in -o $out"
- ),
- depfile="$out.d",
- deps=deps,
- )
- else:
- # TODO(scottmg) Separate pdb names is a test to see if it works around
- # http://crbug.com/142362. It seems there's a race between the creation of
- # the .pdb by the precompiled header step for .cc and the compilation of
- # .c files. This should be handled by mspdbsrv, but rarely errors out with
- # c1xx : fatal error C1033: cannot open program database
- # By making the rules target separate pdb files this might be avoided.
- cc_command = (
- "ninja -t msvc -e $arch " + "-- "
- "$cc /nologo /showIncludes /FC "
- "@$out.rsp /c $in /Fo$out /Fd$pdbname_c "
- )
- cxx_command = (
- "ninja -t msvc -e $arch " + "-- "
- "$cxx /nologo /showIncludes /FC "
- "@$out.rsp /c $in /Fo$out /Fd$pdbname_cc "
- )
- master_ninja.rule(
- "cc",
- description="CC $out",
- command=cc_command,
- rspfile="$out.rsp",
- rspfile_content="$defines $includes $cflags $cflags_c",
- deps=deps,
- )
- master_ninja.rule(
- "cxx",
- description="CXX $out",
- command=cxx_command,
- rspfile="$out.rsp",
- rspfile_content="$defines $includes $cflags $cflags_cc",
- deps=deps,
- )
- master_ninja.rule(
- "idl",
- description="IDL $in",
- command=(
- "%s gyp-win-tool midl-wrapper $arch $outdir "
- "$tlb $h $dlldata $iid $proxy $in "
- "$midl_includes $idlflags" % sys.executable
- ),
- )
- master_ninja.rule(
- "rc",
- description="RC $in",
- # Note: $in must be last otherwise rc.exe complains.
- command=(
- "%s gyp-win-tool rc-wrapper "
- "$arch $rc $defines $resource_includes $rcflags /fo$out $in"
- % sys.executable
- ),
- )
- master_ninja.rule(
- "asm",
- description="ASM $out",
- command=(
- "%s gyp-win-tool asm-wrapper "
- "$arch $asm $defines $includes $asmflags /c /Fo $out $in"
- % sys.executable
- ),
- )
- if flavor not in ("ios", "mac", "win"):
- master_ninja.rule(
- "alink",
- description="AR $out",
- command="rm -f $out && $ar rcs $arflags $out $in",
- )
- master_ninja.rule(
- "alink_thin",
- description="AR $out",
- command="rm -f $out && $ar rcsT $arflags $out $in",
- )
- # This allows targets that only need to depend on $lib's API to declare an
- # order-only dependency on $lib.TOC and avoid relinking such downstream
- # dependencies when $lib changes only in non-public ways.
- # The resulting string leaves an uninterpolated %{suffix} which
- # is used in the final substitution below.
- mtime_preserving_solink_base = (
- "if [ ! -e $lib -o ! -e $lib.TOC ]; then "
- "%(solink)s && %(extract_toc)s > $lib.TOC; else "
- "%(solink)s && %(extract_toc)s > $lib.tmp && "
- "if ! cmp -s $lib.tmp $lib.TOC; then mv $lib.tmp $lib.TOC ; "
- "fi; fi"
- % {
- "solink": "$ld -shared $ldflags -o $lib -Wl,-soname=$soname %(suffix)s",
- "extract_toc": (
- "{ $readelf -d $lib | grep SONAME ; "
- "$nm -gD -f p $lib | cut -f1-2 -d' '; }"
- ),
- }
- )
- master_ninja.rule(
- "solink",
- description="SOLINK $lib",
- restat=True,
- command=mtime_preserving_solink_base
- % {"suffix": "@$link_file_list"},
- rspfile="$link_file_list",
- rspfile_content=(
- "-Wl,--whole-archive $in $solibs -Wl," "--no-whole-archive $libs"
- ),
- pool="link_pool",
- )
- master_ninja.rule(
- "solink_module",
- description="SOLINK(module) $lib",
- restat=True,
- command=mtime_preserving_solink_base % {"suffix": "@$link_file_list"},
- rspfile="$link_file_list",
- rspfile_content="-Wl,--start-group $in $solibs $libs -Wl,--end-group",
- pool="link_pool",
- )
- master_ninja.rule(
- "link",
- description="LINK $out",
- command=(
- "$ld $ldflags -o $out "
- "-Wl,--start-group $in $solibs $libs -Wl,--end-group"
- ),
- pool="link_pool",
- )
- elif flavor == "win":
- master_ninja.rule(
- "alink",
- description="LIB $out",
- command=(
- "%s gyp-win-tool link-wrapper $arch False "
- "$ar /nologo /ignore:4221 /OUT:$out @$out.rsp" % sys.executable
- ),
- rspfile="$out.rsp",
- rspfile_content="$in_newline $libflags",
- )
- _AddWinLinkRules(master_ninja, embed_manifest=True)
- _AddWinLinkRules(master_ninja, embed_manifest=False)
- else:
- master_ninja.rule(
- "objc",
- description="OBJC $out",
- command=(
- "$cc -MMD -MF $out.d $defines $includes $cflags $cflags_objc "
- "$cflags_pch_objc -c $in -o $out"
- ),
- depfile="$out.d",
- deps=deps,
- )
- master_ninja.rule(
- "objcxx",
- description="OBJCXX $out",
- command=(
- "$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_objcc "
- "$cflags_pch_objcc -c $in -o $out"
- ),
- depfile="$out.d",
- deps=deps,
- )
- master_ninja.rule(
- "alink",
- description="LIBTOOL-STATIC $out, POSTBUILDS",
- command="rm -f $out && "
- "%s gyp-mac-tool filter-libtool libtool $libtool_flags "
- "-static -o $out $in"
- "$postbuilds" % sys.executable,
- )
- master_ninja.rule(
- "lipo",
- description="LIPO $out, POSTBUILDS",
- command="rm -f $out && lipo -create $in -output $out$postbuilds",
- )
- master_ninja.rule(
- "solipo",
- description="SOLIPO $out, POSTBUILDS",
- command=(
- "rm -f $lib $lib.TOC && lipo -create $in -output $lib$postbuilds &&"
- "%(extract_toc)s > $lib.TOC"
- % {
- "extract_toc": "{ otool -l $lib | grep LC_ID_DYLIB -A 5; "
- "nm -gP $lib | cut -f1-2 -d' ' | grep -v U$$; true; }"
- }
- ),
- )
- # Record the public interface of $lib in $lib.TOC. See the corresponding
- # comment in the posix section above for details.
- solink_base = "$ld %(type)s $ldflags -o $lib %(suffix)s"
- mtime_preserving_solink_base = (
- "if [ ! -e $lib -o ! -e $lib.TOC ] || "
- # Always force dependent targets to relink if this library
- # reexports something. Handling this correctly would require
- # recursive TOC dumping but this is rare in practice, so punt.
- "otool -l $lib | grep -q LC_REEXPORT_DYLIB ; then "
- "%(solink)s && %(extract_toc)s > $lib.TOC; "
- "else "
- "%(solink)s && %(extract_toc)s > $lib.tmp && "
- "if ! cmp -s $lib.tmp $lib.TOC; then "
- "mv $lib.tmp $lib.TOC ; "
- "fi; "
- "fi"
- % {
- "solink": solink_base,
- "extract_toc": "{ otool -l $lib | grep LC_ID_DYLIB -A 5; "
- "nm -gP $lib | cut -f1-2 -d' ' | grep -v U$$; true; }",
- }
- )
- solink_suffix = "@$link_file_list$postbuilds"
- master_ninja.rule(
- "solink",
- description="SOLINK $lib, POSTBUILDS",
- restat=True,
- command=mtime_preserving_solink_base
- % {"suffix": solink_suffix, "type": "-shared"},
- rspfile="$link_file_list",
- rspfile_content="$in $solibs $libs",
- pool="link_pool",
- )
- master_ninja.rule(
- "solink_notoc",
- description="SOLINK $lib, POSTBUILDS",
- restat=True,
- command=solink_base % {"suffix": solink_suffix, "type": "-shared"},
- rspfile="$link_file_list",
- rspfile_content="$in $solibs $libs",
- pool="link_pool",
- )
- master_ninja.rule(
- "solink_module",
- description="SOLINK(module) $lib, POSTBUILDS",
- restat=True,
- command=mtime_preserving_solink_base
- % {"suffix": solink_suffix, "type": "-bundle"},
- rspfile="$link_file_list",
- rspfile_content="$in $solibs $libs",
- pool="link_pool",
- )
- master_ninja.rule(
- "solink_module_notoc",
- description="SOLINK(module) $lib, POSTBUILDS",
- restat=True,
- command=solink_base % {"suffix": solink_suffix, "type": "-bundle"},
- rspfile="$link_file_list",
- rspfile_content="$in $solibs $libs",
- pool="link_pool",
- )
- master_ninja.rule(
- "link",
- description="LINK $out, POSTBUILDS",
- command=("$ld $ldflags -o $out " "$in $solibs $libs$postbuilds"),
- pool="link_pool",
- )
- master_ninja.rule(
- "preprocess_infoplist",
- description="PREPROCESS INFOPLIST $out",
- command=(
- "$cc -E -P -Wno-trigraphs -x c $defines $in -o $out && "
- "plutil -convert xml1 $out $out"
- ),
- )
- master_ninja.rule(
- "copy_infoplist",
- description="COPY INFOPLIST $in",
- command="$env %s gyp-mac-tool copy-info-plist $in $out $binary $keys"
- % sys.executable,
- )
- master_ninja.rule(
- "merge_infoplist",
- description="MERGE INFOPLISTS $in",
- command="$env %s gyp-mac-tool merge-info-plist $out $in" % sys.executable,
- )
- master_ninja.rule(
- "compile_xcassets",
- description="COMPILE XCASSETS $in",
- command="$env %s gyp-mac-tool compile-xcassets $keys $in" % sys.executable,
- )
- master_ninja.rule(
- "compile_ios_framework_headers",
- description="COMPILE HEADER MAPS AND COPY FRAMEWORK HEADERS $in",
- command="$env %(python)s gyp-mac-tool compile-ios-framework-header-map "
- "$out $framework $in && $env %(python)s gyp-mac-tool "
- "copy-ios-framework-headers $framework $copy_headers"
- % {'python': sys.executable},
- )
- master_ninja.rule(
- "mac_tool",
- description="MACTOOL $mactool_cmd $in",
- command="$env %s gyp-mac-tool $mactool_cmd $in $out $binary"
- % sys.executable,
- )
- master_ninja.rule(
- "package_framework",
- description="PACKAGE FRAMEWORK $out, POSTBUILDS",
- command="%s gyp-mac-tool package-framework $out $version$postbuilds "
- "&& touch $out" % sys.executable,
- )
- master_ninja.rule(
- "package_ios_framework",
- description="PACKAGE IOS FRAMEWORK $out, POSTBUILDS",
- command="%s gyp-mac-tool package-ios-framework $out $postbuilds "
- "&& touch $out" % sys.executable,
- )
- if flavor == "win":
- master_ninja.rule(
- "stamp",
- description="STAMP $out",
- command="%s gyp-win-tool stamp $out" % sys.executable,
- )
- else:
- master_ninja.rule(
- "stamp", description="STAMP $out", command="${postbuilds}touch $out"
- )
- if flavor == "win":
- master_ninja.rule(
- "copy",
- description="COPY $in $out",
- command="%s gyp-win-tool recursive-mirror $in $out" % sys.executable,
- )
- elif flavor == "zos":
- master_ninja.rule(
- "copy",
- description="COPY $in $out",
- command="rm -rf $out && cp -fRP $in $out",
- )
- else:
- master_ninja.rule(
- "copy",
- description="COPY $in $out",
- command="ln -f $in $out 2>/dev/null || (rm -rf $out && cp -af $in $out)",
- )
- master_ninja.newline()
- all_targets = set()
- for build_file in params["build_files"]:
- for target in gyp.common.AllTargets(
- target_list, target_dicts, os.path.normpath(build_file)
- ):
- all_targets.add(target)
- all_outputs = set()
- # target_outputs is a map from qualified target name to a Target object.
- target_outputs = {}
- # target_short_names is a map from target short name to a list of Target
- # objects.
- target_short_names = {}
- # short name of targets that were skipped because they didn't contain anything
- # interesting.
- # NOTE: there may be overlap between this an non_empty_target_names.
- empty_target_names = set()
- # Set of non-empty short target names.
- # NOTE: there may be overlap between this an empty_target_names.
- non_empty_target_names = set()
- for qualified_target in target_list:
- # qualified_target is like: third_party/icu/icu.gyp:icui18n#target
- build_file, name, toolset = gyp.common.ParseQualifiedTarget(qualified_target)
- this_make_global_settings = data[build_file].get("make_global_settings", [])
- assert make_global_settings == this_make_global_settings, (
- "make_global_settings needs to be the same for all targets. "
- f"{this_make_global_settings} vs. {make_global_settings}"
- )
- spec = target_dicts[qualified_target]
- if flavor == "mac":
- gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec)
- # If build_file is a symlink, we must not follow it because there's a chance
- # it could point to a path above toplevel_dir, and we cannot correctly deal
- # with that case at the moment.
- build_file = gyp.common.RelativePath(build_file, options.toplevel_dir, False)
- qualified_target_for_hash = gyp.common.QualifiedTarget(
- build_file, name, toolset
- )
- qualified_target_for_hash = qualified_target_for_hash.encode("utf-8")
- hash_for_rules = hashlib.md5(qualified_target_for_hash).hexdigest()
- base_path = os.path.dirname(build_file)
- obj = "obj"
- if toolset != "target":
- obj += "." + toolset
- output_file = os.path.join(obj, base_path, name + ".ninja")
- ninja_output = StringIO()
- writer = NinjaWriter(
- hash_for_rules,
- target_outputs,
- base_path,
- build_dir,
- ninja_output,
- toplevel_build,
- output_file,
- flavor,
- toplevel_dir=options.toplevel_dir,
- )
- target = writer.WriteSpec(spec, config_name, generator_flags)
- if ninja_output.tell() > 0:
- # Only create files for ninja files that actually have contents.
- with OpenOutput(os.path.join(toplevel_build, output_file)) as ninja_file:
- ninja_file.write(ninja_output.getvalue())
- ninja_output.close()
- master_ninja.subninja(output_file)
- if target:
- if name != target.FinalOutput() and spec["toolset"] == "target":
- target_short_names.setdefault(name, []).append(target)
- target_outputs[qualified_target] = target
- if qualified_target in all_targets:
- all_outputs.add(target.FinalOutput())
- non_empty_target_names.add(name)
- else:
- empty_target_names.add(name)
- if target_short_names:
- # Write a short name to build this target. This benefits both the
- # "build chrome" case as well as the gyp tests, which expect to be
- # able to run actions and build libraries by their short name.
- master_ninja.newline()
- master_ninja.comment("Short names for targets.")
- for short_name in sorted(target_short_names):
- master_ninja.build(
- short_name,
- "phony",
- [x.FinalOutput() for x in target_short_names[short_name]],
- )
- # Write phony targets for any empty targets that weren't written yet. As
- # short names are not necessarily unique only do this for short names that
- # haven't already been output for another target.
- empty_target_names = empty_target_names - non_empty_target_names
- if empty_target_names:
- master_ninja.newline()
- master_ninja.comment("Empty targets (output for completeness).")
- for name in sorted(empty_target_names):
- master_ninja.build(name, "phony")
- if all_outputs:
- master_ninja.newline()
- master_ninja.build("all", "phony", sorted(all_outputs))
- master_ninja.default(generator_flags.get("default_target", "all"))
- master_ninja_file.close()
- if generate_compile_commands:
- compile_db = GenerateCompileDBWithNinja(toplevel_build)
- compile_db_file = OpenOutput(
- os.path.join(toplevel_build, "compile_commands.json")
- )
- compile_db_file.write(json.dumps(compile_db, indent=2))
- compile_db_file.close()
- def GenerateCompileDBWithNinja(path, targets=["all"]):
- """Generates a compile database using ninja.
- Args:
- path: The build directory to generate a compile database for.
- targets: Additional targets to pass to ninja.
- Returns:
- List of the contents of the compile database.
- """
- ninja_path = shutil.which("ninja")
- if ninja_path is None:
- raise Exception("ninja not found in PATH")
- json_compile_db = subprocess.check_output(
- [ninja_path, "-C", path]
- + targets
- + ["-t", "compdb", "cc", "cxx", "objc", "objcxx"]
- )
- return json.loads(json_compile_db)
- def PerformBuild(data, configurations, params):
- options = params["options"]
- for config in configurations:
- builddir = os.path.join(options.toplevel_dir, "out", config)
- arguments = ["ninja", "-C", builddir]
- print(f"Building [{config}]: {arguments}")
- subprocess.check_call(arguments)
- def CallGenerateOutputForConfig(arglist):
- # Ignore the interrupt signal so that the parent process catches it and
- # kills all multiprocessing children.
- signal.signal(signal.SIGINT, signal.SIG_IGN)
- (target_list, target_dicts, data, params, config_name) = arglist
- GenerateOutputForConfig(target_list, target_dicts, data, params, config_name)
- def GenerateOutput(target_list, target_dicts, data, params):
- # Update target_dicts for iOS device builds.
- target_dicts = gyp.xcode_emulation.CloneConfigurationForDeviceAndEmulator(
- target_dicts
- )
- user_config = params.get("generator_flags", {}).get("config", None)
- if gyp.common.GetFlavor(params) == "win":
- target_list, target_dicts = MSVSUtil.ShardTargets(target_list, target_dicts)
- target_list, target_dicts = MSVSUtil.InsertLargePdbShims(
- target_list, target_dicts, generator_default_variables
- )
- if user_config:
- GenerateOutputForConfig(target_list, target_dicts, data, params, user_config)
- else:
- config_names = target_dicts[target_list[0]]["configurations"]
- if params["parallel"]:
- try:
- pool = multiprocessing.Pool(len(config_names))
- arglists = []
- for config_name in config_names:
- arglists.append(
- (target_list, target_dicts, data, params, config_name)
- )
- pool.map(CallGenerateOutputForConfig, arglists)
- except KeyboardInterrupt as e:
- pool.terminate()
- raise e
- else:
- for config_name in config_names:
- GenerateOutputForConfig(
- target_list, target_dicts, data, params, config_name
- )
|