===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.