diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cd4c1d4d..9332edf4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,3 +28,27 @@ jobs: uses: gradle/actions/setup-gradle@v4.2.1 - name: Test with Gradle run: ./gradlew test --stacktrace + + android-native: + name: Build Android native library + runs-on: ubuntu-22.04 + env: + ORG_GRADLE_PROJECT_ossrhUsername: na + ORG_GRADLE_PROJECT_ossrhPassword: na + ANDROID_NDK_VERSION: "27.1.12297006" + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'corretto' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4.2.1 + - name: Set up Android SDK + uses: android-actions/setup-android@v3 + - name: Install Android NDK + run: | + "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" "ndk;${ANDROID_NDK_VERSION}" + - name: Build Android native library + run: ./gradlew :tree-sitter:buildNative -PtreeSitterTargets=aarch64-linux-android --stacktrace diff --git a/buildSrc/src/main/groovy/BuildNativeTask.groovy b/buildSrc/src/main/groovy/BuildNativeTask.groovy index b00b7f4b..51e9ceae 100644 --- a/buildSrc/src/main/groovy/BuildNativeTask.groovy +++ b/buildSrc/src/main/groovy/BuildNativeTask.groovy @@ -13,6 +13,7 @@ import org.gradle.api.tasks.Internal import org.gradle.api.tasks.OutputFiles import org.gradle.api.tasks.TaskAction import org.treesitter.build.Utils +import java.util.Locale class BuildNativeTask extends DefaultTask{ @@ -160,18 +161,19 @@ class BuildNativeTask extends DefaultTask{ def jniMdIncludeDir = getJniMdInclude(target) def jniOutFile = Utils.jniOutFile(project, target, libName) - def cmd = [ - zigExe, "c++", + def cmd = [] + cmd.addAll(this.compilerArgsForTarget(target)) + cmd.addAll([ "-g0", "-fno-sanitize=undefined", + "-fPIC", "-shared", - "-target", target, "-I", srcDir, "-I", srcDir.dir("lib/include"), "-I", jniIncludeDir, "-I", jniMdIncludeDir, "-o", jniOutFile - ] + ]) additionalIncludeDirs.forEach { f -> cmd.add("-I") cmd.add(f) @@ -187,6 +189,44 @@ class BuildNativeTask extends DefaultTask{ this.removeWindowsDebugFiles() } + List compilerArgsForTarget(String target) { + if (!target.contains("android")) { + return [zigExe, "c++", "-target", target] + } + + def ndkDir = findAndroidNdkDir() + def hostTag = System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("mac") + ? "darwin-x86_64" + : "linux-x86_64" + def apiLevel = project.findProperty("androidApiLevel") ?: "21" + def compiler = new File( + ndkDir, + "toolchains/llvm/prebuilt/${hostTag}/bin/aarch64-linux-android${apiLevel}-clang" + ) + if (!compiler.exists()) { + throw new GradleException("Android compiler not found: ${compiler}. Set ANDROID_NDK_HOME or ANDROID_NDK_ROOT.") + } + return [compiler] + } + + File findAndroidNdkDir() { + def envNdk = System.getenv("ANDROID_NDK_HOME") ?: System.getenv("ANDROID_NDK_ROOT") + if (envNdk) { + return new File(envNdk) + } + + def sdkDir = System.getenv("ANDROID_HOME") ?: System.getenv("ANDROID_SDK_ROOT") + if (sdkDir) { + def ndkRoot = new File(sdkDir, "ndk") + def ndks = ndkRoot.listFiles()?.findAll { it.isDirectory() }?.sort { a, b -> b.name <=> a.name } + if (ndks) { + return ndks.first() + } + } + + throw new GradleException("Android NDK not found. Set ANDROID_NDK_HOME, ANDROID_NDK_ROOT, ANDROID_HOME, or ANDROID_SDK_ROOT.") + } + private void removeWindowsDebugFiles(){ def files = jniOutDir.asFileTree.matching { include("**/*.pdb") diff --git a/tree-sitter/src/main/resources/lib/aarch64-linux-android-tree-sitter.so b/tree-sitter/src/main/resources/lib/aarch64-linux-android-tree-sitter.so new file mode 100755 index 00000000..addc1f74 Binary files /dev/null and b/tree-sitter/src/main/resources/lib/aarch64-linux-android-tree-sitter.so differ