parameters: (env)
docstring: Generate the NINJA builders.
docstring_summary: Generate the NINJA builders.
docstring_tokens: Generate the NINJA builders .
function: def generate(env):
      """Generate the NINJA builders."""
      env[NINJA_SYNTAX] = env.get(NINJA_SYNTAX, "ninja_syntax.py")
  
      # Add the Ninja builder.
      always_exec_ninja_action = AlwaysExecAction(ninja_builder, {})
      ninja_builder_obj = SCons.Builder.Builder(action=always_exec_ninja_action)
      env.Append(BUILDERS={"Ninja": ninja_builder_obj})
  
      env["NINJA_PREFIX"] = env.get("NINJA_PREFIX", "build")
      env["NINJA_SUFFIX"] = env.get("NINJA_SUFFIX", "ninja")
      env["NINJA_ALIAS_NAME"] = env.get("NINJA_ALIAS_NAME", "generate-ninja")
      env['NINJA_BUILDDIR'] = env.get("NINJA_BUILDDIR", env.Dir(".ninja").path)
      ninja_file_name = env.subst("${NINJA_PREFIX}.${NINJA_SUFFIX}")
      ninja_file = env.Ninja(target=ninja_file_name, source=[])
      env.AlwaysBuild(ninja_file)
      env.Alias("$NINJA_ALIAS_NAME", ninja_file)
  
      # TODO: API for getting the SConscripts programmatically
      # exists upstream: https://github.com/SCons/scons/issues/3625
      def ninja_generate_deps(env):
          return sorted([env.File("#SConstruct").path] + glob("**/SConscript", recursive=True))
      env['_NINJA_REGENERATE_DEPS_FUNC'] = ninja_generate_deps
  
      env['NINJA_REGENERATE_DEPS'] = env.get('NINJA_REGENERATE_DEPS', '${_NINJA_REGENERATE_DEPS_FUNC(__env__)}')
  
      # This adds the required flags such that the generated compile
      # commands will create depfiles as appropriate in the Ninja file.
      if env["PLATFORM"] == "win32":
          env.Append(CCFLAGS=["/showIncludes"])
      else:
          env.Append(CCFLAGS=["-MMD", "-MF", "${TARGET}.d"])
  
      env.AddMethod(CheckNinjaCompdbExpand, "CheckNinjaCompdbExpand")
  
      # Provide a way for custom rule authors to easily access command
      # generation.
      env.AddMethod(get_generic_shell_command, "NinjaGetGenericShellCommand")
      env.AddMethod(get_command, "NinjaGetCommand")
      env.AddMethod(gen_get_response_file_command, "NinjaGenResponseFileProvider")
      env.AddMethod(set_build_node_callback, "NinjaSetBuildNodeCallback")
  
      # Provides a way for users to handle custom FunctionActions they
      # want to translate to Ninja.
      env[NINJA_CUSTOM_HANDLERS] = {}
      env.AddMethod(register_custom_handler, "NinjaRegisterFunctionHandler")
  
      # Provides a mechanism for inject custom Ninja rules which can
      # then be mapped using NinjaRuleMapping.
      env[NINJA_RULES] = {}
      env.AddMethod(register_custom_rule, "NinjaRule")
  
      # Provides a mechanism for inject custom Ninja pools which can
      # be used by providing the NINJA_POOL="name" as an
      # OverrideEnvironment variable in a builder call.
      env[NINJA_POOLS] = {}
      env.AddMethod(register_custom_pool, "NinjaPool")
  
      # Add the ability to register custom NinjaRuleMappings for Command
      # builders. We don't store this dictionary in the env to prevent
      # accidental deletion of the CC/XXCOM mappings. You can still
      # overwrite them if you really want to but you have to explicit
      # about it this way. The reason is that if they were accidentally
      # deleted you would get a very subtly incorrect Ninja file and
      # might not catch it.
      env.AddMethod(register_custom_rule_mapping, "NinjaRuleMapping")
  
      # TODO: change LINKCOM and SHLINKCOM to handle embedding manifest exe checks
      # without relying on the SCons hacks that SCons uses by default.
      if env["PLATFORM"] == "win32":
          from SCons.Tool.mslink import compositeLinkAction
  
          if env["LINKCOM"] == compositeLinkAction:
              env[
                  "LINKCOM"
              ] = '${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET.windows $_LIBDIRFLAGS $_LIBFLAGS $_PDB $SOURCES.windows", "$LINKCOMSTR")}'
              env[
                  "SHLINKCOM"
              ] = '${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_SHLINK_SOURCES", "$SHLINKCOMSTR")}'
  
      # Normally in SCons actions for the Program and *Library builders
      # will return "${*COM}" as their pre-subst'd command line. However
      # if a user in a SConscript overwrites those values via key access
      # like env["LINKCOM"] = "$( $ICERUN $)" + env["LINKCOM"] then
      # those actions no longer return the "bracketted" string and
      # instead return something that looks more expanded. So to
      # continue working even if a user has done this we map both the
      # "bracketted" and semi-expanded versions.
      def robust_rule_mapping(var, rule, tool):
          provider = gen_get_response_file_command(env, rule, tool)
          env.NinjaRuleMapping("${" + var + "}", provider)
          env.NinjaRuleMapping(env[var], provider)
  
      robust_rule_mapping("CCCOM", "CC", env["CC"])
      robust_rule_mapping("SHCCCOM", "CC", env["CC"])
      robust_rule_mapping("CXXCOM", "CXX", env["CXX"])
      robust_rule_mapping("SHCXXCOM", "CXX", env["CXX"])
      robust_rule_mapping("LINKCOM", "LINK", "$LINK")
      robust_rule_mapping("SHLINKCOM", "LINK", "$SHLINK")
      robust_rule_mapping("ARCOM", "AR", env["AR"])
  
      # Make SCons node walk faster by preventing unnecessary work
      env.Decider("timestamp-match")
  
      # Used to determine if a build generates a source file. Ninja
      # requires that all generated sources are added as order_only
      # dependencies to any builds that *might* use them.
      env["NINJA_GENERATED_SOURCE_SUFFIXES"] = [".h", ".hpp"]
  
      if env["PLATFORM"] != "win32" and env.get("RANLIBCOM"):
          # There is no way to translate the ranlib list action into
          # Ninja so add the s flag and disable ranlib.
          #
          # This is equivalent to Meson.
          # https://github.com/mesonbuild/meson/blob/master/mesonbuild/linkers.py#L143
          old_arflags = str(env["ARFLAGS"])
          if "s" not in old_arflags:
              old_arflags += "s"
  
          env["ARFLAGS"] = SCons.Util.CLVar([old_arflags])
  
          # Disable running ranlib, since we added 's' above
          env["RANLIBCOM"] = ""
  
      # This is the point of no return, anything after this comment
      # makes changes to SCons that are irreversible and incompatible
      # with a normal SCons build. We return early if __NINJA_NO=1 has
      # been given on the command line (i.e. by us in the generated
      # ninja file) here to prevent these modifications from happening
      # when we want SCons to do work. Everything before this was
      # necessary to setup the builder and other functions so that the
      # tool can be unconditionally used in the users's SCons files.
  
      if not exists(env):
          return
  
      # Set a known variable that other tools can query so they can
      # behave correctly during ninja generation.
      env["GENERATING_NINJA"] = True
  
      # These methods are no-op'd because they do not work during ninja
      # generation, expected to do no work, or simply fail. All of which
      # are slow in SCons. So we overwrite them with no logic.
      SCons.Node.FS.File.make_ready = ninja_noop
      SCons.Node.FS.File.prepare = ninja_noop
      SCons.Node.FS.File.push_to_cache = ninja_noop
      SCons.Executor.Executor.prepare = ninja_noop
      SCons.Taskmaster.Task.prepare = ninja_noop
      SCons.Node.FS.File.built = ninja_noop
      SCons.Node.Node.visited = ninja_noop
  
      # We make lstat a no-op because it is only used for SONAME
      # symlinks which we're not producing.
      SCons.Node.FS.LocalFS.lstat = ninja_noop
  
      # This is a slow method that isn't memoized. We make it a noop
      # since during our generation we will never use the results of
      # this or change the results.
      SCons.Node.FS.is_up_to_date = ninja_noop
  
      # We overwrite stat and WhereIs with eternally memoized
      # implementations. See the docstring of ninja_stat and
      # ninja_whereis for detailed explanations.
      SCons.Node.FS.LocalFS.stat = ninja_stat
      SCons.Util.WhereIs = ninja_whereis
  
      # Monkey patch get_csig and get_contents for some classes. It
      # slows down the build significantly and we don't need contents or
      # content signatures calculated when generating a ninja file since
      # we're not doing any SCons caching or building.
      SCons.Executor.Executor.get_contents = ninja_contents(
          SCons.Executor.Executor.get_contents
      )
      SCons.Node.Alias.Alias.get_contents = ninja_contents(
          SCons.Node.Alias.Alias.get_contents
      )
      SCons.Node.FS.File.get_contents = ninja_contents(SCons.Node.FS.File.get_contents)
      SCons.Node.FS.File.get_csig = ninja_csig(SCons.Node.FS.File.get_csig)
      SCons.Node.FS.Dir.get_csig = ninja_csig(SCons.Node.FS.Dir.get_csig)
      SCons.Node.Alias.Alias.get_csig = ninja_csig(SCons.Node.Alias.Alias.get_csig)
  
      # Ignore CHANGED_SOURCES and CHANGED_TARGETS. We don't want those
      # to have effect in a generation pass because the generator
      # shouldn't generate differently depending on the current local
      # state. Without this, when generating on Windows, if you already
      # had a foo.obj, you would omit foo.cpp from the response file. Do the same for UNCHANGED.
      SCons.Executor.Executor._get_changed_sources = SCons.Executor.Executor._get_sources
      SCons.Executor.Executor._get_changed_targets = SCons.Executor.Executor._get_targets
      SCons.Executor.Executor._get_unchanged_sources = SCons.Executor.Executor._get_sources
      SCons.Executor.Executor._get_unchanged_targets = SCons.Executor.Executor._get_targets
  
      # Replace false action messages with nothing.
      env["PRINT_CMD_LINE_FUNC"] = ninja_print_conf_log
  
      # This reduces unnecessary subst_list calls to add the compiler to
      # the implicit dependencies of targets. Since we encode full paths
      # in our generated commands we do not need these slow subst calls
      # as executing the command will fail if the file is not found
      # where we expect it.
      env["IMPLICIT_COMMAND_DEPENDENCIES"] = False
  
      # This makes SCons more aggressively cache MD5 signatures in the
      # SConsign file.
      env.SetOption("max_drift", 1)
  
      # The Serial job class is SIGNIFICANTLY (almost twice as) faster
      # than the Parallel job class for generating Ninja files. So we
      # monkey the Jobs constructor to only use the Serial Job class.
      SCons.Job.Jobs.__init__ = ninja_always_serial
  
      # The environment variable NINJA_SYNTAX points to the
      # ninja_syntax.py module from the ninja sources found here:
      # https://github.com/ninja-build/ninja/blob/master/misc/ninja_syntax.py
      #
      # This should be vendored into the build sources and it's location
      # set in NINJA_SYNTAX. This code block loads the location from
      # that variable, gets the absolute path to the vendored file, gets
      # it's parent directory then uses importlib to import the module
      # dynamically.
      ninja_syntax_file = env[NINJA_SYNTAX]
      if isinstance(ninja_syntax_file, str):
          ninja_syntax_file = env.File(ninja_syntax_file).get_abspath()
      ninja_syntax_mod_dir = os.path.dirname(ninja_syntax_file)
      sys.path.append(ninja_syntax_mod_dir)
      ninja_syntax_mod_name = os.path.basename(ninja_syntax_file)
      ninja_syntax = importlib.import_module(ninja_syntax_mod_name.replace(".py", ""))
  
      global NINJA_STATE
      NINJA_STATE = NinjaState(env, ninja_syntax)
  
      # Here we will force every builder to use an emitter which makes the ninja
      # file depend on it's target. This forces the ninja file to the bottom of
      # the DAG which is required so that we walk every target, and therefore add
      # it to the global NINJA_STATE, before we try to write the ninja file.
      def ninja_file_depends_on_all(target, source, env):
          if not any("conftest" in str(t) for t in target):
              env.Depends(ninja_file, target)
          return target, source
  
      # The "Alias Builder" isn't in the BUILDERS map so we have to
      # modify it directly.
      SCons.Environment.AliasBuilder.emitter = ninja_file_depends_on_all
  
      for _, builder in env["BUILDERS"].items():
          try:
              emitter = builder.emitter
              if emitter is not None:
                  builder.emitter = SCons.Builder.ListEmitter(
                      [emitter, ninja_file_depends_on_all]
                  )
              else:
                  builder.emitter = ninja_file_depends_on_all
          # Users can inject whatever they want into the BUILDERS
          # dictionary so if the thing doesn't have an emitter we'll
          # just ignore it.
          except AttributeError:
              pass
  
      # Here we monkey patch the Task.execute method to not do a bunch of
      # unnecessary work. If a build is a regular builder (i.e not a conftest and
      # not our own Ninja builder) then we add it to the NINJA_STATE. Otherwise we
      # build it like normal. This skips all of the caching work that this method
      # would normally do since we aren't pulling any of these targets from the
      # cache.
      #
      # In the future we may be able to use this to actually cache the build.ninja
      # file once we have the upstream support for referencing SConscripts as File
      # nodes.
      def ninja_execute(self):
          global NINJA_STATE
  
          target = self.targets[0]
          target_name = str(target)
          if target_name != ninja_file_name and "conftest" not in target_name:
              NINJA_STATE.add_build(target)
          else:
              target.build()
  
      SCons.Taskmaster.Task.execute = ninja_execute
  
      # Make needs_execute always return true instead of determining out of
      # date-ness.
      SCons.Script.Main.BuildTask.needs_execute = lambda x: True
  
      # We will eventually need to overwrite TempFileMunge to make it
      # handle persistent tempfiles or get an upstreamed change to add
      # some configurability to it's behavior in regards to tempfiles.
      #
      # Set all three environment variables that Python's
      # tempfile.mkstemp looks at as it behaves differently on different
      # platforms and versions of Python.
      os.environ["TMPDIR"] = env.Dir("$BUILD_DIR/response_files").get_abspath()
      os.environ["TEMP"] = os.environ["TMPDIR"]
      os.environ["TMP"] = os.environ["TMPDIR"]
      if not os.path.isdir(os.environ["TMPDIR"]):
          env.Execute(SCons.Defaults.Mkdir(os.environ["TMPDIR"]))
  
      env["TEMPFILE"] = NinjaNoResponseFiles
function_tokens: def generate ( env ) : env [ NINJA_SYNTAX ] = env . get ( NINJA_SYNTAX , "ninja_syntax.py" ) # Add the Ninja builder. always_exec_ninja_action = AlwaysExecAction ( ninja_builder , { } ) ninja_builder_obj = SCons . Builder . Builder ( action = always_exec_ninja_action ) env . Append ( BUILDERS = { "Ninja" : ninja_builder_obj } ) env [ "NINJA_PREFIX" ] = env . get ( "NINJA_PREFIX" , "build" ) env [ "NINJA_SUFFIX" ] = env . get ( "NINJA_SUFFIX" , "ninja" ) env [ "NINJA_ALIAS_NAME" ] = env . get ( "NINJA_ALIAS_NAME" , "generate-ninja" ) env [ 'NINJA_BUILDDIR' ] = env . get ( "NINJA_BUILDDIR" , env . Dir ( ".ninja" ) . path ) ninja_file_name = env . subst ( "${NINJA_PREFIX}.${NINJA_SUFFIX}" ) ninja_file = env . Ninja ( target = ninja_file_name , source = [ ] ) env . AlwaysBuild ( ninja_file ) env . Alias ( "$NINJA_ALIAS_NAME" , ninja_file ) # TODO: API for getting the SConscripts programmatically # exists upstream: https://github.com/SCons/scons/issues/3625 def ninja_generate_deps ( env ) : return sorted ( [ env . File ( "#SConstruct" ) . path ] + glob ( "**/SConscript" , recursive = True ) ) env [ '_NINJA_REGENERATE_DEPS_FUNC' ] = ninja_generate_deps env [ 'NINJA_REGENERATE_DEPS' ] = env . get ( 'NINJA_REGENERATE_DEPS' , '${_NINJA_REGENERATE_DEPS_FUNC(__env__)}' ) # This adds the required flags such that the generated compile # commands will create depfiles as appropriate in the Ninja file. if env [ "PLATFORM" ] == "win32" : env . Append ( CCFLAGS = [ "/showIncludes" ] ) else : env . Append ( CCFLAGS = [ "-MMD" , "-MF" , "${TARGET}.d" ] ) env . AddMethod ( CheckNinjaCompdbExpand , "CheckNinjaCompdbExpand" ) # Provide a way for custom rule authors to easily access command # generation. env . AddMethod ( get_generic_shell_command , "NinjaGetGenericShellCommand" ) env . AddMethod ( get_command , "NinjaGetCommand" ) env . AddMethod ( gen_get_response_file_command , "NinjaGenResponseFileProvider" ) env . AddMethod ( set_build_node_callback , "NinjaSetBuildNodeCallback" ) # Provides a way for users to handle custom FunctionActions they # want to translate to Ninja. env [ NINJA_CUSTOM_HANDLERS ] = { } env . AddMethod ( register_custom_handler , "NinjaRegisterFunctionHandler" ) # Provides a mechanism for inject custom Ninja rules which can # then be mapped using NinjaRuleMapping. env [ NINJA_RULES ] = { } env . AddMethod ( register_custom_rule , "NinjaRule" ) # Provides a mechanism for inject custom Ninja pools which can # be used by providing the NINJA_POOL="name" as an # OverrideEnvironment variable in a builder call. env [ NINJA_POOLS ] = { } env . AddMethod ( register_custom_pool , "NinjaPool" ) # Add the ability to register custom NinjaRuleMappings for Command # builders. We don't store this dictionary in the env to prevent # accidental deletion of the CC/XXCOM mappings. You can still # overwrite them if you really want to but you have to explicit # about it this way. The reason is that if they were accidentally # deleted you would get a very subtly incorrect Ninja file and # might not catch it. env . AddMethod ( register_custom_rule_mapping , "NinjaRuleMapping" ) # TODO: change LINKCOM and SHLINKCOM to handle embedding manifest exe checks # without relying on the SCons hacks that SCons uses by default. if env [ "PLATFORM" ] == "win32" : from SCons . Tool . mslink import compositeLinkAction if env [ "LINKCOM" ] == compositeLinkAction : env [ "LINKCOM" ] = '${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET.windows $_LIBDIRFLAGS $_LIBFLAGS $_PDB $SOURCES.windows", "$LINKCOMSTR")}' env [ "SHLINKCOM" ] = '${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $_LIBDIRFLAGS $_LIBFLAGS $_PDB $_SHLINK_SOURCES", "$SHLINKCOMSTR")}' # Normally in SCons actions for the Program and *Library builders # will return "${*COM}" as their pre-subst'd command line. However # if a user in a SConscript overwrites those values via key access # like env["LINKCOM"] = "$( $ICERUN $)" + env["LINKCOM"] then # those actions no longer return the "bracketted" string and # instead return something that looks more expanded. So to # continue working even if a user has done this we map both the # "bracketted" and semi-expanded versions. def robust_rule_mapping ( var , rule , tool ) : provider = gen_get_response_file_command ( env , rule , tool ) env . NinjaRuleMapping ( "${" + var + "}" , provider ) env . NinjaRuleMapping ( env [ var ] , provider ) robust_rule_mapping ( "CCCOM" , "CC" , env [ "CC" ] ) robust_rule_mapping ( "SHCCCOM" , "CC" , env [ "CC" ] ) robust_rule_mapping ( "CXXCOM" , "CXX" , env [ "CXX" ] ) robust_rule_mapping ( "SHCXXCOM" , "CXX" , env [ "CXX" ] ) robust_rule_mapping ( "LINKCOM" , "LINK" , "$LINK" ) robust_rule_mapping ( "SHLINKCOM" , "LINK" , "$SHLINK" ) robust_rule_mapping ( "ARCOM" , "AR" , env [ "AR" ] ) # Make SCons node walk faster by preventing unnecessary work env . Decider ( "timestamp-match" ) # Used to determine if a build generates a source file. Ninja # requires that all generated sources are added as order_only # dependencies to any builds that *might* use them. env [ "NINJA_GENERATED_SOURCE_SUFFIXES" ] = [ ".h" , ".hpp" ] if env [ "PLATFORM" ] != "win32" and env . get ( "RANLIBCOM" ) : # There is no way to translate the ranlib list action into # Ninja so add the s flag and disable ranlib. # # This is equivalent to Meson. # https://github.com/mesonbuild/meson/blob/master/mesonbuild/linkers.py#L143 old_arflags = str ( env [ "ARFLAGS" ] ) if "s" not in old_arflags : old_arflags += "s" env [ "ARFLAGS" ] = SCons . Util . CLVar ( [ old_arflags ] ) # Disable running ranlib, since we added 's' above env [ "RANLIBCOM" ] = "" # This is the point of no return, anything after this comment # makes changes to SCons that are irreversible and incompatible # with a normal SCons build. We return early if __NINJA_NO=1 has # been given on the command line (i.e. by us in the generated # ninja file) here to prevent these modifications from happening # when we want SCons to do work. Everything before this was # necessary to setup the builder and other functions so that the # tool can be unconditionally used in the users's SCons files. if not exists ( env ) : return # Set a known variable that other tools can query so they can # behave correctly during ninja generation. env [ "GENERATING_NINJA" ] = True # These methods are no-op'd because they do not work during ninja # generation, expected to do no work, or simply fail. All of which # are slow in SCons. So we overwrite them with no logic. SCons . Node . FS . File . make_ready = ninja_noop SCons . Node . FS . File . prepare = ninja_noop SCons . Node . FS . File . push_to_cache = ninja_noop SCons . Executor . Executor . prepare = ninja_noop SCons . Taskmaster . Task . prepare = ninja_noop SCons . Node . FS . File . built = ninja_noop SCons . Node . Node . visited = ninja_noop # We make lstat a no-op because it is only used for SONAME # symlinks which we're not producing. SCons . Node . FS . LocalFS . lstat = ninja_noop # This is a slow method that isn't memoized. We make it a noop # since during our generation we will never use the results of # this or change the results. SCons . Node . FS . is_up_to_date = ninja_noop # We overwrite stat and WhereIs with eternally memoized # implementations. See the docstring of ninja_stat and # ninja_whereis for detailed explanations. SCons . Node . FS . LocalFS . stat = ninja_stat SCons . Util . WhereIs = ninja_whereis # Monkey patch get_csig and get_contents for some classes. It # slows down the build significantly and we don't need contents or # content signatures calculated when generating a ninja file since # we're not doing any SCons caching or building. SCons . Executor . Executor . get_contents = ninja_contents ( SCons . Executor . Executor . get_contents ) SCons . Node . Alias . Alias . get_contents = ninja_contents ( SCons . Node . Alias . Alias . get_contents ) SCons . Node . FS . File . get_contents = ninja_contents ( SCons . Node . FS . File . get_contents ) SCons . Node . FS . File . get_csig = ninja_csig ( SCons . Node . FS . File . get_csig ) SCons . Node . FS . Dir . get_csig = ninja_csig ( SCons . Node . FS . Dir . get_csig ) SCons . Node . Alias . Alias . get_csig = ninja_csig ( SCons . Node . Alias . Alias . get_csig ) # Ignore CHANGED_SOURCES and CHANGED_TARGETS. We don't want those # to have effect in a generation pass because the generator # shouldn't generate differently depending on the current local # state. Without this, when generating on Windows, if you already # had a foo.obj, you would omit foo.cpp from the response file. Do the same for UNCHANGED. SCons . Executor . Executor . _get_changed_sources = SCons . Executor . Executor . _get_sources SCons . Executor . Executor . _get_changed_targets = SCons . Executor . Executor . _get_targets SCons . Executor . Executor . _get_unchanged_sources = SCons . Executor . Executor . _get_sources SCons . Executor . Executor . _get_unchanged_targets = SCons . Executor . Executor . _get_targets # Replace false action messages with nothing. env [ "PRINT_CMD_LINE_FUNC" ] = ninja_print_conf_log # This reduces unnecessary subst_list calls to add the compiler to # the implicit dependencies of targets. Since we encode full paths # in our generated commands we do not need these slow subst calls # as executing the command will fail if the file is not found # where we expect it. env [ "IMPLICIT_COMMAND_DEPENDENCIES" ] = False # This makes SCons more aggressively cache MD5 signatures in the # SConsign file. env . SetOption ( "max_drift" , 1 ) # The Serial job class is SIGNIFICANTLY (almost twice as) faster # than the Parallel job class for generating Ninja files. So we # monkey the Jobs constructor to only use the Serial Job class. SCons . Job . Jobs . __init__ = ninja_always_serial # The environment variable NINJA_SYNTAX points to the # ninja_syntax.py module from the ninja sources found here: # https://github.com/ninja-build/ninja/blob/master/misc/ninja_syntax.py # # This should be vendored into the build sources and it's location # set in NINJA_SYNTAX. This code block loads the location from # that variable, gets the absolute path to the vendored file, gets # it's parent directory then uses importlib to import the module # dynamically. ninja_syntax_file = env [ NINJA_SYNTAX ] if isinstance ( ninja_syntax_file , str ) : ninja_syntax_file = env . File ( ninja_syntax_file ) . get_abspath ( ) ninja_syntax_mod_dir = os . path . dirname ( ninja_syntax_file ) sys . path . append ( ninja_syntax_mod_dir ) ninja_syntax_mod_name = os . path . basename ( ninja_syntax_file ) ninja_syntax = importlib . import_module ( ninja_syntax_mod_name . replace ( ".py" , "" ) ) global NINJA_STATE NINJA_STATE = NinjaState ( env , ninja_syntax ) # Here we will force every builder to use an emitter which makes the ninja # file depend on it's target. This forces the ninja file to the bottom of # the DAG which is required so that we walk every target, and therefore add # it to the global NINJA_STATE, before we try to write the ninja file. def ninja_file_depends_on_all ( target , source , env ) : if not any ( "conftest" in str ( t ) for t in target ) : env . Depends ( ninja_file , target ) return target , source # The "Alias Builder" isn't in the BUILDERS map so we have to # modify it directly. SCons . Environment . AliasBuilder . emitter = ninja_file_depends_on_all for _ , builder in env [ "BUILDERS" ] . items ( ) : try : emitter = builder . emitter if emitter is not None : builder . emitter = SCons . Builder . ListEmitter ( [ emitter , ninja_file_depends_on_all ] ) else : builder . emitter = ninja_file_depends_on_all # Users can inject whatever they want into the BUILDERS # dictionary so if the thing doesn't have an emitter we'll # just ignore it. except AttributeError : pass # Here we monkey patch the Task.execute method to not do a bunch of # unnecessary work. If a build is a regular builder (i.e not a conftest and # not our own Ninja builder) then we add it to the NINJA_STATE. Otherwise we # build it like normal. This skips all of the caching work that this method # would normally do since we aren't pulling any of these targets from the # cache. # # In the future we may be able to use this to actually cache the build.ninja # file once we have the upstream support for referencing SConscripts as File # nodes. def ninja_execute ( self ) : global NINJA_STATE target = self . targets [ 0 ] target_name = str ( target ) if target_name != ninja_file_name and "conftest" not in target_name : NINJA_STATE . add_build ( target ) else : target . build ( ) SCons . Taskmaster . Task . execute = ninja_execute # Make needs_execute always return true instead of determining out of # date-ness. SCons . Script . Main . BuildTask . needs_execute = lambda x : True # We will eventually need to overwrite TempFileMunge to make it # handle persistent tempfiles or get an upstreamed change to add # some configurability to it's behavior in regards to tempfiles. # # Set all three environment variables that Python's # tempfile.mkstemp looks at as it behaves differently on different # platforms and versions of Python. os . environ [ "TMPDIR" ] = env . Dir ( "$BUILD_DIR/response_files" ) . get_abspath ( ) os . environ [ "TEMP" ] = os . environ [ "TMPDIR" ] os . environ [ "TMP" ] = os . environ [ "TMPDIR" ] if not os . path . isdir ( os . environ [ "TMPDIR" ] ) : env . Execute ( SCons . Defaults . Mkdir ( os . environ [ "TMPDIR" ] ) ) env [ "TEMPFILE" ] = NinjaNoResponseFiles