blob: d9e977d2f6d21025bbb25f79a7a9973270b21e6f [file] [log] [blame]
# Copyright 2025 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/python/python_action.gni")
import("//build/python/python_binary.gni")
import("//build/python/python_library.gni")
import("//build/testing/golden_files.gni")
import("../libc.gni")
import("libc_headers.gni")
# See README.md for a full explanation.
#
# This file (with libc_headers.gni) manages all the public libc header files.
# These go into the "sysroot" built by //zircon/public/sysroot; that's used to
# compile most Fuchsia user code, and it goes into the SDK.
#
# Any changes to these files require manual API review approval via update of
# the sysroot.api file. Some of these files come from llvm-libc. But the
# auto-roller that updates the llvm-libc code will never update sysroot.api; so
# llvm-libc header changes always require a manual roll that includes the
# sysroot.api update along with the //manifests update in a single commit.
#
# Public llvm-libc headers used are generated from "$llvm_libc/include/*.yaml"
# files. This list drives all the generation work done here, including
# regenerating llvm-libc-generated.json using the golden_files() mechanism.
# However, the golden JSON file in the source tree is what drives the
# libc_headers.gni lists that everything else uses. After changing this list,
# the next build with update_goldens=true will update the JSON file in the
# sources; that will necessitate another build to pick up the new lists and
# generate the headers correctly so that the sysroot can be updated.
# LINT.IfChange
llvm_libc_generated_headers = [
"dlfcn.h",
"fenv.h",
"malloc.h",
]
# LINT.ThenChange(//build/bazel/fuchsia_idk/generate_repository.py:sysroot_generated_headers)
# The generated headers go here, where libc_headers.gni looks for them.
gen_dir = libc_headers.llvm_libc_gen_dir
# Directory holding the hdrgen Python sources.
hdrgen_dir = "$llvm_libc/utils/hdrgen"
# This target is in an sdk.deps list in sysroot_entries, to ensure that the
# generated headers are in place. This checking only ever happens in the
# $default_toolchain and is not repeated elsewhere.
group("include") {
public_deps = [
":check-llvm-libc-json($default_toolchain)",
":llvm-libc-generated-headers($default_toolchain)",
]
}
if (current_toolchain == default_toolchain) {
python_action_foreach("llvm-libc-generated-headers") {
binary_label = ":hdrgen"
sources = []
generated_headers = []
foreach(header, llvm_libc_generated_headers) {
header_dir = get_path_info(header, "dir")
header_name = get_path_info(header, "name")
if (header_dir == ".") {
header_dir = ""
} else {
header_dir += "/"
}
sources += [ "$llvm_libc/include/$header_dir$header_name.yaml" ]
generated_headers += [ "$gen_dir/$header_dir$header_name.h" ]
}
# This is evaluated only in $default_toolchain, where $root_gen_dir is the
# prefix of $gen_dir before the full source-relative path in $llvm_libc.
# This isn't simpler because the action_foreach() path expansion logic only
# does a few fixed things, and it can't be used to distinguish the
# directory prefix that's part of the header name (e.g. sys/) from the rest
# of the path to the sources or outputs directory.
outputs =
[ "$root_gen_dir/{{source_root_relative_dir}}/{{source_name_part}}.h" ]
# That should wind up the same as "$gen_dir/$header" for each one.
_processed_sources = process_file_template(sources, outputs)
assert(_processed_sources == generated_headers,
"process_file_template($sources, $outputs) => " +
"$_processed_sources != $generated_headers")
depfile = "${outputs[0]}.d"
args = [
"--output=" + rebase_path(outputs[0], root_build_dir),
"--depfile=" + rebase_path(depfile, root_build_dir),
"--write-if-changed",
"{{source}}",
]
metadata = {
# Populate build_api("generated_sources") to ensure all the headers are
# present for analysis.
generated_sources = rebase_path(generated_headers, root_build_dir)
}
}
python_action("llvm-libc-json") {
visibility = [ ":*" ]
binary_label = ":hdrgen"
sources = []
foreach(header, llvm_libc_generated_headers) {
header_dir = get_path_info(header, "dir")
header_name = get_path_info(header, "name")
if (header_dir == ".") {
header_dir = ""
} else {
header_dir += "/"
}
sources += [ "$llvm_libc/include/$header_dir$header_name.yaml" ]
}
outputs = [ "$target_gen_dir/llvm-libc-generated.json" ]
depfile = "${outputs[0]}.d"
args = [
"--output=" + rebase_path(outputs[0], root_build_dir),
"--depfile=" + rebase_path(depfile, root_build_dir),
"--write-if-changed",
"--json",
] + rebase_path(sources, root_build_dir)
}
golden_files("check-llvm-libc-json") {
visibility = [ ":*" ]
deps = [ ":llvm-libc-json" ]
_json_outputs = get_target_outputs(deps[0])
comparisons = [
{
golden = libc_headers.llvm_libc_json_file
candidate = _json_outputs[0]
},
]
}
}
# This reaches all the public libc headers (ones that will be installed)
# directly at the source of truth. It's only used for compiling libc code.
# Note this includes the libc unit test code, as well as the hermetic partial
# libc bits built for special environments.
#
# source_set() is actually a template that injects additional deps...that
# reach back here. So this must use basic_source_set() to have only the
# direct effects spelled out here and nothing else explicit.
basic_source_set("headers") {
visibility = [
"../*",
"//build/config/zircon:user_deps",
"//zircon/kernel/lib/userabi/userboot/*",
"//zircon/system/ulib/ldmsg/*",
"//zircon/system/utest/core/threads/*",
"//zircon/third_party/scudo/*",
"//zircon/third_party/ulib/musl/*",
]
# Make sure no set_defaults() configs contribute anything that might
# propagate up from here.
configs = []
public_configs = [ ":headers.config" ]
public = []
public_deps = []
foreach(file,
libc_headers.root_public_headers + libc_headers.root_impl_headers) {
public += [ file ]
}
foreach(file,
libc_headers.musl_public_headers + libc_headers.musl_impl_headers) {
public += [ "${libc_headers.musl_dir}/$file" ]
}
foreach(file, libc_headers.llvm_libc_impl_headers) {
public += [ "$llvm_libc/include/$file" ]
}
foreach(file, llvm_libc_generated_headers) {
public += [ "$gen_dir/$file" ]
}
public_deps += [ ":llvm-libc-generated-headers($default_toolchain)" ]
public_deps += [ "//zircon/system/public" ]
}
config("headers.config") {
visibility = [ ":*" ]
cflags = []
dirs = [
".",
libc_headers.musl_dir,
gen_dir,
"$llvm_libc/include",
]
foreach(dir, dirs) {
cflags += [
"-idirafter",
rebase_path(dir, root_build_dir),
]
}
asmflags = cflags
}
python_binary("hdrgen") {
visibility = [ ":*" ]
main_source = "$hdrgen_dir/main.py"
deps = [ ":hdrgen.library" ]
enable_mypy = false
}
python_library("hdrgen.library") {
visibility = [ ":*" ]
library_name = "hdrgen"
source_root = "$hdrgen_dir/hdrgen"
sources = [
"__init__.py",
"enumeration.py",
"function.py",
"gpu_headers.py",
"header.py",
"macro.py",
"main.py",
"object.py",
"type.py",
"yaml_to_classes.py",
]
library_deps = [ "//third_party/pyyaml:yaml" ]
enable_mypy = false
}
# Generate standlaone "#include <foo.h>" compile-time tests for each public
# header file, and compile them in every known language mode.
header_test_stdc = [
# TODO(https://fxbug.dev/376333113): musl vs llvm-libc repeated typedefs are
# invalid in C<11.
#
#89, 99,
11,
17,
23,
]
header_test_stdcxx = [
# These can be either integers or strings, doesn't matter. But GN doesn't
# allow (nor would anything else preserve) a leading 0 on an integer and the
# result must be exactly "03", not "3".
# TODO(https://fxbug.dev/376333113): libc++ headers are not C++03 clean!
#"03",
11,
14,
17,
20,
23,
26,
]
header_test_c_deps = []
header_test_c_sources = []
header_test_cxx_deps = []
header_test_cxx_sources = []
header_test_zircon_c_deps = []
header_test_zircon_c_sources = []
header_test_zircon_cxx_deps = []
header_test_zircon_cxx_sources = []
foreach(header, libc_headers.all_public_headers) {
header_target = string_replace(header, "/", "-")
foreach(extension,
[
"c",
"cc",
]) {
generated_file("$header_target.$extension") {
visibility = [ ":*" ]
testonly = true
output_conversion = "list lines"
outputs = [ "$target_gen_dir/header-tests/$header.$extension" ]
contents = [
"/* Generated by" +
get_label_info(":target_name", "label_no_toolchain") +
". DO NOT EDIT! */",
"#include <$header>",
]
}
}
# The <zircon/*.h> headers go in a separate list that isn't tested in C89.
if (get_path_info(header, "dir") == "zircon") {
header_test_zircon_c_deps += [ ":$header_target.c" ]
header_test_zircon_c_sources += get_target_outputs(":$header_target.c")
header_test_zircon_cxx_deps += [ ":$header_target.cc" ]
header_test_zircon_cxx_sources += get_target_outputs(":$header_target.cc")
} else {
header_test_c_deps += [ ":$header_target.c" ]
header_test_c_sources += get_target_outputs(":$header_target.c")
header_test_cxx_deps += [ ":$header_target.cc" ]
header_test_cxx_sources += get_target_outputs(":$header_target.cc")
}
}
unittest_deps = []
foreach(version, header_test_stdc) {
foreach(prefix,
[
"c",
"gnu",
]) {
unittest_deps += [ ":header-tests.$prefix$version" ]
libc_test("header-tests.$prefix$version") {
sources = header_test_c_sources
deps = header_test_c_deps
# The <zircon/*.h> headers (and their transitive dependencies on syscall
# headers) don't all support strict -std=c89 mode, though they do support
# -std=gnu89 mode.
if ("$prefix$version" != "c89") {
sources += header_test_zircon_c_sources
deps += header_test_zircon_c_deps
}
deps += [ ":headers" ]
configs = [
":test.$prefix$version",
"//build/config:Wsystem-headers",
]
}
config("test.$prefix$version") {
visibility = [ ":*" ]
cflags_c = [ "-std=$prefix$version" ]
}
}
}
foreach(version, header_test_stdcxx) {
foreach(prefix,
[
"c",
"gnu",
]) {
# libc++ provides wrapper headers that interpose on various libc headers.
# Those will be found implicitly in preference to the libc headers, unless
# -nostdinc++ is used. The libc headers should be usable and warning-clean
# when used in C++ language modes directly without the libc++ wrappers;
# they won't be standard C++ library compatible with `std::` namespace
# declarations and such, but they should not get compilation errors.
#
# The libc headers should also be usable (and warning-clean) when used via
# libc++'s wrapper headers. So test both ways.
foreach(libcxx,
[
"",
"-libcxx",
]) {
unittest_deps += [ ":header-tests.${prefix}xx$version$libcxx" ]
libc_test("header-tests.${prefix}xx$version$libcxx") {
sources = header_test_cxx_sources
deps = header_test_cxx_deps
# The <zircon/*.h> headers (and their transitive dependencies on
# syscall headers) don't need to support C++ < 17.
if (version != "03" && version >= 17) {
sources += header_test_zircon_cxx_sources
deps += header_test_zircon_cxx_deps
}
deps += [ ":headers" ]
configs = [ ":test.$prefix++$version" ]
if (libcxx == "") {
# Don't use the libc++ headers.
configs += [ "//build/config:no-libc++-include" ]
remove_configs = [ "//build/config:libc++-include" ]
# The libc++ headers are not warning-clean, and instead rely on the
# system-headers exemption from warnings. This is an unfortunate and
# counterproductive choice by the libc++ maintainers and it should be
# changed, but it is unlikely to change soon. If it ever does
# change, then this should be added unconditionally.
configs += [ "//build/config:Wsystem-headers" ]
}
}
}
config("test.$prefix++$version") {
visibility = [ ":*" ]
cflags_cc = [ "-std=$prefix++$version" ]
}
}
}
libc_test("unittests") {
sources = []
deps = unittest_deps
}