Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
280 views
in Technique[技术] by (71.8m points)

c++ - CMake 3.8.0 generates wrong link command in makefiles

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 FilesCMakeincmake.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
CMakeFileshello_wsl.diruild.make:93: recipe for target 'hello_wsl.lib' failed
mingw32-make.exe[2]: *** [hello_wsl.lib] Error 2
CMakeFilesMakefile2: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_rootuildCMakeFileshello_wsl.dirlink.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_rootuild
    2. project_rootinclude
    3. project_rootincludelib_hello_world
  • Create these files:

    1. project_rootincludelib_hello_worldlib_hello_world.cpp:

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

      cmake_minimum_required(VERSION 3.8.0)
      project(lib_hello_world)
      
      add_library(lib_hello_world STATIC lib_hello_world.cpp)
      
    3. project_rootoolchain.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_rootconfigure.bat:

      @ECHO OFF
      SETLOCAL
      @ECHO OFF
      REM Change these variables to the corresponding paths on your own system
      SET "CMAKE_EXECUTABLE=C:Program FilesCMakeincmake.exe"
      SET "MINGW_W64_BIN_DIR=C:/Program Files/MinGWx64/bin"
      CD "%~dp0uild"
      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_rootuild.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 "%~dp0uild"
      CALL "%MINGW_W64_BIN_DIR%/mingw32-make.exe"
      ENDLOCAL
      
  • Finally, open CMD and run the commands:

    project_rootconfigure.bat
    project_rootuild.bat
    

This is a sketchy fix/workaround that I found:

  • Create this file: project_rootfix.bat:

    @ECHO OFF
    SETLOCAL
    @ECHO OFF
    
    MOVE "%~dp0uildCMakeCache.txt" "."
    RMDIR "%~dp0uild" /S /Q
    MKDIR "%~dp0uild"
    MOVE "%~dp0CMakeCache.txt" ".uild"
    
    ENDLOCAL
    
  • After reproducing the error, open CMD and run:

    project_rootfix.bat
    project_rootconfigure.bat
    project_rootuild.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 ules.ninja.before_fix" "project_rootuild ules.ninja":

    Comparando archivos .
    ules.ninja.before_fix y .BUILDRULES.NINJA
    ***** .
    ules.ninja.before_fix
    rule CXX_STATIC_LIBRARY_LINKER__lib_hello_world
    command = cmd.exe /C "$PRE_LINK && "C:Program FilesCMakeincmake.exe" -E remove $TARGET_FILE && "" qc $TARGET_FILE $LINK_
    FLAGS $in && $POST_BUILD"
    description = Linking CXX static library $TARGET_FILE
    ***** .BUILDRULES.NINJA
    rule CXX_STATIC_LIBRARY_LINKER__lib_hello_world
    command = cmd.exe /C "$PRE_LINK && "C:Program FilesCMakeincmake.exe" -E remove $TARGET_FILE && C:PROGRA~1MinGWx64in
    ar.exe qc $TARGET_FILE $LINK_FLAGS $in && C:PROGRA~1MinGWx64in
    anlib.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_rootlink.txt.before_fix" "project_rootuildCMakeFileslib_hello_world.dirlink.txt":

    Comparando archivos .link.txt.before_fix y .BUILDCMAKEFILESLIB_HELLO_WORLD.DIRLINK.TXT
    ***** .link.txt.before_fix
    "" qc hello_wsl.lib  CMakeFiles/hello_wsl.dir/lib_hello_world.cpp.obj
    ***** .BUILDCMAKEFILESLIB_HELLO_WORLD.DIRLINK.TXT
    C:PROGRA~1MinGWx64inar.exe qc lib_hello_world.lib  CMakeFiles/lib_hello_world.dir/lib_hello_world.cpp.obj
    C:PROGRA~1MinGWx64in
    anlib.exe lib_hello_world.lib
    *****
    
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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")

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...