第一个 C++ 程序无法编译 - 不确定将第三方库放在哪里
First C++ program can't compile - Unsure where to put third party lib
我正在设计我的第一个 C++ 程序,但我很难理解如何安装和使用名为 pugixml 的第三方库。
这是我的结构:
- root/Makefile
- root/src/main.cpp
- root/include/pugi/pugixml.hpp
- root/include/pugi/pugixml.cpp
- root/include/pugi/pugiconfig.hpp
我相信我打算将所有 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。
问题
- 各种 pugi 文件应该放在哪里,如果不在我放的地方?
- 我应该如何相应地修改我的 Makefile?
代码编辑
#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.
对于你的情况,你有两个选择。
- 您可以静态构建 pugixml 库,这应该不是问题,因为它是轻量级的。
- 您可以构建一个动态库,然后 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
,因为从外观上看,您不是在编写库,也不会有需要具有 include
和 lib
目录等的复杂目录结构
如果您正在编写要一起分发的可执行文件和库,则选择 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 其他项目。
我正在设计我的第一个 C++ 程序,但我很难理解如何安装和使用名为 pugixml 的第三方库。
这是我的结构:
- root/Makefile
- root/src/main.cpp
- root/include/pugi/pugixml.hpp
- root/include/pugi/pugixml.cpp
- root/include/pugi/pugiconfig.hpp
我相信我打算将所有 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。
问题
- 各种 pugi 文件应该放在哪里,如果不在我放的地方?
- 我应该如何相应地修改我的 Makefile?
代码编辑
#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.
对于你的情况,你有两个选择。
- 您可以静态构建 pugixml 库,这应该不是问题,因为它是轻量级的。
- 您可以构建一个动态库,然后 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
,因为从外观上看,您不是在编写库,也不会有需要具有 include
和 lib
目录等的复杂目录结构
如果您正在编写要一起分发的可执行文件和库,则选择 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 其他项目。