“Undefined reference to” errors during native libr

2019-02-20 01:03发布

问题:

I tried to create a simple JNI application with new build system based on "experimental" com.android.model.application (com.android.tools.build:gradle-experimental:0.9.2).

./gradle/wrapper/gradle-wrapper.properties:



    distributionBase=GRADLE_USER_HOME
    distributionPath=wrapper/dists
    zipStoreBase=GRADLE_USER_HOME
    zipStorePath=wrapper/dists
    distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip

./build.gradle:



    buildscript {
        repositories {
            jcenter()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle-experimental:0.9.2'
        }
    }
    allprojects {
        repositories {
            jcenter()
        }
    }
    task clean(type: Delete) {
        delete rootProject.buildDir
    }

./app/build.gradle:



    apply plugin: 'com.android.model.application'

    Properties properties = new Properties()
    properties.load(project.rootProject.file('local.properties').newDataInputStream())
    def project_src_main = project.getRootDir().absolutePath + '/app/src/main'

    model {
        android {
            compileSdkVersion 25
            buildToolsVersion "25.0.3"
            ndk {
                moduleName = 'android-cpp-sandbox'
                toolchainVersion "4.9"
                platformVersion 16
                stl 'gnustl_static'
                abiFilters.addAll(['armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'])
            }
            defaultConfig {
                applicationId 'com.lamersoft.androidcppsandbox'
                minSdkVersion.apiLevel 23
                targetSdkVersion.apiLevel 25
            }
            sources {
                main {
                    manifest {
                        source.srcDir project_src_main
                    }
                    java {
                        source.srcDir project_src_main + '/java'
                    }
                    res {
                        source.srcDir project_src_main + '/res'
                    }
                    jni {
                        source.srcDir project_src_main + '/jni'
                    }
                }
            }
            productFlavors {
                create("arm7") {
                    ndk.abiFilters.add('armeabi-v7a')
                }
                create("arm8") {
                    ndk.abiFilters.add('arm64-v8a')
                }
                create("x86") {
                    ndk.abiFilters.add('x86')
                }
                create("x86-64") {
                    ndk.abiFilters.add('x86_64')
                }
                create("all") {
                }
            }
            buildTypes {
                release {
                    minifyEnabled false
                    proguardFiles.add(file('proguard-android.txt'))
                }
            }
        }
    }
    dependencies {
        compile fileTree(include: ['*.jar'], dir: 'libs')
        androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
            exclude group: 'com.android.support', module: 'support-annotations'
        })
        compile 'com.android.support:appcompat-v7:25.3.1'
        compile 'com.android.support.constraint:constraint-layout:1.0.2'
        compile 'com.android.support:design:25.3.1'
    }

./app/src/main/res/layout/activity_main.xml:



    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.lamersoft.androidcppsandbox.MainActivity">
        <android.support.design.widget.AppBarLayout
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:theme="@style/AppTheme.AppBarOverlay">
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/AppTheme.PopupOverlay" />
        </android.support.design.widget.AppBarLayout>
        <include layout="@layout/content_main"/>
        <android.support.design.widget.FloatingActionButton
            android:id="@+id/fab"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom|end"
            android:layout_margin="@dimen/fab_margin"
            app:srcCompat="@android:drawable/ic_dialog_email" />
    </android.support.design.widget.CoordinatorLayout>

./app/src/main/res/layout/content_main.xml:



    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:showIn="@layout/activity_main"
        tools:context="com.lamersoft.androidcppsandbox.MainActivity">
        <TextView
            android:id="@+id/sample_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </android.support.constraint.ConstraintLayout>

./app/src/main/java/com/lamersoft/androidcppsandbox/MainActivity.java:



    package com.lamersoft.androidcppsandbox;

    import android.os.Bundle;
    import android.support.design.widget.FloatingActionButton;
    import android.support.design.widget.Snackbar;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.Toolbar;
    import android.view.View;

    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);

           FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
            fab.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Snackbar.make(view, stringFromJNI(), Snackbar.LENGTH_LONG).setAction("Action", null).show();
                }
            });

        }

        private native String stringFromJNI();

        static {
            System.loadLibrary("android-cpp-sandbox");
        }
    }

./app/src/main/jni/main.cpp:



    #include <jni.h>
    #include <string>
    int counter = 1;
    extern "C"
    JNIEXPORT jstring JNICALL
    Java_com_lamersoft_androidcppsandbox_MainActivity_stringFromJNI(
            JNIEnv* env,
            jobject /* this */) {
        char s[100];
        snprintf(s, sizeof(s), "Hello  from C++ #%d", counter++);
        return env->NewStringUTF(s);
    }

The outcome

Modifications

To reproduce the issue I'll create a C++ class in a separate folder and include it to the project by adding the path with a cppFlag "-I".

./app/src/main/externalCPP/SampleClass.h:



    #ifndef ANDROIDCPPSANDBOX_SAMPLECLASS_H
    #define ANDROIDCPPSANDBOX_SAMPLECLASS_H

    class SampleClass {
    public:
        SampleClass(int start);
        int count();
    private:
        int counter = 0;
    };

    #endif //ANDROIDCPPSANDBOX_SAMPLECLASS_H

./app/src/main/externalCPP/SampleClass.cpp:



    #include "SampleClass.h"
    SampleClass::SampleClass(int start) {
        counter = start;
    }

    int SampleClass::count() {
        return counter++;
    }

./app/src/main/jni/main.cpp:



    #include <jni.h>
    #include <string>
    #include <SampleClass.h>

    SampleClass *counter = new SampleClass(1);

    extern "C"
    JNIEXPORT jstring JNICALL
    Java_com_lamersoft_androidcppsandbox_MainActivity_stringFromJNI(
            JNIEnv* env,
            jobject /* this */) {
        char s[100];
        snprintf(s, sizeof(s), "Hello  from C++ #%d", counter->count());
        return env->NewStringUTF(s);
    }

./app/build.gradle:

only one line added:



    ...
    model {
        android {
            ...
            ndk {
                ...
                cppFlags.add('-I' + project_src_main + '/externalCPP')
                ...

The outcome

As you can see, the project does not compile. It fails with error: undefined reference to 'SampleClass::count()' and error: undefined reference to 'SampleClass::SampleClass(int)'.

The same happens when I try to use third-party prebuilt static library in the project by defining it this way:



    ...
    model {
        repositories {
            libs(PrebuiltLibraries) {
                ThirdPartyLibrary {
                    headers.srcDir project_src_main + '/externalCPP'
                    binaries.withType(StaticLibraryBinary) {
                        staticLibraryFile = file(project_src_main + '/externalCPP/lib/libSomeThirdPartyLibrary' + targetPlatform.getName() + '.a')
                    }
                }
            }
        }
        android {
            ...
            sources {
                main {
                    ...
                    jni {
                        source.srcDir project_src_main + '/jni'
                        dependencies {
                            library 'ThirdPartyLibrary' linkage 'static'
                        }
                    }
                }
            }
            ...

Does anybody how to solve this with the experimental gradle plugin?