blob: 6e27e0e49787ccffcf5ee7aec9c0df8fb49b2ed2 [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_MakeVar *common_objs;
chaz_MakeVar *test_cfc_objs;
chaz_MakeVar *common_test_objs;
} SourceFileContext;
static const char cfc_version[] = "0.5.1";
static const char cfc_major_version[] = "0.5";
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_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()) {
if (getenv("LUCY_VALGRIND")) {
chaz_CFlags_append(extra_cflags, "-fno-inline-functions");
}
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_add_staticlib_makefile_target(chaz_MakeFile *makefile) {
chaz_Lib *static_lib = chaz_Lib_new("cfc", chaz_Lib_STATIC, cfc_version,
cfc_major_version);
char *static_lib_filename = chaz_Lib_filename(static_lib);
chaz_MakeFile_add_rule(makefile, "static", static_lib_filename);
chaz_MakeFile_add_static_lib(makefile, static_lib, "$(COMMON_OBJS)");
free(static_lib_filename);
chaz_Lib_destroy(static_lib);
}
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();
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 *cfc_exe = chaz_Util_join("", "cfc", exe_ext, NULL);
char *test_cfc_exe = chaz_Util_join("", "t", dir_sep, "test_cfc", exe_ext,
NULL);
char *scratch;
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;
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);
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, "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 */
sfc.common_objs = chaz_MakeFile_add_var(makefile, "COMMON_OBJS", NULL);
sfc.test_cfc_objs = chaz_MakeFile_add_var(makefile, "TEST_CFC_OBJS", NULL);
sfc.common_test_objs = chaz_MakeFile_add_var(makefile, "COMMON_TEST_OBJS", NULL);
chaz_Make_list_files(src_dir, "c", S_source_file_callback, &sfc);
chaz_Make_list_files(cmark_dir, "c", S_source_file_callback, &sfc);
scratch = chaz_Util_join("", parse_header, obj_ext, NULL);
chaz_MakeVar_append(sfc.common_objs, scratch);
free(scratch);
scratch = chaz_Util_join("", "t", dir_sep, "test_cfc", obj_ext, NULL);
chaz_MakeVar_append(sfc.test_cfc_objs, scratch);
free(scratch);
scratch = chaz_Util_join("", "cfc", obj_ext, NULL);
chaz_MakeFile_add_var(makefile, "CFC_OBJS", scratch);
free(scratch);
/* Rules */
chaz_MakeFile_add_rule(makefile, "all", cfc_exe);
S_add_staticlib_makefile_target(makefile);
chaz_MakeFile_add_lemon_exe(makefile, lemon_dir);
chaz_MakeFile_add_lemon_grammar(makefile, parse_header);
/*
* The dependency is actually on CFCParseHeader.h, but make doesn't cope
* well with multiple output files.
*/
scratch = chaz_Util_join(".", parse_header, "c", NULL);
chaz_MakeFile_add_rule(makefile, "$(COMMON_OBJS)", scratch);
free(scratch);
link_flags = chaz_CC_new_cflags();
if (chaz_CC_msvc_version_num()) {
chaz_CFlags_append(link_flags, "/nologo");
}
if (chaz_CLI_defined(cli, "enable-coverage")) {
chaz_CFlags_enable_code_coverage(link_flags);
}
if (strcmp(chaz_CLI_strval(cli, "host"), "c") == 0) {
chaz_MakeFile_add_exe(makefile, cfc_exe, "$(COMMON_OBJS) $(CFC_OBJS)",
link_flags);
chaz_MakeFile_add_exe(makefile, test_cfc_exe,
"$(COMMON_OBJS) $(COMMON_TEST_OBJS) $(TEST_CFC_OBJS)",
link_flags);
}
chaz_CFlags_destroy(link_flags);
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_CLI_defined(cli, "enable-coverage")) {
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");
}
clean_rule = chaz_MakeFile_clean_rule(makefile);
chaz_MakeRule_add_rm_command(clean_rule, "$(COMMON_OBJS)");
chaz_MakeRule_add_rm_command(clean_rule, "$(COMMON_TEST_OBJS)");
chaz_MakeRule_add_rm_command(clean_rule, "$(CFC_OBJS)");
chaz_MakeRule_add_rm_command(clean_rule, "$(TEST_CFC_OBJS)");
if (chaz_CLI_defined(cli, "enable-coverage")) {
chaz_MakeRule_add_rm_command(clean_rule, "cfc.info");
chaz_MakeRule_add_recursive_rm_command(clean_rule, "coverage");
}
if (chaz_Probe_msvc_version_num()) {
chaz_MakeRule_add_rm_command(clean_rule, "lemon.obj");
}
chaz_MakeFile_write(makefile);
chaz_MakeFile_destroy(makefile);
free(lemon_dir);
free(src_dir);
free(include_dir);
free(parse_header);
free(cfc_exe);
free(test_cfc_exe);
}
static void
S_source_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;
if (strcmp(file, "CFCParseHeader.c") == 0) { return; }
/* Strip extension */
if (file_len <= 2 || memcmp(file + file_len - 2, ".c", 2) != 0) {
chaz_Util_warn("Unexpected source file name: %s", file);
return;
}
file[file_len-2] = '\0';
obj_file = chaz_Util_join("", dir, dir_sep, file, obj_ext, NULL);
if (strlen(file) >= 7 && memcmp(file, "CFCTest", 7) == 0) {
chaz_MakeVar_append(sfc->common_test_objs, obj_file);
}
else {
chaz_MakeVar_append(sfc->common_objs, obj_file);
}
free(obj_file);
}