Makefile Explanation - c

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.

Related

ESQL INFORMIX precompilation step in makefile : error -33042 cannot open input file

I have to modify some very old source code. It comes with a very long Makefile and I'm not very familiar with Makefiles in general. It also relies on a precompiling step by an old Informix server.
I need to modify the Makefile so that the source code inside my_directory will compile by including the sources in common_src.
Here is the structure :
- common_src
- my_directory
- Makefile
The .o files should be generated inside my_directory.
I have the following :
COMMONSRC=$(HOME)/common_src
ESQL= esql
ESQLFLAGS= -e
CFLAGS= -DGTKDVPT \
-I$(INFORMIXDIR)/incl/esql \
-I$(COMMONSRC)
OFILES= rwfich.o
HFILES= sqlheader.h
COMMON_HFILES= $(HFILES:%.h=$(COMMONSRC)/%.h)
gcd: bin/gcd
bin/gcd: $(LIBGC) gcd.o $(LIBOUTILS)
#echo -n "Link: bin/gcd "
#$(CC) $(CFLAGS) gcd.o $(LIBGC) \
$(SQLLIBS) $(LNKOPT) -obin/gcd_lnk > gcdlnk.err 2>&1
#touch bin/gcd
#mv -f bin/gcd bin/oldgcd
#mv -f bin/gcd_lnk bin/gcd
#echo "Ok."
$(LIBGC): $(RWFOBJ)
#echo -n "$(LIBGC) : "
#rm -f $(LIBGC)
#echo -n "Construction ... "
#ar rc $(LIBGC)
#echo "Ok."
$(RWFOBJ): $(OBJDIR)/%.o : $(COMMONSRC)/%.ec $(COMMON_HFILES)
#echo -n "$< : Precompilation "
#$(ESQL) $(ESQLFLAGS) $< > $*.err 2>&1
#echo -n "Compilation "
#$(CC) $(CFLAGS) -c $*.c >> $*.err 2>&1
#rm $*.c
#echo "Ok."
When I run the Makefile, and it gets to $(RWFOBJ), the .err file output by the Informix recompilation step for rwfich.ec says : esqlc: "/home/my_directory/rwfich.ec", line 7: Error -33042: Cannot open input file 'sqlheader.h'. 1 error(s) found.
Does anyone have an idea what's wrong with this ? I have even tried hardcoding the .h file with its complete path:
$(RWFOBJ): $(OBJDIR)/%.o : $(COMMONSRC)/%.ec $(HOME)/common_src/sqlheader.h
but no dice : I get the same error.
Thanks a lot to anyone who could point me in the right direction.
When asking for help please always include the make command you invoked and the error output you got, cut and pasted into your question (with proper formatting) rather than paraphrased. Even slight differences in the exact text of the error can make a big difference.
Also, your makefile is incomplete because you don't show us what the value of RWOBJ is, and you have a typo (you define COMMON_HFILES but you refer to $(COMMON_HFILE). Since make doesn't complain about this I assume the typo only exists here in your question not in your actual makefile (this another reason why cut and paste is better).
Also please include the operating system you're using and the version of make you're using.
In this case the error you're getting "Cannot open input file" is not coming from make, it's coming from your compiler. Make is able to find your header files just fine, but then it invokes the compiler and you haven't told the compiler where to find the header files.
You need to add the -I$(COMMONSRC) option to your compile line (assuming you're using a POSIX-ish compiler such as GCC or clang).
You haven't provided enough information about your complete makefile for us to advise you on where to put that option but see if there's a make variable containing other -I options etc.
I solved the problem by adding the $(CFLAGS) to the Informix precompiling line, like so: #$(ESQL) $(ESQLFLAGS) $(CFLAGS) $< > $*.err 2>&1, so the total target looks like:
$(RWFOBJ): $(OBJDIR)/%.o : $(COMMONSRC)/%.ec $(COMMON_HFILES)
#echo -n "$< : Precompilation "
#$(ESQL) $(ESQLFLAGS) $(CFLAGS) $< > $*.err 2>&1
#echo -n "Compilation "
#$(CC) $(CFLAGS) -c $*.c >> $*.err 2>&1
#rm $*.c
#echo "Ok."

using MAKEFILE to copy files before compilation and delete them after

I am trying to copy files befoe compilation (I have two source files with same name so I copy the files to a files with a different name) and delete them at the end of the MAKEFILE.
I am trying to do the folliwng but probably there is mismatch in the execution order.
How can I do it correctly?
all: copy_dup_files $(dst_dir) $(APP_TARGET_LIB) delete_dup_files
copy_dup_files:
#echo "COPYING DUP FILES"
$(shell cp /aaa/hmac.c /aaa/hmac1.c )
$(shell cp /bbb/hmac.c /bbb/hmac2.c )
delete_dup_files:
#echo "DELETING DUP FILES"
$(shell rm /aaa/hmac1.c )
$(shell rm /bbb/hmac2.c )
Thanks
The purpose of $(shell) is to produce an output which Make reads. The recipe lines should not have this construct at all.
# this is evaluated when the Makefile is read
value := $(shell echo "Use the shell to produce a value for a variable")
# this is evaluated when you say "make foo"
foo:
echo 'No $$(shell ...) stuff here'
So, all the $(shell ...) stuff in your attempt gets evaluated when the Makefile is read, but before any actual target is executed.
Your makefile is trying to say /aaa/hmac1.c depends on /aaa/hmac.c.
Thus we have:
/aaa/hmac1.c: /aaa/hmac.c
cp $< $#
/bbb/hmac2.c: /bbb/hmac.c
cp $< $#
/aaa/hmac1.o /bbb/hmac2.o: %.o: %.c
gcc $< -o $#
myprog: /aaa/hmac1.o /bbb/hmac2.o
gcc $^ -o $#
This is clean and parallel safe (a good test of any makefile).
There are innumerable style improvements you could make, like
Get rid of the absolute paths
Use symbolic links instead of copying
Automatic dependency generation (for .h files, etc.)
Don't besmirch the source tree — put all the intermediate files (the .os and the temporary .cs) in their own build folder
&c. &c.

Script to compile C code

Usually if I want to compile a C program called number_input.c I would type
cc -o number_input number_input.c
I want to use my mac terminal to make a script so that I don't have to type that extra word. Originally I did this to save myself 1 sec of programming but ironically I've spent over 2 hrs trying to get this to work.
a= echo "$1" | rev | cut -c3- | rev
echo $a
cc -o $a $1
echo $1
This is my output:
number_input
clang: error: no input files
number_input.c
I can tell that the names are being inputted correctly but for some reason the cc command isn't taking in the value of $1? I am assuming that somehow the $1 isn't directly converted into a string or something like that but I am not sure.
Your error is on the first line, since you're not assigning anything to a:
a=$(echo "$1" | rev | cut -c3- | rev)
Would fix the problem (for well-behaved filenames, at least, since you're missing quotes further down in your script). A space after a means you're assigning an empty string to it and then running the commands in the pipeline.
Instead of going to all the effort of reversing the twice, just remove the last two characters with ${1%??}:
cc -o "${1%??}" "$1"
The most common tool to do this is make. It reads the recipes from a file named Makefile in the directory it is run, and performs any tasks necessary. It is smart enough to check the file timestamps to detect if or which parts of your projects need to be re-compiled. Here is an example Makefile:
CC := gcc
CFLAGS := -Wall -O2
LDFLAGS := -lm
PROGS := number_input
.PHONY: all clean
all: $(PROGS)
clean:
rm -f $(PROGS)
$(PROGS): %: %.c
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $#
Note that indentation in a Makefile must use tabs, not spaces. If you copy the above, and paste to a file, you will need to run sed -e 's|^ *|\t|' -i Makefile to fix the indentation.
The first three lines name the compiler used, the compiler options, and the linking options. The -lm linking option is not needed for your particular use case; I just included it because you will sooner or later want to use <math.h>, and then you do need to include the -lm linking option.
The PROGS line names your programs. You can specify more than one, just separate them by spaces.
The .PHONY: line tells make that targets all and clean are "phony", that they do not generate files of that name.
The all recipe, as the first recipe in a Makefile, is the default recipe that is followed, when you run make. This one tells that all programs listed in PROGS should be built.
The clean recipe (run make clean) removes all temporary files and compiled files from the directory -- essentially cleaning it.
The last recipe is a tricky one. It says that all the files listed in PROGS are each built from a file having the same name plus a .c suffix. The $^ refers to the .c file name, and $# to the file name without the suffix.
If this Makefile were used for returning exercises via email to a teacher, I'd also add a new .PHONY target, tarball:
CC := gcc
CFLAGS := -Wall -O2
LDFLAGS := -lm
PROGS := number_input
TAR := $(notdir $(CURDIR)).tar
.PHONY: all clean tarball
all: $(PROGS)
clean:
rm -f $(PROGS)
tarball: clean
rm -f ../$(TAR)
tar -cf ../$(TAR) $(notdir $(CURDIR))/
$(PROGS): %: %.c
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $#
Running make will compile number_input, if number_input.c has been modified after the last time number_input was compiled, or if number_input does not exist yet.
Running make TAR=myname-ex01.tar tarball removes the compiled files from the current directory, then creates a tarball of the current directory (and its subdirectories, if any) in the parent directory as myname-ex01.tar. If you run just make tarball, the tar file name will be the same as the name of the current directory, but with a .tar suffix.
I hope you can see why writing a Makefile is so useful.

Can I regenerate a makefile for a C project, with correct link order and dependencies?

I have source code I last worked on in the late 90's-2000 and have it all backed up, apart from the makefile (yes berate away, bad backups are almost as good as no backups): so... I am wondering if there is any automated way to generate the makefile or a good way to analyse the dependencies quickly?
Specifically I am looking for:
a tool which could analyse the dependencies and correct the link order for me.
if such does not exist, then advice is greatly appreciated as to how to best approach this problem from someone who has had similar problem(s) in the past
failing either of the above two options, I think the best approach is to create an analysis/make-file creation tool which can automatically generate the dependencies order for linking (I have held off on this approach as time is always in short supply to squeeze in another project).
The reason for this quest for help/advice is that the code-base is 300,000 lines of code (excluding comments) and spans hundreds of C/O files, and as often as I have tried creating a make-file by hand, it frustrates and confounds, hence my last attempt to seek help and ask in here.
For reference: I have tried Cmake, AutoMake, GenMake and similar tools in the past to generate the makefile, all to no avail, as the dependencies are horrendous.
Generic makefile script
As it may be of use to others, here is the makefile I usually use for less convoluted C and C++ projects, as it saves me having to worry about creating a new one every time:
$(VERBOSE).SILENT:
PROGRAMNAME = prog
CC = gcc
CC += -c
CPP = g++
CPP += -c
ASM = nasm
ASM += -f elf -d ELF_TYPE
LD = g++
OBJFILES = $(patsubst %.c,%.o,$(wildcard *.c))
OBJFILES += $(patsubst %.s,%.o,$(wildcard *.s))
OBJFILES += $(patsubst %.cpp,%.o,$(wildcard *.cpp))
all: $(PROGRAMNAME)
clean:
#echo "Cleaning object files"
#echo " rm -f *.o"
rm -f *.o
#echo "Cleaning backups"
#echo " rm -f *~"
rm -f *~
#echo "Removing program file"
#echo " rm -f "$(PROGRAMNAME)
rm -f $(PROGRAMNAME)
%.o: %.s
#echo "Assembling ASMs "$#
#echo " ASM "$<
$(ASM) $<
%.o: %.c
#echo "(C)ompiling "$#
#echo " CC "$<
$(CC) $<
%.o: %.cpp
#echo "(C++)ompiling "$#
#echo " CPP "$<
$(CPP) $<
$(PROGRAMNAME): $(OBJFILES)
#echo "Get ready...."
#echo "Linking "$#
#echo " LD -o "$(PROGRAMNAME)" "$(OBJFILES)
$(LD) -o $(PROGRAMNAME) $(OBJFILES)
#echo "Cry if it worked! Scream swear and cry if it did not..."
strip: $(PROGRAMNAME)
#echo "Stripping "$(PROGRAMNAME)
echo -n "Size of "$(PROGRAMNAME)" before stripping is "
ls -sh $(PROGRAMNAME) | cut -d' ' -f1
#echo " Stripping "$(PROGRAMNAME)
strip $(PROGRAMNAME)
echo -n "Size of "$(PROGRAMNAME)" after stripping is "
ls -sh $(PROGRAMNAME) | cut -d' ' -f1
nothing:
#echo "Nothing to do; see you later - I'm going home!!!"
#echo "Hey, try some of these:"
#echo "make all - this would be the one you want"
#echo "make strip - does not work in the real world, only in computers"
#echo "make clean - will help clean your mind up"
You are looking for the classic Unix tool from MIT, makedepend.
gcc & clang can generate dependencies, see Advanced Auto-Dependency Generation , this won't solve the whole problem but shall help you.
On a linux/unix system:
find . -name "*.c" -print > sources will give you a list of all the sources.
find . -name "*.c" -print|sed s/\.c/\.o > objects should give you a list that you can stick "OBJECTS=" in front of [maybe manually add some linebreaks].
cat sources|xargs gcc -M > myprog.deps should give you a list of header dependencies that you can include myprog.deps in your makefile. [1]
Now all you need is
TARGET = myprog # Whatever you call your program!
OBJECTS = ... # from "objects" file above.
SOURCES = ... # from "sources" file above
INCLUDES = -I subdir1 -I subdir2 ... # include directories used by this product
CFLAGS = ... ${INCLUDES} # Some suitable settings
CC = gcc
LD = ${CC}
LDFLAGS = ... # I don't know what this needs to be - usually nothing complicated.
all: ${TARGET}
clean:
rm -f ${TARGET} ${OBJECTS}
${TARGET}: ${OBJECTS}
${LD} -o $# ${OBJECTS}
.c.o:
${CC} -o $# $<
That should have MOST of the hard work done, unless you have to build internal tools or your many source files don't actually produce one final binary of course - in the latter case, you'll probably have to search for "main" and go through the above steps for each executable - you can still use a top-level Makefile and use include of the intermediate ones.
[1] You could add to the makefile - particularly if your project produces many different executables.
myprog.deps: ${SOURCES}
${CC} -MM ${SOURCES} > myprog.deps
include myprog.deps
Thanks for the great response: concise and very informational. Based on the answers I am now using a mixture of manual effort and GNU AutoMake (the modern successor to makedepend) to try recompiling, and so far seems to be quite effective.
Then shall come the fun and games of porting to OO code in C++...that's a task I would gladly avoid but needs must.
Thanks again!

Why does my regex break inside a Makefile?

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.

Resources