crc check for external files to compile; bugfix: os.parseCmdLine

This commit is contained in:
Andreas Rumpf
2010-04-05 19:47:18 +02:00
parent b2ad7b30dc
commit e90665bff2
7 changed files with 145 additions and 54 deletions

View File

@@ -926,28 +926,90 @@ proc createDir*(dir: string) =
rawCreateDir(dir)
proc parseCmdLine*(c: string): seq[string] =
## Splits a command line into several components; components are separated by
## whitespace unless the whitespace occurs within ``"`` or ``'`` quotes.
## Splits a command line into several components;
## This proc is only occassionally useful, better use the `parseopt` module.
##
## On Windows, it uses the following parsing rules
## (see http://msdn.microsoft.com/en-us/library/17w5ykft.aspx):
##
## * Arguments are delimited by white space, which is either a space or a tab.
## * The caret character (^) is not recognized as an escape character or
## delimiter. The character is handled completely by the command-line parser
## in the operating system before being passed to the argv array in the
## program.
## * A string surrounded by double quotation marks ("string") is interpreted
## as a single argument, regardless of white space contained within. A
## quoted string can be embedded in an argument.
## * A double quotation mark preceded by a backslash (\") is interpreted as a
## literal double quotation mark character (").
## * Backslashes are interpreted literally, unless they immediately precede
## a double quotation mark.
## * If an even number of backslashes is followed by a double quotation mark,
## one backslash is placed in the argv array for every pair of backslashes,
## and the double quotation mark is interpreted as a string delimiter.
## * If an odd number of backslashes is followed by a double quotation mark,
## one backslash is placed in the argv array for every pair of backslashes,
## and the double quotation mark is "escaped" by the remaining backslash,
## causing a literal double quotation mark (") to be placed in argv.
##
## On Posix systems, it uses the following parsing rules:
## components are separated by
## whitespace unless the whitespace occurs within ``"`` or ``'`` quotes.
result = @[]
var i = 0
var a = ""
while true:
setLen(a, 0)
while c[i] >= '\1' and c[i] <= ' ': inc(i) # skip whitespace
case c[i]
of '\'', '\"':
var delim = c[i]
inc(i) # skip ' or "
while c[i] != '\0' and c[i] != delim:
add a, c[i]
inc(i)
if c[i] != '\0': inc(i)
of '\0': break
while c[i] == ' ' or c[i] == '\t': inc(i)
when defined(windows):
# parse a single argument according to the above rules:
var inQuote = false
while true:
case c[i]
of '\0': break
of '\\':
var j = i
while c[j] == '\\': inc(j)
if c[j] == '"':
for k in 0..(j-i) div 2: a.add('\\')
if (j-i) mod 2 == 0:
i = j
else:
a.add('"')
i = j+1
else:
a.add(c[i])
inc(i)
of '"':
inc(i)
if not inQuote: inQuote = true
elif c[i] == '"':
a.add(c[i])
inc(i)
else:
inQuote = false
break
of ' ', '\t':
if not inQuote: break
a.add(c[i])
inc(i)
else:
a.add(c[i])
inc(i)
else:
while c[i] > ' ':
add(a, c[i])
inc(i)
case c[i]
of '\'', '\"':
var delim = c[i]
inc(i) # skip ' or "
while c[i] != '\0' and c[i] != delim:
add a, c[i]
inc(i)
if c[i] != '\0': inc(i)
of '\0': break
else:
while c[i] > ' ':
add(a, c[i])
inc(i)
add(result, a)
type

View File

@@ -233,9 +233,9 @@ type
## that is too small to be represented as
## a normal number
EFloatInexact* {.compilerproc.} =
object of EFloatingPoint ## Inexact. Operation produces a result that cannot
## be represented with infinite precision --
## for example, 2.0 / 3.0, log(1.1)
object of EFloatingPoint ## Inexact. Operation produces a result
## that cannot be represented with infinite
## precision -- for example, 2.0 / 3.0, log(1.1)
## NOTE: Nimrod currently does not detect these!
TResult* = enum Failure, Success
@@ -962,7 +962,7 @@ proc `$` *(x: Cstring): string {.magic: "CStrToStr", noSideEffect.}
proc `$` *(x: string): string {.magic: "StrToStr", noSideEffect.}
## The stingify operator for a string argument. Returns `x`
## as it is. This operator is useful for generic code, so
## that ``$expr`` also works if ``expr`` is already a string.
## that ``$expr`` also works if ``expr`` already is a string.
proc `$` *[T](x: ordinal[T]): string {.magic: "EnumToStr", noSideEffect.}
## The stingify operator for an enumeration argument. This works for

View File

@@ -216,11 +216,10 @@ proc processPath(path: string): string =
result = UnixToNativePath(path % ["nimrod", getPrefixDir(), "lib", libpath])
proc processCompile(filename: string) =
var found, trunc: string
found = findFile(filename)
var found = findFile(filename)
if found == "": found = filename
trunc = changeFileExt(found, "")
extccomp.addExternalFileToCompile(trunc)
var trunc = changeFileExt(found, "")
extccomp.addExternalFileToCompile(found)
extccomp.addFileToLink(completeCFilePath(trunc, false))
proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =

View File

@@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf
# (c) Copyright 2010 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -90,8 +90,7 @@ type
PByteArray = ref TByteArray
proc crcFromBuf(buf: Pointer, length: int): TCrc32 =
var p: PByteArray
p = cast[PByteArray](buf)
var p = cast[PByteArray](buf)
result = InitCrc32
for i in countup(0, length - 1): result = updateCrc32(p[i], result)
@@ -117,10 +116,12 @@ proc crcFromFile(filename: string): TCrc32 =
const
base = int32(65521) # largest prime smaller than 65536
#NMAX = 5552; original code with unsigned 32 bit integer
# NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
# NMAX = 5552; original code with unsigned 32 bit integer
# NMAX is the largest n
# such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1
nmax = 3854 # code with signed 32 bit integer
# NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^31-1
# NMAX is the largest n such that
# 255n(n+1)/2 + (n+1)(BASE-1) <= 2^31-1
# The penalty is the time loss in the extra MOD-calls.
proc updateAdler32(adler: int32, buf: pointer, length: int): int32 =

View File

@@ -11,7 +11,7 @@
# some things are read in from the configuration file
import
lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs
lists, ropes, os, strutils, osproc, platform, condsyms, options, msgs, crc
type
TSystemCC* = enum
@@ -299,12 +299,30 @@ proc toObjFile(filenameWithoutExt: string): string =
proc addFileToCompile(filename: string) =
appendStr(toCompile, filename)
proc externalFileChanged(filename: string): bool =
var crcFile = toGeneratedFile(filename, "crc")
var currentCrc = int(crcFromFile(filename))
var f: TFile
if open(f, crcFile, fmRead):
var line = f.readLine()
if isNil(line) or line.len == 0: line = "0"
close(f)
var oldCrc = parseInt(line)
result = oldCrc != currentCrc
else:
result = true
if result:
if open(f, crcFile, fmWrite):
f.writeln($currentCrc)
close(f)
proc addExternalFileToCompile(filename: string) =
appendStr(externalToCompile, filename)
if optForceFullMake in gGlobalOptions or externalFileChanged(filename):
appendStr(externalToCompile, changeFileExt(filename, ""))
proc addFileToLink(filename: string) =
prependStr(toLink, filename) # BUGFIX
#appendStr(toLink, filename);
prependStr(toLink, filename)
# BUGFIX: was ``appendStr``
proc execExternalProgram(cmd: string) =
if (optListCmd in gGlobalOptions) or (gVerbosity > 0): MessageOut(cmd)
@@ -361,8 +379,8 @@ proc getCompileCFileCmd(cfilename: string, isExternal: bool = false): string =
key = cc[c].name & ".exe"
if existsConfigVar(key): exe = getConfigVar(key)
if targetOS == osWindows: exe = addFileExt(exe, "exe")
if (optGenDynLib in gGlobalOptions) and
(ospNeedsPIC in platform.OS[targetOS].props):
if optGenDynLib in gGlobalOptions and
ospNeedsPIC in platform.OS[targetOS].props:
add(options, ' ' & cc[c].pic)
if targetOS == platform.hostOS:
# compute include paths:
@@ -383,11 +401,11 @@ proc getCompileCFileCmd(cfilename: string, isExternal: bool = false): string =
objfile, "options", options, "include", includeCmd, "nimrod",
getPrefixDir(), "lib", libpath]))
add(result, ' ')
add(result, `%`(cc[c].compileTmpl, ["file", cfile, "objfile", objfile,
"options", options, "include", includeCmd,
"nimrod",
quoteIfContainsWhite(getPrefixDir()),
"lib", quoteIfContainsWhite(libpath)]))
addf(result, cc[c].compileTmpl, [
"file", cfile, "objfile", objfile,
"options", options, "include", includeCmd,
"nimrod", quoteIfContainsWhite(getPrefixDir()),
"lib", quoteIfContainsWhite(libpath)])
proc CompileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq,
isExternal: bool) =
@@ -396,7 +414,7 @@ proc CompileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq,
inc(fileCounter) # call the C compiler for the .c file:
var compileCmd = getCompileCFileCmd(it.data, isExternal)
if not (optCompileOnly in gGlobalOptions):
add(cmds, compileCmd) #execExternalProgram(compileCmd);
add(cmds, compileCmd)
if (optGenScript in gGlobalOptions):
app(script, compileCmd)
app(script, tnl)
@@ -405,7 +423,7 @@ proc CompileCFile(list: TLinkedList, script: var PRope, cmds: var TStringSeq,
proc CallCCompiler(projectfile: string) =
var
linkCmd, buildgui, builddll: string
if (gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}):
if gGlobalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
return # speed up that call if only compiling and no script shall be
# generated
fileCounter = 0
@@ -414,7 +432,7 @@ proc CallCCompiler(projectfile: string) =
var cmds: TStringSeq = @[]
CompileCFile(toCompile, script, cmds, false)
CompileCFile(externalToCompile, script, cmds, true)
if not (optCompileOnly in gGlobalOptions):
if optCompileOnly notin gGlobalOptions:
if gNumberOfProcessors == 0: gNumberOfProcessors = countProcessors()
var res = 0
if gNumberOfProcessors <= 1:
@@ -426,7 +444,7 @@ proc CallCCompiler(projectfile: string) =
res = execProcesses(cmds, {poUseShell, poParentStreams},
gNumberOfProcessors)
if res != 0: rawMessage(errExecutionOfProgramFailed, [])
if not (optNoLinking in gGlobalOptions):
if optNoLinking notin gGlobalOptions:
# call the linker:
var linkerExe = getConfigVar(cc[c].name & ".linkerexe")
if len(linkerExe) == 0: linkerExe = cc[c].linkerExe
@@ -448,22 +466,21 @@ proc CallCCompiler(projectfile: string) =
var it = PStrEntry(toLink.head)
var objfiles = ""
while it != nil:
add(objfiles, " ")
add(objfiles, ' ')
if targetOS == platform.hostOS:
add(objfiles, quoteIfContainsWhite(toObjfile(it.data)))
else:
add(objfiles, quoteIfContainsWhite(toObjfile(extractFileName(it.data))))
it = PStrEntry(it.next)
linkCmd = quoteIfContainsWhite(`%`(linkCmd, ["builddll", builddll,
linkCmd = quoteIfContainsWhite(linkCmd % ["builddll", builddll,
"buildgui", buildgui, "options", linkOptions, "objfiles", objfiles,
"exefile", exefile, "nimrod", getPrefixDir(), "lib", libpath]))
"exefile", exefile, "nimrod", getPrefixDir(), "lib", libpath])
add(linkCmd, ' ')
add(linkCmd, `%`(cc[c].linkTmpl, ["builddll", builddll, "buildgui",
buildgui, "options", linkOptions,
"objfiles", objfiles, "exefile", exefile,
"nimrod",
quoteIfContainsWhite(getPrefixDir()),
"lib", quoteIfContainsWhite(libpath)]))
addf(linkCmd, cc[c].linkTmpl, ["builddll", builddll,
"buildgui", buildgui, "options", linkOptions,
"objfiles", objfiles, "exefile", exefile,
"nimrod", quoteIfContainsWhite(getPrefixDir()),
"lib", quoteIfContainsWhite(libpath)])
if not (optCompileOnly in gGlobalOptions): execExternalProgram(linkCmd)
else:
linkCmd = ""

View File

@@ -299,7 +299,7 @@ proc processCompile(c: PContext, n: PNode) =
var found = findFile(s)
if found == "": found = s
var trunc = ChangeFileExt(found, "")
extccomp.addExternalFileToCompile(trunc)
extccomp.addExternalFileToCompile(found)
extccomp.addFileToLink(completeCFilePath(trunc, false))
proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =

View File

@@ -5,6 +5,12 @@ News
2010-XX-XX Version 0.8.10 released
==================================
Bugfixes
--------
- Bugfix: Command line parsing on Windows and ``os.parseCmdLine`` now adheres
to the same parsing rules as Microsoft's C/C++ startup code.
Changes affecting backwards compatibility
-----------------------------------------
@@ -13,6 +19,12 @@ Changes affecting backwards compatibility
unless they are used in the same module.
Additions
---------
- The ``{.compile: "file.c".}`` pragma uses a CRC check to see if the file
needs to be recompiled.
2010-03-14 Version 0.8.8 released
=================================