Skip to content

Instantly share code, notes, and snippets.

@hoyhoy
Last active July 15, 2025 11:24
Show Gist options
  • Select an option

  • Save hoyhoy/dc46c8edbf442fd51f44e51d542d185c to your computer and use it in GitHub Desktop.

Select an option

Save hoyhoy/dc46c8edbf442fd51f44e51d542d185c to your computer and use it in GitHub Desktop.
Compiler Address Sanitizer, Memory Sanitizer, Thread Sanitizer and Ccache Using Jinja2-enabled profiles with Conan 2.0.
{# █████████████████████████████████████████████████████████████████ #}
{# █ @hoyhoy - 1/12/2024 █ #}
{# █████████████████████████████████████████████████████████████████ #}
{# █ Jinja2 Profile Example to Auto-generate Compiler Sanitizer █ #}
{# █ Flags for a custom llvm build on MacOS and Linux █ #}
{# █████████████████████████████████████████████████████████████████ #}
{# ██████████████████████████████████████████████████████████████████ #}
{# █ $CONAN_HOME/profiles/common/clang_local.j2 █ #}
{# ██████████████████████████████████████████████████████████████████ #}
{% set compiler.name = "clang" %}
{% set compiler.version = detect_api.detect_compiler()[1].major %}
{% set llvm_root = "/usr/local" %}
{% set ccache_bin = "/opt/ccache/bin" %}
{% set llvm_bin = llvm_root ~ "/bin" %}
{% set llvm_lib = llvm_root ~ "/lib" %}
{% set llvm_system = platform.system().lower() %}
{% set compiler.system = llvm_system %}
{% set compiler.llvm_root = llvm_root %}
{% set compiler.llvm_bin = llvm_root ~ "bin" %}
{% set compiler.lib_dir = llvm_lib %}
{% set compiler.include_dir = llvm_root ~ "/include" %}
{# ██████████████████████████████████████████████████████████████████ #}
{# █ The only reason these ccaache symlinks are necessary is █ #}
{# █ because conan2 doesn't support adding a compiler launcher █ #}
{# █ to the CMakeToolChain() directly. I suggest adding these. █ #}
{# █ to conan3. (NOTE: THESE SETTINGS DO NOT EXIST BUT THEY SHOULD! █ #}
{# █ █ #}
{# █ - tools.build:compiler_executables.cmake_cxx_compiler_launcher █ #}
{# █ - tools.build:compiler_executables.cmake_c_compiler_launcher █ #}
{# ██████████████████████████████████████████████████████████████████ #}
{# ██████████████████████████████████████████████████████████████████ #}
{# █ $CMAKE_MODULE_PATH/ccache.cmake █ #}
{# ██████████████████████████████████████████████████████████████████ #}
{# █ A CMakeLists.txt as a Jinja2 comment? Sure why not? █ #}
{# █ This is how to auto-detect ccache using cmake! █ #}
{# ██████████████████████████████████████████████████████████████████ #}
{# #}
{# include_guard(GLOBAL) #}
{# if (NOT "$ENV{CCACHE_DISABLE}" STREQUAL "1") #}
{# find_program(CCACHE_PROGRAM ccache) #}
{# if (CCACHE_PROGRAM) #}
{# set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") #}
{# set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") #}
{# endif () #}
{# endif () #}
{# ██████████████████████████████████████████████████████████████████ #}
{# ██████████████████████████████████████████████████████████████████ #}
{# █ $CONAN_HOME/profiles/common/clang_local.j2 (continues...) █ #}
{# ██████████████████████████████████████████████████████████████████ #}
{% set compiler.cc = ccache_bin ~ "/clang" %}
{% set compiler.cxx = ccache_bin ~ "/clang++" %}
{% set compiler.cc = llvm_bin ~ "/clang" %}
{% set compiler.cxx = llvm_bin ~ "/clang++" %}
{% set compiler.ld = llvm_bin ~ "/lld" %}
{% set compiler.ar = llvm_bin ~ "/llvm-ar" %}
{% set compiler.as = llvm_bin ~ "/llvm-as" %}
{% set compiler.nm = llvm_bin ~ "/llvm-nm" %}
{% set compiler.ranlib = llvm_bin ~ "/llvm-ranlib" %}
{% set compiler.symbolizer = llvm_bin ~ "/llvm-symbolizer" %}
{% set compiler.cppstd = "17" %}
{% set compiler.definitions = [ "_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION" ] %}
{% set compiler.ldflags = [ "-L" ~ llvm_lib, "-fuse-ld=lld", "-Wl,-rpath," ~ llvm_lib ] %}
{% set compiler.flags = [] %}
{% set compiler.address_sanitizer = {
flags: [
"-fsanitize=address",
"-fsanitize-address-use-after-scope",
"-fsanitize-address-use-after-return=always"
],
ldflags: [ "-fsanitize=address", ]
}
%}
{% set compiler.thread_sanitizer = {
flags: [
"-fsanitize=address"
],
ldflags: [ "-fsanitize=thread" ]
}
%}
{% set compiler.memory_sanitizer = {
flags: [
"-fsanitize=mmory"
],
ldflags: [ "-fsanitize=memory" ]
}
%}
{% set compiler.debug_flags = [ "-g", "-glldb", "-gcolumn-info" ] %}
{% set compiler.disabled_warnings = [ "-Wno-everything" ] %}
{% set _ = profile.settings.update({
"compiler": compiler.name,
"compiler.libcxx": "libc++",
"compiler.cppstd": compiler.cppstd,
"compiler.version": compiler.version,
"compiler.warnings": "All",
"compiler.warnings_as_errors": True
})
%}
{% set compiler.dependency_options = {
"flags": compiler.flags + compiler.disabled_warnings
}
%}
{% if build_type == "Debug" %}
{% set _ = compiler.dependency_options["flags"].append(compiler.debug_flags) %}
{% if compiler.sanitizer == "address" %}
{% set _ = compiler.dependency_options.flags.append(compiler.address_sanitizer["compiler_flags"]) %}
{% set _ = compiler.ldflags.extend(compiler.dependency_options["flags"]) %}
{% elif compiler.sanitizer == "memory" %}
{% set _ = compiler.dependency_options.flags.append(compiler.memory_sanitizer["compiler_flags"]) %}
{% set _ = compiler.ldflags.extend(compiler.dependency_options["flags"]) %}
{% elif compiler.sanitizer == "thread" %}
{% set _ = compiler.dependency_options.flags.append(compiler.memory_sanitizer["compiler_flags"]) %}
{% set _ = compiler.ldflags.extend(compiler.dependency_options["flags"]) %}
{% endif %}
{% endif %}
{% set compiler.optimized_flags = ["-O3", "-march=native", "-funroll-loops", "-finline-functions", "-mtune=native" ] %}
{% set _ = profile.build_configuration.update({
"tools.build:compiler_executables": { "c" : compiler.cc, "cpp": compiler.cxx },
"tools.build:defines": compiler.definitions,
"tools.build:exelinkflags": compiler.ldflags,
"tools.build:compiler_executables": { "c" : compiler.cc, "cpp": compiler.cxx },
"tools.build:cflags": compiler.dependency_options["flags"] + compiler.optimized_flags,
"tools.build:cxxflags": compiler.dependency_options["flags"] + compiler.optimized_flags,
"tools.build:defines": compiler.definitions,
"myapp/*:tools.build:cflags": compiler.flags,
"myapp/*:tools.build:cxxflags": compiler.flags,
"tools.build:exelinkflags": compiler.ldflags
})
%}
{% set _ = profile.build_configuration.update({
"tools.build:compiler_executables": { "c" : compiler.cc, "cpp": compiler.cxx },
"tools.build:defines": compiler.definitions,
"tools.build:exelinkflags": compiler.ldflags
})
%}
{% set _ = profile.configuration.update({
"tools.build:compiler_executables": { "c" : compiler.cc, "cpp": compiler.cxx },
"tools.build:cflags": compiler.dependency_options["flags"],
"tools.build:cxxflags": compiler.dependency_options["flags"],
"tools.build:defines": compiler.definitions,
"myapp/*:tools.build:cflags": compiler.flags,
"myapp/*:tools.build:cxxflags": compiler.flags,
"tools.build:exelinkflags": compiler.ldflags
})
%}
{% set _ = profile.environment.update({
"CPATH": compiler.include_dir,
"CC": compiler.cc,
"CXX": compiler.cxx,
"LD": compiler.ld,
"AS": compiler.as,
"AR": compiler.ar,
"NM": compiler.nm,
"RANLIB": compiler.ranlib,
"LINKER": compiler.ld,
"LLVM_DIR": compiler.llvm_root,
"LLVM_PATH": compiler.llvm_bin,
"LD_LIBRARY_PATH": compiler.libpath,
"LIBPATH": compiler.libpath,
"FIPSLD_CC": compiler.cc,
"FIPSLD_CXX": compiler.cxx,
"FIPS_LD": compiler.ld,
"FIPS_CXXLD": compiler.ld,
"ASAN_SYMBOLIZER_PATH": compiler.symbolizer,
"MSAN_SYMBOLIZER_PATH": compiler.symbolizer,
"TSAN_SYMBOLIZER_PATH": compiler.symbolizer
})
%}
{# I then include clang_local.j2 into my default host profile.... #}
{# ██████████████████████████████████████████████████████████████ #}
{# █ CONAN_HOME/profiles/default █ #}
{# ██████████████████████████████████████████████████████████████ #}
{% with
profile = namespace(
build_type = "Debug",
settings = {},
options = {},
devtest_mode = {},
sanitizer = None,
configuration = {},
build_configuration = {},
environment = {}),
os = namespace(),
compiler = namespace()
%}
{% include "common/native.j2" %}
{% include "common/clang_local.j2" %}
{% include "common/dependencies.j2" %}
{% include "common/boost.j2" %}
[settings]
build_type={{profile.build_type}}
{% for s,value in profile.settings.items() %}
{{s}}={{value}}
{% endfor %}
[options]
{% for option,value in profile.options.items() -%}
{{option}}={{value}}
{% endfor %}
tools.cmake.cmake_layout:build_folder_vars=["settings.build_type"]
[conf]
{% for option,value in profile.configuration.items() -%}
{{option}}={{value}}
{% endfor %}
[buildenv]
{% for option,value in profile.environment.items() -%}
{{option}}={{value}}
{% endfor %}
b2/*:CXXFLAGS={{compiler.optimized_flags ~ " " ~ " ".join(compiler.ldflags) }}
{% endwith %}
{# ██████████████████████████████████████████████████████████████ #}
{# █ $CONAN_HOME/profiles/native.j2 █ #}
{# ██████████████████████████████████████████████████████████████ #}
{# █ Automatically generate os, and os version █ #}
{# ██████████████████████████████████████████████████████████████ #}
{% set current_os = detect_api.detect_os() %}
{% set _ = profile.settings.update({
"os": current_os,
"arch": platform.machine()
})
%}
{% if current_os == "Macos" %}
{# why isn't a function to emit the version string built-in to conan2? #}
{% set _ = profile.settings.update({
"os.version": ".".join(platform.mac_ver()[0].split(".")[0:2])
})
%}
{% endif %}
{# ██████████████████████████████████████████████████████████████ #}
{# █ $CONAN_HOME/profiles/common/boost.j2 █ #}
{# ██████████████████████████████████████████████████████████████ #}
{# █ Boost has so many flags, it gets its own include file! █ #}
{# ██████████████████████████████████████████████████████████████ #}
{% set _ = profile.options.update({
"boost/*:extra_b2_flags": "cxxflags=-Wno-everything cxxflags=-D_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION",
"boost/*:without_thread": False,
"boost/*:without_log": False,
"boost/*:without_locale": False,
"boost/*:without_random": False,
"boost/*:without_date_time": False,
"boost/*:without_container": False,
"boost/*:without_regex": False,
"boost/*:without_filesystem": False,
"boost/*:without_math": False,
"boost/*:without_atomic": False,
"boost/*:without_chrono": False,
"boost/*:without_exception": False,
"boost/*:without_system": False,
"boost/*:without_context": True,
"boost/*:without_contract": True,
"boost/*:without_coroutine": True,
"boost/*:without_fiber": True,
"boost/*:without_graph": True,
"boost/*:without_graph_parallel": True,
"boost/*:without_iostreams": True,
"boost/*:without_json": True,
"boost/*:without_mpi": True,
"boost/*:without_nowide": True,
"boost/*:without_program_options": True,
"boost/*:without_python": True,
"boost/*:without_serialization": True,
"boost/*:without_stacktrace": True,
"boost/*:without_test": True,
"boost/*:without_timer": True,
"boost/*:without_type_erasure": True,
"boost/*:without_wave": True,
"boost/*:without_url": True,
"boost/*:shared": False,
"boost/*:i18n_backend_icu": True,
"boost/*:i18n_backend_iconv": "off",
"boost/*:bzip2": False
})
%}
{# ██████████████████████████████████████████████████████████████ #}
{# █ $CONAN_HOME/profiles/common/dependencies.j2 █ #}
{# ██████████████████████████████████████████████████████████████ #}
{% set _ = profile.options.update({
"icu/*:shared": False,
"*:fPIC": True,
"libxml2/*:include_utils": True,
"libxml2/*:shared": False,
"libxml2/*:iconv": False,
"libxml2/*:icu": False,
"hiredis/*:with_ssl": True,
"azure-storage-cpp/*:shared": False,
"cpprestsdk/*:shared": False,
"openssl/*:no_ssl3": True,
"openssl/*:no_rc5": True,
"openssl/*:shared": False,
"openssl/*:no_zlib": True,
"openssl/*:no_fips": False
})
%}
{# Custom options per-os! #}
{% set current_os = detect_api.detect_os() %}
{% if current_os == "Windows" %}
{% set _ = profile.options.update({
"cpprestsdk/*:http_client_impl": "asio",
"cpprestsdk/*:http_listener_impl": "asio"
}) %}
{% endif %}
@steinerthomas
Copy link

nice generic profile!
Are you missing the "tools.build:sharedlinkflags": compiler.ldflags or is it intended to not set them?

@hoyhoy
Copy link
Author

hoyhoy commented Jul 15, 2025

Didn’t seem to matter for ASAN with all of our dependencies and we have quite a few.

Our app’s CMakelists.txt does set CMAKE_SHARED_LINKER_FLAGS outside of conan, but not for sanitization purposes.

The regular ldflags are applied to the shared libraries as well. IIRC, CMAKE_SHARED_LINKER_FLAGS is just added on top of the regular LDFLAGS for shared libraries. I don’t think Gnu Make even has an environment variable for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment