如何仅在GCC中链接库一次?

时间:2019-05-15 11:14:37

标签: gcc linker

请原谅我的问题,我是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/*


1 个答案:

答案 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,就像cleanphony 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