// This file is part of the Essence operating system. // It is released under the terms of the MIT license -- see LICENSE.md. // Written by: nakst. // TODO Better configuration over what files are imported to the drive image. // TODO Resetting after system builds. #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #ifdef OS_ESSENCE #define ES_CRT_WITHOUT_PREFIX #include #include #define MSG_BUILD_SUCCESS (ES_MSG_USER_START + 1) #define MSG_BUILD_FAILED (ES_MSG_USER_START + 2) #define MSG_LOG (ES_MSG_USER_START + 3) bool logSendMessages; typedef struct File { bool error, ready; EsHandle handle; EsFileOffset offset; } File; #define FileSeek(file, _offset) (file.offset = _offset) #define FileRead(file, size, buffer) (_FileRead(file.handle, &file.offset, size, buffer)) #define FileWrite(file, size, buffer) (_FileWrite(file.handle, &file.offset, size, buffer)) #define FileClose(file) EsHandleClose(file.handle) #define FilePrintFormat(file, ...) (_FilePrintFormat(&file, __VA_ARGS__)) void Log(const char *format, ...) { va_list arguments; va_start(arguments, format); char buffer[4096]; size_t bytes = EsCRTvsnprintf(buffer, sizeof(buffer), format, arguments); va_end(arguments); if (bytes >= sizeof(buffer) - 1) { buffer[sizeof(buffer) - 1] = 0; bytes = sizeof(buffer) - 1; } if (logSendMessages) { EsMessage m; m.type = MSG_LOG; char *copy = EsHeapAllocate(bytes + 1, false, NULL); EsCRTstrcpy(copy, buffer); m.user.context1.p = copy; EsMessagePost(NULL, &m); } else { EsPOSIXSystemCall(SYS_write, (intptr_t) 1, (intptr_t) buffer, (intptr_t) bytes, 0, 0, 0); } } File FileOpen(const char *path, char mode) { size_t path2Bytes; char *path2 = EsPOSIXConvertPath(path, &path2Bytes, true); EsFileInformation information = EsFileOpen(path2, path2Bytes, mode == 'r' ? (ES_FILE_READ | ES_NODE_FAIL_IF_NOT_FOUND) : ES_FILE_WRITE); EsHeapFree(path2, 0, NULL); if (information.error != ES_SUCCESS) return (File) { .error = true }; if (mode == 'w') EsFileResize(information.handle, 0); return (File) { .ready = true, .handle = information.handle }; } EsFileOffset _FileRead(EsHandle handle, EsFileOffset *offset, size_t bytes, void *buffer) { bytes = EsFileReadSync(handle, *offset, bytes, buffer); if (bytes > 0) *offset += bytes; return bytes; } EsFileOffset _FileWrite(EsHandle handle, EsFileOffset *offset, size_t bytes, void *buffer) { bytes = EsFileWriteSync(handle, *offset, bytes, buffer); if (bytes > 0) *offset += bytes; return bytes; } void _FilePrintFormat(File *file, const char *format, ...) { va_list arguments; va_start(arguments, format); char buffer[4096]; size_t bytes = EsCRTvsnprintf(buffer, sizeof(buffer), format, arguments); EsAssert(bytes < sizeof(buffer)); va_end(arguments); FileWrite((*file), bytes, buffer); } #define _exit(x) EsPOSIXSystemCall(SYS_exit_group, (intptr_t) x, 0, 0, 0, 0, 0) #define close(x) EsPOSIXSystemCall(SYS_close, (intptr_t) x, 0, 0, 0, 0, 0) #define dup2(x, y) EsPOSIXSystemCall(SYS_dup2, (intptr_t) x, (intptr_t) y, 0, 0, 0, 0) #define execve(x, y, z) EsPOSIXSystemCall(SYS_execve, (intptr_t) x, (intptr_t) y, (intptr_t) z, 0, 0, 0) #define exit(x) EsPOSIXSystemCall(SYS_exit_group, (intptr_t) x, 0, 0, 0, 0, 0) #define pipe(x) EsPOSIXSystemCall(SYS_pipe, (intptr_t) x, 0, 0, 0, 0, 0) #define read(x, y, z) EsPOSIXSystemCall(SYS_read, (intptr_t) x, (intptr_t) y, (intptr_t) z, 0, 0, 0) #define rename(x, y) EsPOSIXSystemCall(SYS_rename, (intptr_t) x, (intptr_t) y, 0, 0, 0, 0) #define truncate(x, y) EsPOSIXSystemCall(SYS_truncate, (intptr_t) x, (intptr_t) y, 0, 0, 0, 0) #define unlink(x) EsPOSIXSystemCall(SYS_unlink, (intptr_t) x, 0, 0, 0, 0, 0) #define vfork() EsPOSIXSystemCall(SYS_vfork, 0, 0, 0, 0, 0, 0) #define wait4(x, y, z, w) EsPOSIXSystemCall(SYS_wait4, (intptr_t) x, (intptr_t) y, (intptr_t) z, (intptr_t) w, 0, 0) typedef uint64_t pid_t; typedef uint64_t time_t; time_t time(time_t *timer) { (void) timer; return 0; } // TODO. #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PARALLEL_BUILD #include #endif typedef struct File { bool error, ready; FILE *f; } File; File FileOpen(const char *path, char mode) { FILE *f = NULL; if (mode == 'r') f = fopen(path, "rb"); if (mode == 'w') f = fopen(path, "wb"); if (mode == '+') f = fopen(path, "r+b"); return (File) { .error = f == NULL, .ready = f != NULL, .f = f }; } #define FileSeek(file, offset) (fseek(file.f, offset, SEEK_SET)) #define FileRead(file, size, buffer) (fread(buffer, 1, size, file.f)) #define FileWrite(file, size, buffer) (fwrite(buffer, 1, size, file.f)) #define FileClose(file) fclose(file.f) #define FilePrintFormat(file, ...) fprintf(file.f, __VA_ARGS__) #define Log(...) fprintf(stderr, __VA_ARGS__) #define EsFileOffset uint64_t #endif #define EsFSError() exit(1) #include "../shared/crc.h" #include "../shared/partitions.cpp" #include "build_common.h" #include "../shared/esfs2.h" #include "header_generator.c" // Toolchain flags: const char *commonCompileFlagsFreestanding = " -ffreestanding -fno-exceptions "; char commonCompileFlags[4096] = " -Wall -Wextra -Wno-missing-field-initializers -Wno-frame-address -Wno-unused-function -Wno-format-truncation " " -g -I. -Iroot/Applications/POSIX/include -fdiagnostics-column-unit=byte "; char commonCompileFlagsWithCStdLib[4096]; char cCompileFlags[4096] = ""; char cppCompileFlags[4096] = " -std=c++14 -Wno-pmf-conversions -Wno-invalid-offsetof -fno-rtti "; char kernelCompileFlags[4096] = " -fno-omit-frame-pointer "; char applicationLinkFlags[4096] = " -ffreestanding -nostdlib -lgcc -g -z max-page-size=0x1000 "; char apiLinkFlags1[4096] = " -T util/linker/api64.ld -ffreestanding -nostdlib -g -z max-page-size=0x1000 -Wl,--start-group "; // TODO Don't hardcode the target. char apiLinkFlags2[4096] = " -lgcc "; char apiLinkFlags3[4096] = " -Wl,--end-group -Lroot/Applications/POSIX/lib "; char kernelLinkFlags[4096] = " -ffreestanding -nostdlib -lgcc -g -z max-page-size=0x1000 "; char commonAssemblyFlags[4096] = " -Fdwarf "; const char *desktopProfilingFlags = ""; // Specific configuration options: bool verbose; bool useColoredOutput; bool forEmulator, bootUseVBE, noImportPOSIX; bool systemBuild; bool hasNativeToolchain; EsINIState *fontLines; EsINIState *generalOptions; // State: char *builtinModules; volatile uint8_t encounteredErrors; volatile uint8_t encounteredErrorsInKernelModules; size_t singleConfigFileBytes; void *singleConfigFileData; bool singleConfigFileUse; ////////////////////////////////// #define COLOR_ERROR "\033[0;33m" #define COLOR_HIGHLIGHT "\033[0;36m" #define COLOR_NORMAL "\033[0m" const char *toolchainAR = "/Applications/POSIX/bin/ar"; const char *toolchainCC = "/Applications/POSIX/bin/gcc"; const char *toolchainCXX = "/Applications/POSIX/bin/g++"; const char *toolchainLD = "/Applications/POSIX/bin/ld"; const char *toolchainNM = "/Applications/POSIX/bin/nm"; const char *toolchainStrip = "/Applications/POSIX/bin/strip"; const char *toolchainNasm = "/Applications/POSIX/bin/nasm"; const char *toolchainConvertSVG = "/Applications/POSIX/bin/render_svg"; const char *toolchainLinkerScripts = "/Applications/POSIX/lib"; const char *toolchainCRTObjects = "/Applications/POSIX/lib"; const char *toolchainCompilerObjects = "/Applications/POSIX/lib/gcc/x86_64-essence/" GCC_VERSION; const char *target = "x86_64"; // TODO Don't hard code the target. char *executeEnvironment[3] = { (char *) "PATH=/Applications/POSIX/bin", (char *) "TMPDIR=/Applications/POSIX/tmp", NULL, }; #define Execute(...) _Execute(NULL, __VA_ARGS__, NULL, NULL) #define ExecuteWithOutput(...) _Execute(__VA_ARGS__, NULL, NULL) #define ExecuteForApp(application, ...) if (!application->error && ExecuteWithOutput(&application->output, __VA_ARGS__)) application->error = true #define ArgString(x) NULL, x int _Execute(char **output, const char *executable, ...) { char *argv[64]; char *copies[64]; size_t copyCount = 0; va_list argList; va_start(argList, executable); argv[0] = (char *) executable; for (uintptr_t i = 1; i <= 64; i++) { assert(i != 64); argv[i] = va_arg(argList, char *); if (!argv[i]) { char *string = va_arg(argList, char *); if (!string) { break; } char *copy = (char *) malloc(strlen(string) + 2); assert(copyCount != 64); copies[copyCount++] = copy; strcpy(copy, string); strcat(copy, " "); uintptr_t start = 0; bool inQuotes = false; for (uintptr_t j = 0; copy[j]; j++) { assert(i != 64); if ((!inQuotes && copy[j] == ' ') || !copy[j]) { if (start != j) { argv[i] = copy + start; if (argv[i][0] == '"') argv[i]++; i++; } if (j && copy[j - 1] == '"') copy[j - 1] = 0; copy[j] = 0; start = j + 1; } else if (copy[j] == '"') { inQuotes = !inQuotes; } } i--; } } va_end(argList); if (verbose) { for (uintptr_t i = 0; i < 64; i++) { if (!argv[i]) break; Log("\"%s\" ", argv[i]); } Log("\n"); } int stdoutPipe[2]; if (output) { pipe(stdoutPipe); } int status = -1; pid_t pid = vfork(); if (pid == 0) { if (output) { dup2(stdoutPipe[1], 1); dup2(stdoutPipe[1], 2); close(stdoutPipe[1]); } execve(executable, argv, executeEnvironment); _exit(-1); } else if (pid > 0) { if (output) { close(stdoutPipe[1]); while (true) { char buffer[1024]; intptr_t bytesRead = read(stdoutPipe[0], buffer, 1024); if (bytesRead <= 0) { break; } else { size_t previousLength = arrlenu(*output); arrsetlen(*output, previousLength + bytesRead); memcpy(*output + previousLength, buffer, bytesRead); } } } wait4(-1, &status, 0, NULL); } else { Log("Error: could not vfork process.\n"); exit(1); } if (output) { close(stdoutPipe[0]); } if (verbose && status) { Log("(status = %d)\n", status); } for (uintptr_t i = 0; i < copyCount; i++) { free(copies[i]); } if (status) __sync_fetch_and_or(&encounteredErrors, 1); return status; } void MakeDirectory(const char *path) { #ifdef OS_ESSENCE EsPOSIXSystemCall(SYS_mkdir, (intptr_t) path, 0, 0, 0, 0, 0); #else mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); #endif } void DeleteFile(const char *path) { unlink(path); } void CopyFile(const char *oldPath, const char *newPath, bool quietError) { File source = FileOpen(oldPath, 'r'); File destination = FileOpen(newPath, 'w'); if (source.error) { if (quietError) return; Log("Error: Could not open file '%s' as a copy source.\n", oldPath); __sync_fetch_and_or(&encounteredErrors, 1); return; } if (destination.error) { if (quietError) return; Log("Error: Could not open file '%s' as a copy destination.\n", newPath); __sync_fetch_and_or(&encounteredErrors, 1); return; } char buffer[4096]; while (true) { size_t bytesRead = FileRead(source, sizeof(buffer), buffer); if (!bytesRead) break; FileWrite(destination, bytesRead, buffer); } FileClose(source); FileClose(destination); } void MoveFile(const char *oldPath, const char *newPath) { rename(oldPath, newPath); } bool FileExists(const char *path) { #ifdef OS_ESSENCE size_t path2Bytes; char *path2 = EsPOSIXConvertPath(path, &path2Bytes, true); bool exists = EsPathExists(path2, path2Bytes, NULL); EsHeapFree(path2, 0, NULL); return exists; #else struct stat s; return !stat(path, &s); #endif } #ifndef OS_ESSENCE EsUniqueIdentifier MatchFileContentType(const char *pathBuffer) { FILE *f = fopen(pathBuffer, "rb"); assert(f); uint32_t elfSignature; fread(&elfSignature, 1, sizeof(elfSignature), f); bool isELFExecutable = elfSignature == 0x464C457F; fclose(f); EsUniqueIdentifier t = { 0 }; #define ENDS_WITH(s) (strlen(pathBuffer) >= strlen(s) && 0 == strcmp(pathBuffer + strlen(pathBuffer) - strlen(s), s)) #define STARTS_WITH(s) (strlen(pathBuffer) >= strlen(s) && 0 == memcmp(pathBuffer, s, strlen(s))) if (ENDS_WITH(".bochsrc")) t = (EsUniqueIdentifier) {{ 0xFF, 0x92, 0xF0, 0x53, 0x28, 0x83, 0xDC, 0xF1, 0xBA, 0xDD, 0xDE, 0x79, 0x92, 0x33, 0x3F, 0x73 }}; else if (ENDS_WITH(".build_core")) t = (EsUniqueIdentifier) {{ 0x6F, 0x2A, 0x23, 0x16, 0xAF, 0xA3, 0xD0, 0xFB, 0x95, 0x06, 0x52, 0xB3, 0x67, 0xFB, 0x37, 0xFA }}; else if (ENDS_WITH(".designer")) t = (EsUniqueIdentifier) {{ 0x26, 0x4A, 0xE0, 0x6C, 0x0F, 0xE8, 0x2C, 0xE7, 0xF4, 0x6E, 0x4E, 0x76, 0x5B, 0x4A, 0xCF, 0x4F }}; else if (ENDS_WITH(".uxn")) t = (EsUniqueIdentifier) {{ 0x76, 0xC2, 0xC1, 0x73, 0xB1, 0x20, 0x3D, 0x42, 0x70, 0xFD, 0xD4, 0xBD, 0x66, 0xE9, 0x2A, 0x39 }}; else if (ENDS_WITH(".a")) t = (EsUniqueIdentifier) {{ 0x7D, 0x39, 0xBF, 0x18, 0x9E, 0x07, 0xBC, 0xD3, 0x4F, 0x9F, 0x87, 0xEC, 0x6F, 0x70, 0x65, 0x5D }}; else if (ENDS_WITH(".la")) t = (EsUniqueIdentifier) {{ 0x8F, 0x10, 0x4F, 0x33, 0x4A, 0xE5, 0x2D, 0xA7, 0x2A, 0x80, 0x2C, 0x4F, 0xEA, 0xE4, 0x99, 0xB1 }}; else if (ENDS_WITH(".esx")) t = (EsUniqueIdentifier) {{ 0xBF, 0x88, 0xE4, 0xDD, 0x71, 0xE8, 0x5F, 0xDE, 0x16, 0xC5, 0xAF, 0x33, 0x41, 0xC7, 0xA2, 0x96 }}; else if (ENDS_WITH(".o")) t = (EsUniqueIdentifier) {{ 0xB5, 0x19, 0xF2, 0x0D, 0xAD, 0x4F, 0xD4, 0xC9, 0xA4, 0xBF, 0x97, 0xE4, 0x5E, 0x4C, 0x55, 0x00 }}; else if (ENDS_WITH(".c")) t = (EsUniqueIdentifier) {{ 0x36, 0x02, 0xD3, 0xAC, 0x6C, 0xDE, 0x8D, 0x31, 0xFA, 0x70, 0xB2, 0xDA, 0xFA, 0x81, 0x53, 0x69 }}; else if (ENDS_WITH(".cpp")) t = (EsUniqueIdentifier) {{ 0x35, 0x84, 0xA6, 0x0E, 0x52, 0x28, 0xBA, 0xAB, 0xCA, 0x74, 0x14, 0xF4, 0xE1, 0x15, 0xCA, 0x5F }}; else if (ENDS_WITH(".h")) t = (EsUniqueIdentifier) {{ 0xD1, 0x16, 0xC4, 0xC1, 0x7B, 0xC0, 0xED, 0x4F, 0xCA, 0xAC, 0x18, 0x05, 0x32, 0x1D, 0x56, 0x32 }}; else if (ENDS_WITH(".html")) t = (EsUniqueIdentifier) {{ 0x4A, 0x5A, 0x1C, 0xE5, 0xEE, 0x08, 0x6E, 0x04, 0x13, 0x9C, 0x6D, 0x05, 0x48, 0x5C, 0xE5, 0xD2 }}; else if (ENDS_WITH(".in")) t = (EsUniqueIdentifier) {{ 0x76, 0x35, 0xF3, 0xF5, 0xC2, 0x69, 0x8A, 0xA4, 0xE4, 0x68, 0x5F, 0xE5, 0x2C, 0x73, 0x10, 0xF9 }}; else if (ENDS_WITH("/Makefile")) t = (EsUniqueIdentifier) {{ 0x3A, 0x58, 0x08, 0x24, 0x00, 0x75, 0xB5, 0x8B, 0xA8, 0x39, 0xD8, 0x37, 0x03, 0x6B, 0x20, 0x85 }}; else if (ENDS_WITH(".ld") || STARTS_WITH("root/Applications/POSIX/x86_64-essence/lib/ldscripts/")) t = (EsUniqueIdentifier) {{ 0x23, 0x56, 0xBC, 0xD4, 0x5A, 0xD6, 0x56, 0x08, 0x06, 0x61, 0x7C, 0x33, 0x38, 0x66, 0xD5, 0x1F }}; else if (ENDS_WITH(".md")) t = (EsUniqueIdentifier) {{ 0xB1, 0xC3, 0x9E, 0x56, 0xC9, 0xB0, 0x85, 0xD6, 0xAF, 0x98, 0x12, 0x1C, 0x2E, 0x29, 0x8D, 0x24 }}; else if (ENDS_WITH(".pc")) t = (EsUniqueIdentifier) {{ 0x74, 0x72, 0x01, 0x17, 0x97, 0x4B, 0x6B, 0x4B, 0x74, 0xCD, 0x18, 0x7C, 0x8F, 0x3C, 0x58, 0xBC }}; else if (ENDS_WITH(".py")) t = (EsUniqueIdentifier) {{ 0x77, 0x19, 0xBA, 0x87, 0x2E, 0xDB, 0x51, 0xA4, 0x71, 0x5D, 0xFF, 0x8D, 0x39, 0x86, 0x96, 0xF0 }}; else if (ENDS_WITH(".sh")) t = (EsUniqueIdentifier) {{ 0x66, 0x42, 0x88, 0xF9, 0xD1, 0x68, 0x47, 0xBF, 0x7F, 0x48, 0x82, 0x77, 0x2B, 0xE3, 0x39, 0xBA }}; else if (ENDS_WITH(".txt") || ENDS_WITH("/README") || ENDS_WITH("/LICENSE") || ENDS_WITH("/COPYING") || ENDS_WITH("/AUTHORS") || ENDS_WITH("/TODO") || ENDS_WITH("/BUGS") || ENDS_WITH("/NEWS") || ENDS_WITH("/CHANGES") || ENDS_WITH("/ReadMe") || ENDS_WITH(".LESSER")) t = (EsUniqueIdentifier) {{ 0x25, 0x65, 0x4C, 0x89, 0xE7, 0x29, 0xEA, 0x9E, 0xB5, 0xBE, 0xB5, 0xCA, 0xA7, 0x60, 0xBD, 0x3D }}; else if (ENDS_WITH(".1") || ENDS_WITH(".7") || ENDS_WITH(".info") || ENDS_WITH(".info-1") || ENDS_WITH(".info-2")) t = (EsUniqueIdentifier) {{ 0xDB, 0x72, 0xDD, 0x5D, 0x92, 0x54, 0x09, 0x1A, 0xC4, 0x16, 0xDD, 0x89, 0x0B, 0x13, 0x12, 0x1F }}; else if (ENDS_WITH(".conf") || ENDS_WITH(".bfd")) t = (EsUniqueIdentifier) {{ 0x18, 0x8D, 0xD3, 0xAC, 0x35, 0xF5, 0xD3, 0x01, 0x8C, 0x66, 0xFD, 0x12, 0xE1, 0xED, 0xE2, 0x6F }}; else if (ENDS_WITH(".ini")) t = (EsUniqueIdentifier) {{ 0x81, 0x72, 0x43, 0x29, 0xE5, 0x37, 0x89, 0x51, 0x8E, 0x22, 0xA0, 0x67, 0xA5, 0xB9, 0x42, 0x2C }}; else if (ENDS_WITH(".gz")) t = (EsUniqueIdentifier) {{ 0x7D, 0x93, 0x66, 0x74, 0x80, 0x0B, 0x64, 0x9F, 0x16, 0x5D, 0x44, 0xA5, 0x45, 0xFF, 0x80, 0xBF }}; else if (ENDS_WITH(".img")) t = (EsUniqueIdentifier) {{ 0xE1, 0x61, 0x4D, 0x0D, 0x32, 0xEC, 0xE0, 0x0F, 0x36, 0x7F, 0xE9, 0x7B, 0x3F, 0x16, 0x43, 0x02 }}; else if (ENDS_WITH(".jpg")) t = (EsUniqueIdentifier) {{ 0xD8, 0xC2, 0x13, 0xB0, 0x53, 0x64, 0x82, 0x11, 0x48, 0x7B, 0x5B, 0x64, 0x0F, 0x92, 0xB9, 0x38 }}; else if (ENDS_WITH(".mkv")) t = (EsUniqueIdentifier) {{ 0x0D, 0xCA, 0x26, 0x92, 0x22, 0x44, 0xEB, 0x6B, 0xC6, 0xE9, 0x6C, 0x8E, 0xFC, 0x28, 0xD2, 0x8E }}; else if (ENDS_WITH(".obj")) t = (EsUniqueIdentifier) {{ 0xF7, 0x81, 0xE9, 0x21, 0xDF, 0x89, 0x77, 0xD0, 0xC6, 0x97, 0xA8, 0x63, 0xF1, 0xF3, 0x4F, 0x63 }}; else if (ENDS_WITH(".png")) t = (EsUniqueIdentifier) {{ 0x59, 0x21, 0x05, 0x4D, 0x34, 0x40, 0xAB, 0x61, 0xEC, 0xF9, 0x7D, 0x5C, 0x6E, 0x04, 0x96, 0xAE }}; else if (ENDS_WITH(".ttf")) t = (EsUniqueIdentifier) {{ 0xDA, 0xBF, 0xEC, 0x06, 0x31, 0x8A, 0x67, 0xB6, 0xE3, 0x95, 0xBC, 0xD1, 0x92, 0xD2, 0x9A, 0x56 }}; else if (STARTS_WITH("root/Applications/POSIX/share/bochs/")) t = (EsUniqueIdentifier) {{ 0xB5, 0x32, 0x22, 0x14, 0x01, 0xCB, 0xD0, 0x1D, 0xA2, 0x55, 0x08, 0xC7, 0x0D, 0x46, 0x86, 0x53 }}; else if (isELFExecutable) t = (EsUniqueIdentifier) {{ 0xAB, 0xDE, 0x98, 0xB5, 0x56, 0x2C, 0x04, 0xDF, 0x1E, 0x43, 0xC8, 0x10, 0x24, 0x63, 0xDB, 0xB8 }}; #undef ENDS_WITH #undef STARTS_WITH return t; } #endif void CreateImportNode(const char *path, ImportNode *node) { char pathBuffer[256]; #ifdef OS_ESSENCE size_t path2Bytes; char *path2 = EsPOSIXConvertPath(path, &path2Bytes, true); EsError error; uintptr_t childCount; EsDirectoryChild *children = EsDirectoryEnumerate(path2, path2Bytes, &childCount, &error); EsAssert(error == ES_SUCCESS); EsHeapFree(path2, 0, NULL); for (uintptr_t i = 0; i < childCount; i++) { snprintf(pathBuffer, sizeof(pathBuffer), "%s/%.*s", path, (int) children[i].nameBytes, children[i].name); ImportNode child = {}; if (noImportPOSIX && 0 == strcmp(pathBuffer, "root/Applications/POSIX")) { continue; } else if (children[i].type == ES_NODE_DIRECTORY) { CreateImportNode(pathBuffer, &child); } else { child.isFile = true; child.contentType = children[i].contentType; } child.name = EsStringAllocateAndFormat(NULL, "%s", children[i].nameBytes, children[i].name); child.path = strdup(pathBuffer); arrput(node->children, child); } EsHeapFree(children, 0, NULL); #else DIR *d = opendir(path); struct dirent *dir; if (!d) { return; } while ((dir = readdir(d))) { if (dir->d_name[0] == '.') { continue; } snprintf(pathBuffer, sizeof(pathBuffer), "%s/%s", path, dir->d_name); struct stat s = {}; lstat(pathBuffer, &s); ImportNode child = {}; if (noImportPOSIX && 0 == strcmp(pathBuffer, "root/Applications/POSIX")) { continue; } else if (S_ISDIR(s.st_mode)) { CreateImportNode(pathBuffer, &child); } else if ((s.st_mode & S_IFMT) == S_IFLNK) { continue; } else { child.isFile = true; child.contentType = MatchFileContentType(pathBuffer); } child.name = strdup(dir->d_name); child.path = strdup(pathBuffer); arrput(node->children, child); } closedir(d); #endif } ////////////////////////////////// typedef struct BundleInput { const char *path; const char *name; uint64_t alignment; } BundleInput; bool MakeBundle(const char *outputFile, BundleInput *inputFiles, size_t inputFileCount, uint64_t mapAddress) { File output = FileOpen(outputFile, 'w'); if (output.error) { Log("Error: Could not open output file '%s'.\n", outputFile); __sync_fetch_and_or(&encounteredErrors, 1); return false; } uint32_t signature = 0x63BDAF45; FileWrite(output, sizeof(uint32_t), &signature); uint32_t version = 1; FileWrite(output, sizeof(uint32_t), &version); uint32_t fileCount = inputFileCount; FileWrite(output, sizeof(uint32_t), &fileCount); uint32_t zero = 0; FileWrite(output, sizeof(uint32_t), &zero); FileWrite(output, sizeof(uint64_t), &mapAddress); for (uintptr_t i = 0; i < fileCount; i++) { uint64_t zero = 0; FileWrite(output, sizeof(uint64_t), &zero); FileWrite(output, sizeof(uint64_t), &zero); FileWrite(output, sizeof(uint64_t), &zero); } uint64_t outputPosition = 24 + fileCount * 24; for (uintptr_t i = 0; i < fileCount; i++) { FileSeek(output, 24 + i * 24); const char *nameString = inputFiles[i].name; outputPosition = (outputPosition + inputFiles[i].alignment - 1) & ~(inputFiles[i].alignment - 1); uint64_t name = CalculateCRC64(nameString, strlen(nameString), 0); FileWrite(output, sizeof(uint64_t), &name); size_t size; void *buffer = LoadFile(inputFiles[i].path, &size); if (!buffer) { Log("Error: Could not open input file '%s'.\n", inputFiles[i].path); __sync_fetch_and_or(&encounteredErrors, 1); return false; } if (size > 0xFFFFFFFF) { Log("Error: Input file '%s' too large (max: 4GB).\n", inputFiles[i].path); __sync_fetch_and_or(&encounteredErrors, 1); return false; } FileWrite(output, sizeof(uint64_t), &size); FileWrite(output, sizeof(uint64_t), &outputPosition); FileSeek(output, outputPosition); FileWrite(output, size, buffer); outputPosition += size; free(buffer); } FileClose(output); return true; } ////////////////////////////////// typedef struct DependencyFile { char path[256]; const char *name; } DependencyFile; typedef struct Application { char *manifest; const char *name; EsINIState *properties; EsINIState *fileTypeLines; int id; bool install, builtin, withCStdLib; const char **sources; const char *compileFlags; const char *linkFlags; const char *customCompileCommand; const char *manifestPath; DependencyFile *dependencyFiles; char *output; bool error, skipped; void (*buildCallback)(struct Application *); // Called on a build thread. BundleInput *bundleInputFiles; } Application; int nextID = 1; Application *applications; const char **kernelModules; #define ADD_BUNDLE_INPUT(_path, _name, _alignment) do { \ BundleInput bundleInputFile = {}; \ bundleInputFile.path = _path; \ bundleInputFile.name = _name; \ bundleInputFile.alignment = _alignment; \ arrput(application->bundleInputFiles, bundleInputFile); \ } while (0) void BuildDesktop(Application *application) { char buffer[4096]; snprintf(buffer, sizeof(buffer), "arch/%s/api.s", target); ExecuteForApp(application, toolchainNasm, buffer, "-MD", "bin/dependency_files/api1.d", "-o", "bin/Object Files/api1.o", ArgString(commonAssemblyFlags)); ExecuteForApp(application, toolchainCXX, "-MD", "-MF", "bin/dependency_files/api2.d", "-c", "desktop/api.cpp", "-o", "bin/Object Files/api2.o", ArgString(commonCompileFlags), ArgString(desktopProfilingFlags)); ExecuteForApp(application, toolchainCXX, "-MD", "-MF", "bin/dependency_files/api3.d", "-c", "desktop/posix.cpp", "-o", "bin/Object Files/api3.o", ArgString(commonCompileFlags)); ExecuteForApp(application, toolchainCC, "-o", "bin/Desktop", "bin/Object Files/crti.o", "bin/Object Files/crtbegin.o", "bin/Object Files/api1.o", "bin/Object Files/api2.o", "bin/Object Files/api3.o", "bin/Object Files/crtend.o", "bin/Object Files/crtn.o", ArgString(apiLinkFlags1), ArgString(apiLinkFlags2), ArgString(apiLinkFlags3)); ExecuteForApp(application, toolchainStrip, "-o", "bin/Stripped Executables/Desktop", "--strip-all", "bin/Desktop"); for (uintptr_t i = 0; i < arrlenu(fontLines); i++) { if ((fontLines[i].key[0] == '.' || 0 == strcmp(fontLines[i].key, "license")) && fontLines[i].value[0]) { snprintf(buffer, sizeof(buffer), "res/Fonts/%s", fontLines[i].value); ADD_BUNDLE_INPUT(strdup(buffer), fontLines[i].value, 16); } } EsINIState s = {}; s.buffer = (char *) LoadFile("res/Keyboard Layouts/index.ini", &s.bytes); while (EsINIParse(&s)) { EsINIZeroTerminate(&s); if (s.key[0] != ';') { char in[128]; char name[128]; snprintf(in, sizeof(in), "res/Keyboard Layouts/%s.dat", s.key); snprintf(name, sizeof(name), "Keyboard Layouts/%s.dat", s.key); ADD_BUNDLE_INPUT(strdup(in), strdup(name), 16); } } ADD_BUNDLE_INPUT("res/Keyboard Layouts/index.ini", "Keyboard Layouts.ini", 16); ADD_BUNDLE_INPUT("res/Keyboard Layouts/License.txt", "Keyboard Layouts License.txt", 16); ADD_BUNDLE_INPUT("res/Theme.dat", "Theme.dat", 16); ADD_BUNDLE_INPUT("res/elementary Icons.dat", "Icons.dat", 16); ADD_BUNDLE_INPUT("res/elementary Icons License.txt", "Icons License.txt", 16); ADD_BUNDLE_INPUT("res/Cursors.png", "Cursors.png", 16); ADD_BUNDLE_INPUT("bin/Stripped Executables/Desktop", "$Executables/x86_64", 0x1000); // TODO Don't hardcode the target. if (!application->error) { application->error = !MakeBundle("root/" SYSTEM_FOLDER_NAME "/Desktop.esx", application->bundleInputFiles, arrlenu(application->bundleInputFiles), 0); } } void BuildApplication(Application *application) { char symbolFile[256]; char objectFiles[4096]; char strippedFile[256]; char executable[256]; char linkerScript[256]; char crti[256]; char crtbegin[256]; char crtend[256]; char crtn[256]; size_t objectFilesPosition = 0; snprintf(symbolFile, sizeof(symbolFile), "bin/%s", application->name); snprintf(strippedFile, sizeof(strippedFile), "bin/Stripped Executables/%s", application->name); snprintf(linkerScript, sizeof(linkerScript), "%s/linker/userland64.ld", toolchainLinkerScripts); // TODO Don't hardcode the target. snprintf(crti, sizeof(crti), "%s/crti.o", toolchainCRTObjects); snprintf(crtbegin, sizeof(crtbegin), "%s/crtbegin.o", toolchainCRTObjects); snprintf(crtend, sizeof(crtend), "%s/crtend.o", toolchainCRTObjects); snprintf(crtn, sizeof(crtn), "%s/crtn.o", toolchainCRTObjects); if (systemBuild) { snprintf(executable, sizeof(executable), "root/Applications/%s.esx", application->name); } else { snprintf(executable, sizeof(executable), "bin/%s.esx", application->name); } if (application->customCompileCommand) { #ifdef OS_ESSENCE // TODO. #else application->error = system(application->customCompileCommand); ExecuteForApp(application, toolchainStrip, "-o", executable, "--strip-all", symbolFile); #endif } else { for (uintptr_t i = 0; i < arrlenu(application->sources); i++) { const char *source = application->sources[i]; size_t sourceBytes = strlen(source); char objectFile[256], dependencyFile[256]; snprintf(objectFile, sizeof(objectFile), "bin/Object Files/%s_%d.o", application->name, (int) i); snprintf(dependencyFile, sizeof(dependencyFile), "bin/dependency_files/%s_%d.d", application->name, (int) i); objectFilesPosition += sprintf(objectFiles + objectFilesPosition, "\"%s\" ", objectFile); bool isC = sourceBytes > 2 && source[sourceBytes - 1] == 'c' && source[sourceBytes - 2] == '.'; const char *cstdlibFlags = application->withCStdLib ? commonCompileFlagsWithCStdLib : commonCompileFlags; const char *languageFlags = isC ? cCompileFlags : cppCompileFlags; const char *compiler = isC ? toolchainCC : toolchainCXX; ExecuteForApp(application, compiler, "-MD", "-MF", dependencyFile, "-o", objectFile, "-c", source, ArgString(languageFlags), ArgString(application->compileFlags), ArgString(cstdlibFlags)); } assert(objectFilesPosition < sizeof(objectFiles)); objectFiles[objectFilesPosition] = 0; if (application->withCStdLib) { ExecuteForApp(application, toolchainCC, "-o", symbolFile, ArgString(objectFiles), ArgString(application->linkFlags)); } else { ExecuteForApp(application, toolchainCC, "-o", symbolFile, "-Wl,--start-group", ArgString(application->linkFlags), crti, crtbegin, ArgString(objectFiles), crtend, crtn, "-Wl,--end-group", ArgString(applicationLinkFlags), "-T", linkerScript); } ExecuteForApp(application, toolchainStrip, "-o", strippedFile, "--strip-all", symbolFile); ADD_BUNDLE_INPUT(strippedFile, "$Executables/x86_64", 0x1000); // TODO Don't hardcode the target. // Convert any files for the bundle marked with a '!'. for (uintptr_t i = 0; i < arrlenu(application->bundleInputFiles); i++) { const char *path = application->bundleInputFiles[i].path; if (path[0] == '!') { if (strlen(path) > 5 && 0 == memcmp(path + strlen(path) - 4, ".svg", 4)) { char output[256]; snprintf(output, sizeof(output), "bin/temp_%d_%d", application->id, (int) i); ExecuteForApp(application, toolchainConvertSVG, "convert", path + 1, output); application->bundleInputFiles[i].path = output; } else { char buffer[256]; snprintf(buffer, sizeof(buffer), "Error: Unknown embed convertion file type '%s'.\n", path); size_t previousLength = arrlenu(application->output); arrsetlen(application->output, previousLength + strlen(buffer)); memcpy(application->output + previousLength, buffer, strlen(buffer)); application->error = true; } } } if (!application->error) { application->error = !MakeBundle(executable, application->bundleInputFiles, arrlenu(application->bundleInputFiles), 0); } } } #ifdef PARALLEL_BUILD volatile uintptr_t applicationsIndex = 0; void *BuildApplicationThread(void *_unused) { while (true) { uintptr_t i = __sync_fetch_and_add(&applicationsIndex, 1); if (i >= arrlenu(applications)) { return NULL; } if (applications[i].skipped) continue; applications[i].buildCallback(&applications[i]); } } #endif void ParseApplicationManifest(const char *manifestPath) { EsINIState s = {}; char *manifest; if (singleConfigFileUse) { manifest = s.buffer = malloc(singleConfigFileBytes); s.bytes = singleConfigFileBytes; memcpy(s.buffer, singleConfigFileData, singleConfigFileBytes); } else { manifest = s.buffer = (char *) LoadFile(manifestPath, &s.bytes); } if (!manifest) { return; } const char *require = ""; bool needsNativeToolchain = false; bool disabled = false; Application application = {}; application.manifest = manifest; application.id = nextID++; application.manifestPath = manifestPath; application.compileFlags = ""; application.linkFlags = ""; while (EsINIParse(&s)) { EsINIZeroTerminate(&s); if (0 == strcmp(s.section, "build")) { if (0 == strcmp("source", s.key)) { arrput(application.sources, s.value); } INI_READ_STRING_PTR(compile_flags, application.compileFlags); INI_READ_STRING_PTR(link_flags, application.linkFlags); INI_READ_STRING_PTR(custom_compile_command, application.customCompileCommand); INI_READ_BOOL(with_cstdlib, application.withCStdLib); INI_READ_STRING_PTR(require, require); } else if (0 == strcmp(s.section, "general")) { INI_READ_STRING_PTR(name, application.name); else INI_READ_BOOL(disabled, disabled); else INI_READ_BOOL(needs_native_toolchain, needsNativeToolchain); else if (s.keyBytes && s.valueBytes) arrput(application.properties, s); } else if (0 == strcmp(s.section, "file_type")) { arrput(application.fileTypeLines, s); } else if (0 == strcmp(s.section, "embed") && s.key[0] != ';' && s.value[0]) { BundleInput input = { 0 }; input.path = s.value; input.name = s.key; input.alignment = 1; arrput(application.bundleInputFiles, input); } } if (disabled || (require[0] && !FileExists(require)) || (needsNativeToolchain && !hasNativeToolchain) || !application.name) { return; } for (uintptr_t i = 0; i < arrlenu(application.sources); i++) { DependencyFile dependencyFile = {}; dependencyFile.name = application.name; snprintf(dependencyFile.path, sizeof(dependencyFile.path), "bin/dependency_files/%s_%d.d", application.name, (int) i); arrput(application.dependencyFiles, dependencyFile); } application.buildCallback = BuildApplication; application.install = true; arrput(applications, application); } void OutputSystemConfiguration() { File file = FileOpen("root/" SYSTEM_FOLDER_NAME "/Default.ini", 'w'); FilePrintFormat(file, "\n[paths]\n" "fonts=0:/" SYSTEM_FOLDER_NAME "/Fonts\n" "temporary=0:/" SYSTEM_FOLDER_NAME "/Temporary\n" "default_settings=0:/" SYSTEM_FOLDER_NAME "/Settings\n" "\n[ui_fonts]\n" "fallback=Bitmap Sans\n" "sans=Inter\n" "serif=Inter\n" "mono=Hack\n"); FilePrintFormat(file, "\n[general]\nnext_id=%d\n", nextID); for (uintptr_t i = 0; i < arrlenu(generalOptions); i++) { char buffer[4096]; FileWrite(file, EsINIFormat(generalOptions + i, buffer, sizeof(buffer)), buffer); } for (uintptr_t i = 0; i < arrlenu(applications); i++) { if (!applications[i].install) { continue; } FilePrintFormat(file, "\n[application]\n"); FilePrintFormat(file, "id=%d\n", applications[i].id); FilePrintFormat(file, "name=%s\n", applications[i].name); FilePrintFormat(file, "executable=0:/Applications/%s.esx\n", applications[i].name); FilePrintFormat(file, "settings_path=0:/" SYSTEM_FOLDER_NAME "/Settings/%s\n", applications[i].name); for (uintptr_t j = 0; j < arrlenu(applications[i].properties); j++) { FilePrintFormat(file, "%s=%s\n", applications[i].properties[j].key, applications[i].properties[j].value); } for (uintptr_t j = 0; j < arrlenu(applications[i].fileTypeLines); j++) { char buffer[4096]; FileWrite(file, EsINIFormat(applications[i].fileTypeLines + j, buffer, sizeof(buffer)), buffer); if (!applications[i].fileTypeLines[j].keyBytes) { FilePrintFormat(file, "application=%d\n", applications[i].id); } } } for (uintptr_t i = 0; i < arrlenu(fontLines); i++) { char buffer[4096]; if (fontLines[i].key[0] == '.') { FilePrintFormat(file, "%s=:%s\n", fontLines[i].key, fontLines[i].value); } else { size_t bytes = EsINIFormat(fontLines + i, buffer, sizeof(buffer)); FileWrite(file, bytes, buffer); } } FileClose(file); } void BuildModule(Application *application) { char output[256], dependencyFile[256]; snprintf(output, sizeof(output), "bin/Object Files/%s.ekm", application->name); snprintf(dependencyFile, sizeof(dependencyFile), "bin/dependency_files/%s.d", application->name); assert(arrlenu(application->sources) == 1); ExecuteForApp(application, toolchainCXX, "-MD", "-MF", dependencyFile, "-c", application->sources[0], "-o", output, ArgString(cppCompileFlags), ArgString(kernelCompileFlags), ArgString(commonCompileFlags), application->builtin ? "-DBUILTIN_MODULE" : "-DKERNEL_MODULE"); if (!application->builtin) { char target[4096]; snprintf(target, sizeof(target), "root/" SYSTEM_FOLDER_NAME "/Modules/%s.ekm", application->name); MakeDirectory("root/" SYSTEM_FOLDER_NAME "/Modules"); MoveFile(output, target); } if (application->error) __sync_fetch_and_or(&encounteredErrorsInKernelModules, 1); } bool IsModuleEnabled(const char *name, size_t nameBytes) { for (uintptr_t i = 0; i < arrlenu(kernelModules); i++) { if (IsStringEqual(name, nameBytes, kernelModules[i])) { return true; } } return false; } void ParseKernelConfiguration() { if (!CheckDependencies("Kernel Config")) { return; } size_t kernelConfigBytes; char *kernelConfig = (char *) LoadFile("kernel/config.ini", &kernelConfigBytes); File f = FileOpen("bin/generated_code/kernel_config.h", 'w'); EsINIState s = {}; s.buffer = (char *) kernelConfig; s.bytes = kernelConfigBytes; char *moduleName = NULL, *parentName = NULL, *dataStart = s.buffer; size_t moduleNameBytes = 0, parentNameBytes = 0; bool builtin = false; bool foundMatchingArchitecture = false, anyArchitecturesListed = false; while (EsINIParse(&s)) { if (!IsStringEqual(s.section, s.sectionBytes, "driver")) continue; if (IsStringEqual(s.key, s.keyBytes, "name")) moduleName = s.value, moduleNameBytes = s.valueBytes; if (!EsINIPeek(&s) || !s.keyBytes) FilePrintFormat(f, "extern \"C\" KDriver driver%.*s;\n", (int) moduleNameBytes, moduleName); } FilePrintFormat(f, "#ifdef K_IN_CORE_KERNEL\n"); FilePrintFormat(f, "const KInstalledDriver builtinDrivers[] = {\n"); s.buffer = (char *) kernelConfig; s.bytes = kernelConfigBytes; while (EsINIParse(&s)) { if (!IsStringEqual(s.section, s.sectionBytes, "driver")) { continue; } if (IsStringEqual(s.key, s.keyBytes, "name")) { moduleName = s.value, moduleNameBytes = s.valueBytes; } if (IsStringEqual(s.key, s.keyBytes, "parent")) { parentName = s.value, parentNameBytes = s.valueBytes; } if (IsStringEqual(s.key, s.keyBytes, "builtin")) { builtin = s.valueBytes && s.value[0] == '1'; } if (!foundMatchingArchitecture && IsStringEqual(s.key, s.keyBytes, "arch")) { anyArchitecturesListed = true; if (IsStringEqual(s.value, s.valueBytes, "x86_common") || IsStringEqual(s.value, s.valueBytes, "x86_64")) { // TODO Don't hardcode the target. foundMatchingArchitecture = true; } } char *dataEnd = s.buffer; if (!EsINIPeek(&s) || !s.keyBytes) { if ((foundMatchingArchitecture || !anyArchitecturesListed) && IsModuleEnabled(moduleName, moduleNameBytes)) { FilePrintFormat(f, "\t{\n"); FilePrintFormat(f, "\t\t.name = (char *) \"%.*s\",\n", (int) moduleNameBytes, moduleName); FilePrintFormat(f, "\t\t.nameBytes = %d,\n", (int) moduleNameBytes); FilePrintFormat(f, "\t\t.parent = (char *) \"%.*s\",\n", (int) parentNameBytes, parentName); FilePrintFormat(f, "\t\t.parentBytes = %d,\n", (int) parentNameBytes); FilePrintFormat(f, "\t\t.config = (char *) R\"(%.*s)\",\n", (int) (dataEnd - dataStart), dataStart); FilePrintFormat(f, "\t\t.configBytes = %d,\n", (int) (dataEnd - dataStart)); FilePrintFormat(f, "\t\t.builtin = %d,\n", builtin); FilePrintFormat(f, "\t\t.loadedDriver = &driver%.*s,\n", (int) moduleNameBytes, moduleName); FilePrintFormat(f, "\t},\n"); } moduleName = parentName = NULL; moduleNameBytes = parentNameBytes = 0; builtin = false; foundMatchingArchitecture = anyArchitecturesListed = false; dataStart = dataEnd; } } FilePrintFormat(f, "};\n"); FilePrintFormat(f, "#endif"); FileClose(f); f = FileOpen("bin/dependency_files/system_config.d", 'w'); FilePrintFormat(f, ": kernel/config.ini\n"); FileClose(f); ParseDependencies("bin/dependency_files/system_config.d", "Kernel Config", false); DeleteFile("bin/dependency_files/system_config.d"); } void LinkKernel() { if (encounteredErrorsInKernelModules) { return; } arrput(builtinModules, 0); if (Execute(toolchainLD, "-r", "bin/Object Files/kernel.o", "bin/Object Files/kernel_arch.o", ArgString(builtinModules), "-o" "bin/Object Files/kernel_all.o")) { return; } { char *output = NULL; if (_Execute(&output, toolchainNM, "bin/Object Files/kernel_all.o", NULL, NULL)) { return; } else { File f = FileOpen("bin/generated_code/kernel_symbols.h", 'w'); uintptr_t lineStart = 0, position = 0; while (position < arrlenu(output)) { if (output[position] == '\n') { output[position] = 0; const char *line = output + lineStart; const char *t = strstr(line, " T "); if (t) { FilePrintFormat(f, "{ (void *) 0x%.*s, \"%s\" },\n", (int) (t - line), line, t + 3); } lineStart = position + 1; } position++; } FileClose(f); Execute(toolchainCXX, "-c", "kernel/symbols.cpp", "-o", "bin/Object Files/kernel_symbols.o", ArgString(cppCompileFlags), ArgString(kernelCompileFlags), ArgString(commonCompileFlags)); } } if (Execute(toolchainCXX, "-o", "bin/Kernel", "bin/Object Files/kernel_symbols.o", "bin/Object Files/kernel_all.o", ArgString(kernelLinkFlags))) { return; } Execute(toolchainStrip, "-o", "bin/Stripped Executables/Kernel", "--strip-all", "bin/Kernel"); CopyFile("bin/Stripped Executables/Kernel", "root/" SYSTEM_FOLDER_NAME "/Kernel.esx", false); } void BuildKernel(Application *application) { char buffer[4096]; snprintf(buffer, sizeof(buffer), "arch/%s/kernel.s", target); ExecuteForApp(application, toolchainNasm, "-MD", "bin/dependency_files/kernel2.d", buffer, "-o", "bin/Object Files/kernel_arch.o", ArgString(commonAssemblyFlags)); snprintf(buffer, sizeof(buffer), "-DARCH_KERNEL_SOURCE=", target); ExecuteForApp(application, toolchainCXX, "-MD", "-MF", "bin/dependency_files/kernel.d", "-c", "kernel/main.cpp", "-o", "bin/Object Files/kernel.o", ArgString(kernelCompileFlags), ArgString(cppCompileFlags), ArgString(commonCompileFlags), buffer); if (application->error) __sync_fetch_and_or(&encounteredErrorsInKernelModules, 1); } void BuildBootloader(Application *application) { ExecuteForApp(application, toolchainNasm, "-MD", "bin/dependency_files/boot1.d", "-fbin", forEmulator ? "boot/x86/mbr.s" : "boot/x86/mbr-emu.s" , "-obin/mbr"); ExecuteForApp(application, toolchainNasm, "-MD", "bin/dependency_files/boot2.d", "-fbin", "boot/x86/esfs-stage1.s", "-obin/stage1"); ExecuteForApp(application, toolchainNasm, "-MD", "bin/dependency_files/boot3.d", "-fbin", "boot/x86/loader.s", "-obin/stage2", "-Pboot/x86/esfs-stage2.s", (forEmulator && !bootUseVBE) ? "" : "-D BOOT_USE_VBE"); ExecuteForApp(application, toolchainNasm, "-MD", "bin/dependency_files/boot4.d", "-fbin", "boot/x86/uefi_loader.s", "-obin/uefi_loader"); } File _drive; uint64_t _partitionOffset; bool ReadBlock(uint64_t block, uint64_t count, void *buffer) { FileSeek(_drive, block * blockSize + _partitionOffset); // printf("read of block %ld\n", block); if (FileRead(_drive, blockSize * count, buffer) != blockSize * count) { Log("Error: Could not read blocks %d->%d of drive.\n", (int) block, (int) (block + count)); exit(1); } return true; } bool WriteBlock(uint64_t block, uint64_t count, void *buffer) { FileSeek(_drive, block * blockSize + _partitionOffset); assert(block < 4294967296); if (FileWrite(_drive, blockSize * count, buffer) != blockSize * count) { Log("Error: Could not write to blocks %d->%d of drive.\n", (int) block, (int) (block + count)); exit(1); } return true; } bool WriteBytes(uint64_t offset, uint64_t count, void *buffer) { FileSeek(_drive, offset + _partitionOffset); if (FileWrite(_drive, count, buffer) != count) { Log("Error: Could not write to bytes %d->%d of drive.\n", (int) offset, (int) (offset + count)); exit(1); } return true; } void Install(const char *driveFile, uint64_t partitionSize, const char *partitionLabel) { Log("Installing...\n"); EsUniqueIdentifier installationIdentifier; #ifndef OS_ESSENCE srand(time(NULL)); #endif for (int i = 0; i < 16; i++) { installationIdentifier.d[i] = rand(); } File iid = FileOpen("bin/iid.dat", 'w'); FileWrite(iid, 16, &installationIdentifier); FileClose(iid); File f = FileOpen(driveFile, 'w'); File mbr = FileOpen("bin/mbr", 'r'); char mbrBuffer[446] = {}; FileRead(mbr, 446, mbrBuffer); FileWrite(f, 446, mbrBuffer); FileClose(mbr); uint32_t partitions[16] = { 0x80 /* bootable */, 0x83 /* type */, 0x800 /* offset */, (uint32_t) ((partitionSize / 0x200) - 0x800) /* sector count */ }; uint16_t bootSignature = 0xAA55; MBRFixPartition(partitions); FileWrite(f, 64, partitions); FileWrite(f, 2, &bootSignature); void *blank = calloc(1, 0x800 * 0x200 - 0x200); FileWrite(f, 0x800 * 0x200 - 0x200, blank); free(blank); File stage1 = FileOpen("bin/stage1", 'r'); char stage1Buffer[0x200] = {}; FileRead(stage1, 0x200, stage1Buffer); FileWrite(f, 0x200, stage1Buffer); FileClose(stage1); File stage2 = FileOpen("bin/stage2", 'r'); char stage2Buffer[0x200 * 15] = {}; if (sizeof(stage2Buffer) == FileRead(stage2, sizeof(stage2Buffer), stage2Buffer)) { Log("Error: Stage 2 bootloader too large. Must fit in 7.5KB.\n"); exit(1); } FileWrite(f, sizeof(stage2Buffer), stage2Buffer); FileClose(stage2); FileClose(f); size_t kernelBytes; void *kernel = LoadFile("bin/Stripped Executables/Kernel", &kernelBytes); if (truncate(driveFile, partitionSize)) { Log("Error: Could not change the file's size to %d bytes.\n", (int) partitionSize); exit(1); } _drive = FileOpen(driveFile, '+'); _partitionOffset = 1048576; Format(partitionSize - _partitionOffset, partitionLabel, installationIdentifier, kernel, kernelBytes); Log("Copying files to the drive... "); ImportNode root = {}; CreateImportNode("root", &root); MountVolume(); Import(root, superblock.root); UnmountVolume(); Log("(%u MB)\n", (unsigned) (copiedCount / 1048576)); FileClose(_drive); } ////////////////////////////////// #ifdef OS_ESSENCE int BuildCore(int argc, char **argv) { #else int main(int argc, char **argv) { #endif if (argc < 2 && !singleConfigFileUse) { Log("Usage: build_core \n"); return 1; } char **applicationManifests = NULL; bool skipCompile = false; bool skipHeaderGeneration = false; bool minimalRebuild = false; bool withoutKernel = false; #ifdef PARALLEL_BUILD size_t threadCount = 1; #endif const char *driveFile = NULL; uint64_t partitionSize = 0; const char *partitionLabel = "New Volume"; char *driverSource = NULL, *driverName = NULL; bool driverBuiltin = false; if (singleConfigFileUse) { arrput(applicationManifests, "$SingleConfigFile"); } else if (0 == strcmp(argv[1], "standard")) { if (argc != 3) { Log("Usage: standard \n"); return 1; } EsINIState s = {}; s.buffer = (char *) LoadFile(argv[2], &s.bytes); if (!s.buffer) { Log("Error: could not load configuration file '%s'.\n", argv[2]); return 1; } while (EsINIParse(&s)) { EsINIZeroTerminate(&s); if (0 == strcmp(s.section, "toolchain")) { if (0 == strcmp(s.key, "path")) { executeEnvironment[0] = (char *) malloc(5 + s.valueBytes + 1); strcpy(executeEnvironment[0], "PATH="); strcat(executeEnvironment[0], s.value); } else if (0 == strcmp(s.key, "tmpdir")) { if (s.value[0]) { executeEnvironment[1] = (char *) malloc(7 + s.valueBytes + 1); strcpy(executeEnvironment[1], "TMPDIR="); strcat(executeEnvironment[1], s.value); } else { executeEnvironment[1] = NULL; } } else if (0 == strcmp(s.key, "ar")) { toolchainAR = s.value; } else if (0 == strcmp(s.key, "cc")) { toolchainCC = s.value; } else if (0 == strcmp(s.key, "cxx")) { toolchainCXX = s.value; } else if (0 == strcmp(s.key, "ld")) { toolchainLD = s.value; } else if (0 == strcmp(s.key, "nm")) { toolchainNM = s.value; } else if (0 == strcmp(s.key, "strip")) { toolchainStrip = s.value; } else if (0 == strcmp(s.key, "nasm")) { toolchainNasm = s.value; } else if (0 == strcmp(s.key, "convert_svg")) { toolchainConvertSVG = s.value; } else if (0 == strcmp(s.key, "linker_scripts")) { toolchainLinkerScripts = s.value; } else if (0 == strcmp(s.key, "crt_objects")) { toolchainCRTObjects = s.value; } else if (0 == strcmp(s.key, "compiler_objects")) { toolchainCompilerObjects = s.value; } } else if (0 == strcmp(s.section, "application")) { if (0 == strcmp(s.key, "manifest")) { arrput(applicationManifests, s.value); } } else if (0 == strcmp(s.section, "options")) { if (0 == strcmp(s.key, "Dependency.ACPICA") && atoi(s.value)) { strcat(kernelLinkFlags, " -lacpica -Lports/acpica "); strcat(kernelCompileFlags, " -DUSE_ACPICA "); } else if (0 == strcmp(s.key, "Dependency.stb_image") && atoi(s.value)) { strcat(commonCompileFlags, " -DUSE_STB_IMAGE "); } else if (0 == strcmp(s.key, "Dependency.stb_image_write") && atoi(s.value)) { strcat(commonCompileFlags, " -DUSE_STB_IMAGE_WRITE "); } else if (0 == strcmp(s.key, "Dependency.stb_sprintf") && atoi(s.value)) { strcat(commonCompileFlags, " -DUSE_STB_SPRINTF "); } else if (0 == strcmp(s.key, "Dependency.FreeTypeAndHarfBuzz") && atoi(s.value)) { strcat(apiLinkFlags2, " -lharfbuzz -lfreetype "); strcat(commonCompileFlags, " -DUSE_FREETYPE_AND_HARFBUZZ "); } else if (0 == strcmp(s.key, "Flag._ALWAYS_USE_VBE")) { bootUseVBE = !!atoi(s.value); } else if (0 == strcmp(s.key, "Flag.COM_OUTPUT") && atoi(s.value)) { strcat(commonAssemblyFlags, " -DCOM_OUTPUT "); strcat(commonCompileFlags, " -DCOM_OUTPUT "); } else if (0 == strcmp(s.key, "Flag.PROFILE_DESKTOP_FUNCTIONS") && atoi(s.value)) { desktopProfilingFlags = "-finstrument-functions"; } else if (0 == strcmp(s.key, "BuildCore.NoImportPOSIX")) { noImportPOSIX = !!atoi(s.value); } else if (0 == memcmp(s.key, "General.", 8)) { EsINIState s2 = s; s2.key += 8, s2.keyBytes -= 8; arrput(generalOptions, s2); } } else if (0 == strcmp(s.section, "general")) { if (0 == strcmp(s.key, "skip_compile")) { skipCompile = !!atoi(s.value); } else if (0 == strcmp(s.key, "skip_header_generation")) { skipHeaderGeneration = !!atoi(s.value); } else if (0 == strcmp(s.key, "verbose")) { verbose = !!atoi(s.value); } else if (0 == strcmp(s.key, "for_emulator")) { forEmulator = !!atoi(s.value); } else if (0 == strcmp(s.key, "system_build")) { systemBuild = !!atoi(s.value); } else if (0 == strcmp(s.key, "minimal_rebuild")) { minimalRebuild = !!atoi(s.value); } else if (0 == strcmp(s.key, "optimise")) { if (atoi(s.value)) { strcat(commonCompileFlags, " -O2 "); } } else if (0 == strcmp(s.key, "colored_output")) { if (atoi(s.value)) { strcat(commonCompileFlags, " -fdiagnostics-color=always "); useColoredOutput = true; } } else if (0 == strcmp(s.key, "common_compile_flags")) { strcat(commonCompileFlags, s.value); } else if (0 == strcmp(s.key, "thread_count")) { #ifndef PARALLEL_BUILD Log("Warning: thread_count not supported.\n"); #else threadCount = atoi(s.value); if (threadCount < 1) threadCount = 1; if (threadCount > 100) threadCount = 100; #endif } else if (0 == strcmp(s.key, "without_kernel")) { withoutKernel = atoi(s.value); } else if (0 == strcmp(s.key, "target")) { target = s.value; char buffer[4096]; snprintf(buffer, sizeof(buffer), "arch/%s/config.ini", target); EsINIState s2 = {}; s2.buffer = (char *) LoadFile(buffer, &s2.bytes); if (!s2.buffer) { Log("Error: could not load target configuration file '%s'.\n", buffer); return 1; } while (EsINIParse(&s2)) { EsINIZeroTerminate(&s2); if (0 == strcmp(s2.key, "additional_kernel_compile_flags")) { strcat(kernelCompileFlags, s2.value); } else if (0 == strcmp(s2.key, "additional_kernel_link_flags")) { strcat(kernelLinkFlags, s2.value); } else if (0 == strcmp(s2.key, "additional_common_assembly_flags")) { strcat(commonAssemblyFlags, s2.value); } else if (0 == strcmp(s2.key, "additional_common_compile_flags")) { strcat(commonCompileFlags, s2.value); } else if (0 == strcmp(s2.key, "has_native_toolchain")) { hasNativeToolchain = atoi(s2.value); } } } } else if (0 == strcmp(s.section, "install")) { if (0 == strcmp(s.key, "file")) { driveFile = s.value; } else if (0 == strcmp(s.key, "partition_label")) { partitionLabel = s.value; } else if (0 == strcmp(s.key, "partition_size")) { partitionSize = atoi(s.value) * 1048576UL; } } else if (0 == strcmp(s.section, "font")) { arrput(fontLines, s); } else if (0 == strcmp(s.section, "driver")) { if (0 == strcmp(s.key, "name")) driverName = s.value; if (0 == strcmp(s.key, "source")) driverSource = s.value; if (0 == strcmp(s.key, "builtin")) driverBuiltin = !!atoi(s.value); if (!EsINIPeek(&s) || !s.keyBytes) { arrput(kernelModules, driverName); if (driverSource && *driverSource) { DependencyFile dependencyFile = {}; dependencyFile.name = driverName; snprintf(dependencyFile.path, sizeof(dependencyFile.path), "bin/dependency_files/%s.d", driverName); Application application = {}; arrput(application.sources, driverSource); application.name = driverName; application.builtin = driverBuiltin; application.buildCallback = BuildModule; arrput(application.dependencyFiles, dependencyFile); arrput(applications, application); if (driverBuiltin) { char append[256]; snprintf(append, sizeof(append), " \"bin/Object Files/%s.ekm\" ", driverName); size_t previousLength = arrlenu(builtinModules); arrsetlen(builtinModules, previousLength + strlen(append)); memcpy(builtinModules + previousLength, append, strlen(append)); } } driverSource = driverName = NULL; driverBuiltin = false; } } if (0 != strcmp(s.section, "install")) { configurationHash = CalculateCRC64(s.section, s.sectionBytes, configurationHash); configurationHash = CalculateCRC64(s.key, s.keyBytes, configurationHash); configurationHash = CalculateCRC64(s.value, s.valueBytes, configurationHash); } } } else if (0 == strcmp(argv[1], "application")) { if (argc != 3) { Log("Usage: application \n"); return 1; } arrput(applicationManifests, argv[2]); } else if (0 == strcmp(argv[1], "headers")) { MakeDirectory("bin"); return HeaderGeneratorMain(argc - 1, argv + 1); } else { Log("Error: Unsupported mode '%s'.\n", argv[1]); return 1; } strcpy(commonCompileFlagsWithCStdLib, commonCompileFlags); strcat(commonCompileFlags, commonCompileFlagsFreestanding); buildStartTimeStamp = time(NULL); sh_new_strdup(applicationDependencies); if (minimalRebuild) { DependenciesListRead(); } MakeDirectory("bin"); MakeDirectory("bin/dependency_files"); MakeDirectory("bin/Object Files"); MakeDirectory("bin/Stripped Executables"); MakeDirectory("bin/generated_code"); if (systemBuild) { MakeDirectory("root"); MakeDirectory("root/Applications"); MakeDirectory("root/Applications/POSIX"); MakeDirectory("root/Applications/POSIX/bin"); MakeDirectory("root/Applications/POSIX/include"); MakeDirectory("root/Applications/POSIX/lib"); MakeDirectory("root/Applications/POSIX/tmp"); MakeDirectory("root/Applications/POSIX/lib/linker"); MakeDirectory("root/" SYSTEM_FOLDER_NAME); if (!skipHeaderGeneration) { HeaderGeneratorMain(1, NULL); } } if (!skipCompile) { if (systemBuild) { char buffer[4096]; snprintf(buffer, sizeof(buffer), "arch/%s/crti.s", target); Execute(toolchainNasm, buffer, "-o", "bin/Object Files/crti.o", ArgString(commonAssemblyFlags)); snprintf(buffer, sizeof(buffer), "arch/%s/crtn.s", target); Execute(toolchainNasm, buffer, "-o", "bin/Object Files/crtn.o", ArgString(commonAssemblyFlags)); snprintf(buffer, sizeof(buffer), "%s/crtbegin.o", toolchainCompilerObjects); CopyFile(buffer, "bin/Object Files/crtbegin.o", false); snprintf(buffer, sizeof(buffer), "%s/crtend.o", toolchainCompilerObjects); CopyFile(buffer, "bin/Object Files/crtend.o", false); Execute(toolchainCC, "-c", "desktop/crt1.c", "-o", "bin/Object Files/crt1.o", ArgString(cCompileFlags), ArgString(commonCompileFlags)); Execute(toolchainCC, "-c", "desktop/crtglue.c", "-o" "bin/Object Files/crtglue.o", ArgString(cCompileFlags), ArgString(commonCompileFlags)); CopyFile("bin/Object Files/crti.o", "root/Applications/POSIX/lib/crti.o", false); CopyFile("bin/Object Files/crtbegin.o", "root/Applications/POSIX/lib/crtbegin.o", false); CopyFile("bin/Object Files/crtend.o", "root/Applications/POSIX/lib/crtend.o", false); CopyFile("bin/Object Files/crtn.o", "root/Applications/POSIX/lib/crtn.o", false); CopyFile("bin/Object Files/crt1.o", "root/Applications/POSIX/lib/crt1.o", false); CopyFile("bin/Object Files/crtglue.o", "root/Applications/POSIX/lib/crtglue.o", false); CopyFile("bin/Object Files/crt1.o", "cross/lib/gcc/x86_64-essence/" GCC_VERSION "/crt1.o", true); // TODO Don't hardcode the target. CopyFile("bin/Object Files/crtglue.o", "cross/lib/gcc/x86_64-essence/" GCC_VERSION "/crtglue.o", true); CopyFile("util/linker/userland64.ld", "root/Applications/POSIX/lib/linker/userland64.ld", false); } #define ADD_DEPENDENCY_FILE(application, _path, _name) \ { \ DependencyFile file = {}; \ strcpy(file.path, _path); \ file.name = _name; \ arrput(application.dependencyFiles, file); \ } if (systemBuild) { Application application = {}; application.name = "Bootloader"; application.buildCallback = BuildBootloader; ADD_DEPENDENCY_FILE(application, "bin/dependency_files/boot1.d", "Boot1"); ADD_DEPENDENCY_FILE(application, "bin/dependency_files/boot2.d", "Boot2"); ADD_DEPENDENCY_FILE(application, "bin/dependency_files/boot3.d", "Boot3"); ADD_DEPENDENCY_FILE(application, "bin/dependency_files/boot4.d", "Boot4"); arrput(applications, application); } if (systemBuild) { Application application = {}; application.name = "Desktop"; application.buildCallback = BuildDesktop; ADD_DEPENDENCY_FILE(application, "bin/dependency_files/api1.d", "API1"); ADD_DEPENDENCY_FILE(application, "bin/dependency_files/api2.d", "API2"); ADD_DEPENDENCY_FILE(application, "bin/dependency_files/api3.d", "API3"); arrput(applications, application); } for (uintptr_t i = 0; i < arrlenu(applicationManifests); i++) { ParseApplicationManifest(applicationManifests[i]); } if (systemBuild && !withoutKernel) { ParseKernelConfiguration(); Application application = {}; application.name = "Kernel"; application.buildCallback = BuildKernel; ADD_DEPENDENCY_FILE(application, "bin/dependency_files/kernel.d", "Kernel1"); ADD_DEPENDENCY_FILE(application, "bin/dependency_files/kernel2.d", "Kernel2"); arrput(applications, application); } // Check which applications need to be rebuilt. for (uintptr_t i = 0; i < arrlenu(applications); i++) { bool rebuild = false; for (uintptr_t j = 0; j < arrlenu(applications[i].dependencyFiles); j++) { if (CheckDependencies(applications[i].dependencyFiles[j].name)) { rebuild = true; break; } } if (!rebuild && arrlenu(applications[i].dependencyFiles)) { applications[i].skipped = true; } } // Build all these applications. bool skip = false; #ifdef PARALLEL_BUILD if (!verbose) { if (useColoredOutput) StartSpinner(); pthread_t *threads = (pthread_t *) malloc(sizeof(pthread_t) * threadCount); for (uintptr_t i = 0; i < threadCount; i++) { pthread_create(threads + i, NULL, BuildApplicationThread, NULL); } for (uintptr_t i = 0; i < threadCount; i++) { pthread_join(threads[i], NULL); } if (useColoredOutput) StopSpinner(); skip = true; } #endif if (!skip) { for (uintptr_t i = 0; i < arrlenu(applications); i++) { Log("[%d/%d] Compiling %s...\n", (int) i + 1, (int) arrlenu(applications), applications[i].name); if (applications[i].skipped) continue; applications[i].buildCallback(&applications[i]); } } // Output information about the built applications, // and parse the dependency files for successfully built ones. bool firstEmptyOutput = true; for (uintptr_t i = 0; i < arrlenu(applications); i++) { if (applications[i].skipped) { continue; } if (!applications[i].error && !arrlenu(applications[i].output)) { if (firstEmptyOutput) { firstEmptyOutput = false; Log("Compiled "); } else { Log(", "); } if (useColoredOutput) { Log(COLOR_HIGHLIGHT "%s" COLOR_NORMAL, applications[i].name); } else { Log("%s", applications[i].name); } } } if (!firstEmptyOutput) { Log(".\n"); } for (uintptr_t i = 0; i < arrlenu(applications); i++) { if (applications[i].skipped) { continue; } if (applications[i].error) { if (useColoredOutput) { Log(">> Could not build " COLOR_ERROR "%s" COLOR_NORMAL ".\n", applications[i].name); } else { Log(">> Could not build %s.\n", applications[i].name); } } else if (arrlenu(applications[i].output)) { if (useColoredOutput) { Log(">> Built " COLOR_HIGHLIGHT "%s" COLOR_NORMAL ".\n", applications[i].name); } else { Log(">> Built %s.\n", applications[i].name); } } Log("%.*s", (int) arrlen(applications[i].output), applications[i].output); if (!applications[i].error) { for (uintptr_t j = 0; j < arrlenu(applications[i].dependencyFiles); j++) { ParseDependencies(applications[i].dependencyFiles[j].path, applications[i].dependencyFiles[j].name, j > 0); } } } if (systemBuild) { if (!withoutKernel) { LinkKernel(); } OutputSystemConfiguration(); } } if (driveFile) { Install(driveFile, partitionSize, partitionLabel); } if (minimalRebuild) { DependenciesListWrite(); } for (uintptr_t i = 0; i < arrlenu(applications); i++) { free(applications[i].manifest); arrfree(applications[i].sources); arrfree(applications[i].dependencyFiles); arrfree(applications[i].bundleInputFiles); arrfree(applications[i].fileTypeLines); } arrfree(applicationManifests); arrfree(applications); shfree(applicationDependencies); uint8_t _encounteredErrors = encounteredErrors; encounteredErrors = false; #ifdef PARALLEL_BUILD applicationsIndex = 0; #endif return _encounteredErrors; } #ifdef OS_ESSENCE #include EsCommand commandBuild; EsCommand commandLaunch; EsButton *buttonBuild; EsButton *buttonLaunch; EsTextbox *textboxLog; char *workingDirectory; size_t workingDirectoryBytes; bool buildRunning; const EsStyle stylePanelStack = { .metrics = { .mask = ES_THEME_METRICS_INSETS | ES_THEME_METRICS_GAP_MAJOR, .insets = ES_RECT_1(20), .gapMajor = 15, }, }; const EsStyle stylePanelButtonStack = { .metrics = { .mask = ES_THEME_METRICS_GAP_MAJOR, .gapMajor = 5, }, }; const EsStyle styleTextboxLog = { .inherit = ES_STYLE_TEXTBOX_BORDERED_MULTILINE, .metrics = { .mask = ES_THEME_METRICS_FONT_FAMILY | ES_THEME_METRICS_TEXT_SIZE | ES_THEME_METRICS_PREFERRED_WIDTH, .textSize = 10, .fontFamily = ES_FONT_MONOSPACED, .preferredWidth = 400, }, }; void ThreadBuild(EsGeneric argument) { (void) argument; logSendMessages = true; int result = BuildCore(0, NULL); EsMessage m; m.type = result ? MSG_BUILD_FAILED : MSG_BUILD_SUCCESS; EsMessagePost(NULL, &m); } void CommandBuild(EsInstance *instance, EsElement *element, EsCommand *command) { (void) element; EsAssert(command == &commandBuild); if (!buildRunning) { EsGeneric argument = { 0 }; if (ES_SUCCESS == EsThreadCreate(ThreadBuild, NULL, argument)) { buildRunning = true; EsCommandSetEnabled(&commandBuild, false); EsCommandSetEnabled(&commandLaunch, false); } else { EsDialogShow(instance->window, INTERFACE_STRING(BuildCoreCannotCreateBuildThread), NULL, 0, ES_ICON_DIALOG_ERROR, ES_DIALOG_ALERT_OK_BUTTON); } } } void CommandLaunch(EsInstance *instance, EsElement *element, EsCommand *command) { (void) instance; (void) element; EsAssert(command == &commandLaunch); if (!singleConfigFileData || !workingDirectory) { return; } EsINIState s = {}; s.buffer = singleConfigFileData; s.bytes = singleConfigFileBytes; char *executablePath = NULL; size_t executablePathBytes = 0; while (EsINIParse(&s)) { if (0 == EsStringCompareRaw(s.section, s.sectionBytes, EsLiteral("general")) && 0 == EsStringCompareRaw(s.key, s.keyBytes, EsLiteral("name"))) { executablePath = EsStringAllocateAndFormat(&executablePathBytes, "%s/bin/%s.esx", workingDirectoryBytes, workingDirectory, s.valueBytes, s.value); } } if (executablePath) { EsApplicationRunTemporary(executablePath, executablePathBytes); EsHeapFree(executablePath, 0, NULL); } } int InstanceCallback(EsInstance *instance, EsMessage *message) { if (message->type == ES_MSG_INSTANCE_OPEN) { EsFileStore *fileStore = message->instanceOpen.file; const char *path = message->instanceOpen.nameOrPath; size_t pathBytes = message->instanceOpen.nameOrPathBytes; bool pathIsReal = false; for (uintptr_t i = 0; i < pathBytes; i++) if (path[i] == '/') pathIsReal = true; // HACK Remove this limitation. bool pathCanBeAccessedByPOSIXSubsystem = pathBytes > 3 && path[0] == '0' && path[1] == ':' && path[2] == '/'; if (!pathIsReal) { // Not a real file, we cannot build here. EsInstanceOpenComplete(instance, fileStore, false, INTERFACE_STRING(BuildCoreNoFilePath)); } else if (!pathCanBeAccessedByPOSIXSubsystem) { EsInstanceOpenComplete(instance, fileStore, false, INTERFACE_STRING(BuildCorePathCannotBeAccessedByPOSIXSubsystem)); } else { size_t newFileBytes; void *newFileData = EsFileStoreReadAll(fileStore, &newFileBytes); EsInstanceOpenComplete(instance, fileStore, newFileData != NULL, NULL, 0); if (newFileData) { EsHeapFree(singleConfigFileData, 0, NULL); singleConfigFileBytes = newFileBytes; singleConfigFileData = newFileData; singleConfigFileUse = true; // Change directory. size_t directoryBytes = pathBytes; for (uintptr_t i = 0; i < pathBytes; i++) { if (path[i] == '/') { directoryBytes = i; } } char *directory = EsHeapAllocate(directoryBytes + 1, false, NULL); directory[directoryBytes] = 0; EsMemoryCopy(directory, path, directoryBytes); EsPOSIXSystemCall(SYS_chdir, (intptr_t) (directory + 2 /* skip drive prefix */), 0, 0, 0, 0, 0); EsHeapFree(workingDirectory, workingDirectoryBytes ? workingDirectoryBytes + 1 : 0, NULL); workingDirectory = directory; workingDirectoryBytes = directoryBytes; if (!buildRunning) { EsCommandSetEnabled(&commandBuild, true); EsCommandSetEnabled(&commandLaunch, true); } } } } return ES_HANDLED; } void InstanceCreate(EsMessage *message) { EsInstance *instance = EsInstanceCreate(message, INTERFACE_STRING(BuildCoreTitle)); instance->callback = InstanceCallback; EsCommandRegister(&commandBuild, instance, INTERFACE_STRING(BuildCoreBuild), CommandBuild, 1, "F5", false); EsCommandRegister(&commandLaunch, instance, INTERFACE_STRING(BuildCoreLaunch), CommandLaunch, 2, "F6", false); EsWindowSetIcon(instance->window, ES_ICON_TEXT_X_MAKEFILE); EsPanel *panelRoot = EsPanelCreate(instance->window, ES_CELL_FILL, ES_STYLE_PANEL_WINDOW_BACKGROUND); EsPanel *panelStack = EsPanelCreate(panelRoot, ES_CELL_FILL | ES_PANEL_HORIZONTAL, EsStyleIntern(&stylePanelStack)); EsPanel *panelButtonStack = EsPanelCreate(panelStack, ES_CELL_V_TOP | ES_PANEL_VERTICAL, EsStyleIntern(&stylePanelButtonStack)); buttonBuild = EsButtonCreate(panelButtonStack, ES_BUTTON_DEFAULT, ES_NULL, INTERFACE_STRING(BuildCoreBuild)); EsCommandAddButton(&commandBuild, buttonBuild); buttonBuild->accessKey = 'B'; buttonLaunch = EsButtonCreate(panelButtonStack, ES_FLAGS_DEFAULT, ES_NULL, INTERFACE_STRING(BuildCoreLaunch)); EsCommandAddButton(&commandLaunch, buttonLaunch); buttonLaunch->accessKey = 'L'; EsSpacerCreate(panelStack, ES_CELL_V_FILL, ES_STYLE_SEPARATOR_VERTICAL, 0, 0); textboxLog = EsTextboxCreate(panelStack, ES_TEXTBOX_MULTILINE | ES_CELL_FILL, EsStyleIntern(&styleTextboxLog)); textboxLog->accessKey = 'O'; EsTextboxSetReadOnly(textboxLog, true); } void _start() { _init(); int argc; char **argv; EsPOSIXInitialise(&argc, &argv); EsProcessCreateData createData; EsProcessGetCreateData(&createData); if (createData.subsystemID == ES_SUBSYSTEM_ID_POSIX) { exit(BuildCore(argc, argv)); } while (true) { EsMessage *message = EsMessageReceive(); if (message->type == ES_MSG_INSTANCE_CREATE) { InstanceCreate(message); } else if (message->type == ES_MSG_APPLICATION_EXIT) { EsHeapFree(singleConfigFileData, 0, NULL); singleConfigFileData = NULL; } else if (message->type == MSG_BUILD_SUCCESS || message->type == MSG_BUILD_FAILED) { buildRunning = false; EsCommandSetEnabled(&commandBuild, true); EsCommandSetEnabled(&commandLaunch, true); if (message->type == MSG_BUILD_FAILED) { EsTextboxAppend(textboxLog, INTERFACE_STRING(BuildCoreBuildFailed), false); EsElementFocus(buttonBuild, ES_ELEMENT_FOCUS_ONLY_IF_NO_FOCUSED_ELEMENT); } else { EsTextboxAppend(textboxLog, INTERFACE_STRING(BuildCoreBuildSuccess), false); EsElementFocus(buttonLaunch, ES_ELEMENT_FOCUS_ONLY_IF_NO_FOCUSED_ELEMENT); } EsTextboxEnsureCaretVisible(textboxLog, false); _EsPathAnnouncePathMoved(NULL, 0, workingDirectory, workingDirectoryBytes); } else if (message->type == MSG_LOG) { char *copy = message->user.context1.p; EsTextboxAppend(textboxLog, copy, -1, false); EsHeapFree(copy, 0, NULL); } } } #endif