栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > Java

Makefile相关操作

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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 就是直接强行删除,不作任何提示的意思

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
include "SoTest.h"

void SoTest::func1(){
    printf("func1n");
};

void SoTest::func2(){
    printf("func2n");
};

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 
#include "SoTest.h"

class Test:public SoTest{

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


    void func3(){
        printf("test-func3n");
    }
};


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)
六: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 此处省略
转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1039786.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号