Android build with ABI specific pre-built SO files

1,073 views
Skip to first unread message

Menny Even Danan

unread,
Feb 19, 2016, 12:26:02 PM2/19/16
to bazel-discuss, Alex Humesky
Hi,
I have a pre-built SO file I wish to add to my Android app (this is an SO file from a 3rd party, I do not have the source).
Since my app's native library is very large, I split it by ABIs (x86,armv7a) :

I have two cc_library rules that hold the right SO file for each ABI:
cc_library(name="precompiled_armv7_jni_so", abi="armeabi-v7a", srcs=glob(["src/main/jniLibs/armeabi-v7a/*.so"]), visibility=["//visibility:private"])
cc_library(name="precompiled_x86_jni_so", abi="x86", srcs=glob(["src/main/jniLibs/x86/*.so"]), visibility=["//visibility:private"])

And then I have two android_binary rules (one for each ABI) that depends on the specific ABI, e.g:
android_binary(
        name
="app_arm",
....
        deps
=[..., "precompiled_armv7_jni_so"],
        legacy_native_support
=1,
        visibility
=["//visibility:public"]

android_binary
(
        name
="app_z86",
....
        deps
=[..., "precompiled_x86_jni_so"],
        legacy_native_support
=1,
        visibility
=["//visibility:public"]


When I build the arm rule, the APK is valid, installs, and runs.
When I build the x86 rule, the APK has the right SO files in it (e.g., x86), but they are placed under the `lib/armeabi-v7a` folder. And when trying to install the APK into a x86 device (or Emulator) it fails with:
Error:
adb command
: ['external/androidsdk/platform-tools/adb', 'install', '-r', './bazel-out/local_darwin-fastbuild/bin/app/debug_x86_incremental.apk']
return code: 0
stdout
: pkg: /data/local/tmp/debug_x86_incremental.apk
Failure [INSTALL_FAILED_NO_MATCHING_ABIS]


Any suggestions on how to force correct folder structure in the APK?

Thanks.

Alex Humesky

unread,
Feb 19, 2016, 1:10:18 PM2/19/16
to Menny Even Danan, bazel-discuss
Try using the --android_cpu flag set to "x86". The --android_cpu flag tells bazel where to put the pre-built so files in the apk lib folder.

Menny Even Danan

unread,
Feb 19, 2016, 1:45:31 PM2/19/16
to bazel-discuss, men...@gmail.com
Did not help.
Still getting the same error when installing, and the APK folder structure still includes armabi-a7v folder.

Alex Humesky

unread,
Feb 19, 2016, 9:50:41 PM2/19/16
to Menny Even Danan, bazel-discuss
Hmm, it appears that we've broken --android_cpu, I'll have to do some investigating to figure out how to fix it.
For now, it looks like you can use --fat_apk_cpu=x86 (although that's not really the flag to use since that's supposed to be used with legacy_native_support = 0)

--
You received this message because you are subscribed to the Google Groups "bazel-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bazel-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/bazel-discuss/b7b88188-2f69-4ce8-ba00-c4b67fb3650d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Menny Even Danan

unread,
Feb 22, 2016, 5:37:39 PM2/22/16
to bazel-discuss, men...@gmail.com
Alex, --fat_apk_cpu flag worked: I see the SO file in the right folder.

a question: It feels a bit redundant (and error prone) to provide both the rule and the flag when building the app for x86, for example:
bazel build --fat_apk_cpu=x86 app:debug_x86

Is there a way that the `app:debug_x86` rule will also set the cpu target?

Alex Humesky

unread,
Feb 22, 2016, 6:37:03 PM2/22/16
to Menny Even Danan, bazel-discuss
Unfortunately it's not possible today to put such information into a build rule, because we'll need dynamic configurations, which are a long ways out:
https://docs.google.com/document/d/1uoU8t7loTOu6uyzez-ilhcYgEXg4xjViJu0aqrf69TY/edit#heading=h.xsc8wmorka3u

Note that if you use legacy_native_support = 0 (the default) and depend on cc_library rules, you can specify multiple architectures in --fat_apk_cpu (e.g. "--fat_apk_cpu=x86,armeabi-v7a") and bazel will compile the native code multiple times for each architecture and place them in the right spot in the apk.

For your situation though, you can base the decision on which .so to include in the rule on the --fat_apk_cpu flag, using configurable attributes, like this


config_setting(
    name = "x86",
    values = {
      "fat_apk_cpu": "x86"
    },
)

config_setting(
    name = "armeabi-v7a",
    values = {
      "fat_apk_cpu": "armeabi-v7a"
    },
)

android_binary(
    name = "hello_world",
    srcs = glob([
        "MainActivity.java",
        "Jni.java",
    ]),
    legacy_native_support = 1,
    manifest = "AndroidManifest.xml",
    deps = select({
        "x86": ["precompiled_x86_so"],
        "armeabi-v7a": ["precompiled_armeabi-v7a_so"],
    }),
)

cc_library(
    name = "precompiled_x86_so",
    srcs = ["jni-x86.so"]
)

cc_library(
    name = "precompiled_armeabi-v7a_so",
    srcs = ["jni-armeabi-v7a.so"]
)

Note also that this should work equally well, but for some reason that I haven't figured out yet it fails for me:



android_binary(
    name = "hello_world",
    srcs = glob([
        "MainActivity.java",
        "Jni.java",
    ]),
    legacy_native_support = 1,
    manifest = "AndroidManifest.xml",
    deps = [
        ":precompiled_so",
    ],
)

config_setting(
    name = "x86",
    values = {
      "fat_apk_cpu": "x86"
    },
)

config_setting(
    name = "armeabi-v7a",
    values = {
      "fat_apk_cpu": "armeabi-v7a"
    },
)

cc_library(
    name = "precompiled_so",
    srcs = select({
      ":x86": ["jni-x86.so"],
      ":armeabi-v7a": ["jni-armeabi-v7a.so"],
    }),
)


Alex Humesky

unread,
Feb 23, 2016, 3:53:17 PM2/23/16
to Menny Even Danan, bazel-discuss, Greg Estren
Ok after talking with Greg, I know now why the 2nd arrangement didn't work. It has to do with how --fat_apk_cpu is implemented for android_binary when legacy_native_support = 0 (i.e. how it's "supposed" to be used). 

With legacy_native_support = 0, you can set --fat_apk_cpu to multiple architectures, like --fat_apk_cpu=x86,armeabi-v7a. This causes the configuration to "split" into multiple separate configurations for each target architecture. This means that when you have an android_binary depending on a cc_library, the android_binary is actually getting 2 different cc_library rules: one with a configuration that says --cpu (which is used for native compilation) is x86, and another that says --cpu is armeabi-v7a. Tihs means that the cc_library rule that is using the select() is actually seeing the --cpu flag change, not the --fat_apk_cpu flag change. The reason that the android_binary sees the fat_apk_cpu change is that it's not part of the split configurations (it sees the "top level" configuration). So the way to make the 2nd arrangement work is:

android_binary(
    name = "hello_world",
    srcs = glob([
        "MainActivity.java",
        "Jni.java",
    ]),
    legacy_native_support = 1,
    manifest = "AndroidManifest.xml",
    deps = [
        ":precompiled_so",
    ],
)

config_setting(
    name = "x86",
    values = {
      "cpu": "x86"
    },
)

config_setting(
    name = "armeabi-v7a",
    values = {
      "cpu": "armeabi-v7a"
    },
)

cc_library(
    name = "precompiled_so",
    srcs = select({
      ":x86": ["jni-x86.so"],
      ":armeabi-v7a": ["jni-armeabi-v7a.so"],
    }),
)

Ketki Garg

unread,
Sep 14, 2021, 4:56:54 PM9/14/21
to bazel-discuss
I tried enabling the below configuration in the `android_binary` rule to spit native libs on the basis of architecture but no luck :|

```
android_binary(
    fat_apk_cpu = ["x86", "armeabi-v7a"],
    legacy_native_support = 0,
)
```
Any leads would be really helpful!

Alex Humesky

unread,
Sep 14, 2021, 5:31:35 PM9/14/21
to Ketki Garg, bazel-discuss
Hi,

The legacy_native_support attribute has been removed, and fat_apk_cpu is a command line argument to bazel, rather than an attribute of android_binary, so you'd use "--fat_apk_cpu=x86,armeabi-v7a", for example, in your invocation to bazel. The example below should work for including pre-compiled .so files into an android app. Also, if you're only compiling c/c++ sources, then all you should need is a cc_library, and add the cc_library as a dependency of the android_binary (or some android_library that the android_binary depends on).

android_binary(
    name = "hello_world",
    srcs = glob([
        "MainActivity.java",
        "Jni.java",
    ]),
    manifest = "AndroidManifest.xml",
    deps = [
        ":precompiled_so",
    ],
)

config_setting(
    name = "x86",
    values = {
      "cpu": "x86"
    },
)

config_setting(
    name = "armeabi-v7a",
    values = {
      "cpu": "armeabi-v7a"
    },
)

cc_library(
    name = "precompiled_so",
    srcs = select({
      ":x86": ["jni-x86.so"],
      ":armeabi-v7a": ["jni-armeabi-v7a.so"],
    }),
)
Reply all
Reply to author
Forward
0 new messages