nathan lachenmyer nathan lachenmyer - 4 years ago 241
Linux Question

Compiling standalone ASIO with Makefile on Linux

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!

Answer Source

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.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download