gmake function / ifneq/else/endif - function

I am trying to create a function that will determine if a directory exist, and if it does, add a target to the all list. but something is wrong. Here is the Makefile code snippet:
define buildMQ
$(info **** Checking to see if the MQ series directory exist *****)
ifneq "$(wildcard $(MQ_DIR) )" ""
$(info /opt/mqm was found)
MQ_APPS=MQSAPP
else
$(error $n$n**** ERROR - The MQ Series direcory: "$(MQ_DIR)" does not exist ******$n$n)
endif
endef
ifeq ('$(FN)' , 'TEST')
TEST_APPS=
endif
ifeq ('$(FN)' , 'ONSITE_TEST')
ONSITE_TEST_APPS= # insert ONSITE_TEST only apps here
$(call buildMQ)
endif
ifeq ('$(FN)' , 'ACCOUNT')
ACCOUNT_APPS=
$(call buildMQ)
endif
all:$(COMMON_APPS) $(TEST_APPS) $(ONSITE_TEST_APPS) $(ACCOUNT_APPS) $(MQ_APPS) makexit
and when I run it with FN = ONSITE_TEST:
**** Checking to see if the MQ series directory exist *****
/opt/mqm was found
Makefile:128: ***
**** ERROR - The MQ Series direcory: "/opt/mqm" does not exist ******
How can both print statements get printed? What am I missing?
The directory does exist

There's a lot of misunderstanding here about how call works. The call function takes a variable (name), plus zero or more arguments. It assigns the arguments to $1, $2, etc. and then it expands the variable.
Note that by "expands" here we don't mean "interprets the variable value as if it were a makefile". We mean very simply, go through the value of the variable and locate all make variables and functions and replace them with their appropriate values.
So, you invoke $(call buildMQ). This will not assign any values to $1, etc. since you didn't provide any arguments: in effect this is exactly the same as just using $(buildMQ); the call function has no impact here.
So make expands the value of the buildMQ variable... basically it takes the value as one long string:
$(info **** Checking to see if the MQ series directory exist *****) ifneq "$(wildcard $(MQ_DIR) )" "" $(info /opt/mqm was found) MQ_APPS=MQSAPP else $(error $n$n**** ERROR - The MQ Series direcory: "$(MQ_DIR)" does not exist ******$n$n) endif
and expands it. So first it expands the $(info ... Checking ... function and prints that. Then it expands the $(wildcard ..) and replaces that. Then it expands the $(info /opt/mqm ...) and prints that. Then it expands the $(error ...) and shows the message and exits.
If it hadn't exited, then you'd have a syntax error because a function like call cannot expand to a multi-line statement; as above it's not evaluated like a set of makefile lines. It has to expand to a single value makefile line.
You need to use the eval function if you want make to parse the contents of a variable as if it were a makefile; eval doesn't take a variable name it takes a string to parse, so it would be:
$(eval $(buildMQ))
However, this won't do what you want for the same reason: it expands the buildMQ variable and that causes all the functions to be expanded first, before eval even sees them.
One option would be to escape all the variables and functions reference in buildMQ. But in your situation a simpler solution is to use the value function to prevent expansion before eval sees the value:
$(eval $(value buildMQ))

Related

I can not understand the output of patsubst in a makefile

This is the guilty makefile :
$ cat -n example.mak
1 define this
2 $(patsubst $(1)/%.o,%.o,why_this_does/that.o)
3 $(patsubst butnot/%.o,%.o, butnot/but_not_that.o)
4 endef
5
6 why:
7 $(info $(call this, why_this_does) ?)
And this is my question :
$ make -f example.mak
why_this_does/that.o
but_not_that.o ?
make: 'why' is up to date.
The root cause is not in patsubst but in call. The manual has a note:
A final caution: be careful when adding whitespace to the arguments to call. As with other functions, any whitespace contained in the second and subsequent arguments is kept; this can cause strange effects. It’s generally safest to remove all extraneous whitespace when providing parameters to call.
and indeed, if you replace
$(info $(call this, why_this_does) ?)
by
$(info $(call this,why_this_does) ?)
you get what you want.

How to evaluate if a environment variable is set in vimrc

How to evaluate if an bash environment variable is set
for example
function! Myfoo(arg)
if $SomeVar is set/exist ----> how to eval the SomeVar has been set
....
endif
endfunction
You've (intuitively?) used the correct syntax; as :help expression-syntax explains (under :help expr-env), the syntax is $VAR.
You can compare with an empty string (if $SomeVar != "") or use empty() (if !empty($SomeVar)) to check whether a (non-empty) value has been supplied. It's not so easy to differentiate between empty environment variable and non-existing environment variable, so this is best avoided. (This distinction also is rarely used in shell scripts itself, neither.)

Not able to get entire text of current buffer in vim

I am trying to get entire text of current buffer. I believe it is represented by '%' (see answer by SnoringFrog at https://vi.stackexchange.com/questions/2319/is-there-a-text-object-for-the-entire-buffer). However, following function gives an error:
function Check ()
echo %
endfunction
I call it with following command:
:call Check()
The error is:
Error detected while processing function Check:
line 1:
E15: Invalid expression: %
E15: Invalid expression: %
Where is the problem and how can it be solved?
Depending on the context, % can be a shortcut for the 1,$ range or a placeholder for the filename associated with the current buffer.
In the first case (the one in your link), it's not meant to be echoed at all which would be completely pointless.
In the second case, it needs to be expanded with expand('%') if you want to use it in a function.
Anyway, none of that matters because % is not what you want at all. What you want is :help getline():
function Check ()
echo getline(1,'$')
endfunction

Printing variables including functions from Makefile and/or variable introspection

If you iterate over .VARIABLES and print each, any true variable can be printed correctly with the following rule:
print_variables: $(foreach V,$(.VARIABLES),print-$(V)) .phony_explicit
print-%: .phony_explicit; #echo "$* = \"$($*)\""
.PHONY: .phony_explicit ...
A 0- or 1-line function will still work, but any more will result in Syntax error: Unterminated quote string. Just one multiline function will break the entire print_variables rule. As a workaround, I have added ;\ to each line in my function definitions, but that won't fix existing multiline functions (either via includes from this makefile or via other makefiles including this one.) What can I do? Is there a container of just function variables, or a way to test if a variable is a function definition?
A simple minimal example would be easier to understand; this has nothing to do with .VARIABLES, pattern rules, etc. (and I'm not sure what the point of the .phony_explicit prereq is..)
define F
foo
bar
endef
print: ; echo "F = $(F)"
will show the problem:
echo "F = foo
/bin/sh: 1: Syntax error: Unterminated quoted string
This is because when make sees a variable that contains newlines in a recipe, it assumes that the newlines mean you want the lines of the variable to become lines in the recipe.
First in general you should use single-quotes around strings you send to the shell, unless you need the shell to expand them; it won't help in this situation but in general it's much safer.
There's no way to undo that, really. You have a number of options.
The first is to not use echo but instead use the make function info:
print-F: ; $(info F = "$(F)")
yields:
F = "foo
bar"
Another option is to use subst to replace the newlines with some other value. The new value cannot itself contain explicit newlines, but you can ask the shell to print a newline for you:
# Create a variable containing a single newline
# Note this must contain TWO newlines!
define NL
endef
print-F: printf 'F = "$(subst %,%%,$(subst $(NL),\n,$(F))"\n'
Yields:
printf 'F = "foo\nbar"\n'
F = "foo
bar"
One final option is to convert your makefile to use the .ONESHELL feature, but I assume that's a step too far just to get this debugging output available :).

GNU make call function with multiple arguments and multiple commands

I am trying to write a GNU make call function (example below) which has multiple shell commands to execute, such that it can be called with different arguments.
shell_commands = $(shell echo $(1); ls -ltr $(2))
try:
$(call shell_commands,$(FILE1),$(FILE2))
1) Is above the correct way to write a call function with multiple commands? By using a semi-colon to separate them? To make it readable, I write my targets as shown below. Is there a similar way to write a call function?
shell_commands:
echo $(1)
ls -ltr $(2)
2) I get this error from make when I execute make -B try. It looks like it is trying to execute /home/user/file1. But why?
make: execvp: /home/user/file1: Permission denied
make: *** [try] Error 127
3) Is it possible to pass variable number of parameters to a call function? Like pass in just the second parameter and not the first one.
$(call shell_commands,,$(FILE2))
I tried google-ing, searching on SO, and looking on gnu.org but I did not get any solutions. Will appreciate any answers or pointers to any resources which document call function with multiple optional arguments and commands.
Question 1: No, this is not right. The shell make function should NEVER be used inside a recipe: the recipe is already running in the shell, so why would you run another shell? It's just confusing. Second, it's generally recommended to use && between multiple commands in a recipe, so that if the first command fails the entire command will immediately fail, rather than continuing on and perhaps succeeding. Of course, that is not always correct either, it depends on what you're trying to do.
Question 2: This happens because the shell make function is like backticks in the shell: it expands to the output printed by the shell command it runs. Your shell command that make runs is:
echo $(1); ls -ltr $(2)
(where, one assumes, $1 expands to /home/user/file1) which prints the string /home/user/file1. After the expansion, that string is substituted into the recipe and make tries to run that recipe, giving the error you see above.
You want this, most likely:
shell_commands = echo $(1) && ls -ltr $(2)
try:
$(call shell_commands,$(FILE1),$(FILE2))
Now the call expands to the actual text, not an invocation of make's shell function, and then that text is run as the recipe.
Question 3: Sure, just using empty parameters means that the variable $1 (in this case) expands to the empty string.