https://blog.csdn.net/twc829/article/details/72729799

make命令是一个常用的编译命令,尤其在C/C++开发中,make命令通过makefile文件中描述源程序之间的依赖关系进行自动编译;

makefile文件是按照规定格式编写,需说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系;

首次执行make命令时,编译所有相关文件,之后再执行make命令时,以增量方式进行编译,即只对修改的源文件相关的目标文件进行编译;

注:许多tarball格式的开源软件,解压后先执行./configure,再执行make,然后执行make install进行安装;

makefile文件支持include,即把一些基本依赖规则写在一个公共文件中,其他makefile文件包含此文件;

通常公共makefile文件命名为common.mk;

一、make命令
make命令后接参数,称为目标;

1 常见目标
make all:编译所有目标

make install:安装已编译的程序

make uninstall:卸载已安装的程序

make clean:删除由make命令产生的文件,通常删除目标文件.o

make distclean:删除由./configure产生的文件

make check:测试刚编译的软件

make installcheck:检查安装的库和程序

make dist:重新打包成packname-version.tar.gz

执行make命令时,需要一个Makefile文件,以告诉make命令如何编译和链接程序;

2 参数
-B:重新建立所有目标

-d:打印调试信息

-C:切换到指定路径下寻找Makefile

-f:将指定文件看做Makefile

-j:同时运行命令的个数,即多线程执行Makefile,后接的个数可由nproc命令返回值来指定

注:nproc命令打印当前进程可用的处理数(线程数);

二、程序的编译和链接
一般在C/C++开发中,首先将源文件编译成目标文件(Windows下.obj文件,Unix下.o文件)——编译compile,再将目标文件合成执行文件——链接link;

三、make命令如何工作?
1 make在当前目录下寻找“Makefile”或“makefile”文件

2 若找到,查找文件中的第一个目标文件.o

3 若目标文件不存在,根据依赖关系查找.s文件

4 若.s文件不存在,根据依赖关系查找.i文件

5 若.i文件不存在,根据依赖关系查找.c文件,此时.c文件一定存在,于是生成一个.o文件,再去执行

四、Makefile文件格式
1 概述
Makefile文件由一系列规则rules构成,每条规则形式如下:

<target>: <prerequisites>
[Tab]<commands>

第一行冒号前为目标,冒号后为前置条件;第二行必须由一个Tab键起首,后接命令;

目标是必须的,不可省略;前置条件和命令是可选的,但两者必须至少存在一个;

每条规则明确两件事——构建目标的前置条件是什么?如何构建?

2 目标target
目标可以是文件名,指明make命令所要构建的对象;也可以是某个操作名称,称“伪目标”;

clean:
rm *.o
以上代码目标是clean,命令是rm *.o;

执行make clean命令,实现对象文件的删除;

为避免设置的伪目标名称在当前路径下有相同名称的文件,make命令发现该名称的文件已存在,便不再构建,也就不执行rm操作的情况发生,先将该名称声明为伪目标,因此make命令不会检查是否存在该名称的文件,每次执行对应的操作;

.PHONY:clean
clean:
rm *.o

若make命令没有指定目标,默认执行Makefile文件中第一个目标;

3 前置条件prerequisites
前置条件通常是一组文件名,用空格隔开;

指定目标是否重新构建的判断标准——只要有一个前置条件不存在或有更新,则该目标需重新构建;

result.txt:source.txt
cp source.txt result.txt
若当前路径下source.txt存在,make result.txt可正常执行,否则需再写一条规则,用于生成source.txt;

source.txt:
echo "This is a source file." > source.txt
source.txt没有前置条件,与其他文件文官,只要该文件不存在,每次执行make source.txt命令都会生成该文件;

若要生成多个文件,写法如下:

source:file1 file2 file3

source是伪目标,只有3个前置条件,没有对应命令;执行make source命令后一次性生成file1 file2 file3文件,比如下写法方便:

make file1
make file2
make file3

4 命令commands
命令表示如何更新目标文件,由一行或多行shell命令组成;

注:

shell命令一定是写在命令中,否则会被make忽略;

每行命令前必须有一个Tab键;

每行命令在一个独立的shell中执行,shell之间没有继承关系,因此上一行为的变量赋值,在下一行无效;

若前后两条命令有共享数据,可写在一行,用分号隔开;

var-kept:
export foo=bar;echo "foo=[$$foo]"

或,在换行符前加反斜杠\进行转义;

var-kept:
export foo=bar;\
echo "foo=[$$foo]"

或,加上.ONSHELL命令;

.ONESHELL:
var-kept:
export foo=bar;
echo "foo=[$$foo]"

五、Makefile文件语法
1 注释
每行以#开头的为注释;

2 回声echoing
正常情况下,make打印每条命令,再执行该命令,称回声;

在命令前加@,关闭回声,即只输出命令的执行结果,出错则停止执行;

注:

前缀-表示命令执行有错,忽略错误,继续执行;

不加前缀输出执行的命令和命令执行的结果,出错则停止执行;

3 通配符
通配符指定一组符合条件的文件;

Makefile通配符与bash一致;

*:任意0个或多个字符

?:任意一个字符

[...]:方括号内任意一个字符

4 模式匹配
make命令允许对文件名进行类似正则运算的匹配,主要用到%;

5 变量和赋值符
自定义变量,使用=赋值;

调用变量,将变量名放在$()中;

Makefile提供四种赋值运算符——=、:=、?=、+=;

(1)=

递归展开赋值,默认赋值方式;

var2=$(var1)
var1="TEST"
all:
echo $(var2)
输出:TEST

(2):=

直接赋值,不会递归展开,若引用的变量不存在,则为空串;

var2:=$(var1)
var1="TEST"
all:
echo $(var2)
输出:(空串)

(3)?=

若未初始化,则赋值;

var1="test"
var1?="TEST"
var2?="TEST"
all:
<span> </span>echo $(var1) and $(var2)
输出:test and TEST

(4)+=

将值追加到现有内容末尾;

var1="TEST"
var1+="test"
all:
<span> </span>echo var1
输出:TESTtest

六、内置变量
make命令提供一系列内置变量,如$(CC)指向当前使用的编译器,$(MAKE)指向当前使用的make工具;

RM:rm -f

AR:ar

CC:cc

CXX:g++

七、自动变量
自动变量的值与当前规则有关;

1 $@

表示当前目标;

2 $<

表示第一个前置条件;

3 $?

表示所有比目标更新的前置条件;

4 $^

表示所有前置条件;

5 $(@D)和$(@F)

$(@D)表示$@的目标名,$(@F)表示$@的文件名;

八、判断
Makefile文件使用bash语法,完成判断;

<条件语句>
<条件为真,执行程序段>
else
<条件为假,执行程序段>
endif

1 条件语句

(1)ifeq 比较两个参数值是否相等

ifeq (arg1, arg2)

ifeq 'arg1' 'arg2'

ifeq "arg1" "arg2"

ifeq 'arg1' "arg2"

ifeq "arg1" 'arg2'

注:参数还可用make函数,如ifeq ($(strip $(foo)),);

(2)ifneq 比较两个参数值是否不等

ifneq (arg1, arg2)

ifneq 'arg1' 'arg2'

ifneq "arg1" "arg2"

ifneq 'arg1' "arg2"

ifneq "arg1" 'arg2'

(3)ifdef 判断变量是否有值

ifdef var

(4)ifndef 判断变量是否无值

ifndef var

举例1:

bar=
foo=$(bar)
ifdef foo
frobozz=yes
else
frobozz=no
endif
#frobozz值为yes

举例2:

foo=
ifdef foo
frobozz=yes
else
frobozz=no
endif
#frobozz值为no
注:以上举例说明ifdef只是测试一个变量是否有值,而非把变量扩展到当前位置!

九、循环
Makefile文件使用bash语法,完成判断;

1 写法一
LIST变量是Makefile变量,引用Makefile变量需使用$()括起来;

而all目标后的命令是shell命令,其中定义的变量也是shell变量,引用shell变量需使用$$作为开头,但shell变量不需括号;

LIST = one two three
all:
for i in $(LIST); do \
echo $$i; \
done

2 写法二
all:
for i in one two three; do \
echo $$i; \
done

十、函数
1 函数的调用语法
函数调用,类似变量使用,语法如下:

$(func args) # 推荐!
# 或
${func args}
其中,args参数之间以","隔开,func和args之间以" "隔开;

2 字符串处理函数
(1)$(subst <from>,<to>,<text>)

字符串替换,将字符串<text>中的<from>转换成<to>,返回替换后的字符串;

(2)$(patsubst <pattern>,<replacement>,<text>)

模式字符串替换,查找字符串<text>中符合模式<pattern>的单词,并替换成<replacement>;

注:<pattern>可包括通配符%,表示任意长度的字符串;

若<replacement>也包括%,则这里的%是<pattern>中%代表的字符串;

(3)$(strip <string>)

去空格,去掉<string>中开头和结尾的空白符,返回去掉空格的字符串;

(4)$(findstring <find>,<in>)

查找字符串,在字符串<in>中查找<find>字符串,若找到则返回<find>,否则返回空字符串;

(5)$(filter <pattern...>,<text>)

过滤,以<pattern>模式过滤<text>字符串中的单词,保留符合模式的单词,返回符合模式的字符串;

可有多个模式,模式之间以" "隔开;

(6)$(filter-out <pattern...>,<text>)

反向过滤,以<pattern>模式过滤<text>字符串中的单词,去掉符合模式的单词,返回不符合模式的字符串;

可有多个模式,模式之间以" "隔开;

(7)$(sort <list>)

排序,给字符串<list>中的单词以升序排序,返回排序后的字符串;

(8)$(word <n>,<text>)

取单词,获取并返回字符串<text>中第<n>个单词(从1开始),若<n>比<text>中的单词数大,则返回空字符串;

(9)$(wordlist <s>,<e>,<text>)

取单词串,获取并返回字符串<text>中第<s>个到第<e>个的单词串;

(10)$(words <text>)

单词个数统计,统计并返回字符串<text>中单词个数;

注:获取字符串中最后一个单词,使用$(word $(words <text>),<text>)

(11)$(firstword <text>)

首单词,获取并返回字符串<text>中第一个单词;

注:等价于$(word 1,<text>)

3 文件名处理函数
(1)$(dir <names...>)

取目录,从文件名序列<names>中获取并返回目录部分;

注:目录部分指最后一个反斜杠之前的部分,若无反斜杠,则返回"./";

(2)$(notdir <names...>)

取文件,从文件名序列<names>中获取并返回文件部分;

注:文件部分指最后一个反斜杠之后的部分;

(3)$(suffix <names...>)

取后缀,从文件名序列<names>中获取并返回各文件的后缀序列,若无后缀,则返回空字符串;

(4)$(basename <names>)

取前缀,从文件名序列<names>中获取并返回各文件的前缀序列,若无前缀,则返回空字符串;

(5)$(addsuffix <suffix>,<names...>)

加后缀,把<suffix>加到<names>中每个单词后面并返回;

(6)$(addprefix <prefix>,<names...>)

加前缀,把<prefix>加到<names>中每个单词前面并返回;

(7)$(join <list1>,<list2>)

连接,将<list2>中的单词对应的加到<list1>中单词之后;

(8)$(wildcard <pattern...>)

拓展通配符,用于定义变量和引用函数时通配符失效的情况,获取符合模式的字符串并返回;

4 其他函数
(1)$(foreach <var>,<list>,<text>)

把<list>中单词逐一取出放到<var>所指定的变量中,再执行<text>所包含的表达式,每次执行<text>都会返回一个字符串,执行完foreach后返回由多个字符串组成、空格隔开的字符串;

### Makefile 内容
targets := a b c d
objects := $(foreach i,$(targets),$(i).o)

all:
@echo $(targets)
@echo $(objects)

### bash 中执行 make
$ make
a b c d
a.o b.o c.o d.o

(2)$(if <condition>,<then-part>,<else-part>)

### Makefile 内容
val := a
objects := $(if $(val),$(val).o,nothing)
no-objects := $(if $(no-val),$(val).o,nothing)

all:
@echo $(objects)
@echo $(no-objects)

### bash 中执行 make
$ make
a.o
nothing

(3)$(shell <shell-command>)

执行一个shell命令,将执行结果作为函数返回;

(4)make控制函数

$(error <text>):产生一个致命错误,Makefile停止执行

$(warning <text>):输出警告信息,Makefile继续执行

(5)$(call <expression>,<para1>,<para2>,...)

<expression>表达式中的变量,如$(1)、$(2)、...等被参数<para1>、<para2>、...依次取代,<expression>的返回值就是call函数的返回值;

### Makefile 内容
log = "====debug====" $(1) "====end===="

all:
@echo $(call log,"正在 Make")

### bash 中执行 make
$ make
====debug==== 正在 Make ====end====

十一、Makefile实例
1 编译C语言项目
edit : main.o kbd.o command.o display.o
cc -o edit main.o kbd.o command.o display.o

main.o : main.c defs.h
cc -c main.c

kbd.o : kbd.c defs.h command.h
cc -c kbd.c

command.o : command.c defs.h command.h
cc -c command.c

display.o : display.c defs.h
cc -c display.c

clean :
rm edit main.o kbd.o command.o display.o

.PHONY: edit clean

2 公共Makefile文件common.mk
#This is the common part for makefile

SOURCE := $(wildcard *.c) $(wildcard *.cc) $(wildcard *.cpp)
OBJS := $(patsubst %.c,%.o,$(patsubst %.cc,%.o,$(patsubst %.cpp,%.o,$(SOURCE))))
DEPS := $(patsubst %.o,%.d,$(OBJS))
MISSING_DEPS := $(filter-out $(wildcard $(DEPS)),$(DEPS))
CPPFLAGS += -MD

.PHONY : everything objs clean veryclean vc rebuild ct rl

everything : $(TARGETS)

objs : $(OBJS)

clean :
@$(RM) *.o
@$(RM) *.d

veryclean: clean
@$(RM) $(TARGETS)
@$(RM) cscope.out
@$(RM) core*

vc: veryclean

ct:
@$(RM) $(TARGETS)

rl: ct everything

rebuild: veryclean everything

ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS) :
@$(RM) $(patsubst %.d,%.o,$@)
endif

-include $(DEPS)

3 Makefile文件编写训练
(1)准备工作

准备三个文件file1.c、file2.c、file2.h:

file1.c:

#include <stdio.h>
#include "file2.h"
int main()
{
printf("print file1\n");
File2Print();
return 0;
}
file2.c:

#include "file2.h"
void File2Print()
{
printf("Print file2\n");
}
file2.h:

#ifndef FILE2_H_
#define FILE2_H_

#ifdef __cplusplus

extern "C" {

#endif

void File2Print();

#ifdef __cplusplus

}

#endif

#endif

(2)Makefile文件基础编程

helloworld: file1.o file2.o
gcc file1.o file2.o -o helloworld

file1.o: file1.c file2.h
gcc -c file1.c -o file1.o

file2.o: file2.c file2.h
gcc -c file2.c -o file2.o

clean:
rm -rf *.o helloworld
注:

gcc命令中,-c参数将后接文件(.c或.cc)编译成目标文件(.o),-o参数指定输出文件名(默认为.o文件);

(3)Makefile文件进阶编程一——使用变量

OBJS = file1.o file2.o
CC = gcc
CFLAGS = -Wall -O -g

helloworld : $(OBJS)
$(CC) $(OBJS) -o helloworld

file1.o : file1.c file2.h
$(CC) $(CFLAGS) -c file1.c -o file1.o

file2.o : file2.c file2.h
$(CC) $(CFLAGS) -c file2.c -o file2.o

clean:
rm -rf *.o helloworld
注:

gcc命令中,-Wall -O -g参数用于配置编译器,-Wall参数输出所有警告信息,-O参数在编译时优化,-g参数编译debug版本;

ARFLAGS:ar命令的参数

CFLAGS:C语言编译器的参数

CXXFLAGS:C++语言编译器的参数

(4)Makefile文件进阶编程二——使用函数

CC = gcc
XX = g++
CFLAGS = -Wall -O –g
TARGET = ./helloworld

%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@

%.o:%.cpp
$(XX) $(CFLAGS) -c $< -o $@

SOURCES = $(wildcard *.c *.cpp)
OBJS = $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCES)))

$(TARGET) : $(OBJS)
$(XX) $(OBJS) -o $(TARGET)
chmod a+x $(TARGET)

clean:
rm -rf *.o helloworld
注:

$(wildcard *.c *.cpp)函数获取并返回所有以.c或.cpp结尾的文件列表;

$(patsubst %.c,%o,$(SOURCE))函数获取所有以.c结尾的字符串并替换成以.o结尾的新的文件列表;

---------------------
作者:twc829
来源:CSDN
原文:https://blog.csdn.net/twc829/article/details/72729799
版权声明:本文为博主原创文章,转载请附上博文链接!

Shell脚本——make命令和Makefile文件【转】的更多相关文章

  1. linux 执行远程linux上的shell脚本或者命令以及scp 上传文件到ftp--免密码登陆

    场景:在linux A 上执行Linux B上的shell脚本和命令 步骤1.设置ssh免登陆 1.SSH无密码登录 # 本地服务器执行(A机器):生成密钥对 ssh-keygen -t dsa -P ...

  2. 简介make命令和makefile文件

    一.为什么要用到 make 命令和 makefile 文件 在 Linux 下编写一个程序,每次编译都需要在命令行一行一行的敲命令.如果是一个很小的程序还好说,命令不怎的复杂,编译速度也挺快,但是对于 ...

  3. make命令和makefile文件

    make命令和makefile文件的结合提供了一个在项目管理领域十分强大的工具,它不仅常被用于控制源代码的编译,而且还用于手册页的编写以及将应用程序安装到目标目录. makefile文件由一组依赖关系 ...

  4. Shell脚本调用ftp上传文件

    Shell脚本调用ftp上传文件 1.脚本如下 ftp -n<<! open x.x.x.x ###x.x.x.x为ftp地址 user username password ###user ...

  5. shell 脚本控制命令的执行顺序

    &&,||,(),{},& 五个符号的运用shell脚本执行命令的时候,有时候会依赖于前一个命令是否执行成功.而&&和||就是用来判断前一个命令执行效果的. 也 ...

  6. Makefile Shell 脚本;sed命令

    1. 在Makefile中想使用shell脚本,需要添加"@"符号,例如: @if [ -d xxx ]; then \                        //-d 判 ...

  7. Linux Shell脚本Ldd命令原理及使用方法

    1.首先ldd不是一个可执行程序,而只是一个shell脚本2.ldd能够显示可执行模块的dependency,其原理是通过设置一系列的环境变量如下:LD_TRACE_LOADED_OBJECTS.LD ...

  8. Shell 脚本常用命令

    Shell脚本是Linux开发工作中常用的工具,但是我一直没有找到一个适合自己的简明扼要的HandBook.在工作过程中整理了一下,贴在这里已备查看. 1           Shell中的特殊符号 ...

  9. linux shell脚本、命令学习

    1,echo "test" > test.txt    输出重定向到text.txt,文件不存在就创建 echo "test" >> test ...

随机推荐

  1. 初始化构建React+Ts项目时出现:Module build failed (from ./node_modules/css-loader/dist/cjs.js): CssSyntaxError

    具体错误 ERROR in ./src/index.tsx Module build failed (from ./node_modules/css-loader/dist/cjs.js): CssS ...

  2. 阿里云ACA—— I

    ACA 阿里云大数据助理工程师 阿里云大学 大数据助理工程师课程笔记分享, 本资料仅供个人学习,不允许用作商业用途,侵权必删 == Alibaba Cloud Certification Associ ...

  3. Python日记(二):Python之禅

    The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Si ...

  4. 烂笔头@WP 的博文仅供自己学习的备忘录

    前记:本博主的博文仅供自己学习的备忘录. 说明:很久未用博客,登录密码已忘记,费劲找回来,特写本博文申明.因为,今天邮件收到一条博文的评论,有谩骂本博主之意,甚觉委屈.所以,写以下文字说明“1.我的博 ...

  5. Codeforces C. A Simple Task(状态压缩dp)

    题目描述:  A Simple Task time limit per test 2 seconds memory limit per test 256 megabytes input standar ...

  6. readme.txt 为什么我们不重视

    1.很多源码项目下面都有编译说明,readme 2.我们的电脑上每个目录都有很多文件,我们似乎可以建立一个readme.txt 知道是什么 节约一些时间

  7. 题解 洛谷P1281 【书的复制】

    蒟蒻的\(DP\)很菜,\(SO\)我准备上一套二分的玄学操作 一.简单的二分答案 二分主要是用来解决一些最值问题,它可以有效的优化暴力,使复杂度减少到\(O(logn)\). 我先给大家介绍一下二分 ...

  8. 《转》BAT批处理教程

    第一章 批处理基础第一节 常用批处理内部命令简介 批处理定义:顾名思义,批处理文件是将一系列命令按一定的顺序集合为一个可执行的文本文件,其扩展名为BAT或者CMD.这些命令统称批处理命令.小知识:可以 ...

  9. 二次封装Response类 | 视图类传递参数给序列化类context

    二次封装Response类 源码: class Response(SimpleTemplateResponse): """ An HttpResponse that al ...

  10. LightOJ - 1354 - IP Checking(进制)

    链接: https://vjudge.net/problem/LightOJ-1354 题意: An IP address is a 32 bit address formatted in the f ...