summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xlib/pure/os.nim92
-rwxr-xr-xlib/system.nim8
-rwxr-xr-xrod/commands.nim7
-rwxr-xr-xrod/crc.nim13
-rwxr-xr-xrod/extccomp.nim65
-rwxr-xr-xrod/pragmas.nim2
-rwxr-xr-xweb/news.txt12
7 files changed, 145 insertions, 54 deletions
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index f807c39f5..129774f6e 100755
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -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
diff --git a/lib/system.nim b/lib/system.nim
index 66c5b4dfd..a83812ed0 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -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
diff --git a/rod/commands.nim b/rod/commands.nim
index 01b015b53..289f9c68e 100755
--- a/rod/commands.nim
+++ b/rod/commands.nim
@@ -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) = 
diff --git a/rod/crc.nim b/rod/crc.nim
index e66ce30fb..b397d5382 100755
--- a/rod/crc.nim
+++ b/rod/crc.nim
@@ -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 = 
diff --git a/rod/extccomp.nim b/rod/extccomp.nim
index 8d70bf16d..6ca816eb1 100755
--- a/rod/extccomp.nim
+++ b/rod/extccomp.nim
@@ -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 = ""
diff --git a/rod/pragmas.nim b/rod/pragmas.nim
index 02411ee52..28e4b2a7b 100755
--- a/rod/pragmas.nim
+++ b/rod/pragmas.nim
@@ -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) = 
diff --git a/web/news.txt b/web/news.txt
index 5096f9af4..94ba076b7 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -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
 =================================