blob: e08fdfc31b94f2f4aaf8e2b536edeaad1898e9a5 [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 Lucy's charmonizer.c.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Charmonizer/Probe.h"
#include "Charmonizer/Probe/AtomicOps.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/ConfWriter.h"
#include "Charmonizer/Core/ConfWriterC.h"
#include "Charmonizer/Core/ConfWriterPerl.h"
#include "Charmonizer/Core/ConfWriterRuby.h"
typedef struct lucy_MakeFile {
chaz_CLI *cli;
chaz_MakeFile *makefile;
chaz_MakeBinary *lib;
/* Directories. */
const char *base_dir;
char *core_dir;
char *test_dir;
const char *host_src_dir;
char *autogen_src_dir;
char *autogen_inc_dir;
char *lemon_dir;
char *modules_dir;
char *snowstem_dir;
char *snowstem_inc_dir;
char *snowstop_dir;
char *ucd_dir;
char *utf8proc_dir;
char *json_dir;
/* Files. */
char *autogen_target;
const char **autogen_src_files;
/* Clownfish library. */
char *cfish_lib_dir;
const char *cfish_lib_name;
} lucy_MakeFile;
typedef struct SourceFileContext {
chaz_MakeVar *var;
} SourceFileContext;
static const char lucy_version[] = "0.5.0";
static const char lucy_major_version[] = "0.5";
static void
S_add_compiler_flags(struct chaz_CLI *cli);
static lucy_MakeFile*
lucy_MakeFile_new(chaz_CLI *cli);
static void
lucy_MakeFile_destroy(lucy_MakeFile *self);
static void
lucy_MakeFile_write(lucy_MakeFile *self);
static void
lucy_MakeFile_write_c_cfc_rules(lucy_MakeFile *self);
static void
lucy_MakeFile_write_c_test_rules(lucy_MakeFile *self);
static void
S_cfh_file_callback(const char *dir, char *file, void *context);
static int
S_ends_with(const char *string, const char *postfix);
int main(int argc, const char **argv) {
/* Initialize. */
chaz_CLI *cli
= chaz_CLI_new(argv[0], "charmonizer: Probe C build environment");
chaz_CLI_register(cli, "host", "specify host binding language",
CHAZ_CLI_ARG_REQUIRED);
chaz_CLI_register(cli, "disable-threads", "whether to disable threads",
CHAZ_CLI_NO_ARG);
chaz_CLI_register(cli, "clownfish-prefix",
"prefix of Clownfish installation",
CHAZ_CLI_ARG_OPTIONAL);
chaz_CLI_register(cli, "enable-go", "enable Go bindings",
CHAZ_CLI_NO_ARG);
chaz_CLI_set_usage(cli, "Usage: charmonizer [OPTIONS] [-- [CFLAGS]]");
if (!chaz_Probe_parse_cli_args(argc, argv, cli)) {
chaz_Probe_die_usage();
}
chaz_Probe_init(cli);
S_add_compiler_flags(cli);
/* 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 is only needed for the Charmonizer tests. */
chaz_BuildEnv_run();
chaz_DirManip_run();
chaz_Headers_run();
chaz_Booleans_run();
chaz_Integers_run();
chaz_Floats_run();
chaz_LargeFiles_run();
chaz_Memory_run();
chaz_RegularExpressions_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")) {
lucy_MakeFile *mf = lucy_MakeFile_new(cli);
lucy_MakeFile_write(mf);
lucy_MakeFile_destroy(mf);
}
/* Clean up. */
chaz_CLI_destroy(cli);
chaz_Probe_clean_up();
return 0;
}
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");
}
chaz_CFlags_append(extra_cflags,
"-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()) {
if (chaz_Probe_msvc_version_num() < 1800) {
/* Compile as C++ under MSVC11 and below. */
chaz_CFlags_append(extra_cflags, "/TP");
}
chaz_CFlags_append(extra_cflags, "/W3");
/* Thwart stupid warnings. */
chaz_CFlags_append(extra_cflags,
"/D_CRT_SECURE_NO_WARNINGS /D_SCL_SECURE_NO_WARNINGS /wd4996");
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_hide_symbols(extra_cflags);
}
static lucy_MakeFile*
lucy_MakeFile_new(chaz_CLI *cli) {
const char *dir_sep = chaz_OS_dir_sep();
const char *cfish_prefix = chaz_CLI_strval(cli, "clownfish-prefix");
lucy_MakeFile *self = malloc(sizeof(lucy_MakeFile));
self->cli = cli;
self->makefile = chaz_MakeFile_new();
self->lib = NULL;
/* Initialize directories. */
self->base_dir = "..";
self->core_dir = chaz_Util_join(dir_sep, self->base_dir, "core", NULL);
self->test_dir = chaz_Util_join(dir_sep, self->base_dir, "test", NULL);
if (chaz_CLI_defined(cli, "enable-perl")) {
self->host_src_dir = "xs";
}
else if (chaz_CLI_defined(cli, "enable-go")) {
self->host_src_dir = "cfext";
}
else {
self->host_src_dir = "src";
}
self->autogen_src_dir = chaz_Util_join(dir_sep, "autogen", "source", NULL);
self->autogen_inc_dir
= chaz_Util_join(dir_sep, "autogen", "include", NULL);
self->lemon_dir = chaz_Util_join(dir_sep, self->base_dir, "lemon", NULL);
self->modules_dir
= chaz_Util_join(dir_sep, self->base_dir, "modules", NULL);
self->snowstem_dir
= chaz_Util_join(dir_sep, self->modules_dir, "analysis", "snowstem",
"source", NULL);
self->snowstem_inc_dir
= chaz_Util_join(dir_sep, self->snowstem_dir, "include", NULL);
self->snowstop_dir
= chaz_Util_join(dir_sep, self->modules_dir, "analysis", "snowstop",
"source", NULL);
self->ucd_dir
= chaz_Util_join(dir_sep, self->modules_dir, "unicode", "ucd", NULL);
self->utf8proc_dir
= chaz_Util_join(dir_sep, self->modules_dir, "unicode", "utf8proc",
NULL);
self->json_dir
= chaz_Util_join(dir_sep, self->core_dir, "Lucy", "Util", "Json",
NULL);
/* Initialize file names. */
if (chaz_CLI_defined(cli, "enable-perl")) {
static const char *perl_autogen_src_files[] = {
"boot.c",
"callbacks.c",
"lucy_parcel.c",
"testlucy_parcel.c",
NULL
};
self->autogen_src_files = perl_autogen_src_files;
}
else {
static const char *c_autogen_src_files[] = {
"lucy_parcel.c",
"testlucy_parcel.c",
NULL
};
self->autogen_src_files = c_autogen_src_files;
}
self->autogen_target
= chaz_Util_join(dir_sep, "autogen", "hierarchy.json", NULL);
/* Clownfish library. */
if (cfish_prefix) {
self->cfish_lib_dir
= chaz_Util_join(dir_sep, cfish_prefix, "lib", NULL);
}
else {
self->cfish_lib_dir = NULL;
}
if (chaz_CC_binary_format() == CHAZ_CC_BINFMT_PE) {
self->cfish_lib_name = "cfish-0.5";
}
else {
self->cfish_lib_name = "cfish";
}
return self;
}
static void
lucy_MakeFile_destroy(lucy_MakeFile *self) {
chaz_MakeFile_destroy(self->makefile);
free(self->core_dir);
free(self->test_dir);
free(self->autogen_inc_dir);
free(self->autogen_src_dir);
free(self->lemon_dir);
free(self->modules_dir);
free(self->snowstem_dir);
free(self->snowstem_inc_dir);
free(self->snowstop_dir);
free(self->ucd_dir);
free(self->utf8proc_dir);
free(self->json_dir);
free(self->autogen_target);
free(self->cfish_lib_dir);
free(self);
}
static void
lucy_MakeFile_write(lucy_MakeFile *self) {
const char *dir_sep = chaz_OS_dir_sep();
const char *host = chaz_CLI_strval(self->cli, "host");
const char *math_lib = chaz_Floats_math_library();
chaz_MakeVar *var;
chaz_MakeRule *rule;
chaz_CFlags *extra_cflags = chaz_CC_get_extra_cflags();
chaz_CFlags *makefile_cflags;
chaz_CFlags *compile_flags;
chaz_CFlags *link_flags;
char *scratch;
int i;
printf("Creating Makefile...\n");
/* Directories */
chaz_MakeFile_add_var(self->makefile, "BASE_DIR", self->base_dir);
/* C compiler */
chaz_MakeFile_add_var(self->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);
if (chaz_CLI_defined(self->cli, "enable-coverage")) {
chaz_CFlags_enable_code_coverage(makefile_cflags);
}
chaz_CFlags_add_include_dir(makefile_cflags, ".");
chaz_CFlags_add_include_dir(makefile_cflags, self->core_dir);
chaz_CFlags_add_include_dir(makefile_cflags, self->autogen_inc_dir);
chaz_CFlags_add_include_dir(makefile_cflags, self->snowstem_inc_dir);
chaz_CFlags_add_include_dir(makefile_cflags, self->ucd_dir);
chaz_CFlags_add_include_dir(makefile_cflags, self->utf8proc_dir);
var = chaz_MakeFile_add_var(self->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);
/* Binary. */
if (strcmp(host, "c") == 0 || strcmp(host, "perl") == 0) {
chaz_MakeFile_add_rule(self->makefile, "all", "$(LUCY_SHARED_LIB)");
self->lib
= chaz_MakeFile_add_shared_lib(self->makefile, NULL, "lucy",
lucy_version, lucy_major_version);
rule = chaz_MakeFile_add_rule(self->makefile,
"$(LUCY_SHARED_LIB_OBJS)",
self->autogen_target);
/*
* The dependency is actually on JsonParser.h, but make doesn't cope
* well with multiple output files.
*/
scratch = chaz_Util_join(dir_sep, self->json_dir, "JsonParser.c",
NULL);
chaz_MakeRule_add_prereq(rule, scratch);
free(scratch);
link_flags = chaz_MakeBinary_get_link_flags(self->lib);
chaz_CFlags_enable_debugging(link_flags);
if (self->cfish_lib_dir) {
chaz_CFlags_add_library_path(link_flags, self->cfish_lib_dir);
}
if (math_lib) {
chaz_CFlags_add_external_lib(link_flags, math_lib);
}
chaz_CFlags_add_external_lib(link_flags, self->cfish_lib_name);
if (chaz_HeadCheck_check_header("pcre.h")) {
chaz_CFlags_add_external_lib(link_flags, "pcre");
}
if (chaz_CLI_defined(self->cli, "enable-coverage")) {
chaz_CFlags_enable_code_coverage(link_flags);
}
}
else {
chaz_MakeFile_add_rule(self->makefile, "static", "$(LUCY_STATIC_LIB)");
self->lib
= chaz_MakeFile_add_static_lib(self->makefile, NULL, "lucy");
rule = chaz_MakeFile_add_rule(self->makefile,
"$(LUCY_STATIC_LIB_OBJS)",
self->autogen_target);
scratch = chaz_Util_join(dir_sep, self->json_dir, "JsonParser.c",
NULL);
chaz_MakeRule_add_prereq(rule, scratch);
free(scratch);
}
chaz_MakeBinary_add_src_dir(self->lib, self->host_src_dir);
chaz_MakeBinary_add_src_dir(self->lib, self->core_dir);
chaz_MakeBinary_add_src_dir(self->lib, self->test_dir);
chaz_MakeBinary_add_src_dir(self->lib, self->snowstem_dir);
chaz_MakeBinary_add_src_dir(self->lib, self->snowstop_dir);
chaz_MakeBinary_add_src_dir(self->lib, self->utf8proc_dir);
chaz_MakeBinary_add_src_file(self->lib, self->json_dir, "JsonParser.c");
for (i = 0; self->autogen_src_files[i] != NULL; ++i) {
chaz_MakeBinary_add_src_file(self->lib, self->autogen_src_dir,
self->autogen_src_files[i]);
}
compile_flags = chaz_MakeBinary_get_compile_flags(self->lib);
chaz_CFlags_add_define(compile_flags, "CFP_LUCY", NULL);
chaz_CFlags_add_define(compile_flags, "CFP_TESTLUCY", NULL);
/* Additional rules. */
chaz_MakeFile_add_lemon_exe(self->makefile, self->lemon_dir);
scratch = chaz_Util_join(dir_sep, self->json_dir, "JsonParser", NULL);
chaz_MakeFile_add_lemon_grammar(self->makefile, scratch);
free(scratch);
if (strcmp(host, "c") == 0) {
lucy_MakeFile_write_c_cfc_rules(self);
lucy_MakeFile_write_c_test_rules(self);
}
/* Needed for parallel builds. */
for (i = 0; self->autogen_src_files[i] != NULL; ++i) {
char *path = chaz_Util_join("", self->autogen_src_dir, dir_sep,
self->autogen_src_files[i], NULL);
rule = chaz_MakeFile_add_rule(self->makefile, path,
self->autogen_target);
free(path);
}
rule = chaz_MakeFile_clean_rule(self->makefile);
chaz_MakeRule_add_recursive_rm_command(rule, "autogen");
chaz_MakeFile_write(self->makefile);
}
static void
lucy_MakeFile_write_c_cfc_rules(lucy_MakeFile *self) {
SourceFileContext sfc;
chaz_MakeRule *rule;
const char *dir_sep = chaz_OS_dir_sep();
const char *cfish_prefix = chaz_CLI_strval(self->cli, "clownfish-prefix");
char *cfc_command;
sfc.var = chaz_MakeFile_add_var(self->makefile, "CLOWNFISH_HEADERS", NULL);
chaz_Make_list_files(self->core_dir, "cfh", S_cfh_file_callback, &sfc);
chaz_Make_list_files(self->test_dir, "cfh", S_cfh_file_callback, &sfc);
rule = chaz_MakeFile_add_rule(self->makefile, self->autogen_target, NULL);
chaz_MakeRule_add_prereq(rule, "$(CLOWNFISH_HEADERS)");
if (cfish_prefix == NULL) {
cfc_command
= chaz_Util_join("", "cfc --source=", self->core_dir,
" --source=", self->test_dir,
" --dest=autogen --header=cfc_header", NULL);
}
else {
cfc_command
= chaz_Util_join("", cfish_prefix, dir_sep, "bin", dir_sep,
"cfc --source=", self->core_dir, " --source=",
self->test_dir, " --include=", cfish_prefix,
dir_sep, "share", dir_sep, "clownfish", dir_sep,
"include --dest=autogen --header=cfc_header",
NULL);
}
chaz_MakeRule_add_command(rule, cfc_command);
free(cfc_command);
}
static void
lucy_MakeFile_write_c_test_rules(lucy_MakeFile *self) {
chaz_MakeBinary *test_exe;
chaz_CFlags *link_flags;
chaz_MakeRule *rule;
test_exe = chaz_MakeFile_add_exe(self->makefile, "t", "test_lucy");
chaz_MakeBinary_add_src_file(test_exe, "t", "test_lucy.c");
chaz_MakeFile_add_rule(self->makefile, "$(TEST_LUCY_EXE_OBJS)",
self->autogen_target);
link_flags = chaz_MakeBinary_get_link_flags(test_exe);
chaz_CFlags_add_shared_lib(link_flags, NULL, "lucy", lucy_major_version);
chaz_MakeBinary_add_prereq(test_exe, "$(LUCY_SHARED_LIB)");
if (self->cfish_lib_dir) {
chaz_CFlags_add_library_path(link_flags, self->cfish_lib_dir);
}
chaz_CFlags_add_external_lib(link_flags, self->cfish_lib_name);
chaz_CFlags_add_rpath(link_flags, "\"$$PWD\"");
if (self->cfish_lib_dir) {
chaz_CFlags_add_rpath(link_flags, self->cfish_lib_dir);
}
rule = chaz_MakeFile_add_rule(self->makefile, "test", "$(TEST_LUCY_EXE)");
chaz_MakeRule_add_command(rule, "$(TEST_LUCY_EXE)");
if (chaz_CLI_defined(self->cli, "enable-coverage")) {
rule = chaz_MakeFile_add_rule(self->makefile, "coverage",
"$(TEST_LUCY_EXE)");
chaz_MakeRule_add_command(rule,
"lcov"
" --zerocounters"
" --directory $(BASE_DIR)");
chaz_MakeRule_add_command(rule, "$(TEST_LUCY_EXE)");
chaz_MakeRule_add_command(rule,
"lcov"
" --capture"
" --directory $(BASE_DIR)"
" --base-directory ."
" --rc lcov_branch_coverage=1"
" --output-file lucy.info");
chaz_MakeRule_add_command(rule,
"genhtml"
" --branch-coverage"
" --output-directory coverage"
" lucy.info");
rule = chaz_MakeFile_clean_rule(self->makefile);
chaz_MakeRule_add_rm_command(rule, "lucy.info");
chaz_MakeRule_add_recursive_rm_command(rule, "coverage");
}
}
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 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;
}