212 lines
4.9 KiB
C
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);
|
|
}
|