#define CHAZ_USE_SHORT_NAMES
#include <string.h>
#include <stdlib.h>
#include "Charmonizer/Core/Util.h"
#include "Charmonizer/Core/Compiler.h"
#include "Charmonizer/Core/ConfWriter.h"
#include "Charmonizer/Core/OperatingSystem.h"
/* Temporary files. */
#define TRY_SOURCE_PATH "_charmonizer_try.c"
#define TRY_APP_BASENAME "_charmonizer_try"
#define TARGET_PATH "_charmonizer_target"
/* Static vars. */
static char *cc_command = NULL;
static char *cc_flags = NULL;
static char **inc_dirs = NULL;
static char *try_app_name = NULL;
/* Detect a supported compiler, or assume a generic GCC-compatible compiler
* and hope for the best. */
#ifdef __GNUC__
static char *include_flag = "-I ";
static char *object_flag = "-o ";
static char *exe_flag = "-o ";
#elif defined(_MSC_VER)
static char *include_flag = "/I";
static char *object_flag = "/Fo";
static char *exe_flag = "/Fe";
#else
static char *include_flag = "-I ";
static char *object_flag = "-o ";
static char *exe_flag = "-o ";
#endif
/* Clean up the files associated with CC_capture_output().
*/
static void
S_clean_up_try();
static void
S_do_test_compile();
void
CC_init(const char *compiler_command, const char *compiler_flags)
{
if (Util_verbosity) { printf("Creating compiler object...\n"); }
/* Assign. */
cc_command = Util_strdup(compiler_command);
cc_flags = Util_strdup(compiler_flags);
/* Init. */
inc_dirs = (char**)calloc(sizeof(char*), 1);
/* Add the current directory as an include dir. */
CC_add_inc_dir(".");
/* Set the name of the application which we "try" to execute. */
{
const char *exe_ext = OS_exe_ext();
size_t len = strlen(TRY_APP_BASENAME) + strlen(exe_ext) + 1;
try_app_name = (char*)malloc(len);
sprintf(try_app_name, "%s%s", TRY_APP_BASENAME, exe_ext);
}
/* If we can't compile anything, game over. */
S_do_test_compile();
}
void
CC_clean_up()
{
char **dirs;
for (dirs = inc_dirs; *dirs != NULL; dirs++) {
free(*dirs);
}
free(inc_dirs);
free(cc_command);
free(cc_flags);
}
static char*
S_inc_dir_string()
{
size_t needed = 0;
char *inc_dir_string;
char **dirs;
for (dirs = inc_dirs; *dirs != NULL; dirs++) {
needed += strlen(include_flag) + 2;
needed += strlen(*dirs);
}
inc_dir_string = (char*)malloc(needed + 1);
inc_dir_string[0] = '\0';
for (dirs = inc_dirs; *dirs != NULL; dirs++) {
strcat(inc_dir_string, include_flag);
strcat(inc_dir_string, *dirs);
strcat(inc_dir_string, " ");
}
return inc_dir_string;
}
chaz_bool_t
CC_compile_exe(const char *source_path, const char *exe_name,
const char *code, size_t code_len)
{
const char *exe_ext = OS_exe_ext();
size_t exe_file_buf_size = strlen(exe_name) + strlen(exe_ext) + 1;
char *exe_file = (char*)malloc(exe_file_buf_size);
size_t exe_file_buf_len = sprintf(exe_file, "%s%s", exe_name, exe_ext);
char *inc_dir_string = S_inc_dir_string();
size_t command_max_size = strlen(cc_command)
+ strlen(source_path)
+ strlen(exe_flag)
+ exe_file_buf_len
+ strlen(inc_dir_string)
+ strlen(cc_flags)
+ 200; /* command start, _charm_run, etc. */
char *command = (char*)malloc(command_max_size);
chaz_bool_t result;
(void)code_len; /* Unused. */
/* Write the source file. */
Util_write_file(source_path, code);
/* Prepare and run the compiler command. */
sprintf(command, "%s %s %s%s %s %s",
cc_command, source_path,
exe_flag, exe_file,
inc_dir_string, cc_flags);
if (Util_verbosity < 2) {
OS_run_quietly(command);
}
else {
system(command);
}
/* See if compilation was successful. Remove the source file. */
result = Util_can_open_file(exe_file);
if (!Util_remove_and_verify(source_path)) {
Util_die("Failed to remove '%s'", source_path);
}
free(command);
free(inc_dir_string);
free(exe_file);
return result;
}
chaz_bool_t
CC_compile_obj(const char *source_path, const char *obj_name,
const char *code, size_t code_len)
{
const char *obj_ext = OS_obj_ext();
size_t obj_file_buf_size = strlen(obj_name) + strlen(obj_ext) + 1;
char *obj_file = (char*)malloc(obj_file_buf_size);
size_t obj_file_buf_len = sprintf(obj_file, "%s%s", obj_name, obj_ext);
char *inc_dir_string = S_inc_dir_string();
size_t command_max_size = strlen(cc_command)
+ strlen(source_path)
+ strlen(object_flag)
+ obj_file_buf_len
+ strlen(inc_dir_string)
+ strlen(cc_flags)
+ 200; /* command start, _charm_run, etc. */
char *command = (char*)malloc(command_max_size);
chaz_bool_t result;
(void)code_len; /* Unused. */
/* Write the source file. */
Util_write_file(source_path, code);
/* Prepare and run the compiler command. */
sprintf(command, "%s %s %s%s %s %s",
cc_command, source_path,
object_flag, obj_file,
inc_dir_string,
cc_flags);
if (Util_verbosity < 2) {
OS_run_quietly(command);
}
else {
system(command);
}
/* See if compilation was successful. Remove the source file. */
result = Util_can_open_file(obj_file);
if (!Util_remove_and_verify(source_path)) {
Util_die("Failed to remove '%s'", source_path);
}
free(command);
free(inc_dir_string);
free(obj_file);
return result;
}
chaz_bool_t
CC_test_compile(char *source, size_t source_len)
{
chaz_bool_t compile_succeeded;
if ( !Util_remove_and_verify(try_app_name) ) {
Util_die("Failed to delete file '%s'", try_app_name);
}
compile_succeeded = CC_compile_exe(TRY_SOURCE_PATH, TRY_APP_BASENAME,
source, source_len);
S_clean_up_try();
return compile_succeeded;
}
char*
CC_capture_output(char *source, size_t source_len, size_t *output_len)
{
char *captured_output = NULL;
chaz_bool_t compile_succeeded;
/* Clear out previous versions and test to make sure removal worked. */
if ( !Util_remove_and_verify(try_app_name) ) {
Util_die("Failed to delete file '%s'", try_app_name);
}
if ( !Util_remove_and_verify(TARGET_PATH) ) {
Util_die("Failed to delete file '%s'", TARGET_PATH);
}
/* Attempt compilation; if successful, run app and slurp output. */
compile_succeeded = CC_compile_exe(TRY_SOURCE_PATH, TRY_APP_BASENAME,
source, source_len);
if (compile_succeeded) {
OS_run_local(try_app_name, NULL);
captured_output = Util_slurp_file(TARGET_PATH, output_len);
}
else {
*output_len = 0;
}
/* Remove all the files we just created. */
S_clean_up_try();
return captured_output;
}
static void
S_clean_up_try()
{
remove(TRY_SOURCE_PATH);
OS_remove_exe(TRY_APP_BASENAME);
remove(TARGET_PATH);
}
static void
S_do_test_compile()
{
char *code = "int main() { return 0; }\n";
chaz_bool_t success;
if (Util_verbosity) {
printf("Trying to compile a small test file...\n");
}
/* Attempt compilation. */
success = CC_compile_exe("_charm_try.c", "_charm_try",
code, strlen(code));
if (!success) { Util_die("Failed to compile a small test file"); }
/* Clean up. */
remove("_charm_try.c");
OS_remove_exe("_charm_try");
}
void
CC_add_inc_dir(const char *dir)
{
size_t num_dirs = 0;
char **dirs = inc_dirs;
/* Count up the present number of dirs, reallocate. */
while (*dirs++ != NULL) { num_dirs++; }
num_dirs += 1; /* Passed-in dir. */
inc_dirs = (char**)realloc(inc_dirs, (num_dirs + 1)*sizeof(char*));
/* Put the passed-in dir at the end of the list. */
inc_dirs[num_dirs - 1] = Util_strdup(dir);
inc_dirs[num_dirs] = NULL;
}
/* Copyright 2006-2011 Marvin Humphrey
*
* This program is free software; you can redistribute it and/or modify
* under the same terms as Perl itself.
*/