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

nat*_*yer 1 c c++ linux makefile boost-asio

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

项目的文件结构为:

project/
    dependencies/
        asio/
    cpp/
        cpp_server/
        cpp_client/
            Makefile
            src/
                cpp_client.cpp
                ImageClient.cpp
                ImageClient.h
                ImageProtocol.h
Run Code Online (Sandbox Code Playgroud)

我的项目 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 $@ $<
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试编译它时,我的编译器会吐出数十个undefined referenceASIO 函数的错误:

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()'
Run Code Online (Sandbox Code Playgroud)

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

谢谢!

Mik*_*han 6

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

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

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

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

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

sudo apt-get install libasio-dev
Run Code Online (Sandbox Code Playgroud)

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

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

- GCC C++ toolchain
- GNU make
- GNU autotools (autoconf, automake at least)
Run Code Online (Sandbox Code Playgroud)

然后:

asio-1.10.8.tar.bz2从其 Sourceforge 页面下载源 tarball eg并解压缩包目录,例如asio-1.10.8

cd 进入包目录并运行:

$ autoreconf -i
$ ./configure
Run Code Online (Sandbox Code Playgroud)

错误来自./configure将表明您的系统不满足的依赖项或其他要求。修复并重复直到成功。然后运行

$ make
Run Code Online (Sandbox Code Playgroud)

来构建包。如果一切顺利,以 root 身份运行:

$ make install
Run Code Online (Sandbox Code Playgroud)

安装软件包。

从开发包或源代码安装 Standalone Asioproject/dependencies/asioproject/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
Run Code Online (Sandbox Code Playgroud)

对于排练,我建议您先使用此生成的文件,以建立asio该公司在提供聊天客户端/asio-package-dir/src/examples/cpp11/chat。为此,只需将其放入chat_client.cpp chat_message.hpp您的src文件夹中。

注意注释掉的行:

#LDFLAGS := -L/path/to/your/libv4l2
#LDLIBS := -lv4l2
Run Code Online (Sandbox Code Playgroud)

您指出您的程序需要与库链接,libv4l2 但您自己的 makefile 未提及任何此类链接。如果您确实需要链接它,那么您至少必须通过取消注释来通知链接器该事实:

LDLIBS := -lv4l2
Run Code Online (Sandbox Code Playgroud)

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

如果你从源代码安装这个库,并决定在某些目录不是的连接器的默认搜索路径之一进行安装(/usr/lib/usr/local/lib/等...),那么你还需要通知链接的地方是,通过取消注释:

LDFLAGS := -L/path/to/your/libv4l2
Run Code Online (Sandbox Code Playgroud)

请注意,通过添加libv4l2到与 的链接-lv4l2,您迫使链接器找到libv4l2 反过来依赖的任何其他库。因此,如果您的链接现在因未定义的引用而失败,则来自libv4l2其他库中的符号libfoo,您需要LDLIBS像这样扩展:

LDLIBS := -lv4l2 -lfoo
Run Code Online (Sandbox Code Playgroud)

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

LDFLAGS := -L/path/to/your/libv4l2 -L/path/to/libfoo
Run Code Online (Sandbox Code Playgroud)

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

有鉴于此,您可能想知道为什么该asio库没有类似地显示在链接中。不需要链接器选项-lasio?您自己的 makefile 表明您认为链接器需要被告知在哪里寻找这样的库,以及它的设置:

LD_FLAGS := -L../../dependencies/asio/asio
Run Code Online (Sandbox Code Playgroud)

尽管已经告诉链接器在那里查找库,但您根本没有告诉它链接任何库。

不需要-lasio,因为这个库 - 通常不典型,但不是不典型的boostor boost-ish 库 - 是一个只有头文件的库。它不提供共享对象文件libasio.so,也不提供任何libasio.a必须链接以获取函数定义的对象文件存档 。相反,它们完全由其头文件中的内联定义实现。因此,如果您只是#include <asio.hpp>在进行这些调用的源文件中,则您需要在程序中调用的任何它们都将被直接编译到其中。

因为它是一个头只图书馆,它可以用它来建立自己的计划只是通过提取源代码包,跳过通常的自动工具./configure; make;make install程序,并设置预处理-I正确的在自己的Makefile选项(在CPPFLAGS- C预处理器旗)为它用于定位asio标题,例如, /home/me/downloads/asio/asio-1.10.8。但是,如果您的目标是实现这一目标,那么您就在途中犯了一些错误;如果一个包自动工具化的——按原样asio——那么如果你尝试使用它,除了自动工具安装程序提供的之外,所有的赌注都将关闭。安装系统中的库也有一个好处,一旦你完成了它,你就可以忘记在每个使用它的项目中设置特殊的编译器和链接器选项等/home/me/downloads/asio/asio-1.10.8不需要成为你的主目录的固定装置。

您的 makefile 以及您对它的问题所说的话表明您正在尝试通过猜测、试验和错误来使用 GCC 和 GNU Make。这是 使用这些工具的相当不错的入门教程。对于权威文档,这里是GNU Make 手册,这里是GCC 手册

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