Filtering symbols from WINDOWS_EXPORT_ALL_SYMBOLS?

When I build a shared library / DLL, I’d like to be able to selectively export only symbols that match certain pattern (e.g. the ‘public’ namespace for my library), excluding things that are internal, or brought in from other imported libraries.

GCC/Clang/XCode linkers support using glob-style patterns for exports (which is perfect for my use case), but MSVC’s .def file doesn’t, alas.

However: what WINDOWS_EXPORT_ALL_SYMBOLS is doing under the hood (building up a list of all known global symbols) is close enough (I just have to filter out the symbols that don’t match my pattern).

Before I reinvent the wheel by writing this tool from scratch, I thought I’d ask:

  • Is there a (legit) way to edit/modify the .def file that CMake creates internally for this purpose? (i.e., after the .def file is created, but before it is handed to the linker?)
  • Failing that, is there a (legit) way for me to just tell CMake to explicitly create that .def file as an ordinary build step, which I can build rules around?
1 Like

For additional context: selectively exporting from a library (eg. via GenerateExportHeader) is insufficient when static-only dependencies do not selectively export. Either way, one requires linker-specific hackery to suppress the symbols from the static libraries (e.g. LINKER:-exclude-libs,ALL for GNU ld).

I don’t believe so. However, a feature to add include/exclude regexes might make sense, though the name of the property then gets a little wonky.

Hmm. I don’t think so. There’s no place for “after all .o files are built, but before linking” unless you use an intermediate OBJECT library. Something like:

add_library(foo-objects OBJECT ${sources})
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/foo.def"
  DEPENDS $<TARGET_OBJECTS:foo-objects> # Might need 3.20; use `foo-objects` if this doesn't work.
  create_def_file "${CMAKE_CURRENT_BINARY_DIR}/foo.def" $<TARGET_OBJECTS:foo-objects>)
add_library(foo SHARED # or STATIC?
  "${CMAKE_CURRENT_BINARY_DIR}/foo.def")
target_link_libraries(foo PRIVATE foo-objects) # will add objects to the foo library.

Note that for multi-config generators, using $<CONFIG> in the output would be handy, but that will definitely need CMake 3.20.

What is the PRE_LINK option in add_custom_command(TARGET ...) for, then? From the docs:

Run after sources have been compiled but before linking the binary or running the librarian or archiver tool of a static library.

Is the exports.def file created after any custom PRE_LINK steps?

Pre-CMake 3.20, CMAKE_CFG_INTDIR would work, right?

That exists, but there’s no mechanism to change the link command from there (and, e.g., add a source file). CMake either needs to see a custom command making it or it already exist for it to be a source file.

On Visual Studio at least, yes. I don’t know how Xcode does it.

Well, could one not edit the CMake-generated exports.def in place from a PRE_LINK command? Maybe run cmake -P with a custom filtering script? The dependencies happen to be correct because exports.def has to be recreated if any object file changes, so the PRE_LINK trigger will always run because the link step depends on exports.def.

The key is whether or not exports.def gets created before or after user-supplied/custom PRE_LINK commands.

XCode supports linker scripts via LINKER:-exported_symbols_list,${linker_script}. :slight_smile:

While I don’t think there’s a conceptual problem with that, there are a few problems:

  • how to get the path to the exports.def CMake is going to use?
  • what if it is made after PRE_LINK (AFAIK, we don’t guarantee when the file (assuming there is one!) is ready right now).

I think the OBJECT library approach I put above is the most reliable one today.

I was more talking about CMAKE_CFG_DIR than linker scripts :slight_smile: .

(nit nit: XCode doesn’t support linker scripts in the same way that GCC does; it’s strictly a list of “export these symbols and hide everything else”, but with glob expressions allowed. That’s all we happen to need, though.)