Categories


Archives


Recent Posts


Categories


Just Enough C for PHP: Make Basics

astorm

Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

Updated for Magento 2! No Frills Magento Layout is the only Magento front end book you'll ever need. Get your copy today!

This entry is part 5 of 5 in the series Just Enough C for PHP. Earlier posts include Just Enough C for PHP, Just Enough C for PHP: Running C Programs, Just Enough C for PHP: Variables and Types, and Just Enough C for PHP: C Macros. This is the most recent post in the series.

So far in this series we’ve written super simple programs, and we’ve compiled those programs directly using cc, gcc, or clang (some of which are just legacy aliases that point at the same system compiler)

$ cc hello-world.c
$ gcc hello-world.c
$ clang hello-world.c

In the real world it’s unommon that a C project will be written or distributed as a single file. SQLite is a rare exception. Most C projects will require you to become familiar with their build systems if you want to compile the project yourself.

If you’re coming from a programming language like Java or PHP you may be familiar with build systems like ant or phing. While there’s far from one universal build tool for C programs, chances are your project will use the grandparent of all build tools — Make. The PHP source is no exception.

This is the first of two articles that will cover the basics of Makefiles. If you want to learn more, the GNU Make documentation is surprisingly cogent.

Make Makes Files

You can test that the make program is installed on your computer in the usual ways

$ which make
/usr/bin/make

$ make --version
GNU Make 3.81
Copyright (C) 2006  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.

This program built for i386-apple-darwin11.3.0

The first thing to know about Make is it’s a file oriented tool, not a task oriented tool. Not clear on what, exactly, that means? Let’s start with a quick demonstration. Create the following two line file in a folder on your computer

# File: Makefile
hello-world.txt:
    touch hello-world.txt

This is the world’s simplest Makefile. The first line (hello-world.txt:) defines a file-to-create, known as a target. The second line (touch hello-world.txt) is the shell command we want to run in order to create the target file

IMPORTANT: The second line of this file must be indented using tabs. Make is strict in this way — it will not recognize plain spaces as block nesting.

With the above file created, run the following command while cded into the same folder as your Makefile

$ make hello-world.txt
touch hello-world.txt

Make will run, and then output the commands it ran for you. If you take a look at your directory contents, you should see a hello-world.txt file.

$ ls -1
Makefile
hello-world.txt

Now — try running the same command again

$ make hello-world.txt
make: `hello-world.txt' is up to date.

Instead of blindly re-running our touch command, make saw that the file already existed, and skipped actually doing the work of creating it.

This is what we mean when we say make is a file based tool. This may seem a little foreign, but (as we’ll see) it does have its advantages.

Hello Make

Let’s create a make file that actually builds a C program. First, we’ll create the ubiquitous hello world

//File: hello-world.c

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

Then, we’ll create a new Makefile in the same directory

#File: Makefile

hello-world:
    cc hello-world.c -o hello-world

Our target name is hello-world, and the command to create our hello-world file is cc hello-world.c -o hello-world. If we run make, we’ll have a compiled binary of our program.

$ make hello-world
cc hello-world.c -o hello-world
$ ./hello-world
Hello, World!

Also, as we saw before, if we re-run our command, make knows that the hello-world binary is already built

$ make hello-world
make: `hello-world' is up to date.

This presents a small problem — let’s try editing our hello world to say Hello Make.

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

and then, we’ll run our Make command again.

$ make hello-world
make: `hello-world' is up to date.

Huh — that’s no good. We’ve edited our program file, we want to rebuild it, but make doesn’t think it needs to recompile the source because the hello-world binary is already there.

We have two solutions. The first is a bit of a sledge hammer: We could just delete the compiled hello-world binary file. While this wouldn’t be a big deal for this simple hello world example, for a Makefile with dozens, or possibly hundreds, of targets, it’s not a solution that scales.

Fortunately, make offers us a solution. For each target, we can create a list of files that the target itself depends on. Give the following a try

#File: Makefile

hello-world: hello-world.c
    cc hello-world.c -o hello-world

By listing a file after the hello-world target, we’re telling make that the hello-world target depends on hello-world.c being present. This, in turn, tells make

Hey, if hello-world.c (the thing the current target is dependent on) has an edited date later than our target file (hello-world) then we need to rebuild.

Give it a try!

$ make hello-world
cc hello-world.c -o hello-world
$ ./hello-world
Hello, Make!
$ make
make: `hello-world' is up to date.

As you can see, make realizes it needed to rebuild the hello-world.c program.

Target dependencies also serve another purpose. Let’s try deleting our hello-world.c file.

$ rm hello-world.c

and then re-running make.

$ make hello-world
make: *** No rule to make target `hello-world.c',
needed by `hello-world'.  Stop.

Our build failed because there was no file to compile. However — the error message is interesting. Instead of a compiler error

$ cc hello-world.c -o hello-world
clang: error: no such file or directory: 'hello-world.c'
clang: error: no input files

We have an error from Make itself.

make: *** No rule to make target `hello-world.c',

For every target dependency, Make will check if the file exists. If the file doesn’t exist, Make will look for another target in order to create the file.

This means we could do something like this.

#File: Makefile

hello-world: hello-world.c
    cc hello-world.c -o hello-world

hello-world.c:
    printf "#include <stdio.h>\n" > hello-world.c
    printf "int main()\n" >> hello-world.c
    printf "{\n" >> hello-world.c
    printf "   printf(\"Hello, Make!\");\n" >> hello-world.c
    printf "   return 0;\n" >> hello-world.c
    printf "}\n" >> hello-world.c

Here, we’ve added a new target to our Makefile named hello-world.c, and the multiline target creates our hello world program for us. This actually gives us a Makefile with a self-contained hello world program

$ make hello-world
printf "#include <stdio.h>\n" > hello-world.c
printf "int main()\n" >> hello-world.c
printf "{\n" >> hello-world.c
printf "   printf(\"Hello, Make! \\\\n\");\n" >> hello-world.c
printf "   return 0;\n" >> hello-world.c
printf "}\n" >> hello-world.c
cc hello-world.c -o hello-world

$ ./hello-world
Hello Make

Like most tutorial examples, this last one is a bit silly — but it’s not that uncommon for some c files in a project to be pre-generated. Just a reminder for the pre-generated javascript crowd that all this has happened before, and all this will happen again.

Wrap Up

We’re going to wrap things up here for today. If you’re used to thinking about build systems as tools that help you organize the scripts that perform your builds, Make’s file/artifact based approach can take a little getting used to. Regardless of what you think about it, Make and C grew up together, and the things that Make makes possible have ended up becoming de-facto standards for how C programs are built.

There’s still a few Make basics to cover, but before we can do that, we’ll need to talk about multi-file C programs. That’s coming next time!

Series Navigation<< Just Enough C for PHP: C Macros

Copyright © Alan Storm 1975 – 2018 All Rights Reserved

Originally Posted: 8th October 2018