summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xkoch.nim4
-rwxr-xr-xlib/impure/re.nim2
-rwxr-xr-xlib/pure/browsers.nim2
-rwxr-xr-xlib/pure/os.nim9
-rwxr-xr-xlib/pure/osproc.nim51
-rwxr-xr-xlib/pure/pegs.nim2
-rwxr-xr-xlib/system/mmdisp.nim73
-rw-r--r--tests/dll/client.nim41
-rw-r--r--tests/dll/dllsimple.nim5
-rw-r--r--tests/dll/server.nim27
-rwxr-xr-xtests/tester.nim69
-rwxr-xr-xtodo.txt3
-rwxr-xr-xweb/news.txt3
13 files changed, 210 insertions, 81 deletions
diff --git a/koch.nim b/koch.nim
index 8faee0cc7..b5d407a0f 100755
--- a/koch.nim
+++ b/koch.nim
@@ -177,7 +177,9 @@ proc clean(args: string) =
       RemoveDir(path)
 
 proc tests(args: string) =
-  exec("nimrod cc tests/tester")
+  # we compile the tester with taintMode:on to have a basic
+  # taint mode test :-)
+  exec("nimrod cc --taintMode:on tests/tester")
   exec("tests/tester reject")
   exec("tests/tester compile")
   exec("tests/tester examples")
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index 635fab0db..e8424a941 100755
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -344,7 +344,7 @@ proc transformFile*(infile, outfile: string,
   ## reads in the file `infile`, performs a parallel replacement (calls
   ## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an
   ## error occurs. This is supposed to be used for quick scripting.
-  var x = readFile(infile)
+  var x = readFile(infile).string
   writeFile(outfile, x.parallelReplace(subs))
   
 iterator split*(s: string, sep: TRegEx): string =
diff --git a/lib/pure/browsers.nim b/lib/pure/browsers.nim
index 243c07dad..529071107 100755
--- a/lib/pure/browsers.nim
+++ b/lib/pure/browsers.nim
@@ -34,7 +34,7 @@ proc openDefaultBrowser*(url: string) =
     var u = quoteIfContainsWhite(url)
     for a in items(attempts):
       if execShellCmd(a & u) == 0: return
-    for b in getEnv("BROWSER").split(PathSep):
+    for b in getEnv("BROWSER").string.split(PathSep):
       try:
         # we use ``startProcess`` here because we don't want to block!
         discard startProcess(command=b, args=[url], options={poUseShell})
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 122b38c57..2edf999d3 100755
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -74,6 +74,10 @@ when defined(Nimdoc): # only for proper documentation:
       ## The file extension of a script file. For example: "" for POSIX,
       ## "bat" on Windows.
 
+    DynlibFormat* = "lib$1.so"
+      ## The format string to turn a filename into a `DLL`:idx: file (also
+      ## called `shared object`:idx: on some operating systems).
+
 elif defined(macos):
   const
     curdir* = ':'
@@ -84,6 +88,7 @@ elif defined(macos):
     FileSystemCaseSensitive* = false
     ExeExt* = ""
     ScriptExt* = ""
+    DynlibFormat* = "$1.dylib"
 
   #  MacOS paths
   #  ===========
@@ -113,6 +118,7 @@ elif doslike:
     FileSystemCaseSensitive* = false
     ExeExt* = "exe"
     ScriptExt* = "bat"
+    DynlibFormat* = "$1.dll"
 elif defined(PalmOS) or defined(MorphOS):
   const
     dirsep* = '/'
@@ -122,6 +128,7 @@ elif defined(PalmOS) or defined(MorphOS):
     FileSystemCaseSensitive* = false
     ExeExt* = ""
     ScriptExt* = ""
+    DynlibFormat* = "$1.prc"
 elif defined(RISCOS):
   const
     dirsep* = '.'
@@ -131,6 +138,7 @@ elif defined(RISCOS):
     FileSystemCaseSensitive* = true
     ExeExt* = ""
     ScriptExt* = ""
+    DynlibFormat* = "lib$1.so"
 else: # UNIX-like operating system
   const
     curdir* = '.'
@@ -141,6 +149,7 @@ else: # UNIX-like operating system
     FileSystemCaseSensitive* = true
     ExeExt* = ""
     ScriptExt* = ""
+    DynlibFormat* = "lib$1.so"
 
 const
   ExtSep* = '.'
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 1b7d41908..dbf7b0e48 100755
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -48,7 +48,8 @@ proc execProcess*(command: string,
 
 proc execCmd*(command: string): int {.rtl, extern: "nosp$1".}
   ## Executes ``command`` and returns its error code. Standard input, output,
-  ## error streams are inherited from the calling process.
+  ## error streams are inherited from the calling process. This operation
+  ## is also often called `system`:idx:.
 
 proc startProcess*(command: string,
                    workingDir: string = "",
@@ -70,6 +71,17 @@ proc startProcess*(command: string,
   ## Return value: The newly created process object. Nil is never returned,
   ## but ``EOS`` is raised in case of an error.
 
+proc startCmd*(command: string, options: set[TProcessOption] = {
+               poStdErrToStdOut, poUseShell}): PProcess =
+  ## a simpler version of `startProcess` that parses the command line into
+  ## program and arguments and then calls `startProcess` with the empty string
+  ## for `workingDir` and the nil string table for `env`.
+  var c = parseCmdLine(command)
+  var a: seq[string]
+  newSeq(a, c.len-1) # avoid slicing for now (still unstable)
+  for i in 1 .. c.len-1: a[i-1] = c[i]
+  result = startProcess(command=c[0], args=a, options=options)
+
 proc close*(p: PProcess) {.rtl, extern: "nosp$1".}
   ## When the process has finished executing, cleanup related handles
 
@@ -140,12 +152,6 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
     result = sysconf(SC_NPROCESSORS_ONLN)
   if result <= 0: result = 1
 
-proc startProcessAux(cmd: string, options: set[TProcessOption]): PProcess =
-  var c = parseCmdLine(cmd)
-  var a: seq[string] = @[] # slicing is not yet implemented :-(
-  for i in 1 .. c.len-1: add(a, c[i])
-  result = startProcess(command=c[0], args=a, options=options)
-
 proc execProcesses*(cmds: openArray[string],
                     options = {poStdErrToStdOut, poParentStreams},
                     n = countProcessors()): int {.rtl, extern: "nosp$1".} =
@@ -158,7 +164,7 @@ proc execProcesses*(cmds: openArray[string],
     newSeq(q, n)
     var m = min(n, cmds.len)
     for i in 0..m-1:
-      q[i] = startProcessAux(cmds[i], options=options)
+      q[i] = startCmd(cmds[i], options=options)
     when defined(noBusyWaiting):
       var r = 0
       for i in m..high(cmds):
@@ -171,7 +177,7 @@ proc execProcesses*(cmds: openArray[string],
           echo(err)
         result = max(waitForExit(q[r]), result)
         if q[r] != nil: close(q[r])
-        q[r] = startProcessAux(cmds[i], options=options)
+        q[r] = startCmd(cmds[i], options=options)
         r = (r + 1) mod n
     else:
       var i = m
@@ -182,7 +188,7 @@ proc execProcesses*(cmds: openArray[string],
             #echo(outputStream(q[r]).readLine())
             result = max(waitForExit(q[r]), result)
             if q[r] != nil: close(q[r])
-            q[r] = startProcessAux(cmds[i], options=options)
+            q[r] = startCmd(cmds[i], options=options)
             inc(i)
             if i > high(cmds): break
     for i in 0..m-1:
@@ -190,7 +196,7 @@ proc execProcesses*(cmds: openArray[string],
       result = max(waitForExit(q[i]), result)
   else:
     for i in 0..high(cmds):
-      var p = startProcessAux(cmds[i], options=options)
+      var p = startCmd(cmds[i], options=options)
       result = max(waitForExit(p), result)
       close(p)
 
@@ -204,7 +210,7 @@ when not defined(useNimRtl):
   proc execProcess(command: string,
                    options: set[TProcessOption] = {poStdErrToStdOut,
                                                    poUseShell}): TaintedString =
-    var p = startProcessAux(command, options=options)
+    var p = startCmd(command, options=options)
     var outp = outputStream(p)
     result = TaintedString""
     while running(p) or not outp.atEnd(outp):
@@ -625,6 +631,27 @@ elif not defined(useNimRtl):
     
     pruneProcessSet(readfds, (rd))
 
+
+proc execCmdEx*(command: string, options: set[TProcessOption] = {
+                poStdErrToStdOut, poUseShell}): tuple[
+                output: TaintedString, 
+                exitCode: int] =
+  ## a convenience proc that runs the `command`, grabs all its output and
+  ## exit code and returns both.
+  var p = startCmd(command, options)
+  var outp = outputStream(p)
+  result = (TaintedString"", -1)
+  while not outp.atEnd(outp):
+    result[0].string.add(outp.readLine().string)
+    result[0].string.add("\n")
+    result[1] = peekExitCode(p)
+    if result[1] != -1: break
+  outp.close(outp)
+  if result[1] == -1:
+    result[1] = peekExitCode(p)
+  close(p)
+
+
 when isMainModule:
   var x = execProcess("gcc -v")
   echo "ECHO ", x
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index 25637cfee..4c4d3472a 100755
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -969,7 +969,7 @@ proc transformFile*(infile, outfile: string,
   ## reads in the file `infile`, performs a parallel replacement (calls
   ## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an
   ## error occurs. This is supposed to be used for quick scripting.
-  var x = readFile(infile)
+  var x = readFile(infile).string
   writeFile(outfile, x.parallelReplace(subs))
   
 iterator split*(s: string, sep: TPeg): string =
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index 5d1371cf9..7e0490747 100755
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -78,45 +78,49 @@ when defined(boehmgc):
   proc boehmRealloc(p: pointer, size: int): pointer {.
     importc: "GC_realloc", dynlib: boehmLib.}
   proc boehmDealloc(p: pointer) {.importc: "GC_free", dynlib: boehmLib.}
+  
+  when not defined(useNimRtl):
     
-  proc alloc(size: int): pointer =
-    result = boehmAlloc(size)
-    if result == nil: raiseOutOfMem()
-  proc alloc0(size: int): pointer =
-    result = alloc(size)
-    zeroMem(result, size)
-  proc realloc(p: Pointer, newsize: int): pointer =
-    result = boehmRealloc(p, newsize)
-    if result == nil: raiseOutOfMem()
-  proc dealloc(p: Pointer) = boehmDealloc(p)
+    proc alloc(size: int): pointer =
+      result = boehmAlloc(size)
+      if result == nil: raiseOutOfMem()
+    proc alloc0(size: int): pointer =
+      result = alloc(size)
+      zeroMem(result, size)
+    proc realloc(p: Pointer, newsize: int): pointer =
+      result = boehmRealloc(p, newsize)
+      if result == nil: raiseOutOfMem()
+    proc dealloc(p: Pointer) = boehmDealloc(p)
+
+    proc allocShared(size: int): pointer =
+      result = boehmAlloc(size)
+      if result == nil: raiseOutOfMem()
+    proc allocShared0(size: int): pointer =
+      result = alloc(size)
+      zeroMem(result, size)
+    proc reallocShared(p: Pointer, newsize: int): pointer =
+      result = boehmRealloc(p, newsize)
+      if result == nil: raiseOutOfMem()
+    proc deallocShared(p: Pointer) = boehmDealloc(p)
+
+    #boehmGCincremental()
+
+    proc GC_disable() = boehmGC_disable()
+    proc GC_enable() = boehmGC_enable()
+    proc GC_fullCollect() = boehmGCfullCollect()
+    proc GC_setStrategy(strategy: TGC_Strategy) = nil
+    proc GC_enableMarkAndSweep() = nil
+    proc GC_disableMarkAndSweep() = nil
+    proc GC_getStatistics(): string = return ""
+    
+    proc getOccupiedMem(): int = return -1
+    proc getFreeMem(): int = return -1
+    proc getTotalMem(): int = return -1
 
-  proc allocShared(size: int): pointer =
-    result = boehmAlloc(size)
-    if result == nil: raiseOutOfMem()
-  proc allocShared0(size: int): pointer =
-    result = alloc(size)
-    zeroMem(result, size)
-  proc reallocShared(p: Pointer, newsize: int): pointer =
-    result = boehmRealloc(p, newsize)
-    if result == nil: raiseOutOfMem()
-  proc deallocShared(p: Pointer) = boehmDealloc(p)
+    proc setStackBottom(theStackBottom: pointer) = nil
 
   proc initGC() = 
     when defined(macosx): boehmGCinit()
-  
-  #boehmGCincremental()
-
-  proc GC_disable() = boehmGC_disable()
-  proc GC_enable() = boehmGC_enable()
-  proc GC_fullCollect() = boehmGCfullCollect()
-  proc GC_setStrategy(strategy: TGC_Strategy) = nil
-  proc GC_enableMarkAndSweep() = nil
-  proc GC_disableMarkAndSweep() = nil
-  proc GC_getStatistics(): string = return ""
-  
-  proc getOccupiedMem(): int = return -1
-  proc getFreeMem(): int = return -1
-  proc getTotalMem(): int = return -1
 
   proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
     result = alloc(size)
@@ -128,7 +132,6 @@ when defined(boehmgc):
   proc growObj(old: pointer, newsize: int): pointer =
     result = realloc(old, newsize)
 
-  proc setStackBottom(theStackBottom: pointer) = nil
   proc nimGCref(p: pointer) {.compilerproc, inline.} = nil
   proc nimGCunref(p: pointer) {.compilerproc, inline.} = nil
   
diff --git a/tests/dll/client.nim b/tests/dll/client.nim
new file mode 100644
index 000000000..a78cef1d4
--- /dev/null
+++ b/tests/dll/client.nim
@@ -0,0 +1,41 @@
+discard """
+  output: "Done"
+  cmd: "nimrod cc --debuginfo --hints:on --define:useNimRtl $# $#"
+"""
+
+type
+  TNodeKind = enum nkLit, nkSub, nkAdd, nkDiv, nkMul
+  TNode = object
+    case k: TNodeKind
+    of nkLit: x: int
+    else: a, b: ref TNode
+
+  PNode = ref TNode
+
+
+when defined(windows):
+  const dllname = "server.dll"
+elif defined(macosx):
+  const dllname = "libserver.dylib"
+else:
+  const dllname = "libserver.so"
+
+proc newLit(x: int): PNode {.importc: "newLit", dynlib: dllname.}
+proc newOp(k: TNodeKind, a, b: PNode): PNode {.
+  importc: "newOp", dynlib: dllname.}  
+proc buildTree(x: int): PNode {.importc: "buildTree", dynlib: dllname.} 
+
+proc eval(n: PNode): int =
+  case n.k
+  of nkLit: result = n.x
+  of nkSub: result = eval(n.a) - eval(n.b)
+  of nkAdd: result = eval(n.a) + eval(n.b)
+  of nkDiv: result = eval(n.a) div eval(n.b)
+  of nkMul: result = eval(n.a) * eval(n.b)
+
+# Test the GC:
+for i in 0..100_000:
+  discard eval(buildTree(2))
+  
+echo "Done"
+
diff --git a/tests/dll/dllsimple.nim b/tests/dll/dllsimple.nim
deleted file mode 100644
index 3f359cd52..000000000
--- a/tests/dll/dllsimple.nim
+++ /dev/null
@@ -1,5 +0,0 @@
-discard """
-  file: tdllgen.nim
-"""
-proc test() {.exportc.} =
-  echo("Hello World!")
diff --git a/tests/dll/server.nim b/tests/dll/server.nim
new file mode 100644
index 000000000..a826ee2a7
--- /dev/null
+++ b/tests/dll/server.nim
@@ -0,0 +1,27 @@
+discard """
+  cmd: "nimrod cc --debuginfo --hints:on --define:useNimRtl --app:lib $# $#"
+"""
+
+type
+  TNodeKind = enum nkLit, nkSub, nkAdd, nkDiv, nkMul
+  TNode = object
+    case k: TNodeKind
+    of nkLit: x: int
+    else: a, b: ref TNode
+
+  PNode = ref TNode
+    
+proc newLit(x: int): PNode {.exportc: "newLit", dynlib.} =
+  new(result)
+  result.x = x
+  
+proc newOp(k: TNodeKind, a, b: PNode): PNode {.exportc: "newOp", dynlib.} =
+  assert a != nil
+  assert b != nil
+  new(result)
+  result.k = k
+  result.a = a
+  result.b = b
+  
+proc buildTree(x: int): PNode {.exportc: "buildTree", dynlib.} = 
+  result = newOp(nkMul, newOp(nkAdd, newLit(x), newLit(x)), newLit(x))
diff --git a/tests/tester.nim b/tests/tester.nim
index 42516fe56..9398a54de 100755
--- a/tests/tester.nim
+++ b/tests/tester.nim
@@ -50,8 +50,7 @@ when not defined(parseCfgBool):
 
 proc extractSpec(filename: string): string =
   const tripleQuote = "\"\"\""
-  var x = readFile(filename)
-  if isNil(x): quit "cannot open file: " & filename
+  var x = readFile(filename).string
   var a = x.find(tripleQuote)
   var b = x.find(tripleQuote, a+3)
   if a >= 0 and b > a:
@@ -101,9 +100,6 @@ proc parseSpec(filename: string): TSpec =
 
 # ----------------------------------------------------------------------------
 
-proc myExec(cmd: string): string =
-  result = osproc.execProcess(cmd)
-
 var
   pegLineError = peg"{[^(]*} '(' {\d+} ', ' \d+ ') Error:' \s* {.*}"
   pegOtherError = peg"'Error:' \s* {.*}"
@@ -119,7 +115,7 @@ proc callCompiler(cmdTemplate, filename, options: string): TSpec =
   var outp = p.outputStream
   var s = ""
   while running(p) or not outp.atEnd(outp):
-    var x = outp.readLine()
+    var x = outp.readLine().string
     if x =~ pegOfInterest:
       # `s` should contain the last error message
       s = x
@@ -145,7 +141,7 @@ proc initResults: TResults =
   result.data = ""
 
 proc readResults(filename: string): TResults =
-  result = marshal.to[TResults](readFile(filename))
+  result = marshal.to[TResults](readFile(filename).string)
 
 proc writeResults(filename: string, r: TResults) =
   writeFile(filename, $$r)
@@ -255,12 +251,16 @@ proc runSingleTest(r: var TResults, test, options: string) =
     else:
       var exeFile = changeFileExt(test, ExeExt)
       if existsFile(exeFile):
-        var buf = myExec(exeFile)
-        var success = strip(buf) == strip(expected.outp)
-        if expected.substr: success = expected.outp in buf
-        if success: inc(r.passed)
-        r.addResult(t, expected.outp,
-            buf, if success: reSuccess else: reFailure)
+        var (buf, exitCode) = execCmdEx(exeFile)
+        if exitCode != 0:
+          r.addResult(t, expected.outp, "exitCode: " & $exitCode, reFailure)
+        else:
+          var success = strip(buf.string) == strip(expected.outp)
+          if expected.substr and not success: 
+            success = expected.outp in buf.string
+          if success: inc(r.passed)
+          r.addResult(t, expected.outp,
+              buf.string, if success: reSuccess else: reFailure)
       else:
         r.addResult(t, expected.outp, "executable not found", reFailure)
 
@@ -320,16 +320,35 @@ proc compileRodFiles(r: var TResults, options: string) =
   test "gtkex2"
   delNimCache()
 
-# -----------------------------------------------------------------------------
+# --------------------- DLL generation tests ----------------------------------
 
-# DLL generation tests
-proc testDLLGen(r: var TResults, options: string) =
-  compileSingleTest(r, "lib/nimrtl.nim", "--app:lib -d:createNimRtl")
+proc runBasicDLLTest(r: var TResults, options: string) =
+  compileSingleTest r, "lib/nimrtl.nim", options & " --app:lib -d:createNimRtl"
+  compileSingleTest r, "tests/dll/server.nim", 
+    options & " --app:lib -d:useNimRtl"
+  
+  when defined(Windows): 
+    # windows looks in the dir of the exe (yay!):
+    var nimrtlDll = DynlibFormat % "nimrtl"
+    copyFile("lib" / nimrtlDll, "tests/dll" / nimrtlDll)
+  else:
+    # posix relies on crappy LD_LIBRARY_PATH (ugh!):
+    var libpath = getenv"LD_LIBRARY_PATH".string
+    if peg"\i '/nimrod' (!'/')* '/lib'" notin libpath:
+      echo "[Warning] insufficient LD_LIBRARY_PATH"
+    var serverDll = DynlibFormat % "server"
+    copyFile("tests/dll" / serverDll, "lib" / serverDll)
+  
+  runSingleTest r, "tests/dll/client.nim", options & " -d:useNimRtl"
+
+proc runDLLTests(r: var TResults, options: string) =
+  runBasicDLLTest(r, options)
+  runBasicDLLTest(r, options & " -d:release")
+  runBasicDLLTest(r, options & " --gc:boehm")
+  runBasicDLLTest(r, options & " -d:release --gc:boehm")
   
-  template test(filename: expr): stmt =
-    compileSingleTest(r, "tests/dll/" / filename, options)
   
-  test "dllsimple.nim"
+# -----------------------------------------------------------------------------
    
 proc compileExample(r: var TResults, pattern, options: string) =
   for test in os.walkFiles(pattern): compileSingleTest(r, test, options)
@@ -356,7 +375,7 @@ proc main(action: string) =
   var options = ""
   for i in 2.. paramCount():
     add(options, " ")
-    add(options, paramStr(i))
+    add(options, paramStr(i).string)
 
   case action
   of "reject":
@@ -368,7 +387,6 @@ proc main(action: string) =
     compile(compileRes, "tests/accept/compile/t*.nim", options)
     compile(compileRes, "tests/ecmas.nim", options)
     compileRodFiles(compileRes, options)
-    testDllGen(compileRes, options)
     writeResults(compileJson, compileRes)
   of "examples":
     var compileRes = readResults(compileJson)
@@ -380,6 +398,7 @@ proc main(action: string) =
     var runRes = initResults()
     run(runRes, "tests/accept/run", options)
     runRodFiles(runRes, options)
+    runDLLTests(runRes, options)
     writeResults(runJson, runRes)
   of "merge":
     var rejectRes = readResults(rejectJson)
@@ -387,10 +406,14 @@ proc main(action: string) =
     var runRes = readResults(runJson)
     listResults(rejectRes, compileRes, runRes)
     outputJSON(rejectRes, compileRes, runRes)
+  of "dll":
+    var runRes = initResults()
+    runDLLTests runRes, ""
+    writeResults(runJson, runRes)
   else:
     quit usage
 
 if paramCount() == 0:
   quit usage
-main(paramStr(1))
+main(paramStr(1).string)
 
diff --git a/todo.txt b/todo.txt
index c62301394..e0fa4d015 100755
--- a/todo.txt
+++ b/todo.txt
@@ -2,7 +2,6 @@ Version 0.8.14
 ==============
 
 - bug: s[1..n] = @[] produces wrong C code
-- test suite should contain DLL tests
 - optimize unused constants away (affected by HLO)
 - fix actors.nim; test with different thread var implementations
 - dead code elim for JS backend
@@ -25,6 +24,8 @@ version 0.9.0
 - tests: run the GC tests
 - change overloading resolution
 - implement closures; implement proper coroutines
+- implicit invokation of `items` seems nice
+- we need to support iteration of 2 different data structures in parallel
 - make exceptions compatible with C++ exceptions
 - ``=`` should be overloadable; requires specialization for ``=``
 - 'const' objects including case objects
diff --git a/web/news.txt b/web/news.txt
index 51d30fcbd..555b3ef69 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -101,7 +101,7 @@ Library Additions
 - Added ``system.slurp`` for easy resource embedding.
 - Added ``system.running`` for threads.
 - Added ``xmltree.innerText``.
-- Added ``os.isAbsolute``.
+- Added ``os.isAbsolute``, ``os.dynLibFormat``.
 - Added ``parseutils.interpolatedFragments``.
 - Added ``macros.treeRepr``, ``macros.lispRepr``, ``macros.dumpTree``, 
   ``macros.dumpLisp``, ``macros.parseExpr``, ``macros.parseStmt``, ``macros.getAst``.
@@ -109,6 +109,7 @@ Library Additions
 - Added ``irc`` module.
 - Added ``ftpclient`` module.
 - Added ``memfiles`` module.
+- Added ``osproc.startCmd``, ``osproc.execCmdEx``.
 
 
 2011-07-10 Version 0.8.12 released