如何进行Makefile的编写?
如何编译动态库?
如何引用动态库?

基础知识

Makefile gcc的参数

-Wall: 是打开警告开关,

-O: 代表默认优化,可选:-O0不优化,-O1低级优化,-O2中级优化,-O3高级优化,-Os代码空间优化。

-g: 是生成调试信息,生成的可执行文件具有和源代码关联的可调试的信息。

-I(大写的i):代表后面跟的目录为第一个寻找头文件的目录

-L: 代表后面跟的目录为第一个寻找库文件的目录

-l(小写的L):表示libxxx.so库文件

-static: 静态编译的

-c(小写): 只编译,不链接

-C(大写): 切换到指定目录再执行 make 过程,makefile 在这个指定目录里面,然后返回到当前目录继续读入、执行当前的Makefile。

gcc -o hello hello.c -I /home/hello/include -L /home/hello/lib -lworld

上面这句表示在编译hello.c时:

-I /home/hello/include表示将/home/hello/include目录作为第一个寻找头文件的目录,寻找的顺序是:/home/hello/include-->/usr/include-->/usr/local/include

-L /home/hello/lib表示将/home/hello/lib目录作为第一个寻找库文件的目录,寻找的顺序是:/home/hello/lib-->/lib-->/usr/lib-->/usr/local/lib

-lworld表示在上面的lib的路径中寻找libworld.so动态库文件(如果gcc编译选项中加入了“-static”表示寻找libworld.a静态库文件)

android.mk

官方介绍:http://android.mk/

基本概念

可以生成一下几个类别:

JAVA代码的 C/C++代码的
JAR包 二进制可执行文件:ELF (BUILD_EXECUTABLE)
应用程序:APK 动态库:.so   (BUILD_SHARED_LIBRARY)
静态库:.a (BUILD_STATIC_LIBRARY)

常用的变量:

名字 含义 常用的方式
LOCAL_PATH 当前模块的相对路径 LOCAL_PATH := $(call my-dir)
LOCAL_MODULE 编译生成的目标名称 LOCAL_MODULE := test
LOCAL_SRC_FILES 编译该模块需要的源文件 LOCAL_SRC_FILES := test.c
LOCAL_C_INCLUDES 包含C的头文件目录
LOCAL_SHARED_LIBRARIES 导入共享库 LOCAL_SHARED_LIBRARIES += liblog

举例

hejin@desk-ubuntu:/home/desk/myandroid$ ls external/test/
Android.mk test2.c test2.h test.c

分别查看四个源文件的内容:

hejin@desk-ubuntu:/home/desk/myandroid$ cat external/test/test.c
#include "stdio.h"
#include "test2.h"
int main(){
printf("hello world\n");
test2();
return 0;
}
hejin@desk-ubuntu:/home/desk/myandroid$ cat external/test/test2.c
#include"stdio.h"

void test2(){
printf("hello test2\n");
}
hejin@desk-ubuntu:/home/desk/myandroid$ cat external/test/test2.h
void test2();

Android.mk:

hejin@desk-ubuntu:/home/desk/myandroid$ cat external/test/Android.mk
LOCAL_PATH := $(call my-dir) #定义了当前模块的相对路径
include $(CLEAR_VARS) #清空当前环境变量, 除了LOCAL_PATH
LOCAL_MODULE := libtest #编译生成的目标名称
LOCAL_SRC_FILES := test.c \ #编译该模块需要的源文件
test2.c
include $(BUILD_SHARED_LIBRARY) #编译所生成的目标文件格(在这里是动态库文件)

编译

hejin@desk-ubuntu:/home/desk/myandroid$ source build/envsetup.sh
hejin@desk-ubuntu:/home/desk/myandroid$ lunch
hejin@desk-ubuntu:/home/desk/myandroid$ mmm external/test/
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=5.1.1
TARGET_PRODUCT=sabresd_6dq
TARGET_BUILD_VARIANT=user
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
TARGET_CPU_VARIANT=cortex-a9
TARGET_2ND_ARCH=
TARGET_2ND_ARCH_VARIANT=
TARGET_2ND_CPU_VARIANT=
HOST_ARCH=x86_64
HOST_OS=linux
HOST_OS_EXTRA=Linux-3.16.0-30-generic-x86_64-with-Ubuntu-14.04-trusty
HOST_BUILD_TYPE=release
BUILD_ID=2.1.0-ga-rc3
OUT_DIR=out
============================================
No private recovery resources for TARGET_DEVICE sabresd_6dq
make: Entering directory `/home/desk/myandroid'
target thumb C: test <= external/test/test.c
target thumb C: test <= external/test/test2.c
target Executable: test (out/target/product/sabresd_6dq/obj/EXECUTABLES/test_intermediates/LINKED/test)
target Symbolic: test (out/target/product/sabresd_6dq/symbols/system/bin/test)
target Strip: test (out/target/product/sabresd_6dq/obj/EXECUTABLES/test_intermediates/test)
Install: out/target/product/sabresd_6dq/system/bin/test //将可执行文件安装到这个目录下
make: Leaving directory `/home/desk/myandroid'

#### make completed successfully (1 seconds) ####

变量

makefile文件有一个基础的东西就是变量。大家可以自己了解下。这里我们说说android.mk这个变量的不同的地方。主要是用它来做代码管理。

首先在执行make文件的时候这些变量是全局有效的,一些公共的变量会通过include $(CLEAR_VARS)给清除掉。我们这里主要是添加自己的变量。我们在使用自己定义变量的时候有两种情况,一种是在mk中使用,一种是在cpp中使用。注意java不支持的。这两种情况有点不同,请注意。

首先我们得定义这个变量,一般来讲都是在产品的BoardConfig.mk中定义,例如:

TARGET_MEMORY_STYLE := TARGET_MEM_256MB
#TARGET_MEMORY_STYLE := TARGET_MEM_256MB_ST
#TARGET_MEMORY_STYLE := TARGET_MEM_512MB
#TARGET_MEMORY_STYLE := TARGET_MEM_512MB_ST

# board-specific configuration
BOARD_OPENCORE_FLAGS := -DTELECHIPS_ALSA






ifeq ($(BOARD_KERNEL_BASE),0x40000000)
BOARD_MEM_FLAGS := -DTCC_MEM_256MB
else
BOARD_MEM_FLAGS := -DTCC_MEM_512MB
endif

这里总共有两个我们经常用的东西。

第一:
如果是mk中使用,定义如前四行就可以了。

变量 := 字符串

在mk中的使用方法就像后面例子一样。

ifeq($(变量),对比值)

else

endif

第二:如果你想在cpp中使用,请定义方式如下:

变量 := -D+变量名

如后面几行示例一样的。

如果想在cpp中应用,首先必须在cpp对于的mk文件中,声明如下
LOCAL_CFLAGS += $(BOARD_MEM_FLAGS)

格式: LOCAL_CFLAGS += $(变量)

然后它的cpp中就可以引用如下:
此时去掉了-D前缀。

ifdef TCC_MEM_512MB
......
endif

Android.mk变量

这些是您在Android.mk文件中常见的变量,按字母顺序列出。首先,关于变量命名的注释:

  • LOCAL_ - 这些变量是按模块设置的。它们被行清除include $(CLEAR_VARS),因此您可以在包含该文件后依赖它们为空。您将在大多数模块中使用的大多数变量都是LOCAL_变量。

  • PRIVATE_ - 这些变量是特定于make-target的变量。这意味着它们只能在该模块的命令中使用。这也意味着他们不可能从你的后面的模块背后改变。此 make链接提供了 有关特定于目标的变量的更多信息。

  • HOST_TARGET_ - 包含特定于主机或目标版本的目录和定义。不要在makefile中设置以HOST_或TARGET_开头的变量。

  • BUILD_CLEAR_VARS - 包含要包含的明确定义的模板makefile的名称。一些示例是CLEAR_VARS和BUILD_HOST_PACKAGE。

  • 任何其他名称都是公平游戏,供您在Android.mk中使用。但是,请记住,这是一个非递归构建系统,因此您的变量可能会被稍后包含的另一个Android.mk更改,并且在执行规则/模块的命令时会有所不同。

  • 定义C/CPP编译的宏时,注意格式,后面=1,不能带空格!

    LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1

参数 描述
LOCAL_AAPT_FLAGS
LOCAL_ACP_UNAVAILABLE
LOCAL_ADDITIONAL_JAVA_DIR
LOCAL_AIDL_INCLUDES
LOCAL_ALLOW_UNDEFINED_SYMBOLS
LOCAL_ARM_MODE
LOCAL_ASFLAGS
LOCAL_ASSET_DIR
LOCAL_ASSET_FILES 在Android.mk文件中,include $(BUILD_PACKAGE)将此文件设置为您希望在应用程序中内置的文件集。通常:LOCAL_ASSET_FILES += $(call find-subdir-assets)
LOCAL_BUILT_MODULE_STEM
LOCAL_C_INCLUDES 指示C / C ++编译器查找头文件的其他目录。这些路径根植于树的顶部。LOCAL_PATH如果您在包含路径中包含您想要的子目录,请使用 此选项。例如:LOCAL_C_INCLUDES += extlibs/zlib-1.2.3LOCAL_C_INCLUDES += $(LOCAL_PATH)/src您不应该添加include的子目录 LOCAL_C_INCLUDES,而应该在#include语句中引用这些文件及其子目录。例如:#include #include
LOCAL_CC 如果要为此模块使用不同的C编译器,请将LOCAL_CC设置为编译器的路径。如果LOCAL_CC为空,则使用相应的默认编译器。
LOCAL_CERTIFICATE
LOCAL_CFLAGS 如果您有其他标志要传递到C或C ++编译器,请在此处添加它们。例如:LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1
LOCAL_CLASSPATH
LOCAL_COMPRESS_MODULE_SYMBOLS
LOCAL_COPY_HEADERS 要复制到安装的文件集包括树。你还必须提供LOCAL_COPY_HEADERS_TO。这会消失,因为复制标题会混淆错误消息,并可能导致人们编辑这些标题而不是正确标题。它还可以更容易地在系统中执行错误的分层,我们希望避免这种分层。我们也没有使用C / C ++ SDK,因此没有最终要求复制任何标头。
LOCAL_COPY_HEADERS_TO “include”中的目录,用于复制LOCAL_COPY_HEADERSto中列出的标题 。这会消失,因为复制标题会混淆错误消息,并可能导致人们编辑这些标题而不是正确标题。它还可以更容易地在系统中执行错误的分层,我们希望避免这种分层。我们也没有使用C / C ++ SDK,因此没有最终要求复制任何标头。
LOCAL_CPP_EXTENSION 如果您的C ++文件以“ .cpp” 之外的其他内容结尾,则可以在此处指定自定义扩展名。例如:LOCAL_CPP_EXTENSION := .cc请注意,给定模块的所有C ++文件必须具有相同的扩展名; 目前无法混合不同的扩展。
LOCAL_CPPFLAGS 如果还有其他标志传递给C ++编译器,请在此处添加。例如:LOCAL_CPPFLAGS += -ffriend-injection``LOCAL_CPPFLAGS保证LOCAL_CFLAGS 在编译行之后,因此您可以使用它来覆盖中列出的标志 LOCAL_CFLAGS
LOCAL_CXX 如果要为此模块使用不同的C ++编译器,请将LOCAL_CXX设置为编译器的路径。如果LOCAL_CXX为空,则使用相应的默认编译器。
LOCAL_DX_FLAGS
LOCAL_EXPORT_PACKAGE_RESOURCES
LOCAL_FORCE_STATIC_EXECUTABLE 如果您的可执行文件应静态链接,请设置 LOCAL_FORCE_STATIC_EXECUTABLE:=true。我们以静态形式存在一个非常短的库列表(目前只有libc)。这实际上只用于根文件系统上/ sbin中的可执行文件。
LOCAL_GENERATED_SOURCES 添加到的文件LOCAL_GENERATED_SOURCES将自动生成,然后在构建模块时链接。有关示例,请参阅自定义工具模板makefile。
LOCAL_INSTRUMENTATION_FOR
LOCAL_INSTRUMENTATION_FOR_PACKAGE_NAME
LOCAL_INTERMEDIATE_SOURCES
LOCAL_INTERMEDIATE_TARGETS
LOCAL_IS_HOST_MODULE
LOCAL_JAR_MANIFEST
LOCAL_JARJAR_RULES
LOCAL_JAVA_LIBRARIES 链接Java应用程序和库时,LOCAL_JAVA_LIBRARIES 指定要包含的Java类集。目前有两个:coreframework。在大多数情况下,它看起来像这样:LOCAL_JAVA_LIBRARIES := core framework请注意,LOCAL_JAVA_LIBRARIES使用“ include $(BUILD_PACKAGE)” 构建APK时,无需进行设置(并且不允许)。将自动包含适当的库。
LOCAL_JAVA_RESOURCE_DIRS
LOCAL_JAVA_RESOURCE_FILES
LOCAL_JNI_SHARED_LIBRARIES
LOCAL_LDFLAGS 您可以通过设置将其他标志传递给链接器 LOCAL_LDFLAGS。请记住,参数的顺序对于ld非常重要,因此请测试您在所有平台上执行的操作。
LOCAL_LDLIBS LOCAL_LDLIBS允许您指定不属于可执行文件或库的构建的其他库。在-lxxx格式中指定所需的库; 它们直接传递给链接线。但是,请记住,不会为这些库生成依赖关系。它在您希望使用主机上预安装的库的模拟器构建中最有用。链接器(ld)是一个特别挑剔的野兽,所以如果你做鬼鬼祟祟的话,有时候必须在这里传递其他标志。一些例子:LOCAL_LDLIBS += -lcurses -lpthreadLOCAL_LDLIBS += -Wl,-z,origin
LOCAL_MODULE LOCAL_MODULE是应该从Android.mk生成的名称。例如,对于libkjs,它LOCAL_MODULE是“libkjs”(构建系统添加适当的后缀 - .so .dylib .dll)。对于应用程序模块,请使用LOCAL_PACKAGE_NAME而不是 LOCAL_MODULE
LOCAL_MODULE_PATH 指示构建系统将模块放在除其类型之外的其他位置。如果覆盖它,请确保您还设置 LOCAL_UNSTRIPPED_PATH它是可执行文件还是共享库,以便未提取的二进制文件可以去哪里。如果忘记,将会发生错误。有关更多信息,请参阅将模块置于其
LOCAL_MODULE_STEM
LOCAL_MODULE_TAGS 设置LOCAL_MODULE_TAGS为任意数量的以空格分隔的标记。此变量控制包所包含的构造风格。例如:user:在user / userdebug构建中包含此内容eng:在eng版本中包含此内容tests:目标是测试目标并使其可用于测试optional:不包括这个
LOCAL_NO_DEFAULT_COMPILER_FLAGS
LOCAL_NO_EMMA_COMPILE
LOCAL_NO_EMMA_INSTRUMENT
LOCAL_NO_STANDARD_LIBRARIES
LOCAL_OVERRIDES_PACKAGES
LOCAL_PACKAGE_NAME LOCAL_PACKAGE_NAME是应用程序的名称。例如,拨号器,联系人等
LOCAL_POST_PROCESS_COMMAND 对于主机可执行文件,您可以指定在链接后在模块上运行的命令。由于早期或晚期变量评估,您可能必须经历一些扭曲以获得正确的变量:module := $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)LOCAL_POST_PROCESS_COMMAND := /Developer/Tools/Rez -d __DARWIN__ -t APPL\ -d __WXMAC__ -o $(module) Carbon.r
LOCAL_PREBUILT_EXECUTABLES 包含$(BUILD_PREBUILT)或$(BUILD_HOST_PREBUILT)时,请将这些设置为要复制的可执行文件。它们自动位于正确的bin目录中。
LOCAL_PREBUILT_JAVA_LIBRARIES
LOCAL_PREBUILT_LIBS 包含$(BUILD_PREBUILT)或$(BUILD_HOST_PREBUILT)时,请将这些设置为要复制的库。它们自动位于右侧lib目录中。
LOCAL_PREBUILT_OBJ_FILES
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES
LOCAL_PRELINK_MODULE
LOCAL_REQUIRED_MODULES 设置LOCAL_REQUIRED_MODULES为任意数量的以空格分隔的模块名称,例如“libblah”或“Email”。如果安装了此模块,则还将安装所需的所有模块。这可用于例如确保在安装给定应用程序时安装必要的共享库或提供程序。
LOCAL_RESOURCE_DIR
LOCAL_SDK_VERSION
LOCAL_SHARED_LIBRARIES 这些是您直接链接的库。您不需要传递包含传递的库。指定不带后缀的名称:LOCAL_SHARED_LIBRARIES := \ libutils \ libui \ libaudio \ libexpat \ libsgl
LOCAL_SRC_FILES 构建系统会查看LOCAL_SRC_FILES要编译的源文件 - .cpp .c .y .l .java。对于lex和yacc文件,它知道如何自动正确执行中间.h和.c / .cpp文件。如果文件位于包含Android.mk的文件的子目录中,请在其前面加上目录名称:LOCAL_SRC_FILES := \ file1.cpp \ dir/file2.cpp
LOCAL_STATIC_JAVA_LIBRARIES
LOCAL_STATIC_LIBRARIES 这些是要包含在模块中的静态库。大多数情况下,我们使用共享库,但有几个地方,比如sbin和主机可执行文件中的可执行文件,我们使用静态库代替。LOCAL_STATIC_LIBRARIES := \ libutils \ libtinyxml
LOCAL_UNINSTALLABLE_MODULE
LOCAL_UNSTRIPPED_PATH 指示构建系统将模块的未剥离版本放在除了其类型的正常之外的某个位置。通常,您重写此操作是因为您LOCAL_MODULE_PATH为可执行文件或共享库进行了覆盖。如果您覆盖LOCAL_MODULE_PATH但不是 LOCAL_UNSTRIPPED_PATH,则会发生错误。有关更多信息,请参阅将模块置于其
LOCAL_WHOLE_STATIC_LIBRARIES 这些是您希望包含在模块中的静态库,而不允许链接器从中删除死代码。如果要将静态库添加到共享库并从共享库中公开静态库的内容,这将非常有用。LOCAL_WHOLE_STATIC_LIBRARIES := \ libsqlite3_android
LOCAL_YACCFLAGS 要传递给模块的yacc调用的任何标志。这里的一个已知限制是,对于模块的所有YACC调用,标志都是相同的。这可以修复。如果您需要它,请问。LOCAL_YACCFLAGS := -p kjsyy
OVERRIDE_BUILT_MODULE_PATH

Makefile模板

文件目录结构:

- dynamiclibapp.c
- Makefile
- comm/inc/apue.h
- comm/errorhandle.c
- dynamiclib/Makefile
- dynamiclib/dynamiclib_add.c
- dynamiclib/dynamiclib_mul.c
- dynamiclib/inc/dynamiclibs.h
- dynamiclib/libs/

dynamiclib目录

  • dynamiclib/inc/dynamiclibs.h (对两个c文件中函数的声明)

文件内容如下:

1 #ifndef __dynamic_libs_h__
2 #define __dynamic_libs_h__
3
4 #include "apue.h"
5 int dynamic_lib_func_add(int i1, int i2);
6 int dynamic_lib_func_mul(int i1, int i2);
7
8 #endif
  • dynamiclib/dynamiclib_add.c 文件内容如下:
1 #include "dynamiclibs.h"
2
3 int dynamic_lib_func_add(int i1, int i2)
4 {
5 int iret = i1 + i2;
6 printf("... in .so func, %d add %d,return %d\n", i1, i2, iret);
7 return iret;
8 }
  • dynamiclib/dynamiclib_mul.c 文件内容如下:
1 #include "dynamiclibs.h"
2
3 int dynamic_lib_func_mul(int i1, int i2)
4 {
5 int iret = i1 * i2;
6 printf("... in .so func, %d multiplys %d, retun %d\n", i1, i2, iret);
7 return iret;
8 }
  • dynamiclib/Makefile 文件内容如下:
 1 CC       = gcc 
2 CFLAGS = -Wall -g -O -fPIC
# 需要加上 -fPIC
# 为了兼容各个系统,在生成位置无关的代码的时候,应该使用-fPIC参数。
3 CXXFLAGS =
4 INCLUDE = -I ./inc -I ../comm/inc
5 TARGET = libmytest.so
6 LIBPATH = ./libs/
7
8 vpath %.h ./inc
9
10 OBJS = dynamiclib_add.o dynamiclib_mul.o
11 SRCS = dynamiclib_add.c dynamiclib_mul.c
12
13 $(OBJS):$(SRCS)
14 $(CC) $(CFLAGS) $(INCLUDE) -c $^
15
16 all:$(OBJS)
17 $(CC) -shared -fPIC -o $(TARGET) $(OBJS) 需要加上 -shared -fPIC
18 mv $(TARGET) $(LIBPATH)
19
20 clean:
21 rm -f *.o
22 rm -f $(LIBPATH)*

加载

以上文件,就可以生成动态库文件 libmytest.so,应用程序以两种方式加载动态库函数,如下

在编译应用程序时加载动态库

dynamiclibapp.c 文件内容如下

 1 #include "apue.h"
2 #include "dynamiclibs.h"
3
4 int main(int argc, char *argv[])
5 {
6 err_msg("step in main\n");
7 dynamic_lib_func_add(1, 9);
8 dynamic_lib_func_mul(1, 9);
9 err_msg("step out main\n");
10
11 return 0;
12 }

Makefile 文件内容如下:

 1 CC       = gcc 
2 CFLAGS = -Wall -O -g
3 CXXFLAGS =
4 INCLUDE = -I ./comm/inc -I ./dynamiclib/inc
5 TARGET = dynamiclibapp
6 LIBVAR = -lmytest 指明需要链接动态库 libmytest.so
7 LIBPATH = -L./dynamiclib/libs 指明 libmytest.so 的路径
8 #search paths for errorhandler.c
9 vpath %.c ./comm
10 #下行是为依赖项 apue.h 准备的,比如 [errorhandler.o:errorhandler.c apue.h] 里的 apue.h
11 vpath %.h ./comm/inc
12
13 OBJS = errorhandler.o dynamiclibapp.o
14 #下行的 apue.h,可以不必写出来
15 errorhandler.o:errorhandler.c apue.h
16 $(CC) $(CFLAGS) $(INCLUDE) -c $^
17 dynamiclibapp.o:dynamiclibapp.c apue.h
18 $(CC) $(CFLAGS) $(INCLUDE) -c $^
19
20 all:$(OBJS) $(LIB)
21 cd ./dynamiclib && make all
22 $(CC) $(CFLAGS) $(INCLUDE) -o $(TARGET) $(OBJS) $(LIBPATH) $(LIBVAR)
23 在上行中,在执行编译时,加载了 libmytest.so 中函数
24 clean:
25 rm -f *.o
26 rm -f comm/inc/*.gch
27 rm -f $(TARGET)
28 cd ./dynamiclib && make clean

对于这种方式编译出来的动态库文件,还需要在 /etc/ld.so.conf.d/ 目录中添加 libmytest.so 库文件的路径说明,即在 /etc/ld.so.conf.d/ 目录中新建配置文件 mytest.conf,且执行 ldconfig, /etc/ld.so.conf.d/mytest.conf 的文件内容为 libmytest.so 库文件的绝对路径,例如:1 /home/lijiangtao/dynamiclib/libs
如果不在编译应用程序时加载动态库文件里的函数,而是改为在应用程序执行时(比如:程序的main函数启动期间,或在程序执行期间)加载 libmytest.so 里函数,那么就可以不需在 /etc/ld.so.conf.d/ 目录中配置 libmytest.so路径,具体如下所述。

在应用程序执行时加载动态库

dynamiclibapp.c 文件内容如下:

1 #include "apue.h"
2 #include "dynamiclibs.h"
3 #include
4
5 typedef int (*fp_lib_add)(int, int);
6 typedef int (*fp_lib_mul)(int, int);
7 typedef void* dlhandle;
8
9 dlhandle dll = NULL;
10 fp_lib_add func_add = NULL;
11 fp_lib_mul func_mul = NULL;
12
13 dlhandle load_dynamic_func(char *psopath, fp_lib_add *padd, fp_lib_mul *pmul);
14
15 int main(int argc, char *argv[])
16 {
17 char *pso = "/home/lijiangtao/dynamiclib/libs/libmytest.so";//指定 .so 路径
18 dll = load_dynamic_func(pso, &func_add, &func_mul);//程序执行时,加载动态函数
19 err_msg("step in main\n");
20 func_add(1, 9);//执行 add 函数
21 func_mul(1, 9);//执行 mul 函数
22 err_msg("step out main\n");
23
24 return 0;
25 }
26
27 dlhandle load_dynamic_func(char *psopath, fp_lib_add *padd, fp_lib_mul *pmul)
28 {
29 if(NULL == psopath ||'\0' == psopath[0])
30 return NULL;
31 char *perrormsg = NULL;
32 dlhandle dllhandle = dlopen(psopath, RTLD_LAZY);
33 if(NULL == dllhandle)
34 {
35 printf("%s\n", dlerror());
36 return NULL;
37 }
38 if(NULL != padd)
39 {
40 *padd = dlsym(dllhandle, "dynamic_lib_func_add");//加载 add 函数
41 perrormsg = dlerror();
42 if(NULL != perrormsg)
43 printf("%s\n", perrormsg);
44 }
45 if(NULL != pmul)
46 {
47 *pmul = dlsym(dllhandle, "dynamic_lib_func_mul");//加载 mul 函数
48 perrormsg = dlerror();
49 if(NULL != perrormsg)
50 printf("%s\n", perrormsg);
51 }
52 return dllhandle;
53 }

c/c++ 混合编译

c用gcc编译,c++用g++编译,分别生成.o文件,最后变成最终的库

CC = arm-poky-linux-gnueabi-gcc
C++ = arm-poky-linux-gnueabi-g++
LINK = arm-poky-linux-gnueabi-g++

LIBS = -lz -lm -lpcre -lpthread
#编译.so must add -fPIC option https://www.cnblogs.com/cswuyg/p/3830703.html
CCFLAGS = -c -g -fPIC
C++FLAGS = -c -g -fPIC

#期望得到的执行文件或动态库.so
TARGET=libxx.so

INCLUDES = -I. -I../../

C++FILES = $(wildcard *.cpp ./../common/*.cpp)
CFILES = $(wildcard *.c ./../common/*.c)

OBJFILE = $(CFILES:.c=.o) $(C++FILES:.cpp=.o)

all:$(TARGET)

$(TARGET): $(OBJFILE)
$(LINK) $^ $(LIBS) -Wall -fPIC -shared -o $@

%.o:%.c
$(CC) -o $@ $(CCFLAGS) $< $(INCLUDES)

%.o:%.cpp
$(C++) -o $@ $(C++FLAGS) $< $(INCLUDES)

install:
tsxs -i -o $(TARGET)

clean:
rm -rf $(TARGET)
rm -rf $(OBJFILE)

需要注意的是为了能够让C++调用C语言代码,需要在头文件加上extern “C”,代码如下:

#ifdef __cplusplus //而这一部分就是告诉编译器,如果定义了__cplusplus(即如果是cpp文件, 
extern "C"{ //因为cpp文件默认定义了该宏),则采用C语言方式进行编译
#endif
...

#ifdef __cplusplus
}
#endif