completed readme for now.

This commit is contained in:
Noah Swerhun 2024-03-06 13:50:51 -06:00
parent 8d835713c8
commit 357598c055
2 changed files with 122 additions and 3 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/target /target
ngen.toml ngen.toml
build.ninja build.ninja
example/

124
README.md
View file

@ -41,6 +41,8 @@ Uninstall: `sudo sh uninstall.sh`
What follows is a tutorial of how to set up ngen for an existing executable What follows is a tutorial of how to set up ngen for an existing executable
project. If you are looking for a reference, look <SOMEWHERE>. project. If you are looking for a reference, look <SOMEWHERE>.
### Basics
The first thing you will need is an `ngen.toml` file. This is what will specify The first thing you will need is an `ngen.toml` file. This is what will specify
all of the build parameters, such as compilation flags, source files to track, all of the build parameters, such as compilation flags, source files to track,
etc. Start by creating this file and opening it in your editor. etc. Start by creating this file and opening it in your editor.
@ -168,6 +170,122 @@ these values will be automatically picked up by Ninja and accounted for in the
build. Running `ninja -v` immediately after saving `ngen.toml` should show that build. Running `ninja -v` immediately after saving `ngen.toml` should show that
the options you set were recognized, and your files were rebuilt accordingly. the options you set were recognized, and your files were rebuilt accordingly.
TODO: explain Now, this is a good start. But, it often the case that in a project, you want to
- seperate targets have multiple different build *targets,* or configurations, that build the
- config table project with slightly different parameters; for example, it is common to have a
"debug" build target that is unoptimized and includes debugging symbols, and a
"release" build that is optimized at compile time. ngen is designed to make this
configuration as easy as possible.
### Targets
When we were specifying parameters above, we were doing so in the "global
scope," so to speak, of the TOML file. Without knowing it, we were actually
configuring the `main` target (this is why the outfiles were placed in
`build/*main/*`. The main target is special because it does not need to be
labeled: all build parameters placed in the global scope (or, more accurately,
the TOML"root table"), will be used for the `main` target. To specify
additional targets, we create a TOML table with the name of our target. Let's
create a target called `debug`. Add the following to your `ngen.toml`:
```toml
[debug]
outfile = "example_dbg"
compiler_flags = ["-g"]
```
What's going on here? How does `debug` know what files to operate on, what
compiler to use, etc? Well, the `main` target is special in another way: all
targets *inherit* the parameters set in the main target. Inheritance works
according to two simple rules: **a**rrays **a**ppend, **s**trings **s**upercede.
The first thing that happens is `debug` takes on all the same parameters from
main. Then, ngen reads the outfile key in `debug`. Becuase outfile is a string,
`debug.outfile` is overwritten as the value specified, in this case
"example\_dbg". On the othe hand, since compiler\_flags is a list, the elements
sepecified in `debug.compiler_flags` are appended to the list of flags specified
in `main`. So in this case, the effective value of `debug.compiler_flags` is
`["-Wall", "-Wextra -O2", "-g"]`.
For our debug build, we probably don't want the `-O2` flag---optimizations
should only happen in a "release" type build. So, lets remove the `-O2` flag
from `main` and place it in a new target called `release`. Our `ngen.toml`
should now look like this:
```toml
outfile = "example"
compiler = "gcc"
compiler_flags = ["-Wall", "-Wextra"]
linker_libs = ["-lm"]
sources = [
"src/main.c",
"src/util.c",
"src/functions.c",
"src/foobar.c",
]
[debug]
outfile = "example_dbg"
compiler_flags = ["-g"]
[release]
compiler_flags = ["-O2"]
```
This brings up an important design pattern you should keep in mind when writing
your `ngen.toml`: the special `main` target should only contain the largest
subset of all your build parameters. Additonal targets should add specific
parameters for specific use cases, as we saw in the above example.
In a nutshell, inheritance allows you to easily create multiple targets with small variations,
without having to rewrite the same thing over and over again.
Save `ngen.toml`, and try running `ninja -v debug` or `ninja -v release`. You
should see that each of these targets uses the parameters that we specified with
inheritance. Outfiles for a given target are always placed in
`build/<target_name>/`, as you can see with `tree build`. This keeps things
organized, and also means we don't have to specify a different outfile name for
each target (I did above just to show you how strings are replaced in the
inheritance system).
Note that by default, running `ninja` alone with no target specifed will run
*every single target* it finds. You can change this behavor by adding the
`default = true` key to the targets you want to be built when Ninja is invoked
with no arguments. Say that this example project is under active developemnt,
and you will be building the `debug` target alot. You can add the `default =
true` flag to the `[debug]` table, and now running `ninja` by it self will only
build the `debug` target. You can still build the release and main targets by
running `ninja release` and `ninja main`.
It is easy to see how powerful this simple configuration file is already.
However, ngen has a few more features that you may find useful.
### Configuration
The `config` table is where you can specify certain options which change the way
ngen behaves. Right now, this only involves a single feature: generating a
compile\_commands.json file for the `clangd` LSP.
To enable the generation of compile\_commands.json, simply add the following
line the **global scope** (i.e. next to your `main` target, not beneath any of
your other targets) of your `ngen.toml`:
```toml
config.compile_commands = true
```
The next time you run Ninja, ngen will automatically generate
`build/compile_commands.json` where it can be picked up by clangd. Ninja will
make sure that this file is kept up to date as well. Just set the option in your
ngen.toml and forget about it.
By default, the compile\_commands.json will be generated according to the build
specs of the `main` target. To change which target it is generated for use the
`config.compile_commands_target` key. For example,
```toml
config.compile_commands_target = "debug"
```
will generate the the compile\_commands for the `debug` target.