Add packaging Documentation (#52)

* Add apple documentation

* Fix web documentation

* Update android docs

* Add script for serving the book

* Update web docs
This commit is contained in:
Max Ammann 2022-05-01 18:30:24 +02:00 committed by GitHub
parent 4f2a5da25a
commit 187cf9f5a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 229 additions and 52 deletions

17
.idea/runConfigurations/Serve_Book.xml generated Normal file
View File

@ -0,0 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Serve Book" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="" />
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/justfile" />
<option name="SCRIPT_OPTIONS" value="book-serve" />
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
<option name="INTERPRETER_PATH" value="/bin/env" />
<option name="INTERPRETER_OPTIONS" value="just --justfile" />
<option name="EXECUTE_IN_TERMINAL" value="false" />
<option name="EXECUTE_SCRIPT_FILE" value="true" />
<envs />
<method v="2" />
</configuration>
</component>

View File

@ -1,6 +1,18 @@
# Android
## Gradle Project Setup
## Gradle Project setup
In order to package an Android `.aar` archive we use
the [rust-android-gradle](https://github.com/mozilla/rust-android-gradle).
Except some customisations for the latest NDK toolchain release everything worked flawlessly.
## NativeActivity
## JNI
There is no way right now to automatically generate JNI stubs for Rust. A manual example is available in the android
crate of maplibre-rs.
## Single NativeActivity
Right now `winit` only allows the usage of a `NativeActivity`. This means the application needs to run in fullscreen.
This native activity is referenced in the ´AndroidManifest.xml` by defining the name of the shared library.
[Tracking Issue](https://github.com/maplibre/maplibre-rs/issues/28)

View File

@ -1,26 +1,166 @@
# Apple
{{#include figures/diff-macOS-entitlements.html}}
On Apple maplibre-rs is packaged as:
* Multiple .xcarchive packages which include a framework. Each for a different architecture and platform.
* A single .xcframework package which contains multiple frameworks of different architectures and platforms.
* A swift package which just references the .xcframework package and makes distributing easier.
## Library Entry
The following diffs are extracted from [this diff](../../../../apple/framework.diff). They should serve as documentation
for the XCode project. This is required because XCode is a mess.
## XCode Project description
### Library Entry
{{#include figures/diff-maplibre-swift.html}}
## Files & Assets
The swift code above is the main entry for the Swift API. From this entry file we can expose more API of maplibre-rs.
Any C functions which are referenced in the XCode framework's header are available automatically in Swift.
{{#include figures/diff-xcode-project-assets.html}}
## Framework
### Framework
{{#include figures/diff-xcode-project-framework.html}}
## Cargo Build Phase
The framework needs to link against the static library `libmaplibre_apple.a`, which has been generated by Cargo.
In order to allow XCode to dynamically select the library based on the `Library Search Path` (Build Settings) one needs
to add a relative file to XCode. The entry in the `project.pbxproj` should look like that:
```js
B085D5A32812987B00906D21 /* libmaplibre_apple.a */ = {
isa = PBXFileReference;
lastKnownFileType = archive.ar;
path = libmaplibre_apple.a;
sourceTree = SOURCE_ROOT;
};
```
Note the `path = libmaplibre_apple.a`. This path does not link to a concrete file, but to a file which can be found
during building.
A file can be added to the frameworks and library link phase in XCode.
### Cargo Build Phase
{{#include figures/diff-xcode-project-build-cargo.html}}
## Build Settings
In order to trigger Cargo builds when starting a XCode build we include a `Cargo Build` script. This build script needs
to run before the linking phase (drag and drop it to the top).
The following build script builds based on XCode environment variables the correct static library. We depend on
the `$ARCHS`
environment variable, as the others seem unreliable. Note that this can include multiple architectures, unless the build
setting `ONLY_ACTIVE_ARCH` is set to `YES`.
```bash
. "$HOME/.cargo/env"
arch="unknown"
vendor="apple"
os_type="unknown"
environment_type=""
mode=""
echo "ARCH: $ARCHS"
if [[ $CONFIGURATION == "Release" ]]
then
mode="--release"
fi
if [[ $ARCHS == "x86_64" ]]
then
arch="x86_64"
elif [[ $ARCHS == "arm64" ]]
then
arch="aarch64"
fi
if [[ $SDK_NAME == *"iphoneos"* ]]
then
os_type="ios"
elif [[ $SDK_NAME == *"macos"* ]]
then
os_type="darwin"
elif [[ $SDK_NAME == *"iphonesimulator"* ]]
then
os_type="ios"
environment_type="sim"
fi
triplet="$arch-$vendor-$os_type"
if [ -n "$environment_type" ]
then
triplet="$triplet-$environment_type"
fi
echo "$mode"
echo "$triplet"
env -i zsh -c "cargo build -p maplibre-apple $mode --target $triplet --lib"
```
### Build Settings
{{#include figures/diff-xcode-project-build-settings.html}}
## Info Plist for Applications
Explanations for the settings:
{{#include figures/diff-xcode-project-info-plist.html}}
* `BUILD_LIBRARY_FOR_DISTRIBUTION`: Define that this is a library (effect unknown to me)
* `CODE_SIGN_STYLE`: The framework is not signed
* `DEVELOPMENT_TEAM`: No development team is set
* `LIBRARY_SEARCH_PATHS[sdk=x][arch=y]`: We set the path for the `libmaplibre_apple.a` lies
* `MACH_O_TYPE` / `SKIP_INSTALL`: If this is not set to `staticlib` and `NO`, then the `libmaplibre_apple.a` binary is not included in the final framework xcarchive.
* `SUPPORTED_PLATFORMS`: Explicitly says that this library works on any platform.
* `SUPPORTS_MACCATALYST`: Explicitly says that this library works on Mac Catalyst.
The same settings are done for Release and Debug.
## xcframework packaging
Creating a xcframework is usually quite straight forward. Just execute the following:
```bash
xargs xcodebuild -create-xcframework -framework ./a -framework ./b -output out.xcframework
```
Unfortunately, it is not possible to bundle some frameworks together like:
* macOS-arm64 and macOS-x86_64
In order to package these architectures and platforms together a fat binary needs to be created using the `lipo` tool.
This means from two frameworks we create a unified framework with a fat binary.
There are two important steps:
1. Create a fat binary using `lipo -create binA binB -output binfat`
2. Copy for example the arm64 framework and add the `.swiftmodule` definitions from the x86_64 framework
## Single UIApplication
Right now `winit` only allows the usage of a `UIApplication`. This means the application needs to run in fullscreen.
[Tracking Issue](https://github.com/maplibre/maplibre-rs/issues/28)
## Example App
The following settings are important for the example application within the XCode project.
### Info Plist for Applications
{{#include figures/diff-xcode-project-info-plist.html}}
* The `INFOPLIST_KEY_UIApplicationSceneManifest_Generation` needs to be unset. Else the application screen is just black.
### Files & Assets
{{#include figures/diff-xcode-project-assets.html}}
* The example/demo application within the XCode project references the `maplibre_rs.framework`. Some default files have
been removed.
### MacOS Entitlements
{{#include figures/diff-macOS-entitlements.html}}
* On macOS one needs to allow network access via `com.apple.security.network.client`

View File

@ -6,7 +6,7 @@ This document describes issues and challenges when packaging maplibre-rs as a np
### ESM
The ESM module format is the standard nowadays which should be followed. If a bundler like webpack encounters an ESM
The ESM module format is the standard nowadays which should be followed. If a JS bundler encounters an ESM
module it can resolve WebAssembly files or WebWorkers dynamically.
The following syntax is used to resolve referenced WebWorkers:
@ -28,7 +28,8 @@ new URL('index_bg.wasm', import.meta.url);
> object. This allows quick prototyping/playgrounds/experiments using maplibre-rs.
In order to support this we need to create a bundle which works on any modern browser. Additionally, a WASM file and
WebWorker needs to be deployed at a predictable path, because there is no bundler active which manages assets.
WebWorker needs to be deployed at a predictable path, because there is no bundler active which manages assets. Users of
these libraries have to specify where WASM or non-inlined WebWorkers are located.
Both assets could be inlined theoretically. This is common for WebWorkers, but not for WASM files.
@ -42,18 +43,14 @@ Both assets could be inlined theoretically. This is common for WebWorkers, but n
> Not needed for the browser build of maplibre-rs, possibly needed when supporting Node
With a CommonJS module its is not possible for bundlers to dynamically resolve WebWorkers or WASM files. Users of these
libraries have
to specify where WASM or non-inlined WebWorkers are hosted.
With a CommonJS module its is not possible for bundlers to dynamically resolve WebWorkers or WASM files.
The `import.meta.url` token can not exist in a CommonJS module. Therefore, bundlers which encounter a CommonJS module
have to use a different mechanism of resolving files.
* The Parcel bundler translates to `new URL('index_bg.wasm', import.meta.url);`
to `new URL("index_bg.wasm", "file:" + __filename);`
While depending on `file:` and `filename` works for NodeJS, it is unsupported in the browser
* Webpack translates `new URL('index_bg.wasm', import.meta.url);` to something that is equivalent to `'./index_bg.wasm'`
. It just expects that assets are resolvable from the current file.
Generally, we do not need to support CommonJS, because we are not targeting Node with maplibre-rs. It's properly good to
support it as a fallback though, for bundlers which can not deal with ESM modules yet.
This is for example true for test runners like Jest which require that dependencies are available as CJS module.
## wasm-pack output
@ -67,25 +64,22 @@ Therefore, we should stick to the `web` output format.
## Required Features
* WASM Bundling
> Make the WASM binary available to users of the maplibre-rs library
* WebWorker Bundling
> Make the WebWorker available to users of the maplibre-rs library. This could also be achived by inlining.
* WebWorker Inlining
> Inline the WebWorker bundle in the library bundle as a string.
* Predictable paths for CJS
> TODO
* WASM Bundling: Make the WASM binary available to users of the maplibre-rs library
* WebWorker Bundling: Make the WebWorker available to users of the maplibre-rs library. This could also be achived by inlining.
* WebWorker Inlining: Inline the WebWorker bundle in the library bundle as a string.
* Predictable Paths: Without predictable paths, it's difficult for users to reference the wasm file directly from the `node_modules` directory if requried.
## Bundler Feature Comparison
| Bundler | *ESM* | *IIFE* | CJS | UMD | *WebWorker Inlining* | Web Worker Bundling | *WASM Bundling* | *Predictable Paths* |
|---------------|-------|--------|-----|-----|----------------------|---------------------|-----------------|---------------------|
| Babel 1) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| TypeScript 1) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Webpack | ❌ 4) | ❓ | ❌ | ❓ | ❌ 2) | ✅ | ✅ | ❓ |
| Parcel | ✅ | ❌ | ✅ | ❌ | 🛠️ 3) | ✅ | ✅ | ❌ 5) |
| ESBuild | ✅ | ✅ | ✅ | ❌ | ✅ 6) | ❓ | ✅ 6) | ✅ |
| Rollup | ❓ | ❓ | ❓ | ❓ | ❓ | ❓ | ❓ | ❓ |
| Bundler | *ESM* | *IIFE* | CJS | UMD | *WebWorker Inlining* | Web Worker Bundling | *WASM Bundling* | *Predictable Paths* | Inlining Environment Variables |
|---------------|-------|--------|-----|-----|----------------------|---------------------|-----------------|---------------------|--------------------------------|
| Babel 1) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
| TypeScript 1) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| Webpack | ❌ 4) | ❓ | ❌ | ❓ | ❌ 2) | ✅ | ✅ | ❓ | ✅ |
| Parcel | ✅ | ❌ | ✅ | ❌ | 🛠️ 3) | ✅ | ✅ | ❌ 5) | ✅ |
| ESBuild | ✅ | ✅ | ✅ | ❌ | ✅ 6) | ❓ | ✅ 6) | ✅ | ✅ |
| Rollup | ❓ | ❓ | ❓ | ❓ | ❓ | ❓ | ❓ | ❓ | ✅ |
Features in ***italic***s are required for maplibre-rs.
@ -96,6 +90,20 @@ Features in ***italic***s are required for maplibre-rs.
> 5) Plugins exist, but they don't work reliably
> 6) Plugins exist, and work reliably
### ESBuild
ESBuild supports CJS, ESM and IIFI modules equally well. Plugins exist for WebWorker inlining and resolving assets
through `import.meta.url`. The plugin quality seems to be superior compared to Parcel. It is also very fast compared to
all other bundlers.
* IIFI: The esbuild bundler translates to `new URL('index_bg.wasm', import.meta.url);` to
```js
var __currentScriptUrl__ = document.currentScript && document.currentScript.src || document.baseURI;
new URL("./assets/index_bg.wasm?emit=file", __currentScriptUrl__);
```
See config in `web/lib/build.mjs` for an example usage.
### Babel & TypeScript
Babel and TypeScript both can produce ESM modules, but they **fail with transforming references within the source code**
@ -103,13 +111,16 @@ like `new URL("./pool.worker.ts", import.meta.url)`. There exist some Babel plug
Therefore, we actually need a proper bundler which supports outputting ESM modules.
The only stable solution to this is Parcel. Parcel also has good documentation around the bundling of WebWorkers.
### WebPack
TODO
WebPack supports older module formats like CommonJS or UMD very well. It falls short when bundling the format ESM
format which is not yet stable. It also does not support inlining WebWorkers in version 5. The wasm-pack plugin
for WebPack makes including Cargo projects easy.
* CJS: Webpack translates `new URL('index_bg.wasm', import.meta.url);` to something that is equivalent to `'./index_bg.wasm'`
. It just expects that assets are resolvable from the current file.
Example scripts:
Example scripts for `package.json`:
```json
{
@ -156,8 +167,7 @@ module.exports = (env) => ({
use: [
{
loader: 'ts-loader',
options: {
}
options: {}
}
]
},
@ -219,9 +229,14 @@ module.exports = (env) => ({
### Parcel
TODO
Parcel supports CommonJS and ESM modules equally good. The documentation about `import.meta.url` is very good. In other
bundlers documentations around this feature is missing. In the latest Parcel version inlining WebWorkers is not working.
Example scripts:
* CJS: The Parcel bundler translates to `new URL('index_bg.wasm', import.meta.url);`
to `new URL("index_bg.wasm", "file:" + __filename);`
While depending on `file:` and `filename` works for NodeJS, it is unsupported in the browser.
Example scripts for `package.json`:
```json
{
@ -260,13 +275,6 @@ Example config in `package.json:
}
```
### ESBuild
TODO
See config in `web/lib/build.mjs`.
### Rollup
Not yet evaluated