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))))
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...)
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 want to check if all the files that have a specific name have in them a string , if not to report them . I wrote this sequence and tried multiple some others, but I don't know how to access the contain of a file from list.
SOURCES := $(shell find $(SOURCEDIR) -name 'mod.mak')#here I take the list of targeted files (this works fine)
$(foreach File, Files,
$(if $(grep -q "aaabbb" "$File"),,#echo "WARNING Missing sequence")
)
You have multiple issues with your script.
First of all, you need some rule/target. For your example we can make a PHONY target test. Second, to iterate over values in SOURCES, you need to reference it as $(SOURCES). Similarly for $(file) in call to grep. Also, make's if is interpreting value, not exit code, so you shouldn't silence grep.
This will do it:
.PHONY: test
SOURCES := $(shell find "$(SOURCEDIR)" -name 'mod.mak')
test:
$(foreach file,$(SOURCES),$(if $(shell grep "aaabbb" "$(file)"),,#echo "WARNING Missing sequence in $(file)"))
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 have a variable containing list of files separated with string _NEWLINE_. I need to output that variable into a file so that each file is in a separate line. The trick is that it needs to works on FreeBSD and Solaris.
This is what I am trying now:
echo "lib/alarms-1.2/priv/snmp_conf/agent.conf: lib/alarms/priv/snmp_conf/agent.conf_NEWLINE_lib/alarms-1.2/priv/snmp_conf/agent.conf.src: lib/alarms/priv/snmp_conf/agent.conf.src_NEWLINE_lib/alarms-1.2/priv/snmp_conf/community.conf: lib/alarms/priv/snmp_conf/community.conf" | sed 's|_NEWLINE_|\'$'\n|g'
This works on FreeBSD and in shell on Solaris. But when run in GNUmakefile on Solaris I am getting this (notice $ at the end of each line):
lib/alarms-1.2/priv/snmp_conf/agent.conf: lib/alarms/priv/snmp_conf/agent.conf$
lib/alarms-1.2/priv/snmp_conf/agent.conf.src: lib/alarms/priv/snmp_conf/agent.conf.src$
lib/alarms-1.2/priv/snmp_conf/community.conf: lib/alarms/priv/snmp_conf/community.conf$
If I remove \'$' from sed then it works on Solaris but doesn't on FreeBSD. Maybe there is a way of telling which version to use depending on which system the makefile is executed?
EDIT:
Thanks to the solution proposed by bobbogo I created an exemplary makefile that provides the desired outcome and seems to be working on both FreeBSD and Solaris:
one-line := lib/alarms-1.2/priv/snmp_conf/agent.conf: lib/alarms/priv/snmp_conf/agent.conf_NEWLINE_lib/alarms-1.2/\
priv/snmp_conf/agent.conf.src: lib/alarms/priv/snmp_conf/agent.conf.src_NEWLINE_lib/alarms-1.2/priv/snmp_conf/comm\
unity.conf: lib/alarms/priv/snmp_conf/community.conf
many-lines := { echo '$(subst _NEWLINE_,' && echo ',${one-line})'; }
.PHONY: all
all:
$(shell $(many-lines) > test.txt)
If this is GNU make, then do it all in make.
one-line := lib/alarms-1.2/priv/snmp_conf/agent.conf: lib/alarms/priv/snmp_conf/agent.conf_NEWLINE_lib/alarms-1.2/priv/snmp_conf/agent.conf.src: lib/alarms/priv/snmp_conf/agent.conf.src_NEWLINE_lib/alarms-1.2/priv/snmp_conf/community.conf: lib/alarms/priv/snmp_conf/community.conf
define \n
endef
many-lines := $(subst _NEWLINE_,${\n},${one-line})
Now ${many-lines} has just what you want. Annoyingly, it's quite hard to use in shell lines. If you do this:
tgt:
echo '${many-lines}'
make will invoke a separate shell for each line. The first shell invocation will get an un-paired ' and exit with an error.
.ONESHELL:
tgt:
echo '${many-lines}'
will work in an invasive sort of way. The proper fix is to ensure each line of ${many-lines} has valid sh syntax. Some mouthfull like:
echolines = $(subst ${\n},'${\n}echo ',echo '${many-lines}')
.PHONY: aa
aa:
$(call echolines,${many-lines})
Sheesh.
Tried many different solutions, including defining \n as mentioned in this answer: Add a newline in Makefile 'foreach' loop
The real problem is inconsistent implementation of the echo command across platforms, and the fact that by default make invokes shell commands using sh, which itself is quite inflexible.
I found a better way thanks to this answer: "echo -e" when called directly and when called via a shell-script
The better way is to use printf instead of echo
Construct the string with \n instead of _NEWLINE_ to separate parts that go into separate lines in the output file:
some_string = lib/alarms-1.2/priv/snmp_conf/target_params.conf: lib/alarms/priv/snmp_conf/target_params.conf\nlib/alarms-1.2/priv/snmp_conf/community.conf: lib/alarms/priv/snmp_conf/community.conf\n
and then in the makefile print it simply as this:
#printf "$(some_string)" >> $(some_file)
Works on both, FreeBSD and Solaris.
Disclaimer: I have no experience with Solaris or FreeBSD... here goes anyway.
In make, you can use $(patsubst pattern,replacement,text) to substitute a pattern.
try this...
FILENAMES := "lib/alarms-1.2/priv/snmp_conf/agent.conf: lib/alarms/priv/snmp_conf/agent.conf_NEWLINE_lib/alarms-1.2/priv/snmp_conf/agent.conf.src: lib/alarms/priv/snmp_conf/agent.conf.src_NEWLINE_lib/alarms-1.2/priv/snmp_conf/community.conf: lib/alarms/priv/snmp_conf/community.conf"
.PHONY: all
all:
#echo $(patsubst _NEWLINE_,${\n},$(FILENAMES))
As an alternative, I think your first approach will work, if you just double the $ to "escape" it:
sed 's|_NEWLINE_|\'$$'\n|g'