Trim an environmental variable in makefile - makefile

I maintain a project which provides makefile for Arduino
There is a master (common) makefile and the users create a small makefile where they set certain environmental variables and then include the master makefile.
A typical makefile looks likes this
# Arduino Make file. Refer to https://github.com/sudar/Arduino-Makefile
BOARD_TAG = atmega328
ARDUINO_PORT = /dev/ttyACM*
include $(ARDMK_DIR)/arduino-mk/Arduino.mk
Recently, I was debugging a bug and then found out that if there is an additional space at the end of BOARD_TAG environmental variable, then it results in an error, which is not very intuitive for someone to fix.
My initial thought was to fix it by trimming the environmental variable inside the makefile. But on second thought I was not sure whether that is the correct way to fix this issue.
So my question is what is the recommended way to fix these kind of user input issues in makefile? Should I try to trim it or is there an alternate way to fix this?

To trim the variable "in place", just treat it as a simply expanded variable:
BOARD_TAG = atmega328
BOARD_TAG := $(strip $(BOARD_TAG))
If you use "=", it's a recursive expanded variable, so any expression on the right (e.g. $(strip ...)) will not be expanded until the variable itself is evaluated; if you try to put the variable itself on the right, Make warns you that that expression can't be evaluated because it goes into an infinite loop.
If you use ":=", it becomes a simply expanded variable; Make expands the right-hand side then and there, assigns the result to the variable and that's that.

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.

Invoking a Make target in a loop with an argument

I am designing a simple makefile that defines one target which takes an argument, and I would like to define a second target that invokes the first target in a loop, once per every variable defined in an array variable at the top of the Makefile.
my_loop_var = var1 var2
my_thing:
echo $MY_VAR
all:
invoke my_thing once for each value of my_loop_var
What is the right way to solve this?
Note: $MY_VAR will probably expand as Y_VAR because make will try to expand variable M, which is probably undefined, and concatenate Y_VAR to the result. Use $(VARNAME) to expand make variable VARNAME. It is only with single-character variable names that you can expand with $X.
There is not really a "right way". There are many ways. The most straightforward from your specifications (but the less natural) would be something like:
my_loop_var := var1 var2
my_thing:
echo "$(MY_VAR)"
.PHONY: all my_thing
all:
for v in $(my_loop_var); do $(MAKE) my_thing MY_VAR="$$v"; done
The recipe of all is a shell loop that re-invokes make with the my_thing goal and with each value in my_loop_var passed as the value of make variable MY_VAR.
Note the $$v to escape the first expansion that make always performs before passing the recipe to the shell. With just $v make would expand the recipe as ... MY_VAR="" ... and the result would not be what you expect.
Note also the use of the MAKE make variable instead of directly calling make (have a look at the documentation if you want to understand why it is better like this).
all and my_thing are declared as phony because they are not real file names that their recipes create, and make needs to know this kind of things.
But a much more make-ish way would be something like:
my_loop_var := var1 var2
my_thing_targets := $(addprefix my_thing_,$(my_loop_var))
.PHONY: all $(my_thing_targets)
all: $(my_thing_targets)
$(my_thing_targets): my_thing_%:
echo "$*"
Here we define as many my_thing_something targets as there are something values in my_loop_var. And explain make how to build such targets with a static pattern rule. In the recipe of a pattern rule the $* automatic variable expands as the part that matched the % pattern. So, this static pattern rule says that if we need to build my_thing_something, the recipe is echo "something".
We declare all these my_thing_something targets as prerequisites of all such that if you type make or make all, make will build all the my_thing_something targets.
This second solution is better for at least two reasons. First, make is invoked only once, which is better, at least for performance. Second, make can parallelize the build of the my_thing_something if you use the -j N option (to allow make to run up to N jobs in parallel). This also is better for performance.
But it is also a matter of style. Very frequently if you use shell loops in your recipes, especially to invoke make again, it is the sign that you did not really understand what make is intended for and how it works. The make language is not a scripting language (even if the recipes are written in shell language, which is a scripting language); make is designed to "loop" over all targets to build, without the need for explicit loops.

Makefile expanding variables inside conditionals depends on order of definition

I want to define a variable differently depending on another variables value in a makefile. I thought using conditionals would solve the problem, like this in the makefile:
ifeq ($(BOOT_FLAG),installed)
BOOT_TEST=$(BOOT_FLAG)
else
BOOT_TEST=no
endif
BOOT_DEFINE=$(BOOT_FLAG)
BOOT_FLAG=installed
.PHONY: all
all:
#echo $(BOOT_TEST)
#echo $(BOOT_DEFINE)
I expected the output to be:
installed
installed
but I got this instead:
no
installed
apparently the ifeq does not expand the BOOT_FLAG to installed
but setting of the BOOT_DEFINE variable manages to expand it correctly.
I read in the manual that:
"make evaluates conditionals when it reads a makefile. Consequently, you cannot use automatic variables in the tests of conditionals because they are not defined until commands are run"
but the BOOT_FLAG is not an automatic variable. Also if I move the definition of BOOT_FLAG to before the ifeq, then it works as I want it. However, I want to keep the current order of the definitions (and I don't understand why make does an exception to the order independence of the definitions when using conditions)
The answer is right there in the statement you quoted:
make evaluates conditionals when it reads a makefile.
Since make has evaluated the conditional when it read that line in the makefile, and the variable has not been defined when it read that line, there's no way that variables set after the conditional can take effect.
Just because the documentation lists one consequence of this behavior (the one that most people get confused by) doesn't mean that this is the only consequence of this behavior.
However, I want to keep the current order of the definitions
You can't.
(and I don't understand why make does an exception to the order independence of the definitions when using conditions)
It would be virtually impossible, and even if it could be done the resulting behavior would be almost indecipherable except in the most trivial situations. If you don't believe me, try to write down an algorithm describing how that could work. Remember to consider things like simple variable assignments, nested conditionals, variables used in target and prerequisite lists, variables that are intentionally reset in different parts of makefiles, etc.
ETA You could do it, by putting the ifeq into a define variable then using eval later, after BOOT_FLAG is set, to expand it. Seems gross to me but...
This is because makefile is evaulating the ifeq as it parses the file.
So when it gets to the ifeq..., then BOOT_FLAG is yet not set, so BOOT_TEST = no
Then you set BOOT_FLAG.
Then once all the variables are parsed, makefile will go through and run your rule - so in this case BOOT_DEFINE is evaluated to $(BOOT_FLAG) final value of installed
Try this:
$(info start - BOOT_FLAG=$(BOOT_FLAG))
ifeq ($(BOOT_FLAG),installed)
BOOT_TEST=$(BOOT_FLAG)
else
BOOT_TEST=no
endif
$(info after if - BOOT_FLAG=$(BOOT_FLAG))
BOOT_DEFINE=$(BOOT_FLAG)
BOOT_FLAG=installed
$(info after assignment - BOOT_FLAG=$(BOOT_FLAG))
.PHONY: all
all:
#echo $(BOOT_TEST)
#echo $(BOOT_DEFINE)
You will see various values printed at different times during the makefile parsing. On the first pass it evaluates the variables (and if's) and then on the second pass it can do the target rules.
As others noted the problem is that ifeq is expanded and evaluated in-place.
If you want to postpone the evaluation until some late moment, you must keep the whole expression inside of a recursive variable. Then the conditional could be implemented by $(if ...) function, instead of ifeq (okay, $(eval ifeq...) should also be doable, but... well, gross).
Of course, this is quite an overhead for such simple case, but nonetheless it could be done like this:
BOOT_TEST=$(if $(subst _installed,,_$(BOOT_FLAG)),no,installed)
BOOT_DEFINE=$(BOOT_FLAG)
BOOT_FLAG=installed
.PHONY: all
all:
#echo $(BOOT_TEST)
#echo $(BOOT_DEFINE)

can't use variable in Makefile

My main Makefile call config.mk
include $(TOPDIR)/config.mk
then config.mk include some sentences like this:
ifdef CPU
sinclude $(TOPDIR)/cpu/$(CPU)/config.mk
endif
ifdef SOC
sinclude $(TOPDIR)/cpu/$(CPU)/$(SOC)/config.mk
endif
I have prepared these two tree and necessary config.mks. But for "SOC", whose value is "versatile", there is a problem. If I put "versatile" directly here, it could find the file and everything is fine; but when I use $(SOC), il will meet an error, and say
/../../../cpu/arm926ejs/versatile: is a folder, stop
Anyone know what the problem is ??
Are you sure you gave the exact error message? What version of make are you using? That error doesn't look like anything GNU make would print.
Anyway, I'll bet the problem is that your assignment of the SOC variable has trailing whitespace. According to the POSIX definition of make, leading whitespace before a variable value is removed, but trailing whitespace is preserved. That means, for example, if you write your makefile like this:
SOC = versatile # this is the versatile SOC
then make will remove the comment, but keep the space, so the value will be 'versatile' (space at the end). This means when the value is expanded in the sinclude line you get:
sinclude $(TOPDIR)/cpu/$(CPU)/versatile /config.mk
which make interprets as trying to include two different values, the first of which is a directory.
Even if you don't have a comment there, any trailing whitespace will be preserved. When editing makefiles you should try to put your editor into a mode where it flags trailing whitespace, or even better removes it automatically. GNU Emacs, for example, can do this.

Makefile define variable using if

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=

Resources