请原谅我的问题,我是GCC的初学者。我有一个框架项目,其中包含多个子组件的源代码。
结构如下:
Framework/
makefile //Master makefile in root
Component1/
src/
bin/
makefile
Component2/
src/
bin/
makefile
...
...
...
ComponentN/
src/
bin/
makefile
现在ComponentN /中的每个makefile文件,每个目录将在其各自的src /中编译代码,并将.o输出到bin /目录。
但是,根makefile递归搜索所有.o文件,并将它们全部链接到一个名为“ framework”的可执行文件中。
问题:
对于glib,gdbus,gio之类的代码依赖项,在每个组件项目中创建.o对象时,都必须将它们链接一次。
另外,在根目录级别将所有.o链接到一个可执行文件时,我必须再次链接依赖项。
为什么我必须做两次?我对了解内部机制很感兴趣。
根据请求,我将生成* .o文件的单个组件库的makefile放入
CC = gcc
CFLAGS = -g3
LIBS = `pkg-config --cflags --libs glib-2.0`
BINDIR = bin
OUTOBJ = $(addprefix $(BINDIR)/, objex.o)
$(BINDIR)/%.o : %.c
$(CC) -c $< $(CFLAGS) -o $@ $(LIBS)
all: $(OUTOBJ)
$(OUTOBJ): | $(BINDIR)
$(BINDIR):
mkdir $(BINDIR)
.PHONY: clean
clean:
rm bin/*
答案 0 :(得分:1)
目标文件(.o
)是由编译命令创建的,例如
gcc -c -o foo.o foo.c ...
g++ -c -o baz.o baz.cpp ...
-c
表示 compile;不要链接。创建过程中不会发生任何关联
目标文件由编译器提供。您添加到编译中的任何链接选项
命令,例如
gcc -c -o foo.o foo.c -L/my/libs -lbar -lgum
只是被忽略了。
链接选项由链接命令作用,该命令创建一个程序或共享/动态 库,方法是将目标文件和库链接在一起,例如
gcc -o prog foo.o baz.o -L/my/libs -lbar -lgum
gcc -shared -o libfoobaz.so foo.o baz.o -L/my/libs -lbar -lgum
所以:
对于诸如glib,gdbus,gio之类的代码依赖项,我必须在每个组件项目中创建.o对象时将它们链接一次。
不,你不会,你不能。
稍后
鉴于问题makefile,很清楚如何消除
编译食谱中的$(LIBS)
参考,以及阻止您前进的原因。生成文件定义:
LIBS = `pkg-config --cflags --libs glib-2.0`
这是一个错误。这使得$(LIBS)
扩展到
命令:
pkg-config --cflags --libs glib-2.0
是包含所需的两个编译选项的单个字符串
用于编译#include
-s glib-2.0
API的源(由于--cflags
的原因)
以及链接程序或共享库所需的链接选项
针对libglib-2.0
(由于--libs
)。在我的系统上是:
$ pkg-config --cflags --libs glib-2.0
-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -lglib-2.0
其中单独的编译选项将通过以下方式输出:
$ pkg-config --cflags glib-2.0
-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
和单独的链接选项将通过以下方式输出:
$ pkg-config --libs glib-2.0
-lglib-2.0
但是因为这两组选项仅在扩展时一起可用
的$(LIBS)
中,如果不通过
链接选项-lglib-2.0
,它是多余的并且被忽略。
由于您的make工具显然是GNU Make,因此makefile(BTW还不错!)最好写成:
制作文件
CC := gcc
CFLAGS := -g3 $(shell pkg-config --cflags glib-2.0)
BINDIR := bin
SRCS := objex.c
OUTOBJ := $(addprefix $(BINDIR)/, $(SRCS:.c=.o))
.PHONY: all clean
all: $(OUTOBJ)
$(BINDIR)/%.o : %.c
$(CC) -c $< $(CFLAGS) -o $@
$(OUTOBJ): | $(BINDIR)
$(BINDIR):
mkdir -p $(BINDIR)
clean:
$(RM) $(OUTOBJ)
省去了LIBS
并从头开始运行,例如:
$ make
mkdir -p bin
gcc -c objex.c -g3 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -o bin/objex.o
请注意其他一些改进:-
在适用时优先使用不必要的立即扩展(:=
)
递归扩展(=
)。参见6.2 The Two Flavors of Variables
通过make -$(shell command)
直接使用外壳替换-优先于
配方执行。参见8.13 The shell Function。
all
,就像clean
是phony target
而且您需要告诉make这样做是为了避免在某些情况下在其中创建名为all
的文件的陷阱
项目目录没有引起您的注意,并且使神秘地停止检测要执行的任何工作。
收据clean
:
clean:
rm bin/*
make clean
除非运行成功,否则将一直失败。食谱
使用GNU Make的预定义删除宏将其替换为$(RM) $(OUTOBJ)
,
不会失败。
最后,请记住您的 linkage 食谱,无论它在哪里,确实都需要glib-2.0
的库选项,
您应该在其makefile中提供以下内容:
LIBS := $(shell pkg-config --libs glib-2.0) # ...and any more library options required
用于类似于以下内容的食谱
prog: $(OBJS)
$(CC) -o $@ $(LDFLAGS) $^ $(LIBS)
[1]严格来说,预处理器选项应出现在CPPFLAGS
的定义中
(C预处理器标志),不要与CXXFLAGS
(C ++编译选项)相混淆。
[2]严格地,除库以外的链接选项应出现在定义中
的LDFLAGS
。