completed readme for now.
This commit is contained in:
parent
8d835713c8
commit
357598c055
2 changed files with 122 additions and 3 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
/target
|
||||
ngen.toml
|
||||
build.ninja
|
||||
example/
|
||||
|
|
124
README.md
124
README.md
|
@ -41,6 +41,8 @@ Uninstall: `sudo sh uninstall.sh`
|
|||
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>.
|
||||
|
||||
### Basics
|
||||
|
||||
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,
|
||||
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
|
||||
the options you set were recognized, and your files were rebuilt accordingly.
|
||||
|
||||
TODO: explain
|
||||
- seperate targets
|
||||
- config table
|
||||
Now, this is a good start. But, it often the case that in a project, you want to
|
||||
have multiple different build *targets,* or configurations, that build the
|
||||
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.
|
||||
|
||||
|
|
Loading…
Reference in a new issue