diff options
-rw-r--r-- | .gitlab-ci.yml | 39 | ||||
-rw-r--r-- | .travis.yml | 2 | ||||
-rw-r--r-- | ci/deps.bat | 2 | ||||
-rw-r--r-- | ci/deps.sh | 2 | ||||
-rw-r--r-- | ci/nsis_build.bat | 34 | ||||
-rw-r--r-- | compiler.nimble | 4 | ||||
-rw-r--r-- | compiler/ast.nim | 5 | ||||
-rw-r--r-- | compiler/ccgtypes.nim | 14 | ||||
-rw-r--r-- | compiler/ccgutils.nim | 3 | ||||
-rw-r--r-- | compiler/cgen.nim | 11 | ||||
-rw-r--r-- | compiler/docgen.nim | 2 | ||||
-rw-r--r-- | compiler/installer.ini | 22 | ||||
-rw-r--r-- | compiler/jsgen.nim | 3 | ||||
-rw-r--r-- | compiler/msgs.nim | 4 | ||||
-rw-r--r-- | compiler/nim.nim | 5 | ||||
-rw-r--r-- | compiler/nimfix/nimfix.nim | 2 | ||||
-rw-r--r-- | compiler/parampatterns.nim | 2 | ||||
-rw-r--r-- | compiler/semasgn.nim | 3 | ||||
-rw-r--r-- | compiler/semcall.nim | 2 | ||||
-rw-r--r-- | compiler/semexprs.nim | 40 | ||||
-rw-r--r-- | compiler/seminst.nim | 5 | ||||
-rw-r--r-- | compiler/semmagic.nim | 2 | ||||
-rw-r--r-- | compiler/sempass2.nim | 12 | ||||
-rw-r--r-- | compiler/semstmts.nim | 5 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 32 | ||||
-rw-r--r-- | compiler/types.nim | 12 | ||||
-rw-r--r-- | compiler/typesrenderer.nim | 18 | ||||
-rw-r--r-- | compiler/vm.nim | 10 | ||||
-rw-r--r-- | compiler/vmdeps.nim | 11 | ||||
-rw-r--r-- | config/nim.cfg | 10 | ||||
-rw-r--r-- | config/nimdoc.cfg | 2 | ||||
-rw-r--r-- | contributors.txt | 12 | ||||
-rw-r--r-- | doc/advopt.txt | 1 | ||||
-rw-r--r-- | doc/astspec.txt | 1 | ||||
-rw-r--r-- | doc/contributing.rst (renamed from contributing.rst) | 0 | ||||
-rw-r--r-- | doc/docs.rst (renamed from doc/docs.txt) | 0 | ||||
-rw-r--r-- | doc/docstyle.rst (renamed from docstyle.rst) | 0 | ||||
-rw-r--r-- | doc/gc.rst | 30 | ||||
-rw-r--r-- | doc/lib.rst | 2 | ||||
-rw-r--r-- | doc/manual/exceptions.txt | 2 | ||||
-rw-r--r-- | doc/overview.rst (renamed from doc/overview.txt) | 2 | ||||
-rw-r--r-- | icons/koch-amd64-windows-vcc.res | bin | 0 -> 5572 bytes | |||
-rw-r--r-- | icons/koch-i386-windows-vcc.res | bin | 0 -> 5572 bytes | |||
-rw-r--r-- | icons/nim-amd64-windows-vcc.res | bin | 0 -> 30548 bytes | |||
-rw-r--r-- | icons/nim-i386-windows-vcc.res | bin | 0 -> 30548 bytes | |||
-rw-r--r-- | install.txt | 19 | ||||
-rw-r--r-- | install_nimble.nims | 4 | ||||
-rw-r--r-- | install_tools.nims | 20 | ||||
-rw-r--r-- | koch.nim | 216 | ||||
-rw-r--r-- | lib/arch/arch.nim | 5 | ||||
-rw-r--r-- | lib/core/macros.nim | 2 | ||||
-rw-r--r-- | lib/impure/re.nim | 2 | ||||
-rw-r--r-- | lib/js/jsconsole.nim | 44 | ||||
-rw-r--r-- | lib/packages/docutils/highlite.nim | 15 | ||||
-rw-r--r-- | lib/posix/kqueue.nim | 2 | ||||
-rw-r--r-- | lib/pure/asyncdispatch.nim | 292 | ||||
-rw-r--r-- | lib/pure/asyncftpclient.nim | 19 | ||||
-rw-r--r-- | lib/pure/asynchttpserver.nim | 30 | ||||
-rw-r--r-- | lib/pure/asyncmacro.nim | 51 | ||||
-rw-r--r-- | lib/pure/asyncnet.nim | 51 | ||||
-rw-r--r-- | lib/pure/basic2d.nim | 4 | ||||
-rw-r--r-- | lib/pure/basic3d.nim | 6 | ||||
-rw-r--r-- | lib/pure/collections/sequtils.nim | 4 | ||||
-rw-r--r-- | lib/pure/collections/tables.nim | 47 | ||||
-rw-r--r-- | lib/pure/httpclient.nim | 253 | ||||
-rw-r--r-- | lib/pure/httpcore.nim | 8 | ||||
-rw-r--r-- | lib/pure/includes/asyncfutures.nim | 295 | ||||
-rw-r--r-- | lib/pure/ioselectors.nim | 38 | ||||
-rw-r--r-- | lib/pure/ioselects/ioselectors_kqueue.nim | 51 | ||||
-rw-r--r-- | lib/pure/json.nim | 2 | ||||
-rw-r--r-- | lib/pure/net.nim | 22 | ||||
-rw-r--r-- | lib/pure/os.nim | 161 | ||||
-rw-r--r-- | lib/pure/ospaths.nim | 4 | ||||
-rw-r--r-- | lib/pure/osproc.nim | 72 | ||||
-rw-r--r-- | lib/pure/parsexml.nim | 32 | ||||
-rw-r--r-- | lib/pure/random.nim | 4 | ||||
-rw-r--r-- | lib/pure/rationals.nim | 2 | ||||
-rw-r--r-- | lib/pure/selectors.nim | 3 | ||||
-rw-r--r-- | lib/pure/stats.nim | 26 | ||||
-rw-r--r-- | lib/pure/times.nim | 34 | ||||
-rw-r--r-- | lib/pure/unittest.nim | 24 | ||||
-rw-r--r-- | lib/pure/xmlparser.nim | 5 | ||||
-rw-r--r-- | lib/stdlib.nimble | 2 | ||||
-rw-r--r-- | lib/system.nim | 14 | ||||
-rw-r--r-- | lib/system/alloc.nim | 4 | ||||
-rw-r--r-- | lib/system/avltree.nim | 8 | ||||
-rw-r--r-- | lib/system/channels.nim | 54 | ||||
-rw-r--r-- | lib/system/deepcopy.nim | 19 | ||||
-rw-r--r-- | lib/system/dyncalls.nim | 5 | ||||
-rw-r--r-- | lib/system/gc2.nim | 12 | ||||
-rw-r--r-- | lib/system/gc_common.nim | 1 | ||||
-rw-r--r-- | lib/system/gc_stack.nim | 7 | ||||
-rw-r--r-- | lib/system/osalloc.nim | 2 | ||||
-rw-r--r-- | lib/system/sysio.nim | 27 | ||||
-rw-r--r-- | lib/system/sysstr.nim | 5 | ||||
-rw-r--r-- | lib/system/threads.nim | 2 | ||||
-rw-r--r-- | lib/upcoming/asyncdispatch.nim | 368 | ||||
-rw-r--r-- | lib/windows/registry.nim | 77 | ||||
-rw-r--r-- | lib/wrappers/linenoise/clinenoise.c | 4 | ||||
-rw-r--r-- | lib/wrappers/linenoise/linenoise.nim | 3 | ||||
-rw-r--r-- | lib/wrappers/mysql.nim | 4 | ||||
-rw-r--r-- | lib/wrappers/openssl.nim | 2 | ||||
-rw-r--r-- | readme.md | 12 | ||||
-rw-r--r-- | tests/async/tasynceverror.nim | 66 | ||||
-rw-r--r-- | tests/async/tfuturevar.nim | 47 | ||||
-rw-r--r-- | tests/async/tgenericasync.nim | 14 | ||||
-rw-r--r-- | tests/async/tioselectors.nim | 213 | ||||
-rw-r--r-- | tests/async/tupcoming_async.nim | 13 | ||||
-rw-r--r-- | tests/collections/ttables.nim | 33 | ||||
-rw-r--r-- | tests/float/tfloat4.nim | 6 | ||||
-rw-r--r-- | tests/generics/tstatictalias.nim | 20 | ||||
-rw-r--r-- | tests/js/tconsole.nim | 13 | ||||
-rw-r--r-- | tests/osproc/tafalse.nim | 3 | ||||
-rw-r--r-- | tests/osproc/texitcode.nim | 18 | ||||
-rw-r--r-- | tests/osproc/tworkingdir.nim | 16 | ||||
-rw-r--r-- | tests/parallel/tsendtwice.nim | 71 | ||||
-rw-r--r-- | tests/stdlib/thttpclient.nim | 79 | ||||
-rw-r--r-- | tests/stdlib/ttime.nim | 25 | ||||
-rw-r--r-- | tests/template/tconfusinglocal.nim | 13 | ||||
-rw-r--r-- | tests/testament/categories.nim | 7 | ||||
-rw-r--r-- | tests/vm/tgorge.bat | 1 | ||||
-rw-r--r-- | tests/vm/tgorge.nim | 12 | ||||
-rwxr-xr-x | tests/vm/tgorge.sh | 2 | ||||
-rw-r--r-- | tools/finish.nim | 165 | ||||
-rw-r--r-- | tools/nimgrep.nim | 4 | ||||
-rw-r--r-- | tools/niminst/buildbat.tmpl | 6 | ||||
-rw-r--r-- | tools/niminst/buildsh.tmpl | 6 | ||||
-rw-r--r-- | tools/niminst/debcreation.nim | 4 | ||||
-rw-r--r-- | tools/niminst/deinstall.tmpl | 2 | ||||
-rw-r--r-- | tools/niminst/install.tmpl | 2 | ||||
-rw-r--r-- | tools/niminst/makefile.tmpl | 4 | ||||
-rw-r--r-- | tools/niminst/niminst.nim | 95 | ||||
-rw-r--r-- | tools/niminst/nsis.tmpl | 219 | ||||
-rw-r--r-- | tools/nimweb.nim | 36 | ||||
-rw-r--r-- | tools/noprefix.nim | 32 | ||||
-rw-r--r-- | tools/start.bat (renamed from start.bat) | 6 | ||||
-rw-r--r-- | web/assets/news/images/0.15.0/doc_search.gif | bin | 0 -> 4916578 bytes | |||
-rw-r--r-- | web/assets/news/images/0.15.0/doc_sort.gif | bin | 0 -> 9215210 bytes | |||
-rw-r--r-- | web/download.rst | 51 | ||||
-rw-r--r-- | web/inactive_sponsors.csv | 12 | ||||
-rw-r--r-- | web/news.rst | 58 | ||||
-rw-r--r-- | web/news/e001_version_0_8_6.rst (renamed from web/news/2009_12_21_version_0_8_6_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e002_version_0_8_8.rst (renamed from web/news/2010_03_14_version_0_8_8_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e003_version_0_8_10.rst (renamed from web/news/2010_10_20_version_0_8_10_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e004_version_0_8_12.rst (renamed from web/news/2011_07_10_version_0_8_12_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e005_version_0_8_14.rst (renamed from web/news/2012_02_09_version_0_8_14_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e006_version_0_9_0.rst (renamed from web/news/2012_09_23_version_0_9_0_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e007_version_0_9_2.rst (renamed from web/news/2013_05_20_version_0_9_2_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e008_new_website.rst (renamed from web/news/2013_05_20_new_website_design.rst) | 0 | ||||
-rw-r--r-- | web/news/e009_andreas_rumpfs_talk.rst (renamed from web/news/2014_01_15_andreas_rumpfs_talk_on_nimrod.rst) | 0 | ||||
-rw-r--r-- | web/news/e010_dr_dobbs_journal.rst (renamed from web/news/2014_02_11_nimrod_featured_in_dr_dobbs_journal.rst) | 0 | ||||
-rw-r--r-- | web/news/e011_version_0_9_4.rst (renamed from web/news/2014_04_21_version_0_9_4_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e012_version_0_9_6.rst (renamed from web/news/2014_10_19_version_0_9_6_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e013_version_0_10_2.rst (renamed from web/news/2014_12_29_version_0_10_2_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e014_version_0_11_0.rst (renamed from web/news/2015_04_30_version_0_11_0_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e015_version_0_11_2.rst (renamed from web/news/2015_05_04_version_0_11_2_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e016_nim_conf1.rst (renamed from web/news/2015_10_16_first_nim_conference.rst) | 0 | ||||
-rw-r--r-- | web/news/e017_version_0_12_0.rst (renamed from web/news/2015_10_27_version_0_12_0_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e018_oscon_amsterdam.rst (renamed from web/news/2016_01_18_andreas_rumpfs_talk_at_oscon_amsterdam.rst) | 0 | ||||
-rw-r--r-- | web/news/e019_version_0_13_0.rst (renamed from web/news/2016_01_18_version_0_13_0_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e020_nim_in_action.rst (renamed from web/news/2016_01_27_nim_in_action_is_now_available.rst) | 0 | ||||
-rw-r--r-- | web/news/e021_meet_sponsors.rst (renamed from web/news/2016_06_04_meet_our_bountysource_sponsors.rst) | 0 | ||||
-rw-r--r-- | web/news/e022_version_0_14_0.rst (renamed from web/news/2016_06_07_version_0_14_0_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e023_version_0_14_2.rst (renamed from web/news/2016_06_11_version_0_14_2_released.rst) | 0 | ||||
-rw-r--r-- | web/news/e024_survey.rst (renamed from web/news/2016_06_23_launching_the_2016_nim_community_survey.rst) | 0 | ||||
-rw-r--r-- | web/news/e025_bountysource_update.rst (renamed from web/news/2016_08_06_bountysource_update_the_road_to_v10.rst) | 0 | ||||
-rw-r--r-- | web/news/e026_survey_results.rst (renamed from web/news/2016_09_03_nim_community_survey_results.rst) | 0 | ||||
-rw-r--r-- | web/news/e027_version_0_15_0.rst (renamed from web/news/version_0_15_released.rst) | 242 | ||||
-rw-r--r-- | web/news/e028_version_0_15_2.rst | 77 | ||||
-rw-r--r-- | web/news/e029_version_0_16_0.rst | 53 | ||||
-rw-r--r-- | web/sponsors.csv | 66 | ||||
-rw-r--r-- | web/ticker.html | 17 | ||||
-rw-r--r-- | web/website.ini | 6 |
173 files changed, 3122 insertions, 1584 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0cf30eb89..76c94c8e7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,19 +18,6 @@ stages: tags: - windows -build-linux: - stage: build - script: - - sh ci/build.sh - artifacts: - paths: - - bin/nim - - bin/nimd - - compiler/nim - - koch - expire_in: 1 week - tags: - - linux build-windows: stage: build @@ -53,20 +40,13 @@ deploy-windows: artifacts: paths: - build/*.exe + - build/*.zip expire_in: 1 week tags: - windows - fast -test-linux: - stage: test - <<: *linux_set_path_def - script: - - sh ci/deps.sh - - nim c --taintMode:on tests/testament/tester - - tests/testament/tester --pedantic all - tags: - - linux + test-windows: stage: test @@ -79,18 +59,3 @@ test-windows: - windows - fast -.csources: &csources_definition - stage: test - script: - - apt-get update -qq - - apt-get install -y -qq build-essential git - - nim -v - - ./koch csources - - ./koch xz - artifacts: - paths: - - build/c_code - -csources-linux: - <<: *csources_definition - <<: *linux_set_path_def diff --git a/.travis.yml b/.travis.yml index 0ebeeb995..ebf287502 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ script: - nimble install zip - nimble install opengl - nimble install sdl1 - - nimble install jester + - nimble install jester@#head - nimble install niminst - nim c --taintMode:on tests/testament/tester - tests/testament/tester --pedantic all diff --git a/ci/deps.bat b/ci/deps.bat index 477c70d1d..bda1fe14f 100644 --- a/ci/deps.bat +++ b/ci/deps.bat @@ -1,4 +1,4 @@ nim e install_nimble.nims nim e tests/test_nimscript.nims nimble update -nimble install -y zip opengl sdl1 jester niminst +nimble install -y zip opengl sdl1 jester@#head niminst diff --git a/ci/deps.sh b/ci/deps.sh index 50680e604..3385a213b 100644 --- a/ci/deps.sh +++ b/ci/deps.sh @@ -14,4 +14,4 @@ export PATH=$(pwd)/bin:$PATH nim e install_nimble.nims nim e tests/test_nimscript.nims nimble update -nimble install zip opengl sdl1 jester niminst +nimble install zip opengl sdl1 jester@#head niminst diff --git a/ci/nsis_build.bat b/ci/nsis_build.bat index 338a975d7..4e9d0d67c 100644 --- a/ci/nsis_build.bat +++ b/ci/nsis_build.bat @@ -21,15 +21,11 @@ cd web\upload move /y docs-%NIMVER%.zip download cd ..\.. -Rem Build .zip file: -rem koch csources -d:release -rem koch xz -d:release -rem move /y build\nim-%NIMVER%.zip web\upload\download - +Rem Build csources +koch csources -d:release || exit /b rem Grab C sources and nimsuggest git clone --depth 1 https://github.com/nim-lang/csources.git -git clone --depth 1 https://github.com/nim-lang/nimsuggest.git set PATH=%CD%\bin;%PATH% @@ -39,15 +35,15 @@ set PATH=C:\Users\araq\projects\mingw32\bin;%PATH% cd csources call build.bat cd .. -nim c koch || exit /b -koch boot -d:release || exit /b -cd nimsuggest -nim c -d:release --noNimblePath --path:.. nimsuggest || exit /b -copy /y nimsuggest.exe ..\bin || exit /b -cd .. -koch nsis -d:release || exit /b +ReM Rebuilding koch is necessary because it uses its pointer size to determine +ReM which mingw link to put in the NSIS installer. +nim c --out:koch_temp koch || exit /b +koch_temp boot -d:release || exit /b +koch_temp nsis -d:release || exit /b +koch_temp zip -d:release || exit /b dir build move /y build\nim_%NIMVER%.exe build\nim-%NIMVER%_x32.exe || exit /b +move /y build\nim-%NIMVER%.zip build\nim-%NIMVER%_x32.zip || exit /b ReM Build Win64 version: @@ -55,11 +51,9 @@ set PATH=C:\Users\araq\projects\mingw64\bin;%PATH% cd csources call build64.bat cd .. -nim c koch || exit /b -koch boot -d:release || exit /b -cd nimsuggest -nim c -d:release --noNimblePath --path:.. nimsuggest || exit /b -copy /y nimsuggest.exe ..\bin || exit /b -cd .. -koch nsis -d:release || exit /b +nim c --out:koch_temp koch || exit /b +koch_temp boot -d:release || exit /b +koch_temp nsis -d:release || exit /b +koch_temp zip -d:release || exit /b move /y build\nim_%NIMVER%.exe build\nim-%NIMVER%_x64.exe || exit /b +move /y build\nim-%NIMVER%.zip build\nim-%NIMVER%_x64.zip || exit /b diff --git a/compiler.nimble b/compiler.nimble index c63fe49bf..cb3eb34f6 100644 --- a/compiler.nimble +++ b/compiler.nimble @@ -1,6 +1,6 @@ [Package] name = "compiler" -version = "0.14.3" +version = "0.15.2" author = "Andreas Rumpf" description = "Compiler package providing the compiler sources as a library." license = "MIT" @@ -8,4 +8,4 @@ license = "MIT" InstallDirs = "doc, compiler" [Deps] -Requires: "nim >= 0.13.0" +Requires: "nim >= 0.14.0" diff --git a/compiler/ast.nim b/compiler/ast.nim index 131c2a38f..2426dcc02 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -347,7 +347,7 @@ type tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64, tyBigNum, tyConst, tyMutable, tyVarargs, - tyIter, # unused + tyUnused # kept for enum ordinal compatibility tyProxy # used as errornous type (for idetools) tyBuiltInTypeClass #\ @@ -1408,6 +1408,7 @@ proc copyNode*(src: PNode): PNode = result.info = src.info result.typ = src.typ result.flags = src.flags * PersistentNodeFlags + result.comment = src.comment when defined(useNodeIds): if result.id == nodeIdToDebug: echo "COMES FROM ", src.id @@ -1426,6 +1427,7 @@ proc shallowCopy*(src: PNode): PNode = result.info = src.info result.typ = src.typ result.flags = src.flags * PersistentNodeFlags + result.comment = src.comment when defined(useNodeIds): if result.id == nodeIdToDebug: echo "COMES FROM ", src.id @@ -1445,6 +1447,7 @@ proc copyTree*(src: PNode): PNode = result.info = src.info result.typ = src.typ result.flags = src.flags * PersistentNodeFlags + result.comment = src.comment when defined(useNodeIds): if result.id == nodeIdToDebug: echo "COMES FROM ", src.id diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index dee98aab8..6dcf80f2f 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -122,7 +122,7 @@ proc mapType(typ: PType): TCTypeKind = of tyOpenArray, tyArrayConstr, tyArray, tyVarargs: result = ctArray of tyObject, tyTuple: result = ctStruct of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal, - tyConst, tyMutable, tyIter, tyTypeDesc: + tyConst, tyMutable, tyTypeDesc: result = mapType(lastSon(typ)) of tyEnum: if firstOrd(typ) < 0: @@ -711,8 +711,7 @@ proc getTypeDescAux(m: BModule, typ: PType, check: var IntSet): Rope = of 1, 2, 4, 8: addf(m.s[cfsTypes], "typedef NU$2 $1;$n", [result, rope(s*8)]) else: addf(m.s[cfsTypes], "typedef NU8 $1[$2];$n", [result, rope(getSize(t))]) - of tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable, - tyIter, tyTypeDesc: + of tyGenericInst, tyDistinct, tyOrdinal, tyConst, tyMutable, tyTypeDesc: result = getTypeDescAux(m, lastSon(t), check) else: internalError("getTypeDescAux(" & $t.kind & ')') @@ -888,10 +887,11 @@ proc genObjectFields(m: BModule, typ: PType, n: PNode, expr: Rope) = else: internalError(n.info, "genObjectFields(nkRecCase)") of nkSym: var field = n.sym - addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" & - "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & - "$1.name = $5;$n", [expr, getTypeDesc(m, typ), - field.loc.r, genTypeInfo(m, field.typ), makeCString(field.name.s)]) + if field.bitsize == 0: + addf(m.s[cfsTypeInit3], "$1.kind = 1;$n" & + "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & + "$1.name = $5;$n", [expr, getTypeDesc(m, typ), + field.loc.r, genTypeInfo(m, field.typ), makeCString(field.name.s)]) else: internalError(n.info, "genObjectFields") proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope) = diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index ecd98a2bf..898c00f73 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -106,7 +106,7 @@ proc getUniqueType*(key: PType): PType = of tyDistinct: if key.deepCopy != nil: result = key else: result = getUniqueType(lastSon(key)) - of tyGenericInst, tyOrdinal, tyMutable, tyConst, tyIter, tyStatic: + of tyGenericInst, tyOrdinal, tyMutable, tyConst, tyStatic: result = getUniqueType(lastSon(key)) #let obj = lastSon(key) #if obj.sym != nil and obj.sym.name.s == "TOption": @@ -153,6 +153,7 @@ proc getUniqueType*(key: PType): PType = else: # ugh, we need the canon here: result = slowSearch(key, k) + of tyUnused: internalError("getUniqueType") proc tableGetType*(tab: TIdTable, key: PType): RootRef = # returns nil if we need to declare this type diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 620ee4887..d80a68609 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -867,10 +867,11 @@ proc genMainProc(m: BModule) = MainProcsWithResult = MainProcs & "\treturn nim_program_result;$N" - NimMainBody = - "N_CDECL(void, NimMainInner)(void) {$N" & + NimMainInner = "N_CDECL(void, NimMainInner)(void) {$N" & "$1" & - "}$N$N" & + "}$N$N" + + NimMainProc = "N_CDECL(void, NimMain)(void) {$N" & "\tvoid (*volatile inner)();$N" & "\tPreMain();$N" & @@ -879,6 +880,8 @@ proc genMainProc(m: BModule) = "\t(*inner)();$N" & "}$N$N" + NimMainBody = NimMainInner & NimMainProc + PosixNimMain = "int cmdCount;$N" & "char** cmdLine;$N" & @@ -906,7 +909,7 @@ proc genMainProc(m: BModule) = " LPSTR lpCmdLine, int nCmdShow) {$N" & MainProcsWithResult & "}$N$N" - WinNimDllMain = "N_LIB_EXPORT " & NimMainBody + WinNimDllMain = NimMainInner & "N_LIB_EXPORT " & NimMainProc WinCDllMain = "BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" & diff --git a/compiler/docgen.nim b/compiler/docgen.nim index c220902ff..189a055c3 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -184,7 +184,7 @@ proc genRecComment(d: PDoc, n: PNode): Rope = if n == nil: return nil result = genComment(d, n).rope if result == nil: - if n.kind notin {nkEmpty..nkNilLit, nkEnumTy}: + if n.kind notin {nkEmpty..nkNilLit, nkEnumTy, nkTupleTy}: for i in countup(0, len(n)-1): result = genRecComment(d, n.sons[i]) if result != nil: return diff --git a/compiler/installer.ini b/compiler/installer.ini index 4fc03dd1d..5e4b3ddbc 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -41,8 +41,8 @@ Files: "config/nimdoc.tex.cfg" ; Files: "doc/*.cfg" ; Files: "doc/*.pdf" ; Files: "doc/*.ini" -Files: "doc/overview.html" -Start: "doc/overview.html" +Files: "doc/html/overview.html" +Start: "doc/html/overview.html" [Other] @@ -50,6 +50,7 @@ Files: "readme.txt;install.txt;contributors.txt;copying.txt" Files: "makefile" Files: "koch.nim" Files: "install_nimble.nims" +Files: "install_tools.nims" Files: "icons/nim.ico" Files: "icons/nim.rc" @@ -62,6 +63,7 @@ Files: "icons/koch_icon.o" Files: "compiler" Files: "doc" +Files: "doc/html" Files: "tools" Files: "web/website.ini" Files: "web/ticker.html" @@ -77,7 +79,8 @@ Files: "lib" [Other] Files: "examples" -#Files: "dist/nimble" +Files: "dist/nimble" +Files: "dist/nimsuggest" Files: "tests" @@ -87,21 +90,24 @@ Files: "bin/c2nim.exe" Files: "bin/nimgrep.exe" Files: "bin/nimsuggest.exe" Files: "bin/nimble.exe" -Files: "bin/*.dll" -Files: "dist/*.dll" Files: "koch.exe" +Files: "finish.exe" ; Files: "dist/mingw" -Files: "start.bat" +Files: r"tools\start.bat" BinPath: r"bin;dist\mingw\bin;dist" ; Section | dir | zipFile | size hint (in KB) | url | exe start menu entry Download: r"Documentation|doc|docs.zip|13824|http://nim-lang.org/download/docs-${version}.zip|overview.html" Download: r"C Compiler (MingW)|dist|mingw.zip|82944|http://nim-lang.org/download/${mingw}.zip" -Download: r"Support DLL's|bin|nim_dlls.zip|479|http://nim-lang.org/download/dlls.zip" -Download: r"Aporia IDE|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.4.0.zip|aporia-0.4.0\bin\aporia.exe" +Download: r"Support DLLs|bin|nim_dlls.zip|479|http://nim-lang.org/download/dlls.zip" +Download: r"Aporia Text Editor|dist|aporia.zip|97997|http://nim-lang.org/download/aporia-0.4.0.zip|aporia-0.4.0\bin\aporia.exe" ; for now only NSIS supports optional downloads +[WinBin] +Files: "$NIMINSTDEPS/makelink.exe" +Files: "$NIMINSTDEPS/*.dll" + [UnixBin] Files: "bin/nim" diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index e7fe8cc27..6b95014f1 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -151,7 +151,7 @@ proc mapType(typ: PType): TJSTypeKind = of tyPointer: # treat a tyPointer like a typed pointer to an array of bytes result = etyBaseIndex - of tyRange, tyDistinct, tyOrdinal, tyConst, tyMutable, tyIter, tyProxy: + of tyRange, tyDistinct, tyOrdinal, tyConst, tyMutable, tyProxy: result = mapType(t.sons[0]) of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt of tyBool: result = etyBool @@ -171,6 +171,7 @@ proc mapType(typ: PType): TJSTypeKind = else: result = etyNone of tyProc: result = etyProc of tyCString: result = etyString + of tyUnused: internalError("mapType") proc mapType(p: PProc; typ: PType): TJSTypeKind = if p.target == targetPHP: result = etyObject diff --git a/compiler/msgs.nim b/compiler/msgs.nim index c10c26ec5..a44a1306c 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -215,7 +215,7 @@ const errOrdinalTypeExpected: "ordinal type expected", errOrdinalOrFloatTypeExpected: "ordinal or float type expected", errOverOrUnderflow: "over- or underflow", - errCannotEvalXBecauseIncompletelyDefined: "cannot evalutate '$1' because type is not defined completely", + errCannotEvalXBecauseIncompletelyDefined: "cannot evaluate '$1' because type is not defined completely", errChrExpectsRange0_255: "\'chr\' expects an int in the range 0..255", errDynlibRequiresExportc: "\'dynlib\' requires \'exportc\'", errUndeclaredFieldX: "undeclared field: \'$1\'", @@ -521,7 +521,7 @@ const hintCodeBegin, hintCodeEnd, hintSource, hintStackTrace, hintGCStats}, - {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace}, + {low(TNoteKind)..high(TNoteKind)} - {hintStackTrace, warnUninit}, {low(TNoteKind)..high(TNoteKind)}] const diff --git a/compiler/nim.nim b/compiler/nim.nim index a58afd593..0afefa853 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -13,6 +13,11 @@ when defined(gcc) and defined(windows): else: {.link: "icons/nim_icon.o".} +when defined(amd64) and defined(windows) and defined(vcc): + {.link: "icons/nim-amd64-windows-vcc.res" .} +when defined(i386) and defined(windows) and defined(vcc): + {.link: "icons/nim-i386-windows-vcc.res" .} + import commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes, extccomp, strutils, os, osproc, platform, main, parseopt, service, diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim index 39436702f..b4007cdaf 100644 --- a/compiler/nimfix/nimfix.nim +++ b/compiler/nimfix/nimfix.nim @@ -73,7 +73,7 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string) = of "auto": gStyleCheck = StyleCheck.Auto else: localError(gCmdLineInfo, errOnOrOffExpected) of "wholeproject": gOnlyMainfile = false - of "besteffort": msgs.gErrorMax = high(int) # dont stop after first error + of "besteffort": msgs.gErrorMax = high(int) # don't stop after first error else: processSwitch(pass, p) of cmdArgument: diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index f8f1f355c..c51d406ac 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -230,6 +230,8 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult # builtin slice keeps lvalue-ness: if getMagic(n) in {mArrGet, mSlice}: result = isAssignable(owner, n.sons[1], isUnsafeAddr) + elif n.typ != nil and n.typ.kind == tyVar: + result = arLValue of nkStmtList, nkStmtListExpr: if n.typ != nil: result = isAssignable(owner, n.lastSon, isUnsafeAddr) diff --git a/compiler/semasgn.nim b/compiler/semasgn.nim index 2e925e386..64a501e25 100644 --- a/compiler/semasgn.nim +++ b/compiler/semasgn.nim @@ -221,7 +221,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add newAsgnStmt(x, call) of tyVarargs, tyOpenArray: localError(c.info, errGenerated, "cannot copy openArray") - of tyFromExpr, tyIter, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, + of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything, tyMutable, tyGenericParam, tyGenericBody, tyNil, tyExpr, tyStmt, tyTypeDesc, tyGenericInvocation, tyBigNum, tyConst, tyForward: @@ -229,6 +229,7 @@ proc liftBodyAux(c: var TLiftCtx; t: PType; body, x, y: PNode) = of tyOrdinal, tyRange, tyGenericInst, tyFieldAccessor, tyStatic, tyVar: liftBodyAux(c, lastSon(t), body, x, y) + of tyUnused: internalError("liftBodyAux") proc newProcType(info: TLineInfo; owner: PSym): PType = result = newType(tyProc, owner) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 3037a6ecc..ca9b5effb 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -462,7 +462,7 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym = call.add(newIdentNode(fn.name, fn.info)) for i in 1.. <fn.typ.n.len: let param = fn.typ.n.sons[i] - let t = skipTypes(param.typ, abstractVar-{tyTypeDesc}) + let t = skipTypes(param.typ, abstractVar-{tyTypeDesc, tyDistinct}) if t.kind == tyDistinct or param.typ.kind == tyDistinct: hasDistinct = true var x: PType if param.typ.kind == tyVar: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 93d5ed1a2..1756e4d0e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -136,8 +136,10 @@ proc isCastable(dst, src: PType): bool = # tyProc, tySet, tyEnum, tyBool, tyChar} if skipTypes(dst, abstractInst-{tyOpenArray}).kind == tyOpenArray: return false - var dstSize, srcSize: BiggestInt + if skipTypes(src, abstractInst-{tyTypeDesc}).kind == tyTypeDesc: + return false + var dstSize, srcSize: BiggestInt dstSize = computeSize(dst) srcSize = computeSize(src) if dstSize < 0: @@ -693,6 +695,22 @@ proc semBracketedMacro(c: PContext; outer, inner: PNode; s: PSym; else: assert(false) return +proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = + result = n + let callee = result.sons[0].sym + case callee.kind + of skMacro: result = semMacroExpr(c, result, orig, callee, flags) + of skTemplate: result = semTemplateExpr(c, result, callee, flags) + else: + semFinishOperands(c, result) + activate(c, result) + fixAbstractType(c, result) + analyseIfAddressTakenInCall(c, result) + if callee.magic != mNone: + result = magicsAfterOverloadResolution(c, result, flags) + if c.inTypeClass == 0: + result = evalAtCompileTime(c, result) + proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = result = nil checkMinSonsLen(n, 1) @@ -771,27 +789,11 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # See bug #904 of how to trigger it: return result #result = afterCallActions(c, result, nOrig, flags) - fixAbstractType(c, result) - analyseIfAddressTakenInCall(c, result) - if result.sons[0].kind == nkSym and result.sons[0].sym.magic != mNone: - result = magicsAfterOverloadResolution(c, result, flags) - result = evalAtCompileTime(c, result) - -proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = - result = n - let callee = result.sons[0].sym - case callee.kind - of skMacro: result = semMacroExpr(c, result, orig, callee, flags) - of skTemplate: result = semTemplateExpr(c, result, callee, flags) + if result.sons[0].kind == nkSym: + result = afterCallActions(c, result, nOrig, flags) else: - semFinishOperands(c, result) - activate(c, result) fixAbstractType(c, result) analyseIfAddressTakenInCall(c, result) - if callee.magic != mNone: - result = magicsAfterOverloadResolution(c, result, flags) - if c.inTypeClass == 0: - result = evalAtCompileTime(c, result) proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # this seems to be a hotspot in the compiler! diff --git a/compiler/seminst.nim b/compiler/seminst.nim index d7cad6a2f..e1a65da74 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -58,7 +58,7 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym for i, a in n.pairs: internalAssert a.kind == nkSym var q = a.sym - if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses: + if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic}+tyTypeClasses: continue let symKind = if q.typ.kind == tyStatic: skConst else: skType var s = newSym(symKind, q.name, getCurrOwner(), q.info) @@ -99,7 +99,8 @@ proc genericCacheGet(genericSym: PSym, entry: TInstantiation; proc freshGenSyms(n: PNode, owner, orig: PSym, symMap: var TIdTable) = # we need to create a fresh set of gensym'ed symbols: - if n.kind == nkSym and sfGenSym in n.sym.flags and n.sym.owner == orig: + if n.kind == nkSym and sfGenSym in n.sym.flags and + (n.sym.owner == orig or n.sym.owner.kind == skPackage): let s = n.sym var x = PSym(idTableGet(symMap, s)) if x == nil: diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 806b00db6..cd90782d1 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -24,7 +24,7 @@ proc semTypeOf(c: PContext; n: PNode): PNode = result = newNodeI(nkTypeOfExpr, n.info) let typExpr = semExprWithType(c, n, {efInTypeof}) result.add typExpr - result.typ = makeTypeDesc(c, typExpr.typ.skipTypes({tyTypeDesc, tyIter})) + result.typ = makeTypeDesc(c, typExpr.typ.skipTypes({tyTypeDesc})) type SemAsgnMode = enum asgnNormal, noOverloadedSubscript, noOverloadedAsgn diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index df9b3f69c..8aa8f15c8 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -204,17 +204,22 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet) = let u = s.gcUnsafetyReason if u != nil and not cycleCheck.containsOrIncl(u.id): let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated - if u.kind in {skLet, skVar}: + case u.kind + of skLet, skVar: message(s.info, msgKind, ("'$#' is not GC-safe as it accesses '$#'" & " which is a global using GC'ed memory") % [s.name.s, u.name.s]) - elif u.kind in routineKinds: + of routineKinds: # recursive call *always* produces only a warning so the full error # message is printed: listGcUnsafety(u, true, cycleCheck) message(s.info, msgKind, "'$#' is not GC-safe as it calls '$#'" % [s.name.s, u.name.s]) + of skParam: + message(s.info, msgKind, + "'$#' is not GC-safe as it performs an indirect call via '$#'" % + [s.name.s, u.name.s]) else: internalAssert u.kind == skUnknown message(u.info, msgKind, @@ -721,7 +726,8 @@ proc track(tracked: PEffects, n: PNode) = # and it's not a recursive call: if not (a.kind == nkSym and a.sym == tracked.owner): markSideEffect(tracked, a) - for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i)) + if a.kind != nkSym or a.sym.magic != mNBindSym: + for i in 1 .. <len(n): trackOperand(tracked, n.sons[i], paramType(op, i)) if a.kind == nkSym and a.sym.magic in {mNew, mNewFinalize, mNewSeq}: # may not look like an assignment, but it is: let arg = n.sons[1] diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 97c3b6ddd..ebcff643f 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -155,7 +155,10 @@ proc discardCheck(c: PContext, result: PNode) = else: var n = result while n.kind in skipForDiscardable: n = n.lastSon - localError(n.info, errDiscardValueX, result.typ.typeToString) + if result.typ.kind == tyProc: + localError(n.info, "value of type '" & result.typ.typeToString & "' has to be discarded; for a function call use ()") + else: + localError(n.info, errDiscardValueX, result.typ.typeToString) proc semIf(c: PContext, n: PNode): PNode = result = n diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index b8dfede1f..15171874f 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -549,8 +549,6 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = of tyNil: result = f.allowsNil - of tyIter: - if tfIterator in f.flags: result = typeRel(c, f.base, a.base) else: discard proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = @@ -1021,11 +1019,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = var fskip = skippedNone let aobj = x.skipToObject(askip) let fobj = genericBody.lastSon.skipToObject(fskip) + var depth = -1 if fobj != nil and aobj != nil and askip == fskip: - let depth = isObjectSubtype(c, aobj, fobj, f) - if depth >= 0: - c.inheritancePenalty += depth - return if depth == 0: isGeneric else: isSubtype + depth = isObjectSubtype(c, aobj, fobj, f) result = typeRel(c, genericBody, x) if result != isNone: # see tests/generics/tgeneric3.nim for an example that triggers this @@ -1047,6 +1043,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = else: put(c, f.sons[i], x) + if depth >= 0: + c.inheritancePenalty += depth + # bug #4863: We still need to bind generic alias crap, so + # we cannot return immediately: + result = if depth == 0: isGeneric else: isSubtype of tyAnd: considerPreviousT: result = isEqual @@ -1220,13 +1221,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = else: result = isNone - of tyIter: - if a.kind == tyIter or - (a.kind == tyProc and tfIterator in a.flags): - result = typeRel(c, f.base, a.base) - else: - result = isNone - of tyStmt: if aOrig != nil and tfOldSchoolExprStmt notin f.flags: put(c, f, aOrig) @@ -1567,8 +1561,13 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, proc setSon(father: PNode, at: int, son: PNode) = - if sonsLen(father) <= at: setLen(father.sons, at + 1) + let oldLen = father.len + if oldLen <= at: + setLen(father.sons, at + 1) father.sons[at] = son + # insert potential 'void' parameters: + #for i in oldLen ..< at: + # father.sons[i] = newNodeIT(nkEmpty, son.info, getSysType(tyVoid)) # we are allowed to modify the calling node in the 'prepare*' procs: proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode = @@ -1579,8 +1578,9 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode = elif a.typ.isNil: # XXX This is unsound! 'formal' can differ from overloaded routine to # overloaded routine! - let flags = if formal.kind == tyIter: {efDetermineType, efWantIterator} - else: {efDetermineType, efAllowStmt} + let flags = {efDetermineType, efAllowStmt} + #if formal.kind == tyIter: {efDetermineType, efWantIterator} + #else: {efDetermineType, efAllowStmt} #elif formal.kind == tyStmt: {efDetermineType, efWantStmt} #else: {efDetermineType} result = c.semOperand(c, a, flags) diff --git a/compiler/types.nim b/compiler/types.nim index 3db0c4507..d7651aad2 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -572,7 +572,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = addSep(prag) add(prag, "locks: " & $t.lockLevel) if len(prag) != 0: add(result, "{." & prag & ".}") - of tyVarargs, tyIter: + of tyVarargs: result = typeToStr[t.kind] % typeToString(t.sons[0]) else: result = typeToStr[t.kind] @@ -965,7 +965,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = result = a.sym.position == b.sym.position of tyGenericInvocation, tyGenericBody, tySequence, tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, - tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter, + tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyOrdinal, tyTypeClasses, tyFieldAccessor: cycleCheck() if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n @@ -980,6 +980,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = sameValue(a.n.sons[1], b.n.sons[1]) of tyGenericInst: discard of tyNone: result = false + of tyUnused: internalError("sameFlags") proc sameBackendType*(x, y: PType): bool = var c = initSameTypeClosure() @@ -1143,7 +1144,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, else: result = typeAllowedAux(marker, t.lastSon, skVar, flags+{taHeap}) of tyPtr: result = typeAllowedAux(marker, t.lastSon, skVar, flags+{taHeap}) - of tyArrayConstr, tySet, tyConst, tyMutable, tyIter: + of tyArrayConstr, tySet, tyConst, tyMutable: for i in countup(0, sonsLen(t) - 1): result = typeAllowedAux(marker, t.sons[i], kind, flags) if result != nil: break @@ -1160,6 +1161,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, # for now same as error node; we say it's a valid type as it should # prevent cascading errors: result = nil + of tyUnused: internalError("typeAllowedAux") proc typeAllowed*(t: PType, kind: TSymKind): PType = # returns 'nil' on success and otherwise the part of the type that is @@ -1311,7 +1313,7 @@ proc computeSizeAux(typ: PType, a: var BiggestInt): BiggestInt = if result < 0: return if a < maxAlign: a = maxAlign result = align(result, a) - of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst, tyIter: + of tyGenericInst, tyDistinct, tyGenericBody, tyMutable, tyConst: result = computeSizeAux(lastSon(typ), a) of tyTypeDesc: result = computeSizeAux(typ.base, a) @@ -1374,7 +1376,7 @@ proc safeInheritanceDiff*(a, b: PType): int = if a.kind == tyError or b.kind == tyError: result = -1 else: - result = inheritanceDiff(a, b) + result = inheritanceDiff(a.skipTypes(skipPtrs), b.skipTypes(skipPtrs)) proc compatibleEffectsAux(se, re: PNode): bool = if re.isNil: return false diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim index 438744b1c..e9c27ac9d 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -37,14 +37,20 @@ proc renderType(n: PNode): string = of nkIdent: result = n.ident.s of nkSym: result = typeToString(n.sym.typ) of nkVarTy: - assert len(n) == 1 - result = renderType(n[0]) + if n.len == 1: + result = renderType(n[0]) + else: + result = "var" of nkRefTy: - assert len(n) == 1 - result = "ref." & renderType(n[0]) + if n.len == 1: + result = "ref." & renderType(n[0]) + else: + result = "ref" of nkPtrTy: - assert len(n) == 1 - result = "ptr." & renderType(n[0]) + if n.len == 1: + result = "ptr." & renderType(n[0]) + else: + result = "ptr" of nkProcTy: assert len(n) > 1 let params = n[0] diff --git a/compiler/vm.nim b/compiler/vm.nim index 2e2a49db5..be522ced0 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -171,6 +171,7 @@ proc copyValue(src: PNode): PNode = result.info = src.info result.typ = src.typ result.flags = src.flags * PersistentNodeFlags + result.comment = src.comment when defined(useNodeIds): if result.id == nodeIdToDebug: echo "COMES FROM ", src.id @@ -899,7 +900,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if newPc < pc: handleJmpBack() #echo "new pc ", newPc, " calling: ", prc.name.s var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos) - newSeq(newFrame.slots, prc.offset) + newSeq(newFrame.slots, prc.offset+ord(isClosure)) if not isEmptyType(prc.typ.sons[0]) or prc.kind == skMacro: putIntoReg(newFrame.slots[0], getNullValue(prc.typ.sons[0], prc.info)) for i in 1 .. rc-1: @@ -1071,7 +1072,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcRepr: decodeB(rkNode) createStr regs[ra] - regs[ra].node.strVal = renderTree(regs[rb].regToNode, {renderNoComments}) + regs[ra].node.strVal = renderTree(regs[rb].regToNode, {renderNoComments, renderDocComments}) of opcQuit: if c.mode in {emRepl, emStaticExpr, emStaticStmt}: message(c.debug[pc], hintQuitCalled) @@ -1241,7 +1242,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = createStr regs[ra] regs[ra].node.strVal = opGorge(regs[rb].node.strVal, - regs[rc].node.strVal, regs[rd].node.strVal) + regs[rc].node.strVal, regs[rd].node.strVal, + c.debug[pc]) of opcNError: stackTrace(c, tos, pc, errUser, regs[ra].node.strVal) of opcNWarning: @@ -1420,7 +1422,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: regs[rc].node.strVal if k < 0 or k > ord(high(TSymKind)): internalError(c.debug[pc], "request to create symbol of invalid kind") - var sym = newSym(k.TSymKind, name.getIdent, c.module, c.debug[pc]) + var sym = newSym(k.TSymKind, name.getIdent, c.module.owner, c.debug[pc]) incl(sym.flags, sfGenSym) regs[ra].node = newSymNode(sym) of opcTypeTrait: diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index b40ca1058..88287c366 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -19,7 +19,8 @@ proc readOutput(p: Process): string = result.setLen(result.len - "\n".len) discard p.waitForExit -proc opGorge*(cmd, input, cache: string): string = +proc opGorge*(cmd, input, cache: string, info: TLineInfo): string = + let workingDir = parentDir(info.toFullPath) if cache.len > 0:# and optForceFullMake notin gGlobalOptions: let h = secureHash(cmd & "\t" & input & "\t" & cache) let filename = options.toGeneratedFile("gorge_" & $h, "txt") @@ -30,7 +31,8 @@ proc opGorge*(cmd, input, cache: string): string = return var readSuccessful = false try: - var p = startProcess(cmd, options={poEvalCommand, poStderrToStdout}) + var p = startProcess(cmd, workingDir, + options={poEvalCommand, poStderrToStdout}) if input.len != 0: p.inputStream.write(input) p.inputStream.close() @@ -41,7 +43,8 @@ proc opGorge*(cmd, input, cache: string): string = if not readSuccessful: result = "" else: try: - var p = startProcess(cmd, options={poEvalCommand, poStderrToStdout}) + var p = startProcess(cmd, workingDir, + options={poEvalCommand, poStderrToStdout}) if input.len != 0: p.inputStream.write(input) p.inputStream.close() @@ -270,7 +273,6 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; of tyConst: result = mapTypeToBracket("const", mNone, t, info) of tyMutable: result = mapTypeToBracket("mutable", mNone, t, info) of tyVarargs: result = mapTypeToBracket("varargs", mVarargs, t, info) - of tyIter: result = mapTypeToBracket("iter", mNone, t, info) of tyProxy: result = atomicType("error", mNone) of tyBuiltInTypeClass: result = mapTypeToBracket("builtinTypeClass", mNone, t, info) @@ -292,6 +294,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; result.add atomicType("static", mNone) if t.n != nil: result.add t.n.copyTree + of tyUnused: internalError("mapTypeToAstX") proc opMapTypeToAst*(t: PType; info: TLineInfo): PNode = result = mapTypeToAstX(t, info, false, true) diff --git a/config/nim.cfg b/config/nim.cfg index 0373de135..8c8270f3e 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -66,7 +66,7 @@ path="$lib/pure" @end @if unix: - @if not bsd: + @if not bsd or haiku: # -fopenmp gcc.options.linker = "-ldl" gcc.cpp.options.linker = "-ldl" @@ -80,6 +80,14 @@ path="$lib/pure" # at least NetBSD has problems with thread local storage: tlsEmulation:on @end + @if haiku: + # -fopenmp + gcc.options.linker = "-lroot -lnetwork" + gcc.cpp.options.linker = "-lroot -lnetwork" + clang.options.linker = "-lroot -lnetwork" + clang.cpp.options.linker = "-lroot -lnetwork" + tcc.options.linker = "-lroot -lnetwork" + @end @end # Configuration for the Intel C/C++ compiler: diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 819444874..832b4ffd0 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -1279,8 +1279,8 @@ dt pre > span.Operator ~ span.Identifier, dt pre > span.Operator ~ span.Operator div.search_results { background-color: antiquewhite; margin: 3em; - border-style: inset; padding: 1em; + border: 1px solid #4d4d4d; } </style> diff --git a/contributors.txt b/contributors.txt deleted file mode 100644 index d54ec844a..000000000 --- a/contributors.txt +++ /dev/null @@ -1,12 +0,0 @@ -Comex -Eric Doughty-Papassideris -Simon Hafner -Keita Haga -Grzegorz Adam Hankiewicz -Philippe Lhoste -Zahary Karadjov -Mario Ray Mahardhika -Alexander Mitchell-Robinson (Amrykid) -Dominik Picheta -Jonathan Plona -Alexander Rødseth diff --git a/doc/advopt.txt b/doc/advopt.txt index 02e69c5b8..b8980fa9c 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -3,6 +3,7 @@ Advanced commands: //compileToCpp, cpp compile project to C++ code //compileToOC, objc compile project to Objective C code //js compile project to Javascript + //e run a Nimscript file //rst2html convert a reStructuredText file to HTML //rst2tex convert a reStructuredText file to TeX //jsondoc extract the documentation to a json file diff --git a/doc/astspec.txt b/doc/astspec.txt index 35eb1727b..f430677af 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -1184,6 +1184,7 @@ Nim type Corresponding AST ``proc`` ``nnkProcTy`` ``iterator`` ``nnkIteratorTy`` ``object`` ``nnkObjectTy`` +------------- --------------------------------------------- Take special care when declaring types as ``proc``. The behavior is similar to ``Procedure declaration``, below, but does not treat ``nnkGenericParams``. diff --git a/contributing.rst b/doc/contributing.rst index 31f04a5e0..31f04a5e0 100644 --- a/contributing.rst +++ b/doc/contributing.rst diff --git a/doc/docs.txt b/doc/docs.rst index 4484784ae..4484784ae 100644 --- a/doc/docs.txt +++ b/doc/docs.rst diff --git a/docstyle.rst b/doc/docstyle.rst index d789b1df9..d789b1df9 100644 --- a/docstyle.rst +++ b/doc/docstyle.rst diff --git a/doc/gc.rst b/doc/gc.rst index 1c8cb9122..bb0088617 100644 --- a/doc/gc.rst +++ b/doc/gc.rst @@ -19,8 +19,10 @@ This document describes how the GC works and how to tune it for The basic algorithm is *Deferred Reference Counting* with cycle detection. References on the stack are not counted for better performance (and easier C -code generation). The GC **never** scans the whole heap but it may scan the -delta-subgraph of the heap that changed since its last run. +code generation). Cycle detection is currently done by a simple mark&sweep +GC that has to scan the full (thread local heap). ``--gc:v2`` replaces this +with an incremental mark and sweep. That it is not production ready yet, +however. The GC is only triggered in a memory allocation operation. It is not triggered @@ -34,17 +36,7 @@ Cycle collector =============== The cycle collector can be en-/disabled independently from the other parts of -the GC with ``GC_enableMarkAndSweep`` and ``GC_disableMarkAndSweep``. The -compiler analyses the types for their possibility to build cycles, but often -it is necessary to help this analysis with the ``acyclic`` pragma (see -`acyclic <manual.html#acyclic-pragma>`_ for further information). - -You can also use the ``acyclic`` pragma for data that is cyclic in reality and -then break up the cycles explicitly with ``GC_addCycleRoot``. This can be a -very valuable optimization; the Nim compiler itself relies on this -optimization trick to improve performance. Note that ``GC_addCycleRoot`` is -a quick operation; the root is only registered for the next run of the -cycle collector. +the GC with ``GC_enableMarkAndSweep`` and ``GC_disableMarkAndSweep``. Realtime support @@ -55,19 +47,19 @@ defined via ``--define:useRealtimeGC`` (you can put this into your config file as well). With this switch the GC supports the following operations: .. code-block:: nim - proc GC_setMaxPause*(MaxPauseInUs: int) + proc GC_setMaxPause*(maxPauseInUs: int) proc GC_step*(us: int, strongAdvice = false, stackSize = -1) -The unit of the parameters ``MaxPauseInUs`` and ``us`` is microseconds. +The unit of the parameters ``maxPauseInUs`` and ``us`` is microseconds. These two procs are the two modus operandi of the realtime GC: (1) GC_SetMaxPause Mode You can call ``GC_SetMaxPause`` at program startup and then each triggered - GC run tries to not take longer than ``MaxPause`` time. However, it is + GC run tries to not take longer than ``maxPause`` time. However, it is possible (and common) that the work is nevertheless not evenly distributed - as each call to ``new`` can trigger the GC and thus take ``MaxPause`` + as each call to ``new`` can trigger the GC and thus take ``maxPause`` time. (2) GC_step Mode @@ -86,8 +78,8 @@ These two procs are the two modus operandi of the realtime GC: These procs provide a "best effort" realtime guarantee; in particular the cycle collector is not aware of deadlines yet. Deactivate it to get more predictable realtime behaviour. Tests show that a 2ms max pause -time will be met in almost all cases on modern CPUs unless the cycle collector -is triggered. +time will be met in almost all cases on modern CPUs (with the cycle collector +disabled). Time measurement diff --git a/doc/lib.rst b/doc/lib.rst index 66928055a..828889968 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -582,4 +582,4 @@ Nim programming language. nimblepkglist.js or have javascript disabled in your browser.</b></div> <script type="text/javascript" src="nimblepkglist.js"></script> - <script type="text/javascript" src="http://irclogs.nim-lang.org/packages?callback=gotPackageList"></script> + <script type="text/javascript" src="http://irclogs.nim-lang.org/packages?callback=gotPackageList" async></script> diff --git a/doc/manual/exceptions.txt b/doc/manual/exceptions.txt index d06c13df4..0f1240a4a 100644 --- a/doc/manual/exceptions.txt +++ b/doc/manual/exceptions.txt @@ -147,7 +147,7 @@ the ``raise`` statement is the only way to raise an exception. If no exception name is given, the current exception is `re-raised`:idx:. The `ReraiseError`:idx: exception is raised if there is no exception to re-raise. It follows that the ``raise`` statement *always* raises an -exception (unless a raise hook has been provided). +exception. Exception hierarchy diff --git a/doc/overview.txt b/doc/overview.rst index 5b41752ae..e01520d7c 100644 --- a/doc/overview.txt +++ b/doc/overview.rst @@ -5,5 +5,5 @@ Nim Documentation Overview :Author: Andreas Rumpf :Version: |nimversion| -.. include:: ../doc/docs.txt +.. include:: docs.rst diff --git a/icons/koch-amd64-windows-vcc.res b/icons/koch-amd64-windows-vcc.res new file mode 100644 index 000000000..90d7d1f7a --- /dev/null +++ b/icons/koch-amd64-windows-vcc.res Binary files differdiff --git a/icons/koch-i386-windows-vcc.res b/icons/koch-i386-windows-vcc.res new file mode 100644 index 000000000..90d7d1f7a --- /dev/null +++ b/icons/koch-i386-windows-vcc.res Binary files differdiff --git a/icons/nim-amd64-windows-vcc.res b/icons/nim-amd64-windows-vcc.res new file mode 100644 index 000000000..b2d8fc9eb --- /dev/null +++ b/icons/nim-amd64-windows-vcc.res Binary files differdiff --git a/icons/nim-i386-windows-vcc.res b/icons/nim-i386-windows-vcc.res new file mode 100644 index 000000000..b2d8fc9eb --- /dev/null +++ b/icons/nim-i386-windows-vcc.res Binary files differdiff --git a/install.txt b/install.txt index 67be2221b..6ab562b07 100644 --- a/install.txt +++ b/install.txt @@ -30,6 +30,16 @@ There are also ``install.sh`` and ``deinstall.sh`` scripts for distributing the files over the UNIX hierarchy. However, updating your Nim installation is more cumbersome then. +To complete the installation you should also build Nim's tools like +``nimsuggest``, ``nimble`` or ``nimgrep`` via:: + + nim c koch + koch tools + +Note that these tools should also end up in your ``PATH`` so adding +``$your_install_dir/bin/nim`` to your ``PATH`` is preferred over the symlink +solution. + Installation on the Macintosh ----------------------------- @@ -44,10 +54,8 @@ or clang. Installation on Windows ----------------------- -Install Nim by downloading and running the ``nim_$version.exe`` file. -As default, the ``GCC`` compiler is used that is bundled with this installer. -You can change the configuration file ``config/nim.cfg`` to use -another C compiler or change the path to GCC. +Install Nim by downloading and unzipping the ``nim_$version.zip`` file. +Run ``finish.exe`` to detect and setup your MingW environment. Currently, the following C compilers are supported under Windows: @@ -76,4 +84,5 @@ Nimble is Nim's package manager. For the source based installations where you added Nim's ``bin`` directory to your ``$PATH`` the easiest way of installing Nimble is via:: - nim e install_nimble.nims + nim c koch + koch nimble diff --git a/install_nimble.nims b/install_nimble.nims index 05024c046..6e929f499 100644 --- a/install_nimble.nims +++ b/install_nimble.nims @@ -3,6 +3,8 @@ import ospaths mode = ScriptMode.Verbose +echo "This script is deprecated. Use 'koch nimble' instead." + var id = 0 while dirExists("nimble" & $id): inc id @@ -20,4 +22,4 @@ try: mvFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe except OSError: cpFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe - + diff --git a/install_tools.nims b/install_tools.nims new file mode 100644 index 000000000..f5f320f78 --- /dev/null +++ b/install_tools.nims @@ -0,0 +1,20 @@ + +import ospaths + +mode = ScriptMode.Verbose + +echo "This script is deprecated. Use 'koch tools' instead." + +if not dirExists"dist/nimble": + echo "[Error] This script only works for the tarball." +else: + let nimbleExe = "./bin/nimble".toExe + selfExec "c --noNimblePath -p:compiler -o:" & nimbleExe & + " dist/nimble/src/nimble.nim" + + let nimsugExe = "./bin/nimsuggest".toExe + selfExec "c --noNimblePath -d:release -p:compiler -o:" & nimsugExe & + " dist/nimsuggest/nimsuggest.nim" + + let nimgrepExe = "./bin/nimgrep".toExe + selfExec "c -d:release -o:" & nimgrepExe & " tools/nimgrep.nim" diff --git a/koch.nim b/koch.nim index fab0e67cb..a3f5fb3de 100644 --- a/koch.nim +++ b/koch.nim @@ -15,16 +15,16 @@ when defined(gcc) and defined(windows): else: {.link: "icons/koch_icon.o".} +when defined(amd64) and defined(windows) and defined(vcc): + {.link: "icons/koch-amd64-windows-vcc.res" .} +when defined(i386) and defined(windows) and defined(vcc): + {.link: "icons/koch-i386-windows-vcc.res" .} + import os, strutils, parseopt, osproc, streams const VersionAsString = system.NimVersion #"0.10.2" -when defined(withUpdate): - import httpclient -when defined(haveZipLib): - import zipfiles - const HelpText = """ +-----------------------------------------------------------------+ @@ -40,7 +40,7 @@ Options: --help, -h shows this help and quits Possible Commands: boot [options] bootstraps with given command line options - install [bindir] installs to given directory; Unix only! + distrohelper [bindir] helper for distro packagers geninstall generate ./install.sh; Unix only! testinstall test tar.xz package; Unix only! Only for devs! clean cleans Nim project; removes generated files @@ -52,10 +52,10 @@ Possible Commands: xz builds the installation XZ package nsis [options] builds the NSIS Setup installer (for Windows) tests [options] run the testsuite - update updates nim to the latest version from github - (compile koch with -d:withUpdate to enable) temp options creates a temporary compiler for testing winrelease creates a release (for coredevs only) + nimble builds the Nimble tool + tools builds Nim related tools Boot options: -d:release produce a release version of the compiler -d:tinyc include the Tiny C backend (not supported on Windows) @@ -69,7 +69,10 @@ Web options: build the official docs, use UA-48159761-1 """ -proc exe(f: string): string = return addFileExt(f, ExeExt) +proc exe(f: string): string = + result = addFileExt(f, ExeExt) + when defined(windows): + result = result.replace('/','\\') proc findNim(): string = var nim = "nim".exe @@ -92,6 +95,9 @@ proc exec(cmd: string, errorcode: int = QuitFailure, additionalPath = "") = if execShellCmd(cmd) != 0: quit("FAILURE", errorcode) putEnv("PATH", prevPath) +proc nimexec(cmd: string) = + exec findNim() & " " & cmd + proc execCleanPath(cmd: string, additionalPath = ""; errorcode: int = QuitFailure) = # simulate a poor man's virtual environment @@ -123,6 +129,8 @@ proc testUnixInstall() = execCleanPath("./koch boot -d:release", destDir / "bin") # check the docs build: execCleanPath("./koch web", destDir / "bin") + # check nimble builds: + execCleanPath("./bin/nim e install_tools.nims") # check the tests work: execCleanPath("./koch tests", destDir / "bin") else: @@ -143,13 +151,16 @@ proc copyExe(source, dest: string) = inclFilePermissions(dest, {fpUserExec}) const - compileNimInst = "-d:useLibzipSrc tools/niminst/niminst" + compileNimInst = "tools/niminst/niminst" proc csource(args: string) = - exec("$4 cc $1 -r $3 --var:version=$2 --var:mingw=none csource --main:compiler/nim.nim compiler/installer.ini $1" % - [args, VersionAsString, compileNimInst, findNim()]) + nimexec(("cc $1 -r $3 --var:version=$2 --var:mingw=none csource " & + "--main:compiler/nim.nim compiler/installer.ini $1") % + [args, VersionAsString, compileNimInst]) -proc bundleNimble() = +proc bundleNimbleSrc() = + ## bunldeNimbleSrc() bundles a specific Nimble commit with the tarball. We + ## always bundle the latest official release. if dirExists("dist/nimble/.git"): exec("git --git-dir dist/nimble/.git pull") else: @@ -157,31 +168,86 @@ proc bundleNimble() = let tags = execProcess("git --git-dir dist/nimble/.git tag -l v*").splitLines let tag = tags[^1] exec("git --git-dir dist/nimble/.git checkout " & tag) + +proc bundleNimbleExe() = + bundleNimbleSrc() # now compile Nimble and copy it to $nim/bin for the installer.ini # to pick it up: - exec(findNim() & " c dist/nimble/src/nimble.nim") + nimexec("c dist/nimble/src/nimble.nim") copyExe("dist/nimble/src/nimble".exe, "bin/nimble".exe) +proc buildNimble() = + ## buildNimble() builds Nimble for the building via "github". As such, we + ## choose the most recent commit of Nimble too. + var installDir = "dist/nimble" + if dirExists("dist/nimble/.git"): + exec("git --git-dir dist/nimble/.git pull") + else: + # if dist/nimble exist, but is not a git repo, don't mess with it: + if dirExists(installDir): + var id = 0 + while dirExists("dist/nimble" & $id): + inc id + installDir = "dist/nimble" & $id + exec("git clone https://github.com/nim-lang/nimble.git " & installDir) + nimexec("c " & installDir / "src/nimble.nim") + copyExe(installDir / "src/nimble".exe, "bin/nimble".exe) + +proc bundleNimsuggest(buildExe: bool) = + if dirExists("dist/nimsuggest/.git"): + exec("git --git-dir dist/nimsuggest/.git pull") + else: + exec("git clone https://github.com/nim-lang/nimsuggest.git dist/nimsuggest") + if buildExe: + nimexec("c --noNimblePath -d:release -p:compiler dist/nimsuggest/nimsuggest.nim") + copyExe("dist/nimsuggest/nimsuggest".exe, "bin/nimsuggest".exe) + removeFile("dist/nimsuggest/nimsuggest".exe) + +proc bundleFinishExe() = + nimexec("c tools/finish.nim") + copyExe("tools/finish".exe, "finish".exe) + removeFile("tools/finish".exe) + proc zip(args: string) = - bundleNimble() - exec("$3 cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" % - [VersionAsString, compileNimInst, findNim()]) + bundleNimbleSrc() + bundleNimsuggest(false) + bundleFinishExe() + nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" % + [VersionAsString, compileNimInst]) exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim zip compiler/installer.ini" % ["tools/niminst/niminst".exe, VersionAsString]) proc xz(args: string) = - bundleNimble() - exec("$3 cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" % - [VersionAsString, compileNimInst, findNim()]) + bundleNimbleSrc() + bundleNimsuggest(false) + nimexec("cc -r $2 --var:version=$1 --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini" % + [VersionAsString, compileNimInst]) exec("$# --var:version=$# --var:mingw=none --main:compiler/nim.nim xz compiler/installer.ini" % ["tools" / "niminst" / "niminst".exe, VersionAsString]) proc buildTool(toolname, args: string) = - exec("$# cc $# $#" % [findNim(), args, toolname]) + nimexec("cc $# $#" % [args, toolname]) copyFile(dest="bin"/ splitFile(toolname).name.exe, source=toolname.exe) +proc buildTools() = + if not dirExists"dist/nimble": + echo "[Error] 'koch tools' only works for the tarball." + else: + let nimbleExe = "bin/nimble".exe + nimexec "c --noNimblePath -p:compiler -o:" & nimbleExe & + " dist/nimble/src/nimble.nim" + + let nimsugExe = "bin/nimsuggest".exe + nimexec "c --noNimblePath -p:compiler -d:release -o:" & nimsugExe & + " dist/nimsuggest/nimsuggest.nim" + + let nimgrepExe = "bin/nimgrep".exe + nimexec "c -o:" & nimgrepExe & " tools/nimgrep.nim" + proc nsis(args: string) = - bundleNimble() + bundleNimbleExe() + bundleNimsuggest(true) + bundleFinishExe() # make sure we have generated the niminst executables: buildTool("tools/niminst/niminst", args) #buildTool("tools/nimgrep", args) @@ -192,21 +258,21 @@ proc nsis(args: string) = " nsis compiler/installer.ini") % [VersionAsString, $(sizeof(pointer)*8)]) proc geninstall(args="") = - exec("$# cc -r $# --var:version=$# --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini $#" % - [findNim(), compileNimInst, VersionAsString, args]) + nimexec("cc -r $# --var:version=$# --var:mingw=none --main:compiler/nim.nim scripts compiler/installer.ini $#" % + [compileNimInst, VersionAsString, args]) proc install(args: string) = geninstall() exec("sh ./install.sh $#" % args) proc web(args: string) = - exec("$# js tools/dochack/dochack.nim" % findNim()) - exec("$# cc -r tools/nimweb.nim $# web/website.ini --putenv:nimversion=$#" % - [findNim(), args, VersionAsString]) + nimexec("js tools/dochack/dochack.nim") + nimexec("cc -r tools/nimweb.nim $# web/website.ini --putenv:nimversion=$#" % + [args, VersionAsString]) proc website(args: string) = - exec("$# cc -r tools/nimweb.nim $# --website web/website.ini --putenv:nimversion=$#" % - [findNim(), args, VersionAsString]) + nimexec("cc -r tools/nimweb.nim $# --website web/website.ini --putenv:nimversion=$#" % + [args, VersionAsString]) proc pdf(args="") = exec("$# cc -r tools/nimweb.nim $# --pdf web/website.ini --putenv:nimversion=$#" % @@ -245,7 +311,7 @@ proc boot(args: string) = var finalDest = "bin" / "nim".exe # default to use the 'c' command: let bootOptions = if args.len == 0 or args.startsWith("-"): "c" else: "" - let smartNimcache = if "release" in args: "rnimcache" else: "dnimcache" + let smartNimcache = if "release" in args: "nimcache/release" else: "nimcache/debug" copyExe(findStartNim(), 0.thVersion) for i in 0..2: @@ -296,7 +362,6 @@ proc removePattern(pattern: string) = removeFile(f) proc clean(args: string) = - if existsFile("koch.dat"): removeFile("koch.dat") removePattern("web/*.html") removePattern("doc/*.html") cleanAux(getCurrentDir()) @@ -305,83 +370,8 @@ proc clean(args: string) = echo "removing dir: ", path removeDir(path) -# -------------- update ------------------------------------------------------- - -when defined(withUpdate): - when defined(windows): - {.warning: "Windows users: Make sure to run 'koch update' in Bash.".} - - proc update(args: string) = - when defined(windows): - echo("Windows users: Make sure to be running this in Bash. ", - "If you aren't, press CTRL+C now.") - - var thisDir = getAppDir() - var git = findExe("git") - echo("Checking for git repo and git executable...") - if existsDir(thisDir & "/.git") and git != "": - echo("Git repo found!") - # use git to download latest source - echo("Checking for updates...") - discard startCmd(git & " fetch origin master") - var procs = startCmd(git & " diff origin/master master") - var errcode = procs.waitForExit() - var output = readLine(procs.outputStream) - echo(output) - if errcode == 0: - if output == "": - # No changes - echo("No update. Exiting...") - return - else: - echo("Fetching updates from repo...") - var pullout = execCmdEx(git & " pull origin master") - if pullout[1] != 0: - quit("An error has occurred.") - else: - if pullout[0].startsWith("Already up-to-date."): - quit("No new changes fetched from the repo. " & - "Local branch must be ahead of it. Exiting...") - else: - quit("An error has occurred.") - - else: - echo("No repo or executable found!") - when defined(haveZipLib): - echo("Falling back.. Downloading source code from repo...") - # use dom96's httpclient to download zip - downloadFile("https://github.com/Araq/Nim/zipball/master", - thisDir / "update.zip") - try: - echo("Extracting source code from archive...") - var zip: TZipArchive - discard open(zip, thisDir & "/update.zip", fmRead) - extractAll(zip, thisDir & "/") - except: - quit("Error reading archive.") - else: - quit("No failback available. Exiting...") - - echo("Starting update...") - boot(args) - echo("Update complete!") - # -------------- builds a release --------------------------------------------- -#[ -proc run7z(platform: string, patterns: varargs[string]) = - const tmpDir = "nim-" & VersionAsString - createDir tmpDir - try: - for pattern in patterns: - for f in walkFiles(pattern): - if "nimcache" notin f: - copyFile(f, tmpDir / f) - exec("7z a -tzip $1-$2.zip $1" % [tmpDir, platform]) - finally: - removeDir tmpDir -]# - proc winRelease() = exec(r"call ci\nsis_build.bat " & VersionAsString) @@ -392,11 +382,10 @@ template `|`(a, b): string = (if a.len > 0: a else: b) proc tests(args: string) = # we compile the tester with taintMode:on to have a basic # taint mode test :-) - let nimexe = findNim() - exec nimexe & " cc --taintMode:on tests/testament/tester" + nimexec "cc --taintMode:on tests/testament/tester" # Since tests take a long time (on my machine), and we want to defy Murhpys # law - lets make sure the compiler really is freshly compiled! - exec nimexe & " c --lib:lib -d:release --opt:speed compiler/nim.nim" + nimexec "c --lib:lib -d:release --opt:speed compiler/nim.nim" let tester = quoteShell(getCurrentDir() / "tests/testament/tester".exe) let success = tryExec tester & " " & (args|"all") if not existsEnv("TRAVIS") and not existsEnv("APPVEYOR"): @@ -426,6 +415,7 @@ of cmdArgument: of "boot": boot(op.cmdLineRest) of "clean": clean(op.cmdLineRest) of "web": web(op.cmdLineRest) + of "doc", "docs": web("--onlyDocs " & op.cmdLineRest) of "json2": web("--json2 " & op.cmdLineRest) of "website": website(op.cmdLineRest & " --googleAnalytics:UA-48159761-1") of "web0": @@ -437,15 +427,13 @@ of cmdArgument: of "xz": xz(op.cmdLineRest) of "nsis": nsis(op.cmdLineRest) of "geninstall": geninstall(op.cmdLineRest) + of "distrohelper": geninstall() of "install": install(op.cmdLineRest) of "testinstall": testUnixInstall() of "test", "tests": tests(op.cmdLineRest) - of "update": - when defined(withUpdate): - update(op.cmdLineRest) - else: - quit "this Koch has not been compiled with -d:withUpdate" of "temp": temp(op.cmdLineRest) of "winrelease": winRelease() + of "nimble": buildNimble() + of "tools": buildTools() else: showHelp() of cmdEnd: showHelp() diff --git a/lib/arch/arch.nim b/lib/arch/arch.nim index a11bfb21f..0b3df3d3c 100644 --- a/lib/arch/arch.nim +++ b/lib/arch/arch.nim @@ -6,6 +6,9 @@ # See the file "copying.txt", included in this # distribution, for details about the copyright. # +# Architecture-specific optimizations and features. +# arch.nim can be imported by only a subset of the +# architectures supported by Nim. when defined(windows): const @@ -31,7 +34,7 @@ when defined(amd64): Reg* {.pure.} = enum AX, BX, CX, DX, SI, DI, BP, SP, IP, R8, R9, R10, R11, R12, R13, R14, R15, TOTAL -elif defined(i386): +elif defined(i386) or defined(nimdoc): # identical fastcall calling convention on all x86 OS type JmpBufReg* {.pure.} = enum diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 1a26d5522..801abe0cc 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -92,7 +92,7 @@ type ntyUInt, ntyUInt8, ntyUInt16, ntyUInt32, ntyUInt64, ntyBigNum, ntyConst, ntyMutable, ntyVarargs, - ntyIter, + ntyUnused, ntyError, ntyBuiltinTypeClass, ntyConcept, ntyConceptInst, ntyComposite, ntyAnd, ntyOr, ntyNot diff --git a/lib/impure/re.nim b/lib/impure/re.nim index bf397550a..bd86bcdcf 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -359,7 +359,7 @@ proc parallelReplace*(s: string, subs: openArray[ proc transformFile*(infile, outfile: string, subs: openArray[tuple[pattern: Regex, repl: string]]) = ## reads in the file `infile`, performs a parallel replacement (calls - ## `parallelReplace`) and writes back to `outfile`. Raises ``EIO`` if an + ## `parallelReplace`) and writes back to `outfile`. Raises ``IOError`` if an ## error occurs. This is supposed to be used for quick scripting. var x = readFile(infile).string writeFile(outfile, x.parallelReplace(subs)) diff --git a/lib/js/jsconsole.nim b/lib/js/jsconsole.nim new file mode 100644 index 000000000..d9ced95f0 --- /dev/null +++ b/lib/js/jsconsole.nim @@ -0,0 +1,44 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Wrapper for the `console` object for the `JavaScript backend +## <backends.html#the-javascript-target>`_. + +when not defined(js) and not defined(Nimdoc): + {.error: "This module only works on the JavaScript platform".} + +import macros + +type Console* {.importc.} = ref object of RootObj + +proc convertToConsoleLoggable*[T](v: T): RootRef {.importcpp: "#".} +template convertToConsoleLoggable*(v: string): RootRef = cast[RootRef](cstring(v)) + +proc logImpl(console: Console) {.importcpp: "log", varargs.} +proc debugImpl(console: Console) {.importcpp: "debug", varargs.} +proc infoImpl(console: Console) {.importcpp: "info", varargs.} +proc errorImpl(console: Console) {.importcpp: "error", varargs.} + +proc makeConsoleCall(console: NimNode, procName: NimNode, args: NimNode): NimNode = + result = newCall(procName, console) + for c in args: result.add(c) + +macro log*(console: Console, args: varargs[RootRef, convertToConsoleLoggable]): untyped = + makeConsoleCall(console, bindSym "logImpl", args) + +macro debug*(console: Console, args: varargs[RootRef, convertToConsoleLoggable]): untyped = + makeConsoleCall(console, bindSym "debugImpl", args) + +macro info*(console: Console, args: varargs[RootRef, convertToConsoleLoggable]): untyped = + makeConsoleCall(console, bindSym "infoImpl", args) + +macro error*(console: Console, args: varargs[RootRef, convertToConsoleLoggable]): untyped = + makeConsoleCall(console, bindSym "errorImpl", args) + +var console* {.importc, nodecl.}: Console \ No newline at end of file diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index 9de25f82b..06b90768c 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -242,14 +242,17 @@ proc nimNextToken(g: var GeneralTokenizer) = inc(pos) case g.buf[pos] of 'b', 'B': + g.kind = gtBinNumber inc(pos) while g.buf[pos] in binChars: inc(pos) pos = nimNumberPostfix(g, pos) of 'x', 'X': + g.kind = gtHexNumber inc(pos) while g.buf[pos] in hexChars: inc(pos) pos = nimNumberPostfix(g, pos) of 'o', 'O': + g.kind = gtOctNumber inc(pos) while g.buf[pos] in octChars: inc(pos) pos = nimNumberPostfix(g, pos) @@ -700,8 +703,8 @@ proc yamlNextToken(g: var GeneralTokenizer) = g.state = gtLongStringLit elif g.state == gtLongStringLit: # beware, this is the only token where we actually have to parse - # indentation. - + # indentation. + g.kind = gtLongStringLit # first, we have to find the parent indentation of the block scalar, so that # we know when to stop @@ -738,20 +741,20 @@ proc yamlNextToken(g: var GeneralTokenizer) = # because lookbehind was at newline char when calculating indentation, we're # off by one. fix that. top level's parent will have indentation of -1. let parentIndentation = indentation - 1 - + # find first content while g.buf[pos] in {' ', '\x0A', '\x0D'}: if g.buf[pos] == ' ': inc(indentation) else: indentation = 0 inc(pos) var minIndentation = indentation - + # for stupid edge cases, we must check whether an explicit indentation depth # is given at the header. while g.buf[headerStart] in {'>', '|', '+', '-'}: inc(headerStart) if g.buf[headerStart] in {'0'..'9'}: minIndentation = min(minIndentation, ord(g.buf[headerStart]) - ord('0')) - + # process content lines while indentation > parentIndentation and g.buf[pos] != '\0': if (indentation < minIndentation and g.buf[pos] == '#') or @@ -766,7 +769,7 @@ proc yamlNextToken(g: var GeneralTokenizer) = if g.buf[pos] == ' ': inc(indentation) else: indentation = 0 inc(pos) - + g.state = gtOther elif g.state == gtOther: # gtOther means 'inside YAML document' diff --git a/lib/posix/kqueue.nim b/lib/posix/kqueue.nim index 5c67d621e..19d661490 100644 --- a/lib/posix/kqueue.nim +++ b/lib/posix/kqueue.nim @@ -123,7 +123,7 @@ when defined(macosx) or defined(freebsd): NOTE_USECONDS* = 0x00000004'u32 ## data is microseconds NOTE_NSECONDS* = 0x00000008'u32 ## data is nanoseconds else: - # NetBSD and OpenBSD doesnt support NOTE_{TIME} constants, but + # NetBSD and OpenBSD doesn't support NOTE_{TIME} constants, but # support EVFILT_TIMER with granularity of milliseconds. const NOTE_MSECONDS* = 0x00000000'u32 diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index f9085de55..8c4a0e41d 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -132,7 +132,7 @@ export Port, SocketFlag ## # Handle exception ## ## Unfortunately the semantics of the try statement may not always be correct, -## and occassionally the compilation may fail altogether. +## and occasionally the compilation may fail altogether. ## As such it is better to use the former style when possible. ## ## Discarding futures @@ -156,290 +156,10 @@ export Port, SocketFlag ## * Can't await in a ``except`` body ## * Forward declarations for async procs are broken, ## link includes workaround: https://github.com/nim-lang/Nim/issues/3182. -## * FutureVar[T] needs to be completed manually. # TODO: Check if yielded future is nil and throw a more meaningful exception -# -- Futures - -type - FutureBase* = ref object of RootObj ## Untyped future. - cb: proc () {.closure,gcsafe.} - finished: bool - error*: ref Exception ## Stored exception - errorStackTrace*: string - when not defined(release): - stackTrace: string ## For debugging purposes only. - id: int - fromProc: string - - Future*[T] = ref object of FutureBase ## Typed future. - value: T ## Stored value - - FutureVar*[T] = distinct Future[T] - - FutureError* = object of Exception - cause*: FutureBase - -{.deprecated: [PFutureBase: FutureBase, PFuture: Future].} - -when not defined(release): - var currentID = 0 - -proc callSoon*(cbproc: proc ()) {.gcsafe.} - -proc newFuture*[T](fromProc: string = "unspecified"): Future[T] = - ## Creates a new future. - ## - ## Specifying ``fromProc``, which is a string specifying the name of the proc - ## that this future belongs to, is a good habit as it helps with debugging. - new(result) - result.finished = false - when not defined(release): - result.stackTrace = getStackTrace() - result.id = currentID - result.fromProc = fromProc - currentID.inc() - -proc newFutureVar*[T](fromProc = "unspecified"): FutureVar[T] = - ## Create a new ``FutureVar``. This Future type is ideally suited for - ## situations where you want to avoid unnecessary allocations of Futures. - ## - ## Specifying ``fromProc``, which is a string specifying the name of the proc - ## that this future belongs to, is a good habit as it helps with debugging. - result = FutureVar[T](newFuture[T](fromProc)) - -proc clean*[T](future: FutureVar[T]) = - ## Resets the ``finished`` status of ``future``. - Future[T](future).finished = false - Future[T](future).error = nil - -proc checkFinished[T](future: Future[T]) = - ## Checks whether `future` is finished. If it is then raises a - ## ``FutureError``. - when not defined(release): - if future.finished: - var msg = "" - msg.add("An attempt was made to complete a Future more than once. ") - msg.add("Details:") - msg.add("\n Future ID: " & $future.id) - msg.add("\n Created in proc: " & future.fromProc) - msg.add("\n Stack trace to moment of creation:") - msg.add("\n" & indent(future.stackTrace.strip(), 4)) - when T is string: - msg.add("\n Contents (string): ") - msg.add("\n" & indent(future.value.repr, 4)) - msg.add("\n Stack trace to moment of secondary completion:") - msg.add("\n" & indent(getStackTrace().strip(), 4)) - var err = newException(FutureError, msg) - err.cause = future - raise err - -proc complete*[T](future: Future[T], val: T) = - ## Completes ``future`` with value ``val``. - #assert(not future.finished, "Future already finished, cannot finish twice.") - checkFinished(future) - assert(future.error == nil) - future.value = val - future.finished = true - if future.cb != nil: - future.cb() - -proc complete*(future: Future[void]) = - ## Completes a void ``future``. - #assert(not future.finished, "Future already finished, cannot finish twice.") - checkFinished(future) - assert(future.error == nil) - future.finished = true - if future.cb != nil: - future.cb() - -proc complete*[T](future: FutureVar[T]) = - ## Completes a ``FutureVar``. - template fut: expr = Future[T](future) - checkFinished(fut) - assert(fut.error == nil) - fut.finished = true - if fut.cb != nil: - fut.cb() - -proc fail*[T](future: Future[T], error: ref Exception) = - ## Completes ``future`` with ``error``. - #assert(not future.finished, "Future already finished, cannot finish twice.") - checkFinished(future) - future.finished = true - future.error = error - future.errorStackTrace = - if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error) - if future.cb != nil: - future.cb() - else: - # This is to prevent exceptions from being silently ignored when a future - # is discarded. - # TODO: This may turn out to be a bad idea. - # Turns out this is a bad idea. - #raise error - discard - -proc `callback=`*(future: FutureBase, cb: proc () {.closure,gcsafe.}) = - ## Sets the callback proc to be called when the future completes. - ## - ## If future has already completed then ``cb`` will be called immediately. - ## - ## **Note**: You most likely want the other ``callback`` setter which - ## passes ``future`` as a param to the callback. - future.cb = cb - if future.finished: - callSoon(future.cb) - -proc `callback=`*[T](future: Future[T], - cb: proc (future: Future[T]) {.closure,gcsafe.}) = - ## Sets the callback proc to be called when the future completes. - ## - ## If future has already completed then ``cb`` will be called immediately. - future.callback = proc () = cb(future) - -proc injectStacktrace[T](future: Future[T]) = - # TODO: Come up with something better. - when not defined(release): - var msg = "" - msg.add("\n " & future.fromProc & "'s lead up to read of failed Future:") - - if not future.errorStackTrace.isNil and future.errorStackTrace != "": - msg.add("\n" & indent(future.errorStackTrace.strip(), 4)) - else: - msg.add("\n Empty or nil stack trace.") - future.error.msg.add(msg) - -proc read*[T](future: Future[T]): T = - ## Retrieves the value of ``future``. Future must be finished otherwise - ## this function will fail with a ``ValueError`` exception. - ## - ## If the result of the future is an error then that error will be raised. - if future.finished: - if future.error != nil: - injectStacktrace(future) - raise future.error - when T isnot void: - return future.value - else: - # TODO: Make a custom exception type for this? - raise newException(ValueError, "Future still in progress.") - -proc readError*[T](future: Future[T]): ref Exception = - ## Retrieves the exception stored in ``future``. - ## - ## An ``ValueError`` exception will be thrown if no exception exists - ## in the specified Future. - if future.error != nil: return future.error - else: - raise newException(ValueError, "No error in future.") - -proc mget*[T](future: FutureVar[T]): var T = - ## Returns a mutable value stored in ``future``. - ## - ## Unlike ``read``, this function will not raise an exception if the - ## Future has not been finished. - result = Future[T](future).value - -proc finished*[T](future: Future[T]): bool = - ## Determines whether ``future`` has completed. - ## - ## ``True`` may indicate an error or a value. Use ``failed`` to distinguish. - future.finished - -proc failed*(future: FutureBase): bool = - ## Determines whether ``future`` completed with an error. - return future.error != nil - -proc asyncCheck*[T](future: Future[T]) = - ## Sets a callback on ``future`` which raises an exception if the future - ## finished with an error. - ## - ## This should be used instead of ``discard`` to discard void futures. - future.callback = - proc () = - if future.failed: - injectStacktrace(future) - raise future.error - -proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = - ## Returns a future which will complete once both ``fut1`` and ``fut2`` - ## complete. - var retFuture = newFuture[void]("asyncdispatch.`and`") - fut1.callback = - proc () = - if not retFuture.finished: - if fut1.failed: retFuture.fail(fut1.error) - elif fut2.finished: retFuture.complete() - fut2.callback = - proc () = - if not retFuture.finished: - if fut2.failed: retFuture.fail(fut2.error) - elif fut1.finished: retFuture.complete() - return retFuture - -proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = - ## Returns a future which will complete once either ``fut1`` or ``fut2`` - ## complete. - var retFuture = newFuture[void]("asyncdispatch.`or`") - proc cb[X](fut: Future[X]) = - if fut.failed: retFuture.fail(fut.error) - if not retFuture.finished: retFuture.complete() - fut1.callback = cb[T] - fut2.callback = cb[Y] - return retFuture - -proc all*[T](futs: varargs[Future[T]]): auto = - ## Returns a future which will complete once - ## all futures in ``futs`` complete. - ## - ## If the awaited futures are not ``Future[void]``, the returned future - ## will hold the values of all awaited futures in a sequence. - ## - ## If the awaited futures *are* ``Future[void]``, - ## this proc returns ``Future[void]``. - - when T is void: - var - retFuture = newFuture[void]("asyncdispatch.all") - completedFutures = 0 - - let totalFutures = len(futs) - - for fut in futs: - fut.callback = proc(f: Future[T]) = - if f.failed: - retFuture.fail(f.error) - elif not retFuture.finished: - inc(completedFutures) - - if completedFutures == totalFutures: - retFuture.complete() - - return retFuture - - else: - var - retFuture = newFuture[seq[T]]("asyncdispatch.all") - retValues = newSeq[T](len(futs)) - completedFutures = 0 - - for i, fut in futs: - proc setCallback(i: int) = - fut.callback = proc(f: Future[T]) = - if f.failed: - retFuture.fail(f.error) - elif not retFuture.finished: - retValues[i] = f.read() - inc(completedFutures) - - if completedFutures == len(retValues): - retFuture.complete(retValues) - - setCallback(i) - - return retFuture +include includes/asyncfutures type PDispatcherBase = ref object of RootRef @@ -1335,7 +1055,7 @@ else: # so that exceptions can be raised from `send(...)` and # `recv(...)` routines. - if EvRead in info.events: + if EvRead in info.events or info.events == {EvError}: # Callback may add items to ``data.readCBs`` which causes issues if # we are iterating over ``data.readCBs`` at the same time. We therefore # make a copy to iterate over. @@ -1346,7 +1066,7 @@ else: # Callback wants to be called again. data.readCBs.add(cb) - if EvWrite in info.events: + if EvWrite in info.events or info.events == {EvError}: let currentCBs = data.writeCBs data.writeCBs = @[] for cb in currentCBs: @@ -1646,7 +1366,7 @@ proc send*(socket: AsyncFD, data: string, # -- Await Macro include asyncmacro -proc recvLine*(socket: AsyncFD): Future[string] {.async.} = +proc recvLine*(socket: AsyncFD): Future[string] {.async, deprecated.} = ## Reads a line of data from ``socket``. Returned future will complete once ## a full line is read or an error occurs. ## @@ -1664,6 +1384,8 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async.} = ## ## **Note**: This procedure is mostly used for testing. You likely want to ## use ``asyncnet.recvLine`` instead. + ## + ## **Deprecated since version 0.15.0**: Use ``asyncnet.recvLine()`` instead. template addNLIfEmpty(): stmt = if result.len == 0: diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim index 037d55e1d..019a18f55 100644 --- a/lib/pure/asyncftpclient.nim +++ b/lib/pure/asyncftpclient.nim @@ -354,6 +354,20 @@ proc store*(ftp: AsyncFtpClient, file, dest: string, await doUpload(ftp, destFile, onProgressChanged) +proc rename*(ftp: AsyncFtpClient, nameFrom: string, nameTo: string) {.async.} = + ## Rename a file or directory on the remote FTP Server from current name + ## ``name_from`` to new name ``name_to`` + assertReply(await ftp.send("RNFR " & name_from), "350") + assertReply(await ftp.send("RNTO " & name_to), "250") + +proc removeFile*(ftp: AsyncFtpClient, filename: string) {.async.} = + ## Delete a file ``filename`` on the remote FTP server + assertReply(await ftp.send("DELE " & filename), "250") + +proc removeDir*(ftp: AsyncFtpClient, dir: string) {.async.} = + ## Delete a directory ``dir`` on the remote FTP server + assertReply(await ftp.send("RMD " & dir), "250") + proc newAsyncFtpClient*(address: string, port = Port(21), user, pass = ""): AsyncFtpClient = ## Creates a new ``AsyncFtpClient`` object. @@ -373,6 +387,11 @@ when not defined(testing) and isMainModule: echo await ftp.listDirs() await ftp.store("payload.jpg", "payload.jpg") await ftp.retrFile("payload.jpg", "payload2.jpg") + await ftp.rename("payload.jpg", "payload_renamed.jpg") + await ftp.store("payload.jpg", "payload_remove.jpg") + await ftp.removeFile("payload_remove.jpg") + await ftp.createDir("deleteme") + await ftp.removeDir("deleteme") echo("Finished") waitFor main(ftp) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 6a7326e83..a658097f9 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -9,6 +9,12 @@ ## This module implements a high performance asynchronous HTTP server. ## +## This HTTP server has not been designed to be used in production, but +## for testing applications locally. Because of this, when deploying your +## application you should use a reverse proxy (for example nginx) instead of +## allowing users to connect directly to this server. +## +## ## Examples ## -------- ## @@ -38,7 +44,7 @@ export httpcore except parseHeader type Request* = object client*: AsyncSocket # TODO: Separate this into a Response object? - reqMethod*: string + reqMethod*: HttpMethod headers*: HttpHeaders protocol*: tuple[orig: string, major, minor: int] url*: Uri @@ -127,7 +133,14 @@ proc processClient(client: AsyncSocket, address: string, var i = 0 for linePart in lineFut.mget.split(' '): case i - of 0: request.reqMethod.shallowCopy(linePart.normalize) + of 0: + try: + # TODO: this is likely slow. + request.reqMethod = parseEnum[HttpMethod]("http" & linePart) + except ValueError: + asyncCheck request.respond(Http400, "Invalid request method. Got: " & + linePart) + continue of 1: parseUri(linePart, request.url) of 2: try: @@ -159,7 +172,7 @@ proc processClient(client: AsyncSocket, address: string, request.client.close() return - if request.reqMethod == "post": + if request.reqMethod == HttpPost: # Check for Expect header if request.headers.hasKey("Expect"): if "100-continue" in request.headers["Expect"]: @@ -178,17 +191,12 @@ proc processClient(client: AsyncSocket, address: string, else: request.body = await client.recv(contentLength) assert request.body.len == contentLength - elif request.reqMethod == "post": + elif request.reqMethod == HttpPost: await request.respond(Http400, "Bad Request. No Content-Length.") continue - case request.reqMethod - of "get", "post", "head", "put", "delete", "trace", "options", - "connect", "patch": - await callback(request) - else: - await request.respond(Http400, "Invalid request method. Got: " & - request.reqMethod) + # Call the user's callback. + await callback(request) if "upgrade" in request.headers.getOrDefault("connection"): return diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index f70afaafa..3d004e84c 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -25,7 +25,7 @@ proc skipStmtList(node: NimNode): NimNode {.compileTime.} = result = node[0] template createCb(retFutureSym, iteratorNameSym, - name: untyped) = + name, futureVarCompletions: untyped) = var nameIterVar = iteratorNameSym #{.push stackTrace: off.} proc cb {.closure,gcsafe.} = @@ -44,6 +44,8 @@ template createCb(retFutureSym, iteratorNameSym, raise else: retFutureSym.fail(getCurrentException()) + + futureVarCompletions cb() #{.pop.} proc generateExceptionCheck(futSym, @@ -119,8 +121,22 @@ template createVar(result: var NimNode, futSymName: string, result.add newVarStmt(futSym, asyncProc) # -> var future<x> = y useVar(result, futSym, valueReceiver, rootReceiver, fromNode) +proc createFutureVarCompletions(futureVarIdents: seq[NimNode]): NimNode + {.compileTime.} = + result = newStmtList() + # Add calls to complete each FutureVar parameter. + for ident in futureVarIdents: + # Only complete them if they have not been completed already by the user. + result.add newIfStmt( + ( + newCall(newIdentNode("not"), + newDotExpr(ident, newIdentNode("finished"))), + newCall(newIdentNode("complete"), ident) + ) + ) + proc processBody(node, retFutureSym: NimNode, - subTypeIsVoid: bool, + subTypeIsVoid: bool, futureVarIdents: seq[NimNode], tryStmt: NimNode): NimNode {.compileTime.} = #echo(node.treeRepr) result = node @@ -134,11 +150,14 @@ proc processBody(node, retFutureSym: NimNode, else: result.add newCall(newIdentNode("complete"), retFutureSym) else: - let x = node[0].processBody(retFutureSym, subTypeIsVoid, tryStmt) + let x = node[0].processBody(retFutureSym, subTypeIsVoid, + futureVarIdents, tryStmt) if x.kind == nnkYieldStmt: result.add x else: result.add newCall(newIdentNode("complete"), retFutureSym, x) + result.add createFutureVarCompletions(futureVarIdents) + result.add newNimNode(nnkReturnStmt, node).add(newNilLit()) return # Don't process the children of this return stmt of nnkCommand, nnkCall: @@ -196,7 +215,8 @@ proc processBody(node, retFutureSym: NimNode, # Transform ``except`` body. # TODO: Could we perform some ``await`` transformation here to get it # working in ``except``? - tryBody[1] = processBody(n[1], retFutureSym, subTypeIsVoid, nil) + tryBody[1] = processBody(n[1], retFutureSym, subTypeIsVoid, + futureVarIdents, nil) proc processForTry(n: NimNode, i: var int, res: NimNode): bool {.compileTime.} = @@ -207,7 +227,7 @@ proc processBody(node, retFutureSym: NimNode, var skipped = n.skipStmtList() while i < skipped.len: var processed = processBody(skipped[i], retFutureSym, - subTypeIsVoid, n) + subTypeIsVoid, futureVarIdents, n) # Check if we transformed the node into an exception check. # This suggests skipped[i] contains ``await``. @@ -239,7 +259,8 @@ proc processBody(node, retFutureSym: NimNode, else: discard for i in 0 .. <result.len: - result[i] = processBody(result[i], retFutureSym, subTypeIsVoid, nil) + result[i] = processBody(result[i], retFutureSym, subTypeIsVoid, + futureVarIdents, nil) proc getName(node: NimNode): string {.compileTime.} = case node.kind @@ -252,6 +273,14 @@ proc getName(node: NimNode): string {.compileTime.} = else: error("Unknown name.") +proc getFutureVarIdents(params: NimNode): seq[NimNode] {.compileTime.} = + result = @[] + for i in 1 .. <len(params): + expectKind(params[i], nnkIdentDefs) + if params[i][1].kind == nnkBracketExpr and + ($params[i][1][0].ident).normalize == "futurevar": + result.add(params[i][0]) + proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = ## This macro transforms a single procedure into a closure iterator. ## The ``async`` macro supports a stmtList holding multiple async procedures. @@ -282,6 +311,8 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = let subtypeIsVoid = returnType.kind == nnkEmpty or (baseType.kind == nnkIdent and returnType[1].ident == !"void") + let futureVarIdents = getFutureVarIdents(prc[3]) + var outerProcBody = newNimNode(nnkStmtList, prc[6]) # -> var retFuture = newFuture[T]() @@ -304,7 +335,8 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = # -> <proc_body> # -> complete(retFuture, result) var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter") - var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid, nil) + var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid, + futureVarIdents, nil) # don't do anything with forward bodies (empty) if procBody.kind != nnkEmpty: if not subtypeIsVoid: @@ -326,6 +358,8 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = # -> complete(retFuture) procBody.add(newCall(newIdentNode("complete"), retFutureSym)) + procBody.add(createFutureVarCompletions(futureVarIdents)) + var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")], procBody, nnkIteratorDef) closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure")) @@ -334,7 +368,8 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = # -> createCb(retFuture) #var cbName = newIdentNode("cb") var procCb = getAst createCb(retFutureSym, iteratorNameSym, - newStrLitNode(prc[0].getName)) + newStrLitNode(prc[0].getName), + createFutureVarCompletions(futureVarIdents)) outerProcBody.add procCb # -> return retFuture diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 334f95baa..3b64c278f 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -159,7 +159,9 @@ when defineSsl: await socket.fd.AsyncFd.send(data, flags) proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag], - sslError: cint) {.async.} = + sslError: cint): Future[bool] {.async.} = + ## Returns ``true`` if ``socket`` is still connected, otherwise ``false``. + result = true case sslError of SSL_ERROR_WANT_WRITE: await sendPendingSslData(socket, flags) @@ -173,6 +175,7 @@ when defineSsl: elif length == 0: # connection not properly closed by remote side or connection dropped SSL_set_shutdown(socket.sslHandle, SSL_RECEIVED_SHUTDOWN) + result = false else: raiseSSLError("Cannot appease SSL.") @@ -180,13 +183,27 @@ when defineSsl: op: expr) = var opResult {.inject.} = -1.cint while opResult < 0: + # Call the desired operation. opResult = op # Bit hackish here. # TODO: Introduce an async template transformation pragma? + + # Send any remaining pending SSL data. yield sendPendingSslData(socket, flags) + + # If the operation failed, try to see if SSL has some data to read + # or write. if opResult < 0: let err = getSslError(socket.sslHandle, opResult.cint) - yield appeaseSsl(socket, flags, err.cint) + let fut = appeaseSsl(socket, flags, err.cint) + yield fut + if not fut.read(): + # Socket disconnected. + if SocketFlag.SafeDisconn in flags: + break + else: + raiseSSLError("Socket has been disconnected") + proc connect*(socket: AsyncSocket, address: string, port: Port) {.async.} = ## Connects ``socket`` to server at ``address:port``. @@ -388,7 +405,7 @@ proc accept*(socket: AsyncSocket, return retFut proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string], - flags = {SocketFlag.SafeDisconn}) {.async.} = + flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength) {.async.} = ## Reads a line of data from ``socket`` into ``resString``. ## ## If a full line is read ``\r\L`` is not @@ -401,13 +418,14 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string], ## is read) then line will be set to ``""``. ## The partial line **will be lost**. ## + ## The ``maxLength`` parameter determines the maximum amount of characters + ## that can be read before a ``ValueError`` is raised. This prevents Denial + ## of Service (DOS) attacks. + ## ## **Warning**: The ``Peek`` flag is not yet implemented. ## ## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the ## protocol uses ``\r\L`` to delimit a new line. - ## - ## **Warning**: ``recvLineInto`` currently uses a raw pointer to a string for - ## performance reasons. This will likely change soon to use FutureVars. assert SocketFlag.Peek notin flags ## TODO: assert(not resString.mget.isNil(), "String inside resString future needs to be initialised") @@ -454,6 +472,12 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string], else: resString.mget.add socket.buffer[socket.currPos] socket.currPos.inc() + + # Verify that this isn't a DOS attack: #3847. + if resString.mget.len > maxLength: + let msg = "recvLine received more than the specified `maxLength` " & + "allowed." + raise newException(ValueError, msg) else: var c = "" while true: @@ -475,10 +499,17 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string], resString.complete() return resString.mget.add c + + # Verify that this isn't a DOS attack: #3847. + if resString.mget.len > maxLength: + let msg = "recvLine received more than the specified `maxLength` " & + "allowed." + raise newException(ValueError, msg) resString.complete() proc recvLine*(socket: AsyncSocket, - flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} = + flags = {SocketFlag.SafeDisconn}, + maxLength = MaxLineLength): Future[string] {.async.} = ## Reads a line of data from ``socket``. Returned future will complete once ## a full line is read or an error occurs. ## @@ -492,6 +523,10 @@ proc recvLine*(socket: AsyncSocket, ## is read) then line will be set to ``""``. ## The partial line **will be lost**. ## + ## The ``maxLength`` parameter determines the maximum amount of characters + ## that can be read before a ``ValueError`` is raised. This prevents Denial + ## of Service (DOS) attacks. + ## ## **Warning**: The ``Peek`` flag is not yet implemented. ## ## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol @@ -501,7 +536,7 @@ proc recvLine*(socket: AsyncSocket, # TODO: Optimise this var resString = newFutureVar[string]("asyncnet.recvLine") resString.mget() = "" - await socket.recvLineInto(resString, flags) + await socket.recvLineInto(resString, flags, maxLength) result = resString.mget() proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = diff --git a/lib/pure/basic2d.nim b/lib/pure/basic2d.nim index 7d74424fa..e4696c6a8 100644 --- a/lib/pure/basic2d.nim +++ b/lib/pure/basic2d.nim @@ -117,13 +117,13 @@ proc safeArccos(v:float):float= template makeBinOpVector(s:expr)= - ## implements binary operators + , - , * and / for vectors + ## implements binary operators ``+``, ``-``, ``*`` and ``/`` for vectors proc s*(a,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a.x,b.x),s(a.y,b.y)) proc s*(a:Vector2d,b:float):Vector2d {.inline,noInit.} = vector2d(s(a.x,b),s(a.y,b)) proc s*(a:float,b:Vector2d):Vector2d {.inline,noInit.} = vector2d(s(a,b.x),s(a,b.y)) template makeBinOpAssignVector(s:expr)= - ## implements inplace binary operators += , -= , /= and *= for vectors + ## implements inplace binary operators ``+=``, ``-=``, ``/=`` and ``*=`` for vectors proc s*(a:var Vector2d,b:Vector2d) {.inline.} = s(a.x,b.x) ; s(a.y,b.y) proc s*(a:var Vector2d,b:float) {.inline.} = s(a.x,b) ; s(a.y,b) diff --git a/lib/pure/basic3d.nim b/lib/pure/basic3d.nim index 424c191f8..f7a9c237c 100644 --- a/lib/pure/basic3d.nim +++ b/lib/pure/basic3d.nim @@ -117,7 +117,6 @@ proc safeArccos(v:float):float= return arccos(clamp(v,-1.0,1.0)) template makeBinOpVector(s:expr)= - ## implements binary operators + , - , * and / for vectors proc s*(a,b:Vector3d):Vector3d {.inline,noInit.} = vector3d(s(a.x,b.x),s(a.y,b.y),s(a.z,b.z)) proc s*(a:Vector3d,b:float):Vector3d {.inline,noInit.} = @@ -126,11 +125,10 @@ template makeBinOpVector(s:expr)= vector3d(s(a,b.x),s(a,b.y),s(a,b.z)) template makeBinOpAssignVector(s:expr)= - ## implements inplace binary operators += , -= , /= and *= for vectors proc s*(a:var Vector3d,b:Vector3d) {.inline.} = - s(a.x,b.x) ; s(a.y,b.y) ; s(a.z,b.z) + s(a.x,b.x); s(a.y,b.y); s(a.z,b.z) proc s*(a:var Vector3d,b:float) {.inline.} = - s(a.x,b) ; s(a.y,b) ; s(a.z,b) + s(a.x,b); s(a.y,b); s(a.z,b) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index f458b7636..45a148fbf 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -228,7 +228,7 @@ proc apply*[T](data: var seq[T], op: proc (x: var T) {.closure.}) ## var a = @["1", "2", "3", "4"] ## echo repr(a) ## # --> ["1", "2", "3", "4"] - ## map(a, proc(x: var string) = x &= "42") + ## apply(a, proc(x: var string) = x &= "42") ## echo repr(a) ## # --> ["142", "242", "342", "442"] ## @@ -247,7 +247,7 @@ proc apply*[T](data: var seq[T], op: proc (x: T): T {.closure.}) ## var a = @["1", "2", "3", "4"] ## echo repr(a) ## # --> ["1", "2", "3", "4"] - ## map(a, proc(x: string): string = x & "42") + ## apply(a, proc(x: string): string = x & "42") ## echo repr(a) ## # --> ["142", "242", "342", "442"] ## diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 9fa8f5263..fe75f9a58 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -118,7 +118,11 @@ template dataLen(t): untyped = len(t.data) include tableimpl -proc clear*[A, B](t: var Table[A, B] | TableRef[A, B]) = +proc clear*[A, B](t: var Table[A, B]) = + ## Resets the table so that it is empty. + clearImpl() + +proc clear*[A, B](t: TableRef[A, B]) = ## Resets the table so that it is empty. clearImpl() @@ -334,7 +338,7 @@ proc hasKey*[A, B](t: TableRef[A, B], key: A): bool = ## returns true iff `key` is in the table `t`. result = t[].hasKey(key) -template equalsImpl(t) = +template equalsImpl(s, t: typed): typed = if s.counter == t.counter: # different insertion orders mean different 'data' seqs, so we have # to use the slow route here: @@ -344,7 +348,9 @@ template equalsImpl(t) = return true proc `==`*[A, B](s, t: Table[A, B]): bool = - equalsImpl(t) + ## The `==` operator for hash tables. Returns ``true`` iff the content of both + ## tables contains the same key-value pairs. Insert order does not matter. + equalsImpl(s, t) proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): Table[C, B] = ## Index the collection with the proc provided. @@ -432,9 +438,12 @@ proc `$`*[A, B](t: TableRef[A, B]): string = dollarImpl() proc `==`*[A, B](s, t: TableRef[A, B]): bool = + ## The `==` operator for hash tables. Returns ``true`` iff either both tables + ## are ``nil`` or none is ``nil`` and the content of both tables contains the + ## same key-value pairs. Insert order does not matter. if isNil(s): result = isNil(t) elif isNil(t): result = false - else: equalsImpl(t[]) + else: equalsImpl(s[], t[]) proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] = ## Index the collection with the proc provided. @@ -460,12 +469,16 @@ proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} = ## returns the number of keys in `t`. result = t.counter -proc clear*[A, B](t: var OrderedTable[A, B] | OrderedTableRef[A, B]) = +proc clear*[A, B](t: var OrderedTable[A, B]) = ## Resets the table so that it is empty. clearImpl() t.first = -1 t.last = -1 +proc clear*[A, B](t: var OrderedTableRef[A, B]) = + ## Resets the table so that is is empty. + clear(t[]) + template forAllOrderedPairs(yieldStmt: untyped) {.oldimmediate, dirty.} = var h = t.first while h >= 0: @@ -602,6 +615,15 @@ proc `$`*[A, B](t: OrderedTable[A, B]): string = ## The `$` operator for ordered hash tables. dollarImpl() +proc `==`*[A, B](s, t: OrderedTable[A, B]): bool = + ## The `==` operator for ordered hash tables. Both the content and the order + ## must be equal for this to return ``true``. + if s.counter == t.counter: + forAllOrderedPairs: + if s.data[h] != t.data[h]: return false + result = true + else: result = false + proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x,y: (A, B)): int) = ## sorts `t` according to `cmp`. This modifies the internal list @@ -745,6 +767,11 @@ proc `$`*[A, B](t: OrderedTableRef[A, B]): string = ## The `$` operator for ordered hash tables. dollarImpl() +proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool = + ## The `==` operator for ordered hash tables. Both the content and the order + ## must be equal for this to return ``true``. + result = s[] == t[] + proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x,y: (A, B)): int) = ## sorts `t` according to `cmp`. This modifies the internal list @@ -912,6 +939,11 @@ proc `$`*[A](t: CountTable[A]): string = ## The `$` operator for count tables. dollarImpl() +proc `==`*[A](s, t: CountTable[A]): bool = + ## The `==` operator for count tables. Returns ``true`` iff both tables + ## contain the same keys with the same count. Insert order does not matter. + equalsImpl(s, t) + proc inc*[A](t: var CountTable[A], key: A, val = 1) = ## increments `t[key]` by `val`. var index = rawGet(t, key) @@ -1036,6 +1068,11 @@ proc `$`*[A](t: CountTableRef[A]): string = ## The `$` operator for count tables. dollarImpl() +proc `==`*[A](s, t: CountTableRef[A]): bool = + ## The `==` operator for count tables. Returns ``true`` iff both tables + ## contain the same keys with the same count. Insert order does not matter. + result = s[] == t[] + proc inc*[A](t: CountTableRef[A], key: A, val = 1) = ## increments `t[key]` by `val`. t[].inc(key, val) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 27b3b46be..c56d13b57 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -8,79 +8,116 @@ # ## This module implements a simple HTTP client that can be used to retrieve -## webpages/other data. -## -## -## **Note**: This module is not ideal, connection is not kept alive so sites with -## many redirects are expensive. As such in the future this module may change, -## and the current procedures will be deprecated. +## webpages and other data. ## ## Retrieving a website ## ==================== ## ## This example uses HTTP GET to retrieve -## ``http://google.com`` +## ``http://google.com``: ## ## .. code-block:: Nim -## echo(getContent("http://google.com")) +## var client = newHttpClient() +## echo client.getContent("http://google.com") +## +## The same action can also be performed asynchronously, simply use the +## ``AsyncHttpClient``: +## +## .. code-block:: Nim +## var client = newAsyncHttpClient() +## echo await client.getContent("http://google.com") +## +## The functionality implemented by ``HttpClient`` and ``AsyncHttpClient`` +## is the same, so you can use whichever one suits you best in the examples +## shown here. +## +## **Note:** You will need to run asynchronous examples in an async proc +## otherwise you will get an ``Undeclared identifier: 'await'`` error. ## ## Using HTTP POST ## =============== ## ## This example demonstrates the usage of the W3 HTML Validator, it -## uses ``multipart/form-data`` as the ``Content-Type`` to send the HTML to -## the server. +## uses ``multipart/form-data`` as the ``Content-Type`` to send the HTML to be +## validated to the server. ## ## .. code-block:: Nim +## var client = newHttpClient() ## var data = newMultipartData() ## data["output"] = "soap12" ## data["uploaded_file"] = ("test.html", "text/html", ## "<html><head></head><body><p>test</p></body></html>") ## -## echo postContent("http://validator.w3.org/check", multipart=data) +## echo client.postContent("http://validator.w3.org/check", multipart=data) ## -## Asynchronous HTTP requests -## ========================== +## You can also make post requests with custom headers. +## This example sets ``Content-Type`` to ``application/json`` +## and uses a json object for the body ## -## You simply have to create a new instance of the ``AsyncHttpClient`` object. -## You may then use ``await`` on the functions defined for that object. -## Keep in mind that the following code needs to be inside an asynchronous -## procedure. +## .. code-block:: Nim +## import httpclient, json +## +## let client = newHttpClient() +## client.headers = newHttpHeaders({ "Content-Type": "application/json" }) +## let body = %*{ +## "data": "some text" +## } +## echo client.request("http://some.api", httpMethod = HttpPost, body = $body) +## +## Progress reporting +## ================== ## -## .. code-block::nim +## You may specify a callback procedure to be called during an HTTP request. +## This callback will be executed every second with information about the +## progress of the HTTP request. ## +## .. code-block:: Nim ## var client = newAsyncHttpClient() -## var resp = await client.request("http://google.com") +## proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} = +## echo("Downloaded ", progress, " of ", total) +## echo("Current rate: ", speed div 1000, "kb/s") +## client.onProgressChanged = onProgressChanged +## discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test") +## +## If you would like to remove the callback simply set it to ``nil``. +## +## .. code-block:: Nim +## client.onProgressChanged = nil ## ## SSL/TLS support ## =============== ## This requires the OpenSSL library, fortunately it's widely used and installed ## on many operating systems. httpclient will use SSL automatically if you give ## any of the functions a url with the ``https`` schema, for example: -## ``https://github.com/``, you also have to compile with ``ssl`` defined like so: +## ``https://github.com/``. +## +## You will also have to compile with ``ssl`` defined like so: ## ``nim c -d:ssl ...``. ## ## Timeouts ## ======== -## Currently all functions support an optional timeout, by default the timeout is set to -## `-1` which means that the function will never time out. The timeout is +## +## Currently only the synchronous functions support a timeout. +## The timeout is ## measured in milliseconds, once it is set any call on a socket which may -## block will be susceptible to this timeout, however please remember that the +## block will be susceptible to this timeout. +## +## It may be surprising but the ## function as a whole can take longer than the specified timeout, only ## individual internal calls on the socket are affected. In practice this means ## that as long as the server is sending data an exception will not be raised, -## if however data does not reach client within the specified timeout an ETimeout -## exception will then be raised. +## if however data does not reach the client within the specified timeout a +## ``TimeoutError`` exception will be raised. ## ## Proxy ## ===== ## -## A proxy can be specified as a param to any of these procedures, the ``newProxy`` -## constructor should be used for this purpose. However, -## currently only basic authentication is supported. +## A proxy can be specified as a param to any of the procedures defined in +## this module. To do this, use the ``newProxy`` constructor. Unfortunately, +## only basic authentication is supported at the moment. import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes, - math, random, httpcore + math, random, httpcore, times import asyncnet, asyncdispatch import nativesockets @@ -379,15 +416,18 @@ proc format(p: MultipartData): tuple[header, body: string] = proc request*(url: string, httpMethod: string, extraHeaders = "", body = "", sslContext = defaultSSLContext, timeout = -1, - userAgent = defUserAgent, proxy: Proxy = nil): Response = + userAgent = defUserAgent, proxy: Proxy = nil): Response + {.deprecated.} = ## | Requests ``url`` with the custom method string specified by the ## | ``httpMethod`` parameter. ## | Extra headers can be specified and must be separated by ``\c\L`` ## | An optional timeout can be specified in milliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. + ## + ## **Deprecated since version 0.15.0**: use ``HttpClient.request`` instead. var r = if proxy == nil: parseUri(url) else: proxy.url var hostUrl = if proxy == nil: r else: parseUri(url) - var headers = substr(httpMethod, len("http")).toUpper() + var headers = httpMethod.toUpper() # TODO: Use generateHeaders further down once it supports proxies. var s = newSocket() @@ -406,7 +446,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "", # get the socket ready. If we are connecting through a proxy to SSL, - # send the appropiate CONNECT header. If not, simply connect to the proper + # send the appropriate CONNECT header. If not, simply connect to the proper # host (which may still be the proxy, for normal HTTP) if proxy != nil and hostUrl.scheme == "https": when defined(ssl): @@ -471,15 +511,18 @@ proc request*(url: string, httpMethod: string, extraHeaders = "", if body != "": s.send(body) - result = parseResponse(s, httpMethod != "httpHEAD", timeout) + result = parseResponse(s, httpMethod != "HEAD", timeout) proc request*(url: string, httpMethod = httpGET, extraHeaders = "", body = "", sslContext = defaultSSLContext, timeout = -1, - userAgent = defUserAgent, proxy: Proxy = nil): Response = + userAgent = defUserAgent, proxy: Proxy = nil): Response + {.deprecated.} = ## | Requests ``url`` with the specified ``httpMethod``. ## | Extra headers can be specified and must be separated by ``\c\L`` ## | An optional timeout can be specified in milliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. + ## + ## **Deprecated since version 0.15.0**: use ``HttpClient.request`` instead. result = request(url, $httpMethod, extraHeaders, body, sslContext, timeout, userAgent, proxy) @@ -502,12 +545,14 @@ proc getNewLocation(lastURL: string, headers: HttpHeaders): string = proc get*(url: string, extraHeaders = "", maxRedirects = 5, sslContext: SSLContext = defaultSSLContext, timeout = -1, userAgent = defUserAgent, - proxy: Proxy = nil): Response = + proxy: Proxy = nil): Response {.deprecated.} = ## | GETs the ``url`` and returns a ``Response`` object ## | This proc also handles redirection ## | Extra headers can be specified and must be separated by ``\c\L``. ## | An optional timeout can be specified in milliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. + ## + ## ## **Deprecated since version 0.15.0**: use ``HttpClient.get`` instead. result = request(url, httpGET, extraHeaders, "", sslContext, timeout, userAgent, proxy) var lastURL = url @@ -521,12 +566,14 @@ proc get*(url: string, extraHeaders = "", maxRedirects = 5, proc getContent*(url: string, extraHeaders = "", maxRedirects = 5, sslContext: SSLContext = defaultSSLContext, timeout = -1, userAgent = defUserAgent, - proxy: Proxy = nil): string = + proxy: Proxy = nil): string {.deprecated.} = ## | GETs the body and returns it as a string. ## | Raises exceptions for the status codes ``4xx`` and ``5xx`` ## | Extra headers can be specified and must be separated by ``\c\L``. ## | An optional timeout can be specified in milliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. + ## + ## **Deprecated since version 0.15.0**: use ``HttpClient.getContent`` instead. var r = get(url, extraHeaders, maxRedirects, sslContext, timeout, userAgent, proxy) if r.status[0] in {'4','5'}: @@ -539,7 +586,7 @@ proc post*(url: string, extraHeaders = "", body = "", sslContext: SSLContext = defaultSSLContext, timeout = -1, userAgent = defUserAgent, proxy: Proxy = nil, - multipart: MultipartData = nil): Response = + multipart: MultipartData = nil): Response {.deprecated.} = ## | POSTs ``body`` to the ``url`` and returns a ``Response`` object. ## | This proc adds the necessary Content-Length header. ## | This proc also handles redirection. @@ -548,6 +595,8 @@ proc post*(url: string, extraHeaders = "", body = "", ## server takes longer than specified an ETimeout exception will be raised. ## | The optional ``multipart`` parameter can be used to create ## ``multipart/form-data`` POSTs comfortably. + ## + ## **Deprecated since version 0.15.0**: use ``HttpClient.post`` instead. let (mpHeaders, mpBody) = format(multipart) template withNewLine(x): expr = @@ -577,7 +626,8 @@ proc postContent*(url: string, extraHeaders = "", body = "", sslContext: SSLContext = defaultSSLContext, timeout = -1, userAgent = defUserAgent, proxy: Proxy = nil, - multipart: MultipartData = nil): string = + multipart: MultipartData = nil): string + {.deprecated.} = ## | POSTs ``body`` to ``url`` and returns the response's body as a string ## | Raises exceptions for the status codes ``4xx`` and ``5xx`` ## | Extra headers can be specified and must be separated by ``\c\L``. @@ -585,6 +635,9 @@ proc postContent*(url: string, extraHeaders = "", body = "", ## server takes longer than specified an ETimeout exception will be raised. ## | The optional ``multipart`` parameter can be used to create ## ``multipart/form-data`` POSTs comfortably. + ## + ## **Deprecated since version 0.15.0**: use ``HttpClient.postContent`` + ## instead. var r = post(url, extraHeaders, body, maxRedirects, sslContext, timeout, userAgent, proxy, multipart) if r.status[0] in {'4','5'}: @@ -610,7 +663,7 @@ proc downloadFile*(url: string, outputFilename: string, proc generateHeaders(requestUrl: Uri, httpMethod: string, headers: HttpHeaders, body: string, proxy: Proxy): string = # GET - result = substr(httpMethod, len("http")).toUpper() + result = httpMethod.toUpper() result.add ' ' if proxy.isNil: @@ -653,17 +706,30 @@ proc generateHeaders(requestUrl: Uri, httpMethod: string, add(result, "\c\L") type + ProgressChangedProc*[ReturnType] = + proc (total, progress, speed: BiggestInt): + ReturnType {.closure, gcsafe.} + HttpClientBase*[SocketType] = ref object socket: SocketType connected: bool currentURL: Uri ## Where we are currently connected. - headers*: HttpHeaders + headers*: HttpHeaders ## Headers to send in requests. maxRedirects: int userAgent: string timeout: int ## Only used for blocking HttpClient for now. proxy: Proxy + ## ``nil`` or the callback to call when request progress changes. + when SocketType is Socket: + onProgressChanged*: ProgressChangedProc[void] + else: + onProgressChanged*: ProgressChangedProc[Future[void]] when defined(ssl): sslContext: net.SslContext + contentTotal: BiggestInt + contentProgress: BiggestInt + oneSecondProgress: BiggestInt + lastProgressReport: float type HttpClient* = HttpClientBase[Socket] @@ -684,7 +750,7 @@ proc newHttpClient*(userAgent = defUserAgent, ## ``proxy`` specifies an HTTP proxy to use for this HTTP client's ## connections. ## - ## ``timeout`` specifies the number of miliseconds to allow before a + ## ``timeout`` specifies the number of milliseconds to allow before a ## ``TimeoutError`` is raised. new result result.headers = newHttpHeaders() @@ -692,6 +758,7 @@ proc newHttpClient*(userAgent = defUserAgent, result.maxRedirects = maxRedirects result.proxy = proxy result.timeout = timeout + result.onProgressChanged = nil when defined(ssl): result.sslContext = sslContext @@ -721,6 +788,7 @@ proc newAsyncHttpClient*(userAgent = defUserAgent, result.maxRedirects = maxRedirects result.proxy = proxy result.timeout = -1 # TODO + result.onProgressChanged = nil when defined(ssl): result.sslContext = sslContext @@ -730,19 +798,37 @@ proc close*(client: HttpClient | AsyncHttpClient) = client.socket.close() client.connected = false -proc recvFull(socket: Socket | AsyncSocket, +proc reportProgress(client: HttpClient | AsyncHttpClient, + progress: BiggestInt) {.multisync.} = + client.contentProgress += progress + client.oneSecondProgress += progress + if epochTime() - client.lastProgressReport >= 1.0: + if not client.onProgressChanged.isNil: + await client.onProgressChanged(client.contentTotal, + client.contentProgress, + client.oneSecondProgress) + client.oneSecondProgress = 0 + client.lastProgressReport = epochTime() + +proc recvFull(client: HttpClient | AsyncHttpClient, size: int, timeout: int): Future[string] {.multisync.} = ## Ensures that all the data requested is read and returned. result = "" while true: if size == result.len: break - when socket is Socket: - let data = socket.recv(size - result.len, timeout) + + let remainingSize = size - result.len + let sizeToRecv = min(remainingSize, net.BufferSize) + + when client.socket is Socket: + let data = client.socket.recv(sizeToRecv, timeout) else: - let data = await socket.recv(size - result.len) + let data = await client.socket.recv(sizeToRecv) if data == "": break # We've been disconnected. result.add data + await reportProgress(client, data.len) + proc parseChunks(client: HttpClient | AsyncHttpClient): Future[string] {.multisync.} = result = "" @@ -770,10 +856,10 @@ proc parseChunks(client: HttpClient | AsyncHttpClient): Future[string] httpError("Invalid chunk size: " & chunkSizeStr) inc(i) if chunkSize <= 0: - discard await recvFull(client.socket, 2, client.timeout) # Skip \c\L + discard await recvFull(client, 2, client.timeout) # Skip \c\L break - result.add await recvFull(client.socket, chunkSize, client.timeout) - discard await recvFull(client.socket, 2, client.timeout) # Skip \c\L + result.add await recvFull(client, chunkSize, client.timeout) + discard await recvFull(client, 2, client.timeout) # Skip \c\L # Trailer headers will only be sent if the request specifies that we want # them: http://tools.ietf.org/html/rfc2616#section-3.6.1 @@ -781,6 +867,12 @@ proc parseBody(client: HttpClient | AsyncHttpClient, headers: HttpHeaders, httpVersion: string): Future[string] {.multisync.} = result = "" + # Reset progress from previous requests. + client.contentTotal = 0 + client.contentProgress = 0 + client.oneSecondProgress = 0 + client.lastProgressReport = 0 + if headers.getOrDefault"Transfer-Encoding" == "chunked": result = await parseChunks(client) else: @@ -789,8 +881,9 @@ proc parseBody(client: HttpClient | AsyncHttpClient, var contentLengthHeader = headers.getOrDefault"Content-Length" if contentLengthHeader != "": var length = contentLengthHeader.parseint() + client.contentTotal = length if length > 0: - result = await client.socket.recvFull(length, client.timeout) + result = await client.recvFull(length, client.timeout) if result == "": httpError("Got disconnected while trying to read body.") if result.len != length: @@ -804,7 +897,7 @@ proc parseBody(client: HttpClient | AsyncHttpClient, if headers.getOrDefault"Connection" == "close" or httpVersion == "1.0": var buf = "" while true: - buf = await client.socket.recvFull(4000, client.timeout) + buf = await client.recvFull(4000, client.timeout) if buf == "": break result.add(buf) @@ -865,8 +958,10 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, proc newConnection(client: HttpClient | AsyncHttpClient, url: Uri) {.multisync.} = if client.currentURL.hostname != url.hostname or - client.currentURL.scheme != url.scheme: - if client.connected: client.close() + client.currentURL.scheme != url.scheme or + client.currentURL.port != url.port: + if client.connected: + client.close() when client is HttpClient: client.socket = newSocket() @@ -902,8 +997,6 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string, ## Connection will kept alive. Further requests on the same ``client`` to ## the same hostname will not require a new connection to be made. The ## connection can be closed by using the ``close`` procedure. - ## - ## The returned future will complete once the request is completed. let connectionUrl = if client.proxy.isNil: parseUri(url) else: client.proxy.url let requestUrl = parseUri(url) @@ -933,14 +1026,15 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string, if not client.headers.hasKey("user-agent") and client.userAgent != "": client.headers["User-Agent"] = client.userAgent - var headers = generateHeaders(requestUrl, $httpMethod, + var headers = generateHeaders(requestUrl, httpMethod, client.headers, body, client.proxy) await client.socket.send(headers) if body != "": await client.socket.send(body) - result = await parseResponse(client, httpMethod notin {HttpHead, HttpConnect}) + result = await parseResponse(client, + httpMethod.toLower() notin ["head", "connect"]) # Restore the clients proxy in case it was overwritten. client.proxy = savedProxy @@ -950,11 +1044,12 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string, ## Connects to the hostname specified by the URL and performs a request ## using the method specified. ## - ## Connection will kept alive. Further requests on the same ``client`` to + ## Connection will be kept alive. Further requests on the same ``client`` to ## the same hostname will not require a new connection to be made. The ## connection can be closed by using the ``close`` procedure. ## - ## The returned future will complete once the request is completed. + ## When a request is made to a different hostname, the current connection will + ## be closed. result = await request(client, url, $httpMethod, body) proc get*(client: HttpClient | AsyncHttpClient, @@ -964,6 +1059,8 @@ proc get*(client: HttpClient | AsyncHttpClient, ## This procedure will follow redirects up to a maximum number of redirects ## specified in ``client.maxRedirects``. result = await client.request(url, HttpGET) + + # Handle redirects. var lastURL = url for i in 1..client.maxRedirects: if result.status.redirection(): @@ -971,6 +1068,21 @@ proc get*(client: HttpClient | AsyncHttpClient, result = await client.request(redirectTo, HttpGET) lastURL = redirectTo +proc getContent*(client: HttpClient | AsyncHttpClient, + url: string): Future[string] {.multisync.} = + ## Connects to the hostname specified by the URL and performs a GET request. + ## + ## This procedure will follow redirects up to a maximum number of redirects + ## specified in ``client.maxRedirects``. + ## + ## A ``HttpRequestError`` will be raised if the server responds with a + ## client error (status code 4xx) or a server error (status code 5xx). + let resp = await get(client, url) + if resp.code.is4xx or resp.code.is5xx: + raise newException(HttpRequestError, resp.status) + else: + return resp.body + proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "", multipart: MultipartData = nil): Future[Response] {.multisync.} = ## Connects to the hostname specified by the URL and performs a POST request. @@ -990,3 +1102,28 @@ proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "", client.headers["Content-Length"] = $len(xb) result = await client.request(url, HttpPOST, xb) + # Handle redirects. + var lastURL = url + for i in 1..client.maxRedirects: + if result.status.redirection(): + let redirectTo = getNewLocation(lastURL, result.headers) + var meth = if result.status != "307": HttpGet else: HttpPost + result = await client.request(redirectTo, meth, xb) + lastURL = redirectTo + +proc postContent*(client: HttpClient | AsyncHttpClient, url: string, + body = "", + multipart: MultipartData = nil): Future[string] + {.multisync.} = + ## Connects to the hostname specified by the URL and performs a POST request. + ## + ## This procedure will follow redirects up to a maximum number of redirects + ## specified in ``client.maxRedirects``. + ## + ## A ``HttpRequestError`` will be raised if the server responds with a + ## client error (status code 4xx) or a server error (status code 5xx). + let resp = await post(client, url, body, multipart) + if resp.code.is4xx or resp.code.is5xx: + raise newException(HttpRequestError, resp.status) + else: + return resp.body diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index ba69c5669..8147f1c50 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -41,8 +41,9 @@ type ## changing in the request. HttpOptions, ## Returns the HTTP methods that the server supports ## for specified address. - HttpConnect ## Converts the request connection to a transparent + HttpConnect, ## Converts the request connection to a transparent ## TCP/IP tunnel, usually used for proxies. + HttpPatch ## Applies partial modifications to a resource. {.deprecated: [httpGet: HttpGet, httpHead: HttpHead, httpPost: HttpPost, httpPut: HttpPut, httpDelete: HttpDelete, httpTrace: HttpTrace, @@ -224,7 +225,7 @@ proc `$`*(code: HttpCode): string = ## For example: ## ## .. code-block:: nim - ## doAssert(Http404.status == "404 Not Found") + ## doAssert($Http404 == "404 Not Found") case code.int of 100: "100 Continue" of 101: "101 Switching Protocols" @@ -296,6 +297,9 @@ proc is5xx*(code: HttpCode): bool = ## Determines whether ``code`` is a 5xx HTTP status code. return code.int in {500 .. 599} +proc `$`*(httpMethod: HttpMethod): string = + return (system.`$`(httpMethod))[4 .. ^1].toUpper() + when isMainModule: var test = newHttpHeaders() test["Connection"] = @["Upgrade", "Close"] diff --git a/lib/pure/includes/asyncfutures.nim b/lib/pure/includes/asyncfutures.nim new file mode 100644 index 000000000..d78464a91 --- /dev/null +++ b/lib/pure/includes/asyncfutures.nim @@ -0,0 +1,295 @@ + +# TODO: This shouldn't need to be included, but should ideally be exported. +type + FutureBase* = ref object of RootObj ## Untyped future. + cb: proc () {.closure,gcsafe.} + finished: bool + error*: ref Exception ## Stored exception + errorStackTrace*: string + when not defined(release): + stackTrace: string ## For debugging purposes only. + id: int + fromProc: string + + Future*[T] = ref object of FutureBase ## Typed future. + value: T ## Stored value + + FutureVar*[T] = distinct Future[T] + + FutureError* = object of Exception + cause*: FutureBase + +{.deprecated: [PFutureBase: FutureBase, PFuture: Future].} + +when not defined(release): + var currentID = 0 + +proc callSoon*(cbproc: proc ()) {.gcsafe.} + +proc newFuture*[T](fromProc: string = "unspecified"): Future[T] = + ## Creates a new future. + ## + ## Specifying ``fromProc``, which is a string specifying the name of the proc + ## that this future belongs to, is a good habit as it helps with debugging. + new(result) + result.finished = false + when not defined(release): + result.stackTrace = getStackTrace() + result.id = currentID + result.fromProc = fromProc + currentID.inc() + +proc newFutureVar*[T](fromProc = "unspecified"): FutureVar[T] = + ## Create a new ``FutureVar``. This Future type is ideally suited for + ## situations where you want to avoid unnecessary allocations of Futures. + ## + ## Specifying ``fromProc``, which is a string specifying the name of the proc + ## that this future belongs to, is a good habit as it helps with debugging. + result = FutureVar[T](newFuture[T](fromProc)) + +proc clean*[T](future: FutureVar[T]) = + ## Resets the ``finished`` status of ``future``. + Future[T](future).finished = false + Future[T](future).error = nil + +proc checkFinished[T](future: Future[T]) = + ## Checks whether `future` is finished. If it is then raises a + ## ``FutureError``. + when not defined(release): + if future.finished: + var msg = "" + msg.add("An attempt was made to complete a Future more than once. ") + msg.add("Details:") + msg.add("\n Future ID: " & $future.id) + msg.add("\n Created in proc: " & future.fromProc) + msg.add("\n Stack trace to moment of creation:") + msg.add("\n" & indent(future.stackTrace.strip(), 4)) + when T is string: + msg.add("\n Contents (string): ") + msg.add("\n" & indent(future.value.repr, 4)) + msg.add("\n Stack trace to moment of secondary completion:") + msg.add("\n" & indent(getStackTrace().strip(), 4)) + var err = newException(FutureError, msg) + err.cause = future + raise err + +proc complete*[T](future: Future[T], val: T) = + ## Completes ``future`` with value ``val``. + #assert(not future.finished, "Future already finished, cannot finish twice.") + checkFinished(future) + assert(future.error == nil) + future.value = val + future.finished = true + if future.cb != nil: + future.cb() + +proc complete*(future: Future[void]) = + ## Completes a void ``future``. + #assert(not future.finished, "Future already finished, cannot finish twice.") + checkFinished(future) + assert(future.error == nil) + future.finished = true + if future.cb != nil: + future.cb() + +proc complete*[T](future: FutureVar[T]) = + ## Completes a ``FutureVar``. + template fut: expr = Future[T](future) + checkFinished(fut) + assert(fut.error == nil) + fut.finished = true + if fut.cb != nil: + fut.cb() + +proc complete*[T](future: FutureVar[T], val: T) = + ## Completes a ``FutureVar`` with value ``val``. + ## + ## Any previously stored value will be overwritten. + template fut: expr = Future[T](future) + checkFinished(fut) + assert(fut.error == nil) + fut.finished = true + fut.value = val + if fut.cb != nil: + fut.cb() + +proc fail*[T](future: Future[T], error: ref Exception) = + ## Completes ``future`` with ``error``. + #assert(not future.finished, "Future already finished, cannot finish twice.") + checkFinished(future) + future.finished = true + future.error = error + future.errorStackTrace = + if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error) + if future.cb != nil: + future.cb() + else: + # This is to prevent exceptions from being silently ignored when a future + # is discarded. + # TODO: This may turn out to be a bad idea. + # Turns out this is a bad idea. + #raise error + discard + +proc `callback=`*(future: FutureBase, cb: proc () {.closure,gcsafe.}) = + ## Sets the callback proc to be called when the future completes. + ## + ## If future has already completed then ``cb`` will be called immediately. + ## + ## **Note**: You most likely want the other ``callback`` setter which + ## passes ``future`` as a param to the callback. + future.cb = cb + if future.finished: + callSoon(future.cb) + +proc `callback=`*[T](future: Future[T], + cb: proc (future: Future[T]) {.closure,gcsafe.}) = + ## Sets the callback proc to be called when the future completes. + ## + ## If future has already completed then ``cb`` will be called immediately. + future.callback = proc () = cb(future) + +proc injectStacktrace[T](future: Future[T]) = + # TODO: Come up with something better. + when not defined(release): + var msg = "" + msg.add("\n " & future.fromProc & "'s lead up to read of failed Future:") + + if not future.errorStackTrace.isNil and future.errorStackTrace != "": + msg.add("\n" & indent(future.errorStackTrace.strip(), 4)) + else: + msg.add("\n Empty or nil stack trace.") + future.error.msg.add(msg) + +proc read*[T](future: Future[T] | FutureVar[T]): T = + ## Retrieves the value of ``future``. Future must be finished otherwise + ## this function will fail with a ``ValueError`` exception. + ## + ## If the result of the future is an error then that error will be raised. + {.push hint[ConvFromXtoItselfNotNeeded]: off.} + let fut = Future[T](future) + {.pop.} + if fut.finished: + if fut.error != nil: + injectStacktrace(fut) + raise fut.error + when T isnot void: + return fut.value + else: + # TODO: Make a custom exception type for this? + raise newException(ValueError, "Future still in progress.") + +proc readError*[T](future: Future[T]): ref Exception = + ## Retrieves the exception stored in ``future``. + ## + ## An ``ValueError`` exception will be thrown if no exception exists + ## in the specified Future. + if future.error != nil: return future.error + else: + raise newException(ValueError, "No error in future.") + +proc mget*[T](future: FutureVar[T]): var T = + ## Returns a mutable value stored in ``future``. + ## + ## Unlike ``read``, this function will not raise an exception if the + ## Future has not been finished. + result = Future[T](future).value + +proc finished*[T](future: Future[T] | FutureVar[T]): bool = + ## Determines whether ``future`` has completed. + ## + ## ``True`` may indicate an error or a value. Use ``failed`` to distinguish. + (Future[T](future)).finished + +proc failed*(future: FutureBase): bool = + ## Determines whether ``future`` completed with an error. + return future.error != nil + +proc asyncCheck*[T](future: Future[T]) = + ## Sets a callback on ``future`` which raises an exception if the future + ## finished with an error. + ## + ## This should be used instead of ``discard`` to discard void futures. + future.callback = + proc () = + if future.failed: + injectStacktrace(future) + raise future.error + +proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = + ## Returns a future which will complete once both ``fut1`` and ``fut2`` + ## complete. + var retFuture = newFuture[void]("asyncdispatch.`and`") + fut1.callback = + proc () = + if not retFuture.finished: + if fut1.failed: retFuture.fail(fut1.error) + elif fut2.finished: retFuture.complete() + fut2.callback = + proc () = + if not retFuture.finished: + if fut2.failed: retFuture.fail(fut2.error) + elif fut1.finished: retFuture.complete() + return retFuture + +proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = + ## Returns a future which will complete once either ``fut1`` or ``fut2`` + ## complete. + var retFuture = newFuture[void]("asyncdispatch.`or`") + proc cb[X](fut: Future[X]) = + if fut.failed: retFuture.fail(fut.error) + if not retFuture.finished: retFuture.complete() + fut1.callback = cb[T] + fut2.callback = cb[Y] + return retFuture + +proc all*[T](futs: varargs[Future[T]]): auto = + ## Returns a future which will complete once + ## all futures in ``futs`` complete. + ## + ## If the awaited futures are not ``Future[void]``, the returned future + ## will hold the values of all awaited futures in a sequence. + ## + ## If the awaited futures *are* ``Future[void]``, + ## this proc returns ``Future[void]``. + + when T is void: + var + retFuture = newFuture[void]("asyncdispatch.all") + completedFutures = 0 + + let totalFutures = len(futs) + + for fut in futs: + fut.callback = proc(f: Future[T]) = + if f.failed: + retFuture.fail(f.error) + elif not retFuture.finished: + inc(completedFutures) + + if completedFutures == totalFutures: + retFuture.complete() + + return retFuture + + else: + var + retFuture = newFuture[seq[T]]("asyncdispatch.all") + retValues = newSeq[T](len(futs)) + completedFutures = 0 + + for i, fut in futs: + proc setCallback(i: int) = + fut.callback = proc(f: Future[T]) = + if f.failed: + retFuture.fail(f.error) + elif not retFuture.finished: + retValues[i] = f.read() + inc(completedFutures) + + if completedFutures == len(retValues): + retFuture.complete(retValues) + + setCallback(i) + + return retFuture diff --git a/lib/pure/ioselectors.nim b/lib/pure/ioselectors.nim index a5d5d2c01..744bdbaa1 100644 --- a/lib/pure/ioselectors.nim +++ b/lib/pure/ioselectors.nim @@ -44,14 +44,21 @@ when defined(nimdoc): Event* {.pure.} = enum ## An enum which hold event types - Read, ## Descriptor is available for read - Write, ## Descriptor is available for write - Timer, ## Timer descriptor is completed - Signal, ## Signal is raised - Process, ## Process is finished - Vnode, ## Currently not supported - User, ## User event is raised - Error ## Error happens while waiting, for descriptor + Read, ## Descriptor is available for read + Write, ## Descriptor is available for write + Timer, ## Timer descriptor is completed + Signal, ## Signal is raised + Process, ## Process is finished + Vnode, ## BSD specific file change happens + User, ## User event is raised + Error, ## Error happens while waiting, for descriptor + VnodeWrite, ## NOTE_WRITE (BSD specific, write to file occurred) + VnodeDelete, ## NOTE_DELETE (BSD specific, unlink of file occurred) + VnodeExtend, ## NOTE_EXTEND (BSD specific, file extended) + VnodeAttrib, ## NOTE_ATTRIB (BSD specific, file attributes changed) + VnodeLink, ## NOTE_LINK (BSD specific, file link count changed) + VnodeRename, ## NOTE_RENAME (BSD specific, file renamed) + VnodeRevoke ## NOTE_REVOKE (BSD specific, file revoke occurred) ReadyKey*[T] = object ## An object which holds result for descriptor @@ -107,6 +114,15 @@ when defined(nimdoc): ## ``data`` application-defined data, which to be passed, when ## ``ev`` happens. + proc registerVnode*[T](s: Selector[T], fd: cint, events: set[Event], + data: T) = + ## Registers selector BSD/MacOSX specific vnode events for file + ## descriptor ``fd`` and events ``events``. + ## ``data`` application-defined data, which to be passed, when + ## vnode event happens. + ## + ## This function is supported only by BSD and MacOSX. + proc newSelectEvent*(): SelectEvent = ## Creates new event ``SelectEvent``. @@ -124,7 +140,7 @@ when defined(nimdoc): proc flush*[T](s: Selector[T]) = ## Flushes all changes was made to kernel pool/queue. - ## This function is usefull only for BSD and MacOS, because + ## This function is useful only for BSD and MacOS, because ## kqueue supports bulk changes to be made. ## On Linux/Windows and other Posix compatible operation systems, ## ``flush`` is alias for `discard`. @@ -194,7 +210,9 @@ else: deallocShared(cast[pointer](sa)) type Event* {.pure.} = enum - Read, Write, Timer, Signal, Process, Vnode, User, Error, Oneshot + Read, Write, Timer, Signal, Process, Vnode, User, Error, Oneshot, + VnodeWrite, VnodeDelete, VnodeExtend, VnodeAttrib, VnodeLink, + VnodeRename, VnodeRevoke ReadyKey*[T] = object fd* : int diff --git a/lib/pure/ioselects/ioselectors_kqueue.nim b/lib/pure/ioselects/ioselectors_kqueue.nim index 3e86f19aa..3c0cf4e90 100644 --- a/lib/pure/ioselects/ioselectors_kqueue.nim +++ b/lib/pure/ioselects/ioselectors_kqueue.nim @@ -26,8 +26,8 @@ when defined(macosx) or defined(freebsd): const MAX_DESCRIPTORS_ID = 29 # KERN_MAXFILESPERPROC (MacOS) else: const MAX_DESCRIPTORS_ID = 27 # KERN_MAXFILESPERPROC (FreeBSD) - proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr int, - newp: pointer, newplen: int): cint + proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize, + newp: pointer, newplen: csize): cint {.importc: "sysctl",header: """#include <sys/types.h> #include <sys/sysctl.h>"""} elif defined(netbsd) or defined(openbsd): @@ -35,8 +35,8 @@ elif defined(netbsd) or defined(openbsd): # KERN_MAXFILES, because KERN_MAXFILES is always bigger, # than KERN_MAXFILESPERPROC. const MAX_DESCRIPTORS_ID = 7 # KERN_MAXFILES - proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr int, - newp: pointer, newplen: int): cint + proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize, + newp: pointer, newplen: csize): cint {.importc: "sysctl",header: """#include <sys/param.h> #include <sys/sysctl.h>"""} @@ -72,7 +72,7 @@ type SelectEvent* = ptr SelectEventImpl proc newSelector*[T](): Selector[T] = var maxFD = 0.cint - var size = sizeof(cint) + var size = csize(sizeof(cint)) var namearr = [1.cint, MAX_DESCRIPTORS_ID.cint] # Obtain maximum number of file descriptors for process if sysctl(addr(namearr[0]), 2, cast[pointer](addr maxFD), addr size, @@ -262,6 +262,30 @@ proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) = modifyKQueue(s, fdi.uint, EVFILT_READ, EV_ADD, 0, 0, nil) inc(s.count) +template processVnodeEvents(events: set[Event]): cuint = + var rfflags = 0.cuint + if events == {Event.VnodeWrite, Event.VnodeDelete, Event.VnodeExtend, + Event.VnodeAttrib, Event.VnodeLink, Event.VnodeRename, + Event.VnodeRevoke}: + rfflags = NOTE_DELETE or NOTE_WRITE or NOTE_EXTEND or NOTE_ATTRIB or + NOTE_LINK or NOTE_RENAME or NOTE_REVOKE + else: + if Event.VnodeDelete in events: rfflags = rfflags or NOTE_DELETE + if Event.VnodeWrite in events: rfflags = rfflags or NOTE_WRITE + if Event.VnodeExtend in events: rfflags = rfflags or NOTE_EXTEND + if Event.VnodeAttrib in events: rfflags = rfflags or NOTE_ATTRIB + if Event.VnodeLink in events: rfflags = rfflags or NOTE_LINK + if Event.VnodeRename in events: rfflags = rfflags or NOTE_RENAME + if Event.VnodeRevoke in events: rfflags = rfflags or NOTE_REVOKE + rfflags + +proc registerVnode*[T](s: Selector[T], fd: cint, events: set[Event], data: T) = + let fdi = fd.int + setKey(s, fdi, fdi, {Event.Vnode} + events, 0, data) + var fflags = processVnodeEvents(events) + modifyKQueue(s, fdi.uint, EVFILT_VNODE, EV_ADD or EV_CLEAR, fflags, 0, nil) + inc(s.count) + proc unregister*[T](s: Selector[T], fd: int|SocketHandle) = let fdi = int(fd) s.checkFd(fdi) @@ -295,6 +319,9 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) = discard posix.close(cint(pkey.key.fd)) modifyKQueue(s, fdi.uint, EVFILT_PROC, EV_DELETE, 0, 0, nil) dec(s.count) + elif Event.Vnode in pkey.events: + modifyKQueue(s, fdi.uint, EVFILT_VNODE, EV_DELETE, 0, 0, nil) + dec(s.count) elif Event.User in pkey.events: modifyKQueue(s, fdi.uint, EVFILT_READ, EV_DELETE, 0, 0, nil) dec(s.count) @@ -392,6 +419,20 @@ proc selectInto*[T](s: Selector[T], timeout: int, of EVFILT_VNODE: pkey = addr(s.fds[kevent.ident.int]) pkey.key.events = {Event.Vnode} + if (kevent.fflags and NOTE_DELETE) != 0: + pkey.key.events.incl(Event.VnodeDelete) + if (kevent.fflags and NOTE_WRITE) != 0: + pkey.key.events.incl(Event.VnodeWrite) + if (kevent.fflags and NOTE_EXTEND) != 0: + pkey.key.events.incl(Event.VnodeExtend) + if (kevent.fflags and NOTE_ATTRIB) != 0: + pkey.key.events.incl(Event.VnodeAttrib) + if (kevent.fflags and NOTE_LINK) != 0: + pkey.key.events.incl(Event.VnodeLink) + if (kevent.fflags and NOTE_RENAME) != 0: + pkey.key.events.incl(Event.VnodeRename) + if (kevent.fflags and NOTE_REVOKE) != 0: + pkey.key.events.incl(Event.VnodeRevoke) of EVFILT_SIGNAL: pkey = addr(s.fds[cast[int](kevent.udata)]) pkey.key.events = {Event.Signal} diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 7ad7efd23..0b7908c02 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -24,6 +24,8 @@ ## jobj["test"] = newJFloat(0.7) # create or update ## echo($jobj["test"].fnum) ## echo($jobj["key2"].bval) +## echo jobj{"missing key"}.getFNum(0.1) # read a float value using a default +## jobj{"a", "b", "c"} = newJFloat(3.3) # created nested keys ## ## Results in: ## diff --git a/lib/pure/net.nim b/lib/pure/net.nim index a70f60a8e..d4f239c49 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -112,6 +112,7 @@ else: const BufferSize*: int = 4000 ## size of a buffered socket's buffer + MaxLineLength* = 1_000_000 type SocketImpl* = object ## socket type @@ -1006,7 +1007,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} = result = recv(socket.fd, addr(c), 1, MSG_PEEK) proc readLine*(socket: Socket, line: var TaintedString, timeout = -1, - flags = {SocketFlag.SafeDisconn}) {. + flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength) {. tags: [ReadIOEffect, TimeEffect].} = ## Reads a line of data from ``socket``. ## @@ -1021,6 +1022,10 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1, ## A timeout can be specified in milliseconds, if data is not received within ## the specified time an ETimeout exception will be raised. ## + ## The ``maxLength`` parameter determines the maximum amount of characters + ## that can be read before a ``ValueError`` is raised. This prevents Denial + ## of Service (DOS) attacks. + ## ## **Warning**: Only the ``SafeDisconn`` flag is currently supported. template addNLIfEmpty() = @@ -1054,8 +1059,15 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1, return add(line.string, c) + # Verify that this isn't a DOS attack: #3847. + if line.string.len > maxLength: + let msg = "recvLine received more than the specified `maxLength` " & + "allowed." + raise newException(ValueError, msg) + proc recvLine*(socket: Socket, timeout = -1, - flags = {SocketFlag.SafeDisconn}): TaintedString = + flags = {SocketFlag.SafeDisconn}, + maxLength = MaxLineLength): TaintedString = ## Reads a line of data from ``socket``. ## ## If a full line is read ``\r\L`` is not @@ -1069,9 +1081,13 @@ proc recvLine*(socket: Socket, timeout = -1, ## A timeout can be specified in milliseconds, if data is not received within ## the specified time an ETimeout exception will be raised. ## + ## The ``maxLength`` parameter determines the maximum amount of characters + ## that can be read before a ``ValueError`` is raised. This prevents Denial + ## of Service (DOS) attacks. + ## ## **Warning**: Only the ``SafeDisconn`` flag is currently supported. result = "" - readLine(socket, result, timeout, flags) + readLine(socket, result, timeout, flags, maxLength) proc recvFrom*(socket: Socket, data: var string, length: int, address: var string, port: var Port, flags = 0'i32): int {. diff --git a/lib/pure/os.nim b/lib/pure/os.nim index cdbe170cc..001d3d250 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -50,6 +50,8 @@ proc c_getenv(env: cstring): cstring {. importc: "getenv", header: "<stdlib.h>".} proc c_putenv(env: cstring): cint {. importc: "putenv", header: "<stdlib.h>".} +proc c_free(p: pointer) {. + importc: "free", header: "<stdlib.h>".} var errno {.importc, header: "<errno.h>".}: cint @@ -303,24 +305,47 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1".} = proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} = ## Returns the `current working directory`:idx:. - const bufsize = 512 # should be enough when defined(windows): + var bufsize = MAX_PATH.int32 when useWinUnicode: var res = newWideCString("", bufsize) - var L = getCurrentDirectoryW(bufsize, res) - if L == 0'i32: raiseOSError(osLastError()) - result = res$L + while true: + var L = getCurrentDirectoryW(bufsize, res) + if L == 0'i32: + raiseOSError(osLastError()) + elif L > bufsize: + res = newWideCString("", L) + bufsize = L + else: + result = res$L + break else: result = newString(bufsize) - var L = getCurrentDirectoryA(bufsize, result) - if L == 0'i32: raiseOSError(osLastError()) - setLen(result, L) + while true: + var L = getCurrentDirectoryA(bufsize, result) + if L == 0'i32: + raiseOSError(osLastError()) + elif L > bufsize: + result = newString(L) + bufsize = L + else: + setLen(result, L) + break else: + var bufsize = 1024 # should be enough result = newString(bufsize) - if getcwd(result, bufsize) != nil: - setLen(result, c_strlen(result)) - else: - raiseOSError(osLastError()) + while true: + if getcwd(result, bufsize) != nil: + setLen(result, c_strlen(result)) + break + else: + let err = osLastError() + if err.int32 == ERANGE: + bufsize = bufsize shl 1 + doAssert(bufsize >= 0) + result = newString(bufsize) + else: + raiseOSError(osLastError()) proc setCurrentDir*(newDir: string) {.inline, tags: [].} = ## Sets the `current working directory`:idx:; `OSError` is raised if @@ -336,28 +361,45 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [].} = proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", tags: [ReadDirEffect].} = - ## Returns the full (`absolute`:idx:) path of the file `filename`, raises OSError in case of an error. + ## Returns the full (`absolute`:idx:) path of the file `filename`, + ## raises OSError in case of an error. when defined(windows): - const bufsize = 3072'i32 + var bufsize = MAX_PATH.int32 when useWinUnicode: - var unused: WideCString - var res = newWideCString("", bufsize div 2) - var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused) - if L <= 0'i32 or L >= bufsize: - raiseOSError(osLastError()) - result = res$L + var unused: WideCString = nil + var res = newWideCString("", bufsize) + while true: + var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused) + if L == 0'i32: + raiseOSError(osLastError()) + elif L > bufsize: + res = newWideCString("", L) + bufsize = L + else: + result = res$L + break else: - var unused: cstring + var unused: cstring = nil result = newString(bufsize) - var L = getFullPathNameA(filename, bufsize, result, unused) - if L <= 0'i32 or L >= bufsize: raiseOSError(osLastError()) - setLen(result, L) + while true: + var L = getFullPathNameA(filename, bufsize, result, unused) + if L == 0'i32: + raiseOSError(osLastError()) + elif L > bufsize: + result = newString(L) + bufsize = L + else: + setLen(result, L) + break else: - # careful, realpath needs to take an allocated buffer according to Posix: - result = newString(pathMax) - var r = realpath(filename, result) - if r.isNil: raiseOSError(osLastError()) - setLen(result, c_strlen(result)) + # according to Posix we don't need to allocate space for result pathname. + # But we need to free return value with free(3). + var r = realpath(filename, nil) + if r.isNil: + raiseOSError(osLastError()) + else: + result = $r + c_free(cast[pointer](r)) when defined(Windows): proc openHandle(path: string, followSymlink=true): Handle = @@ -1382,6 +1424,35 @@ when declared(paramCount) or defined(nimdoc): for i in 1..paramCount(): result.add(paramStr(i)) +when defined(freebsd): + proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize, + newp: pointer, newplen: csize): cint + {.importc: "sysctl",header: """#include <sys/types.h> + #include <sys/sysctl.h>"""} + const + CTL_KERN = 1 + KERN_PROC = 14 + KERN_PROC_PATHNAME = 12 + MAX_PATH = 1024 + + proc getApplFreebsd(): string = + var pathLength = csize(MAX_PATH) + result = newString(pathLength) + var req = [CTL_KERN.cint, KERN_PROC.cint, KERN_PROC_PATHNAME.cint, -1.cint] + while true: + let res = sysctl(addr req[0], 4, cast[pointer](addr result[0]), + addr pathLength, nil, 0) + if res < 0: + let err = osLastError() + if err.int32 == ENOMEM: + result = newString(pathLength) + else: + result.setLen(0) # error! + break + else: + result.setLen(pathLength) + break + when defined(linux) or defined(solaris) or defined(bsd) or defined(aix): proc getApplAux(procPath: string): string = result = newString(256) @@ -1426,16 +1497,34 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = # Solaris: # /proc/<pid>/object/a.out (filename only) # /proc/<pid>/path/a.out (complete pathname) - # FreeBSD: /proc/<pid>/file when defined(windows): + var bufsize = int32(MAX_PATH) when useWinUnicode: - var buf = newWideCString("", 256) - var len = getModuleFileNameW(0, buf, 256) - result = buf$len + var buf = newWideCString("", bufsize) + while true: + var L = getModuleFileNameW(0, buf, bufsize) + if L == 0'i32: + result = "" # error! + break + elif L > bufsize: + buf = newWideCString("", L) + bufsize = L + else: + result = buf$L + break else: - result = newString(256) - var len = getModuleFileNameA(0, result, 256) - setlen(result, int(len)) + result = newString(bufsize) + while true: + var L = getModuleFileNameA(0, result, bufsize) + if L == 0'i32: + result = "" # error! + break + elif L > bufsize: + result = newString(L) + bufsize = L + else: + setLen(result, L) + break elif defined(macosx): var size: cuint32 getExecPath1(nil, size) @@ -1450,7 +1539,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect].} = elif defined(solaris): result = getApplAux("/proc/" & $getpid() & "/path/a.out") elif defined(freebsd): - result = getApplAux("/proc/" & $getpid() & "/file") + result = getApplFreebsd() # little heuristic that may work on other POSIX-like systems: if result.len == 0: result = getApplHeuristic() diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim index 56671ee62..3d3a105f0 100644 --- a/lib/pure/ospaths.nim +++ b/lib/pure/ospaths.nim @@ -434,8 +434,8 @@ when not declared(getEnv) or defined(nimscript): ## On Windows, network paths are considered absolute too. when doslike: var len = len(path) - result = (len > 1 and path[0] in {'/', '\\'}) or - (len > 2 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':') + result = (len > 0 and path[0] in {'/', '\\'}) or + (len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':') elif defined(macos): result = path.len > 0 and path[0] != ':' elif defined(RISCOS): diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index abc21b2b2..44ec5b548 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -48,7 +48,7 @@ type inHandle, outHandle, errHandle: FileHandle inStream, outStream, errStream: Stream id: Pid - exitCode: cint + exitStatus: cint options: set[ProcessOption] Process* = ref ProcessObj ## represents an operating system process @@ -731,7 +731,7 @@ elif not defined(useNimRtl): pStdin, pStdout, pStderr: array[0..1, cint] new(result) result.options = options - result.exitCode = -3 # for ``waitForExit`` + result.exitStatus = -3 # for ``waitForExit`` if poParentStreams notin options: if pipe(pStdin) != 0'i32 or pipe(pStdout) != 0'i32 or pipe(pStderr) != 0'i32: @@ -774,7 +774,10 @@ elif not defined(useNimRtl): data.workingDir = workingDir when useProcessAuxSpawn: + var currentDir = getCurrentDir() pid = startProcessAuxSpawn(data) + if workingDir.len > 0: + setCurrentDir(currentDir) else: pid = startProcessAuxFork(data) @@ -835,7 +838,6 @@ elif not defined(useNimRtl): chck posix_spawn_file_actions_adddup2(fops, data.pStderr[writeIdx], 2) var res: cint - # FIXME: chdir is global to process if data.workingDir.len > 0: setCurrentDir($data.workingDir) var pid: Pid @@ -957,13 +959,10 @@ elif not defined(useNimRtl): proc running(p: Process): bool = var ret : int - when not defined(freebsd): - ret = waitpid(p.id, p.exitCode, WNOHANG) - else: - var status : cint = 1 - ret = waitpid(p.id, status, WNOHANG) - if WIFEXITED(status): - p.exitCode = status + var status : cint = 1 + ret = waitpid(p.id, status, WNOHANG) + if WIFEXITED(status): + p.exitStatus = status if ret == 0: return true # Can't establish status. Assume running. result = ret == int(p.id) @@ -980,11 +979,12 @@ elif not defined(useNimRtl): import kqueue, times proc waitForExit(p: Process, timeout: int = -1): int = - if p.exitCode != -3: return p.exitCode + if p.exitStatus != -3: return int(p.exitStatus) shr 8 if timeout == -1: - if waitpid(p.id, p.exitCode, 0) < 0: - p.exitCode = -3 + var status : cint = 1 + if waitpid(p.id, status, 0) < 0: raiseOSError(osLastError()) + p.exitStatus = status else: var kqFD = kqueue() if kqFD == -1: @@ -1004,6 +1004,7 @@ elif not defined(useNimRtl): try: while true: + var status : cint = 1 var count = kevent(kqFD, addr(kevIn), 1, addr(kevOut), 1, addr(tmspec)) if count < 0: @@ -1014,22 +1015,22 @@ elif not defined(useNimRtl): # timeout expired, so we trying to kill process if posix.kill(p.id, SIGKILL) == -1: raiseOSError(osLastError()) - if waitpid(p.id, p.exitCode, 0) < 0: - p.exitCode = -3 + if waitpid(p.id, status, 0) < 0: raiseOSError(osLastError()) + p.exitStatus = status break else: if kevOut.ident == p.id.uint and kevOut.filter == EVFILT_PROC: - if waitpid(p.id, p.exitCode, 0) < 0: - p.exitCode = -3 + if waitpid(p.id, status, 0) < 0: raiseOSError(osLastError()) + p.exitStatus = status break else: raiseOSError(osLastError()) finally: discard posix.close(kqFD) - result = int(p.exitCode) shr 8 + result = int(p.exitStatus) shr 8 else: import times @@ -1061,15 +1062,16 @@ elif not defined(useNimRtl): s.tv_sec = b.tv_sec s.tv_nsec = b.tv_nsec - #if waitPid(p.id, p.exitCode, 0) == int(p.id): + #if waitPid(p.id, p.exitStatus, 0) == int(p.id): # ``waitPid`` fails if the process is not running anymore. But then - # ``running`` probably set ``p.exitCode`` for us. Since ``p.exitCode`` is + # ``running`` probably set ``p.exitStatus`` for us. Since ``p.exitStatus`` is # initialized with -3, wrong success exit codes are prevented. - if p.exitCode != -3: return p.exitCode + if p.exitStatus != -3: return int(p.exitStatus) shr 8 if timeout == -1: - if waitpid(p.id, p.exitCode, 0) < 0: - p.exitCode = -3 + var status : cint = 1 + if waitpid(p.id, status, 0) < 0: raiseOSError(osLastError()) + p.exitStatus = status else: var nmask, omask: Sigset var sinfo: SigInfo @@ -1100,9 +1102,10 @@ elif not defined(useNimRtl): let res = sigtimedwait(nmask, sinfo, tmspec) if res == SIGCHLD: if sinfo.si_pid == p.id: - if waitpid(p.id, p.exitCode, 0) < 0: - p.exitCode = -3 + var status : cint = 1 + if waitpid(p.id, status, 0) < 0: raiseOSError(osLastError()) + p.exitStatus = status break else: # we have SIGCHLD, but not for process we are waiting, @@ -1122,9 +1125,10 @@ elif not defined(useNimRtl): # timeout expired, so we trying to kill process if posix.kill(p.id, SIGKILL) == -1: raiseOSError(osLastError()) - if waitpid(p.id, p.exitCode, 0) < 0: - p.exitCode = -3 + var status : cint = 1 + if waitpid(p.id, status, 0) < 0: raiseOSError(osLastError()) + p.exitStatus = status break else: raiseOSError(err) @@ -1136,17 +1140,19 @@ elif not defined(useNimRtl): if sigprocmask(SIG_UNBLOCK, nmask, omask) == -1: raiseOSError(osLastError()) - result = int(p.exitCode) shr 8 + result = int(p.exitStatus) shr 8 proc peekExitCode(p: Process): int = - if p.exitCode != -3: return p.exitCode - var ret = waitpid(p.id, p.exitCode, WNOHANG) + var status : cint = 1 + if p.exitStatus != -3: return int(p.exitStatus) shr 8 + var ret = waitpid(p.id, status, WNOHANG) var b = ret == int(p.id) if b: result = -1 - if not WIFEXITED(p.exitCode): - p.exitCode = -3 + if WIFEXITED(status): + p.exitStatus = status + result = p.exitStatus.int shr 8 + else: result = -1 - else: result = p.exitCode.int shr 8 proc createStream(stream: var Stream, handle: var FileHandle, fileMode: FileMode) = diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim index d16a55302..aa4a13ecf 100644 --- a/lib/pure/parsexml.nim +++ b/lib/pure/parsexml.nim @@ -142,9 +142,9 @@ proc kind*(my: XmlParser): XmlEventKind {.inline.} = template charData*(my: XmlParser): string = ## returns the character data for the events: ``xmlCharData``, ## ``xmlWhitespace``, ``xmlComment``, ``xmlCData``, ``xmlSpecial`` - ## Raises an assertion in debug mode if ``my.kind`` is not one + ## Raises an assertion in debug mode if ``my.kind`` is not one ## of those events. In release mode, this will not trigger an error - ## but the value returned will not be valid. + ## but the value returned will not be valid. assert(my.kind in {xmlCharData, xmlWhitespace, xmlComment, xmlCData, xmlSpecial}) my.a @@ -152,49 +152,49 @@ template charData*(my: XmlParser): string = template elementName*(my: XmlParser): string = ## returns the element name for the events: ``xmlElementStart``, ## ``xmlElementEnd``, ``xmlElementOpen`` - ## Raises an assertion in debug mode if ``my.kind`` is not one + ## Raises an assertion in debug mode if ``my.kind`` is not one ## of those events. In release mode, this will not trigger an error - ## but the value returned will not be valid. + ## but the value returned will not be valid. assert(my.kind in {xmlElementStart, xmlElementEnd, xmlElementOpen}) my.a template entityName*(my: XmlParser): string = ## returns the entity name for the event: ``xmlEntity`` - ## Raises an assertion in debug mode if ``my.kind`` is not + ## Raises an assertion in debug mode if ``my.kind`` is not ## ``xmlEntity``. In release mode, this will not trigger an error - ## but the value returned will not be valid. + ## but the value returned will not be valid. assert(my.kind == xmlEntity) my.a template attrKey*(my: XmlParser): string = ## returns the attribute key for the event ``xmlAttribute`` - ## Raises an assertion in debug mode if ``my.kind`` is not + ## Raises an assertion in debug mode if ``my.kind`` is not ## ``xmlAttribute``. In release mode, this will not trigger an error - ## but the value returned will not be valid. + ## but the value returned will not be valid. assert(my.kind == xmlAttribute) my.a template attrValue*(my: XmlParser): string = ## returns the attribute value for the event ``xmlAttribute`` - ## Raises an assertion in debug mode if ``my.kind`` is not + ## Raises an assertion in debug mode if ``my.kind`` is not ## ``xmlAttribute``. In release mode, this will not trigger an error - ## but the value returned will not be valid. + ## but the value returned will not be valid. assert(my.kind == xmlAttribute) my.b template piName*(my: XmlParser): string = ## returns the processing instruction name for the event ``xmlPI`` - ## Raises an assertion in debug mode if ``my.kind`` is not + ## Raises an assertion in debug mode if ``my.kind`` is not ## ``xmlPI``. In release mode, this will not trigger an error - ## but the value returned will not be valid. + ## but the value returned will not be valid. assert(my.kind == xmlPI) my.a template piRest*(my: XmlParser): string = ## returns the rest of the processing instruction for the event ``xmlPI`` - ## Raises an assertion in debug mode if ``my.kind`` is not + ## Raises an assertion in debug mode if ``my.kind`` is not ## ``xmlPI``. In release mode, this will not trigger an error - ## but the value returned will not be valid. + ## but the value returned will not be valid. assert(my.kind == xmlPI) my.b @@ -636,12 +636,14 @@ proc rawGetTok(my: var XmlParser) = proc getTok(my: var XmlParser) = while true: + let lastKind = my.kind rawGetTok(my) case my.kind of xmlComment: if my.options.contains(reportComments): break of xmlWhitespace: - if my.options.contains(reportWhitespace): break + if my.options.contains(reportWhitespace) or lastKind in {xmlCharData, xmlComment, xmlEntity}: + break else: break proc next*(my: var XmlParser) = diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 08da771dc..955a70143 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -122,7 +122,7 @@ when isMainModule: inc occur[x] for i, oc in occur: if oc < 69: - doAssert false, "too few occurances of " & $i + doAssert false, "too few occurrences of " & $i elif oc > 130: - doAssert false, "too many occurances of " & $i + doAssert false, "too many occurrences of " & $i main() diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index bf134f2ae..c2ba2b1f3 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -349,4 +349,4 @@ when isMainModule: assert toRational(0.98765432) == 12345679 // 12500000 assert toRational(0.1, 1000000) == 1 // 10 assert toRational(0.9, 1000000) == 9 // 10 - assert toRational(PI) == 80143857 // 25510582 + #assert toRational(PI) == 80143857 // 25510582 diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index cba101fff..506b2cec0 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -377,7 +377,8 @@ proc contains*(s: Selector, key: SelectorKey): bool = proc len*(s: Selector): int = ## Retrieves the number of registered file descriptors in this Selector. - return s.fds.len + when not defined(nimdoc): + return s.fds.len {.deprecated: [TEvent: Event, PSelectorKey: SelectorKey, TReadyInfo: ReadyInfo, PSelector: Selector].} diff --git a/lib/pure/stats.nim b/lib/pure/stats.nim index ec4cd182b..2004337df 100644 --- a/lib/pure/stats.nim +++ b/lib/pure/stats.nim @@ -334,15 +334,17 @@ when isMainModule: doAssert(rs1.sum == 9.5) doAssert(rs1.mean() == 2.375) - var rr: RunningRegress - rr.push(@[0.0,1.0,2.8,3.0,4.0], @[0.0,1.0,2.3,3.0,4.0]) - doAssert(rr.slope() == 0.9695585996955861) - doAssert(rr.intercept() == -0.03424657534246611) - doAssert(rr.correlation() == 0.9905100362239381) - var rr1, rr2: RunningRegress - rr1.push(@[0.0,1.0], @[0.0,1.0]) - rr2.push(@[2.8,3.0,4.0], @[2.3,3.0,4.0]) - let rr3 = rr1 + rr2 - doAssert(rr3.correlation() == rr.correlation()) - doAssert(clean(rr3.slope()) == clean(rr.slope())) - doAssert(clean(rr3.intercept()) == clean(rr.intercept())) + when not defined(cpu32): + # XXX For some reason on 32bit CPUs these results differ + var rr: RunningRegress + rr.push(@[0.0,1.0,2.8,3.0,4.0], @[0.0,1.0,2.3,3.0,4.0]) + doAssert(rr.slope() == 0.9695585996955861) + doAssert(rr.intercept() == -0.03424657534246611) + doAssert(rr.correlation() == 0.9905100362239381) + var rr1, rr2: RunningRegress + rr1.push(@[0.0,1.0], @[0.0,1.0]) + rr2.push(@[2.8,3.0,4.0], @[2.3,3.0,4.0]) + let rr3 = rr1 + rr2 + doAssert(rr3.correlation() == rr.correlation()) + doAssert(clean(rr3.slope()) == clean(rr.slope())) + doAssert(clean(rr3.intercept()) == clean(rr.intercept())) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index b78a2b966..efc1dfa92 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -266,13 +266,13 @@ proc initInterval*(milliseconds, seconds, minutes, hours, days, months, result.milliseconds = `mod`(milliseconds, 1000) carryO = `div`(milliseconds, 1000) result.seconds = `mod`(carryO + seconds, 60) - carryO = `div`(seconds, 60) + carryO = `div`(carryO + seconds, 60) result.minutes = `mod`(carryO + minutes, 60) - carryO = `div`(minutes, 60) + carryO = `div`(carryO + minutes, 60) result.hours = `mod`(carryO + hours, 24) - carryO = `div`(hours, 24) + carryO = `div`(carryO + hours, 24) result.days = carryO + days - carryO = 0 + result.months = `mod`(months, 12) carryO = `div`(months, 12) result.years = carryO + years @@ -283,13 +283,13 @@ proc `+`*(ti1, ti2: TimeInterval): TimeInterval = result.milliseconds = `mod`(ti1.milliseconds + ti2.milliseconds, 1000) carryO = `div`(ti1.milliseconds + ti2.milliseconds, 1000) result.seconds = `mod`(carryO + ti1.seconds + ti2.seconds, 60) - carryO = `div`(ti1.seconds + ti2.seconds, 60) + carryO = `div`(carryO + ti1.seconds + ti2.seconds, 60) result.minutes = `mod`(carryO + ti1.minutes + ti2.minutes, 60) - carryO = `div`(ti1.minutes + ti2.minutes, 60) + carryO = `div`(carryO + ti1.minutes + ti2.minutes, 60) result.hours = `mod`(carryO + ti1.hours + ti2.hours, 24) - carryO = `div`(ti1.hours + ti2.hours, 24) + carryO = `div`(carryO + ti1.hours + ti2.hours, 24) result.days = carryO + ti1.days + ti2.days - carryO = 0 + result.months = `mod`(ti1.months + ti2.months, 12) carryO = `div`(ti1.months + ti2.months, 12) result.years = carryO + ti1.years + ti2.years @@ -1248,12 +1248,18 @@ proc parse*(value, layout: string): TimeInfo = else: parseToken(info, token, value, j) token = "" - # Reset weekday (might not have been provided and the default may be wrong) - # and yearday (is never provided directly and therefore probably wrong) - let processed = getLocalTime(toTime(info)) - info.weekday = processed.weekday - info.yearday = processed.yearday - return info + + # We are going to process the date to find out if we are in DST, because the + # default based on the current time may be wrong. Calling getLocalTime will + # set this correctly, but the actual time may be offset from when we called + # toTime with a possibly incorrect DST setting, so we are only going to take + # the isDST from this result. + let correctDST = getLocalTime(toTime(info)) + info.isDST = correctDST.isDST + + # Now we preocess it again with the correct isDST to correct things like + # weekday and yearday. + return getLocalTime(toTime(info)) # Leap year calculations are adapted from: # http://www.codeproject.com/Articles/7358/Ultra-fast-Algorithms-for-Working-with-Leap-Years diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 0fc2e441e..12553e3da 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -11,11 +11,21 @@ ## ## This module implements boilerplate to make unit testing easy. ## +## The test status and name is printed after any output or traceback. +## ## Example: ## ## .. code:: nim ## ## suite "description for this stuff": +## echo "suite setup: run once before the tests" +## +## setup: +## echo "run before each test" +## +## teardown: +## echo "run after each test": +## ## test "essential truths": ## # give up and stop if this fails ## require(true) @@ -30,6 +40,13 @@ ## let v = @[1, 2, 3] # you can do initialization here ## expect(IndexError): ## discard v[4] +## +## echo "suite teardown: run once after the tests" +## +## +## Tests can be nested, however failure of a nested test will not mark the +## parent test as failed. Setup and teardown are inherited. Setup can be +## overridden locally. import macros @@ -226,10 +243,11 @@ template fail* = checkpoints = @[] template skip* = - ## Makes test to be skipped. Should be used directly + ## Mark the test as skipped. Should be used directly ## in case when it is not possible to perform test ## for reasons depending on outer environment, ## or certain application logic conditions or configurations. + ## The test code is still executed. ## ## .. code-block:: nim ## @@ -250,12 +268,12 @@ macro check*(conditions: untyped): untyped = ## ## import strutils ## - ## check("AKB48".toLower() == "akb48") + ## check("AKB48".toLowerAscii() == "akb48") ## ## let teams = {'A', 'K', 'B', '4', '8'} ## ## check: - ## "AKB48".toLower() == "akb48" + ## "AKB48".toLowerAscii() == "akb48" ## 'C' in teams let checked = callsite()[1] var diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim index 2a2c3e1dd..22bd259b7 100644 --- a/lib/pure/xmlparser.nim +++ b/lib/pure/xmlparser.nim @@ -28,7 +28,7 @@ proc raiseInvalidXml(errors: seq[string]) = proc addNode(father, son: XmlNode) = if son != nil: add(father, son) -proc parse(x: var XmlParser, errors: var seq[string]): XmlNode +proc parse(x: var XmlParser, errors: var seq[string]): XmlNode {.gcsafe.} proc untilElementEnd(x: var XmlParser, result: XmlNode, errors: var seq[string]) = @@ -164,3 +164,6 @@ when isMainModule: var xml = loadXml(filePath, errors) assert(errors.len == 0, "The file tests/testdata/doc1.xml should be parsed without errors.") + block bug1518: + var err: seq[string] = @[] + assert $parsexml(newStringStream"<tag>One & two</tag>", "temp.xml", err) == "<tag>One & two</tag>" diff --git a/lib/stdlib.nimble b/lib/stdlib.nimble index 4b0066ee8..5238d900b 100644 --- a/lib/stdlib.nimble +++ b/lib/stdlib.nimble @@ -1,6 +1,6 @@ [Package] name = "stdlib" -version = "0.14.3" +version = "0.15.2" author = "Dominik Picheta" description = "Nim's standard library." license = "MIT" diff --git a/lib/system.nim b/lib/system.nim index 31d14d4bf..919b4694c 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1824,10 +1824,10 @@ const NimMajor*: int = 0 ## is the major number of Nim's version. - NimMinor*: int = 14 + NimMinor*: int = 15 ## is the minor number of Nim's version. - NimPatch*: int = 3 + NimPatch*: int = 2 ## is the patch number of Nim's version. NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch @@ -2581,10 +2581,11 @@ else: when not defined(JS): #and not defined(nimscript): {.push stack_trace: off, profiler:off.} - when not defined(nimscript) and not defined(nogc): + when hasAlloc: when not defined(gcStack): proc initGC() - when not defined(boehmgc) and not defined(useMalloc) and not defined(gogc) and not defined(gcStack): + when not defined(boehmgc) and not defined(useMalloc) and + not defined(gogc) and not defined(gcStack): proc initAllocator() {.inline.} proc initStackBottom() {.inline, compilerproc.} = @@ -2602,7 +2603,6 @@ when not defined(JS): #and not defined(nimscript): when declared(setStackBottom): setStackBottom(locals) - when hasAlloc: var strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic}) @@ -2731,7 +2731,7 @@ when not defined(JS): #and not defined(nimscript): proc setStdIoUnbuffered*() {.tags: [], benign.} ## Configures `stdin`, `stdout` and `stderr` to be unbuffered. - proc close*(f: File) {.tags: [].} + proc close*(f: File) {.tags: [], gcsafe.} ## Closes the file. proc endOfFile*(f: File): bool {.tags: [], benign.} @@ -3676,7 +3676,7 @@ template closureScope*(body: untyped): untyped = when defined(nimconfig): include "system/nimscript" -when defined(windows) and appType == "console" and not defined(nimconfig): +when defined(windows) and appType == "console" and defined(nimSetUtf8CodePage): proc setConsoleOutputCP(codepage: cint): cint {.stdcall, dynlib: "kernel32", importc: "SetConsoleOutputCP".} discard setConsoleOutputCP(65001) # 65001 - utf-8 codepage diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index bed9fd906..745bbbf62 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -101,8 +101,8 @@ type # shared: var - bottomData: AvlNode - bottom: PAvlNode + bottomData {.threadvar.}: AvlNode + bottom {.threadvar.}: PAvlNode {.push stack_trace: off.} proc initAllocator() = diff --git a/lib/system/avltree.nim b/lib/system/avltree.nim index d5c901542..50faada26 100644 --- a/lib/system/avltree.nim +++ b/lib/system/avltree.nim @@ -9,7 +9,7 @@ # not really an AVL tree anymore, but still balanced ... -template isBottom(n: PAvlNode): bool = n == bottom +template isBottom(n: PAvlNode): bool = n.link[0] == n proc lowGauge(n: PAvlNode): int = var it = n @@ -52,7 +52,7 @@ proc split(t: var PAvlNode) = inc t.level proc add(a: var MemRegion, t: var PAvlNode, key, upperBound: int) {.benign.} = - if t == bottom: + if t.isBottom: t = allocAvlNode(a, key, upperBound) else: if key <% t.key: @@ -65,14 +65,14 @@ proc add(a: var MemRegion, t: var PAvlNode, key, upperBound: int) {.benign.} = split(t) proc del(a: var MemRegion, t: var PAvlNode, x: int) {.benign.} = - if t == bottom: return + if isBottom(t): return a.last = t if x <% t.key: del(a, t.link[0], x) else: a.deleted = t del(a, t.link[1], x) - if t == a.last and a.deleted != bottom and x == a.deleted.key: + if t == a.last and not isBottom(a.deleted) and x == a.deleted.key: a.deleted.key = t.key a.deleted.upperBound = t.upperBound a.deleted = bottom diff --git a/lib/system/channels.nim b/lib/system/channels.nim index caa709229..4b8b895a5 100644 --- a/lib/system/channels.nim +++ b/lib/system/channels.nim @@ -52,6 +52,7 @@ proc deinitRawChannel(p: pointer) = proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, mode: LoadStoreMode) {.benign.} + proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel, mode: LoadStoreMode) {.benign.} = var @@ -71,6 +72,9 @@ proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel, proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, mode: LoadStoreMode) = + template `+!`(p: pointer; x: int): pointer = + cast[pointer](cast[int](p) +% x) + var d = cast[ByteAddress](dest) s = cast[ByteAddress](src) @@ -93,7 +97,9 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, if s2 == nil: unsureAsgnRef(x, s2) else: - unsureAsgnRef(x, copyString(cast[NimString](s2))) + let y = copyDeepString(cast[NimString](s2)) + #echo "loaded ", cast[int](y), " ", cast[string](y) + unsureAsgnRef(x, y) dealloc(t.region, s2) of tySequence: var s2 = cast[PPointer](src)[] @@ -107,26 +113,27 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, else: sysAssert(dest != nil, "dest == nil") if mode == mStore: - x[] = alloc(t.region, seq.len *% mt.base.size +% GenericSeqSize) + x[] = alloc0(t.region, seq.len *% mt.base.size +% GenericSeqSize) else: unsureAsgnRef(x, newObj(mt, seq.len * mt.base.size + GenericSeqSize)) var dst = cast[ByteAddress](cast[PPointer](dest)[]) + var dstseq = cast[PGenericSeq](dst) + dstseq.len = seq.len + dstseq.reserved = seq.len for i in 0..seq.len-1: storeAux( cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), cast[pointer](cast[ByteAddress](s2) +% i *% mt.base.size +% GenericSeqSize), mt.base, t, mode) - var dstseq = cast[PGenericSeq](dst) - dstseq.len = seq.len - dstseq.reserved = seq.len if mode != mStore: dealloc(t.region, s2) of tyObject: - # copy type field: - var pint = cast[ptr PNimType](dest) - pint[] = cast[ptr PNimType](src)[] if mt.base != nil: storeAux(dest, src, mt.base, t, mode) + else: + # copy type field: + var pint = cast[ptr PNimType](dest) + pint[] = cast[ptr PNimType](src)[] storeAux(dest, src, mt.node, t, mode) of tyTuple: storeAux(dest, src, mt.node, t, mode) @@ -143,15 +150,24 @@ proc storeAux(dest, src: pointer, mt: PNimType, t: PRawChannel, else: unsureAsgnRef(x, nil) else: - let size = if mt.base.kind == tyObject: cast[ptr PNimType](s)[].size - else: mt.base.size + #let size = if mt.base.kind == tyObject: cast[ptr PNimType](s)[].size + # else: mt.base.size if mode == mStore: - x[] = alloc(t.region, size) + let dyntype = when declared(usrToCell): usrToCell(s).typ + else: mt + let size = dyntype.base.size + # we store the real dynamic 'ref type' at offset 0, so that + # no information is lost + let a = alloc0(t.region, size+sizeof(pointer)) + x[] = a + cast[PPointer](a)[] = dyntype + storeAux(a +! sizeof(pointer), s, dyntype.base, t, mode) else: - var obj = newObj(mt, size) + let dyntype = cast[ptr PNimType](s)[] + var obj = newObj(dyntype, dyntype.base.size) unsureAsgnRef(x, obj) - storeAux(x[], s, mt.base, t, mode) - if mode != mStore: dealloc(t.region, s) + storeAux(x[], s +! sizeof(pointer), dyntype.base, t, mode) + dealloc(t.region, s) else: copyMem(dest, src, mt.size) # copy raw bits @@ -194,10 +210,8 @@ template sendImpl(q: expr) {.immediate.} = if q.mask == ChannelDeadMask: sysFatal(DeadThreadError, "cannot send message; thread died") acquireSys(q.lock) - var m: TMsg - shallowCopy(m, msg) var typ = cast[PNimType](getTypeInfo(msg)) - rawSend(q, addr(m), typ) + rawSend(q, unsafeAddr(msg), typ) q.elemType = typ releaseSys(q.lock) signalSysCond(q.cond) @@ -228,8 +242,10 @@ proc recv*[TMsg](c: var Channel[TMsg]): TMsg = proc tryRecv*[TMsg](c: var Channel[TMsg]): tuple[dataAvailable: bool, msg: TMsg] = - ## try to receives a message from the channel `c` if available. Otherwise - ## it returns ``(false, default(msg))``. + ## Tries to receive a message from the channel `c`, but this can fail + ## for all sort of reasons, including contention. If it fails, + ## it returns ``(false, default(msg))`` otherwise it + ## returns ``(true, msg)``. var q = cast[PRawChannel](addr(c)) if q.mask != ChannelDeadMask: if tryAcquireSys(q.lock): diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim index 5445a067c..38cc8cbf3 100644 --- a/lib/system/deepcopy.nim +++ b/lib/system/deepcopy.nim @@ -32,12 +32,6 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode) {.benign.} = genericDeepCopyAux(dest, src, m) of nkNone: sysAssert(false, "genericDeepCopyAux") -proc copyDeepString(src: NimString): NimString {.inline.} = - if src != nil: - result = rawNewStringNoInit(src.len) - result.len = src.len - copyMem(addr(result.data), addr(src.data), src.len + 1) - proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) = var d = cast[ByteAddress](dest) @@ -70,10 +64,11 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) = of tyObject: # we need to copy m_type field for tyObject, as it could be empty for # sequence reallocations: - var pint = cast[ptr PNimType](dest) - pint[] = cast[ptr PNimType](src)[] if mt.base != nil: genericDeepCopyAux(dest, src, mt.base) + else: + var pint = cast[ptr PNimType](dest) + pint[] = cast[ptr PNimType](src)[] genericDeepCopyAux(dest, src, mt.node) of tyTuple: genericDeepCopyAux(dest, src, mt.node) @@ -103,16 +98,16 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType) = else: let realType = x.typ let z = newObj(realType, realType.base.size) - unsureAsgnRef(cast[PPointer](dest), z) x.typ = cast[PNimType](cast[int](z) or 1) genericDeepCopyAux(z, s2, realType.base) x.typ = realType else: - let realType = mt - let z = newObj(realType, realType.base.size) + let size = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[].size + else: mt.base.size + let z = newObj(mt, size) unsureAsgnRef(cast[PPointer](dest), z) - genericDeepCopyAux(z, s2, realType.base) + genericDeepCopyAux(z, s2, mt.base) of tyPtr: # no cycle check here, but also not really required let s2 = cast[PPointer](src)[] diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index 0a994efac..fa997e982 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -26,6 +26,8 @@ proc nimLoadLibraryError(path: string) = stderr.rawWrite("could not load: ") stderr.rawWrite(path) stderr.rawWrite("\n") + when not(defined(nimDebugDlOpen)): + stderr.rawWrite("compile with -d:nimDebugDlOpen for more information\n") quit(1) proc procAddrError(name: cstring) {.noinline.} = @@ -74,7 +76,8 @@ when defined(posix): when defined(nimDebugDlOpen): let error = dlerror() if error != nil: - c_fprintf(c_stderr, "%s\n", error) + stderr.write(error) + stderr.rawWrite("\n") proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr = result = dlsym(lib, name) diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index 089c9c915..ce2bfc2ae 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -33,6 +33,9 @@ when withRealTime and not declared(getTicks): when defined(memProfiler): proc nimProfile(requestedSize: int) {.benign.} +when hasThreadSupport: + include sharedlist + type ObjectSpaceIter = object state: range[-1..0] @@ -96,7 +99,7 @@ type stat: GcStat additionalRoots: CellSeq # dummy roots for GC_ref/unref spaceIter: ObjectSpaceIter - dumpHeapFile: File # File that is used for GC_dumpHeap + pDumpHeapFile: pointer # File that is used for GC_dumpHeap when hasThreadSupport: toDispose: SharedList[pointer] @@ -612,6 +615,9 @@ template checkTime {.dirty.} = # ---------------- dump heap ---------------- +template dumpHeapFile(gch: var GcHeap): File = + cast[File](gch.pDumpHeapFile) + proc debugGraph(s: PCell) = c_fprintf(gch.dumpHeapFile, "child %p\n", s) @@ -625,7 +631,7 @@ proc GC_dumpHeap*(file: File) = ## Dumps the GCed heap's content to a file. Can be useful for ## debugging. Produces an undocumented text file format that ## can be translated into "dot" syntax via the "heapdump2dot" tool. - gch.dumpHeapFile = file + gch.pDumpHeapFile = file var spaceIter: ObjectSpaceIter var d = gch.decStack.d for i in 0 .. < gch.decStack.len: @@ -643,7 +649,7 @@ proc GC_dumpHeap*(file: File) = writeCell(file, "cell ", c) forAllChildren(c, waDebug) c_fprintf(file, "end\n") - gch.dumpHeapFile = nil + gch.pDumpHeapFile = nil proc GC_dumpHeap() = var f: File diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index 7a1b88c84..513ede173 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -120,6 +120,7 @@ when allowForeignThreadGc: ## switches are used if not localGcInitialized: localGcInitialized = true + initAllocator() var stackTop {.volatile.}: pointer setStackBottom(addr(stackTop)) initGC() diff --git a/lib/system/gc_stack.nim b/lib/system/gc_stack.nim index 5f72b8959..3eda08df9 100644 --- a/lib/system/gc_stack.nim +++ b/lib/system/gc_stack.nim @@ -79,6 +79,7 @@ template withRegion*(r: MemRegion; body: untyped) = try: body finally: + r = tlRegion tlRegion = oldRegion template inc(p: pointer, s: int) = @@ -464,4 +465,10 @@ proc getFreeMem(): int = tlRegion.remaining proc getTotalMem(): int = result = tlRegion.totalSize +proc getOccupiedMem*(r: MemRegion): int = + result = r.totalSize - r.remaining +proc getFreeMem*(r: MemRegion): int = r.remaining +proc getTotalMem*(r: MemRegion): int = + result = r.totalSize + proc setStackBottom(theStackBottom: pointer) = discard diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index b07a362a0..316dd74d7 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -87,8 +87,6 @@ elif defined(posix): const MAP_ANONYMOUS = 0x1000 elif defined(solaris): const MAP_ANONYMOUS = 0x100 - elif defined(linux): - const MAP_ANONYMOUS = 0x20 else: var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "<sys/mman.h>".}: cint diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index 3e9657ce0..5c10392f1 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -104,21 +104,22 @@ proc getFileHandle*(f: File): FileHandle = c_fileno(f) proc readLine(f: File, line: var TaintedString): bool = var pos = 0 + var sp: cint = 80 # Use the currently reserved space for a first try - when defined(nimscript): - var space: cint = 80 + if line.string.isNil: + line = TaintedString(newStringOfCap(80)) else: - var space: cint = cint(cast[PGenericSeq](line.string).space) - line.string.setLen(space) - + when not defined(nimscript): + sp = cint(cast[PGenericSeq](line.string).space) + line.string.setLen(sp) while true: # memset to \l so that we can tell how far fgets wrote, even on EOF, where # fgets doesn't append an \l - c_memset(addr line.string[pos], '\l'.ord, space) - if c_fgets(addr line.string[pos], space, f) == nil: + c_memset(addr line.string[pos], '\l'.ord, sp) + if c_fgets(addr line.string[pos], sp, f) == nil: line.string.setLen(0) return false - let m = c_memchr(addr line.string[pos], '\l'.ord, space) + let m = c_memchr(addr line.string[pos], '\l'.ord, sp) if m != nil: # \l found: Could be our own or the one by fgets, in any case, we're done var last = cast[ByteAddress](m) - cast[ByteAddress](addr line.string[0]) @@ -129,17 +130,17 @@ proc readLine(f: File, line: var TaintedString): bool = # \0\l\0 => line ending in a null character. # \0\l\l => last line without newline, null was put there by fgets. elif last > 0 and line.string[last-1] == '\0': - if last < pos + space - 1 and line.string[last+1] != '\0': + if last < pos + sp - 1 and line.string[last+1] != '\0': dec last line.string.setLen(last) return true else: # fgets will have inserted a null byte at the end of the string. - dec space + dec sp # No \l found: Increase buffer and read more - inc pos, space - space = 128 # read in 128 bytes at a time - line.string.setLen(pos+space) + inc pos, sp + sp = 128 # read in 128 bytes at a time + line.string.setLen(pos+sp) proc readLine(f: File): TaintedString = result = TaintedString(newStringOfCap(80)) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 3e170172b..3a93221e0 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -110,6 +110,11 @@ proc copyStringRC1(src: NimString): NimString {.compilerRtl.} = result.len = src.len copyMem(addr(result.data), addr(src.data), src.len + 1) +proc copyDeepString(src: NimString): NimString {.inline.} = + if src != nil: + result = rawNewStringNoInit(src.len) + result.len = src.len + copyMem(addr(result.data), addr(src.data), src.len + 1) proc hashString(s: string): int {.compilerproc.} = # the compiler needs exactly the same hash function! diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 62829f62c..6f5bb38b1 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -356,6 +356,8 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) = template threadProcWrapperBody(closure: expr) {.immediate.} = when declared(globalsSlot): threadVarSetValue(globalsSlot, closure) + when declared(initAllocator): + initAllocator() var thrd = cast[ptr Thread[TArg]](closure) threadProcWrapStackFrame(thrd) # Since an unhandled exception terminates the whole process (!), there is diff --git a/lib/upcoming/asyncdispatch.nim b/lib/upcoming/asyncdispatch.nim index 4465e961c..731ef52dc 100644 --- a/lib/upcoming/asyncdispatch.nim +++ b/lib/upcoming/asyncdispatch.nim @@ -130,286 +130,7 @@ export Port, SocketFlag # TODO: Check if yielded future is nil and throw a more meaningful exception -# -- Futures - -type - FutureBase* = ref object of RootObj ## Untyped future. - cb: proc () {.closure,gcsafe.} - finished: bool - error*: ref Exception ## Stored exception - errorStackTrace*: string - when not defined(release): - stackTrace: string ## For debugging purposes only. - id: int - fromProc: string - - Future*[T] = ref object of FutureBase ## Typed future. - value: T ## Stored value - - FutureVar*[T] = distinct Future[T] - - FutureError* = object of Exception - cause*: FutureBase - -{.deprecated: [PFutureBase: FutureBase, PFuture: Future].} - -when not defined(release): - var currentID = 0 - -proc callSoon*(cbproc: proc ()) {.gcsafe.} - -proc newFuture*[T](fromProc: string = "unspecified"): Future[T] = - ## Creates a new future. - ## - ## Specifying ``fromProc``, which is a string specifying the name of the proc - ## that this future belongs to, is a good habit as it helps with debugging. - new(result) - result.finished = false - when not defined(release): - result.stackTrace = getStackTrace() - result.id = currentID - result.fromProc = fromProc - currentID.inc() - -proc newFutureVar*[T](fromProc = "unspecified"): FutureVar[T] = - ## Create a new ``FutureVar``. This Future type is ideally suited for - ## situations where you want to avoid unnecessary allocations of Futures. - ## - ## Specifying ``fromProc``, which is a string specifying the name of the proc - ## that this future belongs to, is a good habit as it helps with debugging. - result = FutureVar[T](newFuture[T](fromProc)) - -proc clean*[T](future: FutureVar[T]) = - ## Resets the ``finished`` status of ``future``. - Future[T](future).finished = false - Future[T](future).error = nil - -proc checkFinished[T](future: Future[T]) = - ## Checks whether `future` is finished. If it is then raises a - ## ``FutureError``. - when not defined(release): - if future.finished: - var msg = "" - msg.add("An attempt was made to complete a Future more than once. ") - msg.add("Details:") - msg.add("\n Future ID: " & $future.id) - msg.add("\n Created in proc: " & future.fromProc) - msg.add("\n Stack trace to moment of creation:") - msg.add("\n" & indent(future.stackTrace.strip(), 4)) - when T is string: - msg.add("\n Contents (string): ") - msg.add("\n" & indent(future.value.repr, 4)) - msg.add("\n Stack trace to moment of secondary completion:") - msg.add("\n" & indent(getStackTrace().strip(), 4)) - var err = newException(FutureError, msg) - err.cause = future - raise err - -proc complete*[T](future: Future[T], val: T) = - ## Completes ``future`` with value ``val``. - #assert(not future.finished, "Future already finished, cannot finish twice.") - checkFinished(future) - assert(future.error == nil) - future.value = val - future.finished = true - if future.cb != nil: - future.cb() - -proc complete*(future: Future[void]) = - ## Completes a void ``future``. - #assert(not future.finished, "Future already finished, cannot finish twice.") - checkFinished(future) - assert(future.error == nil) - future.finished = true - if future.cb != nil: - future.cb() - -proc complete*[T](future: FutureVar[T]) = - ## Completes a ``FutureVar``. - template fut: expr = Future[T](future) - checkFinished(fut) - assert(fut.error == nil) - fut.finished = true - if fut.cb != nil: - fut.cb() - -proc fail*[T](future: Future[T], error: ref Exception) = - ## Completes ``future`` with ``error``. - #assert(not future.finished, "Future already finished, cannot finish twice.") - checkFinished(future) - future.finished = true - future.error = error - future.errorStackTrace = - if getStackTrace(error) == "": getStackTrace() else: getStackTrace(error) - if future.cb != nil: - future.cb() - else: - # This is to prevent exceptions from being silently ignored when a future - # is discarded. - # TODO: This may turn out to be a bad idea. - # Turns out this is a bad idea. - #raise error - discard - -proc `callback=`*(future: FutureBase, cb: proc () {.closure,gcsafe.}) = - ## Sets the callback proc to be called when the future completes. - ## - ## If future has already completed then ``cb`` will be called immediately. - ## - ## **Note**: You most likely want the other ``callback`` setter which - ## passes ``future`` as a param to the callback. - future.cb = cb - if future.finished: - callSoon(future.cb) - -proc `callback=`*[T](future: Future[T], - cb: proc (future: Future[T]) {.closure,gcsafe.}) = - ## Sets the callback proc to be called when the future completes. - ## - ## If future has already completed then ``cb`` will be called immediately. - future.callback = proc () = cb(future) - -proc injectStacktrace[T](future: Future[T]) = - # TODO: Come up with something better. - when not defined(release): - var msg = "" - msg.add("\n " & future.fromProc & "'s lead up to read of failed Future:") - - if not future.errorStackTrace.isNil and future.errorStackTrace != "": - msg.add("\n" & indent(future.errorStackTrace.strip(), 4)) - else: - msg.add("\n Empty or nil stack trace.") - future.error.msg.add(msg) - -proc read*[T](future: Future[T]): T = - ## Retrieves the value of ``future``. Future must be finished otherwise - ## this function will fail with a ``ValueError`` exception. - ## - ## If the result of the future is an error then that error will be raised. - if future.finished: - if future.error != nil: - injectStacktrace(future) - raise future.error - when T isnot void: - return future.value - else: - # TODO: Make a custom exception type for this? - raise newException(ValueError, "Future still in progress.") - -proc readError*[T](future: Future[T]): ref Exception = - ## Retrieves the exception stored in ``future``. - ## - ## An ``ValueError`` exception will be thrown if no exception exists - ## in the specified Future. - if future.error != nil: return future.error - else: - raise newException(ValueError, "No error in future.") - -proc mget*[T](future: FutureVar[T]): var T = - ## Returns a mutable value stored in ``future``. - ## - ## Unlike ``read``, this function will not raise an exception if the - ## Future has not been finished. - result = Future[T](future).value - -proc finished*[T](future: Future[T]): bool = - ## Determines whether ``future`` has completed. - ## - ## ``True`` may indicate an error or a value. Use ``failed`` to distinguish. - future.finished - -proc failed*(future: FutureBase): bool = - ## Determines whether ``future`` completed with an error. - return future.error != nil - -proc asyncCheck*[T](future: Future[T]) = - ## Sets a callback on ``future`` which raises an exception if the future - ## finished with an error. - ## - ## This should be used instead of ``discard`` to discard void futures. - future.callback = - proc () = - if future.failed: - injectStacktrace(future) - raise future.error - -proc `and`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = - ## Returns a future which will complete once both ``fut1`` and ``fut2`` - ## complete. - var retFuture = newFuture[void]("asyncdispatch.`and`") - fut1.callback = - proc () = - if not retFuture.finished: - if fut1.failed: retFuture.fail(fut1.error) - elif fut2.finished: retFuture.complete() - fut2.callback = - proc () = - if not retFuture.finished: - if fut2.failed: retFuture.fail(fut2.error) - elif fut1.finished: retFuture.complete() - return retFuture - -proc `or`*[T, Y](fut1: Future[T], fut2: Future[Y]): Future[void] = - ## Returns a future which will complete once either ``fut1`` or ``fut2`` - ## complete. - var retFuture = newFuture[void]("asyncdispatch.`or`") - proc cb[X](fut: Future[X]) = - if fut.failed: retFuture.fail(fut.error) - if not retFuture.finished: retFuture.complete() - fut1.callback = cb[T] - fut2.callback = cb[Y] - return retFuture - -proc all*[T](futs: varargs[Future[T]]): auto = - ## Returns a future which will complete once - ## all futures in ``futs`` complete. - ## - ## If the awaited futures are not ``Future[void]``, the returned future - ## will hold the values of all awaited futures in a sequence. - ## - ## If the awaited futures *are* ``Future[void]``, - ## this proc returns ``Future[void]``. - - when T is void: - var - retFuture = newFuture[void]("asyncdispatch.all") - completedFutures = 0 - - let totalFutures = len(futs) - - for fut in futs: - fut.callback = proc(f: Future[T]) = - if f.failed: - retFuture.fail(f.error) - elif not retFuture.finished: - inc(completedFutures) - - if completedFutures == totalFutures: - retFuture.complete() - - return retFuture - - else: - var - retFuture = newFuture[seq[T]]("asyncdispatch.all") - retValues = newSeq[T](len(futs)) - completedFutures = 0 - - for i, fut in futs: - proc setCallback(i: int) = - fut.callback = proc(f: Future[T]) = - if f.failed: - retFuture.fail(f.error) - elif not retFuture.finished: - retValues[i] = f.read() - inc(completedFutures) - - if completedFutures == len(retValues): - retFuture.complete(retValues) - - setCallback(i) - - return retFuture +include "../includes/asyncfutures" type PDispatcherBase = ref object of RootRef @@ -522,43 +243,44 @@ when defined(windows) or defined(nimdoc): if at == -1: winlean.INFINITE else: at.int32 - var lpNumberOfBytesTransferred: Dword - var lpCompletionKey: ULONG_PTR - var customOverlapped: PCustomOverlapped - let res = getQueuedCompletionStatus(p.ioPort, - addr lpNumberOfBytesTransferred, addr lpCompletionKey, - cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool - - # http://stackoverflow.com/a/12277264/492186 - # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html - if res: - # This is useful for ensuring the reliability of the overlapped struct. - assert customOverlapped.data.fd == lpCompletionKey.AsyncFD - - customOverlapped.data.cb(customOverlapped.data.fd, - lpNumberOfBytesTransferred, OSErrorCode(-1)) - - # If cell.data != nil, then system.protect(rawEnv(cb)) was called, - # so we need to dispose our `cb` environment, because it is not needed - # anymore. - if customOverlapped.data.cell.data != nil: - system.dispose(customOverlapped.data.cell) - - GC_unref(customOverlapped) - else: - let errCode = osLastError() - if customOverlapped != nil: + if p.handles.len != 0: + var lpNumberOfBytesTransferred: Dword + var lpCompletionKey: ULONG_PTR + var customOverlapped: PCustomOverlapped + let res = getQueuedCompletionStatus(p.ioPort, + addr lpNumberOfBytesTransferred, addr lpCompletionKey, + cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool + + # http://stackoverflow.com/a/12277264/492186 + # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html + if res: + # This is useful for ensuring the reliability of the overlapped struct. assert customOverlapped.data.fd == lpCompletionKey.AsyncFD + customOverlapped.data.cb(customOverlapped.data.fd, - lpNumberOfBytesTransferred, errCode) + lpNumberOfBytesTransferred, OSErrorCode(-1)) + + # If cell.data != nil, then system.protect(rawEnv(cb)) was called, + # so we need to dispose our `cb` environment, because it is not needed + # anymore. if customOverlapped.data.cell.data != nil: system.dispose(customOverlapped.data.cell) + GC_unref(customOverlapped) else: - if errCode.int32 == WAIT_TIMEOUT: - # Timed out - discard - else: raiseOSError(errCode) + let errCode = osLastError() + if customOverlapped != nil: + assert customOverlapped.data.fd == lpCompletionKey.AsyncFD + customOverlapped.data.cb(customOverlapped.data.fd, + lpNumberOfBytesTransferred, errCode) + if customOverlapped.data.cell.data != nil: + system.dispose(customOverlapped.data.cell) + GC_unref(customOverlapped) + else: + if errCode.int32 == WAIT_TIMEOUT: + # Timed out + discard + else: raiseOSError(errCode) # Timer processing. processTimers(p) @@ -1472,21 +1194,21 @@ else: var fd = keys[i].fd.SocketHandle let events = keys[i].events - if Event.Read in events: + if Event.Read in events or events == {Event.Error}: let cb = keys[i].data.readCB - doAssert(cb != nil) - if cb(fd.AsyncFD): - p.selector.withData(fd, adata) do: - if adata.readCB == cb: - adata.readCB = nil + if cb != nil: + if cb(fd.AsyncFD): + p.selector.withData(fd, adata) do: + if adata.readCB == cb: + adata.readCB = nil - if Event.Write in events: + if Event.Write in events or events == {Event.Error}: let cb = keys[i].data.writeCB - doAssert(cb != nil) - if cb(fd.AsyncFD): - p.selector.withData(fd, adata) do: - if adata.writeCB == cb: - adata.writeCB = nil + if cb != nil: + if cb(fd.AsyncFD): + p.selector.withData(fd, adata) do: + if adata.writeCB == cb: + adata.writeCB = nil when supportedPlatform: if (customSet * events) != {}: diff --git a/lib/windows/registry.nim b/lib/windows/registry.nim new file mode 100644 index 000000000..06f84c881 --- /dev/null +++ b/lib/windows/registry.nim @@ -0,0 +1,77 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2016 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module is experimental and its interface may change. + +import winlean, os + +type + HKEY* = uint + +const + HKEY_LOCAL_MACHINE* = HKEY(0x80000002u) + HKEY_CURRENT_USER* = HKEY(2147483649) + + RRF_RT_ANY = 0x0000ffff + KEY_WOW64_64KEY = 0x0100 + KEY_WOW64_32KEY = 0x0200 + KEY_READ = 0x00020019 + REG_SZ = 1 + +proc regOpenKeyEx(hKey: HKEY, lpSubKey: WideCString, ulOptions: int32, + samDesired: int32, + phkResult: var HKEY): int32 {. + importc: "RegOpenKeyExW", dynlib: "Advapi32.dll", stdcall.} + +proc regCloseKey(hkey: HKEY): int32 {. + importc: "RegCloseKey", dynlib: "Advapi32.dll", stdcall.} + +proc regGetValue(key: HKEY, lpSubKey, lpValue: WideCString; + dwFlags: int32 = RRF_RT_ANY, pdwType: ptr int32, + pvData: pointer, + pcbData: ptr int32): int32 {. + importc: "RegGetValueW", dynlib: "Advapi32.dll", stdcall.} + +template call(f) = + let err = f + if err != 0: + raiseOSError(err.OSErrorCode, astToStr(f)) + +proc getUnicodeValue*(path, key: string; handle: HKEY): string = + let hh = newWideCString path + let kk = newWideCString key + var bufsize: int32 + # try a couple of different flag settings: + var flags: int32 = RRF_RT_ANY + let err = regGetValue(handle, hh, kk, flags, nil, nil, addr bufsize) + if err != 0: + var newHandle: HKEY + call regOpenKeyEx(handle, hh, 0, KEY_READ or KEY_WOW64_64KEY, newHandle) + call regGetValue(newHandle, nil, kk, flags, nil, nil, addr bufsize) + var res = newWideCString("", bufsize) + call regGetValue(newHandle, nil, kk, flags, nil, cast[pointer](res), + addr bufsize) + result = res $ bufsize + call regCloseKey(newHandle) + else: + var res = newWideCString("", bufsize) + call regGetValue(handle, hh, kk, flags, nil, cast[pointer](res), + addr bufsize) + result = res $ bufsize + +proc regSetValue(key: HKEY, lpSubKey, lpValueName: WideCString, + dwType: int32; lpData: WideCString; cbData: int32): int32 {. + importc: "RegSetKeyValueW", dynlib: "Advapi32.dll", stdcall.} + +proc setUnicodeValue*(path, key, val: string; handle: HKey) = + let hh = newWideCString path + let kk = newWideCString key + let vv = newWideCString val + call regSetValue(handle, hh, kk, REG_SZ, vv, (vv.len.int32+1)*2) + diff --git a/lib/wrappers/linenoise/clinenoise.c b/lib/wrappers/linenoise/clinenoise.c index a03e6f379..c76fc6586 100644 --- a/lib/wrappers/linenoise/clinenoise.c +++ b/lib/wrappers/linenoise/clinenoise.c @@ -116,7 +116,9 @@ #include <sys/types.h> #include <sys/ioctl.h> #include <unistd.h> -#include "clinenoise.h" +#ifndef __LINENOISE_H +# include "clinenoise.h" +#endif #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 #define LINENOISE_MAX_LINE 4096 diff --git a/lib/wrappers/linenoise/linenoise.nim b/lib/wrappers/linenoise/linenoise.nim index b7004c6e2..4ac7bf4a8 100644 --- a/lib/wrappers/linenoise/linenoise.nim +++ b/lib/wrappers/linenoise/linenoise.nim @@ -14,7 +14,8 @@ type CompletionCallback* = proc (a2: cstring; a3: ptr Completions) {.cdecl.} -{.compile: "clinenoise.c".} +{.emit: staticRead"clinenoise.h".} +{.emit: staticRead"clinenoise.c".} proc setCompletionCallback*(a2: ptr CompletionCallback) {. importc: "linenoiseSetCompletionCallback".} diff --git a/lib/wrappers/mysql.nim b/lib/wrappers/mysql.nim index af504864d..6dbed23b3 100644 --- a/lib/wrappers/mysql.nim +++ b/lib/wrappers/mysql.nim @@ -13,10 +13,10 @@ when defined(Unix): when defined(macosx): const - lib = "libmysqlclient.(15|16|17|18).dylib" + lib = "libmysqlclient.(15|16|17|18|19|20).dylib" else: const - lib = "libmysqlclient.so.(15|16|17|18)" + lib = "libmysqlclient.so.(15|16|17|18|19|20)" when defined(Windows): const lib = "libmysql.dll" diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 9dad7e489..204e5bb40 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -26,7 +26,7 @@ when useWinVersion: from winlean import SocketHandle else: const - versions = "(|.10|.1.0.1|.1.0.0|.0.9.9|.0.9.8)" + versions = "(|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8)" when defined(macosx): const DLLSSLName = "libssl" & versions & ".dylib" diff --git a/readme.md b/readme.md index 204b51906..b4a484f60 100644 --- a/readme.md +++ b/readme.md @@ -27,7 +27,8 @@ To build from source you will need: are: clang, Visual C++, Intel's C++ compiler * git or wget -**Note:** When installing ``gcc`` on Ubuntu (and likely other distros) ensure that the ``build-essentials`` package is installed also. +**Note:** When installing ``gcc`` on Ubuntu (and likely other distros) ensure +that the ``build-essentials`` package is installed also. If you are on a fairly modern *nix system, the following steps should work: @@ -51,21 +52,16 @@ instead of ``build.sh``. The ``koch`` tool is the Nim build tool, more ``koch`` related options are documented in [doc/koch.rst](doc/koch.rst). + ## Nimble [Nimble](https://github.com/nim-lang/nimble) is Nim's package manager. For the source based installations where you added Nim's ``bin`` directory to your PATH the easiest way of installing Nimble is via: ``` -$ nim e install_nimble.nims +$ koch nimble ``` -**Warning:** If you install Nimble this way, you will not be able to use binary -Nimble packages or update Nimble easily. -The [Nimble readme](https://github.com/nim-lang/nimble#installation) -provides thorough instructions on how to install Nimble, so that this isn't a -problem. - ## Community [![Join the Chat at irc.freenode.net#nim](https://img.shields.io/badge/IRC-join_chat_in_%23nim-blue.svg)](https://webchat.freenode.net/?channels=nim) [![Join the Gitter channel](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/nim-lang/Nim) diff --git a/tests/async/tasynceverror.nim b/tests/async/tasynceverror.nim deleted file mode 100644 index dd05c831b..000000000 --- a/tests/async/tasynceverror.nim +++ /dev/null @@ -1,66 +0,0 @@ -discard """ - file: "tasynceverror.nim" - exitcode: 1 - outputsub: "Error: unhandled exception: " -""" -# error message is actually different on OSX -import - asyncdispatch, - asyncnet, - nativesockets, - os - - -const - testHost = "127.0.0.1" - testPort = Port(17357) - - -when defined(windows) or defined(nimdoc): - # TODO: just make it work on Windows for now. - quit("Error: unhandled exception: Connection reset by peer") -else: - proc createListenSocket(host: string, port: Port): TAsyncFD = - result = newAsyncNativeSocket() - - SocketHandle(result).setSockOptInt(SOL_SOCKET, SO_REUSEADDR, 1) - - var aiList = getAddrInfo(host, port, AF_INET) - if SocketHandle(result).bindAddr(aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32: - dealloc(aiList) - raiseOSError(osLastError()) - dealloc(aiList) - - if SocketHandle(result).listen(1) < 0'i32: - raiseOSError(osLastError()) - - - proc testAsyncSend() {.async.} = - var - ls = createListenSocket(testHost, testPort) - s = newAsyncSocket() - - await s.connect(testHost, testPort) - - var ps = await ls.accept() - closeSocket(ls) - - await ps.send("test 1", flags={}) - s.close() - # This send should raise EPIPE - await ps.send("test 2", flags={}) - SocketHandle(ps).close() - - - # The bug was, when the poll function handled EvError for us, - # our callbacks may never get executed, thus making the event - # loop block indefinitely. This is a timer to keep everything - # rolling. 400 ms is an arbitrary value, should be enough though. - proc timer() {.async.} = - await sleepAsync(400) - echo("Timer expired.") - quit(2) - - - asyncCheck(testAsyncSend()) - waitFor(timer()) diff --git a/tests/async/tfuturevar.nim b/tests/async/tfuturevar.nim new file mode 100644 index 000000000..73c0fddf7 --- /dev/null +++ b/tests/async/tfuturevar.nim @@ -0,0 +1,47 @@ +import asyncdispatch + +proc completeOnReturn(fut: FutureVar[string], x: bool) {.async.} = + if x: + fut.mget() = "" + fut.mget.add("foobar") + return + +proc completeOnImplicitReturn(fut: FutureVar[string], x: bool) {.async.} = + if x: + fut.mget() = "" + fut.mget.add("foobar") + +proc failureTest(fut: FutureVar[string], x: bool) {.async.} = + if x: + raise newException(Exception, "Test") + +proc manualComplete(fut: FutureVar[string], x: bool) {.async.} = + if x: + fut.mget() = "Hello World" + fut.complete() + return + +proc main() {.async.} = + var fut: FutureVar[string] + + fut = newFutureVar[string]() + await completeOnReturn(fut, true) + doAssert(fut.read() == "foobar") + + fut = newFutureVar[string]() + await completeOnImplicitReturn(fut, true) + doAssert(fut.read() == "foobar") + + fut = newFutureVar[string]() + let retFut = failureTest(fut, true) + yield retFut + doAssert(fut.read().isNil) + doAssert(fut.finished) + + fut = newFutureVar[string]() + await manualComplete(fut, true) + doAssert(fut.read() == "Hello World") + + +waitFor main() + diff --git a/tests/async/tgenericasync.nim b/tests/async/tgenericasync.nim new file mode 100644 index 000000000..ab704238a --- /dev/null +++ b/tests/async/tgenericasync.nim @@ -0,0 +1,14 @@ +discard """ + output: '''123 +abc''' +""" + +# bug #4856 + +import asyncdispatch + +proc say[T](t: T): Future[void] {.async.} = + echo $t + +waitFor(say(123)) +waitFor(say("abc")) diff --git a/tests/async/tioselectors.nim b/tests/async/tioselectors.nim index 2237de01a..71d901e69 100644 --- a/tests/async/tioselectors.nim +++ b/tests/async/tioselectors.nim @@ -217,6 +217,216 @@ when not defined(windows): assert(selector.isEmpty()) result = true + when defined(macosx) or defined(freebsd) or defined(openbsd) or + defined(netbsd): + + proc rename(frompath: cstring, topath: cstring): cint + {.importc: "rename", header: "<stdio.h>".} + + proc createFile(name: string): cint = + result = posix.open(cstring(name), posix.O_CREAT or posix.O_RDWR) + if result == -1: + raiseOsError(osLastError()) + + proc writeFile(name: string, data: string) = + let fd = posix.open(cstring(name), posix.O_APPEND or posix.O_RDWR) + if fd == -1: + raiseOsError(osLastError()) + let length = len(data).cint + if posix.write(fd, cast[pointer](unsafeAddr data[0]), + len(data).cint) != length: + raiseOsError(osLastError()) + if posix.close(fd) == -1: + raiseOsError(osLastError()) + + proc closeFile(fd: cint) = + if posix.close(fd) == -1: + raiseOsError(osLastError()) + + proc removeFile(name: string) = + let err = posix.unlink(cstring(name)) + if err == -1: + raiseOsError(osLastError()) + + proc createDir(name: string) = + let err = posix.mkdir(cstring(name), 0x1FF) + if err == -1: + raiseOsError(osLastError()) + + proc removeDir(name: string) = + let err = posix.rmdir(cstring(name)) + if err == -1: + raiseOsError(osLastError()) + + proc chmodPath(name: string, mode: cint) = + let err = posix.chmod(cstring(name), Mode(mode)) + if err == -1: + raiseOsError(osLastError()) + + proc renameFile(names: string, named: string) = + let err = rename(cstring(names), cstring(named)) + if err == -1: + raiseOsError(osLastError()) + + proc symlink(names: string, named: string) = + let err = posix.symlink(cstring(names), cstring(named)) + if err == -1: + raiseOsError(osLastError()) + + proc openWatch(name: string): cint = + result = posix.open(cstring(name), posix.O_RDONLY) + if result == -1: + raiseOsError(osLastError()) + + const + testDirectory = "/tmp/kqtest" + + type + valType = object + fd: cint + events: set[Event] + + proc vnode_test(): bool = + proc validate[T](test: openarray[ReadyKey[T]], + check: openarray[valType]): bool = + result = false + if len(test) == len(check): + for checkItem in check: + result = false + for testItem in test: + if testItem.fd == checkItem.fd and + checkItem.events <= testItem.events: + result = true + break + if not result: + break + + var res: seq[ReadyKey[int]] + var selector = newSelector[int]() + var events = {Event.VnodeWrite, Event.VnodeDelete, Event.VnodeExtend, + Event.VnodeAttrib, Event.VnodeLink, Event.VnodeRename, + Event.VnodeRevoke} + + result = true + discard posix.unlink(testDirectory) + + createDir(testDirectory) + var dirfd = posix.open(cstring(testDirectory), posix.O_RDONLY) + if dirfd == -1: + raiseOsError(osLastError()) + + selector.registerVnode(dirfd, events, 1) + selector.flush() + + # chmod testDirectory to 0777 + chmodPath(testDirectory, 0x1FF) + res = selector.select(0) + doAssert(len(res) == 1) + doAssert(len(selector.select(0)) == 0) + doAssert(res[0].fd == dirfd and + {Event.Vnode, Event.VnodeAttrib} <= res[0].events) + + # create subdirectory + createDir(testDirectory & "/test") + res = selector.select(0) + doAssert(len(res) == 1) + doAssert(len(selector.select(0)) == 0) + doAssert(res[0].fd == dirfd and + {Event.Vnode, Event.VnodeWrite, + Event.VnodeLink} <= res[0].events) + + # open test directory for watching + var testfd = openWatch(testDirectory & "/test") + selector.registerVnode(testfd, events, 2) + selector.flush() + doAssert(len(selector.select(0)) == 0) + + # rename test directory + renameFile(testDirectory & "/test", testDirectory & "/renamed") + res = selector.select(0) + doAssert(len(res) == 2) + doAssert(len(selector.select(0)) == 0) + doAssert(validate(res, + [valType(fd: dirfd, events: {Event.Vnode, Event.VnodeWrite}), + valType(fd: testfd, + events: {Event.Vnode, Event.VnodeRename})]) + ) + + # remove test directory + removeDir(testDirectory & "/renamed") + res = selector.select(0) + doAssert(len(res) == 2) + doAssert(len(selector.select(0)) == 0) + doAssert(validate(res, + [valType(fd: dirfd, events: {Event.Vnode, Event.VnodeWrite, + Event.VnodeLink}), + valType(fd: testfd, + events: {Event.Vnode, Event.VnodeDelete})]) + ) + # create file new test file + testfd = createFile(testDirectory & "/testfile") + res = selector.select(0) + doAssert(len(res) == 1) + doAssert(len(selector.select(0)) == 0) + doAssert(res[0].fd == dirfd and + {Event.Vnode, Event.VnodeWrite} <= res[0].events) + + # close new test file + closeFile(testfd) + doAssert(len(selector.select(0)) == 0) + doAssert(len(selector.select(0)) == 0) + + # chmod test file with 0666 + chmodPath(testDirectory & "/testfile", 0x1B6) + doAssert(len(selector.select(0)) == 0) + + testfd = openWatch(testDirectory & "/testfile") + selector.registerVnode(testfd, events, 1) + selector.flush() + + # write data to test file + writeFile(testDirectory & "/testfile", "TESTDATA") + res = selector.select(0) + doAssert(len(res) == 1) + doAssert(len(selector.select(0)) == 0) + doAssert(res[0].fd == testfd and + {Event.Vnode, Event.VnodeWrite, + Event.VnodeExtend} <= res[0].events) + + # symlink test file + symlink(testDirectory & "/testfile", testDirectory & "/testlink") + res = selector.select(0) + doAssert(len(res) == 1) + doAssert(len(selector.select(0)) == 0) + doAssert(res[0].fd == dirfd and + {Event.Vnode, Event.VnodeWrite} <= res[0].events) + + # remove test file + removeFile(testDirectory & "/testfile") + res = selector.select(0) + doAssert(len(res) == 2) + doAssert(len(selector.select(0)) == 0) + doAssert(validate(res, + [valType(fd: testfd, events: {Event.Vnode, Event.VnodeDelete}), + valType(fd: dirfd, events: {Event.Vnode, Event.VnodeWrite})]) + ) + + # remove symlink + removeFile(testDirectory & "/testlink") + res = selector.select(0) + doAssert(len(res) == 1) + doAssert(len(selector.select(0)) == 0) + doAssert(res[0].fd == dirfd and + {Event.Vnode, Event.VnodeWrite} <= res[0].events) + + # remove testDirectory + removeDir(testDirectory) + res = selector.select(0) + doAssert(len(res) == 1) + doAssert(len(selector.select(0)) == 0) + doAssert(res[0].fd == dirfd and + {Event.Vnode, Event.VnodeDelete} <= res[0].events) + when hasThreadSupport: var counter = 0 @@ -256,6 +466,9 @@ when not defined(windows): processTest("Timer notification test...", timer_notification_test()) processTest("Process notification test...", process_notification_test()) processTest("Signal notification test...", signal_notification_test()) + when defined(macosx) or defined(freebsd) or defined(openbsd) or + defined(netbsd): + processTest("File notification test...", vnode_test()) echo("All tests passed!") else: import nativesockets, winlean, os, osproc diff --git a/tests/async/tupcoming_async.nim b/tests/async/tupcoming_async.nim index e53482dae..137794afd 100644 --- a/tests/async/tupcoming_async.nim +++ b/tests/async/tupcoming_async.nim @@ -44,19 +44,8 @@ when defined(upcoming): ev.setEvent() proc timerTest() = - var timeout = 200 - var errorRate = 40.0 - var start = epochTime() waitFor(waitTimer(200)) - var finish = epochTime() - var lowlimit = float(timeout) - float(timeout) * errorRate / 100.0 - var highlimit = float(timeout) + float(timeout) * errorRate / 100.0 - var elapsed = (finish - start) * 1_000 # convert to milliseconds - if elapsed >= lowlimit and elapsed < highlimit: - echo "OK" - else: - echo "timerTest: Timeout = " & $(elapsed) & ", but must be inside of [" & - $lowlimit & ", " & $highlimit & ")" + echo "OK" proc eventTest() = var event = newAsyncEvent() diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim index 59fef4920..4f286d0ed 100644 --- a/tests/collections/ttables.nim +++ b/tests/collections/ttables.nim @@ -95,9 +95,24 @@ block orderedTableTest1: for key, val in mpairs(t): val = 99 for val in mvalues(t): assert val == 99 +block orderedTableTest2: + var + s = initOrderedTable[string, int]() + t = initOrderedTable[string, int]() + assert s == t + for key, val in items(data): t[key] = val + assert s != t + for key, val in items(sorteddata): s[key] = val + assert s != t + t.clear() + assert s != t + for key, val in items(sorteddata): t[key] = val + assert s == t + block countTableTest1: var s = data.toTable var t = initCountTable[string]() + for k in s.keys: t.inc(k) for k in t.keys: assert t[k] == 1 t.inc("90", 3) @@ -115,6 +130,24 @@ block countTableTest1: else: break inc i +block countTableTest2: + var + s = initCountTable[int]() + t = initCountTable[int]() + assert s == t + s.inc(1) + assert s != t + t.inc(2) + assert s != t + t.inc(1) + assert s != t + s.inc(2) + assert s == t + s.inc(1) + assert s != t + t.inc(1) + assert s == t + block mpairsTableTest1: var t = initTable[string, int]() t["a"] = 1 diff --git a/tests/float/tfloat4.nim b/tests/float/tfloat4.nim index 006b4d88f..d7783ce26 100644 --- a/tests/float/tfloat4.nim +++ b/tests/float/tfloat4.nim @@ -30,8 +30,10 @@ let testFloats = [ "0.00097656250000000021684043449710088680149056017398834228515625" ] -for num in testFloats: - doAssert num.parseFloat.floatToStr.parseFloat == num.parseFloat +when not defined(windows): + # Windows' sprintf produces niceties like -1.#INF... + for num in testFloats: + doAssert num.parseFloat.floatToStr.parseFloat == num.parseFloat doAssert "0".parseFloat == 0.0 doAssert "-.1".parseFloat == -0.1 diff --git a/tests/generics/tstatictalias.nim b/tests/generics/tstatictalias.nim new file mode 100644 index 000000000..98751b8cb --- /dev/null +++ b/tests/generics/tstatictalias.nim @@ -0,0 +1,20 @@ +discard """ + output: '''G:0,1:0.1 +G:0,1:0.1 +H:1:0.1''' +""" + +type + G[i,j:static[int]] = object + v:float + H[j:static[int]] = G[0,j] +proc p[i,j:static[int]](x:G[i,j]) = echo "G:",i,",",j,":",x.v +proc q[j:static[int]](x:H[j]) = echo "H:",j,":",x.v + +var + g0 = G[0,1](v: 0.1) + h0:H[1] = g0 +p(g0) +p(h0) +q(h0) +# bug #4863 diff --git a/tests/js/tconsole.nim b/tests/js/tconsole.nim new file mode 100644 index 000000000..f6da71c20 --- /dev/null +++ b/tests/js/tconsole.nim @@ -0,0 +1,13 @@ +discard """ + output: '''Hello, console +1 2 3 +1 'hi' 1.1''' +""" + +# This file tests the JavaScript console + +import jsconsole + +console.log("Hello, console") +console.log(1, 2, 3) +console.log(1, "hi", 1.1) \ No newline at end of file diff --git a/tests/osproc/tafalse.nim b/tests/osproc/tafalse.nim new file mode 100644 index 000000000..24fd4fb2e --- /dev/null +++ b/tests/osproc/tafalse.nim @@ -0,0 +1,3 @@ +# 'tafalse.nim' to ensure it is compiled before texitcode.nim +import system +quit(QuitFailure) diff --git a/tests/osproc/texitcode.nim b/tests/osproc/texitcode.nim new file mode 100644 index 000000000..1e83658c2 --- /dev/null +++ b/tests/osproc/texitcode.nim @@ -0,0 +1,18 @@ +discard """ + file: "texitcode.nim" + output: "" +""" +import osproc, os + +const filename = when defined(Windows): "tafalse.exe" else: "tafalse" +let dir = getCurrentDir() / "tests" / "osproc" +doAssert fileExists(dir / filename) + +var p = startProcess(filename, dir) +doAssert(waitForExit(p) == QuitFailure) + +p = startProcess(filename, dir) +var running = true +while running: + running = running(p) +doAssert(waitForExit(p) == QuitFailure) diff --git a/tests/osproc/tworkingdir.nim b/tests/osproc/tworkingdir.nim new file mode 100644 index 000000000..84ba3375c --- /dev/null +++ b/tests/osproc/tworkingdir.nim @@ -0,0 +1,16 @@ +discard """ + file: "tworkingdir.nim" + output: "" +""" + +import osproc, os +when defined(windows): + # Windows don't have this issue, so we won't test it. + discard +else: + let dir1 = getCurrentDir() + var process = startProcess("/usr/bin/env", "/usr/bin", ["true"]) + let dir2 = getCurrentDir() + discard process.waitForExit() + process.close() + doAssert(dir1 == dir2, $dir1 & " != " & $dir2) diff --git a/tests/parallel/tsendtwice.nim b/tests/parallel/tsendtwice.nim new file mode 100644 index 000000000..0700fc4da --- /dev/null +++ b/tests/parallel/tsendtwice.nim @@ -0,0 +1,71 @@ +discard """ + output: '''obj2 nil +obj nil +obj3 nil +3 +obj2 nil +obj nil +obj3 nil''' + cmd: "nim c -r --threads:on $file" +""" + +# bug #4776 + +import tables + +type + Base* = ref object of RootObj + someSeq: seq[int] + baseData: array[400000, byte] + Derived* = ref object of Base + data: array[400000, byte] + +type + ThreadPool = ref object + threads: seq[ptr Thread[ThreadArg]] + channels: seq[ThreadArg] + TableChannel = Channel[TableRef[string, Base]] + ThreadArg = ptr TableChannel + +var globalTable {.threadvar.}: TableRef[string, Base] +globalTable = newTable[string, Base]() +let d = new(Derived) +globalTable.add("obj", d) +globalTable.add("obj2", d) +globalTable.add("obj3", d) + +proc testThread(channel: ptr TableChannel) {.thread.} = + globalTable = channel[].recv() + for k, v in pairs globaltable: + echo k, " ", v.someSeq + var myObj: Base + deepCopy(myObj, globalTable["obj"]) + myObj.someSeq = newSeq[int](100) + let table = channel[].recv() # same table + echo table.len + for k, v in mpairs table: + echo k, " ", v.someSeq + assert(table.contains("obj")) # fails! + assert(table.contains("obj2")) # fails! + assert(table.contains("obj3")) # fails! + +var channel: TableChannel + +proc newThreadPool(threadCount: int) = #: ThreadPool = + #new(result) + #result.threads = newSeq[ptr Thread[ThreadArg]](threadCount) + #var channel = cast[ptr TableChannel](allocShared0(sizeof(TableChannel))) + channel.open() + channel.send(globalTable) + channel.send(globalTable) + #createThread(threadPtr[], testThread, addr channel) + testThread(addr channel) + #result.threads[i] = threadPtr + +proc stop(p: ThreadPool) = + for t in p.threads: + joinThread(t[]) + dealloc(t) + + +newThreadPool(1)#.stop() diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim index 9412a5afa..dd9a6139a 100644 --- a/tests/stdlib/thttpclient.nim +++ b/tests/stdlib/thttpclient.nim @@ -7,6 +7,8 @@ from net import TimeoutError import httpclient, asyncdispatch +const manualTests = false + proc asyncTest() {.async.} = var client = newAsyncHttpClient() var resp = await client.request("http://example.com/") @@ -20,12 +22,40 @@ proc asyncTest() {.async.} = resp = await client.request("https://google.com/") doAssert(resp.code.is2xx or resp.code.is3xx) + + # getContent + try: + discard await client.getContent("https://google.com/404") + doAssert(false, "HttpRequestError should have been raised") + except HttpRequestError: + discard + except: + doAssert(false, "HttpRequestError should have been raised") + + + # Multipart test. + var data = newMultipartData() + data["output"] = "soap12" + data["uploaded_file"] = ("test.html", "text/html", + "<html><head></head><body><p>test</p></body></html>") + resp = await client.post("http://validator.w3.org/check", multipart=data) + doAssert(resp.code.is2xx) + + # onProgressChanged + when manualTests: + proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} = + echo("Downloaded ", progress, " of ", total) + echo("Current rate: ", speed div 1000, "kb/s") + client.onProgressChanged = onProgressChanged + discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test") + client.close() # Proxy test - #client = newAsyncHttpClient(proxy = newProxy("http://51.254.106.76:80/")) - #var resp = await client.request("https://github.com") - #echo resp + #when manualTests: + # client = newAsyncHttpClient(proxy = newProxy("http://51.254.106.76:80/")) + # var resp = await client.request("https://github.com") + # echo resp proc syncTest() = var client = newHttpClient() @@ -41,6 +71,31 @@ proc syncTest() = resp = client.request("https://google.com/") doAssert(resp.code.is2xx or resp.code.is3xx) + # getContent + try: + discard client.getContent("https://google.com/404") + doAssert(false, "HttpRequestError should have been raised") + except HttpRequestError: + discard + except: + doAssert(false, "HttpRequestError should have been raised") + + # Multipart test. + var data = newMultipartData() + data["output"] = "soap12" + data["uploaded_file"] = ("test.html", "text/html", + "<html><head></head><body><p>test</p></body></html>") + resp = client.post("http://validator.w3.org/check", multipart=data) + doAssert(resp.code.is2xx) + + # onProgressChanged + when manualTests: + proc onProgressChanged(total, progress, speed: BiggestInt) = + echo("Downloaded ", progress, " of ", total) + echo("Current rate: ", speed div 1000, "kb/s") + client.onProgressChanged = onProgressChanged + discard client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test") + client.close() # Timeout test. @@ -56,21 +111,3 @@ proc syncTest() = syncTest() waitFor(asyncTest()) - -#[ - - else: - #downloadFile("http://force7.de/nim/index.html", "nimindex.html") - #downloadFile("http://www.httpwatch.com/", "ChunkTest.html") - #downloadFile("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com", - # "validator.html") - - #var r = get("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com& - # charset=%28detect+automatically%29&doctype=Inline&group=0") - - var data = newMultipartData() - data["output"] = "soap12" - data["uploaded_file"] = ("test.html", "text/html", - "<html><head></head><body><p>test</p></body></html>") - - echo postContent("http://validator.w3.org/check", multipart=data)]# diff --git a/tests/stdlib/ttime.nim b/tests/stdlib/ttime.nim index 3ab287c4e..065009535 100644 --- a/tests/stdlib/ttime.nim +++ b/tests/stdlib/ttime.nim @@ -140,4 +140,27 @@ doAssert initInterval(months = 13) == initInterval(months = 1, years = 1) # Bug with adding a day to a Time let day = 24.hours let tomorrow = getTime() + day -doAssert tomorrow - getTime() == 60*60*24 \ No newline at end of file +doAssert tomorrow - getTime() == 60*60*24 + +doAssert milliseconds(1000 * 60) == minutes(1) +doAssert milliseconds(1000 * 60 * 60) == hours(1) +doAssert milliseconds(1000 * 60 * 60 * 24) == days(1) +doAssert seconds(60 * 60) == hours(1) +doAssert seconds(60 * 60 * 24) == days(1) +doAssert seconds(60 * 60 + 65) == (hours(1) + minutes(1) + seconds(5)) + +# Bug with parse not setting DST properly if the current local DST differs from +# the date being parsed. Need to test parse dates both in and out of DST. We +# are testing that be relying on the fact that tranforming a TimeInfo to a Time +# and back again will correctly set the DST value. With the incorrect parse +# behavior this will introduce a one hour offset from the named time and the +# parsed time if the DST value differs between the current time and the date we +# are parsing. +# +# Unfortunately these tests depend on the locale of the system in which they +# are run. They will not be meaningful when run in a locale without DST. They +# also assume that Jan. 1 and Jun. 1 will have differing isDST values. +let dstT1 = parse("2016-01-01 00:00:00", "yyyy-MM-dd HH:mm:ss") +let dstT2 = parse("2016-06-01 00:00:00", "yyyy-MM-dd HH:mm:ss") +doAssert dstT1 == getLocalTime(toTime(dstT1)) +doAssert dstT2 == getLocalTime(toTime(dstT2)) diff --git a/tests/template/tconfusinglocal.nim b/tests/template/tconfusinglocal.nim new file mode 100644 index 000000000..9b2cdc954 --- /dev/null +++ b/tests/template/tconfusinglocal.nim @@ -0,0 +1,13 @@ + +# bug #4875 +type Bar = object + mFoo: int + +template foo(a: Bar): int = a.mFoo + +proc main = + let foo = 5 # Rename this to smth else to make it work + var b: Bar + echo b.foo + +main() diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 3ed2f2196..2b0b55c0b 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -115,6 +115,12 @@ proc dllTests(r: var TResults, cat: Category, options: string) = # ------------------------------ GC tests ------------------------------------- proc gcTests(r: var TResults, cat: Category, options: string) = + template testWithNone(filename: untyped) = + testSpec r, makeTest("tests/gc" / filename, options & + " --gc:none", cat, actionRun) + testSpec r, makeTest("tests/gc" / filename, options & + " -d:release --gc:none", cat, actionRun) + template testWithoutMs(filename: untyped) = testSpec r, makeTest("tests/gc" / filename, options, cat, actionRun) testSpec r, makeTest("tests/gc" / filename, options & @@ -144,6 +150,7 @@ proc gcTests(r: var TResults, cat: Category, options: string) = test "gcleak" test "gcleak2" test "gctest" + testWithNone "gctest" test "gcleak3" test "gcleak4" # Disabled because it works and takes too long to run: diff --git a/tests/vm/tgorge.bat b/tests/vm/tgorge.bat new file mode 100644 index 000000000..24d365842 --- /dev/null +++ b/tests/vm/tgorge.bat @@ -0,0 +1 @@ +@echo gorge test \ No newline at end of file diff --git a/tests/vm/tgorge.nim b/tests/vm/tgorge.nim new file mode 100644 index 000000000..aeaf54df8 --- /dev/null +++ b/tests/vm/tgorge.nim @@ -0,0 +1,12 @@ +import os + +template getScriptDir(): string = + parentDir(instantiationInfo(-1, true).filename) + +const + execName = when defined(windows): "tgorge.bat" else: "./tgorge.sh" + relOutput = gorge(execName) + absOutput = gorge(getScriptDir() / execName) + +doAssert relOutput == "gorge test" +doAssert absOutput == "gorge test" \ No newline at end of file diff --git a/tests/vm/tgorge.sh b/tests/vm/tgorge.sh new file mode 100755 index 000000000..ba47afeae --- /dev/null +++ b/tests/vm/tgorge.sh @@ -0,0 +1,2 @@ +#!/bin/sh +echo "gorge test" \ No newline at end of file diff --git a/tools/finish.nim b/tools/finish.nim new file mode 100644 index 000000000..8b9acfc56 --- /dev/null +++ b/tools/finish.nim @@ -0,0 +1,165 @@ + +# -------------- post unzip steps --------------------------------------------- + +import strutils, os, osproc + +when defined(windows): + import registry + + proc askBool(m: string): bool = + stdout.write m + while true: + let answer = stdin.readLine().normalize + case answer + of "y", "yes": + return true + of "n", "no": + return false + else: + echo "Please type 'y' or 'n'" + + proc askNumber(m: string; a, b: int): int = + stdout.write m + stdout.write " [" & $a & ".." & $b & "] " + while true: + let answer = stdin.readLine() + try: + result = parseInt answer + if result < a or result > b: + raise newException(ValueError, "number out of range") + break + except ValueError: + echo "Please type in a number between ", a, " and ", b + + proc patchConfig(mingw: string) = + const + cfgFile = "config/nim.cfg" + lookFor = """#gcc.path = r"$nim\dist\mingw\bin"""" + replacePattern = """gcc.path = r"$1"""" + try: + let cfg = readFile(cfgFile) + let newCfg = cfg.replace(lookFor, replacePattern % mingw) + if newCfg == cfg: + echo "Could not patch 'config/nim.cfg' [Error]" + echo "Reason: patch substring not found:" + echo lookFor + else: + writeFile(cfgFile, newCfg) + except IOError: + echo "Could not access 'config/nim.cfg' [Error]" + + proc addToPathEnv(e: string) = + let p = getUnicodeValue(r"Environment", "Path", HKEY_CURRENT_USER) + let x = if e.contains(Whitespace): "\"" & e & "\"" else: e + setUnicodeValue(r"Environment", "Path", p & ";" & x, HKEY_CURRENT_USER) + + proc createShortcut(src, dest: string; icon = "") = + var cmd = "bin\\makelink.exe \"" & src & "\" \"\" \"" & dest & + ".lnk\" \"\" 1 \"" & splitFile(src).dir & "\"" + if icon.len != 0: + cmd.add " \"" & icon & "\" 0" + discard execShellCmd(cmd) + + proc createStartMenuEntry() = + let appdata = getEnv("APPDATA") + if appdata.len == 0: return + let dest = appdata & r"\Microsoft\Windows\Start Menu\Programs\Nim-" & + NimVersion + if dirExists(dest): return + if askBool("Would like to add Nim-" & NimVersion & + " to your start menu? (y/n) "): + createDir(dest) + createShortcut(getCurrentDir() / "tools" / "start.bat", dest / "Nim", + getCurrentDir() / r"icons\nim.ico") + if fileExists("doc/overview.html"): + createShortcut(getCurrentDir() / "doc" / "html" / "overview.html", + dest / "Overview") + if dirExists(r"dist\aporia-0.4.0"): + createShortcut(getCurrentDir() / r"dist\aporia-0.4.0\bin\aporia.exe", + dest / "Aporia") + + proc checkGccArch(mingw: string): bool = + let gccExe = mingw / r"gcc.exe" + if fileExists(gccExe): + try: + let arch = execProcess(gccExe, ["-dumpmachine"], nil, {poStdErrToStdOut, + poUsePath}) + when hostCPU == "i386": + result = arch.startsWith("i686-") + elif hostCPU == "amd64": + result = arch.startsWith("x86_64-") + else: + {.error: "Unknown CPU for Windows.".} + except OSError, IOError: + result = false + + proc tryDirs(incompat: var seq[string]; dirs: varargs[string]): string = + let bits = $(sizeof(pointer)*8) + for d in dirs: + if dirExists d: + let x = expandFilename(d / "bin") + if checkGccArch(x): return x + else: incompat.add x + elif dirExists(d & bits): + let x = expandFilename((d & bits) / "bin") + if checkGccArch(x): return x + else: incompat.add x + +proc main() = + when defined(windows): + let desiredPath = expandFilename(getCurrentDir() / "bin") + let p = getUnicodeValue(r"Environment", "Path", + HKEY_CURRENT_USER) + var alreadyInPath = false + var mingWchoices: seq[string] = @[] + var incompat: seq[string] = @[] + for x in p.split(';'): + let y = expandFilename(if x[0] == '"' and x[^1] == '"': + substr(x, 1, x.len-2) else: x) + if y == desiredPath: alreadyInPath = true + if y.toLowerAscii.contains("mingw"): + if dirExists(y): + if checkGccArch(y): mingWchoices.add y + else: incompat.add y + + if alreadyInPath: + echo "bin/nim.exe is already in your PATH [Skipping]" + else: + if askBool("nim.exe is not in your PATH environment variable.\n" & + "Should it be added permanently? (y/n) "): + addToPathEnv(desiredPath) + if mingWchoices.len == 0: + # No mingw in path, so try a few locations: + let alternative = tryDirs(incompat, "dist/mingw", "../mingw", r"C:\mingw") + if alternative.len == 0: + if incompat.len > 0: + echo "The following *incompatible* MingW installations exist" + for x in incompat: echo x + echo "No compatible MingW candidates found " & + "in the standard locations [Error]" + else: + if askBool("Found a MingW directory that is not in your PATH.\n" & + alternative & + "\nShould it be added to your PATH permanently? (y/n) "): + addToPathEnv(alternative) + elif askBool("Do you want to patch Nim's config to use this? (y/n) "): + patchConfig(alternative) + elif mingWchoices.len == 1: + if askBool("MingW installation found at " & mingWchoices[0] & "\n" & + "Do you want to patch Nim's config to use this?\n" & + "(Not required since it's in your PATH!) (y/n) "): + patchConfig(mingWchoices[0]) + else: + echo "Multiple MingW installations found: " + for i in 0..high(mingWchoices): + echo "[", i, "] ", mingWchoices[i] + if askBool("Do you want to patch Nim's config to use one of these? (y/n) "): + let idx = askNumber("Which one do you want to use for Nim? ", + 1, len(mingWchoices)) + patchConfig(mingWchoices[idx-1]) + createStartMenuEntry() + else: + echo("Add ", getCurrentDir(), "/bin to your PATH...") + +when isMainModule: + main() diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim index a1f564fbc..8dff722ec 100644 --- a/tools/nimgrep.nim +++ b/tools/nimgrep.nim @@ -135,7 +135,7 @@ proc processFile(filename: string) = if optVerbose in options: stdout.writeLine(filename) stdout.flushFile() - var pegp: TPeg + var pegp: Peg var rep: Regex var result: string @@ -213,7 +213,7 @@ proc hasRightExt(filename: string, exts: seq[string]): bool = if os.cmpPaths(x, y) == 0: return true proc styleInsensitive(s: string): string = - template addx: stmt = + template addx = result.add(s[i]) inc(i) result = "" diff --git a/tools/niminst/buildbat.tmpl b/tools/niminst/buildbat.tmpl index 2a8da144d..278b6caea 100644 --- a/tools/niminst/buildbat.tmpl +++ b/tools/niminst/buildbat.tmpl @@ -1,5 +1,5 @@ #? stdtmpl(subsChar='?') | standard -#proc generateBuildBatchScript(c: ConfigData, winIndex, cpuIndex: int): string = +#proc generateBuildBatchScript(c: ConfigData, winIndex, cpuIndex: int): string = # result = "@echo off\nREM Generated by niminst\n" SET CC=gcc SET LINKER=gcc @@ -23,8 +23,8 @@ CALL %CC% %COMP_FLAGS% -Ic_code -c ?{f} -o ?{changeFileExt(f, "o")} IF ERRORLEVEL 1 (GOTO:END) # end for -ECHO %LINKER% -o ?{"%BIN_DIR%"\toLower(c.name)}.exe ?linkCmd %LINK_FLAGS% -CALL %LINKER% -o ?{"%BIN_DIR%"\toLower(c.name)}.exe ?linkCmd %LINK_FLAGS% +ECHO %LINKER% -o ?{"%BIN_DIR%"\toLowerAscii(c.name)}.exe ?linkCmd %LINK_FLAGS% +CALL %LINKER% -o ?{"%BIN_DIR%"\toLowerAscii(c.name)}.exe ?linkCmd %LINK_FLAGS% # end block diff --git a/tools/niminst/buildsh.tmpl b/tools/niminst/buildsh.tmpl index 13dfe5226..f82fc0fee 100644 --- a/tools/niminst/buildsh.tmpl +++ b/tools/niminst/buildsh.tmpl @@ -92,6 +92,10 @@ case $uos in ;; *haiku* ) myos="haiku" + LINK_FLAGS="$LINK_FLAGS -lroot -lnetwork" + ;; + *mingw* ) + myos="windows" ;; *) echo 2>&1 "Error: unknown operating system: $uos" @@ -143,7 +147,7 @@ case $myos in $CC $COMP_FLAGS -Ic_code -c ?{f} -o ?{changeFileExt(f, "o")} # add(linkCmd, " \\\n" & changeFileExt(f, "o")) # end for - $LINKER -o ?{"$binDir/" & toLower(c.name)} ?linkCmd $LINK_FLAGS + $LINKER -o ?{"$binDir/" & toLowerAscii(c.name)} ?linkCmd $LINK_FLAGS ;; # end for *) diff --git a/tools/niminst/debcreation.nim b/tools/niminst/debcreation.nim index 36b2a29ec..0ecea132f 100644 --- a/tools/niminst/debcreation.nim +++ b/tools/niminst/debcreation.nim @@ -148,7 +148,7 @@ proc prepDeb*(packName, version, mtnName, mtnEmail, shortDesc, desc: string, ## binaries/config/docs/lib: files relative to nim's root, that need to ## be installed. - let pkgName = packName.toLower() + let pkgName = packName.toLowerAscii() var workingDir = getTempDir() / "niminst" / "deb" var upstreamSource = (pkgName & "-" & version) @@ -168,7 +168,7 @@ proc prepDeb*(packName, version, mtnName, mtnEmail, shortDesc, desc: string, echo("Creating necessary files in debian/") createDir(workingDir / upstreamSource / "debian") - template writeDebian(f, s: string): expr = + template writeDebian(f, s: string) = writeFile(workingDir / upstreamSource / "debian" / f, s) var controlFile = createControl(pkgName, makeMtn(mtnName, mtnEmail), diff --git a/tools/niminst/deinstall.tmpl b/tools/niminst/deinstall.tmpl index 3cdfbf45d..bba310f76 100644 --- a/tools/niminst/deinstall.tmpl +++ b/tools/niminst/deinstall.tmpl @@ -1,7 +1,7 @@ #? stdtmpl(subsChar='?') | standard #proc generateDeinstallScript(c: ConfigData): string = # result = "#! /bin/sh\n# Generated by niminst\n" -# var proj = c.name.toLower +# var proj = c.name.toLowerAscii if [ $# -eq 1 ] ; then case $1 in diff --git a/tools/niminst/install.tmpl b/tools/niminst/install.tmpl index 3f17840a8..d72b132ef 100644 --- a/tools/niminst/install.tmpl +++ b/tools/niminst/install.tmpl @@ -1,7 +1,7 @@ #? stdtmpl(subsChar = '?') | standard #proc generateInstallScript(c: ConfigData): string = # result = "#! /bin/sh\n# Generated by niminst\n" -# var proj = c.name.toLower +# var proj = c.name.toLowerAscii ## Current directory you start script from BASE_DIR=$(pwd) diff --git a/tools/niminst/makefile.tmpl b/tools/niminst/makefile.tmpl index 5c95ccda9..ce2db1c48 100644 --- a/tools/niminst/makefile.tmpl +++ b/tools/niminst/makefile.tmpl @@ -157,7 +157,7 @@ endif %.o: %.c $(CC) $(COMP_FLAGS) -Ic_code -c $< -o $@ -?{"$(binDir)/" & toLower(c.name)}: $(oFiles) +?{"$(binDir)/" & toLowerAscii(c.name)}: $(oFiles) @mkdir -p $(binDir) $(LINKER) -o $@ $^ $(LINK_FLAGS) @echo "SUCCESS" @@ -165,4 +165,4 @@ endif .PHONY: clean clean: - rm -f $(oFiles) ?{"$(binDir)/" & toLower(c.name)} + rm -f $(oFiles) ?{"$(binDir)/" & toLowerAscii(c.name)} diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index 4c8dfcddf..e0b8ad9b3 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -313,7 +313,7 @@ proc parseIniFile(c: var ConfigData) = of cfgSectionStart: section = normalize(k.section) of cfgKeyValuePair: - var v = k.value % c.vars + var v = `%`(k.value, c.vars, {useEnvironment, useEmpty}) c.vars[k.key] = v case section @@ -447,7 +447,8 @@ proc readCFiles(c: var ConfigData, osA, cpuA: int) = # HACK: we conditionally add ``-lm -ldl``, so remove them from the # linker flags: c.linker.flags = c.linker.flags.replaceWord("-lm").replaceWord( - "-ldl").strip + "-ldl").replaceWord("-lroot").replaceWord( + "-lnetwork").strip else: if cmpIgnoreStyle(k.key, "libpath") == 0: c.libpath = k.value @@ -551,7 +552,7 @@ proc srcdist(c: var ConfigData) = # --------------------- generate inno setup ----------------------------------- proc setupDist(c: var ConfigData) = let scrpt = generateInnoSetup(c) - let n = "build" / "install_$#_$#.iss" % [toLower(c.name), c.version] + let n = "build" / "install_$#_$#.iss" % [toLowerAscii(c.name), c.version] writeFile(n, scrpt, "\13\10") when defined(windows): if c.innosetup.path.len == 0: @@ -568,7 +569,7 @@ proc setupDist(c: var ConfigData) = # --------------------- generate NSIS setup ----------------------------------- proc setupDist2(c: var ConfigData) = let scrpt = generateNsisSetup(c) - let n = "build" / "install_$#_$#.nsi" % [toLower(c.name), c.version] + let n = "build" / "install_$#_$#.nsi" % [toLowerAscii(c.name), c.version] writeFile(n, scrpt, "\13\10") when defined(windows): if c.nsisSetup.path.len == 0: @@ -585,7 +586,7 @@ proc setupDist2(c: var ConfigData) = # ------------------ generate ZIP file --------------------------------------- when haveZipLib: proc zipDist(c: var ConfigData) = - var proj = toLower(c.name) & "-" & c.version + var proj = toLowerAscii(c.name) & "-" & c.version var n = "$#.zip" % proj if c.outdir.len == 0: n = "build" / n else: n = c.outdir / n @@ -616,49 +617,64 @@ when haveZipLib: else: quit("Cannot open for writing: " & n) -proc xzDist(c: var ConfigData) = - let proj = toLower(c.name) & "-" & c.version - var n = "$#.tar.xz" % proj +proc xzDist(c: var ConfigData; windowsZip=false) = + let proj = toLowerAscii(c.name) & "-" & c.version let tmpDir = if c.outdir.len == 0: "build" else: c.outdir - template processFile(z, dest, src) = - let s = src - let d = dest - echo "Copying ", s, " to ", tmpDir / d - let destdir = tmpdir / d.splitFile.dir - if not dirExists(destdir): createDir(destdir) - copyFileWithPermissions(s, tmpDir / d) - - processFile(z, proj / buildBatFile32, "build" / buildBatFile32) - processFile(z, proj / buildBatFile64, "build" / buildBatFile64) - processFile(z, proj / buildShFile, "build" / buildShFile) - processFile(z, proj / makeFile, "build" / makeFile) - processFile(z, proj / installShFile, installShFile) - processFile(z, proj / deinstallShFile, deinstallShFile) - for f in walkFiles(c.libpath / "lib/*.h"): - processFile(z, proj / "c_code" / extractFilename(f), f) - for osA in 1..c.oses.len: - for cpuA in 1..c.cpus.len: - var dir = buildDir(osA, cpuA) - for k, f in walkDir("build" / dir): - if k == pcFile: processFile(z, proj / dir / extractFilename(f), f) + proc processFile(destFile, src: string) = + let dest = tmpDir / destFile + echo "Copying ", src, " to ", dest + if not existsFile(src): + echo "[Warning] Source file doesn't exist: ", src + let destDir = dest.splitFile.dir + if not dirExists(destDir): createDir(destDir) + copyFileWithPermissions(src, dest) + + if not windowsZip and not existsFile("build" / buildBatFile32): + quit("No C sources found in ./build/, please build by running " & + "./koch csource -d:release.") + + if not windowsZip: + processFile(proj / buildBatFile32, "build" / buildBatFile32) + processFile(proj / buildBatFile64, "build" / buildBatFile64) + processFile(proj / buildShFile, "build" / buildShFile) + processFile(proj / makeFile, "build" / makeFile) + processFile(proj / installShFile, installShFile) + processFile(proj / deinstallShFile, deinstallShFile) + for f in walkFiles(c.libpath / "lib/*.h"): + processFile(proj / "c_code" / extractFilename(f), f) + for osA in 1..c.oses.len: + for cpuA in 1..c.cpus.len: + var dir = buildDir(osA, cpuA) + for k, f in walkDir("build" / dir): + if k == pcFile: processFile(proj / dir / extractFilename(f), f) + else: + for f in items(c.cat[fcWinBin]): + let filename = f.extractFilename + processFile(proj / "bin" / filename, f) - for cat in items({fcConfig..fcOther, fcUnix, fcNimble}): + let osSpecific = if windowsZip: fcWindows else: fcUnix + for cat in items({fcConfig..fcOther, osSpecific, fcNimble}): echo("Current category: ", cat) - for f in items(c.cat[cat]): processFile(z, proj / f, f) + for f in items(c.cat[cat]): processFile(proj / f, f) # Copy the .nimble file over let nimbleFile = c.nimblePkgName & ".nimble" - processFile(z, proj / nimbleFile, nimbleFile) + processFile(proj / nimbleFile, nimbleFile) when true: let oldDir = getCurrentDir() setCurrentDir(tmpDir) try: - if execShellCmd("XZ_OPT=-9 gtar Jcf $1.tar.xz $1 --exclude=.DS_Store" % proj) != 0: - # try old 'tar' without --exclude feature: - if execShellCmd("XZ_OPT=-9 tar Jcf $1.tar.xz $1" % proj) != 0: + if windowsZip: + if execShellCmd("7z a -tzip $1.zip $1" % proj) != 0: echo("External program failed") + else: + if execShellCmd("XZ_OPT=-9 gtar Jcf $1.tar.xz $1 --exclude=.DS_Store" % + proj) != 0: + # try old 'tar' without --exclude feature: + if execShellCmd("XZ_OPT=-9 tar Jcf $1.tar.xz $1" % proj) != 0: + echo("External program failed") finally: setCurrentDir(oldDir) @@ -676,11 +692,11 @@ proc debDist(c: var ConfigData) = echo("Copying source to tmp/niminst/deb/") var currentSource = getCurrentDir() var workingDir = getTempDir() / "niminst" / "deb" - var upstreamSource = (c.name.toLower() & "-" & c.version) + var upstreamSource = (c.name.toLowerAscii() & "-" & c.version) createDir(workingDir / upstreamSource) - template copyNimDist(f, dest: string): stmt = + template copyNimDist(f, dest: string) = createDir((workingDir / upstreamSource / dest).splitFile.dir) copyFile(currentSource / f, workingDir / upstreamSource / dest) @@ -724,10 +740,7 @@ if actionCSource in c.actions: if actionScripts in c.actions: writeInstallScripts(c) if actionZip in c.actions: - when haveZipLib: - zipDist(c) - else: - quit("libzip is not installed") + xzDist(c, true) if actionXz in c.actions: xzDist(c) if actionDeb in c.actions: diff --git a/tools/niminst/nsis.tmpl b/tools/niminst/nsis.tmpl index 0897d36a8..95d4652e3 100644 --- a/tools/niminst/nsis.tmpl +++ b/tools/niminst/nsis.tmpl @@ -11,10 +11,6 @@ ; File Functions Header, used to get the current drive root. !include "FileFunc.nsh" - ; *Patched* Environment Variable Manipulation Header, used to add - ; tools to the user's PATH environment variable. - !include "EnvVarUpdate.nsh" - ;-------------------------------- ; Global variables and defines !define PRODUCT_NAME "?c.displayName" @@ -35,7 +31,7 @@ ; Default installation folder ; This is changed later (in .onInit) to the root directory, if possible. - InstallDir "$PROGRAMFILES?{when sizeof(int) == 8: "64" else: ""}\?{c.name}-?{c.version}" + InstallDir "$PROGRAMFILES?{when sizeof(int) == 8: "64" else: ""}\?{c.name}-?{c.version}\" ; Get installation folder from registry if available InstallDirRegKey HKCU "Software\c.name\c.version" "" @@ -44,7 +40,7 @@ RequestExecutionLevel user ; Allow installation to the root drive directory. - AllowRootDirInstall true + AllowRootDirInstall false ; Maximum compression! SetCompressor /SOLID /FINAL lzma @@ -156,10 +152,14 @@ ; Section for adding tools to the PATH variable Section "Setup Path Environment" PathSection - ${EnvVarUpdate} $R0 "PATH" "A" "HKCU" "$INSTDIR\dist\mingw" - ${EnvVarUpdate} $R0 "PATH" "A" "HKCU" "$INSTDIR\dist\mingw\bin" - ${EnvVarUpdate} $R0 "PATH" "A" "HKCU" "$INSTDIR\bin" - ${EnvVarUpdate} $R0 "PATH" "A" "HKCU" "$INSTDIR\dist\babel" + Push "$INSTDIR\bin" + Call AddToPath + Push "$INSTDIR\dist\mingw" + Call AddToPath + Push "$INSTDIR\dist\mingw\bin" + Call AddToPath + Push "$PROFILE\.nimble\bin" + Call AddToPath SectionEnd ; The downloadable sections. These sections are automatically generated by @@ -202,7 +202,7 @@ ; Shortcuts # if d.len >= 6: # let startMenuEntry = d[5] - # let e = splitFile(startMenuEntry).name.capitalize + # let e = splitFile(startMenuEntry).name.capitalizeAscii !insertmacro MUI_STARTMENU_WRITE_BEGIN Application CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\?{e}.lnk" "$INSTDIR\?dir\?{startMenuEntry.toWin}" !insertmacro MUI_STARTMENU_WRITE_END @@ -243,10 +243,14 @@ SetAutoClose true ; Remove entries from the PATH environment variable - ${un.EnvVarUpdate} $R0 "PATH" "R" "HKCU" "$INSTDIR\dist\mingw" - ${un.EnvVarUpdate} $R0 "PATH" "R" "HKCU" "$INSTDIR\dist\mingw\bin" - ${un.EnvVarUpdate} $R0 "PATH" "R" "HKCU" "$INSTDIR\bin" - ${un.EnvVarUpdate} $R0 "PATH" "R" "HKCU" "$INSTDIR\dist\babel" + Push "$INSTDIR\bin" + Call un.RemoveFromPath + Push "$INSTDIR\dist\mingw" + Call un.RemoveFromPath + Push "$INSTDIR\dist\mingw\bin" + Call un.RemoveFromPath + Push "$PROFILE\.nimble\bin" + Call un.RemoveFromPath SectionEnd ;-------------------------------- @@ -254,5 +258,188 @@ Function .onInit ${GetRoot} "$EXEDIR" $R0 - strCpy $INSTDIR "$R0\?{c.name}" + ;strCpy $INSTDIR "$R0\?{c.name}" FunctionEnd + + +;-------------------------------------------------------------------- +; Path functions +; +; Based on example from: +; http://nsis.sourceforge.net/Path_Manipulation +; +; Actually based on: +; https://www.smartmontools.org/browser/trunk/smartmontools/os_win32/installer.nsi#L636 + + +!include "WinMessages.nsh" + +; Registry Entry for environment (NT4,2000,XP) +; All users: +;!define Environ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' +; Current user only: +!define Environ 'HKCU "Environment"' + + +; AddToPath - Appends dir to PATH +; (does not work on Win9x/ME) +; +; Usage: +; Push "dir" +; Call AddToPath + +Function AddToPath + Exch $0 + Push $1 + Push $2 + Push $3 + Push $4 + + ; NSIS ReadRegStr returns empty string on string overflow + ; Native calls are used here to check actual length of PATH + + ; $4 = RegOpenKey(HKEY_CURRENT_USER, "Environment", &$3) + System::Call "advapi32::RegOpenKey(i 0x80000001, t'Environment', *i.r3) i.r4" + IntCmp $4 0 0 done done + ; $4 = RegQueryValueEx($3, "PATH", (DWORD*)0, (DWORD*)0, &$1, ($2=NSIS_MAX_STRLEN, &$2)) + ; RegCloseKey($3) + System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i ${NSIS_MAX_STRLEN} r2) i.r4" + System::Call "advapi32::RegCloseKey(i $3)" + + IntCmp $4 234 0 +4 +4 ; $4 == ERROR_MORE_DATA + DetailPrint "AddToPath: original length $2 > ${NSIS_MAX_STRLEN}" + MessageBox MB_OK "PATH not updated, original length $2 > ${NSIS_MAX_STRLEN}" + Goto done + + IntCmp $4 0 +5 ; $4 != NO_ERROR + IntCmp $4 2 +3 ; $4 != ERROR_FILE_NOT_FOUND + DetailPrint "AddToPath: unexpected error code $4" + Goto done + StrCpy $1 "" + + ; Check if already in PATH + Push "$1;" + Push "$0;" + Call StrStr + Pop $2 + StrCmp $2 "" 0 done + Push "$1;" + Push "$0\;" + Call StrStr + Pop $2 + StrCmp $2 "" 0 done + + ; Prevent NSIS string overflow + StrLen $2 $0 + StrLen $3 $1 + IntOp $2 $2 + $3 + IntOp $2 $2 + 2 ; $2 = strlen(dir) + strlen(PATH) + sizeof(";") + IntCmp $2 ${NSIS_MAX_STRLEN} +4 +4 0 + DetailPrint "AddToPath: new length $2 > ${NSIS_MAX_STRLEN}" + MessageBox MB_OK "PATH not updated, new length $2 > ${NSIS_MAX_STRLEN}." + Goto done + + ; Append dir to PATH + DetailPrint "Add to PATH: $0" + StrCpy $2 $1 1 -1 + StrCmp $2 ";" 0 +2 + StrCpy $1 $1 -1 ; remove trailing ';' + StrCmp $1 "" +2 ; no leading ';' + StrCpy $0 "$1;$0" + WriteRegExpandStr ${Environ} "PATH" $0 + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + +done: + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + + +; RemoveFromPath - Removes dir from PATH +; +; Usage: +; Push "dir" +; Call RemoveFromPath + +Function un.RemoveFromPath + Exch $0 + Push $1 + Push $2 + Push $3 + Push $4 + Push $5 + Push $6 + + ReadRegStr $1 ${Environ} "PATH" + StrCpy $5 $1 1 -1 + StrCmp $5 ";" +2 + StrCpy $1 "$1;" ; ensure trailing ';' + Push $1 + Push "$0;" + Call un.StrStr + Pop $2 ; pos of our dir + StrCmp $2 "" done + + DetailPrint "Remove from PATH: $0" + StrLen $3 "$0;" + StrLen $4 $2 + StrCpy $5 $1 -$4 ; $5 is now the part before the path to remove + StrCpy $6 $2 "" $3 ; $6 is now the part after the path to remove + StrCpy $3 "$5$6" + StrCpy $5 $3 1 -1 + StrCmp $5 ";" 0 +2 + StrCpy $3 $3 -1 ; remove trailing ';' + WriteRegExpandStr ${Environ} "PATH" $3 + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + +done: + Pop $6 + Pop $5 + Pop $4 + Pop $3 + Pop $2 + Pop $1 + Pop $0 +FunctionEnd + + +; StrStr - find substring in a string +; +; Usage: +; Push "this is some string" +; Push "some" +; Call StrStr +; Pop $0 ; "some string" + +!macro StrStr un +Function ${un}StrStr + Exch $R1 ; $R1=substring, stack=[old$R1,string,...] + Exch ; stack=[string,old$R1,...] + Exch $R2 ; $R2=string, stack=[old$R2,old$R1,...] + Push $R3 + Push $R4 + Push $R5 + StrLen $R3 $R1 + StrCpy $R4 0 + ; $R1=substring, $R2=string, $R3=strlen(substring) + ; $R4=count, $R5=tmp + loop: + StrCpy $R5 $R2 $R3 $R4 + StrCmp $R5 $R1 done + StrCmp $R5 "" done + IntOp $R4 $R4 + 1 + Goto loop +done: + StrCpy $R1 $R2 "" $R4 + Pop $R5 + Pop $R4 + Pop $R3 + Pop $R2 + Exch $R1 ; $R1=old$R1, stack=[result,...] +FunctionEnd +!macroend +!insertmacro StrStr "" +!insertmacro StrStr "un." diff --git a/tools/nimweb.nim b/tools/nimweb.nim index 65af67216..9a21a0fb5 100644 --- a/tools/nimweb.nim +++ b/tools/nimweb.nim @@ -29,7 +29,7 @@ type TRssItem = object year, month, day, title, url, content: string TAction = enum - actAll, actOnlyWebsite, actPdf, actJson2 + actAll, actOnlyWebsite, actPdf, actJson2, actOnlyDocs Sponsor = object logo: string @@ -72,7 +72,7 @@ include "website.tmpl" # ------------------------- configuration file ------------------------------- const - version = "0.7" + version = "0.8" usage = "nimweb - Nim Website Generator Version " & version & """ (c) 2015 Andreas Rumpf @@ -85,11 +85,13 @@ Options: -v, --version shows the version --website only build the website, not the full documentation --pdf build the PDF version of the documentation + --json2 build JSON of the documentation + --onlyDocs build only the documentation Compile_options: will be passed to the Nim compiler """ - rYearMonthDay = r"(\d{4})_(\d{2})_(\d{2})" + rYearMonthDay = r"on\s+(\d{2})\/(\d{2})\/(\d{4})" rssUrl = "http://nim-lang.org/news.xml" rssNewsUrl = "http://nim-lang.org/news.html" activeSponsors = "web/sponsors.csv" @@ -157,6 +159,7 @@ proc parseCmdLine(c: var TConfigData) = of "website": action = actOnlyWebsite of "pdf": action = actPdf of "json2": action = actJson2 + of "onlydocs": action = actOnlyDocs of "googleanalytics": c.gaId = val c.nimArgs.add("--doc.googleAnalytics:" & val & " ") @@ -315,6 +318,7 @@ proc buildDoc(c: var TConfigData, destPath: string) = exec(findNim() & " buildIndex -o:$1/theindex.html $1" % [destPath]) proc buildPdfDoc(c: var TConfigData, destPath: string) = + createDir(destPath) if os.execShellCmd("pdflatex -version") != 0: echo "pdflatex not found; no PDF documentation generated" else: @@ -347,6 +351,7 @@ proc buildAddDoc(c: var TConfigData, destPath: string) = proc parseNewsTitles(inputFilename: string): seq[TRssItem] = # Goes through each news file, returns its date/title. result = @[] + var matches: array[3, string] let reYearMonthDay = re(rYearMonthDay) for kind, path in walkDir(inputFilename): let (dir, name, ext) = path.splitFile @@ -354,8 +359,8 @@ proc parseNewsTitles(inputFilename: string): seq[TRssItem] = let content = readFile(path) let title = content.splitLines()[0] let urlPath = "news/" & name & ".html" - if name =~ reYearMonthDay: - result.add(TRssItem(year: matches[0], month: matches[1], day: matches[2], + if content.find(reYearMonthDay, matches) >= 0: + result.add(TRssItem(year: matches[2], month: matches[1], day: matches[0], title: title, url: "http://nim-lang.org/" & urlPath, content: content)) result.reverse() @@ -501,11 +506,19 @@ proc buildWebsite(c: var TConfigData) = proc main(c: var TConfigData) = buildWebsite(c) buildJS("web/upload") - buildAddDoc(c, "web/upload") - buildDocSamples(c, "web/upload") - buildDoc(c, "web/upload") - buildDocSamples(c, "doc") - buildDoc(c, "doc") + const docup = "web/upload/" & NimVersion + createDir(docup) + buildAddDoc(c, docup) + buildDocSamples(c, docup) + buildDoc(c, docup) + createDir("doc/html") + buildDocSamples(c, "doc/html") + buildDoc(c, "doc/html") + +proc onlyDocs(c: var TConfigData) = + createDir("doc/html") + buildDocSamples(c, "doc/html") + buildDoc(c, "doc/html") proc json2(c: var TConfigData) = const destPath = "web/json2" @@ -526,6 +539,7 @@ parseCmdLine(c) parseIniFile(c) case action of actOnlyWebsite: buildWebsite(c) -of actPdf: buildPdfDoc(c, "doc") +of actPdf: buildPdfDoc(c, "doc/pdf") +of actOnlyDocs: onlyDocs(c) of actAll: main(c) of actJson2: json2(c) diff --git a/tools/noprefix.nim b/tools/noprefix.nim deleted file mode 100644 index d16c2b520..000000000 --- a/tools/noprefix.nim +++ /dev/null @@ -1,32 +0,0 @@ -# strip those silly GTK/ATK prefixes... - -import - expandimportc, os - -const - filelist = [ - ("sdl/sdl", "sdl"), - ("sdl/sdl_net", "sdl"), - ("sdl/sdl_gfx", "sdl"), - ("sdl/sdl_image", "sdl"), - ("sdl/sdl_mixer_nosmpeg", "sdl"), - ("sdl/sdl_mixer", "sdl"), - ("sdl/sdl_ttf", "sdl"), - ("sdl/smpeg", "sdl"), - - ("libcurl", "curl"), - ("mysql", "mysql"), - ("postgres", ""), - ("sqlite3", "sqlite3"), - - ("pcre/pcre", "pcre") - ] - -proc createDirs = - createDir("lib/newwrap/sdl") - createDir("lib/newwrap/pcre") - -for filename, prefix in items(filelist): - var f = addFileExt(filename, "nim") - main("lib/wrappers" / f, "lib/newwrap" / f, prefix) - diff --git a/start.bat b/tools/start.bat index 685932c47..a4475fac7 100644 --- a/start.bat +++ b/tools/start.bat @@ -1,6 +1,8 @@ @echo off REM COLOR 0A -SET NIMPATH=%~dp0 +SET NIMPATH=%~dp0\.. SET PATH=%NIMPATH%\bin;%NIMPATH%\dist\mingw\bin;%PATH% -cmd +cd %NIMPATH% +cmd + diff --git a/web/assets/news/images/0.15.0/doc_search.gif b/web/assets/news/images/0.15.0/doc_search.gif new file mode 100644 index 000000000..ac757b404 --- /dev/null +++ b/web/assets/news/images/0.15.0/doc_search.gif Binary files differdiff --git a/web/assets/news/images/0.15.0/doc_sort.gif b/web/assets/news/images/0.15.0/doc_sort.gif new file mode 100644 index 000000000..edd253c4a --- /dev/null +++ b/web/assets/news/images/0.15.0/doc_sort.gif Binary files differdiff --git a/web/download.rst b/web/download.rst index 583398c20..c388dd132 100644 --- a/web/download.rst +++ b/web/download.rst @@ -3,22 +3,34 @@ Download the compiler You can download the latest version of the Nim compiler here. -**Note:** The Nim compiler requires a C compiler to compile software. On -Windows we recommend that you use -`Mingw-w64 <http://mingw-w64.sourceforge.net/>`_. GCC is recommended on Linux -and Clang on Mac. +Windows +------- + +Zips +%%%% + +We now encourage you to install via the provided zipfiles: + +* | 32 bit: `nim-0.15.2_x32.zip <download/nim-0.15.2_x32.zip>`_ + | SHA-256 0f1bfb74751f55e090140a361c08e9f39f1dd03f1f0c070c061f2d5049ab9f96 +* | 64 bit: `nim-0.15.2_x64.zip <download/nim-0.15.2_x64.zip>`_ + | SHA-256 ceea42de6ebcd41032ee51f04526dc4cf2cbb0958ca6ad2321cf21944e05f553 +Unzip these where you want and optionally run ``finish.exe`` to +detect your MingW environment. -Binaries --------- +Exes +%%%% -Unfortunately, right now we only provide binaries for Windows. You can download -an installer for both 32 bit and 64 bit versions of Windows below. +You can download an installer for both 32 bit and 64 bit versions of +Windows below. Note that these installers have some known issues and +so will unlikely to be provided further in the future. These +installers have everything you need to use Nim, including a C compiler. -* | 32 bit: `nim-0.14.2_x32.exe <download/nim-0.14.2_x32.exe>`_ - | SHA-256 ca2de37759006d95db98732083a6fab20151bb9819186af2fa29d41884df78c9 -* | 64 bit: `nim-0.14.2_x64.exe <download/nim-0.14.2_x64.exe>`_ - | SHA-256 1fec054d3a5f54c0a67a40db615bb9ecb1d56413b19e324244110713bd4337d1 +* | 32 bit: `nim-0.15.2_x32.exe <download/nim-0.15.2_x32.exe>`_ + | SHA-256 8d648295dbd59cb315c98926a1da9f1f68773a1a2ef3d9d4c91c59387167efa3 +* | 64 bit: `nim-0.15.2_x64.exe <download/nim-0.15.2_x64.exe>`_ + | SHA-256 8c7efc6571921c2d2e5e995f801d4229ea1de19fbdabdcba1628307bd4612392 These installers also include Aporia, Nimble and other useful Nim tools to get you started with Nim development! @@ -26,14 +38,18 @@ you started with Nim development! Installation based on generated C code -------------------------------------- -This installation method is the preferred way for Linux, Mac OS X, and other Unix -like systems. Binary packages may be provided later. +**Note:** The Nim compiler requires a C compiler to compile software. On +Windows we recommend that you use +`Mingw-w64 <http://mingw-w64.sourceforge.net/>`_. GCC is recommended on Linux +and Clang on Mac. The Windows installers above already includes a C compiler. +This installation method is the preferred way for Linux, Mac OS X, and other Unix +like systems. Firstly, download this archive: -* | `nim-0.14.2.tar.xz (4.5MB) <download/nim-0.14.2.tar.xz>`_ - | SHA-256 8f8d38d70ed57164795fc55e19de4c11488fcd31dbe42094e44a92a23e3f5e92 +* | `nim-0.15.2.tar.xz (4.5MB) <download/nim-0.15.2.tar.xz>`_ + | SHA-256 905df2316262aa2cbacae067acf45fc05c2a71c8c6fde1f2a70c927ebafcfe8a Extract the archive. Then copy the extracted files into your chosen installation directory, ideally somewhere in your home directory. @@ -45,6 +61,7 @@ Now open a terminal and follow these instructions: ``cd ~/programs/nim``. * run ``sh build.sh``. * Add ``$your_install_dir/bin`` to your PATH. +* To build associated tools like ``nimble`` and ``nimsuggest`` run ``nim c koch && koch tools``. After restarting your terminal, you should be able to run ``nim -v`` which should show you the version of Nim you just installed. @@ -86,7 +103,7 @@ Docker Hub ---------- The `official Docker images <https://hub.docker.com/r/nimlang/nim/>`_ -are published Docker Hub and include the compiler and Nimble. There are images +are published on Docker Hub and include the compiler and Nimble. There are images for standalone scripts as well as Nimble packages. Get the latest stable image:: diff --git a/web/inactive_sponsors.csv b/web/inactive_sponsors.csv index 929882ff4..d466f3f31 100644 --- a/web/inactive_sponsors.csv +++ b/web/inactive_sponsors.csv @@ -1,22 +1,28 @@ logo, name, url, this_month, all_time, since, level +,bogen,,0,1010,"Jul 23, 2016",1 ,mikra,,0,400,"Apr 28, 2016",1 ,linkmonitor,,0,180,"Jan 28, 2016",1 +,avsej,,0,110,"Jun 10, 2016",1 +,WilRubin,,0,100,"Aug 11, 2015",1 ,"Benny Luypaert",,0,100,"Apr 10, 2016",1 ,"Chris Heller",,0,100,"May 19, 2016",1 ,PhilipWitte,,0,100,"Aug 5, 2016",1 ,Boxifier,,0,75,"Apr 12, 2016",1 ,iolloyd,,0,75,"Apr 29, 2016",1 -,WilRubin,,0,50,"Aug 11, 2015",1 ,rb01,,0,50,"May 4, 2016",1 ,TedSinger,,0,45,"Apr 9, 2016",1 ,martinbbjerregaard,,0,35,"Jun 9, 2016",1 +,RationalG,,0,30,"Jun 17, 2016",1 ,benbve,,0,30,"Jul 12, 2016",1 ,barcharcraz,,0,25,"Jun 2, 2016",1 ,"Landon Bass",,0,25,"Jun 7, 2016",1 ,jimrichards,,0,25,"Jun 8, 2016",1 ,jjzazuet,,0,25,"Jul 10, 2016",1 -,zolern,,0,20,"Apr 15, 2016",1 +,moigagoo,,0,20,"May 13, 2016",1 +,kteza1,,0,20,"Jun 10, 2016",1 +,tomkeus,,0,20,"Sep 4, 2016",1 ,mirek,,0,15,"Apr 9, 2016",1 +,DateinAsia,,0,15,"Jul 30, 2016",1 ,rickc,,0,15,"Jul 31, 2016",1 ,jpkx1984,,0,13,"Jul 11, 2016",1 ,vlkrav,,0,12,"Aug 9, 2015",1 @@ -29,7 +35,7 @@ logo, name, url, this_month, all_time, since, level ,Angluca,,0,10,"May 3, 2016",1 ,calind,,0,10,"Jun 7, 2016",1 ,goldenreign,,0,10,"Jun 10, 2016",1 -,kteza1,,0,10,"Jun 10, 2016",1 +,Blumenversand,,0,10,"Jul 21, 2016",1 ,cinnabardk,,0,10,"Aug 6, 2016",1 ,reddec,,0,10,"Aug 31, 2016",1 ,niv,,0,5,"Apr 6, 2016",1 diff --git a/web/news.rst b/web/news.rst index 2b43034cc..327cf6cca 100644 --- a/web/news.rst +++ b/web/news.rst @@ -2,99 +2,105 @@ News ==== -`2016-09-03 Nim Community Survey results <news/2016_09_03_nim_community_survey_results.html>`_ +`2016-10-23 Nim Version 0.15.2 released <news/e028_version_0_15_2.html>`_ =================================== -`2016-08-06 BountySource Update: The Road to v1.0 <news/2016_08_06_bountysource_update_the_road_to_v10.html>`_ +`2016-09-30 Nim Version 0.15.0 released <news/e027_version_0_15_0.html>`_ =================================== -`2016-06-23 Launching the 2016 Nim community survey <news/2016_06_23_launching_the_2016_nim_community_survey.html>`_ +`2016-09-03 Nim Community Survey results <news/e026_survey_results.html>`_ =================================== -`2016-06-11 Version 0.14.2 released <news/2016_06_11_version_0_14_2_released.html>`_ +`2016-08-06 BountySource Update: The Road to v1.0 <news/e025_bountysource_update.html>`_ =================================== -`2016-06-07 Version 0.14.0 released <news/2016_06_07_version_0_14_0_released.html>`_ +`2016-06-23 Launching the 2016 Nim community survey <news/e024_survey.html>`_ =================================== -`2016-06-04 Meet our BountySource sponsors <news/2016_06_04_meet_our_bountysource_sponsors.html>`_ +`2016-06-11 Version 0.14.2 released <news/e023_version_0_14_2.html>`_ =================================== -`2016-01-27 Nim in Action is now available! <news/2016_01_27_nim_in_action_is_now_available.html>`_ +`2016-06-07 Version 0.14.0 released <news/e022_version_0_14_0.html>`_ +=================================== + +`2016-06-04 Meet our BountySource sponsors <news/e021_meet_sponsors.html>`_ +=================================== + +`2016-01-27 Nim in Action is now available! <news/e020_nim_in_action.html>`_ ================================== -`2016-01-18 Version 0.13.0 released <news/2016_01_18_version_0_13_0_released.html>`_ +`2016-01-18 Version 0.13.0 released <news/e019_version_0_13_0.html>`_ ================================== -`2016-01-18 Andreas Rumpf's talk at OSCON Amsterdam <news/2016_01_18_andreas_rumpfs_talk_at_oscon_amsterdam.html>`_ +`2016-01-18 Andreas Rumpf's talk at OSCON Amsterdam <news/e018_oscon_amsterdam.html>`_ ================================================== -`2015-10-27 Version 0.12.0 released <news/2015_10_27_version_0_12_0_released.html>`_ +`2015-10-27 Version 0.12.0 released <news/e017_version_0_12_0.html>`_ ================================== -`2015-10-16 First Nim conference <news/2015_10_16_first_nim_conference.html>`_ +`2015-10-16 First Nim conference <news/e016_nim_conf1.html>`_ =============================== -`2015-05-04 Version 0.11.2 released <news/2015_05_04_version_0_11_2_released.html>`_ +`2015-05-04 Version 0.11.2 released <news/e015_version_0_11_2.html>`_ ================================== -`2015-04-30 Version 0.11.0 released <news/2015_04_30_version_0_11_0_released.html>`_ +`2015-04-30 Version 0.11.0 released <news/e014_version_0_11_0.html>`_ ================================== -`2014-12-29 Version 0.10.2 released <news/2014_12_29_version_0_10_2_released.html>`_ +`2014-12-29 Version 0.10.2 released <news/e013_version_0_10_2.html>`_ ================================== -`2014-10-19 Version 0.9.6 released <news/2014_10_19_version_0_9_6_released.html>`_ +`2014-10-19 Version 0.9.6 released <news/e012_version_0_9_6.html>`_ ================================= -`2014-04-21 Version 0.9.4 released <news/2014_04_21_version_0_9_4_released.html>`_ +`2014-04-21 Version 0.9.4 released <news/e011_version_0_9_4.html>`_ ================================= -`2014-02-11 Nimrod Featured in Dr. Dobb's Journal <news/2014_02_11_nimrod_featured_in_dr_dobbs_journal.html>`_ +`2014-02-11 Nimrod Featured in Dr. Dobb's Journal <news/e010_dr_dobbs_journal.html>`_ ================================================ -`2014-01-15 Andreas Rumpf's talk on Nimrod at Strange Loop 2013 is now online <news/2014_01_15_andreas_rumpfs_talk_on_nimrod.html>`_ +`2014-01-15 Andreas Rumpf's talk on Nimrod at Strange Loop 2013 is now online <news/e009_andreas_rumpfs_talk.html>`_ ============================================================================ -`2013-05-20 New website design! <news/2013_05_20_new_website_design.html>`_ +`2013-05-20 New website design! <news/e008_new_website.html>`_ ============================== -`2013-05-20 Version 0.9.2 released <news/2013_05_20_version_0_9_2_released.html>`_ +`2013-05-20 Version 0.9.2 released <news/e007_version_0_9_2.html>`_ ================================= -`2012-09-23 Version 0.9.0 released <news/2012_09_23_version_0_9_0_released.html>`_ +`2012-09-23 Version 0.9.0 released <news/e006_version_0_9_0.html>`_ ================================= -`2012-02-09 Version 0.8.14 released <news/2012_02_09_version_0_8_14_released.html>`_ +`2012-02-09 Version 0.8.14 released <news/e005_version_0_8_14.html>`_ ================================== -`2011-07-10 Version 0.8.12 released <news/2011_07_10_version_0_8_12_released.html>`_ +`2011-07-10 Version 0.8.12 released <news/e004_version_0_8_12.html>`_ ================================== -`2010-10-20 Version 0.8.10 released <news/2010_10_20_version_0_8_10_released.html>`_ +`2010-10-20 Version 0.8.10 released <news/e003_version_0_8_10.html>`_ ================================== -`2010-03-14 Version 0.8.8 released <news/2010_03_14_version_0_8_8_released.html>`_ +`2010-03-14 Version 0.8.8 released <news/e002_version_0_8_8.html>`_ ================================= -`2009-12-21 Version 0.8.6 released <news/2009_12_21_version_0_8_6_released.html>`_ +`2009-12-21 Version 0.8.6 released <news/e001_version_0_8_6.html>`_ ================================= diff --git a/web/news/2009_12_21_version_0_8_6_released.rst b/web/news/e001_version_0_8_6.rst index 019168a44..019168a44 100644 --- a/web/news/2009_12_21_version_0_8_6_released.rst +++ b/web/news/e001_version_0_8_6.rst diff --git a/web/news/2010_03_14_version_0_8_8_released.rst b/web/news/e002_version_0_8_8.rst index 2df476814..2df476814 100644 --- a/web/news/2010_03_14_version_0_8_8_released.rst +++ b/web/news/e002_version_0_8_8.rst diff --git a/web/news/2010_10_20_version_0_8_10_released.rst b/web/news/e003_version_0_8_10.rst index f72c0076c..f72c0076c 100644 --- a/web/news/2010_10_20_version_0_8_10_released.rst +++ b/web/news/e003_version_0_8_10.rst diff --git a/web/news/2011_07_10_version_0_8_12_released.rst b/web/news/e004_version_0_8_12.rst index 5f154b2db..5f154b2db 100644 --- a/web/news/2011_07_10_version_0_8_12_released.rst +++ b/web/news/e004_version_0_8_12.rst diff --git a/web/news/2012_02_09_version_0_8_14_released.rst b/web/news/e005_version_0_8_14.rst index 4050c8b93..4050c8b93 100644 --- a/web/news/2012_02_09_version_0_8_14_released.rst +++ b/web/news/e005_version_0_8_14.rst diff --git a/web/news/2012_09_23_version_0_9_0_released.rst b/web/news/e006_version_0_9_0.rst index 5635ca94c..5635ca94c 100644 --- a/web/news/2012_09_23_version_0_9_0_released.rst +++ b/web/news/e006_version_0_9_0.rst diff --git a/web/news/2013_05_20_version_0_9_2_released.rst b/web/news/e007_version_0_9_2.rst index 89352c06c..89352c06c 100644 --- a/web/news/2013_05_20_version_0_9_2_released.rst +++ b/web/news/e007_version_0_9_2.rst diff --git a/web/news/2013_05_20_new_website_design.rst b/web/news/e008_new_website.rst index b36cc99dd..b36cc99dd 100644 --- a/web/news/2013_05_20_new_website_design.rst +++ b/web/news/e008_new_website.rst diff --git a/web/news/2014_01_15_andreas_rumpfs_talk_on_nimrod.rst b/web/news/e009_andreas_rumpfs_talk.rst index 00cc5e101..00cc5e101 100644 --- a/web/news/2014_01_15_andreas_rumpfs_talk_on_nimrod.rst +++ b/web/news/e009_andreas_rumpfs_talk.rst diff --git a/web/news/2014_02_11_nimrod_featured_in_dr_dobbs_journal.rst b/web/news/e010_dr_dobbs_journal.rst index b48ccf31f..b48ccf31f 100644 --- a/web/news/2014_02_11_nimrod_featured_in_dr_dobbs_journal.rst +++ b/web/news/e010_dr_dobbs_journal.rst diff --git a/web/news/2014_04_21_version_0_9_4_released.rst b/web/news/e011_version_0_9_4.rst index 2714c5c78..2714c5c78 100644 --- a/web/news/2014_04_21_version_0_9_4_released.rst +++ b/web/news/e011_version_0_9_4.rst diff --git a/web/news/2014_10_19_version_0_9_6_released.rst b/web/news/e012_version_0_9_6.rst index 7a148aaa5..7a148aaa5 100644 --- a/web/news/2014_10_19_version_0_9_6_released.rst +++ b/web/news/e012_version_0_9_6.rst diff --git a/web/news/2014_12_29_version_0_10_2_released.rst b/web/news/e013_version_0_10_2.rst index ad8afa3bf..ad8afa3bf 100644 --- a/web/news/2014_12_29_version_0_10_2_released.rst +++ b/web/news/e013_version_0_10_2.rst diff --git a/web/news/2015_04_30_version_0_11_0_released.rst b/web/news/e014_version_0_11_0.rst index a8a58f2ae..a8a58f2ae 100644 --- a/web/news/2015_04_30_version_0_11_0_released.rst +++ b/web/news/e014_version_0_11_0.rst diff --git a/web/news/2015_05_04_version_0_11_2_released.rst b/web/news/e015_version_0_11_2.rst index 273182340..273182340 100644 --- a/web/news/2015_05_04_version_0_11_2_released.rst +++ b/web/news/e015_version_0_11_2.rst diff --git a/web/news/2015_10_16_first_nim_conference.rst b/web/news/e016_nim_conf1.rst index 228bffd28..228bffd28 100644 --- a/web/news/2015_10_16_first_nim_conference.rst +++ b/web/news/e016_nim_conf1.rst diff --git a/web/news/2015_10_27_version_0_12_0_released.rst b/web/news/e017_version_0_12_0.rst index 63088f9e2..63088f9e2 100644 --- a/web/news/2015_10_27_version_0_12_0_released.rst +++ b/web/news/e017_version_0_12_0.rst diff --git a/web/news/2016_01_18_andreas_rumpfs_talk_at_oscon_amsterdam.rst b/web/news/e018_oscon_amsterdam.rst index fcb4a8794..fcb4a8794 100644 --- a/web/news/2016_01_18_andreas_rumpfs_talk_at_oscon_amsterdam.rst +++ b/web/news/e018_oscon_amsterdam.rst diff --git a/web/news/2016_01_18_version_0_13_0_released.rst b/web/news/e019_version_0_13_0.rst index 2c8e66fa3..2c8e66fa3 100644 --- a/web/news/2016_01_18_version_0_13_0_released.rst +++ b/web/news/e019_version_0_13_0.rst diff --git a/web/news/2016_01_27_nim_in_action_is_now_available.rst b/web/news/e020_nim_in_action.rst index 33bcb7947..33bcb7947 100644 --- a/web/news/2016_01_27_nim_in_action_is_now_available.rst +++ b/web/news/e020_nim_in_action.rst diff --git a/web/news/2016_06_04_meet_our_bountysource_sponsors.rst b/web/news/e021_meet_sponsors.rst index 0bfb472c5..0bfb472c5 100644 --- a/web/news/2016_06_04_meet_our_bountysource_sponsors.rst +++ b/web/news/e021_meet_sponsors.rst diff --git a/web/news/2016_06_07_version_0_14_0_released.rst b/web/news/e022_version_0_14_0.rst index 6634d0053..6634d0053 100644 --- a/web/news/2016_06_07_version_0_14_0_released.rst +++ b/web/news/e022_version_0_14_0.rst diff --git a/web/news/2016_06_11_version_0_14_2_released.rst b/web/news/e023_version_0_14_2.rst index cbfe52713..cbfe52713 100644 --- a/web/news/2016_06_11_version_0_14_2_released.rst +++ b/web/news/e023_version_0_14_2.rst diff --git a/web/news/2016_06_23_launching_the_2016_nim_community_survey.rst b/web/news/e024_survey.rst index 0b87577aa..0b87577aa 100644 --- a/web/news/2016_06_23_launching_the_2016_nim_community_survey.rst +++ b/web/news/e024_survey.rst diff --git a/web/news/2016_08_06_bountysource_update_the_road_to_v10.rst b/web/news/e025_bountysource_update.rst index 00ca7022e..00ca7022e 100644 --- a/web/news/2016_08_06_bountysource_update_the_road_to_v10.rst +++ b/web/news/e025_bountysource_update.rst diff --git a/web/news/2016_09_03_nim_community_survey_results.rst b/web/news/e026_survey_results.rst index 106dce0e4..106dce0e4 100644 --- a/web/news/2016_09_03_nim_community_survey_results.rst +++ b/web/news/e026_survey_results.rst diff --git a/web/news/version_0_15_released.rst b/web/news/e027_version_0_15_0.rst index dea893ed1..47c02e9e4 100644 --- a/web/news/version_0_15_released.rst +++ b/web/news/e027_version_0_15_0.rst @@ -3,24 +3,102 @@ Version 0.15.0 released .. container:: metadata - Posted by Dominik Picheta on 17/09/2016 + Posted by Dominik Picheta and Andreas Rumpf on 30/09/2016 -Some text here. +We're happy to announce that the latest release of Nim, version 0.15.0, is now +available! + +As always, you can grab the latest version from the +`downloads page <http://nim-lang.org/download.html>`_. + +This release includes almost 180 bug fixes and improvements. To see a full list +of changes, take a look at the detailed changelog +`below <#changelog>`_. + +Some of the most significant changes in this release include: improvements to +the documentation, addition of a new ``multisync`` macro, and a new +``HttpClient`` implementation. + +Documentation +~~~~~~~~~~~~~ + +All pages in the documentation now contain a search box and a drop down to +select how procedures should be sorted. This allows you to search for +procedures, types, macros and more from any documentation page. + +.. raw::html + + <a href="../assets/news/images/0.15.0/doc_search.gif"> + <img src="../assets/news/images/0.15.0/doc_search.gif" alt="Doc search" style="width:100%"/> + </a> + +Sorting the procedures by type shows a more natural table of contents. This +should also help you to find procedures and other identifiers. + +.. raw::html + + <a href="../assets/news/images/0.15.0/doc_sort.gif"> + <img src="../assets/news/images/0.15.0/doc_sort.gif" alt="Doc sort" style="width:100%"/> + </a> + +Multisync macro +~~~~~~~~~~~~~~~ + +The ``multisync`` macro was implemented to enable you to define both +synchronous and asynchronous IO procedures without having to duplicate a +lot of code. + +As an example, consider the ``recvTwice`` procedure below: + +.. code-block:: nim + proc recvTwice(socket: Socket | AsyncSocket): Future[string] {.multisync.} = + result = "" + result.add(await socket.recv(25)) + result.add(await socket.recv(20)) + +The ``multisync`` macro will transform this procedure into the following: + +.. code-block:: nim + proc recvTwice(socket: Socket): string = + result = "" + result.add(socket.recv(25)) + result.add(socket.recv(20)) + + proc recvTwice(socket: AsyncSocket): Future[string] {.async.} = + result = "" + result.add(await socket.recv(25)) + result.add(await socket.recv(20)) + +Allowing you to use ``recvTwice`` with both synchronous and asynchronous sockets. + +HttpClient +~~~~~~~~~~ + +Many of the ``httpclient`` module's procedures have been deprecated in +favour of a new implementation using the ``multisync`` macro. There are now +two types: ``HttpClient`` and ``AsyncHttpClient``. Both of these implement the +same procedures and functionality, the only difference is timeout support and +whether they are blocking or not. + +See the `httpclient <http://nim-lang.org/docs/httpclient.html>`_ module +documentation for more information. + +Changelog +~~~~~~~~~ Changes affecting backwards compatibility ----------------------------------------- -- The ``json`` module uses an ``OrderedTable`` rather than a ``Table`` +- The ``json`` module now uses an ``OrderedTable`` rather than a ``Table`` for JSON objects. -- De-deprecated ``re.nim`` because we have too much code using it - and it got the basic API right. - -- ``split`` with ``set[char]`` as a delimiter in ``strutils.nim`` - no longer strips and splits characters out of the target string +- The ``split`` `(doc) <http://nim-lang.org/docs/strutils.html#split,string,set[char],int>`_ + procedure in the ``strutils`` module (with a delimiter of type + ``set[char]``) no longer strips and splits characters out of the target string by the entire set of characters. Instead, it now behaves in a similar fashion to ``split`` with ``string`` and ``char`` delimiters. Use ``splitWhitespace`` to get the old behaviour. + - The command invocation syntax will soon apply to open brackets and curlies too. This means that code like ``a [i]`` will be interpreted as ``a([i])`` and not as ``a[i]`` anymore. Likewise @@ -29,92 +107,150 @@ Changes affecting backwards compatibility Warning: a [b] will be parsed as command syntax; spacing is deprecated - See `<https://github.com/nim-lang/Nim/issues/3898>`_ for the relevant - discussion. + See `Issue #3898 <https://github.com/nim-lang/Nim/issues/3898>`_ for the + relevant discussion. + - Overloading the special operators ``.``, ``.()``, ``.=``, ``()`` now - should be enabled via ``{.experimental.}``. + needs to be enabled via the ``{.experimental.}`` pragma. + - ``immediate`` templates and macros are now deprecated. - Instead use ``untyped`` parameters. -- The metatype ``expr`` is deprecated. Use ``untyped`` instead. -- The metatype ``stmt`` is deprecated. Use ``typed`` instead. + Use ``untyped`` `(doc) <http://nim-lang.org/docs/manual.html#templates-typed-vs-untyped-parameters>`_ + parameters instead. + +- The metatype ``expr`` is deprecated. Use ``untyped`` + `(doc) <http://nim-lang.org/docs/manual.html#templates-typed-vs-untyped-parameters>`_ instead. + +- The metatype ``stmt`` is deprecated. Use ``typed`` + `(doc) <http://nim-lang.org/docs/manual.html#templates-typed-vs-untyped-parameters>`_ instead. + - The compiler is now more picky when it comes to ``tuple`` types. The following code used to compile, now it's rejected: .. code-block:: nim import tables - var rocketaims = initOrderedTable[string, Table[tuple[k: int8, v: int8], int64] ]() + var rocketaims = initOrderedTable[string, Table[tuple[k: int8, v: int8], int64]]() rocketaims["hi"] = {(-1.int8, 0.int8): 0.int64}.toTable() -Instead be consistent in your tuple usage and use tuple names for tuples -that have tuple name: +Instead be consistent in your tuple usage and use tuple names for named tuples: .. code-block:: nim import tables - var rocketaims = initOrderedTable[string, Table[tuple[k: int8, v: int8], int64] ]() + var rocketaims = initOrderedTable[string, Table[tuple[k: int8, v: int8], int64]]() rocketaims["hi"] = {(k: -1.int8, v: 0.int8): 0.int64}.toTable() -- Now when you compile console application for Windows, console output +- Now when you compile console applications for Windows, console output encoding is automatically set to UTF-8. -- Unhandled exceptions in JavaScript are now thrown regardless ``noUnhandledHandler`` - is defined. But now they do their best to provide a readable stack trace. +- Unhandled exceptions in JavaScript are now thrown regardless of whether + ``noUnhandledHandler`` is defined. But the stack traces should be much more + readable now. + +- In JavaScript, the ``system.alert`` procedure has been deprecated. + Use ``dom.alert`` instead. + +- De-deprecated ``re.nim`` because there is too much code using it + and it got the basic API right. + +- The type of ``headers`` field in the ``AsyncHttpClient`` type + `(doc) <http://nim-lang.org/docs/httpclient.html#AsyncHttpClient>`_ + has been changed + from a string table to the specialised ``HttpHeaders`` type. -- In JavaScript ``system.alert`` is deprecated. Use ``dom.alert`` instead. +- The ``httpclient.request`` + `(doc) <http://nim-lang.org/docs/httpclient.html#request,AsyncHttpClient,string,string,string>`_ + procedure which takes the ``httpMethod`` as a string + value no longer requires it to be prefixed with ``"http"`` + (or similar). -- ``AsyncHttpClient.headers`` type is now ``HttpHeaders``. +- Converting a ``HttpMethod`` + `(doc) <nim-lang.org/docs/httpcore.html#HttpMethod>`_ + value to a string using the ``$`` operator will + give string values without the ``"Http"`` prefix now. + +- The ``Request`` + `(doc) <http://nim-lang.org/docs/asynchttpserver.html#Request>`_ + object defined in the ``asynchttpserver`` module now uses + the ``HttpMethod`` type for the request method. Library Additions ----------------- -- Added ``readHeaderRow`` and ``rowEntry`` to ``parsecsv.nim`` to provide +- Added ``readHeaderRow`` and ``rowEntry`` to the ``parsecsv`` + `(doc) <http://nim-lang.org/docs/parsecsv.html>`_ module + to provide a lightweight alternative to python's ``csv.DictReader``. -- Added ``setStdIoUnbuffered`` proc to ``system.nim`` to enable unbuffered I/O. -- Added ``center`` and ``rsplit`` to ``strutils.nim`` to - provide similar Python functionality for Nim's strings. +- Added ``setStdIoUnbuffered`` proc to the ``system`` module to enable + unbuffered I/O. + +- Added ``center`` and ``rsplit`` to the ``strutils`` + `(doc) <http://nim-lang.org/docs/strutils.html>`_ module + to provide similar Python functionality for Nim's strings. - Added ``isTitle``, ``title``, ``swapCase``, ``isUpper``, ``toUpper``, ``isLower``, ``toLower``, ``isAlpha``, ``isSpace``, and ``capitalize`` - to ``unicode.nim`` to provide unicode aware case manipulation and case + to the ``unicode.nim`` + `(doc) <http://nim-lang.org/docs/unicode.html>`_ module + to provide unicode aware case manipulation and case testing. -- Added a new module ``lib/pure/strmisc.nim`` to hold uncommon string +- Added a new module ``strmisc`` + `(doc) <http://nim-lang.org/docs/strmisc.html>`_ + to hold uncommon string operations. Currently contains ``partition``, ``rpartition`` and ``expandTabs``. -- Split out ``walkFiles`` in ``os.nim`` to three separate procs in order - to make a clear distinction of functionality. ``walkPattern`` iterates +- Split out ``walkFiles`` in the ``os`` + `(doc) <http://nim-lang.org/docs/os.html>`_ module to three separate procs + in order to make a clear distinction of functionality. ``walkPattern`` iterates over both files and directories, while ``walkFiles`` now only iterates over files and ``walkDirs`` only iterates over directories. -- Added synchronous ``HttpClient`` in the ``httpclient`` module. +- Added a synchronous ``HttpClient`` in the ``httpclient`` + `(doc) <http://nim-lang.org/docs/httpclient.html>`_ + module. The old + ``get``, ``post`` and similar procedures are now deprecated in favour of it. - Added a new macro called ``multisync`` allowing you to write procedures for -synchronous and asynchronous sockets with no duplication. + synchronous and asynchronous sockets with no duplication. + +- The ``async`` macro will now complete ``FutureVar[T]`` parameters + automatically unless they have been completed already. + +Tool Additions +-------------- + +- The documentation is now searchable and sortable by type. +- Pragmas are now hidden by default in the documentation to reduce noise. +- Edit links are now present in the documentation. + Compiler Additions ------------------ - The ``-d/--define`` flag can now optionally take a value to be used by code at compile time. + `(doc) <http://nim-lang.org/docs/manual.html#implementation-specific-pragmas-compile-time-define-pragmas>`_ Nimscript Additions ------------------- -- Finally it's possible to dis/enable specific hints and warnings in - Nimscript via the procs ``warning`` and ``hint``. +- It's possible to enable and disable specific hints and warnings in + Nimscript via the ``warning`` and ``hint`` procedures. + - Nimscript exports a proc named ``patchFile`` which can be used to patch modules or include files for different Nimble packages, including the ``stdlib`` package. - Language Additions ------------------ - Added ``{.intdefine.}`` and ``{.strdefine.}`` macros to make use of (optional) compile time defines. + `(doc) <http://nim-lang.org/docs/manual.html#implementation-specific-pragmas-compile-time-define-pragmas>`_ + - If the first statement is an ``import system`` statement then ``system`` is not imported implicitly anymore. This allows for code like ``import system except echo`` or ``from system import nil``. @@ -122,6 +258,11 @@ Language Additions Bugfixes -------- +The list below has been generated based on the commits in Nim's git +repository. As such it lists only the issues which have been closed +via a commit, for a full list see +`this link on Github <https://github.com/nim-lang/Nim/issues?utf8=%E2%9C%93&q=is%3Aissue+closed%3A%222016-06-22+..+2016-09-30%22+>`_. + - Fixed "RFC: should startsWith and endsWith work with characters?" (`#4252 <https://github.com/nim-lang/Nim/issues/4252>`_) @@ -324,7 +465,7 @@ Bugfixes (`#4699 <https://github.com/nim-lang/Nim/issues/4699>`_) - Fixed "Closing AsyncEvent now also unregisters it on non-Windows platforms" - (`#4694 <https://github.com/nim-lang/Nim/issues/4694>`_) + (`#4694 <https://github.com/nim-lang/Nim/issues/4694>`_) - Fixed "Don't update handle in upcoming/asyncdispatch poll() if it was closed" (`#4697 <https://github.com/nim-lang/Nim/issues/4697>`_) - Fixed "generated local variables declared outside block" @@ -347,3 +488,30 @@ Bugfixes (`#4626 <https://github.com/nim-lang/Nim/issues/4626>`_) - Fixed "module securehash not gcsafe" (`#4760 <https://github.com/nim-lang/Nim/issues/4760>`_) + +- Fixed "Nimble installation failed on Windows x86." + (`#4764 <https://github.com/nim-lang/Nim/issues/4764>`_) +- Fixed "Recent changes to marshal module break old marshalled data" + (`#4779 <https://github.com/nim-lang/Nim/issues/4779>`_) +- Fixed "tnewasyncudp.nim test loops forever" + (`#4777 <https://github.com/nim-lang/Nim/issues/4777>`_) +- Fixed "Wrong poll timeout behavior in asyncdispatch" + (`#4262 <https://github.com/nim-lang/Nim/issues/4262>`_) +- Fixed "Standalone await shouldn't read future" + (`#4170 <https://github.com/nim-lang/Nim/issues/4170>`_) +- Fixed "Regression: httpclient fails to compile without -d:ssl" + (`#4797 <https://github.com/nim-lang/Nim/issues/4797>`_) +- Fixed "C Error on declaring array of heritable objects with bitfields" + (`#3567 <https://github.com/nim-lang/Nim/issues/3567>`_) +- Fixed "Corruption when using Channels and Threads" + (`#4776 <https://github.com/nim-lang/Nim/issues/4776>`_) +- Fixed "Sometimes Channel tryRecv() erroneously reports no messages available on the first call on Windows" + (`#4746 <https://github.com/nim-lang/Nim/issues/4746>`_) +- Fixed "Improve error message of functions called without parenthesis" + (`#4813 <https://github.com/nim-lang/Nim/issues/4813>`_) +- Fixed "Docgen doesn't find doc comments in macro generated procs" + (`#4803 <https://github.com/nim-lang/Nim/issues/4803>`_) +- Fixed "asynchttpserver may consume unbounded memory reading headers" + (`#3847 <https://github.com/nim-lang/Nim/issues/3847>`_) +- Fixed "TLS connection to api.clashofclans.com hangs forever." + (`#4587 <https://github.com/nim-lang/Nim/issues/4587>`_) diff --git a/web/news/e028_version_0_15_2.rst b/web/news/e028_version_0_15_2.rst new file mode 100644 index 000000000..601a26646 --- /dev/null +++ b/web/news/e028_version_0_15_2.rst @@ -0,0 +1,77 @@ +Version 0.15.2 released +======================= + +.. container:: metadata + + Posted by Andreas Rumpf on 23/10/2016 + +We're happy to announce that the latest release of Nim, version 0.15.2, is now +available! + +As always, you can grab the latest version from the +`downloads page <http://nim-lang.org/download.html>`_. + +This release is a pure bugfix release fixing the most pressing issues and +regressions of 0.15.0. For Windows we now provide zipfiles in addition to the +NSIS based installer which proves to be hard to maintain and after all these +months still has serious issues. So we encourage you download the .zip +file instead of the .exe file! Unzip it somewhere, run ``finish.exe`` to +detect your MingW installation, done. ``finish.exe`` can also set your PATH +environment variable. + + +Bugfixes +-------- + +The list below has been generated based on the commits in Nim's git +repository. As such it lists only the issues which have been closed +via a commit, for a full list see +`this link on Github <https://github.com/nim-lang/Nim/issues?utf8=%E2%9C%93&q=is%3Aissue+closed%3A%222016-09-30+..+2016-10-23%22+>`_. + + +- Fixed "`NimMain` not exported in DLL, but `NimMainInner` is" + (`#4840 <https://github.com/nim-lang/Nim/issues/4840>`_) +- Fixed "Tables clear seems to be broken" + (`#4844 <https://github.com/nim-lang/Nim/issues/4844>`_) +- Fixed "compiler: internal error" + (`#4845 <https://github.com/nim-lang/Nim/issues/4845>`_) +- Fixed "trivial macro breaks type checking in the compiler" + (`#4608 <https://github.com/nim-lang/Nim/issues/4608>`_) +- Fixed "derived generic types with static[T] breaks type checking in v0.15.0 (worked in v0.14.2)" + (`#4863 <https://github.com/nim-lang/Nim/issues/4863>`_) +- Fixed "xmlparser.parseXml is not recognised as GC-safe" + (`#4899 <https://github.com/nim-lang/Nim/issues/4899>`_) +- Fixed "async makes generics instantiate only once" + (`#4856 <https://github.com/nim-lang/Nim/issues/4856>`_) +- Fixed "db_common docs aren't generated" + (`#4895 <https://github.com/nim-lang/Nim/issues/4895>`_) +- Fixed "rdstdin disappeared from documentation index" + (`#3755 <https://github.com/nim-lang/Nim/issues/3755>`_) +- Fixed "ICE on template call resolution" + (`#4875 <https://github.com/nim-lang/Nim/issues/4875>`_) +- Fixed "Invisible code-block" + (`#3078 <https://github.com/nim-lang/Nim/issues/3078>`_) +- Fixed "nim doc does not generate doc comments correctly" + (`#4913 <https://github.com/nim-lang/Nim/issues/4913>`_) +- Fixed "nim doc2 fails on ARM when running against lib/pure/coro.nim" + (`#4879 <https://github.com/nim-lang/Nim/issues/4879>`_) +- Fixed "xmlparser does not unescape correctly" + (`#1518 <https://github.com/nim-lang/Nim/issues/1518>`_) +- Fixed "[docs] mysterious "raise hook"" + (`#3485 <https://github.com/nim-lang/Nim/issues/3485>`_) +- Fixed "assertion failure in non-release Nim when compiling NimYAML" + (`#4869 <https://github.com/nim-lang/Nim/issues/4869>`_) +- Fixed "A closure causes nimscript to fail with unhandled exception" + (`#4906 <https://github.com/nim-lang/Nim/issues/4906>`_) +- Fixed "startProcess changes working directory" + (`#4867 <https://github.com/nim-lang/Nim/issues/4867>`_) +- Fixed "bindsym to void template produces ICE" + (`#4808 <https://github.com/nim-lang/Nim/issues/4808>`_) +- Fixed "readline(TFile, var string) segfaults if second argument is nil" + (`#564 <https://github.com/nim-lang/Nim/issues/564>`_) +- Fixed "times.parse gives the wrong day of the week for the first hour of the day." + (`#4922 <https://github.com/nim-lang/Nim/issues/4922>`_) +- Fixed "Internal error when passing parameter proc inside .gcsafe closure" + (`#4927 <https://github.com/nim-lang/Nim/issues/4927>`_) +- Fixed "Upcoming asyncdispatch doesn't compile with C++ backend on OS X" + (`#4928 <https://github.com/nim-lang/Nim/issues/4928>`_) diff --git a/web/news/e029_version_0_16_0.rst b/web/news/e029_version_0_16_0.rst new file mode 100644 index 000000000..1d3f3b3df --- /dev/null +++ b/web/news/e029_version_0_16_0.rst @@ -0,0 +1,53 @@ +Version 0.16.0 released +======================= + +.. container:: metadata + + Posted by xyz on dd/mm/yyyy + +We're happy to announce that the latest release of Nim, version 0.16.0, is now +available! + +As always, you can grab the latest version from the +`downloads page <http://nim-lang.org/download.html>`_. + +This release includes almost xyz bug fixes and improvements. To see a full list +of changes, take a look at the detailed changelog +`below <#changelog>`_. + +Some of the most significant changes in this release include: xyz + + +Changelog +~~~~~~~~~ + +Changes affecting backwards compatibility +----------------------------------------- + +- ``staticExec`` now uses the directory of the nim file that contains the + ``staticExec`` call as the current working directory. + + +Library Additions +----------------- + + +Tool Additions +-------------- + + +Compiler Additions +------------------ + + +Language Additions +------------------ + + +Bugfixes +-------- + +The list below has been generated based on the commits in Nim's git +repository. As such it lists only the issues which have been closed +via a commit, for a full list see +`this link on Github <https://github.com/nim-lang/Nim/issues?utf8=%E2%9C%93&q=is%3Aissue+closed%3A%222016-06-22+..+2016-09-30%22+>`_. diff --git a/web/sponsors.csv b/web/sponsors.csv index fe0261d17..0701575d5 100644 --- a/web/sponsors.csv +++ b/web/sponsors.csv @@ -1,36 +1,34 @@ logo, name, url, this_month, all_time, since, level -,bogen,https://github.com/bogen,250,1010,"Jul 23, 2016",250 -assets/bountysource/secondspectrum.png,Second Spectrum,http://www.secondspectrum.com/,250,1000,"May 5, 2016",250 -assets/bountysource/xored.svg,"Xored Software, Inc.",http://xored.com/,250,750,"Jun 20, 2016",250 -,flyx,http://flyx.org,35,175,"Apr 7, 2016",75 -,shkolnick-kun,https://github.com/shkolnick-kun,75,150,"Jul 6, 2016",75 -,"Yuriy Glukhov",,25,125,"Apr 6, 2016",25 -,endragor,https://github.com/endragor,25,125,"Apr 7, 2016",25 -,FedericoCeratto,http://firelet.net,25,125,"Apr 7, 2016",25 -,"Adrian Veith",,25,125,"Apr 20, 2016",25 -,avsej,http://avsej.net,25,110,"Jun 10, 2016",25 -,euantorano,http://euantorano.co.uk,25,75,"Jun 7, 2016",25 -,xxlabaza,https://github.com/xxlabaza,25,70,"Jun 17, 2016",25 -,btbytes,https://www.btbytes.com/,10,50,"Apr 6, 2016",10 -,niebaopeng,https://github.com/niebaopeng,10,40,"Apr 15, 2016",10 +assets/bountysource/secondspectrum.png,Second Spectrum,http://www.secondspectrum.com/,250,1250,"May 5, 2016",250 +assets/bountysource/xored.svg,"Xored Software, Inc.",http://xored.com/,250,1000,"Jun 20, 2016",250 +,shkolnick-kun,https://github.com/shkolnick-kun,75,225,"Jul 6, 2016",75 +,flyx,http://flyx.org,35,210,"Apr 7, 2016",75 +,"Yuriy Glukhov",,25,150,"Apr 6, 2016",25 +,endragor,https://github.com/endragor,25,150,"Apr 7, 2016",25 +,FedericoCeratto,http://firelet.net,25,150,"Apr 7, 2016",25 +,"Adrian Veith",,25,150,"Apr 20, 2016",25 +,skunkiferous,https://github.com/skunkiferous,100,100,"Oct 2, 2016",150 +,euantorano,http://euantorano.co.uk,25,100,"Jun 7, 2016",25 +,xxlabaza,https://github.com/xxlabaza,25,95,"Jun 17, 2016",25 +,btbytes,https://www.btbytes.com/,10,60,"Apr 6, 2016",10 +,niebaopeng,https://github.com/niebaopeng,10,50,"Apr 15, 2016",10 +,"Jonathan Arnett",,10,50,"May 20, 2016",10 +,swalf,https://github.com/swalf,5,45,"May 9, 2016",5 +,zolern,https://github.com/zolern,10,40,"Apr 15, 2016",10 ,"pyloor ",https://schwarz-weiss.cc/,10,40,"May 16, 2016",10 -,"Jonathan Arnett",,10,40,"May 20, 2016",10 -,swalf,https://github.com/swalf,5,40,"May 9, 2016",5 -,zachaysan,http://venn.lc,10,30,"Jun 7, 2016",10 -,"Matthew Baulch",,10,30,"Jun 7, 2016",10 -,"Oskari Timperi",,10,30,"Jun 8, 2016",10 -,RationalG,https://github.com/RationalG,10,30,"Jun 17, 2016",10 -,"Handojo Goenadi",,5,25,"Apr 19, 2016",5 -,"Matthew Newton",,5,25,"Apr 20, 2016",5 -,johnnovak,http://www.johnnovak.net/,5,25,"Apr 29, 2016",5 -,moigagoo,http://sloth-ci.com,5,20,"May 13, 2016",5 -,RyanMarcus,http://rmarcus.info,5,10,"Jul 19, 2016",5 -,Blumenversand,https://github.com/blumenversand,5,10,"Jul 21, 2016",5 -,lenzenmi,https://github.com/lenzenmi,5,10,"Jul 28, 2016",5 -,DateinAsia,,5,10,"Jul 30, 2016",5 -,pandada8,https://github.com/pandada8,5,5,"Aug 12, 2016",5 -,abeaumont,http://alfredobeaumont.org/blog,5,5,"Aug 12, 2016",5 -,"Svend Knudsen",,1,5,"Apr 11, 2016",1 -,"Michael D. Sklaroff",,1,5,"Apr 27, 2016",1 -,nicck,,1,1,"Aug 9, 2016",1 - +,zachaysan,http://venn.lc,10,40,"Jun 7, 2016",10 +,"Matthew Baulch",,10,40,"Jun 7, 2016",10 +,"Oskari Timperi",,10,40,"Jun 8, 2016",10 +,"Handojo Goenadi",,5,35,"Apr 19, 2016",5 +,"Matthew Newton",,5,30,"Apr 20, 2016",5 +,johnnovak,http://www.johnnovak.net/,5,30,"Apr 29, 2016",5 +,RyanMarcus,http://rmarcus.info,5,15,"Jul 19, 2016",5 +,lenzenmi,https://github.com/lenzenmi,5,15,"Jul 28, 2016",5 +,cpunion,https://github.com/cpunion,10,10,"Sep 9, 2016",10 +,pandada8,https://github.com/pandada8,5,10,"Aug 12, 2016",5 +,abeaumont,http://alfredobeaumont.org/blog,5,10,"Aug 12, 2016",5 +,"Svend Knudsen",,1,6,"Apr 11, 2016",1 +,"Michael D. Sklaroff",,1,6,"Apr 27, 2016",1 +,csoriano89,https://github.com/csoriano89,5,5,"Sep 7, 2016",5 +,nicck,,1,2,"Aug 9, 2016",1 +,campbellr,,1,1,"Sep 4, 2016",1 diff --git a/web/ticker.html b/web/ticker.html index 7a1d620d3..b59c0a3e9 100644 --- a/web/ticker.html +++ b/web/ticker.html @@ -1,25 +1,26 @@ -<a class="news" href="$1news/2016_09_03_nim_community_survey_results.html"> +<a class="news" href="$1news/e028_version_0_15_2.html"> + <h4>October 23, 2016</h4> + <p>Nim version 0.15.2 has been released!</p> +</a> + +<a class="news" href="$1news/e026_survey_results.html"> <h4>September 3, 2016</h4> <p>Nim Community Survey results</p> </a> -<a class="news" href="$1news/2016_08_06_bountysource_update_the_road_to_v10.html"> +<a class="news" href="$1news/e025_bountysource_update.html"> <h4>August 6, 2016</h4> <p>BountySource Update: The Road to v1.0</p> </a> -<a class="news" href="$1news/2016_06_23_launching_the_2016_nim_community_survey.html"> +<a class="news" href="$1news/e024_survey.html"> <h4>June 23, 2016</h4> <p>Launching the 2016 Nim community survey!</p> </a> -<a class="news" href="$1news/2016_06_11_version_0_14_2_released.html"> +<a class="news" href="$1news/e023_version_0_14_2.html"> <h4>June 11, 2016</h4> <p>Nim version 0.14.2 has been released!</p> </a> -<a class="news" href="$1news/2016_06_04_meet_our_bountysource_sponsors.html"> - <h4>June 04, 2016</h4> - <p>Meet our BountySource sponsors</p> -</a> <a href="$1news.html" class="blue">See All News...</a> diff --git a/web/website.ini b/web/website.ini index 860ab9338..0d1be4b63 100644 --- a/web/website.ini +++ b/web/website.ini @@ -31,7 +31,7 @@ file: ticker.html [Documentation] doc: "endb;intern;apis;lib;manual.rst;tut1.rst;tut2.rst;nimc;overview;filters" doc: "tools;niminst;nimgrep;gc;estp;idetools;docgen;koch;backends.rst" -doc: "nimfix.rst;nimsuggest.rst;nep1.rst;nims.rst" +doc: "nimfix.rst;nimsuggest.rst;nep1.rst;nims.rst;contributing.rst" pdf: "manual.rst;lib.rst;tut1.rst;tut2.rst;nimc.rst;niminst.rst;gc.rst" srcdoc2: "system.nim;system/nimscript;pure/ospaths" srcdoc2: "core/macros;pure/marshal;core/typeinfo" @@ -45,7 +45,7 @@ srcdoc2: "pure/parseopt;pure/parseopt2;pure/hashes;pure/strtabs;pure/lexbase" srcdoc2: "pure/parsecfg;pure/parsexml;pure/parsecsv;pure/parsesql" srcdoc2: "pure/streams;pure/terminal;pure/cgi;pure/unicode;pure/strmisc" srcdoc2: "pure/htmlgen;pure/parseutils;pure/browsers" -srcdoc2: "impure/db_postgres;impure/db_mysql;impure/db_sqlite" +srcdoc2: "impure/db_postgres;impure/db_mysql;impure/db_sqlite;pure/db_common" srcdoc2: "pure/httpserver;pure/httpclient;pure/smtp;impure/ssl;pure/fsmonitor" srcdoc2: "pure/ropes;pure/unidecode/unidecode;pure/xmldom;pure/xmldomparser" srcdoc2: "pure/xmlparser;pure/htmlparser;pure/xmltree;pure/colors;pure/mimetypes" @@ -63,7 +63,7 @@ srcdoc2: "deprecated/pure/ftpclient" srcdoc2: "pure/asyncfile;pure/asyncftpclient" srcdoc2: "pure/md5;pure/rationals" srcdoc2: "posix/posix" -srcdoc2: "pure/fenv;pure/securehash" +srcdoc2: "pure/fenv;pure/securehash;impure/rdstdin" srcdoc2: "pure/basic2d;pure/basic3d;pure/mersenne;pure/coro;pure/httpcore" ; Note: everything under 'webdoc' doesn't get listed in the index, so wrappers |