2

Problem:

After I run cmake to generate a project with a STATIC library, which completes successfully, both ninja and mingw32-make fail to make their targets at linking. For SHARED libraries or executables this same setup worked fine. I've tried this both for "Ninja" and "MinGW Makefiles" generators:

ninja output:

[2/2] Linking CXX static library hello_wsl.lib
FAILED: hello_wsl.lib
cmd.exe /C "cd . && "C:\Program Files\CMake\bin\cmake.exe" -E remove hello_wsl.lib && "" qc hello_wsl.lib  CMakeFiles/hello_wsl.dir/lib_hello_world.cpp.obj && cd ."
"""" no se reconoce como un comando interno o externo,
programa o archivo por lotes ejecutable.
ninja: build stopped: subcommand failed.

mingw32-make output:

Scanning dependencies of target hello_wsl
[ 50%] Building CXX object CMakeFiles/hello_wsl.dir/lib_hello_world.cpp.obj
[100%] Linking CXX static library hello_wsl.lib
Error running link command: El parámetro no es correcto
CMakeFiles\hello_wsl.dir\build.make:93: recipe for target 'hello_wsl.lib' failed
mingw32-make.exe[2]: *** [hello_wsl.lib] Error 2
CMakeFiles\Makefile2:66: recipe for target 'CMakeFiles/hello_wsl.dir/all' failed
mingw32-make.exe[1]: *** [CMakeFiles/hello_wsl.dir/all] Error 2
Makefile:82: recipe for target 'all' failed
mingw32-make.exe: *** [all] Error 2

Also take a look at "project_root\build\CMakeFiles\hello_wsl.dir\link.txt" generated by "MinGW Makefiles":

"" qc hello_wsl.lib  CMakeFiles/hello_wsl.dir/lib_hello_world.cpp.obj

Sample project, in steps, to reproduce the problem for "MinGW Makefiles":

  • Install CMake 3.8.0
  • Install MinGWx64 6.3 windows binaries (I've downloaded them from here)
  • Create a root folder for this project, I'll call it project_root in this sample.
  • Create these subfolders inside:
    1. project_root\build
    2. project_root\include
    3. project_root\include\lib_hello_world
  • Create these files:

    1. project_root\include\lib_hello_world\lib_hello_world.cpp:

      #include <iostream>
      
      class HelloWorldClass{
          HelloWorldClass(){
              std::cout << "Hello, world!" << std::endl;
          }
      };
      
    2. project_root\include\lib_hello_world\CMakeLists.txt:

      cmake_minimum_required(VERSION 3.8.0)
      project(lib_hello_world)
      
      add_library(lib_hello_world STATIC lib_hello_world.cpp)
      
    3. project_root\toolchain.cmake:

      # Target system (cross compile)
      set(CMAKE_SYSTEM_NAME WindowsStore)
      set(CMAKE_SYSTEM_VERSION 10.0)
      
      # BIN utils
      SET(CMAKE_AR      "$ENV{MINGW_W64_BIN_DIR}/ar.exe")
      SET(CMAKE_OBJCOPY "$ENV{MINGW_W64_BIN_DIR}/objcopy.exe")
      SET(CMAKE_OBJDUMP "$ENV{MINGW_W64_BIN_DIR}/objdump.exe")
      SET(CMAKE_RANLIB  "$ENV{MINGW_W64_BIN_DIR}/ranlib.exe")
      SET(CMAKE_NM      "$ENV{MINGW_W64_BIN_DIR}/nm.exe")
      SET(CMAKE_STRIP   "$ENV{MINGW_W64_BIN_DIR}/strip.exe")
      
      # C compiler
      SET(CMAKE_C_COMPILER "$ENV{MINGW_W64_BIN_DIR}/gcc.exe")
      
      # CXX compiler
      SET(CMAKE_CXX_COMPILER "$ENV{MINGW_W64_BIN_DIR}/g++.exe")
      
      # LINKER
      SET(CMAKE_LINKER "$ENV{MINGW_W64_BIN_DIR}/ld.bfd.exe")
      
    4. project_root\configure.bat:

      @ECHO OFF
      SETLOCAL
      @ECHO OFF
      REM Change these variables to the corresponding paths on your own system
      SET "CMAKE_EXECUTABLE=C:\Program Files\CMake\bin\cmake.exe"
      SET "MINGW_W64_BIN_DIR=C:/Program Files/MinGWx64/bin"
      CD "%~dp0\build"
      CALL "%CMAKE_EXECUTABLE%" "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON" "-DCMAKE_BUILD_TYPE=Debug" -G "MinGW Makefiles" "-DCMAKE_MAKE_PROGRAM='%MINGW_W64_BIN_DIR%/mingw32-make.exe'" "-DCMAKE_TOOLCHAIN_FILE='%~dp0/toolchain.cmake'" "%~dp0/include/lib_hello_world"
      ENDLOCAL
      
    5. project_root\build.bat:

      @ECHO OFF
      SETLOCAL
      @ECHO OFF
      REM Change this variable to the corresponding path on your own system
      SET "MINGW_W64_BIN_DIR=C:/Program Files/MinGWx64/bin"
      CD "%~dp0\build"
      CALL "%MINGW_W64_BIN_DIR%/mingw32-make.exe"
      ENDLOCAL
      
  • Finally, open CMD and run the commands:

    project_root\configure.bat
    project_root\build.bat
    

This is a sketchy fix/workaround that I found:

  • Create this file: project_root\fix.bat:

    @ECHO OFF
    SETLOCAL
    @ECHO OFF
    
    MOVE "%~dp0\build\CMakeCache.txt" ".\"
    RMDIR "%~dp0\build" /S /Q
    MKDIR "%~dp0\build"
    MOVE "%~dp0\CMakeCache.txt" ".\build\"
    
    ENDLOCAL
    
  • After reproducing the error, open CMD and run:

    project_root\fix.bat
    project_root\configure.bat
    project_root\build.bat
    
  • ninja output after fix (successfully linked):

    [2/2] Linking CXX static library hello_wsl.lib
    
  • mingw32-make after fix (successfully linked):

    Scanning dependencies of target hello_wsl
    [ 50%] Building CXX object CMakeFiles/hello_wsl.dir/lib_hello_world.cpp.obj
    [100%] Linking CXX static library hello_wsl.lib
    [100%] Built target hello_wsl
    

A few things I did to try to figure this out:

  • I made a backup of CMakeCache.txt (CMakeCache.txt.before_fix) before applying the fix and reruning configure and build. There was no diference between the CMakeCache.txt.before_fix and CMakeCache.txt files after reconfiguring and successfully building the project.

  • I also made a backup of "rules.ninja" and then compared them with FC. This is the output of FC "project_root\rules.ninja.before_fix" "project_root\build\rules.ninja":

    Comparando archivos .\rules.ninja.before_fix y .\BUILD\RULES.NINJA
    ***** .\rules.ninja.before_fix
    rule CXX_STATIC_LIBRARY_LINKER__lib_hello_world
    command = cmd.exe /C "$PRE_LINK && "C:\Program Files\CMake\bin\cmake.exe" -E remove $TARGET_FILE && "" qc $TARGET_FILE $LINK_
    FLAGS $in && $POST_BUILD"
    description = Linking CXX static library $TARGET_FILE
    ***** .\BUILD\RULES.NINJA
    rule CXX_STATIC_LIBRARY_LINKER__lib_hello_world
    command = cmd.exe /C "$PRE_LINK && "C:\Program Files\CMake\bin\cmake.exe" -E remove $TARGET_FILE && C:\PROGRA~1\MinGWx64\bin\
    ar.exe qc $TARGET_FILE $LINK_FLAGS $in && C:\PROGRA~1\MinGWx64\bin\ranlib.exe $TARGET_FILE && $POST_BUILD"
    description = Linking CXX static library $TARGET_FILE
    *****
    
  • And did the same for "MinGW Makefiles". This is the output of FC "project_root\link.txt.before_fix" "project_root\build\CMakeFiles\lib_hello_world.dir\link.txt":

    Comparando archivos .\link.txt.before_fix y .\BUILD\CMAKEFILES\LIB_HELLO_WORLD.DIR\LINK.TXT
    ***** .\link.txt.before_fix
    "" qc hello_wsl.lib  CMakeFiles/hello_wsl.dir/lib_hello_world.cpp.obj
    ***** .\BUILD\CMAKEFILES\LIB_HELLO_WORLD.DIR\LINK.TXT
    C:\PROGRA~1\MinGWx64\bin\ar.exe qc lib_hello_world.lib  CMakeFiles/lib_hello_world.dir/lib_hello_world.cpp.obj
    C:\PROGRA~1\MinGWx64\bin\ranlib.exe lib_hello_world.lib
    *****
    
NicoBerrogorry
  • 446
  • 4
  • 15
  • CMake is unaware about variable *CMAKE_LINKER*. See [that question](http://stackoverflow.com/questions/1867745/cmake-use-a-custom-linker) about setting the linker. – Tsyvarev May 03 '17 at 07:22
  • @Tsyvarev How can it be unaware about CMAKE_LINKER if I define it in "project_root\toolchain.cmake" (last line): '# LINKER SET(CMAKE_LINKER "$ENV{MINGW_W64_BIN_DIR}/ld.bfd.exe")'? – NicoBerrogorry May 03 '17 at 10:59
  • I should be able to specify the linker in the toolchain file, right? – NicoBerrogorry May 03 '17 at 11:04
  • I meant that CMake doesn't use *CMAKE_LINKER* variable **internally**: that name doesn't denote anything special for CMake. Again, see the referenced question in my previous comment about setting the linker. – Tsyvarev May 03 '17 at 11:17
  • How do you explain my fix working then? The CMakeCache does not change, only the generated files change after I remove everything but CMakeCache. My fix doesn't change the toolchain file nor the cmake parameters, but the second time the project is generated the link command is properly generated. I'm really lost and conviced that It's a bug, I hope I'm wrong and It's just something I need to learn. – NicoBerrogorry May 03 '17 at 23:07
  • 1
    Linker isn't used for building *STATIC* library, so it is probably unrelated to your problem. I just pointed that for future toolchain usage. – Tsyvarev May 04 '17 at 08:18
  • Thank you very much, just before I read your comment I was studying about how static libraries are linked, you made me realise I didn't know a bit about that. This was very useful! – NicoBerrogorry May 04 '17 at 19:22

1 Answers1

9

There is some specific about CMAKE_AR and CMAKE_RUNLIB variables: according to that bugreport they should be declared as CACHED:

SET(CMAKE_AR      "$ENV{MINGW_W64_BIN_DIR}/ar.exe" CACHE FILEPATH "Arhiver")
SET(CMAKE_RANLIB  "$ENV{MINGW_W64_BIN_DIR}/ranlib.exe" CACHE FILEPATH "Runlib")
Tsyvarev
  • 60,011
  • 17
  • 110
  • 153