把源码编译打包为动态库so文件,做平台的可能对这些不熟悉。
对我们这些算是经常用到的。
总结个模板,一看就懂的那种,提供给有需要的人。
前提条件,机器上有 gcc工具链。
如果文件个数少,可以直接单个编译,如下:
Building shared lib... g++ -c -fPIC Quote.cpp -o Quote.o g++ -c -fPIC QuoteExport.cpp -o QuoteExport.o g++ -c -fPIC Start.cpp -o Start.o Generating shared lib... g++ -shared -fPIC -o libQuoteLib.so ./Quote.o ./QuoteExport.o ./Start.o cp libQuoteLib.so ../
OK!
如果文件个数较多,或者夸文件夹了,层层嵌套。
那么就整个makefile模板文件,放到代码的根目录下,直接执行一个make即可。
模板文件如下:
############################################################################ #makefile,created by yangyongzhen qq:534117529 ############################################################################ #**************************************************************************** # Cross complie path #**************************************************************************** #GCC_PATH=D:\msysgit\mingw CROSS_COMPILE= CC := $(CROSS_COMPILE)gcc CXX := $(CROSS_COMPILE)g++ AS := $(CROSS_COMPILE)as AR := $(CROSS_COMPILE)ar LD := $(CROSS_COMPILE)ld RANLIB := $(CROSS_COMPILE)ranlib OBJDUMP:= $(CROSS_COMPILE)objdump OBJCOPY:= $(CROSS_COMPILE)objcopy STRIP := $(CROSS_COMPILE)strip #**************************************************************************** # Flags #**************************************************************************** CFLAGS= LDSCRIPT= LDFLAGS= #**************************************************************************** # Source files #**************************************************************************** SRC_C=$(shell find . -name "*.cpp") OBJ_C=$(patsubst %.cpp, %.o, $(SRC_C)) SRCS := $(SRC_C) $(SRC_C) OBJS := $(OBJ_C) #**************************************************************************** # Targets of the build #**************************************************************************** TARGET := libQuoteLib .PHONY: clean all: prebuild $(TARGET).so #**************************************************************************** # TARGET #**************************************************************************** prebuild: @echo Building shared lib... $(TARGET).so : $(OBJS) @echo Generating shared lib... $(CXX) -shared -fPIC -o $(TARGET).so $(OBJS) cp $(TARGET).so ../ @echo OK! %.o : %.cpp $(CXX) -c -fPIC $(CFLAGS) $< -o $@ clean: @echo The following files: rm -f $(TARGET) *.so find . -name "*.[od]" |xargs rm @echo Removed!
注:在linux上,源文件的函数或方法前,不需要声明 __declspec(dllexport)
在WIn32上才需要。
所以在头文件中一般会看到:
#ifdef _WIN32 #define TAP_CDECL __cdecl #define TAP_DLLEXPORT __declspec(dllexport) #else #define TAP_CDECL #define TAP_DLLEXPORT #endif
__cdecl 是C Declaration的缩写(declaration,声明),表示C语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。
被调用函数不会要求调用者传递多少参数,调用者传递过多或者过少的参数,甚至完全不同的参数都不会产生编译阶段的错误。
_stdcall 是StandardCall的缩写,是C++的标准调用方式:所有参数从右到左依次入栈,如果是调用类成员的话,最后一个入栈的是this指针。这些堆栈中的参数由被调用的函数在返回后清除,使用的指令是 retnX,X表示参数占用的字节数,CPU在ret之后自动弹出X个字节的堆栈空间。称为自动清栈。函数在编译的时候就必须确定参数个数,并且调用者必须严格的控制参数的生成,不能多,不能少,否则返回后会出错。
C中不加说明默认函数为_cdecl方式(C中也只能用这种方式),C++也一样,但是默认的调用方式可以在IDE环境中设置。
带有可变参数的函数必须且只能使用_cdecl方式
__cdecl __fastcall与__stdcall,三者都是调用约定(Calling convention),它决定以下内容:1)函数参数的压栈顺序,2)由调用者还是被调用者把参数弹出栈,3)以及产生函数修饰名的方法。
1、__stdcall调用约定:函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈。
2、__cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。注意:对于可变参数的成员函数,始终使用__cdecl的转换方式。
3、__fastcall调用约定:它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈)。
4、thiscall仅仅应用于"C++"成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。
5、nakedcall采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。
关于__declspec的解释,转自:CSDN博主「fengbingchun」
原文链接:https://blog.csdn.net/fengbingchun/article/details/78825004
__declspec是Microsoft VC中专用的关键字,它配合着一些属性可以对标准C/C++进行扩充。__declspec关键字应该出现在声明的前面。
__declspec(dllexport)用于Windows中的动态库中,声明导出函数、类、对象等供外面调用,省略给出.def文件。即将函数、类等声明为导出函数,供其它程序调用,作为动态库的对外接口函数、类等。
.def文件(模块定义文件)是包含一个或多个描述各种DLL属性的Module语句的文本文件。.def文件或__declspec(dllexport)都是将公共符号导入到应用程序或从DLL导出函数。如果不提供__declspec(dllexport)导出DLL函数,则DLL需要提供.def文件。
__declspec(dllimport)用于Windows中,从别的动态库中声明导入函数、类、对象等供本动态库或exe文件使用。当你需要使用DLL中的函数时,往往不需要显示地导入函数,编译器可自动完成。
不使用__declspec(dllimport)也能正确编译代码,但使用__declspec(dllimport)使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于DLL中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨DLL边界的函数调用中。声明一个导入函数,是说这个函数是从别的DLL导入。一般用于使用某个DLL的exe中。
---------------------