diff --git a/system/Makefiles.md b/system/Makefiles.md new file mode 100644 index 0000000..22057a8 --- /dev/null +++ b/system/Makefiles.md @@ -0,0 +1,185 @@ +--- +title: "Makefiles" +tags: [ "system", "makefiles" ] +--- + +The `make` system wants to know: + +1. What file you want to make, +1. Which other files it depends on, and +1. How to build the file. + +Start with a basic test-area. + +```bash +mkdir make_test ; cd $_ +printf "%s:\n" README.md > Makefile +printf "\t%s\n" 'echo "Basic makefile example." > $@' >> Makefile +make +``` + +**NB:** Always tell `make` how to build files with a `\t` (tab) character. +Using four spaces will not work! + +## Dependency Files + +Now we've made a `README.md` file, we can show how a makefile looks in the README: + +```make +README.md: Makefile + echo "Basic makefile example." > $@ + echo "" >> $@ + echo '```' >> $@ + cat $< >> $@ + echo '```' >> $@ + + +``` + +Note the order: + +1. The first thing is the file you want, then a colon (`:`). +1. After the colon, any file it depends on. +1. Finally, the shell commands to execute. + +# Strange Sigils + +Notice that the file above can print into the README by using `echo "" >> $@`. +The `$@` stands for 'the file which we want', and `$<` stands for 'the first dependency file'. +The `make` program starts by replacing those variables, and the result it: + +```make +README.md: Makefile + echo "Basic makefile example." > README.md + echo "" >> README.md + echo '```' >> README.md + cat Makefile >> README.md + echo '```' >> README.md + + +``` + +| Sigil | Meaning | +|:-------:|:--------------------------------------:| +| `$@` | The file we want | +| `$<` | First dependency file | +| `$^` | All dependency files | +| `$(@F)` | Filename of the file we want | +| `$(@D)` | Directory path of the file we want | +| `$( $@ + echo "" >> $@ + echo '```' >> $@ + cat $< >> $@ + echo '```' >> $@ + +$(storage_directory)/README.md: README.md + mkdir $(@D) + cp $< $@ + +``` + +Now you can tell `make` to create the backup: + +```bash +make backups/README.md +``` + +## Command Variables + +The backup `README.md` could be named after the current minute of the day, using `date +%M`. +This allows up-to-the-minute backups: + +```make +current_minute != date +%M + +storage_directory = backups + +README.md: Makefile + echo "Basic makefile example." > $@ + echo "" >> $@ + echo '```' >> $@ + cat $< >> $@ + echo '```' >> $@ + +$(storage_directory)/backup_$(current_minute).md: README.md + mkdir $(@D) + cp $< $@ + +``` + +...but the repeated use of `mkdir` is causing an error, because that directory already exists. + +We can solve this by using `mkdir -p`. + +## Phony Targets + +But we don't want to look up the current minute of the day to make backups. +Better to just say `make backup`. +However, this will confuse `make`, because `make` thinks everything is a file, so it would try to make a file called `backup`. + +The solution is to tell `make` that `backup` is a phony target. + +```make + [ ... ] + +.PHONY: backup +backup: $(storage_directory)/backup_$(current_minute).md +$(storage_directory)/backup_$(current_minute).md: README.md + mkdir -p $(@D) + cp $< $@ + +``` + +Now run `make backup` to create an up-to-date backup. + +# Order + +Makefile thinks like this: + +1. Fill in all the variables in the file, from top to bottom. +1. If variables are missing, go through the file again. +1. Figure out the order the files should be built in. + +In this case, the makefile can see that `backup` depends on the current backup file (with the minute in the filename), which depends on the `README.md` file, which depends on the Makefile itself. + +```graph + +┌──────────────────────┐ +│ Makefile │ +└──────────────────────┘ + │ + │ + ▼ +┌──────────────────────┐ +│ README.md │ +└──────────────────────┘ + │ + │ + ▼ +┌──────────────────────┐ +│ backups/backup_06.md │ +└──────────────────────┘ + │ + │ + ▼ +┌──────────────────────┐ +│ backup │ +└──────────────────────┘ +``` + +# The Rest + +- [File patterns](Makefiles/patterns.md) +- [Makefile graphs](Makefiles/graph-easy.md) diff --git a/system/Makefiles/graph-easy.md b/system/Makefiles/graph-easy.md new file mode 100644 index 0000000..04d1b9c --- /dev/null +++ b/system/Makefiles/graph-easy.md @@ -0,0 +1,15 @@ +--- +title: "Makefile Graphs" +tags: [ "system", "makefiles", "graph" ] +--- + +If you have `graph-easy` (often in the package `perl-graph-easy` or similar), you can make a graph from the makefile with `make2graph` (the package is often called `makefile2graph`). + +Start with the command to 'make all targets' (`-B`), and 'do a dummy run' (`-n`) with debug into (`-d`): + +```bash +make -Bnd +make -Bnd | make2graph +make -Bnd | make2graph | graph-easy --boxart +``` + diff --git a/system/Makefiles/patterns.md b/system/Makefiles/patterns.md new file mode 100644 index 0000000..d4df0d4 --- /dev/null +++ b/system/Makefiles/patterns.md @@ -0,0 +1,57 @@ + +--- +title: "Makefile Patterns" +tags: [ "system", "makefiles" ] +--- + +Using the [basic example](../Makefile.md), you can make a complete backup of all backup files. +This file will depend upon everything inside the `$(storage_directory)`. +Unlike `bash`, you can't just say `storage_directory/*`: the pattern must be stated as a 'wildcard'. + +```make +$(storage_directory)/backup.tgz: $(wildcard $(storage_directory)/*.md) + tar czf $@ $^ +``` + +The `make` rules start by processing variables: + +```make +backups/backup.tgz: $(wildcard backups/*.md) + tar czf backups/backup.tgz $^ +``` + +Then the `wildcard` variable equals whichever backup files are in the `backups/` directory: + +```make +backups/backup.tgz: backups/backup_29.md backups/backup_30.md + tar czf backups/backup.tgz backups/backup_29.md backups/backup_30.md +``` + + +The phony `backup` target should now point to this tar backup. + + +```make +current_minute != date +%M + +storage_directory = backups + +.PHONY: backup +backup: $(storage_directory)/backup.tgz +$(storage_directory)/backup.tgz: $(wildcard $(storage_directory)/*.md) + tar czf $@ $^ + +README.md: Makefile + echo "Basic makefile example." > $@ + echo "" >> $@ + echo '```' >> $@ + cat $< >> $@ + echo '```' >> $@ + +$(storage_directory)/backup_$(current_minute).md: README.md + mkdir -p $(@D) + cp $< $@ + +``` + +