Merge 'build: support (and prefer) sccache as the compiler cache' from Avi Kivity
Currently, we support ccache as the compiler cache. Since it is transparent, nothing much is needed to support it. This series adds support to sccache[1] and prefers it over ccache when it is installed. sccache brings the following benefits over ccache: 1. Integrated distributed build support similar to distcc, but with automatic toolchain packaging and a scheduler 2. Rust support 3. C++20 modules (upcoming[2]) It is the C++20 modules support that motivates the series. C++20 modules have the potential to reduce build times, but without a compiler cache and distributed build support, they come with too large a penalty. This removes the penalty. The series detects that sccache is installed, selects it if so (and if not overridden by a new option), enables it for C++ and Rust, and disables ccache transparent caching if sccache is selected. Note: this series doesn't add sccache to the frozen toolchain or add dbuild support. That is left for later. [1] https://github.com/mozilla/sccache [2] https://github.com/mozilla/sccache/pull/2516 Toolchain improvement, won't be backported. Closes scylladb/scylladb#27834 * github.com:scylladb/scylladb: build: apply sccache to rust builds too build: prevent double caching by compiler cache build: allow selecting compiler cache, including sccache
This commit is contained in:
131
configure.py
131
configure.py
@@ -368,6 +368,87 @@ def find_ninja():
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def find_compiler(name):
|
||||
"""
|
||||
Find a compiler by name, skipping ccache wrapper directories.
|
||||
|
||||
This is useful when using sccache to avoid double-caching through ccache.
|
||||
|
||||
Args:
|
||||
name: The compiler name (e.g., 'clang++', 'clang', 'gcc')
|
||||
|
||||
Returns:
|
||||
Path to the compiler, skipping ccache directories, or None if not found.
|
||||
"""
|
||||
ccache_dirs = {'/usr/lib/ccache', '/usr/lib64/ccache'}
|
||||
for path_dir in os.environ.get('PATH', '').split(os.pathsep):
|
||||
# Skip ccache wrapper directories
|
||||
if os.path.realpath(path_dir) in ccache_dirs or path_dir in ccache_dirs:
|
||||
continue
|
||||
candidate = os.path.join(path_dir, name)
|
||||
if os.path.isfile(candidate) and os.access(candidate, os.X_OK):
|
||||
return candidate
|
||||
return None
|
||||
|
||||
|
||||
def resolve_compilers_for_sccache(args, compiler_cache):
|
||||
"""
|
||||
When using sccache, resolve compiler paths to avoid ccache directories.
|
||||
|
||||
This prevents double-caching when ccache symlinks are in PATH.
|
||||
|
||||
Args:
|
||||
args: The argument namespace with cc and cxx attributes.
|
||||
compiler_cache: Path to the compiler cache binary, or None.
|
||||
"""
|
||||
if not compiler_cache or 'sccache' not in compiler_cache:
|
||||
return
|
||||
if not os.path.isabs(args.cxx):
|
||||
real_cxx = find_compiler(args.cxx)
|
||||
if real_cxx:
|
||||
args.cxx = real_cxx
|
||||
if not os.path.isabs(args.cc):
|
||||
real_cc = find_compiler(args.cc)
|
||||
if real_cc:
|
||||
args.cc = real_cc
|
||||
|
||||
|
||||
def find_compiler_cache(preference):
|
||||
"""
|
||||
Find a compiler cache based on the preference.
|
||||
|
||||
Args:
|
||||
preference: One of 'auto', 'sccache', 'ccache', 'none', or a path to a binary.
|
||||
|
||||
Returns:
|
||||
Path to the compiler cache binary, or None if not found/disabled.
|
||||
"""
|
||||
if preference == 'none':
|
||||
return None
|
||||
|
||||
if preference == 'auto':
|
||||
# Prefer sccache over ccache
|
||||
for cache in ['sccache', 'ccache']:
|
||||
path = which(cache)
|
||||
if path:
|
||||
return path
|
||||
return None
|
||||
|
||||
if preference in ('sccache', 'ccache'):
|
||||
path = which(preference)
|
||||
if path:
|
||||
return path
|
||||
print(f"Warning: {preference} not found on PATH, disabling compiler cache")
|
||||
return None
|
||||
|
||||
# Assume it's a path to a binary
|
||||
if os.path.isfile(preference) and os.access(preference, os.X_OK):
|
||||
return preference
|
||||
|
||||
print(f"Warning: compiler cache '{preference}' not found or not executable, disabling compiler cache")
|
||||
return None
|
||||
|
||||
|
||||
modes = {
|
||||
'debug': {
|
||||
'cxxflags': '-DDEBUG -DSANITIZE -DDEBUG_LSA_SANITIZER -DSCYLLA_ENABLE_ERROR_INJECTION',
|
||||
@@ -732,6 +813,8 @@ arg_parser.add_argument('--compiler', action='store', dest='cxx', default='clang
|
||||
help='C++ compiler path')
|
||||
arg_parser.add_argument('--c-compiler', action='store', dest='cc', default='clang',
|
||||
help='C compiler path')
|
||||
arg_parser.add_argument('--compiler-cache', action='store', dest='compiler_cache', default='auto',
|
||||
help='Compiler cache to use: auto (default, prefers sccache), sccache, ccache, none, or a path to a binary')
|
||||
add_tristate(arg_parser, name='dpdk', dest='dpdk', default=False,
|
||||
help='Use dpdk (from seastar dpdk sources)')
|
||||
arg_parser.add_argument('--dpdk-target', action='store', dest='dpdk_target', default='',
|
||||
@@ -2014,7 +2097,7 @@ def semicolon_separated(*flags):
|
||||
def real_relpath(path, start):
|
||||
return os.path.relpath(os.path.realpath(path), os.path.realpath(start))
|
||||
|
||||
def configure_seastar(build_dir, mode, mode_config):
|
||||
def configure_seastar(build_dir, mode, mode_config, compiler_cache=None):
|
||||
seastar_cxx_ld_flags = mode_config['cxx_ld_flags']
|
||||
# We want to "undo" coverage for seastar if we have it enabled.
|
||||
if args.coverage:
|
||||
@@ -2061,6 +2144,10 @@ def configure_seastar(build_dir, mode, mode_config):
|
||||
'-DSeastar_IO_URING=ON',
|
||||
]
|
||||
|
||||
if compiler_cache:
|
||||
seastar_cmake_args += [f'-DCMAKE_CXX_COMPILER_LAUNCHER={compiler_cache}',
|
||||
f'-DCMAKE_C_COMPILER_LAUNCHER={compiler_cache}']
|
||||
|
||||
if args.stack_guards is not None:
|
||||
stack_guards = 'ON' if args.stack_guards else 'OFF'
|
||||
seastar_cmake_args += ['-DSeastar_STACK_GUARDS={}'.format(stack_guards)]
|
||||
@@ -2092,7 +2179,7 @@ def configure_seastar(build_dir, mode, mode_config):
|
||||
subprocess.check_call(seastar_cmd, shell=False, cwd=cmake_dir)
|
||||
|
||||
|
||||
def configure_abseil(build_dir, mode, mode_config):
|
||||
def configure_abseil(build_dir, mode, mode_config, compiler_cache=None):
|
||||
abseil_cflags = mode_config['lib_cflags']
|
||||
cxx_flags = mode_config['cxxflags']
|
||||
if '-DSANITIZE' in cxx_flags:
|
||||
@@ -2118,6 +2205,10 @@ def configure_abseil(build_dir, mode, mode_config):
|
||||
'-DABSL_PROPAGATE_CXX_STD=ON',
|
||||
]
|
||||
|
||||
if compiler_cache:
|
||||
abseil_cmake_args += [f'-DCMAKE_CXX_COMPILER_LAUNCHER={compiler_cache}',
|
||||
f'-DCMAKE_C_COMPILER_LAUNCHER={compiler_cache}']
|
||||
|
||||
cmake_args = abseil_cmake_args[:]
|
||||
abseil_build_dir = os.path.join(build_dir, mode, 'abseil')
|
||||
abseil_cmd = ['cmake', '-G', 'Ninja', real_relpath('abseil', abseil_build_dir)] + cmake_args
|
||||
@@ -2290,10 +2381,15 @@ def write_build_file(f,
|
||||
scylla_product,
|
||||
scylla_version,
|
||||
scylla_release,
|
||||
compiler_cache,
|
||||
args):
|
||||
use_precompiled_header = not args.disable_precompiled_header
|
||||
warnings = get_warning_options(args.cxx)
|
||||
rustc_target = pick_rustc_target('wasm32-wasi', 'wasm32-wasip1')
|
||||
# If compiler cache is available, prefix the compiler with it
|
||||
cxx_with_cache = f'{compiler_cache} {args.cxx}' if compiler_cache else args.cxx
|
||||
# For Rust, sccache is used via RUSTC_WRAPPER environment variable
|
||||
rustc_wrapper = f'RUSTC_WRAPPER={compiler_cache} ' if compiler_cache and 'sccache' in compiler_cache else ''
|
||||
f.write(textwrap.dedent('''\
|
||||
configure_args = {configure_args}
|
||||
builddir = {outdir}
|
||||
@@ -2356,7 +2452,7 @@ def write_build_file(f,
|
||||
command = clang --target=wasm32 --no-standard-libraries -Wl,--export-all -Wl,--no-entry $in -o $out
|
||||
description = C2WASM $out
|
||||
rule rust2wasm
|
||||
command = cargo build --target={rustc_target} --example=$example --locked --manifest-path=test/resource/wasm/rust/Cargo.toml --target-dir=$builddir/wasm/ $
|
||||
command = {rustc_wrapper}cargo build --target={rustc_target} --example=$example --locked --manifest-path=test/resource/wasm/rust/Cargo.toml --target-dir=$builddir/wasm/ $
|
||||
&& wasm-opt -Oz $builddir/wasm/{rustc_target}/debug/examples/$example.wasm -o $builddir/wasm/$example.wasm $
|
||||
&& wasm-strip $builddir/wasm/$example.wasm
|
||||
description = RUST2WASM $out
|
||||
@@ -2372,7 +2468,7 @@ def write_build_file(f,
|
||||
command = llvm-profdata merge $in -output=$out
|
||||
''').format(configure_args=configure_args,
|
||||
outdir=outdir,
|
||||
cxx=args.cxx,
|
||||
cxx=cxx_with_cache,
|
||||
user_cflags=user_cflags,
|
||||
warnings=warnings,
|
||||
defines=defines,
|
||||
@@ -2380,6 +2476,7 @@ def write_build_file(f,
|
||||
user_ldflags=user_ldflags,
|
||||
libs=libs,
|
||||
rustc_target=rustc_target,
|
||||
rustc_wrapper=rustc_wrapper,
|
||||
link_pool_depth=link_pool_depth,
|
||||
seastar_path=args.seastar_path,
|
||||
ninja=ninja,
|
||||
@@ -2464,10 +2561,10 @@ def write_build_file(f,
|
||||
description = TEST {mode}
|
||||
# This rule is unused for PGO stages. They use the rust lib from the parent mode.
|
||||
rule rust_lib.{mode}
|
||||
command = CARGO_BUILD_DEP_INFO_BASEDIR='.' cargo build --locked --manifest-path=rust/Cargo.toml --target-dir=$builddir/{mode} --profile=rust-{mode} $
|
||||
command = CARGO_BUILD_DEP_INFO_BASEDIR='.' {rustc_wrapper}cargo build --locked --manifest-path=rust/Cargo.toml --target-dir=$builddir/{mode} --profile=rust-{mode} $
|
||||
&& touch $out
|
||||
description = RUST_LIB $out
|
||||
''').format(mode=mode, antlr3_exec=args.antlr3_exec, fmt_lib=fmt_lib, test_repeat=args.test_repeat, test_timeout=args.test_timeout, **modeval))
|
||||
''').format(mode=mode, antlr3_exec=args.antlr3_exec, fmt_lib=fmt_lib, test_repeat=args.test_repeat, test_timeout=args.test_timeout, rustc_wrapper=rustc_wrapper, **modeval))
|
||||
f.write(
|
||||
'build {mode}-build: phony {artifacts} {wasms} {vector_search_validator_bins}\n'.format(
|
||||
mode=mode,
|
||||
@@ -2927,6 +3024,9 @@ def create_build_system(args):
|
||||
|
||||
os.makedirs(outdir, exist_ok=True)
|
||||
|
||||
compiler_cache = find_compiler_cache(args.compiler_cache)
|
||||
resolve_compilers_for_sccache(args, compiler_cache)
|
||||
|
||||
scylla_product, scylla_version, scylla_release = generate_version(args.date_stamp)
|
||||
|
||||
for mode, mode_config in build_modes.items():
|
||||
@@ -2943,8 +3043,8 @@ def create_build_system(args):
|
||||
# {outdir}/{mode}/seastar/build.ninja, and
|
||||
# {outdir}/{mode}/seastar/seastar.pc is queried for building flags
|
||||
for mode, mode_config in build_modes.items():
|
||||
configure_seastar(outdir, mode, mode_config)
|
||||
configure_abseil(outdir, mode, mode_config)
|
||||
configure_seastar(outdir, mode, mode_config, compiler_cache)
|
||||
configure_abseil(outdir, mode, mode_config, compiler_cache)
|
||||
user_cflags += ' -isystem abseil'
|
||||
|
||||
for mode, mode_config in build_modes.items():
|
||||
@@ -2967,6 +3067,7 @@ def create_build_system(args):
|
||||
scylla_product,
|
||||
scylla_version,
|
||||
scylla_release,
|
||||
compiler_cache,
|
||||
args)
|
||||
generate_compdb('compile_commands.json', ninja, args.buildfile, selected_modes)
|
||||
|
||||
@@ -3009,6 +3110,10 @@ def configure_using_cmake(args):
|
||||
selected_modes = args.selected_modes or default_modes
|
||||
selected_configs = ';'.join(build_modes[mode].cmake_build_type for mode
|
||||
in selected_modes)
|
||||
|
||||
compiler_cache = find_compiler_cache(args.compiler_cache)
|
||||
resolve_compilers_for_sccache(args, compiler_cache)
|
||||
|
||||
settings = {
|
||||
'CMAKE_CONFIGURATION_TYPES': selected_configs,
|
||||
'CMAKE_CROSS_CONFIGS': selected_configs,
|
||||
@@ -3026,6 +3131,14 @@ def configure_using_cmake(args):
|
||||
'Scylla_WITH_DEBUG_INFO' : 'ON' if args.debuginfo else 'OFF',
|
||||
'Scylla_USE_PRECOMPILED_HEADER': 'OFF' if args.disable_precompiled_header else 'ON',
|
||||
}
|
||||
|
||||
if compiler_cache:
|
||||
settings['CMAKE_CXX_COMPILER_LAUNCHER'] = compiler_cache
|
||||
settings['CMAKE_C_COMPILER_LAUNCHER'] = compiler_cache
|
||||
# For Rust, sccache is used via RUSTC_WRAPPER
|
||||
if 'sccache' in compiler_cache:
|
||||
settings['Scylla_RUSTC_WRAPPER'] = compiler_cache
|
||||
|
||||
if args.date_stamp:
|
||||
settings['Scylla_DATE_STAMP'] = args.date_stamp
|
||||
if args.staticboost:
|
||||
@@ -3057,7 +3170,7 @@ def configure_using_cmake(args):
|
||||
|
||||
if not args.dist_only:
|
||||
for mode in selected_modes:
|
||||
configure_seastar(build_dir, build_modes[mode].cmake_build_type, modes[mode])
|
||||
configure_seastar(build_dir, build_modes[mode].cmake_build_type, modes[mode], compiler_cache)
|
||||
|
||||
cmake_command = ['cmake']
|
||||
cmake_command += [f'-D{var}={value}' for var, value in settings.items()]
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
find_program(CARGO cargo
|
||||
REQUIRED)
|
||||
|
||||
# Set up RUSTC_WRAPPER for sccache support if configured
|
||||
if(Scylla_RUSTC_WRAPPER)
|
||||
set(RUSTC_WRAPPER_ENV "RUSTC_WRAPPER=${Scylla_RUSTC_WRAPPER}")
|
||||
else()
|
||||
set(RUSTC_WRAPPER_ENV "")
|
||||
endif()
|
||||
|
||||
function(add_rust_library name)
|
||||
# used for profiles defined in Cargo.toml
|
||||
if(CMAKE_CONFIGURATION_TYPES)
|
||||
@@ -16,7 +23,7 @@ function(add_rust_library name)
|
||||
set(library ${target_dir}/lib${name}.a)
|
||||
add_custom_command(
|
||||
OUTPUT ${library}
|
||||
COMMAND ${CMAKE_COMMAND} -E env CARGO_BUILD_DEP_INFO_BASEDIR=. ${CARGO} build --locked --target-dir=${target_dir} --profile=${profile}
|
||||
COMMAND ${CMAKE_COMMAND} -E env CARGO_BUILD_DEP_INFO_BASEDIR=. ${RUSTC_WRAPPER_ENV} ${CARGO} build --locked --target-dir=${target_dir} --profile=${profile}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${target_dir}/${profile}/lib${name}.a ${library}
|
||||
DEPENDS Cargo.lock
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
|
||||
@@ -3,6 +3,13 @@ find_program(CARGO cargo
|
||||
find_program(RUSTC rustc
|
||||
REQUIRED)
|
||||
|
||||
# Set up RUSTC_WRAPPER for sccache support if configured
|
||||
if(Scylla_RUSTC_WRAPPER)
|
||||
set(RUSTC_WRAPPER_ENV "RUSTC_WRAPPER=${Scylla_RUSTC_WRAPPER}")
|
||||
else()
|
||||
set(RUSTC_WRAPPER_ENV "")
|
||||
endif()
|
||||
|
||||
function(pick_rustc_target output_var candidates)
|
||||
execute_process(
|
||||
COMMAND
|
||||
@@ -33,6 +40,7 @@ function(compile_rust_to_wasm target input)
|
||||
add_custom_command(
|
||||
OUTPUT ${output}
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E env ${RUSTC_WRAPPER_ENV}
|
||||
${CARGO} build
|
||||
--target=${target}
|
||||
--example=${basename}
|
||||
|
||||
Reference in New Issue
Block a user