在 Linux 上使用 Makefile 编译独立 ASIO

Posted

技术标签:

【中文标题】在 Linux 上使用 Makefile 编译独立 ASIO【英文标题】:Compiling standalone ASIO with Makefile on Linux 【发布时间】:2017-02-03 23:25:55 【问题描述】:

我正在尝试编译一个小型 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 进行独立编译有什么建议吗?

谢谢!

【问题讨论】:

似乎您指定了库的路径,但在最后一步中没有指定要链接/加载的任何库。 因为standalone是header-only,它不会创建一个.lib——所以我认为它不需要链接到库?我应该如何在最后一步链接/加载它? ../../dependencies/asio/asio 中有什么?你使用了 -L,这意味着你告诉 ld 在那里寻找需要的库,但你仍然需要告诉 ld 你需要链接什么库。 【参考方案1】:

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

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

然后你建立你自己的程序基于你的系统满足的(真实的)假设 库依赖。您不会重复构建库依赖项 在构建您的程序时。

独立这个名字可能已经向你建议这个库是 意味着在每个使用它的应用程序中重建。它不是。它是 独立 asio 在某种意义上它本身并不依赖于任何 boost 库,与 boost::asio 不同,是从它派生的。 独立 甚至并不意味着该库不依赖于其他库 非增强库。例如。在您的链接错误中,有一些是 报告未定义的引用来自 asio 函数到 pthread_key_delete, 这意味着 asio 依赖于 Posix 线程库 libpthread, 而你没有链接它。

独立的 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)

然后:

从其下载源 tarball,例如 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 中使用这样的生成文件:

生成文件

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来构建asiochat-c​​lient 在/asio-package-dir/src/examples/cpp11/chat 中提供。把chat_client.cpp chat_message.hpp 在您的 src 文件夹中。

注意注释掉的行:

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

您表示您的程序需要与库libv4l2 链接 但是您自己的 makefile 没有提及任何此类链接。如果你确实需要 与它链接,那么您至少必须通过以下方式通知链接器该事实 取消注释:

LDLIBS := -lv4l2

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

如果您从源代码安装此库并决定将其安装在 某些不是链接器默认搜索路径之一的目录 (/usr/lib/usr/local/lib/ 等...)那么您还需要告知 它所在的链接器,通过取消注释:

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

请注意,通过将 libv4l2 添加到与 -lv4l2 的链接,您 要求链接器找到libv4l2 的任何其他库 反过来取决于。因此,如果您的链接现在因未定义的引用而失败 从libv4l2 到其他库libfoo 中的符号,您需要 扩展LDLIBS 喜欢:

LDLIBS := -lv4l2 -lfoo

如有必要,告诉链接器在哪里可以找到libfoo

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

以此类推,直到链接成功。

在这种情况下,您可能想知道为什么 asio 库没有类似的数字 在联动中。不需要链接器选项-lasio?您自己的 makefile 建议您 相信链接器需要被告知在哪里寻找这样的库, 它的设置:

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

虽然告诉链接器去那里寻找库,但你没有告诉它链接任何 图书馆。

不需要-lasio,因为这个库 - 通常不典型,但不是 对于boostboost-ish 库,不典型地是一个header only 库。 它不提供共享对象文件libasio.so,也不提供任何对象文件存档 libasio.a 必须链接才能获取函数的定义。反而, 它们完全由其头文件中的内联定义实现。因此, 您需要在程序中调用的任何一个都将被直接编译 如果您只是在制作的源文件中 #include &lt;asio.hpp&gt; 进入它 那些电话。

因为它是一个只有头文件的库,所以可以用它来构建你自己的 程序只需提取源包,跳过通常的自动工具./configure; make;make install 过程,并设置预处理器-I 选项 在您自己的 makefile 中正确(在 CPPFLAGS - C PreProcessor Flags 中) 让它找到asio 标头,比如说, /home/me/downloads/asio/asio-1.10.8。但如果你的目标是实现 那,你犯了一些错误途中;如果一个包 autotooled - 正如asio 一样 - 如果您尝试使用它,那么所有赌注都将关闭,除非自动工具提供 安装程序。 在您的系统中安装一个库也有 这样做的好处是,您可以忘记设置特殊的 每个使用它的项目中的编译器和链接器选项以及 /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。链接器会在创建它时使其可执行。

【讨论】:

哇!非常感谢您的彻底回复;这对于查看我出错的几个地方非常有帮助。也就是说——我的目标是通过定位标头将 asio 编译为仅标头项目。你说我在途中犯了一些错误——那会是什么? @nathanlachenmyer 不客气!至于您的asio 项目内设置出了什么问题,如果我自己没有该设置,我无法判断,+您实际尝试构建的client 源。如果我在没有安装 asio 的情况下构建示例 chat_client 并且仅将 -I/path/to/asio/download/dir 传递给编译器并忽略 -pthread,则链接将失败,未定义的引用与您的不同,全部指向 pthread 符号; -pthread 成功。造成差异的差异不在我的视线范围内;但是安装 lib 会正确并保护它免受任何意外损坏。 当这样的答案隐藏在 SO 的底层时,这让我很恼火。希望我能投票 100 次【参考方案2】:

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

您要么需要将 ASIO 库添加到链接步骤,要么正如 Makefile 通过定义 ASIO_STANDALONE 宏所建议的那样,需要将 ASIO 的独立标头包含在您的一个源文件中以进行编译。

【讨论】:

以上是关于在 Linux 上使用 Makefile 编译独立 ASIO的主要内容,如果未能解决你的问题,请参考以下文章

如何在 linux 中使用 Makefile 编译 tensorflow c_api

linux中用C语言编写完模块后怎么编写makefile文件?用到啥命令?以啥格式编写?

linux内核模块编译makefile

Eclipse makefile 仅适用于 C(windows 版本)

linux里面的make和makefile是做啥的?

Linux系统移植:Kernel 顶层 Makefile(上)