VS: Object library with config-dependent source path, dependents have incorrect <Object> paths
Issue: When the path of the source file depends on the current configuration, the output path of <ClCompile>
in the object library and the path of <Object>
in the dependent target do not match like they should. This would come up, for example, when generating intermediate sources and passing them to an OBJECT library (to use a different warning level on them, etc).
I've found a minimum repro project that builds in Ninja and Ninja Multi-Config but fails to build in the VS generator.
I've also detailed what is going wrong and how it might be fixed below.
Layout:
- Root
- -- TestIntermediate_Debug
- ----- TestIntermediateSourceFile.cpp
- -- TestIntermediate_Release
- ----- TestIntermediateSourceFile.cpp
- -- CMakeLists.txt
- -- Main.cpp
Main.cpp:
int result();
int main(int argc, char** argv) {
return result();
}
TestIntermediateSourceFile.cpp:
int result() {
return 0;
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.25)
set(CMAKE_CONFIGURATION_TYPES Debug Release)
project(TestProject VERSION 0.0.0 LANGUAGES CXX)
add_executable(TestExecutable Main.cpp)
add_library(TestIntermediate OBJECT "TestIntermediate_$<CONFIG>/TestIntermediateSourceFile.cpp")
target_link_libraries(TestExecutable PRIVATE TestIntermediate)
Commands:
cmake -B ./build -G "Visual Studio 17 2022" --fresh
cmake --build ./build
This will give the error:
MSBuild version 17.3.1+2badb37d1 for .NET Framework
Checking Build System
Building Custom Rule C:/Users/npatella/Desktop/Test/CMakeLists.txt
TestIntermediateSourceFile.cpp
TestIntermediate.vcxproj -> C:\Users\npatella\Desktop\Test\build\TestIntermediate.dir\Debug\TestIntermediate.lib
Building Custom Rule C:/Users/npatella/Desktop/Test/CMakeLists.txt
Main.cpp
LINK : fatal error LNK1104: cannot open file 'C:\Users\npatella\Desktop\Test\build\TestIntermediate.dir\Debug\TestIntermediateSourceFile.obj' [C:\Users\npatella\Desktop\Test
\build\TestExecutable.vcxproj]
Notes:
- The project will build if TestIntermediate is made STATIC instead of OBJECT.
- The project will build if the source file is relocated to
TestIntermediate/TestIntermediateSourceFile_$<CONFIG>.cpp
.
The cause is evident if you consider that second point about where the source file is located.
In a working build, the following <ItemGroup>
is generated in TestIntermediate.vcxproj:
<ItemGroup>
<ClCompile Include="C:\Users\npatella\Desktop\Test\TestIntermediate\TestIntermediateSourceFile_Debug.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="C:\Users\npatella\Desktop\Test\TestIntermediate\TestIntermediateSourceFile_Release.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
</ItemGroup>
The following <Object>
is added in TestExecutable.vcxproj:
<Object Include="C:\Users\npatella\Desktop\Test\build\TestIntermediate.dir\$(Configuration)\TestIntermediateSourceFile_Debug.obj">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</Object>
<Object Include="C:\Users\npatella\Desktop\Test\build\TestIntermediate.dir\$(Configuration)\TestIntermediateSourceFile_Release.obj">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</Object>
This works fine because the two paths match.
In the faulty build, the following <ItemGroup>
is generated in TestIntermediate.vcxproj:
<ItemGroup>
<ClCompile Include="C:\Users\npatella\Desktop\Test\TestIntermediate_Debug\TestIntermediateSourceFile.cpp">
<ObjectFileName>$(IntDir)/TestIntermediate_Debug/TestIntermediateSourceFile.cpp.obj</ObjectFileName>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="C:\Users\npatella\Desktop\Test\TestIntermediate_Release\TestIntermediateSourceFile.cpp">
<ObjectFileName>$(IntDir)/TestIntermediate_Release/TestIntermediateSourceFile.cpp.obj</ObjectFileName>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
</ItemGroup>
The following <Object>
is added in TestExecutable.vcxproj:
<Object Include="C:\Users\npatella\Desktop\Test\build\TestIntermediate.dir\$(Configuration)\TestIntermediateSourceFile.obj" />
This fails because the two paths don't match.
This is what should be generated in TestExecutable.vcxproj instead:
<Object Include="C:\Users\npatella\Desktop\Test\build\TestIntermediate.dir\$(Configuration)\TestIntermediate_Debug\TestIntermediateSourceFile.cpp.obj">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</Object>
<Object Include="C:\Users\npatella\Desktop\Test\build\TestIntermediate.dir\$(Configuration)\TestIntermediate_Release\TestIntermediateSourceFile.cpp.obj">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</Object>