Linking object libraries into a Windows DLL in 3.12rc1

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Linking object libraries into a Windows DLL in 3.12rc1

Jason Heeris
I was very excited to see that CMake 3.12rc1 now has support for linking object libraries, making usage requirements propagate to top level targets that use these libraries. However, I'm having trouble putting the concept into practice to build a DLL on Windows 10 using Visual Studio 2017's compiler (MSVC 19.x I think).

There's a minimal self-contained example here:

https://gitlab.com/detly/cmake-dll-ex

The point is to make a DLL of "module2" that exports the "module2()" function. This is the "module2_dll" target in modules/module2/CMakeLists.txt. If you don't want to pore over that project, basically I have this structure:

  /
    - common
    - modules/
      - module1 (depends on "common")
      - module2 (depends on "common" and "module1", exports "module2()")

In common/CMakeLists.txt I have 'add_library(common OBJECT "common.c")'. Similarly for module1 and module2, and module1 also has 'target_link_libraries(module1 PUBLIC common)' and module2 has 'target_link_libraries(module2 PUBLIC module1 common)'. (I am aware that some build systems don't like build targets without source files, but adding "module2.c" doesn't change things.)

Finally there's a DLL target:

add_library(module2_dll SHARED)
target_link_libraries(module2_dll module2)

I build this by doing:

    mkdir build
    cd build
    cmake ..
    cmake --build . --target module2_dll

The toolchain information shows:

-- Building for: Visual Studio 15 2017
-- The C compiler identification is MSVC 19.13.26129.0
-- The CXX compiler identification is MSVC 19.13.26129.0
-- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.13.26128/bin/Hostx86/x86/cl.exe
-- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.13.26128/bin/Hostx86/x86/cl.exe -- works

Now I would expect that building the module2_dll target would build .obj files for all dependencies and link them all together as a DLL. But it ultimately fails with:

"C:\Users\heerij\Code\cmake-dll-ex\build\modules\module2\module2_dll.vcxproj" (default target) (1) ->
(Link target) ->
  module2.obj : error LNK2019: unresolved external symbol _common referenced in function _module2 [C:\Users\heerij\Code \cmake-dll-ex\build\modules\module2\module2_dll.vcxproj]
  module2.obj : error LNK2019: unresolved external symbol _module1 referenced in function _module2 [C:\Users\heerij\Cod e\cmake-dll-ex\build\modules\module2\module2_dll.vcxproj]
  C:\Users\heerij\Code\cmake-dll-ex\build\modules\module2\Debug\module2_dll.dll : fatal error LNK1120: 2 unresolved ext ernals [C:\Users\heerij\Code\cmake-dll-ex\build\modules\module2\module2_dll.vcxproj]

If I remove the "SHARED" option from 'add_library(module2_dll)', it builds a .lib file just fine. If I build the module2 target (not module2_dll), it builds a .lib file as well. But I want a DLL.

I was surprised to see two things:

1. The final link command only runs on module2.obj, not module1.obj or common.obj. If I were doing it by hand I'd link all three as the final step.

2. The intermediate commands build both .obj and .lib files. Are the .lib files required? I don't know enough about Windows build mechanics to know.

Example output for #1:

Link:
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.13.26128\bin\HostX86\x86\link.exe /ERR
  ORREPORT:QUEUE /OUT:"C:\Users\heerij\Code\cmake-dll-ex\build\modules\module2\Debug\module2_dll.dll" /INCREMENTAL /NOL
  OGO kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.
  lib /MANIFEST /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /manifest:embed /DEBUG /PDB:"C:/Users/heerij/Code/cma
  ke-dll-ex/build/modules/module2/Debug/module2_dll.pdb" /SUBSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT /IMPLIB:"C:
  /Users/heerij/Code/cmake-dll-ex/build/modules/module2/Debug/module2_dll.lib" /MACHINE:X86 /SAFESEH  /machine:X86 /DLL
   "C:\Users\heerij\Code\cmake-dll-ex\build\modules\module2\module2.dir\Debug\module2.obj"

Example output for #2:

ClCompile:
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.13.26128\bin\HostX86\x86\CL.exe /c /I"
  C:\Users\heerij\Code\cmake-dll-ex\modules\module2" /I"C:\Users\heerij\Code\cmake-dll-ex\common" /I"C:\Users\heerij\Co
  de\cmake-dll-ex\modules\module1" /Zi /nologo /W3 /WX- /diagnostics:classic /Od /Ob0 /Oy- /D WIN32 /D _WINDOWS /D "CMA
  KE_INTDIR=\"Debug\"" /D _MBCS /Gm- /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /Fo"module2.dir\Deb
  ug\\" /Fd"module2.dir\Debug\module2.pdb" /Gd /TC /analyze- /FC /errorReport:queue "C:\Users\heerij\Code\cmake-dll-ex\
  modules\module2\module2.c"
  module2.c
Lib:
  C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.13.26128\bin\HostX86\x86\Lib.exe /OUT:
  "module2.dir\Debug\module2.lib" /NOLOGO  /machine:X86 module2.dir\Debug\module2.obj
  "C:\Users\heerij\Code\cmake-dll-ex\build\common\common.dir\Debug\common.obj"
  "C:\Users\heerij\Code\cmake-dll-ex\build\modules\module1\module1.dir\Debug\module1.obj"
  module2.vcxproj -> C:\Users\heerij\Code\cmake-dll-ex\build\modules\module2\module2.dir\Debug\module2.lib

I didn't want to dump the entire MSVC output here because there's a tonne of it. But if you don't want to try this yourself, the output is here:

https://gitlab.com/snippets/1728169

Have I misunderstood how object library usage requirements and dependencies propagate? I would appreciate any pointers on this.

- Jason Heeris

--

Powered by www.kitware.com

Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ

Kitware offers various services to support the CMake community. For more information on each offering, please visit:

CMake Support: http://cmake.org/cmake/help/support.html
CMake Consulting: http://cmake.org/cmake/help/consulting.html
CMake Training Courses: http://cmake.org/cmake/help/training.html

Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html

Follow this link to subscribe/unsubscribe:
https://cmake.org/mailman/listinfo/cmake
Reply | Threaded
Open this post in threaded view
|

Re: Linking object libraries into a Windows DLL in 3.12rc1

Deniz Bahadir-2
Hi Jason,

Am 26.06.2018 um 07:45 schrieb Jason Heeris:
>
>
> ...
>
> https://gitlab.com/snippets/1728169
>
> Have I misunderstood how object library usage requirements and
> dependencies propagate? I would appreciate any pointers on this.

Yes, you have overlooked an important detail. Only usage-requirements
are transitive, the object-files are only propagated to directly
dependent targets (not to indirectly dependent ones) *and only if they
have a link/archive step* (which object-files do not have). (See:
https://cmake.org/cmake/help/latest/command/target_link_libraries.html#linking-object-libraries)

That means for your particular example:

With `target_link_libraries(module1 PUBLIC common)` your OBJECT library
"module1" gets the usage-requirements of OBJECT library "common" but not
its object-files.
The same applies to `target_link_libraries(module2 PUBLIC module1
common)`: OBJECT library "module2" gets the usage-requirements of OBJECT
libraries "module1" and "common" but not their object-files.
When linking the DLL with `target_link_libraries(module2_dll module2)`
your DLL `module2_dll` gets all usage-requirements through "module2"
(even indirect ones from "module1" and "common") but only the
object-files of its direct dependency (which is "module2").

You need to explicitly add "module1" and "common" to the DLL's
target_link_library call to get their object-files, too.

When creating a static library "module2_lib" your original approach
worked, because linking/archiving a static library works different and
includes all object-files, even indirect ones.

Currently, there is some debate about changing the behavior of OBJECT
libraries:
https://gitlab.kitware.com/cmake/cmake/issues/18090 
https://gitlab.kitware.com/cmake/cmake/issues/17905


HTH,
Deniz Bahadir

--

Powered by www.kitware.com

Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ

Kitware offers various services to support the CMake community. For more information on each offering, please visit:

CMake Support: http://cmake.org/cmake/help/support.html
CMake Consulting: http://cmake.org/cmake/help/consulting.html
CMake Training Courses: http://cmake.org/cmake/help/training.html

Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html

Follow this link to subscribe/unsubscribe:
https://cmake.org/mailman/listinfo/cmake
Reply | Threaded
Open this post in threaded view
|

Re: Linking object libraries into a Windows DLL in 3.12rc1

Jason Heeris


On Tue, Jun 26, 2018 at 7:36 PM, Deniz Bahadir <[hidden email]>
wrote:
> Yes, you have overlooked an important detail. Only usage-requirements
> are transitive, the object-files are only propagated to directly
> dependent targets (not to indirectly dependent ones) *and only if
> they have a link/archive step*

Ah, for some reason I had thought object files formed part of the usage
requirements of object libraries, but that's clearly not the case.

> You need to explicitly add "module1" and "common" to the DLL's
> target_link_library call to get their object-files, too.

That's unfortunate (for me), but it is what it is I suppose. In my real
project I have about 100 modules with dependencies on each other, and I
was hoping CMake would take the work out of walking the dependency
graph and constructing the final link command. Each module has its
immediate dependencies listed, but not further than that. Resolving
them all by hand and keeping them up to date isn't really feasible.

Thanks for the explanation!

- Jason



--

Powered by www.kitware.com

Please keep messages on-topic and check the CMake FAQ at: http://www.cmake.org/Wiki/CMake_FAQ

Kitware offers various services to support the CMake community. For more information on each offering, please visit:

CMake Support: http://cmake.org/cmake/help/support.html
CMake Consulting: http://cmake.org/cmake/help/consulting.html
CMake Training Courses: http://cmake.org/cmake/help/training.html

Visit other Kitware open-source projects at http://www.kitware.com/opensource/opensource.html

Follow this link to subscribe/unsubscribe:
https://cmake.org/mailman/listinfo/cmake