Export environment variables from makefile to shell - makefile

I want to modify and export "LD_LIBRARY_PATH" environment variable in order to link libjvm.so with my code. Below is my Makefile:
all: run
helloWorld.class: helloWorld.java
javac helloWorld.java
hello_world: hello_world.c
gcc -L/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/ -I/usr/lib/jvm/java-8-openjdk-amd64/include -I/usr/lib/jvm/java-8-openjdk-amd64/include/linux/ hello_world.c -o hello_world_c_exec -ljvm
run: helloWorld.class hello_world
export LD_LIBRARY_PATH="/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/"
./hello_world_c_exec
clean:
rm -f helloWorld.class hello_world
In the target named "run", I export 'LD_LIBRARY_PATH' environment variable. But it wont get exported to my current shell.
I am getting the below error:
./hello_world_c_exec: error while loading shared libraries: libjvm.so: cannot open shared object file: No such file or directory
How do I export 'LD_LIBRARY_PATH' using makefile ??

Every line of a recipe is normally executed in a separate shell, meaning that your export line gets into different shell than your ./hello_world_c_exec line. See for yourself:
$ cat Makefile
JVM_DIR := /tools/opt/jdk-10.0.2/lib/server
foo: LDLIBS := -ljvm
foo: LDFLAGS := -L$(JVM_DIR)
.PHONY: run
run: foo
export LD_LIBRARY_PATH=$(JVM_DIR)
$(<D)/$(<F)
Output:
$ make run -d
...
Must remake target 'run'.
export LD_LIBRARY_PATH=/tools/opt/jdk-10.0.2/lib/server
Putting child 0x81ee20 (run) PID 18709 on the chain.
Live child 0x81ee20 (run) PID 18709
Reaping winning child 0x81ee20 PID 18709
./foo
Live child 0x81ee20 (run) PID 18710
./foo: error while loading shared libraries: libjvm.so: cannot open shared object file: No such file or directory
Reaping losing child 0x81ee20 PID 18710
make: *** [Makefile:9: run] Error 127
Note two separate processes started (18709 and 18710) for each separate line. You can make it work if it's run in a single shell with a couple of ways:
Make it a single script:
$ cat Makefile
JVM_DIR := /tools/opt/jdk-10.0.2/lib/server
foo: LDLIBS := -ljvm
foo: LDFLAGS := -L$(JVM_DIR)
.PHONY: run
run: foo
export LD_LIBRARY_PATH=$(JVM_DIR); \
$(<D)/$(<F)
$ make run -d
...
Must remake target 'run'.
export LD_LIBRARY_PATH=/tools/opt/jdk-10.0.2/lib/server; \
./foo
Putting child 0x21aa780 (run) PID 22009 on the chain.
Live child 0x21aa780 (run) PID 22009
Reaping winning child 0x21aa780 PID 22009
Removing child 0x21aa780 PID 22009 from chain.
Successfully remade target file 'run'.
Set in on the command line directly:
$ cat Makefile
JVM_DIR := /tools/opt/jdk-10.0.2/lib/server
foo: LDLIBS := -ljvm
foo: LDFLAGS := -L$(JVM_DIR)
.PHONY: run
run: foo
LD_LIBRARY_PATH=$(JVM_DIR) $(<D)/$(<F)
$ make run
LD_LIBRARY_PATH=/tools/opt/jdk-10.0.2/lib/server ./foo
Turn on One Shell so that recipe is executed in a single shell instead of separate shell for each line:
$ cat Makefile
JVM_DIR := /tools/opt/jdk-10.0.2/lib/server
foo: LDLIBS := -ljvm
foo: LDFLAGS := -L$(JVM_DIR)
.ONESHELL:
.PHONY: run
run: foo
export LD_LIBRARY_PATH=$(JVM_DIR)
$(<D)/$(<F)
$ make run -d
...
Must remake target 'run'.
export LD_LIBRARY_PATH=/tools/opt/jdk-10.0.2/lib/server
./foo
Putting child 0x25258f0 (run) PID 31367 on the chain.
Live child 0x25258f0 (run) PID 31367
Reaping winning child 0x25258f0 PID 31367
Removing child 0x25258f0 PID 31367 from chain.
Successfully remade target file 'run'.
Personally, I would choose yet different approach.
First, setting up LD_LIBRARY_PATH is cumbersome and depending on various settings may cause other things to fail, for example when your system already sets LD_LIBRARY_PATH to something else; at the very least you should be appending to the variable not setting it to a strict value.
Second, this still leaves the problem of running the executable outside of your Makefile. Even when the binary was built, it is not that trivial to run it by just a simple invocation:
$ make run
cc -L/tools/opt/jdk-10.0.2/lib/server foo.c -ljvm -o foo
export LD_LIBRARY_PATH=/tools/opt/jdk-10.0.2/lib/server
./foo
$ ./foo
./foo: error while loading shared libraries: libjvm.so: cannot open shared object file: No such file or directory
Instead, I would tell the linker to embed the hint where to find the library, so that the LD_LIBRARY_PATH is not needed at all:
$ cat Makefile
JVM_DIR := /tools/opt/jdk-10.0.2/lib/server
foo: LDLIBS := -ljvm
foo: LDFLAGS := -L$(JVM_DIR) -Wl,-rpath,$(JVM_DIR)
.PHONY: run
run: foo
$(<D)/$(<F)
$ make run
cc -L/tools/opt/jdk-10.0.2/lib/server -Wl,-rpath,/tools/opt/jdk-10.0.2/lib/server foo.c -ljvm -o foo
./foo
$ ./foo
$ echo $?
0

Related

Makefile 'missing separator' for ifneq

I know there are other issues with similar titles, but they don't seem to hold the solution.
Heres my makefile:
# Compiler Command
CC = mpiCC
CFLAGS = -c -I./header
# collecting object file names
src = $(wildcard source/*.cpp)
src1 = $(src:.cpp=.o)
objects := $(src1:source/%=bin/%)
# Compile object files into binary
all : $(objects)
$(CC) -o run $(objects)
ifneq($(n),) // <- error location , line 15
mpirun -np $(n) run
endif
# Generate object files by compiling .cpp and .h files
bin/%.o : source/%.cpp
$(CC) $(CFLAGS) $?
mv *.o bin
# Clean Recipe
.PHONY : clean
clean :
rm -rf all $(objects)
The goal of the ifneq is to have the binary run whenever it finishes compiling.
for example, a user runs the command:
make <- builds without running
make n=5 <- builds and runs on 5 processes
Whenever I use either of these, I get the error:
makefile:15: *** missing separator. Stop.
I've used cat -e -t -v to verify everything is tabbed instead of spaced. according to (https://www.gnu.org/software/make/manual/make.html#Conditional-Example) this conditional should function.
#MadScientist solved it. You need to put a space in between ifneq and its argument. For example:
ifneq($(n),0) is invalid.
ifneq ($(n),0) is valid.

Environment variable escaping across multiple Makefiles

What's the correct way to pass and modify environment variables through several levels of makefiles? Specifically I'm setting LDFLAGS in the parent makefile using the special $ORIGIN RPATH value, and I need to add to the variable in some of the child makefiles. The linker wants the literal string $ORIGIN, that's not another variable to expand.
Makefile:
# default value for all children
export LDFLAGS = -Wl,-rpath='$$ORIGIN'
all:
env | grep LDFLAGS
$(MAKE) -f child1.mk
$(MAKE) -f child2.mk
child1.mk:
# add an additional RPATH value
export LDFLAGS += -Wl,-rpath='$$ORIGIN/../..'
all:
env | grep LDFLAGS
child2.mk:
all:
env | grep LDFLAGS
This is what happens:
$ make
env | grep LDFLAGS
LDFLAGS=-Wl,-rpath='$ORIGIN'
make -f child1.mk
make[1]: Entering directory `/build/test'
env | grep LDFLAGS
LDFLAGS=-Wl,-rpath='RIGIN' -Wl,-rpath='$ORIGIN/../..'
make[1]: Leaving directory `/build/test'
make -f child2.mk
make[1]: Entering directory `/build/test'
env | grep LDFLAGS
LDFLAGS=-Wl,-rpath='$ORIGIN'
make[1]: Leaving directory `/build/test'
In the child1 output, 'RIGIN' is bad, the $$ appears to have been evaluated a second time.
The documentation has this note about exports, which could be relevant, but doesn't really explain how to avoid it:
In both of these forms, the arguments to export and unexport are expanded, and so could be variables or functions which expand to a (list of) variable names to be (un)exported.
Using $$$$ORIGIN in the top-level Makefile "works" for child1, but not for child2 which doesn't change the LDFLAGS value and ends up with $$ORIGIN.
This is GNU Make 3.81 under Linux.
The RPATH post I linked to has a workaround, but feels like this shouldn't be necessary:
LDFLAGS="-Wl,-rpath=XORIGIN/../lib" ./configure --prefix=/blabla/place
See the X? That will be replaced by a dollar sign later when you run chrpath on the resultant binaries.
On surface, this seems like a possible unintended bug with make integration with environment variables. It seems that whenever an environment variables is passed picked up by 'make', it will 'expand' any '$v', (and potentially other constructs).
Consider this very simple makefile
all:
echo "V=$V"
env | grep V=
Setting V to '$$SHELL' (bash export V='$$SHELL'), and running make will show that the internal 'V' variable is /bin/bash, while the environment variable is '$$SHELL'
echo "V=$SHELL"
V=/bin/bash
env | grep V=
V=$$SHELL
Putting a side the question if this is a feature or bug, you can use the $(value var) function to access the "raw" value. In the child make use:
# Append extra flags to env LDFLAGS, without 'expansion'
export LDFLAGS := $(value LDFLAGS) -Wl,-rpath='$$ORIGIN/../..'
Here is a working example how to pass $ORIGIN through multiple levels of makefiles with bash:
SHELL := /bin/bash
ifeq (${MAKELEVEL},0)
export LDFLAGS := -Wl,-rpath=`printf "%bORIGIN" "\044"`
endif
all :
#echo MAKELEVEL=${MAKELEVEL} LDFLAGS=${LDFLAGS}
#if [[ ${MAKELEVEL} -lt 3 ]]; then ${MAKE}; fi
Outputs:
$ make --no-print-directory
MAKELEVEL=0 LDFLAGS=-Wl,-rpath=$ORIGIN
MAKELEVEL=1 LDFLAGS=-Wl,-rpath=$ORIGIN
MAKELEVEL=2 LDFLAGS=-Wl,-rpath=$ORIGIN
MAKELEVEL=3 LDFLAGS=-Wl,-rpath=$ORIGIN
Alternatively, if you are on Linux, build without $ORIGIN but then use Exodus for painless relocation of Linux binaries – and all of their dependencies – without containers.
You can also set up additional variable of O to expand to the same before appending to LDFLAGS, so that even when make expands $O in $ORIGIN, it will still result in $ORIGIN:
$ cat child1.mk
# add an additional RPATH value
O := $$O
LDFLAGS += -Wl,-rpath='$$ORIGIN/../..'
all:
env | grep LDFLAGS
Output:
$ make
env | grep LDFLAGS
LDFLAGS=-Wl,-rpath='$ORIGIN'
make -f child1.mk
make[1]: Entering directory '/home/raspy/so-62298900'
env | grep LDFLAGS
LDFLAGS=-Wl,-rpath='$ORIGIN' -Wl,-rpath='$ORIGIN/../..'
make[1]: Leaving directory '/home/raspy/so-62298900'
make -f child2.mk
make[1]: Entering directory '/home/raspy/so-62298900'
env | grep LDFLAGS
LDFLAGS=-Wl,-rpath='$ORIGIN'
make[1]: Leaving directory '/home/raspy/so-62298900'

Targets in the form of {a,b} are not recognized in my Makefile

I have the following makefile:
SHELL:/bin/bash
all: euro.n.{a,b}
euro.{a,b}:
touch euro.{a,b}
euro.n.{a,b}: euro.{a,b}
cat euro.a > euro.n.a
cat euro.b > euro.n.b
Now if I run make twice, the makefile won't recognize in the second run that the files euro.n.a and euro.n.b have already been created (and it will be executed again).
What is the problem?
What is the problem?
{a,b} is not syntax recognized by GNU make.
SHELL := /bin/bash (you missed = there) only affects the syntax of recipes.
One alternative solution:
SHELL := /bin/bash
all: $(addprefix euro.n.,a b)
euro.%:
touch $#
euro.n.% : euro.%
cp $< $#

Makefile - export declarations to sub-makefiles

I know variables can be exported to sub-makefiles: Communicating Variables to a Sub-make
Example:
Makefile:
export PWD := $(shell pwd)
target:
#echo $(PWD)
#cd somewhere; $(MAKE)
somewhere/Makefile
target:
#echo $(PWD)
Supposing that the first Makefile is located at /path/to/first/makefile, the code above will print:
/path/to/first/makefile
/path/to/first/makefile
My question is: is there a way to let the variable PWD be implicitly evaluated inside sub-makefiles?
The output should look like this:
/path/to/first/makefile
/path/to/first/makefile/somewhere
So far I can only think of:
Exporting the literal declaration and use the function eval
Do it somehow with .SECONDEXPANSION
Put the declaration into a separate file and include it
in both the first and the second Makefile
All this solution are explicits: they imply code to be added to the sub-makefiles.
What I'm searching is an implicit solution which will change only the code inside the first Makefile.
Plus, honestly...the first two solutions are so ugly I would rather declare manually PWD in every sub-makefile.
[EDIT]
Just to make it more clear: the variable PWD is just an example, I'm not trying to obtain the path of every Makefile.
Use ${MAKEFILE_LIST} variable and let make change directories for you:
[max#earth:~/tmp]$ cat Makefile
target:
#echo $(abspath $(lastword ${MAKEFILE_LIST}))
${MAKE} -C somewhere $#
[max#earth:~/tmp]$ cat somewhere/Makefile
target:
#echo $(abspath $(lastword ${MAKEFILE_LIST}))
[max#earth:~/tmp]$ make target
/home/max/tmp/Makefile
make -C somewhere target
make[1]: Entering directory '/home/max/tmp/somewhere'
/home/max/tmp/somewhere/Makefile
make[1]: Leaving directory '/home/max/tmp/somewhere'
That prints the full path to the makefile being processed. Use $(dir $(abspath $(lastword ${MAKEFILE_LIST}))) to chop off filename Makefile.

Makefile: Explanation of circular dependencies on .o files

Consider the following makefile:
all : a.vo a.glob
a.gl%b a.v% : a.v
touch a.glob a.vo
When I run make -j2, I get:
$ rm -f a.vo a.glob; make -j2
make: Circular a.v.o <- a.v dependency dropped.
touch a.glob a.vo
I see why a.v.o depends on a.v, but I don't see how a.v depends on a.v.o. Where does this dependency come from (and is there a way to disable it)?
GNU make uses Implicit Rules when no rule is defined. In particular,
n is made automatically from n.o by running the linker (usually called ld) via the C compiler. The precise recipe used is ‘$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)’.
As you can see:
~ $ cat Makefile
all: a.v
echo "foo"
a.v.o:
echo "bar"
~ $ make
echo "bar"
bar
cc a.v.o -o a.v
gcc: error: a.v.o: No such file or directory
gcc: fatal error: no input files
compilation terminated.
<builtin>: recipe for target 'a.v' failed
make: *** [a.v] Error 1
cc is executed.

Resources