Mk is a reboot of the Plan 9 mk
command, which itself is a successor to make.
go get github.com/ctSkennerton/mk
$GOPATH/bin
is in your PATH
.Way back in the 90s, some smart guys at Bell Labs got together and decided to write new operating system to replace Unix. The idea was to keep everything that was great about Unix, but totally disregard backwards compatibility in a quest for something better. The operating system they designed, Plan 9, had a lot of terrific ideas, and though some were cherry picked, the OS as a whole never really caught on.
Among the gems in Plan 9 was a rewrite of the venerable Unix make command, in the form of mk
. Simply put, mk
is make
, but with a large collection of relatively minor improvements, adding up to something more consistent, elegant, and powerful. To name a few specifics:
.SECONDARY:
.$target
, $prereq
, and $stem
in place of make’s pointlessly cryptic $@
, $^
, and $*
.%.o: %.c
), mk has more powerful regular expression rules.<|sh config.sh
.$
.And much more! Read Maintaining Files on Plan 9 with Mk for good overview.
This mk stays mostly faithful to Plan 9, but makes a few improvements.
-p=1
if this is the case.$stem1
, $stem2
, etc, rather than \1
, \2
, etc.$shell
variable which will be sourced as the shell for recipe blocks unless overriden by an ‘S’ attribute.See the manual for more info
mk [options] [target] ...
-C directory
Change directory to directory
first.-f filename
Use the given file as the mkfile.-n
Dry run, print commands without actually executing.-r
Force building of the immediate targets.-a
Force building the targets and of all their dependencies.-p
Maximum number of jobs to execute in parallel (default: # CPU cores)-i
Show rules that will execute and prompt before executing.-color
Boolean flag to force color on / off.-F
Don’t drop shell arguments when no further arguments are specified.-s name
Default shell to use if none are specified via $shell (default: “sh -c”)-l int
Maximum number of recursive invocations of a rule. (default 1)-q
Don’t print recipesbefore executing them.Non-shell recipes are a major addition over Plan 9 mk. They can be used with the S[command]
attribute, where command
is an arbitrary command that the recipe will be piped into. For example, here’s a recipe to add the read numbers from a file and write their mean to another file. Unlike a typical recipe, it’s written in Julia.
mean.txt:Sjulia: input.txt
println(open("$target", "w"),
mean(map(parseint, eachline(open("$prereq")))))
Another major addition over Plan 9 mk is the ability to have remote files in the dependancy graph. A common usage (in my work at least) is to download files from a public http repository or from Amazon S3. This has previously been difficult to do as make/mk had no (easy) way of comparing whether these files were older than the target.
somefile.txt: "s3://bucket/path/to/key/file.txt.gz"
aws s3 cp $prereq - | gunzip -c - > $target
anotherfile.csv: "https://example.com/data/results.csv"
curl $prereq > $target
Remote files need to be enclosed in double quotes to protect the colon character from being interpreted as the separator between targets, attributes, and prerequisites.
For http(s) files the Last-Modified
header is inspected to determine if the resource is older than the target. If that header doesn’t exist then it is assumed that the resource is older than the target. S3 files utilize the AWS S3 api to determine the last modification time.
Functional, but with some bugs and some unimplemented minor features. Give it a try and see what you think!
This work is provided under the BSD 2-clause license
Copyright © 2013, Daniel C. Jones - All rights reserved.
With additional updates by people listed in contributors.