Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile standalone binaries with NDK 13

With NDK 10 releases, I used to use ndk-build to compile standalone binaries for many different ABIs and multiple API levels. These binaries were to be included in an app. However, I installed the NDK on a new development machine as described in this article. This resulted in a folder ndk-bundle within my Android SDK directory. I used to compile the code from a command line and then copy the binaries to the resources of my Android Studio project, but I could not figure out how to do this with NDK 13 so I tried to follow the tutorial to include my native code in the Android Studio project. However, almost all recent instructions assume that one wants to build a library, not a standalone binary, so I did not get far.

I would switch to CMake if I figured out how to get it to work. My native project has the following (simplified) structure:

  • native

    • Android.mk

      LOCAL_PATH := $(call my-dir)/my_tool/src
      include $(CLEAR_VARS)
      LOCAL_MODULE    := my_tool
      LOCAL_SRC_FILES := main.c
      include $(BUILD_EXECUTABLE)
      
    • Application.mk

      APP_ABI := all
      APP_PLATFORM := android-21
      
    • my_tool

      • src
        • main.c

How can I compile this using either Android Studio or the NDK from the command line on our Windows 10 development machines?

Edit:

I am using this in build.gradle:

externalNativeBuild {
    ndkBuild {
        path "../native/Android.mk"
    }
}

Gradle creates a directory .externalNativeBuild which contains build configurations, but I cannot find out how to actually build the native code. No binaries are created when I run gradle.

I cannot find any information about gradle configuration for ndk-build.

like image 900
just.me Avatar asked Dec 10 '16 11:12

just.me


1 Answers

I tried to follow your simplified structure as closely as possible.

Here is the file app/build.gradle:

apply plugin: 'com.android.library'

android {
    compileSdkVersion 24
    buildToolsVersion "25.0.1"
    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 24
        externalNativeBuild {
            ndkBuild {
                targets "my_tool"
                abiFilters "armeabi-v7a"
            }
        }
    }
    externalNativeBuild {
        ndkBuild {
            path "../native/Android.mk"
        }
    }
}

The file native/Android.mk is identical to yours:

LOCAL_PATH := $(call my-dir)/my_tool/src
include $(CLEAR_VARS)
LOCAL_MODULE    := my_tool
LOCAL_SRC_FILES := main.c
include $(BUILD_EXECUTABLE)

I also have the files native/main.c and a minimal app/src/main/AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="my.tool" />

I did not touch the root build.gradle script generated by Android Studio wizard:

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.0-alpha3'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

Now I can build the project and here is what I get:

$> file ./app/build/intermediates/ndkBuild/debug/obj/local/armeabi-v7a/my_tool
ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

Android Studio shows my main.c in the cpp folder in default view:

enter image description here

Update: to have the executable stripped and packaged in the APK, native/Android.mk must be changed:

LOCAL_PATH := $(call my-dir)/my_tool/src

install: LIB_PATH := $(call my-dir)/libs

include $(CLEAR_VARS)
LOCAL_MODULE    := my_tool
LOCAL_SRC_FILES := main.c
include $(BUILD_EXECUTABLE)

install: $(LOCAL_INSTALLED)
    -mkdir $(LIB_PATH)
    -rm -r $(LIB_PATH)
    mv $< $(<:my_tool=lib-my_tool-.so)
    mv $(realpath $(dir $<)..) $(LIB_PATH)

.PHONY: install

Also, app/build.gradle needs some tweaking:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "25.0.1"
    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 24
        externalNativeBuild {
            ndkBuild {
                targets "my_tool"
                abiFilters "armeabi-v7a"
                arguments 'V=1', 'install'
            }
        }
    }
    externalNativeBuild {
        ndkBuild {
            path "../native/Android.mk"
        }
    }
    sourceSets {
        main {
            jniLibs.srcDirs = ['../native/libs']
        }
    }
}

This relies on the old hack which depends on undocumented behavior of NDK, and may break without notice on a future NDK upgrade.

like image 155
Alex Cohn Avatar answered Sep 18 '22 12:09

Alex Cohn