I am seeking for a solution to only match files when wildcarding in make with pure make functions.
Say we have a file structure such as
Makefile
dir1/
file1
file2
dir2/
file3
where we want to obtain file1 and file2 without any hardcoding, i.e. without explicitly removing or adding files or directories, and where every piece of code is written in pure make. It is okay to assume the depth of the directories.
When writing
FILES_DEPTH1 = $(wildcard *)
FILES_DEPTH2 = $(wildcard */*)
FILES_DEPTH3 = $(wildcard */*/*)
these expands to
FILES_DEPTH1 = Makefile dir1
FILES_DEPTH2 = dir1/file1 dir1/file2 dir1/dir2
FILES_DEPTH3 = dir1/dir2/file3
which is not what we want. While one could solve this problem using find as shown here, I am only interested in solutions that only utilizes pure make functions. Does such a solution exist?
Try this:
DIRS := $(patsubst %/.,%,$(wildcard */*/.))
FILES := $(filter-out $(DIRS),$(wildcard */*))
(note, not tested...)
Related
In a makefile is there any way to specifiy the same element of a list (I think this is the correct term) more than once? For example, having a a list with 3 files A.txt B.txt C.txt, I'd like to create targets based on these elements that would be contained within a directory where the pattern would have to be repeated twice: A/A-rambo B/B-rambo C/C-rambo.
I tried:
TXT = A.txt B.txt C.txt
DIR := $(patsubst %.txt,%,$(TXT))
OUT := $(patsubst %,%/%-rambo,$(DIR))
$(info $(OUT))
but this prints A/%-rambo B/%-rambo C/%-rambo, where the second %is not being replaced.
And so does OUT := $(patsubst %,$(addsuffix /%-rambo,%),$(DIR))
Make cannot handle a pattern rule with two wildcards in the target name, even if they're required to have the same value.
There's more than one way to get the effect you want, none ideal. I'd suggest generating a rule for each txt file:
define template
$(1)/$(1)-rambo: $(1).txt
#echo building $$# from $$^
endef
STEMS := A B C
$(foreach x,$(STEMS),$(eval $(call template,$(x))))
Try:
OUT := $(foreach T,$(TXT),$(T:.txt=)/$(T:.txt=-rambo))
I have a variable
SRC = file1.csv file2.csv file3.csv
If I echo $(subst .csv,.,$(SRC)) or echo $(subst csv,,$(SRC)), I'll get
file1 file2 file3
What I looking for is
file1. file2. file3.
But GNU make keep stripping the dot. How to solve this?
Add:
My real lines has extra things like this:
SRC = $(shell dir /b /s $(SRC_ROOT)\*.csv))
echo $(subst .csv,.,$(basename $(notdir $(SRC))))
EDIT: Thanks guys, MadScientist was right, I have some additional $(basename $(notdir in the line that causes the problem. Below is my real lines and with MadScientist's sharp eye and comment, my solution turn out like this:
SRC = $(shell dir /b /s $(SRC_ROOT)\*.csv))
echo $(patsubst %csv,%,$(notdir $(SRC)))
I agree with Etan, your example works fine. I think you're doing something different in real life than you've described here.
I'll note though that using subst is a bad idea. If you had a file like cacsv20.csv then subst on csv would do the wrong thing.
I suggest you use patsubst, such as: $(patsubst %csv,%,$(SRC))
ETA: based on the updated question, you probably want:
SRC := $(shell dir /b /s $(SRC_ROOT)\*.csv))
BASE := $(patsubst %csv,%,$(notdir $(SRC)))
Use the notdir to take off the directory name, and the patsubst (not basename) to remove the suffix, since you want to keep the trailing period.
Note I'm using := rather than =; except in very special situations you always want to use := when you use $(shell ...), for performance reasons.
I was unable to find a way to get this done in a makefile, perhaps someone can help me out:
I have two lists one with filenames
$FILES = file1 file2 ...
and one with suffixes
$SUF = .suf1 .suf2 ...
and I want to get a list like
file1.suf1 file2.suf1 file1.suf2 file2.suf2 ...
how can I do this in a makefile?
Everything can always be done in a makefile, by definition.
First of all, your variable syntax is incorrect. Should be without $
FILES := file0 file1
Note that you should always use := unless you have a good reason to use =. (Also, if an interviewee started their array with 1 I would say "no hire". But that's maybe just me).
Now, one solution to your question is
$(foreach suf, $(SUF),$(addsuffix $(suf), $(FILES)))
Something like the following will work.
FILES = file1 file2 file3 file4
SUF = .suf1 .suf2 .suf3 .suf4
$(info $(foreach s,$(SUF),$(foreach f,$(FILES),$f$s)))
$(info $(foreach s,$(SUF),$(addsuffix $s,$(FILES))))
So some anonymous developers have decided to use a ridiculous convention of using spaces in their folder names that contain their source files. I would change these folders not to use spaces but sadly I don't make the rules around here so that's not an option (though I wish it were).
LUAC = luac
SRC_DIR = .
SOURCE = \
stupid/naming\ convention/a.lua \
stupid/naming\ convention/very\ annoying/b.lua \
vpath .lua $(SRC_DIR)
OUT_DIR = ../out/
OUTPUT = $(patsubst %.lua, $(OUT_DIR)/%.luac, $(SOURCE))
all: $(OUTPUT)
$(OUT_DIR)/%.luac: %.lua
$(LUAC) "$<"
mv luac.out "$#"
.PHONY: all
Simple Makefile. All it's meant to do is compile all the Lua files that I have and put them into an output directory.
No matter I do it keeps wanting to split the SOURCE string on the spaces in the folder, so I end with a beautiful error like this:
make: *** No rule to make target `stupid/naming ', needed by `all'. Stop.
Is there a way to fix this without renaming the folders?
Thanks in advance.
The very short, but IMO ultimately correct, answer is that make (not just GNU make, but all POSIX-style make implementations) does not support pathnames containing whitespace. If you want to use make, your "anonymous developers" simply cannot use them. If they insist that this is an absolute requirement you should switch to a different build tool altogether, that does support whitespace in filenames.
Yes, it's barely possible to create a makefile that will work with filenames containing whitespace, but you will essentially have to rewrite all your makefiles from scratch, and you will not be able to use many of the features of GNU make so your makefiles will be long, difficult to read, and difficult to maintain.
Just tell them to get over themselves. Or if they really can't, try having them create their workspace in a pathname without any whitespace in the names, then create a symbolic link containing whitespace pointing to the real workspace (the other way around won't work in all situations).
Unfortunately, GNU Make's functions that deal with space-separated list do not
respect the escaping of the space. The only exception is wildcard.
Edit:
Here's my workaround:
LUAC = luac
SRC_DIR = .
SOURCE = \
stupid/naming\ convention/a.lua \
stupid/naming\ convention/very\ annoying/b.lua \
vpath .lua $(SRC_DIR)
OUT_DIR = ../out/
OUTPUT = $(patsubst %.lua,%.luac,$(SOURCE))
all: $(OUTPUT)
%.luac: %.lua
$(LUAC) "$<"
mv luac.out "$#""
.PHONY: all
I tried to output it first like that:
%.luac: %.lua
#echo "$<"
#echo "$#""
Output looks as follows:
stupid/naming convention/a.lua
../out/stupid/naming convention/a.luac
stupid/naming convention/very annoying/b.lua
../out/stupid/naming convention/very annoying/b.luac
If you look at this excellent write up: http://www.cmcrossroads.com/article/gnu-make-meets-file-names-spaces-them, the author suggests that this is mostly a difficult task. But his substitution functions could get you going in case you really can't avoid the spaces.
Putting this into your makefile would look like this (sorry if I changed some of your paths, but this works on my Cygwin installation):
LUAC = luac
s+ = $(subst \\ ,+,$1)
+s = $(subst +,\ ,$1)
SRC_DIR = .
SOURCE := stupid/naming\\ convention/a.lua
SOURCE := $(call s+,$(SOURCE))
vpath .lua $(SRC_DIR)
OUT_DIR = out/
OUTPUT = $(patsubst %.lua, $(OUT_DIR)/%.luac, $(SOURCE))
all: $(call +s,$(OUTPUT))
$(OUT_DIR)/%.luac: %.lua
$(LUAC) "$<"
mv luac.out "$#"
.PHONY: all
I know that's not a complete answer, but maybe an encouragement that it actually is possible. But I agree with the other posters that if you can actually avoid spaces altogether, you will have a much easier life!
Another strategy which works when you are generating your Makefile automatically is this one, also used in Perl's ExtUtils::MakeMaker: to separate the name formatted to be usable in recipes, versus it being usable as a dependency. The example here has a THISFILE and a THISFILEDEP.
AWKWARD_DIR = sub dir
AWKWARD_DIRDEP = sub\ dir
THISFILE = $(AWKWARD_DIR)/d1
THISFILEDEP = $(AWKWARD_DIRDEP)/d1
AWKWARD_DIR_EXISTS = $(AWKWARD_DIR)/.exists
AWKWARD_DIR_EXISTSDEP = $(AWKWARD_DIRDEP)/.exists
TARGET = $(AWKWARD_DIR)/t1
TARGETDEP = $(AWKWARD_DIRDEP)/t1
MAKEFILE = spacemake.mk
$(TARGETDEP): $(THISFILEDEP) $(AWKWARD_DIR_EXISTSDEP)
cat "$(THISFILE)" >"$(TARGET)"
$(THISFILEDEP): $(AWKWARD_DIR_EXISTSDEP)
echo "yo" >"$(THISFILE)"
$(AWKWARD_DIR_EXISTSDEP): $(MAKEFILE)
#echo MAKEFILE = $(MAKEFILE)
-mkdir "$(AWKWARD_DIR)"
touch "$(AWKWARD_DIR_EXISTS)"
You can try it by placing it in a file called e.g. spacemake.mk, then run it with gmake -f spacemake.mk.
I have a makefile which looks for .txt files in a directory and for each file makes echo of it name.
pchs := $(wildcard $(OUTPUT:%=%/*.txt))
txt: $(pchs)
%.txt:
echo $#
But when I start it the make utility returns me that nothing to be done for txt. Why?
EDIT1:
After some answers I understand what I should make with my makefile. Now it looks like this:
pchs := $(wildcard $(OUTPUT:%=%/*.txt))
.PHONY : $(pchs)
txt: $(pchs)
%.txt:
#echo pch is '$<'
But .PHONY does not help me the result of making is the same.
Why does make says, that there ist nothing to do? Because make calculates dependencies of targets, usually file targets. And the "txt" target produces no file.
.PHONY is for targets, that produce no file, like the clean target.
This here should work:
pchs := $(wildcard $(OUTPUT:%=%/*.txt))
.PHONY: txt
txt: $(pchs)
echo $#
But, since you only echo the filename, I guess that you are post processing this output. Maybe you could formulate this post processing as a rule in the makefile?
Because makefiles define what you want to have built. And the .txt files already exist, so there is nothing to do.
To solve this there are a number of possibilities, but you should look into the .PHONY record if using gnu-make at least.
You can build fake-things out of the txt records and mark them as phony. But... it might just be easier to do this:
pchs := $(wildcard $(OUTPUT:%=%/*.txt))
txt:
for i in $(pchs) ; do echo $$i ; done
That's because every .txt file you've listed in $(pchs) is up-to-date and Make decides to take no action.
Add them to .PHONY target to force rebuilding them every time you run Make:
.PHONY : $(pchs)
UPD.
Also check that $(pchs) list is not empty, it could be done i.e. as follows:
txt : $(pchs)
#echo pchs is '$^'
I would use Bash to determine the *.txt files, instead of Make:
txt:
ls | grep -F '.txt'
You could also use this as a template to make a more general target, that echos any files that exist in the directory with a particular extension.
You may want the target to be PHONY, since it's not making a file.