编译带有库的c++项目

Compiling C++ project with libraries

本文关键字:c++ 项目 编译      更新时间:2023-10-16

我的朋友在Windows上用Visual Studio开发了一个c++游戏,我想在我的Linux x64机器上编译它。我不是很熟悉c++,但我正在尝试在命令行上使用g++。然而,我只得到一堆未定义的引用错误。

基本文件结构为:

Libraries/SFML-2.0/lib
Libraries/SFML-2.0/include
Libraries/SFML_Linux64/lib
Libraries/SFML_Linux64/include
Libraries/Box2D/lib
Libraries/Box2D/include
Libraries/Box2DLinux/lib
Libraries/Box2DLinux/include
Game
Game/lib
Game/includes
Game/... (other subdirectories)

我尝试了以下命令:

g++ -Wall Multiplaya/app.cpp -I Libraries/SFML_Linux64/include/ -I Libraries/Box2DLinux/include/ -L Libraries/SFML_Linux64/lib/ -L Libraries/Box2DLinux/lib/

这是我得到的错误类型(一些行被截断并替换为...):

/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/crt1.o: I funktionen "_start":
(.text+0x20): undefined reference to `main'
/tmp/ccFXe37c.o: I funktionen "mp::createNetworkThread(void*)":
app.cpp:(.text+0x10): undefined reference to `worldDataMutex'
app.cpp:(.text+0x15): undefined reference to `sf::Mutex::lock()'
...
/tmp/ccFXe37c.o: I funktionen "mp::App::exec()":
app.cpp:(.text+0x148): undefined reference to `mp::ResourceHandler::instance()'
app.cpp:(.text+0x15a): undefined reference to `mp::ResourceHandler::loadTexture(std::string)'
app.cpp:(.text+0x3d7): undefined reference to `mp::Window::Window(mp::WorldData*)'
app.cpp:(.text+0x406): undefined reference to `mp::Controller::Controller(mp::World*, mp::Window*)'
...
app.cpp:(.text+0x471): undefined reference to `sf::Mutex::unlock()'
app.cpp:(.text+0x4bb): undefined reference to `sf::Thread::launch()'
app.cpp:(.text+0x4d7): undefined reference to `sf::Clock::Clock()'
app.cpp:(.text+0x4e6): undefined reference to `sf::Clock::getElapsedTime() const'
...
collect2: fel: ld returnerade avslutningsstatus 1

(我希望你能看懂上面的瑞典语)

您提供链接器的库路径真是太客气了。然而,链接器是忘恩负义的懒汉,当看到对函数的未定义引用时,通常不会在它们自己的上查找任何库文件。

undefined reference to `sf::Mutex::lock() -我打赌在Libraries/SFML_Linux64/lib/目录中有libsfml-system.so.2.0或任何东西,具有sf::Mutex::lock()的定义。但是链接器不在乎。您必须在编译调用的末尾说-lsfml-system

这将使g++理解不仅在libstdc++库中查找函数,而且在libsfml-system文件中查找函数。如果g++碰巧在默认的或额外的(用-L标志指定的)库目录中找到这样的文件,他将使用它来解析对函数调用的引用。

但是你必须明确地告诉它你想要加入的库文件,只指定目录和库并没有多大作用。所以,试着用

g++ -Wall Multiplaya/app.cpp -I Libraries/SFML_Linux64/include/ -I Libraries/Box2DLinux/include/ -L Libraries/SFML_Linux64/lib/ -L Libraries/Box2DLinux/lib/ -lsfml-system

如何构建c++程序

c++程序分两步构建:第一步是编译,第二步是链接。

在编译过程中,您将源文件转换为目标文件-包含编译后的机器代码的文件。现在有一个技巧你必须明白。比如,如果你有a.cpp

// a.cpp
int f(int x);
int main() {
    return f(42);
}

你可以用g++ -c a.cpp编译它,它会得到你的目标文件a.o(编译代码),没有任何编译错误。但是等等!a.cpp中没有f()的实际定义!

现在,当您转到第二步(链接)并调用g++ -o test a.o时,它会报错对f()的引用未定义。我们将b.cpp的文本设置为:

// b.cpp
int f(int x) {
    return 2 * x - 3;
}

使用g++ -c b.cpp编译它,然后作为g++ -o test a.o b.o执行链接-现在,它链接没有错误!

发生了什么事?好吧,当编译器看到一个函数调用时,它在对象文件中放入的不是一个实际的call指令,而是一个占位符,上面写着"调用一个具有这样或那样名称和这样或那样参数的函数"。然后链接器取一堆目标文件,把它们缝在一起。当它看到这样的占位符时,它会在给定的对象文件中查找上述函数,并对其进行实际调用,而不是放置占位符。

因此,c++程序的构建看起来像这样:
    对于每个x.cpp文件,调用g++ -c x.cpp <bunch of flags> -I<include directories> 然后调用g++ -o resultprogram a.o b.o c.o d.o ... <bunch of flags> -L<library directories> -l<additional libraries>

-l标志告诉链接器,如果他看到一个函数的调用,并且在指定的目标文件(a.o, b.o等)中没有定义这样的函数,那么它应该在这个库中查找。请不要,链接器不会查找任何目标文件和/或库,除了你指定的那些(好吧,它也会查找标准c++库libstdc++,但仅此而已)。

然而,如果您有10个或更多的文件,那么手工完成这个过程是相当无聊的。这就是人们使用"项目文件"answers"构建系统"的原因。当Visual Studio构建一个项目时,它会执行我提到的所有步骤:它编译项目中的每个文件,然后将结果链接在一起。在Linux上,你没有Visual Studio,但你有make实用程序。我相信有一些实用程序可以将VS项目转换为makefile .