-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add script to compare core bazel and CMake headers (#379)
This gives us the most important portion of #255. In fact, it's even better than what we originally suggested, which was a blocking checkbox in code review. This solution uses `bazel query` to get the authoritative set of headers included in our main targets, `//au` and `//au:io`. It compares them to the CMake target `au`, which encompasses both of these targets. The script returns an exit code of `0` if the headers are an exact match, and `1` otherwise. If any files differ, it prints out the list of every file that is found in only bazel or only CMake. A good way to test this is to run: ```sh check-cmake-headers && echo $? ``` This should print `0` on the last line. Then, if we edit `au/code/au/CMakeLists.txt`, and _change the name_ of one of the files (say, prepend `asdf`), we should see the discrepancy printed out, and the printed exit code should change to `1`. A future PR will add a GitHub action to call this script and gate PRs on its result.
- Loading branch information
Showing
3 changed files
with
138 additions
and
0 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
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,128 @@ | ||
#!/usr/bin/python3 | ||
# Copyright 2025 Aurora Operations, Inc. | ||
# | ||
# 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. | ||
|
||
import os | ||
import subprocess | ||
import sys | ||
|
||
|
||
def main(argv=None): | ||
cmake_headers = _get_cmake_headers() | ||
bazel_headers = _get_bazel_headers() | ||
ok = _check_equality_and_print_any_differences( | ||
cmake_headers=cmake_headers, bazel_headers=bazel_headers | ||
) | ||
return 0 if ok else 1 | ||
|
||
|
||
def _get_cmake_headers(): | ||
# Ensure that CMake has been set up appropriately. Command taken from: | ||
# <https://aurora-opensource.github.io/au/main/install/#__tabbed_1_2> | ||
subprocess.run( | ||
[ | ||
"cmake", | ||
"-S", | ||
".", | ||
"-B", | ||
"cmake/build", | ||
"-DCMAKE_VERIFY_INTERFACE_HEADER_SETS=TRUE", | ||
], | ||
check=True, | ||
) | ||
|
||
# Run the actual CMake custom target that lists the relevant headers. | ||
raw_output = subprocess.run( | ||
["cmake", "--build", "cmake/build", "--target", "print_au_files"], | ||
stdout=subprocess.PIPE, | ||
check=True, | ||
) | ||
|
||
# De-duplicate headers and remove common prefix. | ||
return _as_set_relative_to_common_prefix( | ||
[ | ||
path | ||
for line in raw_output.stdout.decode().splitlines() | ||
for path in _get_cmake_headers_from_line(line) | ||
] | ||
) | ||
|
||
|
||
def _get_bazel_headers(): | ||
raw_output = subprocess.run( | ||
[ | ||
"bazel", | ||
"query", | ||
"kind(source, filter(//au, deps(//au) union deps(//au:io)))", | ||
], | ||
stdout=subprocess.PIPE, | ||
check=True, | ||
) | ||
paths = raw_output.stdout.decode().splitlines() | ||
return _as_set_relative_to_common_prefix(paths) | ||
|
||
|
||
def _check_equality_and_print_any_differences(cmake_headers, bazel_headers): | ||
cmake = (cmake_headers, "CMake") | ||
bazel = (bazel_headers, "bazel") | ||
result = (cmake_headers == bazel_headers) | ||
|
||
if not result: | ||
print("\n** ERROR **: Headers in CMake and bazel are not identical.") | ||
_print_any_only_in_first(first=cmake, second=bazel) | ||
_print_any_only_in_first(first=bazel, second=cmake) | ||
|
||
return result | ||
|
||
|
||
def _as_set_relative_to_common_prefix(paths): | ||
"""Turn the input into a set, and remove the common prefix.""" | ||
p = os.path.commonprefix(paths) | ||
return set([path.removeprefix(p) for path in paths]) | ||
|
||
|
||
def _get_cmake_headers_from_line(line): | ||
""" | ||
Extract header names from one line of output from CMake's `print_au_files` target. | ||
This custom target outputs relevant headers only on a line that begins with the special token | ||
"PRESERVE:", and the headers are the rest of the tokens. | ||
""" | ||
tokens = line.split() | ||
if not tokens: | ||
return [] | ||
first, *rest = tokens | ||
return rest if first == "PRESERVE:" else [] | ||
|
||
|
||
def _print_any_only_in_first(first, second): | ||
""" | ||
Print any headers that are in the first group, but not the second. | ||
Args: | ||
first: A tuple of (headers, name) where `headers` is a set of headers and `name` is a string. | ||
second: A tuple of (headers, name) where `headers` is a set of headers and `name` is a string. | ||
""" | ||
first_headers, first_name = first | ||
second_headers, second_name = second | ||
|
||
extras = first_headers - second_headers | ||
if extras: | ||
print(f"\nHeaders in {first_name} but not {second_name}:") | ||
for header in extras: | ||
print(f" {header}") | ||
|
||
|
||
if __name__ == "__main__": | ||
sys.exit(main()) |