I use a Makefile for web development, where I compile scss (sass) files to css and I want to add a banner (header with date, version, copyright info, git branch etc) to the compiled file.
While I finally managed to achieve this, when I open the result there is <feff> displayed at the joining position.
The Makefile is like this:
BANNER:=\
"/**\n"\
" * #project $(PROJECT_NAME)\n"\
" * #author John Doe <j.doe#example.com>\n"\
" * #build $(DATE)\n"\
" * #copyright Copyright (c) " $(shell date +%Y) ", <Example Inc.>\n"\
" */\n"\
"\n"
css: $(CSS_DEST_DIR)/main.css
$(call colorecho, 3, $(shell du -h $^))
$(CSS_DEST_DIR)/main.css: $(CSS_SRC_DIR)/main.scss $(CSS_SRC_FILES)
#mkdir -p $(CSS_DEST_DIR)
$(call colorecho, 3, "Compiling $#");
$(eval TMPFILE := $(shell mktemp))
#-$(SASS_COMPILER) $(SASS_COMPILER_OPTIONS) -o $(TMPFILE) $<
#echo $(BANNER) | cat - $(TMPFILE) > $#
And the resulting file looks like this in vim:
/**
* #project data-al
* #author Johannes Braun <j.braun#agentur-halma.de>
* #build 2018-12-13-1619
* #release gitRevSync.long() + gitRevSync.branch()
* #copyright Copyright (c) 2018 , <HALMA GmbH & Co. KG>
*/
<feff>.button,.button--primary,.cookie-notification__accept,.search-form__submit,.mobnav__trigger,.mobnav__button{padding:0;...
od -a outputs
0000400 nl <EF> <BB> <BF> . b u t t o n , . b u t
When I do the same on the bash command line, everything is ok. I am on OSX btyw.
How could I get rid of this? Thanks for your help
I urge you strongly to dump echo. It's massively non-portable for anything other than printing simple strings followed by a newline. For example the \n won't resolve to a newline on all systems. Instead, consider using printf (the program, not the function) which is well-defined for all sorts of special characters and formatting strings.
However, as mentioned by Raymond these characters are a UTF-8 BOM (byte order mark). They're probably generated by $(SASS_COMPILER); see if there's an option to prevent that from happening.
Simply strip the bom from the start before concatenation. Many options, but dos2unix will work admirably. (Yes, it will preserve any other unicode characters, including any embedded non-breaking-zero-width-spaces.)
cp ${BANNER} $#
dos2unix <${TMPFILE} >>$#
Related
I want to obtain the Boost Library Version number 1.58 out of the string "Version: 1.58.0.1ubuntu1" or generally any other version of Boost. This would allow me to compare the current version of boost to my specific version that I need to match. This is what I have so far.
Configure:
if ( test -d $(Boost) )
then
CurrVer=$$(dpkg -s libboost-dev | grep 'Version'
echo $$CurrVer
echo $$CurrVer | tr -cd [:digit:]
else
make DLBoostV1_58
fi
The problem is that I can narrow the string down to the digits 158011 but I can't figure out how to remove the digits 011.
I have read geeksforgeeks website for grep, sed, and awk commands but what got me to this point are, How to extract numbers from a string?, https://askubuntu.com/questions/147474/how-can-i-find-boost-version, https://www.geeksforgeeks.org/tr-command-in-unix-linux-with-examples/nd .
Expecting output: 158
Resulting output: 158011
Supposing you have captured the Boost version message in shell variable CurrVer, you can use the prefix-removal option of parameter expansion to remove the lead text (${CurrVer##* } removes everything up to the last space character), inside an array assignment (a=(...)) with the period (.) as a field separator (IFS=.) to split the version string at the right delimiters. Then you just need to read back the array elements you want.
For example,
ShortVer=$( IFS=.; a=(${CurrVer##* }); echo "${a[0]}.${a[1]}" )
Escaping that for make is left as an exercise.
You can use gmtt which can take apart strings per glob-match:
include gmtt-master/gmtt-master/gmtt.mk
VERSION := Version: 1.58.0.1ubuntu1
VERS-AS-LIST := $(call glob-match,$(VERSION),Version: *.*.*.*ubuntu*)
$(info $(VERS-AS-LIST))
MAJOR := $(word 2,$(VERS-AS-LIST))
MINOR := $(word 4,$(VERS-AS-LIST))
BUGFIX := $(word 6,$(VERS-AS-LIST))
$(info $(MAJOR)-$(MINOR)-$(BUGFIX))
Output:
$ make
Version:§ 1 . 58 . 0 . 1 ubuntu 1
1-58-0
Notice that the first part of the glob pattern was Version: with a space character at the end, which is conserved via the § character (contained in the gmtt variable $(-spacereplace) and removable with the function $(call spc-unmask,_string_))
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
Say I have a rule like this¹:
SOURCES = one.pdf two.pdf three.pdf
out.pdf: $(SOURCES)
pdftk $^ cat output $#
This works great as long as I only want to use $^ as a space separated list, but now lets ay I need to prefix each value with something. In the case of pdftk I need to name each input stream with a capital leter in order to fetch page ranges from each. If I want the first page of all the input files I need to run a command like this:
pdftk A=one.pdf B=two.pdf C=three.pdf cat A1 B1 C1 output out.pdf …
I am aware of the foreach function that I could iterate over something like this:
pdftk $(foreach SOURCE,$^,A=$(SOURCE))
…but of course A=one.pdf A=two.pdf A=three.pdf isn't going to get me very far. How can I both iterate over the list of inputs and assotiate them with keys from another array? Note I also need the length and keys from that other array to use again as handles in the rest of the command line.
Is GNU Make able to handle this situation gracefully or should I write a little wrapper script?
¹ Note this use case is simplified for the purpose of this question, the actual list of sources is generated at runtime and the length is not known, so hard coding a substitute array or changing the filenames to A.pdf B.pdf so that the basename can be used as the pdftk handle won't work.
The language implemented by GNU make is not a general purpose language, which makes this kind of string processing awkward. I suggest to use a language more suitable to string processing--what about a little perl such as
SOURCES = one.pdf two.pdf three.pdf
out.pdf: $(SOURCES)
#echo pdftk $$(perl -e '$$x=65; \
foreach (#ARGV) { \
$$a .= sprintf "%c=%s ", $$x, $$_; \
$$b .= sprintf "%c1 ", $$x; ++$$x; \
}; print "$$a cat $$b output $#\n"' $^)
$ make
pdftk A=one.pdf B=two.pdf C=three.pdf cat A1 B1 C1 output out.pdf
This is pretty flexible and extensible.
Let's say my Makefile is something like:
DIR :=#
foobar:
ls ${DIR}
When i type
mak[tab] f[tab]
it gives correctly
make foobar
But
make foobar D[tab]
doesn't do the magic
make foobar DIR=
So my question is: is there a way to autocomplete Makefile variables too (aside targets) in bash?
This answer is far from complete. To grep all variables in a Makefile we use make -p to print the Makefile database:
# GNU Make 3.81
# Copyright (C) 2006 Free Software Foundation, Inc.
# This is free software; see the source for copying conditions.
# There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
# This program built for x86_64-pc-linux-gnu
# Make data base, printed on Mon Oct 13 13:36:12 2014
# Variables
# automatic
<D = $(patsubst %/,%,$(dir $<))
# automatic
?F = $(notdir $?)
# environment
DESKTOP_SESSION = kde-plasma
# ...
# makefile (from `Makefile', line 1)
DIR :=
We are looking for lines starting with # makefile (from 'Makefile', line xy) and extract the name of the following variable:
$ make -p | sed -n '/# makefile (from/ {n; p;}'
MAKEFILE_LIST := Makefile
DIR :=
In the next step we remove everything but the variable name (everything after :=):
$ make -p Makefile | sed -n '/# makefile (from/ {n; s/^\([^.#:= ]\+\) *:\?=.*$/\1/p;}'
MAKEFILE_LIST
DIR
The following lines demonstrate how it could be done:
_make_variables()
{
# get current completion
local cur=${COMP_WORDS[COMP_CWORD]}
# get list of possible makefile variables
local var=$(make -p Makefile | sed -n '/# makefile (from/ {n; s/^\([^.#:= ]\+\) *:\?=.*$/\1=/p;}')
# don't add a space after completion
compopt -o nospace
# find possible matches
COMPREPLY=( $(compgen -W "${var}" -- ${cur}) )
}
# use _make_variables to complete make arguments
complete -F _make_variables make
Now make D[tab] results in make DIR=.
Sadly you will loose all the file and target completion with this approach. Also it would be useful to remove some more variables (e.g. MAKEFILE_LIST) from the completion output.
Maybe it is worth to fill a wish/bug report against the bash-completion project to add this feature.
i'm new to gnu make.
i searched around but i cannot find anything working...
I have a list of tool prefix suxh as:
DEFTOOL= /usr/bin/i686-mingw32- /usr/bin/x86_64-w64-mingw32- /usr/bin/
I want to change the extension of the binary file if string "mingw" is in the current tool, so
$(foreach c, $(DEFTOOL),$(call dist_make, $(c)))
...
define dist_make
result= ${shell echo $(1) | grep mingw }
echo x$(result)x
# $(if $(result),\
# $(1)gcc $(DIST_CFLAGS) -o cap2hccap$(WINEXT) ./cap2hccap.c; echo "windows";,
# $(1)gcc $(DIST_CFLAGS) -o cap2hccap$(LNXEXT) ./cap2hccap.c; echo "linux"; \
# )
endef
where LNXEXT is empty and WINEXT is ".exe".
i cannot get this working....
how can i known if the argument of the function contains "mingw" ?
PS:
i known that the 64bit and the 32bit mingw output is the same but i will fix it when i have understood how check if a string is inside another.
If you known a better way to automate cross dev building spit it out :)
Your question is a little unclear. If you want to change a variable, outside of any rule:
ifneq ($(findstring mingw,$(DEFTOOL)),)
FILENAME = foo.xxx
endif
If you want to rename a file conditionally within a rule:
someTarget:
ifneq ($(findstring mingw,$(DEFTOOL)),)
mv foo.aaa foo.xxx
endif
You could also put the conditional within the command, but I can't see why you'd want to.