blob: 2897fa89107e2bb3e98aba2c8232b0dc029cd192 [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"
struct lucy_CLIArgs {
const char *clownfish_prefix;
};
typedef struct SourceFileContext {
chaz_MakeVar *var;
} SourceFileContext;
static const char lucy_version[] = "0.4.1";
static const char lucy_major_version[] = "0.4";
static void
S_add_compiler_flags(struct chaz_CLIArgs *args) {
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 (args->charmony_pm) {
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_LUCY", NULL);
chaz_CFlags_add_define(extra_cflags, "CFP_TESTLUCY", 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';
if (!S_ends_with(file, "JsonParser")) {
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_CLIArgs *chaz_args,
struct lucy_CLIArgs *lucy_args) {
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();
const char *math_lib = chaz_Floats_math_library();
const char *cfish_prefix = lucy_args->clownfish_prefix;
char *core_dir = chaz_Util_join(dir_sep, base_dir, "core", NULL);
char *lemon_dir = chaz_Util_join(dir_sep, base_dir, "lemon", NULL);
char *modules_dir = chaz_Util_join(dir_sep, base_dir, "modules", NULL);
char *snowstem_dir = chaz_Util_join(dir_sep, modules_dir, "analysis",
"snowstem", "source", NULL);
char *snowstop_dir = chaz_Util_join(dir_sep, modules_dir, "analysis",
"snowstop", "source", NULL);
char *ucd_dir = chaz_Util_join(dir_sep, modules_dir, "unicode",
"ucd", NULL);
char *utf8proc_dir = chaz_Util_join(dir_sep, modules_dir, "unicode",
"utf8proc", NULL);
char *json_parser = chaz_Util_join(dir_sep, core_dir, "Lucy", "Util",
"Json", "JsonParser", NULL);
char *test_lucy_exe = chaz_Util_join("", "t", dir_sep, "test_lucy",
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);
char *snowstem_inc_dir
= chaz_Util_join(dir_sep, snowstem_dir, "include", NULL);
chaz_MakeFile *makefile;
chaz_MakeVar *var;
chaz_MakeRule *rule;
chaz_MakeRule *clean_rule;
chaz_CFlags *extra_cflags = chaz_CC_get_extra_cflags();
chaz_CFlags *makefile_cflags;
chaz_CFlags *link_flags;
chaz_CFlags *test_cflags;
chaz_SharedLib *lib;
const char *cfish_lib_name = NULL;
char *cfish_lib_dir = NULL;
char *lib_filename = NULL;
char *test_command = NULL;
char *scratch = NULL;
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_args->code_coverage) {
chaz_CFlags_enable_code_coverage(makefile_cflags);
}
chaz_CFlags_add_include_dir(makefile_cflags, ".");
chaz_CFlags_add_include_dir(makefile_cflags, core_dir);
chaz_CFlags_add_include_dir(makefile_cflags, autogen_inc_dir);
chaz_CFlags_add_include_dir(makefile_cflags, snowstem_inc_dir);
chaz_CFlags_add_include_dir(makefile_cflags, ucd_dir);
chaz_CFlags_add_include_dir(makefile_cflags, utf8proc_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, "LUCY_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);
chaz_Make_list_files(snowstem_dir, "c", S_c_file_callback, &sfc);
chaz_Make_list_files(snowstop_dir, "c", S_c_file_callback, &sfc);
chaz_Make_list_files(utf8proc_dir, "c", S_c_file_callback, &sfc);
scratch = chaz_Util_join("", json_parser, obj_ext, NULL);
chaz_MakeVar_append(var, scratch);
free(scratch);
scratch = chaz_Util_join("", "autogen", dir_sep, "source", dir_sep,
"lucy_parcel", obj_ext, NULL);
chaz_MakeVar_append(var, scratch);
free(scratch);
scratch = chaz_Util_join("", "autogen", dir_sep, "source", dir_sep,
"testlucy_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 */
lib = chaz_SharedLib_new("lucy", lucy_version, lucy_major_version);
lib_filename = chaz_SharedLib_filename(lib);
chaz_MakeFile_add_rule(makefile, "all", lib_filename);
chaz_MakeFile_add_lemon_exe(makefile, lemon_dir);
chaz_MakeFile_add_lemon_grammar(makefile, json_parser);
rule = chaz_MakeFile_add_rule(makefile, autogen_target, NULL);
chaz_MakeRule_add_prereq(rule, "$(CLOWNFISH_HEADERS)");
if (cfish_prefix == NULL) {
scratch = chaz_Util_join("", "cfc --source=", core_dir,
" --dest=autogen --header=cfc_header", NULL);
}
else {
scratch = chaz_Util_join("", cfish_prefix, dir_sep, "bin", dir_sep,
"cfc --source=", core_dir, " --include=",
cfish_prefix, dir_sep, "share", dir_sep,
"clownfish", dir_sep, "include",
" --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", "lucy_parcel.c",
NULL);
rule = chaz_MakeFile_add_rule(makefile, scratch, autogen_target);
free(scratch);
scratch = chaz_Util_join(dir_sep, "autogen", "source", "testlucy_parcel.c",
NULL);
rule = chaz_MakeFile_add_rule(makefile, scratch, autogen_target);
free(scratch);
rule = chaz_MakeFile_add_rule(makefile, "$(LUCY_OBJS)", autogen_target);
/*
* The dependency is actually on JsonParser.h, but make doesn't cope
* well with multiple output files.
*/
scratch = chaz_Util_join(".", json_parser, "c", NULL);
chaz_MakeRule_add_prereq(rule, scratch);
free(scratch);
if (cfish_prefix) {
cfish_lib_dir = chaz_Util_join(dir_sep, cfish_prefix, "lib", NULL);
}
if (strcmp(chaz_OS_shared_lib_ext(), ".dll") == 0) {
cfish_lib_name = "cfish-0.4";
}
else {
cfish_lib_name = "cfish";
}
link_flags = chaz_CC_new_cflags();
chaz_CFlags_enable_debugging(link_flags);
if (cfish_lib_dir) {
chaz_CFlags_add_library_path(link_flags, cfish_lib_dir);
}
if (math_lib) {
chaz_CFlags_add_external_library(link_flags, math_lib);
}
chaz_CFlags_add_external_library(link_flags, cfish_lib_name);
if (chaz_HeadCheck_check_header("pcre.h")) {
chaz_CFlags_add_external_library(link_flags, "pcre");
}
if (chaz_args->code_coverage) {
chaz_CFlags_enable_code_coverage(link_flags);
}
rule = chaz_MakeFile_add_shared_lib(makefile, lib, "$(LUCY_OBJS)",
link_flags);
chaz_CFlags_destroy(link_flags);
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, lib);
if (cfish_lib_dir) {
chaz_CFlags_add_library_path(link_flags, cfish_lib_dir);
}
chaz_CFlags_add_external_library(test_cflags, cfish_lib_name);
scratch = chaz_Util_join(dir_sep, "t", "test_lucy.c", NULL);
rule = chaz_MakeFile_add_compiled_exe(makefile, test_lucy_exe, scratch,
test_cflags);
free(scratch);
chaz_MakeRule_add_prereq(rule, lib_filename);
chaz_CFlags_destroy(test_cflags);
rule = chaz_MakeFile_add_rule(makefile, "test", test_lucy_exe);
if (strcmp(chaz_OS_shared_lib_ext(), ".so") == 0) {
if (cfish_lib_dir) {
test_command
= chaz_Util_join("", "LD_LIBRARY_PATH=.:", cfish_lib_dir,
":$$LD_LIBRARY_PATH ", test_lucy_exe, NULL);
}
else {
test_command
= chaz_Util_join(" ", "LD_LIBRARY_PATH=.:$$LD_LIBRARY_PATH",
test_lucy_exe, NULL);
}
}
else {
test_command = chaz_Util_strdup(test_lucy_exe);
}
chaz_MakeRule_add_command(rule, test_command);
if (chaz_args->code_coverage) {
rule = chaz_MakeFile_add_rule(makefile, "coverage", test_lucy_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 lucy.info");
chaz_MakeRule_add_command(rule,
"genhtml"
" --branch-coverage"
" --output-directory coverage"
" lucy.info");
}
clean_rule = chaz_MakeFile_clean_rule(makefile);
if (chaz_Make_shell_type() == CHAZ_OS_CMD_EXE) {
/*
* The length of the command would exceed the limit of 8191
* characters. As a work-around, delete all .obj files in BASE_DIR
* using 'del /s /q'.
*/
scratch = chaz_Util_join("", "del /s /q ", base_dir, "\\*", obj_ext,
NULL);
chaz_MakeRule_add_command(clean_rule, scratch);
free(scratch);
}
else {
chaz_MakeRule_add_rm_command(clean_rule, "$(LUCY_OBJS)");
}
chaz_MakeRule_add_recursive_rm_command(clean_rule, "autogen");
if (chaz_args->code_coverage) {
chaz_MakeRule_add_rm_command(clean_rule, "lucy.info");
chaz_MakeRule_add_recursive_rm_command(clean_rule, "coverage");
}
chaz_MakeFile_distclean_rule(makefile);
chaz_MakeFile_write(makefile);
chaz_MakeFile_destroy(makefile);
chaz_SharedLib_destroy(lib);
free(core_dir);
free(lemon_dir);
free(modules_dir);
free(snowstem_dir);
free(snowstop_dir);
free(ucd_dir);
free(utf8proc_dir);
free(json_parser);
free(test_lucy_exe);
free(autogen_inc_dir);
free(autogen_target);
free(snowstem_inc_dir);
free(cfish_lib_dir);
free(lib_filename);
free(test_command);
}
int main(int argc, const char **argv) {
/* Initialize. */
struct chaz_CLIArgs chaz_args;
struct lucy_CLIArgs lucy_args = { NULL };
{
int result = chaz_Probe_parse_cli_args(argc, argv, &chaz_args);
if (!result) {
chaz_Probe_die_usage();
}
chaz_Probe_init(&chaz_args);
S_add_compiler_flags(&chaz_args);
}
{
int i;
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
if (strncmp(arg, "--disable-threads", 17) == 0) {
chaz_CFlags *extra_cflags = chaz_CC_get_extra_cflags();
chaz_CFlags_append(extra_cflags, "-DCFISH_NOTHREADS");
break;
}
else if (memcmp(arg, "--clownfish-prefix=", 19) == 0) {
lucy_args.clownfish_prefix = arg + 19;
}
}
}
/* 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_args.write_makefile) {
S_write_makefile(&chaz_args, &lucy_args);
}
/* Clean up. */
chaz_Probe_clean_up();
return 0;
}