Getting a linker error on one of the dll on windows-latest

So I’m working on a project that has build&test on ubuntu-latest and windows-latest.

Ubuntu-latest workflow works without a problem, but the windows-latest returns a linker error:

2024-11-05T10:50:55.6113330Z LINK : fatal error LNK1181: cannot open input file '..\lib\Release\wing_workshop_lib.lib' [D:\a\...\build\apps\WingWorkshop.vcxproj]

Files

I’m not sure what the problem is. But, this is my workflow for windows:

name: CMake build and test on Windows

on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]

env:
  BUILD_TYPE: Release

jobs:
  build:
    name: build and test
    runs-on: windows-latest

    steps:
      - uses: actions/checkout@v4

      - name: Cache dependencies and build tools
        uses: actions/cache@v3
        with:
          path: |
            ${{ github.workspace }}/build/_deps
            C:/ProgramData/ccache
          key: deps-${{ runner.os }}-${{ hashFiles('CMakeLists.txt') }}
          restore-keys: |
            deps-${{ runner.os }}-

      # Install ccache (only runs if cache not found)
      - name: Install ccache
        if: steps.cache.outputs.cache-hit != 'true'
        run: choco install ccache -y

      - name: Configure CMake
        run: >
          cmake -B build
          -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
          -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}
          -S ${{ github.workspace }}

      - name: Build
        run: cmake --build build --config ${{ env.BUILD_TYPE }}

      - name: Test
        run: ctest --test-dir build -C ${{ env.BUILD_TYPE }} -L WW --verbose

While my root cmake is:

cmake_minimum_required(VERSION 3.20)
project(WingWorkshop
        VERSION 1.0.0
        LANGUAGES CXX)

# Only do these if this is the main project, and not if it is included through add_subdirectory
if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
  # Optionally set things like CMAKE_CXX_STANDARD, CMAKE_POSITION_INDEPENDENT_CODE here
  set(CMAKE_CXX_STANDARD 20)
  set(CMAKE_CXX_STANDARD_REQUIRED ON)
  IF (WIN32)
    # set stuff for windows
    set(CMAKE_CXX_FLAGS "-Wall ${CMAKE_CXX_FLAGS}")
  ELSE ()
    set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-deprecated-copy -Wno-maybe-uninitialized -Wno-unused -pedantic -O3 -fprofile-arcs -ftest-coverage -Wno-error=float-conversion -fPIC ${CMAKE_CXX_FLAGS}")
  ENDIF ()
  # Let's ensure -std=c++xx instead of -std=g++xx
  set(CMAKE_CXX_EXTENSIONS OFF)

  # Let's nicely support folders in IDEs
  set_property(GLOBAL PROPERTY USE_FOLDERS ON)

  # Find ccache in the system
  find_program(CCACHE_PROGRAM ccache)

  # If ccache is found, use it as the compiler launcher
  if(CCACHE_PROGRAM)
    message(STATUS "ccache found: ${CCACHE_PROGRAM}")
    set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PROGRAM})  # For C++ compiler
  else()
    message(STATUS "ccache not found, proceeding without it.")
  endif()

  # Testing only available if this is the main app
  # Note this needs to be done in the main CMakeLists
  # since it calls enable_testing, which must be in the
  # main CMakeLists.
  include(CTest)
endif ()

add_compile_definitions(_USE_MATH_DEFINES)

# Include CPM for dependency management
include(cmake/CPM.cmake)

# Use CPM to fetch spdlog
CPMAddPackage(
        NAME spdlog
        GITHUB_REPOSITORY gabime/spdlog
        VERSION 1.14.1
        OPTIONS "SPDLOG_BUILD_SHARED OFF"  # Ensure static library
)
# Use CPM to fetch cxxopts
CPMAddPackage(
        NAME cxxopts
        GITHUB_REPOSITORY jarro2783/cxxopts
        VERSION 3.0.0
)
# Use CPM to fetch openxlsx
CPMAddPackage(
        NAME OpenXLSX
        GITHUB_REPOSITORY troldal/OpenXLSX
        GIT_TAG ededa1e8398305d0983648d3e9f120ff7e228d2b
        OPTIONS "OPENXLSX_BUILD_DOCS=OFF" "OPENXLSX_BUILD_TESTS=OFF"
)
# Use CPM to fetch json
CPMAddPackage(
        NAME nlohmann_json
        GITHUB_REPOSITORY nlohmann/json
        GIT_TAG v3.11.2 # You can change this to the latest version if needed
)

# Add Eigen using CPM
CPMAddPackage(
        NAME Eigen
        GITLAB_REPOSITORY libeigen/eigen
        GIT_TAG 3.4
        OPTIONS
        "EIGEN_BUILD_DOC OFF"           # Do not build documentation
        "EIGEN_BUILD_PKGCONFIG OFF"     # Optional, disables pkg-config
        "BUILD_TESTING OFF"             # Disable building and running tests
)

# CSV parser.
# Add csv-parser using CPM
CPMAddPackage(
        NAME csv-parser
        GITHUB_REPOSITORY vincentlaucsb/csv-parser
        GIT_TAG 2.3.0  # Replace with the appropriate version you want
        OPTIONS "CMAKE_POSITION_INDEPENDENT_CODE ON"
)
# Add PMP library using CPM
# Set EIGEN3_INCLUDE_DIR if it is not already defined
if (NOT DEFINED EIGEN3_INCLUDE_DIR)
  set(EIGEN3_INCLUDE_DIR "${Eigen_SOURCE_DIR}/")
endif()
find_package(Eigen 3.4.0 REQUIRED)
# Check if PMP is already included with CPM
CPMAddPackage(
        NAME pmp
        GITHUB_REPOSITORY pmp-library/pmp-library
        GIT_TAG 3.0.0  # Specify the version you need, or leave it out for the latest
        OPTIONS
        "PMP_BUILD_TESTS OFF"      # Prevents building tests
        "PMP_BUILD_EXAMPLES OFF"   # Prevents building examples
        "PMP_BUILD_DOCS OFF"       # Prevents building documentation
        "PMP_BUILD_VIS OFF"
)

# Pass version number to the code
add_definitions(-DWINGWORKSHOP_VERSION="${PROJECT_VERSION}")

# Subdirectories
add_subdirectory(apps)
add_subdirectory(src)
add_subdirectory(tests)

and my src/CMakeLists:

file(GLOB_RECURSE SOURCES "*.cpp")

# Create the library from the collected source files
add_library(wing_workshop_lib ${SOURCES})

add_library(wing_workshop::lib ALIAS wing_workshop_lib)

target_include_directories(wing_workshop_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../include)
target_compile_features(wing_workshop_lib PUBLIC cxx_std_20)

target_link_libraries(wing_workshop_lib PUBLIC spdlog::spdlog cxxopts OpenXLSX::OpenXLSX nlohmann_json Eigen3::Eigen csv pmp)

set_target_properties(wing_workshop_lib PROPERTIES
        ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
        LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
        RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)

# IDEs should put the headers in a nice place
source_group(
        TREE "${PROJECT_SOURCE_DIR}/include"
        PREFIX "Header Files"
        FILES ${HEADER_LIST})

and finally my apps/CMakeLists.txt:

add_executable(WingWorkshop app.cpp)
target_link_libraries(WingWorkshop PRIVATE wing_workshop_lib spdlog::spdlog nlohmann_json)

set_target_properties(WingWorkshop PROPERTIES CXX_STANDARD 20)

target_compile_options(WingWorkshop PUBLIC "$<$<COMPILE_LANG_AND_ID:CXX,MSVC>:/permissive->")

set_target_properties(WingWorkshop PROPERTIES
        RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)

Again note that it seems to build on ubuntu-latest but not on windows.

What is really the problem?

I don’t really see what the problem would be and how to fix it. The wing_workshop_lin.dll is created and is added into build/bin directory. So at least it is created.

But remember that

2024-11-05T10:50:55.6113330Z LINK : fatal error LNK1181: cannot open input file '..\lib\Release\wing_workshop_lib.lib' [D:\a\...\build\apps\WingWorkshop.vcxproj]

So the linker looks for the dll in lib/ folder. I don’t get it.

You say that wing_workshop_lin.dll is created, but the error is regarding wing_workshop_lib.lib. My understanding is that

  1. Build-time linking with CMake on Windows is looking for a .lib
  2. A .lib for a corresponding .dll (for a SHARED CMake library target) requires explicitly exporting symbols (see /EXPORT (Exports a Function) | Microsoft Learn and https://cmake.org/cmake/help/latest/module/GenerateExportHeader.html)

It’s going to be a little harder to troubleshoot without being explicit about the type of the wing_workshop_lib library target. Try explicitly setting STATIC, SHARED, MODULE, or OBJECT, and refer to https://cmake.org/cmake/help/latest/command/add_library.html for help figuring out which type makes the most sense for you.

If the library is only used in the build directory (by the WingWorkshop executable) then a STATIC or OBJECT target might make the most sense. If you go with MODULE, I think you’d be using dlopen in your code and not calling target_link_libraries. If you go with SHARED, you’ll have to look more closely at the peculiarities of shared library linking on Windows.

1 Like