diff --git a/how_to/002_object_dependencies/.gitignore b/how_to/002_object_dependencies/.gitignore new file mode 100644 index 0000000..59c52e1 --- /dev/null +++ b/how_to/002_object_dependencies/.gitignore @@ -0,0 +1,3 @@ +nob +nob.old +build/ \ No newline at end of file diff --git a/how_to/002_object_dependencies/nob.c b/how_to/002_object_dependencies/nob.c new file mode 100644 index 0000000..57dca64 --- /dev/null +++ b/how_to/002_object_dependencies/nob.c @@ -0,0 +1,104 @@ +// This is your build script. You only need to "bootstrap" it once with `cc -o nob nob.c` +// (you can call the executable whatever actually) or `cl nob.c` on MSVC. After that every +// time you run the `nob` executable if it detects that you modifed nob.c it will rebuild +// itself automatically thanks to NOB_GO_REBUILD_URSELF (see below) + +// nob.h is an stb-style library https://github.com/nothings/stb/blob/master/docs/stb_howto.txt +// What that means is that it's a single file that acts both like .c and .h files, but by default +// when you include it, it acts only as .h. To make it include implementations of the functions +// you must define NOB_IMPLEMENTATION macro. This is done to give you full control over where +// the implementations go. +#define NOB_IMPLEMENTATION + +// Always keep a copy of nob.h in your repo. One of my main pet peeves with build systems like CMake +// and Autotools is that the codebases that use them naturally rot. That is if you do not actively update +// your build scripts, they may not work with the latest version of the build tools. Here we basically +// include the entirety of the source code of the tool along with the code base. It will never get +// outdated (unless you got no standard compliant C compiler lying around, but at that point why are +// you trying to build a C project?) +// +// (In these examples we actually symlinking nob.h, but this is to keep nob.h-s synced among all the +// examples) +#include "nob.h" + +// Some folder paths that we use throughout the build process. +#define BUILD_FOLDER "build/" +#define SRC_FOLDER "src/" + +typedef enum { + IS_BINARY, + IS_O_FILE, +} Is; + +int go_rebuild_and_reset(const char *binary_path, Is path_is, Nob_File_Paths *dependency_paths); + +int main(int argc, char **argv) +{ + // This line enables the self-rebuilding. It detects when nob.c is updated and auto rebuilds it then + // runs it again. + NOB_GO_REBUILD_URSELF(argc, argv); + + // It's better to keep all the building artifacts in a separate build folder. Let's create it if it + // does not exist yet. + // + // Majority of the nob command return bool which indicates whether operation has failed or not (true - + // success, false - failure). If the operation returned false you don't need to log anything, the + // convention is usually that the function logs what happened to itself. Just do + // `if (!nob_function()) return;` + if (!nob_mkdir_if_not_exists(BUILD_FOLDER)) return 1; + Nob_File_Paths dependency_paths = {0}; + + // For building a `.o` file, the corresponding `.c` file comes first: + nob_da_append(&dependency_paths, SRC_FOLDER "foo.c"); + // Then any dependencies: + nob_da_append(&dependency_paths, SRC_FOLDER "foo.h"); + if (!go_rebuild_and_reset(BUILD_FOLDER "foo.o", IS_O_FILE, &dependency_paths)) { + return 1; + } + + // For building a `.o` file, the corresponding `.c` file comes first: + nob_da_append(&dependency_paths, SRC_FOLDER "global.c"); + // Then any dependencies: + nob_da_append(&dependency_paths, SRC_FOLDER "global.h"); + nob_da_append(&dependency_paths, SRC_FOLDER "foo.h"); + if (!go_rebuild_and_reset(BUILD_FOLDER "global.o", IS_O_FILE, &dependency_paths)) { + return 1; + } + + // For building the main executable, just add all `.o` files in. + nob_da_append(&dependency_paths, SRC_FOLDER "main.c"); + nob_da_append(&dependency_paths, BUILD_FOLDER "foo.o"); + nob_da_append(&dependency_paths, BUILD_FOLDER "global.o"); + if (!go_rebuild_and_reset(BUILD_FOLDER "main", IS_BINARY, &dependency_paths)) { + return 1; + } + + return 0; +} + +int go_rebuild_and_reset(const char *binary_path, Is path_is, Nob_File_Paths *dependency_paths) { + int ok = 1; + int rebuild_is_needed = nob_needs_rebuild(binary_path, dependency_paths->items, dependency_paths->count); + if (rebuild_is_needed) { + Nob_Cmd cmd = {0}; + + nob_cc(&cmd); + nob_cc_flags(&cmd); + nob_cc_output(&cmd, binary_path); + if (path_is == IS_O_FILE) { + // Not sure if this is portable, but generate object files only. + nob_cmd_append(&cmd, "-c"); + // Only the first input is needed for .o files. + nob_cc_inputs(&cmd, dependency_paths->items[0]); + } else for (int i = 0; i < dependency_paths->count; ++i) { + nob_cc_inputs(&cmd, dependency_paths->items[i]); + } + + if (!nob_cmd_run_sync(cmd)) { + ok = 0; + } + } + NOB_FREE(dependency_paths->items); + *dependency_paths = (Nob_File_Paths){0}; + return ok; +} diff --git a/how_to/002_object_dependencies/nob.h b/how_to/002_object_dependencies/nob.h new file mode 120000 index 0000000..53e38de --- /dev/null +++ b/how_to/002_object_dependencies/nob.h @@ -0,0 +1 @@ +../../nob.h \ No newline at end of file diff --git a/how_to/002_object_dependencies/src/foo.c b/how_to/002_object_dependencies/src/foo.c new file mode 100644 index 0000000..0e534e0 --- /dev/null +++ b/how_to/002_object_dependencies/src/foo.c @@ -0,0 +1,6 @@ +#include "foo.h" + +void frobnicate(struct foo *foo) +{ + ++foo->bar; +} diff --git a/how_to/002_object_dependencies/src/foo.h b/how_to/002_object_dependencies/src/foo.h new file mode 100644 index 0000000..721825c --- /dev/null +++ b/how_to/002_object_dependencies/src/foo.h @@ -0,0 +1,8 @@ +#pragma once + +struct foo +{ + int bar; +}; + +void frobnicate(struct foo *foo); diff --git a/how_to/002_object_dependencies/src/global.c b/how_to/002_object_dependencies/src/global.c new file mode 100644 index 0000000..e4c61a4 --- /dev/null +++ b/how_to/002_object_dependencies/src/global.c @@ -0,0 +1,17 @@ +#include "foo.h" + +#include + +struct foo global_foo = (struct foo){ + .bar = 0, +}; + +void frobnicate_global() +{ + frobnicate(&global_foo); +} + +void print_global_status() +{ + printf("foo is %d\n", global_foo.bar); +} diff --git a/how_to/002_object_dependencies/src/global.h b/how_to/002_object_dependencies/src/global.h new file mode 100644 index 0000000..8172024 --- /dev/null +++ b/how_to/002_object_dependencies/src/global.h @@ -0,0 +1,8 @@ +#pragma once + +#include "foo.h" + +extern struct foo global_foo; + +void frobnicate_global(); +void print_global_status(); diff --git a/how_to/002_object_dependencies/src/main.c b/how_to/002_object_dependencies/src/main.c new file mode 100644 index 0000000..324af99 --- /dev/null +++ b/how_to/002_object_dependencies/src/main.c @@ -0,0 +1,11 @@ +#include "global.h" + +int main(void) +{ + frobnicate_global(); + print_global_status(); + + frobnicate_global(); + print_global_status(); + return 0; +} diff --git a/how_to/nob.c b/how_to/nob.c index 4981ce3..d60cbc6 100644 --- a/how_to/nob.c +++ b/how_to/nob.c @@ -8,6 +8,7 @@ const char *examples[] = { "001_basic_usage", + "002_object_dependencies", "005_parallel_build", "010_nob_two_stage", };