-
Notifications
You must be signed in to change notification settings - Fork 918
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This PR adds [`include-what-you-use`](https://github.com/include-what-you-use/include-what-you-use/) to the CI job running clang-tidy. Like clang-tidy, IWYU runs via CMake integration and only runs on cpp files, not cu files. This should help us shrink binaries and reduce compilation times in cases where headers are being included unnecessarily, and it helps keep our include lists clean. The IWYU suggestions for additions are quite noisy and the team determined this to be unnecessary, so this PR instead post-filters the outputs to only show the removals. The final suggestions are uploaded to a file that is uploaded to the GHA page so that it can be downloaded, inspected, and easily applied locally. Resolves #581. Authors: - Vyas Ramasubramani (https://github.com/vyasr) Approvers: - Mark Harris (https://github.com/harrism) - David Wendt (https://github.com/davidwendt) - Yunsong Wang (https://github.com/PointKernel) - James Lamb (https://github.com/jameslamb) - Karthikeyan (https://github.com/karthikeyann) URL: #17078
- Loading branch information
Showing
8 changed files
with
271 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,7 +63,8 @@ jobs: | |
branch: ${{ inputs.branch }} | ||
date: ${{ inputs.date }} | ||
sha: ${{ inputs.sha }} | ||
run_script: "ci/clang_tidy.sh" | ||
run_script: "ci/cpp_linters.sh" | ||
file_to_upload: iwyu_results.txt | ||
conda-python-cudf-tests: | ||
secrets: inherit | ||
uses: rapidsai/shared-workflows/.github/workflows/[email protected] | ||
|
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
#!/bin/bash | ||
# Copyright (c) 2024, NVIDIA CORPORATION. | ||
|
||
set -euo pipefail | ||
|
||
rapids-logger "Create checks conda environment" | ||
. /opt/conda/etc/profile.d/conda.sh | ||
|
||
ENV_YAML_DIR="$(mktemp -d)" | ||
|
||
rapids-dependency-file-generator \ | ||
--output conda \ | ||
--file-key clang_tidy \ | ||
--matrix "cuda=${RAPIDS_CUDA_VERSION%.*};arch=$(arch);py=${RAPIDS_PY_VERSION}" | tee "${ENV_YAML_DIR}/env.yaml" | ||
|
||
rapids-mamba-retry env create --yes -f "${ENV_YAML_DIR}/env.yaml" -n clang_tidy | ||
|
||
# Temporarily allow unbound variables for conda activation. | ||
set +u | ||
conda activate clang_tidy | ||
set -u | ||
|
||
RAPIDS_VERSION_MAJOR_MINOR="$(rapids-version-major-minor)" | ||
|
||
source rapids-configure-sccache | ||
|
||
# TODO: For testing purposes, clone and build IWYU. We can switch to a release | ||
# once a clang 19-compatible version is available, which should be soon | ||
# (https://github.com/include-what-you-use/include-what-you-use/issues/1641). | ||
git clone --depth 1 https://github.com/include-what-you-use/include-what-you-use.git | ||
pushd include-what-you-use | ||
# IWYU's CMake build uses some Python scripts that assume that the cwd is | ||
# importable, so support that legacy behavior. | ||
export PYTHONPATH=${PWD}:${PYTHONPATH:-} | ||
cmake -S . -B build -GNinja --install-prefix=${CONDA_PREFIX} | ||
cmake --build build | ||
cmake --install build | ||
popd | ||
|
||
# Run the build via CMake, which will run clang-tidy when CUDF_STATIC_LINTERS is enabled. | ||
cmake -S cpp -B cpp/build -DCMAKE_BUILD_TYPE=Release -DCUDF_STATIC_LINTERS=ON -GNinja | ||
cmake --build cpp/build 2>&1 | python cpp/scripts/parse_iwyu_output.py | ||
|
||
# Remove invalid components of the path for local usage. The path below is | ||
# valid in the CI due to where the project is cloned, but presumably the fixes | ||
# will be applied locally from inside a clone of cudf. | ||
sed -i 's/\/__w\/cudf\/cudf\///' iwyu_results.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
# Copyright (c) 2024, NVIDIA CORPORATION. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
|
||
"""Helper script to modify IWYU output to only include removals. | ||
Lines that are not from include-what-you-use are removed from the output. | ||
""" | ||
|
||
import argparse | ||
import re | ||
from enum import Enum | ||
|
||
|
||
class Mode(Enum): | ||
NORMAL = 0 | ||
ADD = 1 | ||
REMOVE = 2 | ||
FULL_INCLUDE_LIST = 3 | ||
|
||
|
||
def extract_include_file(include_line): | ||
"""Extract the core file path from an #include directive.""" | ||
match = re.search(r'#include\s+[<"]([^">]+)[">]', include_line) | ||
if match: | ||
return match.group(1) | ||
return None | ||
|
||
|
||
def parse_output(input_stream): | ||
include_modifications = {} | ||
current_file = None | ||
mode = Mode.NORMAL | ||
|
||
for line in input_stream: | ||
if match := re.match(r"(\/\S+) should add these lines:", line): | ||
current_file = match.group(1) | ||
include_modifications.setdefault( | ||
current_file, | ||
{ | ||
"add_includes": [], | ||
"remove_includes": [], | ||
"full_include_list": [], | ||
}, | ||
) | ||
mode = Mode.ADD | ||
elif match := re.match(r"(\/\S+) should remove these lines:", line): | ||
mode = Mode.REMOVE | ||
elif match := re.match(r"The full include-list for (\/\S+):", line): | ||
mode = Mode.FULL_INCLUDE_LIST | ||
elif line.strip() == "---": | ||
current_file = None | ||
mode = Mode.NORMAL | ||
else: | ||
if current_file: | ||
if mode == Mode.ADD: | ||
include_modifications[current_file]["add_includes"].append( | ||
line.strip() | ||
) | ||
elif mode == Mode.REMOVE: | ||
include_modifications[current_file][ | ||
"remove_includes" | ||
].append(line.strip()) | ||
elif mode == Mode.FULL_INCLUDE_LIST: | ||
include_modifications[current_file][ | ||
"full_include_list" | ||
].append(line.strip()) | ||
else: | ||
if ( | ||
line.strip() | ||
and "include-what-you-use reported diagnostics" not in line | ||
and "In file included from" not in line | ||
and "has correct #includes/fwd-decls" not in line | ||
): | ||
print(line, end="") | ||
|
||
return include_modifications | ||
|
||
|
||
def post_process_includes(include_modifications): | ||
"""Deduplicate and remove redundant entries from add and remove includes.""" | ||
for mods in include_modifications.values(): | ||
# Deduplicate add_includes and remove_includes | ||
mods["add_includes"] = list(set(mods["add_includes"])) | ||
mods["remove_includes"] = list(set(mods["remove_includes"])) | ||
|
||
# Extract file paths from add_includes and remove_includes | ||
add_files = { | ||
extract_include_file(line) for line in mods["add_includes"] | ||
} | ||
remove_files = { | ||
extract_include_file(line) for line in mods["remove_includes"] | ||
} | ||
|
||
# Remove entries that exist in both add_includes and remove_includes | ||
common_files = add_files & remove_files | ||
mods["add_includes"] = [ | ||
line | ||
for line in mods["add_includes"] | ||
if extract_include_file(line) not in common_files | ||
] | ||
mods["remove_includes"] = [ | ||
line | ||
for line in mods["remove_includes"] | ||
if extract_include_file(line) not in common_files | ||
] | ||
|
||
# Remove entries that exist in add_includes from full_include_list | ||
mods["full_include_list"] = [ | ||
include | ||
for include in mods["full_include_list"] | ||
if extract_include_file(include) not in add_files | ||
] | ||
|
||
|
||
def write_output(include_modifications, output_stream): | ||
for filename, mods in include_modifications.items(): | ||
if mods["remove_includes"]: | ||
# IWYU requires all sections to exist, so we write out this header even | ||
# though we never write out any actual additions. | ||
output_stream.write(f"{filename} should add these lines:\n\n") | ||
|
||
output_stream.write(f"{filename} should remove these lines:\n") | ||
for line in mods["remove_includes"]: | ||
output_stream.write(line + "\n") | ||
output_stream.write("\n") | ||
|
||
output_stream.write(f"The full include-list for {filename}:\n") | ||
for line in mods["full_include_list"]: | ||
output_stream.write(line + "\n") | ||
output_stream.write("---\n") | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser( | ||
description="Process include modifications from a build output log." | ||
) | ||
parser.add_argument( | ||
"input", | ||
nargs="?", | ||
type=argparse.FileType("r"), | ||
default="-", | ||
help="Input file to read (default: stdin)", | ||
) | ||
parser.add_argument( | ||
"--output", | ||
type=argparse.FileType("w"), | ||
default="iwyu_results.txt", | ||
help="Output file to write (default: iwyu_output.txt)", | ||
) | ||
args = parser.parse_args() | ||
|
||
include_modifications = parse_output(args.input) | ||
post_process_includes(include_modifications) | ||
write_output(include_modifications, args.output) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters