Makefile

gcc

c语言
.c文件 → .exe文件,几个过程:

顺序号字符名称功能
1-E预处理把.h .c展开形成一个文件。宏定义直接替换,头文件、库文件直接展开,生成 .i文件
2-s汇编.i文件生成一个汇编代码文件 .s文件
3-c编译.s文件生成一个 .obj文件
4-o链接.o文件链接 .exe文件

预处理: gcc -E hello.c -o helle.i(将hello.c生成hello.i)
编译 : gcc -S hello.i -o hello.s()
汇编 : gcc -c hello.s -o hello.o()
链接: gcc -o hello.o -o hello()


Makefile

1、Makefile规则书写

工程的组成:
一般而言,基本工程复杂版工程目录

  1. 主函数和功能函数 源文件 *.c
  2. 功能函数的头文件―头文件.h宏定义结构体函数声明变量共用体
  3. 库文件目录库文件 .so .a (包装函数)
  4. 执行文件目录:二进制文件编译生成的程序
  5. makefile:工程管理文件(管理上面的所有文件

语法

  • #是注释
  • 显示规则:
目标文件:依赖文件
[tab键位]执行指令
  1. makefile核心:目标和依赖
    依赖:一般指的是你要编译的文件对象,也就是.c目标:一般指的是你要生成的执行文件
    例子:gcc *.c -o main
    依赖:gcc *.c
    目标:main

  2. makefile书写规则

  • 目标:依赖(多文件时,各个依赖文件之间用空格连接)
    目标文件一定要有,依赖文件可以没有
  • <tab键规则>
  • 在代码中的全部表示:
    $@:代表所有目标文件
    $^:代表所有依赖文件

练习

  1. 练习1:makefile版的helloworld
    目标文件一定要存在,依赖文件可以不要
target:  # 目标文件名
	echo hello word

执行的时候使用:make
运行结果:

在这里插入图片描述
如果在each前面+@就只会输出helloword


  1. 练习2:写一个makefile,实现gcc main.c add.c -o main
    目标:
    gcc main.c add. c -o main
// add函数
#includee<stdio.h>
int add (int a , int b)
{
	printf("%d+%d=%d") ;
}

// 主函数
int main (void)
{
	add (12,13) ;
	return 0;
}
main : main. c add.c  // 目标文件:依赖文件
	gcc $^ -o $@ 

编译器,依赖文件,指定生成目标文件


  1. 练习3:写一个Makefile,实现三个数获取最大值
    三个文件:main.c max.c max.h
max.h文件:

#ifndef _MAX_H
#define _MAX_H
#include <stdio.h>
#include <stdlib.h>
	void max (int a ,int b ,int c) ;//函数声明
#endif
max.c文件中:

#include "max.h"
大实现三个数获取最大值*/
void max (int a ,int b ,int c)
{
	/*思路
	int max=a > b ? a : b;
	int maxl=max >c ? max : c;
					(a > b ? a : b) > c ? (a > b ? a : b):c;
	*/
	int max=(a > b ? a : b)> c ? (a > b ? a : b) :c;
	printf("最大值为%d",max);
}
main.c函数里面:

#include "max.h"
int main (int argc ,char **argv)
{
	//调用
	max (atoi(argv [1]) ,atoi(argv [2] ) ,atoi (argv[3]));
	return 0 ;
}

Makefile文件里:

main:main.c max.c
	gcc $* -o $@

运行结果:
在这里插入图片描述


但我们已经对文件执行了make指令,再按一次make后会提示:make:'main' is up to date:表示 这段代码里面你的代码较上一次执行没有修改不需要再次编译

当一个Makefile文件中有多条命令的时候执行make命令要制定执行哪一个
add : main1.c  add.c
	gcc $^ -o $@

ax: main.c max.c
	gcc $^ -o $@

clean:
	rm max add

想要只执行max,执行命令:makc max

在这里插入图片描述


第一个目标文件是我的最终目标,也就是递归的执行

以执行一个编译链接C语言程序为例:他的命令行和最后的执行翻译过程是相反的

hello: hello.o
	gcc hello.o -o hello
hello.o:hello.s
	gcc -c hello.s -o hello.o
hello.s: hello.i
	gcc -s hello.i -o hello.s
hello.i: hello.c
	gcc -E hello.c -o hello.i

将Makefile文件移到和.c文件同一个文件夹,然后执行指令make

在这里插入图片描述

伪目标

伪目标,就是执行指令没有目标文件
伪目标:.phony:

.phony:
clear:
	rm -rf hello.o hello.s hello.i hello

在这里插入图片描述


2、Makefile变量操作

变量种类

变量的种类

  1. 自定义变量:和shell编程一样,默认字符串类型
    命名规则:
    1. 命名规则和c语言的命名规则一致
    2. 大小写敏感
    3. 不使用#
A = hello
B = hello world
C = $(B) lisi # 将B和其他的字符串连接起来,
all:
	echo $A
	echo $B
	echo $c

注意:

  1. 引用变量的时候,和shell编程一样,加上符号$
    如果访问不成功,那么给变量加上括号
  2. makefile不是shell脚本,所以等号两边有空格也可以
  3. 所有的变量类型都是字符串,所以使用的时候不需要的加 " "

例题:将所有的依赖文件全部存储定义到变量里面

C_SOURCE = main1.c add.c
main: $ (C_SOURCE)
	gcc $^ -o $@
clean :
	rm main
  • 系统变量环境
    有些变量是系统预设,可以直接使用常用:
    CC :编译器的名字默认cc等于gcc
    CC = arm-linux-gcc
    RM:册除文件相当于rm -f
C_SOURCE = main1.c add.c
main : $ (C_SOURCE)
	$ (CC) $^ -o $@
clean :
	$ (RM) main
  • 自动化变量
    $^代表所有的依赖文件
    $@代表所有目标文件
C_SOURCE = mainl.c add.c	依赖文件
_BIN = main	执行文件目标
C_CLEAN = clean	删除文件目录

$ (C_BIN):$ (c_sOURCE)
	$(CC) $^ -o $@

$ (C_CLEAN):
	$(RM) $(C_BIN)

伪指令

当使用(RM),使用make clean出现错误make : 'clean' is up to date.
需要makefile加上一个伪指令
伪指令:.PHONY:clean

C_SOURCE = main1.c add.c	依赖文件
C_BIN = main	执行文件目标
CCLEAN = clean  删除文件目录

$ (C_BIN):$ (C_SOURCE)
	$ (CC)$^ -o $@

.PHONY:$(c_CLEAN)
$ (c_CLEAN):
	$(RM) $(C_BIN)

3、Makefile复杂文件

wildcard 与 patsubst

  1. $(wildcard)作用:在某一个路径下寻找对应匹配文件
    用法:$( wildcard argl,arg2 , arg3 )

例如:寻找当前文件下的所有*.c

C_SOUCRE = $(wildcard  *.c)
C_BIN = main
C_CLEAN = clean

$(C_BIN):$(C_SOUCRE)
	$(CC) $^ -o $@
$(c_CLEAN):
	$(RM) $(C_BIN)
  1. $patsubst作用:将所有的.c文件转化为.o文件
    用法$(patsubst %c,%o,依赖文件)
C_SOUCRE = $( wildcard * .c)
C_OBJ = $(patsubst %c,%o,$ (c_soUCRE))
C_BIN = main
C_CLEAN =clean

$(C_BIN):$(c_OBJ)
	$(CC) $^ -o $@ -wall

多目录工程

序号目录名称放置文件
1源文件目录src放置 *.c文件
2头文件目录include放置 *.h头文件
3库文件目录lib放置 *.so文件
4可执行文件bin最终生成的二进制

连接:

参数连接类型
-I工头文件连接(大写的i)
-L库文件目录连接
-l库文件访问(小写的L)

头文件,放在include文件夹里

头文件,放在include文件夹里
#ifndef _SQ_H
#define _SQ_H
#include <stdio.h>
#include <math. h>
	void sqrts ();
#endif

源文件,放在src文件夹里

源文件,放在src文件夹里
#include "sq.h"
void sqrts ()
{
double num;
double a=rand () 8100;
printf ( "a %lf\n " ,a) ;
num=sqrt (a) ;
printf ( "num 8lf\n " ,num) ;
}


主函数源文件,放在src文件夹里
#include "sq.h"
int main (int argc , char **argv)
{
	sqrts () ;
}

当我们gcc调用的时候,会出现头文件出错的问题,需要连接一下头文件用-I

让src文件里的源文件和include文件里的头文件连接

 gcc ./src/*.c -o main -I ./include/

写成Makefile

c_SRC = $( wildcard ./src/*.c)
C_OBJ = $(patsubst %c,%o,$ (c_SRC))
I_DIR = -L ./include
L_DIR =-L ./lib -lm
CBIN = ./bin/main
C_CLEAN = clean

$(C_BIN):$(C_SRC)
	$(cc) $^ -o $@ $(I_DIR) $(L_DIR)
.PHONY:$(C_CLEAN)
$(C_CLEAN):
	$(RM) $(C_BIN)


4、Makefile库的制作

  1. 什么是库?
    库在linux环境中以二进制的形式存储,在编译的需要链接库

  2. 库的种类与格式
    静态库:lib* .a(开头要有lib)和 libada.a
    动态库:lib*.so 和 libadd.so

两种库的特点

静态库:lib**.a

特点:
1. 静态库就是**编译的时候需要静态连接**,直接将库里面的函数加入到你的可执行程序
2. 由于是静态连接,所以在**编译之后不需要静态库即可使用**
相当于到图书馆里借书,把书带走了

动态库:lib**.so

特点:
1. 程序**编译的时候**并没有将库的内容编译,**只是进行了连接**
2. 由于是动态链接,所以在**执行文件**的时候**需要动态库的支持**
相当于到图书馆里看书

静态库制作的步骤

主函数测试数据:main.c
功能函数实现:add.c add.h
将功能函数编译为库文件(二进制文件)

  1. 将功能文件*.c编译成* .o文件
gcc add.c -o add.o -c
  1. 将*.o文件塞入.a文件之中,文件名开头需要添加lib
ar rcs libadd.a add.o
  1. 联合编译
gcc main.c libadd.a -o main
  1. 测试
./ main

在这里插入图片描述

动态库制作的步骤

主函数测试数据:main. c
功能函数实现:add.c add.h
将功能函数编译为库文件(二进制文件)

  1. 将功能函数源文件编译为*.o文件
gcc add.c -o add.o -fPIC -c
  1. 将*.o文件塞入.a文件之中,文件名开头需要添加lib
gcc -shared -fPIC -o libadd.so add.o
  1. 联合编译需要链接动态库
gcc main.c -o main -ladd

4.测试

./main

在这里插入图片描述
当./ main是出现错误: error while loading shared libraries :libadd.so: cannot open shared object file :No such file or directory
解决方案:将对应的动态库拷贝到linux系统中的lib文件中sudo cp libadd.so /lib

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值