Variable dereferncing in makefiles - shell
Given a list of paths I want to separate out the directory part and the filename part of each of the element of the list inside a makefile. Something like following
MYLIST = \
/home/folder1/folder2/fileName1 \
/home/folder3/folder4/fileName2 \
/home/folder5/folder6/fileName3 \
MYLIST:
#for elems in $(MYLIST); \
do \
echo $(dir $$elems); \
echo $(notdir $$elems); \
done
But there is a problem with the variable dereferencing. I get the output as
./
home/folder1/folder2/fileName1
./
home/folder3/folder4/fileName2
./
home/folder5/folder6/fileName3
whereas i want it to be
/home/folder1/folder2/
fileName1
/home/folder3/folder4/
fileName2
/home/folder5/folder6/
fileName3
Somehow $(#D) and $(#F) are not giving all the dir and fileName parts just the first one in the list.
Can somebody please tell how to get about this problem ?
This occurs because you're mixing two stages of expansion. Before invoking the shell to execute the rule all make variables and functions are expanded. So $$elems becomes $elems and this string is then used as the input for the $(dir ...) and $(notdir ...) functions. This string doesn't contain a /, so dir returns ./, and notdir returns $elems. In the end, the following command is executed in the shell.
#for elems in /home/folder1/folder2/fileName1 /home/folder3/folder4/fileName2 /home/folder5/folder6/fileName3; \
do \
echo ./; \
echo $elems; \
done
William Pursell has given a possible workaround by using shell functions. Another possibility would be to perform the expansion before execution of the rule, like such:
MYLIST = \
/home/folder1/folder2/fileName1 \
/home/folder3/folder4/fileName2 \
/home/folder5/folder6/fileName3 \
MYDIRS = $(dir $(MYLIST))
MYFILES = $(notdir $(MYLIST))
MYLIST:
#for elems in $(MYDIRS) $(MYFILES); \
do \
echo $$elems; \
done
$(#D) and $(#F) are not giving you all dir and fileName parts because they give the file and directory of the target of the current invokation of the rule. There is only one target at each moment. You may be able to use these automatic variables, however, to do what you want, by letting make do the looping, instead of the shell, like this:
MYLIST = \
/home/folder1/folder2/fileName1 \
/home/folder3/folder4/fileName2 \
/home/folder5/folder6/fileName3 \
all: $(MYLIST)
$(MYLIST):
#echo $(#D)
#echo $(#F)
I would suggest using the shell instead of make:
MYLIST = \
/home/folder1/folder2/fileName1 \
/home/folder3/folder4/fileName2 \
/home/folder5/folder6/fileName3
MYLIST:
for elems in $(MYLIST); \
do \
echo $$(dirname $$elems); \
echo $$(basename $$elems); \
done
Of course, at this point, the echo is redundant, and you could just as well do:
for elems in $(MYLIST); \
do \
dirname $$elems; \
basename $$elems; \
done
I found another way to do that now...
MYLIST = \
/home/folder1/folder2/fileName1 \
/home/folder3/folder4/fileName2 \
/home/folder5/folder6/fileName3
MYLIST:
#$(foreach ELEMS,$(MYLIST), echo $(dir $(ELEMS)); echo $(notdir $(ELEMS));)
Don't know how i missed this before.
Related
For loop values in a Makefile not being used as arguments to a shell command
I'm trying to use a Makefile to iterate over several date values and execute a python script for each one, here's the Makefile I'm using (Makefile.study.s1): include Makefile # Dates to test SNAP_TST := 2019-10-12 2020-02-08 2020-10-10 2021-01-02 2021-07-24 2021-12-31 2022-05-27 buildDataset: for date in $(SNAP_TST) ; do \ python src/_buildDataset.py --table $(TABLE) \ --nan-values $(NAN_CONFIG) \ --patterns-rmv $(PATTERNS_RMV) \ --target-bin $(TARGET_CLASS) \ --target-surv $(TARGET_SURV) \ --target-init $(TARGET_INIT) \ --train-file $(DATA_TRN) \ --test-file $(DIR_DATA)/main/churnvol_test_$$date.csv \ --train-date $(SNAP_TRN) \ --test-date $$date \ --config-input $(CONFIG_INPUT) \ --feats $(FEATS) ; \ done .PHONY: buildDataset When I run make -f Makefile.study.s1 buildDataset it replaces the value date with the string "$date" instead of one of the dates in SNAP_TST. Can you guys help me figure out what I did wrong here, and how I can fix this makefile so that $$date is replaced with one of the dates in SNAP_TST? Thank you in advance.
Bazel genrule: Use linebreaks in command
I use a genrule with a lot of sources, that have a long identifier. The command needs to list all sources explicitely, which would result in a reeaally long cmd. Therefore I tried to use linebreaks (as known from bash or shell commands)... However, bazel complains about unterminated strings. genrule( name = "Aggregate_Reports", srcs = ["//really/long/path/to/module/ModuleA/src:CoverageHtml", "//really/long/path/to/module/ModuleA/src:TestRun", "//really/long/path/to/module/ModuleB/src:CoverageHtml",], outs = ["UT_Summary.txt"], message = "Create unified report", tools = [":Create_Summary"], cmd = "$(location :Create_Summary) -t \ $(location //really/long/path/to/module/ModuleA/src:TestRun) \ $(location //really/long/path/to/module/ModuleB/src:TestRun) \ -c \ $(location //really/long/path/to/module/ModuleA/src:CoverageHtml) \ $(location //really/long/path/to/module/ModuleB/src:CoverageHtml) \ -o $(#)", executable = True, visibility=["//visibility:public"], ) Escaping the \ with $ does not change anything...
As in Python, you can use triple-quotes to preserve the newlines: cmd = """$(location :Create_Summary) -t \ $(location //really/long/path/to/module/ModuleA/src:TestRun) \ $(location //really/long/path/to/module/ModuleB/src:TestRun) \ -c \ $(location //really/long/path/to/module/ModuleA/src:CoverageHtml) \ $(location //really/long/path/to/module/ModuleB/src:CoverageHtml) \ -o $(#)""",
Converting Windows path to Unix path in a makefile
This question is related to Convert Cygwin path to Windows path in a makefile but it is not the same. I need to convert a Windows path like: C:\src\bin into a Unix path like: /c/src/bin Inside a makefile, I can use the following code to convert such paths: slashedpath = $(subst \\,\/,$(windowspath)) unixpath = $(shell cygpath -u $(slashedpath)) How can I perform the same conversion in a makefile that is being processed by GNU Make, when the cygpath function is not available? p.s. What if $(windowspath) contains multiple paths? How to convert them all ?
The makefile: windowspath=C:\src\bin unixpath=$(subst \,/,$(subst C:\,/c/,$(windowspath))) all: #echo "$(windowspath)" #echo "$(unixpath)" gives the output: C:\src\bin /c/src/bin This will also work if $(windowspath) contains multiple paths. Tested on GNU Make 4.2.1 for i686-pc-cygwin, and also on GNU Make 3.81 built for i686-redhat-linux-gnu. I was surprised that this worked. Update: This second version will handle various drives such as C:, D:, etc. Some of these ideas are from Eric Melski's answer to In GNU Make, how do I convert a variable to lower case?. If the Makefile is: DRIVE = $(subst \ A:,/a,$(subst B:,/b,$(subst C:,/c,$(subst D:,/d,$(subst \ E:,/e,$(subst F:,/f,$(subst G:,/g,$(subst H:,/h,$(subst \ I:,/i,$(subst J:,/j,$(subst K:,/k,$(subst L:,/l,$(subst \ M:,/m,$(subst N:,/n,$(subst O:,/o,$(subst P:,/p,$(subst \ Q:,/q,$(subst R:,/r,$(subst S:,/s,$(subst T:,/t,$(subst \ U:,/u,$(subst V:,/v,$(subst W:,/w,$(subst X:,/x,$(subst \ Y:,/y,$(subst Z:,/z,$1)))))))))))))))))))))))))) drive = $(subst \ a:,/a,$(subst b:,/b,$(subst c:,/c,$(subst d:,/d,$(subst \ e:,/e,$(subst f:,/f,$(subst g:,/g,$(subst h:,/h,$(subst \ i:,/i,$(subst j:,/j,$(subst k:,/k,$(subst l:,/l,$(subst \ m:,/m,$(subst n:,/n,$(subst o:,/o,$(subst p:,/p,$(subst \ q:,/q,$(subst r:,/r,$(subst s:,/s,$(subst t:,/t,$(subst \ u:,/u,$(subst v:,/v,$(subst w:,/w,$(subst x:,/x,$(subst \ y:,/y,$(subst z:,/z,$1)))))))))))))))))))))))))) windowspath = c:\src\bin D:\FOO\BAR unixpath = $(subst \,/,$(call DRIVE,$(call drive,$(windowspath)))) all: #echo Original: "$(windowspath)" #echo Modified: "$(unixpath)" then the output to make is: Original: c:\src\bin D:\FOO\BAR Modified: /c/src/bin /d/FOO/BAR Update 2: The most straight-forward approach, and the most flexible, is to use a standard regular-expression handler such as perl or sed, if these are available. For example, with GNU sed, this Makefile will work as required: windowspath = c:\src\bin D:\FOO\BAR unixpath = $(shell echo '$(windowspath)' | \ sed -E 's_\<(.):_/\l\1_g; s_\\_/_g') all: #echo Original: "$(windowspath)" #echo Modified: "$(unixpath)" Explanation of sed: s_\<(.):_/\l\1_g For every word starting with something like A: or a:, replace the start with /a. s_\\_/_g Replace all backslashes with forward slashes.
Explain this makefile recipe construction
install_target: first FORCE if not exist C:\Qt\Qt5.1.1\5.1.1\Sources\qtbase\bin\ \ C:\Qt\Qt5.1.1\5.1.1\static\?\?C:\Qt\Qt5.1.1\5.1.1\static\?\lib \ mkdir C:$(INSTALL_ROOT)\Qt\Qt5.1.1\5.1.1\Sources\qtbase\bin\? \ C:\Qt\Qt5.1.1\5.1.1\static\?\?C:\Qt\Qt5.1.1\5.1.1\static\?\lib & \ if not exist C:\Qt\Qt5.1.1\5.1.1\Sources\qtbase\bin\? \ C:\Qt\Qt5.1.1\5.1.1\static\?\?C:\Qt\Qt5.1.1\5.1.1\static\?\lib exit 1 What does question mark mean?
Disable all but few warnings in gcc
In gcc -w is used to disable all warnings. However in this case I can't enable specific ones (e.g. -Wreturn-type). Is it possible to disable all warnings, but enable few specific ones? As a workaround, is there a way to generate list of all -Wno-xxx at once? And will it help? I wouldn't want to do this manually just to find out that it is not equal to -w.
You can use the following command to get an WARN_OPTS variable suitable for injecting directly into your Makefile: gcc --help=warnings | awk ' BEGIN { print "WARN_OPTS = \\" } /-W[^ ]/ { print $1" \\"} ' | sed 's/^-W/ -Wno-/' >makefile.inject This gives you output (in makefile.inject) like: WARN_OPTS = \ -Wno- \ -Wno-abi \ -Wno-address \ -Wno-aggregate-return \ -Wno-aliasing \ -Wno-align-commons \ -Wno-all \ -Wno-ampersand \ -Wno-array-bounds \ -Wno-array-temporaries \ : : : -Wno-variadic-macros \ -Wno-vector-operation-performance \ -Wno-vla \ -Wno-volatile-register-var \ -Wno-write-strings \ -Wno-zero-as-null-pointer-constant \ Once that's put in your actual Makefile, simply use $(WARN_OPTS) as part of your gcc command. It may need a small amount of touch up to: get rid of invalid options such as -Wno-; fix certain -Wno-<key>=<value> types; and remove the final \ character. but that's minimal effort compared to the generation of the long list, something you can now do rather simply. When you establish that you want one of those warnings, simply switch from the -Wno-<something> back to -W<something>.