├── .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 | ![Edit the program screenshot](Screenshots/Screenshot1.png) 6 | ![Interpret the program screenshot](Screenshots/Screenshot2.png) 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 | ![Add #include non-existing header](Screenshots/Screenshot_Real_iPhone1.png) 53 | ![Compilation error was printed out](Screenshots/Screenshot_Real_iPhone2.png) 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 | ![Objective-C Bridging Header Setting](Screenshots/ObjCBridgeHeader.png) 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 | ![Bitcode Setting](Screenshots/DisableBitcode.png) 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." --------------------------------------------------------------------------------