From 4148aa63e2285ec7198c67d9b231b9097076ba8e Mon Sep 17 00:00:00 2001 From: Joseph Huber Date: Wed, 27 Nov 2024 20:14:26 -0600 Subject: [PATCH] [Reland][Runtimes] Merge 'compile_commands.json' files from runtimes build (#116303) Summary: When building a project in a runtime mode, the compilation database is a separate CMake invocation. So its `compile_commands.json` file will be placed elsewhere in the `runtimes/runtime-bins` directory. This is somewhat annoying for ongoing development when a runtimes build is necessary. This patch adds some CMake magic to merge the two files. Fixed issue w/ standalone runtimes build by checking if the LLVM src and CMake src are the same. --- llvm/utils/merge-json.py | 48 ++++++++++++++++++++++++++++++++++++++++ runtimes/CMakeLists.txt | 18 +++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 llvm/utils/merge-json.py diff --git a/llvm/utils/merge-json.py b/llvm/utils/merge-json.py new file mode 100644 index 000000000000000..bf0d651fe5f52b8 --- /dev/null +++ b/llvm/utils/merge-json.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +"""A command line utility to merge two JSON files. + +This is a python program that merges two JSON files into a single one. The +intended use for this is to combine generated 'compile_commands.json' files +created by CMake when performing an LLVM runtime build. +""" + +import argparse +import json +import sys + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "-o", + type=str, + help="The output file to write JSON data to", + default=None, + nargs="?", + ) + parser.add_argument( + "json_files", type=str, nargs="+", help="Input JSON files to merge" + ) + args = parser.parse_args() + + merged_data = [] + + for json_file in args.json_files: + try: + with open(json_file, "r") as f: + data = json.load(f) + merged_data.extend(data) + except (IOError, json.JSONDecodeError) as e: + print("Failed to parse {json_file}: {e}", file=sys.stderr) + continue + + # Deduplicate by converting each entry to a tuple of sorted key-value pairs + unique_data = list({json.dumps(entry, sort_keys=True) for entry in merged_data}) + unique_data = [json.loads(entry) for entry in unique_data] + + with open(args.o, "w") as f: + json.dump(unique_data, f, indent=2) + + +if __name__ == "__main__": + main() diff --git a/runtimes/CMakeLists.txt b/runtimes/CMakeLists.txt index ef8ec171a1657da..4a6b317a03f660f 100644 --- a/runtimes/CMakeLists.txt +++ b/runtimes/CMakeLists.txt @@ -346,3 +346,21 @@ if(SUB_COMPONENTS) ${CMAKE_CURRENT_BINARY_DIR}/runtimes/Components.cmake) endif() endif() + +# If the user requested 'compile_commands.json' we merge the generated JSON from +# the created directories. +if(CMAKE_EXPORT_COMPILE_COMMANDS AND NOT ("${LLVM_BINARY_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")) + # Make a dependency so that we don't error if the file gets deleted somehow. + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/compile_commands.json + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_BINARY_DIR}/compile_commands.json) + + file(TO_NATIVE_PATH "${LLVM_MAIN_SRC_DIR}/utils/merge-json.py" MERGE_JSON_PATH) + add_custom_command(OUTPUT ${LLVM_BINARY_DIR}/compile_commands.json + COMMAND ${CMAKE_COMMAND} -E touch ${LLVM_BINARY_DIR}/compile_commands.json + COMMAND ${Python3_EXECUTABLE} ${MERGE_JSON_PATH} + ${LLVM_BINARY_DIR}/compile_commands.json + ${CMAKE_BINARY_DIR}/compile_commands.json + -o ${LLVM_BINARY_DIR}/compile_commands.json + DEPENDS ${CMAKE_BINARY_DIR}/compile_commands.json) + add_custom_target(merge_runtime_commands ALL DEPENDS ${LLVM_BINARY_DIR}/compile_commands.json) +endif()