0

Introduction

  • I'm trying to cross compile to Windows from Linux using MinGW-W64.
  • I had it working before I added enet to my project, however I'm now receiving issues with linking to enet
  • CMake finds enet correctly. ENET_LIBRARY and ENET_INCLUDE_DIR are set to the right locations.
  • ENet contains the symbols, as verified using /usr/x86_64-w64-mingw32/bin/objdump /usr/local/mingw64/lib/libenet.a -t
  • Build fails with "undefined reference to `enet_address_set_host'"
  • I'm able to compile the same code base natively using Visual Studio and VCPkg
  • Edit: Checking the contents of libenet.a verifies that it's a problem with cross-compiling enet, not my program in particular

None of my other dependencies use GNU autoconf, so I expect there's a problem there.

The Error

-- The C compiler identification is GNU 8.2.0
-- The CXX compiler identification is GNU 8.2.0
-- Check for working C compiler: /usr/bin/x86_64-w64-mingw32-gcc
-- Check for working C compiler: /usr/bin/x86_64-w64-mingw32-gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/x86_64-w64-mingw32-g++
-- Check for working CXX compiler: /usr/bin/x86_64-w64-mingw32-g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found SFML 2.5 in /usr/local/mingw64/include
-- Found SFGUI in /usr/local/mingw64/include
-- Found Thor in /usr/local/mingw64/include
-- Found Lua in /usr/local/mingw64/include/lua5.1/
-- Found Lua: /usr/local/mingw64/lib/liblua5.1.a  
-- Found ENet: /usr/local/mingw64/lib/libenet.a  
-- Found ENet in /usr/local/mingw64/include
-- Looking for pthread.h
-- Looking for pthread.h - found
-- Looking for pthread_create
-- Looking for pthread_create - found
-- Found Threads: TRUE  
/usr/local/mingw64/lib/liblua5.1.a/usr/local/mingw64/lib/libenet.awsock32ws2_32winmm
-- Adding executable: client (with server)
-- Configuring done
-- Generating done
-- Build files have been written to: /home/ruben/dev/rvwp
Scanning dependencies of target rvwp
[SNIP]
[ 98%] Building CXX object CMakeFiles/rvwp.dir/source/tests/t_chunk.cpp.obj
[100%] Linking CXX executable bin/rvwp.exe
/usr/lib/gcc/x86_64-w64-mingw32/8.2.0/../../../../x86_64-w64-mingw32/bin/ld: CMakeFiles/rvwp.dir/objects.a(address.cpp.obj):address.cpp:(.text+0xc6): undefined reference to `enet_address_set_host'
/usr/lib/gcc/x86_64-w64-mingw32/8.2.0/../../../../x86_64-w64-mingw32/bin/ld: CMakeFiles/rvwp.dir/objects.a(net.cpp.obj):net.cpp:(.text+0x105): undefined reference to `enet_initialize'
/usr/lib/gcc/x86_64-w64-mingw32/8.2.0/../../../../x86_64-w64-mingw32/bin/ld: CMakeFiles/rvwp.dir/objects.a(net.cpp.obj):net.cpp:(.text+0x204): undefined reference to `enet_packet_create'
/usr/lib/gcc/x86_64-w64-mingw32/8.2.0/../../../../x86_64-w64-mingw32/bin/ld: CMakeFiles/rvwp.dir/objects.a(net.cpp.obj):net.cpp:(.text+0x217): undefined reference to `enet_peer_send'
/usr/lib/gcc/x86_64-w64-mingw32/8.2.0/../../../../x86_64-w64-mingw32/bin/ld: CMakeFiles/rvwp.dir/objects.a(net.cpp.obj):net.cpp:(.text+0x29f): undefined reference to `enet_packet_create'
/usr/lib/gcc/x86_64-w64-mingw32/8.2.0/../../../../x86_64-w64-mingw32/bin/ld: CMakeFiles/rvwp.dir/objects.a(net.cpp.obj):net.cpp:(.text+0x2b3): undefined reference to `enet_peer_send'
/usr/lib/gcc/x86_64-w64-mingw32/8.2.0/../../../../x86_64-w64-mingw32/bin/ld: CMakeFiles/rvwp.dir/objects.a(net.cpp.obj):net.cpp:(.text+0x5af): undefined reference to `enet_host_destroy'
/usr/lib/gcc/x86_64-w64-mingw32/8.2.0/../../../../x86_64-w64-mingw32/bin/ld: CMakeFiles/rvwp.dir/objects.a(net.cpp.obj):net.cpp:(.text+0x65b): undefined reference to `enet_host_create'
/usr/lib/gcc/x86_64-w64-mingw32/8.2.0/../../../../x86_64-w64-mingw32/bin/ld: CMakeFiles/rvwp.dir/objects.a(net.cpp.obj):net.cpp:(.text+0x91f): undefined reference to `enet_host_create'
/usr/lib/gcc/x86_64-w64-mingw32/8.2.0/../../../../x86_64-w64-mingw32/bin/ld: CMakeFiles/rvwp.dir/objects.a(net.cpp.obj):net.cpp:(.text+0x950): undefined reference to `enet_host_connect'
/usr/lib/gcc/x86_64-w64-mingw32/8.2.0/../../../../x86_64-w64-mingw32/bin/ld: CMakeFiles/rvwp.dir/objects.a(net.cpp.obj):net.cpp:(.text+0x96c): undefined reference to `enet_host_service'
/usr/lib/gcc/x86_64-w64-mingw32/8.2.0/../../../../x86_64-w64-mingw32/bin/ld: CMakeFiles/rvwp.dir/objects.a(net.cpp.obj):net.cpp:(.text+0x1023): undefined reference to `enet_host_service'
/usr/lib/gcc/x86_64-w64-mingw32/8.2.0/../../../../x86_64-w64-mingw32/bin/ld: CMakeFiles/rvwp.dir/objects.a(net.cpp.obj):net.cpp:(.text+0x1437): undefined reference to `enet_packet_destroy'
/usr/lib/gcc/x86_64-w64-mingw32/8.2.0/../../../../x86_64-w64-mingw32/bin/ld: CMakeFiles/rvwp.dir/objects.a(net.cpp.obj):net.cpp:(.text+0x121): undefined reference to `enet_deinitialize'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/rvwp.dir/build.make:991: bin/rvwp.exe] Error 1
make[1]: *** [CMakeFiles/Makefile2:73: CMakeFiles/rvwp.dir/all] Error 2
make: *** [Makefile:130: all] Error 2

Cross-compiling Enet

To cross compile enet, I use the following script

#!/bin/bash

TOOLSET="x86_64-w64-mingw32"

wget http://enet.bespin.org/download/enet-1.3.13.tar.gz
tar -xzf enet-1.3.13.tar.gz
cd enet-1.3.13
./configure \
    --build=${TOOLSET} \
    --host=x86_64-windows \
    --target=${TOOLSET} \
    --prefix=/usr/local/mingw64 \
    --enable-shared
make -j3
sudo make install

My Program

I'm using cmake to generate the makefiles, and a toolchain to allow cross-compilation. The program compiles with SFML, thor, std::thread, and Lua fine. None of these libraries use GNU autoconf

My CMakeLists.txt looks like this:

find_package(ENet REQUIRED)
include_directories(${ENET_INCLUDE_DIR})

set(BASE_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} ${LUA_LIBRARY} ${CMAKE_DL_LIBS} ${ENET_LIBRARY})
set(BASE_LIBRARIES ${BASE_LIBRARIES} wsock32 ws2_32 winmm)
message(${BASE_LIBRARIES})
set(EXECUTABLE_NAME "rvwp")

add_executable(${EXECUTABLE_NAME} WIN32 ${CLIENT_SRC})
set_target_properties(${EXECUTABLE_NAME} PROPERTIES BUILD_WITH_INSTALL_RPATH true)

install(TARGETS ${EXECUTABLE_NAME} DESTINATION bin)
target_link_libraries(${EXECUTABLE_NAME} ${BASE_LIBRARIES} ${SFML_LIBRARIES} ${SFGUI_LIBRARY} ${THOR_LIBRARY})

The findENet file looks like this:

FIND_PATH(ENET_INCLUDE_DIR enet/enet.h
    PATHS
    $ENV{ENETDIR}
    /usr/local
    /usr
    PATH_SUFFIXES include)

FIND_LIBRARY(ENET_LIBRARY
    NAMES enet
    PATHS
    $ENV{ENETDIR}
    /usr/local
    /usr
    PATH_SUFFIXES lib)

INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(ENet DEFAULT_MSG ENET_LIBRARY ENET_INCLUDE_DIR)

IF (ENet_FOUND)
    MESSAGE(STATUS "Found ENet in ${ENET_INCLUDE_DIR}")
    IF(WIN32)
        SET(WINDOWS_ENET_DEPENDENCIES "ws2_32;winmm")
        SET(ENET_LIBRARIES ${ENET_LIBRARY} ${WINDOWS_ENET_DEPENDENCIES})
    ELSE(WIN32)
        SET(ENET_LIBRARIES ${ENET_LIBRARY})
    ENDIF(WIN32)
ENDIF (ENet_FOUND)

MARK_AS_ADVANCED(ENET_LIBRARY ENET_LIBRARIES ENET_INCLUDE_DIR)

The tool chain looks like this:

# Sample toolchain file for building for Windows from an Ubuntu Linux system.
#
# Typical usage:
#    *) install cross compiler: `sudo apt-get install mingw-w64 g++-mingw-w64`
#    *) cd build
#    *) cmake -DCMAKE_TOOLCHAIN_FILE=~/Toolchain-Ubuntu-mingw64.cmake ..

set(CMAKE_SYSTEM_NAME Windows)

set(TOOLCHAIN_PREFIX x86_64-w64-mingw32)

# cross compilers to use for C and C++
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++)
set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres)

SET( CMAKE_EXE_LINKER_FLAGS  "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++" )

# target environment on the build host system
#   set 1st to dir with the cross compiler's C/C++ headers/libs
set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX} /usr/local/mingw64 ./extlibs)

set(LUA_INCLUDE_DIR /usr/local/mingw64/include/lua5.1/)
set(LUA_LIBRARY /usr/local/mingw64/lib/liblua5.1.a)
set(OPENAL_LIBRARY /usr/local/mingw64/lib/libopenal32.a)

# modify default behavior of FIND_XXX() commands to
# search for headers/libs in the target environment and
# search for programs in the build host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

The linklibs.rsp file is used to pass linking commands to the linker, here is its value:

/usr/local/mingw64/lib/liblua5.1.a /usr/local/mingw64/lib/libenet.a -lwsock32 -lws2_32 -lwinmm /usr/local/mingw64/lib/libsfml-system.a /usr/local/mingw64/lib/libsfml-window.a /usr/local/mingw64/lib/libsfml-graphics.a /usr/local/mingw64/lib/libsfml-network.a /usr/local/mingw64/lib/libsfml-audio.a /usr/local/mingw64/bin/sfgui.dll /usr/local/mingw64/bin/libthor.dll -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 

(EDIT) AR Formats

Extracting the not-working libenet.a and using file results in this:

callbacks.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), with debug_info, not stripped

Extracting the working libsfml-graphics.a results in this:

d000508.o: Intel amd64 COFF object file, no line number info, not stripped, 5 sections, symbol offset=0x144, 8 symbols

So it appears that the problem is in compiling enet

rubenwardy
  • 679
  • 5
  • 21
  • Why cross-compile? Why not simply compile the code *on* the target platform? Use a VM or whatever. Path of least resistance. – Jesper Juhl Feb 21 '19 at 20:00
  • @JesperJuhl continuous integration – rubenwardy Feb 21 '19 at 20:01
  • CI systems can spin up VMs running different Operating Systems and build on those. We do that all the time at my workplace. CI is *not* a reason for cross-compiling. – Jesper Juhl Feb 21 '19 at 20:03
  • That sounds like a world of pain – rubenwardy Feb 21 '19 at 20:08
  • It's actually not. Once set up it's quite pain free and you get everything built on the native target OS using whatever compiler you want there and can use native tools to create packages/installers for that platform etc. It's actually quite nice. And post the initial set-up phase it can all be completely automated. Whenever I push something to our CI system it is automatically built on Linux, Windows, MacOs and the tests are run, installers generated and all I needed to do was "git push". – Jesper Juhl Feb 21 '19 at 20:14
  • Thanks for the advice, I think I'll try that after all. I'd still be interested in the answer to this question anyway, and it may help other people – rubenwardy Feb 21 '19 at 20:21
  • Turns out that ENet offers prebuilt libraries for this. It works when I use that. `wget http://enet.bespin.org/download/enet-1.3.13.tar.gz tar -xzf enet-1.3.13.tar.gz cd enet-1.3.13 sudo cp -r include/* /usr/local/mingw64/include/ sudo cp enet64.lib /usr/local/mingw64/lib/enet.lib` – rubenwardy Feb 21 '19 at 20:50
  • Can you use ENET without linking to it? In my case, would prefer to add the source/header files to our solution . found these macros ENET_DLL & ENET_BUILDING_LIB, but setting them to zero , I still get linker errors. is this even possible ? – notNullGothik Dec 17 '20 at 21:47

0 Answers0