Chinaunix首页 | 论坛 | 博客
  • 博客访问: 59733
  • 博文数量: 20
  • 博客积分: 1031
  • 博客等级: 少尉
  • 技术积分: 230
  • 用 户 组: 普通用户
  • 注册时间: 2006-08-11 14:22
文章分类

全部博文(20)

文章存档

2011年(2)

2010年(4)

2009年(14)

我的朋友

分类: C/C++

2009-09-28 11:13:02

昨天晚上,今天早上二个小时,弄Makefile,对Makefile的理解更上一层楼,^_^
 
没太多时间写完整的过程,把错误的Makefile和正确的Makefile列出来,以后再整理
 
错误的的Makefile如下:
PROG   := hmain
 
CC     := arm-linux-gcc
LIBS   := -lp_base-1.0.0 -lp_util-1.0.0 -lp_driver-1.0.0 -lct_db -lpthread
LDFLAGS:= -Wl,-rpath,/usr1/lib:/usr1/bin/lib:/usr1/bin -L../../lib $(LIBS)
INCLUDE:= -I../include -I../lib_ct_db-0.1
CFLAGS := -Wall
 
SOURCE := $(wildcard *.c)
OBJ    := $(patsubst %.c,%.o,$(SOURCE))

$(PROG): $(OBJ)
       $(CC) -o $@ $(OBJ) $(CFLAGS) $(INCLUDE) $(LDFLAGS)
 
all: $(PROG)
 
clean:
       rm -f *~ *.o $(PROG)
 
 
 
这个Makefile有两处错误:
其中一处是我无法理解也不知道如何修改的,就是OBJ这行,因为这行的作用是从“*.c”文件得到“*.o”文件,这个过程似乎无法利用“INCLUDE”规定的头文件路径,因此报了很多找不到头文件的错误;
另一处是“-lct_db -lpthread ”这里实际上要到另一个目录里去找,因此改下第一个错误后会报找不到库的错误。
 
 
 
除了这两个错误外,还有对这些宏的用法也比较模糊,参考这里:
 
写了一个接近正确的写法,并逐步修改后摸索出了这几个宏的用法
 
 
 
避开第一个错误,改正第二个错误后,得到正确的Makefile如下:
#目标
TARGET  := hmain
#源, 自动找所有.c文件
SOURCE  := $(wildcard *.c)
 
#编译参数
CC      := arm-linux-gcc
# 注意这里有两个库来源, 一般这是不够规范的用法, 不过万一用到了, 下面的“+=”写法可以兼容
LIBS    += -L../../lib -lp_base-1.0.0 -lp_util-1.0.0 -lp_driver-1.0.0
LIBS    += -L../../bin -lct_db -lpthread
LDFLAGS := -Wl,-rpath,/usr1/lib:/usr1/bin/lib:/usr1/bin
# 同LIBS
INCLUDE += -I../include
INCLUDE += -I../lib_ct_db-0.1
CFLAGS  := -g -Wall $(INCLUDE)
CXXFLAGS:= $(CFLAGS)
 
# 这行是编译的关键
$(TARGET) : $(SOURCE)
        $(CC) -o $@ $(CXXFLAGS) $(SOURCE) $(LIBS) $(LDFLAGS)
clean :
        rm -fr *~ *.o $(TARGET)
 
 
 
难、疑点:
如何让OBJ这步可以利用“INCLUDE”规定的头文件路径?
 
 
 
一点说明:
Makefile里,“$@”代表目标。可以做实验,修改“clean:”这个目标为:
clean:
       rm -f $@ *~ *.o $(PROG)
 
然后执行“make clean”,“$@”的含义一目了然,^_^:
[ppp@ll ct_hmi]$ make clean
rm -f clean *~ *.o hmain
 
 
 
注:
今天又试验了另一个Makefile,发现可以用第一个方式包含两个不同的头文件目录:
INCLUDE:= -I../include -I../lib_ct_db-0.1
 
因此思考,是不是LIBS也可以用这种方式来写,以后再试验
 
 
 
 
2009.11.18 补充______________________________________________________________
 
 
今天的一点发现,Makefile的目标需要用“:=”来声明(或说定义?),如果只用“:”,后面用到$(PROG)的地方会找不到目标,如下:
 
#PROG:  hello # 没有 = , 后面的$(PROG) 会无内容
PROG    := hello
 
CC      := arm-linux-gcc
CFLAGS  := -Wall
 
all: $(PROG)
 
clean:
        rm -rf $(PROG) *~
 
 
 
另补充:
前面的“难、疑点”,如何让OBJ这步可以利用“INCLUDE”规定的头文件路径?
下面的OBJ实际上已经使用了“INCLUDE”、“CFLAGS”等编译参数。
OBJ    := $(patsubst %.c,%.o,$(SOURCE))
 
 
 
 
2009.11.19 补充______________________________________________________________
 
 
make有一些预定义变量,下面是常用到的,之前的“$@”也是其中之一:
$@,目标的完整名称。
$*,不包含扩展名的目标文件名称。
$<,第一个依赖文件的名称。
$^,所有的依赖文件,以空格分开,不包含重复的依赖文件。
CXX,C++ 编译器的名称,默认值为 g++。
CXXFLAGS,C++ 编译器的选项。
 
以上内容主要参考了这里:
 
 
 
 
2009.11.20 补充______________________________________________________________

昨天、今天上午整理同事的一个Makefile,写得巨复杂,定义了很多层的编译、链接、汇编选项,收获很多,除了加强了对Makefile的理解,也知道了更多的Makefile的“潜规则”,^_^

那是一个生成动态库的Makefile,经过一番整理,写了一个新的Makefile,见后面,其中还有一点注释。


### ### ### ### ###
 
在整理过程中,快到最后的时候,遇到一个问题,是关于$(LIBS)的位置问题,放在$(LDFLAGS)后面,链接时出错如下:
...
arm-linux-ld -fPIC -shared --warn-common --warn-once -z combreloc -z defs -rpath /usr1/lib -L./lib -lc /home/pax/buildroot/build_arm/staging_dir/usr/bin/../lib/gcc/arm-linux-uclibc/4.2.4/libgcc.a -L../../../lib -lpax_base-1.0.0 -o libpax_csv-1.0.0.so --whole-archive libpax_csv.a
libpax_csv.a(csv_read.o): In function `csv_get_cycle_info':
csv_read.c:(.text+0x34): undefined reference to `__divsi3'
...(报错)

在网络上搜索了一下,在下面这个网站上搜到了非常有用的信息:



摘抄其主要内容如下:

将多个.o文件链接成可执行文件的时候。如果链接的顺序不对,会产生错误。
《An introduction of gcc》里面有下面一段话:
On Unix-like systems, the traditional behavior of compilers and linkers is to search for external functions from left to right in the object files specified on the command line. This means that the object file which contains the definition of a function should appear after any files which call that function.
 
但是也说了:
Most current compilers and linkers will search all object files, regardless of order, but since not all compilers do this it is best to follow the convention of ordering object files from left to right.
但是同时发现了另一个问题,就是我们自己的操作系统提供的库文件有两个:libgcc.a和libcs.a。链接的时候要加上 -lcs -lgcc ,如果这两个顺序搞乱了,就会报错。

为什么编译器在搜索目标文件的时候可以不看顺序,搜索库文件的时候却会报错呢?
 
下面是hh的解答:
不依命令行的顺序的话就得重复扫描目标文件,我猜可能是重复扫描库文件的开销比较大所以必须在命令行指定正确的顺序。海东有空也可读读此书:



这个文章的主要意思如下:
在unix世界,编译器链接器习惯从左到右搜索文件,虽然现在很多编译器链接器可以不管顺序了,但为安全起见,建议还是按传统顺序来写Makefile。

另外,搜索目标文件时不看顺序而搜索库文件时看顺序,可能原因是搜索库文件开销比较大。

提到的那本书“Linkers and Loaders”,英文版,感觉头大,有时间再看吧,:)



具体到本问题,原因大概知道了,不过更深入的可能还要再琢磨...



### ### ### ### ###

去掉了原来的“SHARE   := -fPIC -shared -o”,把其中的其中“-shared”和“-fPIC”放到LDFLAGS里去了。“-fPIC”的含义,见 man gcc:
...
    -shared
       Produce a shared object which can then be linked with other objects to form an executable.  Not all systems support this option.  For predictable results, you must also specify the same set of options that were used to generate code (-fpic, -fPIC, or model suboptions) when you specify this option.[1]
...



### ### ### ### ###

# Makefile for lib***

#共享库文件名, lib*.so
TARGET          := lib***-1.0.0.so
LIBBASE_SH      := lib***.so
LIBBASE         := lib***.a

CC              := arm-linux-gcc
AR              := arm-linux-ar
LD              := arm-linux-ld
STRIP           := arm-linux-strip

# ar选项
ARFLAGS         := cr

# 头文件路径
INCLUDE         := -isystem $(shell $(CC) -print-file-name=include) -I./

# C预编译选项
CPU_CFLAGS      := -mtune=arm9tdmi -msoft-float
XWARNINGS       := -Wstrict-prototypes -Wno-trigraphs -Wall -fno-strict-aliasing
SSP_CFLAGS      := $(call check_gcc,-fno-stack-protector,)
CFLAGS          += $(XWARNINGS) $(CPU_CFLAGS) $(SSP_CFLAGS) -std=gnu99 -fno-builtin
CFLAGS          += $(INCLUDE)
CFLAGS          += -O2

# ld的食物, :)
LIBGCC_CFLAGS   ?= $(CFLAGS)
LIBGCC          := $(shell $(CC) $(LIBGCC_CFLAGS) -print-libgcc-file-name)
LIBS            += -L./lib -lc $(LIBGCC)
LIBS            += -L../../../lib -lpax_base-1.0.0

# ld选项
LDFLAGS         := -fPIC -shared --warn-common --warn-once -z combreloc -z defs
LDFLAGS         += -rpath /usr1/lib

# 源文件和对象文件, 自动找所有.c, 并将目标定义为同名.o文件
SOURCE          := src/csv_index.c src/csv_read.c src/csv_pub.c
OBJ             := $(patsubst %.c,%.o, $(SOURCE))

shared: $(TARGET)
        cp $(TARGET) ./lib/
        cp $(TARGET) ../../bin/

# 这个可以删掉, 不过all在Makefile一向都有, 就留它一条性命, ^_^
all : $(TARGET)

$(OBJ): %.o : %.c
        $(CC) $(CFLAGS) -c $< -o $@
        $(STRIP) -x -R .note -R .comment $*.o

$(LIBBASE): $(OBJ)
        $(AR) $(ARFLAGS) $@ $^

# $(LIBS)的顺序有什么影响? 放在$(LDFLAGS)后面, 链接出错, 这个问题下面网站有讲到, 可以参考
#
#
# 其中说到:
# 发现了另一个问题,就是我们自己的操作系统提供的库文件有两个:libgcc.a和libcs.a。
# 链接的时候要加上 -lcs -lgcc ,如果这两个顺序搞乱了,就会报错。
#
# 又提到:
# From 《An introduction of gcc》:
# Most current compilers and linkers will search all object files, regardless
# of order, but since not all compilers do this it is best to follow the
# convention of ordering object files from left to right.
$(TARGET) : $(LIBBASE)
        $(LD) $(LDFLAGS) -o $@ --whole-archive $^ $(LIBS)

clean :
        rm -fr *.o $(TARGET) $(LIBBASE) lib/* *~ src/*.o src/*~




2009.11.27 补充______________________________________________________________
 
 
今天看同事的Makefile,再次学到一个有用的技巧,在目标的执行体里使用命令需要在前面加“@”,如下:
...
$(TARGET):$(OBJS)
    @echo $(CC)
...




2009.12.17 补充______________________________________________________________
 

想在INCLUDE 里直接引用当前目录,Makefile里怎么传进地址去?
 
在chinaunix上发了个帖子,“albcamus (百無一用書生) ”回复的,用“$(shell pwd)”就可以,自己试了下,用“${shell pwd}”也可以。
 
 
 
 
 
2010.6.1 补充______________________________________________________________
 
# 这个例子说明, 如果目标文件与其中某个文件同名, 则不需要写明要怎么做也可以生成目标文件;
#PROG = test_define
#PROG = mainn
PROG = secondd


CC      = gcc
CFLAGS  = -Wall -g
LDFLAGS =


clean:
rm -f *~ *.o ${PROG}

${PROG}: mainn.c secondd.c
# $(CC) $(CFLAGS) $^ -o ${PROG}

 
 
 
2010.9.7 补充______________________________________________________________

前面说的同事的那个Makefile 又有一点营养被我发现了  ^_^

其中有一行:
LDFLAGS :=--warn-common --warn-once -z combreloc -z defs

这行的几个选项以前没有注意,今天在修改另一个同事的Makefile的时候(也是用的这个Makefil),发现一个特别的现象,也就是这个Makefile不能适应不同对象的make。

也就是说其中定义了一个宏EXEC,表明要make的对象,如果要make的对象不是这个EXEC指定的时候,比如 make ***,那么LDFLAGS不会发生作用,后来看是这位同事把相关的ld的食物放到EXEC的实现里去了,因此不会用到相关的LDFLAGS,反倒是把LDFLAGS当成GCC的选项了,因此报了错:
[test@localhost bdbenv]$ make test_msg
arm-linux-gcc -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -mtune=arm9tdmi -msoft-float -std=gnu99 -fno-builtin -I./include -I. -I../../../platform/runtime/lib_head -O2 -isystem /home/pax/platform/buildroot/build_arm/staging_dir/usr/bin-ccache/../lib/gcc/arm-linux-uclibc/4.2.4/include -L../../../platform/runtime/lib --warn-common --warn-once -z combreloc -z defs test_msg.c -o test_msg
arm-linux-gcc: combreloc: No such file or directory
arm-linux-gcc: defs: No such file or directory
cc1: error: unrecognized command line option "-Wcommon"
cc1: error: unrecognized command line option "-Wonce"
make: *** [test_msg] 错误 1
[test@localhost bdbenv]$ 

后来又去仔细找了这个问题,才发现上述的几个选项是在ld 的manual 里有说到。

上面说到的那行 挪下来看得更清楚:
LDFLAGS :=--warn-common --warn-once -z combreloc -z defs

--warn-common
  Warn when a common symbol is combined with another common symbol or with a symbol definition.
--warn-once
  Only warn once for each undefined symbol, rather than once per module which refers to it.


-z keyword
The recognized keywords are:

combreloc
Combines multiple reloc sections and sorts them to make dynamic symbol lookup
caching possible.

defs
Disallows undefined symbols in object files. Undefined symbols in shared libraries
are still allowed.

目前暂时不知道它们分别会有什么具体的影响,但对它们出现的原因还是完全的知道了  ^_^

下一步也许就是知道它们的影响了,加油!




2011.1.20 补充______________________________________________________________

Makefile 里不能识别 本用户根目录标志 “~”

在一个Makefile 里一开始写了一个“-I~/platform/runtime/lib_head/”,可是总不能正确的编译通过,某个在当前用户的 “~/platform/runtime/lib_head/” 下的头文件总提示找不到。

一开始总不明白是什么原因,直到把那个头文件改成 绝对路径 后才能正常的编译,才发现可能是这个原因 :(

这个问题耽误了十几分钟,记录一下,以作备忘 :)





2011.5.11 补充1______________________________________________________________

一直没注意 -Wl,-rpath... 的含义,今天搜了一下,知道了 -Wl 是把-rpath 传递为linker的选项;

man gcc
...
-Wl,option
  Pass option as an option to the linker. If option contains commas, it is split into multiple options at the commas.
...





2011.5.11 补充2______________________________________________________________

经常看到Makefile 有一行“CXXFLAGS := $(CFLAGS) -DHAVE_CONFIG_H”

不太明白其中 “-DHAVE_CONFIG_H” 的含义,于是在chinaunix 发了个帖子,由回帧中知道了这项的含义,特记录于下,以作备忘。 ^_^


帖子链接如下:
%3D1#pid10149304


主要回帖内容如下:

一般Makefie里的 -DHAVE_CONFIG_H是作为CFLAGS参数传给gcc的。

gcc [-c|-S|-E] [-std=standard]
[-Dmacro[=defn]...] [-Umacro]

查man手册知,就是通过 gcc -DHAVE_CONFIG , 定义了 HAVE_CONFIG_H 这个宏。
随手找了个bash的源码,截一段: ./lib/tilde/tilde.c bash源码中的源文件~~

#if defined (HAVE_CONFIG_H)
# include
#endif

而这里的config.h文件,是通过configure生成的,里面关于编译环境的一些宏。
通过configure检查出来的这些宏,在做跨平台时使用非常方便。


通常./configure之后都会生成一个config.h,这个文件常常存放编译系统环境有关的一些常数,估计这个HAVE_CONFIG_H就是说有config.h这个文件吧。





2011.5.17 ______________________________________________________________

修改文件后,是否重新编译,看下面的解释:

考虑一下这个 Makefile:
myprog : foo.o bar.o
gcc foo.o bar.o -o myprog

foo.o : foo.c foo.h bar.h
gcc -c foo.c -o foo.o

bar.o : bar.c bar.h
gcc -c bar.c -o bar.o


这是一个非常简单的 Makefile — make,从最上面开始,把上面第一个目的 ‘myprog’ 做为它的主要目标(一个它需要保证其总是最新的最终目标)。给出的规则说明,只要文件‘myprog’比文件 ‘foo.o’ 或 ‘bar.o’ 中的任何一个旧,下一行的命令都将会被执行。





2011.8.2 ______________________________________________________________  

今天看原来的一个Makefile,发现有几个选项不明白意思,-mtune=arm9tdmi,后面改成了-mcpu=54418

查了一下gcc的手册,得知其含义如下:
man gcc
...
ARM Options

These -m options are defined for Advanced RISC Machines (ARM) architectures:
...
-mcpu=name
This specifies the name of the target ARM processor. GCC uses this name to determine what kind of instructions it
can emit when generating assembly code. Permissible names are: arm2, arm250, arm3, arm6, arm60, arm600, arm610,
arm620, arm7, arm7m, arm7d, arm7dm, arm7di, arm7dmi, arm70, arm700, arm700i, arm710, arm710c, arm7100, arm7500,
arm7500fe, arm7tdmi, arm7tdmi-s, arm8, strongarm, strongarm110, strongarm1100, arm8, arm810, arm9, arm9e, arm920,
arm920t, arm922t, arm946e-s, arm966e-s, arm968e-s, arm926ej-s, arm940t, arm9tdmi, arm10tdmi, arm1020t,
arm1026ej-s, arm10e, arm1020e, arm1022e, arm1136j-s, arm1136jf-s, mpcore, mpcorenovfp, arm1176jz-s, arm1176jzf-s,
xscale, iwmmxt, ep9312.

-mtune=name
This option is very similar to the -mcpu= option, except that instead of specifying the actual target processor
type, and hence restricting which instructions can be used, it specifies that GCC should tune the performance of
the code as if the target were of the type specified in this option, but still choosing the instructions that it
will generate based on the cpu specified by a -mcpu= option. For some ARM implementations better performance can
be obtained by using this option.
...



[freescale@localhost lib_base]$ make shared
... // 生成 .o 文件

// 下面指令将很多的 .o 文件打包成 .a 文件
m68k-linux-gnu-ar cr libbase.a base_cfo.o base_data_transform.o base_log.o base_public.o base_sleep.o
install -d ./lib
rm -f ./lib/libbase.a
install -m 644 libbase.a ./lib/

// 下面指令将 .a 文件链接成相应的动态链接库文件,就是这步出错的,原因就是 -z defs  ^_^
m68k-linux-gnu-ld -shared -shared-libgcc --warn-common --warn-once -z defs -z combreloc --allow-shlib-undefined -soname=libbase.so.1 -o libbase-1.2.0.so --whole-archive libbase.a
libbase.a(base_cfo.o): In function `cfo_get_value_info_from_string':
base_cfo.c:(.text+0x488): undefined reference to `strlen'
...
libbase.a(base_cfo.o): In function `cfo_get_line_num':
base_cfo.c:(.text+0x70): undefined reference to `fseek'
base_cfo.c:(.text+0x7e): undefined reference to `_IO_getc'
...
libbase.a(base_sleep.o): In function `usleep_plus':
base_sleep.c:(.text+0x20): undefined reference to `nanosleep'
make: *** [shared] 错误 1


在Makefile 里把 “-z defs” 去掉就不会报这些错了,再比对 ld 的manual 里 -z defs 的含义:
-z keyword
The recognized keywords are:
...
defs
Disallows undefined symbols in object files. Undefined symbols in shared libraries
are still allowed.

可以知道去掉这个链接(ld)选项 后,在对象文件中(*.o文件) 允许存在未定义的符号,同时在生成的共享库文件里也允许有未定义的符号;

一个问题,未定义的符号不是本来就需要通过共享库去找别的库的嘛?
为什么要特意加一个允许有未定义符号这个选项,这是共享库的本意啊? 
阅读(1159) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~