From 357598c0551fe4dd7ccd55f55cc25de14036903f Mon Sep 17 00:00:00 2001 From: Noah Swerhun Date: Wed, 6 Mar 2024 13:50:51 -0600 Subject: [PATCH] completed readme for now. --- .gitignore | 1 + README.md | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 122 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 85e9471..2bff697 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target ngen.toml build.ninja +example/ diff --git a/README.md b/README.md index b624736..3857a4f 100644 --- a/README.md +++ b/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 . +### 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//`, 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. +