Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 55 additions & 16 deletions libc/utils/hdrgen/hdrgen/header.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@

COMMON_HEADER = PurePosixPath("__llvm-libc-common.h")

# These "attributes" are known macros defined in COMMON_HEADER.
# Others are found in "llvm-libc-macros/{name}.h".
COMMON_ATTRIBUTES = {
"_Noreturn",
"_Returns_twice",
}

# All the canonical identifiers are in lowercase for easy maintenance.
# This maps them to the pretty descriptions to generate in header comments.
LIBRARY_DESCRIPTIONS = {
Expand All @@ -50,9 +57,7 @@
HEADER_TEMPLATE = """\
//===-- {library} header <{header}> --===//
//
// Part of the LLVM Project, 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
{license_lines}
//
//===---------------------------------------------------------------------===//

Expand All @@ -64,6 +69,12 @@
#endif // {guard}
"""

LLVM_LICENSE_TEXT = [
"Part of the LLVM Project, 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",
]


class HeaderFile:
def __init__(self, name):
Expand All @@ -74,8 +85,10 @@ def __init__(self, name):
self.enumerations = []
self.objects = []
self.functions = []
self.extra_standards = {}
self.standards = []
self.merge_yaml_files = []
self.license_text = []

def add_macro(self, macro):
self.macros.append(macro)
Expand All @@ -98,6 +111,11 @@ def merge(self, other):
self.enumerations = sorted(set(self.enumerations) | set(other.enumerations))
self.objects = sorted(set(self.objects) | set(other.objects))
self.functions = sorted(set(self.functions) | set(other.functions))
self.extra_standards |= other.extra_standards
if self.license_text:
assert not other.license_text, "only one `license_text` allowed"
else:
self.license_text = other.license_text

def all_types(self):
return reduce(
Expand All @@ -106,6 +124,13 @@ def all_types(self):
set(self.types),
)

def all_attributes(self):
return reduce(
lambda a, b: a | b,
[set(f.attributes) for f in self.functions],
set(),
)

def all_standards(self):
# FIXME: Only functions have the "standard" field, but all the entity
# types should have one too.
Expand All @@ -114,41 +139,54 @@ def all_standards(self):
)

def includes(self):
return {
PurePosixPath("llvm-libc-macros") / macro.header
for macro in self.macros
if macro.header is not None
} | {
COMPILER_HEADER_TYPES.get(
typ.type_name, PurePosixPath("llvm-libc-types") / f"{typ.type_name}.h"
)
for typ in self.all_types()
}
return (
{
PurePosixPath("llvm-libc-macros") / macro.header
for macro in self.macros
if macro.header is not None
}
| {
COMPILER_HEADER_TYPES.get(
typ.type_name,
PurePosixPath("llvm-libc-types") / f"{typ.type_name}.h",
)
for typ in self.all_types()
}
| {
PurePosixPath("llvm-libc-macros") / f"{attr}.h"
for attr in self.all_attributes() - COMMON_ATTRIBUTES
}
)

def header_guard(self):
return "_LLVM_LIBC_" + "_".join(
word.upper() for word in NONIDENTIFIER.split(self.name) if word
)

def library_description(self):
descriptions = LIBRARY_DESCRIPTIONS | self.extra_standards
# If the header itself is in standard C, just call it that.
if "stdc" in self.standards:
return LIBRARY_DESCRIPTIONS["stdc"]
return descriptions["stdc"]
# If the header itself is in POSIX, just call it that.
if "posix" in self.standards:
return LIBRARY_DESCRIPTIONS["posix"]
return descriptions["posix"]
# Otherwise, consider the standards for each symbol as well.
standards = self.all_standards()
# Otherwise, it's described by all those that apply, but ignoring
# "stdc" and "posix" since this is not a "stdc" or "posix" header.
return " / ".join(
sorted(
LIBRARY_DESCRIPTIONS[standard]
descriptions[standard]
for standard in standards
if standard not in {"stdc", "posix"}
)
)

def license_lines(self):
lines = self.license_text or LLVM_LICENSE_TEXT
return "\n".join([f"// {line}" for line in lines])

def template(self, dir, files_read):
if self.template_file is not None:
# There's a custom template file, so just read it in and record
Expand All @@ -162,6 +200,7 @@ def template(self, dir, files_read):
library=self.library_description(),
header=self.name,
guard=self.header_guard(),
license_lines=self.license_lines(),
)

def public_api(self):
Expand Down
2 changes: 2 additions & 0 deletions libc/utils/hdrgen/hdrgen/yaml_to_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ def yaml_to_classes(yaml_data, header_class, entry_points=None):
header = header_class(header_name)
header.template_file = yaml_data.get("header_template")
header.standards = yaml_data.get("standards", [])
header.extra_standards = yaml_data.get("extra_standards", {})
header.license_text = yaml_data.get("license_text", [])
header.merge_yaml_files = yaml_data.get("merge_yaml_files", [])

for macro_data in yaml_data.get("macros", []):
Expand Down
21 changes: 21 additions & 0 deletions libc/utils/hdrgen/tests/expected_output/custom.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Wile E. Coyote header <custom.h> --===//
//
// Caveat emptor.
// I never studied law.
//
//===---------------------------------------------------------------------===//

#ifndef _LLVM_LIBC_CUSTOM_H
#define _LLVM_LIBC_CUSTOM_H

#include "__llvm-libc-common.h"
#include "llvm-libc-types/meep.h"
#include "llvm-libc-types/road.h"

__BEGIN_C_DECLS

road runner(meep, meep) __NOEXCEPT;

__END_C_DECLS

#endif // _LLVM_LIBC_CUSTOM_H
1 change: 1 addition & 0 deletions libc/utils/hdrgen/tests/expected_output/test_header.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "__llvm-libc-common.h"
#include "llvm-libc-macros/float16-macros.h"

#include "llvm-libc-macros/CONST_FUNC_A.h"
#include "llvm-libc-macros/test_more-macros.h"
#include "llvm-libc-macros/test_small-macros.h"
#include "llvm-libc-types/float128.h"
Expand Down
1 change: 1 addition & 0 deletions libc/utils/hdrgen/tests/expected_output/test_small.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"standards": [],
"includes": [
"__llvm-libc-common.h",
"llvm-libc-macros/CONST_FUNC_A.h",
"llvm-libc-macros/test_more-macros.h",
"llvm-libc-macros/test_small-macros.h",
"llvm-libc-types/float128.h",
Expand Down
6 changes: 6 additions & 0 deletions libc/utils/hdrgen/tests/input/custom-common.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
license_text:
- Caveat emptor.
- I never studied law.

extra_standards:
acme: Wile E. Coyote
13 changes: 13 additions & 0 deletions libc/utils/hdrgen/tests/input/custom.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
merge_yaml_files:
- custom-common.yaml

header: custom.h
standards:
- acme

functions:
- name: runner
return_type: road
arguments:
- type: meep
- type: meep
7 changes: 7 additions & 0 deletions libc/utils/hdrgen/tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ def test_generate_subdir_header(self):
self.run_script(yaml_file, output_file)
self.compare_files(output_file, expected_output_file)

def test_custom_license_and_standards(self):
yaml_file = self.source_dir / "input" / "custom.yaml"
expected_output_file = self.source_dir / "expected_output" / "custom.h"
output_file = self.output_dir / "custom.h"
self.run_script(yaml_file, output_file)
self.compare_files(output_file, expected_output_file)

def test_generate_json(self):
yaml_file = self.source_dir / "input/test_small.yaml"
expected_output_file = self.source_dir / "expected_output/test_small.json"
Expand Down
Loading