Merge branch 'binfmt-and-shell-detection'
diff --git a/src/Charmonizer/Core/CFlags.c b/src/Charmonizer/Core/CFlags.c
index 99c6668..1c177b5 100644
--- a/src/Charmonizer/Core/CFlags.c
+++ b/src/Charmonizer/Core/CFlags.c
@@ -207,19 +207,15 @@
string = "/MD";
}
else if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
- const char *shlib_ext = chaz_OS_shared_lib_ext();
- if (strcmp(shlib_ext, ".dylib") == 0) {
+ int binary_format = chaz_CC_binary_format();
+ if (binary_format == CHAZ_CC_BINFMT_MACHO) {
string = "-fno-common";
}
- else if (strcmp(shlib_ext, ".so") == 0) {
+ else if (binary_format == CHAZ_CC_BINFMT_ELF) {
string = "-fPIC";
}
- else if (strcmp(shlib_ext, ".dll") == 0) {
- /* MinGW */
- return;
- }
else {
- /* unknown */
+ /* MinGW. */
return;
}
}
@@ -235,7 +231,7 @@
void
chaz_CFlags_hide_symbols(chaz_CFlags *flags) {
if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
- if (strcmp(chaz_OS_shared_lib_ext(), ".dll") != 0) {
+ if (chaz_CC_binary_format() != CHAZ_CC_BINFMT_PE) {
chaz_CFlags_append(flags, "-fvisibility=hidden");
}
}
@@ -254,7 +250,7 @@
string = "/DLL";
}
else if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
- if (strcmp(chaz_OS_shared_lib_ext(), ".dylib") == 0) {
+ if (chaz_CC_binary_format() == CHAZ_CC_BINFMT_MACHO) {
string = "-dynamiclib";
}
else {
@@ -274,16 +270,16 @@
void
chaz_CFlags_set_shared_library_version(chaz_CFlags *flags, chaz_Lib *lib) {
if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
- const char *shlib_ext = chaz_OS_shared_lib_ext();
+ int binary_format = chaz_CC_binary_format();
- if (strcmp(shlib_ext, ".dylib") == 0) {
+ if (binary_format == CHAZ_CC_BINFMT_MACHO) {
const char *version = chaz_Lib_get_version(lib);
char *string
= chaz_Util_join(" ", "-current_version", version, NULL);
chaz_CFlags_append(flags, string);
free(string);
}
- else if (strcmp(shlib_ext, ".so") == 0) {
+ else if (binary_format == CHAZ_CC_BINFMT_ELF) {
char *soname = chaz_Lib_major_version_filename(lib);
char *string = chaz_Util_join("", "-Wl,-soname,", soname, NULL);
chaz_CFlags_append(flags, string);
diff --git a/src/Charmonizer/Core/Compiler.c b/src/Charmonizer/Core/Compiler.c
index 32a6d43..dd4fa6c 100644
--- a/src/Charmonizer/Core/Compiler.c
+++ b/src/Charmonizer/Core/Compiler.c
@@ -22,6 +22,16 @@
#include "Charmonizer/Core/ConfWriter.h"
#include "Charmonizer/Core/OperatingSystem.h"
+/* Detect binary format.
+ */
+static void
+chaz_CC_detect_binary_format(const char *filename);
+
+/** Return the numeric value of a macro or 0 if it isn't defined.
+ */
+static int
+chaz_CC_eval_macro(const char *macro);
+
/* Detect macros which may help to identify some compilers.
*/
static void
@@ -37,8 +47,13 @@
char *cc_command;
char *cflags;
char *try_exe_name;
+ char exe_ext[10];
+ char shared_lib_ext[10];
+ char static_lib_ext[10];
+ char import_lib_ext[10];
char obj_ext[10];
char gcc_version_str[30];
+ int binary_format;
int cflags_style;
int intval___GNUC__;
int intval___GNUC_MINOR__;
@@ -46,12 +61,13 @@
int intval__MSC_VER;
int intval___clang__;
int intval___SUNPRO_C;
+ int is_cygwin;
chaz_CFlags *extra_cflags;
chaz_CFlags *temp_cflags;
} chaz_CC = {
NULL, NULL, NULL,
- "", "",
- 0, 0, 0, 0, 0, 0, 0,
+ "", "", "", "", "", "",
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
NULL, NULL
};
@@ -70,40 +86,40 @@
chaz_CC.temp_cflags = NULL;
/* Set names for the targets which we "try" to compile. */
+ strcpy(chaz_CC.exe_ext, ".exe");
chaz_CC.try_exe_name
- = chaz_Util_join("", CHAZ_CC_TRY_BASENAME, chaz_OS_exe_ext(), NULL);
+ = chaz_Util_join("", CHAZ_CC_TRY_BASENAME, chaz_CC.exe_ext, NULL);
/* If we can't compile or execute anything, game over. */
if (chaz_Util_verbosity) {
printf("Trying to compile and execute a small test file...\n");
}
- if (!chaz_Util_remove_and_verify(chaz_CC.try_exe_name)) {
- chaz_Util_die("Failed to delete file '%s'", chaz_CC.try_exe_name);
- }
+
/* Try MSVC argument style. */
- strcpy(chaz_CC.obj_ext, ".obj");
- chaz_CC.cflags_style = CHAZ_CFLAGS_STYLE_MSVC;
- compile_succeeded = chaz_CC_compile_exe(CHAZ_CC_TRY_SOURCE_PATH,
- CHAZ_CC_TRY_BASENAME, code);
if (!compile_succeeded) {
- /* Try POSIX argument style. */
- strcpy(chaz_CC.obj_ext, ".o");
- chaz_CC.cflags_style = CHAZ_CFLAGS_STYLE_POSIX;
+ chaz_CC.cflags_style = CHAZ_CFLAGS_STYLE_MSVC;
+ if (!chaz_Util_remove_and_verify(chaz_CC.try_exe_name)) {
+ chaz_Util_die("Failed to delete file '%s'", chaz_CC.try_exe_name);
+ }
compile_succeeded = chaz_CC_compile_exe(CHAZ_CC_TRY_SOURCE_PATH,
CHAZ_CC_TRY_BASENAME, code);
}
+
+ /* Try POSIX argument style. */
+ if (!compile_succeeded) {
+ chaz_CC.cflags_style = CHAZ_CFLAGS_STYLE_POSIX;
+ if (!chaz_Util_remove_and_verify(chaz_CC.try_exe_name)) {
+ chaz_Util_die("Failed to delete file '%s'", chaz_CC.try_exe_name);
+ }
+ compile_succeeded = chaz_CC_compile_exe(CHAZ_CC_TRY_SOURCE_PATH,
+ CHAZ_CC_TRY_BASENAME, code);
+ }
+
if (!compile_succeeded) {
chaz_Util_die("Failed to compile a small test file");
}
- retval = chaz_OS_run_local_redirected(chaz_CC.try_exe_name,
- chaz_OS_dev_null());
+ chaz_CC_detect_binary_format(chaz_CC.try_exe_name);
chaz_Util_remove_and_verify(chaz_CC.try_exe_name);
- if (retval < 0) {
- chaz_Util_die("Failed to execute test file: %s", strerror(errno));
- }
- if (retval > 0) {
- chaz_Util_die("Unexpected exit code %d from test file", retval);
- }
chaz_CC_detect_known_compilers();
@@ -121,9 +137,104 @@
}
chaz_CC.extra_cflags = chaz_CFlags_new(chaz_CC.cflags_style);
chaz_CC.temp_cflags = chaz_CFlags_new(chaz_CC.cflags_style);
+
+ /* File extensions. */
+ if (chaz_CC.binary_format == CHAZ_CC_BINFMT_ELF) {
+ if (chaz_Util_verbosity) {
+ printf("Detected binary format: ELF\n");
+ }
+ strcpy(chaz_CC.exe_ext, "");
+ strcpy(chaz_CC.shared_lib_ext, ".so");
+ strcpy(chaz_CC.static_lib_ext, ".a");
+ strcpy(chaz_CC.obj_ext, ".o");
+ }
+ else if (chaz_CC.binary_format == CHAZ_CC_BINFMT_MACHO) {
+ if (chaz_Util_verbosity) {
+ printf("Detected binary format: Mach-O\n");
+ }
+ strcpy(chaz_CC.exe_ext, "");
+ strcpy(chaz_CC.shared_lib_ext, ".dylib");
+ strcpy(chaz_CC.static_lib_ext, ".a");
+ strcpy(chaz_CC.obj_ext, ".o");
+ }
+ else if (chaz_CC.binary_format == CHAZ_CC_BINFMT_PE) {
+ if (chaz_Util_verbosity) {
+ printf("Detected binary format: Portable Executable\n");
+ }
+ strcpy(chaz_CC.exe_ext, ".exe");
+ strcpy(chaz_CC.shared_lib_ext, ".dll");
+ if (chaz_CC.intval___GNUC__) {
+ strcpy(chaz_CC.static_lib_ext, ".a");
+ strcpy(chaz_CC.import_lib_ext, ".dll.a");
+ strcpy(chaz_CC.obj_ext, ".o");
+ }
+ else {
+ strcpy(chaz_CC.static_lib_ext, ".lib");
+ strcpy(chaz_CC.import_lib_ext, ".lib");
+ strcpy(chaz_CC.obj_ext, ".obj");
+ }
+
+ if (chaz_CC_has_macro("__CYGWIN__")) {
+ chaz_CC.is_cygwin = 1;
+ }
+ }
+ else {
+ chaz_Util_die("Failed to detect binary format");
+ }
+
+ free(chaz_CC.try_exe_name);
+ chaz_CC.try_exe_name
+ = chaz_Util_join("", CHAZ_CC_TRY_BASENAME, chaz_CC.exe_ext, NULL);
}
-static const char chaz_CC_detect_macro_code[] =
+static void
+chaz_CC_detect_binary_format(const char *filename) {
+ char *output;
+ size_t output_len;
+ int binary_format = 0;
+
+ output = chaz_Util_slurp_file(filename, &output_len);
+
+ /* ELF. */
+ if (binary_format == 0 && output_len >= 4
+ && memcmp(output, "\x7F" "ELF", 4) == 0
+ ) {
+ binary_format = CHAZ_CC_BINFMT_ELF;
+ }
+
+ /* Macho-O. */
+ if (binary_format == 0 && output_len >= 4
+ && (memcmp(output, "\xCA\xFE\xBA\xBE", 4) == 0 /* Fat binary. */
+ || memcmp(output, "\xFE\xED\xFA\xCE", 4) == 0 /* 32-bit BE. */
+ || memcmp(output, "\xFE\xED\xFA\xCF", 4) == 0 /* 64-bit BE. */
+ || memcmp(output, "\xCE\xFA\xED\xFE", 4) == 0 /* 32-bit LE. */
+ || memcmp(output, "\xCF\xFA\xED\xFE", 4) == 0) /* 64-bit LE. */
+ ) {
+ binary_format = CHAZ_CC_BINFMT_MACHO;
+ }
+
+ /* Portable Executable. */
+ if (binary_format == 0 && output_len >= 0x40
+ && memcmp(output, "MZ", 2) == 0
+ ) {
+ size_t pe_header_off =
+ (unsigned char)output[0x3C]
+ | ((unsigned char)output[0x3D] << 8)
+ | ((unsigned char)output[0x3E] << 16)
+ | ((unsigned char)output[0x3F] << 24);
+
+ if (output_len >= pe_header_off + 4
+ && memcmp(output + pe_header_off, "PE\0\0", 4) == 0
+ ) {
+ binary_format = CHAZ_CC_BINFMT_PE;
+ }
+ }
+
+ chaz_CC.binary_format = binary_format;
+ free(output);
+}
+
+static const char chaz_CC_eval_macro_code[] =
CHAZ_QUOTE( #include <stdio.h> )
CHAZ_QUOTE( int main() { )
CHAZ_QUOTE( #ifndef %s )
@@ -134,15 +245,15 @@
CHAZ_QUOTE( } );
static int
-chaz_CC_detect_macro(const char *macro) {
- size_t size = sizeof(chaz_CC_detect_macro_code)
+chaz_CC_eval_macro(const char *macro) {
+ size_t size = sizeof(chaz_CC_eval_macro_code)
+ (strlen(macro) * 2)
+ 20;
char *code = (char*)malloc(size);
int retval = 0;
char *output;
size_t len;
- sprintf(code, chaz_CC_detect_macro_code, macro, macro);
+ sprintf(code, chaz_CC_eval_macro_code, macro, macro);
output = chaz_CC_capture_output(code, &len);
if (output) {
retval = atoi(output);
@@ -152,21 +263,34 @@
return retval;
}
+int
+chaz_CC_has_macro(const char *macro) {
+ size_t size = sizeof(chaz_CC_eval_macro_code)
+ + (strlen(macro) * 2)
+ + 20;
+ char *code = (char*)malloc(size);
+ int retval = 0;
+ sprintf(code, chaz_CC_eval_macro_code, macro, macro);
+ retval = chaz_CC_test_compile(code);
+ free(code);
+ return retval;
+}
+
static void
chaz_CC_detect_known_compilers(void) {
- chaz_CC.intval___GNUC__ = chaz_CC_detect_macro("__GNUC__");
+ chaz_CC.intval___GNUC__ = chaz_CC_eval_macro("__GNUC__");
if (chaz_CC.intval___GNUC__) {
chaz_CC.intval___GNUC_MINOR__
- = chaz_CC_detect_macro("__GNUC_MINOR__");
+ = chaz_CC_eval_macro("__GNUC_MINOR__");
chaz_CC.intval___GNUC_PATCHLEVEL__
- = chaz_CC_detect_macro("__GNUC_PATCHLEVEL__");
+ = chaz_CC_eval_macro("__GNUC_PATCHLEVEL__");
sprintf(chaz_CC.gcc_version_str, "%d.%d.%d", chaz_CC.intval___GNUC__,
chaz_CC.intval___GNUC_MINOR__,
chaz_CC.intval___GNUC_PATCHLEVEL__);
}
- chaz_CC.intval__MSC_VER = chaz_CC_detect_macro("_MSC_VER");
- chaz_CC.intval___clang__ = chaz_CC_detect_macro("__clang__");
- chaz_CC.intval___SUNPRO_C = chaz_CC_detect_macro("__SUNPRO_C");
+ chaz_CC.intval__MSC_VER = chaz_CC_eval_macro("_MSC_VER");
+ chaz_CC.intval___clang__ = chaz_CC_eval_macro("__clang__");
+ chaz_CC.intval___SUNPRO_C = chaz_CC_eval_macro("__SUNPRO_C");
}
void
@@ -185,7 +309,7 @@
const char *extra_cflags_string = "";
const char *temp_cflags_string = "";
const char *local_cflags_string;
- char *exe_file = chaz_Util_join("", exe_name, chaz_OS_exe_ext(), NULL);
+ char *exe_file = chaz_Util_join("", exe_name, chaz_CC.exe_ext, NULL);
char *command;
int result;
@@ -369,6 +493,31 @@
return chaz_CFlags_new(chaz_CC.cflags_style);
}
+int
+chaz_CC_binary_format(void) {
+ return chaz_CC.binary_format;
+}
+
+const char*
+chaz_CC_exe_ext(void) {
+ return chaz_CC.exe_ext;
+}
+
+const char*
+chaz_CC_shared_lib_ext(void) {
+ return chaz_CC.shared_lib_ext;
+}
+
+const char*
+chaz_CC_static_lib_ext(void) {
+ return chaz_CC.static_lib_ext;
+}
+
+const char*
+chaz_CC_import_lib_ext(void) {
+ return chaz_CC.import_lib_ext;
+}
+
const char*
chaz_CC_obj_ext(void) {
return chaz_CC.obj_ext;
@@ -396,6 +545,11 @@
return chaz_CC.intval___SUNPRO_C;
}
+int
+chaz_CC_is_cygwin(void) {
+ return chaz_CC.is_cygwin;
+}
+
const char*
chaz_CC_link_command() {
if (chaz_CC.intval__MSC_VER) {
diff --git a/src/Charmonizer/Core/Compiler.h b/src/Charmonizer/Core/Compiler.h
index e916553..6361af7 100644
--- a/src/Charmonizer/Core/Compiler.h
+++ b/src/Charmonizer/Core/Compiler.h
@@ -28,6 +28,10 @@
#include "Charmonizer/Core/Defines.h"
#include "Charmonizer/Core/CFlags.h"
+#define CHAZ_CC_BINFMT_ELF 1
+#define CHAZ_CC_BINFMT_MACHO 2
+#define CHAZ_CC_BINFMT_PE 3
+
/* Attempt to compile and link an executable. Return true if the executable
* file exists after the attempt.
*/
@@ -62,6 +66,11 @@
char*
chaz_CC_capture_output(const char *source, size_t *output_len);
+/** Return true if macro is defined.
+ */
+int
+chaz_CC_has_macro(const char *macro);
+
/** Initialize the compiler environment.
*/
void
@@ -97,6 +106,31 @@
chaz_CFlags*
chaz_CC_new_cflags(void);
+/* Return the binary format.
+ */
+int
+chaz_CC_binary_format(void);
+
+/* Return the extension for an executable.
+ */
+const char*
+chaz_CC_exe_ext(void);
+
+/* Return the extension for a shared (dynamic) library.
+ */
+const char*
+chaz_CC_shared_lib_ext(void);
+
+/* Return the extension for a static library.
+ */
+const char*
+chaz_CC_static_lib_ext(void);
+
+/* Return the extension for an import library (Windows).
+ */
+const char*
+chaz_CC_import_lib_ext(void);
+
/* Return the extension for a compiled object.
*/
const char*
@@ -114,6 +148,9 @@
int
chaz_CC_sun_c_version_num(void);
+int
+chaz_CC_is_cygwin(void);
+
const char*
chaz_CC_link_command(void);
diff --git a/src/Charmonizer/Core/Library.c b/src/Charmonizer/Core/Library.c
index 3b1225e..d1e0cb6 100644
--- a/src/Charmonizer/Core/Library.c
+++ b/src/Charmonizer/Core/Library.c
@@ -33,7 +33,7 @@
S_build_filename(chaz_Lib *lib, const char *version, const char *ext);
static const char*
-S_get_prefix(void);
+S_get_prefix(chaz_Lib *lib);
chaz_Lib*
chaz_Lib_new_shared(const char *name, const char *version,
@@ -97,8 +97,8 @@
return chaz_Lib_no_version_filename(lib);
}
else {
- const char *ext = chaz_OS_shared_lib_ext();
- if (strcmp(ext, ".dll") == 0) {
+ const char *ext = chaz_CC_shared_lib_ext();
+ if (chaz_CC_binary_format() == CHAZ_CC_BINFMT_PE) {
return S_build_filename(lib, lib->major_version, ext);
}
else {
@@ -113,23 +113,24 @@
return chaz_Lib_no_version_filename(lib);
}
else {
- const char *ext = chaz_OS_shared_lib_ext();
+ const char *ext = chaz_CC_shared_lib_ext();
return S_build_filename(lib, lib->major_version, ext);
}
}
char*
chaz_Lib_no_version_filename(chaz_Lib *lib) {
- const char *prefix = S_get_prefix();
+ const char *prefix = S_get_prefix(lib);
const char *ext = lib->is_shared
- ? chaz_OS_shared_lib_ext()
- : chaz_OS_static_lib_ext();
+ ? chaz_CC_shared_lib_ext()
+ : chaz_CC_static_lib_ext();
return chaz_Util_join("", prefix, lib->name, ext, NULL);
}
char*
chaz_Lib_implib_filename(chaz_Lib *lib) {
- return S_build_filename(lib, lib->major_version, ".lib");
+ const char *ext = chaz_CC_import_lib_ext();
+ return S_build_filename(lib, lib->major_version, ext);
}
char*
@@ -139,29 +140,31 @@
static char*
S_build_filename(chaz_Lib *lib, const char *version, const char *ext) {
- const char *prefix = S_get_prefix();
- const char *shlib_ext = chaz_OS_shared_lib_ext();
+ const char *prefix = S_get_prefix(lib);
+ int binary_format = chaz_CC_binary_format();
- /* Use `shlib_ext` as a proxy for OS to determine behavior, but append
- * the supplied `ext`. */
- if (strcmp(shlib_ext, ".dll") == 0) {
+ if (binary_format == CHAZ_CC_BINFMT_PE) {
return chaz_Util_join("", prefix, lib->name, "-", version, ext, NULL);
}
- else if (strcmp(shlib_ext, ".dylib") == 0) {
+ else if (binary_format == CHAZ_CC_BINFMT_MACHO) {
return chaz_Util_join("", prefix, lib->name, ".", version, ext, NULL);
}
- else {
+ else if (binary_format == CHAZ_CC_BINFMT_ELF) {
return chaz_Util_join("", prefix, lib->name, ext, ".", version, NULL);
}
+ else {
+ chaz_Util_die("Unsupported binary format");
+ return NULL;
+ }
}
static const char*
-S_get_prefix() {
+S_get_prefix(chaz_Lib *lib) {
if (chaz_CC_msvc_version_num()) {
return "";
}
- else if (chaz_OS_is_cygwin()) {
- return "cyg";
+ else if (chaz_CC_is_cygwin()) {
+ return lib->is_static ? "lib" : "cyg";
}
else {
return "lib";
diff --git a/src/Charmonizer/Core/Make.c b/src/Charmonizer/Core/Make.c
index df2a217..8e8699b 100644
--- a/src/Charmonizer/Core/Make.c
+++ b/src/Charmonizer/Core/Make.c
@@ -80,6 +80,8 @@
void
chaz_Make_init(const char *make_command) {
+ chaz_Make.shell_type = chaz_OS_shell_type();
+
if (make_command) {
if (!chaz_Make_detect(make_command, NULL)) {
chaz_Util_warn("Make utility '%s' doesn't appear to work");
@@ -95,11 +97,6 @@
printf("Detected make utility '%s'\n", chaz_Make.make_command);
}
}
-
- if (chaz_Make.shell_type == 0) {
- // Assume POSIX.
- chaz_Make.shell_type = CHAZ_OS_POSIX;
- }
}
void
@@ -149,12 +146,14 @@
size_t len;
char *content = chaz_Util_slurp_file("_charm_foo", &len);
if (NULL != strstr(content, "foo\\bar")) {
- chaz_Make.shell_type = CHAZ_OS_CMD_EXE;
- succeeded = 1;
+ if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
+ succeeded = 1;
+ }
}
else if (NULL != strstr(content, "foo^bar")) {
- chaz_Make.shell_type = CHAZ_OS_POSIX;
- succeeded = 1;
+ if (chaz_Make.shell_type == CHAZ_OS_POSIX) {
+ succeeded = 1;
+ }
}
free(content);
}
@@ -171,7 +170,7 @@
chaz_MakeFile*
chaz_MakeFile_new() {
chaz_MakeFile *makefile = (chaz_MakeFile*)malloc(sizeof(chaz_MakeFile));
- const char *exe_ext = chaz_OS_exe_ext();
+ const char *exe_ext = chaz_CC_exe_ext();
const char *obj_ext = chaz_CC_obj_ext();
char *generated;
@@ -333,14 +332,14 @@
chaz_MakeRule*
chaz_MakeFile_add_shared_lib(chaz_MakeFile *makefile, chaz_Lib *lib,
const char *sources, chaz_CFlags *link_flags) {
- chaz_CFlags *local_flags = chaz_CC_new_cflags();
- const char *link = chaz_CC_link_command();
- const char *shlib_ext = chaz_OS_shared_lib_ext();
- const char *link_flags_string = "";
- const char *local_flags_string;
+ chaz_CFlags *local_flags = chaz_CC_new_cflags();
+ const char *link = chaz_CC_link_command();
+ const char *link_flags_string = "";
+ const char *local_flags_string;
+ int binfmt = chaz_CC_binary_format();
chaz_MakeRule *rule;
- char *filename;
- char *command;
+ char *filename;
+ char *command;
filename = chaz_Lib_filename(lib);
rule = chaz_MakeFile_add_rule(makefile, filename, sources);
@@ -353,7 +352,7 @@
chaz_CFlags_append(local_flags, "/nologo");
}
chaz_CFlags_link_shared_library(local_flags);
- if (strcmp(shlib_ext, ".dylib") == 0) {
+ if (binfmt == CHAZ_CC_BINFMT_MACHO) {
/* Set temporary install name with full path on Darwin. */
const char *dir_sep = chaz_OS_dir_sep();
char *major_v_name = chaz_Lib_major_version_filename(lib);
@@ -375,7 +374,7 @@
chaz_MakeRule_add_rm_command(makefile->clean, filename);
/* Add symlinks. */
- if (strcmp(shlib_ext, ".dll") != 0) {
+ if (binfmt == CHAZ_CC_BINFMT_ELF || binfmt == CHAZ_CC_BINFMT_MACHO) {
char *major_v_name = chaz_Lib_major_version_filename(lib);
char *no_v_name = chaz_Lib_no_version_filename(lib);
@@ -383,7 +382,7 @@
chaz_MakeRule_add_command(rule, command);
free(command);
- if (strcmp(shlib_ext, ".dylib") == 0) {
+ if (binfmt == CHAZ_CC_BINFMT_MACHO) {
command = chaz_Util_join(" ", "ln -sf", filename, no_v_name,
NULL);
}
@@ -419,7 +418,6 @@
chaz_MakeRule*
chaz_MakeFile_add_static_lib(chaz_MakeFile *makefile, chaz_Lib *lib,
const char *objects) {
- const char *shlib_ext = chaz_OS_shared_lib_ext();
chaz_MakeRule *rule;
char *filename;
char *command;
@@ -445,7 +443,7 @@
chaz_CFlags *cflags = chaz_CC_new_cflags();
chaz_MakeRule *rule;
const char *dir_sep = chaz_OS_dir_sep();
- const char *exe_ext = chaz_OS_exe_ext();
+ const char *exe_ext = chaz_CC_exe_ext();
char *lemon_exe = chaz_Util_join("", dir, dir_sep, "lemon", exe_ext, NULL);
char *lemon_c = chaz_Util_join(dir_sep, dir, "lemon.c", NULL);
@@ -676,8 +674,9 @@
va_list args;
char *path = NULL;
char *lib_command = NULL;
+ int binfmt = chaz_CC_binary_format();
- if (strcmp(chaz_OS_shared_lib_ext(), ".so") == 0) {
+ if (binfmt == CHAZ_CC_BINFMT_ELF) {
va_start(args, command);
path = chaz_Util_vjoin(":", args);
va_end(args);
@@ -687,16 +686,28 @@
free(path);
}
- else if (strcmp(chaz_OS_shared_lib_ext(), ".dll") == 0) {
- va_start(args, command);
- path = chaz_Util_vjoin(";", args);
- va_end(args);
+ else if (binfmt == CHAZ_CC_BINFMT_PE) {
+ if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
+ va_start(args, command);
+ path = chaz_Util_vjoin(";", args);
+ va_end(args);
- /* It's important to not add a space before `&&`. Otherwise, the
- * space is added to the search path.
- */
- lib_command = chaz_Util_join("", "path ", path, ";%path%&& ", command,
- NULL);
+ /* It's important to not add a space before `&&`. Otherwise, the
+ * space is added to the search path.
+ */
+ lib_command = chaz_Util_join("", "path ", path, ";%path%&& ",
+ command, NULL);
+ }
+ else {
+ va_start(args, command);
+ path = chaz_Util_vjoin(":", args);
+ va_end(args);
+
+ lib_command = chaz_Util_join("", "PATH=", path, ":$$PATH ",
+ command, NULL);
+ }
+
+ free(path);
}
else {
/* Assume that library paths are compiled into the executable on
diff --git a/src/Charmonizer/Core/OperatingSystem.c b/src/Charmonizer/Core/OperatingSystem.c
index c872b68..47a66af 100644
--- a/src/Charmonizer/Core/OperatingSystem.c
+++ b/src/Charmonizer/Core/OperatingSystem.c
@@ -33,107 +33,81 @@
char name[CHAZ_OS_NAME_MAX+1];
char dev_null[20];
char dir_sep[2];
- char exe_ext[5];
- char static_lib_ext[5];
- char shared_lib_ext[7];
char local_command_start[3];
int shell_type;
-} chaz_OS = { "", "", "", "", "", "", "", 0 };
+ int run_sh_via_cmd_exe;
+} chaz_OS = { "", "", "", "", 0, 0 };
+
+static int
+chaz_OS_run_sh_via_cmd_exe(const char *command);
void
chaz_OS_init(void) {
+ char *output;
+ size_t output_len;
+
if (chaz_Util_verbosity) {
printf("Initializing Charmonizer/Core/OperatingSystem...\n");
}
- if (chaz_Util_verbosity) {
- printf("Trying to find a bit-bucket a la /dev/null...\n");
- }
+ /* Detect shell based on escape character. */
- /* Detect shell based on whether the bitbucket is "/dev/null" or "nul".
- * Start with "nul" as some Windows boxes seem to have a "/dev/null".
- */
- if (chaz_Util_can_open_file("nul")) {
- strcpy(chaz_OS.name, "windows");
- strcpy(chaz_OS.dev_null, "nul");
- strcpy(chaz_OS.dir_sep, "\\");
- strcpy(chaz_OS.exe_ext, ".exe");
- strcpy(chaz_OS.shared_lib_ext, ".dll");
- strcpy(chaz_OS.static_lib_ext, ".lib");
- strcpy(chaz_OS.local_command_start, ".\\");
- chaz_OS.shell_type = CHAZ_OS_CMD_EXE;
- }
- else if (chaz_Util_can_open_file("/dev/null")) {
- char *uname;
- size_t uname_len;
- size_t i;
+ /* Needed to make redirection work. */
+ chaz_OS.shell_type = CHAZ_OS_POSIX;
- chaz_OS.shell_type = CHAZ_OS_POSIX;
+ output = chaz_OS_run_and_capture("echo foo\\^bar", &output_len);
- /* Detect Unix name. */
- uname = chaz_OS_run_and_capture("uname", &uname_len);
- for (i = 0; i < CHAZ_OS_NAME_MAX && i < uname_len; i++) {
- char c = uname[i];
- if (!c || isspace((unsigned char)c)) { break; }
- chaz_OS.name[i] = tolower((unsigned char)c);
+ if (output_len >= 7 && memcmp(output, "foo\\bar", 7) == 0) {
+ /* Escape character is caret. */
+ if (chaz_Util_verbosity) {
+ printf("Detected cmd.exe shell\n");
}
- if (i > 0) { chaz_OS.name[i] = '\0'; }
- else { strcpy(chaz_OS.name, "unknown_unix"); }
- free(uname);
- strcpy(chaz_OS.dev_null, "/dev/null");
- strcpy(chaz_OS.dir_sep, "/");
- strcpy(chaz_OS.exe_ext, "");
- strcpy(chaz_OS.static_lib_ext, ".a");
- if (memcmp(chaz_OS.name, "darwin", 6) == 0) {
- strcpy(chaz_OS.shared_lib_ext, ".dylib");
- }
- else if (memcmp(chaz_OS.name, "cygwin", 6) == 0) {
- strcpy(chaz_OS.shared_lib_ext, ".dll");
+ /* Try to see whether running commands via the `sh` command works.
+ * Run the `find` command to check whether we're in a somewhat POSIX
+ * compatible environment. */
+ free(output);
+ chaz_OS.run_sh_via_cmd_exe = 1;
+ output = chaz_OS_run_and_capture("find . -prune", &output_len);
+
+ if (output_len >= 2
+ && output[0] == '.'
+ && isspace((unsigned char)output[1])
+ ) {
+ if (chaz_Util_verbosity) {
+ printf("Detected POSIX shell via cmd.exe\n");
+ }
+ chaz_OS.shell_type = CHAZ_OS_POSIX;
}
else {
- strcpy(chaz_OS.shared_lib_ext, ".so");
+ chaz_OS.shell_type = CHAZ_OS_CMD_EXE;
+ chaz_OS.run_sh_via_cmd_exe = 0;
}
+ }
+ else if (output_len >= 7 && memcmp(output, "foo^bar", 7) == 0) {
+ /* Escape character is backslash. */
+ if (chaz_Util_verbosity) {
+ printf("Detected POSIX shell\n");
+ }
+ chaz_OS.shell_type = CHAZ_OS_POSIX;
+ }
+
+ if (chaz_OS.shell_type == CHAZ_OS_CMD_EXE) {
+ strcpy(chaz_OS.dir_sep, "\\");
+ strcpy(chaz_OS.dev_null, "nul");
+ /* Empty string should work, too. */
+ strcpy(chaz_OS.local_command_start, ".\\");
+ }
+ else if (chaz_OS.shell_type == CHAZ_OS_POSIX) {
+ strcpy(chaz_OS.dir_sep, "/");
+ strcpy(chaz_OS.dev_null, "/dev/null");
strcpy(chaz_OS.local_command_start, "./");
}
else {
- /* Bail out because we couldn't find anything like /dev/null. */
- chaz_Util_die("Couldn't find anything like /dev/null");
+ chaz_Util_die("Couldn't identify shell");
}
- if (chaz_Util_verbosity) {
- printf("Detected OS: %s\n", chaz_OS.name);
- }
-}
-
-const char*
-chaz_OS_name(void) {
- return chaz_OS.name;
-}
-
-int
-chaz_OS_is_darwin(void) {
- return memcmp(chaz_OS.name, "darwin", 6) == 0;
-}
-
-int
-chaz_OS_is_cygwin(void) {
- return memcmp(chaz_OS.name, "cygwin", 6) == 0;
-}
-
-const char*
-chaz_OS_exe_ext(void) {
- return chaz_OS.exe_ext;
-}
-
-const char*
-chaz_OS_shared_lib_ext(void) {
- return chaz_OS.shared_lib_ext;
-}
-
-const char*
-chaz_OS_static_lib_ext(void) {
- return chaz_OS.static_lib_ext;
+ free(output);
}
const char*
@@ -232,11 +206,94 @@
else {
chaz_Util_die("Don't know the shell type");
}
- retval = system(quiet_command);
+ if (chaz_OS.run_sh_via_cmd_exe) {
+ retval = chaz_OS_run_sh_via_cmd_exe(quiet_command);
+ }
+ else {
+ retval = system(quiet_command);
+ }
free(quiet_command);
return retval;
}
+static int
+chaz_OS_run_sh_via_cmd_exe(const char *command) {
+ size_t i;
+ size_t size;
+ char *sh_command;
+ char *p;
+ int retval;
+
+ /* Compute size. */
+
+ size = sizeof("sh -c \"\"");
+
+ for (i = 0; command[i] != '\0'; i++) {
+ char c = command[i];
+
+ switch (c) {
+ case '"':
+ case '\\':
+ size += 2;
+ break;
+
+ case '%':
+ case '!':
+ size += 3;
+ break;
+
+ default:
+ size += 1;
+ break;
+ }
+ }
+
+ /* Build sh command. */
+
+ sh_command = (char*)malloc(size);
+ p = sh_command;
+ memcpy(p, "sh -c \"", 7);
+ p += 7;
+
+ /* Escape special characters. */
+
+ for (i = 0; command[i] != '\0'; i++) {
+ char c = command[i];
+
+ switch (c) {
+ case '"':
+ case '\\':
+ /* Escape double quote and backslash. */
+ *p++ = '\\';
+ *p++ = c;
+ break;
+
+ case '%':
+ case '!':
+ /* Break out of double quotes for percent sign and
+ * exclamation mark. This prevents variable expansion. */
+ *p++ = '"';
+ *p++ = c;
+ *p++ = '"';
+ break;
+
+ default:
+ *p++ = c;
+ break;
+ }
+ }
+
+ /* Finish and run sh command. */
+
+ *p++ = '"';
+ *p++ = '\0';
+
+ retval = system(sh_command);
+
+ free(sh_command);
+ return retval;
+}
+
char*
chaz_OS_run_and_capture(const char *command, size_t *output_len) {
char *output;
diff --git a/src/Charmonizer/Core/OperatingSystem.h b/src/Charmonizer/Core/OperatingSystem.h
index 7c01db8..eda3890 100644
--- a/src/Charmonizer/Core/OperatingSystem.h
+++ b/src/Charmonizer/Core/OperatingSystem.h
@@ -68,32 +68,6 @@
void
chaz_OS_rmdir(const char *filepath);
-/* Return the operating system name.
- */
-const char*
-chaz_OS_name(void);
-
-int
-chaz_OS_is_darwin(void);
-
-int
-chaz_OS_is_cygwin(void);
-
-/* Return the extension for an executable on this system.
- */
-const char*
-chaz_OS_exe_ext(void);
-
-/* Return the extension for a shared object on this system.
- */
-const char*
-chaz_OS_shared_lib_ext(void);
-
-/* Return the extension for a static library on this system.
- */
-const char*
-chaz_OS_static_lib_ext(void);
-
/* Return the equivalent of /dev/null on this system.
*/
const char*
diff --git a/src/Charmonizer/Probe/DirManip.c b/src/Charmonizer/Probe/DirManip.c
index c737f22..50e7cdc 100644
--- a/src/Charmonizer/Probe/DirManip.c
+++ b/src/Charmonizer/Probe/DirManip.c
@@ -119,7 +119,6 @@
void
chaz_DirManip_run(void) {
- const char *dir_sep = chaz_OS_dir_sep();
int has_dirent_h = chaz_HeadCheck_check_header("dirent.h");
int has_direct_h = chaz_HeadCheck_check_header("direct.h");
int has_dirent_d_namlen = false;
@@ -176,16 +175,13 @@
chaz_ConfWriter_add_def("MAKEDIR_MODE_IGNORED", "1");
}
- if (strcmp(dir_sep, "\\") == 0) {
+ if (chaz_CC_has_macro("_WIN32") && !chaz_CC_is_cygwin()) {
chaz_ConfWriter_add_def("DIR_SEP", "\"\\\\\"");
chaz_ConfWriter_add_def("DIR_SEP_CHAR", "'\\\\'");
}
else {
- char scratch[5];
- sprintf(scratch, "\"%s\"", dir_sep);
- chaz_ConfWriter_add_def("DIR_SEP", scratch);
- sprintf(scratch, "'%s'", dir_sep);
- chaz_ConfWriter_add_def("DIR_SEP_CHAR", scratch);
+ chaz_ConfWriter_add_def("DIR_SEP", "\"/\"");
+ chaz_ConfWriter_add_def("DIR_SEP_CHAR", "'/'");
}
/* See whether remove works on directories. */