blob: 91194d5ea1bcdbd4102f5c3aa099624316dafb83 [file] [log] [blame] [edit]
import collections.abc
import pathlib
import shlex
import subprocess
from ModuleVerifierLanguage import Language, LanguageStandard
from ModuleVerifierTypes import (
ModuleVerifierInputs,
ModuleVerifierTargetSet,
)
class VerifyModuleTask:
"""
A type used to verify a module for correctness by compiling a test framework from a set of
inputs with modules enabled.
The test framework should contain include statements for each exposed header in the target
library product. If any of the exposed headers are missing from the library module map, or
if there are any other module-related issues in any header, running this task will indicate
the verification has failed.
"""
output_path: pathlib.Path
target: str
other_c_flags: list[str]
other_verifier_flags: list[str]
other_cxx_flags: list[str]
language: Language
standard: LanguageStandard
sys_root: str
framework_search_paths: list[str]
source_file: str
def __init__(
self,
target_set: ModuleVerifierTargetSet,
inputs: ModuleVerifierInputs,
environment: collections.abc.Mapping[str, str],
):
"""
Creates a new VerifyModuleTask whose properties are derived using the specified target set, inputs, and environment.
The environment variables this routine depends on are:
```
PRODUCT_NAME
TARGET_TEMP_DIR
OTHER_CFLAGS
OTHER_MODULE_VERIFIER_FLAGS
OTHER_CPLUSPLUSFLAGS
SDKROOT
FRAMEWORK_SEARCH_PATHS
```
Args:
target_set: The target set used to derive the language, language standards, and target properties.
inputs: The set of input paths used to verify the module.
environment: Specifies the configuration options to use for the verification command.
"""
product_name = environment["PRODUCT_NAME"]
target_temp_dir = environment["TARGET_TEMP_DIR"]
self.language = target_set.language
self.standard = target_set.standard
self.output_path = (
pathlib.Path(target_temp_dir)
/ "VerifyModule"
/ product_name
/ target_set.path_component()
)
self.target = target_set.target.value()
self.source_file = str(inputs.main)
self.other_c_flags = shlex.split(environment.get("OTHER_CFLAGS", ""))
self.other_c_flags += [
f"-Wsystem-headers-in-module={product_name}",
"-Werror=non-modular-include-in-module",
"-Werror=non-modular-include-in-framework-module",
"-Werror=incomplete-umbrella",
"-Werror=quoted-include-in-framework-header",
"-Werror=atimport-in-framework-header",
"-Werror=framework-include-private-from-public",
"-Werror=incomplete-framework-module-declaration",
"-Wundef-prefix=TARGET_OS",
"-Werror=undef-prefix",
"-Werror=module-import-in-extern-c",
"-ferror-limit=0",
]
self.other_verifier_flags = shlex.split(
environment.get("OTHER_MODULE_VERIFIER_FLAGS", "")
)
if len(self.other_verifier_flags) > 1:
self.other_c_flags += self.other_verifier_flags[1:]
self.other_cxx_flags = shlex.split(environment.get("OTHER_CPLUSPLUSFLAGS", ""))
self.other_cxx_flags += ["-fcxx-modules"]
self.other_cxx_flags += self.other_c_flags
self.sys_root = environment["SDKROOT"]
self.framework_search_paths = shlex.split(
environment.get("FRAMEWORK_SEARCH_PATHS", "")
)
self.framework_search_paths += [str(inputs.directory)]
def create_command(self) -> list[str]:
"""
Creates the set of command arguments described by this task to be evaluated.
Returns:
A set of arguments describing a clang invocation, suitable to pass to a process to be run.
"""
arguments: list[str] = []
arguments += ["-x", self.language.value]
arguments += ["-target", self.target]
arguments += [f"-std={self.standard.value}"]
arguments += [
"-fmodules",
"-fsyntax-only",
"-fdiagnostics-show-note-include-stack",
]
if (
self.language == Language.C_PLUS_PLUS
or self.language == Language.OBJECTIVE_C_PLUS_PLUS
):
arguments += self.other_cxx_flags
else:
arguments += self.other_c_flags
arguments += ["-isysroot", self.sys_root]
for framework_search_path in self.framework_search_paths:
arguments += [f"-F{framework_search_path}"]
return ["xcrun", "clang"] + arguments + [self.source_file]
def perform_action(self) -> subprocess.CompletedProcess[str]:
"""
Verifies the module by running a clang command produced by this task.
Returns:
The result of running the command. If the return code of the result is `0`, the module succeeded in verification.
Otherwise, the module failed verification, and `stderr` describes the errors.
"""
command = self.create_command()
return subprocess.run(command, stderr=subprocess.PIPE, text=True)