实用的GCC Makefile语法及参数详解

二话不说,先上一个Makefile的源码。

基于下述的Makefile,可以直接执行命令:

编译: make or make -f Makefile all

清除: make clean or make -f Makefile clean

CC = g++

CUR_PATH = $(PWD)

FLAGS = -std=c++11 -O2 -W -Wall
FLAGS += -I/home/project/opencv/build/ -I/home/project/opencv/include/
FLAGS += -I/home/project/opencv/modules/calib3d/include
#FLAGS += ... 其他必要的头文件的路径

LDFLAGS = -L../libopencv_calib3d.a
LDFLAGS += ./libopencv_core.a
LDFLAGS += ./libopencv_dnn.a
LDFLAGS += ./libopencv_core.a
LDFLAGS += ./libopencv_features2d.a
LDFLAGS += ./libopencv_flann.a
LDFLAGS += ./libopencv_gapi.a
LDFLAGS += ./libopencv_highgui.a
LDFLAGS += ./libopencv_imgcodecs.a
LDFLAGS += ./libopencv_imgproc.a
LDFLAGS += ./libopencv_ml.a
LDFLAGS += ./libopencv_objdetect.a
LDFLAGS += ./libopencv_photo.a
LDFLAGS += ./libopencv_stitching.a
LDFLAGS += ./libopencv_video.a
LDFLAGS += ./libopencv_videoio.a
LDFLAGS += ./libade.a
LDFLAGS += ./libIlmImf.a
LDFLAGS += ./libippicv.a
LDFLAGS += ./libippiw.a
LDFLAGS += ./libittnotify.a
LDFLAGS += ./liblibopenjp2.a
LDFLAGS += ./liblibprotobuf.a
LDFLAGS += ./liblibtiff.a
LDFLAGS += -ldl -lm -lpthread -lrt

TARGET = main

SMP_SRCS = test_resize.cpp

OBJS  := $(SMP_SRCS:%.c=%.o)

CFLAGS += $(FLAGS)

MPI_LIBS = $(LDFLAGS)

.PHONY : clean all

all: $(TARGET)
$(TARGET):$(OBJS)
    @$(CC) $(CFLAGS) -o $@ $^ -Wl,--start-group $(MPI_LIBS)  \
    Wl,--end-group,-gc-sections,-g
    @echo "start the compilexxxxxxxxxxxxxxxxxxxxxxx"
    @echo $(MPI_LIBS)
    @echo $@
    @echo $^
    @echo $(CFLAGS)
    @echo $(OBJS)
    @echo $(TARGET)

clean:
    @rm -f *.o main
  1. 分析Makefile的入口在 .PHONY的位置。

Makefile的target默认是文件,何为target?拿make clean举例,clean就可以认为是target。但是这里的clean并不希望是文件,而希望是Makefile中clean标识,所以使用.PHONY来伪造一下。总结就是: .PHONY后面的target表示的是一个伪造的target, 而不是真实存在的target文件。 .PHONY后面的target也是此Makefile所支持的命令。默认只执行make命令时,实际就想当于执行make all.

make则执行”all:”下面的内容,直到出现另外一个标识。

make clean则执行”clean:”下面的内容,直到出现另外一个标识。

注意:标识内容下面的空白(例如@echo $^之前的空白)均是tab,不是空格。

  1. 依赖关系

all: $(TARGET) 或者 $(TARGET):$(OBJS) 这种,由 冒号隔开,即存在依赖关系,表示前面的标识依赖后面的内容。其中$(TARGET) 表示取出TARGET中的值(即main),故 all: $(TARGET)可以翻译为all: main。详细的可以参考下面链接中的说明: makefile文件中的依赖关系理解_墨墨无文的博客-CSDN博客_makefile 依赖

  1. 编译

@$(CC) $(CFLAGS) -o $@ $^ -Wl,–start-group $(MPI_LIBS) -Wl,–end-group,-gc-sections,-g

$(xxx)是取xxx中的值,用前面的内容,替换其中的值,就可以得到正常的编译命令。

  1. 参数说明

1)@

放在整行之前,表示不打印此行内容。举例:@echo “start the compilexxxxxxxxx”,实际打印出来的内容为”start the compilexxxxxxxxx”, 如果没有此@,则打印出两行,分别是:

echo “start the compilexxxxxxxxx”

start the compilexxxxxxxxx

2)-o

生成指定的输出文件。用在生成可执行文件时。默认的时候, gcc 编译出来的文件是 a.out,如果不想使用这个名字,则可以使用此关键字,后面跟你想要的可执行文件的名字即可。本事例使用的名字是-o $@

3)$@

表示目标文件。在此文中,$@的内容即是$(TARGET),即是main。所以-o $@即是-o main.

4) $^

5)–start-group and –end-group

如果有多个静态库文件需要一起编译,那可以使用这两个参数将库包含起来。并且静态库要放在源码的后面,gcc是对文件的放置顺序有要求的。切记切记。此文中源码$^ 放在了库文件$(MPI_LIBS)之前。大致原因是gcc先编译的文件,如果其中没有找到对应的函数,则将这些函数暂存为未解析的符号,然后再编译后面的文件是,从后面的文件中找此未解析符号对应的源码,如果有,则将未解析的符号进行删除。此过程只顺序进行一次,如果反过来,则未解析的符号就找不到对应的源码了,对导致编译错误。相信解释可以参考下面的链接:为什么gcc中’-l’选项的顺序很重要? [重复]_编程黑洞网

–start-group … –end-group:之间的内容只能为文件名或-l选项;为了保证内容项中的符号能被解析,链接器会在所有的内容项中循环查找。这种用法存在性能开销,最好是当有两个或两个以上内容项之间存在有循环引用时才使用。

6)-l

指定静态库的名称(例如-lgcc、 -lgcc_eh 、-lc实际是指文件名为libgcc.a、libgcc_eh.a、libc.a的库),此文中用到了-ldl -lm -lpthread -lrt。

7)-g

告知编译器,在编译的时候,产生调试信息

8)-L

链接外部静态库与动态库的查找路径。编译器会自动解析后面的一个参数。此文中的语句LDFLAGS = -L../libopencv_calib3d.a实际可以拆分为2行即 LDFLAGS = -L. 和 LDFLAGS += ./libopencv_calib3d.a。

9)-std=c++11

使用C++11的标准。

10)-O2

优化级别。分为 -O0 、-O1 、-O2 、-O3。编译器的优化选项的 4 个级别,-O0 表示没有优化, -O1 为默认值,-O3 优化级别最高

11)-I

头文件所在的路径。此文中使用多个-Ixxx,表示有多个头文件的路径。

12)echo

打印命令,可以作为调试时打印一些参数的值。

13)SMP_SRCS:%.c=%.o

将SMP_SRCS中所有的.c替换为.o

参考链接:

Original: https://blog.csdn.net/yangsong4353/article/details/126606513
Author: yangsong4353
Title: 实用的GCC Makefile语法及参数详解

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/702861/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球