Major refactoring (and overcomplicating) of compiler and messages to prepare for threaded dynamic code.
This commit is contained in:
parent
cc6eb18baf
commit
721b23d584
8 changed files with 351 additions and 177 deletions
|
@ -24,7 +24,57 @@
|
|||
#define COMPILER_H
|
||||
|
||||
|
||||
int compilerRunRecipe(char *recipe, char *input, char *outputPath, void *context);
|
||||
#include "libtcc.h"
|
||||
#include "sigsegv.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
|
||||
#endif
|
||||
|
||||
|
||||
enum CompilerErrorsE {
|
||||
COMPILER_ERROR_NONE = 0,
|
||||
COMPILER_ERROR_ALREADY_RUNNING,
|
||||
COMPILER_ERROR_IN_CODE,
|
||||
COMPILER_ERROR_CANNOT_RELOCATE,
|
||||
COMPILER_ERROR_NO_ENTRYPOINT,
|
||||
COMPILER_ERROR_SIGHANDLER_FAILED,
|
||||
COMPILER_ERROR_SEGFAULT,
|
||||
COMPILER_ERROR_COUNT
|
||||
};
|
||||
|
||||
|
||||
struct CompilerContextS;
|
||||
|
||||
|
||||
typedef void (*CompilerCallback)(struct CompilerContextS **context);
|
||||
|
||||
|
||||
typedef struct CompilerContextS {
|
||||
TCCState *s;
|
||||
int compilerResult;
|
||||
int programResult;
|
||||
CompilerCallback callback;
|
||||
void *userData;
|
||||
#if HAVE_SIGSEGV_RECOVERY
|
||||
volatile int runPass;
|
||||
jmp_buf runJump;
|
||||
sigset_t runSigSet;
|
||||
#endif
|
||||
} CompilerContextT;
|
||||
|
||||
|
||||
void compilerDeleteContext(CompilerContextT **context);
|
||||
gboolean compilerHadError(CompilerContextT **context);
|
||||
CompilerContextT *compilerNewContext(CompilerCallback callback, void *userData);
|
||||
void compilerRunRecipe(CompilerContextT *context, char *recipe, char *input, char *outputPath);
|
||||
|
||||
|
||||
#endif // COMPILER_H
|
||||
|
|
|
@ -35,6 +35,8 @@ typedef enum MessageTypesE MessageTypesT;
|
|||
|
||||
|
||||
void message(MessageTypesT level, char *format, ...);
|
||||
void messageShutdown(void);
|
||||
void messageStartup(void);
|
||||
|
||||
|
||||
#endif //MESSAGES_H
|
||||
#endif // MESSAGES_H
|
||||
|
|
209
src/compiler.c
209
src/compiler.c
|
@ -20,10 +20,10 @@
|
|||
*/
|
||||
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "compiler.h"
|
||||
#include "libtcc.h"
|
||||
#include "sigsegv.h"
|
||||
#include "project.h"
|
||||
#include "messages.h"
|
||||
#include "utils.h"
|
||||
|
@ -31,156 +31,214 @@
|
|||
|
||||
|
||||
#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
|
||||
|
||||
|
||||
static CompilerContextT *_currentRunningContext = NULL; // Right now we can only have one compile running at a time.
|
||||
|
||||
char **___recipeTargets = NULL;
|
||||
|
||||
|
||||
static void compilerErrorHandler(void *opaque, const char *msg);
|
||||
|
||||
// Exposed to dynamic code. Not static.
|
||||
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);
|
||||
void compilerDeleteContext(CompilerContextT **context) {
|
||||
CompilerContextT *c = *context;
|
||||
tcc_delete(c->s);
|
||||
DEL(c);
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
static void compilerErrorHandler(void *opaque, const char *msg) {
|
||||
(void)opaque;
|
||||
|
||||
message(strstr(msg, " warning: ") == NULL ? MSG_ERROR : MSG_WARN, "%s", msg);
|
||||
}
|
||||
|
||||
|
||||
gboolean compilerHadError(CompilerContextT **context) {
|
||||
CompilerContextT *c = *context;
|
||||
|
||||
if (c->compilerResult != COMPILER_ERROR_NONE) {
|
||||
// Handle errors not handled elsewhere.
|
||||
switch (c->compilerResult) {
|
||||
case COMPILER_ERROR_ALREADY_RUNNING:
|
||||
message(MSG_ERROR, "Compiled code is already running");
|
||||
break;
|
||||
|
||||
case COMPILER_ERROR_CANNOT_RELOCATE:
|
||||
message(MSG_ERROR, "Unable to link compiled code");
|
||||
break;
|
||||
|
||||
case COMPILER_ERROR_NO_ENTRYPOINT:
|
||||
message(MSG_ERROR, "Compiled code is missing entrypoint");
|
||||
break;
|
||||
|
||||
case COMPILER_ERROR_SIGHANDLER_FAILED:
|
||||
message(MSG_ERROR, "Unable to set up sigsegv handler");
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
CompilerContextT *compilerNewContext(CompilerCallback callback, void *userData) {
|
||||
CompilerContextT *c;
|
||||
int x;
|
||||
int result = -255;
|
||||
int (*entry)(char *, char *);
|
||||
char ch;
|
||||
|
||||
___recipeTargets = NULL;
|
||||
c = NEW(CompilerContextT);
|
||||
|
||||
s = tcc_new();
|
||||
if (!s) {
|
||||
c->compilerResult = COMPILER_ERROR_NONE;
|
||||
c->programResult = 0;
|
||||
c->callback = callback;
|
||||
c->userData = userData;
|
||||
|
||||
c->s = tcc_new();
|
||||
if (!c->s) {
|
||||
DEL(c);
|
||||
// Something bad happened.
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// __resourcePath comes in with a trailing slash. Temporarily remove it.
|
||||
x = (int)strlen(__resourcePath) - 1;
|
||||
c = __resourcePath[x];
|
||||
ch = __resourcePath[x];
|
||||
__resourcePath[x] = 0;
|
||||
tcc_set_lib_path(s, __resourcePath);
|
||||
tcc_add_sysinclude_path(s, __resourcePath);
|
||||
__resourcePath[x] = c;
|
||||
tcc_set_lib_path(c->s, __resourcePath);
|
||||
tcc_add_sysinclude_path(c->s, __resourcePath);
|
||||
__resourcePath[x] = ch;
|
||||
|
||||
tcc_set_options(s, "-Wall -Wno-write-strings");
|
||||
tcc_set_error_func(s, stdout, compilerErrorHandler);
|
||||
tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
|
||||
tcc_set_options(c->s, "-Wall -Wno-write-strings"); // Debug options not working right: -g -bt 5
|
||||
tcc_set_error_func(c->s, NULL, compilerErrorHandler);
|
||||
tcc_set_output_type(c->s, TCC_OUTPUT_MEMORY);
|
||||
|
||||
if (tcc_add_file(s, recipe) < 0) {
|
||||
tcc_add_symbol(c->s, "fputc", fputc);
|
||||
tcc_add_symbol(c->s, "qsort", qsort);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
void compilerRunRecipe(CompilerContextT *context, char *recipe, char *input, char *outputPath) {
|
||||
char *oldLocation;
|
||||
int (*entry)(char *, char *);
|
||||
void *self;
|
||||
|
||||
// All the paths passed in here are expected to be complete and absolute.
|
||||
|
||||
//***TODO*** Oh my god this is so bad. The next line depends on 'userData' being set to 'self' when called from project.c.
|
||||
self = context->userData;
|
||||
|
||||
if (_currentRunningContext != NULL) {
|
||||
context->compilerResult = COMPILER_ERROR_ALREADY_RUNNING;
|
||||
context->callback(&context);
|
||||
return;
|
||||
}
|
||||
|
||||
___recipeTargets = NULL;
|
||||
|
||||
if (tcc_add_file(context->s, recipe) < 0) {
|
||||
// Errors in code.
|
||||
tcc_delete(s);
|
||||
return -4;
|
||||
context->compilerResult = COMPILER_ERROR_IN_CODE;
|
||||
context->callback(&context);
|
||||
return;
|
||||
}
|
||||
|
||||
tcc_add_symbol(s, "fputc", fputc);
|
||||
tcc_add_symbol(s, "qsort", qsort);
|
||||
tcc_add_symbol(context->s, "___recipeTargets", ___recipeTargets);
|
||||
tcc_add_symbol(context->s, "recipeAddTarget", recipeAddTarget);
|
||||
tcc_add_symbol(context->s, "recipeMessage", recipeMessage);
|
||||
tcc_add_symbol(context->s, "utilCreateString", utilCreateString);
|
||||
|
||||
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) {
|
||||
if (tcc_relocate(context->s, TCC_RELOCATE_AUTO) < 0) {
|
||||
// Something bad happened.
|
||||
tcc_delete(s);
|
||||
return -2;
|
||||
context->compilerResult = COMPILER_ERROR_CANNOT_RELOCATE;
|
||||
context->callback(&context);
|
||||
return;
|
||||
}
|
||||
|
||||
entry = tcc_get_symbol(s, "recipe");
|
||||
entry = tcc_get_symbol(context->s, "recipe");
|
||||
if (!entry) {
|
||||
// Something bad happened.
|
||||
tcc_delete(s);
|
||||
return -3;
|
||||
context->compilerResult = COMPILER_ERROR_NO_ENTRYPOINT;
|
||||
context->callback(&context);
|
||||
return;
|
||||
}
|
||||
|
||||
getcwd(__utilFilenameBuffer, FILENAME_MAX);
|
||||
oldLocation = strdup(__utilFilenameBuffer);
|
||||
chdir(outputPath);
|
||||
|
||||
_currentRunningContext = context;
|
||||
|
||||
#if HAVE_SIGSEGV_RECOVERY
|
||||
sigset_t emptySet;
|
||||
|
||||
runPass = 0;
|
||||
context->runPass = 0;
|
||||
|
||||
if (sigsegv_install_handler(&sigHandler) < 0)
|
||||
return -5;
|
||||
if (sigsegv_install_handler(&sigHandler) < 0) {
|
||||
context->compilerResult = COMPILER_ERROR_SIGHANDLER_FAILED;
|
||||
context->callback(&context);
|
||||
return;
|
||||
}
|
||||
|
||||
sigemptyset(&emptySet);
|
||||
sigprocmask(SIG_BLOCK, &emptySet, &runSigSet);
|
||||
sigprocmask(SIG_BLOCK, &emptySet, &context->runSigSet);
|
||||
|
||||
switch (setjmp(runRecipe)) {
|
||||
switch (setjmp(context->runJump)) {
|
||||
case 0:
|
||||
result = entry(input, outputPath);
|
||||
context->programResult = entry(input, outputPath);
|
||||
case 1:
|
||||
sigprocmask(SIG_SETMASK, &runSigSet, NULL);
|
||||
sigprocmask(SIG_SETMASK, &context->runSigSet, NULL);
|
||||
sigsegv_install_handler(NULL);
|
||||
if (runPass != 0) {
|
||||
if (context->runPass != 0) {
|
||||
message(MSG_SEVERE, "%s caused a segmentation fault!", recipe);
|
||||
context->compilerResult = COMPILER_ERROR_SEGFAULT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
context->callback(&context);
|
||||
|
||||
#else
|
||||
|
||||
result = entry(input, outputPath);
|
||||
context->programResult = entry(input, outputPath);
|
||||
context->callback(&context);
|
||||
|
||||
#endif
|
||||
|
||||
_currentRunningContext = NULL;
|
||||
|
||||
chdir(oldLocation);
|
||||
DEL(oldLocation);
|
||||
|
||||
while (arrlen(___recipeTargets) > 0) {
|
||||
if (projectAddToTree(context, ___recipeTargets[0])) {
|
||||
if (projectAddToTree(self, ___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;
|
||||
CompilerContextT *c = (CompilerContextT *)arg1;
|
||||
|
||||
(void)arg2;
|
||||
(void)arg3;
|
||||
longjmp(runRecipe, runPass);
|
||||
|
||||
longjmp(c->runJump, c->runPass);
|
||||
}
|
||||
|
||||
|
||||
|
@ -188,11 +246,12 @@ int sigHandler(void *faultAddress, int serious) {
|
|||
(void)faultAddress;
|
||||
(void)serious;
|
||||
|
||||
runPass++;
|
||||
return sigsegv_leave_handler(sigHandlerContinuation, NULL, NULL, NULL);
|
||||
_currentRunningContext->runPass++;
|
||||
return sigsegv_leave_handler(sigHandlerContinuation, _currentRunningContext, NULL, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void recipeAddTarget(char *target) {
|
||||
arrput(___recipeTargets, strdup(target));
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "ssh.h"
|
||||
#include "utils.h"
|
||||
#include "cwalk.h"
|
||||
#include "messages.h"
|
||||
|
||||
|
||||
char *__resourcePath = NULL;
|
||||
|
@ -47,6 +48,7 @@ int main(int argc, char **argv) {
|
|||
|
||||
gtk_init(&argc, &argv);
|
||||
utilStartup();
|
||||
messageStartup();
|
||||
|
||||
// Make sure we have a config & resources folder.
|
||||
__resourcePath = utilCreateString("%s%cjoeydev%cresources%c", g_get_user_config_dir(), UTIL_PATH_CHAR, UTIL_PATH_CHAR, UTIL_PATH_CHAR);
|
||||
|
@ -79,6 +81,7 @@ int main(int argc, char **argv) {
|
|||
scintilla_release_resources();
|
||||
sshShutdown();
|
||||
httpShutdown();
|
||||
messageShutdown();
|
||||
utilShutdown();
|
||||
|
||||
DEL(__resourcePath);
|
||||
|
|
133
src/messages.c
133
src/messages.c
|
@ -20,21 +20,74 @@
|
|||
*/
|
||||
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "messages.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
static GtkWidget *_lstMessages = NULL;
|
||||
static int _autoScroll = 0;
|
||||
static char **_pendingMessages = NULL;
|
||||
static pthread_mutex_t _mtxMessage;
|
||||
|
||||
|
||||
static gboolean messagesScroll(gpointer userData);
|
||||
static gboolean messagesUpdate(gpointer userData);
|
||||
EVENT gboolean winMessagesClose(GtkWidget *object, gpointer userData);
|
||||
static void winMessagesDelete(gpointer userData);
|
||||
|
||||
|
||||
void message(MessageTypesT level, char *format, ...) {
|
||||
va_list args;
|
||||
char *string;
|
||||
char *msg;
|
||||
char *tok;
|
||||
char *labels[MSG_COUNT] = {
|
||||
"<span foreground=\"gray\"> Info:</span>",
|
||||
"<span foreground=\"yellow\">Warning:</span>",
|
||||
"<span foreground=\"red\"> Error:</span>",
|
||||
"<span foreground=\"red\"><b> Severe:</b></span>"
|
||||
};
|
||||
|
||||
// Construct message.
|
||||
va_start(args, format);
|
||||
msg = utilCreateStringVArgs(format, args);
|
||||
va_end(args);
|
||||
|
||||
// Break multiline messages down into individual lines.
|
||||
tok = strtok(msg, "\n");
|
||||
while (tok != NULL) {
|
||||
//***TODO*** Filter out things that could be mistaken as markup tags.
|
||||
string = utilCreateString("<tt>%s %s</tt>", labels[level], tok);
|
||||
|
||||
// Save for UI thread. message() is not always called from the UI thread!
|
||||
pthread_mutex_lock(&_mtxMessage);
|
||||
arrput(_pendingMessages, string); // Do not free string.
|
||||
pthread_mutex_unlock(&_mtxMessage);
|
||||
|
||||
tok = strtok(NULL, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void messageShutdown(void) {
|
||||
g_idle_remove_by_data(messagesUpdate);
|
||||
pthread_mutex_destroy(&_mtxMessage);
|
||||
}
|
||||
|
||||
|
||||
void messageStartup(void) {
|
||||
if (pthread_mutex_init(&_mtxMessage, NULL) != 0) {
|
||||
debug("Message mutex init failed!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set up updates on the UI thread.
|
||||
g_idle_add(messagesUpdate, messagesUpdate);
|
||||
}
|
||||
|
||||
|
||||
static gboolean messagesUpdate(gpointer userData) {
|
||||
WindowDataT *self = NULL;
|
||||
char *widgetNames[] = {
|
||||
"winMessages",
|
||||
|
@ -48,18 +101,23 @@ void message(MessageTypesT level, char *format, ...) {
|
|||
GtkWidget *row;
|
||||
GtkWidget *box;
|
||||
GtkWidget *label;
|
||||
va_list args;
|
||||
char *string;
|
||||
char *msg;
|
||||
char *tok;
|
||||
char *labels[MSG_COUNT] = {
|
||||
"<span foreground=\"gray\"> Info:</span>",
|
||||
"<span foreground=\"yellow\">Warning:</span>",
|
||||
"<span foreground=\"red\"> Error:</span>",
|
||||
"<span foreground=\"red\"><b> Severe:</b></span>"
|
||||
};
|
||||
GtkAdjustment *adjustment;
|
||||
char *string = NULL;
|
||||
|
||||
// Do we need to open this window?
|
||||
(void)userData;
|
||||
|
||||
// Lock and access the pending message list.
|
||||
pthread_mutex_lock(&_mtxMessage);
|
||||
if (arrlen(_pendingMessages) > 0) {
|
||||
string = _pendingMessages[0]; // We just need the pointer.
|
||||
arrdel(_pendingMessages, 0);
|
||||
}
|
||||
pthread_mutex_unlock(&_mtxMessage);
|
||||
|
||||
// Are there pending messages?
|
||||
if (string != NULL) {
|
||||
|
||||
// Do we need to open the message window?
|
||||
if (!_lstMessages) {
|
||||
// Set up instance data. We only need WindowDataT since this is a "singleton" window.
|
||||
self = NEW(WindowDataT);
|
||||
|
@ -73,58 +131,29 @@ void message(MessageTypesT level, char *format, ...) {
|
|||
|
||||
// Show window.
|
||||
gtk_widget_show_all(self->window);
|
||||
|
||||
// Set up automatic scrolling.
|
||||
g_idle_add(messagesScroll, messagesScroll);
|
||||
}
|
||||
|
||||
// Display message.
|
||||
va_start(args, format);
|
||||
msg = utilCreateStringVArgs(format, args);
|
||||
va_end(args);
|
||||
|
||||
// Break multiline messages down into individual lines.
|
||||
tok = strtok(msg, "\n");
|
||||
while (tok != NULL) {
|
||||
//***TODO*** Filter out things that could be mistaken as markup tags.
|
||||
string = utilCreateString("<tt>%s %s</tt>", labels[level], tok);
|
||||
|
||||
// Create new row with the message string.
|
||||
row = gtk_list_box_row_new();
|
||||
box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
||||
gtk_widget_set_hexpand(box, TRUE);
|
||||
label = gtk_label_new(string);
|
||||
gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
|
||||
|
||||
DEL(string);
|
||||
|
||||
// Add new row to the message box.
|
||||
gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
|
||||
gtk_container_add(GTK_CONTAINER(row), box);
|
||||
gtk_list_box_insert(GTK_LIST_BOX(_lstMessages), row, -1);
|
||||
|
||||
// Force paint.
|
||||
gtk_widget_show_all(row);
|
||||
utilForceUpdate();
|
||||
_autoScroll = 15; // Try several times to show the new row.
|
||||
|
||||
tok = strtok(NULL, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static gboolean messagesScroll(gpointer userData) {
|
||||
GtkAdjustment *adjustment;
|
||||
|
||||
(void)userData;
|
||||
|
||||
if (_autoScroll <= 0) return G_SOURCE_CONTINUE;
|
||||
|
||||
// Scroll to show new line.
|
||||
// Scroll to show new row.
|
||||
//***TODO*** This doesn't always scroll to the very end.
|
||||
adjustment = gtk_list_box_get_adjustment(GTK_LIST_BOX(_lstMessages));
|
||||
gtk_adjustment_set_value(adjustment, gtk_adjustment_get_upper(adjustment));
|
||||
gtk_widget_show_all(_lstMessages);
|
||||
utilForceUpdate();
|
||||
|
||||
_autoScroll--;
|
||||
DEL(string); // Finally free the string allocated in message().
|
||||
}
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
@ -142,7 +171,13 @@ EVENT gboolean winMessagesClose(GtkWidget *object, gpointer userData) {
|
|||
|
||||
|
||||
static void winMessagesDelete(gpointer userData) {
|
||||
g_idle_remove_by_data(messagesScroll);
|
||||
// Delete any pending messages.
|
||||
while (arrlen(_pendingMessages) > 0) {
|
||||
DEL(_pendingMessages[0]);
|
||||
arrdel(_pendingMessages, 0);
|
||||
}
|
||||
ARRFREE(_pendingMessages);
|
||||
|
||||
utilWindowUnRegister(userData);
|
||||
_lstMessages = NULL;
|
||||
DEL(userData);
|
||||
|
|
|
@ -85,6 +85,7 @@ typedef struct ProjectDataS {
|
|||
char *buildPassword;
|
||||
TargetT **targets;
|
||||
GtkWidget *tempWidget; // Used to pass data around dialogs.
|
||||
int tempInteger; // Used during recipe building.
|
||||
} ProjectDataT;
|
||||
|
||||
typedef struct SectionDataS {
|
||||
|
@ -107,7 +108,6 @@ static SectionDataT _sectionData[] = {
|
|||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static ProjectDataT *_cookingProjectData = NULL;
|
||||
static char **_sendList = NULL;
|
||||
|
||||
|
||||
|
@ -120,6 +120,7 @@ EVENT void btnEditRawClicked(GtkButton *widget, gpointer userData);
|
|||
EVENT void btnEditRecipeClicked(GtkButton *widget, gpointer userData);
|
||||
EVENT void btnNewRecipeClicked(GtkButton *widget, gpointer userData);
|
||||
static void clearRecipeData(ProjectDataT *self);
|
||||
static void cookFinished(CompilerContextT **context);
|
||||
static void decompressBuild(ArchiveT *archive);
|
||||
static void dialogCookOptions(char *filename, ProjectDataT *self);
|
||||
static int findRecipeData(ProjectDataT *self, char *key);
|
||||
|
@ -244,6 +245,50 @@ static void clearRecipeData(ProjectDataT *self) {
|
|||
}
|
||||
|
||||
|
||||
static void cookFinished(CompilerContextT **context) {
|
||||
CompilerContextT *ctx = *context;
|
||||
ProjectDataT *self = (ProjectDataT *)ctx->userData;
|
||||
char *raw;
|
||||
char *recipe;
|
||||
|
||||
// Was there a compiler error?
|
||||
if (compilerHadError(context)) {
|
||||
compilerDeleteContext(context);
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is not first call, process context.
|
||||
if (self->tempInteger != -1) {
|
||||
if (ctx->programResult != 0) {
|
||||
//***TODO*** Not all negative returns are severe.
|
||||
message(ctx->programResult > 0 ? MSG_ERROR : MSG_SEVERE, "Recipe %s returned %d", self->recipes[self->tempInteger]->value, ctx->programResult);
|
||||
}
|
||||
}
|
||||
|
||||
compilerDeleteContext(context);
|
||||
|
||||
// Is there something to cook?
|
||||
self->tempInteger++;
|
||||
if (self->tempInteger < arrlen(self->recipes)) {
|
||||
// Build path names.
|
||||
cwk_path_change_basename(self->windowData.filename, self->recipes[self->tempInteger]->key, __utilFilenameBuffer, sizeof(__utilFilenameBuffer));
|
||||
raw = strdup(__utilFilenameBuffer);
|
||||
cwk_path_change_basename(self->windowData.filename, self->recipes[self->tempInteger]->value, __utilFilenameBuffer, sizeof(__utilFilenameBuffer));
|
||||
recipe = strdup(__utilFilenameBuffer);
|
||||
|
||||
// Run it!
|
||||
message(MSG_INFO, "Cooking %s", self->recipes[self->tempInteger]->key);
|
||||
ctx = compilerNewContext(cookFinished, self);
|
||||
compilerRunRecipe(ctx, recipe, raw, self->windowData.path);
|
||||
DEL(recipe);
|
||||
DEL(raw);
|
||||
} else {
|
||||
// Finished with recipe list.
|
||||
message(MSG_INFO, "Finished Cooking");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void decompressBuild(ArchiveT *archive) {
|
||||
ProjectDataT *self = (ProjectDataT *)archive->userData;
|
||||
char *temp;
|
||||
|
@ -254,6 +299,7 @@ static void decompressBuild(ArchiveT *archive) {
|
|||
DEL(temp);
|
||||
|
||||
//***TODO*** Process build results.
|
||||
message(MSG_INFO, "Processing build results");
|
||||
}
|
||||
|
||||
|
||||
|
@ -923,38 +969,14 @@ EVENT void menuProjectBuildTargets(GtkWidget *object, gpointer userData) {
|
|||
|
||||
EVENT void menuProjectBuildCookRecipes(GtkWidget *object, gpointer userData) {
|
||||
ProjectDataT *self = (ProjectDataT *)userData;
|
||||
int i;
|
||||
char *raw;
|
||||
char *recipe;
|
||||
int result;
|
||||
CompilerContextT *ctx;
|
||||
|
||||
// Only one cook at a time. Should not be able to happen.
|
||||
if (_cookingProjectData) {
|
||||
message(MSG_ERROR, "Cook currently running");
|
||||
return;
|
||||
// Are there recipes to cook?
|
||||
if (arrlen(self->recipes) > 0) {
|
||||
self->tempInteger = -1; // Tell cookFinished() we're just starting.
|
||||
ctx = compilerNewContext(cookFinished, self);
|
||||
cookFinished(&ctx);
|
||||
}
|
||||
|
||||
// Remember who started the cook.
|
||||
_cookingProjectData = self;
|
||||
|
||||
for (i=0; i<arrlen(self->recipes); i++) {
|
||||
// Build pathnames.
|
||||
cwk_path_change_basename(self->windowData.filename, self->recipes[i]->key, __utilFilenameBuffer, sizeof(__utilFilenameBuffer));
|
||||
raw = strdup(__utilFilenameBuffer);
|
||||
cwk_path_change_basename(self->windowData.filename, self->recipes[i]->value, __utilFilenameBuffer, sizeof(__utilFilenameBuffer));
|
||||
recipe = strdup(__utilFilenameBuffer);
|
||||
|
||||
// Run it!
|
||||
message(MSG_INFO, "Cooking %s", self->recipes[i]->key);
|
||||
result = compilerRunRecipe(recipe, raw, self->windowData.path, self);
|
||||
if (result != 0) {
|
||||
//***TODO*** Not all negative returns are severe.
|
||||
message(result > 0 ? MSG_ERROR : MSG_SEVERE, "Recipe %s returned %d", self->recipes[i]->value, result);
|
||||
}
|
||||
message(MSG_INFO, "Finished Cooking");
|
||||
}
|
||||
|
||||
_cookingProjectData = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -975,8 +997,6 @@ EVENT void menuProjectBuildBuild(GtkWidget *object, gpointer userData) {
|
|||
gboolean archPrinted;
|
||||
FILE *out;
|
||||
|
||||
menuProjectBuildCookRecipes(object, userData);
|
||||
|
||||
ssh = NEW(SSHT);
|
||||
ssh = sshConnect(self->buildHost, self->buildSSHPort, self->buildUser, self->buildPassword);
|
||||
|
||||
|
|
|
@ -118,6 +118,8 @@ SSHT *sshConnect(char *hostname, uint16_t port, char *user, char *password) {
|
|||
}
|
||||
}
|
||||
|
||||
//***TODO*** Move SFTP session setup here so we don't keep repeating it.
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -125,6 +127,8 @@ SSHT *sshConnect(char *hostname, uint16_t port, char *user, char *password) {
|
|||
void sshDisconnect(SSHT **sshData) {
|
||||
SSHT *data = *sshData;
|
||||
|
||||
//***TODO*** Move SFTP session teardown here so we don't keep repeating it.
|
||||
|
||||
libssh2_session_disconnect(data->session, "Normal Shutdown.");
|
||||
libssh2_session_free(data->session);
|
||||
socketclose(data->sock);
|
||||
|
|
|
@ -233,6 +233,7 @@ gboolean utilDecompressUpdate(gpointer userData) {
|
|||
|
||||
if (toClose >= 0) {
|
||||
a = _activeArchives[toClose];
|
||||
arrdel(_activeArchives, toClose);
|
||||
if (a->callback) {
|
||||
a->callback(a);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue