# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/apple/apple_sdk.gni")
import("//build/config/c++/modules.gni")
import("//build/config/clang/clang.gni")
import("//build/config/compiler/compiler.gni")
import("//build/config/sysroot.gni")
if (use_xcode_symlinks) {
import("//build/config/apple/mobile_config.gni")
}
if (is_win) {
import("//build/toolchain/win/win_toolchain_data.gni")
}
configs_to_add = [
"//buildtools/third_party/libc++:extra_flags",
"//buildtools/third_party/libc++:stdver",
"//build/config/compiler:no_chromium_code",
]
configs_to_remove = [
"//build/config/compiler:chromium_code",
"//build/config/coverage:default_coverage",
]
if (use_clang_modules) {
template("clang_module") {
source_set(target_name) {
forward_variables_from(invoker, "*", [ "modulemap" ])
use_libcxx_modules = false
sources = [ invoker.modulemap ]
module_name = target_name
if (!defined(public_deps)) {
public_deps = []
}
if (!defined(public_configs)) {
public_configs = []
}
public_configs += [ "//build/config/compiler:libcxx_module" ]
# Implicit module maps won't work on apple because we're currently
# missing some dependencies.
if (use_autogenerated_modules) {
# Explicitly DO NOT use public configs here. If we have two framework
# modules foo and bar, and only foo is transitively depended on by
# libcxx:
# * When building the module for libcxx, we want both -fmodule-map-file
# for both foo and bar, to ensure that if libcxx did depend on bar, we
# would throw an error.
# * We may choose not to build bar because it's so rarely used that it
# won't increase performance to precompile it
# * If we provide -fmodule-map-file=bar but choose not to build bar,
# then any target that did depend on bar would fail to compile.
configs += [ "//buildtools/third_party/libc++:all_modulemap_configs" ]
} else {
if (use_xcode_symlinks) {
# The `copy_sysroot_modulemaps` action depends on SDK paths that differ
# between macOS and iOS, so it must be built with the correct toolchain
# for the target OS.
toolchain = "//build/toolchain/mac:clang_$target_cpu"
if (is_ios || target_environment == "catalyst") {
toolchain = "//build/toolchain/ios:ios_clang_$target_cpu"
} else if (target_os == "ios" && is_mac) {
toolchain = "//build/toolchain/mac:clang_$host_cpu"
}
public_deps += [ "//buildtools/third_party/libc++:copy_sysroot_modulemaps($toolchain)" ]
}
# By always including all module maps, we can get it to error out if
# we attempt to include something not in your dependencies.
# Otherwise, it would silently add it to your own AST.
public_configs += [
"//buildtools/third_party/libc++:builtin_modulemap",
"//buildtools/third_party/libc++:libcxx_modulemap",
"//buildtools/third_party/libc++:sysroot_modulemaps",
]
}
# Header files in modules need to be built with chromium_code config as
# some warnings are not triggered if we use no_chromium_code config for
# modules compiles.
configs -= configs_to_remove - [ "//build/config/compiler:chromium_code" ]
# The module needs to use the same version of -std that is used by the
# libraries depending on it. Otherwise we get a module config mismatch.
configs += configs_to_add - [
"//build/config/compiler:no_chromium_code",
"//buildtools/third_party/libc++:stdver",
]
# Any complex target-specific logic needs to go in here.
# The modularize tool supports simple attributes, but not conditional
# logic.
if (target_name == "std_stdatomic_h" && use_cxx23) {
# In cxx23, stdatomic.h uses libcxx's stdatomic rather than the
# builtin.
if (!is_win) {
public_deps -= [ ":_Builtin_stdatomic" ]
}
public_deps += [ ":std" ]
}
}
}
template("builtin_module") {
# This is a template to build modules that reside in clang builtin's module file.
clang_module(target_name) {
forward_variables_from(invoker, "*")
modulemap = "${clang_base_path}/lib/clang/${clang_version}/include/module.modulemap"
if (use_autogenerated_modules) {
public_configs = [ "//buildtools/third_party/libc++:builtin_modulemap" ]
}
}
}
template("libcxx_module") {
# This is a template to build modules that reside in libc++'s module file.
clang_module(target_name) {
forward_variables_from(invoker, "*")
modulemap = "${root_build_dir}/gen/third_party/libc++/src/include/module.modulemap"
deps = [
"//buildtools/third_party/libc++:custom_headers",
"//buildtools/third_party/libc++:libcxx_headers",
]
if (use_autogenerated_modules) {
public_configs = [ "//buildtools/third_party/libc++:libcxx_modulemap" ]
}
}
}
if (use_autogenerated_modules) {
template("sysroot_module") {
clang_module(target_name) {
forward_variables_from(invoker, "*")
modulemap = "${target_gen_dir}/module.modulemap"
public_configs = [ ":sysroot_modulemap" ]
}
}
} else if (is_apple) {
template("DarwinBasic_module") {
clang_module(target_name) {
forward_variables_from(invoker, "*")
modulemap = "$sdk_path/usr/include/DarwinBasic.modulemap"
}
}
template("DarwinFoundation1_module") {
clang_module(target_name) {
forward_variables_from(invoker, "*")
modulemap = "$sdk_path/usr/include/DarwinFoundation1.modulemap"
}
}
template("DarwinFoundation2_module") {
clang_module(target_name) {
forward_variables_from(invoker, "*")
modulemap = "$sdk_path/usr/include/DarwinFoundation2.modulemap"
}
}
template("DarwinFoundation3_module") {
clang_module(target_name) {
forward_variables_from(invoker, "*")
modulemap = "$sdk_path/usr/include/DarwinFoundation3.modulemap"
}
}
} else {
template("sysroot_module") {
clang_module(target_name) {
forward_variables_from(invoker, "*")
modulemap = sysroot_modulemap
deps = [
":custom_headers",
":libcxx_headers",
]
}
}
}
}
template("modulemap_config") {
config(target_name) {
path = rebase_path(invoker.source, root_build_dir)
cflags_cc = [ "-fmodule-map-file=${path}" ]
swiftflags = [ "-Xcc=-fmodule-map-file=${path}" ]
}
}
template("sysroot_modulemap") {
content = read_file(invoker.source, "string")
if (is_win) {
if (current_cpu == "x64") {
win_toolchain_data = win_toolchain_data_x64
} else if (current_cpu == "x86") {
win_toolchain_data = win_toolchain_data_x86
} else if (current_cpu == "arm64") {
win_toolchain_data = win_toolchain_data_arm64
} else {
assert(false,
"Unsuppported windows CPU, add it to win_toolchain_data.gni")
}
# This must be relative to the directory the modulemap is contained within.
content = string_replace(content,
"\$msvc",
rebase_path(win_toolchain_data.msvc_dir,
target_gen_dir,
root_build_dir))
content = string_replace(content,
"\$windows_kits",
rebase_path(win_toolchain_data.windows_kits_dir,
target_gen_dir,
root_build_dir))
} else {
include_dir = "${sysroot}/usr/include"
if (is_fuchsia) {
include_dir = "${sysroot}/include"
}
# This must be relative to the directory the modulemap is contained within.
content =
string_replace(content,
"\$sysroot",
rebase_path(include_dir, target_gen_dir, root_build_dir))
}
write_file(invoker.out, content)
modulemap_config(target_name) {
source = invoker.out
}
}
template("apple_sysroot_modulemap") {
modulemap = "${sysroot_include_dir}/${invoker.sysroot_path}"
modulemap_config(target_name) {
source = modulemap
}
# sysroot_modulemaps can be inside the build directory. This no-op action
# declares the modulemap files as outputs to satisfy GN's dependency
# tracking when other targets use them as inputs.
rel = string_split(rebase_path(modulemap, root_build_dir), "/")
if (rel[0] == "..") {
# It's not in the output directory so we can't declare it as an output file.
group(target_name + "_file") {
}
} else {
action(target_name + "_file") {
script = "//build/noop.py"
outputs = [ modulemap ]
}
}
}
template("apple_sysroot_module") {
clang_module(target_name) {
forward_variables_from(invoker,
"*",
[
"modulemap",
"modulemap_path",
])
modulemap = "${sysroot_include_dir}/${invoker.modulemap_path}"
public_configs = [ invoker.modulemap ]
if (!defined(public_deps)) {
public_deps = []
}
# The no-op action above depends on SDK paths that differ between macOS and
# iOS, so it must be built with the correct toolchain
# for the target OS.
if (use_xcode_symlinks) {
toolchain = "//build/toolchain/mac:clang_$target_cpu"
if (is_ios || target_environment == "catalyst") {
toolchain = "//build/toolchain/ios:ios_clang_$target_cpu"
} else if (target_os == "ios" && is_mac) {
toolchain = "//build/toolchain/mac:clang_$host_cpu"
}
public_deps += [ "${invoker.modulemap}_file(${toolchain})" ]
}
}
}
template("framework_modulemap") {
modulemap_config(target_name) {
source =
"${frameworks_dir}/${invoker.name}.framework/Modules/module.modulemap"
}
}
template("framework_module") {
clang_module(target_name) {
forward_variables_from(invoker, "*", [ "framework" ])
# :foo_modulemap -> ${frameworks_dir}/foo.framework/Modules/module.modulemap
modulemap = string_replace(
string_replace(invoker.modulemap, ":", "${frameworks_dir}/"),
"_modulemap",
".framework/Modules/module.modulemap")
public_configs = [ invoker.modulemap ]
cflags = [
# Some frameworks don't have proper umbrellas.
"-Wno-incomplete-umbrella",
]
}
}