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
commit e90665bff2
7 changed files with 145 additions and 54 deletions

View file

@ -926,28 +926,90 @@ proc createDir*(dir: string) =
rawCreateDir(dir) rawCreateDir(dir)
proc parseCmdLine*(c: string): seq[string] = proc parseCmdLine*(c: string): seq[string] =
## Splits a command line into several components; components are separated by ## Splits a command line into several components;
## whitespace unless the whitespace occurs within ``"`` or ``'`` quotes.
## This proc is only occassionally useful, better use the `parseopt` module. ## 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 = @[] result = @[]
var i = 0 var i = 0
var a = "" var a = ""
while true: while true:
setLen(a, 0) setLen(a, 0)
while c[i] >= '\1' and c[i] <= ' ': inc(i) # skip whitespace while c[i] == ' ' or c[i] == '\t': inc(i)
case c[i] when defined(windows):
of '\'', '\"': # parse a single argument according to the above rules:
var delim = c[i] var inQuote = false
inc(i) # skip ' or " while true:
while c[i] != '\0' and c[i] != delim: case c[i]
add a, c[i] of '\0': break
inc(i) of '\\':
if c[i] != '\0': inc(i) var j = i
of '\0': break 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: else:
while c[i] > ' ': case c[i]
add(a, c[i]) of '\'', '\"':
inc(i) 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) add(result, a)
type type

View file

@ -233,9 +233,9 @@ type
## that is too small to be represented as ## that is too small to be represented as
## a normal number ## a normal number
EFloatInexact* {.compilerproc.} = EFloatInexact* {.compilerproc.} =
object of EFloatingPoint ## Inexact. Operation produces a result that cannot object of EFloatingPoint ## Inexact. Operation produces a result
## be represented with infinite precision -- ## that cannot be represented with infinite
## for example, 2.0 / 3.0, log(1.1) ## precision -- for example, 2.0 / 3.0, log(1.1)
## NOTE: Nimrod currently does not detect these! ## NOTE: Nimrod currently does not detect these!
TResult* = enum Failure, Success TResult* = enum Failure, Success
@ -962,7 +962,7 @@ proc `$` *(x: Cstring): string {.magic: "CStrToStr", noSideEffect.}
proc `$` *(x: string): string {.magic: "StrToStr", noSideEffect.} proc `$` *(x: string): string {.magic: "StrToStr", noSideEffect.}
## The stingify operator for a string argument. Returns `x` ## The stingify operator for a string argument. Returns `x`
## as it is. This operator is useful for generic code, so ## 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.} proc `$` *[T](x: ordinal[T]): string {.magic: "EnumToStr", noSideEffect.}
## The stingify operator for an enumeration argument. This works for ## 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]) result = UnixToNativePath(path % ["nimrod", getPrefixDir(), "lib", libpath])
proc processCompile(filename: string) = proc processCompile(filename: string) =
var found, trunc: string var found = findFile(filename)
found = findFile(filename)
if found == "": found = filename if found == "": found = filename
trunc = changeFileExt(found, "") var trunc = changeFileExt(found, "")
extccomp.addExternalFileToCompile(trunc) extccomp.addExternalFileToCompile(found)
extccomp.addFileToLink(completeCFilePath(trunc, false)) extccomp.addFileToLink(completeCFilePath(trunc, false))
proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) = proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =

View file

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

View file

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

View file

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

View file

@ -5,6 +5,12 @@ News
2010-XX-XX Version 0.8.10 released 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 Changes affecting backwards compatibility
----------------------------------------- -----------------------------------------
@ -13,6 +19,12 @@ Changes affecting backwards compatibility
unless they are used in the same module. 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 2010-03-14 Version 0.8.8 released
================================= =================================