1. 程式人生 > >第08章上 makefile

第08章上 makefile

c++ code 命名 test 不改變 ble tex RF 註釋

通常一個大型程序有多個程序模塊文件構成,按照功能劃分,模塊文件分布在不同的目錄中,模塊文件之間需要包含頭文件,函數調用的情況,它們之間存在依賴關系。通常情況下,我麽你編寫程序只是修改了某些文件,並不是更新所有的文件,因此只需要重新編譯修改過的文件然後進行連接就可以了。

通過make工具來實現只編譯改動過的文件,而文件之間的編譯規則和依賴關系定義在一個命名為makefile文件中。

make程序的工作原理:

linux中,文件分為屬性和數據兩部分 ,每個文件有三中時間,分別記錄與文件屬性和文件數據相關的時間:

  1. atime,access time表示訪問文件數據部分的時間,每次讀取文件都會更新改時間,cat等,但是ls不改變
  2. ctime,change time,表示文件屬性或是數據改變的時間,當文件屬性或是數據被修改時,就會更新ctime
  3. 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

常用的偽目標名有:

  1. all,所有需要最終生成的文件都在這裏,如果要最終生成多個,那麽就在這裏定義。
  2. cleam,清空辨已完成的所有目標文件
  3. dist,將打包後的tar文件,在壓縮
  4. install,將編譯好的程序復制到安裝目錄下
  5. printf,打印已經發生改變的文件
  6. tar,將文件大寶
  7. 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 自動化變量

自動化變量,代表一組文件名。此變量值是這組文件名的一個子集,自動化變量相當於對文件名集合循環遍歷一遍。
其含義其實是,在項規則中,下面的這些變量分別指代一些文件。

  1. $@,表示規則中的目標文件名的集合,如果有多個目標文件,$@表示其中的每一個文件名
  2. $<,表示規則中以來文件的第一個文件
  3. $^,表示規則中所有依賴文件的集合。自動去重
  4. $?,表示規則中比目標文件新的以來文件的集合。

通常的用法是:

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