I would like to port my C/C++ apps to OS X.
I don't have a Mac, but I have Linux and Windows. Is there any tool for this?
I would like to port my C/C++ apps to OS X.
I don't have a Mac, but I have Linux and Windows. Is there any tool for this?
There appears to be some scripts that have been written to help get you set up cross compiling for the Mac; I can't say how good they are, or how applicable to your project. In the documentation, they refer to these instructions for cross-compiling for 10.4, and these ones for cross compiling for 10.5; those instructions may be more helpful than the script, depending on how well the script fits your needs.
If your program is free or open source software, then you may wish instead to create a MacPorts portfile (documentation here), and allow your users to build your program using MacPorts; that is generally the preferred way to install portable free or open source software on Mac OS X. MacPorts has been known to run on Linux in the past, so it may be possible to develop and test your Portfile on Linux (though it will obviously need to be tested on a Mac).
For Linux, there is a prebuilt GCC cross-compiler (from publicly available Apple's modified GCC sources).
https://launchpad.net/~flosoft/+archive/cross-apple
Update for 2015
http://channel9.msdn.com/Events/Visual-Studio/Connect-event-2014/311
Embarcadero's RadStudio also supports building OSX/iOS/Android apps on Windows.
This answer by Thomas also provides a cross-compilation tool.
For all these options you still need a real mac/i-device to test the application.
I have created a project called OSXCross which aims to target OS X (10.4-10.9) from Linux.
It currently supports clang
3.2 up to 3.8 (trunk) (you can use your dist's clang
).
In addition you can build an up-to-date vanilla GCC
as well (4.6+).
LTO works as well, for both, clang
and GCC
.
Currently using cctools-870 with ld64-242.
There appears to be some scripts that have been written to help get you set up cross compiling for the Mac; I can't say how good they are, or how applicable to your project. In the documentation, they refer to these instructions for cross-compiling for 10.4, and these ones for cross compiling for 10.5; those instructions may be more helpful than the script, depending on how well the script fits your needs.
If your program is free or open source software, then you may wish instead to create a MacPorts portfile (documentation here), and allow your users to build your program using MacPorts; that is generally the preferred way to install portable free or open source software on Mac OS X. MacPorts has been known to run on Linux in the past, so it may be possible to develop and test your Portfile on Linux (though it will obviously need to be tested on a Mac).
When you are trying to get something working on multiple platforms you absolutely must compile/run/integrate/test on the intended platform. You can not just compile/run on one platform and then say "oh it should work the same on the other platform".
Even with the a really good cross-platform language like Java you will run into problems where it won't work exactly the same on the other platform.
The only way I have found that respects my time/productivity/ability-to-rapidly iterate on multiple platforms is to use a VM of the other platforms.
There are other solutions like dual-boot and ones that I haven't mentioned but I find that they don't respect my productivity/time.
Take dual-booting as an example:
BAM there goes 30 minutes of my time and I haven't done anything productive.
For this project we will start by compiling LibreSSL. This is chosen because it offers a CMake build option and should generate a library, static and shared, from C-source that is generally cross-platform that will serve as a demonstration that it can be done.
Xcode is Apple's toolchain and IDE. We however only need to grab the SDKs out of the bundle. Assuming you have access to a Mac with the Xcode you can quickly grab out the SDKs.
To determine which SDK version of for example iphoneos, iphonesimulator, or macosx you use xcrun.
% xcrun --sdk macosx --show-sdk-version
13.3
And where is Xcode installed?
% xcode-select --print-path
/Applications/Xcode.app/Contents/Developer
What platforms are available?
% ls -1 $(xcode-select --print-path)/Platforms
AppleTVOS.platform
AppleTVSimulator.platform
DriverKit.platform
MacOSX.platform
WatchOS.platform
WatchSimulator.platform
iPhoneOS.platform
iPhoneSimulator.platform
Then the SDKs for let's say iphoneos is in
% ls -1 $(xcode-select --print-path)/Platforms/MacOSX.platform/Developer/SDKs
MacOSX.sdk
MacOSX13.3.sdk
MacOSX13.sdk
The contents of MacOSX13.3.sdk
(a symlink to MacOSX.sdk
) are what will be referred to as Sysroot. Inside this folder you will find header files and TBDs (Text-based Definitions, which are YAML based stub libraries) that you compile and link against. These TBDs save a lot of space since the alternative might be shipping all of the various versions of the binaries. I created a tool to grab these SDKs out of an installed version of Xcode and create a gzipped tarball for convenience.
SDK Packager can be downloaded from GitHub
Move the tarball to your linux machine in any manner you see fit.
Compiling Clang and LLVM on Linux is fairly straightforward. After checking out the project and switching to branch llvmorg-15.0.7
I created a script called build.sh
in the root of the source directory.
#!/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 has a few 'special' tools that are used for manipulating binaries that are separate from those shipped with Clang/LLVM. Fortunately, these tools are open-source and the necessary porting work has been done. They are available at
Build libtapi
and libxar
first then build 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
The tools we need are ar
, install_name_tool
, libtool
, lipo
, ranlib
, and ld
. I suspect this additional step is no longer needed in part or in its entirety, but for this version of the tutorial we will stick with them.
Add the installed bin directory to your search path.
PATH=${HOME}/cctools/bin:${PATH}
We will start by building for LibreSSL macOS for x86 and arm so we can run the 'apps' that ship with LibreSSL so we can prove it works.
It is not practical to check out LibreSSL from git since the project is not in the form of a release per se and relies upon OpenBSD. Hence, the tarball.
Download https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-3.7.2.tar.gz
Below is a bash script example on how to compile LibreSSL. Two additional files, the toolchain and launcher script follow.
#!/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
One of the truly great things about Clang/LLVM and CMake is the ease with which you can generate a cross-compiled build. The key component in cross-compiled builds is the CMake toolchain file. Most developers are used to installing a compiler / IDE environment like Xcode and relying 100% on the IDE to know how compilation and linking occurs. However, compiling without this environment is possible.
If you decide to enable BUILD_SHARED_LIBS
you will need to codesign the binaries and dylibs in order for them to run.
codesign -f -s - bin/openssl
for LIB in lib/*.dylib; do
codesign -f -s - $LIB
done
When building the dylib
version of the libraries you normally would adhere to some conventions regarding where the dynamic linker will look for them. In this case we will need to clue dyld
into where they live since we don't want to install our experiment into /usr/local/lib
or /usr/lib
. On a Mac we use the DYLD_LIBRARY_PATH
variable.
DYLD_LIBRARY_PATH=$(pwd)/lib; export DYLD_LIBRARY_PATH
We use a tiny shell script to append the path to the linker
#!/bin/bash
set -ex
# -fuse-ld=/path/to/ld if pre-clang 12
"$@" --ld-path=${HOME}/cctools/bin/ld
The compiler flag -arch x86_64 -arch arm64
is what convinces clang to create, in this case, a FAT Mach-O binary with x86_64 and arm64 architectures. The triple will then be generated in two invocations of clang (behind the scenes) and then lipo called to combine the objects.
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 "")
Zip up and copy the install directory to a Mac and you can then test the binaries to see if they work.
The simplest thing to do is to output the -help
, but for a more interesting test we can dump the DERs from an existing .app bundle and then pretty print them.
codesign -d --extract-certificates MyApp.app
/path/to/openssl x509 -inform der -in codesign0 -text
libc
runtime also. I didn't build LibreSSL, only a small test executable with path/to/new/clang -isysroot path/to/MacOSX.sdk -target arm64-apple-darwin23 -fuse-ld=lld -o app main.c
. I don't know why MacOSX 14 = darwin23, but this was the triple that my M1 MacBook gave me. –
Sevenfold You will definitely need OS X somehow. Even if you don't own a Mac, you can still try some alternatives.
You would need a toolchain to cross compile for mach-o, but even if you had that, you won't have the Apple libraries around to develop with. I'm not sure how you would be able to port without them, unfortunately.
Apple development is a strange beast unto itself. OS X uses a port of GCC with some modifications to make it 'appley'. In theory, it's possible to the the sources to the Apple GCC and toolchain as well as the Apple kernel and library headers and build a cross compiler on your Windows machine.
Why you'd want to go down this path is beyond me. You can have a cheap Mac mini from $600. The time you invest getting a cross compiler working right (particularly with a Windows host for Unix tools) will probably cost more than the $600 anyway.
If you're really keen to make your app cross platform look into Qt, wxWidgets or FLTK. All provide cross-platform support with minimal changes to the base code. At least that way all you need to do is find a Mac to compile your app on, and that's not too hard to do if you have some technically minded friends who don't mind giving you SSH access to their Mac.
I found this small documentation on the net: http://devs.openttd.org/~truebrain/compile-farm/apple-darwin9.txt
This describes exactly what you want. I am interested in it myself, haven't tested it yet though (found it 10 minutes ago). The documentation seems good, though.
You can hire a mac in the cloud from this website. You can hire them from $1, which should be enough (unless you need root access, then you are looking at $49+).
© 2022 - 2024 — McMap. All rights reserved.