outline makefiles
This commit is contained in:
		
							
								
								
									
										185
									
								
								system/Makefiles.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								system/Makefiles.md
									
									
									
									
									
										Normal file
									
								
							| @@ -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     | | ||||
| | `$(<F)` | Filename of the first dependency       | | ||||
| | `$(@D)` | Directory path of the first dependency | | ||||
|  | ||||
| ## Basic Variables | ||||
|  | ||||
| You can assign a variable normally, but must refer to it in brackets. | ||||
|  | ||||
|  | ||||
| ```make | ||||
| storage_directory = backups | ||||
|  | ||||
| README.md: Makefile | ||||
| 	echo "Basic makefile example." > $@ | ||||
| 	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) | ||||
							
								
								
									
										15
									
								
								system/Makefiles/graph-easy.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								system/Makefiles/graph-easy.md
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| ``` | ||||
|  | ||||
							
								
								
									
										57
									
								
								system/Makefiles/patterns.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								system/Makefiles/patterns.md
									
									
									
									
									
										Normal file
									
								
							| @@ -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 $< $@ | ||||
|  | ||||
| ``` | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user