我想将我的C/C++应用程序移植到OS X。
我没有Mac电脑,但我有Linux和Windows。是否有适用于此目的的工具?
我想将我的C/C++应用程序移植到OS X。
我没有Mac电脑,但我有Linux和Windows。是否有适用于此目的的工具?
针对Linux系统,可以使用预编译的GCC交叉编译器(由公开可用的苹果修改版GCC源代码构建)。
https://launchpad.net/~flosoft/+archive/cross-apple
2015年更新
http://channel9.msdn.com/Events/Visual-Studio/Connect-event-2014/311
无论使用哪种选项,您仍需要真正的Mac设备或iOS设备来测试应用程序。
我创建了一个名为OSXCross的项目,旨在从Linux系统针对OS X (10.4-10.9)进行开发。
它目前支持使用dist版本的clang
3.2到3.8(trunk)。
此外还可以构建最新的通用GCC
(4.6+)。
无论是clang
还是GCC
都支持LTO技术。
目前使用的是cctools-870和ld64-242。
当您尝试在多个平台上运行某些内容时,您绝对必须在预期的平台上进行编译/运行/集成测试。您不能只在一个平台上编译/运行,然后说“哦,在另一个平台上应该也能工作”。
即使使用像Java这样非常好的跨平台语言,您也会遇到问题,因为它在其他平台上可能不完全相同。
我发现唯一尊重我的时间/生产力/能够快速迭代多个平台的方法是使用其他平台的虚拟机。
还有其他解决方案,例如双启动,但我发现它们不尊重我的生产力/时间。
以双启动为例:
砰!我的30分钟就这样浪费了,我什么都没做。
您需要一个工具链来进行mach-o的交叉编译,但即使您拥有这个工具链,也没有苹果库可供开发。很遗憾,如果没有这些库,我不确定您如何进行移植。
苹果开发是一种独特的技术。OS X使用了一个经过修改的GCC移植版,使其更具苹果特色。理论上,你可以获取苹果GCC和工具链、苹果内核和库头文件的源代码,并在Windows机器上构建一个交叉编译器。
我不明白为什么要这样做。你可以购买一台仅需$600的廉价Mac mini。而且,尝试让交叉编译器正常工作(特别是在Windows主机上运行Unix工具)所需要的时间可能比$600还要多。
如果你真的想让你的应用程序跨平台,可以考虑Qt、wxWidgets或FLTK。它们都提供跨平台支持,而且对基础代码的更改很少。至少这样,你只需要找到一台Mac来编译你的应用程序,如果你有一些技术方面的朋友不介意给你SSH访问权限的话,这并不难。
对于这个项目,我们将从编译LibreSSL开始。选择它是因为它提供了一个CMake构建选项,并且应该能够从C源代码生成一个通常跨平台的库(静态和共享),作为一个演示来证明这是可行的。
Xcode是苹果的工具链和集成开发环境。然而,我们只需要从捆绑包中获取SDK。假设您可以访问一台安装了Xcode的Mac电脑,您可以快速提取出SDK。
要确定使用哪个SDK版本,例如iphoneos、iphonesimulator或macosx,您可以使用xcrun命令。
% xcrun --sdk macosx --show-sdk-version
13.3
% xcode-select --print-path
/Applications/Xcode.app/Contents/Developer
% ls -1 $(xcode-select --print-path)/Platforms
AppleTVOS.platform
AppleTVSimulator.platform
DriverKit.platform
MacOSX.platform
WatchOS.platform
WatchSimulator.platform
iPhoneOS.platform
iPhoneSimulator.platform
% ls -1 $(xcode-select --print-path)/Platforms/MacOSX.platform/Developer/SDKs
MacOSX.sdk
MacOSX13.3.sdk
MacOSX13.sdk
MacOSX13.3.sdk
的内容(一个指向MacOSX.sdk
的符号链接)将被称为Sysroot。在这个文件夹中,您会找到头文件和TBDs(基于YAML的存根库),您可以编译和链接。这些TBDs节省了很多空间,因为另一种选择可能是将所有不同版本的二进制文件都进行分发。我创建了一个工具,可以从安装的Xcode版本中提取这些SDK,并创建一个压缩的tarball以方便使用。
SDK Packager可以从GitHub下载。
以任何您认为合适的方式将tarball移动到您的Linux机器上。
在Linux上编译Clang和LLVM相当简单。在检出项目并切换到llvmorg-15.0.7
分支后,我在源代码目录的根目录下创建了一个名为build.sh
的脚本。
#!/bin/bash
set -ex
function join_by { local IFS="$1"; shift; echo "$*"; }
PROJECTS=( clang lld clang-tools-extra )
RUNTIMES=( libcxx libcxxabi )
TARGETS=( X86 ARM AArch64 )
SOURCE_DIR=$( pwd )
BUILD_DIR=${SOURCE_DIR}/builddir
DIST_DIR=${SOURCE_DIR}/dist
rm -rf ${BUILD_DIR} ${DIST_DIR}
mkdir -p ${BUILD_DIR}
pushd ${BUILD_DIR}
cmake \
-G Ninja \
-D CMAKE_BUILD_TYPE=Release \
-D CMAKE_INSTALL_PREFIX=${DIST_DIR} \
-D CMAKE_VERBOSE_MAKEFILE=on \
-D LLVM_TARGETS_TO_BUILD=$(join_by ";" "${TARGETS[@]}") \
-D LLVM_ENABLE_PROJECTS=$(join_by ";" "${PROJECTS[@]}") \
-D LLVM_ENABLE_RUNTIMES=$(join_by ";" "${RUNTIMES[@]}") \
${SOURCE_DIR}/llvm
cmake --build .
cmake --build . --target install
popd
Apple有一些与Clang/LLVM附带的工具分开使用的“特殊”工具,用于操作二进制文件。幸运的是,这些工具是开源的,并且已经完成了必要的移植工作。它们可以在以下网址找到:
首先构建 libtapi
和 libxar
,然后构建 cctools。
#!/bin/bash
set -ex
./configure \
--prefix=${HOME}/cctools \
--with-libtapi=${HOME}/cctools \
--with-libxar=${HOME}/cctools \
--with-llvm-config=${HOME}/Development/llvm-project/dist/bin/llvm-config
make -j8
make install
ar
、install_name_tool
、libtool
、lipo
、ranlib
和ld
。我怀疑这个额外的步骤在某种程度上或完全上已经不再需要,但是对于本教程的这个版本,我们将继续使用它们。PATH=${HOME}/cctools/bin:${PATH}
我们将首先为 x86 和 arm 架构的 macOS 构建 LibreSSL,以便我们可以运行与 LibreSSL 一起提供的“应用程序”,以证明其正常工作。
由于 LibreSSL 项目不是以发布形式存在,并且依赖于 OpenBSD,因此从 git 检出 LibreSSL 并不实际。因此,我们使用 tarball 进行操作。
下载 https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-3.7.2.tar.gz
下面是一个 bash 脚本示例,展示了如何编译 LibreSSL。随后还有两个附加文件,即工具链和启动脚本。
#!/bin/bash
APPLE_SDK_SYSROOT="${HOME}/Development/apple_sdks/MacOSX13.3.sdk"
LLVM_DIST="${HOME}/Development/llvm-project/dist"
CCTOOLS_DIST="${HOME}/cctools"
SOURCE_DIR=$( pwd )
BUILD_DIR=${SOURCE_DIR}/builddir
INSTALL_DIR=${SOURCE_DIR}/dist
CMAKE_DIR=${SOURCE_DIR}/cmake
rm -rf ${BUILD_DIR} ${DIST_DIR}
mkdir -p ${BUILD_DIR}
pushd ${BUILD_DIR}
cmake \
-G Ninja \
-D CMAKE_VERBOSE_MAKEFILE=on \
-D CMAKE_TOOLCHAIN_FILE=${CMAKE_DIR}/macosx.toolchain.cmake \
-D LINKER_LAUNCHER=${SOURCE_DIR}/launcher.sh \
-D LLVM_DIST=${LLVM_DIST} \
-D CCTOOLS_DIST=${CCTOOLS_DIST} \
-D APPLE_SDK_SYSROOT=${APPLE_SDK_SYSROOT} \
-D CMAKE_INSTALL_PREFIX=${INSTALL_DIR} \
-D BUILD_SHARED_LIBS=OFF \
-D LIBRESSL_APPS=YES \
-D LIBRESSL_TESTS=NO \
${SOURCE_DIR}
cmake --build .
cmake --build . --target install
popd
codesign -f -s - bin/openssl
for LIB in lib/*.dylib; do
codesign -f -s - $LIB
done
dylib
版本时,通常会遵循一些约定,以确定动态链接器将在何处查找它们。在这种情况下,我们需要告诉dyld
它们所在的位置,因为我们不想将实验安装到/usr/local/lib
或/usr/lib
中。在Mac上,我们使用DYLD_LIBRARY_PATH
变量。DYLD_LIBRARY_PATH=$(pwd)/lib; export DYLD_LIBRARY_PATH
#!/bin/bash
set -ex
# -fuse-ld=/path/to/ld if pre-clang 12
"$@" --ld-path=${HOME}/cctools/bin/ld
编译器标志-arch x86_64 -arch arm64
是让clang在这种情况下创建一个包含x86_64和arm64架构的FAT Mach-O二进制文件的关键。然后,三元组将在两次调用clang(在幕后进行)中生成,然后调用lipo来合并对象。
cmake_minimum_required(VERSION 3.16)
# APPLE_SDK_SYSROOT, LLVM_DIST, CCTOOLS_DIST, LINKER_LAUNCHER
set(SDK_NAME macosx)
list(APPEND ARCHS x86_64 arm64)
set(DEPLOYMENT_TARGET "10.13")
# If you want clang to output universal binaries you must use
# multiple -arch flags. Doing so results in clang calling
# itself multiple times with different target triples. Finally,
# it will call lipo to combine the two object files into one.
# Clang also complains if you don't use -isysroot on these
# Apple SDK builds. This leaves -m<sdk>-version-min=x.xx.
# You can cover it in the triple or pass them through
# CMAKE_OSX_DEPLOYMENT_TARGET to generate the version min
# string. The target triple is necessary if you want to build
# Mac Catalyst and you'll need to add -macabi to the end of the
# triple and it will look like <arch>-apple-ios<version>-macabi
set(CMAKE_SYSTEM_NAME Darwin) # iOS if you're iOS or Simulator
set(CMAKE_OSX_SYSROOT "${APPLE_SDK_SYSROOT}" CACHE PATH "")
set(CMAKE_OSX_DEPLOYMENT_TARGET ${DEPLOYMENT_TARGET})
set(CMAKE_OSX_ARCHITECTURES ${ARCHS})
list(JOIN ARCHS "-" ARCHS_DASH)
set(APPLE_TARGET_TRIPLE ${ARCHS_DASH}-apple-${SDK_NAME}${DEPLOYMENT_TARGET})
set(CMAKE_C_COMPILER_TARGET ${APPLE_TARGET_TRIPLE})
set(CMAKE_ASM_COMPILER_TARGET ${APPLE_TARGET_TRIPLE})
set(CMAKE_C_COMPILER ${LLVM_DIST}/bin/clang CACHE FILEPATH "")
set(CMAKE_C_LINKER_LAUNCHER ${LINKER_LAUNCHER} CACHE FILEPATH "")
set(CMAKE_AR ${CCTOOLS_DIST}/bin/ar CACHE FILEPATH "" FORCE)
set(CMAKE_RANLIB ${CCTOOLS_DIST}/bin/ranlib CACHE FILEPATH "" FORCE)
set(CMAKE_STRIP ${CCTOOLS_DIST}/bin/strip CACHE FILEPATH "" FORCE)
set(BUILD_LIBTOOL ${CCTOOLS_DIST}/bin/libtool CACHE FILEPATH "")
set(CMAKE_INSTALL_NAME_TOOL ${CCTOOLS_DIST}/bin/install_name_tool CACHE FILEPATH "")
set(CMAKE_C_CREATE_STATIC_LIBRARY "${BUILD_LIBTOOL} -static -o <TARGET> <LINK_FLAGS> <OBJECTS> " CACHE INTERNAL "")
将安装目录压缩并复制到Mac上,然后您可以测试二进制文件是否正常工作。
最简单的方法是输出-help
,但为了进行更有趣的测试,我们可以从现有的.app包中转储DER文件,然后对其进行格式化打印。
codesign -d --extract-certificates MyApp.app
/path/to/openssl x509 -inform der -in codesign0 -text