├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── LICENSE
├── README.md
├── Sample.xcodeproj
└── project.pbxproj
├── Sample
├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
├── ContentView.swift
├── Info.plist
├── Interpreter.cpp
├── LLVMBridge.h
├── LLVMBridge.mm
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
├── Sample.entitlements
└── SampleApp.swift
├── Screenshots
├── DisableBitcode.png
├── ObjCBridgeHeader.png
├── Screenshot1.png
├── Screenshot2.png
├── Screenshot_Real_iPhone1.png
└── Screenshot_Real_iPhone2.png
├── build-llvm.sh
└── build-tools.sh
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Build LLVM XCFrameworks for Release
2 | on: [push]
3 | jobs:
4 | prepare-build-tools:
5 | runs-on: macos-11
6 | if: ${{ false }}
7 | steps:
8 | - uses: actions/checkout@v3
9 | - run: ./build-tools.sh
10 | - run: |
11 | curl -OL https://github.com/ninja-build/ninja/releases/download/v1.10.2/ninja-mac.zip
12 | unzip ninja-mac.zip
13 | mv ninja tools/bin
14 | - run: tar -cJf tools.tar.xz tools/
15 | - uses: actions/upload-artifact@v3
16 | with:
17 | name: tools
18 | path: tools.tar.xz
19 |
20 | build-libffi:
21 | runs-on: macos-11
22 | strategy:
23 | matrix:
24 | platformArch: [iphoneos, iphonesimulator, iphonesimulator-arm64, maccatalyst, maccatalyst-arm64]
25 | steps:
26 | - uses: actions/checkout@v3
27 | - name: Build libffi for ${{ matrix.platformArch }}
28 | run: source build-llvm.sh && build_libffi ${{ matrix.platformArch }}
29 | - run: tar -cJf libffi-${{ matrix.platformArch }}.tar.xz libffi-${{ matrix.platformArch }}/
30 | - uses: actions/upload-artifact@v3
31 | with:
32 | name: libffi-${{ matrix.platformArch }}
33 | path: libffi-${{ matrix.platformArch }}.tar.xz
34 |
35 | build-llvm:
36 | needs: build-libffi # prepare-build-tools
37 | runs-on: macos-12
38 | strategy:
39 | matrix:
40 | platformArch: [iphoneos, iphonesimulator, iphonesimulator-arm64, maccatalyst, maccatalyst-arm64]
41 | timeout-minutes: 1200
42 | steps:
43 | - uses: actions/checkout@v3
44 | - uses: actions/download-artifact@v3
45 | - name: Extract artifacts
46 | run: find . -name "*.tar.xz" -exec tar xzf {} \;
47 | - run: brew install ninja
48 | - name: Build LLVM for ${{ matrix.platformArch }}
49 | run: source build-llvm.sh && build_llvm ${{ matrix.platformArch }}
50 | - run: tar -cJf LLVM-${{ matrix.platformArch }}.tar.xz LLVM-${{ matrix.platformArch }}/
51 | - uses: actions/upload-artifact@v3
52 | with:
53 | name: LLVM-${{ matrix.platformArch }}
54 | path: LLVM-${{ matrix.platformArch }}.tar.xz
55 |
56 | create-xcframework:
57 | needs: build-llvm
58 | runs-on: macos-12
59 | steps:
60 | - uses: actions/checkout@v3
61 | - uses: actions/download-artifact@v3
62 | - name: Extract artifacts
63 | run: find . -name "*.tar.xz" -exec tar xzf {} \;
64 |
65 | - name: Create XCFramework for Intel Macs
66 | run: source build-llvm.sh && create_xcframework iphoneos iphonesimulator maccatalyst
67 | - run: tar -cJf LLVM_Intel.xcframework.tar.xz LLVM.xcframework
68 | - uses: actions/upload-artifact@v3
69 | with:
70 | name: LLVM_Intel
71 | path: LLVM_Intel.xcframework.tar.xz
72 |
73 | - uses: actions/upload-artifact@v3
74 | with:
75 | name: libclang
76 | path: libclang.tar.xz
77 |
78 | - name: Create XCFramework for M1 Macs
79 | run: source build-llvm.sh && rm -rf LLVM.xcframework && create_xcframework iphoneos iphonesimulator-arm64 maccatalyst-arm64
80 | - run: tar -cJf LLVM_M1.xcframework.tar.xz LLVM.xcframework
81 | - uses: actions/upload-artifact@v3
82 | with:
83 | name: LLVM_M1
84 | path: LLVM_M1.xcframework.tar.xz
85 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Finder
2 | .DS_Store
3 |
4 | # Xcode - User files
5 | xcuserdata/
6 |
7 | **/*.xcodeproj/project.xcworkspace/*
8 | !**/*.xcodeproj/project.xcworkspace/xcshareddata
9 |
10 | **/*.xcodeproj/project.xcworkspace/xcshareddata/*
11 | !**/*.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
12 |
13 | **/*.playground/playground.xcworkspace/*
14 | !**/*.playground/playground.xcworkspace/xcshareddata
15 |
16 | **/*.playground/playground.xcworkspace/xcshareddata/*
17 | !**/*.playground/playground.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
18 |
19 | llvm-project/
20 | libffi/
21 | tools/
22 | LLVM**.xcframework/
23 |
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 light-tech
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LLVM on iOS
2 |
3 | The goal of this project is to illustrate how to use LLVM + Clang to provide an iOS app with some scripting capability.
4 |
5 | 
6 | 
7 |
8 | For the eager reader, we provide a sample iOS app project which has **NO license attached** so feel free to do whatever you want with it.
9 | In this app, we use Clang's C interpreter example located in `examples/clang-interpreter/main.cpp` of Clang source code to _interpret a simple C++ program_ and _print out the output on the iOS app's user interface_.
10 | (The file was renamed to `Interpreter.cpp` to fit in with iOS development style. Also note that the example was removed from LLVM 14 but it should be available prior.)
11 | The code is pretty much copied verbatim except for some minor modifications, namely:
12 |
13 | 1. We change the `main` function name to `clangInterpret` since iOS app already has `main` function.
14 |
15 | 2. We comment out the last line
16 | ```c++
17 | // llvm::llvm_shutdown();
18 | ```
19 | so that you can _call `clangInterpret` again_ in the app.
20 | This line only makes sense in the original program because it was a one-shot command line program.
21 |
22 | 3. We add a third parameter
23 | ```c++
24 | llvm::raw_ostream &errorOutputStream
25 | ```
26 | to `clangInterpret` and replace all `llvm::errs()` with `errorOutputStream` so we can capture the compilation output and pass it back to the app front-end to display to the user.
27 |
28 | 4. **For real iOS device**: The implementation of [`llvm::sys::getProcessTriple()`](https://github.com/llvm/llvm-project/blob/main/llvm/lib/Support/Host.cpp) is currently bogus according to the implementation of [`JITTargetMachineBuilder::detectHost()`](https://github.com/llvm/llvm-project/blob/main/llvm/lib/ExecutionEngine/Orc/JITTargetMachineBuilder.cpp).
29 | So we need to add the appropriate conditional compilation directive `#ifdef __aarch64__ ... #else ... #endif` to give it the correct triple.
30 |
31 | In the latest version, you should be able to edit the program, interpret it and see the output in the app UI.
32 |
33 | ### Preparations
34 |
35 | Before building the project, you need to either
36 | 1. compile LLVM (see instructions down below); or
37 | 2. download our prebuilt XCFramework (the file named `LLVM.xcframework.tar.xz`) from our [releases](https://github.com/light-tech/LLVM-On-iOS/releases),
38 | then `cd` to the repo folder and do
39 | ```shell
40 | tar -xzf PATH_TO_DOWNLOADED_TAR_XZ # e.g. ~/Downloads/LLVM.xcframework.tar.xz
41 | ```
42 |
43 | ### Known Limitations
44 |
45 | For simulator, can only build **Debug** version only!
46 |
47 | You can run the app on the Mac (thank to Mac Catalyst) and iOS simulator. Do NOT expect the app to work on real iPhone due to iOS security preventing [Just-In-Time (JIT) Execution](https://saagarjha.com/blog/2020/02/23/jailed-just-in-time-compilation-on-ios/) that the interpreter example was doing.
48 | By pulling out the device crash logs, the reason turns out to be the fact the [code generated in-memory by LLVM/Clang wasn't signed](http://iphonedevwiki.net/index.php/Code_Signing) and so the app was terminated with SIGTERM CODESIGN.
49 |
50 | If there is compilation error, the error message was printed out instead of crashing as expected:
51 |
52 | 
53 | 
54 |
55 | **Note**: It does work if one [launches the app from Xcode](https://9to5mac.com/2020/11/06/ios-14-2-brings-jit-compilation-support-which-enables-emulation-apps-at-full-performance/) though.
56 |
57 | To make the app work on real iPhone _untethered from Xcode_, one possibility is to use compilation into binary, somehow sign it and use [system()](https://stackoverflow.com/questions/32439095/how-to-execute-a-command-line-in-iphone).
58 | Another possibility would be to use the slower LLVM bytecode interpreter instead of ORC JIT that the example was doing, as many [existing terminal apps](https://opensource.com/article/20/9/run-linux-ios) illustrated.
59 | Also, check out [L* C++](https://apps.apple.com/us/app/l-c/id1562808282) on the App Store.
60 | I recommend reading [LLVM Programmer's Manual](https://llvm.org/docs/ProgrammersManual.html) before using LLVM API.
61 |
62 | ## Build LLVM for iOS
63 |
64 | ### The tools we needs
65 |
66 | * [Xcode](https://developer.apple.com/xcode/): Download from app store.
67 | - Note that we need the Xcode command line tools as well.
68 | * [CMake](https://cmake.org/download/): See [installation instruction](https://tudat.tudelft.nl/installation/setupDevMacOs.html) to add to `$PATH`.
69 | * [Ninja](https://ninja-build.org/): Download the [binary](https://github.com/ninja-build/ninja/releases) and add it to `$PATH`.
70 |
71 | Except for Xcode, the other items can be easily installed with Homebrew:
72 | ```shell
73 | brew install cmake ninja
74 | ```
75 |
76 | _WARNING_: It has come to our attention that LLVM's CMake Build configuration have some dependency discovery that might be interfered by Homebrew. For example, LLVM depends on `libz` that is both supplied by Xcode and Homebrew. Since we are building for iOS, we really want the Xcode version of the library. But CMake can discover the Homebrew version and uses it instead! So you might want to build on a pristine machine. Don't get yourself **Homescrewed**TM!
77 |
78 | ### Build LLVM and co.
79 |
80 | Apple has introduced [XCFramework](https://developer.apple.com/videos/play/wwdc2019/416/) to allow packaging a library for multiple-platforms (iOS, Simulator, watchOS, macOS) and CPU architectures (x86_64, arm64) that could be easily added to a project so that we do not have to switch out the libraries when we build the app for different targets (e.g. testing the app on real iPhone arm64 vs on the simulator x86_64).
81 |
82 | Our script [build-llvm.sh](build-llvm.sh) provides functions to build LLVM and libffi dependency for several iOS platforms.
83 | It also has a function `create_xcframework` to produce an XCFramework from them.
84 | The script assumes the various tools aforementioned are installed and asccessible in `$PATH`.
85 |
86 | At this repo root:
87 | ```shell
88 | source build-llvm.sh
89 | build_libffi iphoneos
90 | build_llvm iphoneos # iphonesimulator maccatalyst iphonesimulator-arm64 maccatalyst-arm64
91 | create-xcframework iphoneos # iphonesimulator maccatalyst
92 | ```
93 | (If you are building for use in your own development machine, you can skip some platforms that you do not need. For example, an M1 Macs can skip on `iphonesimulator maccatalyst` as those are meant for Intel Macs.)
94 |
95 | Our prebuilt release are built with [GitHub Actions](https://github.com/light-tech/LLVM-On-iOS/actions).
96 |
97 | ## Behind the Scene
98 |
99 | These days, you probably want to write your app in _Swift_ whereas LLVM library is written in _C++_ so we need to create a _bridge_ to expose LLVM backend to your app Swift frontend. This could be accomplished via Objective-C as an intermediate language:
100 | ```
101 | Swift <-> Objective-C <-> C++
102 | ```
103 | So to understand how our Sample project works, you need to know
104 | 1. how language interoperability works; and
105 | 2. how to configure your Xcode project to use it.
106 |
107 | ### Swift-C++ interoperability
108 |
109 | To start, you might want to start with _Anthony Nguyen_'s
110 | [Using C++ in Objective-C iOS app: My first walk](https://medium.com/@nguyenminhphuc/using-c-in-objective-c-ios-app-my-first-walk-77319d94a940)
111 | for a quick intro on how to make use of C++ in Objective-C.
112 | (Note that both C++ and Objective-C are extensions of C and reduces to C.)
113 | An easy read on Objective-C and Swift interoperability could be found in
114 | [Understanding Objective-C and Swift interoperability](https://rderik.com/blog/understanding-objective-c-and-swift-interoperability/#expose-swift-code-to-objective-c)
115 | by _RDerik_.
116 | Combining these two articles is the basis for our Sample app.
117 |
118 | A typical approach to allow C++ in Swift-based iOS app will be using
119 | * _Swift_ : Anything iOS-related (UI, file system access, Internet, ...)
120 | * _Objective-C_ : Simple classes (like [`LLVMBridge`](Sample/LLVMBridge.h) in our Sample app) to expose service written in C++.
121 | The main role is to convert data types between C++ and Swift.
122 | For example: Swift's `Data` to Objective-C's `NSData` to C++'s buffer `char*` (and length).
123 | * _C++_ : Actual implementation of processing functionality.
124 |
125 | **Tip**: When writing bridging classes, you should use `NSData` for arguments instead of `NSString` and leave the `String <-> Data` conversion to Swift since you will want a `char*` in C++ anyway.
126 |
127 | _Apple_'s [Programming with Objective-C](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011210)
128 | is fairly useful in helping us write the Objective-C bridging class `LLVMBridge`: Once we pass to C++, we are in our home turf.
129 |
130 | ### Configure iOS App Xcode Project
131 |
132 | 1. Create a new iOS app project in Xcode and copy `LLVM.xcframework` to the project folder.
133 |
134 | 2. In Xcode, add `LLVM.xcframework` to the project's Framework and Libraries. Choose **Do not embed** so that the static library is linked into the app and the entire framework is NOT copied to the app bundle.
135 |
136 | 3. To create the Objective-C bridge between Swift and C++ mentioned at the beginning, add to your project a new header file, say `LLVMBridge.h` and an implementation file, say `LLVMBridge.mm` (here, we use the `.mm` extension for Objective-C++ since we do need C++ to implement our `LLVMBridge` class) and then change the Objective-C bridging header setting in the project file to tell Xcode that the Objective-C class defined in `LLVMBridge.h` should be exposed to Swift.
137 | Again, go to **Build settings** your project and search for `bridg` and you should find **Objective-C Bridging Header** under **Swift Compiler - General**.
138 | Set it to `PROJECT_NAME/LLVMBridge.h` or if you are using more than just LLVM, a header file of your choice (but that header should include `LLVMBridge.h`).
139 |
140 | **Note**: Only Objective-C classes in *Objective-C Bridging Header* are visible to Swift!
141 |
142 | 
143 |
144 | At this point, we should be able to run the project on iOS simulator.
145 | **To build the app for real iOS devices, an extra step is needed.**
146 |
147 | 4. Since we are using a bunch of precompiled static libraries (and not the actual C++ source code in our app), we need to disable bitcode. Search for `bitcod` and set **Enable Bitcode** setting to `No`.
148 |
149 | 
150 |
151 | Now you are ready to make use of LLVM glory.
152 |
--------------------------------------------------------------------------------
/Sample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 52;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | B7FA402325EE383900563EDC /* LLVM.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = B7FA402225EE383900563EDC /* LLVM.xcframework */; };
11 | C639E815254517EF004DB533 /* SampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C639E814254517EF004DB533 /* SampleApp.swift */; };
12 | C639E817254517EF004DB533 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C639E816254517EF004DB533 /* ContentView.swift */; };
13 | C639E819254517F0004DB533 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C639E818254517F0004DB533 /* Assets.xcassets */; };
14 | C639E81C254517F0004DB533 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C639E81B254517F0004DB533 /* Preview Assets.xcassets */; };
15 | C639E84525451C0A004DB533 /* Interpreter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C639E84225451C0A004DB533 /* Interpreter.cpp */; };
16 | C639E84625451C0A004DB533 /* LLVMBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = C639E84325451C0A004DB533 /* LLVMBridge.mm */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXFileReference section */
20 | B76E87CD27C887770092565F /* build-tools.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "build-tools.sh"; sourceTree = SOURCE_ROOT; };
21 | B76E87CE27C887770092565F /* build-llvm.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "build-llvm.sh"; sourceTree = SOURCE_ROOT; };
22 | B76E87CF27C887770092565F /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; };
23 | B7923BE027FAA1CE003DE86D /* main.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; name = main.yml; path = .github/workflows/main.yml; sourceTree = SOURCE_ROOT; };
24 | B7FA401A25EE377D00563EDC /* Sample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Sample.entitlements; sourceTree = ""; };
25 | B7FA402225EE383900563EDC /* LLVM.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = LLVM.xcframework; sourceTree = ""; };
26 | C639E811254517EF004DB533 /* Sample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample.app; sourceTree = BUILT_PRODUCTS_DIR; };
27 | C639E814254517EF004DB533 /* SampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleApp.swift; sourceTree = ""; };
28 | C639E816254517EF004DB533 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
29 | C639E818254517F0004DB533 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
30 | C639E81B254517F0004DB533 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
31 | C639E81D254517F0004DB533 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
32 | C639E84225451C0A004DB533 /* Interpreter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Interpreter.cpp; sourceTree = ""; };
33 | C639E84325451C0A004DB533 /* LLVMBridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LLVMBridge.mm; sourceTree = ""; };
34 | C639E84425451C0A004DB533 /* LLVMBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LLVMBridge.h; sourceTree = ""; };
35 | /* End PBXFileReference section */
36 |
37 | /* Begin PBXFrameworksBuildPhase section */
38 | C639E80E254517EF004DB533 /* Frameworks */ = {
39 | isa = PBXFrameworksBuildPhase;
40 | buildActionMask = 2147483647;
41 | files = (
42 | B7FA402325EE383900563EDC /* LLVM.xcframework in Frameworks */,
43 | );
44 | runOnlyForDeploymentPostprocessing = 0;
45 | };
46 | /* End PBXFrameworksBuildPhase section */
47 |
48 | /* Begin PBXGroup section */
49 | B76E87CB27C887560092565F /* Scripts */ = {
50 | isa = PBXGroup;
51 | children = (
52 | B7923BE027FAA1CE003DE86D /* main.yml */,
53 | B76E87CE27C887770092565F /* build-llvm.sh */,
54 | B76E87CD27C887770092565F /* build-tools.sh */,
55 | B76E87CF27C887770092565F /* README.md */,
56 | );
57 | name = Scripts;
58 | sourceTree = "";
59 | };
60 | B7FA402125EE383800563EDC /* Frameworks */ = {
61 | isa = PBXGroup;
62 | children = (
63 | B7FA402225EE383900563EDC /* LLVM.xcframework */,
64 | );
65 | name = Frameworks;
66 | sourceTree = "";
67 | };
68 | C639E808254517EF004DB533 = {
69 | isa = PBXGroup;
70 | children = (
71 | C639E813254517EF004DB533 /* Sample */,
72 | C639E812254517EF004DB533 /* Products */,
73 | B7FA402125EE383800563EDC /* Frameworks */,
74 | );
75 | sourceTree = "";
76 | };
77 | C639E812254517EF004DB533 /* Products */ = {
78 | isa = PBXGroup;
79 | children = (
80 | C639E811254517EF004DB533 /* Sample.app */,
81 | );
82 | name = Products;
83 | sourceTree = "";
84 | };
85 | C639E813254517EF004DB533 /* Sample */ = {
86 | isa = PBXGroup;
87 | children = (
88 | B76E87CB27C887560092565F /* Scripts */,
89 | B7FA401A25EE377D00563EDC /* Sample.entitlements */,
90 | C639E84225451C0A004DB533 /* Interpreter.cpp */,
91 | C639E84425451C0A004DB533 /* LLVMBridge.h */,
92 | C639E84325451C0A004DB533 /* LLVMBridge.mm */,
93 | C639E814254517EF004DB533 /* SampleApp.swift */,
94 | C639E816254517EF004DB533 /* ContentView.swift */,
95 | C639E818254517F0004DB533 /* Assets.xcassets */,
96 | C639E81D254517F0004DB533 /* Info.plist */,
97 | C639E81A254517F0004DB533 /* Preview Content */,
98 | );
99 | path = Sample;
100 | sourceTree = "";
101 | };
102 | C639E81A254517F0004DB533 /* Preview Content */ = {
103 | isa = PBXGroup;
104 | children = (
105 | C639E81B254517F0004DB533 /* Preview Assets.xcassets */,
106 | );
107 | path = "Preview Content";
108 | sourceTree = "";
109 | };
110 | /* End PBXGroup section */
111 |
112 | /* Begin PBXNativeTarget section */
113 | C639E810254517EF004DB533 /* Sample */ = {
114 | isa = PBXNativeTarget;
115 | buildConfigurationList = C639E836254517F1004DB533 /* Build configuration list for PBXNativeTarget "Sample" */;
116 | buildPhases = (
117 | C639E80D254517EF004DB533 /* Sources */,
118 | C639E80E254517EF004DB533 /* Frameworks */,
119 | C639E80F254517EF004DB533 /* Resources */,
120 | );
121 | buildRules = (
122 | );
123 | dependencies = (
124 | );
125 | name = Sample;
126 | productName = Sample;
127 | productReference = C639E811254517EF004DB533 /* Sample.app */;
128 | productType = "com.apple.product-type.application";
129 | };
130 | /* End PBXNativeTarget section */
131 |
132 | /* Begin PBXProject section */
133 | C639E809254517EF004DB533 /* Project object */ = {
134 | isa = PBXProject;
135 | attributes = {
136 | LastSwiftUpdateCheck = 1210;
137 | LastUpgradeCheck = 1210;
138 | TargetAttributes = {
139 | C639E810254517EF004DB533 = {
140 | CreatedOnToolsVersion = 12.1;
141 | LastSwiftMigration = 1210;
142 | };
143 | };
144 | };
145 | buildConfigurationList = C639E80C254517EF004DB533 /* Build configuration list for PBXProject "Sample" */;
146 | compatibilityVersion = "Xcode 9.3";
147 | developmentRegion = en;
148 | hasScannedForEncodings = 0;
149 | knownRegions = (
150 | en,
151 | Base,
152 | );
153 | mainGroup = C639E808254517EF004DB533;
154 | productRefGroup = C639E812254517EF004DB533 /* Products */;
155 | projectDirPath = "";
156 | projectRoot = "";
157 | targets = (
158 | C639E810254517EF004DB533 /* Sample */,
159 | );
160 | };
161 | /* End PBXProject section */
162 |
163 | /* Begin PBXResourcesBuildPhase section */
164 | C639E80F254517EF004DB533 /* Resources */ = {
165 | isa = PBXResourcesBuildPhase;
166 | buildActionMask = 2147483647;
167 | files = (
168 | C639E81C254517F0004DB533 /* Preview Assets.xcassets in Resources */,
169 | C639E819254517F0004DB533 /* Assets.xcassets in Resources */,
170 | );
171 | runOnlyForDeploymentPostprocessing = 0;
172 | };
173 | /* End PBXResourcesBuildPhase section */
174 |
175 | /* Begin PBXSourcesBuildPhase section */
176 | C639E80D254517EF004DB533 /* Sources */ = {
177 | isa = PBXSourcesBuildPhase;
178 | buildActionMask = 2147483647;
179 | files = (
180 | C639E817254517EF004DB533 /* ContentView.swift in Sources */,
181 | C639E84625451C0A004DB533 /* LLVMBridge.mm in Sources */,
182 | C639E815254517EF004DB533 /* SampleApp.swift in Sources */,
183 | C639E84525451C0A004DB533 /* Interpreter.cpp in Sources */,
184 | );
185 | runOnlyForDeploymentPostprocessing = 0;
186 | };
187 | /* End PBXSourcesBuildPhase section */
188 |
189 | /* Begin XCBuildConfiguration section */
190 | C639E834254517F1004DB533 /* Debug */ = {
191 | isa = XCBuildConfiguration;
192 | buildSettings = {
193 | ALWAYS_SEARCH_USER_PATHS = NO;
194 | CLANG_ANALYZER_NONNULL = YES;
195 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
196 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
197 | CLANG_CXX_LIBRARY = "libc++";
198 | CLANG_ENABLE_MODULES = YES;
199 | CLANG_ENABLE_OBJC_ARC = YES;
200 | CLANG_ENABLE_OBJC_WEAK = YES;
201 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
202 | CLANG_WARN_BOOL_CONVERSION = YES;
203 | CLANG_WARN_COMMA = YES;
204 | CLANG_WARN_CONSTANT_CONVERSION = YES;
205 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
206 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
207 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
208 | CLANG_WARN_EMPTY_BODY = YES;
209 | CLANG_WARN_ENUM_CONVERSION = YES;
210 | CLANG_WARN_INFINITE_RECURSION = YES;
211 | CLANG_WARN_INT_CONVERSION = YES;
212 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
213 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
214 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
215 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
216 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
217 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
218 | CLANG_WARN_STRICT_PROTOTYPES = YES;
219 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
220 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
221 | CLANG_WARN_UNREACHABLE_CODE = YES;
222 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
223 | COPY_PHASE_STRIP = NO;
224 | DEBUG_INFORMATION_FORMAT = dwarf;
225 | ENABLE_STRICT_OBJC_MSGSEND = YES;
226 | ENABLE_TESTABILITY = YES;
227 | GCC_C_LANGUAGE_STANDARD = gnu11;
228 | GCC_DYNAMIC_NO_PIC = NO;
229 | GCC_NO_COMMON_BLOCKS = YES;
230 | GCC_OPTIMIZATION_LEVEL = 0;
231 | GCC_PREPROCESSOR_DEFINITIONS = (
232 | "DEBUG=1",
233 | "$(inherited)",
234 | );
235 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
236 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
237 | GCC_WARN_UNDECLARED_SELECTOR = YES;
238 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
239 | GCC_WARN_UNUSED_FUNCTION = YES;
240 | GCC_WARN_UNUSED_VARIABLE = YES;
241 | IPHONEOS_DEPLOYMENT_TARGET = 14.1;
242 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
243 | MTL_FAST_MATH = YES;
244 | ONLY_ACTIVE_ARCH = YES;
245 | SDKROOT = iphoneos;
246 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
247 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
248 | };
249 | name = Debug;
250 | };
251 | C639E835254517F1004DB533 /* Release */ = {
252 | isa = XCBuildConfiguration;
253 | buildSettings = {
254 | ALWAYS_SEARCH_USER_PATHS = NO;
255 | CLANG_ANALYZER_NONNULL = YES;
256 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
257 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
258 | CLANG_CXX_LIBRARY = "libc++";
259 | CLANG_ENABLE_MODULES = YES;
260 | CLANG_ENABLE_OBJC_ARC = YES;
261 | CLANG_ENABLE_OBJC_WEAK = YES;
262 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
263 | CLANG_WARN_BOOL_CONVERSION = YES;
264 | CLANG_WARN_COMMA = YES;
265 | CLANG_WARN_CONSTANT_CONVERSION = YES;
266 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
267 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
268 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
269 | CLANG_WARN_EMPTY_BODY = YES;
270 | CLANG_WARN_ENUM_CONVERSION = YES;
271 | CLANG_WARN_INFINITE_RECURSION = YES;
272 | CLANG_WARN_INT_CONVERSION = YES;
273 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
274 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
275 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
276 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
277 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
278 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
279 | CLANG_WARN_STRICT_PROTOTYPES = YES;
280 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
281 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
282 | CLANG_WARN_UNREACHABLE_CODE = YES;
283 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
284 | COPY_PHASE_STRIP = NO;
285 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
286 | ENABLE_NS_ASSERTIONS = NO;
287 | ENABLE_STRICT_OBJC_MSGSEND = YES;
288 | GCC_C_LANGUAGE_STANDARD = gnu11;
289 | GCC_NO_COMMON_BLOCKS = YES;
290 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
291 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
292 | GCC_WARN_UNDECLARED_SELECTOR = YES;
293 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
294 | GCC_WARN_UNUSED_FUNCTION = YES;
295 | GCC_WARN_UNUSED_VARIABLE = YES;
296 | IPHONEOS_DEPLOYMENT_TARGET = 14.1;
297 | MTL_ENABLE_DEBUG_INFO = NO;
298 | MTL_FAST_MATH = YES;
299 | SDKROOT = iphoneos;
300 | SWIFT_COMPILATION_MODE = wholemodule;
301 | SWIFT_OPTIMIZATION_LEVEL = "-O";
302 | VALIDATE_PRODUCT = YES;
303 | };
304 | name = Release;
305 | };
306 | C639E837254517F1004DB533 /* Debug */ = {
307 | isa = XCBuildConfiguration;
308 | buildSettings = {
309 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
310 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
311 | CLANG_ENABLE_MODULES = YES;
312 | CODE_SIGN_ENTITLEMENTS = Sample/Sample.entitlements;
313 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
314 | CODE_SIGN_STYLE = Automatic;
315 | DEVELOPMENT_ASSET_PATHS = "\"Sample/Preview Content\"";
316 | ENABLE_BITCODE = NO;
317 | ENABLE_PREVIEWS = YES;
318 | HEADER_SEARCH_PATHS = "";
319 | INFOPLIST_FILE = Sample/Info.plist;
320 | IPHONEOS_DEPLOYMENT_TARGET = 15.5;
321 | "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 15.0;
322 | LD_RUNPATH_SEARCH_PATHS = (
323 | "$(inherited)",
324 | "@executable_path/Frameworks",
325 | );
326 | LIBRARY_SEARCH_PATHS = "$(inherited)";
327 | PRODUCT_BUNDLE_IDENTIFIER = com.lightech.Sample;
328 | PRODUCT_NAME = "$(TARGET_NAME)";
329 | SUPPORTS_MACCATALYST = YES;
330 | SWIFT_OBJC_BRIDGING_HEADER = Sample/LLVMBridge.h;
331 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
332 | SWIFT_VERSION = 5.0;
333 | TARGETED_DEVICE_FAMILY = "1,2,6";
334 | };
335 | name = Debug;
336 | };
337 | C639E838254517F1004DB533 /* Release */ = {
338 | isa = XCBuildConfiguration;
339 | buildSettings = {
340 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
341 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
342 | CLANG_ENABLE_MODULES = YES;
343 | CODE_SIGN_ENTITLEMENTS = Sample/Sample.entitlements;
344 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
345 | CODE_SIGN_STYLE = Automatic;
346 | DEVELOPMENT_ASSET_PATHS = "\"Sample/Preview Content\"";
347 | ENABLE_BITCODE = NO;
348 | ENABLE_PREVIEWS = YES;
349 | HEADER_SEARCH_PATHS = "";
350 | INFOPLIST_FILE = Sample/Info.plist;
351 | IPHONEOS_DEPLOYMENT_TARGET = 15.5;
352 | "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 15.0;
353 | LD_RUNPATH_SEARCH_PATHS = (
354 | "$(inherited)",
355 | "@executable_path/Frameworks",
356 | );
357 | LIBRARY_SEARCH_PATHS = "$(inherited)";
358 | PRODUCT_BUNDLE_IDENTIFIER = com.lightech.Sample;
359 | PRODUCT_NAME = "$(TARGET_NAME)";
360 | SUPPORTS_MACCATALYST = YES;
361 | SWIFT_OBJC_BRIDGING_HEADER = Sample/LLVMBridge.h;
362 | SWIFT_VERSION = 5.0;
363 | TARGETED_DEVICE_FAMILY = "1,2,6";
364 | };
365 | name = Release;
366 | };
367 | /* End XCBuildConfiguration section */
368 |
369 | /* Begin XCConfigurationList section */
370 | C639E80C254517EF004DB533 /* Build configuration list for PBXProject "Sample" */ = {
371 | isa = XCConfigurationList;
372 | buildConfigurations = (
373 | C639E834254517F1004DB533 /* Debug */,
374 | C639E835254517F1004DB533 /* Release */,
375 | );
376 | defaultConfigurationIsVisible = 0;
377 | defaultConfigurationName = Release;
378 | };
379 | C639E836254517F1004DB533 /* Build configuration list for PBXNativeTarget "Sample" */ = {
380 | isa = XCConfigurationList;
381 | buildConfigurations = (
382 | C639E837254517F1004DB533 /* Debug */,
383 | C639E838254517F1004DB533 /* Release */,
384 | );
385 | defaultConfigurationIsVisible = 0;
386 | defaultConfigurationName = Release;
387 | };
388 | /* End XCConfigurationList section */
389 | };
390 | rootObject = C639E809254517EF004DB533 /* Project object */;
391 | }
392 |
--------------------------------------------------------------------------------
/Sample/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Sample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Sample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Sample/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Sample
4 | //
5 | // Created by Lightech on 10/24/2048.
6 | //
7 |
8 | import SwiftUI
9 |
10 | // Path to the app's ApplicationSupport directory where we will put a.k.a. hide our source code
11 | let applicationSupportURL = try! FileManager.default.url(for: .applicationSupportDirectory,
12 | in: .userDomainMask,
13 | appropriateFor: nil,
14 | create: true)
15 |
16 | // Global instance of LLVMBridge
17 | let llvm: LLVMBridge = LLVMBridge()
18 |
19 | struct ContentView: View {
20 | @Environment(\.horizontalSizeClass) var sizeClass
21 |
22 | @State var program: String = """
23 | extern "C" void printf(const char* fmt, ...);
24 |
25 | extern "C" void AppConsolePrint(const char *str);
26 |
27 | int main() {
28 | printf("Hello world!");
29 | AppConsolePrint("Hello world!");
30 | return 0;
31 | }
32 | """
33 |
34 | @State var compilationOutput: String = ""
35 | @State var programOutput: String = ""
36 |
37 | var body: some View {
38 | if (sizeClass == .compact) {
39 | TabView {
40 | VStack {
41 | Button("Interpret Sample Program", action: interpretSampleProgram)
42 | TextEditor(text: $program)
43 | .disableAutocorrection(true)
44 | .font(.body.monospaced())
45 | }.tabItem {
46 | Image(systemName: "doc.plaintext")
47 | Text("Source code")
48 | }
49 |
50 | VStack {
51 | Text(compilationOutput)
52 | Divider()
53 | Text(programOutput)
54 | }.tabItem {
55 | Image(systemName: "greaterthan.square")
56 | Text("Output")
57 | }
58 | }
59 | } else {
60 | VStack {
61 | Button("Interpret Sample Program", action: interpretSampleProgram)
62 | .padding()
63 | HStack {
64 | TextEditor(text: $program)
65 | .disableAutocorrection(true)
66 | .font(.body.monospaced())
67 | Divider()
68 | VStack {
69 | Text(compilationOutput)
70 | Divider()
71 | Text(programOutput)
72 | }
73 | }
74 | }
75 | }
76 | }
77 |
78 | func interpretSampleProgram() {
79 | // Prepare a sample C++ source code file hello.cpp
80 | let filePath = applicationSupportURL.appendingPathComponent("hello.cpp")
81 |
82 | FileManager.default.createFile(atPath: filePath.path,
83 | contents: program.data(using: .utf8)!,
84 | attributes: nil)
85 |
86 | // Compile and interpret the program
87 | print("Interpret sample program at ", filePath)
88 | let output = llvm.interpretProgram(filePath.path.data(using: .utf8)!)
89 | compilationOutput = output.compilationOutput!
90 | programOutput = output.programOutput!
91 | }
92 | }
93 |
94 | // Disable smart quote (replacing ' and " with Unicode characters) as this prevents compilation
95 | // https://stackoverflow.com/questions/66432017/disable-auto-replacement-of-punctuation-in-swiftui-texteditor
96 | // This does not work on Mac Catalyst: change in System preferences > Keyboard > Text > for Double/Single Quotes, choose the last quote style (amongst the 8 possible types).
97 | // https://developer.apple.com/forums/thread/124410
98 | // Note that simply uncheck "Use smart quotes and dashes" does not work either.
99 | extension UITextView {
100 | open override var frame: CGRect {
101 | didSet {
102 | self.smartQuotesType = UITextSmartQuotesType.no
103 | }
104 | }
105 | }
106 |
107 | struct ContentView_Previews: PreviewProvider {
108 | static var previews: some View {
109 | ContentView()
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/Sample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 |
28 | UIApplicationSupportsIndirectInputEvents
29 |
30 | UILaunchScreen
31 |
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/Sample/Interpreter.cpp:
--------------------------------------------------------------------------------
1 | //===-- examples/clang-interpreter/main.cpp - Clang C Interpreter Example -===//
2 | //
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 | // See https://llvm.org/LICENSE.txt for license information.
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 | //
7 | //===----------------------------------------------------------------------===//
8 |
9 | #include
10 |
11 | #include "clang/Basic/DiagnosticOptions.h"
12 | #include "clang/CodeGen/CodeGenAction.h"
13 | #include "clang/Driver/Compilation.h"
14 | #include "clang/Driver/Driver.h"
15 | #include "clang/Driver/Tool.h"
16 | #include "clang/Frontend/CompilerInstance.h"
17 | #include "clang/Frontend/CompilerInvocation.h"
18 | #include "clang/Frontend/FrontendDiagnostic.h"
19 | #include "clang/Frontend/TextDiagnosticPrinter.h"
20 | #include "llvm/ADT/SmallString.h"
21 | #include "llvm/ExecutionEngine/ExecutionEngine.h"
22 | #include "llvm/ExecutionEngine/Orc/CompileUtils.h"
23 | #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
24 | #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
25 | #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
26 | #include "llvm/ExecutionEngine/SectionMemoryManager.h"
27 | #include "llvm/IR/DataLayout.h"
28 | #include "llvm/IR/Mangler.h"
29 | #include "llvm/IR/Module.h"
30 | #include "llvm/Support/FileSystem.h"
31 | #include "llvm/Support/Host.h"
32 | #include "llvm/Support/ManagedStatic.h"
33 | #include "llvm/Support/Path.h"
34 | #include "llvm/Support/TargetSelect.h"
35 | #include "llvm/Support/raw_ostream.h"
36 | #include "llvm/Target/TargetMachine.h"
37 |
38 | using namespace clang;
39 | using namespace clang::driver;
40 |
41 | // This function isn't referenced outside its translation unit, but it
42 | // can't use the "static" keyword because its address is used for
43 | // GetMainExecutable (since some platforms don't support taking the
44 | // address of main, and some platforms can't implement GetMainExecutable
45 | // without being given the address of a function in the main executable).
46 | std::string GetExecutablePath(const char *Argv0, void *MainAddr) {
47 | return llvm::sys::fs::getMainExecutable(Argv0, MainAddr);
48 | }
49 |
50 | namespace llvm {
51 | namespace orc {
52 |
53 | class SimpleJIT {
54 | private:
55 | ExecutionSession ES { std::move(*SelfExecutorProcessControl::Create()) };
56 | std::unique_ptr TM;
57 | const DataLayout DL;
58 | MangleAndInterner Mangle{ES, DL};
59 | JITDylib &MainJD{ES.createBareJITDylib("")};
60 | RTDyldObjectLinkingLayer ObjectLayer{ES, createMemMgr};
61 | IRCompileLayer CompileLayer{ES, ObjectLayer,
62 | std::make_unique(*TM)};
63 |
64 | static std::unique_ptr createMemMgr() {
65 | return std::make_unique();
66 | }
67 |
68 | SimpleJIT(
69 | std::unique_ptr TM, DataLayout DL,
70 | std::unique_ptr ProcessSymbolsGenerator)
71 | : TM(std::move(TM)), DL(std::move(DL)) {
72 | llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr);
73 | MainJD.addGenerator(std::move(ProcessSymbolsGenerator));
74 | }
75 |
76 | public:
77 | static Expected> Create() {
78 | #ifdef __aarch64__
79 | auto JTMB = new JITTargetMachineBuilder(Triple("arm64-apple-darwin")); // FIXME: Memory leaks here
80 | #else
81 | auto JTMB = JITTargetMachineBuilder::detectHost();
82 | if (!JTMB)
83 | return JTMB.takeError();
84 | #endif
85 |
86 | auto TM = JTMB->createTargetMachine();
87 | if (!TM)
88 | return TM.takeError();
89 |
90 | auto DL = (*TM)->createDataLayout();
91 |
92 | auto ProcessSymbolsGenerator =
93 | DynamicLibrarySearchGenerator::GetForCurrentProcess(
94 | DL.getGlobalPrefix());
95 |
96 | if (!ProcessSymbolsGenerator)
97 | return ProcessSymbolsGenerator.takeError();
98 |
99 | return std::unique_ptr(new SimpleJIT(
100 | std::move(*TM), std::move(DL), std::move(*ProcessSymbolsGenerator)));
101 | }
102 |
103 | const TargetMachine &getTargetMachine() const { return *TM; }
104 |
105 | Error addModule(ThreadSafeModule M) {
106 | return CompileLayer.add(MainJD, std::move(M));
107 | }
108 |
109 | Expected findSymbol(const StringRef &Name) {
110 | return ES.lookup({&MainJD}, Mangle(Name));
111 | }
112 |
113 | Expected getSymbolAddress(const StringRef &Name) {
114 | auto Sym = findSymbol(Name);
115 | if (!Sym)
116 | return Sym.takeError();
117 | return Sym->getAddress();
118 | }
119 | };
120 |
121 | } // end namespace orc
122 | } // end namespace llvm
123 |
124 | llvm::ExitOnError ExitOnErr;
125 |
126 | int clangInterpret(int argc, const char **argv, llvm::raw_ostream &errorOutputStream) {
127 | // This just needs to be some symbol in the binary; C++ doesn't
128 | // allow taking the address of ::main however.
129 | void *MainAddr = (void*) (intptr_t) GetExecutablePath;
130 | std::string Path = GetExecutablePath(argv[0], MainAddr);
131 | IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions();
132 | TextDiagnosticPrinter *DiagClient =
133 | new TextDiagnosticPrinter(errorOutputStream, &*DiagOpts);
134 |
135 | IntrusiveRefCntPtr DiagID(new DiagnosticIDs());
136 | DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);
137 |
138 | #ifdef __aarch64__
139 | const std::string TripleStr = "arm64-apple-darwin";
140 | #else
141 | const std::string TripleStr = llvm::sys::getProcessTriple();
142 | #endif
143 |
144 | llvm::Triple T(TripleStr);
145 |
146 | // Use ELF on Windows-32 and MingW for now.
147 | #ifndef CLANG_INTERPRETER_COFF_FORMAT
148 | if (T.isOSBinFormatCOFF())
149 | T.setObjectFormat(llvm::Triple::ELF);
150 | #endif
151 |
152 | ExitOnErr.setBanner("clang interpreter");
153 |
154 | Driver TheDriver(Path, T.str(), Diags);
155 | TheDriver.setTitle("clang interpreter");
156 | TheDriver.setCheckInputsExist(false);
157 |
158 | // FIXME: This is a hack to try to force the driver to do something we can
159 | // recognize. We need to extend the driver library to support this use model
160 | // (basically, exactly one input, and the operation mode is hard wired).
161 | SmallVector Args(argv, argv + argc);
162 | Args.push_back("-fsyntax-only");
163 | std::unique_ptr C(TheDriver.BuildCompilation(Args));
164 | if (!C)
165 | return 0;
166 |
167 | // FIXME: This is copied from ASTUnit.cpp; simplify and eliminate.
168 |
169 | // We expect to get back exactly one command job, if we didn't something
170 | // failed. Extract that job from the compilation.
171 | const driver::JobList &Jobs = C->getJobs();
172 | if (Jobs.size() != 1 || !isa(*Jobs.begin())) {
173 | SmallString<256> Msg;
174 | llvm::raw_svector_ostream OS(Msg);
175 | Jobs.Print(OS, "; ", true);
176 | Diags.Report(diag::err_fe_expected_compiler_job) << OS.str();
177 | return 1;
178 | }
179 |
180 | const driver::Command &Cmd = cast(*Jobs.begin());
181 | if (llvm::StringRef(Cmd.getCreator().getName()) != "clang") {
182 | Diags.Report(diag::err_fe_expected_clang_command);
183 | return 1;
184 | }
185 |
186 | // Initialize a compiler invocation object from the clang (-cc1) arguments.
187 | const llvm::opt::ArgStringList &CCArgs = Cmd.getArguments();
188 | std::unique_ptr CI(new CompilerInvocation);
189 | CompilerInvocation::CreateFromArgs(*CI, CCArgs, Diags);
190 |
191 | // Show the invocation, with -v.
192 | if (CI->getHeaderSearchOpts().Verbose) {
193 | errorOutputStream << "clang invocation:\n";
194 | Jobs.Print(errorOutputStream, "\n", true);
195 | errorOutputStream << "\n";
196 | }
197 |
198 | // FIXME: This is copied from cc1_main.cpp; simplify and eliminate.
199 |
200 | // Create a compiler instance to handle the actual work.
201 | CompilerInstance Clang;
202 | Clang.setInvocation(std::move(CI));
203 |
204 | // Create the compilers actual diagnostics engine.
205 | Clang.createDiagnostics(DiagClient, false);
206 | if (!Clang.hasDiagnostics())
207 | return 1;
208 |
209 | // Infer the builtin include path if unspecified.
210 | if (Clang.getHeaderSearchOpts().UseBuiltinIncludes &&
211 | Clang.getHeaderSearchOpts().ResourceDir.empty())
212 | Clang.getHeaderSearchOpts().ResourceDir =
213 | CompilerInvocation::GetResourcesPath(argv[0], MainAddr);
214 |
215 | // Create and execute the frontend to generate an LLVM bitcode module.
216 | std::unique_ptr Act(new EmitLLVMOnlyAction());
217 | if (!Clang.ExecuteAction(*Act))
218 | return 1;
219 |
220 | llvm::InitializeNativeTarget();
221 | llvm::InitializeNativeTargetAsmPrinter();
222 |
223 | int Res = 255;
224 | std::unique_ptr Ctx(Act->takeLLVMContext());
225 | std::unique_ptr Module = Act->takeModule();
226 |
227 | if (Module) {
228 | auto J = ExitOnErr(llvm::orc::SimpleJIT::Create());
229 |
230 | ExitOnErr(J->addModule(
231 | llvm::orc::ThreadSafeModule(std::move(Module), std::move(Ctx))));
232 | auto Main = (int (*)(...))ExitOnErr(J->getSymbolAddress("main"));
233 | Res = Main();
234 | }
235 |
236 | // Shutdown.
237 | // llvm::llvm_shutdown();
238 |
239 | return Res;
240 | }
241 |
--------------------------------------------------------------------------------
/Sample/LLVMBridge.h:
--------------------------------------------------------------------------------
1 | //
2 | // LLVMBridge.h
3 | // Declaration of Objective-C class LLVMBridge to expose C++ services of LLVM to Swift
4 | // For simplicity, this file is also used as Swift-ObjC bridging header in this sample project
5 | //
6 | // Created by Lightech on 10/24/2048.
7 | //
8 |
9 | #ifndef LLVMBridge_h
10 | #define LLVMBridge_h
11 |
12 | #import
13 |
14 |
15 | /// Class to encapsulate the interpreter's output
16 | @interface LLVMInterpreterOutput : NSObject
17 |
18 | @property NSString* _Nullable compilationOutput;
19 | @property NSString* _Nullable programOutput;
20 |
21 | @end
22 |
23 |
24 | /// Class (intended to be single-instanced) to provide LLVM C++ service to Swift front-end
25 | @interface LLVMBridge : NSObject
26 |
27 | // Interpret the C++ source code file
28 | - (nonnull LLVMInterpreterOutput*)interpretProgram:(nonnull NSData*)fileName;
29 |
30 | @end
31 |
32 | #endif /* LLVMBridge_h */
33 |
--------------------------------------------------------------------------------
/Sample/LLVMBridge.mm:
--------------------------------------------------------------------------------
1 | //
2 | // LLVMBridge.mm
3 | // LLVMBridge Implementation, pretty much just forwarding calls to C++
4 | //
5 | // Created by Lightech on 10/24/2048.
6 | //
7 |
8 | #include
9 | #include
10 | #include
11 | #include "llvm/Support/raw_ostream.h"
12 |
13 | #import "LLVMBridge.h"
14 |
15 | // Declaration for clangInterpret method, implemented in Interpreter.cpp
16 | // TODO Might want to extract a header
17 | int clangInterpret(int argc, const char **argv, llvm::raw_ostream &errorOutputStream);
18 |
19 | // Let's hope that this method is exported in the app's binary
20 | // Need extern "C" to avoid name mangling so that interpreter can find it
21 | std::string programOutputString;
22 | llvm::raw_string_ostream programOutputStream(programOutputString);
23 | extern "C" void AppConsolePrint(const char *str) {
24 | programOutputStream << str;
25 | }
26 |
27 | // Helper function to construct NSString from a null-terminated C string (presumably UTF-8 encoded)
28 | NSString* NSStringFromCString(const char *text) {
29 | return [NSString stringWithUTF8String:text];
30 | }
31 |
32 | /// Implementation of LLVMInterpreterOutput
33 | @implementation LLVMInterpreterOutput
34 | {
35 | }
36 | @end /* implementation LLVMInterpreterOutput */
37 |
38 | /// Implementation of LLVMBridge
39 | @implementation LLVMBridge
40 | {
41 | }
42 |
43 | - (nonnull LLVMInterpreterOutput*)interpretProgram:(nonnull NSData*)fileName
44 | {
45 | // Prepare null terminate string from fileName buffer
46 | char input_file_path[1024];
47 | memcpy(input_file_path, fileName.bytes, fileName.length);
48 | input_file_path[fileName.length] = '\0';
49 |
50 | // Print out the program for inspection.
51 | printf("\n\n<<>>\n<<<%s>>>\n\n", input_file_path);
52 | auto file = fopen(input_file_path, "rb");
53 | if (file != NULL) {
54 | char c;
55 | while ((c = fgetc(file)) != EOF) printf("%c", c);
56 | fclose(file);
57 | }
58 | printf("\n\n<<>>\n\n");
59 |
60 | // Invoke the interpreter
61 | const char* argv[] = { "clang", input_file_path };
62 | std::string errorString;
63 | programOutputString = ""; // reset program output
64 | llvm::raw_string_ostream errorOutputStream(errorString);
65 | clangInterpret(2, argv, errorOutputStream);
66 |
67 | // Return compilation and program ouput
68 | LLVMInterpreterOutput *result = [[LLVMInterpreterOutput alloc] init];
69 | errorString = errorOutputStream.str(); // Needed to flush the output
70 | programOutputString = programOutputStream.str();
71 | printf("\n\nCompilation output: %s\n", errorString.c_str());
72 | result.compilationOutput = NSStringFromCString(errorString.c_str());
73 | result.programOutput = NSStringFromCString(programOutputString.c_str());
74 |
75 | return result;
76 | }
77 |
78 | @end /* implementation LLVMBridge */
79 |
--------------------------------------------------------------------------------
/Sample/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Sample/Sample.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.network.client
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Sample/SampleApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SampleApp.swift
3 | // Sample
4 | //
5 | // Created by Lightech on 10/24/2048.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct SampleApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Screenshots/DisableBitcode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/light-tech/LLVM-On-iOS/f43c7873c0df031e529f8acc07e012fda3e1dda9/Screenshots/DisableBitcode.png
--------------------------------------------------------------------------------
/Screenshots/ObjCBridgeHeader.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/light-tech/LLVM-On-iOS/f43c7873c0df031e529f8acc07e012fda3e1dda9/Screenshots/ObjCBridgeHeader.png
--------------------------------------------------------------------------------
/Screenshots/Screenshot1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/light-tech/LLVM-On-iOS/f43c7873c0df031e529f8acc07e012fda3e1dda9/Screenshots/Screenshot1.png
--------------------------------------------------------------------------------
/Screenshots/Screenshot2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/light-tech/LLVM-On-iOS/f43c7873c0df031e529f8acc07e012fda3e1dda9/Screenshots/Screenshot2.png
--------------------------------------------------------------------------------
/Screenshots/Screenshot_Real_iPhone1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/light-tech/LLVM-On-iOS/f43c7873c0df031e529f8acc07e012fda3e1dda9/Screenshots/Screenshot_Real_iPhone1.png
--------------------------------------------------------------------------------
/Screenshots/Screenshot_Real_iPhone2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/light-tech/LLVM-On-iOS/f43c7873c0df031e529f8acc07e012fda3e1dda9/Screenshots/Screenshot_Real_iPhone2.png
--------------------------------------------------------------------------------
/build-llvm.sh:
--------------------------------------------------------------------------------
1 | # Build LLVM XCFramework
2 | #
3 | # We assume that all required build tools (CMake, ninja, etc.) are either installed and accessible in $PATH.
4 |
5 | # Assume that this script is source'd at this repo root
6 | export REPO_ROOT=`pwd`
7 |
8 | ### Setup the environment variable $targetBasePlatform and $targetArch from the platform-architecture string
9 | ### Argument: the platform-architecture string, must be one of the following
10 | ###
11 | ### iphoneos iphonesimulator iphonesimulator-arm64 maccatalyst maccatalyst-arm64
12 | ###
13 | ### The base platform would be one of iphoneos iphonesimulator maccatalyst and the architecture
14 | ### would be either arm64 or x86_64.
15 | setup_variables() {
16 | local targetPlatformArch=$1
17 |
18 | case $targetPlatformArch in
19 | "iphoneos")
20 | targetArch="arm64"
21 | targetBasePlatform="iphoneos";;
22 |
23 | "iphonesimulator")
24 | targetArch="x86_64"
25 | targetBasePlatform="iphonesimulator";;
26 |
27 | "iphonesimulator-arm64")
28 | targetArch="arm64"
29 | targetBasePlatform="iphonesimulator";;
30 |
31 | "maccatalyst")
32 | targetArch="x86_64"
33 | targetBasePlatform="maccatalyst";;
34 |
35 | "maccatalyst-arm64")
36 | targetArch="arm64"
37 | targetBasePlatform="maccatalyst";;
38 |
39 | *)
40 | echo "Unknown or missing platform!"
41 | exit 1;;
42 | esac
43 | }
44 |
45 | ### Build libffi for a given platform
46 | ### Argument: the platform-architecture
47 | build_libffi() {
48 | local targetPlatformArch=$1
49 | setup_variables $targetPlatformArch
50 |
51 | echo "Build libffi for $targetPlatformArch"
52 |
53 | cd $REPO_ROOT
54 | local libffiReleaseSrcArchiveUrl=https://github.com/libffi/libffi/archive/refs/tags/v3.4.4.tar.gz
55 | local libffiReleaseUrl=https://github.com/libffi/libffi/releases/download/v3.4.4/libffi-3.4.4.tar.gz
56 | # test -d libffi || git clone https://github.com/libffi/libffi.git
57 | # curl -L -o libffi.tar.gz $libffiReleaseSrcArchive
58 | curl -L -o libffi.tar.gz $libffiReleaseUrl
59 | tar xzf libffi.tar.gz
60 | mv libffi-3.4.4 libffi
61 | cd libffi
62 |
63 | # Imitate libffi continuous integration .ci/build.sh script
64 | # Note that we do not need to run autogen if we are using the 'release' $libffiReleaseUrl as libffi dev already
65 | # runs it to generate configure script.
66 | # It is only needed when using the source archive $libffiReleaseSrcArchiveUrl (zipped repo at certain commit)
67 | # or when we build on the source repo.
68 | # ./autogen.sh
69 | ./generate-darwin-source-and-headers.py --only-ios
70 |
71 | case $targetPlatformArch in
72 | "iphoneos")
73 | xcodeSdkArgs=(-sdk $targetBasePlatform);;
74 |
75 | "iphonesimulator"|"iphonesimulator-arm64")
76 | xcodeSdkArgs=(-sdk $targetBasePlatform -arch $targetArch);;
77 |
78 | "maccatalyst"|"maccatalyst-arm64")
79 | xcodeSdkArgs=(-arch $targetArch);; # Do not set SDK
80 |
81 | *)
82 | echo "Unknown or missing platform!"
83 | exit 1;;
84 | esac
85 |
86 | # xcodebuild -list
87 | # Note that we need to run xcodebuild twice
88 | # The first run generates necessary headers whereas the second run actually compiles the library
89 | local libffiBuildDir=$REPO_ROOT/libffi
90 | for r in {1..2}; do
91 | xcodebuild -scheme libffi-iOS "${xcodeSdkArgs[@]}" -configuration Release SYMROOT="$libffiBuildDir" # >/dev/null 2>/dev/null
92 | done
93 |
94 | local libffiInstallDir=$libffiBuildDir/Release-$targetBasePlatform
95 | lipo -info $libffiInstallDir/libffi.a
96 | mv $libffiInstallDir $REPO_ROOT/libffi-$targetPlatformArch
97 | }
98 |
99 | get_llvm_src() {
100 | #git clone --single-branch --branch release/14.x https://github.com/llvm/llvm-project.git
101 |
102 | curl -OL https://github.com/llvm/llvm-project/releases/download/llvmorg-15.0.6/llvm-project-15.0.6.src.tar.xz
103 | tar xzf llvm-project-15.0.6.src.tar.xz
104 | mv llvm-project-15.0.6.src llvm-project
105 | }
106 |
107 | ### Prepare the LLVM built for usage in Xcode
108 | ### Argument: the platform-architecture
109 | prepare_llvm() {
110 | local targetPlatformArch=$1
111 | local libffiInstallDir=$REPO_ROOT/libffi-$targetPlatformArch
112 |
113 | echo "Prepare LLVM for $targetPlatformArch"
114 | cd $REPO_ROOT/LLVM-$targetPlatformArch
115 |
116 | # Copy libffi
117 | cp -r $libffiInstallDir/include/ffi ./include/
118 | cp $libffiInstallDir/libffi.a ./lib/
119 |
120 | # Combine all *.a into a single llvm.a for ease of use
121 | libtool -static -o llvm.a lib/*.a
122 |
123 | # This is to check if we find platform 1 (macOS desktop) in the mixed with platform 6 (macCatalyst).
124 | # This reveals that the assembly file blake3_sse41_x86-64_unix.S is not compiled for macCatalyst!
125 | # Looking at BLAKE3 https://github.com/llvm/clangir/blob/main/llvm/lib/Support/BLAKE3/CMakeLists.txt
126 | # reveals that we want to configure LLVM with LLVM_DISABLE_ASSEMBLY_FILES.
127 | otool -l llvm.a
128 |
129 | # Remove unnecessary lib files if packaging
130 | rm -rf lib/*.a
131 | }
132 |
133 | ### Build LLVM for a given iOS platform
134 | ### Argument: the platform-architecture
135 | ### Assumptions:
136 | ### * LLVM is checked out inside this repo
137 | ### * libffi is built at libffi-[platform]
138 | build_llvm() {
139 | local targetPlatformArch=$1
140 | local llvmProjectSrcDir=$REPO_ROOT/llvm-project
141 | local llvmInstallDir=$REPO_ROOT/LLVM-$targetPlatformArch
142 | local libffiInstallDir=$REPO_ROOT/libffi-$targetPlatformArch
143 |
144 | setup_variables $targetPlatformArch
145 |
146 | echo "Build llvm for $targetPlatformArch"
147 |
148 | cd $REPO_ROOT
149 | test -d llvm-project || get_llvm_src
150 | cd llvm-project
151 | rm -rf build
152 | mkdir build
153 | cd build
154 |
155 | # https://opensource.com/article/18/5/you-dont-know-bash-intro-bash-arrays
156 | # ;lld;libcxx;libcxxabi
157 | local llvmCmakeArgs=(-G "Ninja" \
158 | -DLLVM_ENABLE_PROJECTS="clang" \
159 | -DLLVM_TARGETS_TO_BUILD="AArch64;X86" \
160 | -DLLVM_BUILD_TOOLS=OFF \
161 | -DCLANG_BUILD_TOOLS=OFF \
162 | -DBUILD_SHARED_LIBS=OFF \
163 | -DLLVM_ENABLE_ZLIB=OFF \
164 | -DLLVM_ENABLE_ZSTD=OFF \
165 | -DLLVM_ENABLE_THREADS=OFF \
166 | -DLLVM_ENABLE_UNWIND_TABLES=OFF \
167 | -DLLVM_ENABLE_EH=OFF \
168 | -DLLVM_ENABLE_RTTI=OFF \
169 | -DLLVM_ENABLE_TERMINFO=OFF \
170 | -DLLVM_ENABLE_FFI=ON \
171 | -DLLVM_DISABLE_ASSEMBLY_FILES=ON \
172 | -DFFI_INCLUDE_DIR=$libffiInstallDir/include/ffi \
173 | -DFFI_LIBRARY_DIR=$libffiInstallDir \
174 | -DCMAKE_BUILD_TYPE=Release \
175 | -DCMAKE_INSTALL_PREFIX=$llvmInstallDir \
176 | -DCMAKE_TOOLCHAIN_FILE=../llvm/cmake/platforms/iOS.cmake)
177 |
178 | case $targetPlatformArch in
179 | "iphoneos")
180 | llvmCmakeArgs+=(-DLLVM_TARGET_ARCH=$targetArch);;
181 |
182 | "iphonesimulator"|"iphonesimulator-arm64")
183 | llvmCmakeArgs+=(-DCMAKE_OSX_SYSROOT=$(xcodebuild -version -sdk iphonesimulator Path));;
184 |
185 | "maccatalyst"|"maccatalyst-arm64")
186 | llvmCmakeArgs+=(-DCMAKE_OSX_SYSROOT=$(xcodebuild -version -sdk macosx Path) \
187 | -DCMAKE_C_FLAGS="-target $targetArch-apple-ios14.1-macabi" \
188 | -DCMAKE_CXX_FLAGS="-target $targetArch-apple-ios14.1-macabi");;
189 |
190 | *)
191 | echo "Unknown or missing platform!"
192 | exit 1;;
193 | esac
194 |
195 | llvmCmakeArgs+=(-DCMAKE_OSX_ARCHITECTURES=$targetArch)
196 |
197 | # https://www.shell-tips.com/bash/arrays/
198 | # https://www.lukeshu.com/blog/bash-arrays.html
199 | printf 'CMake Argument: %s\n' "${llvmCmakeArgs[@]}"
200 |
201 | # Generate configuration for building for iOS Target (on MacOS Host)
202 | # Note: AArch64 = arm64
203 | # Note: We have to use include/ffi subdir for libffi as the main header ffi.h
204 | # includes and not . So if we only use
205 | # $DOWNLOADS/libffi/Release-iphoneos/include for FFI_INCLUDE_DIR
206 | # the platform-specific header would not be found!
207 | cmake "${llvmCmakeArgs[@]}" ../llvm || exit -1 # >/dev/null 2>/dev/null
208 |
209 | # When building for real iOS device, we need to open `build_ios/CMakeCache.txt` at this point, search for and FORCIBLY change the value of **HAVE_FFI_CALL** to **1**.
210 | # For some reason, CMake did not manage to determine that `ffi_call` was available even though it really is the case.
211 | # Without this, the execution engine is not built with libffi at all.
212 | sed -i.bak 's/^HAVE_FFI_CALL:INTERNAL=/HAVE_FFI_CALL:INTERNAL=1/g' CMakeCache.txt
213 |
214 | # Build and install
215 | cmake --build . --target install # >/dev/null 2>/dev/null
216 |
217 | prepare_llvm $targetPlatformArch
218 | }
219 |
220 | # Input: List of (base) platforms to be included in the XCFramework
221 | # Argument: the list of platform-architectures to include in the framework
222 | create_xcframework() {
223 | local xcframeworkSupportedPlatforms=("$@")
224 |
225 | # Construct xcodebuild arguments
226 | local xcodebuildCreateXCFArgs=()
227 | for p in "${xcframeworkSupportedPlatforms[@]}"; do
228 | xcodebuildCreateXCFArgs+=(-library LLVM-$p/llvm.a -headers LLVM-$p/include)
229 |
230 | cd $REPO_ROOT
231 | test -f libclang.tar.xz || echo "Create clang support headers archive" && tar -cJf libclang.tar.xz LLVM-$p/lib/clang/
232 | done
233 |
234 | echo "Create XC framework with arguments ${xcodebuildCreateXCFArgs[@]}"
235 | cd $REPO_ROOT
236 | xcodebuild -create-xcframework "${xcodebuildCreateXCFArgs[@]}" -output LLVM.xcframework
237 | }
238 |
--------------------------------------------------------------------------------
/build-tools.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # From https://gist.github.com/GraemeConradie/49d2f5962fa72952bc6c64ac093db2d5
3 |
4 | ##
5 | # Install autoconf, automake and libtool smoothly on Mac OS X.
6 | # Newer versions of these libraries are available and may work better on OS X
7 | ##
8 |
9 | export build=`pwd`/temp # or wherever you'd like to build
10 | export install=`pwd`/tools
11 | mkdir -p $build
12 |
13 | ##
14 | # Autoconf
15 | # http://ftpmirror.gnu.org/autoconf
16 |
17 | cd $build
18 | curl -OL http://ftpmirror.gnu.org/autoconf/autoconf-2.69.tar.gz
19 | tar xzf autoconf-2.69.tar.gz
20 | cd autoconf-2.69
21 | ./configure --prefix=$install
22 | make
23 | make install
24 | # export PATH=$PATH:$install
25 |
26 | ##
27 | # Automake
28 | # http://ftpmirror.gnu.org/automake
29 |
30 | cd $build
31 | curl -OL http://ftpmirror.gnu.org/automake/automake-1.15.tar.gz
32 | tar xzf automake-1.15.tar.gz
33 | cd automake-1.15
34 | ./configure --prefix=$install
35 | make
36 | make install
37 |
38 | ##
39 | # Libtool
40 | # http://ftpmirror.gnu.org/libtool
41 |
42 | cd $build
43 | curl -OL http://ftpmirror.gnu.org/libtool/libtool-2.4.6.tar.gz
44 | tar xzf libtool-2.4.6.tar.gz
45 | cd libtool-2.4.6
46 | ./configure --prefix=$install
47 | make
48 | make install
49 |
50 | echo "Installation complete."
--------------------------------------------------------------------------------