Janus is a proof of concept for building a "multiversal" fat binary: a binary that runs both on iOS and OSX.
More specifically, the binary is:
- cross-platform: it runs on arm and x86.
- platform-aware: it know what hardware it runs on (armv7, armv7s, arm64, x86_64)
The project contains 3 targets:
-
Janus-iOS: an iOS application that displays the name of the architecture (armv7, armv7s, arm64) of the device it is running on (both on console and screen).
-
Janus-OSX: an OSX binary that displays the name of the architecture (i386, x86_64) of the computer it is running on.
-
Janus-Universal: an aggregated target that add the Janus-OSX binary in Janus-iOS, making it a cross-platform universal binary that runs on both OSX and iOS.
CURRENT_ARCH is an environment variable set by Xcode when building. The Other C flags (in the build settings) has been set to -DCURRENT_ARCH=\"$CURRENT_ARCH\"
. In result, the CURRENT_ARCH is made available in the source code as a macro definition.
In ACViewController (Janus-iOS) and main.m (Janus-OSX), the macro is used to both statically (printf) and dynamically (strcmp + label).
Using this macro, it is possible to have different binaries for each platform. They can both be different (statically, as the source code) and act differently (dynamically, at run time). For instance, It would be possible, if we are completely crazy, to have two complete different games in the same app, depending on the platform it is run on (NOT recommended for the app store).
lipo
is used in Janus-Universal to add the Janus-OSX binary to the Janus-iOS binary. As a result, Janus-iOS.app/Janus-iOS is a binary that runs both on iOS and OSX.
Code signing is specific to each platform and needs to be addressed before lipo-ing. Specifically, an OSX binary will not run if signed with an iOS identity.
Janus-Universal is used to lipo
, after the Janus-iOS as been completly build (including codesigning), because it is not possible to run a build phase script (to lipo
) after the code signing.
Code signing both the iOS and OSX code hasn't been tested.
lipo-ing only means the different binary codes are merged together in one binary file.
This does not mean in any way that a OSX binary can use iOS frameworks (UIKit) nor the other way around (APPKit).
To build the project, build Janus-Universal.
In the build directory (typically DerivedData/Janus/Build/Products), you'll find the Janus-iOS.app. Its binary, Janus-iOS is multiversal and can be run on iOS (arm) and OSX (x86_64).
Inspecting the binary with lipo
will show 4 architectures:
$ lipo -info ./Debug-iphoneos/Janus-iOS.app/Janus-iOS
Architectures in the fat file: ./Debug-iphoneos/Janus-iOS.app/Janus-iOS are: x86_64 armv7 armv7s arm64
As well as otool
:
$ otool -f ./Debug-iphoneos/Janus-iOS.app/Janus-iOS
Fat headers
fat_magic 0xcafebabe
nfat_arch 4
architecture 0
cputype 16777223
cpusubtype 3
capabilities 0x80
offset 4096
size 9160
align 2^12 (4096)
architecture 1
cputype 12
cpusubtype 9
capabilities 0x0
offset 16384
size 64432
align 2^14 (16384)
architecture 2
cputype 12
cpusubtype 11
capabilities 0x0
offset 81920
size 64448
align 2^14 (16384)
architecture 3
cputype 16777228
cpusubtype 0
capabilities 0x0
offset 147456
size 64752
align 2^14 (16384)
In a console, running Janus-iOS.app/Janus-iOS should display the architecture name and a "hello":
$ ./Debug-iphoneos/Janus-iOS.app/Janus-iOS
x86_64
2014-03-27 15:29:31.160 Janus-iOS[73947:507] hello world, I'm an OSX binary running on x86_64
As explained before, you can't run the app in the simulator.
Attach a device, and in Xcode, Run, but without building, Janus-iOS (Product > Perform action > Run Without building or ^⌘R) (do not "Build and run", as it will recompile and remove OSX binary part).
- The app store guidelines does not mention anything about having one app behaving completly differently depending on the architecture it is run on.
- Try to make a complete .app multiversally compatible (not just only in OSX terminal)