Hi, we are writing a custom MSVC toolchain (specifically for Visual Studio 2017) and while doing so, we’ve run into a problem where generated build.ninja file is different on first cmake run compared to subsequent runs.
After first run it generates following (stripped down for readability) build statement:
build test.exe: CXX_EXECUTABLE_LINKER__test_Debug CMakeFiles\test.dir\main.cpp.obj
LINK_LIBRARIES = kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib
However, when build directory is regenerated, LINK_LIBRARIES
variable setting disappears. Note, I have manully removed all variables except LINK_LIBRARIES
, as they remain the same after regeneration and would just get in the way.
This is generally not a problem, as link.exe
will include those libraries in default libraries set by default, so as long as they are found using library search paths, things work and nothing needs to be mentioned on the command line.
We have noticed this because in our toolchain file we set CMAKE_<LANG>_STANDARD_LIBRARIES
to contain these default libraries:
set(CMAKE_C_STANDARD_LIBRARIES
kernel32.lib
user32.lib
gdi32.lib
winspool.lib
shell32.lib
ole32.lib
oleaut32.lib
uuid.lib
comdlg32.lib
advapi32.lib
)
set(CMAKE_CXX_STANDARD_LIBRARIES ${CMAKE_C_STANDARD_LIBRARIES})
What happened was that builds would succeed without any problem after first build directory generation, but then after build directory got regenerated, builds would fail. Looking into it, we found out that regenerating changed the LINK_LIBRARIES
in build.ninja
file to following:
build test.exe: CXX_EXECUTABLE_LINKER__test_Debug CMakeFiles\test.dir\main.cpp.obj
LINK_LIBRARIES = kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib
That is, instead of separating libraries using spaces, it uses ;
(cmake list syntax).
Based on this, it looks like the behavior is to
- on first generation, ignore
CMAKE_<LANG>_STANDARD_LIBRARIES
and use hard-coded value - on subsequent generation, use
CMAKE_<LANG>_STANDARD_LIBRARIES
assuming it holds a string, rather than a list
Now the discrepancy between first and subequent run seems like a bug, but it would be nice to hear someone else’ opinion on the matter before reporting it as such.
Treating CMAKE_<LANG>_STANDARD_LIBRARIES
as string, rather than list, may be intended, but it seems to be inconsistent with CMAKE_<LANG>_STANDARD_INCLUDE_DIRECTORIES
, which is treated as list. Should that also be considered a bug? On the other hand, CMAKE_<TARGET_TYPE>_LINKER_FLAGS_INIT
are also treated as string rather than list, but at least the behavior seems to be consistent and first and subsequent cmake runs.
Note, as a workaround, we can set CMAKE_<LANG>_STANDARD_LIBRARIES
as string using following:
string(JOIN " " CMAKE_C_STANDARD_LIBRARIES
kernel32.lib
user32.lib
gdi32.lib
winspool.lib
shell32.lib
ole32.lib
oleaut32.lib
uuid.lib
comdlg32.lib
advapi32.lib
)
set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES}")
This produces the same value of LINK_LIBRARIES
in build.ninja
for first and also subsequent runs, even though the process of arriving at that value is different.