| /* 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 compiler's charmonizer.c. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include "Charmonizer/Probe.h" |
| #include "Charmonizer/Probe/Integers.h" |
| |
| typedef struct SourceFileContext { |
| chaz_MakeBinary *core_binary; |
| chaz_MakeBinary *test_binary; |
| } SourceFileContext; |
| |
| static const char cfc_version[] = "0.6.3"; |
| static const char cfc_major_version[] = "0.6"; |
| |
| static void |
| S_add_compiler_flags(struct chaz_CLI *cli); |
| |
| static void |
| S_write_makefile(struct chaz_CLI *cli); |
| |
| static void |
| S_source_file_callback(const char *dir, char *file, void *context); |
| |
| 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, "with-system-cmark", "use system cmark library", |
| CHAZ_CLI_NO_ARG); |
| 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); |
| } |
| |
| /* Define stdint types in charmony.h. */ |
| chaz_ConfWriter_append_conf("#define CHY_EMPLOY_INTEGERTYPES\n\n"); |
| chaz_ConfWriter_append_conf("#define CHY_EMPLOY_INTEGERLITERALS\n\n"); |
| chaz_ConfWriter_append_conf("#define CHY_EMPLOY_INTEGERFORMATSTRINGS\n\n"); |
| |
| /* Run probe modules. */ |
| chaz_BuildEnv_run(); |
| chaz_DirManip_run(); |
| chaz_Headers_run(); |
| chaz_FuncMacro_run(); |
| chaz_Booleans_run(); |
| chaz_Integers_run(); |
| chaz_Strings_run(); |
| chaz_Memory_run(); |
| chaz_SymbolVisibility_run(); |
| chaz_UnusedVars_run(); |
| chaz_VariadicMacros_run(); |
| |
| if (chaz_CLI_defined(cli, "enable-makefile")) { |
| S_write_makefile(cli); |
| } |
| |
| /* Needed by cmark. */ |
| if (chaz_HeadCheck_defines_symbol("va_copy", "#include <stdarg.h>")) { |
| chaz_ConfWriter_append_conf("#define CHY_HAS_VA_COPY\n\n"); |
| } |
| |
| /* 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()) { |
| chaz_CFlags_append(extra_cflags, |
| "-pedantic -Wall -Wextra -Wno-variadic-macros" |
| " -Wno-overlength-strings"); |
| if (strcmp(chaz_CLI_strval(cli, "host"), "perl") == 0) { |
| chaz_CFlags_append(extra_cflags, "-DPERL_GCC_PEDANTIC"); |
| } |
| |
| /* Tell GCC explicitly to run with maximum options. */ |
| 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"); |
| } |
| else { |
| /* Fix warnings in flex generated code. */ |
| chaz_CFlags_append(extra_cflags, "/DYY_USE_CONST"); |
| } |
| |
| 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\""); |
| } |
| } |
| } |
| |
| static void |
| S_write_makefile(struct chaz_CLI *cli) { |
| SourceFileContext sfc; |
| |
| int with_system_cmark = chaz_CLI_defined(cli, "with-system-cmark"); |
| |
| const char *base_dir = ".."; |
| const char *dir_sep = chaz_OS_dir_sep(); |
| const char *host = chaz_CLI_strval(cli, "host"); |
| |
| char *lemon_dir = chaz_Util_join(dir_sep, base_dir, "..", "lemon", |
| NULL); |
| char *src_dir = chaz_Util_join(dir_sep, base_dir, "src", NULL); |
| char *include_dir = chaz_Util_join(dir_sep, base_dir, "include", NULL); |
| char *cmark_dir = chaz_Util_join(dir_sep, base_dir, "modules", |
| "CommonMark", "src", NULL); |
| char *parse_header = chaz_Util_join(dir_sep, src_dir, "CFCParseHeader", |
| NULL); |
| char *parse_header_c = chaz_Util_join(dir_sep, src_dir, "CFCParseHeader.c", |
| NULL); |
| |
| chaz_MakeFile *makefile = NULL; |
| chaz_MakeBinary *lib = NULL; |
| chaz_MakeBinary *exe = NULL; |
| chaz_MakeBinary *test_exe = NULL; |
| chaz_MakeVar *var = NULL; |
| chaz_MakeRule *rule = NULL; |
| |
| chaz_CFlags *extra_cflags = chaz_CC_get_extra_cflags(); |
| chaz_CFlags *makefile_cflags; |
| chaz_CFlags *compile_flags; |
| chaz_CFlags *link_flags; |
| |
| 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_add_include_dir(makefile_cflags, "."); |
| chaz_CFlags_add_include_dir(makefile_cflags, include_dir); |
| chaz_CFlags_add_include_dir(makefile_cflags, src_dir); |
| if (!with_system_cmark) { |
| chaz_CFlags_add_include_dir(makefile_cflags, cmark_dir); |
| } |
| if (chaz_CLI_defined(cli, "enable-coverage")) { |
| chaz_CFlags_enable_code_coverage(makefile_cflags); |
| } |
| |
| var = chaz_MakeFile_add_var(makefile, "CFC_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); |
| |
| /* Binaries. */ |
| |
| if (strcmp(host, "c") == 0) { |
| chaz_MakeFile_add_rule(makefile, "all", "$(CFC_EXE)"); |
| |
| exe = chaz_MakeFile_add_exe(makefile, NULL, "cfc"); |
| chaz_MakeBinary_add_src_file(exe, NULL, "cfc.c"); |
| chaz_MakeBinary_add_prereq(exe, "$(CFC_STATIC_LIB)"); |
| link_flags = chaz_MakeBinary_get_link_flags(exe); |
| chaz_CFlags_append(link_flags, "$(CFC_STATIC_LIB)"); |
| if (with_system_cmark) { |
| chaz_CFlags_add_external_lib(link_flags, "cmark"); |
| } |
| compile_flags = chaz_MakeBinary_get_compile_flags(exe); |
| chaz_CFlags_append(compile_flags, "$(CFC_CFLAGS)"); |
| |
| test_exe = chaz_MakeFile_add_exe(makefile, "t", "test_cfc"); |
| chaz_MakeBinary_add_src_file(test_exe, "t", "test_cfc.c"); |
| chaz_MakeBinary_add_prereq(test_exe, "$(CFC_STATIC_LIB)"); |
| link_flags = chaz_MakeBinary_get_link_flags(test_exe); |
| chaz_CFlags_append(link_flags, "$(CFC_STATIC_LIB)"); |
| if (with_system_cmark) { |
| chaz_CFlags_add_external_lib(link_flags, "cmark"); |
| } |
| compile_flags = chaz_MakeBinary_get_compile_flags(test_exe); |
| chaz_CFlags_append(compile_flags, "$(CFC_CFLAGS)"); |
| } |
| |
| lib = chaz_MakeFile_add_static_lib(makefile, NULL, "cfc"); |
| chaz_MakeFile_add_rule(makefile, "static", "$(CFC_STATIC_LIB)"); |
| compile_flags = chaz_MakeBinary_get_compile_flags(lib); |
| chaz_CFlags_append(compile_flags, "$(CFC_CFLAGS)"); |
| |
| /* |
| * The dependency is actually on CFCParseHeader.h, but make doesn't cope |
| * well with multiple output files. |
| */ |
| chaz_MakeFile_add_rule(makefile, "$(CFC_STATIC_LIB_OBJS)", parse_header_c); |
| |
| sfc.core_binary = lib; |
| sfc.test_binary = test_exe; |
| chaz_Make_list_files(src_dir, "c", S_source_file_callback, &sfc); |
| if (!with_system_cmark) { |
| chaz_Make_list_files(cmark_dir, "c", S_source_file_callback, &sfc); |
| } |
| |
| chaz_MakeBinary_add_src_file(lib, src_dir, "CFCParseHeader.c"); |
| |
| /* Rules */ |
| |
| chaz_MakeFile_add_lemon_exe(makefile, lemon_dir); |
| chaz_MakeFile_add_lemon_grammar(makefile, parse_header); |
| |
| if (strcmp(host, "c") == 0) { |
| rule = chaz_MakeFile_add_rule(makefile, "test", "all"); |
| chaz_MakeRule_add_prereq(rule, "$(TEST_CFC_EXE)"); |
| chaz_MakeRule_add_command(rule, "$(TEST_CFC_EXE)"); |
| |
| if (chaz_OS_shell_type() == CHAZ_OS_POSIX) { |
| rule = chaz_MakeFile_add_rule(makefile, "valgrind", "all"); |
| chaz_MakeRule_add_prereq(rule, "$(TEST_CFC_EXE)"); |
| chaz_MakeRule_add_command(rule, |
| "CLOWNFISH_VALGRIND=1 valgrind" |
| " --leak-check=full" |
| " $(TEST_CFC_EXE)"); |
| } |
| |
| if (chaz_CLI_defined(cli, "enable-coverage")) { |
| link_flags = chaz_MakeBinary_get_link_flags(test_exe); |
| chaz_CFlags_enable_code_coverage(link_flags); |
| |
| rule = chaz_MakeFile_add_rule(makefile, "coverage", |
| "$(TEST_CFC_EXE)"); |
| chaz_MakeRule_add_command(rule, |
| "lcov" |
| " --zerocounters" |
| " --directory $(BASE_DIR)"); |
| chaz_MakeRule_add_command(rule, "$(TEST_CFC_EXE)"); |
| chaz_MakeRule_add_command(rule, |
| "lcov" |
| " --capture" |
| " --directory $(BASE_DIR)" |
| " --base-directory ." |
| " --rc lcov_branch_coverage=1" |
| " --output-file cfc.info"); |
| chaz_MakeRule_add_command(rule, |
| "genhtml" |
| " --branch-coverage" |
| " --output-directory coverage" |
| " cfc.info"); |
| |
| rule = chaz_MakeFile_clean_rule(makefile); |
| chaz_MakeRule_add_rm_command(rule, "cfc.info"); |
| chaz_MakeRule_add_recursive_rm_command(rule, "coverage"); |
| } |
| } |
| |
| chaz_MakeFile_write(makefile); |
| |
| chaz_MakeFile_destroy(makefile); |
| free(lemon_dir); |
| free(src_dir); |
| free(include_dir); |
| free(cmark_dir); |
| free(parse_header); |
| free(parse_header_c); |
| } |
| |
| static void |
| S_source_file_callback(const char *dir, char *file, void *context) { |
| SourceFileContext *sfc = (SourceFileContext*)context; |
| |
| if (strcmp(file, "CFCParseHeader.c") == 0) { return; } |
| |
| if (strlen(file) >= 7 && memcmp(file, "CFCTest", 7) == 0) { |
| if (sfc->test_binary) { |
| chaz_MakeBinary_add_src_file(sfc->test_binary, dir, file); |
| } |
| } |
| else { |
| chaz_MakeBinary_add_src_file(sfc->core_binary, dir, file); |
| } |
| } |
| |
| |