Dynamic library cannot load when running program outside build directory - makefile

I have the following Makefile:
OBJ=main.o other.o other1.o other2.o
LINKDIVSUF=-L libdivsufsort-master/build/lib/ -ldivsufsort64 -Wl,-R libdivsufsort-master/build/lib/
INCDIVSUF=-I libdivsufsort-master/build/include -ldivsufsort64
EXE=program
COMPFLAGS=-MMD -fopenmp -std=c++17 -O3
CXX=g++
$(EXE):$(OBJ)
$(CXX) $(COMPFLAGS) $(OBJ) -o $(EXE) $(LINKDIVSUF) -lz -lboost_regex -lboost_program_options
%.o: %.cpp
$(CXX) $(COMPFLAGS) $(INCDIVSUF) -c $<
-include $(OBJ:.o=.d)
The program links to a dynamic library, libdivsufsort64.so.3, located from the build directory at ./libdivsufsort-master/build/lib/.
Use of -Wl,-R libdivsufsort-master/build/lib/is to avoid having to concatenate absolute/path/to/libdivsufsort-master/build/lib/ to LD_LIBRARY_PATH in order to run program. Indeed, when I make program
without -Wl, -R libdivsufsort-master/build/lib/, and without setting the LD_LIBRARY_PATH as mentioned and subsequently run program, I get the following error message:
./program: error while loading shared libraries: libdivsufsort64.so.3: cannot open shared object file: No such file or directory
With -Wl, -R libdivsufsort-master/build/lib/, program runs successfully with no alteration to LD_LIBRARY_PATH, but only when
I run program from the same directory in which it was built.
If I try to run program when compiled with -Wl, -R libdivsufsort-master/build/lib/ from any other directory, it fails to run, terminating with
the aforementioned error message.
How can I change the g++ compilation options (or anything else at compilation time) to enable program to run from
any directory whilst avoiding the need to alter LD_LIBRARY_PATH? The only "solution" I have found is to concatenate libdivsufsort-master/build/lib/ to LD_LIBRARY_PATH. By doing so, I can run program from any
directory, thus removing the need to compile with Wl,-R libdivsufsort-master/build/lib/, however, this of course requires the user of program to manually set their LD_LIBRARY_PATH, which I specifically want to avoid.
Solution
Reading this post that discusses the use of relative or absolute paths with -R (-rpath) I came up with this solution.
Append the following lines, such that the Makefile is now:
libdivsufsort_lib = $(addprefix $(shell pwd), /libdivsufsort-master/build/lib/)
libdivsufsort_include = $(addprefix $(shell pwd), /libdivsufsort-master/build/include/)
OBJ=main.o other.o other1.o other2.o
LINKDIVSUF=-L libdivsufsort-master/build/lib/ -ldivsufsort64 -Wl,-R libdivsufsort-master/build/lib/
INCDIVSUF=-I libdivsufsort-master/build/include -ldivsufsort64
EXE=program
COMPFLAGS=-MMD -fopenmp -std=c++17 -O3
CXX=g++
$(EXE):$(OBJ)
$(CXX) $(COMPFLAGS) $(OBJ) -o $(EXE) $(LINKDIVSUF) -lz -lboost_regex -lboost_program_options
%.o: %.cpp
$(CXX) $(COMPFLAGS) $(INCDIVSUF) -c $<
-include $(OBJ:.o=.d)
This avoids the use of $ORIGIN, to produce an absolute path to programs
directory, which is not supported on some systems. The two additional
lines produce the absolute path irrespective of the binary's location -
it just needs to kept in the build directory and compiled again if the build
directory moves. Importantly, program can now be called from outside the
build directory.

You need to use $ORIGIN with -Wl,-R to locate the library in relative path:
LINKDIVSUF = ... -Wl,-R,'$ORIGIN/libdivsufsort-master/build/lib'

Related

Im trying to compile program on Ubuntu and dont understand some things

Im a Windows dev who has no expirience on building C/C++ programs on Linux, but now I need to. Right way would be to go and learn Make and g++ compiler, but before I commit to that I want to figure out some basic stuff.
So I have .c program which is compiled with this makefile:
CUDA_VER=11.5
ifeq ($(CUDA_VER),)
$(error "CUDA_VER is not set")
endif
APP:= deepstream-test3-app
TARGET_DEVICE = $(shell gcc -dumpmachine | cut -f1 -d -)
NVDS_VERSION:=6.0
LIB_INSTALL_DIR?=/opt/nvidia/deepstream/deepstream-$(NVDS_VERSION)/lib/
APP_INSTALL_DIR?=/opt/nvidia/deepstream/deepstream-$(NVDS_VERSION)/bin/
ifeq ($(TARGET_DEVICE),aarch64)
CFLAGS:= -DPLATFORM_TEGRA
endif
SRCS:= $(wildcard *.c)
$(info info is $(SRCS))
INCS:= $(wildcard *.h)
PKGS:= gstreamer-1.0
OBJS:= $(SRCS:.c=.o)
CFLAGS+= -I../../../includes \
-I /usr/local/cuda-$(CUDA_VER)/include
CFLAGS+= $(shell pkg-config --cflags $(PKGS))
LIBS:= $(shell pkg-config --libs $(PKGS))
LIBS+= -L/usr/local/cuda-$(CUDA_VER)/lib64/ -lcudart -lnvdsgst_helper -lm \
-L$(LIB_INSTALL_DIR) -lnvdsgst_meta -lnvds_meta \
-lcuda -Wl,-rpath,$(LIB_INSTALL_DIR)
$(info info is $(CFLAGS))
all: $(APP)
%.o: %.c $(INCS) Makefile
gcc -c -o $# $(CFLAGS) $<
$(APP): $(OBJS) Makefile
gcc -o $(APP) $(OBJS) $(LIBS)
install: $(APP)
cp -rv $(APP) $(APP_INSTALL_DIR)
clean:
rm -rf $(OBJS) $(APP)
First thing I tried is to change this Makefile to compile it as C++ program. I changed .c file into .cpp, in makefile I change gcc to g++ everywhere and .c to .cpp everywhere. It gave me error that it couldnt find "main" entry point.
I gave up on that pretty fast and decided just to use lines output of original makefile, ending up with this:
g++ -c -o deepstream_test3_app.o -I../../../includes -I /usr/local/cuda-11.5/include -pthread -I/usr/include/gstreamer-1.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include ./deepstream_test3_app.cpp
g++ -o deepstream-test3-app deepstream_test3_app.o -lgstreamer-1.0 -lgobject-2.0 -lglib-2.0 -L/usr/local/cuda-11.5/lib64/ -lcudart -lnvdsgst_helper -lm -L/opt/nvidia/deepstream/deepstream-6.0/lib/ -lnvdsgst_meta -lnvds_meta -lcuda -Wl,-rpath,/opt/nvidia/deepstream/deepstream-6.0/lib/
First question, can I combine this 2 launches of g++ into one?
Second, when I make changes to "./deepstream_test3_app.cpp" they are not noticed by compiler. I added
#include <iostream>
...
std::cout << "hello!" << std::endl;
and they are ignored. Its like g++ gets as input some other copy/older version of the file and I dont understand how to go about it.
Hope for any help, sorry if it's all sounds stupid.
Ignoring for the moment the issues surrounding compiling C code with a C++ compiler,
g++ -c -o deepstream_test3_app.o -I../../../includes -I /usr/local/cuda-11.5/include -pthread -I/usr/include/gstreamer-1.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include ./deepstream_test3_app.cpp
g++ -o deepstream-test3-app deepstream_test3_app.o -lgstreamer-1.0 -lgobject-2.0 -lglib-2.0 -L/usr/local/cuda-11.5/lib64/ -lcudart -lnvdsgst_helper -lm -L/opt/nvidia/deepstream/deepstream-6.0/lib/ -lnvdsgst_meta -lnvds_meta -lcuda -Wl,-rpath,/opt/nvidia/deepstream/deepstream-6.0/lib/
First question, can I combine this 2 launches of g++ into one?
Yes. It is a common practice in makefiles to separate the compilation and linking steps, but that is not mandatory. When there are multiple sources, the separation makes it possible to limit recompilations to only the source files that have changed, but it doesn't make much difference, makefile or not, when there is only one source file.
The one-command version would be mostly a concatenation of the two commands you gave. One would omit the -c option, which instructs g++ to compile but not link, and one would omit the -o deepstream_test3_app.o, which specifies the name of the object file that we are no longer going to create. One would also omit the appearance of deepstream_test3_app.o drawn from the link (second) command, as we are going straight from source file to program. The rest of the options can be reordered to some extent, but all the -l options need to remain in the same order relative to each other and to any object files among the inputs. Here is how I would write it:
g++ -c -o deepstream_test3_app -I../../../includes -I /usr/local/cuda-11.5/include -pthread -I/usr/include/gstreamer-1.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -Wl,-rpath,/opt/nvidia/deepstream/deepstream-6.0/lib/ ./deepstream_test3_app.cpp -lgstreamer-1.0 -lgobject-2.0 -lglib-2.0 -L/usr/local/cuda-11.5/lib64/ -lcudart -lnvdsgst_helper -lm -L/opt/nvidia/deepstream/deepstream-6.0/lib/ -lnvdsgst_meta -lnvds_meta -lcuda
Second, when I make changes to "./deepstream_test3_app.cpp" they are not noticed by compiler.
The compiler compiles the source file(s) you tell it to.
Its like g++ gets as input some other copy/older version of the file
It is possible that you are indeed telling it to compile a different version than the one you modified. It is also possible that compilation fails, so you don't get a new executable. And it is possible that when you try to run the result, you are not running the program you think you are running. We don't have enough information to know.
With regard to the last, however, do be aware that on Linux, unlike on Windows, the working directory is not automatically in the executable search path. If you want to run the compiled result from the above command, you would want to specify the path to it, which you could most easily do by prepending ./ to its simple name: ./deepstream-test3-app.

How to link libs once only in GCC?

Pardon my question, I am a beginner to GCC. I have a framework project that holds source code for multiple subcomponents.
The structure is below:
Framework/
makefile //Master makefile in root
Component1/
src/
bin/
makefile
Component2/
src/
bin/
makefile
...
...
...
ComponentN/
src/
bin/
makefile
Now each makefiles in ComponentN/ each of directories will compile the code in its respective src/ and output .o to bin/ directory.
The root makefile however searches all the .o files recursively and links them all into one executable named 'framework'
Problem:
For code dependencies like glib,gdbus,gio I have to link them once when creating .o objects, in each of the component projects.
Plus I have to link the dependencies again when linking all the .o into one executable at root level.
Why do I have to do it twice? I am interested in understanding the internal mechanics.
As per request I am putting in makefile of the individual component libs that products *.o files
CC = gcc
CFLAGS = -g3
LIBS = `pkg-config --cflags --libs glib-2.0`
BINDIR = bin
OUTOBJ = $(addprefix $(BINDIR)/, objex.o)
$(BINDIR)/%.o : %.c
$(CC) -c $< $(CFLAGS) -o $# $(LIBS)
all: $(OUTOBJ)
$(OUTOBJ): | $(BINDIR)
$(BINDIR):
mkdir $(BINDIR)
.PHONY: clean
clean:
rm bin/*
Object files (.o) are created by compilation commands, e.g.
gcc -c -o foo.o foo.c ...
g++ -c -o baz.o baz.cpp ...
-c means compile; don't link. No linkage happens in the creation of
object files by the compiler. Any linkage options that you add to a compilation
command, e.g.
gcc -c -o foo.o foo.c -L/my/libs -lbar -lgum
are simply ignored.
Linkage options are acted on by a linkage command, which creates a program, or shared/dynamic
library, by linking together object files and libraries, e.g.
gcc -o prog foo.o baz.o -L/my/libs -lbar -lgum
gcc -shared -o libfoobaz.so foo.o baz.o -L/my/libs -lbar -lgum
So:
For code dependencies like glib,gdbus,gio I have to link them once when creating .o objects, in each of the component projects.
No you don't, and you can't.
Later
With sight of the problem makefile it is quite clear how to eliminate
the $(LIBS) reference from the compilation recipe, and what has been stopping you. The makefile defines:
LIBS = `pkg-config --cflags --libs glib-2.0`
which is a mistake. That makes $(LIBS) expand to the standard output of the
command:
pkg-config --cflags --libs glib-2.0
which is a single string containing both the compilation options required
for compiling source that #include-s the glib-2.0 API (on account of --cflags)
and also the linkage options required for linking a program or shared library
against libglib-2.0 (on account of --libs). On my system that is:
$ pkg-config --cflags --libs glib-2.0
-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -lglib-2.0
of which the compilation options alone would be output by:
$ pkg-config --cflags glib-2.0
-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
and the linkage options alone would be output by:
$ pkg-config --libs glib-2.0
-lglib-2.0
But because both sets of options are available only together through the expansion
of $(LIBS) you cannot successfully compile without passing the
linkage option -lglib-2.0, which is redundant and ignored.
As your make tool is evidently GNU Make, the makefile (which BTW is not that bad!) would be better written as:
Makefile
CC := gcc
CFLAGS := -g3 $(shell pkg-config --cflags glib-2.0)
BINDIR := bin
SRCS := objex.c
OUTOBJ := $(addprefix $(BINDIR)/, $(SRCS:.c=.o))
.PHONY: all clean
all: $(OUTOBJ)
$(BINDIR)/%.o : %.c
$(CC) -c $< $(CFLAGS) -o $#
$(OUTOBJ): | $(BINDIR)
$(BINDIR):
mkdir -p $(BINDIR)
clean:
$(RM) $(OUTOBJ)
which dispenses with LIBS and runs from scratch like:
$ make
mkdir -p bin
gcc -c objex.c -g3 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -o bin/objex.o
Note a few other improvements:-
The use of immediate expansion (:=) wherever applicable in preference to unnecessary
recursive expansion (=). See 6.2 The Two Flavors of Variables
The use of direct shell substitution by make - $(shell command) - in preference to backtick-expansion in
recipe execution. See 8.13 The shell Function.
all, like clean is a phony target
and you need to tell make that it is, to avoid the booby-trap in which something creates a file called all in
the project directory without you noticing and make mysteriously stops detecting any work for it to do.
With your clean receipe:
clean:
rm bin/*
make clean will fail if ever run except following a successful build. The recipe
is replaced with $(RM) $(OUTOBJ), using GNU Make's predefined delete macro, which
won't fail.
Finally, remember that your linkage recipe, wherever it is, does need the library options for glib-2.0,
which you should provide in its makefile with:
LIBS := $(shell pkg-config --libs glib-2.0) # ...and any more library options required
for use in a recipe similar to:
prog: $(OBJS)
$(CC) -o $# $(LDFLAGS) $^ $(LIBS)
[1] Strictly, preprocessor options should appear in the definition of CPPFLAGS
(C PreProcessor Flags), not to be confused with CXXFLAGS (C++ compilation options).
[2] Strictly, linkage options other than libraries should appear in the definition
of LDFLAGS.

C Makefile - recompile only changed files

Hello I have a shared library libnsd.so (made up of nsd.c,nsd.h,nd.c,nd.h) linked to main file.
My question is how to write the makefile so that it recompiles only those source files that have been changed.
I have read some topics about this but got somewhat confused, I'm a beginner programmer.
My makefile code so far:
CC=gcc
all : lib alll
alll : main.c
$(CC) main.c -o main -L. libnsd.so
lib : nsd.c nsd.h nd.c nd.h
$(CC) -c -fPIC nsd.c -o nsd.o
$(CC) -c -fPIC nd.c -o nd.o
$(CC) -shared -Wl,-soname,libnsd.so -o libnsd.so nsd.o nd.o
clean:
rm main libnsd.so nd.o nsd.o
Makefiles have the concept of build targets. The build targets are, really, all the intermediate as well as the final files and, by the way they are written, they can be made to use dependencies.
A sample solution for your makefile:
CC=gcc
all: main
main: main.c libnsd.so
$(CC) main.c -o main -L. libnsd.so
libnsd.so: nsd.o nd.o
$(CC) -shared -Wl,-soname,libnsd.so -o libnsd.so $#
%.o: %.c nsd.h nd.h
$(CC) -c -fPIC $< -o $#
A few things to note:
You should properly correct my dependencies on the object file creation (since I consider that each of the C files depends on both of the headers).
You may wish to note the wildcard construction I have used...
If there was nothing special with some of these commands I could have left default commands work. Do note that I have used $< for the first dependency and $# for the output in the wildcard rule.
I haven't copied the clean rule, since it was written correctly in the question itself.
Each of the targets (besides the "phony" target all) creates a file with the same name: The target libnsd.so creates a file with the name libnsd.so. The target main creates a file with the name main.
As a dependency of a target changes date so that the dependency is newer than the output, make will recreate the target, as well as other targets that depend on it. But if you have a target that is not mapped to any output file, that target is always called (in our code, the all target is always called but thankfully it has no commands and it depends only on actual files which may or may not need being recreated)
Do note that GNU Make doesn't need to have compiling in particular. The creation of an output file can happen by any means, and indeed I have seen a target create a .cpio.gz archive. But if that archive is older than any of the dependencies (the folder it would pack in) then it would be recreated, according to make.

Compiling SDL project on Raspberry Pi

I am trying to build a project with make (gcc on Raspbian)
Here is the makefile (I removed some unnecessary parts):
objects = 3d.o Affichage.o [...]
cflags = -I/usr/local/include/SDL2 -L/usr/local/lib -lSDL2
poly : %(objects)
gcc $(cflags) $(objects) -o poly
($objects) : types.h
[...]
When running Make, I got:
cc -c -o Affichage.o Affichage.c
fatal error: SDL.h: No such file or directory
#include <SDL.h>
I checked the folders, everything seems ok. SDL.h is indeed in /usr/local/include/SDL2. I tried to remove options one by one in cflags, no luck...
What am I missing?
Make told you exact command it tried to execute:
cc -c -o Affichage.o Affichage.c
This don't have -I path, which is the source of an error.
You have target for your resulting executable but not for object files. Make have builtin rule to compile object files from C sources, but it isn't aware of your cflags variable. So far your options are:
Define your own pattern rule
e.g:
%.o: %.c
gcc $(cflags) -c $< -o $#
However, your cflags contains -lSDL2, which is linking flag, which should be specified only on linking phase (so technically it isn't cflag). Move it to separate variable (usually LIBS, which may then be enfolded into make's semi-standard LDFLAGS).
Use variables that make is aware of
In that case, it is CFLAGS:
CC:=gcc
CFLAGS:=-I/usr/local/include/SDL2
LIBS:=-lSDL2
LDFLAGS:=-L/usr/local/lib $(LIBS)
objects:=3d.o Affichage.o
poly: $(objects)
$(CC) $^ -o $# $(LDFLAGS)
$(objects): types.h
The rest will be done by implicit rules.

Clang error while creating shared library

I want to compile a shared library on MBP and based on the advise given here, I have switched to clang (from gcc) to create the shared library. My makefile is:
CC = clang
CFLAGS = -c -fPIC -Wall -O3 -lpthread -g
BaseDir = /Users/admin/Prog/
Include = -I$(BaseDir)UMFPACK/Include -I$(BaseDir)AMD/Include -I$(BaseDir)SuiteSparse_config
Lib = -L$(BaseDir)UMFPACK/Lib -L$(BaseDir)AMD/Lib -L$(BaseDir)GotoBLAS2 -L$(BaseDir)SuiteSparse_config
AddFiles = -lumfpack -lamd -lgoto2 -lsuitesparseconfig
StaticLibs = $(BaseDir)GotoBLAS2/libgoto2.a $(BaseDir)UMFPACK/Lib/libumfpack.a $(BaseDir)AMD/Lib/libamd.a $(BaseDir)SuiteSparse_config/libsuitesparseconfig.a
all: TDS.o TDSJac.o ExtraRoutines.o CalcVFC.o TDS.h
$(CC) $(Include) $(Lib) $(AddFiles) -dynamiclib -lpthread -o libTDS.so $^ -lm -g $(StaticLibs)
TDS.o: TDS.c TDS.h
$(CC) $(Include) $(CFLAGS) $^
TDSJac.o: TDSJac.c TDS.h
$(CC) $(CFLAGS) $^
ExtraRoutines.o: ExtraRoutines.c TDS.h
$(CC) $(CFLAGS) $^
CalcVFC.o: CalcVFC.c TDS.h
$(CC) $(CFLAGS) $^
MexFile: TDSGateway.c
mex -g -largeArrayDims -ldl TDSGateway.c
Move:
mv libTDS.so ../../
mv TDSGateway.mexmaci64 ../../
What I do here is:
1) Only compile TDS.c, TDSJac.c, ExtraRoutines.c, and CalcVFC.c to create four corresponding object files.
2) Merging object files and creating a shared library using following command:
clang -I(Some folders) -L(Some folders) -l(Some libraries) -shared -lpthread -o libTDS.so TDS.o TDSJac.o ExtraRoutines.o CalcVFC.o TDS.h -lm -g
clang: error: cannot specify -o when generating multiple output files
As I am absolutely new to clang, I do not know what is wrong with Makefile. (If I replace CC=clang with CC=gcc, everything works good.)
I searched the internet with no success. Manual page of clang says nothing about shared library. I could not even get anything out of clang homepage. It is appreciated if help me to work around this problem.
Thanks for your help in advance.
I had yesterday almost the same problem.
But i never used cmake. I'm using an own makefile system of my company, so I just can tell you, what fixed the problem for me so far.
Clang has still some trouble with giving you the right hints on errorcase.
The linking error was:
/usr/bin/ld: /usr/lib/crt1.o: relocation R_X86_64_32 against `_DYNAMIC' can not be used when making a shared object; recompile with -fPIC
I solved the problem as I figgered out, that the -shared Parameter has to be behind the -o X.o parameters. As I see this is not made in your case, too. You should try that first.
In our case I also had to parse the LFlags with -WL, or -L, (afaik it doesn't matter which one you take but I read about a clang bug in relation to -Wl,).
But idk in how far you have to parse LFlags or not, when using cmake.
You've listed headers (TDS.h) as dependencies, which is fine, but you've then used $^ as inputs, which includes all dependencies. You should not list a header as an input. It should not be on the command-line at all, and in clang this is actually an error.
The error message is obscure but easy to resolve. You have 2 choices:
Be explicit instead of using $^. You could use a variable for inputs and another for dependencies, e.g.; or
Use intermediate .o files instead of .c files as inputs to a .so. The *.o files can depend on any .h they include.

Resources