一些通用的 Makefile 文件模板

來自:CSDN,作者:Acuity.

鏈接:https://blog.csdn.net/qq_20553613/article/details/90649734

寫在前面

對於 Windows 下開發,很多 IDE 都集成了編譯器,如 Visual Studio,提供了 “一鍵編譯”,編碼完成後只需一個操作即可完成編譯、鏈接、生成目標文件。

Linux 開發與 Windows 不同,Linux 下一般用的的 gcc/g++ 編譯器,如果是開發 ARM 下的 Linux 程序,還需用到 arm-linux-gcc/arm-linux-g++ 交叉編譯器。

Linux 下也可以實現 “一鍵編譯” 功能,此時需要一個編譯腳本“Makefile”,Makefile 可以手動編寫,也可以藉助自動化構建工具(如 scons、CMake)生成。手動編寫 Makefile 是 Linux 和 Windows 程序員的區別之一,一般地一個通用的 Makefile 能夠適合大部分 Linux 項目程序。

3 個 Makefile 模板

2.1 編譯可執行文件 Makefile

VERSION  =1.00
CC   =gcc
DEBUG   =-DUSE_DEBUG
CFLAGS  =-Wall
SOURCES   =$(wildcard ./source/*.c)
INCLUDES   =-I./include
LIB_NAMES  =-lfun_a -lfun_so
LIB_PATH  =-L./lib
OBJ   =$(patsubst %.c, %.o, $(SOURCES))
TARGET  =app

#links
$(TARGET):$(OBJ)
 @mkdir -p output
 $(CC) $(OBJ) $(LIB_PATH) $(LIB_NAMES) -o output/$(TARGET)$(VERSION)
 @rm -rf $(OBJ)
 
#compile
%.o: %.c
 $(CC) $(INCLUDES) $(DEBUG) -c $(CFLAGS) $< -o $@

.PHONY:clean
clean:
 @echo "Remove linked and compiled files......"
 rm -rf $(OBJ) $(TARGET) output

【要點說明】

【1】程序版本

開發調試過程可能產生多個程序版本,可以在目標文件後(前)增加版本號標識。

VERSION = 1.00
$(CC) $(OBJ) $(LIB_PATH) $(LIB_NAMES) -o output/$(TARGET)$(VERSION)

【2】編譯器選擇

Linux 下爲 gcc/g++;arm 下爲 arm-linux-gcc;不同 CPU 廠商提供的定製交叉編譯器名稱可能不同,如 Hisilicon“arm-hisiv300-linux-gcc”。

CC = gcc

【3】宏定義

開發過程,特殊代碼一般增加宏條件來選擇是否編譯,如調試打印輸出代碼。-D 是標識,後面接着的是 “宏”。

DEBUG =-DUSE_DEBUG

【4】編譯選項

可以指定編譯條件,如顯示警告(-Wall),優化等級(-O)。

CFLAGS =-Wall -O

【5】源文件

指定源文件目的路徑,利用 “wildcard” 獲取路徑下所有依賴源文件。

SOURCES =$(wildcard ./source/*.c)

【6】頭文件

包含依賴的頭文件,包括源碼文件和庫文件的頭文件。

INCLUDES =-I./include

【7】庫文件名稱

指定庫文件名稱,庫文件有固定格式,靜態庫爲 libxxx.a; 動態庫爲 libxxx.so,指定庫文件名稱只需寫 “xxx” 部分,

LIB_NAMES =-lfun_a -lfun_so

【8】庫文件路徑

指定依賴庫文件的存放路徑。注意如果引用的是動態庫,動態庫也許拷貝到 “/lib” 或者 “/usr/lib” 目錄下,執行應用程序時,系統默認在該文件下索引動態庫。

LIB_PATH =-L./lib

【9】目標文件

調用 “patsubst” 將源文件(.c)編譯爲目標文件(.o)。

OBJ =$(patsubst %.c, %.o, $(SOURCES))

【10】執行文件

執行文件名稱

TARGET =app

【11】編譯

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

【12】鏈接

可創建一個 “output” 文件夾存放目標執行文件。鏈接完輸出目標執行文件,可以刪除編譯產生的臨時文件(.o)。

$(TARGET):$(OBJ)
 @mkdir -p output
 $(CC) $(OBJ) $(LIB_PATH) $(LIB_NAMES) -o output/$(TARGET).$(VERSION)
 @rm -rf $(OBJ)

【13】清除編譯信息

執行 “make clean” 清除編譯產生的臨時文件。

.PHONY:clean
clean:
 @echo "Remove linked and compiled files......"
 rm -rf $(OBJ) $(TARGET) output

2.2 編譯靜態庫 Makefile

VERSION     =
CC          =gcc
DEBUG   =
CFLAGS  =-Wall
AR   =ar
ARFLAGS     =rv
SOURCES   =$(wildcard *.c)
INCLUDES    =-I.
LIB_NAMES   =
LIB_PATH  =
OBJ         =$(patsubst %.c, %.o, $(SOURCES))
TARGET      =libfun_a

#link
$(TARGET):$(OBJ)
 @mkdir -p output
 $(AR) $(ARFLAGS) output/$(TARGET)$(VERSION).a $(OBJ)
 @rm -rf $(OBJ)

#compile
%.o: %.c
 $(CC) $(INCLUDES) $(DEBUG) -c $(CFLAGS) $< -o $@
  
.PHONY:clean
clean:
 @echo "Remove linked and compiled files......"
 rm -rf $(OBJ) $(TARGET) output

【要點說明】

基本格式與 “編譯可執行 Makefile” 一致,不同點包括以下。

【1】使用到 “ar” 命令將目標文件(.o)鏈接成靜態庫文件(.a)。靜態庫文件固定命名格式爲:libxxx.a。

2.3 編譯動態庫 Makefile

VERSION   =
CC        =gcc
DEBUG     =
CFLAGS    =-fPIC -shared 
LFLAGS   =-fPIC -shared 
SOURCES   =$(wildcard *.c)
INCLUDES  =-I.
LIB_NAMES =
LIB_PATH  =
OBJ       =$(patsubst %.c, %.o, $(SOURCES))
TARGET    =libfun_so

#link
$(TARGET):$(OBJ)
 @mkdir -p output
 $(CC) $(OBJ) $(LIB_PATH) $(LIB_NAMES) $(LFLAGS) -o output/$(TARGET)$(VERSION).so
 @rm -rf $(OBJ)
 
#compile
%.o: %.c
 $(CC) $(INCLUDES) $(DEBUG) -c $(CFLAGS) $< -o $@

.PHONY:clean
clean:
 @echo "Remove linked and compiled files......"
 rm -rf $(OBJ) $(TARGET) output

【要點說明】

基本格式與 “編譯可執行 Makefile” 一致,不同點包括以下。

【1】編譯選項和鏈接選項增加 “-fPIC -shared” 選項。動態庫文件固定命名格式爲 libxxx.so。

Demo

3.1 編譯應用程序

編寫測試例程,文件存放目錄結構如下,頭文件存放在 “include” 目錄,庫文件存放在 “lib” 目錄,源文件存放在 “source” 目錄,Makefile 在當前目錄下。

源碼 1:

/*頭文件*/
#ifndef _FUN0_H_
#define _FUN0_H_
#endif

extern void fun0_printf(void);
extern void fun1_printf(void);

/*源文件*/
#include <stdio.h>
#include "fun0.h"

void fun0_printf(void)
{
    printf("Call \'fun0\'. \r\n");
}

源碼 2:

/*頭文件*/
#ifndef _FUN1_H_
#define _FUN1_H_
#endif

extern void fun1_printf(void);

/*源文件*/
#include <stdio.h>
#include "fun1.h"

void fun1_printf(void)
{
    printf("Call \'fun1\'.\r\n");
}

主函數源碼:

/*源文件*/
#include <stdio.h>
#include "fun0.h"
#include "fun1.h"
#include "fun_lib_a.h"
#include "fun_lib_so.h"

int main(void)
{
    #ifdef USE_DEBUG
        printf("Debug Application startup.\r\n");
    #endif
    
        fun0_printf();
        fun1_printf();
        fun_lib_a_printf();
        fun_lib_so_printf();
        return 0;
}

庫文件,“./lib” 目錄下存放兩個庫文件,一個靜態庫 libfun_a.a,一個動態庫 libfun_so.so。

Makefile 文件即爲 “2.1 節” 的 Makefile 模板。

測試運行:

【如果執行文件提示無 “libfun_so.so”, 則需拷貝“libfun_so.so” 到根目錄下的 “/lib” 或者 “/usr/lib” 目錄下,因爲系統執行程序,默認從該路徑引腳動態庫】

3. 2 生成靜態庫

編寫測試例程,生產的庫文件即爲 “3.1 節” 調用的庫文件(libfun_a.a)。文件存放目錄結構如下:

源文件:

/*頭文件*/
#ifndef _FUN_LIB_A_H_
#define _FUN_LIB_A_H_
#endif

extern void fun_lib_a_printf(void);

/*源文件*/
#include <stdio.h>
#include "fun_lib_a.h"

void fun_lib_a_printf(void)
{
    printf("Call \'fun_lib_a\'.\r\n");
}

Makefile 文件即爲 “2.2 節” 的 Makefile 模板。

編譯生成靜態庫:

3. 3 生成動態庫

編寫測試例程,生產的庫文件即爲 “3.1 節” 調用的庫文件(libfun_so.so)。文件存放目錄結構如下:

源文件:

/*頭文件*/
#ifndef _FUN_LIB_SO_H_
#define _FUN_LIB_SO_H_
#endif

extern void fun_lib_so_printf(void);

/*頭文件*/

#include <stdio.h>
#include "fun_lib_so.h"

void fun_lib_so_printf(void)
{
    printf("Call \'fun_lib_so\'.\r\n");
}

編譯生成動態庫:

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/fYWBI87jpfPP5NPS3bRG4A