From 2250275be5c96cb305e48b8adcf60019c06803cd Mon Sep 17 00:00:00 2001 From: Malin Freeborn Date: Fri, 7 Feb 2025 23:40:13 +0100 Subject: [PATCH 1/3] outline makefiles --- system/Makefiles.md | 185 +++++++++++++++++++++++++++++++++ system/Makefiles/graph-easy.md | 15 +++ system/Makefiles/patterns.md | 57 ++++++++++ 3 files changed, 257 insertions(+) create mode 100644 system/Makefiles.md create mode 100644 system/Makefiles/graph-easy.md create mode 100644 system/Makefiles/patterns.md 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 $< $@ + +``` + + From 6b4a84628475c24947e9c61a7dc1f53ee2060ee8 Mon Sep 17 00:00:00 2001 From: Malin Freeborn Date: Sun, 9 Feb 2025 22:20:16 +0100 Subject: [PATCH 2/3] note makefile help --- system/Makefiles.md | 2 ++ system/Makefiles/help.md | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 system/Makefiles/help.md diff --git a/system/Makefiles.md b/system/Makefiles.md index 22057a8..30f0c61 100644 --- a/system/Makefiles.md +++ b/system/Makefiles.md @@ -183,3 +183,5 @@ In this case, the makefile can see that `backup` depends on the current backup f - [File patterns](Makefiles/patterns.md) - [Makefile graphs](Makefiles/graph-easy.md) +- [In-build help](Makefiles/help.md) +- [Makefile graphs](Makefiles/graph-easy.md) diff --git a/system/Makefiles/help.md b/system/Makefiles/help.md new file mode 100644 index 0000000..028634a --- /dev/null +++ b/system/Makefiles/help.md @@ -0,0 +1,18 @@ +--- +title: "Makefiles" +tags: [ "system", "makefiles", "help" ] +--- + +Make your first target 'help' to give an overview of the main targets. +Running `make help` will search for text which starts with `## ` and show what that target does. + +```make +help: ## Print the help message + @awk 'BEGIN {FS = ":.*?## "} /^[0-9a-zA-Z._-]+:.*?## / {printf "\033[36m%s\033[0m : %s\n", $$1, $$2}' $(MAKEFILE_LIST) | \ + sort | \ + column -s ':' -t + +clean: ## Remove generated files + $(RM) $(defaults) +``` + From 53e86fb86e8e2301fd3c24f2b008b112be89357e Mon Sep 17 00:00:00 2001 From: Malin Freeborn Date: Mon, 10 Feb 2025 01:02:12 +0100 Subject: [PATCH 3/3] board games with recfiles --- data/recfiles.md | 1 + data/recfiles/Board_Games.md | 61 ++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 data/recfiles/Board_Games.md diff --git a/data/recfiles.md b/data/recfiles.md index 3942ef3..e5187e6 100644 --- a/data/recfiles.md +++ b/data/recfiles.md @@ -44,6 +44,7 @@ recsel $database ``` - [Extended example](recfiles/extended.md) +- [Playing with board games data](recfiles/Board_Games.md) # Resources diff --git a/data/recfiles/Board_Games.md b/data/recfiles/Board_Games.md new file mode 100644 index 0000000..5556a97 --- /dev/null +++ b/data/recfiles/Board_Games.md @@ -0,0 +1,61 @@ +--- +title: "Board Games" +tags: [ "data", "recfiles" ] +--- + +You can play with a board games database from boardgamegeek.com. + +## Download the Database + +```sh +mkdir board_games +cd board_games +curl -Lo bg.zip 'https://www.kaggle.com/api/v1/datasets/download/threnjen/board-games-database-from-boardgamegeek' +unzip bg.zip +``` + +The header line shows fields with a bunch of colons, which will confused `recutils`, so we'll have to get rid of them. + +```sh +sed -i '1s/://g' *.csv +``` + +Convert the games to `.rec` format. + +```sh +csv2rec games.csv > games.rec +``` + +## Queries + +If you try to look at older games, you'll find lots of results. + +```sh +recsel games.rec -e "YearPublished < 1800" -c +recsel games.rec -e "YearPublished < 1800" -Cp Name +``` +But most are wrong. +The problem is games with a `YearPublished` date of `0`, probably because the year published is unknown. + +```sh +recsel games.rec -e "Name = 'The Goblin King is Angry'" -p YearPublished +``` + +Fix the query by removing games published in '0 AD'. + +```sh +recsel games.rec -e "YearPublished < 1800 && YearPublished != 0" -R YearPublished,Name +``` + +Or fix the database setting `YearPublished` to 'unknown': + +```sh +recsel games.rec -e "YearPublished = 0" -Cp Name +recset games.rec -e "YearPublished = 0" -f "YearPublished" -S 'unknown' +``` + +Strategic games which work best with 3 players, sorted by Average Rating: + +```sh +recsel games.rec -e "BestPlayers = 3 && CatStrategy = 1" -CR Name --sort=AvgRating +```