0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-11-29 16:47:28 +01:00
mongodb/site_scons/site_tools/icecream.py
2019-04-08 14:08:49 -04:00

162 lines
5.8 KiB
Python

# Copyright 2017 MongoDB Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import SCons
import os
import re
import subprocess
from pkg_resources import parse_version
icecream_version_min = '1.1rc2'
def generate(env):
if not exists(env):
return
# 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')
# We can't handle sanitizer blacklist files, so disable icecc then, and just flow through
# icerun to prevent slamming the local system with a huge -j value.
if any(
f.startswith("-fsanitize-blacklist=") for fs in ['CCFLAGS', 'CFLAGS', 'CXXFLAGS']
for f in env[fs]):
env['ICECC'] = '$ICERUN'
# Make CC and CXX absolute paths too. It is better for icecc.
env['CC'] = env.WhereIs('$CC')
env['CXX'] = env.WhereIs('$CXX')
# Make a predictable name for the toolchain
icecc_version_target_filename = env.subst('$CC$CXX').replace('/', '_')
icecc_version = env.Dir('$BUILD_ROOT/scons/icecc').File(icecc_version_target_filename)
# Make an isolated environment so that our setting of ICECC_VERSION in the environment
# doesn't appear when executing icecc_create_env
toolchain_env = env.Clone()
if toolchain_env.ToolchainIs('clang'):
toolchain = env.Command(
target=icecc_version,
source=['$ICECC_CREATE_ENV', '$CC', '$CXX'],
action=[
"${SOURCES[0]} --clang ${SOURCES[1].abspath} /bin/true $TARGET",
],
)
env['ENV']['ICECC_CLANG_REMOTE_CPP'] = 1
else:
toolchain = toolchain_env.Command(
target=icecc_version, source=['$ICECC_CREATE_ENV', '$CC', '$CXX'], action=[
"${SOURCES[0]} --gcc ${SOURCES[1].abspath} ${SOURCES[2].abspath} $TARGET",
])
env.AppendUnique(CCFLAGS=['-fdirectives-only'])
# Add ICECC_VERSION to the environment, pointed at the generated
# file so that we can expand it in the realpath expressions for
# CXXCOM and friends below.
env['ICECC_VERSION'] = icecc_version
if 'ICECC_SCHEDULER' in env:
env['ENV']['USE_SCHEDULER'] = env['ICECC_SCHEDULER']
# Create an emitter that makes all of the targets depend on the
# icecc_version_target (ensuring that we have read the link), which in turn
# depends on the toolchain (ensuring that we have packaged it).
def icecc_toolchain_dependency_emitter(target, source, env):
env.Requires(target, toolchain)
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
])
# Make compile jobs flow through icecc
env['CCCOM'] = '$( ICECC_VERSION=$$(realpath $ICECC_VERSION) $ICECC $) ' + env['CCCOM']
env['CXXCOM'] = '$( ICECC_VERSION=$$(realpath $ICECC_VERSION) $ICECC $) ' + env['CXXCOM']
env['SHCCCOM'] = '$( ICECC_VERSION=$$(realpath $ICECC_VERSION) $ICECC $) ' + env['SHCCCOM']
env['SHCXXCOM'] = '$( ICECC_VERSION=$$(realpath $ICECC_VERSION) $ICECC $) ' + env['SHCXXCOM']
# Make link like jobs flow through icerun so we don't kill the
# local machine.
#
# TODO: Should we somehow flow SPAWN or other universal shell launch through
# ICERUN to avoid saturating the local machine, and build something like
# ninja pools?
env['ARCOM'] = '$( $ICERUN $) ' + env['ARCOM']
env['LINKCOM'] = '$( $ICERUN $) ' + env['LINKCOM']
env['SHLINKCOM'] = '$( $ICERUN $) ' + env['SHLINKCOM']
# Uncomment these to debug your icecc integration
# env['ENV']['ICECC_DEBUG'] = 'debug'
# env['ENV']['ICECC_LOGFILE'] = 'icecc.log'
def exists(env):
icecc = env.get('ICECC', False)
if not icecc:
return False
icecc = env.WhereIs(icecc)
pipe = SCons.Action._subproc(env,
SCons.Util.CLVar(icecc) + ['--version'], stdin='devnull',
stderr='devnull', stdout=subprocess.PIPE, text=True)
if pipe.wait() != 0:
return False
validated = False
for line in pipe.stdout:
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])
needed_version = parse_version(icecream_version_min)
if icecc_version >= needed_version:
validated = True
return validated