├── .gitignore ├── modules ├── Platform │ ├── iOS.cmake │ └── Emscripten.cmake ├── UseEmscripten.cmake ├── UseMingw.cmake └── UseAndroid.cmake ├── COPYING ├── archlinux ├── basic-mingw-w64-32.cmake └── basic-mingw-w64-64.cmake ├── generic ├── iOS.cmake └── Emscripten-wasm.cmake └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /modules/Platform/iOS.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Platform file for generic/iOS.cmake toolchain 3 | # 4 | 5 | # It's just Darwin after all 6 | include(Platform/Darwin) 7 | -------------------------------------------------------------------------------- /modules/UseEmscripten.cmake: -------------------------------------------------------------------------------- 1 | # - Macros for simplifying development with Emscripten 2 | # 3 | # Embed file into the virtual filesystem of given executable 4 | # emscripten_embed_file(MyExecutable 5 | # path/to/file 6 | # /path/in/virtual/filesystem) 7 | # File path is absolute or relative to current source directory, path in 8 | # virtual filesystem should start with `/`. Supports both files and 9 | # directories. Useful e.g. in case of unit tests where you are testing output 10 | # against some external file and manually embedding that file just to test on 11 | # Emscripten is just not worth it. 12 | # 13 | 14 | function(emscripten_embed_file target file destination) 15 | get_filename_component(absolute_file ${file} ABSOLUTE) 16 | get_target_property(${target}_LINK_FLAGS ${target} LINK_FLAGS) 17 | if(NOT ${target}_LINK_FLAGS) 18 | set(${target}_LINK_FLAGS ) 19 | endif() 20 | set_target_properties(${target} PROPERTIES LINK_FLAGS "${${target}_LINK_FLAGS} --embed-file \"${absolute_file}@${destination}\"") 21 | endfunction() 22 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute 4 | this software, either in source code form or as a compiled binary, for any 5 | purpose, commercial or non-commercial, and by any means. 6 | 7 | In jurisdictions that recognize copyright laws, the author or authors of 8 | this software dedicate any and all copyright interest in the software to 9 | the public domain. We make this dedication for the benefit of the public 10 | at large and to the detriment of our heirs and successors. We intend this 11 | dedication to be an overt act of relinquishment in perpetuity of all 12 | present and future rights to this software under copyright law. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | For more information, please refer to 22 | -------------------------------------------------------------------------------- /archlinux/basic-mingw-w64-32.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Toolchain for crosscompiling 32bit Windows applications on ArchLinux. 3 | # 4 | # Minimal dependencies: 5 | # 6 | # mingw-w64-gcc 7 | # 8 | 9 | # Target system name 10 | set(CMAKE_SYSTEM_NAME Windows) 11 | 12 | # Compilers and utilities 13 | set(CMAKE_C_COMPILER i686-w64-mingw32-gcc) 14 | set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++) 15 | set(CMAKE_RC_COMPILER i686-w64-mingw32-windres) 16 | 17 | # Croscompiler path 18 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} 19 | /usr/i686-w64-mingw32) 20 | 21 | # This helps find_path(), otherwise it's not able to find e.g. 22 | # /usr/i686-w64-mingw32/share/cmake/Corrade/. I don't know why it's needed when 23 | # there is already CMAKE_FIND_ROOT_PATH, but probably related to 24 | # https://public.kitware.com/Bug/view.php?id=14337 25 | set(CMAKE_PREFIX_PATH /usr/i686-w64-mingw32) 26 | 27 | # Find executables in root path, libraries and includes are in crosscompiler 28 | # path 29 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 30 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 31 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 32 | 33 | # Directories where to search for DLLs 34 | set(MINGW_PREFIX /usr/i686-w64-mingw32) 35 | set(DLL_SEARCH_PATH 36 | ${MINGW_PREFIX}/lib 37 | ${MINGW_PREFIX}/bin) 38 | -------------------------------------------------------------------------------- /archlinux/basic-mingw-w64-64.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Toolchain for crosscompiling 64bit Windows applications on ArchLinux. 3 | # 4 | # Minimal dependencies: 5 | # 6 | # mingw-w64-gcc 7 | # 8 | 9 | # Target system name 10 | set(CMAKE_SYSTEM_NAME Windows) 11 | 12 | # Compilers and utilities 13 | set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) 14 | set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) 15 | set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) 16 | 17 | # Croscompiler path 18 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} 19 | /usr/x86_64-w64-mingw32) 20 | 21 | # This helps find_path(), otherwise it's not able to find e.g. 22 | # /usr/x86_64-w64-mingw32/share/cmake/Corrade/. I don't know why it's needed when 23 | # there is already CMAKE_FIND_ROOT_PATH, but probably related to 24 | # https://public.kitware.com/Bug/view.php?id=14337 25 | set(CMAKE_PREFIX_PATH /usr/x86_64-w64-mingw32) 26 | 27 | # Find executables in root path, libraries and includes are in crosscompiler 28 | # path 29 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 30 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 31 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 32 | 33 | # Directories where to search for DLLs 34 | set(MINGW_PREFIX /usr/x86_64-w64-mingw32) 35 | set(DLL_SEARCH_PATH 36 | ${MINGW_PREFIX}/lib 37 | ${MINGW_PREFIX}/bin) 38 | -------------------------------------------------------------------------------- /generic/iOS.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Toolchain for cross-compiling to iOS using Xcode 3 | # 4 | # Set CMAKE_OSX_ROOT to SDK you want to target and enable all desired 5 | # architectures in CMAKE_OSX_ARCHITECTURES. 6 | # 7 | # mkdir build-ios && cd build-ios 8 | # cmake .. \ 9 | # -DCMAKE_TOOLCHAIN_FILE=../toolchains/generic/iOS.cmake \ 10 | # -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk \ 11 | # -DCMAKE_OSX_ARCHITECTURES="arm64;armv7;armv7s" -G Xcode 12 | # 13 | 14 | # We just need to name the platform differently so CMAKE_CROSSCOMPILING is set 15 | set(CMAKE_SYSTEM_NAME "iOS") 16 | 17 | # Help CMake find the platform file 18 | set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../modules ${CMAKE_MODULE_PATH}) 19 | 20 | # Required to make CMake's test_compile stuff pass 21 | # https://cmake.org/Bug/view.php?id=15329 22 | set(CMAKE_MACOSX_BUNDLE ON) 23 | set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") 24 | 25 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} 26 | ${CMAKE_OSX_SYSROOT} 27 | ${CMAKE_OSX_SYSROOT}/../.. # XCTest framework is there, e.g. 28 | ${CMAKE_PREFIX_PATH} 29 | ${CMAKE_INSTALL_PREFIX}) 30 | 31 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 32 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 33 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 34 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 35 | 36 | # iOS has pthreads too, only CMake doesn't know that 37 | # source: https://stackoverflow.com/a/54606606 38 | set(CMAKE_THREAD_LIBS_INIT "-lpthread") 39 | set(CMAKE_HAVE_THREADS_LIBRARY 1) 40 | set(CMAKE_USE_WIN32_THREADS_INIT 0) 41 | set(CMAKE_USE_PTHREADS_INIT 1) 42 | set(THREADS_PREFER_PTHREAD_FLAG ON) 43 | -------------------------------------------------------------------------------- /modules/UseMingw.cmake: -------------------------------------------------------------------------------- 1 | # - Macros for simplifying development with Mingw32 2 | # 3 | # Find and install DLLs for bundling with Windows build. 4 | # corrade_bundle_dlls(library_install_dir 5 | # dlls... 6 | # [PATHS paths...]) 7 | # It is possible to specify also additional paths for searching. DLL names can 8 | # also contain paths, they will be installed into exact specified path. If an 9 | # DLL is not found, fatal error message is printed. 10 | # 11 | 12 | function(mingw_bundle_dlls library_install_dir) 13 | # Default search path, taken from mingw32.cmake toolchain 14 | if(DLL_SEARCH_PATH) 15 | set(paths ${DLL_SEARCH_PATH}) 16 | endif() 17 | 18 | # Get DLL and path lists 19 | foreach(arg ${ARGN}) 20 | if(${arg} STREQUAL PATHS) 21 | set(__DOING_PATHS ON) 22 | else() 23 | if(__DOING_PATHS) 24 | set(paths ${paths} ${arg}) 25 | else() 26 | set(dlls ${dlls} ${arg}) 27 | endif() 28 | endif() 29 | endforeach() 30 | 31 | # Find and install all DLLs 32 | foreach(dll ${dlls}) 33 | # Separate filename from path 34 | get_filename_component(path ${dll} PATH) 35 | get_filename_component(filename ${dll} NAME) 36 | 37 | # Add current DLL's path to search paths 38 | foreach(_path ${paths}) 39 | set(${dll}_paths ${${dll}_paths} ${_path}/${path}) 40 | endforeach() 41 | 42 | find_file(${dll}_found ${filename} PATHS ${${dll}_paths} 43 | NO_DEFAULT_PATH) 44 | 45 | if(${dll}_found) 46 | install(FILES ${${dll}_found} DESTINATION ${library_install_dir}/${path}) 47 | else() 48 | message(FATAL_ERROR "DLL ${dll} needed for bundle not found!") 49 | endif() 50 | endforeach() 51 | endfunction() 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository contains toolchains usable for crosscompiling with CMake. 2 | They're tailored for and mainly used by [Magnum](https://github.com/mosra/magnum) 3 | but should work for other projects as well. 4 | 5 | USAGE 6 | ===== 7 | 8 | Assuming a CMake project, point `CMAKE_TOOLCHAIN_FILE` to one of the toolchain 9 | files during the initial CMake invocation. While relative paths may work in 10 | certain cases, it's better to always pass an absolute path. The 11 | `CMAKE_TOOLCHAIN_FILE` variable is only used by CMake during the initial run, 12 | it's ignored (and thus doesn't even need to be specified) in subsequent runs. 13 | 14 | mkdir build-emscripten && cd build-emscripten 15 | cmake .. -DCMAKE_TOOLCHAIN_FILE=path/to/toolchains/generic/Emscripten-wasm.cmake 16 | 17 | The toolchain file then sets up other paths (such as pointing 18 | `CMAKE_MODULE_PATH` to the `modules/Platform/` directory) and the process will 19 | result in a configured build directory that can be subsequently used as any 20 | other. See comments in particular toolchain files for platform-specific 21 | details. 22 | 23 | CONTACT & SUPPORT 24 | ================= 25 | 26 | This project is maintained as part of Magnum, so it shares the same suppport 27 | channels: 28 | 29 | - Project homepage — https://magnum.graphics/ 30 | - Documentation — https://doc.magnum.graphics/ 31 | - GitHub — https://github.com/mosra/toolchains and the 32 | [#magnum](https://github.com/topics/magnum) topic 33 | - GitLab — https://gitlab.com/mosra/toolchains 34 | - Gitter community chat — https://gitter.im/mosra/magnum 35 | - E-mail — info@magnum.graphics 36 | - Google Groups mailing list — magnum-engine@googlegroups.com 37 | ([archive](https://groups.google.com/forum/#!forum/magnum-engine)) 38 | - Twitter — https://twitter.com/czmosra and the 39 | [#MagnumEngine](https://twitter.com/hashtag/MagnumEngine) hashtag 40 | 41 | See also the Magnum Project [Contact & Support page](https://magnum.graphics/contact/) 42 | for further information. 43 | 44 | LICENSE 45 | ======= 46 | 47 | While Magnum itself and its documentation are licensed under the MIT/Expat 48 | license, the toolchains are put into public domain (or UNLICENSE) to free you 49 | from any legal obstacles when using these to build your apps. See the 50 | [COPYING](COPYING) file for details. 51 | -------------------------------------------------------------------------------- /modules/Platform/Emscripten.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Platform file for generic/Emscripten.cmake toolchain 3 | # 4 | 5 | # Start from something sane and working 6 | include(Platform/Linux) 7 | 8 | if(_EMSCRIPTEN_INCLUDED) 9 | return() 10 | endif() 11 | set(_EMSCRIPTEN_INCLUDED 1) 12 | 13 | # Set a global EMSCRIPTEN variable that can be used in client CMakeLists.txt to 14 | # detect when building using Emscripten. The go-to way when using Corrade is 15 | # to check CORRADE_TARGET_* variables, this is mainly to support 3rd party 16 | # projects. It's set as INTERNAL to not be exposed through cmake-gui or ccmake 17 | # which suggests it could be switched off. 18 | set(EMSCRIPTEN ON CACHE INTERNAL "If true, we are targeting Emscripten output.") 19 | 20 | # Make sure user-specified EMSCRIPTEN_PREFIX is propagated to try_compile() 21 | # that's executed by CMakeTestCCompiler.cmake inside a project() call. Without 22 | # that, the toolchain may fail to find the prefix even though EMSCRIPTEN_PREFIX 23 | # is properly set in the outer scope. 24 | set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES EMSCRIPTEN_PREFIX) 25 | 26 | # Prefixes/suffixes for building. This used to be no prefix and *.bc, but as of 27 | # 3.1.52 the *.bc extension is recognized as something different, resulting in 28 | # lib.bc:1:2: error: expected integer 29 | # 1 | ! 30 | # | ^ 31 | # https://github.com/emscripten-core/emscripten/issues/21128 32 | set(CMAKE_STATIC_LIBRARY_PREFIX "lib") 33 | set(CMAKE_STATIC_LIBRARY_SUFFIX ".a") 34 | set(CMAKE_EXECUTABLE_SUFFIX ".js") 35 | 36 | # Prefixes/suffixes for finding libraries. Still recognizing the no-prefix and 37 | # `*.bc` suffix for backwards compatibility, but note that 3.1.52+ will fail to 38 | # use such files. 39 | set(CMAKE_FIND_LIBRARY_PREFIXES ";lib") 40 | set(CMAKE_FIND_LIBRARY_SUFFIXES ".bc;.a") 41 | 42 | # Disable annoying warning about absolute includes 43 | string(APPEND CMAKE_CXX_FLAGS " -Wno-warn-absolute-paths") 44 | 45 | # Setting this prevents CMake from doing a relink on install (that is because 46 | # of RPath, which is of no use on this platform). This also allows us to use 47 | # Ninja, finally. 48 | set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) 49 | 50 | # Emscripten has Clang underneath and it supports -isystem. For some reason the 51 | # compiler autodetection fails to detect this and this cause overwhelming 52 | # amount of spam messages when including third-party headers. 53 | set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-isystem") 54 | 55 | # With Emscripten copying all its includes (but not libraries!) into the cache 56 | # on 2.0.13, CMake configs (such as zstd's) have to be put into share/ (instead 57 | # of lib/cmake/, which also gets copied) in the system directory in order to be 58 | # able to locate the libraries. That however means the imported targets from 59 | # those configs will use the system include directory, and passing it to the 60 | # include path would result in issues with again. To fix 61 | # that, this directory is marked as implicit. Which it actually isn't, but that 62 | # doesn't matter, since the exact contents should be in its copy in the cache, 63 | # which *is* among implicit include directories. 64 | # 65 | # This is slightly (but not completely) related to 66 | # https://github.com/emscripten-core/emscripten/issues/17132 67 | # with the difference that there it caused an ordering issue with the 68 | # `include/common`, while here is no ordering issue but it causes the wrong 69 | # to be picked. In other words, again NONE of this crap 70 | # would be needed if the doomed-to-fail version.h wasn't in the system include 71 | # dir at all. 72 | if(NOT EMSCRIPTEN_VERSION VERSION_LESS 2.0.13) 73 | list(APPEND CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES ${EMSCRIPTEN_PREFIX}/system/include) 74 | list(APPEND CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES ${EMSCRIPTEN_PREFIX}/system/include) 75 | endif() 76 | 77 | # Include the UseEmscripten file with useful macros 78 | include(UseEmscripten) 79 | -------------------------------------------------------------------------------- /modules/UseAndroid.cmake: -------------------------------------------------------------------------------- 1 | # - Android tools for CMake 2 | # 3 | # android_create_apk( 4 | # [RESOURCE_DIRECTORY ] 5 | # [ASSET_DIRECTORY ]) 6 | # 7 | # The script should autodetect the `ANDROID_SDK`, `ANDROID_BUILD_TOOLS_VERSION`, 8 | # `ANDROID_PLATFORM_VERSION` variables and print them to the configure output. 9 | # If it doesn't or the values are different than you'd like, set them from the 10 | # CMake cache. 11 | # 12 | # You may want to update `ANDROID_APKSIGNER_KEY` to match your keystore 13 | # location and password. Here I just took the default debug.keystore that 14 | # Gradle generated on my system and guesstimated its password to be "android". 15 | # 16 | # You can pass a path to a directory with resources using `RESOURCE_DIRECTORY`, 17 | # which will then get copied to the APK. Similarly, a directory with assets can 18 | # be specified with `ASSET_DIRECTORY`. Both options track file dependencies, so 19 | # the APK gets repacked when any files in the directories change contents, are 20 | # added or removed. CMake before 3.12 checks the directories only during CMake 21 | # runs, on newer versions at every incremental build. 22 | # 23 | # The function also creates a new target ${target}-deploy which you can use to 24 | # install the APK directly from the IDE / command-line. 25 | # 26 | # Limitations: 27 | # 28 | # - It's packaging just one ABI, the one you are currently building with CMake. 29 | # It might be possible to work around this somehow in the future, but so far 30 | # it's like this. 31 | # - There is lots of autodetection that can go wrong. 32 | # 33 | 34 | # Android support is since 3.7. 3.10 matches Corrade requirement, see its root 35 | # CMakeLists for more information. 36 | cmake_minimum_required(VERSION 3.7...3.10) 37 | 38 | # Override this to point to your debug.keystore location (and specify a 39 | # password) 40 | if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") 41 | set(ANDROID_APKSIGNER_KEY --ks $ENV{USERPROFILE}/.android/debug.keystore --ks-pass pass:android CACHE STRING "") 42 | else() 43 | set(ANDROID_APKSIGNER_KEY --ks $ENV{HOME}/.android/debug.keystore --ks-pass pass:android CACHE STRING "") 44 | endif() 45 | 46 | # Path to Android SDK. The tools/source.properties file is expected to exist to 47 | # avoid accidentally matching some arbitrary other directory. 48 | if(NOT ANDROID_SDK) 49 | # Use the env var, if present 50 | if(DEFINED ENV{ANDROID_SDK_ROOT}) 51 | set(ANDROID_SDK $ENV{ANDROID_SDK_ROOT}) 52 | # On Arch it's /opt/android-sdk and /opt/android-ndk 53 | elseif(EXISTS ${CMAKE_ANDROID_NDK}/../android-sdk/tools/source.properties) 54 | get_filename_component(ANDROID_SDK ${CMAKE_ANDROID_NDK}/../android-sdk/ REALPATH CACHE) 55 | # On CircleCI it's /opt/android/sdk/ndk/ 56 | elseif(EXISTS ${CMAKE_ANDROID_NDK}/../../tools/source.properties) 57 | get_filename_component(ANDROID_SDK ${CMAKE_ANDROID_NDK}/../../ REALPATH CACHE) 58 | # Otherwise no idea 59 | endif() 60 | 61 | if(ANDROID_SDK) 62 | message(STATUS "ANDROID_SDK not set, detected ${ANDROID_SDK}") 63 | else() 64 | message(FATAL_ERROR "ANDROID_SDK not set and autodetection failed") 65 | endif() 66 | endif() 67 | 68 | # Build tools version to use. Picks the newest version. 69 | if(NOT ANDROID_BUILD_TOOLS_VERSION) 70 | file(GLOB _ANDROID_BUILD_TOOLS_FOR_VERSION RELATIVE ${ANDROID_SDK}/build-tools/ ${ANDROID_SDK}/build-tools/*.*.*) 71 | if(NOT _ANDROID_BUILD_TOOLS_FOR_VERSION) 72 | message(FATAL_ERROR "No Android build tools found in ${ANDROID_SDK}/build-tools") 73 | endif() 74 | list(GET _ANDROID_BUILD_TOOLS_FOR_VERSION -1 ANDROID_BUILD_TOOLS_VERSION) 75 | set(ANDROID_BUILD_TOOLS_VERSION ${ANDROID_BUILD_TOOLS_VERSION} CACHE STRING "") 76 | if(ANDROID_BUILD_TOOLS_VERSION) 77 | message(STATUS "ANDROID_BUILD_TOOLS_VERSION not set, detected ${ANDROID_BUILD_TOOLS_VERSION}") 78 | else() 79 | message(FATAL_ERROR "ANDROID_BUILD_TOOLS_VERSION not set and autodetection failed") 80 | endif() 81 | endif() 82 | 83 | # Platform version that matches build tools version. 84 | # TODO: why not CMAKE_SYSTEM_VERSION? 85 | if(NOT ANDROID_PLATFORM_VERSION) 86 | string(REGEX REPLACE "([0-9]+)\\.[0-9]+\\.[0-9]+" "\\1" ANDROID_PLATFORM_VERSION ${ANDROID_BUILD_TOOLS_VERSION}) 87 | set(ANDROID_PLATFORM_VERSION ${ANDROID_PLATFORM_VERSION} CACHE STRING "") 88 | if(ANDROID_PLATFORM_VERSION) 89 | message(STATUS "ANDROID_PLATFORM_VERSION not set, detected ${ANDROID_PLATFORM_VERSION}") 90 | else() 91 | message(FATAL_ERROR "ANDROID_PLATFORM_VERSION not set and autodetection failed") 92 | endif() 93 | endif() 94 | 95 | function(android_create_apk target manifest) 96 | cmake_parse_arguments(CREATE_APK "" "RESOURCE_DIRECTORY;ASSET_DIRECTORY" "" ${ARGN}) 97 | if(CREATE_APK_UNPARSED_ARGUMENTS) 98 | string (REPLACE ";" " " CREATE_APK_UNPARSED_ARGUMENTS "${CREATE_APK_UNPARSED_ARGUMENTS}") 99 | message(SEND_ERROR "Unrecognized android_create_apk() arguments: ${CREATE_APK_UNPARSED_ARGUMENTS}") 100 | endif() 101 | 102 | set(jar ${ANDROID_SDK}/platforms/android-${ANDROID_PLATFORM_VERSION}/android.jar) 103 | if(NOT EXISTS ${jar}) 104 | message(SEND_ERROR "Android platform JAR not found at ${jar}") 105 | endif() 106 | 107 | set(tools_root ${ANDROID_SDK}/build-tools/${ANDROID_BUILD_TOOLS_VERSION}) 108 | set(apk_root ${CMAKE_CURRENT_BINARY_DIR}/${target}-apk) 109 | # TODO: can't use $ here because of 110 | # https://gitlab.kitware.com/cmake/cmake/issues/12877 -- mention that in 111 | # limitations 112 | set(library_destination_path ${apk_root}/bin/lib/${CMAKE_ANDROID_ARCH_ABI}) 113 | set(library_destination ${library_destination_path}/lib${target}.so) 114 | set(unaligned_apk ${apk_root}/${target}-unaligned.apk) 115 | set(unsigned_apk ${apk_root}/${target}-unsigned.apk) 116 | set(apk ${CMAKE_CURRENT_BINARY_DIR}/${target}.apk) 117 | 118 | # Copy the library to the destination, stripping it in the process. It 119 | # needs to be in a folder that corresponds to its ABI, otherwise the java 120 | # interface won't find it. Without the strip the apk creation would take 121 | # *ages*. 122 | add_custom_command(OUTPUT ${library_destination} 123 | COMMAND ${CMAKE_COMMAND} -E make_directory ${library_destination_path} 124 | COMMAND ${CMAKE_STRIP} $ -o ${library_destination} 125 | COMMENT "Copying stripped ${target} for an APK build" 126 | DEPENDS $) 127 | 128 | # Pass a path to the resource directory, if specified, and track the 129 | # dependencies 130 | if(CREATE_APK_RESOURCE_DIRECTORY) 131 | get_filename_component(resource_directory_absolute ${CREATE_APK_RESOURCE_DIRECTORY} REALPATH) 132 | set(resources -S ${resource_directory_absolute}) 133 | 134 | # Glob all resource files in given directory to properly repack the APK 135 | # when they change. Additionally, on CMake 3.12+ make sure the 136 | # directory is globbed again on every build to ensure newly appearing 137 | # files are properly picked up. 138 | if(NOT CMAKE_VERSION VERSION_LESS 3.12) 139 | set(glob_configure_depends CONFIGURE_DEPENDS) 140 | endif() 141 | file(GLOB_RECURSE resource_files ${glob_configure_depends} ${resource_directory_absolute}/*) 142 | 143 | # On the other hand, if a file gets removed from the directory, or a 144 | # file with an old timestamp gets added, it won't trigger a repack. To 145 | # fix that, record also the list of actual globbed files and track it 146 | # as another dependency. To avoid a repack on every CMake run, (ab)use 147 | # configure_file() to write it with a changed timestamp only if the 148 | # list actually changes. 149 | file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${target}-resource-dependencies.txt.orig ${resource_files}) 150 | configure_file(${CMAKE_CURRENT_BINARY_DIR}/${target}-resource-dependencies.txt.orig ${CMAKE_CURRENT_BINARY_DIR}/${target}-resource-dependencies.txt COPYONLY) 151 | list(APPEND resource_files ${CMAKE_CURRENT_BINARY_DIR}/${target}-resource-dependencies.txt) 152 | endif() 153 | 154 | # Pass a path to the asset directory, if specified, and track the 155 | # dependencies. Logic the same as in the CREATE_APK_RESOURCE_DIRECTORY 156 | # above. 157 | if(CREATE_APK_ASSET_DIRECTORY) 158 | get_filename_component(asset_directory_absolute ${CREATE_APK_ASSET_DIRECTORY} REALPATH) 159 | set(assets -A ${asset_directory_absolute}) 160 | 161 | if(NOT CMAKE_VERSION VERSION_LESS 3.12) 162 | set(glob_configure_depends CONFIGURE_DEPENDS) 163 | endif() 164 | file(GLOB_RECURSE asset_files ${glob_configure_depends} ${asset_directory_absolute}/*) 165 | 166 | file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${target}-asset-dependencies.txt.orig ${resource_files}) 167 | configure_file(${CMAKE_CURRENT_BINARY_DIR}/${target}-asset-dependencies.txt.orig ${CMAKE_CURRENT_BINARY_DIR}/${target}-asset-dependencies.txt COPYONLY) 168 | list(APPEND asset_files ${CMAKE_CURRENT_BINARY_DIR}/${target}-asset-dependencies.txt) 169 | endif() 170 | 171 | # Package this file together with the manifest 172 | # TODO: is there some possibility to set the zip compression ~~speed~~ 173 | # draaaag? it's slow! 174 | # TODO: what about aapt2? there's -A but it puts the so into assets/ :( 175 | # TODO: can pass -0 so to not compress anything, yay! but then upload may 176 | # be slower 177 | # TODO: for resources i need to add -m -J src/ 178 | get_filename_component(manifest_absolute ${manifest} REALPATH) 179 | add_custom_command(OUTPUT ${unaligned_apk} 180 | COMMAND ${tools_root}/aapt package -f 181 | -M ${manifest_absolute} 182 | ${resources} # present only if RESOURCE_DIRECTORY was specified 183 | ${assets} # present only if ASSET_DIRECTORY was specified 184 | -I ${ANDROID_SDK}/platforms/android-${ANDROID_PLATFORM_VERSION}/android.jar 185 | -F ${unaligned_apk} 186 | ${apk_root}/bin 187 | COMMENT "Packaging ${target}-unaligned.apk" 188 | DEPENDS ${library_destination} ${manifest} ${resource_files} ${asset_files}) 189 | 190 | # Align the APK 191 | add_custom_command(OUTPUT ${unsigned_apk} 192 | COMMAND ${tools_root}/zipalign -f 4 ${unaligned_apk} ${unsigned_apk} 193 | COMMENT "Aligning ${target}-unsigned.apk" 194 | DEPENDS ${unaligned_apk}) 195 | 196 | # Sign the APK 197 | # TODO: make this configurable better 198 | add_custom_command(OUTPUT ${apk} 199 | COMMAND ${tools_root}/apksigner sign ${ANDROID_APKSIGNER_KEY} --out ${apk} ${unsigned_apk} 200 | COMMENT "Signing ${target}.apk" 201 | DEPENDS ${unsigned_apk}) 202 | 203 | # Make the APK dependency of an "always build" target so it gets built by 204 | # default 205 | add_custom_target(${target}-apk ALL DEPENDS ${apk}) 206 | 207 | # Add an explicit deploy target for easy install 208 | add_custom_target(${target}-deploy 209 | COMMAND ${ANDROID_SDK}/platform-tools/adb install -r ${apk} 210 | COMMENT "Installing ${target}.apk" 211 | DEPENDS ${apk}) 212 | 213 | # TODO: Could be also possible to do this, but that makes sense only for 214 | # projects that only ever have just one APK output -- have it as an option? 215 | # The COMPONENTS is useless as one can't just `ninja install/component` :( 216 | # install(CODE "execute_process(COMMAND adb install -r ${apk})" 217 | # EXCLUDE_FROM_ALL) 218 | endfunction() 219 | -------------------------------------------------------------------------------- /generic/Emscripten-wasm.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Toolchain for cross-compiling to JS using Emscripten with WebAssembly 3 | # 4 | # Modify EMSCRIPTEN_PREFIX to your liking; use EMSCRIPTEN environment variable 5 | # to point to it or pass it explicitly via -DEMSCRIPTEN_PREFIX=. 6 | # 7 | # mkdir build-emscripten-wasm && cd build-emscripten-wasm 8 | # cmake .. -DCMAKE_TOOLCHAIN_FILE=../toolchains/generic/Emscripten-wasm.cmake 9 | # 10 | # On MSVC set "intelliSenseMode": "linux-gcc-x86" in CMakeSettings.json to get 11 | # IntelliSense to work. 12 | 13 | set(CMAKE_SYSTEM_NAME Emscripten) 14 | 15 | # Help CMake find the platform file 16 | set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../modules ${CMAKE_MODULE_PATH}) 17 | 18 | # Give a hand to first-time Windows users 19 | if(CMAKE_HOST_WIN32) 20 | if(CMAKE_GENERATOR MATCHES "Visual Studio") 21 | message(FATAL_ERROR "Visual Studio project generator doesn't support cross-compiling to Emscripten. Please use -G Ninja or other generators instead.") 22 | endif() 23 | endif() 24 | 25 | if(NOT EMSCRIPTEN_PREFIX) 26 | if(DEFINED ENV{EMSCRIPTEN}) 27 | file(TO_CMAKE_PATH "$ENV{EMSCRIPTEN}" EMSCRIPTEN_PREFIX) 28 | # On Homebrew the sysroot is here, however emcc is also available in 29 | # /usr/local/bin. It's impossible to figure out the sysroot from there, so 30 | # try this first 31 | elseif(EXISTS "/usr/local/opt/emscripten/libexec/emcc") 32 | set(EMSCRIPTEN_PREFIX "/usr/local/opt/emscripten/libexec") 33 | # Some Linux distributions like Arch have it in /usr/lib/emscripten 34 | elseif(EXISTS "/usr/lib/emscripten") 35 | set(EMSCRIPTEN_PREFIX "/usr/lib/emscripten") 36 | # Look for emcc in the path as a last resort 37 | else() 38 | find_file(_EMSCRIPTEN_EMCC_EXECUTABLE emcc) 39 | if(NOT EXISTS ${_EMSCRIPTEN_EMCC_EXECUTABLE}) 40 | message(FATAL_ERROR "Neither the EMSCRIPTEN_PREFIX CMake variable nor the EMSCRIPTEN environment variable was set and emcc wasn't found in PATH. Please specify either of the two explicitly.") 41 | endif() 42 | get_filename_component(EMSCRIPTEN_PREFIX ${_EMSCRIPTEN_EMCC_EXECUTABLE} DIRECTORY) 43 | mark_as_advanced(_EMSCRIPTEN_EMCC_EXECUTABLE) 44 | endif() 45 | endif() 46 | 47 | if(CMAKE_HOST_WIN32) 48 | set(EMCC_SUFFIX ".bat") 49 | else() 50 | set(EMCC_SUFFIX "") 51 | endif() 52 | 53 | # MSVC IntelliSense requires these variables to be put into cache: 54 | # https://devblogs.microsoft.com/cppblog/configure-intellisense-with-cmake-toolchain-files-in-visual-studio-2019-16-9-preview-2/ 55 | set(CMAKE_C_COMPILER "${EMSCRIPTEN_PREFIX}/emcc${EMCC_SUFFIX}" CACHE FILEPATH "C compiler" FORCE) 56 | set(CMAKE_CXX_COMPILER "${EMSCRIPTEN_PREFIX}/em++${EMCC_SUFFIX}" CACHE FILEPATH "CXX compiler" FORCE) 57 | # These variables are usually hidden in the Advanced section by default, but if 58 | # given language isn't enabled for the project, there wouldn't be a pre- 59 | # existing (advanced) CMAKE__COMPILER variable and so it'd show up in the 60 | # default view. 61 | mark_as_advanced(CMAKE_C_COMPILER CMAKE_CXX_COMPILER) 62 | 63 | # The `CACHE PATH "bla"` *has to be* present as otherwise CMake < 3.13.0 would 64 | # for some reason forget the path to `ar`, calling it as `"" qc bla`, failing 65 | # with `/bin/sh: : command not found`. This is probably related to CMP0077 in 66 | # some way but I didn't bother investigating further. 67 | set(CMAKE_AR "${EMSCRIPTEN_PREFIX}/emar${EMCC_SUFFIX}" CACHE PATH "Path to Emscripten ar") 68 | set(CMAKE_RANLIB "${EMSCRIPTEN_PREFIX}/emranlib${EMCC_SUFFIX}" CACHE PATH "Path to Emscripten ranlib") 69 | # When CMAKE_INTERPROCEDURAL_OPTIMIZATION is set, CMake apparently uses these 70 | # instead of CMAKE_AR and such. Couldn't find any official documentation on 71 | # why, https://github.com/emscripten-core/emscripten/issues/11143 seems to be 72 | # the only place mentioning this. 73 | set(CMAKE_C_COMPILER_AR "${CMAKE_AR}") 74 | set(CMAKE_CXX_COMPILER_AR "${CMAKE_AR}") 75 | set(CMAKE_C_COMPILER_RANLIB "${CMAKE_RANLIB}") 76 | set(CMAKE_CXX_COMPILER_RANLIB "${CMAKE_RANLIB}") 77 | 78 | # Fetch Emscripten version, needed for the below setup. Taken from 79 | # https://github.com/emscripten-core/emscripten/blob/74d6a15644e7f6e76ed6a1da9c6937b5cb7aef6e/cmake/Modules/Platform/Emscripten.cmake#L152 80 | execute_process(COMMAND ${CMAKE_C_COMPILER} -v 81 | RESULT_VARIABLE _EMSCRIPTEN_VERSION_RESULT 82 | ERROR_VARIABLE EMSCRIPTEN_VERSION 83 | # The command should only print to the error output if it succeeds. If it 84 | # fails, we may want to know what's the standard output as well, so put 85 | # both to the same variable. 86 | OUTPUT_VARIABLE EMSCRIPTEN_VERSION) 87 | if(NOT _EMSCRIPTEN_VERSION_RESULT EQUAL 0) 88 | message(FATAL_ERROR "Failed to query Emscripten version with the following output:\n${EMSCRIPTEN_VERSION}") 89 | endif() 90 | string(REGEX MATCH "emcc \\([^)]+\\) ([0-9\\.]+)" "\\1" "${EMSCRIPTEN_VERSION}") 91 | if(NOT CMAKE_MATCH_1) 92 | message(FATAL_ERROR "Failed to extract Emscripten version from the following:\n${EMSCRIPTEN_VERSION}") 93 | endif() 94 | set(EMSCRIPTEN_VERSION ${CMAKE_MATCH_1}) 95 | 96 | # As of Emscripten 2.0.13, the whole include dir is copied over to the 97 | # Emscripten cache dir in an attempt to have includes next to the libraries 98 | # that were always built on-the-fly directly into the cache, such as libGL etc. 99 | # See https://github.com/emscripten-core/emscripten/issues/9353 for the 100 | # original discussion. Since 3.1.4, the cache dir is then the *only* sysroot 101 | # that is complete, as it contains a generated version.h: 102 | # https://github.com/emscripten-core/emscripten/pull/16147; and since 3.1.9 103 | # it's not possible to use the system directory anymore: 104 | # https://github.com/emscripten-core/emscripten/pull/16715. 105 | # 106 | # The inevitable consequence is that includes updated in the system path aren't 107 | # automatically picked up, but only after explicitly copying the files to the 108 | # cache dir with `embuilder build sysroot --force`. See 109 | # https://github.com/emscripten-core/emscripten/pull/13090 for related 110 | # discussion. 111 | # 112 | # The code in https://github.com/emscripten-core/emscripten/blob/0566a76b500bd2bbd535e108f657fce1db7f6f75/tools/system_libs.py#L2298-L2345 113 | # however DOES NOT copy any binaries from lib/, only the include files, so 114 | # /system/lib still has to be added to CMAKE_FIND_ROOT_PATH. Yay for 115 | # the "single sysroot" attempt, FFS. 116 | # 117 | # The cache dir can be queried by asking em-config. Taken from 118 | # https://github.com/emscripten-core/emscripten/blob/74d6a15644e7f6e76ed6a1da9c6937b5cb7aef6e/cmake/Modules/Platform/Emscripten.cmake#L223 119 | if(NOT EMSCRIPTEN_VERSION VERSION_LESS 2.0.13) 120 | execute_process(COMMAND ${EMSCRIPTEN_PREFIX}/em-config${EMCC_SUFFIX} CACHE 121 | RESULT_VARIABLE _EMSCRIPTEN_CACHE_RESULT 122 | OUTPUT_VARIABLE _EMSCRIPTEN_CACHE 123 | ERROR_VARIABLE _EMSCRIPTEN_CACHE_ERROR 124 | OUTPUT_STRIP_TRAILING_WHITESPACE) 125 | if(NOT _EMSCRIPTEN_CACHE_RESULT EQUAL 0) 126 | message(FATAL_ERROR "Failed to query Emscripten cache directory with the following output:\n${_EMSCRIPTEN_CACHE_ERROR}") 127 | endif() 128 | set(EMSCRIPTEN_SYSROOTS 129 | "${_EMSCRIPTEN_CACHE}/sysroot" 130 | "${EMSCRIPTEN_PREFIX}/system") 131 | else() 132 | set(EMSCRIPTEN_SYSROOTS "${EMSCRIPTEN_PREFIX}/system") 133 | endif() 134 | 135 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} 136 | ${EMSCRIPTEN_SYSROOTS} 137 | ${EMSCRIPTEN_PREFIX}) 138 | 139 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 140 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 141 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 142 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 143 | 144 | # Otherwise FindCorrade fails to find _CORRADE_MODULE_DIR. Why the heck is this 145 | # not implicit is beyond me. 146 | set(CMAKE_SYSTEM_PREFIX_PATH ${CMAKE_FIND_ROOT_PATH}) 147 | 148 | # Compared to the classic (asm.js) compilation, -s WASM=1 is added to both 149 | # compiler and linker. The *_INIT variables are available since CMake 3.7, so 150 | # it won't work in earlier versions. Sorry. CMake 3.31+ warns if anything older 151 | # than 3.10 is used, so it's used as the policy max version. 152 | cmake_minimum_required(VERSION 3.7...3.10) 153 | set(CMAKE_EXE_LINKER_FLAGS_INIT "-s WASM=1") 154 | set(CMAKE_CXX_FLAGS_RELEASE_INIT "-DNDEBUG -O3 -flto") 155 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE_INIT "-O3 -flto") 156 | 157 | # Look for Node.js. Ïf found, use it as the crosscompiling emulator. You can 158 | # disable this behavior by defining CMAKE_CROSSCOMPILING_EMULATOR to empty. 159 | if(NOT DEFINED CMAKE_CROSSCOMPILING_EMULATOR) 160 | # Not using find_package(NodeJs) as that'd mean I'd have to bundle 161 | # FindNodeJs. Additionally, I cannot use NodeJs::NodeJs here -- while 162 | # CORRADE_CROSSCOMPILING_EMULATOR set to NodeJs::NodeJs seems to work for 163 | # add_test(), it doesn't get expanded to the actual executable location for 164 | # the custom command in corrade_add_resource(), for example. So there's no 165 | # point in using FindNodeJs at all, simply just find_program() and reuse 166 | # the same variable so it doesn't try to find the same thing again in the 167 | # actual FindNodeJs. 168 | # 169 | # Also note that by creating the NodeJs::NodeJs target this early if 170 | # find_package(NodeJs) would be used, it makes it behave like it's not 171 | # project-local. A consequence of that is that if NodeJs::NodeJs is used 172 | # again as a COMMAND in add_test(), it *doesn't* get prepended with 173 | # CMAKE_CROSSCOMPILING_EMULATOR, whereas if the NodeJs::NodeJs target isn't 174 | # created here already, CMAKE_CROSSCOMPILING_EMULATOR gets prepended to 175 | # add_test(), leading to nodejs being twice in the command. See 176 | # https://github.com/mosra/corrade/issues/185 for more details. 177 | find_program(NODEJS_EXECUTABLE node) 178 | mark_as_advanced(NODEJS_EXECUTABLE) 179 | if(NODEJS_EXECUTABLE) 180 | # This is a verbatim copy from FindNodeJs.cmake, see above for why is 181 | # find_package(NodeJs) not called directly 182 | execute_process(COMMAND ${NODEJS_EXECUTABLE} --version 183 | OUTPUT_VARIABLE NodeJs_VERSION 184 | ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) 185 | if(NodeJs_VERSION MATCHES "^v[0-9]") 186 | string(SUBSTRING ${NodeJs_VERSION} 1 -1 NodeJs_VERSION) 187 | else() 188 | unset(NodeJs_VERSION) 189 | endif() 190 | 191 | # Node.js 18 doesn't work with Emscripten before 3.1.13 unless the 192 | # fetch functionality is disabled. 193 | # https://github.com/emscripten-core/emscripten/pull/16917 194 | # https://github.com/emscripten-core/emscripten/issues/16915 195 | # https://cmake.org/cmake/help/latest/variable/CMAKE_CROSSCOMPILING_EMULATOR.html 196 | if(NodeJs_VERSION VERSION_GREATER_EQUAL 18.1.0 AND EMSCRIPTEN_VERSION VERSION_LESS 3.1.13) 197 | if(CMAKE_VERSION VERSION_LESS 3.15) 198 | message(FATAL_ERROR "In order to use Node.js 18 as a CMAKE_CROSSCOMPILING_EMULATOR with Emscripten older than 3.1.13, an extra flag needs to be passed, which requires CMake 3.15+. Use different versions or explicitly set CMAKE_CROSSCOMPILING_EMULATOR to empty to prevent it from being used.") 199 | endif() 200 | set(CMAKE_CROSSCOMPILING_EMULATOR "${NODEJS_EXECUTABLE};--no-experimental-fetch" CACHE FILEPATH "Path to the emulator for the target system.") 201 | else() 202 | set(CMAKE_CROSSCOMPILING_EMULATOR ${NODEJS_EXECUTABLE} CACHE FILEPATH "Path to the emulator for the target system.") 203 | endif() 204 | 205 | mark_as_advanced(CMAKE_CROSSCOMPILING_EMULATOR) 206 | endif() 207 | endif() 208 | --------------------------------------------------------------------------------