Files
Nim/nim/commands.pas
2010-02-26 01:26:16 +01:00

589 lines
20 KiB
ObjectPascal
Executable File

//
//
// The Nimrod Compiler
// (c) Copyright 2009 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
//
unit commands;
// This module handles the parsing of command line arguments.
interface
{$include 'config.inc'}
uses
nsystem, charsets, nos, msgs, options, nversion, condsyms, strutils, extccomp,
platform, lists, wordrecg;
procedure writeCommandLineUsage;
type
TCmdLinePass = (
passCmd1, // first pass over the command line
passCmd2, // second pass over the command line
passPP // preprocessor called ProcessCommand()
);
procedure ProcessCommand(const switch: string; pass: TCmdLinePass);
procedure processSwitch(const switch, arg: string; pass: TCmdlinePass;
const info: TLineInfo);
implementation
{@ignore}
const
{$ifdef fpc}
compileDate = {$I %date%};
{$else}
compileDate = '2009-0-0';
{$endif}
{@emit}
const
HelpMessage = 'Nimrod Compiler Version $1 (' +{&}
compileDate +{&} ') [$2: $3]' +{&} nl +{&}
'Copyright (c) 2004-2009 by Andreas Rumpf' +{&} nl;
const
Usage = ''
//[[[cog
//from string import replace
//def f(x): return "+{&} '" + replace(x, "'", "''")[:-1] + "' +{&} nl"
//for line in open("data/basicopt.txt").readlines():
// cog.outl(f(line))
//]]]
+{&} 'Usage::' +{&} nl
+{&} ' nimrod command [options] inputfile [arguments]' +{&} nl
+{&} 'Command::' +{&} nl
+{&} ' compile, c compile project with default code generator (C)' +{&} nl
+{&} ' compileToC, cc compile project with C code generator' +{&} nl
+{&} ' doc generate the documentation for inputfile' +{&} nl
+{&} ' rst2html converts a reStructuredText file to HTML' +{&} nl
+{&} ' rst2tex converts a reStructuredText file to TeX' +{&} nl
+{&} 'Arguments:' +{&} nl
+{&} ' arguments are passed to the program being run (if --run option is selected)' +{&} nl
+{&} 'Options:' +{&} nl
+{&} ' -p, --path:PATH add path to search paths' +{&} nl
+{&} ' -o, --out:FILE set the output filename' +{&} nl
+{&} ' -d, --define:SYMBOL define a conditional symbol' +{&} nl
+{&} ' -u, --undef:SYMBOL undefine a conditional symbol' +{&} nl
+{&} ' -f, --forceBuild force rebuilding of all modules' +{&} nl
+{&} ' --symbolFiles:on|off use symbol files to speed up compilation (buggy!)' +{&} nl
+{&} ' --stackTrace:on|off code generation for stack trace ON|OFF' +{&} nl
+{&} ' --lineTrace:on|off code generation for line trace ON|OFF' +{&} nl
+{&} ' --debugger:on|off turn Embedded Nimrod Debugger ON|OFF' +{&} nl
+{&} ' -x, --checks:on|off code generation for all runtime checks ON|OFF' +{&} nl
+{&} ' --objChecks:on|off code generation for obj conversion checks ON|OFF' +{&} nl
+{&} ' --fieldChecks:on|off code generation for case variant fields ON|OFF' +{&} nl
+{&} ' --rangeChecks:on|off code generation for range checks ON|OFF' +{&} nl
+{&} ' --boundChecks:on|off code generation for bound checks ON|OFF' +{&} nl
+{&} ' --overflowChecks:on|off code generation for over-/underflow checks ON|OFF' +{&} nl
+{&} ' -a, --assertions:on|off code generation for assertions ON|OFF' +{&} nl
+{&} ' --deadCodeElim:on|off whole program dead code elimination ON|OFF' +{&} nl
+{&} ' --opt:none|speed|size optimize not at all or for speed|size' +{&} nl
+{&} ' --app:console|gui|lib generate a console|GUI application|dynamic library' +{&} nl
+{&} ' -r, --run run the compiled program with given arguments' +{&} nl
+{&} ' --advanced show advanced command line switches' +{&} nl
+{&} ' -h, --help show this help' +{&} nl
//[[[end]]]
;
AdvancedUsage = ''
//[[[cog
//for line in open("data/advopt.txt").readlines():
// cog.outl(f(line))
//]]]
+{&} 'Advanced commands::' +{&} nl
+{&} ' pas convert a Pascal file to Nimrod syntax' +{&} nl
+{&} ' pretty pretty print the inputfile' +{&} nl
+{&} ' genDepend generate a DOT file containing the' +{&} nl
+{&} ' module dependency graph' +{&} nl
+{&} ' listDef list all defined conditionals and exit' +{&} nl
+{&} ' check checks the project for syntax and semantic' +{&} nl
+{&} ' parse parses a single file (for debugging Nimrod)' +{&} nl
+{&} 'Advanced options:' +{&} nl
+{&} ' -w, --warnings:on|off warnings ON|OFF' +{&} nl
+{&} ' --warning[X]:on|off specific warning X ON|OFF' +{&} nl
+{&} ' --hints:on|off hints ON|OFF' +{&} nl
+{&} ' --hint[X]:on|off specific hint X ON|OFF' +{&} nl
+{&} ' --lib:PATH set the system library path' +{&} nl
+{&} ' -c, --compileOnly compile only; do not assemble or link' +{&} nl
+{&} ' --noLinking compile but do not link' +{&} nl
+{&} ' --noMain do not generate a main procedure' +{&} nl
+{&} ' --genScript generate a compile script (in the ''nimcache''' +{&} nl
+{&} ' subdirectory named ''compile_$project$scriptext'')' +{&} nl
+{&} ' --os:SYMBOL set the target operating system (cross-compilation)' +{&} nl
+{&} ' --cpu:SYMBOL set the target processor (cross-compilation)' +{&} nl
+{&} ' --debuginfo enables debug information' +{&} nl
+{&} ' -t, --passc:OPTION pass an option to the C compiler' +{&} nl
+{&} ' -l, --passl:OPTION pass an option to the linker' +{&} nl
+{&} ' --genMapping generate a mapping file containing' +{&} nl
+{&} ' (Nimrod, mangled) identifier pairs' +{&} nl
+{&} ' --lineDir:on|off generation of #line directive ON|OFF' +{&} nl
+{&} ' --checkpoints:on|off turn on|off checkpoints; for debugging Nimrod' +{&} nl
+{&} ' --skipCfg do not read the general configuration file' +{&} nl
+{&} ' --skipProjCfg do not read the project''s configuration file' +{&} nl
+{&} ' --gc:refc|boehm|none use Nimrod''s native GC|Boehm GC|no GC' +{&} nl
+{&} ' --index:FILE use FILE to generate a documenation index file' +{&} nl
+{&} ' --putenv:key=value set an environment variable' +{&} nl
+{&} ' --listCmd list the commands used to execute external programs' +{&} nl
+{&} ' --parallelBuild=0|1|... perform a parallel build' +{&} nl
+{&} ' value = number of processors (0 for auto-detect)' +{&} nl
+{&} ' --verbosity:0|1|2|3 set Nimrod''s verbosity level (0 is default)' +{&} nl
+{&} ' -v, --version show detailed version information' +{&} nl
//[[[end]]]
;
function getCommandLineDesc: string;
begin
result := format(HelpMessage, [VersionAsString,
platform.os[platform.hostOS].name, cpu[platform.hostCPU].name]) +{&} Usage
end;
var
helpWritten: boolean; // BUGFIX 19
versionWritten: boolean;
advHelpWritten: boolean;
procedure HelpOnError(pass: TCmdLinePass);
begin
if (pass = passCmd1) and not helpWritten then begin
// BUGFIX 19
MessageOut(getCommandLineDesc());
helpWritten := true;
halt(0);
end
end;
procedure writeAdvancedUsage(pass: TCmdLinePass);
begin
if (pass = passCmd1) and not advHelpWritten then begin
// BUGFIX 19
MessageOut(format(HelpMessage, [VersionAsString,
platform.os[platform.hostOS].name,
cpu[platform.hostCPU].name]) +{&}
AdvancedUsage);
advHelpWritten := true;
helpWritten := true;
halt(0);
end
end;
procedure writeVersionInfo(pass: TCmdLinePass);
begin
if (pass = passCmd1) and not versionWritten then begin
versionWritten := true;
helpWritten := true;
messageOut(format(HelpMessage, [VersionAsString,
platform.os[platform.hostOS].name,
cpu[platform.hostCPU].name]));
halt(0);
end
end;
procedure writeCommandLineUsage;
begin
if not helpWritten then begin
messageOut(getCommandLineDesc());
helpWritten := true
end
end;
procedure InvalidCmdLineOption(pass: TCmdLinePass; const switch: string;
const info: TLineInfo);
begin
liMessage(info, errInvalidCmdLineOption, switch)
end;
procedure splitSwitch(const switch: string; out cmd, arg: string;
pass: TCmdLinePass; const info: TLineInfo);
var
i: int;
begin
cmd := '';
i := strStart;
if (i < length(switch)+strStart) and (switch[i] = '-') then inc(i);
if (i < length(switch)+strStart) and (switch[i] = '-') then inc(i);
while i < length(switch) + strStart do begin
case switch[i] of
'a'..'z', 'A'..'Z', '0'..'9', '_', '.':
addChar(cmd, switch[i]);
else break;
end;
inc(i);
end;
if i >= length(switch) + strStart then
arg := ''
else if switch[i] in [':', '=', '['] then
arg := ncopy(switch, i + 1)
else
InvalidCmdLineOption(pass, switch, info)
end;
procedure ProcessOnOffSwitch(const op: TOptions; const arg: string;
pass: TCmdlinePass; const info: TLineInfo);
begin
case whichKeyword(arg) of
wOn: gOptions := gOptions + op;
wOff: gOptions := gOptions - op;
else liMessage(info, errOnOrOffExpectedButXFound, arg)
end
end;
procedure ProcessOnOffSwitchG(const op: TGlobalOptions; const arg: string;
pass: TCmdlinePass; const info: TLineInfo);
begin
case whichKeyword(arg) of
wOn: gGlobalOptions := gGlobalOptions + op;
wOff: gGlobalOptions := gGlobalOptions - op;
else liMessage(info, errOnOrOffExpectedButXFound, arg)
end
end;
procedure ExpectArg(const switch, arg: string; pass: TCmdLinePass;
const info: TLineInfo);
begin
if (arg = '') then
liMessage(info, errCmdLineArgExpected, switch)
end;
procedure ExpectNoArg(const switch, arg: string; pass: TCmdLinePass;
const info: TLineInfo);
begin
if (arg <> '') then
liMessage(info, errCmdLineNoArgExpected, switch)
end;
procedure ProcessSpecificNote(const arg: string; state: TSpecialWord;
pass: TCmdlinePass; const info: TLineInfo);
var
i, x: int;
n: TNoteKind;
id: string;
begin
id := '';
// arg = "X]:on|off"
i := strStart;
n := hintMin;
while (i < length(arg)+strStart) and (arg[i] <> ']') do begin
addChar(id, arg[i]);
inc(i)
end;
if (i < length(arg)+strStart) and (arg[i] = ']') then
inc(i)
else
InvalidCmdLineOption(pass, arg, info);
if (i < length(arg)+strStart) and (arg[i] in [':', '=']) then
inc(i)
else
InvalidCmdLineOption(pass, arg, info);
if state = wHint then begin
x := findStr(msgs.HintsToStr, id);
if x >= 0 then
n := TNoteKind(x + ord(hintMin))
else
InvalidCmdLineOption(pass, arg, info)
end
else begin
x := findStr(msgs.WarningsToStr, id);
if x >= 0 then
n := TNoteKind(x + ord(warnMin))
else
InvalidCmdLineOption(pass, arg, info)
end;
case whichKeyword(ncopy(arg, i)) of
wOn: include(gNotes, n);
wOff: exclude(gNotes, n);
else liMessage(info, errOnOrOffExpectedButXFound, arg)
end
end;
function processPath(const path: string): string;
begin
result := UnixToNativePath(format(path,
['nimrod', getPrefixDir(), 'lib', libpath]))
end;
procedure processCompile(const filename: string);
var
found, trunc: string;
begin
found := findFile(filename);
if found = '' then found := filename;
trunc := changeFileExt(found, '');
extccomp.addExternalFileToCompile(trunc);
extccomp.addFileToLink(completeCFilePath(trunc, false));
end;
procedure processSwitch(const switch, arg: string; pass: TCmdlinePass;
const info: TLineInfo);
var
theOS: TSystemOS;
cpu: TSystemCPU;
key, val, path: string;
begin
case whichKeyword(switch) of
wPath, wP: begin
expectArg(switch, arg, pass, info);
path := processPath(arg);
{@discard} lists.IncludeStr(options.searchPaths, path)
end;
wOut, wO: begin
expectArg(switch, arg, pass, info);
options.outFile := arg;
end;
wDefine, wD: begin
expectArg(switch, arg, pass, info);
DefineSymbol(arg)
end;
wUndef, wU: begin
expectArg(switch, arg, pass, info);
UndefSymbol(arg)
end;
wCompile: begin
expectArg(switch, arg, pass, info);
if pass in {@set}[passCmd2, passPP] then
processCompile(arg);
end;
wLink: begin
expectArg(switch, arg, pass, info);
if pass in {@set}[passCmd2, passPP] then
addFileToLink(arg);
end;
wDebuginfo: begin
expectNoArg(switch, arg, pass, info);
include(gGlobalOptions, optCDebug);
end;
wCompileOnly, wC: begin
expectNoArg(switch, arg, pass, info);
include(gGlobalOptions, optCompileOnly);
end;
wNoLinking: begin
expectNoArg(switch, arg, pass, info);
include(gGlobalOptions, optNoLinking);
end;
wNoMain: begin
expectNoArg(switch, arg, pass, info);
include(gGlobalOptions, optNoMain);
end;
wForceBuild, wF: begin
expectNoArg(switch, arg, pass, info);
include(gGlobalOptions, optForceFullMake);
end;
wGC: begin
expectArg(switch, arg, pass, info);
case whichKeyword(arg) of
wBoehm: begin
include(gGlobalOptions, optBoehmGC);
exclude(gGlobalOptions, optRefcGC);
DefineSymbol('boehmgc');
end;
wRefc: begin
exclude(gGlobalOptions, optBoehmGC);
include(gGlobalOptions, optRefcGC)
end;
wNone: begin
exclude(gGlobalOptions, optRefcGC);
exclude(gGlobalOptions, optBoehmGC);
defineSymbol('nogc');
end
else
liMessage(info, errNoneBoehmRefcExpectedButXFound, arg)
end
end;
wWarnings, wW: ProcessOnOffSwitch({@set}[optWarns], arg, pass, info);
wWarning: ProcessSpecificNote(arg, wWarning, pass, info);
wHint: ProcessSpecificNote(arg, wHint, pass, info);
wHints: ProcessOnOffSwitch({@set}[optHints], arg, pass, info);
wCheckpoints: ProcessOnOffSwitch({@set}[optCheckpoints], arg, pass, info);
wStackTrace: ProcessOnOffSwitch({@set}[optStackTrace], arg, pass, info);
wLineTrace: ProcessOnOffSwitch({@set}[optLineTrace], arg, pass, info);
wDebugger: begin
ProcessOnOffSwitch({@set}[optEndb], arg, pass, info);
if optEndb in gOptions then
DefineSymbol('endb')
else
UndefSymbol('endb')
end;
wProfiler: begin
ProcessOnOffSwitch({@set}[optProfiler], arg, pass, info);
if optProfiler in gOptions then DefineSymbol('profiler')
else UndefSymbol('profiler')
end;
wChecks, wX: ProcessOnOffSwitch(checksOptions, arg, pass, info);
wObjChecks: ProcessOnOffSwitch({@set}[optObjCheck], arg, pass, info);
wFieldChecks: ProcessOnOffSwitch({@set}[optFieldCheck], arg, pass, info);
wRangeChecks: ProcessOnOffSwitch({@set}[optRangeCheck], arg, pass, info);
wBoundChecks: ProcessOnOffSwitch({@set}[optBoundsCheck], arg, pass, info);
wOverflowChecks: ProcessOnOffSwitch({@set}[optOverflowCheck], arg, pass, info);
wLineDir: ProcessOnOffSwitch({@set}[optLineDir], arg, pass, info);
wAssertions, wA: ProcessOnOffSwitch({@set}[optAssert], arg, pass, info);
wDeadCodeElim: ProcessOnOffSwitchG({@set}[optDeadCodeElim], arg, pass, info);
wOpt: begin
expectArg(switch, arg, pass, info);
case whichKeyword(arg) of
wSpeed: begin
include(gOptions, optOptimizeSpeed);
exclude(gOptions, optOptimizeSize)
end;
wSize: begin
exclude(gOptions, optOptimizeSpeed);
include(gOptions, optOptimizeSize)
end;
wNone: begin
exclude(gOptions, optOptimizeSpeed);
exclude(gOptions, optOptimizeSize)
end
else
liMessage(info, errNoneSpeedOrSizeExpectedButXFound, arg)
end
end;
wApp: begin
expectArg(switch, arg, pass, info);
case whichKeyword(arg) of
wGui: begin
include(gGlobalOptions, optGenGuiApp);
defineSymbol('guiapp')
end;
wConsole:
exclude(gGlobalOptions, optGenGuiApp);
wLib: begin
include(gGlobalOptions, optGenDynLib);
exclude(gGlobalOptions, optGenGuiApp);
defineSymbol('library')
end;
else
liMessage(info, errGuiConsoleOrLibExpectedButXFound, arg)
end
end;
wListDef: begin
expectNoArg(switch, arg, pass, info);
if pass in {@set}[passCmd2, passPP] then
condsyms.listSymbols();
end;
wPassC, wT: begin
expectArg(switch, arg, pass, info);
if pass in {@set}[passCmd2, passPP] then
extccomp.addCompileOption(arg)
end;
wPassL, wL: begin
expectArg(switch, arg, pass, info);
if pass in {@set}[passCmd2, passPP] then
extccomp.addLinkOption(arg)
end;
wIndex: begin
expectArg(switch, arg, pass, info);
if pass in {@set}[passCmd2, passPP] then
gIndexFile := arg
end;
wImport: begin
expectArg(switch, arg, pass, info);
options.addImplicitMod(arg);
end;
wListCmd: begin
expectNoArg(switch, arg, pass, info);
include(gGlobalOptions, optListCmd);
end;
wGenMapping: begin
expectNoArg(switch, arg, pass, info);
include(gGlobalOptions, optGenMapping);
end;
wOS: begin
expectArg(switch, arg, pass, info);
if (pass = passCmd1) then begin
theOS := platform.NameToOS(arg);
if theOS = osNone then
liMessage(info, errUnknownOS, arg);
if theOS <> platform.hostOS then begin
setTarget(theOS, targetCPU);
include(gGlobalOptions, optCompileOnly);
condsyms.InitDefines()
end
end
end;
wCPU: begin
expectArg(switch, arg, pass, info);
if (pass = passCmd1) then begin
cpu := platform.NameToCPU(arg);
if cpu = cpuNone then
liMessage(info, errUnknownCPU, arg);
if cpu <> platform.hostCPU then begin
setTarget(targetOS, cpu);
include(gGlobalOptions, optCompileOnly);
condsyms.InitDefines()
end
end
end;
wRun, wR: begin
expectNoArg(switch, arg, pass, info);
include(gGlobalOptions, optRun);
end;
wVerbosity: begin
expectArg(switch, arg, pass, info);
gVerbosity := parseInt(arg);
end;
wParallelBuild: begin
expectArg(switch, arg, pass, info);
gNumberOfProcessors := parseInt(arg);
end;
wVersion, wV: begin
expectNoArg(switch, arg, pass, info);
writeVersionInfo(pass);
end;
wAdvanced: begin
expectNoArg(switch, arg, pass, info);
writeAdvancedUsage(pass);
end;
wHelp, wH: begin
expectNoArg(switch, arg, pass, info);
helpOnError(pass);
end;
wSymbolFiles: ProcessOnOffSwitchG({@set}[optSymbolFiles], arg, pass, info);
wSkipCfg: begin
expectNoArg(switch, arg, pass, info);
include(gGlobalOptions, optSkipConfigFile);
end;
wSkipProjCfg: begin
expectNoArg(switch, arg, pass, info);
include(gGlobalOptions, optSkipProjConfigFile);
end;
wGenScript: begin
expectNoArg(switch, arg, pass, info);
include(gGlobalOptions, optGenScript);
end;
wLib: begin
expectArg(switch, arg, pass, info);
libpath := processPath(arg)
end;
wPutEnv: begin
expectArg(switch, arg, pass, info);
splitSwitch(arg, key, val, pass, info);
nos.putEnv(key, val);
end;
wCC: begin
expectArg(switch, arg, pass, info);
setCC(arg)
end;
else if strutils.find(switch, '.') >= strStart then
options.setConfigVar(switch, arg)
else
InvalidCmdLineOption(pass, switch, info)
end
end;
procedure ProcessCommand(const switch: string; pass: TCmdLinePass);
var
cmd, arg: string;
info: TLineInfo;
begin
info := newLineInfo('command line', 1, 1);
splitSwitch(switch, cmd, arg, pass, info);
ProcessSwitch(cmd, arg, pass, info)
end;
end.