Skip to content

Commit 230d672

Browse files
authored
Only run IWYU on affected files + minor get_affected_files.py refactor (CleverRaven#79690)
* Refactor get_affected_files.py slightly Changes: - It now uses argparse for argument parsing, making its configuration somewhat more flexible - It can determine the list of changed files on its own, and does not need to have it passed explicitly (used for local testing, not so much for CI) - It sorts the output, putting the directly changed files (if any) at the top of the list. The hope is that this decreases the feedback latency ever so slightly. * Only run IWYU on potentially affected files drive-by change: remove leftover skip-duplicate check from IWYU job it's not doing nothing other than hard-failing the job on draft PRs. * Forgor that get_affected_files needs to know includes, whoops
1 parent e577b68 commit 230d672

File tree

3 files changed

+83
-22
lines changed

3 files changed

+83
-22
lines changed

.github/workflows/iwyu.yml

+35-5
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ jobs:
2525
COMPILER: clang++-19
2626
steps:
2727
- name: install LLVM 19
28-
if: ${{ needs.skip-duplicates.outputs.should_skip != 'true' && github.event_name != 'pull_request' || github.event.pull_request.draft == false }}
2928
run: |
3029
sudo apt install llvm-19 llvm-19-dev llvm-19-tools clang-19 clang-tidy-19 clang-tools-19 libclang-19-dev
3130
sudo apt install python3-pip ninja-build cmake
@@ -47,24 +46,55 @@ jobs:
4746
echo "IWYU_SRC_DIR=${PWD}/include-what-you-use-src">> "$GITHUB_OUTPUT"
4847
echo "IWYU_BIN_DIR=${PWD}/iwyu-build/bin">> "$GITHUB_OUTPUT"
4948
- uses: ammaraskar/gcc-problem-matcher@master
49+
- name: determine changed files
50+
if: ${{ github.event_name == 'pull_request' }}
51+
uses: actions/github-script@v7
52+
with:
53+
script: |
54+
var fs = require('fs');
55+
const response = await github.paginate(github.rest.pulls.listFiles,
56+
{
57+
owner: context.repo.owner,
58+
repo: context.repo.repo,
59+
pull_number: context.issue.number,
60+
}
61+
);
62+
const files = response.map(x => x.filename);
63+
for (const path of files) {
64+
console.log(path);
65+
}
66+
fs.writeFileSync("Cataclysm-DDA/files_changed", files.join('\n') + '\n');
5067
- name: create CDDA compilation database
5168
run: |
52-
cmake -B Cataclysm-DDA/build \
69+
cd Cataclysm-DDA
70+
cmake -B build \
5371
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
5472
-DCMAKE_CXX_COMPILER=$COMPILER \
5573
-DCMAKE_BUILD_TYPE="Release" \
5674
-DTILES=${TILES:-0} \
5775
-DSOUND=${SOUND:-0} \
5876
-DLOCALIZE=${LOCALIZE:-0} \
59-
Cataclysm-DDA
77+
.
78+
# and include database too, for get_affected_files.py
79+
make includes -j4 --silent TILES=${TILES:-0} SOUND=${SOUND:-0} LOCALIZE=${LOCALIZE:-0}
6080
- name: run the thing
6181
env:
6282
IWYU_SRC_DIR: ${{ steps.build-iwyu.outputs.IWYU_SRC_DIR }}
6383
IWYU_BIN_DIR: ${{ steps.build-iwyu.outputs.IWYU_BIN_DIR }}
6484
run: |
6585
PATH="${PATH}:${IWYU_BIN_DIR}"
66-
6786
cd Cataclysm-DDA
68-
FILES_LIST=$( find src/ tests/ -maxdepth 1 -name '*.cpp' | grep -v -f tools/iwyu/bad_files.txt | sort )
87+
88+
CHECK_ALL=no
89+
if [[ -f ./files_changed ]]; then
90+
CHECK_ALL=$( cat ./files_changed | grep -E -i 'tools/iwyu|github/iwyu.yml|CMakeLists.txt|build-scripts/get_affected_files.py' && echo yes || echo no )
91+
fi
92+
if [[ "${CHECK_ALL}" = "no" ]] && [[ -f ./files_changed ]]; then
93+
FILES_LIST=$( python3 build-scripts/get_affected_files.py --changed-files-list ./files_changed )
94+
else
95+
FILES_LIST=$( find src/ tests/ -maxdepth 1 -name '*.cpp' | sort )
96+
fi
97+
FILES_LIST=$( printf "%s\n" ${FILES_LIST[@]} | grep -v -f tools/iwyu/bad_files.txt )
98+
6999
python ${IWYU_SRC_DIR}/iwyu_tool.py ${FILES_LIST} -p build --output-format clang --jobs 4 -- -Xiwyu "--mapping_file=${PWD}/tools/iwyu/cata.imp" -Xiwyu --cxx17ns -Xiwyu --comment_style=long -Xiwyu --max_line_length=1000 -Xiwyu --error=1
70100

build-scripts/clang-tidy-run.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ else
7575
includes
7676

7777
tidyable_cpp_files="$( \
78-
( test -f ./files_changed && ( build-scripts/get_affected_files.py ./files_changed ) ) || \
78+
( test -f ./files_changed && ( build-scripts/get_affected_files.py --changed-files-list ./files_changed ) ) || \
7979
echo unknown )"
8080

8181
tidyable_cpp_files="$(echo -n "$tidyable_cpp_files" | grep -v third-party || true)"

build-scripts/get_affected_files.py

+47-16
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#!/usr/bin/env python3
22

3+
import argparse
34
import glob
45
import os
56
import sys
7+
import subprocess
68

79

810
class IncludesParser:
@@ -87,9 +89,41 @@ def get_files_affected_by(self, changed_file):
8789
return self.includes_to_sources.get(changed_file, [])
8890

8991

90-
def main(changed_files_file):
91-
parser = IncludesParser()
92+
def get_changed_files_from_list(changed_files_list):
93+
with open(changed_files_list, 'r') as f:
94+
changed_files = [line.strip() for line in f.read().splitlines()]
95+
return changed_files
96+
97+
98+
def get_changed_files_from_git(merge_target):
99+
out = subprocess.run(
100+
["git", "diff", "--merge-base", "--name-only", merge_target],
101+
capture_output=True)
102+
if out.returncode != 0:
103+
print(out.stderr, file=sys.stderr)
104+
raise Exception("git diff failed to run")
105+
changed_files = [line.strip() for line in out.stdout.decode().splitlines()]
106+
return changed_files
107+
108+
109+
def main():
110+
parser = argparse.ArgumentParser()
111+
parser.add_argument("--changed-files-list", help=("Path to a file"
112+
"containing list of changed files, one per line."))
113+
parser.add_argument(
114+
"--merge-target", default="master",
115+
help=("Name of the branch we are intending to merge into."
116+
"Will be used to determine the list of files changed. "
117+
"Ignored if --changed-files-list specified explicilty."))
118+
args = parser.parse_args()
119+
120+
changed_files = None
121+
if args.changed_files_list:
122+
changed_files = get_changed_files_from_list(args.changed_files_list)
123+
else:
124+
changed_files = get_changed_files_from_git(args.merge_target)
92125

126+
parser = IncludesParser()
93127
# Load includes files list.
94128
if not parser.parse_includes_files_from("obj", "src", "."):
95129
return 1
@@ -98,25 +132,22 @@ def main(changed_files_file):
98132
if not parser.parse_includes_files_from("tests/obj", "tests", "tests"):
99133
return 1
100134

101-
changed_files = []
102135
lintable_files = set()
103-
with open(changed_files_file, 'r') as f:
104-
changed_files = f.read().splitlines()
105-
for changed_file in changed_files:
106-
for affected_file in parser.get_files_affected_by(changed_file):
107-
lintable_files.add(affected_file)
136+
for changed_file in changed_files:
137+
for affected_file in parser.get_files_affected_by(changed_file):
138+
lintable_files.add(affected_file)
139+
changed_files = set(changed_files)
108140

109-
for lintable_file in lintable_files:
141+
# Lightly sort the output list so that directly changed files appear first
142+
lintable_files_sorted = []
143+
lintable_files_sorted.extend(sorted(lintable_files & changed_files))
144+
lintable_files_sorted.extend(sorted(lintable_files - changed_files))
145+
146+
for lintable_file in lintable_files_sorted:
110147
print(lintable_file)
111148

112149
return 0
113150

114151

115152
if __name__ == '__main__':
116-
if len(sys.argv) <= 1:
117-
print(
118-
"First argument must be a path to a list of changed files",
119-
file=sys.stderr,
120-
)
121-
sys.exit(1)
122-
sys.exit(main(sys.argv[1]))
153+
sys.exit(main())

0 commit comments

Comments
 (0)