Converting Windows path to Unix path in a makefile - 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.

Related

How to concatenate strings in a Makefile?

I have a Makefile that runs pandoc. I want to turn a list of extensions:
PANDOC_EXTENSIONS = \
multiline_tables \
some_other_extension
into a string that looks like:
PANDOC_EXTENSION_LIST = +multiline_tables+some_other_extension
which will then be passed as a command line option to pandoc like this:
pandoc --from$(PANDOC_EXTENSION_LIST) ...
It's trivial in almost any programming language, but I can't figure out how to do this with the patsubst or subst functions, since make doesn't really have lists. Any ideas?
Here:
Makefile
PANDOC_EXTENSIONS = \
multiline_tables \
some_other_extension
$(foreach word,$(PANDOC_EXTENSIONS),$(eval PANDOC_EXTENSION_LIST := $(PANDOC_EXTENSION_LIST)+$(word)))
.PHONY: all
all:
echo $(PANDOC_EXTENSION_LIST)
Which runs like:
$ make
echo +multiline_tables+some_other_extension
+multiline_tables+some_other_extension
As this illustrates, GNU make really does have lists. A sequence of whitespace-separated words is a list.
Based on example in documentation:
empty:=
space:=$(empty) $(empty)
PANDOC_EXTENSIONS = \
multiline_tables \
some_other_extension
all:
#echo +$(subst ${space},+,${PANDOC_EXTENSIONS})
The result:
$ gmake
+multiline_tables+some_other_extension

GNU MAKE: functions in dependencies

I would like to generate a number of files using GNU Make using the following recipe.
ina_as%.dat: ina_driver.m ina_as$(word 1,$(subst _epsi, , %)).m
echo "modelType = '$(word 1,$(subst _epsi, , $*))'; ofile = '$#'; epsi = '$(word 2,$(subst _epsi, , $*))';" | cat - $< | nohup matlab -nodesktop -nosplash
The targets are in a format -- ina_as%d_epsi%.2f.dat (e.g. ina_as1_epsi0.50.dat) and the second prerequisite is ina_as%d.m (e.g. ina_as1.m) (notice, the second part _epsi%.2f missing in the prerequisite file name).
I have tried several combination for the implicit rule ($, $$, $(eval $*) etc.), but it still does not work. I think it could be because Make could not understand the functions ( '$(word 1,$(subst _epsi, , %))' ) in the dependency definition.
There is any way to overcome this problem?
Thanks.
Questions like this come up from time to time. The short answer is that Make simply can't do this in a clean way; the text manipulation statements expand before executing any rule (i.e. before % has any value), and Make doesn't handle wildcards (or regular expressions) very well.
The longer answer is that it can be done, but only by resorting to one kludge or another. If your version of Make supports SECONDEXPANSION, you can do it this way:
.SECONDEXPANSION:
ina_as%.dat: ina_as$$(word 1,$$(subst _, ,%)).m
#echo "modelType = '$(word 1,$(subst _epsi, , $*))'; ofile = '$#'; epsi\
= '$(word 2,$(subst _epsi, , $*))';" | cat - $< | nohup matlab -nodesktop\
-nosplash
If not, you can resort to recursive Make (useful sometimes, no matter what they say):
ina_as%.dat :
#$(MAKE) dummy MODELTYPE=`echo $* | sed "s/_.*//"` EPSI=`echo $* | sed \
"s/.*_epsi//"`
dummy: ina_as$(MODELTYPE).m
#echo "modelType = $(MODELTYPE); ofile = ina_as$(MODELTYPE)_epsi$(EPSI)\
; epsi = $(EPSI);" | cat - ina_as$(MODELTYPE).m | nohup matlab -nodesktop\
-nosplash

equivalent command for gnumake (for...do, if exist,del)

I'm converting a makefile so it can be used with gnu make. In the other make program (can't find what was the version...) this part was executed :
#******************************************************************************
# Explicit Rules
#******************************************************************************
#
# This section contents the explicit rules to create the Virtual File system.
# This file system actualy contains the files used by the Web interface.
#
#******************************************************************************
CFGFILE = $(WEBDST)/vfs.cfg
# Compiling vfsdata
$(OBJDIR)vfsdata.o : vfsdata.c
#echo .
#echo ::. COMPILING1 : vfsdata.c
#echo ::::.
$(NOECHOC)$(CC) $(CFLAGS) $(DEBUGFLASG) $(subst \,/,$(VFSDIR))/vfsdata.c -o"$#" $(INCLUDE)
# Dependance of vfsdata.c
vfsdata.c : $(addprefix $(WEBSRC)\,$(WEBGLOBAL)) $(addprefix $(WEBDST)\,$(WEBLOCAL))
# Make vfsdata.c writable
#if exist $(VFSDIR)\vfsdata.c \
chmod 777 $(VFSDIR)\vfsdata.c
#
# Generate the configuration file needed by vfscomp
#
#echo [general] > $(CFGFILE)
#echo vfs_root_dir = $(WEBDST)>> $(CFGFILE)
#echo vfs_data_file = $(VFSDIR)\vfsdata>> $(CFGFILE)
#echo vfs_image_var = vfsimage>> $(CFGFILE)
#echo far_pointer = yes>> $(CFGFILE)
#echo compress = yes>> $(CFGFILE)
#echo [file] >> $(CFGFILE)
#echo $(sort $(WEBGLOBAL) $(WEBLOCAL)) | $(TR) -s " " "[\n*]" \
| $(SED) "s/gif/gif -u/g" \
>> $(CFGFILE)
#
# Create the local directory tree
#
#for %%f in ($(subst /,\,$(dir $(WEBGLOBAL)))) do \
if not exist $(WEBDST)\%%f mkdir $(WEBDST)\%%f
#
# Copy the WEB pages directory tree localy
#
#for %%f in ($(subst /,\,$(WEBGLOBAL))) do \
cp -rf $(WEBSRC)\%%f $(WEBDST)\%%f
#
# Generate the virtual file system
#
#$(TOOLDIR)/vfscomp $(subst \,/,$(CFGFILE))
#
# Delete the local directory tree
#
#delete the directory recursively exept the one in the exclusion list
#for %%f in ($(filter-out $(EXCLUSION), $(WEBDIR))) do \
if exist $(WEBDST)\%%f \
rm -rf $(WEBDST)\%%f
#delete the remaining files.
#for %%f in ($(subst /,\,$(WEBGLOBAL))) do \
if exist $(WEBDST)\%%f \
rm -f $(WEBDST)\%%f
#del $(subst /,\,$(CFGFILE))
When I build my code, I get this error
The syntax of the command is incorrect.
I have the feeling that some commands like #for #del #if exist are command that are not supported(or with a different syntax) in the gnu make.
I checked in the online GNU make documentation but I didn't found commands that are similar to those used below.
Is there any equivalent in gnu make to the command used in this part of the makefile?
Thanks in advance!
EDIT :
I removed the # and executed make. I can see when the message "The syntax of the command is incorrect" appear :
if exist ../Pa/Filesys\vfsdata.c \
chmod 777 ../Pa/Filesys\vfsdata.c
echo [general] > ../Pa/Filesys/vfs.cfg
echo vfs_root_dir = ../Pa/Filesys>> ../Pa/Filesys/vfs.cfg
echo vfs_data_file = ../Pa/Filesys\vfsdata>> ../Pa/Filesys/vfs.cfg
echo vfs_image_var = vfsimage>> ../Pa/Filesys/vfs.cfg
echo far_pointer = yes>> ../Pa/Filesys/vfs.cfg
echo compress = yes>> ../Pa/Filesys/vfs.cfg
echo [file] >> ../Pa/Filesys/vfs.cfg
echo behavior/datasync/datasync.htc behavior/defered/defered.htc behavior/inputs/indirect.htc behavior/inputs/inputs.htc behavior/progress/progress.htc behavior/slider/slBottom.gif behavior/slider/slLeft.gif behavior/slider/slRight.gif behavior/slider/slTop.gif behavior/slider/slider.htc css/env.css html/cfgmain.html html/changepass.html html/diagpa.html html/firmhlp.html html/firmware.html html/home.html html/logpage.html html/progressend.html html/progressstart.html html/soundhlp.html html/support.html html/uplmain.html html/uploadstart.html img/button.gif img/logo.gif index.html xslt/activate.xslt xslt/main.xslt xslt/netmenu.xslt xslt/page.xslt xslt/page_func.xslt xslt/progress.xslt xslt/tlmenu.xslt xslt/upload.xslt | tr -s " " "[\n*]" \
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
The syntax of the command is incorrect.
| sed "s/gif/gif -u/g" \
>> ../Pa/Filesys/vfs.cfg
for %%f in (.\ css\ img\ img\ xslt\ xslt\ xslt\ xslt\ xslt\ xslt\ xslt\ xslt\ html\ html\ html\ html\ html\ html\ html\ html\ html\ html\ html\ html\ behavior\slider\ behavior\slider\ behavior\slider\ behavior\slider\ behavior\slider\ behavior\datasync\ behavior\progress\ behavior\inputs\ behavior\inputs\ behavior\defered\) do \
It appear that the syntax error is somewhere on the line
echo $(sort $(WEBGLOBAL) $(WEBLOCAL)) | $(TR) -s " " "[\n*]" \
| $(SED) "s/gif/gif -u/g" \
To be clear, none of the commands you mention are make commands. They are all shell commands. Make is invoking a shell and passing those commands to the shell. As mentioned elsewhere, the "#" is a special command to make (supported by all POSIX-based make implementations including GNU make) that tells make to not print the command before sending it to the shell (the "#" is removed by make before sending the command to the shell).
Second, the error message "the syntax of the command is incorrect" is not anything GNU make ever prints. Either you have paraphrased the error message rather than repeating it verbatim (never a good idea when asking for help), or that message is being printed not by make but by some shell command that make invokes.
This will take a few iterations (especially since you haven't given us a complete sample).
There are two rules here; try them separately.
Try the second rule with only the first command:
# Make vfsdata.c writable
#if exist $(VFSDIR)\vfsdata.c \
chmod 777 $(VFSDIR)\vfsdata.c
Let us know the results of these experiments, and we'll go from there.
EDIT:
Interesting. I'd guess that either tr doesn't like your syntax, or the length of that command has exceeded some limit of your Make or OS.
Try doing it from the command line:
echo behavior/datasync/datasync.htc behavior/defered/defered.htc behavior/inputs/indirect.htc behavior/inputs/inputs.htc behavior/progress/progress.htc behavior/slider/slBottom.gif behavior/slider/slLeft.gif behavior/slider/slRight.gif behavior/slider/slTop.gif behavior/slider/slider.htc css/env.css html/cfgmain.html html/changepass.html html/diagpa.html html/firmhlp.html html/firmware.html html/home.html html/logpage.html html/progressend.html html/progressstart.html html/soundhlp.html html/support.html html/uplmain.html html/uploadstart.html img/button.gif img/logo.gif index.html xslt/activate.xslt xslt/main.xslt xslt/netmenu.xslt xslt/page.xslt xslt/page_func.xslt xslt/progress.xslt xslt/tlmenu.xslt xslt/upload.xslt | tr -s " " "[\n*]" \
| sed "s/gif/gif -u/g" \
>> ../Pa/Filesys/vfs.cfg
If that succeeds, it's a Make length limit; try with fewer file names.If it fails, try omitting the last term:
echo blah blah blah \
| sed "s/gif/gif -u/g"
Tell us the results, and we'll proceed...

gnu make: list the values of all variables (or "macros") in a particular run

How can I list the current value of all variables (also called macros) in a Makefile when running make?
E.g. if this is in the Makefile:
CUR-DIR := $(shell /bin/pwd)
LOG-DIR := $(CUR-DIR)/make-logs
Then I would like it to tell me:
CUR-DIR = /home/johv/src/test
LOG-DIR = /home/johv/src/test/make-logs
GNU make provides .VARIABLES
which holds all global variables' names.
However, this includes built-in variables(like MAKEFLAGS).
If you have to exclude built-in variables, some filtering like the following
might be needed.
The following makefile prints user-defined variables(CUR-DIR, LOG-DIR)
using info:
# Place this line at the top of your Makefile
VARS_OLD := $(.VARIABLES)
# Define your variables
CUR-DIR := $(shell pwd)
LOG-DIR := $(CUR-DIR)/make-logs
# Put this at the point where you want to see the variable values
$(foreach v, \
$(filter-out $(VARS_OLD) VARS_OLD,$(.VARIABLES)), \
$(info $(v) = $($(v))))
Thanks to #Ise Wisteria, condensed down, this shows all variables, useful for large projects with multiple makefiles (Buildroot).
$(foreach v, $(.VARIABLES), $(info $(v) = $($(v))))
output: BR2_GCC_TARGET_TUNE = "cortex-a8" ...
If you get an error like: insufficient number of arguments (1) to function 'addprefix' this project had some broken variables... I trimmed the list of variables to show, only with a prefix BR2_
$(foreach v, $(filter BR2_%,$(.VARIABLES)), $(info $(v) = $($(v))))
I ended up doing it like this:
gmake -pn | grep -A1 "^# makefile"| grep -v "^#\|^--" | sort | uniq > makevars.txt
which gives:
CUR-DIR := /home/johv/src/test
LOG-DIR := /home/johv/src/test/make-logs
MAKEFILE_LIST := Makefile
MAKEFLAGS = pn
SHELL = /bin/sh
VARS_OLD := [...]
gmake -pn is really verbose and looks kinda like this:
# environment
GNOME2_PATH = /usr/local:/opt/gnome:/usr:/usr/local:/opt/gnome:/usr
# automatic
#F = $(notdir $#)
# makefile
SHELL = /bin/sh
# default
RM = rm -f
It's also doable without saving all the .VARIABLES and filtering them out.
Moreover, if one of the original .VARIABLES was modified in your makefile, the two most voted answers won't catch it.
Check out $(origin) function. This target filters out and prints all the variables that were defined in a makefile:
print_file_vars:
$(foreach v, $(.VARIABLES), $(if $(filter file,$(origin $(v))), $(info $(v)=$($(v)))))
I get only a few excess variables this way: CURDIR SHELL MAKEFILE_LIST .DEFAULT_GOAL MAKEFLAGS.
One can replace file with environment or command line to print the respective kinds of variables.
There are a lot of good answers here, but you're going to have problems using $($(v)) if some of your variables are of the recursive flavor. This is why you should use $(value $(v)).
This variation cleans this up a little bit, sorts variables by name and makes the output a bit more readable.
dump:
$(foreach v, \
$(shell echo "$(filter-out .VARIABLES,$(.VARIABLES))" | tr ' ' '\n' | sort), \
$(info $(shell printf "%-20s" "$(v)")= $(value $(v))) \
)
Thanks to #kevinf for the great idea. I would suggest a minor change to prevent .VARIABLE itself from printing out in the variable list:
$(foreach v, $(filter-out .VARIABLES,$(.VARIABLES)), $(info $(v) = $($(v))))
Thanks to #kevinf for the foreach solution -- if one wants to export this list as a somewhat machine-readable file, one will have a hard time with uneven quotes or newlines when using echo or printf, since Make isn't able to quote the data correctly -- one needs to use the $(file ...) function to write the data to avoid sh/bash complaining about invalid syntax. For example, use this in your rule -- it prints variable name, definition and expanded value:
$(file > $(MAKEFILE_ENV_FILE),)
$(foreach v, $(.VARIABLES), \
$(file >> $(MAKEFILE_ENV_FILE),$(v)) \
$(file >> $(MAKEFILE_ENV_FILE), := $(value $(v))) \
$(file >> $(MAKEFILE_ENV_FILE), == $($(v))) \
$(file >> $(MAKEFILE_ENV_FILE),) \
)
(This will still not allow to always distinguish malicious variables with double newlines from two variables, for this one now add a sufficiently unique separator infront of each Makefile-generated newline just after each comma inside $(file >> NAME,TEXT))
Set MAKEFILE_ENV_FILE to some filename, e.g.:
MAKEFILE_ENV_FILE := $(abspath $(lastword $(MAKEFILE_LIST))).env

Variable dereferncing in makefiles

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.

Resources