GNUMake: Missing separator issue in makefile targer - makefile

Here is my Makefile snippet.
1. $(DIR_REL64)/%: $(SHIFT_BINDIR64)/%
2. $(copy_file)
3.ifneq ($(TEST),0)
4.ifneq($(LOG),0)
5. if [ -d $(TGT32)/tools/test/64bit ]; then cp -f $< $(TGT32)/tools/test/64bit; fi
6.else
7. if [ -d $(TGT64)/tools/test/64bit ]; then cp -f $< $(TGT64)/tools/test/64bit; fi
8.endif
9.else
10. if [ -d $(TGT64)/bin ]; then cp -f $< $(TGT64)/bin; fi
11.endif
I am getting error:
GNUmakefile:4: *** missing separator. Stop.
Anything wrong in my makefile rule ? Please help

Every line in the rule needs to be tab indented (no spaces). Your indentations appear to be mixed (though I can't tell 100% from the pasted code).
Also you should try not to mix make and bash syntax where you can avoid it. For example you can do.
There may also be some extra spaces at the ends of some of the lines (I can't tell from here) but makesure there is no white spaces at the ends.
I would re-write it line-by-line starting with just:
$(DIR_REL64)/%: $(SHIFT_BINDIR64)/%
$(copy_file)
Where $(copy_file) is tab indented. Make sure that works and then add the next bit.

Related

Running expressions (without compilation of files) in Makefile without targets?

I wanted to test some expressions of the ifeq kind that run a shell command that I read somewhere, so I wrote this tiny mymakefile (all lines being indented with a tab):
ifeq ($(shell echo test 2>/dev/null; echo $$?),0)
$(info I am inside)
endif
... and I tried to run it:
$ make -f mymakefile
make: *** No targets. Stop.
How could I test expressions like this inside their own makefile? Do I need to define a default target, or not? And how should the commands be formatted (indented with a tab, or space, or not indented at all?)
Well, I got somewhere - apparently, one must specify a target; but since I'm a make noob, I would love to see a more qualified answer.
I found this link https://www.gnu.org/software/make/manual/html_node/Conditional-Example.html that gave me a hint.. Anyways, this is mymakefile now:
.PHONY: default
default: mytarget;
ifeq ($(shell echo test 2>/dev/null; echo $$?),0)
$(info I am inside)
else
$(info I am outside)
endif
mytarget:
\t (TAB) echo A
So, the mytarget here is just a dummy, which simply does an echo A; running this prints:
$ make -f mymakefile
I am outside
echo A
A
If you don't want the echo A printed, suppress it with at sign: #echo A.
The echo A line has to be indented with a TAB - else error "mymakefile:11: *** missing separator. Stop.".
Strangely, if I indent the two $(info... lines with a TAB, then "I am outside" is printed last (?!), but when they are not indented (or indented with spaces), then it is printed first (as per the order in the file).

Transitioning from shell to Makefile

I want to know how I can convert this shell script to a Makefile and use make all to perform all the script's operations. I have two directories pictures and thumbs, where thumbs is empty before running the script and pictures contains some .jpg files. Finally, this is the shell script that I want to convert to a Makefile:
#!/bin/bash
DIR="thumbs"
if [ "$(ls -A $DIR)" ]; then
p=$(find pictures/|grep "jpg"|cut -d"/" -f2)
for i in $p
do
m=$(ls -l pictures/$i | cut -d" " -f7)
n=$(ls -l thumbs/$i | cut -d" " -f7)
if [ "${m//':'}" -gt "${n//':'}" ] ;then
rm thumbs/$i
convert -thumbnail 100 pictures/$i thumbs/$i
fi
done
else
find pictures/ |cut -d"/" -f2 | grep "jpg"| \
awk '{system("convert-thumbnail 100 pictures/" $0 " thumbs/" $0)}'
fi
From Makefile you can call a program.
Example
$ cat a.sh
echo From Makefile
$ cat Makefile
all:
./a.sh
Test :
$ make
./a.sh
From Makefile
You can implement the behavior of your shell code much more simply as a Makefile, at least if you are willing to rely on GNU make. I interpret this to be what you're asking (modulo dependency on GNU make in particular). This is a pretty functional rough cut:
THUMBS = $(patsubst pictures/%,thumbs/%,$(wildcard pictures/*jpg*))
all: $(THUMBS)
thumbs/%: pictures/% thumbs
convert -thumbnail 100 '$<' '$#'
thumbs:
mkdir -p '$#'
.PHONY: all
Notes:
the THUMBS make variable gets set to a list of the thumbnail images you want to manage, based on expanding the shell glob pictures/*jpg* and replacing each occurrence of pictures/ with thumbs/.
The pattern is chosen to match your shell code, but perhaps you really want something more like $(wildcard pictures/*.jpg)
File names with whitespace in them are going to present a tricky problem if you need to worry about them; file names with certain other special characters too, albeit a bit less so
the patsub and wildcard functions are GNU extensions
You could also merge the definition of THUMBS into the rule for all, and avoid a separate variable
The rule for thumbs/%: pictures/% thumbs uses GNU-specific pattern rule syntax; this particular form is hard to express to POSIX make.
The thumbs directory is created if absent, but errors will occur if there is an ordinary file of that name in the way
make all (or just make) will update all out-of-date thumbnails; it does not rely on the same date comparison logic as the original script (which is a good thing)
The .PHONY rule is just to be careful. It prevents the existence of an actual file named "all" from interfering with make's operation.

dividing outputs in make by filename

I am processing some files and want to at one point create two categories depending on the filename so I can compare the two. Is this possible in a makefile?
%.output1: %.input
ifneq (,$(findstring filename1,$(echo $<)))
mv $<.output1 $#
endif
%.output2: %.input
ifneq (,$(findstring filename2,$(echo $<)))
mv $<.output2 $#
endif
%.output_final: %.output1 %.output2
do_something
I think there is two things wrong with this code:
There is a mistake in the ifneq line.
%.output1 %.output2 will always use the same filename - it may not be possible to do this in 'make' and this may require ruffus.
You have tab-indented the ifneq line so make doesn't consider it a make directive and is considering it a shell command and attempting to pass it to the shell to execute (hence the shell error you removed in your recent edit).
Use spaces (or no indentation) on that line to have make process it correctly. That being said having done that you cannot use $< in the comparison as it will not be set at that point.
$(echo) is also not a make function. You have mixed/confused processing times. You cannot combine make and shell operations that way. (Not that you need echo there to begin with.)
If you want the comparison to happen at shell time then do not use make constructs and instead use shell constructs:
%.output1: %.input
if [ filename1 = '$<' ]; then
mv $<.output1 $#
fi
Though that is also incorrect as $< is %.input and $# is %.output1 for whatever stem matched the %. That rule should probably look more like this (though I'm having trouble understanding what you are even trying to have this rule do so I may have gotten this wrong).
%.output1: %.input
# If the stem that matched the '%' is equal to 'filename1'
if [ filename1 = '$*' ]; then
# Then copy the prerequisite/input file to the output file name.
cp $< $#
fi
I'm not sure I understand your second question point. The % in a single rule will always match the same thing but between rules it can differ.
This %.output_final: %.output1 %.output2 target will map the target file foo.output_final to the prerequisite files foo.output1 and foo.output2. But will also map any other *.output_final file to appropriately matching prerequisite files.

echo special characters to a file in bash

I am trying to make a script to help me back port source code to debian files.
trying to write debian/rules file
~$ echo -e "#!/usr/bin/make -f\n%:\n\tdh \$#" > debian/rules
bash: !/usr/bin/make: event not found
i think the # and ! are screwing this up. How can I echo these components of the string without having the shell try to execute them?
I have tried using "#" and "!" but then i would get the exactly that: "!"
I want a rules file that looks exactly like this:
#!/usr/bin/make -f
%:
dh $#
what should I do?
! is not escaped by double quotes
'#!/usr/bin/make -f\n%:\n\tdh \'"$#"
should be fine. However, please look at here-documents:
cat <<MAKEFILE
#!/usr/bin/make -f
%:
dh $#
MAKEFILE
I have an alternative method, using ANSI hex codes (or even Unicode hex codes, if you're clever).
$ echo -e "\x23\x21/usr/bin/make -f\n\x25:\n\tdh \x24\x40"
#!/usr/bin/make -f
%:
dh $#
My method makes a bit more intuitive sense to me, though the use of here documents by #sehe is ingenious. (It would also require less look-ups if you're not very familiar with ANSI.) I needed to know that # is U+0023; that ! is U+0021; etc. I work with Unicode codepoints all the time, so it's quick.

Dynamic conditions in makefile

I maintain a fairly complex makefile for Arduino.
In the makefile, I have target to build *.hex file from *.cpp file. After the *.hex file is generated, I want to check whether the hex size of the file is less than the flash memory of the microcontroller.
To do that, I have added another target called verify_size which touches a *.sizeok file if the hex size is less.
Following is the relevant code
$(TARGET_HEX).sizeok: $(TARGET_HEX)
ifneq ($(strip $(HEX_MAXIMUM_SIZE)),1)
ifeq ($(shell expr `$(call avr_size,$(TARGET_HEX)) | grep Program | awk '{print $$2}'` '<' $(HEX_MAXIMUM_SIZE)), 1)
touch $#
endif
else
#$(ECHO) Maximum Hex size is not specified. Make sure the hex file that you are going to upload is less than microcontrollers flash memory
endif
verify_size: $(TARGET_HEX) $(TARGET_HEX).sizeok
The problem I am facing is that when the makefile is run for the very first time, I get a error saying that the hex file doesn't exist.
After some debugging I found that makefile first goes through the entire file before executing it. When it does this initial pass, the hex file not created yet and therefore the statements which does the parsing of hex file is not executed at all.
Is there a way by which I add dynamic conditions in makefile so that I can find the size of the hex file that was just generated?
Edit:
Based on #beta 's suggestion I changed the code to
$(OBJDIR)/%.hex: $(OBJDIR)/%.elf $(COMMON_DEPS)
$(OBJCOPY) -O ihex -R .eeprom $< $#
#$(ECHO)
$(call avr_size,$<,$#)
ifneq ($(strip $(HEX_MAXIMUM_SIZE)),)
if [ `$(SIZE) $# | awk 'FNR == 2 {print $$2}'` -le $(HEX_MAXIMUM_SIZE) ]; then touch $#.sizeok ; fi
else
#$(ECHO) Maximum Hex size is not specified. Make sure the hex file that you are going to upload is less than microcontrollers flash memory
endif
and it is working. But there is one small issue though.
In the above code, I am using a variable defined in makefile $(SIZE). But when this shell script executes, the value is not replaced. Instead it just replaces it with an empty value.
It works if I hardcode the value, but I am not able to use the value of the variable defined in makefile. Is it possible to access it?
Edit2:
I have posted a separate question for variable expansion issue.
If HEX_MAXIMUM_SIZE hasn't been set, Make should not update the sizeok file, and we shouldn't have a rule that can't actually rebuild its target. And we should update the sizeok file only when we rebuild the hex file. So instead of a rule for $(TARGET_HEX).sizeok, let's just make it a command within the $(TARGET_HEX) rule. (You haven't shown us avr_size, so I can't figure out your method for measuring the size of the hex file, so I'll just use ls and assume you aren't using pathological file names.)
$(TARGET_HEX): %.hex : %.cpps
# Commands to build the target file
if [ `ls -l $# | awk '{print $$5}'` -le $(HEX_MAXIMUM_SIZE) ]; then touch $#.sizeok ; fi
Now we can add a condition, in case HEX_MAXIMUM_SIZE hasn't been set correctly:
$(TARGET_HEX): %.hex : %.cpps
# Commands to build the target file
ifneq ($(strip $(HEX_MAXIMUM_SIZE)),1)
if [ `ls -l $# | awk '{print $$5}'` -le $(HEX_MAXIMUM_SIZE) ]; then touch $#.sizeok
else
#echo Maximum Hex size is not specified. Make sure that $# is small enough for the microcontroller\'s flash memory.
endif
EDIT:
This may take a few iterations. Replace this line:
if [ `$(SIZE) $# | awk 'FNR == 2 {print $$2}'` -le $(HEX_MAXIMUM_SIZE) ]; then touch $#.sizeok ; fi
with this:
$(SIZE) $# | awk 'FNR == 2 {print $$2}'
and tell us the result.

Resources