Prepend to Simply Expanded Variable - makefile

I am working with some MPI code that I need to run with mpirun.
I am using an already existing shell script and I want to prepend to an already existing line.
So the dream is:
app=mpirun $(app)
I know it is possible to append to a variable using +=, but can I prepend?

If app should become (or remain) a simply expanded variable, then the following would be enough:
app := mpirun $(app)

Related

How can i assign the output of a makefile function to a variable

I have a function in a makefile that returns a bucket name in google cloud storage
define get_composer_bucket
gcloud beta composer environments describe --location=europe-west2 ${COMPOSER_SANDBOX_NAME} \
| grep -hnr "dagGcsPrefix"
endef
I want to use the output of this in a makefile command. How can i assign the output of this to a variable? I have tried the below, which returns an empty variable. Am i doing something wrong?
run:
BUCKET=$(call get_composer_bucket)
echo ${BUCKET}
There are many SO answers that will describe to you the difference between make variables and shell variables; knowing this difference is crucial to writing makefiles.
There are also many SO answers that will explain that each command in a recipe is run in a different shell, and that variables set in one shell can't be passed to another shell, so setting a variable in one recipe command line can't be seen in another recipe command line (you can use \ to combine multiple physical lines into a single logical line, but you probably need to add ; as well to satisfy the shell).
And, there are many answers discussing how a makefile is not a shell script (although it can CONTAIN shell scripts) so you can't just write a shell script anywhere in a makefile and have it evaluated.
The simplest thing to do for you is to set a make variable using the make shell function to run the shell script:
BUCKET := $(shell gcloud beta composer environments describe --location=europe-west2 ${COMPOSER_SANDBOX_NAME} | grep -hnr "dagGcsPrefix")
run:
echo $(BUCKET)
Of course, this is different in various ways than what you tried to do above. So maybe it doesn't meet your requirements, but you haven't actually stated what those are.
Please see below code which will help you to extract the name
define get_composer_bucket
`gcloud beta composer environments describe --location=europe-west2 ${COMPOSER_SANDBOX_NAME} \
| grep -hnr "dagGcsPrefix"`
endef
BUCKET:= $(call get_composer_bucket)
run:
#echo "BUCKET=> $(BUCKET)"

How can I trick bash to think I ran a script from within the directory where the binary is located?

I have a binary (emulator from the Android SDK Tools) that I use frequently. I've added it to my $PATH variable so that I can easily run the command with solely emulator.
However, it turns out that the binary uses relative paths; it complains if I run the script from a different directory. I'd really like to run the command from any folder without having to cd into the directory where the binary lives. Is there any way to get the script/bash into thinking I ran the command from the directory that it's located in?
A function is an appropriate tool for the job:
emu() ( cd /dir/with/emulator && exec ./emulator "$#" )
Let's break this down into pieces:
Using a function rather than an alias allows arguments to be substituted at an arbitrary location, rather than only at the end of the string an alias defines. That's critical, in this case, because we want the arguments to be evaluated inside a subshell, as described below:
Using parentheses, instead of curly brackets, causes this function's body to run in a subshell; that ensures that the cd only takes effect for that subshell, and doesn't impact the parent.
Using && to connect the cd and the following command ensures that we abort correctly if the cd fails.
Using exec tells the subshell to replace itself in memory with the emulator, rather than having an extra shell sitting around that does nothing but wait for the emulator to exit.
"$#" expands to the list of arguments passed to the function.
Add an alias (emu) to your ~/.bashrc:
alias emu="(cd /dir/with/emulator; ./emulator)"
I would look into command line aliases.
If you are in linux and using bash you can go to ~/.bash_profile and add a line such as:
alias emulator='./path-to-binary/emulator"
On windows it's a little different. Here is an example on how to do a similar thing in windows.

Export variable declared in script.sh and Import the value in mupltiple Makefiles

I am trying to create something like a global variable that I will use in order to make my project easy to deploy for other developers.
I would like to have an .sh file where there is a variable defining the location of the project.
Later on I want to export this variable and make it accessable in every makefile that I am creating so that I can use this design to keep everything constant and in one place.
This is an example of what I am trying to build:
Creating and exporting the variables in script.sh:
#!/bin/bash
DIRECTORY='some path value here'
Importing the values in multiple Makefiles:
# start script and fetch the value
VAR := $(shell ./script.sh | sed -n '/^result: /s/^.*: //p')
all:
#echo VAR=$(VAR)
I would like to see how other people are dealing with the same problem.
Being a better developer is my goal here. :)
Feedback always welcomed.
Environment variables exported in the shell are visible from make, so in a shell script like this:
#!/bin/sh
VAR=value
export VAR
make $*
The Makefile will start with VAR defined to value. That's one way to get variables from a shell script into make.
If you don't want the shell script to run make, you can have a user source it:
$ source script.sh
$ make
The variables set in the script will be visible to make this way too.
Or course there doesn't seem to be any reason you need a shell script here. Stick your configuration into a fragment of a Makefile (which would look almost exactly like your shell script, but not use quotes for multiple word values) and then include Makefile.inc in your main makefile.
Also note that syntax like this:
#!/bin/sh or another commment
VAR=value
export VAR
It equally valid included in a Makefile or sourced into a shell script. So sometimes it's possible to use the same include file in both places!

Invoking bash commands in makefile [duplicate]

This question already has an answer here:
command substitution doesn't work with echo in makefile [duplicate]
(1 answer)
Closed 6 years ago.
Inside of a makefile, I'm trying to check if fileA was modified more recently than fileB. Using a few prior posts (this and this) as references, I've come up with this as an attempt to store the time since last file modification as a variable:
(I'd rather this be in a function outside of a make recipe, but one step at a time.)
.PHONY: all clean
all : (stuff happens here)
radio :
BASE_MOD_TIME="$( expr $(date +%s) - $(date +%s -r src/radio_interface/profile_init.c) )"
#echo "$(BASE_MOD_TIME)"
I thought that I would be assigning the output of the expr command to a variable, BASE_MOD_TIME, but the output is:
bash-4.1$
BASE_MOD_TIME=""
echo ""
What am I doing wrong here? Simple attempts to save the output of ls -l also didn't work like this.
Make variables are normally global, and you don't normally set make variables in a recipe. A recipe is simply a list of commands to be executed by a shell, and so what looks like a variable assignment in a recipe is a shell variable assignment.
However, each line in a make recipe is run in its own shell subprocess. So a variable set in one line won't be visible in another line; they are not persistent. That makes setting shell variables in recipes less useful. [Note 1]
But you can combine multiple lines of a recipe into a single shell command using the backslash escape at the end of the line, and remembering to terminate the individual commands with semicolons (or, better, link them with &&), because the backslash-escaped newline will not be passed to the shell. Also, don't forget to escape the $ characters so they will be passed to the shell, rather than being interpreted by make.
So you could do the following:
radio:
#BASE_MOD_TIME="$$( expr $$(date +%s) - $$(date +%s -r src/radio_interface/profile_init.c) )"; \
echo "$$BASE_MOD_TIME"; \
# More commands in the same subprocess
But that gets quite awkward if there are more than a couple of commands, and a better solution is usually to write a shell script and invoke it from the recipe (although that means that the Makefile is no longer self-contained.)
Gnu make provides two ways to set make variables in a recipe:
1. Target-specific variables.
You can create a target-specific variable (which is not exactly local to the target) by adding a line like:
target: var := value
To set the variable from a shell command, use the shell function:
target: var := $(shell ....)
This variable will be available in the target recipe and all dependencies triggered by the target. Note that a dependency is only evaluated once, so if it could be triggered by a different target, the target-specific variable might or might not be available in the dependency, depending on the order in which make resolves dependencies.
2. Using the eval function
Since the expansion of recipes is always deferred, you can use the eval function inside a recipe to defer the assignment of a make variable. The eval function can be placed pretty well anywhere in a recipe because its value is the empty string. However, evaling a variable assignment makes the variable assignment global; it will be visible throughout the makefile, but its value in other recipes will depend, again, on the order in which make evaluates recipes, which is not necessarily predictable.
For example:
radio:
$(eval var = $(shell ...))
Notes:
You can change this behaviour using the .ONESHELL: pseudo-target, but that will apply to the entire Makefile; there is no way to mark a single recipe as being executed in a single subprocess. Since changing the behaviour can break recipes in unexpected ways, I don't usually recommend this feature.
What's wrong with this?
fileB: fileA
#echo $< was modified more recently than $#
Instead of forcing the makefile to do all of the heavy lifting via some bash commands, I just called a separate bash script. This provided a lot more clarity for a newbie to bash scripting like myself since I didn't have to worry about escaping the current shell being used by make.
Final solution:
.PHONY: all clean
all : (stuff happens here)
radio :
./radio_init_check.sh
$(MKDIR_P) $(OBJDIR)
make $(radio_10)
with radio_init_check.sh being my sew script.

use shell parameter extension in Makefile

How can I use shell parameter extension in a Makefile?
I need to get the path to the leveldb database.
What I tried so far is in my Makefile:
$(shell db=$(locate leveldb/db.h); export LEVELDB_PATH=${db%%/include/leveldb/db.h})
LEVELDB_LIBS=-L$(LEVELDB_PATH) -I$(LEVELDB_PATH)/include -lleveldb
But LEVELDB_PATH is empty.
Thanks for help.
The shell cannot export stuff into your Makefile. Try something like this instead.
db := $(shell locate leveldb/db.h)
LEVELDB_PATH:=$(patsubst %/include/leveldb/db.h,%,$(db))
... or, to spare the temp variable,
LEVELDB_PATH:=$(shell locate leveldb/db.h | sed 's%/include/leveldb/db.h$$%%')
Edit: Fix to use $(patsubst) instead of $(subst), and double the dollar sign in the sed script.
Generally speaking, whatever goes on inside $(shell ...) happens in a subprocess, which (as ever) cannot modify its parent. make sees the output of the function when it finishes, but not what happened during the execution of the shell commands.

Resources