第08章上 makefile
通常一個大型程序有多個程序模塊文件構成,按照功能劃分,模塊文件分布在不同的目錄中,模塊文件之間需要包含頭文件,函數調用的情況,它們之間存在依賴關系。通常情況下,我麽你編寫程序只是修改了某些文件,並不是更新所有的文件,因此只需要重新編譯修改過的文件然後進行連接就可以了。
通過make工具來實現只編譯改動過的文件,而文件之間的編譯規則和依賴關系定義在一個命名為makefile文件中。
make程序的工作原理:
linux中,文件分為屬性和數據兩部分 ,每個文件有三中時間,分別記錄與文件屬性和文件數據相關的時間:
- atime,access time表示訪問文件數據部分的時間,每次讀取文件都會更新改時間,cat等,但是ls不改變
- ctime,change time,表示文件屬性或是數據改變的時間,當文件屬性或是數據被修改時,就會更新ctime
- mtime,modify time,表示文件數據部分被修改的時間,每次修改數據都會更新此時間
make程序粉筆獲取以來文件和目標文件的mtime,對比以來文件的的mtime是否新於目標文件,當新的時候,則需要重新編譯目標文件。
1 makefile基本語法
1.1 基本語法
基本語法為:
目標文件:以來文件
[tab]命令
目標文件是最終要生成的文件,可以是以.o
結尾的目標文件,也可以是可執行文件,也可以是偽目標文件。
依賴文件主要是指申城此規則中的目標文件,需要那些文件。
命令是值此規則中要執行的動作,這些動作是指各種shell命令。
1.2 命令
test1.o:test.c
gcc -c -o test1.o test.c
test2.o:test.c test1.o
gcc -c -o test2.o test2.c test1.o
test2.out:test1.o test2.o
gcc -o test2.out test1.o test2.o
也就是說,最終的編譯指令還是需要我們來編寫的。
1.3 偽目標
偽目標不產生真是的目標文件,只定義了規則。因此不存在以來文件。偽目標文件純粹的執行命令,只要給make指定改為目標名作參數,就能讓為目標名的中命令直接執行。
偽目標名不能與真是文件名同名。但是當同名的時候可以通過.PHONY:偽目標名
.PHONY:clean
clean:
rm ./build/*.o
常用的偽目標名有:
- all,所有需要最終生成的文件都在這裏,如果要最終生成多個,那麽就在這裏定義。
- cleam,清空辨已完成的所有目標文件
- dist,將打包後的tar文件,在壓縮
- install,將編譯好的程序復制到安裝目錄下
- printf,打印已經發生改變的文件
- tar,將文件大寶
- test,測試makefile流程
makeflie中的目標,是以遞歸的方式逐層向上查找目標的。
1.4 變量
makefile中可以定義變量
變量的定義格式為:變量名=值 ,值是一個字符串,多個值之間用空格分開,值已經被當做字符串處理,所以不需要加引號
使用變量的時候:$(變量名)
make定義了一些系統級的變量:
變量名 | 描述 |
---|---|
AR | 打包程序,默認ar |
AS | 匯編語言編譯器 |
CC | C語言編譯器,默認cc |
CXX | C++語言編譯其,默認g++ |
RM | 刪除命令,默認rm -f |
一些參數類型的變量:
變量名 | 描述 |
---|---|
ARFLAGS | 打包程序AR的參數 |
ASFLAGS | 匯編編譯其的參數 |
CFLAGS | C語言編譯器參數 |
CXXFLAGS | c++編譯器參數 |
CPPFLAGS | C預處理器參數 |
LDFLAGS | 鏈接器參數 |
1.5 隱含規則
註釋使用#
1.6 自動化變量
自動化變量,代表一組文件名。此變量值是這組文件名的一個子集,自動化變量相當於對文件名集合循環遍歷一遍。
其含義其實是,在項規則中,下面的這些變量分別指代一些文件。
$@
,表示規則中的目標文件名的集合,如果有多個目標文件,$@
表示其中的每一個文件名- $<,表示規則中以來文件的第一個文件
- $^,表示規則中所有依賴文件的集合。自動去重
- $?,表示規則中比目標文件新的以來文件的集合。
通常的用法是:
test.o:test1.c
$(CC) -o $@ $^
1.7 模糊規則
%用來匹配任意多個非空字符,比如,g%.o
表示以g
開頭,.o
結尾的文件。make,會在當前目錄下使用該規則匹配所有文件:
test.o:%.c
$(CC) -o $@ $^
2 代碼
我們是用make代替start.h
在根目錄下創建一個makefile
文件,然後創建build
文件夾,註意,make並不能自動的去創建文件夾.
然後在該目錄下使用make
命令,完成編譯和刻錄.緊接著手動執行bochs
BUILD_DIR=./build
AS=nasm
NASM_ELF=-f elf
INCLUDE=-I./lib -I ./lib/kernel
ASINCLUDE=-I./boot/include/
CFLAGS=-m32
LDFLAGS=-Ttext 0xc0001500 -m elf_i386 -e main
CC=gcc
# 註意這裏: $(BUILD_DIR)/kernel.o 一定要放在第一個上,因此,這個變量是為連接器準備的.如果不放在第一項,保證錯
OBJ=$(BUILD_DIR)/kernel.o $(BUILD_DIR)/print.o $(BUILD_DIR)/idt.o $(BUILD_DIR)/interrupt.o $(BUILD_DIR)/init.o
# 最終要生成的文件,
all: $(BUILD_DIR)/kernel.bin $(BUILD_DIR)/loader.bin $(BUILD_DIR)/mbr.bin
# 編譯 並刻錄 loader.bin
$(BUILD_DIR)/loader.bin: ./boot/loader.asm
$(AS) -o $@ $^ $(ASINCLUDE)
dd if=$(BUILD_DIR)/loader.bin of=./hd60m.img bs=512 count=4 seek=2 conv=notrunc
# 編譯 並刻錄 mbr.bin
$(BUILD_DIR)/mbr.bin: ./boot/mbr.asm
$(AS) -o $@ $^ $(ASINCLUDE)
dd if=$(BUILD_DIR)/mbr.bin of=./hd60m.img bs=512 count=1 conv=notrunc
# 編譯 print.asm
$(BUILD_DIR)/print.o:./lib/kernel/print.asm
$(AS) $(NASM_ELF) -o $@ $^ $(ASINCLUDE)
# 編譯 idt.asm
$(BUILD_DIR)/idt.o:./kernel/idt.asm
$(AS) $(NASM_ELF) -o $@ $^ $(ASINCLUDE)
# 編譯 interrupt.c
$(BUILD_DIR)/interrupt.o:./kernel/interrupt.c
$(CC) -o $@ $(CFLAGS) -fno-stack-protector -c $^ $(INCLUDE)
# 編譯 init.c
$(BUILD_DIR)/init.o:./kernel/init.c
$(CC) -o $@ $(CFLAGS) -c $^ $(INCLUDE)
# 編譯 main.c
$(BUILD_DIR)/kernel.o:./kernel/main.c
$(CC) -o $@ $(CFLAGS) -c $^ $(INCLUDE)
# 最終鏈接
$(BUILD_DIR)/kernel.bin:$(OBJ)
$(LD) -Ttext 0xc0001500 -m elf_i386 -e main -o ./build/kernel.bin $(OBJ)
dd if=./build/kernel.bin of=./hd60m.img bs=512 count=40 seek=9 conv=notrunc
clean:
rm -rf ./build/*
第08章上 makefile