Created
February 21, 2023 00:52
-
-
Save judah-caruso/622e7815c729fe1581a32699cf24545f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Simple program that treats a C/C++ source | |
file *as* the build script. This is the | |
step between: compiling a source file | |
directly, and creating a build script. | |
License: MIT | |
@build.cc clang | |
@build.out build | |
@build.std c89 | |
@build.arg -Wall -pedantic | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdarg.h> | |
#include <assert.h> | |
#define NAME "build" | |
#define VERSION "1a" | |
static const char* USAGE = ".. usage %s <build file>\n"; | |
static const char* HELP_HEADER = \ | |
".. version %s\n\n" | |
" flags\n" | |
" -help show a detailed help message\n" | |
" -version show this message\n\n" | |
; | |
static const char* HELP_BODY = \ | |
" Comments containing @build.[option] describe the build process.\n\n" | |
" options\n" | |
" cc which compiler to use (default: cc)\n" | |
" out the resulting binary or library name (default: a.out)\n" | |
" std which version of C/C++ to use (default: c99)\n" | |
" src source files needed (note: the build file is always passed first)\n" | |
" lib libraries to link against (note: '-l' is not needed)\n" | |
" arg arguments that are passed directly\n" | |
; | |
#define S(str) str.count, str.data | |
typedef struct { | |
char* data; | |
int count; | |
} string; | |
typedef struct { | |
enum { | |
C_DETECT, | |
C_CLANG, | |
C_GCC, | |
C_MSVC | |
} compiler; | |
char* file; /* The file containing build information. | |
Always passed as the first source file. */ | |
char* command_buf; | |
int command_buf_len; | |
int written; | |
string cc; | |
string out; | |
string std; | |
string src; | |
string lib; | |
string arg; | |
} Build_Info; | |
void | |
cmd_append(Build_Info* info, const char* format, ...) | |
{ | |
va_list(args); | |
va_start(args, format); | |
info->written += vsnprintf( | |
info->command_buf + info->written, | |
info->command_buf_len - info->written, | |
format, | |
args | |
); | |
va_end(args); | |
} | |
void | |
generate_clang_command(Build_Info* info) | |
{ | |
cmd_append(info, "%.*s ", S(info->cc)); | |
if (info->src.data) { | |
cmd_append(info, "%.*s ", S(info->src)); | |
} | |
cmd_append(info, "%s ", info->file); | |
if (info->out.data) { | |
cmd_append(info, "-o %.*s ", S(info->out)); | |
} | |
if (info->std.data) { | |
cmd_append(info, "-std=%.*s ", S(info->std)); | |
} | |
{ | |
char* chunk = info->lib.data; | |
int chunk_len = 0; | |
int i = 0; | |
for (i = 0; i < info->lib.count; i += 1) { | |
chunk_len += 1; | |
if (info->lib.data[i] == ' ' || i >= info->lib.count - 1) { | |
cmd_append(info, "-l%.*s ", chunk_len, chunk); | |
chunk += chunk_len; | |
chunk_len = 0; | |
continue; | |
} | |
} | |
} | |
cmd_append(info, "%.*s", S(info->arg)); | |
} | |
void | |
generate_gcc_command(Build_Info* info) | |
{ assert(0 && "TODO: GCC Command!"); } | |
void | |
generate_msvc_command(Build_Info* info) | |
{ assert(0 && "TODO: MSVC Command!"); } | |
int | |
main(int argc, char* argv[]) | |
{ | |
int ret = 0; | |
FILE* handle = NULL; | |
size_t total_read = 0; | |
char* file_mem = NULL; | |
char* file_name = NULL; | |
char* file_ext = NULL; | |
size_t file_size = 0; | |
int valid_ext = 0; | |
char* iter = NULL; | |
char* range = NULL; | |
Build_Info build = {0}; | |
build.cc.data = "cc"; | |
build.cc.count = 2; | |
build.std.data = "c99"; | |
build.std.count = 3; | |
build.out.data = "a.out"; | |
build.out.count = 5; | |
if (argc <= 1) { | |
printf(USAGE, NAME); | |
exit(0); | |
} | |
file_name = argv[1]; | |
if (strcmp(file_name, "-version") == 0) { | |
printf(USAGE, NAME); | |
printf(HELP_HEADER, VERSION, NAME); | |
exit(0); | |
} | |
else if (strcmp(file_name, "-help") == 0) { | |
printf(USAGE, NAME); | |
printf(HELP_HEADER, VERSION, NAME); | |
puts(HELP_BODY); | |
exit(0); | |
} | |
file_ext = strchr(file_name, '.'); | |
if (!file_ext) { | |
printf(".. invalid file '%s'\n", file_name); | |
exit(1); | |
} | |
valid_ext = | |
strcmp(file_ext, ".c") == 0 || | |
strcmp(file_ext, ".cpp") == 0 || | |
strcmp(file_ext, ".c++") == 0 || | |
strcmp(file_ext, ".cxx") == 0 || | |
strcmp(file_ext, ".h") == 0 || | |
strcmp(file_ext, ".hpp") == 0 || | |
strcmp(file_ext, ".h++") == 0 || | |
strcmp(file_ext, ".hxx") == 0; | |
if (!valid_ext) { | |
printf(".. invalid extension '%s'\n", file_name); | |
exit(2); | |
} | |
handle = fopen(file_name, "rb"); | |
if (!handle) { | |
printf(".. unable to open '%s'\n", file_name); | |
exit(3); | |
} | |
fseek(handle, 0, SEEK_END); | |
file_size = ftell(handle); | |
fseek(handle, 0, SEEK_SET); | |
file_mem = (char*)malloc(file_size * sizeof(char)); | |
total_read = fread(file_mem, sizeof(char), file_size * sizeof(char), handle); | |
if (total_read != file_size) { | |
printf(".. unable to read '%s'\n", file_name); | |
exit(4); | |
} | |
fclose(handle); | |
iter = file_mem; | |
build.file = file_name; | |
while (*iter++) { | |
if (*iter == '@') { | |
range = iter; | |
continue; | |
} | |
if (range && *iter == ' ') { | |
int len = (int)(iter - range); | |
char* rest = NULL; | |
int rest_len = 0; | |
/* skip whitespace */ | |
while (*iter++) if (*iter != ' ') break; | |
rest = iter; | |
while (*iter++) if (*iter == '\n') break; | |
rest_len = (int)(iter - rest); | |
if (strncmp("@build.cc", range, len) == 0) { | |
build.cc.data = rest; | |
build.cc.count = rest_len; | |
} | |
else if (strncmp("@build.out", range, len) == 0) { | |
build.out.data = rest; | |
build.out.count = rest_len; | |
} | |
else if (strncmp("@build.src", range, len) == 0) { | |
build.src.data = rest; | |
build.src.count = rest_len; | |
} | |
else if (strncmp("@build.std", range, len) == 0) { | |
build.std.data = rest; | |
build.std.count = rest_len; | |
} | |
else if (strncmp("@build.inc", range, len) == 0) { | |
build.src.data = rest; | |
build.src.count = rest_len; | |
} | |
else if (strncmp("@build.lib", range, len) == 0) { | |
build.lib.data = rest; | |
build.lib.count = rest_len; | |
} | |
else if (strncmp("@build.arg", range, len) == 0) { | |
build.arg.data = rest; | |
build.arg.count = rest_len; | |
} | |
range = NULL; | |
continue; | |
} | |
} | |
if (build.cc.data) { | |
if (strncmp("clang", build.cc.data, build.cc.count) == 0 || | |
strncmp("clang++", build.cc.data, build.cc.count) == 0) { | |
build.compiler = C_CLANG; | |
} | |
else if (strncmp("gcc", build.cc.data, build.cc.count) == 0 || | |
strncmp("g++", build.cc.data, build.cc.count) == 0) { | |
build.compiler = C_GCC; | |
} | |
else if (strncmp("cl", build.cc.data, build.cc.count) == 0) { | |
build.compiler = C_MSVC; | |
} | |
} | |
{ | |
#define SIZE 1024 * 1024 | |
char buffer[SIZE] = {0}; | |
build.command_buf = buffer; | |
build.command_buf_len = SIZE; | |
switch (build.compiler) { | |
case C_DETECT: { | |
assert(0 && "TODO: Detect what 'cc' is!"); | |
} break; | |
case C_CLANG: { | |
generate_clang_command(&build); | |
} break; | |
case C_GCC: { | |
generate_gcc_command(&build); | |
} break; | |
case C_MSVC: { | |
generate_msvc_command(&build); | |
} break; | |
} | |
printf(".. %s command: %s\n", NAME, buffer); | |
ret = system(buffer); | |
} | |
free(file_mem); | |
printf(".. %s exit: %d\n", NAME, ret); | |
return ret; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment