如何在Linux或Windows上编译适用于OS X的程序?

84

我想将我的C/C++应用程序移植到OS X。

我没有Mac电脑,但我有Linux和Windows。是否有适用于此目的的工具?

13个回答

52

针对Linux系统,可以使用预编译的GCC交叉编译器(由公开可用的苹果修改版GCC源代码构建)。

https://launchpad.net/~flosoft/+archive/cross-apple

2015年更新

  1. 经过多年发展,行业标准的IDE现在支持在OSX/iOS/Android平台上进行开发。

http://channel9.msdn.com/Events/Visual-Studio/Connect-event-2014/311

  1. Embarcadero的RadStudio也支持在Windows平台上构建OSX/iOS/Android应用程序。

  2. Thomas 提供了一个交叉编译工具,详情请查看这个 答案

无论使用哪种选项,您仍需要真正的Mac设备或iOS设备来测试应用程序。


2
它的工作效果如何?例如,它是否允许编译Qt用于MacOSX? - RushPL
4
这是一个完全有效的GCC 4.2,能够编译大型库。然而,它没有更新适配10.7、10.8或10.9 SDK,所以现在可以认为它已经过时了。我一直在使用它来编译我的C/C++项目,只是为了测试它是否可以移植并修复一些明显的不兼容性问题,然后在真正的Mac上构建它。 - Viktor Latypov
我该如何在Fedora上安装这个? - panzi
@panzi:我只在CrunchBang(Debian的一个变体)上使用过它,也许你应该解压.deb归档文件并尝试配置路径。或者,像往常一样,尝试编译源代码。也许这个答案现在提供了更好的替代方案:https://dev59.com/2HRB5IYBdhLWcg3wLkxM#19891283 - Viktor Latypov
不幸的是,尽管微软宣布了支持其他编译器(包括clang)的Visual Studio Connect,但我找不到任何关于如何实际针对MacOS的信息。 - AnotherParker

34

我创建了一个名为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。

https://github.com/tpoechtrager/osxcross


我已经使用了你的项目,它实现了它所承诺的功能。在最新版本的XCode(7.1)和最新版本的OSX(10.11.1)上进行了测试。 - Panayotis

24
有一些脚本可以帮助您设置Mac的交叉编译,我不能确定它们的质量或适用性。在文档中,他们提到这些说明用于10.4的交叉编译,以及这些说明用于10.5的交叉编译;根据脚本的适应程度,那些说明可能比脚本更有帮助。
如果您的程序是自由或开源软件,则可以创建一个MacPorts portfile(文档在此处),并允许用户使用MacPorts构建您的程序;这通常是在Mac OS X上安装便携式自由或开源软件的首选方式。过去已知MacPorts可以在Linux上运行,因此可能可以在Linux上开发和测试Portfile(但显然需要在Mac上进行测试)。

11
现在可以使用自制软件而非MacPorts。 - Prof. Falken
@jcoffland 不是这样的,相反地,它是用于在Linux上构建OS X应用程序。请看标题:“Cross-Compiling on Linux for Mac OS X 10.3 - 10.5”(已加重)。请注意,此答案现在非常古老(已经近7年了,天哪!),下面的后续答案提供了更多最新信息。 - Brian Campbell

6
  1. 下载“VMware Player
  2. 下载“Mac OS X虚拟机镜像
  3. 在新操作系统上编译/调试/集成测试您的代码,确保一切正常

当您尝试在多个平台上运行某些内容时,您绝对必须在预期的平台上进行编译/运行/集成测试。您不能只在一个平台上编译/运行,然后说“哦,在另一个平台上应该也能工作”。

即使使用像Java这样非常好的跨平台语言,您也会遇到问题,因为它在其他平台上可能不完全相同。

我发现唯一尊重我的时间/生产力/能够快速迭代多个平台的方法是使用其他平台的虚拟机。

还有其他解决方案,例如双启动,但我发现它们不尊重我的生产力/时间。

以双启动为例:

  1. 我在操作系统1上进行更改
  2. 重新启动到操作系统2
  3. 忘记了一些东西在操作系统1上
  4. 重新启动到操作系统1
  5. 我在操作系统1上进行更改
  6. 再次重新启动到操作系统2......

砰!我的30分钟就这样浪费了,我什么都没做。


4
当3个启动需要30分钟时,你需要新的硬件;并且可能需要能够从一个操作系统的文件系统中访问和编辑另一个操作系统的文件系统。但我理解这种情绪,也喜欢虚拟机,因为它们让我大部分时间可以像对待独立物理机一样处理它们。 - Roger Pate
11
重启所需的时间不仅包括开机时间,还包括关机时间、重新启动守护进程、设置开发环境、检查您是否仍然拥有版本控制系统中最新的代码副本等等。 - Arafangion
14
更不用说我的大脑失去了所有缓存一致性,需要慢慢重新缓存我需要做的所有事情...而且当网页浏览器意外打开时,很可能会降低生产力。 - Trevor Boyd Smith
3
这是违反苹果许可协议的行为。其合法性取决于您所在国家的法律。 - rubenvb
9
或许从技术角度来看违反了某个随机许可协议,但你仍可以购买一台Mac Pro,并使用虚拟机在其上运行所有三个操作系统。那为什么你不能购买一台漂亮的Windows电脑并做同样的事情呢?不允许这样做是反竞争的做法,在我看来,这是绝对非法的(或者应该是非法的)。 - smaudet
显示剩余2条评论

3

您肯定需要使用 OS X。即使您没有拥有 Mac,您仍然可以尝试一些替代方案


3

您需要一个工具链来进行mach-o的交叉编译,但即使您拥有这个工具链,也没有苹果库可供开发。很遗憾,如果没有这些库,我不确定您如何进行移植。


3
如果您有跨平台工具链,您可以直接从OS X复制库文件。 - user529758
复制是正确的方法,不过你也需要头文件/静态库。 - Kotauskas

3

苹果开发是一种独特的技术。OS X使用了一个经过修改的GCC移植版,使其更具苹果特色。理论上,你可以获取苹果GCC和工具链、苹果内核和库头文件的源代码,并在Windows机器上构建一个交叉编译器。

我不明白为什么要这样做。你可以购买一台仅需$600的廉价Mac mini。而且,尝试让交叉编译器正常工作(特别是在Windows主机上运行Unix工具)所需要的时间可能比$600还要多。

如果你真的想让你的应用程序跨平台,可以考虑Qt、wxWidgets或FLTK。它们都提供跨平台支持,而且对基础代码的更改很少。至少这样,你只需要找到一台Mac来编译你的应用程序,如果你有一些技术方面的朋友不介意给你SSH访问权限的话,这并不难。


10
我可以访问许多Windows机器。然而,我更喜欢在构建Linux时同时生成Windows二进制文件。当然,这样程序必须在Windows上进行测试,但是一次性地为所有目标平台构建程序非常方便。 - Prof. Falken
2
更不用说你可能没有Mac,但你有兴趣让其他人(想在Mac上使用你的应用程序)测试该应用程序,而无需自己编译它。 - smaudet
3
值得一提的是,苹果公司在XCode中不再使用GCC编译器,而改为使用Clang/LLVM - Travis Pessetto

2

你的链接已经失效。 - Cameron Lowell Palmer

2

在Linux上进行苹果交叉编译

对于这个项目,我们将从编译LibreSSL开始。选择它是因为它提供了一个CMake构建选项,并且应该能够从C源代码生成一个通常跨平台的库(静态和共享),作为一个演示来证明这是可行的。

Xcode SDKs

Xcode是苹果的工具链和集成开发环境。然而,我们只需要从捆绑包中获取SDK。假设您可以访问一台安装了Xcode的Mac电脑,您可以快速提取出SDK。

要确定使用哪个SDK版本,例如iphoneos、iphonesimulator或macosx,您可以使用xcrun命令。

% xcrun --sdk macosx --show-sdk-version
13.3

Xcode安装在哪里?
% 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

然后,假设我们要说的是iPhone操作系统的软件开发工具包(SDK)在哪里。
% 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机器上。

构建Clang/LLVM

在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的cctools

Apple有一些与Clang/LLVM附带的工具分开使用的“特殊”工具,用于操作二进制文件。幸运的是,这些工具是开源的,并且已经完成了必要的移植工作。它们可以在以下网址找到:

首先构建 libtapilibxar,然后构建 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

我们需要的工具有arinstall_name_toollibtoolliporanlibld。我怀疑这个额外的步骤在某种程度上或完全上已经不再需要,但是对于本教程的这个版本,我们将继续使用它们。
将安装的bin目录添加到您的搜索路径中。
PATH=${HOME}/cctools/bin:${PATH}

在 macOS 上构建 LibreSSL

我们将首先为 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

Clang/LLVM和CMake的真正优点之一是可以轻松生成交叉编译构建。交叉编译构建的关键组件是CMake工具链文件。大多数开发人员习惯于安装类似Xcode的编译器/IDE环境,并完全依赖IDE来了解编译和链接的过程。然而,即使没有这个环境,也可以进行编译。
使用共享库(也称为dylibs)
如果您决定启用BUILD_SHARED_LIBS,您需要对二进制文件和dylibs进行代码签名,以便它们能够运行。
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

链接器启动器
我们使用一个小型的shell脚本来追加链接器的路径。
#!/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 "")

使用OpenSSL命令行工具进行测试

将安装目录压缩并复制到Mac上,然后您可以测试二进制文件是否正常工作。

最简单的方法是输出-help,但为了进行更有趣的测试,我们可以从现有的.app包中转储DER文件,然后对其进行格式化打印。

codesign -d --extract-certificates MyApp.app
/path/to/openssl x509 -inform der -in codesign0 -text

非常感谢,@Cameron!按照你的步骤,我在使用clang进行构建时遇到了"无法编译简单测试程序"的问题。使用ld时出现了"找不到-lSystem库"的错误。你有没有什么想法,可能是缺少了什么? - Etienne Salimbeni
是的 - 缺少 -lSystem。这是一个框架,它在Apple SDK中。您是否正确设置了APPLE_SDK_SYSROOT? - Cameron Lowell Palmer
是的 - 缺少 -lSystem。这是一个框架,它在Apple SDK中。您是否正确设置了APPLE_SDK_SYSROOT? - undefined
是的,我解压了macOSX13.3.sdk,并确保CMAKE_OSX_SYSROOT指向该文件夹,无论是在macosx.toolchain.cmake中还是在调用cmake的构建脚本中。如果我输入了错误的路径,会出现两个错误而不是一个:"ld: warning: directory not found for option '-F/System/Library/Frameworks/'"和"ld: library not found for -lSystem"。 - Etienne Salimbeni
是的,我解压了macOSX13.3.sdk,并确保CMAKE_OSX_SYSROOT指向该文件夹,无论是在macosx.toolchain.cmake文件中还是在调用cmake的构建脚本中。如果我输入了错误的路径,我会得到两个错误而不是一个: "ld: warning: directory not found for option '-F/System/Library/Frameworks/'" 和 " ld: library not found for -lSystem "。 - undefined
我注意到了一个错误@EtienneSalimbeni,并且已经更新了答案。 - Cameron Lowell Palmer

1
你可以从这个网站上租用云端的苹果电脑。你可以从1美元开始租用,这应该足够了(除非你需要root访问权限,那么费用将达到49美元或更高)。

3
这种做法基于一种假设,即某人想将自己的源代码复制到“云”中的某台机器上。对于非自由/开源软件而言,这是一个相当大的假设。 - 0xC0000022L
2
不是很长远的计划。使用30次后,购买Mac会更便宜。 - Xantium

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接