blob: 1ec5e5dfc12dbbf0c08d2e1a0bc07b6971ad601b [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#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_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_exe_name = NULL;
static char *try_obj_name = NULL;
/* Detect a supported compiler, or assume a generic GCC-compatible compiler
* and hope for the best. */
#ifdef __GNUC__
static const char *include_flag = "-I ";
static const char *object_flag = "-o ";
static const char *exe_flag = "-o ";
#elif defined(_MSC_VER)
static const char *include_flag = "/I";
static const char *object_flag = "/Fo";
static const char *exe_flag = "/Fe";
#else
static const char *include_flag = "-I ";
static const char *object_flag = "-o ";
static const char *exe_flag = "-o ";
#endif
static void
S_do_test_compile(void);
void
CC_init(const char *compiler_command, const char *compiler_flags) {
const char *code = "int main() { return 0; }\n";
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 names for the targets which we "try" to compile. */
{
const char *exe_ext = OS_exe_ext();
const char *obj_ext = OS_obj_ext();
size_t exe_len = strlen(TRY_BASENAME) + strlen(exe_ext) + 1;
size_t obj_len = strlen(TRY_BASENAME) + strlen(obj_ext) + 1;
try_exe_name = (char*)malloc(exe_len);
try_obj_name = (char*)malloc(obj_len);
sprintf(try_exe_name, "%s%s", TRY_BASENAME, exe_ext);
sprintf(try_obj_name, "%s%s", TRY_BASENAME, obj_ext);
}
/* If we can't compile anything, game over. */
if (Util_verbosity) {
printf("Trying to compile a small test file...\n");
}
if (!CC_test_compile(code, strlen(code))) {
Util_die("Failed to compile a small test file");
}
}
void
CC_clean_up(void) {
char **dirs;
for (dirs = inc_dirs; *dirs != NULL; dirs++) {
free(*dirs);
}
free(inc_dirs);
free(cc_command);
free(cc_flags);
free(try_obj_name);
free(try_exe_name);
}
static char*
S_inc_dir_string(void) {
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 junk_buf_size = exe_file_buf_size + 3;
char *junk = (char*)malloc(junk_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);
}
#ifdef _MSC_VER
/* Zap MSVC junk. */
/* TODO: Key this off the compiler supplied as argument, not the compiler
* used to compile Charmonizer. */
sprintf(junk, "%s.obj", exe_name);
remove(junk);
sprintf(junk, "%s.ilk", exe_name);
remove(junk);
sprintf(junk, "%s.pdb", exe_name);
remove(junk);
#endif
/* 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(junk);
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(const char *source, size_t source_len) {
chaz_bool_t compile_succeeded;
if (!Util_remove_and_verify(try_obj_name)) {
Util_die("Failed to delete file '%s'", try_obj_name);
}
compile_succeeded = CC_compile_obj(TRY_SOURCE_PATH, TRY_BASENAME,
source, source_len);
remove(try_obj_name);
return compile_succeeded;
}
char*
CC_capture_output(const 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_exe_name)) {
Util_die("Failed to delete file '%s'", try_exe_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_BASENAME,
source, source_len);
if (compile_succeeded) {
OS_run_local(try_exe_name, NULL);
captured_output = Util_slurp_file(TARGET_PATH, output_len);
}
else {
*output_len = 0;
}
/* Remove all the files we just created. */
remove(TRY_SOURCE_PATH);
OS_remove_exe(TRY_BASENAME);
remove(TARGET_PATH);
return captured_output;
}
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;
}