target_link_libraries doesn't work correctly with C++/CLI compiled dll

Hi! I noticed that cmake doesn’t link C++/CLI dll correctly. The problem is that cmake tries to link static version of library too, but it doesn’t exist.

I write small example to reproduce a problem:

cmake_minimum_required(VERSION 3.5)

project(BugExample LANGUAGES CSharp CXX)

include(CSharpUtilities)

if(CMAKE_CXX_FLAGS_DEBUG MATCHES "/RTC1")
   string(REPLACE "/RTC1" " " CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
endif()

if(CMAKE_CXX_FLAGS MATCHES "/EHsc")
   string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
endif()

add_library(Class1 SHARED Class1.cpp)
set_property(TARGET Class1 PROPERTY VS_DOTNET_TARGET_FRAMEWORK_VERSION "v4.7.2")
set_property(TARGET Class1 PROPERTY COMMON_LANGUAGE_RUNTIME "")
set_target_properties(Class1 PROPERTIES DEBUG_POSTFIX "d")
set_target_properties(Class1 PROPERTIES VS_GLOBAL_ManagedAssembly "true")
set_target_properties(Class1 PROPERTIES VS_GLOBAL_KEYWORD "ManagedCProj")
set_property(TARGET Class1 PROPERTY VS_DOTNET_REFERENCES
         "Microsoft.CSharp"
         "System"
         "System.Core"
         "System.Windows"
         "System.Data"
         "WindowsBase"
         "mscorlib")

add_library(Class2 SHARED Class2.cpp)
set_property(TARGET Class2 PROPERTY VS_DOTNET_TARGET_FRAMEWORK_VERSION "v4.7.2")
set_property(TARGET Class2 PROPERTY COMMON_LANGUAGE_RUNTIME "")
set_target_properties(Class2 PROPERTIES DEBUG_POSTFIX "d")
set_target_properties(Class2 PROPERTIES VS_GLOBAL_ManagedAssembly "true")
set_target_properties(Class2 PROPERTIES VS_GLOBAL_KEYWORD "ManagedCProj")
set_property(TARGET Class2 PROPERTY VS_DOTNET_REFERENCES
         "Microsoft.CSharp"
         "System"
         "System.Core"
         "System.Windows"
         "System.Data"
         "WindowsBase"
         "mscorlib")


#target_compile_options(Class2 PRIVATE "/FU$<TARGET_FILE:Class1>")

target_link_libraries(Class2 Class1)
namespace Kek {
        public ref class Class1 {};
}
namespace Kek {
        public ref class Class2 {
                void lol() {
                        Class1^ x = gcnew Class1();
                }
        };
}

So, at compilation I get:

LINK : fatal error LNK1104: cannot open file 'Debug\Class1d.lib'

To workaround this problem you can pass /FU flag manually, but it’s not cool:)

If Class1 doesn’t export any symbols, I don’t think the linker produces an import library (.lib) for your DLL. You would need to ensure Class1 does export symbols to address that. I am not familiar with what the /FU option does, but presumably one of its effects is to define at least one symbol, which would force a .lib to be created.

1 Like

Class1 contains at least constructor symbol. Nevertheless, I have the same problem with libraries which contains symbols explicitly.

But maybe I misunderstood you. Can you please provide an example?

About /FU flag

You have defined Class1, but I don’t see anything that is causing that symbol to be exported. Note that defining and exporting are two different things. With the Visual Studio toolchain, no symbols are exported by default, you have to actively specify what to export. Other compilers export all symbols by default, so you can get away without doing anything.

It might be overkill for what you need, but the earlier parts of my 2019 CppCon talk covers this topic: CppCon 2019: Deep CMake For Library Authors - Crascit

I haven’t use the /FU flag personally, so I am not familiar with its effects.

1 Like