Makefile: Trying to understand sequence of execution - c

I have the following Makefile.
objects = foo.o bar.o all.o -- line 1
all: $(objects)
# These files compile via implicit rules
foo.o: foo.c
bar.o: bar.c
all.o: all.c
all.c:
echo "int main() { return 0; }" > all.c
%.c:
touch $#
clean:
rm -f *.c *.o all
When i run make, i get the below output.
echo "int main() { return 0; }" > all.c
cc -c -o all.o all.c
touch foo.c
cc -c -o foo.o foo.c
touch bar.c
cc -c -o bar.o bar.c
cc all.o foo.o bar.o -o all
Question:
Line 1 of the Makefile shows foo.o is the first dependency. So why all.o which depends on all.c are executed first?

You are confusing things by having the file all.c (and object file all.o) and the target all. Conventionally, the all target is a pseudo-target (.PHONY in GNU Make) for 'all the things that are (normally) built by this makefile. This isn't enforced — it is merely convention.
However, here, make is trying to use all.c to build a program all. And it knows it can build all by compiling all.c, so it generates that first.
You could clarify things by either using any.c instead of all.c and building any.o and program any, and having the all: target read:
all: any
That would build the program any

Related

Building binary with not default target 'all' in Makefile

I have simple Makefile below. That runs clear when using default target all.
objects = foo.o bar.o all.o
all: $(objects)
all2: $(objects)
# These files compile via implicit rules
foo.o: foo.c
bar.o: bar.c
all.o: all.c
all.c:
echo "int main() { return 0; }" > all.c
%.c:
touch $#
clean:
rm -f *.c *.o all2
But when I execute make all2 I got:
touch foo.c
cc -c -o foo.o foo.c
touch bar.c
cc -c -o bar.o bar.c
echo "int main() { return 0; }" > all.c
cc -c -o all.o all.c
touch all2.c
cc -c -o all2.o all2.c
cc all2.o foo.o bar.o all.o -o all2
rm all2.o all2.c
all2 target depends on foo.o bar.o all.o, but why it trying to get all2.c ?
Why it executes clean target at he end? Line rm all2.o all2.c
The reason is that make has a built-in rule that knows how to build a binary file from an object file that has the same prefix; that is, given a binary file foo make has a built-in rule that knows how to build it from foo.o.
You have a target all2 and normally that doesn't matter because you don't have an all2.c and so make can't find a way to build all2.o and so it decides there's no rule to build all2.
But in your makefile, you've created a rule to build any .c file, so when make tries to find a way to build all2.o it discovers it can be built from all2.c and there is a rule to build all2.c, so the rule matches.
Of course, you won't have a rule like %.c : ; touch $# in your non-test makefile so you won't see this problem in any real makefile.
If you want to make this work for your test environment you can, as #Cheatah says, declare the all2 rule to be phony which will prevent make from trying to look up any rules to build it.

Compiling any single C code using a Makefile

I am pretty new to Makefiles, so my question might be silly. It would be the following: Can I pass another argument to make so that I can do stuff with this additional argument?
More specifically, if possible, I would want to use this to compile any single C source code into its executable using a Makefile.
For example, if I have two files foo.c and bar.c, I would want to be able to compile them using the same recipe, therefore doing something similar to make compile foo.c or make compile bar.c that compiles them into the foo or bar executables, respectively.
If it is not possible, is there an alternative I can do for simple C programs? It would be useful to save time (not updating the Makefile or not writing gcc -o name name.c every time I want to compile a C file I just created).
Thank you in advance for your patience and time.
If you are dealing with single main file applications, a bash script sounds much simpler, build.sh:
#!/bin/bash
SOURCE=$1
BIN=${SOURCE%.c}
if [[ -z "$SOURCE" || ! -s "$SOURCE" ]]; then
echo "SOURCE is empty"
exit 1
fi
gcc -o "$BIN" "$SOURCE"
and then invoke it like this:
./build.sh foo.c
./build.sh bar.c
I use the makefile below for this. 'make' or 'make all' compiles each .c (eg eg.c) file in the directory to an executable in the same directory (eg eg). 'make eg' compiles just eg.c to eg . There's nothing special about the compiler flags -- CFLAGS -- or linker flags -- LFLAGS -- they're just what I habitually use.
CC = gcc
CFLAGS = -std=gnu11 -Wall -Wextra
LFLAGS = -lm -lrt
OPATH = ./
PROGS += $(patsubst %.c,$(OPATH)%,$(wildcard *.c))
all: $(PROGS)
$(OPATH)% : %.c ; $(CC) $(CFLAGS) $< -o $# $(LFLAGS)
clean:
\rm -f $(PROGS)
Make has built-in rules that knows how to compile a source file into an executable.
So, you don't even have to write a makefile at all (unless you want to use special compiler options). This will work:
$ ls
bar.c foo.c
$ make foo
cc -o foo foo.c
$ make bar
cc -o bar bar.c
If you want to modify the compiler operations, add a makefile that sets the appropriate built-in variables:
$ ls
bar.c foo.c Makefile
$ cat Makefile
CC = gcc
CFLAGS = -O2 -g
$ make foo
gcc -O2 -g -o foo foo.c
$ make bar
gcc -O2 -g -o bar bar.c
how about using this:
================== makefile =====================
CC=gcc
CCFLAGS=-ansi -pedantic -Wall
all : clean foo bar
foo : foo.c
$(CC) $(CCFLAGS) foo.c -o foo
bar : bar.c
$(CC) $(CCFLAGS) bar.c -o bar
clean :
rm -f *.o *.*~ foo bar
====================== end makefile ======================
invoking make as make will run the default target (all in this case) removing all intermediate files and output files and then rebuilding foo and bar
invoking make as make foo will only run the rule for making foo.
invoking make as make bar will only run the rule for making bar.
invoking make as make clean will only run the rule for cleaning the project directory -- removing all intermediate and output files.

I have been working on this makefile for hours. It keep getting the error: make: *** No rule to make target 'hangman.c', needed by 'hangman.o'. Stop

CFLAGS=-std=c99 -Wall
CC=gcc
hangman: hangman.o hangman.c
$(CC) $(CFLAGS) hangman.o hangman.c -o hangman
hangman.o: hangman.c
$(CC) $(CFLAGS) -c hangman.c
clean:
rm -f hangman *.o
Well, hangman (the program binary) only depends of hangman.o, but not of hangman.c, which has already been compiled into hangman.o (at compilation phase).
In Makefile, you only state the direct dependencies, while make(1) does the rest of the work.
I use to designate all the objects to a program in a variable, as they will be used several times. In this way:
# Makefile -- make file for hangman.
targets = hangman
hangman_objs = hangman.o
hangman: $(hangman_objs)
$(CC) $(LDFLAGS) -o hangman $(hangman_objs)
and nothing else, as make(1) has an automatic rule, that is
.c.o:
$(CC) $(CFLAGS) -o $# -c $<
which is the equivalent to this rule:
hangman.o: hangman.c
$(CC) $(CFLAGS) -o hangman.o -c hangman.c
(and the same for each .c and .o file you can have.)
By the way, the error you are receiving, is that make has found a dependency on hangman.c but doesn't find any file called hangman.c so it needs to build it, but you don't provide this file. You have probably erased your hangman.c file (which is something that sometimes happen if you mispell files in the Makefile, make ends erasing files that are important for you) In this case, it tries to build handman which depends on handman.o which depends on handman.c, so no finding handman.c makes make(1) to say, I have a dependency on handman.c but no such file is found (and I have no dependency to follow that allows me to build it)
If your project is a single source project, then you can avoid the generation of hangman.o and create a Makefile like this:
hangman: hangman.c
$(CC) $(CFLAGS) $(LDFLAGS) -o hangman hangman.c
Which states an explicit, direct reference from the binary to the source code. In this case, you don't use the -c flag to the compiler to just compile, and don't link, and build your executable directly with one command. This is not used in large projects, as normally you want to just compile the sources that have changed. As in this example:
hangman_objs = hang.o man.o foo.o bar.o a.o b.o c.o
hangman: $(hangman_objs)
$(CC) $(LDFLAGS) -o hangman $(hangman_objs)
if you expand the variable, you'll get this rule:
hangman: hang.o man.o foo.o bar.o a.o b.o c.o
cc -o hangman hang.o man.o foo.o bar.o a.o b.o c.o
#all this are automatic dependencies generated from
# .c.o:
# $(CC) $(CFLAGS) -c $< -o $#
# for the files hang.o man.o foo.o bar.o a.o b.o c.o
hang.o: hang.c
cc -O2 -pipe -c hang.c -o hang.o
man.o: man.c
cc -O2 -pipe -c man.c -o man.o
foo.o: foo.c
cc -O2 -pipe -c foo.c -o foo.o
bar.o: bar.c
cc -O2 -pipe -c bar.c -o bar.o
a.o: a.c
cc -O2 -pipe -c a.c -o a.o
b.o: b.c
cc -O2 -pipe -c b.c -o b.o
c.c: c.c
cc -O2 -pipe -c c.c -o c.o
but you must not use both, the object code and the source code in the linking phase of your program. The compiler will link the file you provide hangman.o and will compile it also (which generates a new hangman.o) and will try to link both (two versions of the same code) and that can generate new errors.
My approach to your program would be:
# main targets to build (programs)
targets = hangman
# toclean maintains everything must be erased on clean.
toclean = $(targets)
# object files of hangman target
hangman_objs = hangman.o foo.o
# add all those objects to the toclean variable.
toclean += $(hangman_objs)
# libraries
hangman_ldflags = -L path/to/libbar
hangman_libs = -lbar
# main target all
all: $(targets)
# ... and clean
clean:
rm -f $(toclean)
# just the link phase, the compiling is automatically done.
hangman: $(hangman_objs)
$(CC) $(LDFLAGS) $($#_ldflags) -o $# $($#_objs) $($#_libs)

Shared library from archive (.a) file in C

I was given a task to create archive file .a from objects file and create a shared library file from archive .a file. I have tried experiment having following file:
foo.h
#ifndef _foo_h__
#define _foo_h__
extern void foo(void);
extern void bar(void);
#endif //_foo_h__
foo.c
#include<stdio.h>
void foo(void)
{
puts("Hello, I'm a shared library");
}
bar.c
#include<stdio.h>
void bar(void)
{
puts("This is bar function call.");
}
main.c
#include <stdio.h>
#include"foo.h"
int main(void)
{
puts("This is a shared library test...");
foo();
bar();
return 0;
}
Makefile
CFLAGS = -Wall -Werror
LDFLAGS = -L/home/betatest/Public/implicit-rule-archive -Wl,-rpath,'$$ORIGIN'
all : run
run : main.o libfoo.so
$(CC) $(LDFLAGS) -o $# $^
libfoo.so : CFLAGS += -fPIC # Build objects for .so with -fPIC.
libfoo.so : libfoo.a
$(CC) -shared -o $# $^
libfoo.a : foo.o bar.o
ar cvq libfoo.a foo.o bar.o
# ar cr libfoo.a foo.o bar.o
# Compile any .o from .c. Also make dependencies automatically.
%.o : %.c
$(CC) -c $(CFLAGS) -o $# $<
#Include dependencies on subsequent builds.
.PHONY : all clean
clean :
-rm -f *.o *.d run libfoo.*
This simple test program seems to run fine but while compiling using make it producing error as:
cc -c -Wall -Werror -o main.o main.c
cc -c -Wall -Werror -fPIC -o foo.o foo.c
cc -c -Wall -Werror -fPIC -o bar.o bar.c
ar cvq libfoo.a foo.o bar.o
a - foo.o
a - bar.o
cc -shared -o libfoo.so libfoo.a
cc -L/home/betatest/Public/implicit-rule-archive -Wl,-rpath,'$ORIGIN' -o run main.o libfoo.so
main.o: In function `main':
main.c:(.text+0xf): undefined reference to `foo'
main.c:(.text+0x14): undefined reference to `bar'
collect2: ld returned 1 exit status
Makefile-test:7: recipe for target 'run' failed
make: *** [run] Error 1
Somebody please point out that where I am getting wrong?? Thanks a lot.
If you wish to build a shared library from a static library you have to tell the linker to use all function/symbols included in static library (.a). Otherwise nothing will be included in the shared library (.so).
You have to use --whole-archive/--no-whole-archive pair in linking.
CFLAGS = -Wall -Werror
LDFLAGS = -L/home/betatest/Public/implicit-rule-archive -Wl,-rpath,'$$ORIGIN'
all : run
run : main.o libfoo.so
$(CC) $(LDFLAGS) -o $# $^
libfoo.so : CFLAGS += -fPIC # Build objects for .so with -fPIC.
libfoo.so : libfoo.a
$(CC) -shared -o $# -Wl,--whole-archive $^ -Wl,--no-whole-archive
libfoo.a : foo.o bar.o
ar cvq libfoo.a foo.o bar.o
# Compile any .o from .c. Also make dependencies automatically.
%.o : %.c
$(CC) -c $(CFLAGS) -o $# $<
#Include dependencies on subsequent builds.
.PHONY : all clean
clean :
-rm -f *.o *.d run libfoo.*
You can check the exported functions using nm command:
$ nm -D libfoo.so | grep ' T '
0000000000000702 T bar
0000000000000714 T _fini
00000000000006f0 T foo
0000000000000590 T _init
When no --whole-archive/--no-whole-archive pair is used you get:
$ nm -D libfoo.so | grep ' T '
0000000000000714 T _fini
0000000000000590 T _init
Your mistake is in the question: do not make shared library from archive, make it from objects, i.e. change Makefile like this
libfoo.so : foo.o bar.o
$(CC) -shared -o $# foo.o bar.o
you can also use %.o to mean all objects
also you need CFLAGS = -fPIC globally, to affect compilation command, not only the link step, that is you .o object files should be position-independent.
also here extern is not needed, all C functions are implicitly extern
extern void foo(void);
I would also not recommend using rpath and better
export LD_LIBRARY_PATH=.
to load .so from current directory (or export path to .so's directory)

When I use "gcc" in makefile, after making it, I got a "cc" output

For example:
There are 3 source files {main.c test1.c test2.c} in the directory
and a directory file named test3,
and there is a source file named test.c in the directory of test3.
Now I want to create a makefile to compile and link these four source files.
And this is my Makefile:
# Cancel statement "CC=gcc"
src:=$(wildcard *.c) test3.c
obj:=$(patsubst %.c,%.o,$(src))
main:$(obj)
gcc -o main $(obj)
.PHONY:clean
clean:
rm *.o *~
When I called make to compile them, I got a output like this:
cc -c -o main.o main.c
cc -c -o test1.o test1.c
cc -c -o test2.o test2.c
cc -c -o test3.o test3/test3.c
gcc -o main main.o test1.o test2.o test3.o
I know 'cc' is linked to 'gcc' in Linux.
What I don't understand is why did Make call cc to compile these four source files, but call gcc to link the object files?
You changed one rule: the one that links the program main from the object files. And when make did that link, you can see it used gcc.
You didn't do anything to change the built-in rules that make is using to compile the object files, so they use the default (the value of the variable CC) which is cc.
You wrote only the rule to link the object files, and allowed Make to use its default rule to decide how to build the object files from the source files.
GNU Make will expose its rules if you ask it with --print-data-base. In this case, it tells us
%.o: %.c
# recipe to execute (built-in):
$(COMPILE.c) $(OUTPUT_OPTION) $<
and
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
and finally
CC = cc
This explains why Make uses cc to compile your C sources. To change that, simply set CC = gcc. Here's a complete Makefile which does that and also makes best use of Make's built-in rules, to help when you need to extend it:
src := $(wildcard *.c) test3.c
obj := $(patsubst %.c,%.o,$(src))
CC = gcc
main: $(obj)
$(LINK.c) -o $# $^ $(LDLIBS)
.PHONY: clean
clean:
$(RM) *.o *~

Resources