Files
mercury/util/mkinit.c
Julien Fischer d01dc2faed Fix mkinit option processing.
util/mkinit.c:
    The -l option does not take an argument. An incorrect edit in
    commit fcb8ffe9 meant that it was expecting one.
2022-03-26 00:30:28 +11:00

1345 lines
41 KiB
C

/////////////////////////////////////////////////////////////////////////////
// vim:sw=4 ts=4 expandtab
//
// Copyright (C) 1995-2008, 2010-2012 The University of Melbourne.
// Copyright (C) 2014-2016, 2019, 2020, 2022 The Mercury team.
// This file may only be copied under the terms of the GNU General
// Public License - see the file COPYING in the Mercury distribution.
// File: mkinit.c
// Main authors: zs, fjh
//
// Given a list of .c or .init files on the command line, this program
// produces the initialization file (usually called *_init.c) on stdout.
// The initialization file is a small C program that calls the initialization
// functions for all the modules in a Mercury program.
//
// Alternatively, if invoked with the -k option, this program produces a list
// of initialization directives on stdout. This mode of operation is used
// when building .init files for libraries.
//
// If invoked with the -s option, this program produces a standalone runtime
// interface on stdout. This mode of operation is used when using Mercury
// libraries from applications written in foreign languages.
//
// NOTE: any changes to this program may need to be reflected in the
// following places:
//
// - scripts/c2init.in
// - compiler/compile_target_code.m
// in particular the predicates make_init_obj/7 and
// make_standalone_interface/3.
/////////////////////////////////////////////////////////////////////////////
// mercury_std.h includes mercury_regs.h, and must precede system headers.
#include "mercury_conf.h"
#include "mercury_std.h"
#include "mercury_array_macros.h"
#include "mkinit_common.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#ifdef MR_HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef MR_HAVE_UNISTD_H
#include <unistd.h>
#else
#include "getopt.h"
#endif
// Adjustable limits.
#define MAXCALLS 40 // maximum number of calls per function
// These are used to collect lists of strings.
static const char if_need_to_init[] =
"#if defined(MR_MAY_NEED_INITIALIZATION)\n";
static const char if_need_term_size[] =
"#if defined(MR_RECORD_TERM_SIZES)\n";
static const char if_need_deep_prof[] =
"#if defined(MR_DEEP_PROFILING)\n";
static const char if_need_threadscope[] =
"#if defined(MR_THREADSCOPE)\n";
typedef enum
{
TASK_OUTPUT_INIT_PROG = 0,
TASK_OUTPUT_LIB_INIT = 1,
TASK_OUTPUT_STANDALONE_INIT = 2
} Task;
typedef enum
{
PURPOSE_INIT = 0,
PURPOSE_TYPE_TABLE = 1,
PURPOSE_DEBUGGER = 2,
PURPOSE_COMPLEXITY = 3,
PURPOSE_PROC_STATIC = 4,
PURPOSE_THREADSCOPE_STRING_TABLE = 5,
PURPOSE_REQ_INIT = 6,
PURPOSE_REQ_FINAL = 7
} Purpose;
const char *main_func_name[] =
{
"init_modules",
"init_modules_type_tables",
"init_modules_debugger",
"init_modules_complexity_procs",
"write_out_proc_statics",
"init_modules_threadscope_string_table",
"init_modules_required",
"final_modules_required"
};
const char *module_suffix[] =
{
"init",
"init_type_tables",
"init_debugger",
"init_complexity_procs",
"write_out_proc_statics",
"init_threadscope_string_table",
"",
"",
};
const char *init_suffix[] =
{
"",
"_type_tables",
"_debugger",
"_complexity",
"write_out_proc_statics",
"",
"",
""
};
const char *bunch_function_guard[] =
{
if_need_to_init,
NULL,
if_need_to_init,
if_need_term_size,
if_need_deep_prof,
if_need_threadscope,
NULL,
NULL,
};
const char *main_func_guard[] =
{
NULL,
NULL,
NULL,
if_need_term_size,
if_need_deep_prof,
if_need_threadscope,
NULL,
NULL,
};
const char *main_func_body_guard[] =
{
if_need_to_init,
NULL,
if_need_to_init,
NULL,
NULL,
NULL,
NULL,
NULL
};
const char *main_func_arg_defn[] =
{
"void",
"void",
"void",
"void",
"FILE *deep_fp, FILE *procrep_fp",
"void",
"void",
"void"
};
const char *main_func_arg_decl[] =
{
"void",
"void",
"void",
"void",
"FILE *, FILE *",
"void",
"void",
"void"
};
const char *main_func_arg[] =
{
"",
"",
"",
"",
"deep_fp, procrep_fp",
"",
"",
""
};
// Macros.
#define SYS_PREFIX_1 "sys_init"
#define SYS_PREFIX_2 "mercury_sys_init"
#define matches_prefix(s, prefix) \
(strncmp((s), (prefix), sizeof(prefix)-1) == 0)
#define sys_init_prefix(s) \
( matches_prefix(s, SYS_PREFIX_1) || \
matches_prefix(s, SYS_PREFIX_2) )
// Global variables.
// List of names of the modules to call all the usual initialization
// functions for: "init", "init_type_tables", "init_debugger" and (with
// the right #defines) "init_complexity_procs" and "write_out_proc_statics".
static const char **std_modules = NULL;
static int std_module_max = 0;
static int std_module_next = 0;
#define MR_INIT_STD_MODULE_SIZE 100
// List of names of handwritten modules, for which we call a limited set
// of initialization functions: "init", "init_type_tables" and (with
// the right #defines) "write_out_proc_statics". We don't call
// "init_debugger" functions since handwritten modules don't have module
// layouts, and we don't generate "init_complexity_procs" since they have
// no Mercury code to measure the complexity of.
static const char **special_modules = NULL;
static int special_module_max = 0;
static int special_module_next = 0;
#define MR_INIT_SPECIAL_MODULE_SIZE 10
// The concatenation of std_modules and special_modules; created with the
// right size (std_module_next + special_module_next).
static const char **std_and_special_modules = NULL;
// List of names of modules that have initialization functions that should
// always be run. This is currently used to initialize the states of constraint
// solvers. We call an "init_required" function for each such module.
static const char **req_init_modules = NULL;
static int req_init_module_max = 0;
static int req_init_module_next = 0;
#define MR_INIT_REQ_MODULE_SIZE 10
// List of names of modules that have finalisation functions that should
// always be run. We call a "final_required" function for each such module.
static const char **req_final_modules = NULL;
static int req_final_module_max = 0;
static int req_final_module_next = 0;
#define MR_FINAL_REQ_MODULE_SIZE 10
// List of names of environment variables whose values should be sampled
// at initialization. The list is not ordered, but must not contain duplicates,
// since that would lead to duplicate definitions of C global variables and
// hence errors from the C compiler.
static const char **mercury_env_vars = NULL;
static int mercury_env_var_max = 0;
static int mercury_env_var_next = 0;
#define MR_ENV_VAR_LIST_SIZE 10
// This should be kept in sync with the code of c_global_var_name
// in llds_out.m and global_var_name in mlds_to_c.m.
const char *envvar_prefix = "mercury_envvar_";
// Options and arguments, set by parse_options().
static const char *output_file_name = NULL;
static const char *entry_point = "mercury__main_2_0";
static const char *hl_entry_point = "main_2_p_0";
static const char *grade = "";
static int maxcalls = MAXCALLS;
static MR_bool output_main_func = MR_TRUE;
static MR_bool need_initialization_code = MR_FALSE;
static MR_bool need_tracing = MR_FALSE;
static Task output_task = TASK_OUTPUT_INIT_PROG;
static const char *experimental_complexity = NULL;
static int num_experimental_complexity_procs = 0;
// List of options to pass to the runtime.
static String_List *runtime_flags = NULL;
// Pointer to tail of the runtime_flags list.
static String_List **runtime_flags_tail = &runtime_flags;
// List of functions to always execute at initialization.
static String_List *always_exec_funcs = NULL;
// Pointer to tail of the always_exec_funcs list.
static String_List **always_exec_funcs_tail = &always_exec_funcs;
// Code fragments to put in the output file.
static const char header1[] =
"/*\n"
"** This code was automatically generated by mkinit - do not edit.\n"
"**\n"
"** Grade: %s\n"
"** Input files:\n"
"**\n"
;
// NOTE: _DEFAULT_SOURCE is defined in order to suppress a warning about
// _BSD_SOURCE being deprecated in glibc 2.20. We keep the definition of
// the deprecated macro about for compatibility with older versions of glibc.
static const char header2[] =
"*/\n"
"#ifndef _DEFAULT_SOURCE\n"
" #define _DEFAULT_SOURCE\n"
"#endif\n"
"#ifndef _BSD_SOURCE\n"
" #define _BSD_SOURCE\n"
"#endif\n"
"#include <stddef.h>\n"
"#ifdef MR_PROFILE_SBRK\n"
"#include <unistd.h>\n"
"#endif\n"
"#include \"mercury_init.h\"\n"
"#include \"mercury_grade.h\"\n"
"#ifdef MR_WIN32\n"
" #include \"mercury_windows.h\"\n"
"#endif\n"
"\n"
"#define MR_TRACE_ENABLED %d\n"
"#if MR_TRACE_ENABLED\n"
" #define MR_MAY_NEED_INITIALIZATION\n"
"#endif\n"
"\n"
"// Work around a bug in the Solaris 2.X (X<=4) linker;\n"
"// on these machines, init_gc must be statically linked.\n"
"\n"
"#ifdef MR_CONSERVATIVE_GC\n"
"static void init_gc(void)\n"
"{\n"
" GC_INIT();\n"
"}\n"
"#endif\n"
;
static const char mercury_funcs1[] =
"\n"
"#ifdef MR_HIGHLEVEL_CODE\n"
" extern void MR_CALL %s(void);\n"
"#else\n"
" MR_declare_entry(%s);\n"
"#endif\n"
"\n"
"#if defined(MR_USE_DLLS)\n"
" #if !defined(libmer_DEFINE_DLL)\n"
" #define libmer_impure_ptr \\\n"
" (*__imp_libmer_impure_ptr)\n"
" extern void *libmer_impure_ptr;\n"
" #endif\n"
" #if !defined(libmercury_DEFINE_DLL)\n"
" #define libmercury_impure_ptr \\\n"
" (*__imp_libmercury_impure_ptr)\n"
" extern void *libmercury_impure_ptr;\n"
" #endif\n"
"#endif\n"
"\n"
"void\n"
"mercury_init(int argc, char **argv, void *stackbottom)\n"
"{\n"
"\n"
"#if defined(MR_CONSERVATIVE_GC)\n"
" // Explicitly register the bottom of the stack, so that the\n"
" // GC knows where it starts.\n"
" //\n"
" // Boehm GC 7.4.2 deprecates the use of GC_stackbottom and\n"
" // prohibits the use of the alternative GC_register_my_thread()\n"
" // for the primordial thread. Therefore we do not attempt to\n"
" // register the bottom of the C stack but let the collector\n"
" // determine an appropriate value itself.\n"
" #if defined(MR_HGC)\n"
" MR_hgc_set_stack_bot(stackbottom);\n"
" #endif\n"
"#endif\n"
"\n"
"// If we are using DLLs on gnu-win32, then we need to take\n"
"// special steps to initialize _impure_ptr for the DLLs.\n"
"#if defined(MR_USE_DLLS)\n"
" #if !defined(libmer_DEFINE_DLL)\n"
" libmer_impure_ptr = _impure_ptr;\n"
" #endif\n"
" #if !defined(libmercury_DEFINE_DLL)\n"
" libmercury_impure_ptr = _impure_ptr;\n"
" #endif\n"
"#endif\n"
"\n";
static const char mercury_funcs2[] =
" MR_address_of_mercury_init_io = mercury_init_io;\n"
" MR_address_of_init_modules = init_modules;\n"
" MR_address_of_init_modules_type_tables = init_modules_type_tables;\n"
" MR_address_of_init_modules_debugger = init_modules_debugger;\n"
"#ifdef MR_RECORD_TERM_SIZES\n"
" MR_address_of_init_modules_complexity =\n"
" init_modules_complexity_procs;\n"
"#endif\n"
"#ifdef MR_DEEP_PROFILING\n"
" MR_address_of_write_out_proc_statics =\n"
" write_out_proc_statics;\n"
"#endif\n"
"#ifdef MR_THREADSCOPE\n"
" MR_address_of_init_modules_threadscope_string_table =\n"
" init_modules_threadscope_string_table;\n"
"#endif\n"
" MR_address_of_init_modules_required = init_modules_required;\n"
" MR_address_of_final_modules_required = final_modules_required;\n"
"#ifdef MR_RECORD_TERM_SIZES\n"
" MR_complexity_procs = MR_complexity_proc_table;\n"
" MR_num_complexity_procs = %d;\n"
"#endif\n"
" MR_type_ctor_info_for_univ = ML_type_ctor_info_for_univ;\n"
" MR_type_info_for_type_info = (MR_TypeCtorInfo)\n"
" &ML_type_info_for_type_info;\n"
" MR_type_info_for_pseudo_type_info = (MR_TypeCtorInfo)\n"
" &ML_type_info_for_pseudo_type_info;\n"
" MR_type_info_for_list_of_univ = (MR_TypeInfo)\n"
" &ML_type_info_for_list_of_univ;\n"
" MR_type_info_for_list_of_int = (MR_TypeInfo)\n"
" &ML_type_info_for_list_of_int;\n"
" MR_type_info_for_list_of_char = (MR_TypeInfo)\n"
" &ML_type_info_for_list_of_char;\n"
" MR_type_info_for_list_of_string = (MR_TypeInfo)\n"
" &ML_type_info_for_list_of_string;\n"
" MR_type_info_for_list_of_type_info = (MR_TypeInfo)\n"
" &ML_type_info_for_list_of_type_info;\n"
" MR_type_info_for_list_of_pseudo_type_info = (MR_TypeInfo)\n"
" &ML_type_info_for_list_of_pseudo_type_info;\n"
"#ifdef MR_CONSERVATIVE_GC\n"
" MR_address_of_init_gc = init_gc;\n"
"#endif\n"
" MR_library_initializer = ML_std_library_init;\n"
" MR_library_finalizer = ML_std_library_finalize;\n"
" MR_io_stdin_stream = ML_io_stdin_stream;\n"
" MR_io_stdout_stream = ML_io_stdout_stream;\n"
" MR_io_stderr_stream = ML_io_stderr_stream;\n"
" MR_io_print_to_stream = ML_io_print_to_stream;\n"
"#if MR_TRACE_ENABLED\n"
" MR_exec_trace_func_ptr = MR_trace_real;\n"
" MR_register_module_layout = MR_register_module_layout_real;\n"
" MR_address_of_trace_getline = MR_trace_getline;\n"
" MR_address_of_trace_get_command = MR_trace_get_command;\n"
" MR_address_of_trace_browse_all_on_level =\n"
" MR_trace_browse_all_on_level;\n"
" MR_address_of_trace_interrupt_handler =\n"
" MR_trace_interrupt_handler;\n"
" #ifdef MR_USE_EXTERNAL_DEBUGGER\n"
" MR_address_of_trace_init_external = MR_trace_init_external;\n"
" MR_address_of_trace_final_external = MR_trace_final_external;\n"
" #endif\n"
"#else\n"
" MR_exec_trace_func_ptr = MR_trace_fake;\n"
" MR_register_module_layout = NULL;\n"
" MR_address_of_trace_getline = NULL;\n"
" MR_address_of_trace_get_command = NULL;\n"
" MR_address_of_trace_browse_all_on_level = NULL;\n"
" MR_address_of_trace_interrupt_handler = NULL;\n"
" #ifdef MR_USE_EXTERNAL_DEBUGGER\n"
" MR_address_of_trace_init_external = NULL;\n"
" MR_address_of_trace_final_external = NULL;\n"
" #endif\n"
"#endif\n"
"#if defined(MR_USE_GCC_NONLOCAL_GOTOS) && !defined(MR_USE_ASM_LABELS)\n"
" MR_do_init_modules();\n"
"#endif\n"
"#ifdef MR_HIGHLEVEL_CODE\n"
" MR_program_entry_point = %s;\n"
"#else\n"
" MR_program_entry_point = MR_ENTRY(%s);\n"
"#endif\n"
;
static const char mercury_funcs3[] =
"\n"
" mercury_runtime_init(argc, argv);\n"
"\n"
;
static const char mercury_funcs4[] =
" return;\n"
"}\n"
"\n"
;
static const char mercury_call_main_func[] =
"void\n"
"mercury_call_main(void)\n"
"{\n"
" mercury_runtime_main();\n"
"}\n"
"\n"
;
static const char mercury_terminate_func[] =
"int\n"
"mercury_terminate(void)\n"
"{\n"
" return mercury_runtime_terminate();\n"
"}\n"
"\n"
;
static const char mercury_main_func[] =
"int\n"
"mercury_main(int argc, char **argv)\n"
"{\n"
"#ifdef MR_PROFILE_SBRK\n"
" void* old_break;\n"
" void* new_break;\n"
"#endif\n"
" int result;\n"
"#ifdef MR_PROFILE_SBRK\n"
" old_break = sbrk(0);\n"
"#endif\n"
// Note that the address we use for the stack base needs to be
// word-aligned. That is why we give dummy the type `void *'
// rather than e.g. `char'.
" void *dummy;\n"
" mercury_init(argc, argv, &dummy);\n"
" mercury_call_main();\n"
" result = mercury_terminate();\n"
"#ifdef MR_PROFILE_SBRK\n"
" new_break = sbrk(0);\n"
" printf(\"sbrk delta: %d\\n\",\n"
" (MR_Integer) new_break - (MR_Integer) old_break);\n"
"#endif\n"
" return result;\n"
"}\n"
"\n"
// Convert wide-character representation of the command line
// arguments to UTF-8.
"#if defined(MR_MSVC)\n"
"static char **\n"
"mercury_msvc_args(int argc, wchar_t **wargv)\n"
"{\n"
" char **argv;\n"
" int i;\n"
"\n"
" argv = MR_NEW_ARRAY(char *, argc + 1);\n"
" for (i = 0; i < argc; i++) {\n"
" int bytes = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1,\n"
" NULL, 0, NULL, NULL);\n"
" argv[i] = MR_malloc(bytes);\n"
" WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1,\n"
" argv[i], bytes, NULL, NULL);\n"
" }\n"
" argv[i] = NULL;\n"
" return argv;\n"
"}\n"
"#elif defined(MR_MINGW)\n"
"extern int __wgetmainargs(int *, wchar_t ***, wchar_t ***, int, int *);\n"
"\n"
"static void\n"
"mercury_win32_args(int *argc_ret, char ***argv_ret)\n"
"{\n"
" int argc;\n"
" char **argv;\n"
" wchar_t **wargv;\n"
" wchar_t **wenv;\n"
" int start_info = 0;\n"
" int i;\n"
"\n"
" if (__wgetmainargs(&argc, &wargv, &wenv, 0, &start_info) != 0) {\n"
" return;\n"
" }\n"
" argv = MR_NEW_ARRAY(char *, argc + 1);\n"
" for (i = 0; i < argc; i++) {\n"
" int bytes = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1,\n"
" NULL, 0, NULL, NULL);\n"
" argv[i] = MR_malloc(bytes);\n"
" WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1,\n"
" argv[i], bytes, NULL, NULL);\n"
" }\n"
" argv[i] = NULL;\n"
" *argc_ret = argc;\n"
" *argv_ret = argv;\n"
"}\n"
"#endif\n"
"\n"
;
static const char mercury_grade_check_func[] =
"// Ensure that everything gets compiled in the same grade.\n"
"const char *mercury_init_grade_check(void)\n"
"{\n"
" return &MR_GRADE_VAR;\n"
"}\n"
"\n"
;
static const char main_func[] =
"#if defined(MR_MSVC)\n"
"\n"
"int\n"
"wmain(int argc, wchar_t **wargv)\n"
"{\n"
" int result;\n"
" char **argv;\n"
"\n"
" argv = mercury_msvc_args(argc, wargv);\n"
" result = mercury_main(argc, argv);\n"
" return result;\n"
"}\n"
"#else\n"
"\n"
"int\n"
"main(int argc, char **argv)\n"
"{\n"
"#ifdef MR_MINGW\n"
" mercury_win32_args(&argc, &argv);\n"
"#endif\n"
" return mercury_main(argc, argv);\n"
"}\n"
"#endif\n"
;
// Function prototypes.
static void parse_options(int argc, char *argv[]);
static void usage(void);
static void output_complexity_proc(const char *procname);
static void output_complexity_experiment_table(const char *filename);
static void output_headers(void);
static int output_sub_init_functions(Purpose purpose,
const char **func_names, int num_func_names);
static void output_main_init_function(Purpose purpose, int num_bunches);
static void output_main(void);
static int output_lib_init_file(void);
static int output_init_program(void);
static void process_file(const char *filename);
static void process_init_file(const char *filename);
static void output_init_function(const char *func_name,
int *num_bunches_ptr, int *num_calls_in_cur_bunch_ptr,
Purpose purpose);
/////////////////////////////////////////////////////////////////////////////
#ifdef CHECK_GET_LINE
FILE *check_fp;
#endif
int
main(int argc, char **argv)
{
int exit_status;
MR_progname = argv[0];
if (MR_progname == NULL) {
MR_progname = "mkinit";
}
parse_options(argc, argv);
#ifdef CHECK_GET_LINE
check_fp = fopen(".check_get_line", "w");
// If the open fails, we won't write to the file.
#endif
set_output_file(output_file_name);
switch (output_task) {
case TASK_OUTPUT_LIB_INIT:
// Output a .init file.
exit_status = output_lib_init_file();
break;
case TASK_OUTPUT_STANDALONE_INIT:
case TASK_OUTPUT_INIT_PROG:
// Output a _init.c file or a standalone initialisation interface.
exit_status = output_init_program();
break;
default:
fprintf(stderr, "mkinit: unknown task\n");
exit(EXIT_FAILURE);
}
return exit_status;
}
/////////////////////////////////////////////////////////////////////////////
// Output the initialisation file for a Mercury library, the .init file.
static int
output_lib_init_file(void)
{
int filenum;
int i;
for (filenum = 0; filenum < num_files; filenum++) {
process_file(files[filenum]);
}
for (i = 0; i < std_module_next; i++) {
printf("INIT %s%s\n", std_modules[i], module_suffix[PURPOSE_INIT]);
}
for (i = 0; i < req_init_module_next; i++) {
printf("REQUIRED_INIT %s\n", req_init_modules[i]);
}
for (i = 0; i < req_final_module_next; i++) {
printf("REQUIRED_FINAL %s\n", req_final_modules[i]);
}
for (i = 0; i < mercury_env_var_next; i++) {
printf("ENVVAR %s\n", mercury_env_vars[i]);
}
if (num_errors > 0) {
fprintf(stderr, "%s: error while creating .init file.\n", MR_progname);
return EXIT_FAILURE;
} else {
return EXIT_SUCCESS;
}
}
/////////////////////////////////////////////////////////////////////////////
// Output the initialisation program for a Mercury executable, the *_init.c
// file.
static int
output_init_program(void)
{
int filenum;
int num_bunches;
int i;
do_path_search(files, num_files);
output_headers();
for (filenum = 0; filenum < num_files; filenum++) {
process_file(files[filenum]);
}
printf("\n");
for (i = 0; i < mercury_env_var_next; i++) {
printf("MR_Word %s%s = 0;\n", envvar_prefix, mercury_env_vars[i]);
}
if (need_initialization_code) {
printf("#define MR_MAY_NEED_INITIALIZATION\n\n");
}
std_and_special_modules = MR_NEW_ARRAY(const char *,
std_module_next + special_module_next);
for (i = 0; i < std_module_next; i++) {
std_and_special_modules[i] = std_modules[i];
}
for (i = 0; i < special_module_next; i++) {
std_and_special_modules[std_module_next + i] = special_modules[i];
}
num_bunches = output_sub_init_functions(PURPOSE_INIT,
std_and_special_modules, std_module_next + special_module_next);
output_main_init_function(PURPOSE_INIT, num_bunches);
num_bunches = output_sub_init_functions(PURPOSE_TYPE_TABLE,
std_and_special_modules, std_module_next + special_module_next);
output_main_init_function(PURPOSE_TYPE_TABLE, num_bunches);
num_bunches = output_sub_init_functions(PURPOSE_DEBUGGER,
std_modules, std_module_next);
output_main_init_function(PURPOSE_DEBUGGER, num_bunches);
num_bunches = output_sub_init_functions(PURPOSE_COMPLEXITY,
std_modules, std_module_next);
output_main_init_function(PURPOSE_COMPLEXITY, num_bunches);
num_bunches = output_sub_init_functions(PURPOSE_PROC_STATIC,
std_and_special_modules, std_module_next + special_module_next);
output_main_init_function(PURPOSE_PROC_STATIC, num_bunches);
num_bunches = output_sub_init_functions(PURPOSE_THREADSCOPE_STRING_TABLE,
std_modules, std_module_next);
output_main_init_function(PURPOSE_THREADSCOPE_STRING_TABLE, num_bunches);
num_bunches = output_sub_init_functions(PURPOSE_REQ_INIT,
req_init_modules, req_init_module_next);
output_main_init_function(PURPOSE_REQ_INIT, num_bunches);
num_bunches = output_sub_init_functions(PURPOSE_REQ_FINAL,
req_final_modules, req_final_module_next);
output_main_init_function(PURPOSE_REQ_FINAL, num_bunches);
output_main();
if (num_errors > 0) {
fputs("// Force syntax error, since there were errors\n", stdout);
fputs("// in the generation of this file.", stdout);
fputs("#error \"You need to remake this file\"\n", stdout);
if (output_file_name != NULL) {
(void) fclose(stdout);
(void) remove(output_file_name);
}
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
/////////////////////////////////////////////////////////////////////////////
static void
parse_options(int argc, char *argv[])
{
int c;
String_List *tmp_slist;
int seen_f_option = 0;
while ((c = getopt(argc, argv, "A:c:f:g:iI:lo:r:tw:xX:ks")) != EOF) {
switch (c) {
case 'A':
// Add the argument to the end of the list of always executed
// initialization functions.
if (optarg[0] != '\0') {
tmp_slist = (String_List *)
checked_malloc(sizeof(String_List));
tmp_slist->next = NULL;
tmp_slist->data = (char *) checked_malloc(strlen(optarg) + 1);
strcpy(tmp_slist->data, optarg);
*always_exec_funcs_tail = tmp_slist;
always_exec_funcs_tail = &tmp_slist->next;
}
break;
case 'c':
if (sscanf(optarg, "%d", &maxcalls) != 1) {
usage();
}
break;
case 'f':
process_file_list_file(optarg);
seen_f_option = 1;
break;
case 'g':
grade = optarg;
break;
case 'i':
need_initialization_code = MR_TRUE;
break;
case 'I':
add_init_file_dir(optarg);
break;
case 'l':
output_main_func = MR_FALSE;
break;
case 'o':
if (strcmp(optarg, "-") == 0) {
output_file_name = NULL; // output to stdout
} else {
output_file_name = optarg;
}
break;
case 'r':
// Add the argument to the end of the list of runtime flags.
if (optarg[0] != '\0') {
tmp_slist = (String_List *)
checked_malloc(sizeof(String_List));
tmp_slist->next = NULL;
tmp_slist->data = (char *) checked_malloc(strlen(optarg) + 1);
strcpy(tmp_slist->data, optarg);
*runtime_flags_tail = tmp_slist;
runtime_flags_tail = &tmp_slist->next;
}
break;
case 't':
need_tracing = MR_TRUE;
need_initialization_code = MR_TRUE;
break;
case 'w':
hl_entry_point = entry_point = optarg;
break;
case 'x':
// We always assume this option.
break;
case 'X':
experimental_complexity = optarg;
break;
case 'k':
output_task = TASK_OUTPUT_LIB_INIT;
break;
case 's':
output_task = TASK_OUTPUT_STANDALONE_INIT;
output_main_func = MR_FALSE; // -s implies -l
break;
default:
usage();
}
}
if (seen_f_option) {
// -f could be made compatible if we copied the filenames
// from argv into files.
if ((argc - optind) > 0) {
fprintf(stderr,
"%s: -f incompatible with filenames on the command line\n",
MR_progname);
exit(EXIT_FAILURE);
}
} else {
num_files = argc - optind;
files = argv + optind;
}
if (num_files <= 0) {
usage();
}
}
static void
usage(void)
{
fputs("Usage: mkinit [options] files...\n", stderr);
fputs("Options:\n", stderr);
fputs(" -c maxcalls:\tset the max size of an init function\n", stderr);
fputs(" -f filename:\tprocess the files one per line in filename\n", stderr);
fputs(" -g grade:\tset the grade of the executable\n", stderr);
fputs(" -i:\t\tenable initialization code\n", stderr);
fputs(" -l:\t\tdo not output main function\n", stderr);
fputs(" -o file:\toutput to the named file\n", stderr);
fputs(" -r word:\tadd word to the flags for the runtime\n", stderr);
fputs(" -t:\t\tenable execution tracing\n", stderr);
fputs(" -w entry:\tset the entry point to the given label\n", stderr);
fputs(" -I dir:\tadd dir to the search path for init files\n", stderr);
fputs(" -k:\t\tgenerate the .init for a library\n", stderr);
fputs(" -s:\t\tgenerate a standalone runtime interface\n", stderr);
fputs(" -m:\t\t(error)\n", stderr);
exit(EXIT_FAILURE);
}
/////////////////////////////////////////////////////////////////////////////
#define MAX_PROCNAME_LEN 1024
static void
output_complexity_proc(const char *procname)
{
printf("{ \"%s\", -1, -1, NULL,\n", procname);
printf("MR_COMPLEXITY_IS_INACTIVE, NULL, 0, NULL, NULL },\n");
}
static void
output_complexity_experiment_table(const char *filename)
{
const char *procname;
FILE *fp;
fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "%s: error opening file `%s': %s\n",
MR_progname, filename, strerror(errno));
num_errors++;
return;
}
printf("\nMR_ComplexityProc MR_complexity_proc_table[] = {\n");
num_experimental_complexity_procs = 0;
while ((procname = read_line(filename, fp, MAX_PROCNAME_LEN)) != NULL) {
num_experimental_complexity_procs++;
output_complexity_proc(procname);
}
printf("};\n");
}
static void
output_headers(void)
{
int filenum;
printf(header1, grade);
for (filenum = 0; filenum < num_files; filenum++) {
fputs("** ", stdout);
fputs(files[filenum], stdout);
putc('\n', stdout);
}
printf(header2, need_tracing);
}
static int
output_sub_init_functions(Purpose purpose, const char **func_names,
int num_func_names)
{
int funcnum;
int num_bunches;
int num_calls_in_cur_bunch;
fputs("\n", stdout);
if (bunch_function_guard[purpose] != NULL) {
fputs(bunch_function_guard[purpose], stdout);
fputs("\n", stdout);
}
printf("static void %s_0(%s)\n",
main_func_name[purpose], main_func_arg_defn[purpose]);
fputs("{\n", stdout);
num_bunches = 0;
num_calls_in_cur_bunch = 0;
for (funcnum = 0; funcnum < num_func_names; funcnum++) {
output_init_function(func_names[funcnum], &num_bunches,
&num_calls_in_cur_bunch, purpose);
}
fputs("}\n", stdout);
if (bunch_function_guard[purpose] != NULL) {
fputs("\n#endif\n", stdout);
}
return num_bunches;
}
static void
output_main_init_function(Purpose purpose, int num_bunches)
{
int i;
fputs("\n", stdout);
if (main_func_guard[purpose] != NULL) {
fputs(main_func_guard[purpose], stdout);
fputs("\n", stdout);
}
printf("\nstatic void %s(%s)\n",
main_func_name[purpose], main_func_arg_defn[purpose]);
fputs("{\n", stdout);
if (main_func_body_guard[purpose] != NULL) {
fputs(main_func_body_guard[purpose], stdout);
}
for (i = 0; i <= num_bunches; i++) {
printf("\t%s_%d(%s);\n",
main_func_name[purpose], i, main_func_arg[purpose]);
}
if (main_func_body_guard[purpose] != NULL) {
fputs("#endif\n", stdout);
}
fputs("}\n", stdout);
if (main_func_guard[purpose] != NULL) {
fputs("\n#endif\n", stdout);
}
}
static void
output_main(void)
{
String_List *list;
char *options_str;
int i;
if (experimental_complexity != NULL) {
output_complexity_experiment_table(experimental_complexity);
} else {
printf("\nMR_ComplexityProc MR_complexity_proc_table[] = {\n");
output_complexity_proc("dummy_proc/0-0");
printf("};\n");
}
// If we are building a standalone interface then we set the entry
// point as MR_dummy_main. This is defined in runtime/mercury_wrapper.c
// and aborts execution if called. In standalone mode, we are not
// expecting Mercury to be called through the standard entry point.
if (output_task == TASK_OUTPUT_STANDALONE_INIT) {
hl_entry_point = entry_point = "MR_dummy_main";
}
printf(mercury_funcs1, hl_entry_point, entry_point);
printf(mercury_funcs2, num_experimental_complexity_procs,
hl_entry_point, entry_point);
printf(" MR_runtime_flags = \"");
for (list = runtime_flags; list != NULL; list = list->next) {
for (options_str = list->data; *options_str != '\0'; options_str++) {
if (*options_str == '\n') {
putchar('\\');
putchar('n');
} else if (*options_str == '\t') {
putchar('\\');
putchar('t');
} else if (*options_str == '"' ||
*options_str == '\\') {
putchar('\\');
putchar(*options_str);
} else {
putchar(*options_str);
}
}
putchar(' ');
}
printf("\";\n");
printf("\n");
for (i = 0; i < mercury_env_var_next; i++) {
printf(" if (getenv(\"%s\") != NULL) {\n", mercury_env_vars[i]);
printf(" %s%s = 1;\n", envvar_prefix, mercury_env_vars[i]);
printf(" } else {\n");
printf(" %s%s = 0;\n", envvar_prefix, mercury_env_vars[i]);
printf(" }\n");
}
fputs(mercury_funcs3, stdout);
for (list = always_exec_funcs; list != NULL; list = list->next) {
printf(" %s();\n", list->data);
}
fputs(mercury_funcs4, stdout);
if (output_task == TASK_OUTPUT_INIT_PROG) {
fputs(mercury_call_main_func, stdout);
}
fputs(mercury_terminate_func, stdout);
if (output_task == TASK_OUTPUT_INIT_PROG) {
fputs(mercury_main_func, stdout);
}
fputs(mercury_grade_check_func, stdout);
if (output_main_func) {
fputs(main_func, stdout);
}
}
/////////////////////////////////////////////////////////////////////////////
static void
process_file(const char *filename)
{
size_t len;
len = strlen(filename);
if (len >= 2 && strcmp(filename + len - 2, ".c") == 0) {
process_init_file(filename);
} else if (len >= 5 && strcmp(filename + len - 5, ".init") == 0) {
process_init_file(filename);
} else {
fprintf(stderr,
"%s: filename `%s' must end in `.c' or `.init'\n",
MR_progname, filename);
num_errors++;
}
}
static void
process_init_file(const char *filename)
{
// The strings that are supposed to be followed by other information
// (INIT, REQUIRED_INIT, and REQUIRED_FINAL) should end with
// the space that separates the keyword from the following data.
// The string that is not supposed to be following by other information
// (ENDINIT) should not have a following space, since llds_out.m and
// mlds_to_c.m do not add that space.
const char * const init_str = "INIT ";
const char * const reqinit_str = "REQUIRED_INIT ";
const char * const reqfinal_str = "REQUIRED_FINAL ";
const char * const envvar_str = "ENVVAR ";
const char * const endinit_str = "ENDINIT";
const size_t init_strlen = strlen(init_str);
const size_t reqinit_strlen = strlen(reqinit_str);
const size_t reqfinal_strlen = strlen(reqfinal_str);
const size_t envvar_strlen = strlen(envvar_str);
const size_t endinit_strlen = strlen(endinit_str);
char line[MAXLINE];
FILE *cfile;
cfile = fopen(filename, "r");
if (cfile == NULL) {
fprintf(stderr, "%s: error opening file `%s': %s\n",
MR_progname, filename, strerror(errno));
num_errors++;
return;
}
while (get_line(cfile, line, MAXLINE) > 0) {
if (strncmp(line, init_str, init_strlen) == 0) {
char *func_name;
size_t func_name_len;
int j;
for (j = init_strlen; MR_isalnumunder(line[j]); j++) {
// VOID
}
line[j] = '\0';
func_name = line + init_strlen;
func_name_len = strlen(func_name);
if (MR_strneq(&func_name[func_name_len - 4], "init", 4)) {
func_name[func_name_len - 4] = '\0';
MR_ensure_room_for_next(std_module, const char *,
MR_INIT_STD_MODULE_SIZE);
std_modules[std_module_next] = checked_strdup(func_name);
std_module_next++;
} else {
MR_ensure_room_for_next(special_module, const char *,
MR_INIT_SPECIAL_MODULE_SIZE);
special_modules[special_module_next] =
checked_strdupcat(func_name, "_");
special_module_next++;
}
} else if (strncmp(line, reqinit_str, reqinit_strlen) == 0) {
char *func_name;
int j;
for (j = reqinit_strlen; MR_isalnumunder(line[j]); j++) {
// VOID
}
line[j] = '\0';
func_name = line + reqinit_strlen;
MR_ensure_room_for_next(req_init_module, const char *,
MR_INIT_REQ_MODULE_SIZE);
req_init_modules[req_init_module_next] = checked_strdup(func_name);
req_init_module_next++;
} else if (strncmp(line, reqfinal_str, reqfinal_strlen) == 0) {
char *func_name;
int j;
for (j = reqfinal_strlen; MR_isalnumunder(line[j]); j++) {
// VOID
}
line[j] = '\0';
func_name = line + reqfinal_strlen;
MR_ensure_room_for_next(req_final_module, const char *,
MR_FINAL_REQ_MODULE_SIZE);
req_final_modules[req_final_module_next] =
checked_strdup(func_name);
req_final_module_next++;
} else if (strncmp(line, envvar_str, envvar_strlen) == 0) {
char *envvar_name;
int i;
int j;
MR_bool found;
// Check that all characters in the name of the environment
// variable are acceptable as components of a C variable name.
// Note that the variable name doesn't have to start with a letter
// because the variable name has a prefix.
for (j = envvar_strlen; MR_isalnumunder(line[j]); j++) {
// VOID
}
if (line[j] != '\n') {
printf("mkinit: error: bad environment variable name %s\n",
line);
}
line[j] = '\0'; // Overwrite the newline.
envvar_name = line + envvar_strlen;
// Since the number of distinct environment variables used by
// a program is likely to be in the single digits, linear search
// should be efficient enough.
found = MR_FALSE;
for (i = 0; i < mercury_env_var_next; i++) {
if (strcmp(envvar_name, mercury_env_vars[i]) == 0) {
found = MR_TRUE;
break;
}
}
if (!found) {
MR_ensure_room_for_next(mercury_env_var, const char *,
MR_ENV_VAR_LIST_SIZE);
mercury_env_vars[mercury_env_var_next] =
checked_strdup(envvar_name);
mercury_env_var_next++;
}
} else if (strncmp(line, endinit_str, endinit_strlen) == 0) {
break;
}
}
fclose(cfile);
}
// We could in theory put all calls to e.g. <module>_init_type_tables()
// functions in a single C function in the <mainmodule>_init.c file we
// generate. However, doing so turns out to be a bad idea: it leads to large
// compilation times for the <mainmodule>_init.c files. Instead, we divide
// the calls into bunches containing at most max_calls calls, with each bunch
// contained in its own function. *num_calls_in_cur_bunch_ptr says how many
// calls the current bunch already has; *num_bunches_ptr gives the number
// of the current bunch.
static void
output_init_function(const char *func_name, int *num_bunches_ptr,
int *num_calls_in_cur_bunch_ptr, Purpose purpose)
{
if (*num_calls_in_cur_bunch_ptr >= maxcalls) {
printf("}\n\n");
(*num_bunches_ptr)++;
*num_calls_in_cur_bunch_ptr = 0;
printf("static void %s_%d(%s)\n",
main_func_name[purpose], *num_bunches_ptr,
main_func_arg_defn[purpose]);
printf("{\n");
}
(*num_calls_in_cur_bunch_ptr)++;
printf("\t{ extern void %s%s(%s);\n",
func_name, module_suffix[purpose], main_func_arg_decl[purpose]);
printf("\t %s%s(%s); }\n",
func_name, module_suffix[purpose], main_func_arg[purpose]);
}
/////////////////////////////////////////////////////////////////////////////