https://github.com/chelcassanova updated https://github.com/llvm/llvm-project/pull/142051
>From 2d63dfe6e4f9671d3c22c319e01782aacee303e4 Mon Sep 17 00:00:00 2001 From: Chelsea Cassanova <chelsea_cassan...@apple.com> Date: Wed, 28 May 2025 15:45:45 -0700 Subject: [PATCH] [lldb][headers] Create Python script to fix up framework headers This commit replaces the shell script that fixes up includes for the LLDB framework with a Python script. This script will also be used when fixing up includes for the LLDBRPC.framework. --- lldb/cmake/modules/LLDBFramework.cmake | 46 ++++--- lldb/scripts/framework-header-fix.py | 126 ++++++++++++++++++ .../Shell/Scripts/Inputs/Main/SBAddress.h | 13 ++ .../Shell/Scripts/Inputs/RPC/RPCSBAddress.h | 9 ++ .../Shell/Scripts/TestFrameworkFixScript.test | 11 ++ .../Scripts/TestFrameworkFixUnifdef.test | 12 ++ .../Scripts/TestRPCFrameworkFixScript.test | 14 ++ 7 files changed, 210 insertions(+), 21 deletions(-) create mode 100755 lldb/scripts/framework-header-fix.py create mode 100644 lldb/test/Shell/Scripts/Inputs/Main/SBAddress.h create mode 100644 lldb/test/Shell/Scripts/Inputs/RPC/RPCSBAddress.h create mode 100644 lldb/test/Shell/Scripts/TestFrameworkFixScript.test create mode 100644 lldb/test/Shell/Scripts/TestFrameworkFixUnifdef.test create mode 100644 lldb/test/Shell/Scripts/TestRPCFrameworkFixScript.test diff --git a/lldb/cmake/modules/LLDBFramework.cmake b/lldb/cmake/modules/LLDBFramework.cmake index 471aeaaad3c0d..f6dd9bb55fea1 100644 --- a/lldb/cmake/modules/LLDBFramework.cmake +++ b/lldb/cmake/modules/LLDBFramework.cmake @@ -68,24 +68,17 @@ if(NOT APPLE_EMBEDDED) ) endif() -# At configuration time, collect headers for the framework bundle and copy them -# into a staging directory. Later we can copy over the entire folder. -file(GLOB public_headers ${LLDB_SOURCE_DIR}/include/lldb/API/*.h) -set(generated_public_headers ${LLDB_OBJ_DIR}/include/lldb/API/SBLanguages.h) -file(GLOB root_public_headers ${LLDB_SOURCE_DIR}/include/lldb/lldb-*.h) -file(GLOB root_private_headers ${LLDB_SOURCE_DIR}/include/lldb/lldb-private*.h) -list(REMOVE_ITEM root_public_headers ${root_private_headers}) - find_program(unifdef_EXECUTABLE unifdef) -set(lldb_header_staging ${CMAKE_CURRENT_BINARY_DIR}/FrameworkHeaders) -foreach(header - ${public_headers} - ${generated_public_headers} - ${root_public_headers}) +# All necessary header files will be staged in the include directory in the build directory, +# so just copy the files from there into the framework's staging directory. +set(lldb_build_dir_header_staging "${CMAKE_BINARY_DIR}/include/lldb") +set(lldb_framework_header_staging "${CMAKE_CURRENT_BINARY_DIR}/FrameworkHeaders") +file(GLOB lldb_build_dir_header_staging_list ${lldb_build_dir_header_staging}/*) +foreach(header ${lldb_build_dir_header_staging_list}) get_filename_component(basename ${header} NAME) - set(staged_header ${lldb_header_staging}/${basename}) + set(staged_header ${lldb_framework_header_staging}/${basename}) if(unifdef_EXECUTABLE) # unifdef returns 0 when the file is unchanged and 1 if something was changed. @@ -107,15 +100,26 @@ endforeach() # Wrap output in a target, so lldb-framework can depend on it. add_custom_target(liblldb-resource-headers DEPENDS lldb-sbapi-dwarf-enums ${lldb_staged_headers}) set_target_properties(liblldb-resource-headers PROPERTIES FOLDER "LLDB/Resources") + +# We're taking the header files from where they've been staged in the build directory's include folder, +# so create a dependency on the build step that creates that directory. +add_dependencies(liblldb-resource-headers liblldb-header-staging) add_dependencies(liblldb liblldb-resource-headers) -# At build time, copy the staged headers into the framework bundle (and do -# some post-processing in-place). -add_custom_command(TARGET liblldb POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory ${lldb_header_staging} $<TARGET_FILE_DIR:liblldb>/Headers - COMMAND ${LLDB_SOURCE_DIR}/scripts/framework-header-fix.sh $<TARGET_FILE_DIR:liblldb>/Headers ${LLDB_VERSION} - COMMENT "LLDB.framework: copy framework headers" -) +# Take the headers from the staging directory and fix up their includes for the framework. +# Then write them to the output directory. +# Also, run unifdef to remove any specified guards from the header files. +file(GLOB lldb_framework_header_staging_list ${lldb_framework_header_staging}/*) +foreach(header ${lldb_framework_header_staging_list}) + + set(input_header ${header}) + set(output_header $<TARGET_FILE_DIR:liblldb>/Headers/${input_header}) + + add_custom_command(TARGET liblldb POST_BUILD + COMMAND ${LLDB_SOURCE_DIR}/scripts/framework-header-fix.py -f lldb_main -i ${input_header} -o ${output_header} -p ${unifdef_EXECUTABLE} USWIG + COMMENT "LLDB.framework: Fix up and copy framework headers" + ) +endforeach() # Copy vendor-specific headers from clang (without staging). if(NOT APPLE_EMBEDDED) diff --git a/lldb/scripts/framework-header-fix.py b/lldb/scripts/framework-header-fix.py new file mode 100755 index 0000000000000..9e4e5f860a2c0 --- /dev/null +++ b/lldb/scripts/framework-header-fix.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 + +""" +Usage: <path/to/input-directory> <path/to/output-directory> + +This script is used when building LLDB.framework or LLDBRPC.framework. For each framework, local includes are converted to their respective framework includes. + +This script is used in 2 ways: +1. It is used on header files that are copied into LLDB.framework. For these files, local LLDB includes are converted into framework includes, e.g. #include "lldb/API/SBDefines.h" -> #include <LLDB/SBDefines.h>. + +2. It is used on header files for LLDBRPC.framework. For these files, includes of RPC common files will be converted to framework includes, e.g. #include <lldb-rpc/common/RPCCommon.h> -> #include <LLDBRPC/RPCCommon.h>. It will also change local includes to framework includes, e.g. #include "SBAddress.h" -> #include <LLDBRPC/SBAddress.h> +""" + +import argparse +import os +import re +import shutil +import subprocess +import sys + +# Main header regexes +INCLUDE_FILENAME_REGEX = re.compile( + r'#include "lldb/API/(?P<include_filename>.*){0,1}"' +) + +# RPC header regexes +RPC_COMMON_REGEX = re.compile(r"#include <lldb-rpc/common/(?P<include_filename>.*)>") +RPC_INCLUDE_FILENAME_REGEX = re.compile(r'#include "(?P<include_filename>.*)"') + + +def modify_rpc_includes(input_file_path, output_file_path): + with open(input_file_path, "r") as input_file: + lines = input_file.readlines() + file_buffer = "".join(lines) + with open(output_file_path, "w") as output_file: + # Local includes must be changed to RPC framework level includes. + # e.g. #include "SBDefines.h" -> #include <LLDBRPC/SBDefines.h> + # Also, RPC common code includes must change to RPC framework level includes. + # e.g. #include "lldb-rpc/common/RPCPublic.h" -> #include <LLDBRPC/RPCPublic.h> + rpc_common_matches = RPC_COMMON_REGEX.finditer(file_buffer) + rpc_include_filename_matches = RPC_INCLUDE_FILENAME_REGEX.finditer( + file_buffer + ) + for match in rpc_common_matches: + file_buffer = re.sub( + match.group(), + r"#include <LLDBRPC/" + match.group("include_filename") + ">", + file_buffer, + ) + for match in rpc_include_filename_matches: + file_buffer = re.sub( + match.group(), + r"#include <LLDBRPC/" + match.group("include_filename") + ">", + file_buffer, + ) + output_file.write(file_buffer) + + +def modify_main_includes(input_file_path, output_file_path): + with open(input_file_path, "r") as input_file: + lines = input_file.readlines() + file_buffer = "".join(lines) + with open(output_file_path, "w") as output_file: + # Local includes must be changed to framework level includes. + # e.g. #include "lldb/API/SBDefines.h" -> #include <LLDB/SBDefines.h> + regex_matches = INCLUDE_FILENAME_REGEX.finditer(file_buffer) + for match in regex_matches: + file_buffer = re.sub( + match.group(), + r"#include <LLDB/" + match.group("include_filename") + ">", + file_buffer, + ) + output_file.write(file_buffer) + + +def remove_guards(output_file_path, unifdef_path, unifdef_guards): + # The unifdef path should be passed in from CMake. If it wasn't there in CMake or is incorrect, + # find it using shutil. If shutil can't find it, then exit. + if not shutil.which(unifdef_path): + unifdef_path = shutil.which("unifdef") + if not unifdef_path: + print( + "Unable to find unifdef executable. Guards will not be removed from input files. Exiting..." + ) + sys.exit(1) + + subprocess_command = ( + [unifdef_path, "-o", output_file_path] + unifdef_guards + [output_file_path] + ) + subprocess.run(subprocess_command) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-f", "--framework", choices=["lldb_main", "lldb_rpc"]) + parser.add_argument("-i", "--input_file") + parser.add_argument("-o", "--output_file") + parser.add_argument("-p", "--unifdef_path") + parser.add_argument( + "unifdef_guards", + nargs="+", + type=str, + help="Guards to be removed with unifdef. These must be specified in the same way as they would be when passed directly into unifdef.", + ) + args = parser.parse_args() + input_file_path = str(args.input_file) + output_file_path = str(args.output_file) + framework_version = args.framework + unifdef_path = str(args.unifdef_path) + # Prepend dashes to the list of guards passed in from the command line. + # unifdef takes the guards to remove as arguments in their own right (e.g. -USWIG) + # but passing them in with dashes for this script causes argparse to think that they're + # arguments in and of themself, so they need to passed in without dashes. + unifdef_guards = ["-" + guard for guard in args.unifdef_guards] + + if framework_version == "lldb_main": + modify_main_includes(input_file_path, output_file_path) + if framework_version == "lldb_rpc": + modify_rpc_includes(input_file_path, output_file_path) + # After the incldues have been modified, run unifdef on the headers to remove any guards + # specified at the command line. + remove_guards(output_file_path, unifdef_path, unifdef_guards) + + +if __name__ == "__main__": + main() diff --git a/lldb/test/Shell/Scripts/Inputs/Main/SBAddress.h b/lldb/test/Shell/Scripts/Inputs/Main/SBAddress.h new file mode 100644 index 0000000000000..fecc69687cd74 --- /dev/null +++ b/lldb/test/Shell/Scripts/Inputs/Main/SBAddress.h @@ -0,0 +1,13 @@ +// This is a truncated version of an SB API file +// used to test framework-header-fix.py to make sure the includes are correctly fixed +// up for the LLDB.framework. + +// Local includes must be changed to framework level includes. +// e.g. #include "lldb/API/SBDefines.h" -> #include <LLDB/SBDefines.h> +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBModule.h" + +// Any include guards specified at the command line must be removed. +#ifndef SWIG +int a = 10 +#endif diff --git a/lldb/test/Shell/Scripts/Inputs/RPC/RPCSBAddress.h b/lldb/test/Shell/Scripts/Inputs/RPC/RPCSBAddress.h new file mode 100644 index 0000000000000..556afa38a9225 --- /dev/null +++ b/lldb/test/Shell/Scripts/Inputs/RPC/RPCSBAddress.h @@ -0,0 +1,9 @@ +// This is a truncated version of an SB API file generated by lldb-rpc-gen +// used to test framework-header-fix.py to make sure the includes are correctly fixed +// up for the LLDBRPC.framework. + +// Local includes must be changed to framework level includes. +// e.g. #include "lldb/API/SBDefines.h" -> #include <LLDB/SBDefines.h> +#include "LLDBRPC.h" +#include "SBDefines.h" +#include <lldb-rpc/common/RPCPublic.h> diff --git a/lldb/test/Shell/Scripts/TestFrameworkFixScript.test b/lldb/test/Shell/Scripts/TestFrameworkFixScript.test new file mode 100644 index 0000000000000..e90c3bdfc5adb --- /dev/null +++ b/lldb/test/Shell/Scripts/TestFrameworkFixScript.test @@ -0,0 +1,11 @@ +# Create a temp dir for output and run the framework fix script on the truncated version of SBAddress.h in the inputs dir. +RUN: mkdir -p %t/Outputs +RUN: %python %p/../../../scripts/framework-header-fix.py -f lldb_main -i %p/Inputs/Main/SBAddress.h -o %t/Outputs/SBAddress.h -p /usr/bin/unifdef USWIG + +# Check the output +RUN: cat %t/Outputs/SBAddress.h | FileCheck %s + +# Local includes must be changed to framework level includes. +# e.g. #include "lldb/API/SBDefines.h" -> #include <LLDB/SBDefines.h> +CHECK: #include <LLDB/SBDefines.h> +CHECK: #include <LLDB/SBModule.h> diff --git a/lldb/test/Shell/Scripts/TestFrameworkFixUnifdef.test b/lldb/test/Shell/Scripts/TestFrameworkFixUnifdef.test new file mode 100644 index 0000000000000..a7e82d2f3640c --- /dev/null +++ b/lldb/test/Shell/Scripts/TestFrameworkFixUnifdef.test @@ -0,0 +1,12 @@ +# REQUIRES: system-darwin +# Create a temp dir for output and run the framework fix script on the truncated version of SBAddress.h in the inputs dir. +RUN: mkdir -p %t/Outputs +RUN: %python %p/../../../scripts/framework-header-fix.py -f lldb_main -i %p/Inputs/Main/SBAddress.h -o %t/Outputs/SBAddress.h -p /usr/bin/unifdef USWIG + +# Check the output +RUN: cat %t/Outputs/SBAddress.h | FileCheck %s + +# Any include guards specified at the command line must be removed. +CHECK-NOT: #ifndef SWIG +CHECK: int a = 10 +CHECK-NOT: #endif diff --git a/lldb/test/Shell/Scripts/TestRPCFrameworkFixScript.test b/lldb/test/Shell/Scripts/TestRPCFrameworkFixScript.test new file mode 100644 index 0000000000000..8ba03a8c2afa8 --- /dev/null +++ b/lldb/test/Shell/Scripts/TestRPCFrameworkFixScript.test @@ -0,0 +1,14 @@ +# Create a temp dir for output and run the framework fix script on the truncated version of SBAddress.h in the inputs dir. +RUN: mkdir -p %t/Outputs +RUN: %python %p/../../../scripts/framework-header-fix.py -f lldb_rpc -i %p/Inputs/Main/RPCSBAddress.h -o %t/Outputs/RPCSBAddress.h -p /usr/bin/unifdef USWIG + +# Check the output +RUN: cat %t/Outputs/RPCSBAddress.h | FileCheck %s + +# Local includes must be changed to RPC framework level includes. +# e.g. #include "SBDefines.h" -> #include <LLDBRPC/SBDefines.h> +# Also, RPC common code includes must change to RPC framework level includes. +# e.g. #include "lldb-rpc/common/RPCPublic.h" -> #include <LLDBRPC/RPCPublic.h> +CHECK: #include <LLDBRPC/RPCPublic.h> +CHECK: #include <LLDBRPC/SBDefines.h> +CHECK: #include <LLDBRPC/LLDBRPC.h> _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits