/* options.c command line options handlnig Copyright (C) 2001 Jeff Teunissen Author: Jeff Teunissen Date: 2002/06/04 This program 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 of the License, or (at your option) any later version. This program 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 this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include #include #include "QF/va.h" #include "QF/progs/pr_comp.h" #include "tools/qfcc/include/cpp.h" #include "tools/qfcc/include/linker.h" #include "tools/qfcc/include/options.h" #include "tools/qfcc/include/qfcc.h" #include "tools/qfcc/include/strpool.h" const char *this_program; const char **source_files; static int num_files; static int files_size; // keep me sane when adding long options :P enum { start_opts = 255, // not used, starts the enum. OPT_ADVANCED, OPT_BLOCK_DOT, OPT_CPP, OPT_EXTENDED, OPT_FRAMES, OPT_INCLUDE, OPT_NO_DEFAULT_PATHS, OPT_PROGDEFS, OPT_QCCX_ESCAPES, OPT_RUAMOKO, OPT_TRADITIONAL, OPT_BUG, }; static struct option const long_options[] = { {"advanced", no_argument, 0, OPT_ADVANCED}, {"block-dot", optional_argument, 0, OPT_BLOCK_DOT}, {"bug", required_argument, 0, OPT_BUG}, {"code", required_argument, 0, 'C'}, {"cpp", required_argument, 0, OPT_CPP}, {"define", required_argument, 0, 'D'}, {"extended", no_argument, 0, OPT_EXTENDED}, {"files", no_argument, 0, 'F'}, {"frames", no_argument, 0, OPT_FRAMES}, {"help", no_argument, 0, 'h'}, {"include", required_argument, 0, OPT_INCLUDE}, {"no-default-paths", no_argument, 0, OPT_NO_DEFAULT_PATHS}, {"notice", required_argument, 0, 'N'}, {"output-file", required_argument, 0, 'o'}, {"progdefs", no_argument, 0, OPT_PROGDEFS}, {"progs-src", required_argument, 0, 'P'}, {"qccx-escapes", no_argument, 0, OPT_QCCX_ESCAPES}, {"quiet", no_argument, 0, 'q'}, {"raumoko", no_argument, 0, OPT_RUAMOKO}, {"relocatable", no_argument, 0, 'r'}, {"ruamoko", no_argument, 0, OPT_RUAMOKO}, {"save-temps", no_argument, 0, 'S'}, {"source", required_argument, 0, 's'}, {"traditional", no_argument, 0, OPT_TRADITIONAL}, {"undefine", required_argument, 0, 'U'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {"warn", required_argument, 0, 'W'}, {NULL, 0, NULL, 0} }; static const char *short_options = "-" // magic option parsing mode doohicky (must come first) "C:" // code options "c" // separate compilation "D:" // define "E" // only preprocess "F" // generate files.dat "g" // debug "h" // help "I:" // set includes "L:" // lib path "l:" // lib file "M::" "N:" // notice options "o:" // output file "O" // optimize "P:" // progs.src name "q" // quiet "r" // partial linking "S" // save temps "s:" // source dir "U:" // undefine "V" // version "v" // verbose "W:" // warning options "z" // compress qfo files ; static void usage (int status) { printf ("%s - QuakeForge Code Compiler\n", this_program); printf ("Usage: %s [options] [files]\n", this_program); printf ( "Options:\n" " --advanced Advanced Ruamoko mode\n" " --bug OPTION,... Set bug options\n" " -C, --code OPTION,... Set code generation options\n" " -c Only compile, don't link\n" " --cpp CPPSPEC cpp execution command line\n" " -D, --define SYMBOL[=VAL] Define symbols for the preprocessor\n" " -E Only preprocess\n" " --extended Allow extended keywords in traditional mode\n" " -F, --files Generate files.dat\n" " --frames Generate .frame files\n" " -g Generate debugging info\n" " -h, --help Display this help and exit\n" " -I DIR Set directories for the preprocessor\n" " to search for #includes\n" " --include FILE Process file as if `#include \"file\"'\n" " appeared as the first line of the primary\n" " source file.\n" " -L DIR Add linker library search path\n" " -l LIB Link with libLIB.a\n" " -M[flags] Generate depency info. Dependent on cpp\n" " --no-default-paths Do not search default paths for headers or\n" " libs\n" " -N, --notice OPTION,... Set notice options\n" " -o, --output-file FILE Specify output file name\n" " --progdefs Genderate progdefs.h\n" " -P, --progs-src FILE File to use instead of progs.src\n" " --qccx-escapes Use QCCX escape sequences instead of standard\n" " C/QuakeForge sequences.\n" " -q, --quiet Inhibit usual output\n" " -r, --relocatable Incremental linking\n" " --raumoko Use both Ruamoko language features and the\n" " --ruamoko Ruamoko ISA, providing access to SIMD types\n" " and a stack for locals.\n" " default for separate compilation mode\n" " -S, --save-temps Do not delete temporary files\n" " -s, --source DIR Look for progs.src in DIR instead of \".\"\n" " --traditional Traditional QuakeC mode: implies v6only\n" " default when using progs.src\n" " -U, --undefine SYMBOL Undefine preprocessor symbols\n" " -V, --version Output version information and exit\n" " -v, --verbose Display more output than usual\n" " -W, --warn OPTION,... Set warning options\n" " -z Compress object files\n" "\n" "For help on options for --code, --notice and --warn, see the qfcc(1) manual\n" "page, or use `help' as the option.\n" ); exit (status); } static void code_usage (void) { printf ("%s - QuakeForge Code Compiler\n", this_program); printf ("Code generation options\n"); printf ( " [no-]const-initializers Treat initialized globals as constants.\n" " [no-]cow Allow assignment to initialized globals.\n" " [no-]cpp Preprocess all input files with cpp.\n" " [no-]crc Write progdefs.h crc to progs.dat.\n" " [no-]debug Generate debug information.\n" " [no-]fast-float Use float values directly in \"if\" statements.\n" " help Display this text.\n" " [no-]local-merging Merge the local variable blocks into one.\n" " [no-]optimize Perform various optimizations on the code.\n" " [no-]promote-float Promote float when passed through ...\n" " [no-]short-circuit Generate short circuit code for logical\n" " operators.\n" " [no-]single-cpp Convert progs.src to cpp input file.\n" " [no-]vector-calls Generate slower but data efficient code for\n" " passing\n" " vectors to functiosn.\n" " [no-]vector-components Create *_[xyz] symbols for vector variables.\n" " target=v6|v6p|ruamoko Generate code for the specified target VM\n" " v6 Standard Quake VM (qcc compatible)\n" " v6p *QuakeForge extended v6 instructions\n" " ruamoko QuakeForge SIMD instructions\n" "\n" "For details, see the qfcc(1) manual page\n" ); exit (0); } static void warning_usage (void) { printf ("%s - QuakeForge Code Compiler\n", this_program); printf ("Warnings options\n"); printf ( " all Turn on all warnings.\n" " [no-]cow Warn about assignement to initialized globals.\n" " [no-]error Treat warnings as errors.\n" " [no-]executable Warn about statements with no effect.\n" " help Display his text.\n" " [no-]initializer Warn about excessive structure/array\n" " initializers.\n" " [no-]integer-divide Warn about division of integer constants.\n" " [no-]interface-check Warn about methods not declared in the\n" " interface.\n" " none Turn off all warnings.\n" " [no-]precedence Warn about potentially ambiguous logic.\n" " [no-]switch Warn about unhandled enum values in switch\n" " statements.\n" " [no-]redeclared Warn about redeclared local variables.\n" " [no-]traditional Warn about bad code that qcc allowed.\n" " [no-]undef-function Warn about calling a yet to be defined\n" " function.\n" " [no-]unimplemented Warn about unimplemented class methods.\n" " [no-]unused Warn about unused local variables.\n" " [no-]uninited-var Warn about using uninitialied variables.\n" " [no-]vararg-integer Warn about passing integer constants to\n" " functions with variable args.\n" "\n" "For details, see the qfcc(1) manual page\n" ); exit (0); } static void notice_usage (void) { printf ("%s - QuakeForge Code Compiler\n", this_program); printf ("Notice options\n"); printf ( " help Display his text.\n" " none Turn off all notices.\n" " warn Change notices to warnings.\n" "\n" "For details, see the qfcc(1) manual page\n" ); exit (0); } static void bug_usage (void) { printf ("%s - QuakeForge Code Compiler\n", this_program); printf ("Bug options\n"); printf ( " help Display his text.\n" " none Turn off all bugs (don't we wish: messages).\n" " die Change bugs to internal errors.\n" "\n" "This is a developer feature and thus not in the manual page\n" ); exit (0); } static void add_file (const char *file) { if (num_files >= files_size - 1) { files_size += 16; source_files = realloc (source_files, files_size * sizeof (char *)); } source_files[num_files++] = save_string (file); source_files[num_files] = 0; } int DecodeArgs (int argc, char **argv) { int c; int saw_E = 0, saw_MD = 0; add_cpp_undef ("-undef"); add_cpp_undef ("-nostdinc"); add_cpp_def ("-D__QFCC__=1"); add_cpp_def ("-D__QUAKEC__=1"); options.code.short_circuit = -1; options.code.local_merging = -1; options.code.vector_components = -1; options.code.crc = -1; options.code.fast_float = true; options.code.promote_float = true; options.warnings.uninited_variable = true; options.warnings.unused = true; options.warnings.executable = true; options.warnings.traditional = true; options.warnings.precedence = true; options.warnings.initializer = true; options.warnings.unimplemented = true; options.warnings.redeclared = true; options.warnings.enum_switch = true; options.single_cpp = true; options.save_temps = false; options.verbosity = 0; sourcedir = ""; progs_src = "progs.src"; while ((c = getopt_long (argc, argv, short_options, long_options, 0)) != EOF) { switch (c) { case 1: // ordinary file add_file (NORMALIZE (optarg)); break; case 'o': if (options.output_file) { fprintf (stderr, "%s: -o must not be used more than once\n", this_program); exit (1); } else { options.output_file = save_string (NORMALIZE (optarg)); } break; case 'l': // lib file add_file (va (0, "-l%s", NORMALIZE (optarg))); break; case 'L': linker_add_path (NORMALIZE (optarg)); break; case 'h': // help usage (0); break; case 'V': // version printf ("%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION); exit (0); break; case 's': // src dir sourcedir = save_string (NORMALIZE (optarg)); break; case 'P': // progs-src progs_src = save_string (NORMALIZE (optarg)); break; case 'F': options.files_dat = true; break; case OPT_PROGDEFS: options.progdefs_h = true; break; case OPT_QCCX_ESCAPES: options.qccx_escapes = true; break; case 'q': // quiet options.verbosity -= 1; break; case 'v': // verbose options.verbosity += 1; break; case 'g': // debug options.code.debug = true; break; case 'O': // optimize options.code.optimize = true; break; case OPT_FRAMES: options.frames_files = 1; break; case OPT_EXTENDED: options.traditional = 1; options.advanced = false; options.code.progsversion = PROG_ID_VERSION; options.code.const_initializers = true; break; case OPT_TRADITIONAL: options.traditional = 2; options.advanced = 0; options.code.progsversion = PROG_ID_VERSION; options.code.const_initializers = true; break; case OPT_ADVANCED: options.traditional = 0; options.advanced = 1; options.code.progsversion = PROG_V6P_VERSION; options.code.const_initializers = false; break; case OPT_RUAMOKO: options.traditional = 0; options.advanced = 2; options.code.progsversion = PROG_VERSION; options.code.const_initializers = false; break; case OPT_BLOCK_DOT: if (optarg) { char *opts = strdup (optarg); char *temp = strtok (opts, ","); while (temp) { qboolean flag = true; if (!strncasecmp (temp, "no-", 3)) { flag = false; temp += 3; } if (!strcasecmp (temp, "initial")) { options.block_dot.initial = flag; } else if (!(strcasecmp (temp, "thread"))) { options.block_dot.thread = flag; } else if (!(strcasecmp (temp, "dead"))) { options.block_dot.dead = flag; } else if (!(strcasecmp (temp, "final"))) { options.block_dot.final = flag; } else if (!(strcasecmp (temp, "dags"))) { options.block_dot.dags = flag; } else if (!(strcasecmp (temp, "expr"))) { options.block_dot.expr = flag; } else if (!(strcasecmp (temp, "flow"))) { options.block_dot.flow = flag; } else if (!(strcasecmp (temp, "reaching"))) { options.block_dot.reaching = flag; } else if (!(strcasecmp (temp, "statements"))) { options.block_dot.statements = flag; } else if (!(strcasecmp (temp, "live"))) { options.block_dot.live = flag; } else if (!(strcasecmp (temp, "post"))) { options.block_dot.post = flag; } temp = strtok (NULL, ","); } free (opts); } else { options.block_dot.initial = true; options.block_dot.thread = true; options.block_dot.dead = true; options.block_dot.final = true; options.block_dot.dags = true; options.block_dot.expr = true; options.block_dot.flow = true; options.block_dot.reaching = true; options.block_dot.statements = true; options.block_dot.live = true; options.block_dot.post = true; } break; case 'c': options.compile = true; break; case 'r': options.partial_link = true; break; case 'z': options.gzip = true; break; case 'C':{ // code options char *opts = strdup (optarg); char *temp = strtok (opts, ","); while (temp) { qboolean flag = true; if (!(strncasecmp (temp, "target=", 7))) { const char *tgt = temp + 7; if (!strcasecmp (tgt, "v6")) { options.code.progsversion = PROG_ID_VERSION; } else if (!strcasecmp (tgt, "v6p")) { options.code.progsversion = PROG_V6P_VERSION; } else if (!strcasecmp (tgt, "ruamoko")) { options.code.progsversion = PROG_VERSION; } else { fprintf (stderr, "unknown target: %s\n", tgt); exit (1); } } if (!strncasecmp (temp, "no-", 3)) { flag = false; temp += 3; } if (!strcasecmp (temp, "cow")) { options.code.cow = flag; } else if (!(strcasecmp (temp, "cpp"))) { cpp_name = flag ? CPP_NAME : 0; } else if (!(strcasecmp (temp, "crc"))) { options.code.crc = flag; } else if (!(strcasecmp (temp, "debug"))) { options.code.debug = flag; } else if (!(strcasecmp (temp, "fast-float"))) { options.code.fast_float = flag; } else if (!(strcasecmp (temp, "promote-float"))) { options.code.promote_float = flag; } else if (!strcasecmp (temp, "help")) { code_usage (); } else if (!(strcasecmp (temp, "local-merging"))) { options.code.local_merging = flag; } else if (!(strcasecmp (temp, "optimize"))) { options.code.optimize = flag; } else if (!(strcasecmp (temp, "short-circuit"))) { options.code.short_circuit = flag; } else if (!(strcasecmp (temp, "ifstring"))) { options.code.ifstring = flag; } else if (!(strcasecmp (temp, "single-cpp"))) { options.single_cpp = flag; } else if (!(strcasecmp (temp, "vector-calls"))) { options.code.vector_calls = flag; } else if (!(strcasecmp (temp, "vector-components"))) { options.code.vector_components = flag; } else if (!(strcasecmp (temp, "const-initializers"))) { options.code.const_initializers = flag; } temp = strtok (NULL, ","); } free (opts); } break; case 'W':{ // warning options char *opts = strdup (optarg); char *temp = strtok (opts, ","); while (temp) { if (!(strcasecmp (temp, "all"))) { options.warnings.cow = true; options.warnings.undefined_function = true; options.warnings.uninited_variable = true; options.warnings.vararg_integer = true; options.warnings.integer_divide = true; options.warnings.interface_check = true; options.warnings.unused = true; options.warnings.executable = true; options.warnings.traditional = true; options.warnings.precedence = true; options.warnings.initializer = true; options.warnings.unimplemented = true; options.warnings.redeclared = true; options.warnings.enum_switch = true; } else if (!(strcasecmp (temp, "none"))) { options.warnings.cow = false; options.warnings.undefined_function = false; options.warnings.uninited_variable = false; options.warnings.vararg_integer = false; options.warnings.integer_divide = false; options.warnings.interface_check = false; options.warnings.unused = false; options.warnings.executable = false; options.warnings.traditional = false; options.warnings.precedence = false; options.warnings.initializer = false; options.warnings.unimplemented = false; options.warnings.redeclared = false; options.warnings.enum_switch = false; } else { qboolean flag = true; if (!strncasecmp (temp, "no-", 3)) { flag = false; temp += 3; } if (!(strcasecmp (temp, "cow"))) { options.warnings.cow = flag; } else if (!strcasecmp (temp, "error")) { options.warnings.promote = flag; } else if (!strcasecmp (temp, "executable")) { options.warnings.executable = flag; } else if (!strcasecmp (temp, "help")) { warning_usage (); } else if (!strcasecmp (temp, "initializer")) { options.warnings.initializer = flag; } else if (!strcasecmp (temp, "integer-divide")) { options.warnings.integer_divide = flag; } else if (!strcasecmp (temp, "interface-check")) { options.warnings.interface_check = flag; } else if (!strcasecmp (temp, "precedence")) { options.warnings.precedence = flag; } else if (!strcasecmp (temp, "redeclared")) { options.warnings.redeclared = flag; } else if (!strcasecmp (temp, "switch")) { options.warnings.enum_switch = flag; } else if (!strcasecmp (temp, "traditional")) { options.warnings.traditional = flag; } else if (!strcasecmp (temp, "undef-function")) { options.warnings.undefined_function = flag; } else if (!strcasecmp (temp, "unimplemented")) { options.warnings.unimplemented = flag; } else if (!strcasecmp (temp, "unused")) { options.warnings.unused = flag; } else if (!strcasecmp (temp, "uninited-var")) { options.warnings.uninited_variable = flag; } else if (!strcasecmp (temp, "vararg-integer")) { options.warnings.vararg_integer = flag; } } temp = strtok (NULL, ","); } free (opts); } break; case 'N':{ // notice options char *opts = strdup (optarg); char *temp = strtok (opts, ","); while (temp) { if (!strcasecmp (temp, "help")) { notice_usage (); } else if (!(strcasecmp (temp, "none"))) { options.notices.silent = true; } else if (!(strcasecmp (temp, "warn"))) { options.notices.promote = true; } temp = strtok (NULL, ","); } free (opts); } break; case OPT_BUG:{ char *opts = strdup (optarg); char *temp = strtok (opts, ","); while (temp) { if (!strcasecmp (temp, "help")) { bug_usage (); } else if (!(strcasecmp (temp, "none"))) { options.bug.silent = true; } else if (!(strcasecmp (temp, "die"))) { options.bug.promote = true; } temp = strtok (NULL, ","); } free (opts); } break; case OPT_CPP: // --cpp= cpp_name = save_string (optarg); break; case 'S': // save temps options.save_temps = true; break; case 'D': // defines for cpp add_cpp_def (nva ("%s%s", "-D", optarg)); break; case 'E': // defines for cpp saw_E = 1; options.preprocess_only = 1; add_cpp_def ("-E"); break; case OPT_INCLUDE: // include-file add_cpp_def (nva ("%s", "-include")); add_cpp_def (nva ("%s", optarg)); break; case 'I': // includes add_cpp_def (nva ("%s%s", "-I", optarg)); break; case 'U': // undefines add_cpp_def (nva ("%s%s", "-U", optarg)); break; case 'M': options.preprocess_only = 1; if (optarg) { add_cpp_def (nva ("-M%s", optarg)); if (strchr (optarg, 'D')) saw_MD = 1; if (strchr ("FQT", optarg[0])) add_cpp_def (argv[optind++]); } else { options.preprocess_only = 1; add_cpp_def (nva ("-M")); } break; case OPT_NO_DEFAULT_PATHS: options.no_default_paths = 1; break; default: usage (1); } } if (!cpp_name) options.single_cpp = 0; if (saw_E && saw_MD) { fprintf (stderr, "%s: cannot use -E and -MD together\n", this_program); exit (1); } if (saw_MD) options.preprocess_only = 0; if (!source_files && !options.advanced) { // progs.src mode without --advanced or --ruamoko implies --traditional // but --extended overrides if (!options.traditional) options.traditional = 2; options.advanced = false; if (!options.code.progsversion) options.code.progsversion = PROG_ID_VERSION; if (options.code.ifstring == (qboolean) -1) options.code.ifstring = false; if (options.code.short_circuit == (qboolean) -1) options.code.short_circuit = false; if (options.code.local_merging == (qboolean) -1) options.code.local_merging = false; if (options.code.vector_components == (qboolean) -1) options.code.vector_components = true; } if (!options.code.progsversion) options.code.progsversion = PROG_VERSION; if (!options.traditional) { // avanced=2 requires the Ruamoko ISA options.advanced = 2 - (options.code.progsversion < PROG_VERSION); const char *ruamoko = va (0, "-D__RUAMOKO__=%d", options.advanced); add_cpp_def (save_string (ruamoko)); if (options.code.ifstring == (qboolean) -1) options.code.ifstring = false; if (options.code.short_circuit == (qboolean) -1) options.code.short_circuit = true; if (options.code.local_merging == (qboolean) -1) options.code.local_merging = true; if (options.code.vector_components == (qboolean) -1) options.code.vector_components = false; } else { options.code.promote_float = 0; } if (options.code.progsversion == PROG_ID_VERSION) { options.code.promote_float = 0; add_cpp_def ("-D__VERSION6__=1"); if (options.code.crc == (qboolean) -1) options.code.crc = true; } else { if (options.code.crc == (qboolean) -1) options.code.crc = false; } if (options.traditional && options.advanced) { fprintf (stderr, "%s: internal error: traditional and advanced twisted\n", this_program); abort (); } // add the default paths if (!options.no_default_paths) { add_cpp_sysinc ("-isystem"); add_cpp_sysinc (QFCC_INCLUDE_PATH); linker_add_path (QFCC_LIB_PATH); } if (options.verbosity >= 3) { qc_yydebug = 1; qp_yydebug = 1; } return optind; }