mirror of https://gitlab.com/nakst/essence
2004 lines
67 KiB
C
2004 lines
67 KiB
C
// 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 <essence.h>
|
|
#include <bits/syscall.h>
|
|
|
|
#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 <assert.h>
|
|
#include <dirent.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#ifdef PARALLEL_BUILD
|
|
#include <pthread.h>
|
|
#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"
|
|
#define DEPENDENCIES_FILE "bin/dependency_files/dependencies.ini"
|
|
#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=<arch/%s/kernel.cpp>", 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 <mode> <options...>\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 <configuration>\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 <configuration>\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 <shared/strings.cpp>
|
|
|
|
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
|