From 0989f71876ea2aa2011211d8dac2a80d831e832e Mon Sep 17 00:00:00 2001 From: abeimler Date: Mon, 31 Oct 2022 17:11:26 +0100 Subject: [PATCH] feat: add cross compiler detection (#140) * add mingw toolchains * add docker for testing --- Taskfile.yml | 20 +++++ docker/Dockerfile | 39 ++++++++++ docker/Dockerfile.mingw | 32 ++++++++ docker/build.mingw.sh | 12 +++ docker/build.sh | 7 ++ docker/entrypoint.sh | 6 ++ docker/test.sh | 8 ++ src/CrossCompiler.cmake | 78 +++++++++++++++++++ src/Index.cmake | 1 + src/MinGW.cmake | 44 ++++++++--- src/Vcpkg.cmake | 12 +++ .../i686-w64-mingw32.toolchain.cmake | 29 +++++++ .../x86_64-w64-mingw32.toolchain.cmake | 29 +++++++ test/CMakeLists.txt | 5 ++ 14 files changed, 310 insertions(+), 12 deletions(-) create mode 100644 docker/Dockerfile create mode 100644 docker/Dockerfile.mingw create mode 100755 docker/build.mingw.sh create mode 100755 docker/build.sh create mode 100755 docker/entrypoint.sh create mode 100755 docker/test.sh create mode 100644 src/CrossCompiler.cmake create mode 100644 src/toolchains/i686-w64-mingw32.toolchain.cmake create mode 100644 src/toolchains/x86_64-w64-mingw32.toolchain.cmake diff --git a/Taskfile.yml b/Taskfile.yml index 22cc9667..204f791e 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -23,6 +23,26 @@ tasks: CWD: sh: git rev-parse --show-toplevel + test_docker: + # test gcc compiler + - docker build --build-arg compiler=gcc -f ./docker/Dockerfile --target build -t project_options:build . + - docker build --build-arg compiler=gcc -f ./docker/Dockerfile --target test -t project_options:test . + - docker run --rm -it project_options:build + - docker run --rm -it project_options:test + - docker rmi project_options:build project_options:test + # test llvm compiler + - docker build --build-arg compiler=llvm -f ./docker/Dockerfile --target build -t project_options:build-clang . + - docker build --build-arg compiler=llvm -f ./docker/Dockerfile --target test -t project_options:test-clang . + - docker run --rm -it project_options:build-clang + - docker run --rm -it project_options:test-clang + - docker rmi project_options:build-clang project_options:test-clang + test_docker_mingw: + # test mingw (cross) compiler + - docker build -f ./docker/Dockerfile.mingw --target build -t project_options:build-mingw . + - docker run --rm -it project_options:build-mingw + - docker run --rm -it --env CROSS_CC=i686-w64-mingw32-gcc --env CROSS_CXX=i686-w64-mingw32-gcc project_options:build-mingw + - docker rmi project_options:build-mingw + lint: - | {{if eq OS "windows"}} diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 00000000..0db49481 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,39 @@ +FROM ubuntu:20.04 AS base + +# add setup_cpp +ADD https://github.com/aminya/setup-cpp/releases/download/v0.21.0/setup_cpp_linux /setup_cpp_linux +RUN chmod +x /setup_cpp_linux + + +FROM base AS setup + +ARG compiler="gcc" +# install cmake, ninja, and ccache +RUN /setup_cpp_linux --compiler $compiler --llvm true --cmake true --ninja true --ccache true --doxygen true --cppcheck true --vcpkg true --conan true --task true + +# update vcpkg +#WORKDIR /root/vcpkg +#RUN git pull origin master +#RUN ./vcpkg update +#WORKDIR / + +COPY ./docker/entrypoint.sh /docker-entrypoint.sh +ENTRYPOINT [ "/docker-entrypoint.sh" ] + + +FROM setup AS build +COPY . /home/project_options +WORKDIR /home/project_options/test +CMD ["/home/project_options/docker/build.sh"] + + +FROM setup AS test +COPY . /home/project_options +WORKDIR /home/project_options/test +CMD ["/home/project_options/docker/test.sh"] + + +FROM gcr.io/distroless/cc AS runner +COPY --from=build /home/project_options/test/build/Release/ /home/app/ +WORKDIR /home/app/ +ENTRYPOINT ["./build/main"] \ No newline at end of file diff --git a/docker/Dockerfile.mingw b/docker/Dockerfile.mingw new file mode 100644 index 00000000..e76f849f --- /dev/null +++ b/docker/Dockerfile.mingw @@ -0,0 +1,32 @@ +FROM ubuntu:20.04 AS base + +# add setup_cpp +ADD https://github.com/aminya/setup-cpp/releases/download/v0.21.0/setup_cpp_linux /setup_cpp_linux +RUN chmod +x /setup_cpp_linux + + +FROM base AS setup + +# install cmake, ninja, and ccache +RUN /setup_cpp_linux --llvm true --cmake true --ninja true --ccache true --doxygen true --cppcheck true --vcpkg true --conan true --task true + +# TODO: install cross-compiler with setup_cpp_linux +# NOTE: install mingw by hand, waiting for setup-cpp to have mingw cross-compiler support +RUN apt-get update && apt-get install -y \ + mingw-w64 \ + && rm -rf /var/lib/apt/lists/* + +# update vcpkg +#WORKDIR /root/vcpkg +#RUN git pull origin master +#RUN ./vcpkg update +#WORKDIR / + +COPY ./docker/entrypoint.sh /docker-entrypoint.sh +ENTRYPOINT [ "/docker-entrypoint.sh" ] + + +FROM setup AS build +COPY . /home/project_options +WORKDIR /home/project_options/test +CMD ["/home/project_options/docker/build.mingw.sh"] diff --git a/docker/build.mingw.sh b/docker/build.mingw.sh new file mode 100755 index 00000000..cbc8375f --- /dev/null +++ b/docker/build.mingw.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# setup compiler +export CC=${CROSS_CC:-x86_64-w64-mingw32-gcc} +export CXX=${CROSS_CXX:-x86_64-w64-mingw32-g++} + +mkdir build +cd build + +cmake -B . -G "Ninja" -DCMAKE_BUILD_TYPE:STRING=Release \ + -DENABLE_CROSS_COMPILING:BOOL=ON .. +cmake --build . --config Release diff --git a/docker/build.sh b/docker/build.sh new file mode 100755 index 00000000..b96ad98a --- /dev/null +++ b/docker/build.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +mkdir build +cd build + +cmake -B . -G "Ninja" -DCMAKE_BUILD_TYPE:STRING=Release .. +cmake --build . --config Release diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100755 index 00000000..0631eaba --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -e + +source ~/.cpprc + +exec "$@" \ No newline at end of file diff --git a/docker/test.sh b/docker/test.sh new file mode 100755 index 00000000..aa8a2553 --- /dev/null +++ b/docker/test.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +mkdir build +cd build + +cmake -B . -G "Ninja" -DCMAKE_BUILD_TYPE:STRING=Debug .. +cmake --build . --config Debug +ctest -C Debug --verbose \ No newline at end of file diff --git a/src/CrossCompiler.cmake b/src/CrossCompiler.cmake new file mode 100644 index 00000000..8a700bd0 --- /dev/null +++ b/src/CrossCompiler.cmake @@ -0,0 +1,78 @@ +include_guard() + +macro(enable_cross_compiler) + include("${ProjectOptions_SRC_DIR}/Utilities.cmake") + detect_architecture(_arch) + if(NOT DEFINED TARGET_ARCHITECTURE) + if($ENV{CC} MATCHES "x86_64(-w64)?-mingw32-[gc]..?" OR $ENV{CXX} MATCHES "x86_64(-w64)?-mingw32-[gc]..?") + set(TARGET_ARCHITECTURE "x64") + elseif($ENV{CC} MATCHES "i686(-w64)?-mingw32-[gc]..?" OR $ENV{CXX} MATCHES "i686(-w64)?-mingw32-[gc]..?") + set(TARGET_ARCHITECTURE "x86") + elseif($ENV{CC} MATCHES "emcc" OR $ENV{CXX} MATCHES "em++") + set(TARGET_ARCHITECTURE "wasm32") + else() + # TODO: check for arm compiler + set(TARGET_ARCHITECTURE ${_arch}) + endif() + endif() + + if (NOT DEFINED HOST_TRIPLET) + if(WIN32) + set(HOST_TRIPLET "${_arch}-windows") + elseif(APPLE) + set(HOST_TRIPLET "${_arch}-osx") + elseif(UNIX AND NOT APPLE) + set(HOST_TRIPLET "${_arch}-linux") + endif() + endif() + + if($ENV{CC} MATCHES "(x86_64|i686)(-w64)?-mingw32-[gc]..?" OR $ENV{CXX} MATCHES "(x86_64|i686)(-w64)?-mingw32-[gc]..?") + set(MINGW TRUE) + elseif($ENV{CC} MATCHES "emcc" OR $ENV{CXX} MATCHES "em++") + set(EMSCRIPTEN TRUE) + endif() + + set(LIBRARY_LINKAGE) + if(BUILD_SHARED_LIBS) + set(LIBRARY_LINKAGE "dynamic") + else() + set(LIBRARY_LINKAGE "static") + endif() + + if (NOT DEFINED CROSS_ROOT) + if($ENV{CC} MATCHES "x86_64(-w64)?-mingw32-[gc]..?" OR $ENV{CXX} MATCHES "x86_64(-w64)?-mingw32-[gc]..?") + set(CROSS_ROOT "/usr/x86_64-w64-mingw32") + elseif($ENV{CC} MATCHES "i686(-w64)?-mingw32-[gc]..?" OR $ENV{CXX} MATCHES "i686(-w64)?-mingw32-[gc]..?") + set(CROSS_ROOT "/usr/i686-w64-mingw32") + endif() + # TODO: check if path is right, check for header files or something + endif() + + set(_toolchain_file) + get_toolchain_file(_toolchain_file) + set(CMAKE_TOOLCHAIN_FILE ${_toolchain_file}) + set(CROSSCOMPILING TRUE) +endmacro() + +function(get_toolchain_file value) + include("${ProjectOptions_SRC_DIR}/Utilities.cmake") + detect_architecture(_arch) + if(DEFINED TARGET_ARCHITECTURE) + set(_arch ${TARGET_ARCHITECTURE}) + endif() + if("${_arch}" MATCHES "x64") + set(_arch "x86_64") + elseif("${_arch}" MATCHES "x86") + set(_arch "x86_64") + endif() + + if (MINGW) + set(${value} + ${ProjectOptions_SRC_DIR}/toolchains/${_arch}-w64-mingw32.toolchain.cmake + PARENT_SCOPE) + elseif(EMSCRIPTEN) + set(${value} + "/usr/lib/emscripten/cmake/Modules/Platform/Emscripten.cmake" + PARENT_SCOPE) + endif() +endfunction() diff --git a/src/Index.cmake b/src/Index.cmake index 71c8f588..0d4e7295 100644 --- a/src/Index.cmake +++ b/src/Index.cmake @@ -24,6 +24,7 @@ include("${CMAKE_CURRENT_LIST_DIR}/StaticAnalyzers.cmake") include("${CMAKE_CURRENT_LIST_DIR}/VCEnvironment.cmake") include("${CMAKE_CURRENT_LIST_DIR}/MinGW.cmake") include("${CMAKE_CURRENT_LIST_DIR}/DetectCompiler.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/CrossCompiler.cmake") # Include msvc toolchain on windows if the generator is not visual studio. Should be called before run_vcpkg and run_conan to be effective msvc_toolchain() diff --git a/src/MinGW.cmake b/src/MinGW.cmake index 11b06a62..de9b5fe5 100644 --- a/src/MinGW.cmake +++ b/src/MinGW.cmake @@ -2,16 +2,25 @@ include_guard() # detect mingw function(is_mingw value) - if(NOT WIN32 OR MSVC) - set(${value} - OFF - PARENT_SCOPE) - return() + if(CROSSCOMPILING) + if(MINGW) + set(${value} + ON + PARENT_SCOPE) + return() + endif() + else() + if(NOT WIN32 OR MSVC) + set(${value} + OFF + PARENT_SCOPE) + return() + endif() endif() if(MINGW - OR ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") - OR ("${DETECTED_CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND "${DETECTED_CMAKE_C_COMPILER_ID}" STREQUAL "GNU")) + OR ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") + OR ("${DETECTED_CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND "${DETECTED_CMAKE_C_COMPILER_ID}" STREQUAL "GNU")) set(${value} ON PARENT_SCOPE) @@ -85,11 +94,18 @@ macro(configure_mingw_vcpkg) set(VCPKG_DEFAULT_TRIPLET "${_arch}-mingw-${MINGW_LINKAGE}" CACHE STRING "Default triplet for vcpkg") - set(VCPKG_DEFAULT_HOST_TRIPLET - "${_arch}-mingw-${MINGW_LINKAGE}" - CACHE STRING "Default target triplet for vcpkg") set($ENV{VCPKG_DEFAULT_TRIPLET} "${_arch}-mingw-${MINGW_LINKAGE}") - set($ENV{VCPKG_DEFAULT_HOST_TRIPLET} "${_arch}-mingw-${MINGW_LINKAGE}") + if(WIN32 AND NOT MSVC) + set(VCPKG_DEFAULT_HOST_TRIPLET + "${_arch}-mingw-${MINGW_LINKAGE}" + CACHE STRING "Default target triplet for vcpkg") + set($ENV{VCPKG_DEFAULT_HOST_TRIPLET} "${_arch}-mingw-${MINGW_LINKAGE}") + elseif (CROSSCOMPILING AND HOST_TRIPLET) + set(VCPKG_DEFAULT_HOST_TRIPLET + "${HOST_TRIPLET}" + CACHE STRING "Default target triplet for vcpkg") + set($ENV{VCPKG_DEFAULT_HOST_TRIPLET} "${HOST_TRIPLET}") + endif() endif() endmacro() @@ -102,7 +118,11 @@ macro(configure_mingw_vcpkg_after) include("${ProjectOptions_SRC_DIR}/Utilities.cmake") detect_architecture(_arch) string(TOLOWER "${_arch}" _arch) - set(Z_VCPKG_TARGET_TRIPLET_ARCH ${_arch}) + if (CROSSCOMPILING AND TARGET_ARCHITECTURE) + set(Z_VCPKG_TARGET_TRIPLET_ARCH ${TARGET_ARCHITECTURE}) + else() + set(Z_VCPKG_TARGET_TRIPLET_ARCH ${_arch}) + endif() set(VCPKG_TARGET_TRIPLET "${Z_VCPKG_TARGET_TRIPLET_ARCH}-${Z_VCPKG_TARGET_TRIPLET_PLAT}" diff --git a/src/Vcpkg.cmake b/src/Vcpkg.cmake index 4f689c29..ecca3a9a 100644 --- a/src/Vcpkg.cmake +++ b/src/Vcpkg.cmake @@ -111,4 +111,16 @@ macro(run_vcpkg) CACHE STRING "vcpkg toolchain file") configure_mingw_vcpkg_after() + + if(CROSSCOMPILING) + set(_toolchain_file) + get_toolchain_file(_toolchain_file) + if(_toolchain_file) + set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE + ${_toolchain_file} + CACHE STRING "vcpkg chainload toolchain file") + message(STATUS "Setup cross-compiler for ${VCPKG_TARGET_TRIPLET}") + message(STATUS "Use cross-compiler toolchain: ${VCPKG_CHAINLOAD_TOOLCHAIN_FILE}") + endif() + endif() endmacro() diff --git a/src/toolchains/i686-w64-mingw32.toolchain.cmake b/src/toolchains/i686-w64-mingw32.toolchain.cmake new file mode 100644 index 00000000..515fd7ec --- /dev/null +++ b/src/toolchains/i686-w64-mingw32.toolchain.cmake @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.16) + +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR "i686") + +if ($ENV{CROSS_ROOT}) + #set(CMAKE_SYSROOT $ENV{CROSS_ROOT}) + set(CMAKE_FIND_ROOT_PATH $ENV{CROSS_ROOT}) +elseif (DEFINED CROSS_ROOT) + #set(CMAKE_SYSROOT ${CROSS_ROOT}) + set(CMAKE_FIND_ROOT_PATH ${CROSS_ROOT}) +else() + #set(CMAKE_SYSROOT /usr/i686-w64-mingw32) + set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32) +endif() + +set(CMAKE_C_COMPILER i686-w64-mingw32-gcc) +set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) +set(CMAKE_RC_COMPILER i686-w64-mingw32-windres) + +# search for programs in the build host directories +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# for libraries and headers in the target directories +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# override boost thread component suffix as mingw-w64-boost is compiled with threadapi=win32 +set(Boost_THREADAPI win32) \ No newline at end of file diff --git a/src/toolchains/x86_64-w64-mingw32.toolchain.cmake b/src/toolchains/x86_64-w64-mingw32.toolchain.cmake new file mode 100644 index 00000000..12dd0b63 --- /dev/null +++ b/src/toolchains/x86_64-w64-mingw32.toolchain.cmake @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.16) + +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_PROCESSOR "x64") + +if ($ENV{CROSS_ROOT}) + #set(CMAKE_SYSROOT $ENV{CROSS_ROOT}) + set(CMAKE_FIND_ROOT_PATH $ENV{CROSS_ROOT}) +elseif (DEFINED CROSS_ROOT) + #set(CMAKE_SYSROOT ${CROSS_ROOT}) + set(CMAKE_FIND_ROOT_PATH ${CROSS_ROOT}) +else() + #set(CMAKE_SYSROOT /usr/x86_64-w64-mingw32) + set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32) +endif() + +set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) +set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) +set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) + +# search for programs in the build host directories +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# for libraries and headers in the target directories +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# override boost thread component suffix as mingw-w64-boost is compiled with threadapi=win32 +set(Boost_THREADAPI win32) \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5c9f9820..958871ba 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,6 +11,11 @@ set(CMAKE_CXX_STANDARD 20) # include(${_project_options_SOURCE_DIR}/Index.cmake) include(../src/Index.cmake) +# opt-in cross-compiling +option(ENABLE_CROSS_COMPILING "Detect cross compiler and setup toolchain" OFF) +if(ENABLE_CROSS_COMPILING) + enable_cross_compiler() +endif() run_vcpkg() project(