libvpu 的Makefile
CC ?=$(CROSS_COMPILE)gcc
AR ?=$(CROSS_COMPILE)ar
#CC = gcc
# list of platforms which want this test case
#INCLUDE_LIST:= IMX27ADS IMX51 IMX53 IMX6Q
OBJ = vpu_io.o vpu_util.o vpu_lib.o vpu_gdi.o vpu_debug.o
LIBNAME = libvpu
SONAMEVERSION=4
ifeq ($(PLATFORM), $(findstring $(PLATFORM), $(INCLUDE_LIST)))
ifeq ($(findstring IMX5, $(PLATFORM)), IMX5)
VERSION = imx5
else
VERSION = $(shell echo $(PLATFORM_STR) | awk '{print substr($$0, 1, 5)}' \
| awk '{print tolower($$0) }' )
endif
all: $(LIBNAME).so $(LIBNAME).a
install: install_headers
@mkdir -p $(DEST_DIR)/usr/lib
cp -P $(LIBNAME).* $(DEST_DIR)/usr/lib
install_headers:
@mkdir -p $(DEST_DIR)/usr/include
cp vpu_lib.h $(DEST_DIR)/usr/include
cp vpu_io.h $(DEST_DIR)/usr/include
else
all install :
endif
%.o: %.c
$(CC) -Wall -g -O0 -I/home/cbb/unified_3d/GPU_Src_drv/build/sdk/include -fPIC -c $^ -o $@
$(LIBNAME).so.$(SONAMEVERSION): $(OBJ)
$(CC) -shared -nostartfiles -Wl,-soname,$@ $^ -o $@ -lpthread -lGAL
$(LIBNAME).so: $(LIBNAME).so.$(SONAMEVERSION)
ln -sf $< $@
$(LIBNAME).a: $(OBJ)
$(AR) -rc $@ $^
.PHONY: clean
clean:
rm -f $(LIBNAME).* $(OBJ)
CC ?=$(CROSS_COMPILE)gcc
AR ?=$(CROSS_COMPILE)ar
定义要使用的编译器,非交叉编译的场合,CROSS_COMPILE为空,所以使用的就是gcc,交叉编译时(如在x86 PC上编译在ARM上运行的软件),CROSS_COMPILE会定义为类似于arm_linux_gnu_的值,这时会使用交叉编译器(如arm_linux_gnu_gcc)。
使用gcc编译阶段,最常用的编译选项CFLAGS = -Wall -Werror -g -O0的解释:
-Wall:编译阶段显示所有警告。
-Werror:将所有的警告当成错误进行处理,使出现警告时就停止编译。
常见编译报错:cc1: warnings being treated as errors 。解决方法是:把-Werror去掉,不把warnning当作error处理。
-g:编译器在编译时,产生调试信息,最终产生供gdb调试使用的可执行文件。用了-g选项生成的a.out会比没用-g选项生成的a.out明显大点。
-O0:编译器的优化选项的4个级别
-O0:表示编译时没有优化。
-O1:表示编译时使用默认优化。
-O2:表示编译时使用二级优化。
-O3:表示编译时使用最高级优化。
-Os:相当于-O2.5优化,但又不所见代码尺寸。
ifeq ($(PLATFORM), $(findstring $(PLATFORM), $(INCLUDE_LIST)))
#$(findstring , )
#功能:在字串中查找字串。
#返回:如果找到,那么返回,否则返回空字符串。
echo “123456789 3333344” | awk ‘{print substr($1,5,2)}’
$1指的是第一列,也就是123456789
5指的是从第5个字符开始
2指的是截取2个字符
awk '{print tolower($$0) }'
toupper() 小写转大写
tolower() 大写转小写
分别是
@
,
@,
@,^,$<代表的意义分别是:
@
−
−
目标文件,
@--目标文件,
@−−目标文件,^–所有的依赖文件,
<
−
−
第一个依赖文件。其中
<--第一个依赖文件。 其中
<−−第一个依赖文件。其中@的文件名与$^的文件名一致,只是文件后缀一个.o,另一个.c。
.so 文件的生成:
%.o: %.c
$(CC) -Wall -g -O0 -I/home/cbb/unified_3d/GPU_Src_drv/build/sdk/include -fPIC -c $^ -o $@
-fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
gcc -shared -fPIC -o 1.so 1.c
这里有一个-fPIC参数
PIC就是position independent code
PIC使.so文件的代码段变为真正意义上的共享 。
.so 生成的核心代码:
$(LIBNAME).so.$(SONAMEVERSION): $(OBJ)
$(CC) -shared -nostartfiles -Wl,-soname,$@ $^ -o $@ -lpthread -lGAL
从目标文件生成动态链接库:
$ gcc -fPIC -c func.c -o func.o
$ gcc -shared func.o -o libfunc.so
-fPIC 选项作用于编译阶段,在生成目标文件时就得使用该选项,以生成位置无关的代码。
如果希望将一个动态链接库链接到可执行文件,那么需要在命令行中列出动态链接库的名称,具体方式和普通的源文件、目标文件一样。请看下面的例子:
$ gcc main.c libfunc.so -o app.out
将 main.c 和 libfunc.so 一起编译成 app.out,当 app.out 运行时,会动态地加载链接库 libfunc.so。
当然,必须要确保程序在运行时可以找到这个动态链接库。你可以将链接库放到标准目录下,例如 /usr/lib,或者设置一个合适的环境变量,例如 LIBRARY_PATH。不同系统,具有不同的加载链接库的方法。
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。
Linux系统下的动态库命名方 式是 lib*.so,而在链接时表示位 -l* , *是自己起的库名。
-nostartfiles
连接的时候不使用标准系统的启动文件。标准系统库通常被使用,除非选项“-nostdlib”和“-nodefaultlibs”被使用。不链接系统标准启动文件,而标准库文件仍然正常使用。
-Wl
选项告诉编译器将后面的参数传递给链接器。
-soname
则指定了动态库的soname(简单共享名,Short for shared object name)
soname的关键功能是它提供了兼容性的标准:
当要升级系统中的一个库时,并且新库的soname和老库的soname一样,用旧库链接生成的程序使用新库依然能正常运行。这个特性使得在Linux下,升级使得共享库的程序和定位错误变得十分容易。
在Linux中,应用程序通过使用soname,来指定所希望库的版本,库作者可以通过保留或改变soname来声明,哪些版本是兼容的,这使得程序员摆脱了共享库版本冲突问题的困扰。
可以通过readelf -d来查看每个动态库的SONAME
- 声明libto.so.1,并生成libto.so.1.2
[root@localhost c]# gcc -fPIC -shared -Wl,-soname,libto.so.1 -o libto.so.1.2 to.c
[root@localhost c]# ls -lh
-rwxr-xr-x 1 root root 4268 Jan 10 17:22 libto.so.1.2
[root@localhost c]# ldconfig -n ./
lrwxrwxrwx 1 root root 12 Jan 10 17:23 libto.so.1 -> libto.so.1.2
-rwxr-xr-x 1 root root 4.2K Jan 10 17:22 libto.so.1.2
[root@localhost c]# readelf -d libto.so.1.2
一个C/C++文件要经过预处理(preprocessing)、编译(compilation)、汇编(assembly)、和连接(linking)才能变成可执行文件。
这两条命令的功能就是,大目标是OBJS,这个OBJS就是各种.o文件,然后%.o就是具体的解释,而%.c就是对应同样名字的.c文件,而下面的命令,结合了2个自动化变量,
<
表示依赖对象集中的第一个,
< 表示依赖对象集中的第一个,
<表示依赖对象集中的第一个,@ 则代表了目标集。所以这个功能就是要遍历所有的.c文件,对所有的.c文件进行编译,然后编译生成对应的.o文件。我们在实际编写程序时,targets是不需要的,可以简写如下:
%.o : %.c
gcc -c $< -o $@
————————————————
AR = ar rc —–生成静态库文件命令
在Windows下也就是.obj文件,UNIX下是.o文件,即ObjectFile,这个动作叫做编译(compile)。然后再把大量的ObjectFile合成执行文件,这个动作叫作链接(link)
编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。
链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(ObjectFile),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(LibraryFile),也就是.lib文件,在UNIX下,是ArchiveFile,也就是.a文件。
源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成ObjectFile。而在链接程序时,链接器会在所有的ObjectFile中找寻函数的实现,如果找不到,那到就会报链接错误码(LinkerError),在VC下,这种错误一般是:Link2001错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的ObjectFile.
target…:prerequisites…
command
说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。
# Get all dirs with a Makefile
TMP_DIRS := $(foreach dir, $(wildcard *), $(wildcard $(dir)/Makefile))
DIRS := $(patsubst %/Makefile,%,$(TMP_DIRS))
.PHONY: all install clean
.PHONY: $(DIRS)
all: TARGET=all
all: $(DIRS)
install: TARGET=install
install: $(DIRS)
clean: TARGET=clean
clean : $(DIRS)
$(DIRS):
$(MAKE) -C $@ $(TARGET)
1.获取匹配模式的文件名wildcard
这个函数的功能是查找当前目录下所有符合模式 PATTERN 的文件名,其返回值是以空格分割的、当前目录下的所有符合模式 PATTERN 的文件名列表。其原型如下:
$(wildcard PATTERN)
例如,如下模式返回当前目录下所有扩展名位 .c 的文件列表。
$(wildcard *.c)
2.模式替换函数patsubst
这个函数的功能是查找字符串 text 中按照空格分开的单词,将符合模式 pattern 的字符串替换成 replacement。 Pattern 中的模式可以使用通配符, % 代表 0 个到 n 个字符,当 pattern 和 replacement 中都有 % 时,符合条件的字符将被 replacement 中的替换。函数的返回值是替换后的新字符串。其原型如下:
$(patsubst pattern, replacement, text)
例如,需要将 C 文件替换为 .o 的目标文件可以使用如下模式:
$(patsubst %.c, %.o, add.c)
上面的模式将 add.c 字符串作为输入,当扩展名为 .c 时符合模式 %.c ,其中 % 在这里代表 add,替换为 add.o,并作为输出字符串。
$(patsubst %.c, %.o, $(wildcard *.c))
输出的字符串将当前扩展名为 .c 的文件替换成 .o 的文件列表。
3.循环函数foreach
这个函数的原型为:
$(foreach VAR, LIST, TEXT)
函数的功能为 foreach 将 LIST 字符串中一个空格分割的单词,先传给临时变量 VAR ,然后执行 TEXT 表达式, TEXT 表达式处理结束后输出。其返回值是空格分割表达式 TEXT 的计算结果。
例如,对于存在 add 和 sub 的两个目录,设置 DIRS 为 “add sub ./” 包含目录 add、sub 和当前目录。表达式 $(wildcard $(dir)/*.c) ,可以取出目录 add 和 sub 及当前目录中的所有扩展名为 .c 的C语言源文件:
DIRS = sub add ./
FILES = $(foreach dir, $(DIRS), $(wildcard $(dir)/*.c))