[build] iOS support + cmake toolchain changes

* Instead of specifying a lot of command line options when invoking
    cmake, move most of the logic to cmake "proxy" toolchain file. For
    now two such proxies exist - `toolchain.android.cmake` and
   `toolchain.ios.cmake`. Versions expected by each cross build are
    specified in the respective `versions.*.cmake` files in the `cmake/`
    directory.
  * Use https://github.com/leetal/ios-cmake.git as the iOS cmake
    toolchain file.
  * The lowest supported iOS version is 10.0 now
  * Define weak symbols and provide wrapper for POSIX APIs added in the
    later versions of iOS.
This commit is contained in:
Marek Habersack 2021-03-16 18:41:21 +01:00
Родитель 4042e48c65
Коммит be7698450d
14 изменённых файлов: 311 добавлений и 69 удалений

4
.gitmodules поставляемый Normal file
Просмотреть файл

@ -0,0 +1,4 @@
[submodule "external/ios-cmake"]
path = external/ios-cmake
url = https://github.com/leetal/ios-cmake.git
branch = master

Просмотреть файл

@ -7,7 +7,9 @@ being set in stone.
Native code status:
* Linux: **building and linking** (gcc 10.2.1, clang 11.0.1)
* macOS: **building and linking** (Xcode 12.4, x86_64) and arm64
* macOS: **building and linking** (Xcode 12.4, x86_64 and arm64)
* iOS: **building and linking** (Xcode 12.4, arm64, armv7, armv7s,
x86_64)
* Windows: **not tested yet**
* BSD: **not tested yet**

106
build.sh
Просмотреть файл

@ -14,22 +14,11 @@ CONFIGURATION="Release"
USE_COLOR="ON"
CMAKE="cmake"
NINJA="ninja"
XCODEBUILD="xcodebuild"
NDK_ROOT="${ANDROID_NDK_HOME:-${ANDROID_NDK_ROOT}}"
ANDROID_ABI_ARM32=armeabi-v7a
ANDROID_ABI_ARM64=arm64-v8a
ANDROID_ABI_X86=x86
ANDROID_ABI_X64=x86_64
#
# NOTE: API levels must be compatible with the currently supported Xamarin.Android versions
#
ANDROID_ABIS="${ANDROID_ABI_ARM32} ${ANDROID_ABI_ARM64} ${ANDROID_ABI_X86} ${ANDROID_ABI_X64}"
ANDROID_API_ARM32=16
ANDROID_API_ARM64=21
ANDROID_API_X86=16
ANDROID_API_X64=21
ANDROID_ABIS="arm32 arm64 x86 x64"
IOS_ABIS="armv7 armv7s arm64 simx86 simarm64"
function die()
{
@ -49,10 +38,14 @@ Where OPTIONS are:
-v, --verbose make the build verbose
-m, --cmake PATH use the specified cmake binary instead of the default '${CMAKE}'
-n, --ninja PATH use the specified ninja binary instead of the default '${NINJA}'
-x, --xcodebuild PATH use the specified xcodebuild binary instead of the default '${XCODEBUILD}'
-j, --jobs NUM spin up at most NUM jobs
-r, --rebuild rebuild from scratch
-a, --ndk PATH path to the Android NDK root directory (default: ${NDK_ROOT:-UNSET})
-p, --abi ABIS comma-separated list of Android ABIs to build (default: ${ANDROID_ABIS})
-p, --abi ABIS comma-separated list of target platform ABIs to build:
Android default: ${ANDROID_ABIS}
iOS default: ${IOS_ABIS}
-h, --help show help
And TARGET is one of:
@ -68,19 +61,6 @@ EOF
exit 0
}
function get_android_api()
{
local abi="$1"
case "${abi}" in
${ANDROID_ABI_ARM32}) echo ${ANDROID_API_ARM32} ;;
${ANDROID_ABI_ARM64}) echo ${ANDROID_API_ARM64} ;;
${ANDROID_ABI_X86}) echo ${ANDROID_API_X86} ;;
${ANDROID_ABI_X64}) echo ${ANDROID_API_X64} ;;
*) die "Unknown Android ABI '${abi}'"
esac
}
function print_build_banner()
{
echo "$@"
@ -94,32 +74,45 @@ function print_build_banner_native()
function do_build()
{
local strip_debug
local generator="${CMAKE_GENERATOR:-Ninja}"
local make_program="${CMAKE_MAKE:-${NINJA}}"
if [ "${CONFIGURATION}" == "Release" ]; then
strip_debug="ON"
else
strip_debug="OFF"
fi
"${CMAKE}" -GNinja \
"${CMAKE}" -G${generator} \
-DCMAKE_BUILD_TYPE=${CONFIGURATION} \
-DCMAKE_MAKE_PROGRAM="${NINJA}" \
-DCMAKE_MAKE_PROGRAM="${make_program}" \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DSTRIP_DEBUG=${strip_debug} \
-DUSE_COLOR=${USE_COLOR} \
"$@" \
"${NATIVE_SOURCE_DIR}"
local ninja_verbose=""
local ninja_jobs=""
if [ "${VERBOSE}" == "yes" ]; then
ninja_verbose="-v"
fi
local verbose=""
local jobs=""
if [ -n "${JOBS}" ]; then
ninja_jobs="-j${JOBS}"
if [ "${make_program}" == "${NINJA}" ]; then
if [ "${VERBOSE}" == "yes" ]; then
verbose="-v"
fi
if [ -n "${JOBS}" ]; then
jobs="-j${JOBS}"
fi
elif [ "${make_program}" == "${xcodebuild}" ]; then
if [ "${VERBOSE}" == "yes" ]; then
verbose="-verbose"
fi
if [ -n "${JOBS}" ]; then
jobs="-jobs ${JOBS}"
fi
fi
"${NINJA}" ${ninja_verbose} ${ninja_jobs}
"${make_program}" ${verbose} ${jobs}
}
function build_common()
@ -132,7 +125,8 @@ function build_common()
shift
local build_dir="${BUILD_DIR_ROOT}/${build_dir_name}-${CONFIGURATION}"
local config_lower=$(echo ${CONFIGURATION} | tr A-Z a-z)
local build_dir="${BUILD_DIR_ROOT}/${build_dir_name}-${config_lower}"
if [ "${REBUILD}" == "yes" -a -d "${build_dir}" ]; then
rm -rf "${build_dir}"
fi
@ -142,8 +136,9 @@ function build_common()
function __build_host()
{
local OS_LOWER=$(echo ${OS} | tr A-Z a-z)
print_build_banner_native "${OS}"
build_common host
build_common host -DTARGET_PLATFORM=host-${OS_LOWER}
}
function __build_android()
@ -160,21 +155,22 @@ function __build_android()
local api_level
for abi in ${ANDROID_ABIS}; do
api_level=$(get_android_api ${abi})
build_common android-${abi} \
-DCMAKE_TOOLCHAIN_FILE="${NDK_ROOT}/build/cmake/android.toolchain.cmake" \
-DANDROID_STL="none" \
-DANDROID_CPP_FEATURES="no-rtti no-exceptions" \
-DANDROID_NDK="${NDK_ROOT}" \
-DANDROID_NATIVE_API_LEVEL=${api_level} \
-DANDROID_PLATFORM=android-${api_level} \
-DANDROID_ABI=${abi}
-DTARGET_PLATFORM=android \
-DMPH_ANDROID_ABI=${abi} \
-DANDROID_NDK="${NDK_ROOT}"
done
}
function __build_ios()
{
print_build_banner_native iOS
local abi_lower
for abi in ${ABIS:-${IOS_ABIS}}; do
abi_lower=$(echo ${abi} | tr A-Z a-z)
build_common ios-${abi_lower} -DTARGET_PLATFORM=ios-${abi_lower}
done
}
function __build_wasm()
@ -198,6 +194,7 @@ function missing_argument()
}
POSITIONAL_ARGS=""
ABIS=""
while (( "$#" )); do
case "$1" in
-n|--ninja)
@ -218,6 +215,15 @@ while (( "$#" )); do
fi
;;
-x|--xcodebuild)
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
XCODEBUILD="$2";
shift 2
else
missing_argument "$1"
fi
;;
-j|--jobs)
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
JOBS="$2";
@ -238,7 +244,7 @@ while (( "$#" )); do
-p|--abi)
if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
ANDROID_ABIS="$(echo $2 | tr ',' ' ')";
ABIS="$(echo $2 | tr ',' ' ')";
shift 2
else
missing_argument "$1"

Просмотреть файл

@ -0,0 +1,40 @@
if(TARGET_PLATFORM STREQUAL android)
include(${CMAKE_CURRENT_LIST_DIR}/versions.android.cmake)
#
# MPH_ANDROID_ABI, one of (each line is a set of alternative names for the given ABI):
#
# arm64-v8a, arm64, aarch64
# armeabi-v7a, arm32
# x86
# x86_64, x64
#
if(NOT DEFINED MPH_ANDROID_ABI)
message(FATAL_ERROR "Please provide Android ABI by setting the MPH_ANDROID_ABI property on command line")
endif()
if(NOT ANDROID_NDK)
message(FATAL_ERROR "Please provide path to the Android NDK by setting the ANDROID_NDK property on command line")
endif()
if(MPH_ANDROID_ABI MATCHES "^arm64-v8a$|^arm64$|^aarch64$")
set(ANDROID_ABI "arm64-v8a")
elseif(MPH_ANDROID_ABI MATCHES "^armeabi-v7a$|^arm32$")
set(ANDROID_ABI "armeabi-v7a")
elseif(MPH_ANDROID_ABI MATCHES "^x86$")
set(ANDROID_ABI "x86")
elseif(MPH_ANDROID_ABI MATCHES "^x86_64$|^x64$")
set(ANDROID_ABI "x86_64")
else()
message(FATAL "Unknown Android ABI name: ${MPH_ANDROID_ABI}")
endif()
set(MPH_API_LEVEL "${ANDROID_API_${ANDROID_ABI}}")
set(ANDROID_STL "none")
set(ANDROID_CPP_FEATURES "no-rtti no-exceptions")
set(ANDROID_NATIVE_API_LEVEL "${MPH_API_LEVEL}")
set(ANDROID_PLATFORM "android-${MPH_API_LEVEL}")
include(${ANDROID_NDK}/build/cmake/android.toolchain.cmake)
endif()

40
cmake/toolchain.ios.cmake Normal file
Просмотреть файл

@ -0,0 +1,40 @@
if(TARGET_PLATFORM MATCHES "^ios-|^tvos-|^catalyst-")
include(${CMAKE_CURRENT_LIST_DIR}/versions.apple.cmake)
unset(PLATFORM)
if(TARGET_PLATFORM STREQUAL ios-simx86)
set(PLATFORM "SIMULATOR64")
elseif(TARGET_PLATFORM STREQUAL ios-simarm64)
set(PLATFORM "SIMULATORARM64")
elseif(TARGET_PLATFORM MATCHES "^ios-arm")
set(PLATFORM "OS")
if(TARGET_PLATFORM STREQUAL ios-armv7)
set(ARCHS "armv7")
elseif(TARGET_PLATFORM STREQUAL ios-armv7s)
set(ARCHS "armv7s")
elseif(TARGET_PLATFORM STREQUAL ios-arm64)
set(ARCHS "arm64")
else()
message(FATAL_ERROR "Unrecognized iOS target '${TARGET_PLATFORM}'")
endif()
endif()
if(NOT PLATFORM)
message(FATAL_ERROR "Target platform '${TARGET_PLATFORM}' unrecognized")
endif()
if(PLATFORM MATCHES "^OS|^SIMULATOR")
set(DEPLOYMENT_TARGET ${IOS_MIN_VERSION})
elseif(PLATFORM MATCHES "^MAC_CATALYST")
set(DEPLOYMENT_TARGET ${MACOS_CATALYST_MIN_VERSION})
elseif(PLATFORM MATCHES "^TVOS$")
set(DEPLOYMENT_TARGET ${TVOS_MIN_VERSION})
else()
message(FATAL_ERROR "Platform '${PLATFORM}' not supported")
endif()
set(ENABLE_BITCODE OFF)
set(ENABLE_ARC OFF)
include(${CMAKE_CURRENT_LIST_DIR}/../external/ios-cmake/ios.toolchain.cmake)
endif()

Просмотреть файл

@ -0,0 +1,7 @@
#
# API levels must match the ones supported by the current versions of Xamarin.Android
#
set(ANDROID_API_armeabi-v7a "16")
set(ANDROID_API_arm64-v8a "21")
set(ANDROID_API_x86 "16")
set(ANDROID_API_x86_64 "21")

Просмотреть файл

@ -0,0 +1,4 @@
set(IOS_MIN_VERSION "10.0")
set(MACOS_MIN_VERSION "10.9")
set(MACOS_CATALYST_MIN_VERSION "13.1")
set(TVOS_MIN_VERSION "9.0")

1
external/ios-cmake поставляемый Submodule

@ -0,0 +1 @@
Subproject commit 323ff009498c29a78d2203c842371df4ab1cbf88

Просмотреть файл

@ -6,6 +6,16 @@ set(MONO_POSIX_HELPER_VERSION_MICRO 0)
set(MONO_POSIX_HELPER_VERSION "${MONO_POSIX_HELPER_VERSION_MAJOR}.${MONO_POSIX_HELPER_VERSION_MINOR}.${MONO_POSIX_HELPER_VERSION_MICRO}")
if(NOT DEFINED TARGET_PLATFORM)
message(FATAL_ERROR "Please define the TARGET_PLATFORM variable on command line")
endif()
#
# These files MUST be included before the `project` command
#
include(${CMAKE_CURRENT_LIST_DIR}/../../cmake/toolchain.android.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/../../cmake/toolchain.ios.cmake)
project(
MonoPosixHelper
VERSION ${MONO_POSIX_HELPER_VERSION}
@ -54,7 +64,6 @@ set(IS_LINUX False)
set(IS_BSD False)
set(IS_MACOS False)
set(IS_IOS False)
set(IS_WATCHOS False)
set(IS_TVOS False)
set(IS_ANDROID False)
set(IS_WASM False)
@ -79,7 +88,7 @@ else()
elseif(CMAKE_SYSTEM_NAME STREQUAL tvOS)
set(IS_TVOS True)
elseif(CMAKE_SYSTEM_NAME STREQUAL watchOS)
set(IS_WATCHOS True)
message(FATAL "watchOS not supported")
else()
message(FATAL "Unknown system when cross-compiling: ${CMAKE_SYSTEM_NAME}")
endif()
@ -131,6 +140,12 @@ if(IS_ANDROID)
)
endif()
if(IS_IOS OR IS_TVOS OR IS_MACOS)
set(HOST_MACROS
_DARWIN_C_SOURCE
)
endif()
include(CheckIncludeFileCXX)
include(CheckCXXCompilerFlag)
include(CheckLinkerFlag)
@ -215,6 +230,12 @@ set(MPH_SOURCES
x-struct-str.cc
)
if(IS_IOS)
list(APPEND MPH_SOURCES
compat.ios.cc
)
endif()
# Args shared between the C and C++ compilers
set(LOCAL_COMPILER_ARGS
-fvisibility=hidden
@ -240,6 +261,7 @@ set(LOCAL_COMPILER_ARGS
-Wsuggest-final-methods
-Wint-to-pointer-cast
-Wsuggest-override
-Wunguarded-availability
)
if(NOT IS_ANDROID)
@ -306,11 +328,9 @@ foreach(flag ${LOCAL_LINKER_ARGS})
endforeach()
set(MPH_CXX_LINKER_FLAGS "${_CHECKED_FLAGS}")
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:${MPH_CXX_FLAGS}>")
add_compile_options("$<$<COMPILE_LANGUAGE:C>:${MPH_C_FLAGS}>")
add_compile_options(${MPH_CXX_FLAGS})
add_link_options("$<$<COMPILE_LANGUAGE:CXX>:${MPH_CXX_LINKER_FLAGS}>")
add_link_options("$<$<COMPILE_LANGUAGE:C>:${MPH_C_LINKER_FLAGS}>")
add_link_options(${MPH_CXX_LINKER_FLAGS})
#
# Command line macro definitions
@ -528,7 +548,7 @@ endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/compat)
if(APPLE)
if(IS_MACOS)
set(SDK_SUPPORTS_ARM64 False)
set(SDK_SUPPORTS_X86_64 False)
execute_process(
@ -575,13 +595,21 @@ if(APPLE)
endif()
endif()
add_library(
${MPH_LIB_NAME}
SHARED
${MPH_SOURCES}
if(IS_IOS)
add_library(
${MPH_LIB_NAME}
STATIC
${MPH_SOURCES}
)
else()
add_library(
${MPH_LIB_NAME}
SHARED
${MPH_SOURCES}
)
endif()
if(APPLE)
if(IS_MACOS)
set_target_properties(
${MPH_LIB_NAME}
PROPERTIES
@ -589,4 +617,7 @@ if(APPLE)
)
endif()
if(IS_IOS)
message(STATUS "iOS architectures: ${CMAKE_OSX_ARCHITECTURES}")
endif()
configure_file(config.h.in config.h @ONLY)

68
src/native/compat.ios.cc Normal file
Просмотреть файл

@ -0,0 +1,68 @@
#if defined (HAVE_CONFIG_H)
#include <config.h>
#endif // HAVE_CONFIG_H
#include <cerrno>
#include "compat.ios.hh"
extern "C" {
// Available since iOS 11.0
int futimens(int __fd, const struct timespec __times[2]) __attribute__((weak_import));
// Available since iOS 11.0
int utimensat(int __fd, const char *__path, const struct timespec __times[2], int __flag) __attribute__((weak_import));
// Available since iOS 14.0
ssize_t preadv(int d, const struct iovec *iov, int iovcnt, off_t offset) __attribute__((weak_import));
// Available since iOS 14.0
ssize_t pwritev (int fildes, const struct iovec *iov, int iovcnt, off_t offset) __attribute__((weak_import));
}
//
// We cannot use __builtin_available because it works only in shared libraries
//
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
int __ios_futimens(int __fd, const struct timespec __times[2])
{
if (futimens == nullptr) {
errno = ENOSYS;
return -1;
}
return futimens (__fd, __times);
}
int __ios_utimensat(int __fd, const char *__path, const struct timespec __times[2], int __flag)
{
if (utimensat == nullptr) {
errno = ENOSYS;
return -1;
}
return utimensat (__fd, __path, __times, __flag);
}
ssize_t __ios_preadv(int d, const struct iovec *iov, int iovcnt, off_t offset)
{
if (preadv == nullptr) {
errno = ENOSYS;
return -1;
}
return preadv (d, iov, iovcnt, offset);
}
ssize_t __ios_pwritev (int fildes, const struct iovec *iov, int iovcnt, off_t offset)
{
if (pwritev == nullptr) {
errno = ENOSYS;
return -1;
}
return pwritev (fildes, iov, iovcnt, offset);
}
#pragma clang diagnostic pop

15
src/native/compat.ios.hh Normal file
Просмотреть файл

@ -0,0 +1,15 @@
#if !defined (__MPH_COMPAT_IOS_HH)
#define __MPH_COMPAT_IOS_HH
#if defined (HOST_IOS)
#include <sys/stat.h>
#include <sys/uio.h>
int __ios_futimens (int __fd, const struct timespec __times[2]);
int __ios_utimensat (int __fd, const char *__path, const struct timespec __times[2], int __flag);
ssize_t __ios_preadv (int d, const struct iovec *iov, int iovcnt, off_t offset);
ssize_t __ios_pwritev (int fildes, const struct iovec *iov, int iovcnt, off_t offset);
#endif // HOST_IOS
#endif // __MPH_COMPAT_IOS_HH

Просмотреть файл

@ -15,10 +15,6 @@
#define _XOPEN_SOURCE 600
#endif
#ifdef HOST_DARWIN
/* For mincore () */
#define _DARWIN_C_SOURCE
#endif
#if defined(__FreeBSD__) || defined(__OpenBSD__)
/* For mincore () */
#define __BSD_VISIBLE 1

Просмотреть файл

@ -23,6 +23,10 @@
#include "mph.hh" /* Don't remove or move after map.h! Works around issues with Android SDK unified headers */
#include "map.hh"
#if defined(HOST_IOS)
#include "compat.ios.hh"
#endif // def HOST_IOS
int
Mono_Posix_FromStat (struct Mono_Posix_Stat *from, struct stat* to)
{
@ -250,7 +254,12 @@ Mono_Posix_Syscall_futimens(int fd, struct Mono_Posix_Timespec *tv)
ptv = copy_utimens (_tv, tv);
#if !defined(HOST_IOS)
return futimens (fd, ptv);
#else // ndef HOST_IOS
// Available since iOS 11.0
return __ios_futimens (fd, ptv);
#endif // def HOST_IOS
}
#endif /* def HAVE_FUTIMENS */
@ -263,7 +272,12 @@ Mono_Posix_Syscall_utimensat(int dirfd, const char *pathname, struct Mono_Posix_
ptv = copy_utimens (_tv, tv);
#if !defined(HOST_IOS)
return utimensat (dirfd, pathname, ptv, flags);
#else // ndef HOST_IOS
// Available since iOS 11.0
return __ios_utimensat (dirfd, pathname, ptv, flags);
#endif
}
#endif /* def HAVE_UTIMENSAT */

Просмотреть файл

@ -19,6 +19,10 @@
#include "map.hh"
#include "mph.hh"
#if defined(HOST_IOS)
#include "compat.ios.hh"
#endif // def HOST_IOS
MPH_API_INTERNAL struct iovec*
_mph_from_iovec_array (struct Mono_Posix_Iovec *iov, int32_t iovcnt)
{
@ -90,7 +94,12 @@ Mono_Posix_Syscall_preadv (int dirfd, struct Mono_Posix_Iovec *iov, int32_t iovc
return -1;
}
int64_t res = preadv (dirfd, v, iovcnt, off);
int64_t res;
#if !defined(HOST_IOS)
res = preadv (dirfd, v, iovcnt, off);
#else // ndef HOST_IOS
res = __ios_preadv (dirfd, v, iovcnt, off);
#endif // def HOST_IOS
free (v);
return res;
}
@ -109,7 +118,12 @@ Mono_Posix_Syscall_pwritev (int dirfd, struct Mono_Posix_Iovec *iov, int32_t iov
return -1;
}
int64_t res = pwritev (dirfd, v, iovcnt, off);
int64_t res;
#if !defined(HOST_IOS)
res = pwritev (dirfd, v, iovcnt, off);
#else // ndef HOST_IOS
res = __ios_preadv (dirfd, v, iovcnt, off);
#endif
free (v);
return res;
}