joeydev/src/compiler.c
2023-04-26 18:20:48 -05:00

212 lines
4.9 KiB
C

/*
* JoeyDev
* Copyright (C) 2018-2023 Scott Duensing <scott@kangaroopunch.com>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include "common.h"
#include "compiler.h"
#include "libtcc.h"
#include "sigsegv.h"
#include "project.h"
#include "messages.h"
#include "utils.h"
#include "array.h"
#if HAVE_SIGSEGV_RECOVERY
#include <setjmp.h>
#include <signal.h>
#if defined _WIN32 && !defined __CYGWIN__
// Windows doesn't have sigset_t.
typedef int sigset_t;
#define sigemptyset(set)
#define sigprocmask(how,set,oldset)
#endif
volatile int runPass = 0;
jmp_buf runRecipe;
sigset_t runSigSet;
static void sigHandlerContinuation(void *arg1, void *arg2, void *arg3);
int sigHandler(void *faultAddress, int serious);
#endif
char **___recipeTargets = NULL;
static void compilerErrorHandler(void *opaque, const char *msg);
void recipeAddTarget(char *target);
void recipeMessage(char *format, ...);
static void compilerErrorHandler(void *opaque, const char *msg) {
char *isWarning = strstr(msg, " warning: ");
(void)opaque;
message(isWarning == NULL ? MSG_ERROR : MSG_WARN, "%s", msg);
}
// All the paths passed in here are expected to be complete and absolute.
int compilerRunRecipe(char *recipe, char *input, char *outputPath, void *context) {
TCCState *s;
char *oldLocation;
char c;
int x;
int result = -255;
int (*entry)(char *, char *);
___recipeTargets = NULL;
s = tcc_new();
if (!s) {
// Something bad happened.
return -1;
}
// __resourcePath comes in with a trailing slash. Temporarily remove it.
x = (int)strlen(__resourcePath) - 1;
c = __resourcePath[x];
__resourcePath[x] = 0;
tcc_set_lib_path(s, __resourcePath);
tcc_add_sysinclude_path(s, __resourcePath);
__resourcePath[x] = c;
tcc_set_options(s, "-Wall -Wno-write-strings");
tcc_set_error_func(s, stdout, compilerErrorHandler);
tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
if (tcc_add_file(s, recipe) < 0) {
// Errors in code.
tcc_delete(s);
return -4;
}
tcc_add_symbol(s, "fputc", fputc);
tcc_add_symbol(s, "qsort", qsort);
tcc_add_symbol(s, "___recipeTargets", ___recipeTargets);
tcc_add_symbol(s, "recipeAddTarget", recipeAddTarget);
tcc_add_symbol(s, "recipeMessage", recipeMessage);
tcc_add_symbol(s, "utilCreateString", utilCreateString);
if (tcc_relocate(s, TCC_RELOCATE_AUTO) < 0) {
// Something bad happened.
tcc_delete(s);
return -2;
}
entry = tcc_get_symbol(s, "recipe");
if (!entry) {
// Something bad happened.
tcc_delete(s);
return -3;
}
getcwd(__utilFilenameBuffer, FILENAME_MAX);
oldLocation = strdup(__utilFilenameBuffer);
chdir(outputPath);
#if HAVE_SIGSEGV_RECOVERY
sigset_t emptySet;
runPass = 0;
if (sigsegv_install_handler(&sigHandler) < 0)
return -5;
sigemptyset(&emptySet);
sigprocmask(SIG_BLOCK, &emptySet, &runSigSet);
switch (setjmp(runRecipe)) {
case 0:
result = entry(input, outputPath);
case 1:
sigprocmask(SIG_SETMASK, &runSigSet, NULL);
sigsegv_install_handler(NULL);
if (runPass != 0) {
message(MSG_SEVERE, "%s caused a segmentation fault!", recipe);
}
break;
}
#else
result = entry(input, outputPath);
#endif
chdir(oldLocation);
DEL(oldLocation);
while (arrlen(___recipeTargets) > 0) {
if (projectAddToTree(context, ___recipeTargets[0])) {
utilSetDirty((WindowDataT *)context, TRUE);
}
DEL(___recipeTargets[0]);
arrdel(___recipeTargets, 0);
}
ARRFREE(___recipeTargets);
tcc_delete(s);
return result;
}
#if HAVE_SIGSEGV_RECOVERY
static void sigHandlerContinuation(void *arg1, void *arg2, void *arg3) {
(void)arg1;
(void)arg2;
(void)arg3;
longjmp(runRecipe, runPass);
}
int sigHandler(void *faultAddress, int serious) {
(void)faultAddress;
(void)serious;
runPass++;
return sigsegv_leave_handler(sigHandlerContinuation, NULL, NULL, NULL);
}
#endif
void recipeAddTarget(char *target) {
arrput(___recipeTargets, strdup(target));
}
void recipeMessage(char *format, ...) {
va_list args;
char *string;
va_start(args, format);
string = utilCreateStringVArgs(format, args);
va_end(args);
message(MSG_INFO, "%s", string);
DEL(string);
}