/* C/ObjC/C++ command line option handling. Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. Contributed by Neil Booth. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include "system.h" #include "intl.h" #include "cppdefault.h" #include "c-incpath.h" #include "opts.h" #include "options.h" #include "mkdeps.h" #ifndef DOLLARS_IN_IDENTIFIERS # define DOLLARS_IN_IDENTIFIERS true #endif #ifndef TARGET_SYSTEM_ROOT # define TARGET_SYSTEM_ROOT NULL #endif /* CPP's options. */ static cpp_options *cpp_opts; /* Input filename. */ static const char *this_input_filename; /* Filename and stream for preprocessed output. */ static const char *out_fname; static FILE *out_stream; /* Append dependencies to deps_file. */ static bool deps_append; /* If dependency switches (-MF etc.) have been given. */ static bool deps_seen; /* If -v seen. */ static bool verbose; /* Dependency output file. */ static const char *deps_file; /* The prefix given by -iprefix, if any. */ static const char *iprefix; /* The multilib directory given by -imultilib, if any. */ static const char *imultilib; /* The system root, if any. Overridden by -isysroot. */ static const char *sysroot = TARGET_SYSTEM_ROOT; /* Zero disables all standard directories for headers. */ static bool std_inc = true; /* If the quote chain has been split by -I-. */ static bool quote_chain_split; /* If -Wunused-macros. */ static bool warn_unused_macros; /* If -Wvariadic-macros. */ static bool warn_variadic_macros = true; /* Number of deferred options. */ static size_t deferred_count; /* Number of deferred options scanned for -include. */ static size_t include_cursor; static void handle_OPT_d (const char *); static void set_std_c89 (int, int); static void set_std_c99 (int); static void check_deps_environment_vars (void); static void handle_deferred_opts (void); static void sanitize_cpp_opts (void); static void add_prefixed_path (const char *, size_t); static void push_command_line_include (void); static void cb_file_change (cpp_reader *, const struct line_map *); static void cb_dir_change (cpp_reader *, const char *); static void finish_options (void); #ifndef STDC_0_IN_SYSTEM_HEADERS #define STDC_0_IN_SYSTEM_HEADERS 0 #endif /* Holds switches parsed by sdcpp_common_handle_option (), but whose handling is deferred to sdcpp_common_post_options (). */ static void defer_opt (enum opt_code, const char *); static struct deferred_opt { enum opt_code code; const char *arg; } *deferred_opts; /* Complain that switch CODE expects an argument but none was provided. OPT was the command-line option. Return FALSE to get the default message in opts.c, TRUE if we provide a specialized one. */ bool sdcpp_common_missing_argument (const char *opt, size_t code) { switch (code) { default: /* Pick up the default message. */ return false; case OPT_A: error ("assertion missing after \"%s\"", opt); break; case OPT_D: case OPT_U: error ("macro name missing after \"%s\"", opt); break; case OPT_I: case OPT_idirafter: case OPT_isysroot: case OPT_isystem: case OPT_iquote: error ("missing path after \"%s\"", opt); break; case OPT_MF: case OPT_MD: case OPT_MMD: case OPT_include: case OPT_imacros: case OPT_o: error ("missing filename after \"%s\"", opt); break; case OPT_MQ: case OPT_MT: error ("missing makefile target after \"%s\"", opt); break; } return true; } /* Defer option CODE with argument ARG. */ static void defer_opt (enum opt_code code, const char *arg) { deferred_opts[deferred_count].code = code; deferred_opts[deferred_count].arg = arg; deferred_count++; } /* Common initialization before parsing options. */ unsigned int sdcpp_common_init_options (unsigned int argc, const char **argv ATTRIBUTE_UNUSED) { parse_in = cpp_create_reader (CLK_GNUC89, NULL, &line_table); cpp_opts = cpp_get_options (parse_in); cpp_opts->dollars_in_ident = DOLLARS_IN_IDENTIFIERS; cpp_opts->objc = 0; /* Reset to avoid warnings on internal definitions. We set it just before passing on command-line options to cpplib. */ cpp_opts->warn_dollars = 0; deferred_opts = XNEWVEC (struct deferred_opt, argc); return CL_SDCPP; } /* Handle switch SCODE with argument ARG. VALUE is true, unless no- form of an -f or -W option was given. Returns 0 if the switch was invalid, a negative number to prevent language-independent processing in toplev.c (a hack necessary for the short-term). */ int sdcpp_common_handle_option (size_t scode, const char *arg, int value) { enum opt_code code = (enum opt_code) scode; int result = 1; switch (code) { default: result = 0; break; #if 0 // pch not supported by sdcpp case OPT__output_pch_: pch_file = arg; break; #endif case OPT_A: defer_opt (code, arg); break; case OPT_C: cpp_opts->discard_comments = 0; break; case OPT_CC: cpp_opts->discard_comments = 0; cpp_opts->discard_comments_in_macro_exp = 0; break; case OPT_D: defer_opt (code, arg); break; case OPT_H: cpp_opts->print_include_names = 1; break; case OPT_I: if (strcmp (arg, "-")) add_path (xstrdup (arg), BRACKET, 0, true); else { if (quote_chain_split) error ("-I- specified twice"); quote_chain_split = true; split_quote_chain (); inform ("obsolete option -I- used, please use -iquote instead"); } break; case OPT_M: case OPT_MM: /* When doing dependencies with -M or -MM, suppress normal preprocessed output, but still do -dM etc. as software depends on this. Preprocessed output does occur if -MD, -MMD or environment var dependency generation is used. */ cpp_opts->deps.style = (code == OPT_M ? DEPS_SYSTEM: DEPS_USER); flag_no_output = 1; cpp_opts->inhibit_warnings = 1; break; case OPT_MD: case OPT_MMD: cpp_opts->deps.style = (code == OPT_MD ? DEPS_SYSTEM: DEPS_USER); deps_file = arg; break; case OPT_MF: deps_seen = true; deps_file = arg; break; case OPT_MG: deps_seen = true; cpp_opts->deps.missing_files = true; break; case OPT_MP: deps_seen = true; cpp_opts->deps.phony_targets = true; break; case OPT_MQ: case OPT_MT: deps_seen = true; defer_opt (code, arg); break; case OPT_P: flag_no_line_commands = 1; break; case OPT_fworking_directory: flag_working_directory = value; break; case OPT_U: defer_opt (code, arg); break; case OPT_Wall: cpp_opts->warn_trigraphs = value; cpp_opts->warn_comments = value; cpp_opts->warn_num_sign_change = value; cpp_opts->warn_multichar = value; /* Was C++ only. */ break; case OPT_Wcomment: case OPT_Wcomments: cpp_opts->warn_comments = value; break; case OPT_Wdeprecated: cpp_opts->warn_deprecated = value; break; case OPT_Wendif_labels: cpp_opts->warn_endif_labels = value; break; case OPT_Werror: cpp_opts->warnings_are_errors = value; break; case OPT_Wimport: /* Silently ignore for now. */ break; #if 0 // pch not supported by sdcpp case OPT_Winvalid_pch: cpp_opts->warn_invalid_pch = value; break; #endif case OPT_Wsystem_headers: cpp_opts->warn_system_headers = value; break; case OPT_Wtraditional: cpp_opts->warn_traditional = value; break; case OPT_Wtrigraphs: cpp_opts->warn_trigraphs = value; break; case OPT_Wundef: cpp_opts->warn_undef = value; break; case OPT_Wunused_macros: warn_unused_macros = value; break; case OPT_Wvariadic_macros: warn_variadic_macros = value; break; case OPT_ansi: set_std_c89 (false, true); break; case OPT_d: handle_OPT_d (arg); break; case OPT_fdollars_in_identifiers: cpp_opts->dollars_in_ident = value; break; case OPT_fsigned_char: flag_signed_char = value; break; case OPT_funsigned_char: flag_signed_char = !value; break; #if 0 // pch not supported by sdcpp case OPT_fpch_deps: cpp_opts->restore_pch_deps = value; break; case OPT_fpch_preprocess: flag_pch_preprocess = value; break; #endif case OPT_fpreprocessed: cpp_opts->preprocessed = value; break; case OPT_fshow_column: cpp_opts->show_column = value; break; case OPT_ftabstop_: /* It is documented that we silently ignore silly values. */ if (value >= 1 && value <= 100) cpp_opts->tabstop = value; break; case OPT_fexec_charset_: cpp_opts->narrow_charset = arg; break; case OPT_fwide_exec_charset_: cpp_opts->wide_charset = arg; break; case OPT_finput_charset_: cpp_opts->input_charset = arg; break; case OPT_idirafter: add_path (xstrdup (arg), AFTER, 0, true); break; case OPT_imacros: case OPT_include: defer_opt (code, arg); break; case OPT_iprefix: iprefix = arg; break; case OPT_imultilib: imultilib = arg; break; case OPT_iquote: add_path (xstrdup (arg), QUOTE, 0, true); break; case OPT_isysroot: sysroot = arg; break; case OPT_isystem: add_path (xstrdup (arg), SYSTEM, 0, true); break; case OPT_iwithprefix: add_prefixed_path (arg, SYSTEM); break; case OPT_iwithprefixbefore: add_prefixed_path (arg, BRACKET); break; case OPT_lang_asm: cpp_set_lang (parse_in, CLK_ASM); cpp_opts->dollars_in_ident = false; break; case OPT_lang_objc: cpp_opts->objc = 1; break; case OPT_nostdinc: std_inc = false; break; case OPT_o: if (!out_fname) out_fname = arg; else error ("output filename specified twice"); break; /* SDCPP specfic */ case OPT_obj_ext_: cpp_opts->obj_ext = arg; break; /* We need to handle the -pedantic switches here, rather than in sdcpp_common_post_options, so that a subsequent -Wno-endif-labels is not overridden. */ case OPT_pedantic_errors: cpp_opts->pedantic_errors = 1; /* Fall through. */ case OPT_pedantic: cpp_opts->pedantic = 1; cpp_opts->warn_endif_labels = 1; break; /* SDCPP specfic */ case OPT_pedantic_parse_number: cpp_opts->pedantic_parse_number = 1; break; #if 0 // pch not supported by sdcpp case OPT_print_pch_checksum: c_common_print_pch_checksum (stdout); exit_after_options = true; break; #endif case OPT_remap: cpp_opts->remap = 1; break; case OPT_std_c89: case OPT_std_iso9899_1990: case OPT_std_iso9899_199409: set_std_c89 (code == OPT_std_iso9899_199409 /* c94 */, true /* ISO */); break; case OPT_std_c99: case OPT_std_iso9899_1999: set_std_c99 (true /* ISO */); break; case OPT_trigraphs: cpp_opts->trigraphs = 1; break; case OPT_traditional_cpp: cpp_opts->traditional = 1; break; case OPT_w: cpp_opts->inhibit_warnings = 1; break; case OPT_v: verbose = true; break; } return result; } /* Post-switch processing. */ bool sdcpp_common_post_options (const char **pfilename) { struct cpp_callbacks *cb; /* Canonicalize the input and output filenames. */ if (in_fnames == NULL) { in_fnames = XNEWVEC (const char *, 1); in_fnames[0] = ""; } else if (strcmp (in_fnames[0], "-") == 0) in_fnames[0] = ""; if (out_fname == NULL || !strcmp (out_fname, "-")) out_fname = ""; if (cpp_opts->deps.style == DEPS_NONE) check_deps_environment_vars (); handle_deferred_opts (); sanitize_cpp_opts (); register_include_chains (parse_in, sysroot, iprefix, imultilib, std_inc, 0, verbose); /* Open the output now. We must do so even if flag_no_output is on, because there may be other output than from the actual preprocessing (e.g. from -dM). */ if (out_fname[0] == '\0') out_stream = stdout; else out_stream = fopen (out_fname, "w"); if (out_stream == NULL) { fatal_error ("opening output file %s: %m", out_fname); return false; } if (num_in_fnames > 1) error ("too many filenames given. Type %s --help for usage", progname); init_pp_output (out_stream); cb = cpp_get_callbacks (parse_in); cb->file_change = cb_file_change; cb->dir_change = cb_dir_change; cpp_post_options (parse_in); /* If an error has occurred in cpplib, note it so we fail immediately. */ errorcount += cpp_errors (parse_in); *pfilename = this_input_filename = cpp_read_main_file (parse_in, in_fnames[0]); /* Don't do any compilation or preprocessing if there is no input file. */ if (this_input_filename == NULL) { errorcount++; return false; } if (flag_working_directory && !flag_no_line_commands) pp_dir_change (parse_in, get_src_pwd ()); return 1; } /* Front end initialization. */ bool sdcpp_common_init (void) { /* Default CPP arithmetic to something sensible for the host for the benefit of dumb users like fix-header. */ cpp_opts->precision = CHAR_BIT * sizeof (long); cpp_opts->char_precision = CHAR_BIT; cpp_opts->int_precision = CHAR_BIT * sizeof (int); cpp_opts->wchar_precision = CHAR_BIT * sizeof (int); cpp_opts->unsigned_wchar = 1; cpp_opts->bytes_big_endian = BYTES_BIG_ENDIAN; /* This can't happen until after wchar_precision and bytes_big_endian are known. */ cpp_init_iconv (parse_in); #if 0 if (version_flag) c_common_print_pch_checksum (stderr); #endif finish_options (); preprocess_file (parse_in); return false; } /* Common finish hook for the C, ObjC and C++ front ends. */ void sdcpp_common_finish (void) { FILE *deps_stream = NULL; if (cpp_opts->deps.style != DEPS_NONE) { /* If -M or -MM was seen without -MF, default output to the output stream. */ if (!deps_file) deps_stream = out_stream; else { deps_stream = fopen (deps_file, deps_append ? "a": "w"); if (!deps_stream) fatal_error ("opening dependency file %s: %m", deps_file); } } /* For performance, avoid tearing down cpplib's internal structures with cpp_destroy (). */ errorcount += cpp_finish (parse_in, deps_stream); if (deps_stream && deps_stream != out_stream && (ferror (deps_stream) || fclose (deps_stream))) fatal_error ("closing dependency file %s: %m", deps_file); if (out_stream && (ferror (out_stream) || fclose (out_stream))) fatal_error ("when writing output to %s: %m", out_fname); } /* Either of two environment variables can specify output of dependencies. Their value is either "OUTPUT_FILE" or "OUTPUT_FILE DEPS_TARGET", where OUTPUT_FILE is the file to write deps info to and DEPS_TARGET is the target to mention in the deps. They also result in dependency information being appended to the output file rather than overwriting it, and like Sun's compiler SUNPRO_DEPENDENCIES suppresses the dependency on the main file. */ static void check_deps_environment_vars (void) { char *spec; GET_ENVIRONMENT (spec, "DEPENDENCIES_OUTPUT"); if (spec) cpp_opts->deps.style = DEPS_USER; else { GET_ENVIRONMENT (spec, "SUNPRO_DEPENDENCIES"); if (spec) { cpp_opts->deps.style = DEPS_SYSTEM; cpp_opts->deps.ignore_main_file = true; } } if (spec) { /* Find the space before the DEPS_TARGET, if there is one. */ char *s = strchr (spec, ' '); if (s) { /* Let the caller perform MAKE quoting. */ defer_opt (OPT_MT, s + 1); *s = '\0'; } /* Command line -MF overrides environment variables and default. */ if (!deps_file) deps_file = spec; deps_append = 1; deps_seen = true; } } /* Handle deferred command line switches. */ static void handle_deferred_opts (void) { size_t i; struct deps *deps; /* Avoid allocating the deps buffer if we don't need it. (This flag may be true without there having been -MT or -MQ options, but we'll still need the deps buffer.) */ if (!deps_seen) return; deps = cpp_get_deps (parse_in); for (i = 0; i < deferred_count; i++) { struct deferred_opt *opt = &deferred_opts[i]; if (opt->code == OPT_MT || opt->code == OPT_MQ) deps_add_target (deps, opt->arg, opt->code == OPT_MQ); } } /* These settings are appropriate for GCC, but not necessarily so for cpplib as a library. */ static void sanitize_cpp_opts (void) { /* If we don't know what style of dependencies to output, complain if any other dependency switches have been given. */ if (deps_seen && cpp_opts->deps.style == DEPS_NONE) error ("to generate dependencies you must specify either -M or -MM"); /* -dM and dependencies suppress normal output; do it here so that the last -d[MDN] switch overrides earlier ones. */ if (flag_dump_macros == 'M') flag_no_output = 1; /* Disable -dD, -dN and -dI if normal output is suppressed. Allow -dM since at least glibc relies on -M -dM to work. */ /* Also, flag_no_output implies flag_no_line_commands, always. */ if (flag_no_output) { if (flag_dump_macros != 'M') flag_dump_macros = 0; flag_dump_includes = 0; flag_no_line_commands = 1; } cpp_opts->unsigned_char = !flag_signed_char; cpp_opts->stdc_0_in_system_headers = STDC_0_IN_SYSTEM_HEADERS; /* Similarly with -Wno-variadic-macros. No check for c99 here, since this also turns off warnings about GCCs extension. */ cpp_opts->warn_variadic_macros = warn_variadic_macros && (pedantic || warn_traditional); /* If we're generating preprocessor output, emit current directory if explicitly requested */ if (flag_working_directory == -1) flag_working_directory = 0; } /* Add include path with a prefix at the front of its name. */ static void add_prefixed_path (const char *suffix, size_t chain) { char *path; const char *prefix; size_t prefix_len, suffix_len; suffix_len = strlen (suffix); prefix = iprefix ? iprefix : cpp_GCC_INCLUDE_DIR; prefix_len = iprefix ? strlen (iprefix) : cpp_GCC_INCLUDE_DIR_len; path = (char *) xmalloc (prefix_len + suffix_len + 1); memcpy (path, prefix, prefix_len); memcpy (path + prefix_len, suffix, suffix_len); path[prefix_len + suffix_len] = '\0'; add_path (path, chain, 0, false); } /* Handle -D, -U, -A, -imacros, and the first -include. */ static void finish_options (void) { if (!cpp_opts->preprocessed) { size_t i; cb_file_change (parse_in, linemap_add (&line_table, LC_RENAME, 0, _(""), 0)); cpp_init_builtins (parse_in, 0 /*flag_hosted*/); /* We're about to send user input to cpplib, so make it warn for things that we previously (when we sent it internal definitions) told it to not warn. C99 permits implementation-defined characters in identifiers. The documented meaning of -std= is to turn off extensions that conflict with the specified standard, and since a strictly conforming program cannot contain a '$', we do not condition their acceptance on the -std= setting. */ cpp_opts->warn_dollars = (cpp_opts->pedantic && !cpp_opts->c99); cpp_change_file (parse_in, LC_RENAME, _("")); for (i = 0; i < deferred_count; i++) { struct deferred_opt *opt = &deferred_opts[i]; if (opt->code == OPT_D) cpp_define (parse_in, opt->arg); else if (opt->code == OPT_U) cpp_undef (parse_in, opt->arg); else if (opt->code == OPT_A) { if (opt->arg[0] == '-') cpp_unassert (parse_in, opt->arg + 1); else cpp_assert (parse_in, opt->arg); } } /* Handle -imacros after -D and -U. */ for (i = 0; i < deferred_count; i++) { struct deferred_opt *opt = &deferred_opts[i]; if (opt->code == OPT_imacros && cpp_push_include (parse_in, opt->arg)) { /* Disable push_command_line_include callback for now. */ include_cursor = deferred_count + 1; cpp_scan_nooutput (parse_in); } } } include_cursor = 0; push_command_line_include (); } /* Give CPP the next file given by -include, if any. */ static void push_command_line_include (void) { while (include_cursor < deferred_count) { struct deferred_opt *opt = &deferred_opts[include_cursor++]; if (!cpp_opts->preprocessed && opt->code == OPT_include && cpp_push_include (parse_in, opt->arg)) return; } if (include_cursor == deferred_count) { include_cursor++; /* -Wunused-macros should only warn about macros defined hereafter. */ cpp_opts->warn_unused_macros = warn_unused_macros; /* Restore the line map from . */ if (!cpp_opts->preprocessed) cpp_change_file (parse_in, LC_RENAME, this_input_filename); /* Set this here so the client can change the option if it wishes, and after stacking the main file so we don't trace the main file. */ line_table.trace_includes = cpp_opts->print_include_names; } } /* File change callback. Has to handle -include files. */ static void cb_file_change (cpp_reader * ARG_UNUSED (pfile), const struct line_map *new_map) { pp_file_change (new_map); if (new_map == 0 || (new_map->reason == LC_LEAVE && MAIN_FILE_P (new_map))) push_command_line_include (); } void cb_dir_change (cpp_reader * ARG_UNUSED (pfile), const char *dir) { if (!set_src_pwd (dir)) warning (0, "too late for # directive to set debug directory"); } /* Set the C 89 standard (with 1994 amendments if C94, without GNU extensions if ISO). There is no concept of gnu94. */ static void set_std_c89 (int c94, int iso) { cpp_set_lang (parse_in, c94 ? CLK_STDC94: iso ? CLK_STDC89: CLK_GNUC89); } /* Set the C 99 standard (without GNU extensions if ISO). */ static void set_std_c99 (int iso) { cpp_set_lang (parse_in, iso ? CLK_STDC99: CLK_GNUC99); } /* Args to -d specify what to dump. Silently ignore unrecognized options; they may be aimed at toplev.c. */ static void handle_OPT_d (const char *arg) { char c; while ((c = *arg++) != '\0') switch (c) { case 'M': /* Dump macros only. */ case 'N': /* Dump names. */ case 'D': /* Dump definitions. */ flag_dump_macros = c; break; case 'I': flag_dump_includes = 1; break; } }