Why does my regex break inside a Makefile? - c

I have a bunch of C source files named sequentially (say f1.c, f2.c, f3.c etc).
In my Makefile I have a clean: definition which used to look like this:
rm -f f1
rm -rf f1.dSYM
rm -f f2
rm -rf f2.dSYM
# etc
So I wanted to replace that with a regex, and this works great if I input it directly into the command line:
ls | grep -P ^f[0-9]+(|\.dSYM)$ | xargs rm -rf
However, if I then put that command in my clean definition, when I run make clean, I get this:
$ make clean
ls | grep -P ^f[0-9]+(|\.dSYM)| xargs rm -rf
/bin/sh: -c: line 0: syntax error near unexpected token `('
/bin/sh: -c: line 0: `ls | grep -P ^ex[0-9]+(|\.dSYM)| xargs rm -rf'
make: *** [clean] Error 2
I guess there are some special characters in my regex that are causing a syntax error... I've tried quoting and escaping stuff but nothing's really helping, does anyone know how I could get this working inside my Makefile?

Yet another solution, using $(wildcard) to find the C sources and pattern substitution to get the derived file names:
SOURCES := $(wildcard f[0-9]*.c)
clean :
rm -f $(SOURCES:.c=)
rm -rf $(SOURCES:.c=.dSYM)

ls | grep
is a useless use of ls. http://porkmail.org/era/unix/award.html#ls
rm -rf f[0-9] f[0-9]*[0-9] f[0-9]*.dSYM
In clear, use globing. See http://mywiki.wooledge.org/glob.

Direct solution: quote your regex. Better solution: globs, brace expansion, and/or find ... -delete.
rm -rf f{1,2}{,.dSYM}
rm -rf f? f?.dSYM
ffind . -regex '.*/f[0-9]' -o -regex '.*/f[0-9].dSYM' -delete

Your command line shell is probably different from the shell make uses. I guess you use /bin/bash at the command line, but make uses /bin/sh by default. You can change it by prepending
SHELL=/bin/bash
to the Makefile.
Adding quotes around vertical bars and parentheses is always safer than use them unquoted. Also note that the dollar sign must be doubled in a Makefile not to be treated as a special character.

Related

Using rm -i !(*.c|Makefile) in Makefile?

I have a directory where I have some C program files and a Makefile with executables of those C files. I can simply run this to remove all the executable at once in rm !(*.c|Makefile).
But when i am adding this to my makefile
CFLAGS= -Wall -g -lm
SHELL=/bin/bash
careful:
echo "nothing"
clean:
rm -i !(*.c|Makefile)
I am getting this error while executing the command
/bin/bash: -c: line 1: syntax error near unexpected token `('
/bin/bash: -c: line 1: `rm -i !(*.c|Makefile)'
make: *** [Makefile:8: clean] Error 2
I am not too knowledgeable about Bash syntax but I know that () is treated as a subshell, I think that's why I can't run rm (*.c) directly in the terminal because *.c is not a command (or any valid syntax). But running rm -i !(*.c) works in the terminal. So I guess !() is treated differently in Bash.
My assumption on all this: in Makefile when I am running make clean it is treating !(*.c|Makefile), differently than in normal terminal, or somehow it is disregarding ! in !(*.c|Makefile)
So my questions are:
Are !() and () treated differently in Bash?
Why does !(wildcard) work in the terminal but not in my Makefile
How can I make it work in the Makefile?
The Bash-specific extended globbing patterns are not enabled out of the box for noninteractive shells.
!(...) is purely part of this wildcard syntax, and has nothing with subshells to do.
Probably a better solution anyway is to refactor this so you don't depend on Bash.
.PHONY: clean
clean:
rm -i $(filter-out $(wildcard *.c) Makefile,$(wildcard *))
The Bash Reference Manual doesn't have a good anchor to link to, but the extended globbing syntax available with extglob is described in the section on Pattern Matching
If you really wanted to, you could shopt -s extglob in your Makefile, too, but this gets rather complicated as you will have to hide the statement from the parser until the shell is configured to understand this syntax. For example,
.PHONY: clean
clean:
shopt -s extglob; \
eval 'rm -i !(*.c|Makefile)'
Notice also how shopt and eval need to be on the same logical line, as make out of the box executes each recipe line in a separate new shell instance (though you can change that with .ONESHELL)

Compiling C using make in windows (MinGW)

I am following the book "Learn C the Hard Way" and I have reached the section where you compile the code using makefiles. I keep getting errors that the file was not found when running make clean.
Here is the error followed by the make file and the directory
C:\Users\Me\Desktop\C Code\HARD WAY\Dust off that compiler>make clean
rm -f main.c
process_begin: CreateProcess(NULL, rm -f main.c, ...) failed.
make (e=2): The system cannot find the file specified.
make: *** [makefile:7: clean] Error 2
EXECUTABLE=main.exe
CC="C:\Program Files\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin\gcc.exe"
CFLAGS=-Wall -g
clean:
rm -f main
directories
EDIT: I removed the .c from the "rm -f main" as it seems to be a small error but has made no difference to the end result
EDIT 2: I worked out that the make file is issuing linux commands which is why it wasn't working, replacing rm with del fixes the issue
I worked out that the make file is issuing linux commands which is why it wasn't working, replacing rm with del and removing -f fixes the issue

make clean: Only remove files that have been generated

With the Makefile I'm working on, I convert pdf files into txt files.
I've implemented a clean target that would remove all .txt files. However, I do not wish to delete the source files, only those that have been generated.
Example:
I have following files in my folder:
pdfsource.pdf and donotharm.txt
Running my makefile would create following file:
pdfsource.txt
For now, my clean looks like this:
rm -f *.txt
Using make clean would not only delete pdfsource.txt, which is desired, but also donotharm.txt.
I think I could use: .PRECIOUS: donotharm.txt, but this is really specific. I'd like to have a general solution to this.
Thanks in advance!
You can list the generated files in a make variable and use it to clean only these:
PDF := $(wildcard *.pdf)
TEXT := $(patsubst %.pdf,%.txt,$(PDF))
...
clean:
rm -f $(TEXT)
Or, if you prefer a more compact (but a bit less readable) form:
clean:
rm -f $(patsubst %.pdf,%.txt,$(wildcard *.pdf))
Of course, this works only if there is no {foo.pdf,foo.txt} pair for which you want to preserve foo.txt from deletion by make clean.
Note: using make variables, in such a case, is usually a good idea because they can be shared among various rules. Example:
PDF := $(wildcard *.pdf)
TEXT := $(patsubst %.pdf,%.txt,$(PDF))
.PHONY: all clean
all: $(TEXT)
$(TEXT): %.txt: %.pdf
pdftotext $< $#
clean:
rm -f $(TEXT)
Another approach: "make -nps" gives you all make's metadata about dependencies. For any intermediate file, it prints
filename: ...
So you can exactly delete such files with a generic "clean" rule:
clean:; MAKEFLAGS= ${MAKE} -j1 -spinf $(word 1,${MAKEFILE_LIST}) \
| sed -n '/^# I/,$${/^[^\#\[%.][^ %]*: /s/:.*//p;}; 1s|.*|${clean}|p' | xargs rm -rf
The first line handles use of makefiles other than the defaults (makefile, GNUmakefile, Makefile)
In the "sed" command:
/^# I/,$
... selects the zone of make metadata with dependencies.
/^[^\#\[%.][^ %]*: /
... filters out comments, implicit rules, and files with no dependencies (the trailing space). It doesn't filter out phony targets; oh well.
Finally:
1s|.*|${clean}|p
adds any explicit targets for "clean" -- what you know that make does not; e.g.
clean += tmpdir/* *.gcda

Makefile Explanation

I am new to makefiles and trying to understand some code:
%.o:%.c
$(CC) $^ -c -Wall
$(CC) -MM $*.c > $*.dep
$(OBJECTDUMP) -d -M intel -S $#
#mv -f $*.dep $*.dep.tmp
#sed -e 's|.*:|$*.o:|' < $*.dep.tmp > $*.dep
#sed -e 's/.*://' -e 's/\\$$//' < $*.dep.tmp | fmt -1 | \
sed -e 's/^ *//' -e 's/$$/:/' >> $*.dep
#rm -f $*.dep.tmp
Can someone please explain what the last 5 lines of code are doing?
The purpose of those lines is to deal with a problem with dependency handling.
Suppose you have a header file bar.h, and a source file foo.c that contains the line
#include "bar.h"
Now generate the dependency file:
$(CC) -MM $*.c > $*.dep
The file foo.dep now contains:
foo.o: foo.cc bar.h
Wonderful. I'm sure the makefile has a line like -include *.dep, so now Make will handle foo's dependencies correctly. But now suppose you edit foo.c, remove that #include line, and delete the no-longer-needed bar.h. The next time you try to make foo, Make will read the old foo.dep which calls for bar.h, see that there is no such header and no known way to build it, and abort. Make will not know that the header is not needed until it rebuilds the dep file, which it cannot do because the header is missing and Make thinks it is needed.
One solution is to delete foo.dep when this situation arises (before Make aborts, if possible). Another is to add a line to foo.dep:
bar.h:
which will assuage Make's fears about the missing header. The first approach requires human attention, the second can be automated:
#mv -f $*.dep $*.dep.tmp # rename foo.dep -> foo.dep.tmp
#sed -e 's|.*:|$*.o:|' < $*.dep.tmp > $*.dep # this does nothing and appears to be vestigal
#sed -e 's/.*://' \ # remove the target, leaving foo.c bar.h
-e 's/\\$$//' \ # remove line continuation marks, if any
< $*.dep.tmp | fmt -1 | \ # put each word on its own line
sed -e 's/^ *//' \ # remove leading whitespace
-e 's/$$/:/' \ # add a colon to the end of each line (making it a rule)
>> $*.dep # save the result as foo.dep
#rm -f $*.dep.tmp # delete the temporary file
$* corresponds roughly to the % in the first line.
The #mv line moves the old basename.dep file to basename.dep.tmp
The first #sed line edits what's in the basename.dep.tmp, replacing anything up to a colon with basename.o: (because the $* is expanded by make, not the shell).
The second #sed line and the following line do some more editing — appending another variant of the basename.dep.tmp file to the end of basename.dep.
The #rm line removes the temporary basename.dep.tmp file.
A more thorough analysis of the second sed sequence requires more detailed knowledge of what's in the .dep file and exactly what fmt -1 does for you.
However, it seems that the goal is to update the dependencies that apply to the source file, based on the information from the compiler plus massaging it in a way to suit the programmer. The why is not clear to me.

extracting and creating ipk files

ipk packages are the intallation packages used by opkg.
I'm trying to extract the contents of one of them and also create my own ipk.
I've read that I should be able to untar them but that is not true.
I've tried:
tar -zxvf mypack.ipk
and I get:
zip: stdin: not in gzip format
I've also tried:
tar -xvf mypack.ipk
and I get:
tar: This does not look like a tar archive
I've found that most of the information on the internet regarding ipk's are inaccurate.
My ipk was generated by bitbake. I'm having a hard time with bitbake and want to avoid using it.
Any ideas on how to extract and how to create ipk files? A simple template with a single package would be useful to have.
I figured it out.
You can extract the main package with the ar x command, then extract the control.tar.gz with the tar -zxf command.
I have tested "ar x package-name.ipk" command but it didn't help
I found bellow command which worked perfectly
tar zxpvf package-name.ipk
This extracts three files:
debian-binary
data.tar.gz
control.tar.gz
use the same command to open data.tar.gz and control.tar.gz files
for more information refer to
https://cognito.me.uk/computers/manual-extractioninstallation-of-ipk-packages-on-gargoyleopenwrt/
You need to create a control file, and then do some archiving using tar and ar. In my case, I was distributing just python scripts, so there was no architecture dependency. You should check the control and Makefile into version control, and delete all the other intermediate files.
Here are the contents of control
Package: my-thing-python
Version: 1.0
Description: python scripts for MyCompany
Section: extras
Priority: optional
Maintainer: John
License: CLOSED
Architecture: all
OE: my-thing-python
Homepage: unknown
Depends: python python-distutils python-pyserial python-curses python-mmap python-ctypes
Source: N/A
Here is my Makefile which sits in the same directory as all my python scripts.
all: my-thing-python.ipk
my-thing-python.ipk:
rm -rf ipk
mkdir -p ipk/opt/my-thing-python
cp *.py ipk/opt/my-thing-python
tar czvf control.tar.gz control
cd ipk; tar czvf ../data.tar.gz .; cd ..
echo 2.0 > debian-binary
ar r my-thing-python.ipk control.tar.gz data.tar.gz debian-binary
clean: FORCE
rm -rf ipk
rm -f control.tar.gz
rm -f data.tar.gz
rm -f my-thing-python.ipk
FORCE:
Extracting with these commands:
Extract the file by running the command:
ar -xv <.ipk file>
Extract the control.tar.gz file by running the command:
tar -zxvf control.tar.gz
data.tar.gz : untar by running the command:
tar –zxvf data.tar.gz
If you want a list of files in an ipk, you can do something like:
#!/bin/sh
for f
do
tar -x -z -f $f ./data.tar.gz -O | tar tvzf -
done
-O is extract to standard output.
ipk files used to be AR (like DPKG), but are now tgz.
I feel that some dpkg utility ought to cope with ipkg files, but I haven't found the right one.

Resources