在 Linux 上使用 Makefile 编译独立的 ASIO

Compiling standalone ASIO with Makefile on Linux

我正在尝试编译一个小的 c++ 程序,该程序使用 libv4l2 从相机捕获图像,然后使用 asio 通过 UDP 将其发送到另一台计算机。

项目的文件结构为:

project/
    dependencies/
        asio/
    cpp/
        cpp_server/
        cpp_client/
            Makefile
            src/
                cpp_client.cpp
                ImageClient.cpp
                ImageClient.h
                ImageProtocol.h

我的项目 Makefile 是:

CC=g++

CPP_FILES := $(wildcard src/*.cpp)
OBJ_FILES := $(addprefix obj/,$(notdir $(CPP_FILES:.cpp=.o)))
LD_FLAGS := -L../../dependencies/asio/asio
INCLUDES := -I../../dependencies/asio/asio/include

CC_FLAGS := -Wall  $(INCLUDES) -fpermissive -std=c++14 -DASIO_STANDALONE

client.exe : $(OBJ_FILES)
    $(CC) $(LD_FLAGS) -o $@ $^

obj/%.o: src/%.cpp
    $(CC) $(CC_FLAGS) -c -o $@ $<

然而,当我尝试编译它时,我的编译器针对 ASIO 函数吐出数十个 undefined reference 错误:

cpp_client.cpp:(.text+0x15dc): undefined reference to `asio::error::get_netdb_category()'
cpp_client.cpp:(.text+0x15ec): undefined reference to `asio::error::get_addrinfo_category()'
cpp_client.cpp:(.text+0x15fc): undefined reference to `asio::error::get_misc_category()'
obj/cpp_client.o: In function `asio::error::get_system_category()':
cpp_client.cpp:(.text._ZN4asio5error19get_system_categoryEv[_ZN4asio5error19get_system_categoryEv]+0x8): undefined reference to `asio::system_category()'
obj/cpp_client.o: In function `asio::detail::posix_tss_ptr<asio::detail::call_stack<asio::detail::thread_context, asio::detail::thread_info_base>::context>::posix_tss_ptr()':
cpp_client.cpp:(.text._ZN4asio6detail13posix_tss_ptrINS0_10call_stackINS0_14thread_contextENS0_16thread_info_baseEE7contextEEC2Ev[_ZN4asio6detail13posix_tss_ptrINS0_10call_stackINS0_14thread_contextENS0_16thread_info_baseEE7contextEEC5Ev]+0x20): undefined reference to `asio::detail::posix_tss_ptr_create(unsigned int&)'
obj/cpp_client.o: In function `asio::detail::posix_tss_ptr<asio::detail::call_stack<asio::detail::thread_context, asio::detail::thread_info_base>::context>::~posix_tss_ptr()':
cpp_client.cpp:(.text._ZN4asio6detail13posix_tss_ptrINS0_10call_stackINS0_14thread_contextENS0_16thread_info_baseEE7contextEED2Ev[_ZN4asio6detail13posix_tss_ptrINS0_10call_stackINS0_14thread_contextENS0_16thread_info_baseEE7contextEED5Ev]+0x1c): undefined reference to `pthread_key_delete'
obj/cpp_client.o: In function `asio::detail::posix_global_impl<asio::system_executor::context_impl>::~posix_global_impl()':
cpp_client.cpp:(.text._ZN4asio6detail17posix_global_implINS_15system_executor12context_implEED2Ev[_ZN4asio6detail17posix_global_implINS_15system_executor12context_implEED5Ev]+0x24): undefined reference to `asio::system_executor::context_impl::~context_impl()'
obj/ImageClient.o: In function `ImageClient::ImageClient(FHCamera, unsigned short, std::string const&, unsigned short)':
ImageClient.cpp:(.text+0x898): undefined reference to `asio::io_context::io_context()'

我想问题是我的 Makefile 仍然没有正确找到 ASIO 并尝试独立编译它。也就是说,我真的不确定还有什么可以尝试——是否有人对我需要做些什么来让 ASIO 使用 Makefile 进行独立编译提出建议?

谢谢!

您遇到错误是因为在 link 步骤中您没有提供生成最终可执行文件所需的所有符号(全局变量或函数)。

您需要将 ASIO 库添加到 link 步骤,或者如您的 Makefile 通过定义 ASIO_STANDALONE 宏建议的那样,需要包含 ASIO 的独立 header 在你的源文件之一中编译它。

独立的 Asio 库是您程序的依赖项。建造时 一个程序,一个人也不会建立依赖关系(除非在特殊情况下 情况)。如果那是必要的,那么构建几乎任何程序都可以 递归地需要大量重建依赖项。

如果你的程序依赖于未打包提供的库 通过你的 Linux 发行版的包管理器然后你必须得到源包 该库并按照其说明在您的系统上构建和安装。

然后你在你的系统满足的(真实)假设上构建你自己的程序 图书馆的依赖。您不重复构建库依赖项 在构建程序中。

standalone 绰号可能已向您建议此库是 意味着在每个使用它的应用程序中重建。它不是。它是 standalone asio 在某种意义上它本身不依赖于任何 boost 库,与 boost::asio 不同,它是从中派生的。 独立 甚至并不意味着该库不依赖于其他库 non-boost 个图书馆。例如。在你的link年龄错误中有一些是 报告 asio 函数到 pthread_key_delete 的未定义引用, 这意味着 asio 依赖于 Posix 线程库,libpthread, 而你没有 linking 它。

独立的 Asio 库很可能在开发包中提供 由 Linux 发行版的包管理器提供。例如,Debian/Ubuntu 发行版 在 libasio-dev 中提供它,您只需使用以下命令安装它:

sudo apt-get install libasio-dev

调查您的发行版是否也这样做,如果是,请安装库 与您的包管理器。

否则您必须从源安装库。它是一个 GNU autotools源码包, 所以要构建和安装它,您必须之前安装过:

- GCC C++ toolchain
- GNU make
- GNU autotools (autoconf, automake at least)

然后:

从其下载源压缩包,例如 asio-1.10.8.tar.bz2 Sourceforge page 和 提取包目录,例如asio-1.10.8

cd 进入包目录和 运行:

$ autoreconf -i
$ ./configure

来自 ./configure 的错误将指示相关性或其他要求 你的系统不满足。修复并重复直到成功。然后运行

$ make

构建包。如果一切顺利,作为 root 运行:

$ make install

安装包。

从开发包或从 源,删除 project/dependencies/asio 并构建你的程序 在 project/cpp/cpp_client 中使用这样的 makefile:

生成文件

CXX=g++
SRCS := $(wildcard src/*.cpp)
OBJS := $(addprefix obj/,$(notdir $(SRCS:.cpp=.o)))
CXXFLAGS := -pthread
LDFLAGS := -pthread
#LDFLAGS := -L/path/to/your/libv4l2
#LDLIBS := -libv4l2

.PHONY: all clean 

CXXFLAGS := -Wall -std=c++14 -DASIO_STANDALONE

all: client

client : $(OBJS)
    $(CXX) $(LDFLAGS) -o $@ $^ $(LDLIBS)

obj/%.o: src/%.cpp | obj
    $(CXX) $(CXXFLAGS) -c -o $@ $<

obj:
    mkdir -p $@

clean:
    rm -f obj/* client

为了排练,我建议先使用这个 makefile 来构建 asio chat-client /asio-package-dir/src/examples/cpp11/chat 中提供的。只放 chat_client.cpp chat_message.hpp 在您的 src 文件夹中。

注意注释掉的行:

#LDFLAGS := -L/path/to/your/libv4l2
#LDLIBS := -lv4l2

您指出您的程序需要 link 与库 libv4l2 编辑 但是你自己的 makefile 没有提到任何这样的 linkage。如果你确实需要 link 有了它,那么您至少必须通过以下方式告知 linker 这个事实 取消注释:

LDLIBS := -lv4l2

如果您可以从您的包管理器中安装此库的开发包,请这样做。否则 从源代码构建和安装它。 Debian/Ubuntu不提供此类 一个库包,尽管它们确实提供 libv4l-0libv4l-devlibv4l2rds0。也许您还不确定您需要什么库。

如果您从源代码安装此库并决定将其安装在 一些目录不是 linker 的默认搜索路径之一 (/usr/lib, /usr/local/lib/ etc...) 然后你还需要通知 linker 它在哪里,通过取消注释:

LDFLAGS := -L/path/to/your/libv4l2

请注意,将 libv4l2 添加到 -lv4l2 的 link 年龄,您 迫使 linker 找到任何其他 libv4l2 反过来取决于。因此,如果您的 linkage 现在因未定义的引用而失败 从 libv4l2 到其他库 libfoo 中的符号,您需要 将 LDLIBS 扩展为:

LDLIBS := -lv4l2 -lfoo

并在必要时告诉 linker 在哪里可以找到 libfoo:

LDFLAGS := -L/path/to/your/libv4l2 -L/path/to/libfoo

依此类推,直到 linkage 成功。

鉴于此,您可能想知道为什么 asio 库没有类似地计算 在 link 时代。不需要 linker 选项 -lasio?你自己的 makefile 建议你 相信 linker 需要被告知去哪里寻找这样的图书馆, 它的设置:

LD_FLAGS := -L../../dependencies/asio/asio

虽然已经告诉 linker 去那里寻找图书馆,但你没有告诉它 link 图书馆。

不需要 -lasio 因为这个库 - 通常不典型,但不是 对于 boostboost-ish 库来说不常见 - 仅是 header 库。 它不提供共享 object 文件 libasio.so,也不提供任何 object 文件存档 libasio.a 你必须 link 才能得到函数的定义。反而, 它们完全由其 header 文件中的内联定义实现。因此, 他们中的任何一个你需要调用你的程序将被直接编译 如果你只是 #include <asio.hpp> 在制作的源文件中 那些电话。

由于它是一个 header 唯一的库,因此 可以使用它来构建您自己的库 只需提取源代码包,跳过通常的 autotools ./configure; make;make install 过程,并设置预处理器 -I 选项即可编写程序 在你自己的 makefile 中正确(在 CPPFLAGS - C PreProcessor Flags 中) 让它找到 asio headers 在,比方说, /home/me/downloads/asio/asio-1.10.8。但如果你的目标是实现 那,你犯了一些错误途中;如果一个包 autotooled - 因为 asio 是 - 如果您尝试使用它,则所有赌注都会关闭,除非自动工具提供 安装程序。 正在安装您系统中的库也有 好处是一旦你完成了,你就可以忘记设置特殊的 每个使用它的项目中的编译器和 linker 选项以及 喜欢 /home/me/downloads/asio/asio-1.10.8 不需要变成 您的主目录的夹具。

你的 makefile 和你所说的问题表明你是 尝试通过猜测、反复试验来使用 GCC 和 GNU Make。这是 a fairly good starter tutorial in the use of those tools。 对于权威文档,这里是 the GNU Make manual 和 这里是 the GCC manual

顺便说一句,在 Linux 中,可执行文件仅通过其文件来区分 属性而不是像 Windows 那样具有 .exe 扩展名,所以 您的程序目标可以并且通常会被简单地称为 client,而不是 client.exe。 linker 将在创建它时使其可执行。