第一个 C++ 程序无法编译 - 不确定将第三方库放在哪里

First C++ program can't compile - Unsure where to put third party lib

我正在设计我的第一个 C++ 程序,但我很难理解如何安装和使用名为 pugixml 的第三方库。

这是我的结构:

我相信我打算将所有 pugixml 转储到我的 includes 文件夹中一个名为 pug 的文件夹中,然后像这样从我的 Makefile 中引用它;

CC = g++
CFLAGS = -g -Wall -std=c++11
SRC = src
INCLUDES = include
TARGET = main

all: $(TARGET)

$(TARGET): $(SRC)/$(TARGET).cpp pugi/pugixml.cpp
    $(CC) $(CFLAGS) -I $(INCLUDES) -o $@ $^ 

clean:
    $(RM) $(TARGET)

然而当我 运行 Makefile 或得到这个错误:

main in main-bf0b72.o
  "pugi::xml_node::children(char const*) const", referenced from:
      _main in main-bf0b72.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [main] Error 1

有人告诉我这是因为我已将 pugi cpp 文件放入 include。

问题

代码编辑

#include "pugi/pugixml.hpp"

#include <iostream>
#include <string>
#include <map>

int main() {
    pugi::xml_document doca, docb;
    std::map<std::string, pugi::xml_node> mapa, mapb;

    if (!doca.load_file("a.xml") || !docb.load_file("b.xml"))
        return 1;

    for (auto& node: doca.child("site_entries").children("entry")) {
        const char* id = node.child_value("id");
        mapa[id] = node;
    }

    for (auto& node: docb.child("site_entries").children("entry"))
        const char* idcs = node.child_value("id");
        if (!mapa.erase(id)) {
            mapb[id] = node;
        }
    }

Pugixml 并不难用,因为它没有其他必须满足的依赖项。只需编译您的源代码和 pugi 源代码,一切正常:

g++ -I./include/pugi -o [...] src/main.cpp include/pugi/pugixml.cpp

你的 Makefile 对我不起作用,因为找不到 pugixml.cpp 文件。这里是更正后的版本:

[...]
SRC = src
PUGIDIR = include/pugi
INCLUDES = ${PUGIDIR}
TARGET = main
[...]

$(TARGET): $(SRC)/$(TARGET).cpp ${PUGIDIR}/pugixml.cpp
    $(CC) $(CFLAGS) -I $(INCLUDES) -o $@ $^ 
[...]

在您的 main.cpp 中,您只需使用

即可包含 pugixml.hpp
#include "pugixml.hpp"

另请参阅 C++ development on linux - where do I start? and How to make SIMPLE C++ Makefile? 了解一些基础知识。

pugi 开发文件是否带有 make 文件?如果是这种情况,您应该能够进行 make,然后进行 make install,将头文件放在正确的位置。

如果不是,那么我认为头文件通常(在 linux 框中)进入 /usr/lib 或 /usr/lib64 用于我认为的 .so 文件,以及.h 文件进入 /usr/include

然后您可能需要通过 运行 ldconfig 更新您的库路径。

完成后,您应该将 -l 添加到您的 makefile,例如,如果我想使用 pthreads 编写一些东西,我会这样做:

gcc program1.cpp -lpthread -o outputFilename

我现在无法验证,但很可能您只需将 pugi/pugixml.cpp 替换为 include/pugi/pugixml.cpp

在不同的平台上有许多存储 headers 和库的位置。

在 unix 系统中,通常,用户的 headers 将存储在:

/usr/local/include

共享库和静态库将存储在:

/usr/local/lib

在 Windows 中,headers 和库通常存储在已安装程序的目录中。

C:\Program Files\SomeProgram\

C:\Program Files(x86)\SomeProgram\

出于这些原因,存在许多 cross-platform 配置工具。值得注意的是,CMake 将生成独立于平台的 "Makefile" 来构建、link 并安装您的 libraries/executables.

对于你的情况,你有两个选择。

  1. 您可以静态构建 pugixml 库,这应该不是问题,因为它是轻量级的。
  2. 您可以构建一个动态库,然后 link 您的可执行文件。

对于选项 1,最简单的方法是将源文件和包含文件全部放在一个 src 目录下。您的目录结构如下所示:

Root
|--Makefile
|--bin
|--src
|  --main.cpp
|  --pugixml.cpp
|  --pugiconfig.hpp
|  --pugixml.hpp
--

您的 Makefile 将如下所示:

# An explanation of this Makefile can be found here: http://whosebug.com/questions/5098798/could-someone-explain-this-make-file

# Compiler
CC = g++
CFLAGS = -Wall -g -std=c++11

# Linker
LDFLAGS =

# Libraries
LIBS =

# Directories
BINDIR = bin
SRCDIR = src

# Files
SRCS = $(shell find $(SRCDIR) -name '*.cpp')
OBJS = $(patsubst $(SRCDIR)/%.cpp, $(BINDIR)/%.o, $(SRCS))
EXEC = $(BINDIR)/main

all: $(SRCS) $(EXEC)

$(EXEC): $(OBJS)
    $(CC) $(LDFLAGS) $(OBJS) -o $@

$(BINDIR)/%.o: $(SRCDIR)/%.cpp
    $(CC) $(CFLAGS) -c $< -o $@

clean:
    /bin/rm -f $(OBJS) $(EXEC)

Main.cpp

#include <iostream>
#include <string>
#include <map>

#include "pugixml.hpp"

int main(int argc, char * argv[]) {
    pugi::xml_document doca, docb;
    std::map<std::string, pugi::xml_node> mapa, mapb;

    if (!doca.load_file("a.xml") || !docb.load_file("b.xml"))
        return 1;

    for (auto& node: doca.child("site_entries").children("entry")) {
        const char* id = node.child_value("id");
        mapa[id] = node;
    }

    for (auto& node: docb.child("site_entries").children("entry")) {
        const char* id = node.child_value("id");
        if (!mapa.erase(id)) {
            mapb[id] = node;
        }
    }
}

这实际上等同于在您的项目中构建和 linking 一个 pugixml 静态库,缺点是您不会让其他程序 link 随时使用上述库。

您根本不需要担心 makefile 中的自定义包含位置 -I,因为从外观上看,您不是在编写库,也不会有需要具有 includelib 目录等的复杂目录结构

如果您正在编写要一起分发的可执行文件和库,则选择 2。对于这种情况,以及在所有情况下,真的,我建议您使用 Cmake 或其他可配置的实用程序。 您的项目结构如下所示:

Root
|--CMakeLists.txt
|--bin
|--build
|--src
|  --CMakeLists.txt
|  --main.cpp
|--lib
|  --CMakeLists.txt
|  --pugixml.cpp
|--include
|  --pugiconfig.hpp
|  --pugixml.hpp
--

CMakeLists.txt

project(main CXX)
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)

subdirs(src)
subdirs(lib)

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)

set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_BUILD_TYPE Debug)

src/CMakeLists.txt

include_directories(${main_SOURCE_DIR}/include)

file(GLOB SRC_FILES *.cpp)
add_executable(main ${SRC_FILES})

target_link_libraries(main pugixml)

set_property(TARGET main PROPERTY CXX_STANDARD 11)
set_property(TARGET main PROPERTY CXX_STANDARD_REQUIRED ON)

lib/CMakeLists.txt

include_directories(${main_SOURCE_DIR}/include)

file(GLOB SRC_FILES *.cpp)
add_library(pugixml SHARED ${SRC_FILES})

set_property(TARGET pugixml PROPERTY CXX_STANDARD 11)
set_property(TARGET pugixml PROPERTY CXX_STANDARD_REQUIRED ON)

Main.cpp

#include <iostream>
#include <string>
#include <map>

#include "pugixml.hpp"

int main(int argc, char * argv[]) {
    pugi::xml_document doca, docb;
    std::map<std::string, pugi::xml_node> mapa, mapb;

    if (!doca.load_file("a.xml") || !docb.load_file("b.xml"))
        return 1;

    for (auto& node: doca.child("site_entries").children("entry")) {
        const char* id = node.child_value("id");
        mapa[id] = node;
    }

    for (auto& node: docb.child("site_entries").children("entry")) {
        const char* id = node.child_value("id");
        if (!mapa.erase(id)) {
            mapb[id] = node;
        }
    }
}

现在构建您的项目甚至更容易,而且真正 cross-platform,因为您无需担心编写 Makefile。 CMake 将为您生成一个 platform-independent 实用程序!

cd build
cmake ..
make

如果您转到 bin 文件夹,您将找到生成的可执行文件和共享库。你可以 run/distribute 他们。

您还可以通过将 lib/CMakeLists.txt SHARED 更改为 STATIC 来使用库的静态版本编译可执行文件,但是,这基本上等同于我之前提到的选项 1 的静态编译。

您还可以 运行 make install 将二进制文件安装到 /usr/local/bin 并将库安装到 /usr/local/lib。然后,您可以通过在系统中的任何位置键入 main 从终端执行二进制文件(假设您已将 /usr/local/bin 添加到您的路径)。

您现在还可以使用带有 -llibpugixml 编译器标志的 pugixml 轻松 link 其他项目。