【问题标题】:Can't create shared library with static inside无法创建内部带有静态的共享库
【发布时间】:2011-11-12 01:24:31
【问题描述】:

请帮助我搜索整个互联网,但我找不到答案......

C层

我创建了简单的函数int mean(int, int); 并将其放在calc_mean.h 中并在calc_mean.c 中对其进行初始化,这是这两个文件。

calc_mean.c

#include "calc_mean.h"

int mean(int a, int b)  
{  
return (a+b) / 2;  
}  

calc_mean.h

int mean(int, int);

然后我使用以下 make 文件创建 make 文件并构建名为 Test_Archive.a 的归档 (.a) 文件

制作文件

GCC := C:\Tools\ndk-toolchain\ndk-standalone\bin\arm-linux-androideabi-gcc.exe
GPP := C:\Tools\ndk-toolchain\ndk-standalone\bin\arm-linux-androideabi-g++.exe
AR  := C:\Tools\ndk-toolchain\ndk-standalone\bin\arm-linux-androideabi-ar.exe

default: all

all: obj
    $(AR) r Test_Archive.a *.o

obj:
    $(GPP) -c *.c

我得到 Test_Archive.a 存档文件。现在我想将此存档文件添加到 JNI 并从我的 JNI 项目中调用 mean 函数,因为我已经创建了 JNI 和 Java 文件,您可以在下面看到。


JAVA 层

如何在 java 层中看到我有 TestJavaClass,它有一个名为 JMean 的本机方法,此方法有两个 int 参数并返回 int

public class TestJavaClass
{
    /** Default Constructor
     * 
     */
    public TestJavaClass( ) {

    }

    /** Test Function. 
     *  
     * @param a
     * @param b
     * @return
     */
    public native int JMean( int a, int b);
}

JNI 层

这是JNI头文件

Test_Library.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_Fido_OSTXLib_OSTX */

#ifndef _Included_com_Fido_OSTXLib_OSTX
#define _Included_com_Fido_OSTXLib_OSTX
#ifdef __cplusplus
extern "C" {
#endif

/*
 * Class:     com_Fido_OSTXLib_OSTX
 * Method:    JMean
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_Fido_OSTXLib_OSTX_JMean
  (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

Test_Library.c

#include "Test_Library.h"
#include "calc_mean.h"

JNIEXPORT jint JNICALL Java_com_Fido_OSTXLib_OSTX_JMean( JNIEnv* env, jobject thiz, jint a, jint b )
{
    return mean( a, b );
}

我把这两个文件Test_Library.hTest_Library.ccalc_mean.hTest_Archive.a > 并创建 make 文件 Android.mk

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE           := Test_Library
LOCAL_SRC_FILES        := Test_Library.c

LOCAL_LDLIBS := -LTest_Archive.a

include $(BUILD_SHARED_LIBRARY)

但是当我想创建.so文件时,出现如下错误,为什么?

$ ../../ndk-build 编译拇指:Test_Library Java_com_Fido_OSTXLib_OSTX_JMean': C:/cygwin/home/android-ndk-r5b/Fido/ProjectOSTX/jni/OSTX_Library.c:6: undefined reference tomean' collect2: ld 返回 1 退出状态 make: * [/home/android-ndk-r5b/Fido/ProjectOSTX/obj/local/armeabi/libOSTX_Library.so] 错误 1

为什么未定义对 `mean' 的引用?

【问题讨论】:

  • 你也不必在 Test_Library.c 中#include "Test_Library.h" 吗?
  • 不,你能告诉我NDK gcc编译器在哪里吗?
  • 我认为,您必须在本机代码中包含使用 javah 创建的头文件。
  • 我已将它包含到 Test_Library.c
  • 这是同样的问题,没有答案check this

标签: android static shared android-ndk


【解决方案1】:

这是我为成功构建代码所做的:

  1. 在您的 Eclipse Android 项目中创建文件夹 jni

  2. 将文件calc_mean.c; calc_mean.h; Test_Library.c and Android.mk添加到jni文件夹中

  3. 从 Test_Library.c 文件中删除行 #include "Test_Library.h"。其他一切都可以保持原样。

  4. 在 cygwin 命令行中转到 Eclipse Android 项目的根文件夹并运行 ndk-build

如果您想在 ndk-build 过程中也将您的 calc_mean 构建为静态库,那么您可以按如下方式更改您的 Android.mk 文件:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE           := Test_Archive
LOCAL_SRC_FILES        := calc_mean.c

# compiler flags.
LOCAL_LDLIBS   = -lz -lm
LOCAL_CFLAGS   = -Wall -pedantic -std=c99 -g

include $(BUILD_STATIC_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE := Test_Library
LOCAL_STATIC_LIBRARIES := Test_Archive
LOCAL_SRC_FILES := Test_Library.c


include $(BUILD_SHARED_LIBRARY)

【讨论】:

  • 这不是一种方法,因为实际上我没有 calc_mean.c 和 calc_mean.h 文件我只有 calc_mean.a 生成的文件......所以主要问题是如何添加静态( calc_mean.a) 库到共享库?您所说的方法仅在您拥有 calc_mean.c 时才有效; calc_mean.h 来源,但如果我只有 calc_mean.a 我必须如何将它添加到共享库中,你能帮帮我吗?
  • 您可以在 Android.mk 文件中声明静态库。 LOCAL_STATIC_LIBRARIES := YOUR_STATIC_LIBRARIY
  • 什么参数?我添加了LOCAL_STATIC_LIBRARIES := Test_Archive.a,但没有任何变化......
  • 我认为这主要是编译器,我认为我在编译Test_Archive.a时必须提供一些额外的选项,但是我无法想象哪些选项......
  • 只保留声明中的 .a:LOCAL_STATIC_LIBRARIES := Test_Archive 并同时删除 LOCAL_LDLIBS := -LTest_Archive.a
【解决方案2】:

我认为主要原因是编译器设置,我在 makefile 和链接到共享库的 .a 存档中进行了一些修改,这里是修改。

GCC := C:\Tools\ndk-toolchain\ndk-standalone\bin\arm-linux-androideabi-gcc.exe
GPP := C:\Tools\ndk-toolchain\ndk-standalone\bin\arm-linux-androideabi-g++.exe
AR  := C:\Tools\ndk-toolchain\ndk-standalone\bin\arm-linux-androideabi-ar.exe

OPTIONS  :=\
-fpic \
-ffunction-sections \
-funwind-tables  \
-fstack-protector \
-D__ARM_ARCH_5__ \
-D__ARM_ARCH_5T__ \
-D__ARM_ARCH_5E__ \
-D__ARM_ARCH_5TE__ \
-Wno-psabi \
-march=armv5te \
-mtune=xscale \
-msoft-float \
-mthumb \
-Os \
-fomit-frame-pointer \
-fno-strict-aliasing \
-finline-limit=64 \
-DANDROID \
-Wa, \
-O2 \
-DNDEBUG \
-g \

default: all


all: obj
    $(AR) r libtestlibrary.a *.o

obj:
    $(GCC) $(OPTIONS) -c *.c

【讨论】:

    【解决方案3】:

    只需使用 C 编译器!

    您使用 C++ 编译器进行编译(Makefile 有“$(GPP) -c *.c”),并且函数符号名称包含参数信息并且被称为不同:不是“意思”,而是更复杂的东西,类似于 Java 名称内部符号。您可以使用“nm”查看目标文件中的内容。

    例如 my g++ 生成:

    # nm x.o
    00000000 T _Z4meanii
    

    所以没有“平均”(但“Z4meanii”)。使用 C 编译器,符号名称简单明了:

    # nm x.o
    00000000 T mean
    

    (请注意编译的源代码内容相同)

    所以改变你的 Makefile:

    obj:
        $(GCC) -c *.c
    

    如果出于某种原因必须使用 C++ 编译器,则必须添加 extern "C":

    calc_mean.h(简化,没有包含保护等)

    #ifdef __cplusplus
    extern "C" {
    #endif
    
    int mean(int, int);
    
    #ifdef __cplusplus
    } // extern "C"
    #endif
    

    一般来说,如果您需要 C++ 文件中的 C 符号名称,请使用 extern "C"。 Test_Library.h 正确使用它并提供了一个很好的例子。您可以将相同的外部“C”逻辑(最好包括 if #ifdef __cplusplus 块)放入头文件。有些人认为始终这样做是一种很好的做法。

    但请注意,C 和 C++ 之间存在一些(有时是细微的)差异,因此我建议使用 C 编译器编译 C 源代码,尽管我知道使用 C++ 编译器可能会有优势。

    好,

    史蒂芬

    【讨论】:

    • 这与问题无关,但 $(GPP) 听起来像 C PreProcessor。我认为最好使用“CC=gcc”和“CXX=g++”
    猜你喜欢
    • 2021-03-18
    • 1970-01-01
    • 2021-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-09
    • 2011-03-24
    相关资源
    最近更新 更多