
Running `hdrgen` in Bazel hermetically requires adding a new dependency
on PyYAML. This PR uses PyYAML version 5.1 due to keep in line with the
docs:
b878e0d118/libc/docs/dev/header_generation.rst (L22)
See https://github.com/llvm/llvm-project/issues/134780.
Generated headers are placed in a `staging/` directory so that they have
the opportunity to be treated differently from non-generated headers.
This is a follow-up to https://github.com/llvm/llvm-project/pull/141256,
which was reverted in https://github.com/llvm/llvm-project/pull/143001
because it caused downstream failures when bootstrapping builds.
312 lines
9.8 KiB
Python
312 lines
9.8 KiB
Python
# This file is licensed under the Apache License v2.0 with LLVM Exceptions.
|
|
# See https://llvm.org/LICENSE.txt for license information.
|
|
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
"""LLVM libc starlark rules for building individual functions."""
|
|
|
|
load("@bazel_skylib//lib:paths.bzl", "paths")
|
|
load("@bazel_skylib//lib:selects.bzl", "selects")
|
|
load(":libc_configure_options.bzl", "LIBC_CONFIGURE_OPTIONS")
|
|
load(":libc_namespace.bzl", "LIBC_NAMESPACE")
|
|
load(":platforms.bzl", "PLATFORM_CPU_X86_64")
|
|
|
|
def libc_common_copts():
|
|
root_label = Label(":libc")
|
|
libc_include_path = paths.join(root_label.workspace_root, root_label.package)
|
|
return [
|
|
"-I" + libc_include_path,
|
|
"-I" + paths.join(libc_include_path, "include"),
|
|
"-DLIBC_NAMESPACE=" + LIBC_NAMESPACE,
|
|
]
|
|
|
|
def libc_release_copts():
|
|
copts = [
|
|
"-DLIBC_COPT_PUBLIC_PACKAGING",
|
|
# This is used to explicitly give public symbols "default" visibility.
|
|
# See src/__support/common.h for more information.
|
|
"-DLLVM_LIBC_FUNCTION_ATTR='[[gnu::visibility(\"default\")]]'",
|
|
# All other libc sources need to be compiled with "hidden" visibility.
|
|
"-fvisibility=hidden",
|
|
"-O3",
|
|
"-fno-builtin",
|
|
"-fno-lax-vector-conversions",
|
|
"-ftrivial-auto-var-init=pattern",
|
|
"-fno-omit-frame-pointer",
|
|
"-fstack-protector-strong",
|
|
]
|
|
|
|
platform_copts = selects.with_or({
|
|
PLATFORM_CPU_X86_64: ["-mno-omit-leaf-frame-pointer"],
|
|
"//conditions:default": [],
|
|
})
|
|
return copts + platform_copts
|
|
|
|
def _libc_library(name, **kwargs):
|
|
"""Internal macro to serve as a base for all other libc library rules.
|
|
|
|
Args:
|
|
name: Target name.
|
|
**kwargs: All other attributes relevant for the cc_library rule.
|
|
"""
|
|
|
|
for attr in ["copts", "local_defines"]:
|
|
if attr in kwargs:
|
|
fail("disallowed attribute: '{}' in rule: '{}'".format(attr, name))
|
|
native.cc_library(
|
|
name = name,
|
|
copts = libc_common_copts(),
|
|
local_defines = LIBC_CONFIGURE_OPTIONS,
|
|
linkstatic = 1,
|
|
**kwargs
|
|
)
|
|
|
|
# A convenience function which should be used to list all libc support libraries.
|
|
# Any library which does not define a public function should be listed with
|
|
# libc_support_library.
|
|
def libc_support_library(name, **kwargs):
|
|
_libc_library(name = name, **kwargs)
|
|
|
|
def libc_function(name, **kwargs):
|
|
"""Add target for a libc function.
|
|
|
|
This macro creates an internal cc_library that can be used to test this
|
|
function.
|
|
|
|
Args:
|
|
name: Target name. Typically the name of the function this target is for.
|
|
**kwargs: Other attributes relevant for a cc_library. For example, deps.
|
|
"""
|
|
|
|
# Builds "internal" library with a function, exposed as a C++ function in
|
|
# the "LIBC_NAMESPACE" namespace. This allows us to test the function in the
|
|
# presence of another libc.
|
|
_libc_library(name = name, **kwargs)
|
|
|
|
LibcLibraryInfo = provider(
|
|
"All source files and textual headers for building a particular library.",
|
|
fields = ["srcs", "textual_hdrs"],
|
|
)
|
|
|
|
def _get_libc_info_aspect_impl(
|
|
target, # @unused
|
|
ctx):
|
|
maybe_srcs = getattr(ctx.rule.attr, "srcs", [])
|
|
maybe_hdrs = getattr(ctx.rule.attr, "hdrs", [])
|
|
maybe_textual_hdrs = getattr(ctx.rule.attr, "textual_hdrs", [])
|
|
maybe_deps = getattr(ctx.rule.attr, "deps", [])
|
|
return LibcLibraryInfo(
|
|
srcs = depset(
|
|
transitive = [
|
|
dep[LibcLibraryInfo].srcs
|
|
for dep in maybe_deps
|
|
if LibcLibraryInfo in dep
|
|
] + [
|
|
src.files
|
|
for src in maybe_srcs + maybe_hdrs
|
|
],
|
|
),
|
|
textual_hdrs = depset(
|
|
transitive = [
|
|
dep[LibcLibraryInfo].textual_hdrs
|
|
for dep in maybe_deps
|
|
if LibcLibraryInfo in dep
|
|
] + [
|
|
hdr.files
|
|
for hdr in maybe_textual_hdrs
|
|
],
|
|
),
|
|
)
|
|
|
|
_get_libc_info_aspect = aspect(
|
|
implementation = _get_libc_info_aspect_impl,
|
|
attr_aspects = ["deps"],
|
|
)
|
|
|
|
def _libc_srcs_filegroup_impl(ctx):
|
|
srcs = depset(transitive = [
|
|
fn[LibcLibraryInfo].srcs
|
|
for fn in ctx.attr.libs
|
|
])
|
|
if ctx.attr.enforce_headers_only:
|
|
paths = [f.short_path for f in srcs.to_list() if f.extension != "h"]
|
|
if paths:
|
|
fail("Unexpected non-header files: {}".format(paths))
|
|
return DefaultInfo(files = srcs)
|
|
|
|
_libc_srcs_filegroup = rule(
|
|
doc = "Returns all sources for building the specified libraries.",
|
|
implementation = _libc_srcs_filegroup_impl,
|
|
attrs = {
|
|
"libs": attr.label_list(
|
|
mandatory = True,
|
|
aspects = [_get_libc_info_aspect],
|
|
),
|
|
"enforce_headers_only": attr.bool(default = False),
|
|
},
|
|
)
|
|
|
|
def _libc_textual_hdrs_filegroup_impl(ctx):
|
|
return DefaultInfo(
|
|
files = depset(transitive = [
|
|
fn[LibcLibraryInfo].textual_hdrs
|
|
for fn in ctx.attr.libs
|
|
]),
|
|
)
|
|
|
|
_libc_textual_hdrs_filegroup = rule(
|
|
doc = "Returns all textual headers for compiling the specified libraries.",
|
|
implementation = _libc_textual_hdrs_filegroup_impl,
|
|
attrs = {
|
|
"libs": attr.label_list(
|
|
mandatory = True,
|
|
aspects = [_get_libc_info_aspect],
|
|
),
|
|
},
|
|
)
|
|
|
|
def libc_release_library(
|
|
name,
|
|
libc_functions,
|
|
weak_symbols = [],
|
|
**kwargs):
|
|
"""Create the release version of a libc library.
|
|
|
|
Args:
|
|
name: Name of the cc_library target.
|
|
libc_functions: List of functions to include in the library. They should be
|
|
created by libc_function macro.
|
|
weak_symbols: List of function names that should be marked as weak symbols.
|
|
**kwargs: Other arguments relevant to cc_library.
|
|
"""
|
|
|
|
_libc_srcs_filegroup(
|
|
name = name + "_srcs",
|
|
libs = libc_functions,
|
|
)
|
|
|
|
_libc_textual_hdrs_filegroup(
|
|
name = name + "_textual_hdrs",
|
|
libs = libc_functions,
|
|
)
|
|
native.cc_library(
|
|
name = name + "_textual_hdr_library",
|
|
textual_hdrs = [":" + name + "_textual_hdrs"],
|
|
)
|
|
|
|
weak_attributes = [
|
|
"LLVM_LIBC_FUNCTION_ATTR_" + name + "='LLVM_LIBC_EMPTY, [[gnu::weak]]'"
|
|
for name in weak_symbols
|
|
]
|
|
|
|
native.cc_library(
|
|
name = name,
|
|
srcs = [":" + name + "_srcs"],
|
|
copts = libc_common_copts() + libc_release_copts(),
|
|
local_defines = weak_attributes + LIBC_CONFIGURE_OPTIONS,
|
|
deps = [
|
|
":" + name + "_textual_hdr_library",
|
|
],
|
|
**kwargs
|
|
)
|
|
|
|
def libc_header_library(name, hdrs, deps = [], **kwargs):
|
|
"""Creates a header-only library to share libc functionality.
|
|
|
|
Args:
|
|
name: Name of the cc_library target.
|
|
hdrs: List of headers to be shared.
|
|
deps: The list of libc_support_library dependencies if any.
|
|
**kwargs: All other attributes relevant for the cc_library rule.
|
|
"""
|
|
|
|
_libc_srcs_filegroup(
|
|
name = name + "_hdr_deps",
|
|
libs = deps,
|
|
enforce_headers_only = True,
|
|
)
|
|
|
|
_libc_textual_hdrs_filegroup(
|
|
name = name + "_textual_hdrs",
|
|
libs = deps,
|
|
)
|
|
native.cc_library(
|
|
name = name + "_textual_hdr_library",
|
|
textual_hdrs = [":" + name + "_textual_hdrs"],
|
|
)
|
|
|
|
native.cc_library(
|
|
name = name,
|
|
hdrs = hdrs,
|
|
# We put _hdr_deps in srcs, as they are not a part of this cc_library
|
|
# interface, but instead are used to implement shared headers.
|
|
srcs = [":" + name + "_hdr_deps"],
|
|
deps = [":" + name + "_textual_hdr_library"],
|
|
# copts don't really matter, since it's a header-only library, but we
|
|
# need proper -I flags for header validation, which are specified in
|
|
# libc_common_copts().
|
|
copts = libc_common_copts(),
|
|
**kwargs
|
|
)
|
|
|
|
def libc_generated_header(name, hdr, yaml_template, other_srcs = []):
|
|
"""Generates a libc header file from YAML template.
|
|
|
|
Args:
|
|
name: Name of the genrule target.
|
|
hdr: Path of the header file to generate.
|
|
yaml_template: Path of the YAML template file.
|
|
other_srcs: Other files required to generate the header, if any.
|
|
"""
|
|
hdrgen = "//libc:hdrgen"
|
|
cmd = "$(location {hdrgen}) $(location {yaml}) -o $@".format(
|
|
hdrgen = hdrgen,
|
|
yaml = yaml_template,
|
|
)
|
|
|
|
if not hdr.startswith("staging/"):
|
|
fail(
|
|
"Generated headers should be placed in a 'staging/' directory " +
|
|
"so that they can be treated differently from constant source files " +
|
|
"when bootstrapping builds.",
|
|
)
|
|
|
|
native.genrule(
|
|
name = name,
|
|
outs = [hdr],
|
|
srcs = [yaml_template] + other_srcs,
|
|
cmd = cmd,
|
|
tools = [hdrgen],
|
|
)
|
|
|
|
def libc_math_function(
|
|
name,
|
|
additional_deps = None):
|
|
"""Add a target for a math function.
|
|
|
|
Args:
|
|
name: The name of the function.
|
|
additional_deps: Other deps like helper cc_library targets used by the
|
|
math function.
|
|
"""
|
|
additional_deps = additional_deps or []
|
|
|
|
#TODO(michaelrj): Fix the floating point dependencies
|
|
OLD_FPUTIL_DEPS = [
|
|
":__support_fputil_basic_operations",
|
|
":__support_fputil_division_and_remainder_operations",
|
|
":__support_fputil_fenv_impl",
|
|
":__support_fputil_fp_bits",
|
|
":__support_fputil_hypot",
|
|
":__support_fputil_manipulation_functions",
|
|
":__support_fputil_nearest_integer_operations",
|
|
":__support_fputil_normal_float",
|
|
":__support_math_extras",
|
|
":__support_fputil_except_value_utils",
|
|
]
|
|
libc_function(
|
|
name = name,
|
|
srcs = ["src/math/generic/" + name + ".cpp"],
|
|
hdrs = ["src/math/" + name + ".h"],
|
|
deps = [":__support_common"] + OLD_FPUTIL_DEPS + additional_deps,
|
|
)
|