Makefile define variable using if - shell

I'm trying to do something like it
#if[[ 1==1 ]] then;\
COMPILER_CMD = -fPic;\
fi;
But if i call in the next line the variable it don't work.
If i define it outside the if it works perfect.
Someone can help me?

As everyone is saying, you haven't given us enough information. But I'll make a guess. You want to set this variable conditionally, then use it elsewhere in the makefile, and in other makefiles which include this one.
The trouble is that you are trying to use shell syntax. In a command this will work (if the syntax is correct), but the value will apply only in that command. Outside commands, shell syntax is just wrong and will cause an error, malfunction, or be ignored depending on exactly what you do.
Try this in the makefile, outside of any rule (that is, not in the recipe for any particular target):
ifeq (1,1)
COMPILER_CMD = -fPic
endif
$(info $(COMPILER_CMD))
If that works, then you can try to adapt it to do whatever it is you're actually trying to do.

Each line in the Makefile is executed separately in a new shell process, so that's why changes you made to the environment are not propagated to next line.
You can combine both lines into one long one to achieve what you want. You probably have something like this in you Makefile:
#if[[ 1==1 ]] then;\
COMPILER_CMD = -fPic;\
fi;
echo $COMPILER_CMD
You want to add the line continuation backslash to the line before echo:
#if[[ 1==1 ]] then;\
COMPILER_CMD = -fPic;\
fi; \
echo $COMPILER_CMD

I'm assuming that the example you show is the recipe for some rule. By the syntax here it looks like you're trying to set a make variable COMPILER_CMD from within a recipe based on the value of some shell boolean test, which is of course impossible. You have to be very clear in your mind how make works: make is not interpreting the recipes you write, in any way. Make is simply passing those recipes to another program (the shell) and the other program is interpreting those commands. Thus, you can't change the behavior of make, including setting make variables, from within a recipe: that recipe is being run in a completely different program.
As others have said, you don't give enough information about what you REALLY want to do, at a higher level, for us to give a complete solution. Having a boolean like 1==1 doesn't give any hint whatsoever as to why you're doing this. Also your shell syntax contains syntax errors, so we can tell you didn't actually cut and paste this from a real, working example.
You can, as piokuc implies, use a shell variable COMPILER_CMD (you have to remove the whitespace around the = to make it a shell variable assignment) but that value takes effect only while that one recipe line is running. For the next recipe line a new shell is started and any values set in the previous shell are lost:
all:
# if [[ 1 == 1 ]]; then COMPILER_CMD=-fpic; fi; \
echo COMPILER_CMD=$$COMPILER_CMD
# echo COMPILER_CMD=$$COMPILER_CMD
will give:
COMPILER_CMD=-fpic
COMPILER_CMD=

Related

remove Quotes from String in Windows Terminal

I want to retrieve some Values from AWS and use them afterwards in my Makefile. But for that purpose I need to remove the quotes.
I know different ways how to do that in Linux but not in Windows. The only hint I found is this: https://ss64.com/nt/syntax-dequote.html
But all my attempts to use it with my code have not worked so far.
The Code looks like this:
aws/project_name:
# $(eval PROJECT = $(shell aws ssm get-parameter --name "$(PROJECT_PARAMETER)" --query Parameter.Value))
#set PROJECT = %~1
# echo $(PROJECT)
When I run it the result is:
"MyProject"
Can someone give me hint pls how to strip the Double-quotes?
$(eval ) is a make function, not something which is evaluated as part of a shell script like your snippet suggests. Moreover you need to concatenate recipe shell lines with backslash, otherwise each line is executed in a separate shell process, thereby losing access to any variable instantiated in previous lines.
Another issue is the use of $(PROJECT) without quoting: make will replace this with its internal PROJECT variables' value before it goes to evaluate even the first line (see below) of your recipe. If you want to access the shell variable in this line, quote every use of $ with $$.
To answer your original question for replacement of "": $(patsubst "%",%,$(PROJECT)) would do that - you can wrap the whole $(shell ...) in it. but I suspect your troubles with what you are trying to achieve will not end there.
It is rather complicated to introduce make variable values in a running build in the right order. I'm stressing this again: At least you must keep in mind that make evaluates all of its own syntax (e.g. the $(eval ) call) always and entirely before it executes the recipe. Moreover, the order of evaluation of the rule is also split into two phases, see here: https://www.gnu.org/software/make/manual/html_node/Reading-Makefiles.html#Reading-Makefiles. If you don't understand the execution model of a makefile you will not be able to write controlled advanced scripting code in it.
One can do advanced scripting with make like you are trying, but I recommend a strict architectural approach for such makefiles - ad-hoc writing them will likely lead to chaos.
The current file could be written as:
aws/project_name: ;
# ';' reliably separates recipe lines from target & prerequisites
# we still need to $(eval) PROJECT because it is only accessible for other recipes this way
$(eval PROJECT = $(patsubst "%",%,$(shell aws ssm get-parameter --nam`enter code here`e "$(PROJECT_PARAMETER)" --profile enchomepage --query Parameter.Value)))
#echo $(PROJECT)
later_target: aws/project_name
# this should work now:
#echo $(PROJECT)
...but beware, this is exactly the part where such makefiles become hard to trace, becaus now you need to mentally follow the evaluation order, take care that variables used downstream are really $(eval )'ed upstream (they won't if the recipe isn't executed) thereby having sidestepped the usual contract which other programmers expect from a makefile.

GNU make use variable value to call a "function"

I want to iterate through the list, LIST, and want to call a function which writes something into a text file using the name of the extracted list elements as name of the call to the "function".
.PHONY: GEN_BAT
GEN_BAT:
for comp in $(basename $(notdir $(LIST))); do \
echo comp: $$comp ;\
$(call $$comp)
done; \
If I write the line $(call $$comp) without a ;\ I'll receive "syntax error: unexpected end of file".
If I write the line with the original function e.g. $(call FUNCTION);\ with a ;\ it calls the existing function as expected. So, something is wrong with the used variable?
How can I persuade make to use the value of $$comp as a call to "function"?
It's not clear at all what you want to do; providing a complete, minimal example would help a lot.
However, it's not possible to use a shell loop then try to utilize the shell loop variable in a make function. If you think about it that doesn't make any sense: the shell is a completely different program than make, make can't know what the shell's variable are set to, and the shell can't call make functions.
Make runs a command like this: first the entire recipe is expanded so that ALL make variables and functions are resolved. Then make takes that expanded command string and gives it to the shell and the shell process it as a shell script. Make waits for the shell to complete, then reports the exit code as success (0) or failure (not 0).
Also, a statement like $(call xyz) makes no real sense in make: calling a function with no arguments is identical to simply expanding the variable as in $(xyz). The only time call makes sense is if you pass arguments to it.

In makefile, how to store multi-line shell output in variable

I have a shell command where it outputs multiple lines. I want to store it in a variable in makefile for later processing in the target.
A simplified example:
I have this file called zfile1
#zfile1
some text
$echo 123
more text
$$$#&^$
more text
The makefile:
a:
#$(eval v1 = $(shell cat zfile1))
# need to process the variable here, example:
#echo "$(v1)"
# I want to prevent expansion of values in the file and print in multi-line
If you have GNU make 4.2 or above you can use the $(file <zfile1) function. See https://www.gnu.org/software/make/manual/html_node/File-Function.html
If you don't have a new-enough version of GNU make, you can't do it. Of course in your example there's no real need to put the contents of the file into a make variable at all: you can just have your recipe use the file itself. But maybe your real use-case isn't so simple.
ETA
You should never use either the make function eval or the make function shell in a recipe [1].
You can just write:
v1 := $(file <zfile1)
.ONESHELL:
a:
#echo "$(v1)"
You must have the .ONESHELL because otherwise each line of the recipe (after it expands into multiple lines) is considered a separate recipe line. Note that .ONESHELL is in effect for the entire make process so could cause other recipes to break if they rely on each line being invoked in a different shell.
Another option is to export the result into the environment, and use a shell variable like this:
export v1 := $(file <zfile1)
a:
#echo "$$v1"
There are probably better ways to do it but since you've only given us this example and not what you really want to do, that's about all we can say.
[1] There are times where it can be useful but if you have a sufficiently sophisticated requirement to need this you'll probably know how to use it.
I think you're making things too complicated.
Start by writing your recipes as proper self-contained shell scripts.
You can then either store the whole script in a file and run it from make, or you can include it directly in your makefile as a single logical line, as in the following:
a:
#v1=$$(< zfile1); \
echo $$v1
Note the need to "escape" the dollar sign by repeating it.
You could also use global make variables, depending on the actual logic of your real-world use.

Pass variable to make CFLAGS in shell script?

I'm trying to build some c programs with gnu make (4.1) through a shell script. I want to use different CFLAGS parameters, which I read from a file. The file contains lines like:
"-O1 -freorder-functions"
I am trying to do something like
while read line
do
opts="$line"
make CFLAGS="$opts"
done < $1
but all my attempts end up in the following error:
cc1: error: argument to ‘-O’ should be a non-negative integer, ‘g’, ‘s’ or ‘fast’
So I tried to change the file's contents to only the parameters after -O1 (-freorder-functions), and add the -O1 in the shell script:
opts=$(printf "\"-O%d %s\"", 1, "$line")
(and a lot of other things that seem less sensible) but I still get the same error message.
Hard-coding the parameter to make in the shell script works fine:
make CFLAGS="-O1 -freorder-functions"
Sorry if this is a stupid question, but I can't seem to find any examples of how something like this is done, and I'm new to shell scripting, so I don't really understand how it's treating the variables here. An echo of what I'm attempting to pass to CFLAGS looks okay to me.
With double quotes around the flags in the file and you dutifully quoting your shell variables properly, your make call ends up being
make CFLAGS="\"-O1 -freorder-functions\""
That is to say, with double quotes in the flags. This is passed all the way down to the compiler call, which means that the compiler call is something like
cc "-O1 -freorder-functions" -c foo.c
...which asks the compiler to use optimization level 1 -freorder-functions, and it complains that that is not valid.
To solve this, I would remove the quotes from the file. You could also use opts=$line with $line unquoted, but that is not exactly safe.

How to assign the output of a command to a Makefile variable

I need to execute some make rules conditionally, only if the Python installed is greater than a certain version (say 2.5).
I thought I could do something like executing:
python -c 'import sys; print int(sys.version_info >= (2,5))'
and then using the output ('1' if ok, '0' otherwise) in a ifeq make statement.
In a simple bash shell script it's just:
MY_VAR=`python -c 'import sys; print int(sys.version_info >= (2,5))'`
but that doesn't work in a Makefile.
Any suggestions? I could use any other sensible workaround to achieve this.
Use the Make shell builtin like in MY_VAR=$(shell echo whatever)
me#Zack:~$make
MY_VAR IS whatever
me#Zack:~$ cat Makefile
MY_VAR := $(shell echo whatever)
all:
#echo MY_VAR IS $(MY_VAR)
Beware of recipes like this
target:
MY_ID=$(GENERATE_ID);
echo $MY_ID;
It does two things wrong. The first line in the recipe is executed in a separate shell instance from the second line. The variable is lost in the meantime. Second thing wrong is that the $ is not escaped.
target:
MY_ID=$(GENERATE_ID); \
echo $$MY_ID;
Both problems have been fixed and the variable is useable. The backslash combines both lines to run in one single shell, hence the setting of the variable and the reading of the variable afterwords, works.
I realize the original post said how to get the results of a shell command into a MAKE variable, and this answer shows how to get it into a shell variable. But other readers may benefit.
One final improvement, if the consumer expects an "environment variable" to be set, then you have to export it.
my_shell_script
echo $MY_ID
would need this in the makefile
target:
export MY_ID=$(GENERATE_ID); \
./my_shell_script;
Hope that helps someone. In general, one should avoid doing any real work outside of recipes, because if someone use the makefile with '--dry-run' option, to only SEE what it will do, it won't have any undesirable side effects. Every $(shell) call is evaluated at compile time and some real work could accidentally be done. Better to leave the real work, like generating ids, to the inside of the recipes when possible.
Wrapping the assignment in an eval is working for me.
# dependency on .PHONY prevents Make from
# thinking there's `nothing to be done`
set_opts: .PHONY
$(eval DOCKER_OPTS = -v $(shell mktemp -d -p /scratch):/output)
With GNU Make, you can use shell and eval to store, run, and assign output from arbitrary command line invocations. The difference between the example below and those which use := is the := assignment happens once (when it is encountered) and for all. Recursively expanded variables set with = are a bit more "lazy"; references to other variables remain until the variable itself is referenced, and the subsequent recursive expansion takes place each time the variable is referenced, which is desirable for making "consistent, callable, snippets". See the manual on setting variables for more info.
# Generate a random number.
# This is not run initially.
GENERATE_ID = $(shell od -vAn -N2 -tu2 < /dev/urandom)
# Generate a random number, and assign it to MY_ID
# This is not run initially.
SET_ID = $(eval MY_ID=$(GENERATE_ID))
# You can use .PHONY to tell make that we aren't building a target output file
.PHONY: mytarget
mytarget:
# This is empty when we begin
#echo $(MY_ID)
# This recursively expands SET_ID, which calls the shell command and sets MY_ID
$(SET_ID)
# This will now be a random number
#echo $(MY_ID)
# Recursively expand SET_ID again, which calls the shell command (again) and sets MY_ID (again)
$(SET_ID)
# This will now be a different random number
#echo $(MY_ID)
Here's a bit more complicated example with piping and variable assignment inside recipe:
getpodname:
# Getting pod name
#eval $$(minikube docker-env) ;\
$(eval PODNAME=$(shell sh -c "kubectl get pods | grep profile-posts-api | grep Running" | awk '{print $$1}'))
echo $(PODNAME)
I'm writing an answer to increase visibility to the actual syntax that solves the problem. Unfortunately, what someone might see as trivial can become a very significant headache to someone looking for a simple answer to a reasonable question.
Put the following into the file "Makefile".
MY_VAR := $(shell python -c 'import sys; print int(sys.version_info >= (2,5))')
all:
#echo MY_VAR IS $(MY_VAR)
The behavior you would like to see is the following (assuming you have recent python installed).
make
MY_VAR IS 1
If you copy and paste the above text into the Makefile, will you get this? Probably not. You will probably get an error like what is reported here:
makefile:4: *** missing separator. Stop
Why: Because although I personally used a genuine tab, Stack Overflow (attempting to be helpful) converts my tab into a number of spaces. You, frustrated internet citizen, now copy this, thinking that you now have the same text that I used. The make command, now reads the spaces and finds that the "all" command is incorrectly formatted. So copy the above text, paste it, and then convert the whitespace before "#echo" to a tab, and this example should, at last, hopefully, work for you.
In the below example, I have stored the Makefile folder path to LOCAL_PKG_DIR and then use LOCAL_PKG_DIR variable in targets.
Makefile:
LOCAL_PKG_DIR := $(shell eval pwd)
.PHONY: print
print:
#echo $(LOCAL_PKG_DIR)
Terminal output:
$ make print
/home/amrit/folder
From the make manual
The shell assignment operator ‘!=’ can be used to execute a shell script and set a >variable to its output. This operator first evaluates the right-hand side, then passes >that result to the shell for execution. If the result of the execution ends in a >newline, that one newline is removed; all other newlines are replaced by spaces. The >resulting string is then placed into the named recursively-expanded variable. For >example:
hash != printf '\043'
file_list != find . -name '*.c'
source

Resources