| /* 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.3"; |
| static const char lucy_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_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_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(); |
| const char *math_lib = chaz_Floats_math_library(); |
| const char *cfish_prefix = chaz_CLI_strval(cli, "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 *test_lucy_obj = chaz_Util_join("", "t", dir_sep, "test_lucy", |
| obj_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_Lib *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_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, 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_Lib_new("lucy", chaz_Lib_SHARED, lucy_version, |
| lucy_major_version); |
| lib_filename = chaz_Lib_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_CLI_defined(cli, "enable-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); |
| rule = chaz_MakeFile_override_cflags(makefile, test_lucy_obj, test_cflags); |
| chaz_MakeRule_add_prereq(rule, autogen_target); |
| chaz_CFlags_destroy(test_cflags); |
| |
| link_flags = chaz_CC_new_cflags(); |
| chaz_CFlags_add_library(link_flags, lib); |
| if (cfish_lib_dir) { |
| chaz_CFlags_add_library_path(link_flags, cfish_lib_dir); |
| } |
| chaz_CFlags_add_external_library(link_flags, cfish_lib_name); |
| rule = chaz_MakeFile_add_exe(makefile, test_lucy_exe, test_lucy_obj, |
| link_flags); |
| chaz_MakeRule_add_prereq(rule, lib_filename); |
| chaz_CFlags_destroy(link_flags); |
| |
| rule = chaz_MakeFile_add_rule(makefile, "test", test_lucy_exe); |
| if (cfish_lib_dir) { |
| chaz_MakeRule_add_command_with_libpath(rule, test_lucy_exe, ".", |
| cfish_lib_dir, NULL); |
| } |
| else { |
| chaz_MakeRule_add_command_with_libpath(rule, test_lucy_exe, ".", NULL); |
| } |
| |
| if (chaz_CLI_defined(cli, "enable-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_rm_command(clean_rule, test_lucy_obj); |
| chaz_MakeRule_add_recursive_rm_command(clean_rule, "autogen"); |
| |
| if (chaz_CLI_defined(cli, "enable-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_Lib_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(test_lucy_obj); |
| 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. */ |
| chaz_CLI *cli |
| = chaz_CLI_new(argv[0], "charmonizer: Probe C build environment"); |
| 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_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")) { |
| S_write_makefile(cli); |
| } |
| |
| /* Clean up. */ |
| chaz_CLI_destroy(cli); |
| chaz_Probe_clean_up(); |
| |
| return 0; |
| } |
| |
| |