Tests: Add cases covering nm handling for WINDOWS_EXPORT_ALL_SYMBOLS

Add `__create_def` coverage for:

- missing nm tools
- generator selection
- single- and multi-object inputs
- object paths containing spaces
- mixed object and definition inputs
- nm symbol classification
This commit is contained in:
Chuck Atkins
2026-08-05 10:58:08 -04:00
parent 0685dc11db
commit 03e56cd338
22 changed files with 315 additions and 14 deletions
+23 -6
View File
@@ -4,9 +4,21 @@ project(autoexport)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${autoexport_BINARY_DIR}/bin)
add_subdirectory(sub)
add_library(objlib OBJECT objlib.c)
set_property(TARGET objlib PROPERTY POSITION_INDEPENDENT_CODE 1)
add_library(autoexport SHARED hello.cxx world.cxx foo.c $<TARGET_OBJECTS:objlib>)
add_library(objlib_maybe_lto OBJECT objlib_maybe_lto.c)
set_property(TARGET objlib_maybe_lto PROPERTY POSITION_INDEPENDENT_CODE 1)
# Automatic export definition generation for LTO objects is currently only
# supported with Clang targeting the MSVC ABI.
if(MSVC AND CMAKE_C_COMPILER_ID STREQUAL "Clang")
set_property(TARGET objlib_maybe_lto PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
endif()
add_library(autoexport SHARED hello.cxx world.cxx foo.c)
target_link_libraries(autoexport PRIVATE objlib objlib_maybe_lto)
add_library(autoexport3 SHARED cppCLI.cxx)
if(MSVC AND NOT MSVC_VERSION VERSION_LESS 1600
AND NOT CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64")
@@ -18,11 +30,16 @@ if(MSVC)
set_target_properties(say PROPERTIES ENABLE_EXPORTS ON)
add_library(autoexport_for_exec SHARED hello2.c)
target_link_libraries(autoexport_for_exec say)
if(NOT MSVC_VERSION VERSION_LESS 1600 AND
NOT CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64")
enable_language(ASM_MASM)
target_sources(autoexport PRIVATE nop.asm)
set_property(SOURCE nop.asm PROPERTY COMPILE_FLAGS /safeseh)
if(NOT MSVC_VERSION VERSION_LESS 1600)
if(CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64")
enable_language(ASM_MARMASM)
target_sources(autoexport PRIVATE nop_ARM64.asm)
set_property(SOURCE nop_ARM64.asm PROPERTY VS_SETTINGS "PreprocessWithCl=false")
else()
enable_language(ASM_MASM)
target_sources(autoexport PRIVATE nop_x64.asm)
endif()
target_compile_definitions(say PRIVATE HAS_JUSTNOP)
endif()
endif()
@@ -4,9 +4,21 @@ project(autoexport)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${autoexport_BINARY_DIR}/bin)
add_subdirectory(sub)
add_library(objlib OBJECT objlib.c)
set_property(TARGET objlib PROPERTY POSITION_INDEPENDENT_CODE 1)
add_library(autoexport SHARED hello.cxx world.cxx foo.c $<TARGET_OBJECTS:objlib>)
add_library(objlib_maybe_lto OBJECT objlib_maybe_lto.c)
set_property(TARGET objlib_maybe_lto PROPERTY POSITION_INDEPENDENT_CODE 1)
# Automatic export definition generation for LTO objects is currently only
# supported with Clang targeting the MSVC ABI.
if(MSVC AND CMAKE_C_COMPILER_ID STREQUAL "Clang")
set_property(TARGET objlib_maybe_lto PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
endif()
add_library(autoexport SHARED hello.cxx world.cxx foo.c)
target_link_libraries(autoexport PRIVATE objlib objlib_maybe_lto)
add_library(autoexport3 SHARED cppCLI.cxx)
if(MSVC AND NOT MSVC_VERSION VERSION_LESS 1600
AND NOT CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64")
@@ -18,11 +30,16 @@ if(MSVC)
set_target_properties(say PROPERTIES ENABLE_EXPORTS ON)
add_library(autoexport_for_exec SHARED hello2.c)
target_link_libraries(autoexport_for_exec say)
if(NOT MSVC_VERSION VERSION_LESS 1600 AND
NOT CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64")
enable_language(ASM_MASM)
target_sources(autoexport PRIVATE nop.asm)
set_property(SOURCE nop.asm PROPERTY COMPILE_FLAGS /safeseh)
if(NOT MSVC_VERSION VERSION_LESS 1600)
if(CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64")
enable_language(ASM_MARMASM)
target_sources(autoexport PRIVATE nop_ARM64.asm)
set_property(SOURCE nop_ARM64.asm PROPERTY VS_SETTINGS "PreprocessWithCl=false")
else()
enable_language(ASM_MASM)
target_sources(autoexport PRIVATE nop_x64.asm)
endif()
target_compile_definitions(say PRIVATE HAS_JUSTNOP)
endif()
endif()
@@ -0,0 +1,8 @@
AREA SOME, CODE, READONLY
EXPORT justnop
justnop PROC
ret
ENDP
END
@@ -3,10 +3,11 @@ ELSE
.MODEL FLAT,C
ENDIF
SOME SEGMENT EXECUTE READ
.CODE
public justnop
justnop:
justnop PROC
ret
justnop ENDP
END
@@ -0,0 +1,4 @@
int objlib_maybe_lto(void)
{
return 8;
}
+4
View File
@@ -13,6 +13,7 @@ int WINAPI foo();
// test regular C
int bar();
int objlib();
int objlib_maybe_lto();
void justnop();
}
@@ -45,6 +46,9 @@ int main()
printf("\n");
bar();
objlib();
if (objlib_maybe_lto() != 8) {
return 1;
}
printf("\n");
cliFunction();
printf("\n");
@@ -1,2 +1,3 @@
E_cat_binary_files/binary.obj -text
E_cat_good_binary_cat-stdout.txt -text -whitespace
E___create_def-nm-*-expected.def whitespace=-blank-at-eol
@@ -0,0 +1,9 @@
string(REGEX REPLACE [[^E___create_def-nm-]] "" subtest "${test}")
cmake_path(APPEND RunCMake_TEST_BINARY_DIR "${subtest}.def" OUTPUT_VARIABLE actual_def)
cmake_path(APPEND RunCMake_TEST_SOURCE_DIR "${test}-expected.def" OUTPUT_VARIABLE expected_def)
execute_process(
COMMAND ${CMAKE_COMMAND} -E compare_files --ignore-eol ${actual_def} ${expected_def}
RESULT_VARIABLE compare_result)
if(compare_result)
set(RunCMake_TEST_FAILED "${actual_def} does not match ${expected_def}")
endif()
@@ -0,0 +1,3 @@
EXPORTS
manual
mixed
@@ -0,0 +1,3 @@
EXPORTS
first
second
@@ -0,0 +1,2 @@
^cmake-nm-does-not-exist returned an error: [^
]*
@@ -0,0 +1,2 @@
EXPORTS
single
@@ -0,0 +1,2 @@
EXPORTS
spaced
@@ -0,0 +1,6 @@
EXPORTS
??_7Example@@6B@ DATA
bss DATA
common DATA
data DATA
text
@@ -0,0 +1 @@
^Ignoring symbol 'unknown' with unrecognized type '\?' in object 'symbol-types.obj'\.$
@@ -0,0 +1,42 @@
string(ASCII 66 67 192 222 llvm_bitcode_magic)
string(REPEAT "x" 64 llvm_bitcode_padding)
file(WRITE "${CMAKE_BINARY_DIR}/bitcode.obj"
"${llvm_bitcode_magic}${llvm_bitcode_padding}")
file(WRITE "${CMAKE_BINARY_DIR}/nm-not-found.objs" [[
bitcode.obj
]])
file(WRITE "${CMAKE_BINARY_DIR}/single.obj" "")
file(WRITE "${CMAKE_BINARY_DIR}/single.objs" [[
single.obj
]])
file(WRITE "${CMAKE_BINARY_DIR}/symbol-types.obj" "")
file(WRITE "${CMAKE_BINARY_DIR}/symbol-types.objs" [[
symbol-types.obj
]])
file(WRITE "${CMAKE_BINARY_DIR}/first.obj" "")
file(WRITE "${CMAKE_BINARY_DIR}/no-symbols.obj" "")
file(WRITE "${CMAKE_BINARY_DIR}/second.obj" "")
file(WRITE "${CMAKE_BINARY_DIR}/multiple.objs" [[
first.obj
no-symbols.obj
second.obj
]])
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/directory with spaces")
file(WRITE "${CMAKE_BINARY_DIR}/directory with spaces/spaced.obj" "")
file(WRITE "${CMAKE_BINARY_DIR}/spaces.objs" [[
directory with spaces/spaced.obj
]])
file(WRITE "${CMAKE_BINARY_DIR}/manual.def" [[
EXPORTS
manual
]])
file(WRITE "${CMAKE_BINARY_DIR}/mixed.obj" "")
file(WRITE "${CMAKE_BINARY_DIR}/mixed.objs" [[
manual.def
mixed.obj
]])
@@ -87,6 +87,26 @@ run_cmake_command(E___run_co_compile-no--- ${CMAKE_COMMAND} -E __run_co_compile
run_cmake_command(E___run_co_compile-no-cc ${CMAKE_COMMAND} -E __run_co_compile --iwyu=iwyu-does-not-exist --)
run_cmake_command(E___run_co_compile-tidy-remove-fixes ${CMAKE_COMMAND} -E __run_co_compile "--tidy=${CMAKE_COMMAND}\\;-E\\;true\\;--export-fixes=${RunCMake_BINARY_DIR}/tidy-fixes.yaml" -- ${CMAKE_COMMAND} -E true)
block()
set(RunCMake_TEST_BINARY_DIR
${RunCMake_BINARY_DIR}/E___create_def-build)
run_cmake(E___create_def)
set(RunCMake_TEST_NO_CLEAN 1)
# bitcode.obj contains LLVM bitcode magic followed by padding.
run_cmake_command(E___create_def-nm-not-found ${CMAKE_COMMAND} -E __create_def nm-not-found.def nm-not-found.objs --nm=cmake-nm-does-not-exist)
if(CMAKE_HOST_UNIX)
set(fake_nm ${RunCMake_SOURCE_DIR}/fake-nm.sh)
set(RunCMake-check-file E___create_def-nm-check.cmake)
run_cmake_command(E___create_def-nm-single ${CMAKE_COMMAND} -E __create_def single.def single.objs --nm=${fake_nm})
run_cmake_command(E___create_def-nm-symbol-types ${CMAKE_COMMAND} -E __create_def symbol-types.def symbol-types.objs --nm=${fake_nm})
run_cmake_command(E___create_def-nm-multiple ${CMAKE_COMMAND} -E __create_def multiple.def multiple.objs --nm=${fake_nm})
run_cmake_command(E___create_def-nm-spaces ${CMAKE_COMMAND} -E __create_def spaces.def spaces.objs --nm=${fake_nm})
run_cmake_command(E___create_def-nm-mixed ${CMAKE_COMMAND} -E __create_def mixed.def mixed.objs --nm=${fake_nm})
endif()
endblock()
block()
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/list-cache-build)
run_cmake(list-cache)
+122
View File
@@ -0,0 +1,122 @@
#!/bin/sh
set -eu
if test "$#" -eq 4 &&
test "$1" = "--no-weak" &&
test "$2" = "--defined-only" &&
test "$3" = "--format=posix"; then
print_file_name=0
input=$4
elif test "$#" -eq 5 &&
test "$1" = "--no-weak" &&
test "$2" = "--defined-only" &&
test "$3" = "--format=posix" &&
test "$4" = "--print-file-name"; then
print_file_name=1
input=$5
else
echo "unexpected nm arguments: $*" >&2
exit 1
fi
pending_symbol=0
finish_symbol()
{
if test "${pending_symbol}" -eq 1; then
printf ' 0 0\r\n'
pending_symbol=0
fi
}
start_symbol()
{
finish_symbol
if test "${print_file_name}" -eq 1; then
printf '%s: %s %s' "$1" "$2" "$3"
else
printf '%s %s' "$2" "$3"
fi
pending_symbol=1
}
process_object()
{
object=$1
case "${object}" in
\"*\")
object=${object#\"}
object=${object%\"}
;;
esac
case "${object}" in
*single.obj)
start_symbol "${object}" single T
;;
*symbol-types.obj)
start_symbol "${object}" absolute A
start_symbol "${object}" bss B
start_symbol "${object}" common C
start_symbol "${object}" data D
start_symbol "${object}" import I
start_symbol "${object}" debug N
start_symbol "${object}" readonly R
start_symbol "${object}" '??_7Example@@6B@' R
start_symbol "${object}" '??_R4Example@@6B@' R
start_symbol "${object}" section S
start_symbol "${object}" text T
start_symbol "${object}" undefined U
start_symbol "${object}" weak_object V
start_symbol "${object}" weak W
start_symbol "${object}" local_absolute a
start_symbol "${object}" local_bss b
start_symbol "${object}" local_common c
start_symbol "${object}" local_data d
start_symbol "${object}" local_import i
start_symbol "${object}" local_debug n
start_symbol "${object}" local_readonly r
start_symbol "${object}" local_section s
start_symbol "${object}" local_text t
start_symbol "${object}" local_weak_object v
start_symbol "${object}" local_weak w
start_symbol "${object}" unknown '?'
;;
*first.obj)
start_symbol "${object}" first T
;;
*no-symbols.obj)
sleep 0.1
printf '%s: no symbols\n' "${object}" >&2
sleep 0.1
finish_symbol
;;
*second.obj)
start_symbol "${object}" second T
;;
*spaced.obj)
start_symbol "${object}" spaced T
;;
*mixed.obj)
start_symbol "${object}" mixed T
;;
*)
echo "unexpected nm input: ${object}" >&2
exit 1
;;
esac
}
case "${input}" in
@*)
response=${input#@}
while IFS= read -r object || test -n "${object}"; do
process_object "${object}"
done < "${response}"
;;
*)
process_object "${input}"
;;
esac
finish_symbol
@@ -0,0 +1,24 @@
set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/AutoExportNm.vcxproj")
if(NOT EXISTS "${vcProjectFile}")
set(RunCMake_TEST_FAILED
"Project file ${vcProjectFile} does not exist.")
return()
endif()
file(READ "${vcProjectFile}" vcProject)
file(READ "${RunCMake_TEST_BINARY_DIR}/platform-toolset.txt"
platformToolset)
if(platformToolset MATCHES [[^[Ll][Ll][Vv][Mm](_v[0-9]+(_xp)?)?$]] OR
platformToolset MATCHES [[^[Cc][Ll][Aa][Nn][Gg]([Cc][Ll]$|_[0-9])]])
if(NOT vcProject MATCHES
[[__create_def[^<]*--nm=C:/cmake-nm-test/tool-nm\.exe]])
set(RunCMake_TEST_FAILED
"__create_def does not pass CMAKE_NM in ${vcProjectFile}.")
endif()
else()
if(vcProject MATCHES
[[__create_def[^<]*--nm=C:/cmake-nm-test/tool-nm\.exe]])
set(RunCMake_TEST_FAILED
"__create_def passes CMAKE_NM for toolset ${platformToolset}.")
endif()
endif()
@@ -0,0 +1,11 @@
enable_language(C)
set(CMAKE_NM "C:/cmake-nm-test/tool-nm.exe")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/platform-toolset.txt"
"${CMAKE_VS_PLATFORM_TOOLSET}")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/AutoExportNm.c"
"void auto_export_nm(void) {}\n")
add_library(AutoExportNm SHARED
"${CMAKE_CURRENT_BINARY_DIR}/AutoExportNm.c")
set_property(TARGET AutoExportNm PROPERTY WINDOWS_EXPORT_ALL_SYMBOLS ON)
@@ -5,6 +5,7 @@ if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND CMAKE_C_COMPILER_VERSION VERSION_GREA
endif()
run_cmake(CustomCommandGenex)
run_cmake(AutoExportNm)
if(NOT RunCMake_GENERATOR MATCHES "^Visual Studio 1[1-5] ")
run_cmake(CustomCommandParallel)
run_cmake(CustomCommandParallelDisable)