Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@
*.png binary
*.jpg binary
*.pdf binary
*.jar binary
2 changes: 2 additions & 0 deletions .github/config/conan/profiles/android-armv7
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{% set arch = "armv7" %}
{% include "android.jinja" %}
2 changes: 2 additions & 0 deletions .github/config/conan/profiles/android-armv8
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{% set arch = "armv8" %}
{% include "android.jinja" %}
2 changes: 2 additions & 0 deletions .github/config/conan/profiles/android-x86
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{% set arch = "x86" %}
{% include "android.jinja" %}
2 changes: 2 additions & 0 deletions .github/config/conan/profiles/android-x86_64
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{% set arch = "x86_64" %}
{% include "android.jinja" %}
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
{# Shared body of the `android-*` profiles; the includer sets `arch`.
Not usable on its own β€” always include it from a per-ABI profile.

`api_level` is the *minimum* android version the native code runs on, and it
is baked into the target triple, so it has to match the java floor of the AAR
(`android/`, minSdk 26) β€” a higher value produces libraries the linker
refuses to load on older devices. #}
{% set android_home = os.getenv("ANDROID_HOME") %}
{% set ndk_version = "28.1.13356709" %}
{% set api_level = "35" %}
{% set arch = "x86_64" %}
{% set api_level = "26" %}
{% set host_tag = "darwin-x86_64" if platform.system() == "Darwin" else "linux-x86_64" %}
{% set toolchain = android_home + "/ndk/" + ndk_version + "/toolchains/llvm/prebuilt/" + host_tag %}
{% set cc = {
"armv7": "armv7a-linux-androideabi" + api_level + "-clang",
"armv8": "aarch64-linux-android" + api_level + "-clang",
Expand All @@ -27,10 +35,10 @@ tools.cmake.cmaketoolchain:extra_variables={'CMAKE_CXX_COMPILER_LAUNCHER': 'ccac
# Cross compile toolchain evn vars are required to build
# libffi, libgettext, libiconv, libxml2 and other autotools packages for Android
# https://github.com/conan-io/conan/issues/16493
AR={{android_home}}/ndk/{{ndk_version}}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar
AS={{android_home}}/ndk/{{ndk_version}}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-as
RANLIB={{android_home}}/ndk/{{ndk_version}}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ranlib
CC=ccache {{android_home}}/ndk/{{ndk_version}}/toolchains/llvm/prebuilt/linux-x86_64/bin/{{cc}}
CXX=ccache {{android_home}}/ndk/{{ndk_version}}/toolchains/llvm/prebuilt/linux-x86_64/bin/{{cc}}++
LD={{android_home}}/ndk/{{ndk_version}}/toolchains/llvm/prebuilt/linux-x86_64/bin/ld
STRIP={{android_home}}/ndk/{{ndk_version}}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip
AR={{toolchain}}/bin/llvm-ar
AS={{toolchain}}/bin/llvm-as
RANLIB={{toolchain}}/bin/llvm-ranlib
CC=ccache {{toolchain}}/bin/{{cc}}
CXX=ccache {{toolchain}}/bin/{{cc}}++
LD={{toolchain}}/bin/ld
STRIP={{toolchain}}/bin/llvm-strip
244 changes: 244 additions & 0 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
name: android

on:
push:
release:
types: [published]
workflow_dispatch:
inputs:
version:
description: version to publish (e.g. 6.0.0-test1)
required: true

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

# See the cache-key comment in `build_test.yml` for why the keys look like this.
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_MAXSIZE: 1G
CCACHE_KEY_SUFFIX: r1
CONAN_HOME: ${{ github.workspace }}/.conan2
CONAN_KEY_SUFFIX: r1
NDK_VERSION: 28.1.13356709

jobs:
# One cross build per ABI, uploaded as an artifact so the AAR job below can
# assemble all of them without cross compiling four times in one runner.
native:
runs-on: ubuntu-24.04
env:
CACHE_FLAVOR: aar
strategy:
fail-fast: false
matrix:
architecture: [armv7, armv8, x86, x86_64]
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: checkout conan-odr-index
run: git submodule update --init --depth 1 conan-odr-index

- name: install ccache
run: |
sudo apt install ccache
ccache -V

- name: setup python 3.14
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: 3.14
- name: install python dependencies
run: pip install conan

- name: install NDK
run: yes | ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --install "ndk;${NDK_VERSION}"

- name: conan cache key
shell: bash
run: echo "CONAN_CACHE_KEY=$(git rev-parse HEAD:conan-odr-index)-${{ hashFiles('conanfile.py', '.github/config/conan/**') }}" >> "$GITHUB_ENV"

- name: cache conan
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ env.CONAN_HOME }}
key: conan-${{ env.CACHE_FLAVOR }}-android-${{ matrix.architecture }}-${{ env.CONAN_KEY_SUFFIX }}-${{ env.CONAN_CACHE_KEY }}
restore-keys: |
conan-${{ env.CACHE_FLAVOR }}-android-${{ matrix.architecture }}-${{ env.CONAN_KEY_SUFFIX }}-

- name: export conan-odr-index
run: python conan-odr-index/scripts/conan_export_all_packages.py --selection-config conan-odr-index/defaults.yaml
- name: conan config
run: conan config install .github/config/conan

- name: restore ccache
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ env.CACHE_FLAVOR }}-android-${{ matrix.architecture }}-${{ env.CCACHE_KEY_SUFFIX }}-${{ github.run_id }}
restore-keys: |
ccache-${{ env.CACHE_FLAVOR }}-android-${{ matrix.architecture }}-${{ env.CCACHE_KEY_SUFFIX }}-

# Cross compiling the bindings is the cheap half of the guard: #628 was a
# compile error in jni/src that only android sees, and no host job can.
- name: build
run: >
python android/build_native.py
--abi ${{ matrix.architecture }}
--build-profile ubuntu-24.04-clang-18

- name: save ccache
if: github.ref == 'refs/heads/main'
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ env.CACHE_FLAVOR }}-android-${{ matrix.architecture }}-${{ env.CCACHE_KEY_SUFFIX }}-${{ github.run_id }}

- name: upload native libraries
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: android-native-${{ matrix.architecture }}
path: android/native/prebuilt
if-no-files-found: error

aar:
needs: native
Comment thread
andiwand marked this conversation as resolved.
runs-on: ubuntu-24.04
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: setup java
uses: actions/setup-java@f4f1212c880fdec8162ea9a6493f4495191887b4 # v5
with:
distribution: temurin
java-version: 21

- name: setup gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6

# every ABI's artifact carries the same assets/, so they merge into the
# one prebuilt tree the gradle build reads
- name: download native libraries
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: android-native-*
merge-multiple: true
path: android/native/prebuilt

# -Podr.abis= : the libraries are the artifacts downloaded above, so the
# build must not try to cross compile them again
- name: lint
working-directory: android
run: ./gradlew lint -Podr.abis=

- name: assemble
working-directory: android
run: ./gradlew assembleRelease -Podr.abis=

- name: upload aar
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: odr-core-android
path: android/build/outputs/aar/*.aar
if-no-files-found: error

# The other half of the guard, and the only one that sees what a device sees:
# API 26 is the floor OpenDocument.droid ships to, where a java.* method the
# JDK has and android does not throws at the call (#621).
instrumented:
needs: native
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
api-level: [26, 36]
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: setup java
uses: actions/setup-java@f4f1212c880fdec8162ea9a6493f4495191887b4 # v5
with:
distribution: temurin
java-version: 21

- name: setup gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6

# the emulator is x86_64, and nothing here needs the other ABIs
- name: download native libraries
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: android-native-x86_64
path: android/native/prebuilt

- name: enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm

- name: instrumented tests
uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2.38.0
with:
api-level: ${{ matrix.api-level }}
arch: x86_64
target: ${{ matrix.api-level >= 30 && 'google_apis' || 'default' }}
working-directory: android
script: ./gradlew connectedDebugAndroidTest -Podr.abis=

- name: upload test report
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: instrumented-report-${{ matrix.api-level }}
path: android/build/reports/androidTests

# Nothing reaches GitHub Packages that the device suite has not run: an AAR
# that assembles and lints is not evidence the bindings work on a device,
# which is the whole point of the two jobs above.
publish:
if: github.event_name != 'push'
needs: [aar, instrumented]
runs-on: ubuntu-24.04
permissions:
contents: read
packages: write
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: setup java
uses: actions/setup-java@f4f1212c880fdec8162ea9a6493f4495191887b4 # v5
with:
distribution: temurin
java-version: 21

- name: setup gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6

- name: download native libraries
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: android-native-*
merge-multiple: true
path: android/native/prebuilt

- name: get version
env:
INPUT_VERSION: ${{ inputs.version }}
run: |
if [ "${{ github.event_name }}" = release ]; then
echo "ODR_VERSION=${GITHUB_REF_NAME:1}" >> $GITHUB_ENV
else
echo "ODR_VERSION=${INPUT_VERSION}" >> $GITHUB_ENV
fi

- name: publish
working-directory: android
run: ./gradlew publishReleasePublicationToGitHubPackagesRepository -Podr.abis=
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
8 changes: 3 additions & 5 deletions .github/workflows/build_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ jobs:
- { os: macos-15, build_profile: macos-15-armv8-clang-14, host_profile: macos-15-armv8-clang-14 }
- { os: macos-26, build_profile: macos-26-armv8-clang-14, host_profile: macos-26-armv8-clang-14, bindings: true }
- { os: windows-2022, build_profile: windows-2022-msvc-1940, host_profile: windows-2022-msvc-1940 }
- { os: ubuntu-24.04, build_profile: ubuntu-24.04-clang-18, host_profile: android-35-x86_64, ndk_version: 28.1.13356709 }
# android is covered by `android.yml`, which cross compiles the core
# *and* the JNI bindings for every ABI the AAR ships and runs them on
# an emulator
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
Expand Down Expand Up @@ -75,10 +77,6 @@ jobs:
- name: install python dependencies
run: pip install conan ${{ matrix.bindings && 'pytest' || '' }}

- name: install NDK
if: startsWith(matrix.host_profile, 'android')
run: yes | ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --install 'ndk;${{ matrix.ndk_version }}'

- name: conan cache key
shell: bash
run: echo "CONAN_CACHE_KEY=$(git rev-parse HEAD:conan-odr-index)-${{ hashFiles('conanfile.py', '.github/config/conan/**') }}" >> "$GITHUB_ENV"
Expand Down
22 changes: 22 additions & 0 deletions .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,25 @@ jobs:
run: CLANG_FORMAT=clang-format-18 ./scripts/format

- run: git diff --exit-code --name-only

# The kotlin of `android/`, which clang-format above does not see. spotless
# does not go through the android build, so this needs java and the sdk on the
# runner and nothing else β€” no NDK, no conan, no native libraries.
spotless:
runs-on: ubuntu-24.04
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: setup java
uses: actions/setup-java@f4f1212c880fdec8162ea9a6493f4495191887b4 # v5
with:
distribution: temurin
java-version: 21

- name: setup gradle
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6

- name: spotless
working-directory: android
run: ./gradlew spotlessCheck -Podr.abis=
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ build/
cmake-build-*/
jni/target/
jni/.flattened-pom.xml
## Android AAR (gradle); `build/` above already covers android/build and
## android/native/build
android/.gradle/
android/.kotlin/
android/local.properties
android/native/prebuilt/
.clangd
CMakeUserPresets.json
# machine-specific Conan cache paths, generated by scripts/gen-vscode-env.py
Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ bytes ─▢ magic/open_strategy ─▢ DecodedFile ─▢ Document ─▢ Eleme
| `cli/src/` | CLI tools: `translate`, `back_translate`, `meta`, `server`. |
| `python/` | Python bindings (`pyodr`, pybind11); see [`python/AGENTS.md`](python/AGENTS.md). |
| `jni/` | JNI bindings (Java package `app.opendocument.core`); see [`jni/AGENTS.md`](jni/AGENTS.md). |
| `android/` | The bindings packaged as an AAR (`odr-core-android`) + the instrumented tests; see [`android/AGENTS.md`](android/AGENTS.md). |
| `tools/pdf/` | Dev tooling (not built): PDF encoding-data generators, see `tools/pdf/README.md`. |
| `test/src/` | GoogleTest suites; data in `test/data` (git submodules). |
| `offline/documentation/MS-*/` | Vendored Microsoft spec text (see [Specs](#specs)). |
Expand Down
Loading
Loading