1- # proxy_utils.cmake — reusable helpers for DLL proxy generation
1+ # proxy_utils.cmake — DLL proxy generation helpers (requires CMake 3.25+)
22#
33# Provides:
4- # generate_proxy_def(input_dll output_var)
5- # Parses exports from a Windows DLL using llvm-objdump/objdump and
6- # writes a .def module-definition file that forwards every export
7- # through an "original." prefix.
8- #
4+ # generate_proxy_def(<input_dll> <output_var>)
95# add_bass_proxy(VERSION <v> BASS_DLL <path> SDK_TARGET <tgt>)
10- # Creates a SHARED library bass_proxy_<v> in the caller's directory
11- # scope. Links <SDK_TARGET> (usually bass_proxy_sdk). The .def is
12- # generated from the version-specific bass.dll so each proxy matches
13- # its runtime export table.
14- #
15- # Output lands in CMAKE_CURRENT_BINARY_DIR (unique per 2_XX/), so
16- # multiple versions never collide.
176
187include_guard (GLOBAL )
198
20- # ─── generate_proxy_def ───────────────────────────────────────────────────────
21- # Parse the export table of a PE DLL and emit a .def file that forwards every
22- # exported symbol through an "original." prefix, preserving ordinals.
23- #
24- # generate_proxy_def(
25- # input_dll # absolute path to the bass.dll for this version
26- # output_var # name of variable to hold the generated .def path
27- # )
28- #
29- # The .def is written to CMAKE_CURRENT_BINARY_DIR/<dll_stem>.def.
30- # A directory-level configure dependency on input_dll is registered so that
31- # the build system re-generates if the DLL changes.
32- function (generate_proxy_def input_dll output_var )
33- find_program (objdump_bin NAMES llvm-objdump objdump
34- HINTS "${CMAKE_SOURCE_DIR } /llvm_mingw/bin" REQUIRED )
9+ find_program (
10+ OBJDUMP_EXECUTABLE
11+ NAMES llvm-objdump objdump
12+ HINTS "${CMAKE_SOURCE_DIR } /llvm_mingw/bin" REQUIRED
13+ DOC "PE object dump utility for export parsing" )
3514
15+ function (generate_proxy_def input_dll output_var )
3616 cmake_path (
3717 GET
3818 input_dll
3919 STEM
4020 LAST_ONLY
4121 dll_stem )
4222 set (def_file "${CMAKE_CURRENT_BINARY_DIR } /${dll_stem} .def" )
43-
4423 set_property (DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
4524 "${input_dll} " )
4625
47- message (STATUS "parsing exports '${objdump_bin} ': ${input_dll} " )
48-
26+ message (STATUS "Parsing exports: ${input_dll} " )
4927 execute_process (
50- COMMAND "${objdump_bin} " -p "${input_dll} "
51- OUTPUT_VARIABLE dump_output
52- OUTPUT_STRIP_TRAILING_WHITESPACE
53- COMMAND_ERROR_IS_FATAL
54- ANY
55- COMMAND_ECHO
56- STDOUT )
28+ COMMAND "${OBJDUMP_EXECUTABLE} " -p "${input_dll} "
29+ OUTPUT_VARIABLE dump_output OUTPUT_STRIP_TRAILING_WHITESPACE
30+ COMMAND_ERROR_IS_FATAL ANY )
5731
5832 string (
5933 REGEX MATCHALL
6034 "[0-9]+[ \t ]+0x[0-9a-fA-F]+[ \t ]+[^ \t\r\n ]+"
6135 raw_exports
6236 "${dump_output} " )
63-
64- list (LENGTH raw_exports export_count)
65- if (export_count EQUAL 0)
66- message (
67- FATAL_ERROR "generate_proxy_def: no exports found in '${input_dll} '"
68- )
37+ if (NOT raw_exports)
38+ message (FATAL_ERROR "No exports found in '${input_dll} '" )
6939 endif ()
7040
71- set (content "LIBRARY \" ${dll_stem} .dll\"\n EXPORTS\n " )
72-
73- foreach (row IN LISTS raw_exports)
74- string (
75- REGEX
76- REPLACE "([0-9]+)[ \t ]+0x[0-9a-fA-F]+[ \t ]+([^ \t\r\n ]+)"
77- "\\ 2"
78- sym_name
79- "${row} " )
80- string (
81- REGEX
82- REPLACE "([0-9]+)[ \t ]+0x[0-9a-fA-F]+[ \t ]+([^ \t\r\n ]+)"
83- "\\ 1"
84- ordinal
85- "${row} " )
86- string (APPEND content
87- " ${sym_name} =original.${sym_name} @${ordinal} \n " )
88- endforeach ()
41+ list (TRANSFORM raw_exports
42+ REPLACE "([0-9]+)[ \t ]+0x[0-9a-fA-F]+[ \t ]+([^ \t\r\n ]+)"
43+ " \\ 2=original.\\ 2 @\\ 1" )
44+ list (
45+ JOIN
46+ raw_exports
47+ "\n "
48+ exports_block)
8949
90- file (WRITE "${def_file} " "${content} " )
91- message (STATUS "generated '${def_file} ' (${export_count} exports)" )
50+ file (WRITE "${def_file} "
51+ "LIBRARY \" ${dll_stem} .dll\"\n EXPORTS\n ${exports_block} \n " )
52+
53+ list (LENGTH raw_exports export_count)
54+ message (STATUS "Generated '${def_file} ' (${export_count} exports)" )
9255
9356 set (${output_var} "${def_file} " )
9457 return (PROPAGATE ${output_var} )
9558endfunction ()
9659
97- # ─── add_bass_proxy ───────────────────────────────────────────────────────────
98- # Create a SHARED library bass_proxy_<VERSION> in the caller's directory scope.
99- #
100- # add_bass_proxy(
101- # VERSION <version_id> # e.g. "2_06"
102- # BASS_DLL <path> # absolute path to that version's bass.dll
103- # SDK_TARGET <target_name> # the STATIC/OBJECT lib with the SDK code
104- # )
105- #
106- # The resulting target is bass_proxy_<VERSION>. Its output file lives in
107- # CMAKE_CURRENT_BINARY_DIR (build/<version>/), so parallel versions never
108- # collide. The deploy step in the 2_XX CMakeLists copies it to bass.dll.
10960function (add_bass_proxy )
110- set (options "" )
111- set (one_value_args VERSION BASS_DLL SDK_TARGET)
112- set (multi_value_args "" )
11361 cmake_parse_arguments (
11462 PARSE_ARGV
11563 0
116- ARG
117- "${options} "
118- "${one_value_args} "
119- "${multi_value_args} " )
120-
121- if ( NOT ARG_VERSION OR NOT ARG_BASS_DLL OR NOT ARG_SDK_TARGET )
122- message (
123- FATAL_ERROR
124- "add_bass_proxy: VERSION, BASS_DLL, SDK_TARGET are required" )
125- endif ()
64+ arg
65+ ""
66+ "VERSION;BASS_DLL;SDK_TARGET "
67+ "" )
68+
69+ foreach (required IN ITEMS VERSION BASS_DLL SDK_TARGET )
70+ if ( NOT arg_ ${required} )
71+ message ( FATAL_ERROR "add_bass_proxy: ${required} is required" )
72+ endif ( )
73+ endforeach ()
12674
127- if (NOT TARGET ${ARG_SDK_TARGET} )
128- message (
129- FATAL_ERROR
130- "add_bass_proxy: SDK target '${ARG_SDK_TARGET} ' not found. "
131- "Ensure src/proxy/CMakeLists.txt is processed first." )
75+ if (NOT TARGET "${arg_SDK_TARGET} " )
76+ message (FATAL_ERROR "SDK target '${arg_SDK_TARGET} ' not found" )
13277 endif ()
13378
134- generate_proxy_def ("${ARG_BASS_DLL} " def_file )
79+ generate_proxy_def ("${arg_BASS_DLL} " def_file )
80+ set (proxy_target "bass_proxy_${arg_VERSION} " )
13581
136- set (target_name "bass_proxy_${ARG_VERSION} " )
137-
138- add_library (${target_name} SHARED "${def_file} " )
139-
140- target_link_libraries (${target_name} PRIVATE ${ARG_SDK_TARGET} )
82+ add_library (${proxy_target} SHARED "${def_file} " )
83+ target_link_libraries (${proxy_target} PRIVATE "${arg_SDK_TARGET} " )
14184
14285 set_target_properties (
143- ${target_name}
144- PROPERTIES CXX_EXTENSIONS OFF PREFIX "" WINDOWS_EXPORT_ALL_SYMBOLS OFF
145- # No OUTPUT_NAME — the target's natural name is fine.
146- # Deploy step copies to bass.dll.
147- )
148-
149- if (CMAKE_SYSTEM_NAME STREQUAL "Windows" AND MINGW)
150- target_link_options (
151- ${target_name}
152- PRIVATE
153- $<$<CXX_COMPILER_ID :GNU ,Clang >:-static >
154- $<$<CXX_COMPILER_ID :GNU ,Clang >:-s >)
155- endif ()
86+ ${proxy_target}
87+ PROPERTIES PREFIX "" CXX_EXTENSIONS OFF
88+ MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG :Debug >:Debug >" )
15689
157- if (CMAKE_SYSTEM_NAME STREQUAL "Windows" AND CMAKE_CXX_COMPILER_ID STREQUAL
158- "Clang"
159- AND CMAKE_CXX_COMPILER_TARGET MATCHES "windows-msvc" )
90+ target_link_options (${proxy_target} PRIVATE
91+ $<$<NOT :$<CXX_COMPILER_FRONTEND_VARIANT :MSVC >>:-static >)
92+
93+ if (CMAKE_CXX_COMPILER_TARGET MATCHES "windows-msvc" )
16094 target_link_libraries (
161- "${target_name} "
162- PRIVATE $<$<CONFIG :Debug >:msvcrtd .lib >
163- $<$<NOT :$<CONFIG :Debug >>:msvcrt .lib >
164- $<$<CONFIG :Debug >:vcruntimed .lib >
165- $<$<NOT :$<CONFIG :Debug >>:vcruntime .lib >
166- $<$<CONFIG :Debug >:ucrtd .lib >
167- $<$<NOT :$<CONFIG :Debug >>:ucrt .lib >)
95+ ${proxy_target}
96+ PRIVATE $<IF :$<CONFIG :Debug >,msvcrtd ,msvcrt >.lib
97+ $<IF :$<CONFIG :Debug >,vcruntimed ,vcruntime >.lib
98+ $<IF :$<CONFIG :Debug >,ucrtd ,ucrt >.lib )
16899 endif ()
169100
170- message (STATUS "proxy target '${target_name } ' — bass: ${ARG_BASS_DLL } " )
171- endfunction ()
101+ message (STATUS "Proxy target '${proxy_target } ' -> ${arg_BASS_DLL } " )
102+ endfunction ()
0 commit comments