1# ====- Information about standard headers used by docgen ----*- python -*--==# 2# 3# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4# See https://llvm.org/LICENSE.txt for license information. 5# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6# 7# ==-------------------------------------------------------------------------==# 8from pathlib import Path 9from typing import Generator 10 11 12class Header: 13 """ 14 Maintains implementation information about a standard header file: 15 * where does its implementation dir live 16 * where is its macros file 17 * where is its docgen yaml file 18 19 By convention, the macro-only part of a header file is in a header-specific 20 file somewhere in the directory tree with root at 21 ``$LLVM_PROJECT_ROOT/libc/include/llvm-libc-macros``. Docgen expects that 22 if a macro is implemented, that it appears in a string 23 ``#define MACRO_NAME`` in some ``*-macros.h`` file in the directory tree. 24 Docgen searches for this string in the file to set the implementation status 25 shown in the generated rst docs rendered as html for display at 26 <libc.llvm.org>. 27 28 By convention, each function for a header is implemented in a function-specific 29 cpp file somewhere in the directory tree with root at, e.g, 30 ``$LLVM_PROJECT_ROOT/libc/src/fenv``. Some headers have architecture-specific 31 implementations, like ``math``, and some don't, like ``fenv``. Docgen uses the 32 presence of this function-specific cpp file to set the implementation status 33 shown in the generated rst docs rendered as html for display at 34 <libc.llvm.org>. 35 """ 36 37 def __init__(self, header_name: str): 38 """ 39 :param header_name: e.g., ``"threads.h"`` or ``"signal.h"`` 40 """ 41 self.name = header_name 42 self.stem = header_name.rstrip(".h") 43 self.docgen_root = Path(__file__).parent 44 self.libc_root = self.docgen_root.parent.parent 45 self.docgen_yaml = self.docgen_root / Path(header_name).with_suffix(".yaml") 46 self.fns_dir = Path(self.libc_root, "src", self.stem) 47 self.macros_dir = Path(self.libc_root, "include", "llvm-libc-macros") 48 49 def macro_file_exists(self) -> bool: 50 for _ in self.__get_macro_files(): 51 return True 52 53 return False 54 55 def fns_dir_exists(self) -> bool: 56 return self.fns_dir.exists() and self.fns_dir.is_dir() 57 58 def implements_fn(self, fn_name: str) -> bool: 59 for _ in self.fns_dir.glob(f"**/{fn_name}.cpp"): 60 return True 61 62 return False 63 64 def implements_macro(self, m_name: str) -> bool: 65 """ 66 Some macro files are in, e.g., 67 ``$LLVM_PROJECT_ROOT/libc/include/llvm-libc-macros/fenv-macros.h``, 68 but others are in subdirectories, e.g., ``signal.h`` has the macro 69 definitions in 70 ``$LLVM_PROJECT_ROOT/libc/include/llvm-libc-macros/linux/signal-macros.h``. 71 72 :param m_name: name of macro, e.g., ``FE_ALL_EXCEPT`` 73 """ 74 for f in self.__get_macro_files(): 75 if f"#define {m_name}" in f.read_text(): 76 return True 77 78 return False 79 80 def __get_macro_files(self) -> Generator[Path, None, None]: 81 """ 82 This function uses a glob on, e.g., ``"**/fcntl.macros.h"`` because the 83 macro file might be located in a subdirectory: 84 libc/include/llvm-libc-macros/fcntl-macros.h 85 libc/include/llvm-libc-macros/linux/fcntl-macros.h 86 87 When a header would be nested in a dir (such as arpa/, sys/, etc) we 88 instead use a hyphen in the name. 89 libc/include/llvm-libc-macros/sys-mman-macros.h 90 """ 91 stem = self.stem.replace("/", "-") 92 return self.macros_dir.glob(f"**/{stem}-macros.h") 93