Makefile Target: Cannot assign variable from loop - unix

Problem is, furl doesn't get assigned value from file variable. If I use any plain text instead of $$file, it works. I have no idea why this doesn`t work.
Makefile:
test:
#for file in $(shell pwd)/demo/*; do \
$(eval furl := $(shell echo $$file)) \
echo $(furl); \
done
Im pretty sure this should be easy to fix, however I could not find a solution. Any ideas?
Im using the latest lubuntu OS.

You have messed shell and Make variables in the recipe. I think furl should be shell variable. Something like this:
test:; #for file in demo/*; do \
furl="`echo $$file`"; \
echo "$$furl"; \
done
In general you shouldn't assign Make variables in recipes. Obvious exception is $(foreach).

Related

Makefile, iterate over dirs and exclude some

In a toplevel (unix, GNU) Makefile I want to iterate over a list of subdirs and execute tasks in there but exclude some of them. In this case exclude all subdirs starting with an underscore (_)
The following is working in iterating but ignores the exclusion regex. It still visits dir "_exclude".
In particular I think it ignores the "start of string" ^ (or it takes it literally). I have tried other regexes and they do work. Any idea how to fix that? Or indeed if you have any idea how to eliminate the shelling out or not using GNU makefile extensions?
SHELL=/bin/bash
EXCLUDE_DIRS_REGEX=^_
# later addition:
ALLMAKEFILES = $(shell find . -maxdepth 2 -type f -name Makefile)
SUBDIRS = $(filter-out ./,$(dir $(ALLMAKEFILES)))
all:
for dir in $(SUBDIRS); do \
if [[ "$$dir" =~ $(EXCLUDE_DIRS_REGEX) ]]; then continue; fi; \
make -C $$dir all; \
done
You have two problems to solve:
compute the list of target directories
call make in each of them
For the first one a mixture of make and shell functions or built-ins could be something like:
REGEX := ^_
ALLMAKEDIRS := $(patsubst %/,%,$(dir $(wildcard */Makefile)))
SUBDIRS := $(shell for d in $(ALLMAKEDIRS); \
do ! [[ "$$d" =~ $(REGEX) ]] && echo "$$d"; done)
I kept the REGEX just in case you have more complex regular expressions or you would like it to be easily modified. But of course if it is exactly ^_ and you never change it the following is simpler:
SUBDIRS := $(filter-out _%,$(patsubst %/,%,$(dir $(wildcard */Makefile))))
For the second one your approach is not very make-ish. A better approach would be to have one rule per subdirectory. The following is an example:
.PHONY: all $(SUBDIRS)
all: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $# all
It has several advantages:
there is no need to loop over subdirectories in a complicated recipe, make will do it for you,
make can launch several jobs in parallel (see the -j option) and this will speed-up your build.
Note: calling make in a Makefile is not recommended. Prefer $(MAKE). See this section of the manual for the details.

Defining local variable in Makefile target

How to define local variable in Makefile target?
I would like to avoid repeating filename like:
zsh:
FILENAME := "text.txt"
#echo "Copying ${FILENAME}...";
scp "${FILENAME}" "user#host:/home/user/${FILENAME}"
But I am getting an error:
FILENAME := "text.txt"
/bin/sh: FILENAME: command not found
Same with $(FILENAME)
Trying
zsh:
export FILENAME="text.txt"
#echo "Copying ${FILENAME} to $(EC2)";
Gives me an empty value:
Copying ...
You can't define a make variable inside a recipe. Recipes are run in the shell and must use shell syntax.
If you want to define a make variable, define it outside of a recipe, like this:
FILENAME := text.txt
zsh:
#echo "Copying ${FILENAME}...";
scp "${FILENAME}" "user#host:/home/user/${FILENAME}"
Note, it's virtually never correct to add quotes around a value when assigning it to a make variable. Make doesn't care about quotes (in variable values or expansion) and doesn't treat them specially in any way.
The rules for a target are executed by the shell, so you can set a variable using shell syntax:
zsh:
#FILENAME="text.txt"; \
echo "Copying $${FILENAME}..."; \
scp "$${FILENAME}" "user#host:/home/user/$${FILENAME}"
Notice that:
I'm escaping end-of-line using \ so that everything executes in
the same shell
I'm escaping the $ in shell variables by writing $$ (otherwise
make will attempt to interpret them as make variables).
For this rule, which apparently depends on a file named text.txt,
you could alternatively declare text.txt as an explicit dependency and then write:
zsh: text.txt
#echo "Copying $<..."; \
scp "$<" "user#host:/home/user/$<"

Set a variable inside prerequisites or eval function

I have this code, and it works, but as you see I do the substitute three times, I would like to set a variable to the value, but with no success
$($(PKG)-py-valgrind-tests-status): $($(PKG)-swig-dlib)
$($(PKG)-py-valgrind-tests-status): $(OBJ_OUTPUT_DIR)%.valgrind_passed: %.py
#echo env $(PKG-TEST-HELPER-ENV) valgrind $(VALGRIND-FLAGS) --log-file=$(subst valgrind_passed,valgrind.log,$#) $(PYTHON_BIN) $< -v ; \
env $(PKG-TEST-HELPER-ENV) $(VALGRIND) $(VALGRIND-FLAGS) --log-file=$(subst valgrind_passed,valgrind.log,$#) $(PYTHON_BIN) $< -v \
|| (cat $(subst valgrind_passed,valgrind.log,$#); exit 1)
#touch $#
the problematic line $(subst valgrind_passed,valgrind.log,$#)
I tried:
$($(PKG)-py-valgrind-tests-status): LOG-FILE = $(subst valgrind_passed,valgrind.log,$#)
and
.SECONDEXPANSION:
$($(PKG)-py-valgrind-tests-status): LOG-FILE = $$(subst valgrind_passed,valgrind.log,$#)
and(inside the recipe)
$(eval LOG-FILE = $$(subst valgrind_passed,valgrind.log,$#))
but for all, if I write
--log-file=$(LOG-FILE)
log file come's up empty.
I have no more ideas on how to go forward,
appreciate the help, thanks!
Are you sure you're using GNU make? What version are you using (run make --version)?
There's nothing wrong with your first attempt, using a target-specific variable. If this doesn't work then you've got something wrong or different about your makefile that you haven't explained. Maybe if you showed the actual complete section of the makefile with the target-specific variable being set and used we might see what's wrong.
Your second attempt can't work because secondary expansion applies only the prerequisites, not target-specific variables (but, as above, it's not needed anyway).
Your third attempt might work but again, without seeing exactly what you do with the eval we can't say for sure. You don't need to double the $ before the subst function; it can be expanded first and it will still work.

Get parent dir, grandparent dir and so on in gnu make?

I have a variable path, for example this path from $(shell pwd):
C:\a\b\c\d\e\f
what i want to get is this and save it in a variable:
C:\a\b\c\d\e
C:\a\b\c\d
C:\a\b\c
C:\a\b
C:\a
C:\
how to do it in gnu make? And how to stop if there is no more parent (reached C:)
Ok, I'm going to switch into posix path mode.
No appologgies.
Life is too short to mess around with windows paths when cygwin is available.
(I will note though that $(dir) recognises backslashes.)
So, a function.
You just spit out the argument,
and then call the function again but this time with the last path component snipped off.
Something like
parents = $1 $(call parents,$(dir $1))
First problem:
$(dir a/b/c/d) returns a/b/c/.
Fine,
except that $(dir /a/b/c/) just gives you a/b/c/ again.
You need to strip that final slash before the call:
parents = $1 $(call parents,$(patsubst %/,%,$(dir $1)))
OK.
Problem now is this recursive call never terminates the sequence.
We need to stop calling parents once $1 has no slashes in it.
Several methods come to mind.
One way is to transliterate / into
(that's the job of $(subst …)),
then stop if the resulting number of words ($(words …)) is 1 ($(filter …)):
parents = \
$1 \
$(if $(filter-out,1,$(words $(subst /, ,$1))), \
$(call parents,$(patsubst %/,%,$(dir $1))))
(Hope I've got that nesting right.) Giving:
$ cat Makefile
parents = \
$1 \
$(if $(filter-out 1,$(words $(subst /, ,$1))), \
$(call parents,$(patsubst %/,%,$(dir $1))))
$(error [$(call parents,/a/b/c/d/e/f)])
$ make
Makefile:6: *** [/a/b/c/d/e/f /a/b/c/d/e /a/b/c/d /a/b/c /a/b /a ]. Stop.
:-)
Footnote: Dunno what you are trying to achieve,
but I reckon there is probably a more make-like way of doing it!

ifeq issue: compare 2 strings with a dot included

I am trying to implement a simple string comparison to get the type of a file (using its extension) like this:
extract_pkg: $(PKG)
$(eval EXT := $(suffix $(PKG)))
#echo $(EXT)
ifeq ($(EXT), .zip)
#echo "is zip file"
else
#echo "is not a zip file"
endif
extract_pkg : PKG = mypkg.zip
However, when I run it it goes into the else branch. My guess is, it has to do with the dot, but I dont find a solution. Thanks for your help !
Edit 1: the essential code would be actually somewhat like the following, and it works as expected:
test_cmp:
ifeq (.zip,.zip)
#echo ".zip==.zip"
endif
ifeq (zip,zip)
#echo "zip==zip"
endif
thus the problem is somewhere else !
One thing to be careful about -- spaces in if constructs are significant. So if you have something like:
ifeq ($(EXT), .zip)
it will only match if $(EXT) expands to exactly ".zip" -- including the space before the period. So your first example will always print is not a zip file, since $(EXT) will never contain the space.
You cannot use ifeq() etc. inside recipes. ifeq() are preprocessor statements: they are interpreted immediately as the makefile is read in. Recipes are not run until much later, after all makefiles are parsed and make decides that this target needs to be updated. So trying to set a variable in a recipe using eval, etc. then test that variable using ifeq() cannot work.
You have to use shell constructs for this; something like:
extract_pkg: $(PKG)
#EXT=$(suffix $<); \
echo $$EXT; \
if [ $$EXT = .zip ]; then \
echo "is zip file"; \
else \
echo "is not a zip file"; \
fi

Resources