This commit is contained in:
snarmph 2025-03-21 19:09:14 +01:00
parent 01f4ad7f62
commit 6d36aa4442
100 changed files with 5138 additions and 13015 deletions

View file

@ -1,8 +0,0 @@
#!/bin/bash
# cosmocc -Os -mtiny -o docs.com docs.c
# optimised version for x86/64 only, shaves off ~300KB
x86_64-unknown-cosmo-cc -Os -mtiny -o ../docs/docs.com docs.c
rm ../docs/docs.com.dbg

View file

@ -1,465 +0,0 @@
#include "../arena.c"
#include "../file.c"
#include "../format.c"
#include "../ini.c"
#include "../str.c"
#include "../strstream.c"
#include "../tracelog.c"
#include "../vmem.c"
#include "../markdown.c"
#include "../highlight.c"
#include "../dir.c"
#include "../socket.c"
#include "../http.c"
#include "../server.c"
#include "../html.h"
const char *raw_css;
struct {
bool gen;
strview_t gendir;
} options = {0};
typedef struct page_t {
str_t title;
str_t url;
str_t data;
struct page_t *next;
} page_t;
typedef struct {
uint8 arenabuf[KB(5)];
arena_t arena;
hl_ctx_t *hl;
int line;
} cparser_ctx_t;
void md_cparser_init(void *udata) {
cparser_ctx_t *cparser = udata;
cparser->line = 1;
if (cparser->hl) {
return;
}
cparser->arena = arenaMake(ARENA_STATIC, sizeof(cparser->arenabuf), cparser->arenabuf);
cparser->hl = hlInit(&cparser->arena, &(hl_config_t){
.colors = {
[HL_COLOR_NORMAL] = strv("</span>"),
[HL_COLOR_PREPROC] = strv("<span class=\"c-preproc\">"),
[HL_COLOR_TYPES] = strv("<span class=\"c-types\">"),
[HL_COLOR_CUSTOM_TYPES] = strv("<span class=\"c-custom-types\">"),
[HL_COLOR_KEYWORDS] = strv("<span class=\"c-kwrds\">"),
[HL_COLOR_NUMBER] = strv("<span class=\"c-num\">"),
[HL_COLOR_STRING] = strv("<span class=\"c-str\">"),
[HL_COLOR_COMMENT] = strv("<span class=\"c-cmnt\">"),
[HL_COLOR_FUNC] = strv("<span class=\"c-func\">"),
[HL_COLOR_SYMBOL] = strv("<span class=\"c-sym\">"),
[HL_COLOR_MACRO] = strv("<span class=\"c-macro\">"),
},
.flags = HL_FLAG_HTML,
});
}
void md_cparser_callback(strview_t line, outstream_t *out, void *udata) {
cparser_ctx_t *cparser = udata;
arena_t scratch = cparser->arena;
str_t highlighted = hlHighlight(&scratch, cparser->hl, line);
ostrPrintf(out, "<li>%v</li>", highlighted);
}
page_t *get_pages(arena_t *arena, strview_t path, strview_t default_page) {
arena_t scratch = arenaMake(ARENA_VIRTUAL, MB(1));
dir_t *dir = dirOpen(&scratch, path);
dir_entry_t *entry = NULL;
page_t *first = NULL;
page_t *head = NULL;
page_t *tail = NULL;
cparser_ctx_t cparser = {0};
while ((entry = dirNext(&scratch, dir))) {
if (entry->type != DIRTYPE_FILE) {
continue;
}
strview_t name, ext;
fileSplitPath(strv(entry->name), NULL, &name, &ext);
if (!strvEquals(ext, strv(".md"))) {
continue;
}
str_t fullname = strFmt(&scratch, "%v/%v", path, entry->name);
str_t markdown_str = fileReadWholeStr(&scratch, strv(fullname));
str_t md = markdownStr(&scratch, strv(markdown_str), &(md_options_t){
.parsers = (md_parser_t[]){
{
.init = md_cparser_init,
.callback = md_cparser_callback,
.userdata = &cparser,
.lang = strv("c"),
},
},
.parsers_count = 1,
});
page_t *page = alloc(arena, page_t);
page->data = md;
page->url = str(arena, name);
usize line_end = strvFind(strv(markdown_str), '\n', 0);
strview_t line = strvSub(strv(markdown_str), 0, line_end);
strview_t page_title = strvTrim(strvRemovePrefix(line, 1));
page->title = strFmt(arena, "%v", page_title);
if (!first && strvEquals(name, default_page)) {
first = page;
}
else {
if (!head) head = page;
if (tail) tail->next = page;
tail = page;
}
}
if (first) {
first->next = head;
head = first;
}
strview_t css = strv(raw_css);
for_each(page, head) {
str_t html = STR_EMPTY;
htmlBeg(arena, &html);
headBeg();
title(page->title);
style(css);
headEnd();
bodyBeg();
divBeg(.id="main");
divBeg(.class="content");
divBeg(.class="pages-container");
divBeg(.class="pages");
for_each(item, head) {
str_t class = strFmt(&scratch, "page-item%s", item == page ? " page-current" : "");
str_t href = STR_EMPTY;
if (options.gen) {
href = strFmt(&scratch, "%v.html", item->url);
}
else {
href = strFmt(&scratch, "%v", item->url);
}
a(
item->title,
.href = href.buf,
.class = class.buf,
);
}
divEnd();
divEnd();
divBeg(.class="document-container");
divBeg(.class="document");
htmlPuts(page->data);
htmlRaw(<div class="document-padding"></div>);
divEnd();
divEnd();
divEnd();
divEnd();
bodyEnd();
htmlEnd();
page->data = html;
}
arenaCleanup(&scratch);
return head;
}
str_t server_default(arena_t scratch, server_t *server, server_req_t *req, void *userdata) {
strview_t needle = strv(req->page);
if (strvFront(needle) == '/') {
needle = strvRemovePrefix(strv(req->page), 1);
}
page_t *page = userdata;
while (page) {
if (strvEquals(strv(page->url), needle)) {
break;
}
page = page->next;
}
// if the url is invalid, return the default page
if (!page) {
page = userdata;
}
return serverMakeResponse(&scratch, 200, strv("text/html"), strv(page->data));
}
str_t server_quit(arena_t scratch, server_t *server, server_req_t *req, void *userdata) {
serverStop(server);
return STR_EMPTY;
}
int main(int argc, char **argv) {
arena_t arena = arenaMake(ARENA_VIRTUAL, MB(1));
for (int i = 1; i < argc; ++i) {
strview_t arg = strv(argv[i]);
if (strvEquals(arg, strv("-h"))) {
info("usage: %s [-h, -gen <outdir>]", argv[0]);
return 0;
}
else if (strvEquals(arg, strv("-gen"))) {
options.gen = true;
if ((i + 1) < argc) {
options.gendir = strv(argv[++i]);
}
}
}
page_t *pages = get_pages(&arena, strv("."), strv("readme"));
if (!pages) {
err("could not get pages");
return 1;
}
if (options.gen) {
if (strvBack(options.gendir) == '/' || strvBack(options.gendir) == '\\') {
options.gendir.len -= 1;
}
for_each(page, pages) {
arena_t scratch = arena;
str_t fname = strFmt(&scratch, "%v/%v.html", options.gendir, page->url);
if (!fileWriteWhole(strv(fname), page->data.buf, page->data.len)) {
err("couldn't save page %v", fname);
}
else {
info("saved %v", fname);
}
}
return 0;
}
server_t *s = serverSetup(&arena, 8080, true);
serverRouteDefault(&arena, s, server_default, pages);
serverRoute(&arena, s, strv("/quit"), server_quit, NULL);
serverStart(arena, s);
arenaCleanup(&arena);
}
//// HTML GENERATION STUFF ///////////////////////////////
const char *raw_css = ""
"html, body, #main {\n"
" margin: 0;\n"
" width: 100%;\n"
" height: 100%;\n"
" font-family: -apple-system,BlinkMacSystemFont,'Segoe UI','Noto Sans',Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji';\n"
" \n"
" --black: #121212;\n"
" --dark-gray: #212121;\n"
" --gray: #303030;\n"
" --light-gray: #424242;\n"
" --accent: #FFA500;\n"
" /* --accent: #F98334; */\n"
"\n"
" --blue: #45559E;\n"
" --orange: #F98334;\n"
" --yellow: #FABD2F;\n"
" --red: #FB4934;\n"
" --pink: #D3869B;\n"
" --green: #B8BB26;\n"
" --azure: #7FA375;\n"
" --white: #FBF1C7;\n"
" --light-blue: #83A598;\n"
"}\n"
"\n"
"hr {\n"
" color: white;\n"
" opacity: 0.5;\n"
"}\n"
"\n"
"a {\n"
" text-decoration: none;\n"
" font-weight: bold;\n"
" color: var(--red);\n"
"}\n"
"\n"
"a:hover {\n"
" text-decoration: underline;\n"
"}\n"
"\n"
".content {\n"
" width: 100%;\n"
" height: 100%;\n"
" background-color: var(--black);\n"
" display: flex;\n"
" gap: 20px;\n"
" align-items: stretch;\n"
" color: white;\n"
" overflow-x: scroll;\n"
"}\n"
"\n"
".pages-container {\n"
" position: sticky;\n"
" top: 0;\n"
" min-width: 200px;\n"
" background-color: var(--dark-gray);\n"
" border-right: 1px solid var(--light-gray);\n"
" box-shadow: 0 0 20px rgba(0, 0, 0, 1.0);\n"
" overflow-y: scroll;\n"
"}\n"
"\n"
".document-container {\n"
" display: flex;\n"
" justify-content: center;\n"
" width: 100%;\n"
"}\n"
"\n"
".document {\n"
" width: 100%;\n"
" max-width: 700px;\n"
" line-height: 1.5;\n"
"}\n"
"\n"
".document-padding {\n"
" height: 50px;\n"
"}\n"
"\n"
".pages {\n"
" position: relative;\n"
" background-color: var(--dark-gray);\n"
"}\n"
"\n"
".page-item:last-of-type {\n"
" border-bottom: 0;\n"
"}\n"
"\n"
".page-item {\n"
" text-decoration: none;\n"
" display: block;\n"
" color: rgba(255, 255, 255, 0.5);\n"
" padding: 0.2em 0 0.2em 1.5em;\n"
" border-bottom: 1px solid var(--light-gray);\n"
"}\n"
"\n"
".page-item:hover {\n"
" background-color: var(--light-gray);\n"
" cursor: pointer;\n"
" color: white;\n"
" opacity: 1;\n"
"}\n"
"\n"
".page-current {\n"
" color: var(--accent);\n"
" opacity: 1;\n"
"}\n"
"\n"
".page-current:hover {\n"
" color: var(--accent);\n"
"}\n"
"\n"
".page-spacing {\n"
" height: 25px; \n"
"}\n"
"\n"
"code {\n"
" color: var(--accent);\n"
" background-color: var(--dark-gray);\n"
" padding: 0.2em 0.5em 0.2em 0.5em;\n"
" border-radius: 0.5em;\n"
"}\n"
"\n"
"pre {\n"
" margin: 0;\n"
" margin-top: 2em;\n"
" background-color: var(--dark-gray);\n"
" padding: 16px;\n"
" border-radius: 0.3em;\n"
" overflow-x: scroll;\n"
"\n"
" border: 1px solid var(--light-gray);\n"
" box-shadow: 0 0 20px rgba(0, 0, 0, 1.0);\n"
"}\n"
"\n"
"pre > code {\n"
" color: #FBF1C7;\n"
" padding: 0;\n"
" background-color: transparent;\n"
"}\n"
"\n"
"pre ol {\n"
" counter-reset: item;\n"
" padding-left: 0;\n"
"}\n"
"\n"
"pre li {\n"
" display: block;\n"
" margin-left: 0em;\n"
"}\n"
"\n"
"pre li::before {\n"
" display: inline-block;\n"
" content: counter(item);\n"
" counter-increment: item;\n"
" width: 2em;\n"
" padding-right: 1.5em;\n"
" text-align: right;\n"
"}\n"
"\n"
"/* code block colors */\n"
".c-preproc {\n"
" color: #45559E;\n"
"}\n"
"\n"
".c-types {\n"
" color: #F98334;\n"
"}\n"
"\n"
".c-custom-types {\n"
" color: #FABD2F;\n"
"}\n"
"\n"
".c-kwrds {\n"
" color: #FB4934;\n"
"}\n"
"\n"
".c-num {\n"
" color: #D3869B;\n"
"}\n"
"\n"
".c-str {\n"
" color: #B8BB26;\n"
"}\n"
"\n"
".c-cmnt {\n"
" color: #928374;\n"
"}\n"
"\n"
".c-func {\n"
" color: #7FA375;\n"
"}\n"
"\n"
".c-sym {\n"
" color: #FBF1C7;\n"
"}\n"
"\n"
".c-macro {\n"
" color: #83A598;\n"
"}\n"
"";

418
tools/nob.c Normal file
View file

@ -0,0 +1,418 @@
#define COLLA_NO_CONDITION_VARIABLE 1
#include "../build.c"
#include <windows.h>
#include <direct.h>
#if COLLA_TCC
WINBASEAPI LPCH WINAPI GetEnvironmentStringsj(VOID);
WINBASEAPI LPWCH WINAPI GetEnvironmentStringsW(VOID);
WINBASEAPI BOOL WINAPI FreeEnvironmentStringsA(LPCH penv);
WINBASEAPI BOOL WINAPI FreeEnvironmentStringsW(LPWCH penv);
#ifdef UNICODE
#define GetEnvironmentStrings GetEnvironmentStringsW
#define FreeEnvironmentStrings FreeEnvironmentStringsW
#else
#define GetEnvironmentStrings GetEnvironmentStringsA
#define FreeEnvironmentStrings FreeEnvironmentStringsA
#endif
#endif
int strv_to_int(strview_t strv) {
instream_t in = istr_init(strv);
i32 value = 0;
if (!istr_get_i32(&in, &value)) {
return 0;
}
return value;
}
str_t find_vcvars_path(arena_t *arena) {
strview_t base_path = strv("C:/Program Files/Microsoft Visual Studio");
// find year
int year = 0;
{
arena_t tmp = *arena;
dir_t *dir = os_dir_open(&tmp, base_path);
if (!os_dir_is_valid(dir)) {
err("couldn't open directory (%v)", base_path);
return STR_EMPTY;
}
dir_foreach(&tmp, entry, dir) {
if (entry->type != DIRTYPE_DIR) continue;
int number = strv_to_int(strv(entry->name));
if (number > year) year = number;
}
}
if (year == 0) {
err("couldn't find visual studio year version");
return STR_EMPTY;
}
str_t path_with_year = str_fmt(arena, "%v/%d", base_path, year);
// find edition
const char *editions[] = {
"Enterprise",
"Professional",
"Community",
};
int edition = 0;
for (; edition < arrlen(editions); ++edition) {
arena_t tmp = *arena;
str_t path = str_fmt(&tmp, "%v/%s", path_with_year, editions[edition]);
if (os_file_exists(strv(path))) {
break;
}
}
if (edition >= arrlen(editions)) {
err("couldn't find visual studio edition");
return STR_EMPTY;
}
str_t vcvars = str_fmt(arena, "%v/%s/VC/Auxiliary/Build/vcvars64.bat", path_with_year, editions[edition]);
return vcvars;
}
bool load_cache(arena_t *arena) {
if (!os_file_exists(strv("build/cache.ini"))) {
err("build/cache.ini doesn't exist");
return false;
}
{
arena_t scratch = *arena;
ini_t ini = ini_parse(&scratch, strv("build/cache.ini"), &(iniopt_t){ .comment_vals = strv("#") });
initable_t *root = ini_get_table(&ini, INI_ROOT);
if (!root) fatal("fail");
for_each (val, root->values) {
os_set_env_var(scratch, val->key, val->value);
}
}
return true;
}
typedef enum optimise_level_e {
OPTIMISE_NONE,
OPTIMISE_FAST,
OPTIMISE_SMALL,
OPTIMISE__COUNT,
} optimise_level_e;
typedef enum warning_level_e {
WARNING_NONE,
WARNING_DEFAULT,
WARNING_ALL,
WARNING__COUNT,
} warning_level_e;
typedef enum sanitiser_e {
SANITISER_NONE,
SANITISER_ADDRESS,
SANITISER__COUNT,
} sanitiser_e;
typedef enum cversion_e {
CVERSION_LATEST,
CVERSION_17,
CVERSION_11,
CVERSION__COUNT
} cversion_e;
typedef struct options_t options_t;
struct options_t {
strview_t input_fname;
strview_t out_fname;
optimise_level_e optimisation;
warning_level_e warnings;
bool warnings_as_error;
sanitiser_e sanitiser;
bool fast_math;
bool debug;
strv_list_t *defines;
cversion_e cstd;
bool run;
strv_list_t *run_args;
bool is_cpp;
};
void print_help_message(void) {
puts("usage:");
puts(" -r / -run [input.c] [args...] compiles and runs <input.c>, forwards <args...>");
puts(" -h / -help print this message");
puts(" -o / -out [filename] output filename (default: build/<file>.exe)");
puts(" -O / -optimise [fast,small] optimisation level");
puts(" -w / -warning [default,all] warning level");
puts(" -werror treat warnings as errors");
puts(" -fsanitize [address] turn on sanitiser");
puts(" -fastmath turn on fast math");
puts(" -g / -debug generate debug information");
puts(" -D / -define [key=value,key] add a preprocessor define ");
puts(" -std [c11,c17,clatest] select c standard (default: clatest)");
puts(" -cpp compile c++ instead of c");
exit(0);
}
optimise_level_e get_optimisation_level(strview_t arg) {
if (strv_equals(arg, strv("fast"))) {
return OPTIMISE_FAST;
}
else if (strv_equals(arg, strv("small"))) {
return OPTIMISE_SMALL;
}
warn("unrecognised optimisation level: (%v)", arg);
return OPTIMISE_NONE;
}
warning_level_e get_warning_level(strview_t arg) {
if (strv_equals(arg, strv("default"))) {
return WARNING_DEFAULT;
}
else if (strv_equals(arg, strv("all"))) {
return WARNING_ALL;
}
warn("unrecognised warning level: (%v)", arg);
return WARNING_NONE;
}
sanitiser_e get_sanitiser(strview_t arg) {
if (strv_equals(arg, strv("address"))) {
return SANITISER_ADDRESS;
}
warn("unrecognised sanitiser: (%v)", arg);
return SANITISER_NONE;
}
cversion_e get_cversion(strview_t arg) {
if (strv_equals(arg, strv("clatest"))) {
return CVERSION_LATEST;
}
else if (strv_equals(arg, strv("c17"))) {
return CVERSION_17;
}
else if (strv_equals(arg, strv("c11"))) {
return CVERSION_11;
}
warn("unrecognised c std version: (%v)", arg);
return CVERSION_LATEST;
}
options_t parse_options(arena_t *arena, int argc, char **argv) {
options_t out = {0};
for (int i = 1; i < argc; ++i) {
strview_t arg = strv(argv[i]);
#define CHECK_OPT_BEG() if (false) {}
#define CHECK_OPT1(opt) else if (strv_equals(arg, strv("-" opt)))
#define CHECK_OPT2(small, big) else if (strv_equals(arg, strv("-" small)) || strv_equals(arg, strv("-" big)))
#define GET_NEXT_ARG() (i + 1) < argc ? strv(argv[++i]) : STRV_EMPTY
CHECK_OPT_BEG()
CHECK_OPT2("h", "help") {
print_help_message();
}
CHECK_OPT2("o", "out") {
strview_t out_fname = GET_NEXT_ARG();
str_t out_fname_str = str_fmt(arena, "build/%v", out_fname);
out.out_fname = strv(out_fname_str);
}
CHECK_OPT2("O", "optimise") {
out.optimisation = get_optimisation_level(GET_NEXT_ARG());
}
CHECK_OPT2("w", "warning") {
out.warnings = get_warning_level(GET_NEXT_ARG());
}
CHECK_OPT1("werror") {
out.warnings_as_error = true;
}
CHECK_OPT1("fsanitize") {
out.sanitiser = get_sanitiser(GET_NEXT_ARG());
}
CHECK_OPT1("fastmath") {
out.fast_math = true;
}
CHECK_OPT2("g", "debug") {
out.debug = true;
}
CHECK_OPT2("D", "define") {
darr_push(arena, out.defines, GET_NEXT_ARG());
}
CHECK_OPT1("std") {
out.cstd = get_cversion(GET_NEXT_ARG());
}
CHECK_OPT1("cpp") {
out.is_cpp = true;
}
CHECK_OPT2("r", "run") {
out.run = true;
out.input_fname = GET_NEXT_ARG();
for (i += 1; i < argc; ++i) {
darr_push(arena, out.run_args, strv(argv[i]));
}
}
else {
out.input_fname = arg;
}
}
#undef CHECK_OPT_BEG
#undef CHECK_OPT1
#undef CHECK_OPT2
#undef GET_NEXT_ARG
if (strv_is_empty(out.out_fname)) {
strview_t name;
os_file_split_path(out.input_fname, NULL, &name, NULL);
str_t out_fname = str_fmt(arena, "build\\%v", name);
out.out_fname = strv(out_fname);
}
return out;
}
int main(int argc, char **argv) {
os_init();
arena_t arena = arena_make(ARENA_VIRTUAL, GB(1));
if (argc < 2) {
print_help_message();
}
options_t opt = parse_options(&arena, argc, argv);
if (!os_file_exists(strv("build/"))) {
info("creating build folder");
_mkdir("build");
}
if (!os_file_exists(strv("build/cache.ini"))) {
info("couldn't find cache.ini, creating it now");
arena_t scratch = arena;
str_t vcvars_path = find_vcvars_path(&scratch);
if (!os_run_cmd(scratch, os_make_cmd(strv(vcvars_path), strv("&&"), strv("set"), strv(">"), strv("build\\cache.ini")), NULL)) {
fatal("failed to run vcvars64.bat");
os_abort(1);
}
}
{
arena_t scratch = arena;
if (!load_cache(&scratch)) {
os_abort(1);
}
os_cmd_t *cmd = NULL;
darr_push(&scratch, cmd, strv("cl"));
darr_push(&scratch, cmd, strv("/nologo"));
darr_push(&scratch, cmd, strv("/utf-8"));
if (!opt.is_cpp) {
darr_push(&scratch, cmd, strv("/TC"));
}
str_t output = str_fmt(&scratch, "/Fe:%v.exe", opt.out_fname);
str_t object = str_fmt(&scratch, "/Fo:%v.obj", opt.out_fname);
darr_push(&scratch, cmd, strv(output));
darr_push(&scratch, cmd, strv(object));
strview_t optimisations[OPTIMISE__COUNT] = {
strv("/Od"), // disabled
strv("/O2"), // fast code
strv("/O1"), // small code
};
darr_push(&scratch, cmd, optimisations[opt.optimisation]);
strview_t warnings[WARNING__COUNT] = {
strv("/W0"),
strv("/W3"),
strv("/W4"),
};
darr_push(&scratch, cmd, warnings[opt.warnings]);
if (opt.warnings_as_error) {
darr_push(&scratch, cmd, strv("/WX"));
}
if (opt.sanitiser) {
strview_t sanitisers[SANITISER__COUNT] = {
strv(""),
strv("/fsanitize=address"),
};
darr_push(&scratch, cmd, sanitisers[opt.sanitiser]);
}
if (opt.fast_math) {
darr_push(&scratch, cmd, strv("/fp:fast"));
}
if (opt.debug) {
darr_push(&scratch, cmd, strv("/Zi"));
}
for_each (def, opt.defines) {
for (int i = 0; i < def->count; ++i) {
str_t define = str_fmt(&scratch, "/D%v", def->items[i]);
darr_push(&scratch, cmd, strv(define));
}
}
strview_t cversion[CVERSION__COUNT] = {
strv("clatest"),
strv("c17"),
strv("c11"),
};
str_t cstd = str_fmt(&scratch, "/std:%v", cversion[opt.cstd]);
darr_push(&scratch, cmd, strv(cstd));
darr_push(&scratch, cmd, opt.input_fname);
// /LD -> create dynamic lib
// /LDd -> create debug dynamic lib
// /link
if (!os_run_cmd(scratch, cmd, NULL)) {
return 1;
}
}
if (opt.run) {
arena_t scratch = arena;
os_cmd_t *cmd = NULL;
darr_push(&scratch, cmd, opt.out_fname);
for_each (arg, opt.run_args) {
for (int i = 0; i < arg->count; ++i) {
darr_push(&scratch, cmd, arg->items[i]);
}
}
if (!os_run_cmd(scratch, cmd, NULL)) {
return 1;
}
}
arena_cleanup(&arena);
os_cleanup();
}