github.com/zyedidia/knit@v1.1.2-0.20230901152954-f7d4e39a0e24/README.md (about) 1 # Knit 🧶 2 3 ![Test Workflow](https://github.com/zyedidia/knit/actions/workflows/test.yaml/badge.svg) 4 [![Go Reference](https://pkg.go.dev/badge/github.com/zyedidia/knit.svg)](https://pkg.go.dev/github.com/zyedidia/knit) 5 [![Go Report Card](https://goreportcard.com/badge/github.com/zyedidia/knit)](https://goreportcard.com/report/github.com/zyedidia/knit) 6 [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/zyedidia/knit/blob/master/LICENSE) 7 8 Knit is a build tool inspired by Make and Plan9 Mk. You define rules with a 9 Make-like embedded syntax within a Lua program. Rules can be passed around as 10 Lua objects, and generated by Lua code. You can use the Lua module system to 11 make reusable modules for building any kind of source code. Knit combines the 12 readability of a Make-style rules language will the power and expressiveness of 13 Lua. If you are familiar with Make, you can learn Knit very quickly. 14 15 Knit tracks more of your build to give you better incremental builds. For 16 example, Knit automatically adds an implicit dependency on a rule's recipe, so 17 if you change a recipe (either directly or through a variable change), Knit 18 will automatically re-run all rules that were affected. Knit will also skip 19 build steps dynamically if it can determine that they will be unchanged from 20 the previous run (even if earlier dependencies were changed). 21 22 Knit has support for namespaced sub-builds that execute relative to their 23 directory, but Knit avoids build fragmentation because sub-builds don't rely on 24 spawning build sub-processes. No more `make -C` to do sub-builds! Everything is 25 tracked by the root Knitfile, but you can still make directory-specific rules. 26 27 Knit's rules language is heavily inspired by [Plan9 28 Mk](https://9p.io/sys/doc/mk.html). In some ways, Knit can be considered a 29 modern version of Mk with a Lua meta-programming system built on top of it 30 (there are some differences compared to Mk). 31 32 Why make yet another build system? Because it's fun and useful to me! Maybe it 33 will be useful to you too. Everyone hates something about their build system so 34 if you have feedback or a request, let me know! The project is new enough that 35 your feedback may be seriously taken into account. 36 37 **Articles**: I have written two articles with more details about Knit 38 [here](https://zyedidia.github.io/blog/posts/3-knit-better-make/) and 39 [here](https://zyedidia.github.io/blog/posts/4-incremental-d-knit/) (this 40 article is specifically about D, but the optimization Knit applies is general 41 and not tied to D specifically). 42 43 # Features 44 45 * Knit uses Lua for customization. This makes it possible to write reusable 46 build libraries, and in general makes it easier to write powerful and 47 expressive builds. 48 * Knit has built-in syntax for a rules language inspired by Make and Plan9 Mk. 49 This makes it very familiar to anyone who has used Make/Mk. 50 * Knit has direct support for sub-builds (compared to Make, which usually 51 involves spawning a separate make sub-process to perform a sub-build). 52 * Knit can hash files to determine if they are out-of-date, rather than just 53 relying on file modification times. 54 * Knit additionally uses hashes for "dynamic task elision": if Knit can 55 dynamically determine that a prerequisite that was rebuilt actually 56 changed nothing, it won't re-run the dependent build step, allowing for 57 even better incremental builds compared to timestamp-based approaches 58 (Make, Ninja, etc.). 59 * Knit tracks recipe changes, so if you update a variable (in the Knitfile or 60 at the command-line), any dependent rules will be automatically rebuilt. 61 * Knit supports `%` meta-rules and regular expression meta-rules. 62 * Knit uses rule attributes instead of using targets such as `.SECONDARY` to 63 indicate special processing. 64 * Knit supports virtual rules that are independent of the file system. 65 * Knit uses sane variable names like `$input`, `$output`, and `$match` instead 66 of Make's `$^`, `$@`, and `$*`. 67 * Knit supports rules with multiple outputs, and treats them like Make's group 68 targets by default. 69 * Knit supports sub-tools that implement various build utilities including: 70 * Generating a graph visualization using graphviz (dot). 71 * Showing build status information (whether targets are out-of-date and 72 why). 73 * Exporting a compile commands database for use with a language server. 74 * Automatically cleaning all build outputs. 75 * Converting your build into a shell script, Makefile, or Ninja file. 76 * Knit will search up the directory hierarchy for a Knitfile, allowing you 77 to run your build from anywhere in your project. 78 * Knit supports parallel builds and uses all cores by default. 79 * Cross-platform support (Windows support is still experimental). 80 * Knit uses a shell to execute commands. By default, Knit searches for `sh` 81 on your system and uses that. If it cannot find `sh`, it uses an internal 82 (cross-platform) shell. 83 84 # Example Knitfile 85 86 Here is a very basic Knitfile for building a simple C project. 87 88 ```lua 89 return b{ 90 $ hello: hello.o 91 cc -O2 $input -o $output 92 $ %.o: %.c 93 cc -O2 -c $input -o $output 94 } 95 ``` 96 97 The syntax for rules is nearly the same as Make, and Knit supports `%` 98 meta-rules just like Make. However, rather than using a custom language 99 to configure the build, Knit uses Lua. 100 101 Here is a more complex example Knitfile used for building a simple C project. 102 This time the Knitfile supports various configurations (changing `cc` and 103 enabling debug flags), and automatically detects the source files. 104 105 ```lua 106 local knit = require("knit") 107 108 local conf = { 109 cc = cli.cc or "gcc", 110 debug = tobool(cli.debug) or false, 111 } 112 113 local cflags := -Wall 114 115 if conf.debug then 116 cflags := $cflags -Og -g 117 else 118 cflags := $cflags -O2 119 end 120 121 local src = knit.glob("*.c") 122 local obj = knit.extrepl(src, ".c", ".o") 123 local prog := hello 124 125 return b{ 126 $ build:VB: $prog 127 128 $ $prog: $obj 129 $(conf.cc) $cflags $input -o $output 130 $ %.o:D[%.d]: %.c 131 $(conf.cc) $cflags -MMD -c $input -o $output 132 } 133 ``` 134 135 Running `knit hello` would build all the necessary `.o` files and then link 136 them together. Running `knit hello debug=1` would change the flags and re-run 137 the affected rules. Running `knit build` will build `hello` (effectively an 138 alias for `knit hello`). The `VB` attributes on the build rule means that it 139 is virtual (not referring to a file on the system), and should always be built 140 (out-of-date). 141 142 Running `knit -t clean` will run a sub-tool that automatically removes all 143 generated files. 144 145 Header dependencies are automatically handled by using the `-MMD` compiler flag 146 with the `D[%.d]` attribute. To explicitly name the dependency file (e.g., to 147 put it in a `.dep` folder), you could instead use: 148 149 ``` 150 $ %.o:D[.dep/%.dep]: %.c 151 $(conf.cc) $cflags -MMD -MF $dep -c $input -o $output 152 ``` 153 154 Note that Knitfiles are Lua programs with some modified syntax: special syntax 155 using `$` for defining rules, and special syntax using `:=` for defining raw 156 strings (no quotes) with interpolation. 157 158 See the [docs](./docs/knit.md) for more information. 159 160 See [examples](./examples) for a few examples, and see this repository's 161 Knitfile and the tests for even more examples. 162 163 # Installation 164 165 Prebuilt binaries are available from the [release page](https://github.com/zyedidia/knit/releases). 166 167 You can install one automatically using [eget](https://github.com/zyedidia/eget). 168 169 ``` 170 eget zyedidia/knit 171 ``` 172 173 Or you can build from source (requires Go 1.19): 174 175 ``` 176 go install github.com/zyedidia/knit/cmd/knit@latest 177 ``` 178 179 # Experimental or future possible features 180 181 * Ninja to Knit converter (for compatibility with cmake, and for benchmarking). 182 See [knitja](https://github.com/zyedidia/knitja) for the converter tool. 183 * Performance optimizations. 184 * Knit can already be used to build large projects, such as CVC5 (using the 185 knitja converter). For massive builds though, like LLVM, Knit suffers 186 from some performance problems that could be improved. 187 * Better support for dynamic dependencies. Currently it is possible to handle 188 dynamic dependencies by generating rules, but I would like to explore the 189 possibility of a more clean and cohesive solution. 190 * Ptrace enabled automatic dependency discovery (Linux-only feature). 191 See the [xkvt](https://github.com/zyedidia/xkvt) project for some 192 experiments on this front. 193 * Global build file cache (similar to `ccache`, but for every command that is 194 executed). 195 * A restrictive mode for build sandboxing. 196 197 # Feedback 198 199 It is always useful useful to get feedback from others to improve Knit. If you 200 have feedback, or questions about how to use it, please open a discussion. It 201 would be great to discuss the good and bad parts of the current design, and how 202 it can be improved. 203 204 # Usage 205 206 ``` 207 Usage of knit: 208 knit [TARGETS] [ARGS] 209 210 Options: 211 -B, --always-build unconditionally build all targets 212 --cache string directory for caching internal build information (default ".") 213 --cpuprofile string write cpu profile to 'file' 214 -D, --debug print debug information 215 -C, --directory string run command from directory 216 -n, --dry-run print commands without actually executing 217 -f, --file string knitfile to use (default "knitfile") 218 --hash hash files to determine if they are out-of-date (default true) 219 -h, --help show this help message 220 --keep-going keep going even if recipes fail 221 -q, --quiet don't print commands 222 --shell string shell to use when executing commands (default "sh") 223 -s, --style string printer style to use (basic, steps, progress) (default "basic") 224 -j, --threads int number of cores to use (default 8) 225 -t, --tool string subtool to invoke (use '-t list' to list subtools); further flags are passed to the subtool 226 -u, --updated strings treat files as updated 227 -v, --version show version information 228 ``` 229 230 Available sub-tools (`knit -t list`): 231 232 ``` 233 list - list all available tools 234 graph - print build graph in specified format: text, tree, dot, pdf 235 clean - remove all files produced by the build 236 targets - list all targets (pass 'virtual' for just virtual targets) 237 compdb - output a compile commands database 238 commands - output the build commands (formats: knit, json, make, ninja, shell) 239 status - output dependency status information 240 ``` 241 242 # Contributing 243 244 If you find a bug or have a feature request please open an issue for 245 discussion. I am sometimes prone to being unresponsive to pull requests, so I 246 apologize in advance. Please ping me if I forget to respond. If you have a 247 feature you would like to implement, please double check with me about the 248 feature before investing lots of time into implementing it. 249 250 If you have a question or feedback about the current design, please open a 251 discussion.