09-基础篇-一步一步写MakeFile

目录

 

1.单文件编译     

2.多文件编译

2.1 编写

2.2 改进

2.3 改进

2.4 改进

2.5 改进

3.多目录编译

4.动态库与静态库

5.动态库与静态库使用

5.1 静态库

5.2动态库


 

1.单文件编译     

首先我们建立一个hello.c 的程序。

#include 

int main()
{
	printf("hello world!!!\n");
	return 0;
}

       此hello.c文件以ascii码的方式存储于磁盘中,若此程序在要在linux系统运行,比如在ubuntu18.04上运行,必须对其进行编译。

    gcc -o hello hello.c

     运行:

        ./hello

从上面可以看出,我们只用一条命令,可以实现对hello.c的编译,但是假如工程中不仅仅有hello.c,还有很多其它的.c和.h文件呢?

我们能否有种方式,像我们调试windows工程一样,可以一键编译的,答案是有,就是MakeFile,执行一个指令make可以完全所有工作和文件的编译。

2.多文件编译

2.1 编写

如下为此工程的目录结构,对应文件的内存如下

.
|-- Makefile
|-- hello.c
|-- print.c
`-- print.h

 

print.c

#include 
#include "print.h"

int my_print(void)
{
	printf("hello world!!!! ==>>\n");
	return 0;
}

print.h

#ifndef __PRINT_H__
#define __PRINT_H__

int my_print(void);

#endif

hello.c

#include 
#include "print.h"

int main()
{
	my_print();
	return 0;
}

我们要用make完成上面三个文件的编译,并生成hello的可执行二进制文件。

 

makefile组成三要素:目标,依赖, 命令

  targets(目标):prequisities(依赖)
         command(命令,command前面要有一个tab键)

根据以上规则,写出Makefile

hello:hello.o print.o
	gcc hello.o print.o -o hello
hello.o:hello.c print.h
	gcc -c hello.c
print.o:print.c print.h
	gcc -c print.c
clean:
	rm -f *.o hello
​

从上面的Makefile看出,首先print.c和hello.c分别编译和汇编生成print.o,hello.o的机器指令,然后gcc将print.o和hello.o链接成目标程序hello

2.2 改进

将两个中间过程的二制作文件.o文件用OBJ替代

OBJ=hello.o print.o
hello:$(OBJ)
	gcc $(OBJ) -o hello
hello.o:hello.c print.h
	gcc -c hello.c
print.o:print.c print.h
	gcc -c print.c
clean:
	rm -f *.o hello

2.3 改进

省略.h

OBJ=hello.o print.o
hello:$(OBJ)
	gcc $(OBJ) -o hello
hello.o:hello.c
	gcc -c hello.c
print.o:print.c
	gcc -c print.c
clean:
	rm -f *.o hello

2.4 改进

#     $@  : 目标文件 
#     $^   : 所有依赖文件 
#     $<   : 第一个依赖文件
引入部分符号,替代上述的内容,让程序自己去找.c生成对应的.o并将所有的.o链接成目录文件

CC = gcc
TARGETS = hello
OBJ=hello.o print.o
$(TARGETS):$(OBJ)
	$(CC) $^ -o $@
%.o:%.c
	$(CC) -c $<
clean:
	rm -f *.o $@

2.5 改进

进一步根据文件夹内所有的.c文件,推段出所有.o文件。到这一步,就实现了当前目录下的所有文件一键编译。假如我们想将.c文件.h文件分开,该如何做。

CC = gcc
TARGETS = hello
SRC := $(wildcard *.c)
OBJ=$(patsubst %.c,%.o,$(SRC))

$(TARGETS):$(OBJ)
	$(CC) $^ -o $@
%.o:%.c
	$(CC) -c $<
clean:
	rm -f *.o $@

 

3.多目录编译

如下目录,我们重新创建了3个目录,src用于放所有c文件,obj用于放所有的.o文件,inc用于放所有的.h文件。

.
|-- Makefile
|-- inc
|   `-- print.h
|-- obj
`-- src
    |-- hello.c
    `-- print.c

1.加入CFLAGS用于让c文件转化成.o时,能够知道所有的.h文件在哪个位置

2.在生成.o文件的命令后面加了-o $@ 

此举的目的是为了让所有.o文件生成到obj目录

3.src和inc和obj的位置均用相应的宏指定

TARGETS = hello
DIR_INC = ./inc
DIR_SRC = ./src
DIR_OBJ = ./obj
CC = gcc

SRC := $(wildcard  ${DIR_SRC}/*.c)
OBJ := $(patsubst ${DIR_SRC}/%.c,$(DIR_OBJ)/%.o,$(SRC))

INCLUDES	= -I$(DIR_INC)
CFLAGS += $(INCLUDES)

$(TARGETS):$(OBJ)
	$(CC) $^ -o $@
	
$(DIR_OBJ)/%.o:$(DIR_SRC)/%.c
	$(CC) $(CFLAGS) -c $< -o $@
clean:
	rm -f $(TARGETS)
	rm -f $(DIR_OBJ)/*.o

 

4.动态库与静态库

另外在实际的开发过程中,一整个系统,不可能是一个公司能够做完成,A公司和B公司同时做项目,一般A公司不可能对B公司开源自己的代码,因而需要有一种方式将代码屏蔽,而只将功能抽象出来,这种方法就是库。

库有动态库和静态库。

静态库:
扩展名为.a,函数库通常扩展名为libxxx.a
这类库在编译的时候会直接整合到目标程序中,所以利用静态函数库编译成的文件会比较大,这类函数库最大的优点就是编译成功的可执行文件可以独立运行,而不再需要向外部要求读取函数库的内容;但是从升级难易度来看明显没有优势,如果函数库更新,需要重新编译。

动态库
扩展名为.so,函数库通常名为libxxx.so
与静态函数库被整个捕捉到程序中不同,动态函数库在编译的时候,在程序里只有一个“指向”的位置而已,也就是说当可执行文件需要使用到函数库的机制时,程序才会去读取函数库来使用;也就是说可执行文件无法单独运行。这样从产品功能升级角度方便升级,只要替换对应动态库即可,不必重新编译整个可执行文件。

下面我们来生成动态库与静态库,目录如下,我们将print_lib.c的内容打包成库。

.
|-- Makefile
|-- inc
|   `-- print_lib.h
|-- obj
`-- src
    `-- print_lib.c

print_lib.c

#include 
#include "print_lib.h"

int my_print_api(void)
{
	printf("hello world!!!! this is lib ==>>\n");
	return 0;
}

print_lib.h

#ifndef __PRINT_LIB_H__
#define __PRINT_LIB_H__

int my_print_api(void);

#endif

 

Makefile修改:

1.目录文件名改成libxxx.so和libxxx.a, 记住最前面一定有lib标识

2.CFLAGS位置加-fPIC

3.链接过程中一定要加-shared

4.静态库是用AR打包生成

TARGETS = libprint_lib.so
TARGETS_STATIC = libprint_lib.a
DIR_INC = ./inc
DIR_SRC = ./src
DIR_OBJ = ./obj
CC = gcc

SRC := $(wildcard  ${DIR_SRC}/*.c)
OBJ := $(patsubst ${DIR_SRC}/%.c,$(DIR_OBJ)/%.o,$(SRC))

INCLUDES	= -I$(DIR_INC)
CFLAGS += $(INCLUDES)
CFLAGS += -fPIC

$(TARGETS):$(OBJ)
	$(CC)  -shared $^ -o $@
	$(AR) -r $(TARGETS_STATIC) $(DIR_OBJ)/*.o
	
$(DIR_OBJ)/%.o:$(DIR_SRC)/%.c
	$(CC) $(CFLAGS) -c $< -o $@
clean:
	rm -f $(TARGETS_STATIC)
	rm -f $(TARGETS)
	rm -f $(DIR_OBJ)/*.o

 

5.动态库与静态库使用

5.1 静态库

目录如下:

.
|-- Makefile
|-- inc
|   `-- print.h
|-- lib
|   `-- print_lib
|       |-- inc
|       |   `-- print_lib.h
|       `-- lib
|           `-- libprint_lib.a
|-- obj
`-- src
    |-- hello.c
    `-- print.c

加入LDFLAGS,用于在链接过程中指定库路径

TARGETS = hello
DIR_INC = ./inc
DIR_SRC = ./src
DIR_OBJ = ./obj
CC = gcc

SRC := $(wildcard  ${DIR_SRC}/*.c)
OBJ := $(patsubst ${DIR_SRC}/%.c,$(DIR_OBJ)/%.o,$(SRC))

PRINT_LIB_DIR	= ./lib/print_lib/lib
PRINT_INC_DIR	= ./lib/print_lib/inc

INCLUDES	= -I$(DIR_INC)
INCLUDES	+= -I$(PRINT_INC_DIR)

LDFLAGS = $(PRINT_LIB_DIR)/libprint_lib.a

CFLAGS += $(INCLUDES)


$(TARGETS):$(OBJ)
	$(CC) $^ -o $@ $(LDFLAGS)
	
$(DIR_OBJ)/%.o:$(DIR_SRC)/%.c
	$(CC) $(CFLAGS) -c $< -o $@
clean:
	rm -f $(TARGETS)
	rm -f $(DIR_OBJ)/*.o

 

5.2动态库

   目录如下

.
|-- Makefile
|-- inc
|   `-- print.h
|-- lib
|   `-- print_lib
|       |-- inc
|       |   `-- print_lib.h
|       `-- lib
|           `-- libprint_lib.so
|-- obj
`-- src
    |-- hello.c
    `-- print.c

1.加入LDFLAGS,用于在链接过程中指定库路径

2.在运行前,要将对应.so拷至/usr/lib下

TARGETS = hello
DIR_INC = ./inc
DIR_SRC = ./src
DIR_OBJ = ./obj
CC = gcc

SRC := $(wildcard  ${DIR_SRC}/*.c)
OBJ := $(patsubst ${DIR_SRC}/%.c,$(DIR_OBJ)/%.o,$(SRC))

PRINT_LIB_DIR	= ./lib/print_lib/lib
PRINT_INC_DIR	= ./lib/print_lib/inc

INCLUDES	= -I$(DIR_INC)
INCLUDES	+= -I$(PRINT_INC_DIR)

LDFLAGS = -L $(PRINT_LIB_DIR) -lprint_lib

CFLAGS += $(INCLUDES)


$(TARGETS):$(OBJ)
	$(CC) $^ -o $@ $(LDFLAGS)
	
$(DIR_OBJ)/%.o:$(DIR_SRC)/%.c
	$(CC) $(CFLAGS) -c $< -o $@
clean:
	rm -f $(TARGETS)
	rm -f $(DIR_OBJ)/*.o

 

     

       

 

 

        

 

      
 

 

 

 

 

 

你可能感兴趣的:(Linux开发基础篇,算法)