├── .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 |
--------------------------------------------------------------------------------