《跟我一起学makefile》学后感

二、关于编译和链接

把大量的 Object File 合成执行文件,这个动作叫作链接(link);链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在 Windows 下这种包叫“库文件”(Library File),也就是 .lib 文件,在 UNIX下,是 Archive File,也就是 .a 文件。

 静态库概念:转载https://www.cnblogs.com/zzdbullet/p/10150323.html

1.库是预编译的目标文件(object  files)的集合,它们可以被链接进程序。静态库以后缀为”.a”的特殊的存档(archive file)存储。

2.标准系统库可在目录/usr/lib与/lib中找到。比如,在类Unix系统中C语言的数序库一般存储为文件/usr/lib/libm.a。该库中函数的原型声明在头文件/usr/include/math.h中。

3.C标准库本身存储为/usr/lib/libc.a,它包含ANS1/ISO标准指定的函数,比如printf。对每一个C程序来说,libc.a都默认被链接。

在tiger.c程序中调用一个数学库libm.a中sin函数。系统默认链接的是libc.a但是没有默认链接libm.a

1>函数sin(),未在本程序中定义也不在默认库’libc.a’中,除非被指定,编译器也不会链接’libm.a’。

2>为使编译器能将sin()链接进主程序‘test.c’,需要提供数学库’libm.a’.。

3>使用方法:gcc  tiger.c /usr/lib/libm.a  -o tiger    或者libm.so           gcc tiger.c /usr/lib/libm.so  -o tiger

则可以编译通过。为了避免在命令行中指定长的路径,编译器为链接函数库提供了快捷的选项“-l”。因此可以使用下面的方法:

gcc  tiger.c -lm –o tiger

注:选项-lNAME使用连接器尝试链接系统库目录中的函数库文件libNAME.a。

#include<stdio.h>
#include<math.h>
int  main()
{
         double  x = 2.0;
         double y = sin(x);
         printf(“the result:%f\n”,y);
         return 0;
}
 

 

二.生成和使用静态库

1.静态库是obj文件的一个集合,通常静态库以”.a”为后缀。静态库由程序ar生成。

2.静态库的优点是可以在不用重新编译程序库代码的情况下,进行程序的重新链接,这种方法节省了编译过程的时间(在编译大型程序的时候,需要花费很长的时间)。静态库的另一个优点是开发者可以提供库文件给使用的人员,不用开放源代码,这是库函数提供者经常采用的手段。

3.通过一个实例来了解如何自己生成静态库和使用静态库

首先生成静态库

1>在test文件夹下有三个文件:main.c ,tiger.c,tiger.h;

a. main.c文件中的内容:

#include<stdio.h>
#include”tiger.h”     //注意include该文件,并不是仅链入.a文件即可
int  main(void)
{
       printf(“sum=%d\n”,add(3,5));
       return 0;       
}

b.tiger.h文件中的内容:

#ifndef __TIGER__
#define __TIGER__

int  add(int  a,int b);

#endif

c.tiger.c文件中的内容

int  add(int  a,int b)
{
        return a+b;
}

2>创建静态库的最基本步骤是生成目标文件tiger.o

gcc  -o tiger.o  -c  tiger.c

3>然后生成静态库libadd.a:

ar  -rcs libadd.a  tiger.o    

其次使用静态库

1>使用gcc命令带上库文件就OK了

   gcc  -o  main main.c  libadd.a

2> 也可以使用命令”-l库名”进行,库名是不包含库函数库和扩展名的字符串。

   gcc  -o  main  main.c -ladd

上面的命令执行完后,系统返回:

Cannot  find –ladd

说明:上面的命令将在系统默认的路径查找add函数库,并把他链接到要生成的目标程序上。系统提示没有找到库文件add,这是由于add库函数没有在系统默认的查找路径下,我们需要认为指定库函数的路径,例如:库文件和当前编译文件在同一目录下:    gcc  -o main  main.c -L  ./  -ladd

系统就能正常生成可执行文件。

说明:在使用-l选项时,-o选项的目的名要在-l链接的库名之前,否则gcc会认为-l是生成的目标而出错。

 


三、makefile

1、读入所有的 Makefile。
2、读入被 include 的其它 Makefile。
3、初始化文件中的变量。
4、推导隐晦规则,并分析所有规则。
5、为所有的目标文件创建依赖关系链。
6、根据依赖关系,决定哪些目标要重新生成。
7、执行生成命令。

 

可执行目标:edit

源文件:8个c文件,3个h文件:main.c   defs.h   kbd.c  command.c  command.h  utils.c display.c  insert.c  buffer.h  search.c   files.c

依赖关系:

edit:main.o    kbd.o  command.o  utils.o display.o  insert.o search.o   files.o

     gcc -o edit main.o kbd.o command.o display.o insert.o search.o file s.o utils.o

 

main.o:  main.c defs.h

     gcc -c main.c

kbd.o:  kbd.c defs.h

    gcc -c kbd.c

command.o:  command.c defs.h command.h

    gcc -c command.c

display.o:  display.c defs.h buffer.h

    gcc -c display.c

insert.o:  insert.c defs.h buffer.h

    gcc -c insert.c

search.o:  search.c defs.h buffer.h

    gcc -c search.c

files.o:files.c defs.h buffer.h command.h

    gcc -c files.c 

utils.o : utils.c defs.h
    gcc -c utils.c
 
clean :
 
    rm edit main.o kbd.o command.o display.o  insert.o search.o files.o utils.o

 


2、使用变量的makeflie

 
OBJS =  main.o    kbd.o  command.o  utils.o display.o  insert.o search.o   files.o

edit:$(OBJS)

     gcc -o edit $(OBJS)

main.o:  main.c defs.h

     gcc -c main.c

kbd.o:  kbd.c defs.h

    gcc -c kbd.c

command.o:  command.c defs.h command.h

    gcc -c command.c

display.o:  display.c defs.h buffer.h

    gcc -c display.c

insert.o:  insert.c defs.h buffer.h

    gcc -c insert.c

search.o:  search.c defs.h buffer.h

    gcc -c search.c

files.o:files.c defs.h buffer.h command.h

    gcc -c files.c 

utils.o : utils.c defs.h
    gcc -c utils.c
 
clean :
 
    rm edit $(OBJS)
 

3、让makefile自动推导(如何优化.o依赖的.c文件,以及gcc编译.c文件)

 
只要 make 看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,如果 make 找到一个 whatever.o,那么 whatever.c,就会是 whatever.o 的依赖文件。并且 gcc -c whatever.c 也会被推导出来,
 
OBJS =  main.o    kbd.o  command.o  utils.o display.o  insert.o search.o   files.o
edit : $(objects)
    gcc -o edit $(objects)这一步不能省略吗?.o文件不会自动被编译成可执行文件
 
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
 
.PHONY : clean
clean :
    rm edit $(objects)

 

4、如何优化.o依赖的h文件呢

OBJS = main.o    kbd.o  command.o  utils.o display.o  insert.o search.o   files.o

edit : $(objects)
    cc -o edit $(objects) 把所有的.o文件链接成执行文件不是隐含规则?
 
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h 这样的优化可有可无
 
.PHONY : clean
clean :
    rm edit $(objects)

 

四、在习题中学习

(1)同一目录中根据5个文件编写makefile

//main.c

#include<stdio.h>
//#include"visit.h"
//#include"study.h"

int main(void)
{
    printf("main.c");
    visit();
    study();
    
    return 0;
}

//visit.c
#include<stdio.h>
void visit()
{
    printf("visit friend today\n");
}

//study.c
#include<stdio.h>
void study()
{
    printf("study embedded system today\n");
}

//visit.h
void visit();

//study.h
void study();
 

如何写makefile呢??很多方式

##########################################
#第一种方式
#a.out:main.o visit.o study.o
#    gcc -o a.out main.o visit.o study.o
#    
#main.o:main.c visit.h study.h
#    gcc -c main.c -o main.o
#visit.o:visit.c visit.h
#    gcc -c visit.c -o visit.o
#study.o:study.c study.h
#    gcc -c study.c -o study.o
#    
#clean:
#    rm -rf a.out main.o visit.o study.o
###############################################

 

#########################################
#第二种方式:不知多少为何,这种方式就是出错误,隐含规则好像有问题
TARGET = a.out
OBJ = study.o main.o visit.o  

$(TARGET):$(OBJ)
    gcc -o $(TARGET) $(OBJ)
    
visit.o:visit.h
study.o:study.h

    
clean:
    rm -rf $(TARGET) $(OBJ)
##############################################

#########################################
#第三种方式
#include test1.mk
#extern TARGET 
TARGET=A.OUT
OBJ = study.o main.o visit.o  
SRCS= main.c study.c visit.c

$(TARGET):$(OBJ)
    gcc $^ -o $@
    
visit.o:visit.h
study.o:study.h

%.o:%.c           理论上这两句话是不需要写的,因为隐含规则会自动区编译,可以通过make -p查看隐含规则
    gcc $< -c
    
clean:
    rm -rf $(TARGET) $(OBJ)
##############################################

#第第四种方式:引用外部makefile:include test1.mk

(2)如果不在同一目录中,又该如何处理呢?

在 Makefile 中,我们的依赖关系可能会需要包含一系列的头文件,比如,如果我们的 main.c 中有一句“#include "defs.h"”,那么我们的依赖关系应该是:
 
main.o : main.c defs.h
 
但是,如果是一个比较大型的工程,你必需清楚哪些 C 文件包含了哪些头文件,并且, 你在加入或删除头文件时,也需要小心地修改 Makefile,这是一个很没有维护性的工作。为了避免这种繁重而又容易出错的事情,我们可以使用 C/C++编译的一个功能。大多数的 C/C++编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依 赖关系。例如,如果我们执行下面的命令:
cc -M main.c
 
其输出是:
main.o : main.c defs.h
于是由编译器自动生成的依赖关系,这样一来,你就不必再手动书写若干文件的依赖关 系,而由编译器自动生成了。需要提醒一句的是,如果你使用 GNU 的 C/C++编译器,你得用 “-MM”参数,不然,“-M”参数会把一些标准库的头文件也包含进来。
gcc -M main.c 的输出是:
 
main.o: main.c defs.h /usr/include/stdio.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h \
/usr/include/bits/types.h /usr/include/bits/pthreadtypes.h \
/usr/include/bits/sched.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/include/bits/wchar.h /usr/include/gconv.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h \
/usr/include/bits/stdio_lim.h
 
 
gcc -MM main.c 的输出则是:
main.o: main.c defs.h
 
那么,编译器的这个功能如何与我们的 Makefile 联系在一起呢。因为这样一来,我们 的 Makefile 也要根据这些源文件重新生成,让 Makefile 自已依赖于源文件?这个功能并不现实,不过我们可以有其它手段来迂回地实现这一功能。GNU 组织建议把编译器 为每一个源文件的自动生成的依赖关系放到一个文件中为每一个“name.c”的文件都生成一个“name.d”的 Makefile 文件[.d]文件中就存放对应[.c]文件的依赖关系。于是,我们可以写出[.c]文件和[.d]文件的依赖关系,并让 make 自动更新或自成[.d]文件,并把其包含在我们的主 Makefile 中,这样,我们就可以自动化地生成每个文件的依赖关系了。 这里,我们给出了一个模式规则来产生[.d]文件:
%.d: %.c
@set -e; rm -f $@; \
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
总而言之,这个模式要做的事就是在编译器生成的依赖关系中加入[.d]文件的依赖,即
把依赖关系:
 
main.o : main.c defs.h
转成:
main.o main.d : main.c defs.h
 

目录结构
|
|__->main.c
|__head
|    |__->visit.h  study.h
|__->study.c 
|__->visit.c
|__->Makefile

//Makefile
TGT = a.out
SRCS = main.c test.c foo.c
指定交叉编译环境
#CROSS_COMPILER = arm-linux-
CC = $(CROSS_COMPILER)gcc
CFLAGS=-I$(HEAD_DIR)    //编译选项
CUR_DIR = $(shell pwd)
HEAD_DIR = $(CUR_DIR)/head


$(TGT):$(SRCS:.c=.o)
    $(CC) $^ $(CFLAGS)-o $@

%.o:%.c
    $(CC) $< -c   
#下面的规则用来说明,头文件的更新,应该使得依赖于它的文件被重新生成
%.d:%c 
    $(CC)n -MM $< >$@
#下面的一行用来告诉make,将上面的模式规则中的命令执行结果包含进当前文件(下面三行作用一样)
#include $(SRCS:.c=.o)
#-include $(SRCS:.c=.o)
sinclude $(SRCS:.c=.o)

.PHONY:clean:
    rm -f $(TGT) $(SRCS:.c=.o) $(SRCS:.c=.d)

(3)如果按照下面的目录结构存放文件,请改写Makefile文件。
bin:存放生成的可执行文件 
obj:存放.o文件 
include:存放visit.h、study.h 
src:存放main.c、visit.c、study.c和Makefile.

目录结构
|__src
|      |__->main.c visit.c study.c

|__include
|    |__->visit.h  study.h
|__obj 
|__bin
|__->Makefile

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LuckyDog0623

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值