I’m trying to compile a small c++ program that captures an image from a camera using libv4l2 and then sends it over UDP to a separate computer using asio.
The file structure of the project is:
project/ dependencies/ asio/ cpp/ cpp_server/ cpp_client/ Makefile src/ cpp_client.cpp ImageClient.cpp ImageClient.h ImageProtocol.h
My Makefile for the project is:
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 $@ $<
However, when I try to compile this, my compiler spits on dozens of undefined reference
errors for ASIO functions:
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()'
I imagine that the issue is that my Makefile still isn’t properly finding ASIO and trying to compile it standalone. That said, I’m not really sure what else to try — does anyone else have suggestions for what I need to do to get ASIO to compile standalone with a Makefile?
thanks!
Advertisement
Answer
The Standalone Asio library is a dependancy of your program. When building a program, one does not also build the dependancies (unless in exceptional circumstances). If that were necessary, then building almost any program would recursively require probhibitively huge amounts of rebuilding dependencies.
If your program has a dependency on a library that is not provided packaged by the package manager of your Linux distro then you must get the source package of that library and build and install on your system as per its instructions.
Then you build your own program on the (true) assumption that your system satisfies the library dependency. You do not repeat the building of the library dependency in the building of your program.
The standalone moniker might have suggested to you that this library is
meant to be rebuilt in every application that uses it. It’s not. It’s
standalone asio
in the sense that it’s not itself dependent on any
boost
libaries, unlike boost::asio
, from it is derived. Standalone
doesn’t even imply that the library does not have dependencies on other
non-boost libraries. E.g. amongst your linkage errors are some that
report undefined references from asio
functions to pthread_key_delete
,
which means asio
is dependent on the Posix threads library, libpthread
,
and you’re not linking it.
The Standalone Asio library may well be provided by in a development package
by the package manager of your Linux distro. For example, Debian/Ubuntu distros
provide it in libasio-dev
and you install it simply with:
sudo apt-get install libasio-dev
Investigate whether your distro does likewise, and if so install the library with your package manager.
Otherwise you must install the library from source. It is a GNU autotools source package, so to build and install it you must have previously installed:
- GCC C++ toolchain - GNU make - GNU autotools (autoconf, automake at least)
Then:
Download the source tarball e.g asio-1.10.8.tar.bz2
, from its
Sourceforge page and
extract the package directory, e.g. asio-1.10.8
cd
into the the package directory and run:
$ autoreconf -i $ ./configure
Errors from ./configure
will indicate dependancies or other requirements
that your system does not satisfy. Fix and repeat until success. Then run
$ make
to build the package. If all is well, as root run:
$ make install
to install the package.
Once you have installed Standalone Asio either from a dev package or from
source, delete project/dependencies/asio
and build your program
in project/cpp/cpp_client
with a makefile like this:
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
For a rehearsal, I suggest using this makefile first to build the asio
chat-client
that’s provided in /asio-package-dir/src/examples/cpp11/chat
. Put just chat_client.cpp
chat_message.hpp
in your src
folder for this.
Notice the commented out lines:
#LDFLAGS := -L/path/to/your/libv4l2 #LDLIBS := -lv4l2
You indicated that your program needs to be linked with library libv4l2
but your own makefile does not mention any such linkage. If you do need to
link with it then you must at least inform the linker of that fact by
uncommenting:
LDLIBS := -lv4l2
If you can install dev a package of this library from your package manager, do so. Otherwise
build and install it from source. Debian/Ubuntu does not provide such
a library package, although they do provide libv4l-0
, libv4l-dev
and libv4l2rds0
. Perhaps you’re not precisely sure yet what library you need.
If you install this library from source and decide to install it in
some directory that is not one of the linker’s default search paths
(/usr/lib
, /usr/local/lib/
etc…) then you will also need to inform
the linker where it is, by uncommenting:
LDFLAGS := -L/path/to/your/libv4l2
Be aware that by adding libv4l2
to the linkage with -lv4l2
, you
oblige the linker to find any other library that libv4l2
in turn depends on. So if your linkage now fails with undefined references
from libv4l2
to symbols in some other library libfoo
, you need to
extend LDLIBS
like:
LDLIBS := -lv4l2 -lfoo
and, if necessary, tell the linker where to find libfoo
:
LDFLAGS := -L/path/to/your/libv4l2 -L/path/to/libfoo
And so on until the linkage succeeds.
In this light, you may wonder why the asio
library doesn’t similarly figure
in the linkage. No linker option -lasio
needed? Your own makefile suggests that you
believe the linker needs to be told where to look for such a library, with
its setting:
LD_FLAGS := -L../../dependencies/asio/asio
though having told the linker to look there for libraries, you don’t tell it link any libraries at all.
No -lasio
is needed because this library – untypically in general, but not
untypically for boost
or boost
-ish libraries – is a header only library.
It provides no shared object file libasio.so
, nor any object file archive
libasio.a
that you must link to get the definitions of functions. Instead,
they are wholly implemented by inline definitions in its header files. Thus,
any of them that you need to call in your program will be compiled straight
into it if you just #include <asio.hpp>
in the source(s) file that make
those calls.
As it’s a header only library, it is possible to use it to build your own
programs just by extracting the source package, skipping the usual autotools ./configure;
make;make install
procedure, and setting up the preprocessor -I
options
in your own makefile correctly (in CPPFLAGS
– C PreProcessor Flags)
for it to locate the asio
headers in, say,
/home/me/downloads/asio/asio-1.10.8
. But if you were aiming to achieve
that, you made some mistake(s) en route; and if a package is autotooled –
as asio
is – then all bets are off if you try to use it except as provided by the autotools
installation procedure. Installing a library in your system also has
the upside that once you’ve done it, you can forget about setting up peculiar
compiler and linker options in every project that uses it and the
like of /home/me/downloads/asio/asio-1.10.8
doesn’t need become a
fixture of your home directory.
Your makefile and what you say about its problems suggests that you’re trying to use GCC and GNU Make by guesswork, trial and error. Here is a fairly good starter tutorial in the use of those tools. For authoritative documentation, here is the GNU Make manual and here is the GCC manual
Incidentally, in Linux an executable is distinguished simply by its file
attributes and not by having an .exe
extension as in Windows, so
your program target can and normally would be called simply client
, not client.exe
. The linker will make it executable when it creates it.