Shell腳本——make命令和Makefile文件【轉】


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
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM