summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SConstruct24
-rw-r--r--site_scons/site_tools/ccache.py78
-rw-r--r--site_scons/site_tools/icecream.py91
-rw-r--r--site_scons/site_tools/ninja.py2
4 files changed, 151 insertions, 44 deletions
diff --git a/SConstruct b/SConstruct
index 5c7095c8565..6451263d080 100644
--- a/SConstruct
+++ b/SConstruct
@@ -110,15 +110,6 @@ add_option('ninja',
help='Enable the build.ninja generator tool',
)
-add_option('ccache',
- choices=['true', 'false'],
- default='false',
- nargs='?',
- const='true',
- type='choice',
- help='Enable ccache support',
-)
-
add_option('prefix',
help='installation prefix (conficts with DESTDIR, PREFIX, and --install-mode=hygienic)',
)
@@ -696,18 +687,18 @@ env_vars.Add('ARFLAGS',
converter=variable_shlex_converter)
env_vars.Add('CCACHE',
- help='Path to ccache used for the --ccache option. Defaults to first ccache in PATH.')
+ help='Tell SCons where the ccache binary is')
env_vars.Add(
'CACHE_SIZE',
- help='Maximum size of the cache (in gigabytes)',
+ help='Maximum size of the SCons cache (in gigabytes)',
default=32,
converter=lambda x:int(x)
)
env_vars.Add(
'CACHE_PRUNE_TARGET',
- help='Maximum percent in-use in cache after pruning',
+ help='Maximum percent in-use in SCons cache after pruning',
default=66,
converter=lambda x:int(x)
)
@@ -3740,7 +3731,10 @@ def doConfigure(myenv):
env = doConfigure( env )
env["NINJA_SYNTAX"] = "#site_scons/third_party/ninja_syntax.py"
-# Now that we are done with configure checks, enable icecream, if available.
+# Now that we are done with configure checks, enable ccache and
+# icecream, if available. Per the rules declared in the icecream tool,
+# load the ccache tool first.
+env.Tool('ccache')
env.Tool('icecream')
if get_option('ninja') == 'true':
@@ -3818,10 +3812,6 @@ if get_option('ninja') == 'true':
env.NinjaRegisterFunctionHandler("write_uuid_to_file", fakelib_in_ninja)
- # Load ccache after icecream since order matters when we're both changing CCCOM
- if get_option('ccache') == 'true':
- env.Tool('ccache')
-
# TODO: Later, this should live somewhere more graceful.
if get_option('install-mode') == 'hygienic':
diff --git a/site_scons/site_tools/ccache.py b/site_scons/site_tools/ccache.py
index 077768dbd3d..d5ff49c06cf 100644
--- a/site_scons/site_tools/ccache.py
+++ b/site_scons/site_tools/ccache.py
@@ -14,25 +14,85 @@
import math
import os
+import re
+import subprocess
+
import SCons
+from pkg_resources import parse_version
+
+# This is the oldest version of ccache that offers support for -gsplit-dwarf
+_ccache_version_min = parse_version('3.2.3')
+_ccache_version_found = None
def exists(env):
- """Always enable"""
- ccache_path = env.get('CCACHE', env.WhereIs('ccache'))
- return os.path.exists(ccache_path)
+ """Look for a viable ccache implementation that meets our version requirements."""
+
+ # If we already generated, we definitely exist
+ if 'CCACHE_VERSION' in env:
+ return True
+
+ ccache = env.get('CCACHE', False)
+ if not ccache:
+ return False
+
+ ccache = env.WhereIs(ccache)
+ if not ccache:
+ return False
+
+ pipe = SCons.Action._subproc(env,
+ SCons.Util.CLVar(ccache) + ['--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'^ccache version', line)
+ if not version_banner:
+ continue
+ ccache_version = re.split('ccache version (.+)', line)
+ if len(ccache_version) < 2:
+ continue
+ global _ccache_version_found
+ _ccache_version_found = parse_version(ccache_version[1])
+ if _ccache_version_found >= _ccache_version_min:
+ validated = True
+
+ return validated
def generate(env):
"""Add ccache support."""
+
+ # If we have already generated the tool, don't generate it again.
+ if 'CCACHE_VERSION' in env:
+ return
+
+ # If we can't find ccache, or it is too old a version, don't
+ # generate.
+ if not exists(env):
+ return
+
+ # Record our found CCACHE_VERSION. Other tools that need to know
+ # about ccache (like iecc) should query this variable to determine
+ # if ccache is active. Looking at the CCACHE variable in the
+ # environment is not sufficient, since the user may have set it,
+ # but it doesn't work or is out of date.
+ env['CCACHE_VERSION'] = _ccache_version_found
+
# ccache does not support response files so force scons to always
# use the full command
#
# Note: This only works for Python versions >= 3.5
env['MAXLINELENGTH'] = math.inf
- env['CCACHE'] = env.get('CCACHE', env.WhereIs('ccache'))
- env['CCCOM'] = '$CCACHE ' + env['CCCOM']
- env['CXXCOM'] = '$CCACHE ' + env['CXXCOM']
- env['SHCCCOM'] = '$CCACHE ' + env['SHCCCOM']
- env['SHCXXCOM'] = '$CCACHE ' + env['SHCXXCOM']
-
+ # Add ccache to the relevant command lines. Wrap the reference to
+ # ccache in the $( $) pattern so that turning ccache on or off
+ # doesn't invalidate your build.
+ env['CCCOM'] = '$( $CCACHE $)' + env['CCCOM']
+ env['CXXCOM'] = '$( $CCACHE $)' + env['CXXCOM']
+ env['SHCCCOM'] = '$( $CCACHE $)' + env['SHCCCOM']
+ env['SHCXXCOM'] = '$( $CCACHE $)' + env['SHCXXCOM']
diff --git a/site_scons/site_tools/icecream.py b/site_scons/site_tools/icecream.py
index c97b4ecc817..dfa7aa32748 100644
--- a/site_scons/site_tools/icecream.py
+++ b/site_scons/site_tools/icecream.py
@@ -20,13 +20,40 @@ import subprocess
from pkg_resources import parse_version
-icecream_version_min = '1.1rc2'
+_icecream_version_min = parse_version('1.1rc2')
+_ccache_nocpp2_version = parse_version('3.4.1')
+
+
+# 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 generate(env):
if not exists(env):
return
+ # 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.
+ if 'ccache' in env['TOOLS'] and not 'CCACHE_VERSION' in env:
+ env.Tool('ccache')
+ ccache_enabled = ('CCACHE_VERSION' in env)
+
# Absoluteify, so we can derive ICERUN
env['ICECC'] = env.WhereIs('$ICECC')
@@ -78,7 +105,7 @@ def generate(env):
# doesn't appear when executing icecc_create_env
toolchain_env = env.Clone()
if toolchain_env.ToolchainIs('clang'):
- toolchain = env.Command(
+ toolchain = toolchain_env.Command(
target=icecc_version,
source=[
'$ICECC_CREATE_ENV',
@@ -138,12 +165,22 @@ def generate(env):
if env.ToolchainIs('clang'):
env['ENV']['ICECC_CLANG_REMOTE_CPP'] = 1
+
+ if ccache_enabled and env['CCACHE_VERSION'] >= _ccache_nocpp2_version:
+ env.AppendUnique(
+ CCFLAGS=[
+ '-frewrite-includes'
+ ]
+ )
+ env['ENV']['CCACHE_NOCPP2'] = 1
else:
env.AppendUnique(
CCFLAGS=[
'-fdirectives-only'
]
)
+ if ccache_enabled:
+ env['ENV']['CCACHE_NOCPP2'] = 1
if 'ICECC_SCHEDULER' in env:
env['ENV']['USE_SCHEDULER'] = env['ICECC_SCHEDULER']
@@ -161,26 +198,45 @@ def generate(env):
# Be careful here. If we are running with the ninja tool, many things
# may have been monkey patched away. Rely only on `os`, not things
# that may try to stat. The abspath appears to be ok.
+ #
+ # TODO: Another idea would be to eternally memoize lstat in
+ # the ninja module, and then we could return to using a call
+ # to islink on the ICECC_VERSION file. Similarly, it would be
+ # nice to be able to memoize away this call, but we should
+ # think carefully about where to store the result of such
+ # memoization.
return os.path.realpath(env['ICECC_VERSION'].abspath)
env['ICECC_VERSION_GEN'] = icecc_version_gen
- # Build up the string we will stick at the front of the compile
- # 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.
+ # Build up the string we will set in the environment to tell icecream
+ # about the compiler package.
icecc_version_string = '${ICECC_VERSION_GEN}'
if 'ICECC_VERSION_ARCH' in env:
icecc_version_string = '${ICECC_VERSION_ARCH}:' + icecc_version_string
- icecc_version_string = '$( ICECC_VERSION={value} $ICECC $)'.format(value=icecc_version_string)
- # Amend the various C compilation command strings to start with
- # the icecream prelude.
- env['CCCOM'] = ' '.join([icecc_version_string, env['CCCOM']])
- env['CXXCOM'] = ' '.join([icecc_version_string, env['CXXCOM']])
- env['SHCCCOM'] = ' '.join([icecc_version_string, env['SHCCCOM']])
- env['SHCXXCOM'] = ' '.join([icecc_version_string, env['SHCXXCOM']])
+ # Use our BoundSubstitition class to put ICECC_VERSION into
+ # env['ENV'] with substitution in play. This lets us defer doing
+ # the realpath in the generator above until after we have made the
+ # tarball.
+ env['ENV']['ICECC_VERSION'] = _BoundSubstitution(env, icecc_version_string)
+
+ # 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:
+ env['ENV']['CCACHE_PREFIX'] = _BoundSubstitution(env, '$ICECC')
+ else:
+ icecc_string = '$( $ICECC $)'
+ 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 link like jobs flow through icerun so we don't kill the
# local machine.
@@ -203,6 +259,8 @@ def exists(env):
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',
@@ -223,8 +281,7 @@ def exists(env):
if len(icecc_version) < 2:
continue
icecc_version = parse_version(icecc_version[1])
- needed_version = parse_version(icecream_version_min)
- if icecc_version >= needed_version:
+ if icecc_version >= _icecream_version_min:
validated = True
return validated
diff --git a/site_scons/site_tools/ninja.py b/site_scons/site_tools/ninja.py
index 8823121de90..52b567e2b7c 100644
--- a/site_scons/site_tools/ninja.py
+++ b/site_scons/site_tools/ninja.py
@@ -805,7 +805,7 @@ def get_command(env, node, action): # pylint: disable=too-many-branches
if env["PLATFORM"] == "win32":
command_env += "set '{}={}' && ".format(key, value)
else:
- command_env += "{}={}; ".format(key, value)
+ command_env += "export {}={}; ".format(key, value)
setattr(node.attributes, "NINJA_ENV_ENV", command_env)