Automake linking for multiple PROGRAMS - build

I'm using Autotools for building my project that contains multiple binaries (repo with commandline tools).
How can I link some of libraries to all of the programs and some other libraries only to particular programs?
Example:
My Makefile.am:
bin_PROGRAMS= prog1 prog2
prog1_SOURCES=prog1.cpp
prog2_SOURCES=prog2.cpp
prog1_LDADD= -llib-only-for-prog1
LDADD=-llib-for-all-projects # this does not work
#AM_LDFLAGS=-llib-for-all-projects # this does not work either
#LINK=-llib-for-all-projects # this does not work either
I want prog1 to be linked against -llib-only-for-prog1 and -llib-for-all-projects while prog2 - only against -llib-for-all-projects.
How can I achieve that with autotools?

Basically, if you add LDADD for a speficic program (prog1_LDADD) it will ignore the common LDADD options.
The following works for me:
bin_PROGRAMS=prog1 prog2 prog3
prog1_SOURCES=prog1.c
prog2_SOURCES=prog2.c
prog3_SOURCES=prog3.c
LDADD=-llib-for-all-projects
prog1_LDADD=-llib-only-for-prog1 $(LDADD)
note the extra $(LDADD) in the prog1_LDADD-line.

Related

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!
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.
You are getting errors because on the link step you didn't supply all symbols (global variables or functions) needed to generate the final executable file.
You either need to add the ASIO library to the link step or, as your Makefile suggests by defining the ASIO_STANDALONE macro, need to include ASIO's standalone header in one of your source files to compile it.

Adding a library to a makefile

I just installed RtMidi for a project and compiled it. The examples in the tests folder work and so does my code if I put it in the folder and include it in the Makefile that compiles all the examples. How can I use RtMidi in a project with #include <RtMidi.h> instead of having my code in the tests folder? More specifically, what should I put in my Makefile? I've read a bit about dynamic and static libraries but I have no idea what I should be looking for. I've tried adding -llibrtmidi and /usr/local/lib/librtmidi.a without success.
In a standard Makefile, the CXXFLAGS macro defines flags for the C++ compiler. You will need to add -I<path to header directory> to this macro for the compiler to find the RtMidi header files.
Then you will need to add -L<path to lib directory> to the link step of the Makefile so that -lrtmidi will find the library file. (Note that you omit the lib prefix for the -l command)
Based on your description of your environment, you may require something like
CPPFLAGS += -I/usr/local/include
LDFLAGS += -L/usr/local/lib
LDLIBS += -lrtmidi
in your Makefile. make uses a lot of these implicit variables.

Portably Compile Entire Directory

Is there a clean/portable way to descend recursively from a given directory, compiling all found .cpp files into a single output file? I'm not sure if makefiles are capable of this sort of thing, or if it's a job for some kind of build script, but I'd like to avoid maintaining various IDEs' project files along with my code.
There are different things that you can do here. I would suggest that you use a multiplatform build system, and follow the documentation for it. I have used CMake in the past, but I wouldn't know how to tell it to compile all files in a directory.
The advantage is that the user can use CMake to generate project files for most common IDEs, so it would allow VisualStudio users to generate VS solutions, MacOSX users to generate Xcode projects, Eclipse CDK projects in pretty much any environment, Makefiles...
There's the wildcard function which can be used to match a pattern like so:
CXX_FILES = $(wildcard src/*.cpp) # All .cpp files in the directory
This is not recursive, but will at least save you from having to manually specify the files in a certain directory. The rule for building them would look something like this:
CXX_FILES = $(wildcard src/*.cpp) # All .cpp files in the directory
OBJ_FILES = $(CXX_FILES:src/%.cpp=$(OBJ_DIR)/%.o) # Corresponding .o files
# Rules
all: $(OBJ_FILES)
g++ $(OBJ_FILES) -o output_filename
$(OBJ_DIR)/%.o: src/%.cpp
g++ -c $< -o $#
Oh, and to answer your question, this method is completely portable.

Makefile for compiling a number of .cpp and .h into a lib

I am running Windows 7 with gcc/g++ under Cygwin. What would be the Makefile format (and extension, I think it's .mk?) for compiling a set of .cpp (C++ source) and .h (header) files into a static library (.dll). Say I have a variable set of files:
file1.cpp
file1.h
file2.cpp
file2.h
file3.cpp
file3.h
....
What would be the makefile format (and extension) for compiling these into a static library? (I'm very new to makefiles) What would be the fastest way to do this?
The extension would be none at all, and the file is called Makefile (or makefile) if you want GNU Make to find it automatically.
GNU Make, at least, lets you rely on certain automatic variables that alone give you control over much of the building process with C/C++ files as input. These variables include CC, CPP, CFLAGS, CPPFLAGS, CXX, CXXFLAGS, and LDFLAGS. These control the switches to the C/C++ preprocessor, compiler, and the linker (the program that in the end assembles your program) that make will use.
GNU Make also includes a lot of implicit rules designed to enable it automatically build programs from C/C++ source code, so you don't [always] have to write your own rules.
For instance, even without a makefile, if you try to run make foobar, GNU Make will attempt to first build foobar.o from foobar.c or foobar.cpp if it finds either, by invoking appropriate compiler, and then will attempt to build foobar by assembling (incl. linking) its parts from system libraries and foobar.o. In short, GNU Make knows how to build the foobar program even without a makefile being present -- thanks to implicit rules. You can see these rules by invoking make with the -p switch.
Some people like to rely on GNU Make's implicit rule database to have lean and short makefiles where only that specific to their project is specified, while some people may go as far as to disable the entire implicit rule database (using the -r switch) and have full control of the building process by specifying everything in their makefile(s). I won't comment on superiority of either strategy, rest assured both do work to some degree.
There are a lot of options you can set when building a dll, but here's a basic command that you could use if you were doing it from the command line:
gcc -shared -o mydll.dll file1.o file2.o file3.o
And here's a makefile (typically called Makefile) that will handle the whole build process:
# You will have to modify this line to list the actual files you use.
# You could set it to use all the "fileN" files that you have,
# but that's dangerous for a beginner.
FILES = file1 file2 file3
OBJECTS = $(addsuffix .o,$(FILES)) # This is "file1.o file2.o..."
# This is the rule it uses to assemble file1.o, file2.o... into mydll.dll
mydll.dll: $(OBJECTS)
gcc -shared $^ -o $# # The whitespace at the beginning of this line is a TAB.
# This is the rule it uses to compile fileN.cpp and fileN.h into fileN.o
$(OBJECTS): %.o : %.cpp %.h
g++ -c $< -o $# # Again, a TAB at the beginning.
Now to build mydll.dll, just type "make".
A couple of notes. If you just type "make" without specifying the makefile or the target (the thing to be built), Make will try to use the default makefile ("GNUMakefile", "makefile" or "Makefile") and the default target (the first one in the makefile, in this case mydll.dll).

Creating dummy shared object (.so) to depend on other shared objects

I'm trying to create a shared object (.so) that will make it so, by including one shared object with -lboost, I implicitly include all the boost libraries. Here's what I tried:
#!/bin/sh
BOOST_LIBS="-lboost_date_time-gcc43-mt -lboost_filesystem-gcc43-mt"
#truncated for brevity
g++ $BOOST_LIBS -shared -Wl,-soname,libboost.so.1 -o libboost.so.1.0
ln -si libboost.so.1.0 libboost.so.1
ln -si libboost.so.1 libboost.so
After placing all 3 created files (libboost.so libboost.so.1 libboost.so.1.0) in the same directory as all the boost libraries, I tried compiling a test program with it (which depends on -lboost_date_time-gcc43-mt):
g++ -lboost test.cpp
Doing this, I got the same undefined reference message as not having -lboost. Having -lboost_date_time-gcc43-mt works, but that's too wordy :) How do I get -lboost to automatically bring in the other shared libraries?
You don't. Not really, anyway.
The linker is stripping out all of the symbol dependencies because the .so doesn't use them.
You can get around this, perhaps, by writing a linker script that declares all of the symbols you need as EXTERN() dependencies. But this implies that you'll need to list all of the mangled names for the symbols you need. Not at all worth the effort, IMO.
I don't have a solution for creating a dummy '.so', but I do have something that will simplify your life... I highly suggest that you try using cross-platform make (CMake). In CMake, linking against those libraries is easy:
FIND_PACKAGE(Boost 1.37 COMPONENTS date_time filesystem REQUIRED)
ADD_EXECUTABLE(myexecutable ${myexecutable_SRCS})
TARGET_LINK_LIBRARIES(myexecutable ${Boost_LIBRARIES})
The commands above, if placed in a "CMakeLists.txt" file, is all you need to:
Verify that Boost 1.37 or later is installed, with the "date_time" and "filesystem" libraries installed.
Create an executable named "myexecutable" from the sources listed in the corresponding variable.
Link the executable "myexecutable" against the boost "date_time" and "filesystem" libraries.
See also: Why the KDE project switched to CMake.
Actually, making one .so depend on all boost .so files is quite possible (but might not actually help you). I've just tried this:
$ export BOOST_ROOT=/home/ghost/Work/Boost/boost-svn
$ g++ -shared -Wl,-soname,libboost.so -o libboost.so $BOOST_ROOT/stage/lib/libboost_program_options.so
$ g++ -L . -I $BOOST_ROOT first.cpp -lboost -Wl,-R$BOOST_ROOT/stage/lib
$ LD_LIBRARY_PATH=.:$BOOST_ROOT/stage/lib ./a.out
And it did work. However, note that dancing with -R and LD_LIBRARY_PATH. I don't know an way how you can include the path to Boost .so inside your libboost.so so that they are used both for linking and actually running the application. I can include rpath inside libboost.so just fine, but it's ignored when resolving symbols for the application.