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.
Related
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...)
If I have a list of files:
files := xx_foo1.c yy_foo2.c zz_bar1.c aa_bb_bar2.c
Is there any way of removing everything up to the last underscore from the list to get foo1.c foo2.c bar1.c bar2.c?
I was looking into using patsubst, but I would need two%'s -- one for the first part to be ignored, and one for the last part to be kept.
It can be done but it's a little gross. You want something like this:
final := $(foreach F,$(files),$(word $(words $(subst _, ,$F)),$(subst _, ,$F)))
This says, for each element in files we convert the _ to a space, now we can use our per-word functions to manipulate it: extract the last word in the list of words.
ETA
ReAl points out below that this can be simplified using lastword:
final := $(foreach F,$(files),$(lastword $(subst _, ,$F)))
As I see it you are using the underscore as separating character between hierarchical names. GNUmake is well equipped to work with such a scheme if the character is /: file name functions.
So your example should simply boil down to
$(notdir $(subst _,/,$(files))
Use the external program — sed — and enjoy all its power
files := xx_foo1.c yy_foo2.c zz_bar1.c aa_bb_bar2.c
f := `echo $(files) | sed -e "s/[[:alnum:]]*_//g"`
all:
echo $(f)
as i am currently working on my makefile i encountered another problem. I am using this rule as part of my building process which transforms .mid files into .s files.
$(MIDAS): $(BLDPATH)/%.s: %.mid
$(shell mkdir -p $(dir $#))
#test $($< | sed "*")
$(MID2AGB) $(MIDFLAGS) -G$($< | sed ".*mus/vcg([0-9]{3})/.*\.mid") $< $#
All .mid input files follow the same format: .mus/vcg[0-9]{3}/..mid, meaning they are stored in different directories following the naming convention vcgXXX where X can be any digit from 0-9. (Maybe my regex is even wrong for this).
When i am calling $(MID2AGB) i want to use a compiler flag -GXXX. However the XXX of this flag has to match the XXX from the input file path.
My makefile code does not work. Any idea how to fix this problem?
There is a crude but effective way to do this using Make's string manipulation tools:
# suppose the source is .mus/vcg456/Z.mid
$(MIDAS): $(BLDPATH)/%.s: %.mid
#echo $* # this gives .mus/vcg456/Z
#echo $(subst /, ,$*) # this gives .mus vcg456 Z
#echo $(word 2,$(subst /, ,$*) # this gives vcg456
#echo $(subst vcg,,$(word 2,$(subst /, ,$*)) # this gives 456
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.