使用Makefile创建静态库并链接:C

23

我正在尝试理解静态库和共享库

我想通过创建一个makefile来实现分开编译和链接并创建一个静态库,最终以形成最终的静态可执行文件。

我有以下Makefile代码,但是我得到了以下错误:

Makefile:13: *** missing separator. Stop.

但我也在尝试了解如何实际链接/创建库。

如果我在终端中运行line 12之后的命令,它们可以工作,但在makefile中不行。

myProgram: main.o addSorted.o freeLinks.o
    gcc -lm -o myProgram main.o addSorted.o freeLinks.o

main.o: main.c
    gcc -O -c -lm main.c main.h

addSorted.o: addSorted.c addSorted.h
    gcc -O -c -lm addSorted.c

freeLinks.o: freeLinks.c freeLinks.h
    gcc -O -c -lm freeLinks.c

ar rc libmylib.a main.o addSorted.o freeLinks.o    //Error Line

ranlib libmylib.a

gcc -o foo -L. -lmylib foo.o

clean:
    rm -f myProgram main.o addSorted.o freeLinks.o

此外,如果您能帮助改善代码,我会非常感激。


可能是重复的问题:Make error: missing separator - Etan Reisner
这三行 arranlibgcc 应该是为了哪个目标而定义的配方? - Etan Reisner
@EtanReisner 我是C/makefile和库方面的新手。我猜是为了myProgram。这就是为什么我需要帮助。另外,我遇到错误的那一行没有包含tab - user3337714
1
Q: "我出错的那一行没有制表符..." A: 这就是问题所在。你需要一个“目标”(例如mylib.a,左对齐),然后是两行以“制表符”开头的命令(ar rc libmylib.a main.o addSorted.o freeLinks.o,然后是ranlib libmylib.a)。这两行必须连续(不能有空行)。这里有一个好的教程:一个简单的Makefile教程 - paulsm4
gcc编译器绝对不需要外部库,因此编译语句不应该有“-lm”。 - user3629249
显示剩余2条评论
2个回答

37

试一下这个:

all: myProgram

myProgram: main.o libmylib.a #libmylib.a is the dependency for the executable
        gcc -lm -o myProgram main.o -L. -lmylib

main.o: main.c
        gcc -O -c main.c main.h

addSorted.o: addSorted.c addSorted.h
        gcc -O -c addSorted.c

freeLinks.o: freeLinks.c freeLinks.h
        gcc -O -c freeLinks.c

libmylib.a: addSorted.o freeLinks.o #let's link library files into a static library
        ar rcs libmylib.a addSorted.o freeLinks.o

libs: libmylib.a

clean:
        rm -f myProgram *.o *.a *.gch #This way is cleaner than your clean

这组规则首先编译所有文件,然后生成库(libmylib.a)并使用其产物链接可执行文件。 我还添加了单独的冗余目标,用于只生成库。 所需文件:

user@host> ls
addSorted.c  addSorted.h  freeLinks.c  freeLinks.h  main.c  main.h Makefile

1
那么库只是由除了 main 之外的所有文件组成的吗?一旦我创建了库,它就会链接到 myProgram 吗?这很有帮助。 - user3337714
1
@Dmitri 一定是忘了删掉。已经改正答案了。 - Sergey
@user3337714 共享库应该从Makefile的角度同时构建。只需使用-fpic编译* .o文件,然后使用-shared链接即可。这里有一个很好的解释(仅涉及共享库,而非makefile):http://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html - Sergey
@user3337714 不是的。我的意思是使用 gcc -c -fpic freeLinks.cgcc -c -fpic addSorted.c 命令编译这两个文件,然后使用 gcc -shared -o libmylib.so freeLinks.o addSorted.o 命令生成一个共享库。接着,你可以使用 -lmylib 选项将其链接到可执行文件中,并且不要忘记将其目录添加到你将要运行可执行文件的 shell 的 $LD_LIBRARY_PATH 中。 - Sergey
@user3337714,在编译步骤中,您也不需要链接标志(即“-lm”)。我已经在答案中更正了建议的makefile。 - Sergey
显示剩余7条评论

12

一个 makefile 不是一个 shell 脚本。它是一个专家系统的配置文件,具体来说是一个知道如何高效创建文件及其依赖关系且尽量少重新生成无需重新生成的文件的专家系统。

如果你查看第一条规则,你会发现:

myProgram: main.o addSorted.o freeLinks.o
    gcc -lm -o myProgram main.o addSorted.o freeLinks.o

这段内容是关于如何告诉系统如何创建一个名为“myProgram”的文件,如果系统需要创建该文件。在冒号后面的部分是myProgram需要的文件。如果这些文件不存在,或者make认为它们已经过期了,make将尝试查找一些可以用来创建或更新它们的配方。完成所有这些步骤之后,make将执行“gcc…”行,并假定这将创建或更新myProgram。

您的ar和ranlib行的语法不符合makefile规则所需的语法。从它们的外观来看,它们似乎是用于制作libmylib.a的配方。如果您将它们放入make需要的语法中,则变成:

libmylib.a: main.o addSorted.o freeLinks.o
    ar rcu libmylib.a main.o addSorted.o freeLinks.o
    ranlib libmylib.a

我的程序应该依赖于库本身,而不是库的内容,最好将库选项放在末尾:

myProgram: libmylib.a
    gcc -o myProgram libmylib.a -lm

如果你愿意的话,可以使用一个选项告诉gcc在当前目录中查找库:

gcc -L. -o myProgram main.o -lmylib -lm

还有makefile变量可以帮助您避免重复这么多,因此我会将第一个规则编写为:

还有一些Makefile变量可以帮助您避免重复太多,因此我会将第一个规则写成:

myProgram: libmylib.a
    gcc -L. -o $@ -lmylib -lm

然而,main.o很可能并不是库的一部分,因此:

myProgram: main.o libmylib.a
    gcc -L. -o $@ $< -lmylib -lm

并且该库的规则如下:

libmylib.a: addSorted.o freeLinks.o
    ar rcu $@ $+
    ranlib $@

$+ 在这里表示“所有的依赖文件名”。

最后,如果你想让gcc生成一个实际的静态可执行文件,而不仅仅是使用你创建的库,你需要给gcc传递'-static'选项。


你能否也为我解释一下共享库? - user3337714
共享库高度依赖于您所在的系统。在x86或x86_64 Linux上,您需要使用-fpic编译对象文件,以及其他一些内容。其他系统可能会有所不同。 - user464502

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接