blob: 4664782ad2fa60024b7a0925748d412259d29fdb [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.
*/
/* Source fragment for the Clownfish runtime's charmonizer.c.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Charmonizer/Probe.h"
#include "Charmonizer/Probe/AtomicOps.h"
#include "Charmonizer/Probe/BuildEnv.h"
#include "Charmonizer/Probe/DirManip.h"
#include "Charmonizer/Probe/Floats.h"
#include "Charmonizer/Probe/FuncMacro.h"
#include "Charmonizer/Probe/Headers.h"
#include "Charmonizer/Probe/Integers.h"
#include "Charmonizer/Probe/LargeFiles.h"
#include "Charmonizer/Probe/Memory.h"
#include "Charmonizer/Probe/SymbolVisibility.h"
#include "Charmonizer/Probe/VariadicMacros.h"
#include "Charmonizer/Core/HeaderChecker.h"
#include "Charmonizer/Core/CLI.h"
#include "Charmonizer/Core/ConfWriter.h"
#include "Charmonizer/Core/ConfWriterC.h"
#include "Charmonizer/Core/ConfWriterPerl.h"
#include "Charmonizer/Core/ConfWriterRuby.h"
typedef struct SourceFileContext {
chaz_MakeVar *var;
} SourceFileContext;
static const char cfish_version[] = "0.4.0";
static const char cfish_major_version[] = "0.4";
static void
S_add_compiler_flags(struct chaz_CLI *cli) {
chaz_CFlags *extra_cflags = chaz_CC_get_extra_cflags();
if (chaz_Probe_gcc_version_num()) {
if (getenv("LUCY_VALGRIND")) {
chaz_CFlags_append(extra_cflags,
"-DLUCY_VALGRIND -fno-inline-functions");
}
else if (getenv("LUCY_DEBUG")) {
chaz_CFlags_append(extra_cflags,
"-DLUCY_DEBUG -pedantic -Wall -Wextra -Wno-variadic-macros");
if (chaz_CLI_defined(cli, "enable-perl")) {
chaz_CFlags_append(extra_cflags, "-DPERL_GCC_PEDANTIC");
}
}
/* Only core source files require this -- not our headers and
* autogenerated files. */
chaz_CFlags_append(extra_cflags, "-std=gnu99 -D_GNU_SOURCE");
}
else if (chaz_Probe_msvc_version_num()) {
/* Compile as C++ under MSVC. */
chaz_CFlags_append(extra_cflags, "/TP");
/* Thwart stupid warnings. */
chaz_CFlags_append(extra_cflags, "/D_CRT_SECURE_NO_WARNINGS");
chaz_CFlags_append(extra_cflags, "/D_SCL_SECURE_NO_WARNINGS");
if (chaz_Probe_msvc_version_num() < 1300) {
/* Redefine 'for' to fix broken 'for' scoping under MSVC6. */
chaz_CFlags_append(extra_cflags, "/Dfor=\"if(0);else for\"");
}
}
chaz_CFlags_add_define(extra_cflags, "CFP_CFISH", NULL);
chaz_CFlags_add_define(extra_cflags, "CFP_TESTCFISH", NULL);
chaz_CFlags_hide_symbols(extra_cflags);
}
static int
S_ends_with(const char *string, const char *postfix) {
size_t len = strlen(string);
size_t postfix_len = strlen(postfix);
return len >= postfix_len
&& memcmp(string + len - postfix_len, postfix, postfix_len) == 0;
}
static void
S_c_file_callback(const char *dir, char *file, void *context) {
SourceFileContext *sfc = (SourceFileContext*)context;
const char *dir_sep = chaz_OS_dir_sep();
const char *obj_ext = chaz_CC_obj_ext();
size_t file_len = strlen(file);
char *obj_file;
/* Strip extension */
if (!S_ends_with(file, ".c")) {
chaz_Util_warn("Unexpected C filename: %s", file);
return;
}
file[file_len-2] = '\0';
obj_file = chaz_Util_join("", dir, dir_sep, file, obj_ext, NULL);
chaz_MakeVar_append(sfc->var, obj_file);
free(obj_file);
}
static void
S_cfh_file_callback(const char *dir, char *file, void *context) {
SourceFileContext *sfc = (SourceFileContext*)context;
const char *dir_sep = chaz_OS_dir_sep();
char *cfh_file;
if (!S_ends_with(file, ".cfh")) {
chaz_Util_warn("Unexpected Clownfish header filename: %s", file);
return;
}
cfh_file = chaz_Util_join(dir_sep, dir, file, NULL);
chaz_MakeVar_append(sfc->var, cfh_file);
free(cfh_file);
}
static void
S_write_makefile(struct chaz_CLI *cli) {
SourceFileContext sfc;
const char *base_dir = "..";
const char *dir_sep = chaz_OS_dir_sep();
const char *exe_ext = chaz_OS_exe_ext();
const char *obj_ext = chaz_CC_obj_ext();
char *core_dir = chaz_Util_join(dir_sep, base_dir, "core", NULL);
char *cfc_dir = chaz_Util_join(dir_sep, base_dir, "..", "compiler", "c",
NULL);
char *cfc_exe = chaz_Util_join("", cfc_dir, dir_sep, "cfc", exe_ext,
NULL);
char *test_cfish_exe = chaz_Util_join("", "t", dir_sep, "test_cfish",
exe_ext, NULL);
char *autogen_inc_dir = chaz_Util_join(dir_sep, "autogen", "include",
NULL);
char *autogen_target = chaz_Util_join(dir_sep, "autogen",
"hierarchy.json", NULL);
chaz_MakeFile *makefile;
chaz_MakeVar *var;
chaz_MakeRule *rule;
chaz_MakeRule *clean_rule;
chaz_MakeRule *distclean_rule;
chaz_CFlags *extra_cflags = chaz_CC_get_extra_cflags();
chaz_CFlags *makefile_cflags;
chaz_CFlags *link_flags;
chaz_CFlags *test_cflags;
chaz_Lib *shared_lib;
chaz_Lib *static_lib;
const char *math_library = chaz_Floats_math_library();
char *shared_lib_filename;
char *static_lib_filename;
char *test_command;
char *scratch;
printf("Creating Makefile...\n");
makefile = chaz_MakeFile_new();
/* Directories */
chaz_MakeFile_add_var(makefile, "BASE_DIR", base_dir);
/* C compiler */
chaz_MakeFile_add_var(makefile, "CC", chaz_CC_get_cc());
makefile_cflags = chaz_CC_new_cflags();
chaz_CFlags_enable_optimization(makefile_cflags);
chaz_CFlags_enable_debugging(makefile_cflags);
chaz_CFlags_disable_strict_aliasing(makefile_cflags);
chaz_CFlags_compile_shared_library(makefile_cflags);
if (chaz_CLI_defined(cli, "enable-coverage")) {
chaz_CFlags_enable_code_coverage(makefile_cflags);
}
chaz_CFlags_add_include_dir(makefile_cflags, ".");
chaz_CFlags_add_include_dir(makefile_cflags, autogen_inc_dir);
var = chaz_MakeFile_add_var(makefile, "CFLAGS", NULL);
chaz_MakeVar_append(var, chaz_CFlags_get_string(extra_cflags));
chaz_MakeVar_append(var, chaz_CFlags_get_string(makefile_cflags));
chaz_MakeVar_append(var, chaz_CC_get_cflags());
chaz_CFlags_destroy(makefile_cflags);
/* Object files */
var = chaz_MakeFile_add_var(makefile, "CLOWNFISH_OBJS", NULL);
sfc.var = var;
chaz_Make_list_files("src", "c", S_c_file_callback, &sfc);
chaz_Make_list_files(core_dir, "c", S_c_file_callback, &sfc);
scratch = chaz_Util_join("", "autogen", dir_sep, "source", dir_sep,
"cfish_parcel", obj_ext, NULL);
chaz_MakeVar_append(var, scratch);
free(scratch);
scratch = chaz_Util_join("", "autogen", dir_sep, "source", dir_sep,
"testcfish_parcel", obj_ext, NULL);
chaz_MakeVar_append(var, scratch);
free(scratch);
/* Clownfish header files */
var = chaz_MakeFile_add_var(makefile, "CLOWNFISH_HEADERS", NULL);
sfc.var = var;
chaz_Make_list_files(core_dir, "cfh", S_cfh_file_callback, &sfc);
/* Rules */
shared_lib = chaz_Lib_new("cfish", chaz_Lib_SHARED, cfish_version,
cfish_major_version);
shared_lib_filename = chaz_Lib_filename(shared_lib);
static_lib = chaz_Lib_new("cfish", chaz_Lib_STATIC, cfish_version,
cfish_major_version);
static_lib_filename = chaz_Lib_filename(static_lib);
scratch = chaz_Util_join(" ", shared_lib_filename, static_lib_filename,
NULL);
chaz_MakeFile_add_rule(makefile, "all", scratch);
free(scratch);
rule = chaz_MakeFile_add_rule(makefile, cfc_exe, NULL);
chaz_MakeRule_add_make_command(rule, cfc_dir, NULL);
rule = chaz_MakeFile_add_rule(makefile, autogen_target, cfc_exe);
chaz_MakeRule_add_prereq(rule, "$(CLOWNFISH_HEADERS)");
scratch = chaz_Util_join("", cfc_exe, " --source=", core_dir,
" --dest=autogen --header=cfc_header", NULL);
chaz_MakeRule_add_command(rule, scratch);
free(scratch);
/* Needed for parallel builds. */
scratch = chaz_Util_join(dir_sep, "autogen", "source", "cfish_parcel.c",
NULL);
rule = chaz_MakeFile_add_rule(makefile, scratch, autogen_target);
scratch = chaz_Util_join(dir_sep, "autogen", "source",
"testcfish_parcel.c", NULL);
rule = chaz_MakeFile_add_rule(makefile, scratch, autogen_target);
free(scratch);
chaz_MakeFile_add_rule(makefile, "$(CLOWNFISH_OBJS)", autogen_target);
link_flags = chaz_CC_new_cflags();
chaz_CFlags_enable_debugging(link_flags);
if (math_library) {
chaz_CFlags_add_external_library(link_flags, math_library);
}
if (chaz_CLI_defined(cli, "enable-coverage")) {
chaz_CFlags_enable_code_coverage(link_flags);
}
chaz_MakeFile_add_shared_lib(makefile, shared_lib, "$(CLOWNFISH_OBJS)",
link_flags);
chaz_CFlags_destroy(link_flags);
chaz_MakeFile_add_static_lib(makefile, static_lib, "$(CLOWNFISH_OBJS)");
test_cflags = chaz_CC_new_cflags();
chaz_CFlags_enable_optimization(test_cflags);
chaz_CFlags_add_include_dir(test_cflags, autogen_inc_dir);
chaz_CFlags_add_library(test_cflags, shared_lib);
scratch = chaz_Util_join(dir_sep, "t", "test_cfish.c", NULL);
rule = chaz_MakeFile_add_compiled_exe(makefile, test_cfish_exe, scratch,
test_cflags);
free(scratch);
chaz_MakeRule_add_prereq(rule, shared_lib_filename);
chaz_CFlags_destroy(test_cflags);
rule = chaz_MakeFile_add_rule(makefile, "test", test_cfish_exe);
if (strcmp(chaz_OS_shared_lib_ext(), ".so") == 0) {
test_command = chaz_Util_join(" ", "LD_LIBRARY_PATH=.", test_cfish_exe,
NULL);
}
else {
test_command = chaz_Util_strdup(test_cfish_exe);
}
chaz_MakeRule_add_command(rule, test_command);
if (chaz_CLI_defined(cli, "enable-coverage")) {
rule = chaz_MakeFile_add_rule(makefile, "coverage", test_cfish_exe);
chaz_MakeRule_add_command(rule,
"lcov"
" --zerocounters"
" --directory $(BASE_DIR)");
chaz_MakeRule_add_command(rule, test_command);
chaz_MakeRule_add_command(rule,
"lcov"
" --capture"
" --directory $(BASE_DIR)"
" --base-directory ."
" --rc lcov_branch_coverage=1"
" --output-file clownfish.info");
chaz_MakeRule_add_command(rule,
"genhtml"
" --branch-coverage"
" --output-directory coverage"
" clownfish.info");
}
clean_rule = chaz_MakeFile_clean_rule(makefile);
chaz_MakeRule_add_rm_command(clean_rule, "$(CLOWNFISH_OBJS)");
chaz_MakeRule_add_recursive_rm_command(clean_rule, "autogen");
if (chaz_CLI_defined(cli, "enable-coverage")) {
chaz_MakeRule_add_rm_command(clean_rule, "clownfish.info");
chaz_MakeRule_add_recursive_rm_command(clean_rule, "coverage");
}
if (chaz_Probe_msvc_version_num()) {
chaz_MakeRule_add_rm_command(clean_rule, "test_cfish.obj");
}
chaz_MakeRule_add_make_command(clean_rule, cfc_dir, "clean");
distclean_rule = chaz_MakeFile_distclean_rule(makefile);
chaz_MakeRule_add_make_command(distclean_rule, cfc_dir, "distclean");
chaz_MakeFile_write(makefile);
chaz_MakeFile_destroy(makefile);
chaz_Lib_destroy(shared_lib);
chaz_Lib_destroy(static_lib);
free(core_dir);
free(cfc_dir);
free(cfc_exe);
free(test_cfish_exe);
free(autogen_inc_dir);
free(autogen_target);
free(shared_lib_filename);
free(static_lib_filename);
free(test_command);
}
int main(int argc, const char **argv) {
/* Initialize. */
chaz_CLI *cli
= chaz_CLI_new(argv[0], "charmonizer: Probe C build environment");
chaz_CLI_set_usage(cli, "Usage: charmonizer [OPTIONS] [-- [CFLAGS]]");
{
int result = chaz_Probe_parse_cli_args(argc, argv, cli);
if (!result) {
chaz_Probe_die_usage();
}
chaz_Probe_init(cli);
S_add_compiler_flags(cli);
}
{
int i;
for (i = 0; i < argc; i++) {
if (strncmp(argv[i], "--disable-threads", 17) == 0) {
chaz_CFlags *extra_cflags = chaz_CC_get_extra_cflags();
chaz_CFlags_append(extra_cflags, "-DCFISH_NOTHREADS");
break;
}
}
}
/* Employ integer features but don't define stdint types in charmony.h. */
chaz_ConfWriter_append_conf(
"#define CHY_EMPLOY_INTEGERLIMITS\n"
"#define CHY_EMPLOY_INTEGERLITERALS\n"
"#define CHY_EMPLOY_INTEGERFORMATSTRINGS\n\n"
);
/* Run probe modules. Booleans, DirManip and LargeFiles are only needed for
* the Charmonizer tests.
*/
chaz_BuildEnv_run();
chaz_DirManip_run();
chaz_Headers_run();
chaz_AtomicOps_run();
chaz_FuncMacro_run();
chaz_Booleans_run();
chaz_Integers_run();
chaz_Floats_run();
chaz_LargeFiles_run();
chaz_Memory_run();
chaz_VariadicMacros_run();
/* Write custom postamble. */
chaz_ConfWriter_append_conf(
"#ifdef CHY_HAS_SYS_TYPES_H\n"
" #include <sys/types.h>\n"
"#endif\n\n"
);
chaz_ConfWriter_append_conf(
"#ifdef CHY_HAS_ALLOCA_H\n"
" #include <alloca.h>\n"
"#elif defined(CHY_HAS_MALLOC_H)\n"
" #include <malloc.h>\n"
"#elif defined(CHY_ALLOCA_IN_STDLIB_H)\n"
" #include <stdlib.h>\n"
"#endif\n\n"
);
chaz_ConfWriter_append_conf(
"#ifdef CHY_HAS_WINDOWS_H\n"
" /* Target Windows XP. */\n"
" #ifndef WINVER\n"
" #define WINVER 0x0500\n"
" #endif\n"
" #ifndef _WIN32_WINNT\n"
" #define _WIN32_WINNT 0x0500\n"
" #endif\n"
"#endif\n\n"
);
if (chaz_CLI_defined(cli, "enable-makefile")) {
S_write_makefile(cli);
}
/* Clean up. */
chaz_CLI_destroy(cli);
chaz_Probe_clean_up();
return 0;
}