GNU make and Makefiles

Keywords: Embedded systems, Build Process, Toolchain, Compiler, Object Files


Before you move further with this tutorial it is worth mentioning that this tutorial is for those who want to efficiently compile their code from Command Line.

wait! what?? from command line…? Why would someone do that?

This is indeed a fact that all modern Embedded toolchains provide very nice GUIs for Compiling, downloading and debugging code. Though these GUIs greatly simplify development process – there are some areas where command line option is feasible than the GUIs. For example compiling code on a remote server.

Let’s say a team of developers working on embedded software each one write code and submit to server where the code is compiled and logs report is generated (Continuous Integration). In such cases GUI is definitely not an option. GNUs greatly simplify both small and large scale projects compilation process for single user and solo development.

So what is GNU make?

“GNU make is a classic dependency analyzer that checks source file dependencies and compile only those files that require compilation”.

The GNU make utility can be invoked by using make command from command line. When the make command is executed, it looks for a file with default name makefiles or Makefiles in the current directory. GNU make utility takes direction from makefiles to compile and link a bunch of source files. Makefiles instructs “GNU make” what to build and how to build.

“A makefile is a file (by default named “Makefile”) containing a set of directives used by a make build automation tool to generate a target/goal” [1].

Let’s explain the base idea with few example.

Consider a simple “C” file.

/*
    @file: hello.c
*/

#include <stdio.h>
int main () {
   printf ("Hello World!\n");
   return 0;  
}

In order to compile it from command line.

gcc  -o hello hello.c

Let’s build the same file using GNU make. First of all we have to write makefile for it, the one given bellow.

#simple makefile
all: hello.c
	gcc -o hello hello.c

Note: Don’t worry if you didn’t understand anything from above file.

Now to build it, simple run “make” utility in the same directory containing both “makefile” and “hello.c”.

make all

↪ I guess the command line approach was easy ☹.

HOLD ON…! ♔Let’s consider another example before jumping to conclusion.

Let’s compile two files this time. i.e. main.c , utils.c

/*
    @file: main.c
*/
extern void printString (void);
int main () {
    printString();
    return 0;
}


/*
    @file: utils.c
*/
#include <stdio.h>
void printString (void) {
    printf ("Printing string from Utils.\n");
}

The main.c and utils.c can be compiled from command line using the following command.

gcc -o hello main.c utils.c

To compile main.c and utils.c using GNU make, we need a makefile as given bellow.

#simple makefile 2

all: main.o utils.o
    gcc -o hello main.o utils.o

clean:
    rm -rf hello main.o utils.o

And to build the two files, run the make command in the same directory containing both makefile and main.c and utils.c.

make all

OUTPUT:
--------------------------
cc    -c -o main.o main.c         <---------
cc    -c -o utils.o utils.c       <---------
gcc -o hello main.c utils.c

NOPE… Again the command line approach seems to be easy ☹. If you think so, you missed the point…

↪ The command line arguments (file names) increases with the number of files! Let’s say a project has 100 files or more, how will you write each file name on command line?

OKAY… But what about shell/bash Script??? What if we write all files names in a bash script and run it from terminal. In this case we don’t need to write file names on command line.

Well bash script indeed is very powerful approach but make Utility is even more smarter! Recall the definition…“Compile only those files that require compilation”. Let’s repeat the second example and modify utils.c to print different string keeping main.c as it is and again run make command.

make all

OUTPUT:
--------------------------
cc    -c -o utils.o utils.c    <---------
gcc -o hello main.c utils.c

See the difference between the previous make command output and this time output (arrows). make is smart enough to check that only utils.c is modified so only compile utils.c and not main.c. In case of command line or bash script all source files are compiled every time the gcc command is called.

1. Makefiles

Now that if you are convinced that make is very useful and powerful tool for building large projects, one thing that’s worth mentioning is that as a developer it’s not about learning GNU make utility itself, it’s about exploring the makefiles that will help you make most out of GNU make utility.

From this point forward we will focus on makefiles and will try to keep things as simple as possible. Makefile follows a very simple and straight forward syntax given bellow.

target: dependencies
<TAB> action_1
        .
        .
<TAB> action_n

target: The target specifies WHAT TO BUILD? This target can be specified to make command e.g. make all (In this case “all” is the target). If no target is specified to make command, it will execute the first target. A target can be any name (e.g. all, clear etc.) or output file (main.o, utils.o).
dependencies: The dependencies specify WHICH FILES ARE REQUIRED TO BUILD THE target? dependencies are resolved first before moving forward to actions.
action: The action specifies WHAT ACTIONS/COMMANDS WILL BUILD THE target? The action commands must be preceded by TAB character

Let’s explain above with a sample makefile.

1.2 Makefile Interpretation:

Consider the following makefile.

#sample makefile

all: main.o utils.o
    gcc -o hello main.o utils.o
	
main.o: main.c
    gcc -c main.c -o main.o
	
utils.o: utils.c
    gcc -c utils.c -o utils.o 
	
clean:
    rm -rf hello main.o utils.o

There are 4 targets in the above make file i.e. allmain.outils.o, and clean. Any one of the four can be specified to the make as command line target argument. Lets say we specify:

make all

GNU make by default looks for file with name makefile/Makefile, once found a match it starts interpreting it. The above makefile/Makefile is interpreted as: (also see Figure-1).

  1. make ignores the first line i.e. start with # (comments – discussed later) and jump to the line containing target “all” – Figure-1.
  2. make checks whether there are any dependencies need to be resolved first. In this case make finds 2 dependencies i.e. main.o and utils.o.
  3. make jump to first dependency i.e. main.o and checks whether main.o file “Exists AND Up-to-Date. If the conditions are satisfied it will skip the dependency and move to next dependency.
  4. As the main.o doesn’t exists in this case so make will try to resolve the issue by checking:
    1. Whether there exists a target in current makefile that implies how to build the dependency under consideration i.e. main.o here.
    2. Whether there exists any file with the name dependency.c (main.c in this case) that can be converted to main.o (discussed later).
  5. In this case both options are valid so the make will jump to main.o target… But main.o also has a dependency main.c. So step 3, 4 are repeated for main.o.
  6. In this case as main.c exists so the dependencies are satisfied for main.o, the make will jump to actions and execute user command i.e. gcc -c main.c -o main.o to build main.o
  7. After building main.o, the make will jump back to target “all”. The next dependency is utils.o. All the above steps are repeated for utils.o.
  8. Now that all the dependencies for target “all” have been resolved its time to jump to target “all” actions commands.
  9. The make executes gcc -o hello main.o utils.o action for target “all” thus generating hello and exits.
Figure-1: Makefile Interpretation

1.3 Makefile components:

Now that you have a basic idea of how makefile are interpreted by make let’s deep dive into makefile itself. The makefiles have the following base components.

1.3.1 Comments:

In makefile any line starting with “#” is taken as comment and the rest of text to the end of line is totally ignored by make. A comment can appear anywhere in file.

#This is a comment

all:
   gcc -o hello hello.c  #This is also a comment

1.3.2 Variables/Macros:

A variable (in some versions of make called MACRO) is a name defined in a makefile to represent a string of text, called the variable’s value. These values are substituted by explicit request into targets, dependencies, commands, and other parts of the makefile. The variables names are case sensitive and may be any sequence of characters not containing `:’, `#’, `=’, or leading or trailing whitespace. A $ sign followed by either variable name in either () or {} substitutes the value of the variable in that place e.g. $(foo), ${foo}. Variables/Macros are of the following types in make.

  1. Recursively expanded Variable
  2. Simple expanded Variable
  3. Automatic Variables
  4. Built-in Variables

1.3.2.1 Recursively expanded Variable:

These type of variables are defined by ‘=’ sign. e.g.

CFLAGS = -C -DEBUG

The value of recursive variables are only substituted when its required – this means if the variable is modified, only the latest value will be substituted at the place of consideration. Consider the following example:

DEBUGFLAGS = $(CFLAGS) -ggdb -DDEBUG -O0

The variable $(CFLAGS) is not expanded until $(DEBUGFLAGS) is used [2].

Two things are important about recursive expanded variable.

  1. The are on-demand/on-requirement substituted as in above example.
  2. If it contains reference to other variables, these references are expanded whenever this variable is substituted. e.g.
foo = $(bar)
bar = $(ugh)
ugh = Huh?

F_VAL = $(foo)

The `$(foo)’ expands to `$(bar)’ which expands to `$(ugh)’ which finally expands to `Huh?’ and so the F_VAL will contain the finally expended value ‘Huh?’ when ever expanded.

The main disadvantage of this type of variable is infinite loop issue. The variable can’t be used to append to itself. i.e.

CFLAGS = $(CFLAGS) -o3 

The above statement is not allowed as it will cause the make to stuck into infinite loop due to recursion. The way around it is to use the append assignment method i.e.

CFLAGS += -o3 

1.3.2.2 Simple expanded Variable:

This type of variables are defined by using ‘:=’ sign. e.g.

x := foo

These varaiables are much like #define macros in ‘C’ where the value is simply substituted by preprocessor wherever they are found. Once substituted, it never changes even if the value of the variable being substituted changes (like in recursive expanded variable). e.g.

x := foo
y := $(x) bar
x := later

is equivalent to:

y := foo bar
x := later

As can be seen the variable $(x) later changes but the previous value has already substituted in $(y). In case of recursive expanded variables it would be like:

y = later bar
x = later

1.3.2.3 Automatic Variables:

There are few special variables that are Automatically defined and updated by make utility. Here is a list of few automatic variables and their meaning.

Automatic VariableDescription
$@The target name i.e. on left side of target:dependencies rule.
$*The stem of the target filenamethat is, the part represented by % in a pattern rule.
$<The first prerequisite.
$^The list of prerequisites, excluding duplicate elements.
$?The list of prerequisites that are newer than the target.
$+The full list of prerequisites, including duplicates.

1.3.2.4 Built-in Variables:

These variables are used internally by make for various purposes. They can also be referred to in makefiles but the usage is limited. The complete list can be found via make -p command.

1.3.3 Rules:

Major portion of makefiles consists of rules. A rule tells make when to build a target and how to build it! The make build a target only if it is required and out-of-date. The criterion for being out of date is specified in terms of dependencies. A target is considered out of date if it doesn’t exist or if its date of modification/creation is behind the date of modification/creation of any of its dependencies.

The rules of GNU make is of two types.

  1. Explicit Rules
  2. Implicit Rules

1.3.3.1 Explicit Rules:

This rules explicitly specify to make – “What to build from What and How!”. This is the rule we already discussed so far. In this rule both the target and dependencies are clearly mentioned e.g.

#makefile
	
main.o: main.c
    gcc -c main.c -o main.o	

As shown in the above makefile, it is clearly mentioned that in order to get main.o, you must have main.c file in the working directory and the process is to execute command: gcc -c main.c -o main.o.

1.3.3.2 Implicit Rules:

In this rule no particular target is specified! This is a general rule that states how to build targets matching a specific source target pattern.

%.o : %.c
    gcc -c $< -o $@

This rule says “In order to build a something.o file from a corresponding something.c file, perform the command specified on the second line”. The $< and $@ are Automatic Variables explained before in section 1.3.2.3 . In above case the rule says take the source file with name given by $< and build the targe with name specified by $@. The above rule is sufficient for any “.c” file to be compiled into “.o” file.

References:

[1] – Makefile

[2] – Makefile Variables

[3] – Makefile – How to Use Variables



50 thoughts on “GNU make and Makefiles”

  • Excellent beat ! I wish to apprentice at the same time as
    you amend your website, how could i subscribe for a weblog web site?
    The account aided me a applicable deal. I had been a little bit acquainted of this your broadcast offered vibrant transparent idea asmr 0mniartist

  • Good day! Do you know if they make any plugins to help with Search Engine
    Optimization? I’m trying to get my blog to rank for some targeted keywords
    but I’m not seeing very good results. If you know of any
    please share. Appreciate it!

  • Howdy! This blog post couldn’t be written much better! Looking through this article reminds me of
    my previous roommate! He constantly kept talking about this.

    I most certainly will forward this information to him.
    Fairly certain he’s going to have a very good
    read. I appreciate you for sharing!

  • When someone writes an piece of writing he/she retains the plan of a user in his/her
    brain that how a user can know it. Therefore that’s why this
    piece of writing is amazing. Thanks!

  • Excellent site. Plenty of useful information here. I am sending it
    to some friends ans additionally sharing in delicious. And naturally, thank you in your effort!

  • Hey! This is my first visit to your blog! We are a collection of volunteers and starting a new initiative in a community in the same niche.
    Your blog provided us valuable information to work on. You have done a wonderful job!

  • You really make it appear so easy along with your presentation but
    I in finding this matter to be actually one thing which I think I’d by no means understand.
    It sort of feels too complicated and extremely wide for me.
    I’m looking forward for your subsequent publish,
    I will try to get the grasp of it!

  • My brother suggested I might like this website. He was totally right.

    This post actually made my day. You cann’t imagine simply how much time I had spent for this information!
    Thanks!

  • scoliosis
    Thank you for the auspicious writeup. It in truth used to be a enjoyment account
    it. Glance advanced to more delivered agreeable from you! However, how could we be
    in contact? scoliosis

  • scoliosis
    Hey there this is kinda of off topic but I was wondering if blogs
    use WYSIWYG editors or if you have to manually code with HTML.
    I’m starting a blog soon but have no coding
    experience so I wanted to get guidance from someone with experience.
    Any help would be greatly appreciated! scoliosis

  • You actually make it seem so easy with your presentation but
    I find this topic to be actually something that I believe I would never understand.

    It sort of feels too complicated and very vast for me.
    I’m looking ahead in your next publish, I’ll attempt to get
    the cling of it!

  • I am extremely impressed with your writing abilities as smartly as with the
    format in your weblog. Is this a paid theme or did you
    customize it your self? Either way keep up the excellent quality writing, it is uncommon to peer a great weblog like this one these days..

  • We’re a group of volunteers and opening a new scheme in our community.
    Your web site provided us with helpful information to work
    on. You have performed an impressive activity and
    our entire group might be grateful to you.

  • You are so cool! I do not think I have read a single
    thing like this before. So good to find someone with some genuine
    thoughts on this topic. Seriously.. thank you for starting this up.
    This web site is one thing that is needed on the web, someone with a bit
    of originality!

  • I was pretty pleased to find this web site.
    I need to to thank you for your time for this particularly fantastic
    read!! I definitely really liked every bit of it and I have you saved as a favorite to see
    new stuff on your site.

  • I absolutely love your blog and find most of your post’s to be exactly what I’m looking for.

    Does one offer guest writers to write content for
    you? I wouldn’t mind composing a post or elaborating on many of
    the subjects you write regarding here. Again, awesome web log!

  • Today, I went to the beach front with my kids. I found a sea shell and gave it to
    my 4 year old daughter and said “You can hear the ocean if you put this to your ear.” She placed the shell
    to her ear and screamed. There was a hermit crab inside
    and it pinched her ear. She never wants to
    go back! LoL I know this is completely off topic but I had to tell someone!

  • Hello there I am so thrilled I found your website, I really found you by error,
    while I was looking on Bing for something else,
    Anyhow I am here now and would just like to say thanks
    for a incredible post and a all round interesting blog (I also love the theme/design),
    I don’t have time to read through it all at the moment but I
    have bookmarked it and also added in your RSS feeds,
    so when I have time I will be back to read much more, Please do keep up the excellent jo.

  • My brother suggested I might like this web site. He was once totally right.
    This publish actually made my day. You can not believe simply how a lot time I had spent for this info!
    Thanks!

  • I used to be suggested this web site by way of my cousin. I’m no longer certain whether or not this
    put up is written by means of him as nobody else recognise such
    particular about my difficulty. You are wonderful!

    Thank you!

  • Definitely imagine that which you said. Your favorite reason appeared to be on the net
    the simplest thing to be aware of. I say to you, I definitely get annoyed
    while other people consider concerns that they plainly don’t know about.
    You controlled to hit the nail upon the top as smartly as outlined out the whole thing without having side effect , people can take a signal.

    Will likely be again to get more. Thank you

  • Hi there, just became alert to your blog through Google,
    and found that it’s really informative. I’m gonna watch out for brussels.
    I will be grateful if you continue this in future.
    A lot of people will be benefited from your writing.
    Cheers!

  • What i do not understood is actually how you are now not actually much more
    well-liked than you might be now. You are very intelligent.
    You understand therefore significantly when it comes
    to this matter, made me for my part consider it
    from so many numerous angles. Its like women and men don’t seem
    to be fascinated until it is one thing to accomplish with Girl gaga!
    Your own stuffs nice. All the time deal with it up!
    scoliosis surgery https://0401mm.tumblr.com/ scoliosis surgery

Leave a Reply

Your email address will not be published. Required fields are marked *