blob: bbd2e32718da3536ffc989ed643904a373a0865d [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 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);
}
}