summaryrefslogtreecommitdiff
path: root/site_scons
diff options
context:
space:
mode:
authorAndrew Morrow <acm@mongodb.com>2020-08-17 19:20:56 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-08-18 17:21:36 +0000
commit0662433ac038f4f3cf137a1be30ca245c180e990 (patch)
tree79198ecb2f5570205279f6e47e4b5b158561d1fe /site_scons
parent697b791f6429c6aa9c6ff2cdb4ac1adbe5a2a32b (diff)
downloadmongo-0662433ac038f4f3cf137a1be30ca245c180e990.tar.gz
SERVER-47598 Refactor ICECC_VERSION handling
Diffstat (limited to 'site_scons')
-rw-r--r--site_scons/site_tools/next/icecream.py565
-rw-r--r--site_scons/site_tools/next/ninja.py6
2 files changed, 568 insertions, 3 deletions
diff --git a/site_scons/site_tools/next/icecream.py b/site_scons/site_tools/next/icecream.py
new file mode 100644
index 00000000000..027fdcd3333
--- /dev/null
+++ b/site_scons/site_tools/next/icecream.py
@@ -0,0 +1,565 @@
+# Copyright 2020 MongoDB Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+import SCons
+
+import os
+import re
+import subprocess
+import urllib
+
+from pkg_resources import parse_version
+
+_icecream_version_min = parse_version("1.1rc2")
+_icecream_version_gcc_remote_cpp = parse_version("1.2")
+
+
+# I'd prefer to use value here, but amazingly, its __str__ returns the
+# *initial* value of the Value and not the built value, if
+# available. That seems like a bug. In the meantime, make our own very
+# sinmple Substition thing.
+class _BoundSubstitution:
+ def __init__(self, env, expression):
+ self.env = env
+ self.expression = expression
+ self.result = None
+
+ def __str__(self):
+ if self.result is None:
+ self.result = self.env.subst(self.expression)
+ return self.result
+
+
+def icecc_create_env(env, target, source, for_signature):
+ # Safe to assume unix here because icecream only works on Unix
+ mkdir = "mkdir -p ${TARGET.dir}"
+
+ # Create the env, use awk to get just the tarball name and we store it in
+ # the shell variable $ICECC_VERSION_TMP so the subsequent mv command and
+ # store it in a known location. Add any files requested from the user environment.
+ create_env = "ICECC_VERSION_TMP=$$(${SOURCES[0]} --$ICECC_COMPILER_TYPE ${SOURCES[1]} ${SOURCES[2]}"
+
+ # TODO: It would be a little more elegant if things in
+ # ICECC_CREATE_ENV_ADDFILES were handled as sources, because we
+ # would get automatic dependency tracking. However, there are some
+ # wrinkles around the mapped case so we have opted to leave it as
+ # just interpreting the env for now.
+ for addfile in env.get('ICECC_CREATE_ENV_ADDFILES', []):
+ if (type(addfile) == tuple
+ and len(addfile) == 2):
+ if env['ICECREAM_VERSION'] > parse_version('1.1'):
+ raise Exception("This version of icecream does not support addfile remapping.")
+ create_env += " --addfile {}={}".format(
+ env.File(addfile[0]).srcnode().abspath,
+ env.File(addfile[1]).srcnode().abspath)
+ env.Depends(target, addfile[1])
+ elif type(addfile) == str:
+ create_env += " --addfile {}".format(env.File(addfile).srcnode().abspath)
+ env.Depends(target, addfile)
+ else:
+ # NOTE: abspath is required by icecream because of
+ # this line in icecc-create-env:
+ # https://github.com/icecc/icecream/blob/10b9468f5bd30a0fdb058901e91e7a29f1bfbd42/client/icecc-create-env.in#L534
+ # which cuts out the two files based off the equals sign and
+ # starting slash of the second file
+ raise Exception("Found incorrect icecream addfile format: {}" +
+ "\nicecream addfiles must be a single path or tuple path format: " +
+ "('chroot dest path', 'source file path')".format(
+ str(addfile)))
+ create_env += " | awk '/^creating .*\\.tar\\.gz/ { print $$2 }')"
+
+ # Simply move our tarball to the expected locale.
+ mv = "mv $$ICECC_VERSION_TMP $TARGET"
+
+ # Daisy chain the commands and then let SCons Subst in the rest.
+ cmdline = f"{mkdir} && {create_env} && {mv}"
+ return cmdline
+
+
+def generate(env, verbose=True):
+
+ if not exists(env):
+ return
+
+ # icecc lower then 1.1 supports addfile remapping accidentally
+ # and above it adds an empty cpuinfo so handle cpuinfo issues for icecream
+ # below version 1.1
+ if (env['ICECREAM_VERSION'] <= parse_version('1.1')
+ and env.ToolchainIs("clang")
+ and os.path.exists('/proc/cpuinfo')):
+ env.AppendUnique(ICECC_CREATE_ENV_ADDFILES=[('/proc/cpuinfo', '/dev/null')])
+
+ # If we are going to load the ccache tool, but we haven't done so
+ # yet, then explicitly do it now. We need the ccache tool to be in
+ # place before we setup icecream because we need to do things a
+ # little differently if ccache is in play. If you don't use the
+ # TOOLS variable to configure your tools, you should explicitly
+ # load the ccache tool before you load icecream.
+ ccache_enabled = "CCACHE_VERSION" in env
+ if "ccache" in env["TOOLS"] and not ccache_enabled:
+ env.Tool("ccache")
+
+ # Absoluteify, so we can derive ICERUN
+ env["ICECC"] = env.WhereIs("$ICECC")
+
+ if not "ICERUN" in env:
+ env["ICERUN"] = env.File("$ICECC").File("icerun")
+
+ # Absoluteify, for parity with ICECC
+ env["ICERUN"] = env.WhereIs("$ICERUN")
+
+ if not "ICECC_CREATE_ENV" in env:
+ env["ICECC_CREATE_ENV"] = env.File("$ICECC").File("icecc-create-env")
+
+ # Absoluteify, for parity with ICECC
+ env["ICECC_CREATE_ENV"] = env.WhereIs("$ICECC_CREATE_ENV")
+
+ # Make CC and CXX absolute paths too. It is better for icecc.
+ env["CC"] = env.WhereIs("$CC")
+ env["CXX"] = env.WhereIs("$CXX")
+
+ env['ICECREAM_TARGET_DIR'] = env.Dir(
+ env.get('ICECREAM_TARGET_DIR', '#/.icecream')
+ )
+
+ # We have a lot of things to build and run that the final user
+ # environment doesn't need to see or know about. Make a custom env
+ # that we use consistently from here to where we end up setting
+ # ICECREAM_RUN_ICECC in the user env.
+ setupEnv = env.Clone(
+ NINJA_SKIP=True
+ )
+
+ if 'ICECC_VERSION' in setupEnv and bool(setupEnv['ICECC_VERSION']):
+
+ if setupEnv["ICECC_VERSION"].startswith("http"):
+
+ quoted = urllib.parse.quote(setupEnv['ICECC_VERSION'], safe=[])
+
+ # Use curl / wget to download the toolchain because SCons (and ninja)
+ # are better at running shell commands than Python functions.
+ #
+ # TODO: This all happens SCons side now. Should we just use python to
+ # fetch instead?
+ curl = setupEnv.WhereIs("curl")
+ wget = setupEnv.WhereIs("wget")
+
+ if curl:
+ cmdstr = "curl -L"
+ elif wget:
+ cmdstr = "wget"
+ else:
+ raise Exception(
+ "You have specified an ICECC_VERSION that is a URL but you have neither wget nor curl installed."
+ )
+
+ # Copy ICECC_VERSION into ICECC_VERSION_URL so that we can
+ # change ICECC_VERSION without perturbing the effect of
+ # the action.
+ setupEnv['ICECC_VERSION_URL'] = setupEnv['ICECC_VERSION']
+ setupEnv['ICECC_VERSION'] = icecc_version_file = setupEnv.Command(
+ target=f"$ICECREAM_TARGET_DIR/{quoted}",
+ source=[setupEnv.Value(quoted)],
+ action=SCons.Action.Action(
+ f"{cmdstr} -o $TARGET $ICECC_VERSION_URL",
+ "Downloading compiler package from $ICECC_VERSION_URL" if not verbose else str(),
+ ),
+ )[0]
+
+ else:
+ # Convert the users selection into a File node and do some basic validation
+ setupEnv['ICECC_VERSION'] = icecc_version_file = setupEnv.File('$ICECC_VERSION')
+
+ if not icecc_version_file.exists():
+ raise Exception(
+ 'The ICECC_VERSION variable set set to {}, but this file does not exist'.format(icecc_version_file)
+ )
+
+ # This is what we are going to call the file names as known to SCons on disk
+ setupEnv["ICECC_VERSION_ID"] = "user_provided." + icecc_version_file.name
+
+ else:
+
+ setupEnv["ICECC_COMPILER_TYPE"] = setupEnv.get(
+ "ICECC_COMPILER_TYPE", os.path.basename(setupEnv.WhereIs("${CC}"))
+ )
+
+ # This is what we are going to call the file names as known to SCons on disk. We do the
+ # subst early so that we can call `replace` on the result.
+ setupEnv["ICECC_VERSION_ID"] = setupEnv.subst(
+ "icecc-create-env.${CC}${CXX}.tar.gz").replace("/", "_"
+ )
+
+ setupEnv["ICECC_VERSION"] = icecc_version_file = setupEnv.Command(
+ target="$ICECREAM_TARGET_DIR/$ICECC_VERSION_ID",
+ source=[
+ "$ICECC_CREATE_ENV",
+ "$CC",
+ "$CXX"
+ ],
+ action=SCons.Action.Action(
+ icecc_create_env,
+ "Generating icecream compiler package: $TARGET" if not verbose else str(),
+ generator=True,
+ )
+ )[0]
+
+ # At this point, all paths above have produced a file of some sort. We now move on
+ # to producing our own signature for this local file.
+
+ setupEnv.Append(
+ ICECREAM_TARGET_BASE_DIR='$ICECREAM_TARGET_DIR',
+ ICECREAM_TARGET_BASE_FILE='$ICECC_VERSION_ID',
+ ICECREAM_TARGET_BASE='$ICECREAM_TARGET_BASE_DIR/$ICECREAM_TARGET_BASE_FILE',
+ )
+
+ # If the file we are planning to use is not within
+ # ICECREAM_TARGET_DIR then make a local copy of it that is.
+ if icecc_version_file.dir != env['ICECREAM_TARGET_DIR']:
+ setupEnv["ICECC_VERSION"] = icecc_version_file = setupEnv.Command(
+ target=[
+ '${ICECREAM_TARGET_BASE}.local',
+ ],
+ source=icecc_version_file,
+ action=SCons.Defaults.Copy('$TARGET', '$SOURCE'),
+ )
+
+ # There is no point caching the copy.
+ setupEnv.NoCache(icecc_version_file)
+
+ # Now, we compute our own signature of the local compiler package,
+ # and create yet another link to the compiler package with a name
+ # containing our computed signature. Now we know that we can give
+ # this filename to icecc and it will be assured to really reflect
+ # the contents of the package, and not the arbitrary naming of the
+ # file as found on the users filesystem or from
+ # icecc-create-env. We put the absolute path to that filename into
+ # a file that we can read from.
+ icecc_version_info = setupEnv.File(setupEnv.Command(
+ target=[
+ '${ICECREAM_TARGET_BASE}.sha256',
+ '${ICECREAM_TARGET_BASE}.sha256.path',
+ ],
+ source=icecc_version_file,
+ action=SCons.Action.ListAction(
+ [
+
+ # icecc-create-env run twice with the same input will
+ # create files with identical contents, and identical
+ # filenames, but with different hashes because it
+ # includes timestamps. So we compute a new hash based
+ # on the actual stream contents of the file by
+ # untarring it into shasum.
+ SCons.Action.Action(
+ "tar xfO ${SOURCES[0]} | shasum -b -a 256 - | awk '{ print $1 }' > ${TARGETS[0]}",
+ "Calculating sha256 sum of ${SOURCES[0]}" if not verbose else str(),
+ ),
+
+ SCons.Action.Action(
+ "ln -f ${SOURCES[0]} ${TARGETS[0].dir}/icecream_py_sha256_$$(cat ${TARGETS[0]}).tar.gz",
+ "Linking ${SOURCES[0]} to its sha256 sum name" if not verbose else str(),
+ ),
+
+ SCons.Action.Action(
+ "echo ${TARGETS[0].dir.abspath}/icecream_py_sha256_$$(cat ${TARGETS[0]}).tar.gz > ${TARGETS[1]}",
+ "Storing sha256 sum name for ${SOURCES[0]} to ${TARGETS[1]}" if not verbose else str(),
+ )
+ ],
+ )
+ ))
+
+ # We can't allow these to interact with the cache beacuse the
+ # second action produces a file unknown to SCons. If caching were
+ # permitted, the other two files could be retrieved from cache but
+ # the file produced by the second action could not (and would not)
+ # be. We would end up with a broken setup.
+ setupEnv.NoCache(icecc_version_info)
+
+ # Create a value node that, when built, contains the result of
+ # reading the contents of the sha256.path file. This way we can
+ # pull the value out of the file and substitute it into our
+ # wrapper script.
+ icecc_version_string_value = setupEnv.Command(
+ target=setupEnv.Value(None),
+ source=[
+ icecc_version_info[1]
+ ],
+ action=SCons.Action.Action(
+ lambda env, target, source: target[0].write(source[0].get_text_contents()),
+ "Reading compiler package sha256 sum path from $SOURCE" if not verbose else str(),
+ )
+ )[0]
+
+ def icecc_version_string_generator(source, target, env, for_signature):
+ if for_signature:
+ return icecc_version_string_value.get_csig()
+ return icecc_version_string_value.read()
+
+ # Set the values that will be interpolated into the run-icecc script.
+ setupEnv['ICECC_VERSION'] = icecc_version_string_generator
+
+ # If necessary, we include the users desired architecture in the
+ # interpolated file.
+ icecc_version_arch_string = str()
+ if "ICECC_VERSION_ARCH" in setupEnv:
+ icecc_version_arch_string = "${ICECC_VERSION_ARCH}:"
+
+ # Finally, create the run-icecc wrapper script. The contents will
+ # re-invoke icecc with our sha256 sum named file, ensuring that we
+ # trust the signature to be appropriate. In a pure SCons build, we
+ # actually wouldn't need this Substfile, we could just set
+ # env['ENV]['ICECC_VERSION'] to the Value node above. But that
+ # won't work for Ninja builds where we can't ask for the contents
+ # of such a node easily. Creating a Substfile means that SCons
+ # will take care of generating a file that Ninja can use.
+ run_icecc = setupEnv.Textfile(
+ target="$ICECREAM_TARGET_DIR/run-icecc.sh",
+ source=[
+ '#!/bin/sh',
+ 'ICECC_VERSION=@icecc_version_arch@@icecc_version@ exec @icecc@ "$@"',
+ '',
+ ],
+ SUBST_DICT={
+ '@icecc@' : '$ICECC',
+ '@icecc_version@' : '$ICECC_VERSION',
+ '@icecc_version_arch@' : icecc_version_arch_string,
+ },
+
+ # Don't change around the suffixes
+ TEXTFILEPREFIX=str(),
+ TEXTFILESUFFIX=str(),
+
+ # Somewhat surprising, but even though Ninja will defer to
+ # SCons to invoke this, we still need ninja to be aware of it
+ # so that it knows to invoke SCons to produce it as part of
+ # TEMPLATE expansion. Since we have set NINJA_SKIP=True for
+ # setupEnv, we need to reverse that here.
+ NINJA_SKIP=False
+ )
+
+ setupEnv.AddPostAction(
+ run_icecc,
+ action=SCons.Defaults.Chmod('$TARGET', "u+x"),
+ )
+
+ setupEnv.Depends(
+ target=run_icecc,
+ dependency=[
+
+ # TODO: Without the ICECC dependency, changing ICECC doesn't cause the Substfile
+ # to regenerate. Why is this?
+ '$ICECC',
+
+ # This dependency is necessary so that we build into this
+ # string before we create the file.
+ icecc_version_string_value,
+ ],
+ )
+
+ # From here out, we make changes to the users `env`.
+ setupEnv = None
+
+ env['ICECREAM_RUN_ICECC'] = run_icecc[0]
+
+ def icecc_toolchain_dependency_emitter(target, source, env):
+ if "conftest" not in str(target[0]):
+ # Requires or Depends? There are trade-offs:
+ #
+ # If it is `Depends`, then enabling or disabling icecream
+ # will cause a global recompile. But, if you regenerate a
+ # new compiler package, you will get a rebuild. If it is
+ # `Requires`, then enabling or disabling icecream will not
+ # necessarily cause a global recompile (it depends if
+ # C[,C,XX]FLAGS get changed when you do so), but on the
+ # other hand if you regenerate a new compiler package you
+ # will *not* get a rebuild.
+ #
+ # For now, we are opting for `Requires`, because it seems
+ # preferable that opting in or out of icecream shouldn't
+ # force a rebuild.
+ env.Requires(target, "$ICECREAM_RUN_ICECC")
+ return target, source
+
+ # Cribbed from Tool/cc.py and Tool/c++.py. It would be better if
+ # we could obtain this from SCons.
+ _CSuffixes = [".c"]
+ if not SCons.Util.case_sensitive_suffixes(".c", ".C"):
+ _CSuffixes.append(".C")
+
+ _CXXSuffixes = [".cpp", ".cc", ".cxx", ".c++", ".C++"]
+ if SCons.Util.case_sensitive_suffixes(".c", ".C"):
+ _CXXSuffixes.append(".C")
+
+ suffixes = _CSuffixes + _CXXSuffixes
+ for object_builder in SCons.Tool.createObjBuilders(env):
+ emitterdict = object_builder.builder.emitter
+ for suffix in emitterdict.keys():
+ if not suffix in suffixes:
+ continue
+ base = emitterdict[suffix]
+ emitterdict[suffix] = SCons.Builder.ListEmitter(
+ [base, icecc_toolchain_dependency_emitter]
+ )
+
+ if env.ToolchainIs("clang"):
+ env["ENV"]["ICECC_CLANG_REMOTE_CPP"] = 1
+ elif env.ToolchainIs("gcc"):
+ if env["ICECREAM_VERSION"] < _icecream_version_gcc_remote_cpp:
+ # We aren't going to use ICECC_REMOTE_CPP because icecc
+ # 1.1 doesn't offer it. We disallow fallback to local
+ # builds because the fallback is serial execution.
+ env["ENV"]["ICECC_CARET_WORKAROUND"] = 0
+ else:
+ if ccache_enabled:
+ # Newer versions of Icecream will drop -fdirectives-only from
+ # preprocessor and compiler flags if it does not find a remote
+ # build host to build on. ccache, on the other hand, will not
+ # pass the flag to the compiler if CCACHE_NOCPP2=1, but it will
+ # pass it to the preprocessor. The combination of setting
+ # CCACHE_NOCPP2=1 and passing the flag can lead to build
+ # failures.
+
+ # See: https://jira.mongodb.org/browse/SERVER-48443
+
+ # We have an open issue with Icecream and ccache to resolve the
+ # cause of these build failures. Once the bug is resolved and
+ # the fix is deployed, we can remove this entire conditional
+ # branch and make it like the one for clang.
+ # TODO: https://github.com/icecc/icecream/issues/550
+ env["ENV"].pop("CCACHE_NOCPP2", None)
+ env["ENV"]["CCACHE_CPP2"] = 1
+ try:
+ env["CCFLAGS"].remove("-fdirectives-only")
+ except ValueError:
+ pass
+ else:
+ # If we can, we should make Icecream do its own preprocessing
+ # to reduce concurrency on the local host. We should not do
+ # this when ccache is in use because ccache will execute
+ # Icecream to do its own preprocessing and then execute
+ # Icecream as the compiler on the preprocessed source.
+ env["ENV"]["ICECC_REMOTE_CPP"] = 1
+
+ if "ICECC_SCHEDULER" in env:
+ env["ENV"]["USE_SCHEDULER"] = env["ICECC_SCHEDULER"]
+
+ # If ccache is in play we actually want the icecc binary in the
+ # CCACHE_PREFIX environment variable, not on the command line, per
+ # the ccache documentation on compiler wrappers. Otherwise, just
+ # put $ICECC on the command line. We wrap it in the magic "don't
+ # consider this part of the build signature" sigils in the hope
+ # that enabling and disabling icecream won't cause rebuilds. This
+ # is unlikely to really work, since above we have maybe changed
+ # compiler flags (things like -fdirectives-only), but we still try
+ # to do the right thing.
+ if ccache_enabled:
+ # If the path to CCACHE_PREFIX isn't absolute, then it will
+ # look it up in PATH. That isn't what we want here, we make
+ # the path absolute.
+ env["ENV"]["CCACHE_PREFIX"] = _BoundSubstitution(env, "${ICECREAM_RUN_ICECC.abspath}")
+ else:
+ # Make a generator to expand to ICECC in the case where we are
+ # not a conftest. We never want to run conftests
+ # remotely. Ideally, we would do this for the CCACHE_PREFIX
+ # case above, but unfortunately if we did we would never
+ # actually see the conftests, because the BoundSubst means
+ # that we will never have a meaningful `target` variable when
+ # we are in ENV. Instead, rely on the ccache.py tool to do
+ # it's own filtering out of conftests.
+ def icecc_generator(target, source, env, for_signature):
+ if "conftest" not in str(target[0]):
+ return '$ICECREAM_RUN_ICECC'
+ return ''
+ env['ICECC_GENERATOR'] = icecc_generator
+
+ icecc_string = "$( $ICECC_GENERATOR $)"
+ env["CCCOM"] = " ".join([icecc_string, env["CCCOM"]])
+ env["CXXCOM"] = " ".join([icecc_string, env["CXXCOM"]])
+ env["SHCCCOM"] = " ".join([icecc_string, env["SHCCCOM"]])
+ env["SHCXXCOM"] = " ".join([icecc_string, env["SHCXXCOM"]])
+
+ # Make common non-compile jobs flow through icerun so we don't
+ # kill the local machine. It would be nice to plumb ICERUN in via
+ # SPAWN or SHELL but it is too much. You end up running `icerun
+ # icecc ...`, and icecream doesn't handle that. We could try to
+ # filter and only apply icerun if icecc wasn't present but that
+ # seems fragile. If you find your local machine being overrun by
+ # jobs, figure out what sort they are and extend this part of the
+ # setup.
+ icerun_commands = [
+ "ARCOM",
+ "LINKCOM",
+ "PYTHON",
+ "SHLINKCOM",
+ ]
+
+ for command in icerun_commands:
+ if command in env:
+ env[command] = " ".join(["$( $ICERUN $)", env[command]])
+
+ # Uncomment these to debug your icecc integration
+ # env['ENV']['ICECC_DEBUG'] = 'debug'
+ # env['ENV']['ICECC_LOGFILE'] = 'icecc.log'
+
+
+def exists(env):
+ # Assume the tool has run if we already know the version.
+ if "ICECREAM_VERSION" in env:
+ return True
+
+ icecc = env.get("ICECC", False)
+ if not icecc:
+ return False
+ icecc = env.WhereIs(icecc)
+ if not icecc:
+ return False
+
+ pipe = SCons.Action._subproc(
+ env,
+ SCons.Util.CLVar(icecc) + ["--version"],
+ stdin="devnull",
+ stderr="devnull",
+ stdout=subprocess.PIPE,
+ )
+
+ if pipe.wait() != 0:
+ return False
+
+ validated = False
+ for line in pipe.stdout:
+ line = line.decode("utf-8")
+ if validated:
+ continue # consume all data
+ version_banner = re.search(r"^ICECC ", line)
+ if not version_banner:
+ continue
+ icecc_version = re.split("ICECC (.+)", line)
+ if len(icecc_version) < 2:
+ continue
+ icecc_version = parse_version(icecc_version[1])
+ if icecc_version >= _icecream_version_min:
+ validated = True
+
+ if validated:
+ env['ICECREAM_VERSION'] = icecc_version
+
+ return validated
diff --git a/site_scons/site_tools/next/ninja.py b/site_scons/site_tools/next/ninja.py
index 1317c8a28a0..bcfa1fa6a49 100644
--- a/site_scons/site_tools/next/ninja.py
+++ b/site_scons/site_tools/next/ninja.py
@@ -139,7 +139,7 @@ def get_order_only(node):
"""Return a list of order only dependencies for node."""
if node.prerequisites is None:
return []
- return [get_path(src_file(prereq)) for prereq in node.prerequisites]
+ return [get_path(src_file(prereq)) for prereq in node.prerequisites if is_valid_dependent_node(prereq)]
def get_dependencies(node, skip_sources=False):
@@ -148,9 +148,9 @@ def get_dependencies(node, skip_sources=False):
return [
get_path(src_file(child))
for child in node.children()
- if child not in node.sources
+ if child not in node.sources and is_valid_dependent_node(child)
]
- return [get_path(src_file(child)) for child in node.children()]
+ return [get_path(src_file(child)) for child in node.children() if is_valid_dependent_node(child)]
def get_inputs(node, skip_unknown_types=False):