essence-os/util/build_common.h

444 lines
13 KiB
C

#define BINUTILS_VERSION "2.36.1"
#define GCC_VERSION "11.1.0"
// Duplicated in prefix.h.
#define SYSTEM_FOLDER_NAME "Essence"
#ifndef OS_ESSENCE
#include <sys/stat.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
typedef struct EsINIState {
char *buffer, *section, *key, *value;
size_t bytes, sectionBytes, keyBytes, valueBytes;
} EsINIState;
#include "../shared/ini.h"
#endif
#include "stb_ds.h"
#define INI_READ_BOOL(x, y) if (0 == strcmp(#x, s.key)) y = !!atoi(s.value)
#define INI_READ_STRING(x, y) if (0 == strcmp(#x, s.key)) snprintf(y, sizeof(y), "%s", s.value)
#define INI_READ_STRING_PTR(x, y) if (0 == strcmp(#x, s.key)) y = s.value
typedef struct ApplicationDependencyList {
char **files;
time_t timeStamp;
bool optimised;
} ApplicationDependencyList;
typedef struct ApplicationDependencies {
char *key;
ApplicationDependencyList value;
} ApplicationDependencies;
typedef struct FontFile {
const char *type;
const char *path;
bool required;
} FontFile;
typedef struct BuildFont {
const char *name;
const char *license;
const char *category;
const char *scripts;
FontFile *files;
} BuildFont;
ApplicationDependencies *applicationDependencies;
time_t buildStartTimeStamp;
bool _optimisationsEnabled;
bool forceRebuild;
uint64_t configurationHash;
void *LoadFile(const char *path, size_t *length) {
#ifdef OS_ESSENCE
size_t path2Bytes;
char *path2 = EsPOSIXConvertPath(path, &path2Bytes, true);
size_t bytes;
void *result = EsFileReadAll(path2, path2Bytes, &bytes, NULL);
EsHeapFree(path2, 0, NULL);
if (!result) return NULL;
void *buffer = malloc(bytes + 1);
memcpy(buffer, result, bytes);
EsHeapFree(result, 0, NULL);
((uint8_t *) buffer)[bytes] = 0;
if (length) *length = bytes;
return buffer;
#else
FILE *file = fopen(path, "rb");
if (!file) return NULL;
fseek(file, 0, SEEK_END);
size_t fileSize = ftell(file);
fseek(file, 0, SEEK_SET);
char *buffer = (char *) malloc(fileSize + 1);
buffer[fileSize] = 0;
fread(buffer, 1, fileSize, file);
fclose(file);
if (length) *length = fileSize;
return buffer;
#endif
}
bool IsStringEqual(const char *string, size_t stringBytes, const char *cLiteral) {
return stringBytes == strlen(cLiteral) && 0 == memcmp(string, cLiteral, stringBytes);
}
bool CheckDependencies(const char *applicationName) {
#ifdef OS_ESSENCE
(void) applicationName;
// TODO.
return true;
#else
bool needsRebuild = false;
ApplicationDependencyList dependencies = shget(applicationDependencies, applicationName);
if (!dependencies.timeStamp || dependencies.optimised != _optimisationsEnabled || forceRebuild) {
needsRebuild = true;
}
for (int i = 0; !needsRebuild && i < arrlen(dependencies.files); i++) {
struct stat s = { 0 };
if (stat(dependencies.files[i], &s) || s.st_mtime > dependencies.timeStamp) {
needsRebuild = true;
break;
}
}
if (needsRebuild) {
for (int i = 0; i < arrlen(dependencies.files); i++) {
free(dependencies.files[i]);
}
arrfree(dependencies.files);
(void) shdel(applicationDependencies, applicationName);
}
return needsRebuild;
#endif
}
void ParseDependencies(const char *dependencyFile, const char *applicationName, bool append) {
#ifdef OS_ESSENCE
(void) dependencyFile;
(void) applicationName;
(void) append;
// TODO.
#else
char *dependencies = (char *) LoadFile(dependencyFile, NULL);
if (!dependencies) {
return;
}
ApplicationDependencyList list = {};
if (append) {
list = shget(applicationDependencies, applicationName);
}
int i = 0;
while (dependencies[i++] != ':');
for (; dependencies[i]; i++) {
if (dependencies[i] == '/' || isalpha(dependencies[i])) {
int j = i; for (; dependencies[j] && !isspace(dependencies[j]); j++);
char *string = (char *) malloc(j - i + 1);
memcpy(string, dependencies + i, j - i);
string[j - i] = 0;
if (0 == memcmp(string, "/usr", 4)) {
// Assume system header files do not change.
} else {
arrput(list.files, string);
}
// printf("%s->%s\n", applicationName, string);
i = j - 1;
}
}
struct stat s;
if (stat(dependencyFile, &s) == 0) {
// printf("%s %ld %ld\n", applicationName, buildStartTimeStamp, s.st_mtime);
list.timeStamp = s.st_mtime;
if (buildStartTimeStamp < s.st_mtime) list.timeStamp = buildStartTimeStamp;
list.optimised = _optimisationsEnabled;
shput(applicationDependencies, applicationName, list);
}
free(dependencies);
#endif
}
void DependenciesListRead() {
EsINIState s = { .buffer = (char *) LoadFile(DEPENDENCIES_FILE, &s.bytes) };
char *start = s.buffer;
if (!start) return;
char *key = NULL;
ApplicationDependencyList list = {};
while (EsINIParse(&s)) {
EsINIZeroTerminate(&s);
if (0 == strcmp(s.section, "general")) {
if (0 == strcmp(s.key, "configuration_hash")) {
uint64_t previousConfigurationHash = strtoul(s.value, NULL, 10);
if (previousConfigurationHash != configurationHash) {
shfree(applicationDependencies);
sh_new_strdup(applicationDependencies);
return;
}
}
} else if (s.keyBytes) {
key = s.section;
}
if (0 == strcmp(s.key, "time_stamp")) {
list.timeStamp = strtoul(s.value, NULL, 10);
} else if (0 == strcmp(s.key, "file")) {
arrput(list.files, strdup(s.value));
}
if (!EsINIPeek(&s) || !s.keyBytes) {
if (key) {
shput(applicationDependencies, key, list);
}
key = NULL;
memset(&list, 0, sizeof(list));
}
}
free(start);
}
void DependenciesListWrite() {
#ifdef OS_ESSENCE
// TODO.
#else
FILE *f = fopen(DEPENDENCIES_FILE, "wb");
fprintf(f, "[general]\nconfiguration_hash=%lu\n", configurationHash);
for (uintptr_t i = 0; i < shlenu(applicationDependencies); i++) {
ApplicationDependencyList *list = &applicationDependencies[i].value;
fprintf(f, "[%s]\ntime_stamp=%lu\n", applicationDependencies[i].key, list->timeStamp);
for (uintptr_t j = 0; j < arrlenu(list->files); j++) {
fprintf(f, "file=%s\n", list->files[j]);
}
}
fclose(f);
#endif
}
typedef union OptionVariant {
bool b;
char *s;
} OptionVariant;
typedef struct Option {
const char *id;
#define OPTION_TYPE_BOOL (1)
#define OPTION_TYPE_STRING (2)
int type;
OptionVariant defaultState;
OptionVariant state;
bool useDefaultState;
const char *warning;
} Option;
Option options[] = {
{ "Driver.ACPI", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.ACPIThermal", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.AHCI", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.BGA", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.EssenceFS", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.Ext2", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.FAT", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.HDAudio", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.I8254x", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.IDE", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.ISO9660", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.NTFS", OPTION_TYPE_BOOL, { .b = false }, .warning = "The NTFS driver has not been updated to work with the new FS layer." },
{ "Driver.NVMe", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.Networking", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.PCI", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.PS2", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.Root", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.RTC", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.SVGA", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.USB", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.USBBulk", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.USBHID", OPTION_TYPE_BOOL, { .b = true } },
{ "Driver.xHCI", OPTION_TYPE_BOOL, { .b = true } },
{ "Flag.ENABLE_POSIX_SUBSYSTEM", OPTION_TYPE_BOOL, { .b = false } },
{ "Flag.DEBUG_BUILD", OPTION_TYPE_BOOL, { .b = true } },
{ "Flag.USE_SMP", OPTION_TYPE_BOOL, { .b = true } },
{ "Flag.PROFILE_DESKTOP_FUNCTIONS", OPTION_TYPE_BOOL, { .b = false } },
{ "Flag.BGA_RESOLUTION_WIDTH", OPTION_TYPE_STRING, { .s = "1600" } },
{ "Flag.BGA_RESOLUTION_HEIGHT", OPTION_TYPE_STRING, { .s = "900" } },
{ "Flag.VGA_TEXT_MODE", OPTION_TYPE_BOOL, { .b = false } },
{ "Flag.CHECK_FOR_NOT_RESPONDING", OPTION_TYPE_BOOL, { .b = true } },
{ "Flag._ALWAYS_USE_VBE", OPTION_TYPE_BOOL, { .b = false } },
{ "Flag.COM_OUTPUT", OPTION_TYPE_BOOL, { .b = true } },
{ "Flag.POST_PANIC_DEBUGGING", OPTION_TYPE_BOOL, { .b = false } },
{ "Flag.START_DEBUG_OUTPUT", OPTION_TYPE_BOOL, { .b = false } },
{ "Flag.PAUSE_ON_USERLAND_CRASH", OPTION_TYPE_BOOL, { .b = false } },
{ "Dependency.ACPICA", OPTION_TYPE_BOOL, { .b = true } },
{ "Dependency.stb_image", OPTION_TYPE_BOOL, { .b = true } },
{ "Dependency.stb_image_write", OPTION_TYPE_BOOL, { .b = true } },
{ "Dependency.stb_sprintf", OPTION_TYPE_BOOL, { .b = true } },
{ "Dependency.FreeTypeAndHarfBuzz", OPTION_TYPE_BOOL, { .b = true } },
{ "Emulator.AHCI", OPTION_TYPE_BOOL, { .b = true } },
{ "Emulator.ATA", OPTION_TYPE_BOOL, { .b = false } },
{ "Emulator.NVMe", OPTION_TYPE_BOOL, { .b = false }, .warning = "Recent versions of Qemu have trouble booting from NVMe drives." },
{ "Emulator.CDROMImage", OPTION_TYPE_STRING, { .s = NULL } },
{ "Emulator.USBImage", OPTION_TYPE_STRING, { .s = NULL } },
{ "Emulator.USBHostVendor", OPTION_TYPE_STRING, { .s = NULL } },
{ "Emulator.USBHostProduct", OPTION_TYPE_STRING, { .s = NULL } },
{ "Emulator.RunWithSudo", OPTION_TYPE_BOOL, { .b = false } },
{ "Emulator.Audio", OPTION_TYPE_BOOL, { .b = false } },
{ "Emulator.MemoryMB", OPTION_TYPE_STRING, { .s = "1024" } },
{ "Emulator.Cores", OPTION_TYPE_STRING, { .s = "1" } },
{ "Emulator.PrimaryDriveMB", OPTION_TYPE_STRING, { .s = "1024" } },
{ "Emulator.SecondaryDriveMB", OPTION_TYPE_STRING, { .s = NULL } },
{ "Emulator.VBoxEFI", OPTION_TYPE_BOOL, { .b = false } },
{ "Emulator.QemuEFI", OPTION_TYPE_BOOL, { .b = false } },
{ "Emulator.SerialToFile", OPTION_TYPE_BOOL, { .b = true } },
{ "BuildCore.Verbose", OPTION_TYPE_BOOL, { .b = false } },
{ "BuildCore.NoImportPOSIX", OPTION_TYPE_BOOL, { .b = false } },
{ "BuildCore.RequiredFontsOnly", OPTION_TYPE_BOOL, { .b = false } },
{ "General.first_application", OPTION_TYPE_STRING, { .s = NULL } },
{ "General.wallpaper", OPTION_TYPE_STRING, { .s = NULL } },
{ "General.window_color", OPTION_TYPE_STRING, { .s = "2" } },
{ "General.installation_state", OPTION_TYPE_STRING, { .s = "0" } },
{ "General.ui_scale", OPTION_TYPE_STRING, { .s = "100" } },
{ "General.keyboard_layout", OPTION_TYPE_STRING, { .s = "us" } },
};
char *previousOptionsBuffer;
void LoadDefaultOptions() {
for (uintptr_t i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
options[i].state = options[i].defaultState;
options[i].useDefaultState = true;
if (options[i].type == OPTION_TYPE_STRING && options[i].state.s) {
options[i].state.s = strdup(options[i].state.s);
}
}
}
void LoadOptions() {
free(previousOptionsBuffer);
EsINIState s = { .buffer = (char *) LoadFile("bin/config.ini", &s.bytes) };
previousOptionsBuffer = s.buffer;
LoadDefaultOptions();
while (s.buffer && EsINIParse(&s)) {
EsINIZeroTerminate(&s);
for (uintptr_t i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
if (options[i].id && 0 == strcmp(options[i].id, s.key)) {
if (options[i].type == OPTION_TYPE_BOOL) {
options[i].state.b = atoi(s.value);
} else if (options[i].type == OPTION_TYPE_STRING) {
options[i].state.s = strdup(s.value);
} else {
// TODO.
}
options[i].useDefaultState = false;
break;
}
}
}
}
bool IsOptionEnabled(const char *name) {
for (uintptr_t i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
if (options[i].id && 0 == strcmp(options[i].id, name)) {
return options[i].state.b;
}
}
return false;
}
const char *GetOptionString(const char *name) {
for (uintptr_t i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
if (options[i].id && 0 == strcmp(options[i].id, name)) {
return options[i].state.s;
}
}
return "";
}
bool IsDriverEnabled(const char *name) {
for (uintptr_t i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
if (options[i].id && 0 == memcmp(options[i].id, "Driver.", 7) && 0 == strcmp(options[i].id + 7, name)) {
return options[i].state.b;
}
}
return false;
}
#ifdef PARALLEL_BUILD
volatile uint8_t spinnerRunning;
pthread_t spinnerThread;
void *SpinnerThread(void *_unused) {
(void) _unused;
char spinner[4] = { '/', '-', '\\', '|' };
int index = 0;
fflush(stdout);
do {
fprintf(stderr, "\033[0;36m[%c]\033[0m Compiling...\n", spinner[index]);
index = (index + 1) % 4;
struct timespec sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_nsec = 1000000 * 200;
nanosleep(&sleepTime, NULL);
fprintf(stderr, "\033[A");
} while (__sync_val_compare_and_swap(&spinnerRunning, 2, 2) /* use sync function to silence thread sanitizer warning */);
fprintf(stderr, "\033[K");
return NULL;
}
void StopSpinner() {
if (spinnerThread) {
__sync_fetch_and_sub(&spinnerRunning, 1);
pthread_join(spinnerThread, NULL);
spinnerThread = 0;
}
}
void StartSpinner() {
__sync_fetch_and_add(&spinnerRunning, 1);
pthread_create(&spinnerThread, NULL, SpinnerThread, NULL);
}
#endif