Makefile相关操作

一:Make基本介绍

(1)什么是Makefile

 (2)什么是cmake

(3)重点

 二:基本语法和常用命令

(1)基本语法:

         -------------------------------------------------------------------------------

          目标 : 依赖

                  命令

                  ...
         -------------------------------------------------------------------------------

 默认只执行第一个目标,再寻找依赖(这个依赖可以是其他的目标),最后执行命令。

例如:下面的makefile文件,命令行输入make,就会自动寻找makefile这个文件,执行第一个目标a,输出hello world和ls的命令和结果。如果不想在控制台看到命令,可以用@ls ./这种方法。

在命令行输入make clean,执行指定目标,这种方法以后是为了方便清除已有的输出文件。

a:
	echo "hello world"
	ls ./
clean:
	echo "hello clean"

(2)编译流程详解

编译流程写在了上一章。注意以下代码,这种分开写的模式,可以只编译对应有修改的部分。


# 这样好吗 ?
# 这样不好,这样不讲武德

# 第一次编译两小时
# 第二次编译五分钟
# 这样分开来写,保证只编译有改动的代码

#calc:
#	gcc add.cpp sub.cpp multi.cpp calc.cpp -o calc



calc:add.o sub.o multi.o
	gcc add.o sub.o multi.o calc.cpp -o calc

add.o:add.cpp
	gcc -c add.cpp -o add.o

sub.o:sub.cpp
	gcc -c sub.cpp -o sub.o

multi.o:multi.cpp
	gcc -c multi.cpp -o multi.o

(3)变量详解

TARGET=calc

OBJ=add.o sub.o multi.o calc.o

$(calc) 表示变量的替换操作

OBJ=add.o sub.o multi.o calc.o
TARGET=calc

$(TARGET):$(OBJ)
	gcc $(OBJ) -o $(TARGET)

add.o:add.cpp
	gcc -c add.cpp -o add.o

sub.o:sub.cpp
	gcc -c sub.cpp -o sub.o

multi.o:multi.cpp
	gcc -c multi.cpp -o multi.o

calc.o:calc.cpp
	gcc -c calc.cpp -o calc.o

clean:
	rm -rf *.o calc

(4)几种特殊变量

一般常用的包括$^表示当前的依赖,$@当前目标文件的完整名称

$*当前不包括扩展名的目标文件,和$@的意思相反。

linux下的删除命令 rm -rf 强制删除文件包括递归目录。

-r 就是向下递归,不管有多少级目录,一并删除

-f 就是直接强行删除,不作任何提示的意思

命令行显示中,有波浪号:CLi@PC-CLI-1 ~/develop/docbook

其中,此处的波浪号~,含义是:

表示用户根目录,等价于$HOME

OBJ=add.o sub.o multi.o calc.o
TARGET=calc

$(TARGET):$(OBJ)
	gcc $^ -o $@

add.o:add.cpp
	gcc -c $^ -o $@

sub.o:sub.cpp
	gcc -c $^ -o $@      // $^就表示这里不重复的目标文件sub.cpp,$@就表示目标文件的完整名称sub.o

multi.o:multi.cpp
	gcc -c $^ -o $@

calc.o:calc.cpp
	gcc -c $^ -o $@

clean:
	rm -rf *.o $(TARGET)
OBJ=add.o sub.o multi.o calc.o
TARGET=calc

$(TARGET):$(OBJ)
	$(CXX) $^ -o $@

add.o:add.cpp
	$(CXX) -c $^ -o $@

sub.o:sub.cpp
	$(CXX) -c $^ -o $@

multi.o:multi.cpp
	$(CXX) -c $^ -o $@

calc.o:calc.cpp
	$(CXX) -c $^ -o $@

clean:
	$(RM) *.o $(TARGET)

show:
	echo $(AS)
	echo $(CC)
	echo $(CPP)
	echo $(CXX)
	echo $(RM)

 三:伪目标和模式匹配

(1)伪目标

为什么要声明伪目标?

答:如果当前文件夹里有个clean文件,那么使用make clean命令就会自动寻找当前文件夹的clean文件,不执行当前makefile的目标了。

(2)模式匹配

关于文件查找:

 这两个可以一起用,表示先获取当前目录下的所有的.cpp文件,再将.cpp文件替换成.o的文件名,例如如下显示:

 关于模板替换:

%目标:%依赖:目标和依赖相同的部分可用%来匹配,例如下面代码中

OBJ=$(patsubst %.cpp,%.o,$(wildcard ./*.cpp))  // 找到当前目录下的cpp文件,并替换成.o,显示为add.o sub.o multi.o

$(TARGET):$(OBJ)

...

表示自动寻找OBJ的依赖add.o sub.o multi.o,它是下面的目标,根据%目标:%依赖,作出替换即可,替换的结果就是我们这页第一个代码框的代码。


#伪目标 .PHONY:clean
#声明目标为伪目标之后,makefile将不会判断目标是否存在或该目标是否需要更新
.PHONY:clean show


#模式匹配 %目标:%依赖
#目标和依赖相同部份,可用%来通配
#%.o:%.cpp


#wildcard				$(wildcard  ./*.cpp) 获取当前目录下所有的.cpp文件
#patsubst 				$(patsubst  %.cpp,%.o,./*.cpp) 将对应的cpp 文件名替换成 .o 文件名



#OBJ= sub.o multi.o calc.o add.o
OBJ=$(patsubst %.cpp,%.o,$(wildcard ./*.cpp))
TARGET=calc


$(TARGET):$(OBJ)
	$(CXX) $^ -o $@

%.o:%.cpp
	$(CXX) -c $^ -o $@

clean:
	$(RM) *.o $(TARGET)

show:
	#echo $(AS)
	#echo $(CC)
	#echo $(CPP)
	#echo $(CXX)
	#echo $(RM)

	#echo $(wildcard  ./*.cpp)
	#echo $(patsubst %.cpp,%.o,$(wildcard ./*.cpp))
	echo $(OBJ)

 四:MakeFile编译动态链接库

 

如何编译出动态链接库呢?

源文件:SoTest.cpp,SoTest.h 使用如下命令编译完又生成了libSoTest.so

SoTest.cpp

//
//
include<iostream>
include "SoTest.h"

void SoTest::func1(){
    printf("func1\n");
};

void SoTest::func2(){
    printf("func2\n");
};

SoTest.h

ifndef INC_0303_SOTEST_H
define INC_0303_SOTEST_H


class SoTest {
    public:
        void func1();
        virtual void func2();
        virtual void func3()=0;
};


endif //INC_0303_SOTEST_H
g++ -shared -fPIC SoTest.cpp -o libSoTest.so

---------------------------------------------------------------------------------------------------------------------------------

测试代码Test.c 

#include <iostream>
#include "SoTest.h"

class Test:public SoTest{

public:
    void func2(){
        printf("test-func2\n");
    }


    void func3(){
        printf("test-func3\n");
    }
};


int main() {

    Test t1;
    t1.func1();
    t1.func2();
    t1.func3();

    return 0;
}

使用如下命令编译(将libSoTest.so加载到test.cpp中),其中lSoTest表示指定动态库libSoTest.so,... 发布只需要.so和.h文件

g++ -lSoTest -L./ test.cpp -o test

// 输出
./test

输出结果是 ,可以看到func1函数结果也被加载进去了。

注:以上都是在同一个文件夹里。

为了发布会编译出.so到单独的文件夹,这时候可以编译完成,但是运行的时候会找不到。

这是因为运行的时候会在当前文件夹,系统文件夹里找,因为.so被我们放到自定义的文件夹里了,所以运行会找不到。

 现在有两种解决方案:

(1)将.so文件放到系统库里

 (2)将.so文件目录设置到环境中去,在命令行里输入具体路径如下所示:

DYLD_LIBRARY_PATH=./001    // .so文件所在的路径,这里用的是相对路径,即当前运行文件的相对路径
export DYLD_LIBRARY_PATH

接下来,就可以写makefile一步完成了(用的第一种方法---将.so文件放到系统库里):

test:libSoTest.so
	$(CXX) -lSoTest -L./ test.cpp -o test
	cp libSoTest.so /usr/local/lib/
libSoTest.so:
	$(CXX) -fPIC -shared SoTest.cpp -o libSoTest.so

clean:
	$(RM) *.so test

 五:MakeFile编译静态链接库

Windows的静态链接库是.lib文件,Linux下是.a文件。静态库中保存着代码中所需要的函数执行过程等,只不过保存的形式是一种二进制文件。(PS:也可以自己制作静态库,例如在Linux中静态库的制作和使用_零下10度C_zjw的博客-CSDN博客_linux静态库的生成与使用

 可以使用如下代码编译用来生成libaTest.a:(aTest.app--->libaTest.a),我们手动放到./002文件夹中。

g++ -c aTest.cpp -o aTest.o
ar -r libaTest.a aTest.o

然后我们使用./001和./002文件夹里的libaTest.a和libSoTest.so重新编译进入main.cpp中.

以下代码会在这两个路径中分别找到对应的.so和.a文件,并加载到main.cpp里,然后编译成main的可执行文件。

g++ -lSoTest -L./001 -laTest -L./002 main.cpp -o main

// 测试
./main

 我们可以写makefile来加载了:

TARGET=main
LDFLAGS=-L./001 -L./002
LIBS=-lSoTest -laTest

$(TARGET):
	$(CXX) $(LDFLAGS) $(LIBS) main.cpp -o $(TARGET)

clean:
	$(RM) $(TARGET)

注 -I./inc 之类的表示编译头文件 

六:makefile中通用部份做公共头文件

如下的代码所示,为设置的makefile公共头文件。这里需要注意的是OBJ:=和OBJ=的区别;

因此如果需要二次调用变量,必须使用OBJ:=,不然就会null。

#公共

SOURCE=$(wildcard  ./*.cpp ./*.c)
OBJ=$(patsubst  %.cpp,%.o,$(SOURCE))
OBJ:=$(patsubst  %.c,%.o,$(OBJ))

.PHONY:clean

$(TARGET):$(OBJ)
    $(CXX) $^ -o $@


clean:
    $(RM) $(TARGET) $(OBJ)


show:
    echo $(SOURCE)
    echo $(OBJ)

如果要在/001的代码中使用它编译c.cpp,只需要写如下的makefile,把上面的makefile引入进来就可以了。

TARGET=c

include ../makefile

七:makefile中调用shell

shell命令就是编辑器的相关命令,可以直接使用echo shell XXX方法调用。

FILE=abc

A:=$(shell ls ../)
B:=$(shell pwd)
C :=$(shell if [ ! -f $(FILE) ];then touch $(FILE);fi;)

a:
	echo $(A)
	echo $(B)
	echo $(C)

clean:
	$(RM) $(FILE)

八:makefile中的嵌套调用

如果有两个目录./001和./002都有makefile文件,我想再写一个makefile,让它编译这两个目录的makefile文件。可以用如下的写法。

make -C ./001 就会自动寻找./001下的makefile文件进行编译。

#-C 指定工作目录
#$$表示展开shell 中的变量


.PHONY:001 002 clean

DIR=001 002


all:$(DIR)

$(DIR):
	make -C $@


clean:
	echo $(shell for dir in $(DIR);do make -C $$dir clean;done)




all-v1:
	make -C ./001
	make -C ./002

clean-v1:
	make -C ./001  clean
	make -C ./002 clean

九:makefile中的条件判断和循环

(1)条件判断

 

 示例代码如下:


A:=321123

RS1:=
RS2:=
RS3:=
RS4:=


ifeq ($(A),123)
	RS1:=123
else
	ifeq ($(A),321)
		RS1:=321
	else
		RS1:=no-123-321
	endif

endif

ifndef A
	RS3:=yes
else
	RS3:=no
endif

ifndef FLAG
	FLAG:=default-flag
endif


all:
	echo $(RS1)
	echo $(RS3)
	echo flag=$(FLAG)

(2)循环

#循环
#makefile 中只有一个循环 foreach,只支持 GNU Make ,其它平台的make ,可以用shell 中的循环来实现
#可以在循环中逐个的修改值



TARGET:=a b c d
FILE:=$(foreach v, $(TARGET),$v.txt)

all:
	#echo $(TARGET)
	#echo $(foreach v, $(TARGET),$v)
	#touch $(TARGET)
	#touch $(foreach v, $(TARGET),$v.txt)
	#mkdir $(foreach v, $(TARGET),$v_txt)
	#echo $(FILE)

	for v in $(TARGET);\
		do touch $$v.txt;\
	done;\

	$(shell for v in $(TARGET); do touch $$v-txt;done)


clean:
	$(RM) -rf $(TARGET) *txt

十:自定义函数的实现

自定义函数没有返回值。

 示例代码:

#自定义函数,不是真正的函数,本质上是多行命令,放在外面了
#这里的自定义函数,没有返回值

LS1=$(call FUNC3)

define FUNC3
	echo $(shell ls)
	$(RM) a b c d
endef

LS2:=$(call FUNC3)


define FUNC2
	return 123
endef

default:
#	echo $(call FUNC2)
#	echo return 123
	$(call FUNC3)
	$(LS2)
	$(LS1)




A:=123
B:=$(A)

define FUNC1

	echo $(0)

#	echo $(1) $(2)

#	echo func1
#	echo $(A) $(B)
endef

A:=456
all:
	$(call FUNC1,abc,def,$(A))

#	echo $(A)  $(B)

A:=789

十一:Make install 此处省略

十二:常用命令:

(1).SUFFIXES:
.SUFFIXES:  .g  .o

LIBS=gao.o
all: $(LIBS)

.g.o:
    @echo "in .g.o"

.SUFFIXES 和 .a.b 格式可以相互配合, 和 %b:%a 是无法配合的。结果是,in  .g.o

.SUFFIXES: 会删除所有已知的后缀。这样做是为了:  默认后缀不会干扰您的特殊后缀。  不同的make程序具有不兼容的后缀列表和隐式规则

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值