User Tools

Site Tools


wiki:make:basics

Basics

To illustrate how Make works, here's the an example from our shell lecture: To analyse some molecule data we wrote two scripts: sorted_lengths.sh & middle.sh. sorted_lengths.sh determines the number of lines in each file and saves the files sorted by length in sorted_lengths.txt. middle.sh prints out lines from the middle of a file. Here I modified middle.sh to write the lines to file called result.dat. We have a little analysis pipeline here. We want to write a makefile we can run each time we collect new molecule data.

Let's start by creating a makefile:

  #This is a makefile for analyzing molecule data   
  result.dat: sorted_lengths.txt
          ./middle.sh sorted_lengths.txt -5 -3
          
  sorted_lengths.txt: *.pdb
          ./sorted_lengths.sh *.pdb

A configuration file for Make like this one is called a makefile. The first line, starting with #, is a comment. (As always our comments should be more meaningful). The second and third lines are a rule that tell Make what we want to do.

The filename on the left of the colon in the first line is the target of the rule. The rule tells Make how to update or re-create this file. The target's prerequisites —the things it depends on—are listed to the right of the colon. In our case, results.txt only has one prerequisite, sorted_length.txt.

The second line of the rule is its action. This tells Make what shell command or commands to run to bring the target up to date if it is older than any of its prerequisites. This rule only has one command, but a rule can contain any number. Make will run these commands in a sub shell. The sub shell should run the default bash shell.

One thing to note is that the actions in rules must be indented with a single tab character. Make will not accept spaces, or mixes of spaces and tabs. (As we said in the introduction, it was written by a summer intern in 1976, and sometimes that shows.)

Now that we've created our Makefile, we can tell Make to obey its instructions by running make from the command line. First we run a test:

  $ make -n
  ./sorted_lengths.sh *.pdb
  ./middle.sh sorted_lengths.txt -5 -3

When we type the command make, make looks for a file called makefile in the current directory and runs the make code inside it. The -n option tell make to only print out the commands instead of actually running them. That way we can double check our code. This is important is you are doing potentially irreversible things with make, like deleting files or moving them around.

Let's run make without the -n option.

  $ make
  ./sorted_lengths.sh *.pdb
  ./middle.sh sorted_lengths.txt -5 -3

Make's output shows us that it has run the command we wanted it to. It did this because at least one prerequisite for result.dat was newer than result.dat itself (result.dat didn't exist yet). By default, Make uses the time a file was last modified as its age. (Opening a file in an editor to view it doesn't change this timestamp, but any change to its contents will.) Since the *.pdb files timestamp was younger than result.dat and sorted_lengths.txt, Make ran the shell command we gave it and created a new version of both.

Let's run Make again:

  $ make
  make: 'results.txt' is up to date.

This time, it doesn't execute any commands. This happened—or didn't—because the target is newer than its prerequisites. Since there's nothing to bring up to date, Make doesn't change anything.

That's change the timestamp on the sorted_lengths.txt file with touch and rerun make.

  $ touch sorted_lengths.txt
  $ make
  /.middle.sh sorted_lengths.txt -5 -3

Make only executed the first rule since it is only rule that has a dependency which has a newer timestamp then the target.

One thing to note is that the order in which make executes rules that are not constrained by dependencies is arbitrary.

Something else this example shows us is that a single thing can be a target in one rule, and a prerequisite in others. The dependencies between the files mentioned in the Makefile make up a directed graph. In order for Make to run, this graph must not contain any cycles. For example, if X depends on Y, Y depends on Z, and Z depends on X, everything depends on something else, so there is nothing Make can build first. If it detects a cycle in a Makefile, Make will print an error message and stop. Unfortunately, whether or not a cycle exists depends on which files exist, and Make's error message is usually not particularly informative.

wiki/make/basics.txt · Last modified: 2022/07/21 06:59 by 127.0.0.1