“Make” as a static site generator (2022)
karl.berlin
karl.berlin
Then I added features like news and an RSS feed, a way to automatically list my research publications and course materials, a list of books filterable with tags, etc. So now it still is a Makefile but the Makefile itself is a bit simpler than it used to be, but it calls a few Bash scripts that in particular make use of the awesome xml2 and 2xml utilities to be able to manipulate HTML in a line-oriented manner using the core utils (grep and sed mostly).
On top of that I have a few git hooks that call make automatically when needed, in particular on the remote server where the website is hosted so that the public version is rebuilt when I push updates the repository there.
It's been working like a charm for years! My git history goes back to 2009.
EDIT: I just had a look at the first commits…
beccad7 (FIRST_VERSION) Initial commit
d1cc6d7 adding link to Google Reader shared items
6ccfd0c fix typo
d337959 adding link to Identi.ca account
… 15 years have passed indeed.Seems somewhat abandoned?
And it's complicated by this only being knowable in retrospect, as you can't predict the future. "Not abandoned" is a positive sign for "if we failed to predict the future correctly, it'll be fixed", rather than mostly relying on luck.
(Thankfully full-blown simulation is often an option nowadays too)
Running my website with 2 containers, one being the webserver and the other one the data container.
So only the registry need to be backuped.
Yes, and I think its fair to assume that some backend to execute the x86-64 Linux ABI will out-live most readers.
Projects like https://justine.lol/ape.html, https://guix.gnu.org/manual/en/html_node/Invoking-guix-pack.... or https://github.com/matthewbauer/nix-bundle do make it approachable to "bundle" a lot of software down to libc.
https://tracker.debian.org/pkg/xml2
And eg Arch gets the code from Debian as far as I can tell:
https://aur.archlinux.org/packages/xml2
I don't really expect there to be lots of new features - but bug fixes seem likely even for a mature utility.
In my own projects, simply rebuilding the whole site is fast enough, so I opt to remove the whole build folder before a rebuild:
https://github.com/jez/jez.github.io/blob/source/Makefile#L1...
This defeats a big part of why you’d want a build system in the first place (incremental builds), but at least if you know the page you want to regenerate you can still `make` that file directly.
If there’s a common workaround for this pattern in makefiles I’d love to learn it.
"make clean"?
I guess you could do some magic to delete "unexpected" files, but are there tools which do solve this problem?
Edit: `make install` also protects you against broken builds breaking your live site.
rm/%.html:
@rm -f source/%.html build/%.html
Run with: $ make rm/page.htmlMy initial reaction is: I should probably get around to learning about Flakes. I’m not sure I’d want each blog post to pin its templates, but it’s nice to have that choice.
Personally, I think I'd use two flakes, one that builds the content into something that's ready to hand-off to code, and a second one that turns it into a usable site. That way you wouldn't end up with a new input for each post, but instead would have a versioned something which represents your content all bundled together, and then the site builder consumes it as a single dependency--but conceptually it's the same.
I guess I'm just saying that it's not conventional, but it's a pretty logical conclusion to reach.
I also like the use of flake inputs for content.
It reminds me of a world that I've been imagining where the conclusions in scientific papers are generated as a flake outputs (an adjacent output would be the human readable thing, a PDF or whatever).
In this world, you can just run `nix flake update && nix build`, and if a paper that you cite published an update which invalidates your conclusion, you know right away because their output is your input, so your build fails.
We think about repeatable builds being for executable binaries, but they could equally well be for conclusions and assumptions.
Perhaps nix is too big of a hammer for the job, but it seems like the best shot we have at achieving this without also constraining the scientist re: tooling.
I realize that you don't want to be storing mountains of data in the nix store, but it would work just as well if the output in question is an IPFS CID, to be resolved during the build instead. The publisher can then be in charge of keeping that CID pinned and of notifying scientists when they're "build" starts failing.
Thanks! I took up blogging more often as of recent, and for me, having a manageable system is a large part of that. The last thing I want to happen on a Sunday evening is breaking some page of my website. That being said, I hope to one day make the workflow easier.
> It reminds me of a world that I've been imagining where the conclusions in scientific papers are generated as a flake outputs (and adjacent output would be the human readable thing, a PDF or whatever).
I happen to be a reviewer for software artifacts in a scientific journal, and I often use Nix here. Not that many projects do use it, but if I'm able to reproduce it with Nix, then I know the author has not missed any implicit dependencies. I like to imagine it's also useful for the authors as a feedback, whether they use Nix or not.
> I realize that you don't want to be storing mountains of data in the nix store, but it would work just as well if the output in question is an IPFS CID, to be resolved during the build instead.
I maintain separate build serves of my own using Nix integrations and the Nix cache is quite large already (so called remote builders) sitting at around 500GB. I host these at Hetzner.
I have also thought adding IPFS integration for my website, but haven't got around to it.
That's very cool. I have a question for you.
I'm taking a bioinformatics class, despite not having the chemistry prerequisites. I'm getting a crash course in biochem, and the rest of the class benefits from having an expert in what-kind-of-quotes-to-use.
I've been thinking: would it be helpful if the care and maintenance of these compute environments wasn't left to each scientist but was instead aggregated (perhaps per-class or per-university)?
We're setting these chemists up with conda in Ubuntu in WSL in a terminal whose startup command activates the conda environment. Not exactly a recipe for reproducibility after they get a new laptop.
What if certain compute-heavy classes published flakes which the students could...
a) use while taking the class so we stop wasting time on troubleshooting ssl deps via conda
b) reference in publications after the fact. They could say:
> Here's a Jupyter notebook, download it and run it in the UCCS biochem environment like so: `nix run github:UCCS/CHEM4573?rev=16afd67`, its output lets us make the following conclusions...
I know it would be helpful for the students in the class. Do you think it would be helpful to them later on when they were publishing things?
I'm thinking about packaging the dependencies for this class, giving it to the teacher, and pitching it to the university:
> Set up a technical fellowes program. Waive tuition for us nerds and in exchange we'll support your students and faculty through the maintenance of these environments.
I don't mind paying tuition so much, but I'd like to do something to get a bit more cross pollination going between scientists in need of tech support and techies in need of something meaningful to work on.
Am I dreaming here, or would it solve some problems? Do you think I have a shot at convincing anybody?
> I've been thinking: would it be helpful if the care and maintenance of these compute environments wasn't left to each scientist but was instead aggregated (perhaps per-class or per-university)?
This is definitely something that Nix can abstract quite well. In my company we have [an infrastructure of computers](https://github.com/ponkila/homestaking-infra) that we manage with NixOS. We have gone over the system such that `cd` into a directory "conjures" the environment using devenv or direnv. We don't do anything too fancy yet, but we have a project commencing next month in which we start to also manage routers this way. We speculate that this will help us to do things such as follows: register new node, and it gets automatically imported by the router which establishes DNS entries and SSH keys for each user. The idea is that we could have different "views" of the infrastructure depending on the user which the router could control. For administrators, we have a separate UI created with React that pulls NixOS configuration declarations from a git repository (note: these don't have to be public) and shows how the nodes connect with each other. The UI is still under construction, but imagine this but now with more nodes: https://imgur.com/a/obBfRk0. We have this set up at https://homestakeros.com.
Depending on a project you are working on, you could then have a subset of the infrastructure be shown to the user and have things such as SSH aliases and other niceties set up on `cd` in. When you `cd` out, then your view is destroyed.
We have quite overengineered this approach -- we run the nodes from RAM. NixOS has the idea of "delete your darlings" which is having a temporary rootfs. We have gone the extra mile that we don't even store the OS on the computer, the computers boot via PXE and load the latest image from the router (though any HTTP server will do, I boot some from CloudFlare). We do this because it also forces the administrators to document changes that they do -- there is nothing worse than starting to call up people when theres is downtime and try to figure your way back up from what the mutations are. PXE booting establishes a working initial state for each node -- you just reboot the computer, and you are guaranteed to get into a working initial state. I'm personally big on this -- all my servers and even my laptop works like this. We upgrade servers by using kexec -- the NixOS configurations produce self-contained kexec scripts and ISO images for hypervisors (some stakeholders insist on running on Proxmox). I've suggested some kernel changes to NixOS which would allow boostrapping arbitrary size initial ramdisks, because otherwise you are limited to 2GB file size.
> We're setting these chemists up with conda in Ubuntu in WSL in a terminal whose startup command activates the conda environment. Not exactly setting them up for reproducibility if they ever move to a different laptop.
Python in specific is a PITA to setup with Nix, dream2nix etc., might help but it's definitely the hardest environment to set up of all languages I've tried -- even GPGPU environments are easier. Oftentimes, the only problem is not the packaging, but also the infrastructure used. For that, you could also publish the NixOS configurations and maybe distribute the kexec or ISO images.
A notable thing is that devenv also allows creation of containers from the devShell environment, which may further help your case. Researchers could reference docker images instead of insisting on everyone to use Nix.
In any case, I put some emails on my HN profile so we can also take the discussion off platform -- we are looking for test users for the holistic approach using PXE, and we are currently funded until Q3 next year.
My real gripe is that in that issue, the app developer couldn't really help--since it was a packaging problem--and the conda folks were unaware because the users had gone straight to the app developer. If there must be a third party doing curation, it seems to me that they should be more narrowly focused on whatever particular suite of tools enables whatever particular group of people--not on individual packages.
I know that conda lets users do this too, but I don't think that the environments compose as well as they do with nix. If you want Jim's envioronment, but with Susie's custom build of foo-tool, you can just take both as inputs, overwrite foo-tool as desired, and output composition. Your maintenance burden remains small. If conda handles environments with this kind of compositional attitude, I'm unaware of it.
Conda can be a bit fiddly, it makes life much easier if you specify the required version of python when you create a new environment.
>I know that conda lets users do this too, but I don't think that the environments compose as well as they do with nix.
Yes, though we find Conda great for most of our use cases, we still have to resort to creating containers.
Don't get me wrong, I get that you gain big amounts of flexibility out of it the way you do it but if we think about the tasksat hand, adding a page to a predefined blog, it seems a bit involved.
Edit to link my Makefile: https://github.com/jaredkrinke/make-blog/blob/main/Makefile
comm -23 <(find build -type f -iname "*.html" -printf "%P\n" | sed 's/\.html$//' | sort) \
<(find source -type f -iname "*.md" -printf "%P\n" | sed 's/\.md$//' | sort)
The find commands get you a list of all the files (and only files - directories will have to be removed in a separate step) in each of the build and source folder, sed chops off the extension, while comm -23 compares them, printing only the files unique to the build folder, which you can then deal with as you see fit (e.g., by feeding them to xargs rm).I did save a list of generated files and compared them. This one liner is the meat of the whole solution:
comm -23 <(awk 'NR>1' "$DSTDIR/build-info") <(find "$SRCDIR" -name "*$EXT" -type f -printf "%P\n" | tee >(gen_index) | xargs -n1 "$0" "$SRCDIR" "$DSTDIR" | sort | tee -a "$DSTDIR/build-info.new") | (cd "$DSTDIR" && xargs rm)
Full source here:
https://gist.github.com/hadrianw/060944011acfcadd889d937b960...http://neilmitchell.blogspot.com/2015/04/cleaning-stale-file...
But I think the best solution (that also works with make) is to have a "make dist" target that creates a final .tar.gz archive of the result. If the rule is written properly then it won't contain any stale files. The disadvantage is for large project it may be slow, but you are not supposed to use this rule during development (where it is useless anyway), only for releases (which still can be built incrementally -- only the final .tar.gz needs to be created from scratch)
My personal site is also using a custom make-like ssg, but after spending a disproportionate amount of time writing the bundling/packaging code, I decided to just switch over to one of these tools. It’s a solved problem, and it greatly reduced the complexity of my site.
Other than the nuclear option ("make clean"), another is to have a specific rename / remove make target, so:
make rm sourcefile
or make mv sourcefile newsourcefile
... which will handle the deletion and renaming of both the original and generated targets.In practice for even fairly large blog and online projects, a make clean / make all cycle is reasonably quick (seconds, perhaps minutes), and is often called for when revising templates or other elements of the site design. If you're operating at a scale where rebuild time is a concern, you probably want to be using an actual CMS (content management system) in which source is managed in a database and generated dynamically on client access.
[0]: https://github.com/karlb/karl.berlin/blob/master/blog.sh [1]: https://barf.bt.ht
What I like most about it is I haven't had to upgrade anything, and don't expect to forever. And a close second; it "hot reloads" without javascript.
Yours are much more advanced, but a few years back I made a minimal PHP static page generator and named it...
PHP keep It Stupid Simple, or in short P.I.S.S.
I used to maintain a small website built like that some 20 years back. But I can't see the model working today, personal websites excluded. The problem is that the approach essentially enforces Web 1.0 roles: You either need every contributing user to be html-proficient, or someone willing to assume the drudgery of being the "webmaster".
nononononononononono for the love of everything please no
m4 isn't even a good esolang!
About 10 years ago the idea suddenly got traction once it was legitimized by the SAAS fad, I would tell people “don’t you know they’re going to end the free tier or go out of business or both?” and sure enough they did.
Anyhow, I bring it up because the system used M4 to interpolate variables into PHP, other languages, shell scripts, SQL scripts, etc.
I remember having to write cgi cookie handling code. I remember having to write session-cookie sync code. PHP was a small slice of heaven in the cgi world. Until it wasn’t. Still, being able to import libraries of script functions without having to recompile was wizardry. The problem with php now is they let a certain product somewhat dictate their direction. Class namespaces with slashes is the ugliest design choice.
What was your oss project that couldn’t get traction?
export CURRENT="..."
cat page.html | envsubtA year later, when you are trying to figure out why all instances of the word "cat" are silently disapearing from your website, you dig through 5 layers of macro expansions to discover that a junior dev tried implementing a for loop instead of copying it from the manual and messed up the quotation marks.
Having solved the immediate issue, you decide that debbuging your DSL is too hard, so you import M4 macro file you have been copying between projects. You then spend a day replacinf all usages of 'define' with your macro-creating-macro that adds comments to the output enabling your stacktrace generation script to work.
Next project, I am putting down a hard rule: no m4! (Except for maybe that one instance)
define(`foo',`hello')
define(`bar',`world')
foo bar
foobar
You will get: hello world
foobar
Working around this gets tricky, so someone inevitably ends up writing a cat-like macro such that you can do cat(foo,bar)
To get helloworld.
A side effect of this is that now "cat" is really "cat()" which expands to "". You can work around this by doing `cat'. However, if `cat' is used as an argument to another macro (such as a for loop), the quotation only prevents escaping the first time. When the for macro is expanded, the quotation marks are stripped, giving you just "cat", which gets expanded again. A correctly written for macro would add new quotes as needed, but I have never seen someone correctly write such a macro without just copying it.Not sure if I have seen this interaction specifically with for and cat, but I have seen an interaction like it on almost every project that used m4.
foo`'bar
I only know of this feature because recently read the manual page for m4, and it's mentioned rather early in there, but might have been not as well emphasized in past iterations of the manual. foo`'bar
The empty quotes make foo and bar separate words.I guess autoconf/sendmail still use m4 because there wasn't anything better at the time that doesn't come with a kitchensink attached.
Now, I use nodeJS to replace every m4 file with mustache.js and some JS logic and I don't feel limited anymore. The complexity doesn't increase much.
Just because it worked for sendmail is not sufficient justification for anything.
sendmail, bind, apache, older X11, sudo are examples that come to mind.
but that's got nothing to do my mistake of using M4 when better tools existed.
[1]: https://sgmljs.net/docs/producing-html-tutorial/producing-ht...
[1]: https://www.npmjs.com/package/sgml
Edit: your comment is a welcome reminder to improve the site, which isn't an easy thing to do however due to sheer volume of the material, even though it's using SGML for shared boilerplate inclusion, ToC/site nav and page nav generation, etc. (in fact, by calling sgmlproc from a Makefile)
I suspect I was never doing anything complicated enough to encounter the gotchas mentioned by other commenters...
For every interesting article that I read here I follow the feed. Whether you have a Wordpress site, a Bear Blog, a Micro blog, a blog on Havenweb, or a feed on your self-built site, I add them to the 'Really Social Sites' module of Hey Homepage.
Ultimately, I would like to publish this list of blogs, just like Kagi now does with their Small Web initiative. But I guess curating is key to adding quality. And when I think about curating, starting some kind of online magazine seems only natural.
Collaboration is obviously cool and only works with making it all public, I just don't know where "I'm doing this because I think it's cool" and "I'm going to put effort in to share it with others to get reactions"
Not saying you're right or wrong, but I myself don't want to look at it like it's a competition of the loudest people.
I've read so many blogs through HN over the last years, and every one of 'em had something interesting to say while also portraying something personal from the author. Whether that's a nice layout, nice color scheme, or even some nice jokes in their bio text.
To me, it can not get any more human than this. Pure individuals connecting on a world wide web. By links, by email, by RSS feeds. All without big tech.
Aside, I almost wrote “silent majority” but that seemed like it was veering towards politics so I went with vocal minority; I suspect there is a better term out there but I didn’t find it quickly.
I still encourage people to share though, because I think a lot of people would like to read personal stuff about topics that interest them. Doesn't even have to be with your name and all next to it, anonymous/pseudonymous homepages are usually possible.
Therefor I offer free websites (on a subdomain though) for people that would like to write or post photos about their hobbies. And know that there are way more possibilities to go online, just look at the OP of this thread with a nice SSG.
And I am so glad you do!
Writing my longer reply I realized that early social media is a strong counterpoint — people absolutely loved to share when the barrier to doing so was low, the platforms hadn’t been given over to commercialization, and it was less obvious that those details were going to be ingested into an advertising profile. It sounds like you offer a bit of that without the motive or intent that turned mainstream social media into what it is and I think that’s great!
Yeah, somewhere between the homepages and webrings of the nineties and the added social functionality of the early social media platforms. Ideally without the platforms and their incentives. The web itself is already a social platform, a social medium. No need for more layers, especially if they ultimately are against my interests. I think RSS still holds the potential to connect individual websites/people, albeit in a slightly (or maybe even fundamental?) different way than the social media platforms do.
Question: what would be your number one topic/subject to blog about, other than anything tech related?
On a side note, I get the "entitlement" from nobody. I take it. I also mean that in the best way possible. Nobody's asking for my software, my (future) articles, my point of view, etc. Still, I make stuff and sometimes share stuff. I think it can be a net value for some people (definitely not for everyone). This is only the reasoning behind it, the main motivator was me realizing I matter as a human being and I have only one life to live. I learned that because of experiencing a 'dark night of the soul' a couple of years back. Luckily I got through. And to be honest, if it wasn't for the internet - made up of personal websites and real people sharing their own experience on forums - that taught me everything there is to know about Cluster B disordered personalities (just an example, cough nothing personal cough), I don't think I would be sitting here typing this lengthy response.
I realized I can not sit back, enjoy the decline of the internet, and only complain about it. I would love to see the web have a lot of personal websites and blogs about every kind of subject, so I started to build a website software. The web/internet, and all the information shared and made easily accessible, made me able to save myself. I was probably helped more by some random dude who put up a website fifteen years ago with everything he knew about certain stuff than I was helped by anything else.
- The mere possibility that someone will see it pushes me to put more thought and effort into what I write. Sometimes this reveals weaknesses in my ideas that I would have glossed over if I were just writing private notes for myself; sometimes it leads me to actually change my opinions. It also means the blog posts are easier for me to understand / get value out of than notes are if I come back and reread them years later.
- It creates opportunities for people to connect with me which can pay off at unexpected times. Occasionally people have reached out to me to say a post helped them or resonated with them, or to give a thoughtful reply or ask a question. Those sorts of interactions are really satisfying even if they're rare. (One time, I was interviewing for a dev job and the interviewer asked a question about a post I'd written on the philosophy of John Rawls, and how it could connect to software engineering. I found that absolutely delightful.)
- It's just nice to have an outlet when I feel like writing about something.
I’m certainly grateful for their help, and even written up a few of my own.
Some guy said that it's a progression.
You start using the web by being a casual reader. At some point you get more comfortable in public spaces and start replying small comments like you would reply to someone afk.
Then you start reading more and more about specific subjects, amassing knowledge, and your replies have more content. They start being organized. They have a structure, to guide future readers and show them how you came up with your conclusion. They have links to sources. They leave open doors for the parts you don't know.
Then you start writing more and more comments, with more and more content, as a result of your experience.
Then comes a moment where you realize you're going to write the same thing for the nth time, and being a good engineer with a focus on DRY, you want to write your thoughts once and for all and link to it every time. This is the moment you start writing a blog that you actually maintain: you write not because you feel the need to write more, but because you want to write less and direct people to it rather than repeating yourself.
But the basic idea is the same --- heredocs for templating, using a plaintext -> html compiler (pandoc in my case), an intermediate CSV for index generation. Also some handy sed-fu [3] to lift out front matter. Classic :)
Very nice!
[1] https://github.com/karlb/karl.berlin/blob/master/blog.sh
[2] https://github.com/adityaathalye/shite
[3] I'm doing this: https://github.com/adityaathalye/shite/blob/master/bin/templ...
case ${file_type} in
org )
# Multiline processing of org-style header/preamble syntax, boxed
# between begin/end markers we have defined. We use org-mode's own
# comment line syntax to write the begin/end markers.
# cf. https://orgmode.org/guide/Comment-Lines.html
sed -n -E \
-e '/^\#\s+shite_meta/I,/^\#\s+shite_meta/I{/\#\s+shite_meta.*/Id; s/^\#\+(\w+)\:\s+(.*)/\L\1\E,\2/Ip}'
;;
md )
# Multiline processing of Jekyll-style YAML front matter, boxed
# between `---` separators.
sed -n -E \
-e '/^\-{3,}/,/^\-{3,}/{/^\-{3,}.*/d; s/^(\w+)\:\s+(.*)/\L\1\E,\2/Ip}'
;;
html )
# Use HTML meta tags and parse them, according to this convention:
# <meta name="KEY" content="VALUE">
# cf. https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/The_head_metadata_in_HTML
sed -n -E \
-e 's;^\s?<meta\s+name="?(\w+)"?\s+content="(.*)">;\L\1\E,\2;Ip'
;;
esacThere is a bit of a limitation, though - I organize posts by namespace and with the date in the URL, and make can’t really handle that directly.
Re: namespace organisation. I thought about that a lot, and decided to adopt namespace-only convention for symmetry between text file layout, html file layout, and url scheme.
I've treated Date/time as metadata, which I can use to organise index pages. If I get to years worth of posts, then I'll group them by year/month or something reasonable. Likewise tags. I debated tags _and_ categories. But I decided on "everything is a post with tags, and categories will emerge based on topical coverage + post format".
Do you mean the regexp in https://github.com/karlb/karl.berlin/blob/master/blog.sh#L4 ? It doesn't remove the formatting, just HTML comments (because they would show up on the page, otherwise) and rel="me" attributes (because they don't work with md2gemini). Feel free to read the blog post about adding Gemini support for more details: https://www.karl.berlin/gemini-blog.html
For example,
A) Most importantly, I wanted to tinker and have fun!
B) I already use Bash at work and stuff, so it's easy for me.
C) I am generally averse to fast-changing dependencies, and giant dependency trees, so that rules out most scripting languages.
Besides, if you peruse the README, you will see that my code guarantee is "works on my machine". Your mileage will vary tremendously :)
If your static site can be generated from scratch in under a second by just catting a few hundred HTML files with a common header, there is no benefit to using make over a script. You only risk performing an incomplete build due to a bug in the dependencies.
And still get to have things like make build vs make push, etc.
Your scripted actions can be ./build and ./push.
If you feel you need to type the name of a tool before your command, you can do that: sh build, sh push.
One script with a switch case might be better.
sed \
-e '/[/][/]script/r index.js' -e '/[/][/]script/d' \
-e '/ <script defer src="[.][/]index[.]js"><[/]script>/d' \
-e '/[/][*]style[*][/]/r styles.css' -e '/[/][*]style[*][/]/d' \
-e '/ <link rel="stylesheet" href="styles[.]css">/d' \
index.html
The HTML contains something like this: <link rel="stylesheet" href="styles.css">
<style>
/*style*/
</style>
and the script just deletes the <link> tag and replaces the /style/ comment with the contents of styles.css. Definitely not my finest work but it worked well enough.Point being, I do something very similar to this; except I first simply write/create my website in Zim-wiki, but then I have a bunch of little tasks to "clean up," i.e. fix/modify some links and then use the Canvas API to update my main course page (which, because I hate Canvas that much, simply links out to my own site).
Why make instead of shell scripts?
Not really. The concept of a script is "do these things in this order".
The concept of a makefile is "here are a bunch of things that might or might not need to be done, you figure it out".
Make has at its core one thing that would be pretty tedious to do in shell scripts: update the target (output) file only if it’s older than the prerequisite (dependency/input) file(s). This applies transitively to files that depend on other files that might change during the run of make, which is the part that really separates make from a shell script.
The thing you do when the target needs updating is run a little snippet of shell script, so that part you already know.
After learning how a rule works, you can combine it with ‘pattern rules’ to abstract a rule over all files that share a common prefix or common extension. Suddenly you have a loop but without any loop syntax, and can process a thousand files on demand with 2 lines of make - and without modification you can change a single input file, have it process only a single output file, and not waste your time re-running the 999 other files.
Also pro-tip: make will run in parallel if you use -j, and it will do it without breaking any dependency chains. If you have a process that turns text files into sql files and then turns this sql files into html files (possibly nonsensical example), make running in parallel will not blindly update html files first, it will run parallel jobs in the correct dependency order. You can use make to build something like gnu parallel, but is able to resume a batch job that was interrupted in the middle!
Note that I have recently switched to Just (https://github.com/casey/just). While not technically the exact feature set as make, it covers all of the ground for what I typically do. It gets the benefit of history and discards a lot of make cruft to make a more predictable experience.
The other thing I've been working on is a personal calendar todo list thing; after years of trying to use other peoples things, I realize I know just enough to make something that works with how I think and what I need (in short, a little like Linux Remind, but with the ability to mark things as done in a way, and the flexibility for "due" dates to go "stale" and still show up)
Anyway, the units/items are individual files with content + some tags with dates.
And I've been doing a thing where I source a file with a ton of functions. And..it looks like this is a better version of that. Wild. hmm.
As a side note, I am quite happy with XSLT templates to produce the pages (instead of attaching a static header, as in the article), as well as to generate indexes and an Atom feed.
It’s also interesting to read so many comments of people doing similar things.
For my own site, I find that I want an SSG tool that is simple, intuitive, and stays out of the way. With these goals in mind, I have been able to slowly improve my tool over and over. It’s been awesome to be able to do more using less.
Mine is "an SSG is just a source to HTML compiler and compositor, plus file organiser".
I reviewed a few tools (jekyll, emacs.love/weblorg, hugo), and ended up making mine in big part because I went down the rabbit hole of "well, why is this part here? why is it like this? why can't we do this other thing? wow this bit is cool, how do I make it myself?".
I had one project that involved downloading a jdk, using it to build a project specific jre, patching some class files with local java sources, bundling it, etc.
Without being a make expert, it took me a couple of hours of reading, finding examples, etc...but now I have the dependency stuff working perfectly. Where it now only re-downloads or re-builds things if something upstream from it changed, handles errors well and points out what broke, etc.
All that to say, for some things, it's worth looking into for it's built-in dependency chain stuff. When you need to run arbitrary outside scripts/tools, it sometimes does things that other build tools can't (gradle in my case, couldn't easily do this, or at least I couldn't figure out how).
Excellent too for building a tree of things that depend on prior stages; in your example needing java to run a java applet which generates a file.
the syntax takes some getting used to, but in those cases there is little better.
But I do find people using it as a glorified task runner and it works but it’s quite possibly the least ergonomic tool available. - especially for things like making docker images, which I see extremely often
https://github.com/W4RH4WK/static-page-generators
Over all, I quite like Ruby since it comes with rake and erb.
I learned about envsubst in the process which let me fill in values here and there. This is the rough way the homepage works.
public/index.html: index.md template/header.html template/footer.html
cat template/header.html > public/index.html
DATE=$(shell date +%Y-%m-%d) envsubst < index.md | npx marked --gif >> public/index.html
cat template/footer.html >> public/index.html
GitHub’s newer version of pages that lets you deploy via GitHub Actions rather than being forced into using Jekyll is just so amazing. I have converted a bunch of static sites to using it as hosting.Server Side Includes.
Have a separate files for the header, content and footer and only edit the content files.
The convenience here is largely in being able to use GitHub Pages to host the page while being able to do almost anything you want for a build process. It’s really neat.
I might move to something make-based like this, looks interesting.
https://github.com/askiiart/askiiart.github.io/blob/main/md2... or https://git.askiiart.net/askiiart/askiiart-net/src/branch/ma...
Yeah kinda, except that most people making static sites aren't the people who know Make.
(My site is generated by a Python commit webhook that indexes new files, diffs an Azure storage container and uploads updated files only).
Or Github actions workflow as static site generator would only include one shell wrapper, instead of 2.
A shell script that checks file mtimes to determine what has already been built, and therefore what can be skipped, is close in spirit.
Make variants like GNU Make have additional functionality, like automatic concurrent execution of parts of the build graph.
One of the things I did during the pandemic lockdown was work on the simplest possible blog in a single html file. Something that requires essentially no technical knowledge beyond typing text into a file. I recently dusted it off and yesterday I posted the most recent iteration.
Demo: https://bachmeil.github.io/minblog/blog.html
Source: https://github.com/bachmeil/minblog/blob/main/blog.html
There's very little styling, but that's not the objective (and it'd be trivial to add).
Oh really?:
sed -E 's|(href="$(subst source,,$<))|class="current" \1|' header.html | cat - $< > $@
a sed script that modifies HTML fragments isn't nothing. And this just does one thing that you might want to customize in the header for each page. It doesn't do things like handle pages having different <title> tags. Every feature like this that you come up with becomes another thing to maintain, and another thing that can catch you out later. When you come back to fix this later, will you even remember what it's supposed to do?From a web publishing point of view, make and sed are exotic dependencies. There's not going to be a bunch of helpful pointers online to help you debug issues with using them for this purpose. When you google how to fix your sed regex to match specific HTML attributes and not match others, you're going to find stack overflow posts about Zalgo, not quick answers.
If you want templating then use server side includes. It's much less attack surface than say, PHP or some complex CMS, but you can still just make a footer.html and stick in at the bottom of every other .html page easily and avoid the one problem with file based sites: updating shared bits.
If only it were possible using existing standard web technology. Sadly it was never designed with such goals in mind.
/s