Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically bundle libc++_shared in apk? #106

Open
Rodrigodd opened this issue Jun 19, 2022 · 4 comments
Open

Automatically bundle libc++_shared in apk? #106

Rodrigodd opened this issue Jun 19, 2022 · 4 comments

Comments

@Rodrigodd
Copy link

I am currently migrating a project from using cargo-apk to a Gradle project, using rust-android-gradle.

The problem is that my project need to link to libc++_shared, but I am yet to find a compelling way to automatically bundle this library, that come with the NDK. I know that cargo-apk automatically bundles the library when linking to c++_shared, and, from searching, apparently gradle automatically also do this when using cmake or ndk-build.

Currently, I am simply copying the shared library to jniLibs, and searching how I could specify the original library in NDK to Gradle, but maybe rust-android-gradle should handle that?

@Rodrigodd
Copy link
Author

I found out how to write a task to automatically copy the library to rustJniLibs. I take reference from rust-android-gradle itself, and from the NDK Build System Maintainers Guide.

But I have yet to write a task for each architecture.

tasks.whenTaskAdded { task ->
    if (task.name == 'mergeDebugJniLibFolders' || task.name == 'mergeReleaseJniLibFolders') {
        task.dependsOn 'cargoBuild'
    }
    if (task.name == 'cargoBuildArm64') {
        task.dependsOn 'copy_libc++_sharedArm64'
    }
}

// TODO: make a task for each architecture
tasks.register('copy_libc++_sharedArm64', Copy) {
    def ndkDir = android.ndkDirectory
    // hostTag and archTriple from: https://developer.android.com/ndk/guides/other_build_systems
    // TODO: detect the correct host tag
    def hostTag = 'windows-x86_64'
    def archTriple = 'aarch64-linux-android'
    from "$ndkDir/toolchains/llvm/prebuilt/$hostTag/sysroot/usr/lib/$archTriple/libc++_shared.so"
    into layout.buildDirectory.dir("rustJniLibs/android/arm64-v8a")
}

@Rodrigodd
Copy link
Author

The necessity for fixing the TODOs has finally come:

def localProperties = new Properties()
localProperties.load(new FileInputStream(rootProject.file("local.properties")))

tasks.whenTaskAdded { task ->
    if (task.name == 'mergeDebugJniLibFolders' || task.name == 'mergeReleaseJniLibFolders') {
        task.dependsOn 'cargoBuild'
    }
    for (target in cargo.targets) {
        if (task.name == "cargoBuild${target.capitalize()}") {
            task.dependsOn "copy_libc++_shared${target.capitalize()}"
        }
    }
}

for (target in cargo.targets) {
    tasks.register("copy_libc++_shared${target.capitalize()}", Copy) {
        def ndkDir = android.ndkDirectory
        // hostTag and archTriple from: https://developer.android.com/ndk/guides/other_build_systems
        def hostTag = localProperties['hostTag']
        def archTriple = [
            arm: 'armv7a-linux-androideabi',
            arm64: 'aarch64-linux-android',
            x86: 'i686-linux-android',
            x86_64: 'x86_64-linux-android',
        ][target]
        from "$ndkDir/toolchains/llvm/prebuilt/$hostTag/sysroot/usr/lib/$archTriple/libc++_shared.so"
        into layout.buildDirectory.dir("rustJniLibs/android/arm64-v8a")
    }
}

As long as the code that generate the task name don't change, this probably will continue to work. I didn't find a way to get the current host tag, so I am manually writing it to local.properties.

@ncalexan
Copy link
Member

Hi @Rodrigodd -- sorry for not responding to this when you filed it moons ago. I'd be happy to have this happen automatically, perhaps behind a flag, in the main plugin, since it's probably a common issue. Inside the plugin, we do some similar things with the prebuilt toolchains and must find the host tag somewhere.

Also, there's a small issue where you're copying into the arm64-v8a libs with regard to the target.

@Rodrigodd
Copy link
Author

Rodrigodd commented Sep 14, 2022

Hi @ncalexan , thanks for responding!

there's a small issue where you're copying into the arm64-v8a libs with regard to the target

Oh, true. I also noticed that the abi name for the arm target is wrong, because it is different for bintools, according to the android documentation. I also noticed that the task fails silently if the file does not exists.

we do some similar things with the prebuilt toolchains and must find the host tag somewhere

Ah yes, it happens here. I will copy it, if you don't mind.

Taking everything above in consideration, now I have the following code:

import org.apache.tools.ant.taskdefs.condition.Os

tasks.whenTaskAdded { task ->
    if (task.name == 'mergeDebugJniLibFolders' || task.name == 'mergeReleaseJniLibFolders') {
        task.dependsOn 'cargoBuild'
    }
    for (target in cargo.targets) {
        if (task.name == "cargoBuild${target.capitalize()}") {
            task.dependsOn "copy_libc++_shared${target.capitalize()}"
        }
    }
}

for (target in cargo.targets) {
      tasks.register("copy_libc++_shared${target.capitalize()}", Copy) {
        def ndkDir = android.ndkDirectory
        // hostTag, abi and archTriple from: https://developer.android.com/ndk/guides/other_build_systems

        def hostTag
        if (Os.isFamily(Os.FAMILY_WINDOWS)) {
            if (Os.isArch("x86_64") || Os.isArch("amd64")) {
                hostTag = "windows-x86_64"
            } else {
                hostTag = "windows"
            }
        } else if (Os.isFamily(Os.FAMILY_MAC)) {
            hostTag = "darwin-x86_64"
        } else {
            hostTag = "linux-x86_64"
        }

        def (abi, archTriple) = [
            arm: ['armeabi-v7a', 'arm-linux-androideabi'],
            arm64: ['arm64-v8a', 'aarch64-linux-android'],
            x86: ['x86', 'i686-linux-android'],
            x86_64: ['x86_64', 'x86_64-linux-android'],
        ][target]

        def from_path = "$ndkDir/toolchains/llvm/prebuilt/$hostTag/sysroot/usr/lib/$archTriple/libc++_shared.so"
        def into_path = layout.buildDirectory.dir("rustJniLibs/android/$abi")

        assert file(from_path).exists()

        from from_path
        into into_path
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants