Compare commits

...

185 commits

Author SHA1 Message Date
Timothee Cour
a10691bdf2
show cmd used with --verbose (even on success); fix #783 (#781)
* show cmd used with --verbose (even on success)

* honor -p:-u:release

* address comment

* remove --showCmds flag

* remove quoteShell

* fix #783

* Revert "fix #783"

This reverts commit af0ac004c00f17e9983c63ab99e40cd38ba6aaa4.

* fix #783 by fixing the gist instead

* Revert "fix #783 by fixing the gist instead" now that upstream gist was updated with my patch

This reverts commit 8bec86039d8335af152acf238ab14d0268e003e5.
2020-03-26 23:14:09 +00:00
Daniel M
68a9c4c955 fix and improve the behavior of nimble run
quote arguments appropriately:
  - whitespace in arguments is preserved;
    previously arguments with whitespace were split
  - quotes are now escaped

the exit code of the command that has been "run"
is re-used when nimble exits

show the output of the "run" command
regardless of whether nimble is debugging
2020-02-23 16:01:14 +00:00
Ganesh Viswanathan
27d56f8e9f Remove ~/.nimble from cache 2020-02-11 21:43:41 +00:00
Ganesh Viswanathan
85e5bc7c37 Use travis gist 2020-02-07 00:12:25 +00:00
inv2004
9391fbc56d Run can work without additional option if only one bin in .nimble (#761)
* nimble run can work if only one binary in config

* usage changed

* runFile is option now

* usage updated

* small refactoring to reduce condition

* setRunOptions fix

* some code optimisation
2020-01-17 22:15:34 +00:00
Taylor Hoff
e9d45ca683 Capitalize the "D" in srcDir to avoid errors when using styleChecks
Fixes #740
2019-11-19 22:00:15 +00:00
Xie Yanbo
16ba5db44e tests' config file should be config.nims
which is created by `nimble init`.
2019-11-10 15:30:38 +00:00
itmuckel
051cfa6cd3 Add '==' as operator for pinning versions. 2019-11-09 12:48:00 +00:00
Dominik Picheta
a8a5bdd863
Fixes #734 2019-10-29 22:43:04 +00:00
yuchunzhou
bbb586dbfc add vcs to the new created project (#729)
* add git vcs and a default .nim.cfg file to the new created project

* remove the gitapi dependency

* add vcs support for new nimble project

* add vcs support for new nimble project

* update pull request #729

* add --git/hg flag instead of --vcs flag for nimble init command
2019-10-28 21:28:46 +00:00
Dominik Picheta
b3abee937d Add tests for #666. 2019-10-24 23:26:21 +01:00
yuchunzhou
703abe3d41 fix #725 2019-10-24 00:20:40 +01:00
Ganesh Viswanathan
5bb795a364 Export initOptions for choosenim 2019-10-23 23:22:00 +01:00
genotrance
a2ec2db8f2
Enable Windows CI (#714)
Also fix #676
2019-10-03 18:38:51 -05:00
Dominik Picheta
4007b2a778 Fixes nimble run on Windows.
Cherry picked from 331a33711d
2019-09-23 00:09:26 +01:00
Dominik Picheta
0ed8e6403c Implicitly disable package validation for run/build/compile. 2019-09-22 22:22:21 +01:00
Dominik Picheta
901afa8c71 Further fix for #432. 2019-09-22 22:13:31 +01:00
Dominik Picheta
3625c1f861 Expand Nimble readme slightly (installation/nimble-run). 2019-09-22 21:36:50 +01:00
Dominik Picheta
b68f7849e6 Add 0.11.0 changelog. 2019-09-22 21:21:26 +01:00
Dominik Picheta
1db54cc736 Fixes some small input validation errors and help text. 2019-09-22 18:11:28 +01:00
Dominik Picheta
10e22fec98 Move issue 432 test to the bottom of tester to fix broken uninstall test. 2019-09-22 17:58:51 +01:00
Dominik Picheta
fe252c6ed6 Fixes #432. Fixes #672. 2019-09-22 16:37:47 +01:00
Dominik Picheta
281a1d129a Fixes #708. Refs #571.
Output from Nimble scripts' top-level statements is now
only visible with --verbose.
2019-09-22 14:39:22 +01:00
Dominik Picheta
a703de5dbd Attempt to reproduce #564. 2019-09-22 13:58:59 +01:00
Dominik Picheta
46dc98bb62 Fixes #710. 2019-09-22 13:34:54 +01:00
Dominik Picheta
c85cdfd814 Fixes #706. No more unused import warnings. 2019-09-22 12:00:15 +01:00
Dominik Picheta
5ea2ac34fe Update CI to test against latest Nim HEAD commit. 2019-09-22 11:53:03 +01:00
Dominik Picheta
da3f70cb98 Fixes #713 and adds test for --depsOnly. 2019-09-22 11:48:00 +01:00
Dominik Picheta
419eba036b Improve error message when there are no packages to uninstall. 2019-09-22 11:48:00 +01:00
Dominik Picheta
137bb1ed07 Fixes #606. Build before/after hook executed every time package is built. 2019-09-22 11:26:57 +01:00
Dominik Picheta
4a2aaa07dc Implements NimblePkgVersion define. Fixes #625. 2019-09-22 10:00:50 +01:00
Dominik Picheta
28ff2e04a7 Fixes tests on 0.19.6. 2019-09-22 00:15:37 +01:00
Dominik Picheta
55dd892aab Fixes #631. Test command now respects backend. 2019-09-22 00:11:28 +01:00
Dominik Picheta
c834faf60e Implements nimble run. Fixes #614. 2019-09-21 23:34:02 +01:00
Dominik Picheta
36c4a39674 Temp files are no longer removed when --debug is present. 2019-09-21 22:54:08 +01:00
Dominik Picheta
445ecfe946 Fixes #649. The dump command now has implicit -y. 2019-09-21 13:02:04 +01:00
Dominik Picheta
3eae8d7d61 Add examples to tag prompt in nimble publish. 2019-09-21 12:46:42 +01:00
Araq
bc8856632a version bump to 0.11.0 2019-09-20 20:10:06 +01:00
Araq
8bdc054817 fixes #703 2019-09-20 20:09:37 +01:00
Neelesh Chandola
d6a6a47dd9 Fix https://github.com/nim-lang/nimble/issues/700 2019-09-17 21:23:54 +01:00
Ivan Bobev
46f26e1d4a Move ignored from tests dir to its .gitignore
Related to #680
2019-09-07 12:08:15 +01:00
Ivan Bobev
1880730762 Fix the test for recursive calling of Nimble tasks
The test case for recursive calling of Nimble tasks was not working
properly on Windows so far.

Related to #680
2019-09-07 12:08:15 +01:00
Ivan Bobev
62699afaa8 Revert deletion of two not used procedures
The two currently not used procedures `getSpecificDir` and `doPull` from
`download.nim` file are reverted according to dom96's suggestion in the
code review of pull request #692. Now they are marked with used pragma
to disable the warning.

Related to #680
2019-09-07 12:08:15 +01:00
Ivan Bobev
df11a6f6cf Fix tests to pass with the latest Nim
There are two issues fixed:

 - With the latest Nim version sometimes the files
   `<package_name>_<some_number>.nims`' generated on `nim install`
   contain warning for unused imports which causes the test
   "can validate package structure (#144)" to fail, because it was
   searching for the word "warning" in the output.

 - On Windows Subsystem for Linux, when an import starts sometimes with
   a lowercase, and sometimes with an uppercase, for example
   `import uri` and `import Uri`, this causes Nim to create and compile
   both `stdlib_uri.nim.c` and `stdlib_Uri.nim.c` and to fail on the
   linking step, because of the same symbols are being redefined.

Also the Travis CI build script is changed to test against currently the
latest working Nim version 212ae2f.

Related to #680
2019-09-07 12:08:15 +01:00
Ivan Bobev
9d8cc06724 Add additional directory to .gitignore
The directory "tests/buildDir/" where the build artifacts from the
"compilation without warnings" unit test execution are being written is
added to the .gitignore file.

Related to #680
2019-09-07 12:08:15 +01:00
Ivan Bobev
f0c454a399 Fix compilation without warnings test case
Compilation without warnings test case on Unix systems with older Nim
version 0.19.6 had been failing before the fix, because the compiler
expects full file name to be passed after the '-o' switch, but not only
an output directory name.

Related to #680
2019-09-07 12:08:15 +01:00
Ivan Bobev
427b412ff9 Fix unused import warning on Unix systems
The NimbleError exception from the version module is being used only on
Windows platforms in the packageinstaller.nim file. Conditional import
of the module is used to fix the warning.

Related to #680
2019-09-07 12:08:15 +01:00
Ivan Bobev
bb1dd21224 Add an unit test for compilation without warnings
An unit test which checks whether Nimble compiles without warnings is
added. Checking for three warning types cleaned in previous commits is
implemented:

 - [UnusedImport] cleaned in e8c7d5c

 - [Deprecated] cleaned in 3d6172e

 - [XDeclaredButNotUsed] cleaned in 7df2ef3

Other types of warnings easily can be added to the test by extending the
warnings list.

Related to #680
2019-09-07 12:08:15 +01:00
Ivan Bobev
1eb9f0f01c Remove unused file
The file nimscriptsupport.nim is no longer used in favor of the newer
version nimscriptwrapper.nim.

Related to #680
2019-09-07 12:08:15 +01:00
Ivan Bobev
bfc4f25548 Fix compilation of nimscriptapi.nim
To compile the nimscriptapi.nim file, it is needed to import the os
module, but if so the getParams procedure cannot be executed from
nimscript any more. The problem is resolved by conditionally importing
the module.

Related to #680
2019-09-07 12:08:15 +01:00
Ivan Bobev
6e5761b192 Clear unused symbols warnings
Unused functions and variables are removed from the Nimble's source
code.

Resolves #680
2019-09-07 12:08:15 +01:00
Ivan Bobev
db018f235b Clear deprecation warnings
This fix clears deprecation warnings related to Nim's HashSet
procedures. There were two types of warnings which have been fixed:

 - Warning: Deprecated since v0.20, use 'initHashSet'; initSet is
   deprecated [Deprecated]

 - Warning: Deprecated since v0.20, use 'toHashSet'; toSet is
   deprecated [Deprecated]

Backward compatibility with older Nim versions is also implemented.

Related to #680
2019-09-07 12:08:15 +01:00
Ivan Bobev
2ec470d287 Clear unused imports
Related to #680
2019-09-07 12:08:15 +01:00
Ganesh Viswanathan
38cd55e71a Fix #689 - don't lookup versions for aliases 2019-09-04 23:14:29 +02:00
Dominik Picheta
1af4505219
Merge pull request #698 from genotrance/issue640
Fix #640 - strip out blank spaces
2019-09-04 23:12:53 +02:00
Ganesh Viswanathan
fb57d47421 Fix #640 - strip out blank spaces 2019-09-04 13:17:17 -05:00
genotrance
e39c57482a Fix #633 - pass CLI to tasks (#686)
* Fix #633 - pass CLI to tasks

* Add test case
2019-09-02 22:54:48 +01:00
Hitesh Jasani
2243e3fbc2 Fix #567: Display package versions in sorted order (#688)
* Fix #567: Display package versions in sorted order

* Add docs

* Fix docs to focus on git tags

* Refactor to use version module

* Streamline docs

* Refactor to make things work in nim <= 0.19.6

* Improve code readability
2019-08-24 10:15:29 +01:00
Ivan Bobev
da82e3111e Fix issue678 test repository links
After the ownership of the issue678 test repository was transferred to
the nimble-test account, the links to the repository required for
performing the test had to be updated in order to avoid possible
confusion, despite the fact that GitHub does automatic link redirection
for transferred repositories.

Related to #687
2019-08-04 19:20:53 +01:00
Ivan Bobev
8e3af03e46 Add a changelog entry for the issue #678 fix
Related to #678
2019-07-29 11:56:09 +01:00
Ivan Bobev
f461792686 Fix multiple installs of the same package
When one package is dependency both of some other package and some other
dependency of the other package it has been downloaded and installed
multiple times.

Resolves #678
2019-07-29 11:56:09 +01:00
Ivan Bobev
8cf97e0e06 Add an unit test for #678
This is an unit test for the issue with multiple downloads and installs
of the same dependency package.

Related to #678
2019-07-29 11:56:09 +01:00
Ivan Bobev
871e5c65d1 Add additional files and folders to .gitignore
Several additional file and folder types are added to .gitignore:

 - VSCode directory for for user and workspace settings.

 - *.orig files created by Git on merge conflicts.

 - Some artifacts related to the test procedure.
2019-07-29 11:56:09 +01:00
Oisín Mac Fhearaí
522ef4cf12 Unescape package description in nimble init 2019-07-27 21:06:18 +01:00
Oisín Mac Fhearaí
5b7b061465 Remove escaping of author name and package description in nimble init,
since it replaces characters used in many names and languages with
escape sequences.
Slightly refactor the code for determining author name, to make it
easier to add other version control systems in future (right now it's
just git and hg).

Also, add some binary test artifacts to .gitignore so they don't accidentally
get committed in future.
2019-07-27 21:06:18 +01:00
SolitudeSF
b5b85489fa Add tests for passNim feature. 2019-07-27 19:18:39 +01:00
SolitudeSF
5ec2ecea77 Add passNim/p flag to pass compiler flags explicitly.
Remove redundant buildFromDir overload.
2019-07-27 19:18:39 +01:00
genotrance
added89acc Fixes #504 (#683)
* Improve messaging for uninstall

* Uninstall if in right order

* Fix CI - cannot looks better

* Raise exception when nothing to remove

* Test case, minor test fix

* 0.19.6 fixes

* Fix Nim devel hash

* Feedback
2019-07-27 14:37:56 +01:00
Leonardo Mariscal
7c2b9f612e Update submodules at checkout time to fix #675 2019-07-16 20:36:39 +01:00
liquid600pgm
5e72840336 Test for the #581 fix 2019-06-22 00:46:21 +01:00
liquid600pgm
153866252d File not overwritten by example code message 2019-06-22 00:46:21 +01:00
liquid600pgm
192ea12ab9 Output message when file already exists 2019-06-22 00:46:21 +01:00
liquid600pgm
df8317585f fixed #581 nimble init does not overwrite existing files with its templates anymore 2019-06-22 00:46:21 +01:00
Hitesh Jasani
98e566adab Add example dependency with git commit [docs] (#669)
* Add example dependency with git commit [docs]

* Update readme.markdown
2019-06-12 21:16:52 +01:00
Ganesh Viswanathan
513780a382 Fix #665 - avoid stdout in printPkgInfo 2019-06-11 21:38:27 +01:00
Ganesh Viswanathan
2c87a7fe5e Add 0.20.0 to test matrix, latest nightlies 2019-06-11 21:34:07 +01:00
Ganesh Viswanathan
d15c8530cb Version 0.10.2 2019-06-03 21:06:43 +01:00
genotrance
21616e35a7 Fix issue #655 (#661)
* Fix issue #655

* Update nimscriptwrapper.nim
2019-06-02 12:12:20 +01:00
Ganesh Viswanathan
016f42c34a Fix issue #597 - error if bin is a source file 2019-06-02 12:01:49 +01:00
Ganesh Viswanathan
be83dcdca9 Unique nims filename to enable parallelism 2019-06-01 14:27:31 +01:00
Ganesh Viswanathan
374f62614a Improved test naming 2019-06-01 14:27:31 +01:00
Ganesh Viswanathan
8dc036c896 Update Nim versions, fix recursive test 2019-06-01 14:27:31 +01:00
Ganesh Viswanathan
cbd63e61de Fix outfile issue when binary exists 2019-06-01 14:27:31 +01:00
Andreas Rumpf
0e873c2702
Merge pull request #660 from genotrance/nimOldCaseObjects
Fix object variants - nim issue 1286
2019-06-01 10:39:11 +02:00
Ganesh Viswanathan
d800fb4525 Fix object variants - nim issue 1286 2019-05-31 16:58:54 -05:00
Taylor Rose
9beb6e1529 Add backend selection to nimble init 2019-05-30 23:53:04 +01:00
genotrance
06b9b49449 Update to 0.10.0 (#657)
* Update changelog for 0.10.0

* Multi-user systems

* Minor fixes

* Update changelog.markdown
2019-05-27 19:29:19 +01:00
Dominik Picheta
a4eaa5d3d2
Merge pull request #656 from cdunn2001/master
Choose USER-specific tmpdir
2019-05-27 11:24:31 +01:00
Christopher Dunn
cf7c147121 Choose USER-specific tmpdir
re: #80
2019-05-26 12:56:27 -05:00
Dominik Picheta
f17eaef795
Merge pull request #647 from jrfondren/continue-tests-on-error
Option to continue tests on failure
2019-05-06 16:28:18 +01:00
Dominik Picheta
ebbafec4dd
Merge pull request #648 from jrfondren/no-hang-on-invalid-projname
use displayLine rather than display with displayCategory prompt
2019-05-05 11:00:59 +01:00
Julian Fondren
b2e31fb012 use displayLine rather than display with displayCategory prompt 2019-05-04 19:30:55 -05:00
Julian Fondren
5b94a1b70c test -c,--continue option to continue tests on error 2019-05-04 18:58:12 -05:00
Dominik Picheta
6479316bd5
Merge pull request #646 from genotrance/nocompiler-recursive-test
Test case for #645 - recursive
2019-05-03 22:42:48 +01:00
Ganesh Viswanathan
dcf99adf91 Test case for #645 - recursive 2019-05-03 13:06:42 -05:00
Dominik Picheta
7fd3148cc7
Merge pull request #645 from genotrance/nocompiler-fixes-3
Fix nocompiler for recursive nimble calls
2019-05-03 09:39:13 +01:00
Ganesh Viswanathan
83a1cceb4e Fix for recursive nimble calls 2019-05-02 13:42:59 -05:00
genotrance
a437fd33c4
Merge pull request #643 from genotrance/nocompiler-fixes-2
Print nimscript errors
2019-05-01 07:52:04 -05:00
Ganesh Viswanathan
deb20ee57a Print nimscript errors 2019-04-30 21:29:04 -05:00
genotrance
ca779afb20 Fix quoted switch, multi-line description, more caching (#642)
* Fix quoted switch, multi-line description, more caching

* Incorporate feedback
2019-04-30 20:59:12 +01:00
Dominik Picheta
6542c1ef16 Squashed merge of #635 by @genotrance.
Squashed commit of the following:

commit e86a376f2faf9d26109405a3a9f73f986185f62d
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Sun Apr 28 15:37:22 2019 -0500

    Fix caching issue

commit 640ce3f2e464e52668b5350fdc5a8fe506e79d38
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Thu Apr 25 18:38:48 2019 -0500

    Clean up per feedback

commit ae3ef9f7a0cbad574b725d1bc7a83bd6115e19cc
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Thu Apr 25 16:39:26 2019 -0500

    Fix for 0.19.4

commit 915d6b2be43e33bc51327585193b1899386ee250
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Thu Apr 25 16:13:42 2019 -0500

    Keep nimscript separate, pin devel

commit c278bd6ba09771dc079029a87e3a375998f0b447
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Mon Apr 22 14:57:44 2019 -0500

    Hardcode version, json{}, code width 80, isScriptResultCached, no blank paramStr check

commit 64e5489e256d5fc5abbfe3345789f65edf5980b7
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Wed Apr 17 21:07:03 2019 -0500

    Remove compiler dependency

commit a031fffd70c118c16eb3e16d3b1ed10472baf5d7
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Wed Apr 17 16:49:09 2019 -0500

    Add devel to travis

commit d49916e2a05b6bd7716f45bd8f74253fc8037827
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Wed Apr 17 16:43:14 2019 -0500

    Interactive live, json to file

commit 24131deea4693199922f9a5697aa3d072cceaee1
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Wed Apr 17 12:40:27 2019 -0500

    Fix empty param, json echo

commit b22fe37d47fd03367d49129ea4d2d56a779a6f26
Merge: 5cf0240 2942f11
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Tue Apr 16 22:23:17 2019 -0500

    Merge branch 'nocompiler' of https://github.com/genotrance/nimble into nocompiler

commit 5cf0240b728ab6ff4a39ddf629ba5833eb8985f5
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Tue Apr 16 22:23:06 2019 -0500

    No hints, live output

commit 2942f116c7774e0fa91f770cebde32bc431923a5
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Tue Apr 16 21:02:28 2019 -0500

    Remove osx, test with stable

commit 85f3865ef195c7b813f0b9e30b5cc8c9b2756518
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Tue Apr 16 18:19:42 2019 -0500

    Remove ospaths, fix tests for Windows

commit 74201bcfe4de00bdece5b31715618975f9ce8e6e
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Tue Apr 16 14:00:14 2019 -0500

    No success for missing task

commit 8c2e65e223d32366b03004d9711364504c5d7916
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Tue Apr 16 13:44:32 2019 -0500

    Fix packageName to name

commit b05d9480281ebae7a0f5fd0331c8627bbf2a77d5
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Tue Apr 16 13:29:37 2019 -0500

    Add switch support

commit deecd903102a9baa5d4674cb9871cd9dbb658a04
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Tue Apr 16 12:24:01 2019 -0500

    API cleanup, json setCommand fix

commit 1e95fd4104ec3ffb69fe67b9c2fac23f991e163a
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Tue Apr 16 10:45:12 2019 -0500

    getParams once, hash nimscriptapi, fix loop in setcommand

commit 51d03b3845cd562796bb32d41d5ad17cd09a91e7
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Tue Apr 16 07:21:32 2019 -0500

    getPkgDir impl

commit 7d0a40aa286d114d7557b229852f3c314795dc5d
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Mon Apr 15 14:24:02 2019 -0500

    Before/after hook info

commit cbb3af3e970b20322030331d4849436b821f25ca
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Mon Apr 15 13:44:56 2019 -0500

    Remove nims from package dir after exec

commit 0ed53d60bcdc8bb11beddb965590ed3ee63349d4
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Sat Apr 13 00:44:26 2019 -0500

    Return bool from hooks

commit ab38b81b81e68cfccf3ca84fd854422cd3733c84
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Fri Apr 12 23:20:13 2019 -0500

    Initial version

commit b9ef88b9f79b48435e7b4beeff959b4223f4b8ba
Merge: 220ebae c8d79fc
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Tue Mar 26 20:16:21 2019 -0500

    Merge remote-tracking branch 'upstream/master' into nocompiler

commit 220ebae355c945963591b002a43b262a70640aa5
Merge: 3d7227c 119be48
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Wed Dec 12 18:02:10 2018 -0600

    Merge remote-tracking branch 'upstream/master'

commit 3d7227c8900c205aada488d60565c90e17759639
Merge: cf7263d 66d79bf
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Wed Oct 17 13:39:51 2018 -0500

    Merge remote-tracking branch 'upstream/master'

commit cf7263d6caf27ca4930ed54b05d4aa4f36e1dff1
Merge: 2fc3106 ee4c0ae
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Thu Sep 13 23:03:41 2018 -0500

    Merge remote-tracking branch 'upstream/master'

commit 2fc310623b9f49ea012fc04fa09713fda140a7a3
Merge: e9a8850 c249f9b
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Thu Apr 26 16:27:31 2018 -0500

    Merge remote-tracking branch 'upstream/master'

commit e9a885099b0b97bf3e0cddcde27e8c6b0bd51b10
Merge: 7adfd7b 75b7a21
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Thu Mar 8 14:26:46 2018 -0600

    Merge remote-tracking branch 'upstream/master'

commit 7adfd7be2b38a52886640579845de378139ca0cc
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Mon Jan 15 00:35:55 2018 -0600

    Updated fix for #398

commit de18319159b76a9da6765f35ea4d2e2c963d688a
Merge: 93ba4a0 3dae264
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Sun Jan 14 22:01:20 2018 -0600

    Merge remote-tracking branch 'upstream/master'

commit 93ba4a00820ccb9a5362f0398cf3b5b4782bbefe
Author: Ganesh Viswanathan <dev@genotrance.com>
Date:   Sat Jan 13 19:52:34 2018 -0600

    Fix for #398
2019-04-29 23:03:57 +01:00
Andreas Rumpf
c8d79fc022
update the installation instructions 2019-03-26 10:06:06 +01:00
Dominik Picheta
c6f1417099 Revert "Enforce the execution of the correct task types"
This reverts commit ee0bf06f01.
2019-03-17 11:28:19 +00:00
Dominik Picheta
5749f82ca5 Revert "Add before/after hooks for build action"
This reverts commit 2fc3efcd84.
2019-03-17 11:27:50 +00:00
Arne Döring
587402fa1d
Merge pull request #613 from LemonBoy/x1
Fix a handful of annoying things
2019-03-16 20:30:00 +01:00
LemonBoy
cd5db600dc Fix problem in briefClone
It was too brief. So brief that forgot to copy some important fields.
2019-02-14 19:22:36 +01:00
LemonBoy
1c8919092e Warn the user if no tests are found
Fixes #558
2019-02-14 11:32:00 +01:00
LemonBoy
2fc3efcd84 Add before/after hooks for build action
Fixes #606
2019-02-14 11:15:52 +01:00
LemonBoy
ee0bf06f01 Enforce the execution of the correct task types
Fixes #533
2019-02-14 10:58:01 +01:00
LemonBoy
1678142723 Fix regression in version string parsing (#563) 2019-02-14 10:15:52 +01:00
Ico Doornekamp
3d6dc90cd4 Strip whitespace from authentication token.
My token had a newline in the file, resulting in the following HTTP
request being made to Github:

```
GET /user HTTP/1.1
Host: api.github.com
Connection: Keep-Alive
authorization: token xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

content-type: application/x-www-form-urlencoded
accept: */*
user-agent: Nim httpclient/0.19.9
```

Unfortunately the friendly error message returned by the Github server
is lost in translation because getContent() throws an exception, never
handling the body.
2019-02-04 21:54:50 +00:00
Timothee Cour
61e6afe167 brew doesn't require sudo 2019-02-01 20:06:47 +01:00
Dominik Picheta
e60ab12549
Merge pull request #604 from alaviss/no-pkglist
config: don't error out if no package list found
2019-01-20 08:23:12 -08:00
Leorize
4be5308941
config: don't error out if no package list found 2019-01-20 15:53:06 +07:00
genotrance
88f5545c75 Allow removal of reverse dependencies with uninstall --deps flag (#601)
* Fix for #398

* Updated fix for #398

* Enable uninstall of reverse deps

* Update README

* Updates based on feedback
2019-01-08 19:58:19 +00:00
markfirmware
68bb97f30a Update readme.markdown (#596) 2018-12-18 08:49:49 +01:00
Dominik Picheta
9e87cb77a8
Merge pull request #593 from yglukhov/fix-findExe
Fixed findExe in nimscript
2018-12-14 00:09:44 +00:00
Yuriy Glukhov
22485bbd6a Fixed finExe in nimscript 2018-12-13 19:15:10 +02:00
technicallyagd
119be481df Additional information on Nim compiler integration (#554)
* Additional information on Nim compiler integration

* Move the information to a dedicated section

Emphasize the fact that no extra step is required if using default `nimbleDir`.

* Adds changes requested

* NimblePath to nimblePath for consistency

* Update readme.markdown
2018-12-10 22:51:43 +00:00
Matt Haggard
ba6577d36c Don't swallow control-C (#588)
* Don't swallow control-C

* Raise NimbleError instead of quitting

* Import NimbleError from common rather than version
2018-11-30 01:08:02 +00:00
Dominik Picheta
d9055dbac6 Use fgDefault instead of setting bg/fg. 2018-11-21 23:27:29 +00:00
juancarlospaco
5103541195 Just add a simple arrow 2018-11-20 13:59:49 -03:00
juancarlospaco
10260a1223 Fix #579 2018-11-12 03:36:45 -03:00
Jabba Laci
22aa3c1d13 #555: nimble test removes the created binaries (#566) 2018-10-19 23:40:41 +01:00
Clyybber
66d79bf9a0 Replace deprecated procs (#560)
* Replace deprecated procs

* Remove deprecated proc

* Use sugar instead of future

* Use sugar instead of future 2

* Remove renameBabelToNimble

since it wasn't exported or used anyways

* Use sugar instead of future 3

* Use toUnix
2018-10-14 17:45:18 +01:00
Dominik Picheta
f7373e03d5
Merge pull request #552 from LemonBoy/develop-uri
Don't prepend file:// twice
2018-10-09 00:21:50 +01:00
LemonBoy
a218bc3793 Don't prepend file:// twice 2018-10-08 21:31:25 +02:00
Dominik Picheta
af1c1d73c3 Version 0.9.0. 2018-09-19 23:53:27 +01:00
Dominik Picheta
1f79f17de9
Merge pull request #545 from braunse/master
Properly guard access to NimCompilerApiVersion.
2018-09-15 23:30:46 +01:00
Sebastien Braun
964af450dd Properly guard access to NimCompilerApiVersion.
Makes master compile on latest released Nim 0.18.0.
2018-09-15 21:36:33 +02:00
Andreas Rumpf
ee4c0aef26
Merge pull request #538 from nim-lang/handle-remove-dir-error
Handle remove dir error
2018-09-12 14:32:11 +02:00
Dominik Picheta
3ba8bd940d Fixes Nimble installation problem due to breaking change (090917926). 2018-09-11 23:35:59 +01:00
Dominik Picheta
c09b273eaf Handle removeDir error when removing Nimble's temp dir.
Replaces #537.
2018-09-11 21:11:41 +01:00
Dominik Picheta
0909179261 Breaking change: hybrid packages require installExt = @["nim"]. 2018-09-10 21:46:10 +01:00
Dominik Picheta
9f4081e888 Softened package structure rules when srcDir is not specified.
Fixes #469.
2018-09-10 00:24:20 +01:00
Dominik Picheta
fb879efb72 Binary packages no longer install .nim files implicitly. Refs #469. 2018-09-09 23:24:53 +01:00
Dominik Picheta
5dfe81556b Adds test for #476. Closes #476. 2018-09-09 22:26:27 +01:00
Dominik Picheta
5d765fcc27 Fixes #496. 2018-09-09 22:16:03 +01:00
Dominik Picheta
3ee1e115f4 Improves the directory structure that init creates.
Fixes #413. Refs #315.
2018-09-09 18:15:42 +01:00
Dominik Picheta
ae4fc39a7a Fixes check for existing .nimble file in nimble init. 2018-09-09 16:01:40 +01:00
Dominik Picheta
cc71c6f80e Improves nimble init (more licenses and consistent identifiers). 2018-09-09 15:43:43 +01:00
Andreas Rumpf
094b6fe180
Merge pull request #534 from nim-lang/araq-nim-compilerapi-version-3
prepare Nimble for NimCompilerApiVersion 3
2018-09-07 09:19:19 +02:00
Andreas Rumpf
e6c41248e6 prepare Nimble for NimCompilerApiVersion 3 2018-09-07 01:40:01 +02:00
Dominik Picheta
bce70a21bb
Merge pull request #532 from antizealot1337/init_create_dir
Fixes #503 by creating directory if current and name differ
2018-09-05 21:48:42 +01:00
Christopher Pridgen
260eb854fd Fixes #503 by creating directory if current and name differ 2018-09-04 21:21:41 -04:00
Dominik Picheta
7d1f096626 Ask user to remove whole directory, not just .nimble-link. 2018-09-02 20:05:14 +01:00
Dominik Picheta
d606867da6 Fixes #436. Develop clones HEAD and full history. 2018-09-02 01:11:50 +01:00
Dominik Picheta
225a0ef661 Fixes #501. 2018-09-01 23:29:26 +01:00
Dominik Picheta
fe21329f3d Fixes #531. Fixes #393. Replaces #465. 2018-09-01 23:01:16 +01:00
antizealot1337
a3a60eca51 Prompt for multiple selections interactively when possible (#441)
* Add interactive selection ability and preserve old behavior if not available

* Add description of library and binary packages

* Add function origin to doc

* Fix typos and windows compilation test

* Change select message type and fix style

* Change to use TAB to cycle through options
2018-09-01 13:09:37 +01:00
Dominik Picheta
57f73ea268 Fixes #285. 2018-08-28 17:45:28 +01:00
Dominik Picheta
70c9954c41 Fixes regression introduced by 02945e57b5. 2018-08-28 17:20:25 +01:00
Dominik Picheta
2145f266e1 Fix custom task template. 2018-08-28 16:15:02 +01:00
Dominik Picheta
f9c54f7607 Fixes Travis CI run with choosenim.
Squashed commit of the following:

commit 0f8a7e178d
Author: Dominik Picheta <dominikpicheta@gmail.com>
Date:   Tue Aug 28 14:56:03 2018 +0100

    Fixes NIM_PREFIX_DIR affecting testing logs.

commit f566e8bcd1
Author: Dominik Picheta <dominikpicheta@gmail.com>
Date:   Tue Aug 28 14:26:45 2018 +0100

    Attempt to fix travis.
2018-08-28 15:17:32 +01:00
Dominik Picheta
67cf608f55 Build against a pinned version of Nim devel on travis. 2018-08-27 22:31:17 +01:00
Dominik Picheta
9cf83b281f Re-define `task` template in nimscriptapi.nim in prep for #482. 2018-08-27 21:20:43 +01:00
Dominik Picheta
8134427266 Fixes isNil errors for compatibility with latest Nim. 2018-08-27 17:27:09 +01:00
Dominik Picheta
e06ad6e497 Adds .nimble/bin to PATH in travis. 2018-08-23 00:42:48 +01:00
Dominik Picheta
db222bbae1 Improves pre and post hooks. Fixes #524. 2018-08-23 00:33:06 +01:00
Dominik Picheta
2e803ec9cf Fixes #520. 2018-08-21 17:52:24 +01:00
Dominik Picheta
c169338e54
Merge pull request #522 from trialism/master
Improved handling of proxy environment variables
2018-08-17 12:07:42 +01:00
trialism
eecf4f95b3
Improved handling of proxy environment variables 2018-08-17 13:00:55 +02:00
Dominik Picheta
682a444748
Merge pull request #512 from LemonBoy/cleaner-cleanup
Add missing call to cleanup()
2018-07-13 16:22:00 +01:00
LemonBoy
8d4b0b34dd Add missing call to cleanup() 2018-07-13 14:55:35 +02:00
Dominik Picheta
a7ed90e4b6
Fixes regression introduced by new Nim VM code.
https://github.com/nim-lang/Nim/issues/8096
2018-06-25 23:31:11 +01:00
Andreas Rumpf
3b177e278b Make Nimble compatible with the upcoming compiler API changes (#500)
* Make Nimble compatible with the upcoming compiler API changes

* make travis use nim-0.18.0

* make Nimble compile with older compiler versions

* attempt to make tests green with Nim v0.18
2018-06-10 13:50:40 +01:00
Dominik Picheta
8c57227f55
Merge pull request #497 from yglukhov/fix-getPkgDir
Fixed getPkgDir
2018-05-31 13:10:34 +01:00
Yuriy Glukhov
5fcd7e5965 Fixed getPkgDir 2018-05-31 14:59:35 +03:00
Dominik Picheta
e7f8cb6c81
Merge pull request #494 from Aearnus/master
Add note about SSH key to the section on nimble publish
2018-05-22 00:37:22 +01:00
Aearnus
364f7bc260 Added SSH key note 2018-05-20 22:45:43 -07:00
Dominik Picheta
68fb7837e8
Merge pull request #492 from nim-lang/araq-hotfix-task-execution
fixes #491
2018-05-17 13:51:26 +01:00
Araq
12b5a9a4d1 fixes #491 2018-05-17 00:22:40 +02:00
Andreas Rumpf
02945e57b5 prepare Nimble for the upcoming compiler API (#489)
* prepare Nimble for the upcoming compiler API changes

* attempt to make tests green

* make some tests green with the upcoming compiler API
2018-05-14 11:35:30 +01:00
Andreas Rumpf
e2869714b8 prepare Nimble for the upcoming compiler API changes (#488)
* prepare Nimble for the upcoming compiler API changes

* attempt to make tests green
2018-05-13 19:07:27 +01:00
Ahmed T. Youssef
b86dca4105 Add link to the Pull request after nim publish (#483)
* Add link to the pull requests page

* Add version bump

* inline PR url in the nim publish output

* Small fixes on top of PR.
2018-05-04 16:53:39 +01:00
Dominik Picheta
c249f9b836
Merge pull request #471 from mrwonko/docs/init-directory
document that nimble init uses the current directory
2018-04-01 00:15:02 +01:00
Willi Schinmeyer
9fb0e557f2 document that nimble init uses the current directory 2018-03-31 20:12:07 +02:00
Dominik Picheta
287fa0fe59
Merge pull request #470 from jxy/notty_nocolor
noColor if stdout is not a tty
2018-03-27 18:38:50 +01:00
Xiao-Yong Jin
898d4de826 noColor if stdout is not a tty 2018-03-27 11:46:22 -05:00
Dominik Picheta
347b6b7ab1
Merge pull request #467 from shinriyo/patch-2
for a sense of unity.
2018-03-22 11:11:05 +00:00
Dominik Picheta
35c12cd972
Fixes to PR. 2018-03-21 11:10:23 +00:00
shinriyo
21357d3fa5
apply review feedback
I fix.
2018-03-21 12:09:24 +09:00
shinriyo
bf49330d2c
for a sense of unity.
another is .nimble. so I modified.
2018-03-20 11:39:56 +09:00
Dominik Picheta
75b7a215e8
Merge pull request #462 from k0pernicus/master
Add a new troubleshooting solution about _alert handshake failure_ errors
2018-02-27 18:55:26 +00:00
k0pernicus
64eb274198 Add a new troubleshooting 2018-02-26 18:46:29 +01:00
70 changed files with 2449 additions and 1111 deletions

20
.gitignore vendored
View file

@ -6,12 +6,15 @@ nimcache/
# Absolute paths # Absolute paths
/src/babel /src/babel
/src/nimble /src/nimble
/tests/tester
# executables from test and build # executables from test and build
/nimble /nimble
/tests/nimscript/nimscript src/nimblepkg/cli
/tests/issue27/issue27 src/nimblepkg/packageinfo
src/nimblepkg/packageparser
src/nimblepkg/reversedeps
src/nimblepkg/version
src/nimblepkg/download
# Windows executables # Windows executables
*.exe *.exe
@ -19,4 +22,13 @@ nimcache/
# VCC compiler and linker artifacts # VCC compiler and linker artifacts
*.ilk *.ilk
*.pdb *.pdb
# Editors and IDEs project files and folders
.vscode
# VCS artifacts
*.orig
# Test procedure artifacts
nimble_*.nims

View file

@ -1,21 +1,24 @@
os: os:
- windows
- linux - linux
dist: trusty - osx
language: c language: c
install: env:
- | - BRANCH=0.19.6
wget https://nim-lang.org/download/nim-0.17.2.tar.xz - BRANCH=0.20.2
tar -xf nim-0.17.2.tar.xz - BRANCH=1.0.6
cd nim-0.17.2 # This is the latest working Nim version against which Nimble is being tested
sh build.sh - BRANCH=#ab525cc48abdbbbed1f772e58e9fe21474f70f07
cd ..
before_script: cache:
- set -e directories:
- set -x - "$HOME/.choosenim"
- export PATH=`pwd`/nim-0.17.2/bin:$PATH
install:
- curl https://gist.github.com/genotrance/fb53504a4fba88bc5201d3783df5c522/raw/travis.sh -LsSf -o travis.sh
- source travis.sh
script: script:
- cd tests - cd tests

View file

@ -3,6 +3,122 @@
# Nimble changelog # Nimble changelog
## 0.11.0 - 22/09/2019
This is a major release containing nearly 60 commits. Most changes are
bug fixes, but this release also includes a couple new features:
- Binaries can now be built and run using the new ``run`` command.
- The ``NimblePkgVersion`` is now defined so you can easily get the package
version in your source code
([example](https://github.com/nim-lang/nimble/blob/4a2aaa07d/tests/nimbleVersionDefine/src/nimbleVersionDefine.nim)).
Some other highlights:
- Temporary files are now kept when the ``--debug`` flag is used.
- Fixed dependency resolution issues with "#head" packages (#432 and #672).
- The `install` command can now take Nim compiler flags via the new
``--passNim`` flag.
- Command line arguments are now passed properly to tasks (#633).
- The ``test`` command now respects the specified backend (#631).
- The ``dump`` command will no longer prompt and now has an implicit ``-y``.
- Fixed bugs with the new nimscript executor (#665).
- Fixed multiple downloads and installs of the same package (#678).
- Nimble init no longer overwrites existing files (#581).
- Fixed incorrect submodule version being pulled when in a non-master branch (#675).
----
Full changelog: https://github.com/nim-lang/nimble/compare/v0.10.2...v0.11.0
## 0.10.2 - 03/06/2019
This is a small release which avoids object variant changes that are now
treated as runtime errors (Nim #1286). It also adds support for `backend`
selection during `nimble init`.
Multiple bug fixes are also included:
- Fixed an issue where failing tasks were not returning a non-zero return
value (#655).
- Error out if `bin` is a Nim source file (#597).
- Fixed an issue where nimble task would not run if file of same name exists.
- Fixed an issue that prevented multiple instances of nimble from running on
the same package.
----
Full changelog: https://github.com/nim-lang/nimble/compare/v0.10.0...v0.10.2
## 0.10.0 - 27/05/2019
Nimble now uses the Nim compiler directly via `nim e` to execute nimble
scripts rather than embedding the Nim VM. This has multiple benefits:
- Evolve independently from Nim enabling new versions of Nimble to work
with multiple versions of Nim.
- Inherit all nimscript enhancements and bug fixes rather than having to
duplicate functionality.
- Fast build time and smaller binary.
- No dependency on the compiler package which could cause dependency issues
when nimble is used as a package.
Several other features and fixes have been implemented to improve general
development and test workflows.
- `nimble test` now sports a `-continue` or `-c` flag that allows tests
to continue on failure, removes all created test binaries on completion
and warns if no tests found.
- The `--inclDeps` or `-i` flag enables `nimble uninstall` to remove all
dependent packages during uninstall.
- Added documentation on the usage of a custom `nimbleDir`.
- Package type interactive prompt is more readable.
- Save temporary files in a per-user temp dir to enable Nimble on multi-user
systems.
- CTRL-C is now handled correctly in interactive prompts.
- Fixed issue where empty package list led to error.
- Fixed issue where file:// was prepended incorrectly.
- Fixed miscellaneous issues in version parsing, Github auth and briefClone.
- Miscellaneous cleanup of deprecated procs.
----
Full changelog: https://github.com/nim-lang/nimble/compare/v0.9.0...v0.10.0
## 0.9.0 - 19/09/2018
This is a major new release which contains at least one breaking change.
Unfortunately even though it was planned, support for lock files did not
make it into this release. The release does
however contain a large number of fixes spread across 57 commits.
The breaking change in this release is to do with the handling of binary
package. **Any package that specifies a ``bin`` value in it's .nimble file**
**will no longer install any Nim source code files**, in other words it's not
going to be a hybrid package by default. This means that so called "hybrid
packages" now need to specify ``installExt = @["nim"]`` in their metadata,
otherwise they will become binary packages only.
- **Breaking:** hybrid packages require ``installExt = @["nim"]``
([Commit](https://github.com/nim-lang/nimble/commit/09091792615eacd503e87ca70252c572a4bde2b5))
- **The ``init`` command can now show a list of choices for information such as**
**the license.**
- **The ``init`` command now creates correct project structures for all package**
**types.**
- **Fatal errors are no longer created when the path inside a .nimble-link file**
**doesn't exist.**
- **The ``develop`` command now always clones HEAD and grabs the full repo history.**
- **The default ``test`` task no longer executes all tests (only those starting with 't').**
- Colour is no longer used when `isatty` is false.
- ``publish`` now shows the URL of the created PR.
- The ``getPkgDir`` procedure has been fixed in the Nimble file API.
- Improved handling of proxy environment variables.
- Codebase has been improved not to rely on `nil` in strings and seqs.
- The handling of pre- and post-hooks has been improved significantly.
- Fixed the ``path`` command for packages with a ``srcDir`` and optimised the
package look-up.
----
Full changelog: https://github.com/nim-lang/nimble/compare/v0.8.10...v0.9.0
## 0.8.10 - 23/02/2018 ## 0.8.10 - 23/02/2018
The first release of 2018! Another fairly big release containing 40 commits. The first release of 2018! Another fairly big release containing 40 commits.

View file

@ -1,26 +1,17 @@
import ospaths
template thisModuleFile: string = instantiationInfo(fullPaths = true).filename
when fileExists(thisModuleFile.parentDir / "src/nimblepkg/common.nim"):
# In the git repository the Nimble sources are in a ``src`` directory.
import src/nimblepkg/common
else:
# When the package is installed, the ``src`` directory disappears.
import nimblepkg/common
# Package # Package
version = nimbleVersion version = "0.11.0"
author = "Dominik Picheta" author = "Dominik Picheta"
description = "Nim package manager." description = "Nim package manager."
license = "BSD" license = "BSD"
bin = @["nimble"] bin = @["nimble"]
srcDir = "src" srcDir = "src"
installExt = @["nim"]
# Dependencies # Dependencies
requires "nim >= 0.13.0", "compiler#head" requires "nim >= 0.13.0"
when defined(nimdistros): when defined(nimdistros):
import distros import distros

View file

@ -15,6 +15,7 @@ Interested in learning **how to create a package**? Skip directly to that sectio
- [nimble install](#nimble-install) - [nimble install](#nimble-install)
- [nimble uninstall](#nimble-uninstall) - [nimble uninstall](#nimble-uninstall)
- [nimble build](#nimble-build) - [nimble build](#nimble-build)
- [nimble run](#nimble-run)
- [nimble c](#nimble-c) - [nimble c](#nimble-c)
- [nimble list](#nimble-list) - [nimble list](#nimble-list)
- [nimble search](#nimble-search) - [nimble search](#nimble-search)
@ -74,10 +75,15 @@ not need to install Nimble manually**.
But in case you still want to install Nimble manually, you can follow the But in case you still want to install Nimble manually, you can follow the
following instructions. following instructions.
There are two ways to install Nimble manually. The first is using the There are two ways to install Nimble manually. Using ``koch`` and using Nimble
``koch`` tool included in the Nim distribution and itself.
### Using koch
The ``koch`` tool is included in the Nim distribution and
[repository](https://github.com/nim-lang/Nim/blob/devel/koch.nim). [repository](https://github.com/nim-lang/Nim/blob/devel/koch.nim).
Simply execute the following command to compile and install Nimble. Simply navigate to the location of your Nim installation and execute the
following command to compile and install Nimble.
``` ```
./koch nimble ./koch nimble
@ -86,23 +92,19 @@ Simply execute the following command to compile and install Nimble.
This will clone the Nimble repository, compile Nimble and copy it into This will clone the Nimble repository, compile Nimble and copy it into
Nim's bin directory. Nim's bin directory.
The second approach is to install Nimble as a Nimble package. You can do this ### Using Nimble
by compiling Nimble, then running ``nimble install`` in Nimble's directory.
In most cases you will already have Nimble installed, you can install a newer
version of Nimble by simply running the following command:
``` ```
git clone https://github.com/nim-lang/nimble.git nimble install nimble
cd nimble
nim c src/nimble
src/nimble install
``` ```
**Note for Windows users**: You will need to rename ``nimble.exe`` after This will download the latest release of Nimble and install it on your system.
compilation to something else like ``nimble1.exe``, then run
``src\nimble1.exe install``.
This will install Nimble to the default Nimble packages location: Note that you must have `~/.nimble/bin` in your PATH for this to work, if you're
``~/.nimble/pkgs``. The binary will be installed to ``~/.nimble/bin``, so you using choosenim then you likely already have this set up correctly.
will need to add this directory to your PATH.
## Nimble usage ## Nimble usage
@ -170,12 +172,13 @@ example:
This is of course Git-specific, for Mercurial, use ``tip`` instead of ``head``. A This is of course Git-specific, for Mercurial, use ``tip`` instead of ``head``. A
branch, tag, or commit hash may also be specified in the place of ``head``. branch, tag, or commit hash may also be specified in the place of ``head``.
Instead of specifying a VCS branch, you may also specify a version range, for Instead of specifying a VCS branch, you may also specify a concrete version or a
example: version range, for example:
$ nimble install nimgame@0.5
$ nimble install nimgame@"> 0.5" $ nimble install nimgame@"> 0.5"
In this case a version which is greater than ``0.5`` will be installed. The latter command will install a version which is greater than ``0.5``.
If you don't specify a parameter and there is a ``package.nimble`` file in your If you don't specify a parameter and there is a ``package.nimble`` file in your
current working directory then Nimble will install the package residing in current working directory then Nimble will install the package residing in
@ -221,8 +224,8 @@ instead of a name.
### nimble uninstall ### nimble uninstall
The ``uninstall`` command will remove an installed package. Attempting to remove The ``uninstall`` command will remove an installed package. Attempting to remove
a package which other packages depend on is disallowed and will result in an a package which other packages depend on will result in an error. You can use the
error. You must currently manually remove the reverse dependencies first. ``--inclDeps`` or ``-i`` flag to remove all dependent packages along with the package.
Similar to the ``install`` command you can specify a version range, for example: Similar to the ``install`` command you can specify a version range, for example:
@ -236,6 +239,13 @@ flags, i.e. a debug build which includes stack traces but no GDB debug
information. The ``install`` command will build the package in release mode information. The ``install`` command will build the package in release mode
instead. instead.
### nimble run
The ``run`` command can be used to build and run any binary specified in your
package's ``bin`` list. You can pass any compilation flags you wish by specifying
them before the ``run`` command, and you can specify arguments for your binary
by specifying them after the ``run`` command.
### nimble c ### nimble c
The ``c`` (or ``compile``, ``js``, ``cc``, ``cpp``) command can be used by The ``c`` (or ``compile``, ``js``, ``cc``, ``cpp``) command can be used by
@ -303,7 +313,7 @@ which can be useful to read the bundled documentation. Example:
### nimble init ### nimble init
The nimble ``init`` command will start a simple wizard which will create The nimble ``init`` command will start a simple wizard which will create
a quick ``.nimble`` file for your project. a quick ``.nimble`` file for your project in the current directory.
As of version 0.7.0, the ``.nimble`` file that this command creates will As of version 0.7.0, the ``.nimble`` file that this command creates will
use the new NimScript format. use the new NimScript format.
@ -313,7 +323,7 @@ Check out the [Creating Packages](#creating-packages) section for more info.
Publishes your Nimble package to the official Nimble package repository. Publishes your Nimble package to the official Nimble package repository.
**Note:** Requires a valid GitHub account. **Note:** Requires a valid GitHub account with an SSH key attached to it. To upload your public key onto your GitHub account, follow [this link](https://github.com/settings/keys).
### nimble tasks ### nimble tasks
@ -391,7 +401,7 @@ a package.
A .nimble file can be created easily using Nimble's ``init`` command. This A .nimble file can be created easily using Nimble's ``init`` command. This
command will ask you a bunch of questions about your package, then generate a command will ask you a bunch of questions about your package, then generate a
.nimble file for you. .nimble file for you in the current directory.
A bare minimum .nimble file follows: A bare minimum .nimble file follows:
@ -418,6 +428,7 @@ You can also specify multiple dependencies like so:
requires "nim >= 0.10.0", "foobar >= 0.1.0" requires "nim >= 0.10.0", "foobar >= 0.1.0"
requires "fizzbuzz >= 1.0" requires "fizzbuzz >= 1.0"
requires "https://github.com/user/pkg#5a54b5e"
``` ```
Nimble currently supports installation of packages from a local directory, a Nimble currently supports installation of packages from a local directory, a
@ -494,7 +505,7 @@ For a package named "foobar", the recommended project structure is the following
└── src └── src
└── foobar.nim # Imported via `import foobar` └── foobar.nim # Imported via `import foobar`
└── tests # Contains the tests └── tests # Contains the tests
├── nim.cfg ├── config.nims
├── tfoo1.nim # First test ├── tfoo1.nim # First test
└── tfoo2.nim # Second test └── tfoo2.nim # Second test
@ -647,7 +658,7 @@ combo.
Dependencies are automatically installed before building. Dependencies are automatically installed before building.
It's a good idea to test that the dependencies you specified are correct by It's a good idea to test that the dependencies you specified are correct by
running by running ``nimble build`` or ``nimble install`` in the directory running ``nimble build`` or ``nimble install`` in the directory
of your package. of your package.
### Hybrids ### Hybrids
@ -727,13 +738,13 @@ installing your package (on macOS):
``` ```
Hint: This package requires some external dependencies. Hint: This package requires some external dependencies.
Hint: To install them you may be able to run: Hint: To install them you may be able to run:
Hint: sudo brew install openssl Hint: brew install openssl
``` ```
### Nim compiler ### Nim compiler
The Nim compiler cannot read .nimble files. Its knowledge of Nimble is The Nim compiler cannot read .nimble files. Its knowledge of Nimble is
limited to the ``nimblePaths`` feature which allows it to use packages installed limited to the ``nimblePath`` feature which allows it to use packages installed
in Nimble's package directory when compiling your software. This means that in Nimble's package directory when compiling your software. This means that
it cannot resolve dependencies, and it can only use the latest version of a it cannot resolve dependencies, and it can only use the latest version of a
package when compiling. package when compiling.
@ -746,6 +757,27 @@ This means that you can safely compile using the compiler when developing your
software, but you should use Nimble to build the package before publishing it software, but you should use Nimble to build the package before publishing it
to ensure that the dependencies you specified are correct. to ensure that the dependencies you specified are correct.
### Compile with `nim` after changing the nimble directory
The Nim compiler has been preconfigured to look at the default nimble directory while compiling,
so no extra step is required to use nimble managed packages in your code.
However, if you are using a custom `nimbleDir`, you need to specify the
`--nimblePath:PATH` option. For example,
if your `nimble` directory is located at `/some/custom/path/nimble`, this should work:
```
nim c --nimblePath:/some/custom/path/nimble/pkgs main.nim
```
Some code editors rely on `nim check` to check for errors under the hood (e.g. VScode),
and the editor extension may not allow users to pass custom option to `nim check`, which
will cause `nim check` to scream `Error: cannot open file:<the_package>`. In this case,
you will have to use [Nim compiler's configuration files](https://nim-lang.org/docs/nimc.html#compiler-usage-configuration-files). Simply add the line:
```
nimblePath = "/some/custom/path/nimble/pkgs"
```
to the `nim.cfg` located in any directory listed in the [documentation](https://nim-lang.org/docs/nimc.html#compiler-usage-configuration-files), this should resolve the problem.
### Versions ### Versions
Versions of cloned packages via Git or Mercurial are determined through the Versions of cloned packages via Git or Mercurial are determined through the
@ -781,6 +813,19 @@ To summarise, the steps for release are:
Once the new tag is in the remote repository, Nimble will be able to detect Once the new tag is in the remote repository, Nimble will be able to detect
the new version. the new version.
##### Git Version Tagging
Use dot separated numbers to represent the release version in the git
tag label. Nimble will parse these git tag labels to know which
versions of a package are published.
``` text
v0.2.0 # 0.2.0
v1 # 1
v1.2.3-zuzu # 1.2.3
foo-1.2.3.4 # 1.2.3.4
```
## Publishing packages ## Publishing packages
Publishing packages isn't a requirement. But doing so allows people to associate Publishing packages isn't a requirement. But doing so allows people to associate
@ -881,6 +926,10 @@ flag to the file ```src/nimble.nim.cfg```.
After that, you can run ```src/nimble install``` and overwrite the existing After that, you can run ```src/nimble install``` and overwrite the existing
installation. installation.
* ``Could not download: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure``
If you are on macOS, you need to set and export the ```DYLD_LIBRARY_PATH``` environment variable to the directory where your OpenSSL libraries are. For example, if you use OpenSSL, you have to set ```export DYLD_LIBRARY_PATH=/usr/local/opt/openssl/lib``` in your ```$HOME/.bashrc``` file.
* ``Error: ambiguous identifier: 'version' --use nimscriptapi.version or system.version`` * ``Error: ambiguous identifier: 'version' --use nimscriptapi.version or system.version``
Make sure that you are running at least version 0.16.0 of Nim (or the latest nightly). Make sure that you are running at least version 0.16.0 of Nim (or the latest nightly).
@ -901,7 +950,7 @@ The ``master`` branch is...
* default * default
* bleeding edge * bleeding edge
* tested to compile with the latest Nim version * tested to compile with a pinned (close to HEAD) commit of Nim
The ``stable`` branch is... The ``stable`` branch is...

View file

@ -3,20 +3,21 @@
import system except TResult import system except TResult
import httpclient, parseopt, os, osproc, pegs, tables, parseutils, import os, tables, strtabs, json, algorithm, sets, uri, sugar, sequtils, osproc
strtabs, json, algorithm, sets, uri, future, sequtils import std/options as std_opt
import strutils except toLower import strutils except toLower
from unicode import toLower from unicode import toLower
from sequtils import toSeq from sequtils import toSeq
from strformat import fmt
import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools, import nimblepkg/packageinfo, nimblepkg/version, nimblepkg/tools,
nimblepkg/download, nimblepkg/config, nimblepkg/common, nimblepkg/download, nimblepkg/config, nimblepkg/common,
nimblepkg/publish, nimblepkg/options, nimblepkg/packageparser, nimblepkg/publish, nimblepkg/options, nimblepkg/packageparser,
nimblepkg/cli, nimblepkg/packageinstaller, nimblepkg/reversedeps, nimblepkg/cli, nimblepkg/packageinstaller, nimblepkg/reversedeps,
nimblepkg/nimscriptexecutor nimblepkg/nimscriptexecutor, nimblepkg/init
import nimblepkg/nimscriptsupport import nimblepkg/nimscriptwrapper
proc refresh(options: Options) = proc refresh(options: Options) =
## Downloads the package list from the specified URL. ## Downloads the package list from the specified URL.
@ -95,7 +96,7 @@ proc copyFilesRec(origDir, currentDir, dest: string,
## Copies all the required files, skips files specified in the .nimble file ## Copies all the required files, skips files specified in the .nimble file
## (PackageInfo). ## (PackageInfo).
## Returns a list of filepaths to files which have been installed. ## Returns a list of filepaths to files which have been installed.
result = initSet[string]() result = initHashSet[string]()
let whitelistMode = let whitelistMode =
pkgInfo.installDirs.len != 0 or pkgInfo.installDirs.len != 0 or
pkgInfo.installFiles.len != 0 or pkgInfo.installFiles.len != 0 or
@ -156,7 +157,8 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[PackageInfo] =
"dependencies for $1@$2" % [pkginfo.name, pkginfo.specialVersion], "dependencies for $1@$2" % [pkginfo.name, pkginfo.specialVersion],
priority = HighPriority) priority = HighPriority)
var pkgList = getInstalledPkgsMin(options.getPkgsDir(), options) var pkgList {.global.}: seq[tuple[pkginfo: PackageInfo, meta: MetaData]] = @[]
once: pkgList = getInstalledPkgsMin(options.getPkgsDir(), options)
var reverseDeps: seq[tuple[name, version: string]] = @[] var reverseDeps: seq[tuple[name, version: string]] = @[]
for dep in pkginfo.requires: for dep in pkginfo.requires:
if dep.name == "nimrod" or dep.name == "nim": if dep.name == "nimrod" or dep.name == "nim":
@ -190,11 +192,6 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[PackageInfo] =
else: else:
display("Info:", "Dependency on $1 already satisfied" % $dep, display("Info:", "Dependency on $1 already satisfied" % $dep,
priority = HighPriority) priority = HighPriority)
if pkg.isLinked:
# TODO (#393): This can be optimised since the .nimble-link files have
# a secondary line that specifies the srcDir.
pkg = pkg.toFullInfo(options)
result.add(pkg) result.add(pkg)
# Process the dependencies of this dependency. # Process the dependencies of this dependency.
result.add(processDeps(pkg.toFullInfo(options), options)) result.add(processDeps(pkg.toFullInfo(options), options))
@ -204,12 +201,13 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[PackageInfo] =
# in the path. # in the path.
var pkgsInPath: StringTableRef = newStringTable(modeCaseSensitive) var pkgsInPath: StringTableRef = newStringTable(modeCaseSensitive)
for pkgInfo in result: for pkgInfo in result:
let currentVer = pkgInfo.getConcreteVersion(options)
if pkgsInPath.hasKey(pkgInfo.name) and if pkgsInPath.hasKey(pkgInfo.name) and
pkgsInPath[pkgInfo.name] != pkgInfo.version: pkgsInPath[pkgInfo.name] != currentVer:
raise newException(NimbleError, raise newException(NimbleError,
"Cannot satisfy the dependency on $1 $2 and $1 $3" % "Cannot satisfy the dependency on $1 $2 and $1 $3" %
[pkgInfo.name, pkgInfo.version, pkgsInPath[pkgInfo.name]]) [pkgInfo.name, currentVer, pkgsInPath[pkgInfo.name]])
pkgsInPath[pkgInfo.name] = pkgInfo.version pkgsInPath[pkgInfo.name] = currentVer
# We add the reverse deps to the JSON file here because we don't want # We add the reverse deps to the JSON file here because we don't want
# them added if the above errorenous condition occurs # them added if the above errorenous condition occurs
@ -218,16 +216,33 @@ proc processDeps(pkginfo: PackageInfo, options: Options): seq[PackageInfo] =
for i in reverseDeps: for i in reverseDeps:
addRevDep(options.nimbleData, i, pkginfo) addRevDep(options.nimbleData, i, pkginfo)
proc buildFromDir(pkgInfo: PackageInfo, paths: seq[string], proc buildFromDir(
args: var seq[string]) = pkgInfo: PackageInfo, paths, args: seq[string],
options: Options
) =
## Builds a package as specified by ``pkgInfo``. ## Builds a package as specified by ``pkgInfo``.
let binToBuild = options.getCompilationBinary(pkgInfo)
# Handle pre-`build` hook.
let realDir = pkgInfo.getRealDir()
cd realDir: # Make sure `execHook` executes the correct .nimble file.
if not execHook(options, actionBuild, true):
raise newException(NimbleError, "Pre-hook prevented further execution.")
if pkgInfo.bin.len == 0: if pkgInfo.bin.len == 0:
raise newException(NimbleError, raise newException(NimbleError,
"Nothing to build. Did you specify a module to build using the" & "Nothing to build. Did you specify a module to build using the" &
" `bin` key in your .nimble file?") " `bin` key in your .nimble file?")
let realDir = pkgInfo.getRealDir() var args = args
let nimblePkgVersion = "-d:NimblePkgVersion=" & pkgInfo.version
for path in paths: args.add("--path:\"" & path & "\" ") for path in paths: args.add("--path:\"" & path & "\" ")
var binariesBuilt = 0
for bin in pkgInfo.bin: for bin in pkgInfo.bin:
# Check if this is the only binary that we want to build.
if binToBuild.isSome() and binToBuild.get() != bin:
let binToBuild = binToBuild.get()
if bin.extractFilename().changeFileExt("") != binToBuild:
continue
let outputOpt = "-o:\"" & pkgInfo.getOutputDir(bin) & "\"" let outputOpt = "-o:\"" & pkgInfo.getOutputDir(bin) & "\""
display("Building", "$1/$2 using $3 backend" % display("Building", "$1/$2 using $3 backend" %
[pkginfo.name, bin, pkgInfo.backend], priority = HighPriority) [pkginfo.name, bin, pkgInfo.backend], priority = HighPriority)
@ -236,10 +251,15 @@ proc buildFromDir(pkgInfo: PackageInfo, paths: seq[string],
if not existsDir(outputDir): if not existsDir(outputDir):
createDir(outputDir) createDir(outputDir)
let input = realDir / bin.changeFileExt("nim")
# `quoteShell` would be more robust than `\"` (and avoid quoting when
# un-necessary) but would require changing `extractBin`
let cmd = "\"$#\" $# --noNimblePath $# $# $# \"$#\"" %
[getNimBin(), pkgInfo.backend, nimblePkgVersion,
join(args, " "), outputOpt, input]
try: try:
doCmd("\"" & getNimBin() & "\" $# --noBabelPath $# $# \"$#\"" % doCmd(cmd, showCmd = true)
[pkgInfo.backend, join(args, " "), outputOpt, binariesBuilt.inc()
realDir / bin.changeFileExt("nim")])
except NimbleError: except NimbleError:
let currentExc = (ref NimbleError)(getCurrentException()) let currentExc = (ref NimbleError)(getCurrentException())
let exc = newException(BuildFailed, "Build failed for package: " & let exc = newException(BuildFailed, "Build failed for package: " &
@ -249,13 +269,14 @@ proc buildFromDir(pkgInfo: PackageInfo, paths: seq[string],
exc.hint = hint exc.hint = hint
raise exc raise exc
proc buildFromDir(pkgInfo: PackageInfo, paths: seq[string], forRelease: bool) = if binariesBuilt == 0:
var args: seq[string] raiseNimbleError(
if forRelease: "No binaries built, did you specify a valid binary name?"
args = @["-d:release"] )
else:
args = @[] # Handle post-`build` hook.
buildFromDir(pkgInfo, paths, args) cd realDir: # Make sure `execHook` executes the correct .nimble file.
discard execHook(options, actionBuild, false)
proc removePkgDir(dir: string, options: Options) = proc removePkgDir(dir: string, options: Options) =
## Removes files belonging to the package in ``dir``. ## Removes files belonging to the package in ``dir``.
@ -330,6 +351,13 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
## to the packages this package depends on. ## to the packages this package depends on.
## The return value of this function is used by ## The return value of this function is used by
## ``processDeps`` to gather a list of paths to pass to the nim compiler. ## ``processDeps`` to gather a list of paths to pass to the nim compiler.
# Handle pre-`install` hook.
if not options.depsOnly:
cd dir: # Make sure `execHook` executes the correct .nimble file.
if not execHook(options, actionInstall, true):
raise newException(NimbleError, "Pre-hook prevented further execution.")
var pkgInfo = getPkgInfo(dir, options) var pkgInfo = getPkgInfo(dir, options)
let realDir = pkgInfo.getRealDir() let realDir = pkgInfo.getRealDir()
let binDir = options.getBinDir() let binDir = options.getBinDir()
@ -354,7 +382,11 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
# if the build fails then the old package will still be installed. # if the build fails then the old package will still be installed.
if pkgInfo.bin.len > 0: if pkgInfo.bin.len > 0:
let paths = result.deps.map(dep => dep.getRealDir()) let paths = result.deps.map(dep => dep.getRealDir())
buildFromDir(pkgInfo, paths, true) let flags = if options.action.typ in {actionInstall, actionPath, actionUninstall, actionDevelop}:
options.action.passNimFlags
else:
@[]
buildFromDir(pkgInfo, paths, "-d:release" & flags, options)
let pkgDestDir = pkgInfo.getPkgDest(options) let pkgDestDir = pkgInfo.getPkgDest(options)
if existsDir(pkgDestDir) and existsFile(pkgDestDir / "nimblemeta.json"): if existsDir(pkgDestDir) and existsFile(pkgDestDir / "nimblemeta.json"):
@ -379,7 +411,7 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
createDir(pkgDestDir) createDir(pkgDestDir)
# Copy this package's files based on the preferences specified in PkgInfo. # Copy this package's files based on the preferences specified in PkgInfo.
var filesInstalled = initSet[string]() var filesInstalled = initHashSet[string]()
iterInstallFiles(realDir, pkgInfo, options, iterInstallFiles(realDir, pkgInfo, options,
proc (file: string) = proc (file: string) =
createDir(changeRoot(realDir, pkgDestDir, file.splitFile.dir)) createDir(changeRoot(realDir, pkgDestDir, file.splitFile.dir))
@ -392,7 +424,7 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
pkgInfo.myPath) pkgInfo.myPath)
filesInstalled.incl copyFileD(pkgInfo.myPath, dest) filesInstalled.incl copyFileD(pkgInfo.myPath, dest)
var binariesInstalled = initSet[string]() var binariesInstalled = initHashSet[string]()
if pkgInfo.bin.len > 0: if pkgInfo.bin.len > 0:
# Make sure ~/.nimble/bin directory is created. # Make sure ~/.nimble/bin directory is created.
createDir(binDir) createDir(binDir)
@ -433,6 +465,12 @@ proc installFromDir(dir: string, requestedVer: VersionRange, options: Options,
display("Success:", pkgInfo.name & " installed successfully.", display("Success:", pkgInfo.name & " installed successfully.",
Success, HighPriority) Success, HighPriority)
# Run post-install hook now that package is installed. The `execHook` proc
# executes the hook defined in the CWD, so we set it to where the package
# has been installed.
cd dest.splitFile.dir:
discard execHook(options, actionInstall, false)
proc getDownloadInfo*(pv: PkgTuple, options: Options, proc getDownloadInfo*(pv: PkgTuple, options: Options,
doPrompt: bool): (DownloadMethod, string, doPrompt: bool): (DownloadMethod, string,
Table[string, string]) = Table[string, string]) =
@ -472,17 +510,7 @@ proc install(packages: seq[PkgTuple],
let (downloadDir, downloadVersion) = let (downloadDir, downloadVersion) =
downloadPkg(url, pv.ver, meth, subdir, options) downloadPkg(url, pv.ver, meth, subdir, options)
try: try:
# Run pre-install hook in download directory now that package is downloaded
cd downloadDir:
if not execHook(options, true):
raise newException(NimbleError, "Pre-hook prevented further execution.")
result = installFromDir(downloadDir, pv.ver, options, url) result = installFromDir(downloadDir, pv.ver, options, url)
# Run post-install hook in installed directory now that package is installed
# Standard hooks run in current directory so it won't detect this new package
cd result.pkg.myPath.parentDir():
discard execHook(options, false)
except BuildFailed: except BuildFailed:
# The package failed to build. # The package failed to build.
# Check if we tried building a tagged version of the package. # Check if we tried building a tagged version of the package.
@ -508,12 +536,12 @@ proc build(options: Options) =
nimScriptHint(pkgInfo) nimScriptHint(pkgInfo)
let deps = processDeps(pkginfo, options) let deps = processDeps(pkginfo, options)
let paths = deps.map(dep => dep.getRealDir()) let paths = deps.map(dep => dep.getRealDir())
var args = options.action.compileOptions var args = options.getCompilationFlags()
buildFromDir(pkgInfo, paths, args) buildFromDir(pkgInfo, paths, args, options)
proc execBackend(options: Options) = proc execBackend(pkgInfo: PackageInfo, options: Options) =
let let
bin = options.action.file bin = options.getCompilationBinary(pkgInfo).get()
binDotNim = bin.addFileExt("nim") binDotNim = bin.addFileExt("nim")
if bin == "": if bin == "":
raise newException(NimbleError, "You need to specify a file.") raise newException(NimbleError, "You need to specify a file.")
@ -526,9 +554,10 @@ proc execBackend(options: Options) =
nimScriptHint(pkgInfo) nimScriptHint(pkgInfo)
let deps = processDeps(pkginfo, options) let deps = processDeps(pkginfo, options)
let nimblePkgVersion = "-d:NimblePkgVersion=" & pkgInfo.version
var args = "" var args = ""
for dep in deps: args.add("--path:\"" & dep.getRealDir() & "\" ") for dep in deps: args.add("--path:\"" & dep.getRealDir() & "\" ")
for option in options.action.compileOptions: for option in options.getCompilationFlags():
args.add("\"" & option & "\" ") args.add("\"" & option & "\" ")
let backend = let backend =
@ -543,8 +572,8 @@ proc execBackend(options: Options) =
else: else:
display("Generating", ("documentation for $1 (from package $2) using $3 " & display("Generating", ("documentation for $1 (from package $2) using $3 " &
"backend") % [bin, pkgInfo.name, backend], priority = HighPriority) "backend") % [bin, pkgInfo.name, backend], priority = HighPriority)
doCmd("\"" & getNimBin() & "\" $# --noNimblePath $# \"$#\"" % doCmd("\"" & getNimBin() & "\" $# --noNimblePath $# $# \"$#\"" %
[backend, args, bin], showOutput = true) [backend, nimblePkgVersion, args, bin], showOutput = true)
display("Success:", "Execution finished", Success, HighPriority) display("Success:", "Execution finished", Success, HighPriority)
proc search(options: Options) = proc search(options: Options) =
@ -560,7 +589,7 @@ proc search(options: Options) =
var found = false var found = false
template onFound {.dirty.} = template onFound {.dirty.} =
echoPackage(pkg) echoPackage(pkg)
if options.queryVersions: if pkg.alias.len == 0 and options.queryVersions:
echoPackageVersions(pkg) echoPackageVersions(pkg)
echo(" ") echo(" ")
found = true found = true
@ -586,7 +615,7 @@ proc list(options: Options) =
let pkgList = getPackageList(options) let pkgList = getPackageList(options)
for pkg in pkgList: for pkg in pkgList:
echoPackage(pkg) echoPackage(pkg)
if options.queryVersions: if pkg.alias.len == 0 and options.queryVersions:
echoPackageVersions(pkg) echoPackageVersions(pkg)
echo(" ") echo(" ")
@ -619,29 +648,24 @@ proc listPaths(options: Options) =
## On success the proc returns normally. ## On success the proc returns normally.
cli.setSuppressMessages(true) cli.setSuppressMessages(true)
assert options.action.typ == actionPath assert options.action.typ == actionPath
assert(not options.action.packages.isNil)
if options.action.packages.len == 0: if options.action.packages.len == 0:
raise newException(NimbleError, "A package name needs to be specified") raise newException(NimbleError, "A package name needs to be specified")
var errors = 0 var errors = 0
let pkgs = getInstalledPkgsMin(options.getPkgsDir(), options)
for name, version in options.action.packages.items: for name, version in options.action.packages.items:
var installed: seq[VersionAndPath] = @[] var installed: seq[VersionAndPath] = @[]
# There may be several, list all available ones and sort by version. # There may be several, list all available ones and sort by version.
for kind, path in walkDir(options.getPkgsDir): for x in pkgs.items():
if kind != pcDir or not path.startsWith(options.getPkgsDir / name & "-"): let
continue pName = x.pkginfo.name
pVer = x.pkginfo.specialVersion
var nimbleFile = findNimbleFile(path, false) if name == pName:
if nimbleFile.existsFile:
var pkgInfo = getPkgInfo(path, options)
var v: VersionAndPath var v: VersionAndPath
v.version = newVersion(pkgInfo.specialVersion) v.version = newVersion(pVer)
v.path = pkgInfo.getRealDir() v.path = x.pkginfo.getRealDir()
installed.add(v) installed.add(v)
else:
display("Warning:", "No .nimble file found for " & path, Warning,
MediumPriority)
if installed.len > 0: if installed.len > 0:
sort(installed, cmp[VersionAndPath], Descending) sort(installed, cmp[VersionAndPath], Descending)
@ -705,44 +729,59 @@ proc dump(options: Options) =
echo "backend: ", p.backend.escape echo "backend: ", p.backend.escape
proc init(options: Options) = proc init(options: Options) =
var nimbleFile: string = "" # Check whether the vcs is installed.
let vcsBin = options.action.vcsOption
if vcsBin != "" and findExe(vcsBin, true) == "":
raise newException(NimbleError, "Please install git or mercurial first")
if options.forcePrompts != forcePromptYes: # Determine the package name.
display("Info:",
"In order to initialise a new Nimble package, I will need to ask you\n" &
"some questions. Default values are shown in square brackets, press\n" &
"enter to use them.", priority = HighPriority)
# Ask for package name.
let pkgName = let pkgName =
if options.action.projName != "": if options.action.projName != "":
options.action.projName options.action.projName
else: else:
os.getCurrentDir().splitPath.tail.toValidPackageName() os.getCurrentDir().splitPath.tail.toValidPackageName()
nimbleFile = pkgName.changeFileExt("nimble") # Validate the package name.
validatePackageName(nimbleFile.changeFileExt("")) validatePackageName(pkgName)
if existsFile(os.getCurrentDir() / nimbleFile): # Determine the package root.
raise newException(NimbleError, "Nimble file already exists.") let pkgRoot =
if pkgName == os.getCurrentDir().splitPath.tail:
os.getCurrentDir()
else:
os.getCurrentDir() / pkgName
let nimbleFile = (pkgRoot / pkgName).changeFileExt("nimble")
if existsFile(nimbleFile):
let errMsg = "Nimble file already exists: $#" % nimbleFile
raise newException(NimbleError, errMsg)
if options.forcePrompts != forcePromptYes:
display(
"Info:",
"Package initialisation requires info which could not be inferred.\n" &
"Default values are shown in square brackets, press\n" &
"enter to use them.",
priority = HighPriority
)
display("Using", "$# for new package name" % [pkgName.escape()], display("Using", "$# for new package name" % [pkgName.escape()],
priority = HighPriority) priority = HighPriority)
# Ask for package author # Determine author by running an external command
proc getAuthorWithCmd(cmd: string): string =
let (name, exitCode) = doCmdEx(cmd)
if exitCode == QuitSuccess and name.len > 0:
result = name.strip()
display("Using", "$# for new package author" % [result],
priority = HighPriority)
# Determine package author via git/hg or asking
proc getAuthor(): string = proc getAuthor(): string =
if findExe("git") != "": if findExe("git") != "":
let (name, exitCode) = doCmdEx("git config --global user.name") result = getAuthorWithCmd("git config --global user.name")
if exitCode == QuitSuccess and name.len > 0:
result = name.strip()
display("Using", "$# for new package author" % [result.escape()],
priority = HighPriority)
elif findExe("hg") != "": elif findExe("hg") != "":
let (name, exitCode) = doCmdEx("hg config ui.username") result = getAuthorWithCmd("hg config ui.username")
if exitCode == QuitSuccess and name.len > 0:
result = name.strip()
display("Using", "$# for new package author" % [result.escape()],
priority = HighPriority)
if result.len == 0: if result.len == 0:
result = promptCustom(options, "Your name?", "Anonymous") result = promptCustom(options, "Your name?", "Anonymous")
let pkgAuthor = getAuthor() let pkgAuthor = getAuthor()
@ -753,10 +792,16 @@ proc init(options: Options) =
priority = HighPriority) priority = HighPriority)
# Determine the type of package # Determine the type of package
let pkgType = promptList(options, "Package type?", [ let pkgType = promptList(
"lib", options,
"bin", """Package type?
]) Library - provides functionality for other packages.
Binary - produces an executable for the end-user.
Hybrid - combination of library and binary
For more information see https://goo.gl/cm2RX5""",
["library", "binary", "hybrid"]
)
# Ask for package version. # Ask for package version.
let pkgVersion = promptCustom(options, "Initial version of package?", "0.1.0") let pkgVersion = promptCustom(options, "Initial version of package?", "0.1.0")
@ -767,105 +812,72 @@ proc init(options: Options) =
"A new awesome nimble package") "A new awesome nimble package")
# Ask for license # Ask for license
let pkgLicense = options.promptList("Package License?", [ # License list is based on:
# https://www.blackducksoftware.com/top-open-source-licenses
var pkgLicense = options.promptList(
"""Package License?
This should ideally be a valid SPDX identifier. See https://spdx.org/licenses/.
""", [
"MIT", "MIT",
"BSD2", "GPL-2.0",
"GPLv3", "Apache-2.0",
"LGPLv3", "ISC",
"Apache2", "GPL-3.0",
"BSD-3-Clause",
"LGPL-2.1",
"LGPL-3.0",
"EPL-2.0",
# This is what npm calls "UNLICENSED" (which is too similar to "Unlicense")
"Proprietary",
"Other"
]) ])
if pkgLicense.toLower == "other":
pkgLicense = promptCustom(options,
"""Package license?
Please specify a valid SPDX identifier.""",
"MIT"
)
var pkgBackend = options.promptList(
"""Package Backend?
c - Compile using C backend.
cpp - Compile using C++ backend.
objc - Compile using Objective-C backend.
js - Compile using JavaScript backend.""",
["c", "cpp", "objc", "js"]
)
# Ask for Nim dependency # Ask for Nim dependency
let nimDepDef = getNimrodVersion() let nimDepDef = getNimrodVersion()
let pkgNimDep = promptCustom(options, "Lowest supported Nim version?", let pkgNimDep = promptCustom(options, "Lowest supported Nim version?",
$nimDepDef) $nimDepDef)
validateVersion(pkgNimDep) validateVersion(pkgNimDep)
let pkgTestDir = "tests" createPkgStructure(
(
pkgName,
pkgVersion,
pkgAuthor,
pkgDesc,
pkgLicense,
pkgBackend,
pkgSrcDir,
pkgNimDep,
pkgType
),
pkgRoot
)
# Create source directory # Create a git or hg repo in the new nimble project.
os.createDir(pkgSrcDir) if vcsBin != "":
let cmd = fmt"cd {pkgRoot} && {vcsBin} init"
let ret: tuple[output: string, exitCode: int] = execCmdEx(cmd)
if ret.exitCode != 0: quit ret.output
display("Success:", "Source directory created successfully", Success, var ignoreFile = if vcsBin == "git": ".gitignore" else: ".hgignore"
MediumPriority) var fd = open(joinPath(pkgRoot, ignoreFile), fmWrite)
fd.write(pkgName & "\n")
# Create initial source file fd.close()
cd pkgSrcDir:
let pkgFile = pkgName.changeFileExt("nim")
try:
if pkgType == "bin":
pkgFile.writeFile "# Hello Nim!\necho \"Hello, World!\"\n"
else:
pkgFile.writeFile """# $#
# Copyright $#
# $#
""" % [pkgName, pkgAuthor, pkgDesc]
display("Success:", "Created initial source file successfully", Success,
MediumPriority)
except:
raise newException(NimbleError, "Unable to open file " & pkgFile &
" for writing: " & osErrorMsg(osLastError()))
# Create test directory
os.createDir(pkgTestDir)
display("Success:", "Test directory created successfully", Success,
MediumPriority)
cd pkgTestDir:
try:
"test1.nims".writeFile("""switch("path", "$$projectDir/../$#")""" %
[pkgSrcDir])
display("Success:", "Test config file created successfully", Success,
MediumPriority)
except:
raise newException(NimbleError, "Unable to open file " & "test1.nims" &
" for writing: " & osErrorMsg(osLastError()))
try:
"test1.nim".writeFile("doAssert(1 + 1 == 2)\n")
display("Success:", "Test file created successfully", Success,
MediumPriority)
except:
raise newException(NimbleError, "Unable to open file " & "test1.nim" &
" for writing: " & osErrorMsg(osLastError()))
# Write the nimble file
try:
if pkgType == "lib":
nimbleFile.writeFile """# Package
version = $#
author = $#
description = $#
license = $#
srcDir = $#
# Dependencies
requires "nim >= $#"
""" % [pkgVersion.escape(), pkgAuthor.escape(), pkgDesc.escape(),
pkgLicense.escape(), pkgSrcDir.escape(), pkgNimDep]
else:
nimbleFile.writeFile """# Package
version = $#
author = $#
description = $#
license = $#
srcDir = $#
bin = @[$#]
# Dependencies
requires "nim >= $#"
""" % [pkgVersion.escape(), pkgAuthor.escape(), pkgDesc.escape(),
pkgLicense.escape(), pkgSrcDir.escape(), pkgName.escape(), pkgNimDep]
except:
raise newException(NimbleError, "Unable to open file " & "test1.nim" &
" for writing: " & osErrorMsg(osLastError()))
display("Success:", "Nimble file created successfully", Success,
MediumPriority)
display("Success:", "Package $# created successfully" % [pkgName], Success, display("Success:", "Package $# created successfully" % [pkgName], Success,
HighPriority) HighPriority)
@ -875,7 +887,8 @@ proc uninstall(options: Options) =
raise newException(NimbleError, raise newException(NimbleError,
"Please specify the package(s) to uninstall.") "Please specify the package(s) to uninstall.")
var pkgsToDelete: seq[PackageInfo] = @[] var pkgsToDelete: HashSet[PackageInfo]
pkgsToDelete.init()
# Do some verification. # Do some verification.
for pkgTup in options.action.packages: for pkgTup in options.action.packages:
display("Looking", "for $1 ($2)" % [pkgTup.name, $pkgTup.ver], display("Looking", "for $1 ($2)" % [pkgTup.name, $pkgTup.ver],
@ -886,34 +899,33 @@ proc uninstall(options: Options) =
raise newException(NimbleError, "Package not found") raise newException(NimbleError, "Package not found")
display("Checking", "reverse dependencies", priority = HighPriority) display("Checking", "reverse dependencies", priority = HighPriority)
var errors: seq[string] = @[]
for pkg in pkgList: for pkg in pkgList:
# Check whether any packages depend on the ones the user is trying to # Check whether any packages depend on the ones the user is trying to
# uninstall. # uninstall.
let revDeps = getRevDeps(options, pkg) if options.uninstallRevDeps:
var reason = "" getAllRevDeps(options, pkg, pkgsToDelete)
if revDeps.len == 1:
reason = "$1 ($2) depends on it" % [revDeps[0].name, $revDeps[0].ver]
else: else:
for i in 0 ..< revDeps.len: let
reason.add("$1 ($2)" % [revDeps[i].name, $revDeps[i].ver]) revDeps = getRevDeps(options, pkg)
if i != revDeps.len-1: var reason = ""
reason.add ", " for revDep in revDeps:
reason.add " depend on it" if reason.len != 0: reason.add ", "
reason.add("$1 ($2)" % [revDep.name, revDep.version])
if reason.len != 0:
reason &= " depend" & (if revDeps.len == 1: "s" else: "") & " on it"
if revDeps.len > 0: if len(revDeps - pkgsToDelete) > 0:
errors.add("Cannot uninstall $1 ($2) because $3" % display("Cannot", "uninstall $1 ($2) because $3" %
[pkgTup.name, pkg.specialVersion, reason]) [pkgTup.name, pkg.specialVersion, reason], Warning, HighPriority)
else: else:
pkgsToDelete.add pkg pkgsToDelete.incl pkg
if pkgsToDelete.len == 0: if pkgsToDelete.len == 0:
raise newException(NimbleError, "\n " & errors.join("\n ")) raise newException(NimbleError, "Failed uninstall - no packages to delete")
var pkgNames = "" var pkgNames = ""
for i in 0 ..< pkgsToDelete.len: for pkg in pkgsToDelete.items:
if i != 0: pkgNames.add ", " if pkgNames.len != 0: pkgNames.add ", "
let pkg = pkgsToDelete[i]
pkgNames.add("$1 ($2)" % [pkg.name, pkg.specialVersion]) pkgNames.add("$1 ($2)" % [pkg.name, pkg.specialVersion])
# Let's confirm that the user wants these packages removed. # Let's confirm that the user wants these packages removed.
@ -937,12 +949,16 @@ proc uninstall(options: Options) =
proc listTasks(options: Options) = proc listTasks(options: Options) =
let nimbleFile = findNimbleFile(getCurrentDir(), true) let nimbleFile = findNimbleFile(getCurrentDir(), true)
nimscriptsupport.listTasks(nimbleFile, options) nimscriptwrapper.listTasks(nimbleFile, options)
proc developFromDir(dir: string, options: Options) = proc developFromDir(dir: string, options: Options) =
if options.depsOnly: if options.depsOnly:
raiseNimbleError("Cannot develop dependencies only.") raiseNimbleError("Cannot develop dependencies only.")
cd dir: # Make sure `execHook` executes the correct .nimble file.
if not execHook(options, actionDevelop, true):
raise newException(NimbleError, "Pre-hook prevented further execution.")
var pkgInfo = getPkgInfo(dir, options) var pkgInfo = getPkgInfo(dir, options)
if pkgInfo.bin.len > 0: if pkgInfo.bin.len > 0:
if "nim" in pkgInfo.skipExt: if "nim" in pkgInfo.skipExt:
@ -982,8 +998,7 @@ proc developFromDir(dir: string, options: Options) =
writeNimbleLink(nimbleLinkPath, nimbleLink) writeNimbleLink(nimbleLinkPath, nimbleLink)
# Save a nimblemeta.json file. # Save a nimblemeta.json file.
saveNimbleMeta(pkgDestDir, "file://" & dir, vcsRevisionInDir(dir), saveNimbleMeta(pkgDestDir, dir, vcsRevisionInDir(dir), nimbleLinkPath)
nimbleLinkPath)
# Save the nimble data (which might now contain reverse deps added in # Save the nimble data (which might now contain reverse deps added in
# processDeps). # processDeps).
@ -992,6 +1007,10 @@ proc developFromDir(dir: string, options: Options) =
display("Success:", (pkgInfo.name & " linked successfully to '$1'.") % display("Success:", (pkgInfo.name & " linked successfully to '$1'.") %
dir, Success, HighPriority) dir, Success, HighPriority)
# Execute the post-develop hook.
cd dir:
discard execHook(options, actionDevelop, false)
proc develop(options: Options) = proc develop(options: Options) =
if options.action.packages == @[]: if options.action.packages == @[]:
developFromDir(getCurrentDir(), options) developFromDir(getCurrentDir(), options)
@ -1011,29 +1030,74 @@ proc develop(options: Options) =
let (meth, url, metadata) = getDownloadInfo(pv, options, true) let (meth, url, metadata) = getDownloadInfo(pv, options, true)
let subdir = metadata.getOrDefault("subdir") let subdir = metadata.getOrDefault("subdir")
discard downloadPkg(url, pv.ver, meth, subdir, options, downloadDir)
# Download the HEAD and make sure the full history is downloaded.
let ver =
if pv.ver.kind == verAny:
parseVersionRange("#head")
else:
pv.ver
var options = options
options.forceFullClone = true
discard downloadPkg(url, ver, meth, subdir, options, downloadDir)
developFromDir(downloadDir / subdir, options) developFromDir(downloadDir / subdir, options)
proc test(options: Options) = proc test(options: Options) =
## Executes all tests. ## Executes all tests starting with 't' in the ``tests`` directory.
var files = toSeq(walkDir(getCurrentDir() / "tests")) ## Subdirectories are not walked.
var pkgInfo = getPkgInfo(getCurrentDir(), options)
var
files = toSeq(walkDir(getCurrentDir() / "tests"))
tests, failures: int
if files.len < 1:
display("Warning:", "No tests found!", Warning, HighPriority)
return
files.sort((a, b) => cmp(a.path, b.path)) files.sort((a, b) => cmp(a.path, b.path))
for file in files: for file in files:
if file.path.endsWith(".nim") and file.kind in {pcFile, pcLinkToFile}: let (_, name, ext) = file.path.splitFile()
if ext == ".nim" and name[0] == 't' and file.kind in {pcFile, pcLinkToFile}:
var optsCopy = options.briefClone() var optsCopy = options.briefClone()
optsCopy.action.typ = actionCompile optsCopy.action = Action(typ: actionCompile)
optsCopy.action.file = file.path optsCopy.action.file = file.path
optsCopy.action.backend = "c" optsCopy.action.backend = pkgInfo.backend
optsCopy.action.compileOptions = @[] optsCopy.getCompilationFlags() = @[]
optsCopy.action.compileOptions.add("-r") optsCopy.getCompilationFlags().add("-r")
optsCopy.action.compileOptions.add("--path:.") optsCopy.getCompilationFlags().add("--path:.")
execBackend(optsCopy) let
binFileName = file.path.changeFileExt(ExeExt)
existsBefore = existsFile(binFileName)
display("Success:", "All tests passed", Success, HighPriority) if options.continueTestsOnFailure:
inc tests
try:
execBackend(pkgInfo, optsCopy)
except NimbleError:
inc failures
else:
execBackend(pkgInfo, optsCopy)
let
existsAfter = existsFile(binFileName)
canRemove = not existsBefore and existsAfter
if canRemove:
try:
removeFile(binFileName)
except OSError as exc:
display("Warning:", "Failed to delete " & binFileName & ": " &
exc.msg, Warning, MediumPriority)
if failures == 0:
display("Success:", "All tests passed", Success, HighPriority)
else:
let error = "Only " & $(tests - failures) & "/" & $tests & " tests passed"
display("Error:", error, Error, HighPriority)
proc check(options: Options) = proc check(options: Options) =
## Validates a package a in the current working directory. ## Validates a package in the current working directory.
let nimbleFile = findNimbleFile(getCurrentDir(), true) let nimbleFile = findNimbleFile(getCurrentDir(), true)
var error: ValidationError var error: ValidationError
var pkgInfo: PackageInfo var pkgInfo: PackageInfo
@ -1051,7 +1115,29 @@ proc check(options: Options) =
display("Failure:", "Validation failed", Error, HighPriority) display("Failure:", "Validation failed", Error, HighPriority)
quit(QuitFailure) quit(QuitFailure)
proc doAction(options: Options) = proc run(options: Options) =
# Verify parameters.
var pkgInfo = getPkgInfo(getCurrentDir(), options)
let binary = options.getCompilationBinary(pkgInfo).get("")
if binary.len == 0:
raiseNimbleError("Please specify a binary to run")
if binary notin pkgInfo.bin:
raiseNimbleError(
"Binary '$#' is not defined in '$#' package." % [binary, pkgInfo.name]
)
# Build the binary.
build(options)
let binaryPath = pkgInfo.getOutputDir(binary)
let cmd = quoteShellCommand(binaryPath & options.action.runFlags)
displayDebug("Executing", cmd)
cmd.execCmd.quit
proc doAction(options: var Options) =
if options.showHelp: if options.showHelp:
writeHelp() writeHelp()
if options.showVersion: if options.showVersion:
@ -1062,10 +1148,10 @@ proc doAction(options: Options) =
if not existsDir(options.getPkgsDir): if not existsDir(options.getPkgsDir):
createDir(options.getPkgsDir) createDir(options.getPkgsDir)
if not execHook(options, true): if options.action.typ in {actionTasks, actionRun, actionBuild, actionCompile}:
display("Warning", "Pre-hook prevented further execution.", Warning, # Implicitly disable package validation for these commands.
HighPriority) options.disableValidation = true
return
case options.action.typ case options.action.typ
of actionRefresh: of actionRefresh:
refresh(options) refresh(options)
@ -1091,8 +1177,11 @@ proc doAction(options: Options) =
listPaths(options) listPaths(options)
of actionBuild: of actionBuild:
build(options) build(options)
of actionRun:
run(options)
of actionCompile, actionDoc: of actionCompile, actionDoc:
execBackend(options) var pkgInfo = getPkgInfo(getCurrentDir(), options)
execBackend(pkgInfo, options)
of actionInit: of actionInit:
init(options) init(options)
of actionPublish: of actionPublish:
@ -1109,36 +1198,46 @@ proc doAction(options: Options) =
of actionNil: of actionNil:
assert false assert false
of actionCustom: of actionCustom:
if not execHook(options, actionCustom, true):
display("Warning", "Pre-hook prevented further execution.", Warning,
HighPriority)
return
let isPreDefined = options.action.command.normalize == "test" let isPreDefined = options.action.command.normalize == "test"
var execResult: ExecutionResult[void] var execResult: ExecutionResult[bool]
if execCustom(options, execResult, failFast=not isPreDefined): if execCustom(options, execResult, failFast=not isPreDefined):
if execResult.hasTaskRequestedCommand(): if execResult.hasTaskRequestedCommand():
doAction(execResult.getOptionsForCommand(options)) var options = execResult.getOptionsForCommand(options)
doAction(options)
else: else:
# If there is no task defined for the `test` task, we run the pre-defined # If there is no task defined for the `test` task, we run the pre-defined
# fallback logic. # fallback logic.
if isPreDefined: if isPreDefined:
test(options) test(options)
# Run the post hook for `test` in case it exists. # Run the post hook for `test` in case it exists.
discard execHook(options, false) discard execHook(options, actionCustom, false)
if options.action.typ != actionCustom:
discard execHook(options, false)
when isMainModule: when isMainModule:
var error = "" var error = ""
var hint = "" var hint = ""
var opt: Options
try: try:
parseCmdLine().doAction() opt = parseCmdLine()
opt.doAction()
except NimbleError: except NimbleError:
let currentExc = (ref NimbleError)(getCurrentException()) let currentExc = (ref NimbleError)(getCurrentException())
(error, hint) = getOutputInfo(currentExc) (error, hint) = getOutputInfo(currentExc)
except NimbleQuit: except NimbleQuit:
discard discard
finally: finally:
removeDir(getNimbleTempDir()) try:
let folder = getNimbleTempDir()
if opt.shouldRemoveTmp(folder):
removeDir(folder)
except OSError:
let msg = "Couldn't remove Nimble's temp dir"
display("Warning:", msg, Warning, MediumPriority)
if error.len > 0: if error.len > 0:
displayTip() displayTip()

View file

@ -3,3 +3,4 @@
--path:"$nim/" --path:"$nim/"
--path:"./vendor/nim" --path:"./vendor/nim"
-d:ssl -d:ssl
-d:nimcore # Enable 'gorge' in Nim's VM. See https://github.com/nim-lang/Nim/issues/8096

View file

@ -12,7 +12,11 @@
# - Bright for HighPriority. # - Bright for HighPriority.
# - Normal for MediumPriority. # - Normal for MediumPriority.
import logging, terminal, sets, strutils import terminal, sets, strutils
import version
when not declared(initHashSet):
import common
type type
CLI* = ref object CLI* = ref object
@ -41,10 +45,11 @@ const
styles: array[DebugPriority .. HighPriority, set[Style]] = styles: array[DebugPriority .. HighPriority, set[Style]] =
[{styleDim}, {styleDim}, {}, {styleBright}] [{styleDim}, {styleDim}, {}, {styleBright}]
proc newCLI(): CLI = proc newCLI(): CLI =
result = CLI( result = CLI(
level: HighPriority, level: HighPriority,
warnings: initSet[(string, string)](), warnings: initHashSet[(string, string)](),
suppressionCount: 0, suppressionCount: 0,
showColor: true, showColor: true,
suppressMessages: false suppressMessages: false
@ -57,8 +62,18 @@ proc calculateCategoryOffset(category: string): int =
assert category.len <= longestCategory assert category.len <= longestCategory
return longestCategory - category.len return longestCategory - category.len
proc isSuppressed(displayType: DisplayType): bool =
# Don't print any Warning, Message or Success messages when suppression of
# warnings is enabled. That is, unless the user asked for --verbose output.
if globalCLI.suppressMessages and displayType >= Warning and
globalCLI.level == HighPriority:
return true
proc displayCategory(category: string, displayType: DisplayType, proc displayCategory(category: string, displayType: DisplayType,
priority: Priority) = priority: Priority) =
if isSuppressed(displayType):
return
# Calculate how much the `category` must be offset to align along a center # Calculate how much the `category` must be offset to align along a center
# line. # line.
let offset = calculateCategoryOffset(category) let offset = calculateCategoryOffset(category)
@ -75,6 +90,9 @@ proc displayCategory(category: string, displayType: DisplayType,
proc displayLine(category, line: string, displayType: DisplayType, proc displayLine(category, line: string, displayType: DisplayType,
priority: Priority) = priority: Priority) =
if isSuppressed(displayType):
return
displayCategory(category, displayType, priority) displayCategory(category, displayType, priority)
# Display the message. # Display the message.
@ -82,12 +100,6 @@ proc displayLine(category, line: string, displayType: DisplayType,
proc display*(category, msg: string, displayType = Message, proc display*(category, msg: string, displayType = Message,
priority = MediumPriority) = priority = MediumPriority) =
# Don't print any Warning, Message or Success messages when suppression of
# warnings is enabled. That is, unless the user asked for --verbose output.
if globalCLI.suppressMessages and displayType >= Warning and
globalCLI.level == HighPriority:
return
# Multiple warnings containing the same messages should not be shown. # Multiple warnings containing the same messages should not be shown.
let warningPair = (category, msg) let warningPair = (category, msg)
if displayType == Warning: if displayType == Warning:
@ -135,7 +147,7 @@ proc prompt*(forcePrompts: ForcePrompt, question: string): bool =
display("Prompt:", question & " -> [forced no]", Warning, HighPriority) display("Prompt:", question & " -> [forced no]", Warning, HighPriority)
return false return false
of dontForcePrompt: of dontForcePrompt:
display("Prompt:", question & " [y/N]", Warning, HighPriority) displayLine("Prompt:", question & " [y/N]", Warning, HighPriority)
displayCategory("Answer:", Warning, HighPriority) displayCategory("Answer:", Warning, HighPriority)
let yn = stdin.readLine() let yn = stdin.readLine()
case yn.normalize case yn.normalize
@ -169,6 +181,76 @@ proc promptCustom*(forcePrompts: ForcePrompt, question, default: string): string
proc promptCustom*(question, default: string): string = proc promptCustom*(question, default: string): string =
return promptCustom(dontForcePrompt, question, default) return promptCustom(dontForcePrompt, question, default)
proc promptListInteractive(question: string, args: openarray[string]): string =
display("Prompt:", question, Warning, HighPriority)
display("Select", "Cycle with 'Tab', 'Enter' when done", Message,
HighPriority)
displayCategory("Choices:", Warning, HighPriority)
var
current = 0
selected = false
# Incase the cursor is at the bottom of the terminal
for arg in args:
stdout.write "\n"
# Reset the cursor to the start of the selection prompt
cursorUp(stdout, args.len)
cursorForward(stdout, longestCategory)
hideCursor(stdout)
# The selection loop
while not selected:
setForegroundColor(fgDefault)
# Loop through the options
for i, arg in args:
# Check if the option is the current
if i == current:
writeStyled("> " & arg & " <", {styleBright})
else:
writeStyled(" " & arg & " ", {styleDim})
# Move the cursor back to the start
for s in 0..<(arg.len + 4):
cursorBackward(stdout)
# Move down for the next item
cursorDown(stdout)
# Move the cursor back up to the start of the selection prompt
for i in 0..<(args.len()):
cursorUp(stdout)
resetAttributes(stdout)
# Begin key input
while true:
case getch():
of '\t':
current = (current + 1) mod args.len
break
of '\r':
selected = true
break
of '\3':
showCursor(stdout)
raise newException(NimbleError, "Keyboard interrupt")
else: discard
# Erase all lines of the selection
for i in 0..<args.len:
eraseLine(stdout)
cursorDown(stdout)
# Move the cursor back up to the initial selection line
for i in 0..<args.len():
cursorUp(stdout)
showCursor(stdout)
display("Answer:", args[current], Warning,HighPriority)
return args[current]
proc promptListFallback(question: string, args: openarray[string]): string =
display("Prompt:", question & " [" & join(args, "/") & "]", Warning,
HighPriority)
displayCategory("Answer:", Warning, HighPriority)
result = stdin.readLine()
for arg in args:
if arg.cmpIgnoreCase(result) == 0:
return arg
proc promptList*(forcePrompts: ForcePrompt, question: string, args: openarray[string]): string = proc promptList*(forcePrompts: ForcePrompt, question: string, args: openarray[string]): string =
case forcePrompts: case forcePrompts:
of forcePromptYes: of forcePromptYes:
@ -176,13 +258,10 @@ proc promptList*(forcePrompts: ForcePrompt, question: string, args: openarray[st
display("Prompt:", question & " -> [forced " & result & "]", Warning, display("Prompt:", question & " -> [forced " & result & "]", Warning,
HighPriority) HighPriority)
else: else:
display("Prompt:", question & " [" & join(args, "/") & "]", Warning, HighPriority) if isatty(stdout):
displayCategory("Answer:", Warning, HighPriority) return promptListInteractive(question, args)
result = stdin.readLine() else:
for arg in args: return promptListFallback(question, args)
if arg.cmpIgnoreCase(result) == 0:
return arg
return promptList(forcePrompts, question, args)
proc setVerbosity*(level: Priority) = proc setVerbosity*(level: Priority) =
globalCLI.level = level globalCLI.level = level

View file

@ -8,7 +8,6 @@ when not defined(nimscript):
import sets import sets
import version import version
export version.NimbleError # TODO: Surely there is a better way?
type type
BuildFailed* = object of NimbleError BuildFailed* = object of NimbleError
@ -23,7 +22,8 @@ when not defined(nimscript):
preHooks*: HashSet[string] preHooks*: HashSet[string]
name*: string name*: string
## The version specified in the .nimble file.Assuming info is non-minimal, ## The version specified in the .nimble file.Assuming info is non-minimal,
## it will always be a non-special version such as '0.1.4' ## it will always be a non-special version such as '0.1.4'.
## If in doubt, use `getConcreteVersion` instead.
version*: string version*: string
specialVersion*: string ## Either `myVersion` or a special version such as #head. specialVersion*: string ## Either `myVersion` or a special version such as #head.
author*: string author*: string
@ -63,4 +63,16 @@ when not defined(nimscript):
return (error, hint) return (error, hint)
const const
nimbleVersion* = "0.8.10" nimbleVersion* = "0.11.0"
when not declared(initHashSet):
import sets
template initHashSet*[A](initialSize = 64): HashSet[A] =
initSet[A](initialSize)
when not declared(toHashSet):
import sets
template toHashSet*[A](keys: openArray[A]): HashSet[A] =
toSet(keys)

View file

@ -1,8 +1,8 @@
# Copyright (C) Dominik Picheta. All rights reserved. # Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info. # BSD License. Look at license.txt for more info.
import parsecfg, streams, strutils, os, tables, Uri import parsecfg, streams, strutils, os, tables, uri
import tools, version, common, cli import version, cli
type type
Config* = object Config* = object
@ -68,11 +68,12 @@ proc parseConfig*(): Config =
var e = next(p) var e = next(p)
case e.kind case e.kind
of cfgEof: of cfgEof:
if currentPackageList.urls.len == 0 and currentPackageList.path == "": if currentSection.len > 0:
raise newException(NimbleError, "Package list '$1' requires either url or path" % currentPackageList.name) if currentPackageList.urls.len == 0 and currentPackageList.path == "":
if currentPackageList.urls.len > 0 and currentPackageList.path != "": raise newException(NimbleError, "Package list '$1' requires either url or path" % currentPackageList.name)
raise newException(NimbleError, "Attempted to specify `url` and `path` for the same package list '$1'" % currentPackageList.name) if currentPackageList.urls.len > 0 and currentPackageList.path != "":
addCurrentPkgList(result, currentPackageList) raise newException(NimbleError, "Attempted to specify `url` and `path` for the same package list '$1'" % currentPackageList.name)
addCurrentPkgList(result, currentPackageList)
break break
of cfgSectionStart: of cfgSectionStart:
addCurrentPkgList(result, currentPackageList) addCurrentPkgList(result, currentPackageList)

View file

@ -2,14 +2,15 @@
# BSD License. Look at license.txt for more info. # BSD License. Look at license.txt for more info.
import parseutils, os, osproc, strutils, tables, pegs, uri import parseutils, os, osproc, strutils, tables, pegs, uri
import packageinfo, packageparser, version, tools, common, options, cli import packageinfo, packageparser, version, tools, common, options, cli
from algorithm import SortOrder, sorted
from sequtils import toSeq, filterIt, map
type type
DownloadMethod* {.pure.} = enum DownloadMethod* {.pure.} = enum
git = "git", hg = "hg" git = "git", hg = "hg"
proc getSpecificDir(meth: DownloadMethod): string = proc getSpecificDir(meth: DownloadMethod): string {.used.} =
case meth case meth
of DownloadMethod.git: of DownloadMethod.git:
".git" ".git"
@ -24,11 +25,12 @@ proc doCheckout(meth: DownloadMethod, downloadDir, branch: string) =
# clone has happened. Like in the case of git on Windows where it # clone has happened. Like in the case of git on Windows where it
# messes up the damn line endings. # messes up the damn line endings.
doCmd("git checkout --force " & branch) doCmd("git checkout --force " & branch)
doCmd("git submodule update --recursive")
of DownloadMethod.hg: of DownloadMethod.hg:
cd downloadDir: cd downloadDir:
doCmd("hg checkout " & branch) doCmd("hg checkout " & branch)
proc doPull(meth: DownloadMethod, downloadDir: string) = proc doPull(meth: DownloadMethod, downloadDir: string) {.used.} =
case meth case meth
of DownloadMethod.git: of DownloadMethod.git:
doCheckout(meth, downloadDir, "") doCheckout(meth, downloadDir, "")
@ -42,17 +44,17 @@ proc doPull(meth: DownloadMethod, downloadDir: string) =
doCmd("hg pull") doCmd("hg pull")
proc doClone(meth: DownloadMethod, url, downloadDir: string, branch = "", proc doClone(meth: DownloadMethod, url, downloadDir: string, branch = "",
tip = true) = onlyTip = true) =
case meth case meth
of DownloadMethod.git: of DownloadMethod.git:
let let
depthArg = if tip: "--depth 1 " else: "" depthArg = if onlyTip: "--depth 1 " else: ""
branchArg = if branch == "": "" else: "-b " & branch & " " branchArg = if branch == "": "" else: "-b " & branch & " "
doCmd("git clone --recursive " & depthArg & branchArg & url & doCmd("git clone --recursive " & depthArg & branchArg & url &
" " & downloadDir) " " & downloadDir)
of DownloadMethod.hg: of DownloadMethod.hg:
let let
tipArg = if tip: "-r tip " else: "" tipArg = if onlyTip: "-r tip " else: ""
branchArg = if branch == "": "" else: "-b " & branch & " " branchArg = if branch == "": "" else: "-b " & branch & " "
doCmd("hg clone " & tipArg & branchArg & url & " " & downloadDir) doCmd("hg clone " & tipArg & branchArg & url & " " & downloadDir)
@ -102,14 +104,21 @@ proc getTagsListRemote*(url: string, meth: DownloadMethod): seq[string] =
# http://stackoverflow.com/questions/2039150/show-tags-for-remote-hg-repository # http://stackoverflow.com/questions/2039150/show-tags-for-remote-hg-repository
raise newException(ValueError, "Hg doesn't support remote tag querying.") raise newException(ValueError, "Hg doesn't support remote tag querying.")
proc getVersionList*(tags: seq[string]): Table[Version, string] = proc getVersionList*(tags: seq[string]): OrderedTable[Version, string] =
# Returns: TTable of version -> git tag name ## Return an ordered table of Version -> git tag label. Ordering is
result = initTable[Version, string]() ## in descending order with the most recent version first.
for tag in tags: let taggedVers: seq[tuple[ver: Version, tag: string]] =
if tag != "": tags
let i = skipUntil(tag, Digits) # skip any chars before the version .filterIt(it != "")
# TODO: Better checking, tags can have any names. Add warnings and such. .map(proc(s: string): tuple[ver: Version, tag: string] =
result[newVersion(tag[i .. tag.len-1])] = tag # skip any chars before the version
let i = skipUntil(s, Digits)
# TODO: Better checking, tags can have any
# names. Add warnings and such.
result = (newVersion(s[i .. s.len-1]), s))
.sorted(proc(a, b: (Version, string)): int = cmp(a[0], b[0]),
SortOrder.Descending)
result = toOrderedTable[Version, string](taggedVers)
proc getDownloadMethod*(meth: string): DownloadMethod = proc getDownloadMethod*(meth: string): DownloadMethod =
case meth case meth
@ -166,18 +175,16 @@ proc doDownload(url: string, downloadDir: string, verRange: VersionRange,
meth meth
if $latest.ver != "": if $latest.ver != "":
result = latest.ver result = latest.ver
else:
# Result should already be set to #head here.
assert(not result.isNil)
removeDir(downloadDir) removeDir(downloadDir)
if verRange.kind == verSpecial: if verRange.kind == verSpecial:
# We want a specific commit/branch/tag here. # We want a specific commit/branch/tag here.
if verRange.spe == getHeadName(downMethod): if verRange.spe == getHeadName(downMethod):
doClone(downMethod, url, downloadDir) # Grab HEAD. # Grab HEAD.
doClone(downMethod, url, downloadDir, onlyTip = not options.forceFullClone)
else: else:
# Grab the full repo. # Grab the full repo.
doClone(downMethod, url, downloadDir, tip = false) doClone(downMethod, url, downloadDir, onlyTip = false)
# Then perform a checkout operation to get the specified branch/commit. # Then perform a checkout operation to get the specified branch/commit.
# `spe` starts with '#', trim it. # `spe` starts with '#', trim it.
doAssert(($verRange.spe)[0] == '#') doAssert(($verRange.spe)[0] == '#')
@ -194,12 +201,13 @@ proc doDownload(url: string, downloadDir: string, verRange: VersionRange,
getLatestByTag: getLatestByTag:
display("Cloning", "latest tagged version: " & latest.tag, display("Cloning", "latest tagged version: " & latest.tag,
priority = MediumPriority) priority = MediumPriority)
doClone(downMethod, url, downloadDir, latest.tag) doClone(downMethod, url, downloadDir, latest.tag,
onlyTip = not options.forceFullClone)
else: else:
# If no commits have been tagged on the repo we just clone HEAD. # If no commits have been tagged on the repo we just clone HEAD.
doClone(downMethod, url, downloadDir) # Grab HEAD. doClone(downMethod, url, downloadDir) # Grab HEAD.
of DownloadMethod.hg: of DownloadMethod.hg:
doClone(downMethod, url, downloadDir) doClone(downMethod, url, downloadDir, onlyTip = not options.forceFullClone)
result = getHeadName(downMethod) result = getHeadName(downMethod)
let versions = getTagsList(downloadDir, downMethod).getVersionList() let versions = getTagsList(downloadDir, downMethod).getVersionList()
@ -268,14 +276,8 @@ proc echoPackageVersions*(pkg: Package) =
try: try:
let versions = getTagsListRemote(pkg.url, downMethod).getVersionList() let versions = getTagsListRemote(pkg.url, downMethod).getVersionList()
if versions.len > 0: if versions.len > 0:
var vstr = "" let sortedVersions = toSeq(values(versions))
var i = 0 echo(" versions: " & join(sortedVersions, ", "))
for v in values(versions):
if i != 0:
vstr.add(", ")
vstr.add(v)
i.inc
echo(" versions: " & vstr)
else: else:
echo(" versions: (No versions tagged in the remote repository)") echo(" versions: (No versions tagged in the remote repository)")
except OSError: except OSError:
@ -283,3 +285,32 @@ proc echoPackageVersions*(pkg: Package) =
of DownloadMethod.hg: of DownloadMethod.hg:
echo(" versions: (Remote tag retrieval not supported by " & echo(" versions: (Remote tag retrieval not supported by " &
pkg.downloadMethod & ")") pkg.downloadMethod & ")")
when isMainModule:
# Test version sorting
block:
let data = @["v9.0.0-taeyeon", "v9.0.1-jessica", "v9.2.0-sunny",
"v9.4.0-tiffany", "v9.4.2-hyoyeon"]
let expected = toOrderedTable[Version, string]({
newVersion("9.4.2-hyoyeon"): "v9.4.2-hyoyeon",
newVersion("9.4.0-tiffany"): "v9.4.0-tiffany",
newVersion("9.2.0-sunny"): "v9.2.0-sunny",
newVersion("9.0.1-jessica"): "v9.0.1-jessica",
newVersion("9.0.0-taeyeon"): "v9.0.0-taeyeon"
})
doAssert expected == getVersionList(data)
block:
let data2 = @["v0.1.0", "v0.1.1", "v0.2.0",
"0.4.0", "v0.4.2"]
let expected2 = toOrderedTable[Version, string]({
newVersion("0.4.2"): "v0.4.2",
newVersion("0.4.0"): "0.4.0",
newVersion("0.2.0"): "v0.2.0",
newVersion("0.1.1"): "v0.1.1",
newVersion("0.1.0"): "v0.1.0",
})
doAssert expected2 == getVersionList(data2)
echo("Everything works!")

184
src/nimblepkg/init.nim Normal file
View file

@ -0,0 +1,184 @@
import os, strutils
import ./cli, ./tools
type
PkgInitInfo* = tuple
pkgName: string
pkgVersion: string
pkgAuthor: string
pkgDesc: string
pkgLicense: string
pkgBackend: string
pkgSrcDir: string
pkgNimDep: string
pkgType: string
proc writeExampleIfNonExistent(file: string, content: string) =
if not existsFile(file):
writeFile(file, content)
else:
display("Info:", "File " & file & " already exists, did not write " &
"example code", priority = HighPriority)
proc createPkgStructure*(info: PkgInitInfo, pkgRoot: string) =
# Create source directory
createDirD(pkgRoot / info.pkgSrcDir)
# Initialise the source code directories and create some example code.
var nimbleFileOptions = ""
case info.pkgType
of "binary":
let mainFile = pkgRoot / info.pkgSrcDir / info.pkgName.changeFileExt("nim")
writeExampleIfNonExistent(mainFile,
"""
# This is just an example to get you started. A typical binary package
# uses this file as the main entry point of the application.
when isMainModule:
echo("Hello, World!")
"""
)
nimbleFileOptions.add("bin = @[\"$1\"]\n" % info.pkgName)
of "library":
let mainFile = pkgRoot / info.pkgSrcDir / info.pkgName.changeFileExt("nim")
writeExampleIfNonExistent(mainFile,
"""
# This is just an example to get you started. A typical library package
# exports the main API in this file. Note that you cannot rename this file
# but you can remove it if you wish.
proc add*(x, y: int): int =
## Adds two files together.
return x + y
"""
)
createDirD(pkgRoot / info.pkgSrcDir / info.pkgName)
let submodule = pkgRoot / info.pkgSrcDir / info.pkgName /
"submodule".addFileExt("nim")
writeExampleIfNonExistent(submodule,
"""
# This is just an example to get you started. Users of your library will
# import this file by writing ``import $1/submodule``. Feel free to rename or
# remove this file altogether. You may create additional modules alongside
# this file as required.
type
Submodule* = object
name*: string
proc initSubmodule*(): Submodule =
## Initialises a new ``Submodule`` object.
Submodule(name: "Anonymous")
""" % info.pkgName
)
of "hybrid":
let mainFile = pkgRoot / info.pkgSrcDir / info.pkgName.changeFileExt("nim")
writeExampleIfNonExistent(mainFile,
"""
# This is just an example to get you started. A typical hybrid package
# uses this file as the main entry point of the application.
import $1pkg/submodule
when isMainModule:
echo(getWelcomeMessage())
""" % info.pkgName
)
let pkgSubDir = pkgRoot / info.pkgSrcDir / info.pkgName & "pkg"
createDirD(pkgSubDir)
let submodule = pkgSubDir / "submodule".addFileExt("nim")
writeExampleIfNonExistent(submodule,
"""
# This is just an example to get you started. Users of your hybrid library will
# import this file by writing ``import $1pkg/submodule``. Feel free to rename or
# remove this file altogether. You may create additional modules alongside
# this file as required.
proc getWelcomeMessage*(): string = "Hello, World!"
""" % info.pkgName
)
nimbleFileOptions.add("installExt = @[\"nim\"]\n")
nimbleFileOptions.add("bin = @[\"$1\"]\n" % info.pkgName)
else:
assert false, "Invalid package type specified."
let pkgTestDir = "tests"
# Create test directory
case info.pkgType
of "binary":
discard
of "hybrid", "library":
let pkgTestPath = pkgRoot / pkgTestDir
createDirD(pkgTestPath)
writeFile(pkgTestPath / "config".addFileExt("nims"),
"switch(\"path\", \"$$projectDir/../$#\")" % info.pkgSrcDir
)
if info.pkgType == "library":
writeExampleIfNonExistent(pkgTestPath / "test1".addFileExt("nim"),
"""
# This is just an example to get you started. You may wish to put all of your
# tests into a single file, or separate them into multiple `test1`, `test2`
# etc. files (better names are recommended, just make sure the name starts with
# the letter 't').
#
# To run these tests, simply execute `nimble test`.
import unittest
import $1
test "can add":
check add(5, 5) == 10
""" % info.pkgName
)
else:
writeExampleIfNonExistent(pkgTestPath / "test1".addFileExt("nim"),
"""
# This is just an example to get you started. You may wish to put all of your
# tests into a single file, or separate them into multiple `test1`, `test2`
# etc. files (better names are recommended, just make sure the name starts with
# the letter 't').
#
# To run these tests, simply execute `nimble test`.
import unittest
import $1pkg/submodule
test "correct welcome":
check getWelcomeMessage() == "Hello, World!"
""" % info.pkgName
)
else:
assert false, "Invalid package type specified."
# Write the nimble file
let nimbleFile = pkgRoot / info.pkgName.changeFileExt("nimble")
# Only write backend if it isn't "c"
var pkgBackend = ""
if (info.pkgBackend != "c"):
pkgBackend = "backend = " & info.pkgbackend.escape()
writeFile(nimbleFile, """# Package
version = $#
author = "$#"
description = "$#"
license = $#
srcDir = $#
$#
$#
# Dependencies
requires "nim >= $#"
""" % [
info.pkgVersion.escape(), info.pkgAuthor.replace("\"", "\\\""), info.pkgDesc.replace("\"", "\\\""),
info.pkgLicense.escape(), info.pkgSrcDir.escape(), nimbleFileOptions,
pkgBackend, info.pkgNimDep
]
)
display("Info:", "Nimble file created successfully", priority=MediumPriority)

View file

@ -3,6 +3,12 @@
## This module is implicitly imported in NimScript .nimble files. ## This module is implicitly imported in NimScript .nimble files.
import system except getCommand, setCommand, switch, `--`
import strformat, strutils, tables
when not defined(nimscript):
import os
var var
packageName* = "" ## Set this to the package name. It packageName* = "" ## Set this to the package name. It
## is usually not required to do that, nims' filename is ## is usually not required to do that, nims' filename is
@ -11,7 +17,7 @@ var
author*: string ## The package's author. author*: string ## The package's author.
description*: string ## The package's description. description*: string ## The package's description.
license*: string ## The package's license. license*: string ## The package's license.
srcdir*: string ## The package's source directory. srcDir*: string ## The package's source directory.
binDir*: string ## The package's binary directory. binDir*: string ## The package's binary directory.
backend*: string ## The package's backend. backend*: string ## The package's backend.
@ -22,26 +28,169 @@ var
foreignDeps*: seq[string] = @[] ## The foreign dependencies. Only foreignDeps*: seq[string] = @[] ## The foreign dependencies. Only
## exported for 'distros.nim'. ## exported for 'distros.nim'.
beforeHooks: seq[string] = @[]
afterHooks: seq[string] = @[]
commandLineParams: seq[string] = @[]
flags: TableRef[string, seq[string]]
command = "e"
project = ""
success = false
retVal = true
projectFile = ""
outFile = ""
proc requires*(deps: varargs[string]) = proc requires*(deps: varargs[string]) =
## Call this to set the list of requirements of your Nimble ## Call this to set the list of requirements of your Nimble
## package. ## package.
for d in deps: requiresData.add(d) for d in deps: requiresData.add(d)
proc getParams() =
# Called by nimscriptwrapper.nim:execNimscript()
# nim e --flags /full/path/to/file.nims /full/path/to/file.out action
for i in 2 .. paramCount():
let
param = paramStr(i)
if param[0] != '-':
if projectFile.len == 0:
projectFile = param
elif outFile.len == 0:
outFile = param
else:
commandLineParams.add param.normalize
proc getCommand*(): string =
return command
proc setCommand*(cmd: string, prj = "") =
command = cmd
if prj.len != 0:
project = prj
proc switch*(key: string, value="") =
if flags.isNil:
flags = newTable[string, seq[string]]()
if flags.hasKey(key):
flags[key].add(value)
else:
flags[key] = @[value]
template `--`*(key, val: untyped) =
switch(astToStr(key), strip astToStr(val))
template `--`*(key: untyped) =
switch(astToStr(key), "")
template printIfLen(varName) =
if varName.len != 0:
result &= astToStr(varName) & ": \"\"\"" & varName & "\"\"\"\n"
template printSeqIfLen(varName) =
if varName.len != 0:
result &= astToStr(varName) & ": \"" & varName.join(", ") & "\"\n"
proc printPkgInfo(): string =
if backend.len == 0:
backend = "c"
result = "[Package]\n"
if packageName.len != 0:
result &= "name: \"" & packageName & "\"\n"
printIfLen version
printIfLen author
printIfLen description
printIfLen license
printIfLen srcDir
printIfLen binDir
printIfLen backend
printSeqIfLen skipDirs
printSeqIfLen skipFiles
printSeqIfLen skipExt
printSeqIfLen installDirs
printSeqIfLen installFiles
printSeqIfLen installExt
printSeqIfLen bin
printSeqIfLen beforeHooks
printSeqIfLen afterHooks
if requiresData.len != 0:
result &= "\n[Deps]\n"
result &= &"requires: \"{requiresData.join(\", \")}\"\n"
proc onExit*() =
if "printPkgInfo".normalize in commandLineParams:
if outFile.len != 0:
writeFile(outFile, printPkgInfo())
else:
var
output = ""
output &= "\"success\": " & $success & ", "
output &= "\"command\": \"" & command & "\", "
if project.len != 0:
output &= "\"project\": \"" & project & "\", "
if not flags.isNil and flags.len != 0:
output &= "\"flags\": {"
for key, val in flags.pairs:
output &= "\"" & key & "\": ["
for v in val:
let v = if v.len > 0 and v[0] == '"': strutils.unescape(v)
else: v
output &= v.escape & ", "
output = output[0 .. ^3] & "], "
output = output[0 .. ^3] & "}, "
output &= "\"retVal\": " & $retVal
if outFile.len != 0:
writeFile(outFile, "{" & output & "}")
# TODO: New release of Nim will move this `task` template under a
# `when not defined(nimble)`. This will allow us to override it in the future.
template task*(name: untyped; description: string; body: untyped): untyped =
## Defines a task. Hidden tasks are supported via an empty description.
## Example:
##
## .. code-block:: nim
## task build, "default build is via the C backend":
## setCommand "c"
proc `name Task`*() = body
if commandLineParams.len == 0 or "help" in commandLineParams:
success = true
echo(astToStr(name), " ", description)
elif astToStr(name).normalize in commandLineParams:
success = true
`name Task`()
template before*(action: untyped, body: untyped): untyped = template before*(action: untyped, body: untyped): untyped =
## Defines a block of code which is evaluated before ``action`` is executed. ## Defines a block of code which is evaluated before ``action`` is executed.
proc `action Before`*(): bool = proc `action Before`*(): bool =
result = true result = true
body body
beforeHooks.add astToStr(action)
if (astToStr(action) & "Before").normalize in commandLineParams:
success = true
retVal = `action Before`()
template after*(action: untyped, body: untyped): untyped = template after*(action: untyped, body: untyped): untyped =
## Defines a block of code which is evaluated after ``action`` is executed. ## Defines a block of code which is evaluated after ``action`` is executed.
proc `action After`*(): bool = proc `action After`*(): bool =
result = true result = true
body body
template builtin = discard afterHooks.add astToStr(action)
if (astToStr(action) & "After").normalize in commandLineParams:
success = true
retVal = `action After`()
proc getPkgDir*(): string = proc getPkgDir*(): string =
## Returns the package directory containing the .nimble file currently ## Returns the package directory containing the .nimble file currently
## being evaluated. ## being evaluated.
builtin result = projectFile.rsplit(seps={'/', '\\', ':'}, maxsplit=1)[0]
getParams()

View file

@ -1,16 +1,17 @@
# Copyright (C) Dominik Picheta. All rights reserved. # Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info. # BSD License. Look at license.txt for more info.
import os, tables, strutils, sets import os, strutils, sets
import packageparser, common, packageinfo, options, nimscriptsupport, cli import packageparser, common, packageinfo, options, nimscriptwrapper, cli,
version
proc execHook*(options: Options, before: bool): bool = proc execHook*(options: Options, hookAction: ActionType, before: bool): bool =
## Returns whether to continue. ## Returns whether to continue.
result = true result = true
# For certain commands hooks should not be evaluated. # For certain commands hooks should not be evaluated.
if options.action.typ in noHookActions: if hookAction in noHookActions:
return return
var nimbleFile = "" var nimbleFile = ""
@ -20,8 +21,8 @@ proc execHook*(options: Options, before: bool): bool =
# PackageInfos are cached so we can read them as many times as we want. # PackageInfos are cached so we can read them as many times as we want.
let pkgInfo = getPkgInfoFromFile(nimbleFile, options) let pkgInfo = getPkgInfoFromFile(nimbleFile, options)
let actionName = let actionName =
if options.action.typ == actionCustom: options.action.command if hookAction == actionCustom: options.action.command
else: ($options.action.typ)[6 .. ^1] else: ($hookAction)[6 .. ^1]
let hookExists = let hookExists =
if before: actionName.normalize in pkgInfo.preHooks if before: actionName.normalize in pkgInfo.preHooks
else: actionName.normalize in pkgInfo.postHooks else: actionName.normalize in pkgInfo.postHooks
@ -31,7 +32,7 @@ proc execHook*(options: Options, before: bool): bool =
result = res.retVal result = res.retVal
proc execCustom*(options: Options, proc execCustom*(options: Options,
execResult: var ExecutionResult[void], execResult: var ExecutionResult[bool],
failFast = true): bool = failFast = true): bool =
## Executes the custom command using the nimscript backend. ## Executes the custom command using the nimscript backend.
## ##
@ -57,7 +58,7 @@ proc execCustom*(options: Options,
HighPriority) HighPriority)
return return
if not execHook(options, false): if not execHook(options, actionCustom, false):
return return
return true return true

View file

@ -1,531 +0,0 @@
# Copyright (C) Andreas Rumpf. All rights reserved.
# BSD License. Look at license.txt for more info.
## Implements the new configuration system for Nimble. Uses Nim as a
## scripting language.
import
compiler/ast, compiler/modules, compiler/passes, compiler/passaux,
compiler/condsyms, compiler/sem, compiler/semdata,
compiler/llstream, compiler/vm, compiler/vmdef, compiler/commands,
compiler/msgs, compiler/magicsys, compiler/idents,
compiler/nimconf
from compiler/scriptconfig import setupVM
from compiler/astalgo import strTableGet
import compiler/options as compiler_options
import common, version, options, packageinfo, cli
import os, strutils, strtabs, tables, times, osproc, sets, pegs
when not declared(resetAllModulesHard):
import compiler/modulegraphs
type
Flags = TableRef[string, seq[string]]
ExecutionResult*[T] = object
success*: bool
command*: string
arguments*: seq[string]
flags*: Flags
retVal*: T
const
internalCmd = "NimbleInternal"
nimscriptApi = staticRead("nimscriptapi.nim")
proc raiseVariableError(ident, typ: string) {.noinline.} =
raise newException(NimbleError,
"NimScript's variable '" & ident & "' needs a value of type '" & typ & "'.")
proc isStrLit(n: PNode): bool = n.kind in {nkStrLit..nkTripleStrLit}
proc getGlobal(ident: PSym): string =
let n = vm.globalCtx.getGlobalValue(ident)
if n.isStrLit:
result = if n.strVal.isNil: "" else: n.strVal
else:
raiseVariableError(ident.name.s, "string")
proc getGlobalAsSeq(ident: PSym): seq[string] =
let n = vm.globalCtx.getGlobalValue(ident)
result = @[]
if n.kind == nkBracket:
for x in n:
if x.isStrLit:
result.add x.strVal
else:
raiseVariableError(ident.name.s, "seq[string]")
else:
raiseVariableError(ident.name.s, "seq[string]")
proc extractRequires(ident: PSym, result: var seq[PkgTuple]) =
let n = vm.globalCtx.getGlobalValue(ident)
if n.kind == nkBracket:
for x in n:
if x.kind == nkPar and x.len == 2 and x[0].isStrLit and x[1].isStrLit:
result.add(parseRequires(x[0].strVal & x[1].strVal))
elif x.isStrLit:
result.add(parseRequires(x.strVal))
else:
raiseVariableError("requiresData", "seq[(string, VersionReq)]")
else:
raiseVariableError("requiresData", "seq[(string, VersionReq)]")
when declared(newIdentCache):
var identCache = newIdentCache()
proc setupVM(module: PSym; scriptName: string, flags: Flags): PEvalContext =
## This procedure is exported in the compiler sources, but its implementation
## is too Nim-specific to be used by Nimble.
## Specifically, the implementation of ``switch`` is problematic. Sooo
## I simply copied it here and edited it :)
when declared(newIdentCache):
result = newCtx(module, identCache)
else:
result = newCtx(module)
result.mode = emRepl
registerAdditionalOps(result)
# captured vars:
var errorMsg: string
var vthisDir = scriptName.splitFile.dir
proc listDirs(a: VmArgs, filter: set[PathComponent]) =
let dir = getString(a, 0)
var res: seq[string] = @[]
for kind, path in walkDir(dir):
if kind in filter: res.add path
setResult(a, res)
template cbconf(name, body) {.dirty.} =
result.registerCallback "stdlib.system." & astToStr(name),
proc (a: VmArgs) =
body
template cbos(name, body) {.dirty.} =
result.registerCallback "stdlib.system." & astToStr(name),
proc (a: VmArgs) =
try:
body
except OSError:
errorMsg = getCurrentExceptionMsg()
# Idea: Treat link to file as a file, but ignore link to directory to prevent
# endless recursions out of the box.
cbos listFiles:
listDirs(a, {pcFile, pcLinkToFile})
cbos listDirs:
listDirs(a, {pcDir})
cbos removeDir:
os.removeDir getString(a, 0)
cbos removeFile:
os.removeFile getString(a, 0)
cbos createDir:
os.createDir getString(a, 0)
cbos getOsError:
setResult(a, errorMsg)
cbos setCurrentDir:
os.setCurrentDir getString(a, 0)
cbos getCurrentDir:
setResult(a, os.getCurrentDir())
cbos moveFile:
os.moveFile(getString(a, 0), getString(a, 1))
cbos copyFile:
os.copyFile(getString(a, 0), getString(a, 1))
cbos getLastModificationTime:
setResult(a, toSeconds(getLastModificationTime(getString(a, 0))))
cbos rawExec:
setResult(a, osproc.execCmd getString(a, 0))
cbconf getEnv:
setResult(a, os.getEnv(a.getString 0))
cbconf existsEnv:
setResult(a, os.existsEnv(a.getString 0))
cbconf dirExists:
setResult(a, os.dirExists(a.getString 0))
cbconf fileExists:
setResult(a, os.fileExists(a.getString 0))
cbconf thisDir:
setResult(a, vthisDir)
cbconf put:
compiler_options.setConfigVar(getString(a, 0), getString(a, 1))
cbconf get:
setResult(a, compiler_options.getConfigVar(a.getString 0))
cbconf exists:
setResult(a, compiler_options.existsConfigVar(a.getString 0))
cbconf nimcacheDir:
setResult(a, compiler_options.getNimcacheDir())
cbconf paramStr:
setResult(a, os.paramStr(int a.getInt 0))
cbconf paramCount:
setResult(a, os.paramCount())
cbconf cmpIgnoreStyle:
setResult(a, strutils.cmpIgnoreStyle(a.getString 0, a.getString 1))
cbconf cmpIgnoreCase:
setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1))
cbconf setCommand:
compiler_options.command = a.getString 0
let arg = a.getString 1
if arg.len > 0:
gProjectName = arg
try:
gProjectFull = canonicalizePath(gProjectPath / gProjectName)
except OSError:
gProjectFull = gProjectName
cbconf getCommand:
setResult(a, compiler_options.command)
cbconf switch:
if not flags.isNil:
let
key = a.getString 0
value = a.getString 1
if flags.hasKey(key):
flags[key].add(value)
else:
flags[key] = @[value]
proc isValidLibPath(lib: string): bool =
return fileExists(lib / "system.nim")
proc getNimPrefixDir(options: Options): string =
let env = getEnv("NIM_LIB_PREFIX")
if env != "":
let msg = "Using env var NIM_LIB_PREFIX: " & env
display("Warning:", msg, Warning, HighPriority)
return env
if options.config.nimLibPrefix != "":
result = options.config.nimLibPrefix
let msg = "Using Nim stdlib prefix from Nimble config file: " & result
display("Warning:", msg, Warning, HighPriority)
return
result = splitPath(findExe("nim")).head.parentDir
# The above heuristic doesn't work for 'choosenim' proxies. Thankfully in
# that case the `nimble` binary is beside the `nim` binary so things should
# just work.
if not dirExists(result / "lib"):
# By specifying an empty string we instruct the Nim compiler to use
# getAppDir().head as the prefix dir. See compiler/options module for
# the code responsible for this.
result = ""
proc getLibVersion(lib: string): Version =
## This is quite a hacky procedure, but there is no other way to extract
## this out of the ``system`` module. We could evaluate it, but that would
## cause an error if the stdlib is out of date. The purpose of this
## proc is to give a nice error message to the user instead of a confusing
## Nim compile error.
let systemPath = lib / "system.nim"
if not fileExists(systemPath):
raiseNimbleError("system module not found in stdlib path: " & lib)
let systemFile = readFile(systemPath)
let majorPeg = peg"'NimMajor' @ '=' \s* {\d*}"
let minorPeg = peg"'NimMinor' @ '=' \s* {\d*}"
let patchPeg = peg"'NimPatch' @ '=' \s* {\d*}"
var majorMatches: array[1, string]
let major = find(systemFile, majorPeg, majorMatches)
var minorMatches: array[1, string]
let minor = find(systemFile, minorPeg, minorMatches)
var patchMatches: array[1, string]
let patch = find(systemFile, patchPeg, patchMatches)
if major != -1 and minor != -1 and patch != -1:
return newVersion(majorMatches[0] & "." & minorMatches[0] & "." & patchMatches[0])
else:
return system.NimVersion.newVersion()
when declared(ModuleGraph):
var graph: ModuleGraph
proc execScript(scriptName: string, flags: Flags, options: Options): PSym =
## Executes the specified script. Returns the script's module symbol.
##
## No clean up is performed and must be done manually!
when declared(resetAllModulesHard):
# for compatibility with older Nim versions:
if "nimblepkg/nimscriptapi" notin compiler_options.implicitIncludes:
compiler_options.implicitIncludes.add("nimblepkg/nimscriptapi")
else:
if "nimblepkg/nimscriptapi" notin compiler_options.implicitImports:
compiler_options.implicitImports.add("nimblepkg/nimscriptapi")
# Ensure the compiler can find its standard library #220.
compiler_options.gPrefixDir = getNimPrefixDir(options)
display("Setting", "Nim stdlib prefix to " & compiler_options.gPrefixDir,
priority=LowPriority)
# Verify that lib path points to existing stdlib.
compiler_options.setDefaultLibpath()
display("Setting", "Nim stdlib path to " & compiler_options.libpath,
priority=LowPriority)
if not isValidLibPath(compiler_options.libpath):
let msg = "Nimble cannot find Nim's standard library.\nLast try in:\n - $1" %
compiler_options.libpath
let hint = "Nimble does its best to find Nim's standard library, " &
"sometimes this fails. You can set the environment variable " &
"NIM_LIB_PREFIX to where Nim's `lib` directory is located as " &
"a workaround. " &
"See https://github.com/nim-lang/nimble#troubleshooting for " &
"more info."
raiseNimbleError(msg, hint)
# Verify that the stdlib that was found isn't older than the stdlib that Nimble
# was compiled with.
let libVersion = getLibVersion(compiler_options.libpath)
if NimVersion.newVersion() > libVersion:
let msg = ("Nimble cannot use an older stdlib than the one it was compiled " &
"with.\n Stdlib in '$#' has version: $#.\n Nimble needs at least: $#.") %
[compiler_options.libpath, $libVersion, NimVersion]
let hint = "You may be running a newer version of Nimble than you intended " &
"to. Run an older version of Nimble that is compatible with " &
"the stdlib that Nimble is attempting to use or set the environment variable " &
"NIM_LIB_PREFIX to where a different stdlib's `lib` directory is located as " &
"a workaround." &
"See https://github.com/nim-lang/nimble#troubleshooting for " &
"more info."
raiseNimbleError(msg, hint)
let pkgName = scriptName.splitFile.name
# Ensure that "nimblepkg/nimscriptapi" is in the PATH.
block:
let t = options.getNimbleDir / "nimblecache"
let tmpNimscriptApiPath = t / "nimblepkg" / "nimscriptapi.nim"
createDir(tmpNimscriptApiPath.splitFile.dir)
writeFile(tmpNimscriptApiPath, nimscriptApi)
searchPaths.add(t)
initDefines()
loadConfigs(DefaultConfig)
passes.gIncludeFile = includeModule
passes.gImportModule = importModule
defineSymbol("nimscript")
defineSymbol("nimconfig")
defineSymbol("nimble")
registerPass(semPass)
registerPass(evalPass)
searchPaths.add(compiler_options.libpath)
when declared(resetAllModulesHard):
result = makeModule(scriptName)
else:
graph = newModuleGraph()
result = graph.makeModule(scriptName)
incl(result.flags, sfMainModule)
vm.globalCtx = setupVM(result, scriptName, flags)
# Setup builtins defined in nimscriptapi.nim
template cbApi(name, body) {.dirty.} =
vm.globalCtx.registerCallback pkgName & "." & astToStr(name),
proc (a: VmArgs) =
body
cbApi getPkgDir:
setResult(a, scriptName.splitFile.dir)
when declared(newIdentCache):
graph.compileSystemModule(identCache)
graph.processModule(result, llStreamOpen(scriptName, fmRead), nil, identCache)
else:
compileSystemModule()
processModule(result, llStreamOpen(scriptName, fmRead), nil)
proc cleanup() =
# ensure everything can be called again:
compiler_options.gProjectName = ""
compiler_options.command = ""
when declared(resetAllModulesHard):
resetAllModulesHard()
else:
resetSystemArtifacts()
clearPasses()
msgs.gErrorMax = 1
msgs.writeLnHook = nil
vm.globalCtx = nil
initDefines()
proc readPackageInfoFromNims*(scriptName: string, options: Options,
result: var PackageInfo) =
## Executes the `scriptName` nimscript file. Reads the package information
## that it populates.
# Setup custom error handling.
msgs.gErrorMax = high(int)
var previousMsg = ""
msgs.writeLnHook =
proc (output: string) =
# The error counter is incremented after the writeLnHook is invoked.
if msgs.gErrorCounter > 0:
raise newException(NimbleError, previousMsg)
elif previousMsg.len > 0:
display("Info", previousMsg, priority = MediumPriority)
if output.normalize.startsWith("error"):
raise newException(NimbleError, output)
previousMsg = output
compiler_options.command = internalCmd
# Execute the nimscript file.
let thisModule = execScript(scriptName, nil, options)
when declared(resetAllModulesHard):
let apiModule = thisModule
else:
var apiModule: PSym
for i in 0..<graph.modules.len:
if graph.modules[i] != nil and
graph.modules[i].name.s == "nimscriptapi":
apiModule = graph.modules[i]
break
doAssert apiModule != nil
# Check whether an error has occurred.
if msgs.gErrorCounter > 0:
raise newException(NimbleError, previousMsg)
# Extract all the necessary fields populated by the nimscript file.
proc getSym(apiModule: PSym, ident: string): PSym =
result = apiModule.tab.strTableGet(getIdent(ident))
if result.isNil:
raise newException(NimbleError, "Ident not found: " & ident)
template trivialField(field) =
result.field = getGlobal(getSym(apiModule, astToStr field))
template trivialFieldSeq(field) =
result.field.add getGlobalAsSeq(getSym(apiModule, astToStr field))
# keep reasonable default:
let name = getGlobal(apiModule.tab.strTableGet(getIdent"packageName"))
if name.len > 0: result.name = name
trivialField version
trivialField author
trivialField description
trivialField license
trivialField srcdir
trivialField bindir
trivialFieldSeq skipDirs
trivialFieldSeq skipFiles
trivialFieldSeq skipExt
trivialFieldSeq installDirs
trivialFieldSeq installFiles
trivialFieldSeq installExt
trivialFieldSeq foreignDeps
extractRequires(getSym(apiModule, "requiresData"), result.requires)
let binSeq = getGlobalAsSeq(getSym(apiModule, "bin"))
for i in binSeq:
result.bin.add(i.addFileExt(ExeExt))
let backend = getGlobal(getSym(apiModule, "backend"))
if backend.len == 0:
result.backend = "c"
elif cmpIgnoreStyle(backend, "javascript") == 0:
result.backend = "js"
else:
result.backend = backend.toLowerAscii()
# Grab all the global procs
for i in thisModule.tab.data:
if not i.isNil():
let name = i.name.s.normalize()
if name.endsWith("before"):
result.preHooks.incl(name[0 .. ^7])
if name.endsWith("after"):
result.postHooks.incl(name[0 .. ^6])
cleanup()
proc execTask*(scriptName, taskName: string,
options: Options): ExecutionResult[void] =
## Executes the specified task in the specified script.
##
## `scriptName` should be a filename pointing to the nimscript file.
result.success = true
result.flags = newTable[string, seq[string]]()
compiler_options.command = internalCmd
display("Executing", "task $# in $#" % [taskName, scriptName],
priority = HighPriority)
let thisModule = execScript(scriptName, result.flags, options)
let prc = thisModule.tab.strTableGet(getIdent(taskName & "Task"))
if prc.isNil:
# Procedure not defined in the NimScript module.
result.success = false
return
discard vm.globalCtx.execProc(prc, [])
# Read the command, arguments and flags set by the executed task.
result.command = compiler_options.command
result.arguments = @[]
for arg in compiler_options.gProjectName.split():
result.arguments.add(arg)
cleanup()
proc execHook*(scriptName, actionName: string, before: bool,
options: Options): ExecutionResult[bool] =
## Executes the specified action's hook. Depending on ``before``, either
## the "before" or the "after" hook.
##
## `scriptName` should be a filename pointing to the nimscript file.
result.success = true
result.flags = newTable[string, seq[string]]()
compiler_options.command = internalCmd
let hookName =
if before: actionName.toLowerAscii & "Before"
else: actionName.toLowerAscii & "After"
display("Attempting", "to execute hook $# in $#" % [hookName, scriptName],
priority = MediumPriority)
let thisModule = execScript(scriptName, result.flags, options)
# Explicitly execute the task procedure, instead of relying on hack.
let prc = thisModule.tab.strTableGet(getIdent(hookName))
if prc.isNil:
# Procedure not defined in the NimScript module.
result.success = false
cleanup()
return
let returnVal = vm.globalCtx.execProc(prc, [])
case returnVal.kind
of nkCharLit..nkUInt64Lit:
result.retVal = returnVal.intVal == 1
else: assert false
# Read the command, arguments and flags set by the executed task.
result.command = compiler_options.command
result.arguments = @[]
for arg in compiler_options.gProjectName.split():
result.arguments.add(arg)
cleanup()
proc getNimScriptCommand(): string =
compiler_options.command
proc setNimScriptCommand(command: string) =
compiler_options.command = command
proc hasTaskRequestedCommand*(execResult: ExecutionResult): bool =
## Determines whether the last executed task used ``setCommand``
return execResult.command != internalCmd
proc listTasks*(scriptName: string, options: Options) =
setNimScriptCommand("help")
discard execScript(scriptName, nil, options)
# TODO (#402): Make the 'task' template generate explicit data structure
# containing all the task names + descriptions.
cleanup()

View file

@ -0,0 +1,216 @@
# Copyright (C) Andreas Rumpf. All rights reserved.
# BSD License. Look at license.txt for more info.
## Implements the new configuration system for Nimble. Uses Nim as a
## scripting language.
import hashes, json, os, strutils, tables, times, osproc, strtabs
import version, options, cli, tools
type
Flags = TableRef[string, seq[string]]
ExecutionResult*[T] = object
success*: bool
command*: string
arguments*: seq[string]
flags*: Flags
retVal*: T
stdout*: string
const
internalCmd = "e"
nimscriptApi = staticRead("nimscriptapi.nim")
printPkgInfo = "printPkgInfo"
proc isCustomTask(actionName: string, options: Options): bool =
options.action.typ == actionCustom and actionName != printPkgInfo
proc needsLiveOutput(actionName: string, options: Options, isHook: bool): bool =
let isCustomTask = isCustomTask(actionName, options)
return isCustomTask or isHook or actionName == ""
proc writeExecutionOutput(data: string) =
# TODO: in the future we will likely want this to be live, users will
# undoubtedly be doing loops and other crazy things in their top-level
# Nimble files.
display("Info", data)
proc execNimscript(
nimsFile, projectDir, actionName: string, options: Options, isHook: bool
): tuple[output: string, exitCode: int, stdout: string] =
let
nimsFileCopied = projectDir / nimsFile.splitFile().name & "_" & getProcessId() & ".nims"
outFile = getNimbleTempDir() & ".out"
let
isScriptResultCopied =
nimsFileCopied.fileExists() and
nimsFileCopied.getLastModificationTime() >= nimsFile.getLastModificationTime()
if not isScriptResultCopied:
nimsFile.copyFile(nimsFileCopied)
defer:
# Only if copied in this invocation, allows recursive calls of nimble
if not isScriptResultCopied and options.shouldRemoveTmp(nimsFileCopied):
nimsFileCopied.removeFile()
var cmd = (
"nim e $# -p:$# $# $# $#" % [
"--hints:off --verbosity:0",
(getTempDir() / "nimblecache").quoteShell,
nimsFileCopied.quoteShell,
outFile.quoteShell,
actionName
]
).strip()
let isCustomTask = isCustomTask(actionName, options)
if isCustomTask:
for i in options.action.arguments:
cmd &= " " & i.quoteShell()
for key, val in options.action.flags.pairs():
cmd &= " $#$#" % [if key.len == 1: "-" else: "--", key]
if val.len != 0:
cmd &= ":" & val.quoteShell()
displayDebug("Executing " & cmd)
if needsLiveOutput(actionName, options, isHook):
result.exitCode = execCmd(cmd)
else:
# We want to capture any possible errors when parsing a .nimble
# file's metadata. See #710.
(result.stdout, result.exitCode) = execCmdEx(cmd)
if outFile.fileExists():
result.output = outFile.readFile()
if options.shouldRemoveTmp(outFile):
discard outFile.tryRemoveFile()
proc getNimsFile(scriptName: string, options: Options): string =
let
cacheDir = getTempDir() / "nimblecache"
shash = $scriptName.parentDir().hash().abs()
prjCacheDir = cacheDir / scriptName.splitFile().name & "_" & shash
nimscriptApiFile = cacheDir / "nimscriptapi.nim"
result = prjCacheDir / scriptName.extractFilename().changeFileExt ".nims"
let
iniFile = result.changeFileExt(".ini")
isNimscriptApiCached =
nimscriptApiFile.fileExists() and nimscriptApiFile.getLastModificationTime() >
getAppFilename().getLastModificationTime()
isScriptResultCached =
isNimscriptApiCached and result.fileExists() and result.getLastModificationTime() >
scriptName.getLastModificationTime()
if not isNimscriptApiCached:
createDir(cacheDir)
writeFile(nimscriptApiFile, nimscriptApi)
if not isScriptResultCached:
createDir(result.parentDir())
writeFile(result, """
import system except getCommand, setCommand, switch, `--`,
packageName, version, author, description, license, srcDir, binDir, backend,
skipDirs, skipFiles, skipExt, installDirs, installFiles, installExt, bin, foreignDeps,
requires, task, packageName
""" &
"import nimscriptapi, strutils\n" & scriptName.readFile() & "\nonExit()\n")
discard tryRemoveFile(iniFile)
proc getIniFile*(scriptName: string, options: Options): string =
let
nimsFile = getNimsFile(scriptName, options)
result = nimsFile.changeFileExt(".ini")
let
isIniResultCached =
result.fileExists() and result.getLastModificationTime() >
scriptName.getLastModificationTime()
if not isIniResultCached:
let (output, exitCode, stdout) = execNimscript(
nimsFile, scriptName.parentDir(), printPkgInfo, options, isHook=false
)
if exitCode == 0 and output.len != 0:
result.writeFile(output)
stdout.writeExecutionOutput()
else:
raise newException(NimbleError, stdout & "\nprintPkgInfo() failed")
proc execScript(
scriptName, actionName: string, options: Options, isHook: bool
): ExecutionResult[bool] =
let nimsFile = getNimsFile(scriptName, options)
let (output, exitCode, stdout) =
execNimscript(
nimsFile, scriptName.parentDir(), actionName, options, isHook
)
if exitCode != 0:
let errMsg =
if stdout.len != 0:
stdout
else:
"Exception raised during nimble script execution"
raise newException(NimbleError, errMsg)
let
j =
if output.len != 0:
parseJson(output)
else:
parseJson("{}")
result.flags = newTable[string, seq[string]]()
result.success = j{"success"}.getBool()
result.command = j{"command"}.getStr()
if "project" in j:
result.arguments.add j["project"].getStr()
if "flags" in j:
for flag, vals in j["flags"].pairs:
result.flags[flag] = @[]
for val in vals.items():
result.flags[flag].add val.getStr()
result.retVal = j{"retVal"}.getBool()
stdout.writeExecutionOutput()
proc execTask*(scriptName, taskName: string,
options: Options): ExecutionResult[bool] =
## Executes the specified task in the specified script.
##
## `scriptName` should be a filename pointing to the nimscript file.
display("Executing", "task $# in $#" % [taskName, scriptName],
priority = HighPriority)
result = execScript(scriptName, taskName, options, isHook=false)
proc execHook*(scriptName, actionName: string, before: bool,
options: Options): ExecutionResult[bool] =
## Executes the specified action's hook. Depending on ``before``, either
## the "before" or the "after" hook.
##
## `scriptName` should be a filename pointing to the nimscript file.
let hookName =
if before: actionName.toLowerAscii & "Before"
else: actionName.toLowerAscii & "After"
display("Attempting", "to execute hook $# in $#" % [hookName, scriptName],
priority = MediumPriority)
result = execScript(scriptName, hookName, options, isHook=true)
proc hasTaskRequestedCommand*(execResult: ExecutionResult): bool =
## Determines whether the last executed task used ``setCommand``
return execResult.command != internalCmd
proc listTasks*(scriptName: string, options: Options) =
discard execScript(scriptName, "", options, isHook=false)

View file

@ -1,15 +1,18 @@
# Copyright (C) Dominik Picheta. All rights reserved. # Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info. # BSD License. Look at license.txt for more info.
import json, strutils, os, parseopt, strtabs, uri, tables import json, strutils, os, parseopt, strtabs, uri, tables, terminal
import sequtils, sugar
import std/options as std_opt
from httpclient import Proxy, newProxy from httpclient import Proxy, newProxy
import config, version, tools, common, cli import config, version, common, cli
type type
Options* = object Options* = object
forcePrompts*: ForcePrompt forcePrompts*: ForcePrompt
depsOnly*: bool depsOnly*: bool
uninstallRevDeps*: bool
queryVersions*: bool queryVersions*: bool
queryInstalled*: bool queryInstalled*: bool
nimbleDir*: string nimbleDir*: string
@ -22,12 +25,18 @@ type
showVersion*: bool showVersion*: bool
noColor*: bool noColor*: bool
disableValidation*: bool disableValidation*: bool
continueTestsOnFailure*: bool
## Whether packages' repos should always be downloaded with their history.
forceFullClone*: bool
# Temporary storage of flags that have not been captured by any specific Action.
unknownFlags*: seq[(CmdLineKind, string, string)]
ActionType* = enum ActionType* = enum
actionNil, actionRefresh, actionInit, actionDump, actionPublish, actionNil, actionRefresh, actionInit, actionDump, actionPublish,
actionInstall, actionSearch, actionInstall, actionSearch,
actionList, actionBuild, actionPath, actionUninstall, actionCompile, actionList, actionBuild, actionPath, actionUninstall, actionCompile,
actionDoc, actionCustom, actionTasks, actionDevelop, actionCheck actionDoc, actionCustom, actionTasks, actionDevelop, actionCheck,
actionRun
Action* = object Action* = object
case typ*: ActionType case typ*: ActionType
@ -37,14 +46,20 @@ type
of actionInstall, actionPath, actionUninstall, actionDevelop: of actionInstall, actionPath, actionUninstall, actionDevelop:
packages*: seq[PkgTuple] # Optional only for actionInstall packages*: seq[PkgTuple] # Optional only for actionInstall
# and actionDevelop. # and actionDevelop.
passNimFlags*: seq[string]
of actionSearch: of actionSearch:
search*: seq[string] # Search string. search*: seq[string] # Search string.
of actionInit, actionDump: of actionInit, actionDump:
projName*: string projName*: string
vcsOption*: string
of actionCompile, actionDoc, actionBuild: of actionCompile, actionDoc, actionBuild:
file*: string file*: string
backend*: string backend*: string
compileOptions*: seq[string] compileOptions: seq[string]
of actionRun:
runFile: Option[string]
compileFlags: seq[string]
runFlags*: seq[string]
of actionCustom: of actionCustom:
command*: string command*: string
arguments*: seq[string] arguments*: seq[string]
@ -57,20 +72,32 @@ Usage: nimble COMMAND [opts]
Commands: Commands:
install [pkgname, ...] Installs a list of packages. install [pkgname, ...] Installs a list of packages.
[-d, --depsOnly] Install only dependencies. [-d, --depsOnly] Install only dependencies.
[-p, --passNim] Forward specified flag to compiler.
develop [pkgname, ...] Clones a list of packages for development. develop [pkgname, ...] Clones a list of packages for development.
Symlinks the cloned packages or any package Symlinks the cloned packages or any package
in the current working directory. in the current working directory.
check Verifies the validity of a package in the check Verifies the validity of a package in the
current working directory. current working directory.
init [pkgname] Initializes a new Nimble project. init [pkgname] Initializes a new Nimble project in the
current directory or if a name is provided a
new directory of the same name.
--git
--hg Create a git or hg repo in the new nimble project.
publish Publishes a package on nim-lang/packages. publish Publishes a package on nim-lang/packages.
The current working directory needs to be the The current working directory needs to be the
toplevel directory of the Nimble package. toplevel directory of the Nimble package.
uninstall [pkgname, ...] Uninstalls a list of packages. uninstall [pkgname, ...] Uninstalls a list of packages.
build Builds a package. [-i, --inclDeps] Uninstall package and dependent package(s).
build [opts, ...] [bin] Builds a package.
run [opts, ...] [bin] Builds and runs a package.
Binary needs to be specified after any
compilation options if there are several
binaries defined, any flags after the binary
or -- arg are passed to the binary when it is run.
c, cc, js [opts, ...] f.nim Builds a file inside a package. Passes options c, cc, js [opts, ...] f.nim Builds a file inside a package. Passes options
to the Nim compiler. to the Nim compiler.
test Compiles and executes tests test Compiles and executes tests
[-c, --continue] Don't stop execution on a failed test.
doc, doc2 [opts, ...] f.nim Builds documentation for a file inside a doc, doc2 [opts, ...] f.nim Builds documentation for a file inside a
package. Passes options to the Nim compiler. package. Passes options to the Nim compiler.
refresh [url] Refreshes the package list. A package list URL refresh [url] Refreshes the package list. A package list URL
@ -133,6 +160,8 @@ proc parseActionType*(action: string): ActionType =
result = actionPath result = actionPath
of "build": of "build":
result = actionBuild result = actionBuild
of "run":
result = actionRun
of "c", "compile", "js", "cpp", "cc": of "c", "compile", "js", "cpp", "cc":
result = actionCompile result = actionCompile
of "doc", "doc2": of "doc", "doc2":
@ -167,6 +196,7 @@ proc initAction*(options: var Options, key: string) =
case options.action.typ case options.action.typ
of actionInstall, actionPath, actionDevelop, actionUninstall: of actionInstall, actionPath, actionDevelop, actionUninstall:
options.action.packages = @[] options.action.packages = @[]
options.action.passNimFlags = @[]
of actionCompile, actionDoc, actionBuild: of actionCompile, actionDoc, actionBuild:
options.action.compileOptions = @[] options.action.compileOptions = @[]
options.action.file = "" options.action.file = ""
@ -174,8 +204,11 @@ proc initAction*(options: var Options, key: string) =
else: options.action.backend = keyNorm else: options.action.backend = keyNorm
of actionInit: of actionInit:
options.action.projName = "" options.action.projName = ""
options.action.vcsOption = ""
of actionDump: of actionDump:
options.action.projName = "" options.action.projName = ""
options.action.vcsOption = ""
options.forcePrompts = forcePromptYes
of actionRefresh: of actionRefresh:
options.action.optionalURL = "" options.action.optionalURL = ""
of actionSearch: of actionSearch:
@ -184,7 +217,7 @@ proc initAction*(options: var Options, key: string) =
options.action.command = key options.action.command = key
options.action.arguments = @[] options.action.arguments = @[]
options.action.flags = newStringTable() options.action.flags = newStringTable()
of actionPublish, actionList, actionTasks, actionCheck, of actionPublish, actionList, actionTasks, actionCheck, actionRun,
actionNil: discard actionNil: discard
proc prompt*(options: Options, question: string): bool = proc prompt*(options: Options, question: string): bool =
@ -208,18 +241,6 @@ proc promptList*(options: Options, question: string, args: openarray[string]): s
## options is selected. ## options is selected.
return promptList(options.forcePrompts, question, args) return promptList(options.forcePrompts, question, args)
proc renameBabelToNimble(options: Options) {.deprecated.} =
let babelDir = getHomeDir() / ".babel"
let nimbleDir = getHomeDir() / ".nimble"
if dirExists(babelDir):
if options.prompt("Found deprecated babel package directory, would you " &
"like to rename it to nimble?"):
copyDir(babelDir, nimbleDir)
copyFile(babelDir / "babeldata.json", nimbleDir / "nimbledata.json")
removeDir(babelDir)
removeFile(nimbleDir / "babeldata.json")
proc getNimbleDir*(options: Options): string = proc getNimbleDir*(options: Options): string =
result = options.config.nimbleDir result = options.config.nimbleDir
if options.nimbleDir.len != 0: if options.nimbleDir.len != 0:
@ -242,9 +263,15 @@ proc getBinDir*(options: Options): string =
options.getNimbleDir() / "bin" options.getNimbleDir() / "bin"
proc parseCommand*(key: string, result: var Options) = proc parseCommand*(key: string, result: var Options) =
result.action.typ = parseActionType(key) result.action = Action(typ: parseActionType(key))
initAction(result, key) initAction(result, key)
proc setRunOptions(result: var Options, key, val: string, isArg: bool) =
if result.action.runFile.isNone() and (isArg or val == "--"):
result.action.runFile = some(key)
else:
result.action.runFlags.add(val)
proc parseArgument*(key: string, result: var Options) = proc parseArgument*(key: string, result: var Options) =
case result.action.typ case result.action.typ
of actionNil: of actionNil:
@ -265,23 +292,40 @@ proc parseArgument*(key: string, result: var Options) =
result.action.search.add(key) result.action.search.add(key)
of actionInit, actionDump: of actionInit, actionDump:
if result.action.projName != "": if result.action.projName != "":
raise newException(NimbleError, raise newException(
"Can only initialize one package at a time.") NimbleError, "Can only perform this action on one package at a time."
)
result.action.projName = key result.action.projName = key
of actionCompile, actionDoc: of actionCompile, actionDoc:
result.action.file = key result.action.file = key
of actionList, actionBuild, actionPublish: of actionList, actionPublish:
result.showHelp = true result.showHelp = true
of actionBuild:
result.action.file = key
of actionRun:
result.setRunOptions(key, key, true)
of actionCustom: of actionCustom:
result.action.arguments.add(key) result.action.arguments.add(key)
else: else:
discard discard
proc getFlagString(kind: CmdLineKind, flag, val: string): string =
let prefix =
case kind
of cmdShortOption: "-"
of cmdLongOption: "--"
else: ""
if val == "":
return prefix & flag
else:
return prefix & flag & ":" & val
proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) = proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) =
var wasFlagHandled = true
let f = flag.normalize() let f = flag.normalize()
# Global flags. # Global flags.
var isGlobalFlag = true
case f case f
of "help", "h": result.showHelp = true of "help", "h": result.showHelp = true
of "version", "v": result.showVersion = true of "version", "v": result.showVersion = true
@ -292,42 +336,64 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) =
of "debug": result.verbosity = DebugPriority of "debug": result.verbosity = DebugPriority
of "nocolor": result.noColor = true of "nocolor": result.noColor = true
of "disablevalidation": result.disableValidation = true of "disablevalidation": result.disableValidation = true
else: isGlobalFlag = false
var wasFlagHandled = true
# Action-specific flags. # Action-specific flags.
else: case result.action.typ
case result.action.typ of actionSearch, actionList:
of actionSearch, actionList: case f
case f of "installed", "i":
of "installed", "i": result.queryInstalled = true
result.queryInstalled = true of "ver":
of "ver": result.queryVersions = true
result.queryVersions = true
else:
wasFlagHandled = false
of actionInstall:
case f
of "depsonly", "d":
result.depsOnly = true
else:
wasFlagHandled = false
of actionCompile, actionDoc, actionBuild:
let prefix = if kind == cmdShortOption: "-" else: "--"
if val == "":
result.action.compileOptions.add(prefix & flag)
else:
result.action.compileOptions.add(prefix & flag & ":" & val)
of actionCustom:
result.action.flags[flag] = val
else: else:
wasFlagHandled = false wasFlagHandled = false
of actionInstall:
case f
of "depsonly", "d":
result.depsOnly = true
of "passnim", "p":
result.action.passNimFlags.add(val)
else:
wasFlagHandled = false
of actionInit:
case f
of "git", "hg":
result.action.vcsOption = f
else:
wasFlagHandled = false
of actionUninstall:
case f
of "incldeps", "i":
result.uninstallRevDeps = true
else:
wasFlagHandled = false
of actionCompile, actionDoc, actionBuild:
if not isGlobalFlag:
result.action.compileOptions.add(getFlagString(kind, flag, val))
of actionRun:
result.showHelp = false
result.setRunOptions(flag, getFlagString(kind, flag, val), false)
of actionCustom:
if result.action.command.normalize == "test":
if f == "continue" or f == "c":
result.continueTestsOnFailure = true
result.action.flags[flag] = val
else:
wasFlagHandled = false
if not wasFlagHandled: if not wasFlagHandled and not isGlobalFlag:
raise newException(NimbleError, "Unknown option: --" & flag) result.unknownFlags.add((kind, flag, val))
proc initOptions*(): Options = proc initOptions*(): Options =
result.action.typ = actionNil # Exported for choosenim
result.pkgInfoCache = newTable[string, PackageInfo]() Options(
result.nimbleDir = "" action: Action(typ: actionNil),
result.verbosity = HighPriority pkgInfoCache: newTable[string, PackageInfo](),
verbosity: HighPriority,
noColor: not isatty(stdout)
)
proc parseMisc(options: var Options) = proc parseMisc(options: var Options) =
# Load nimbledata.json # Load nimbledata.json
@ -342,6 +408,29 @@ proc parseMisc(options: var Options) =
else: else:
options.nimbleData = %{"reverseDeps": newJObject()} options.nimbleData = %{"reverseDeps": newJObject()}
proc handleUnknownFlags(options: var Options) =
if options.action.typ == actionRun:
# ActionRun uses flags that come before the command as compilation flags
# and flags that come after as run flags.
options.action.compileFlags =
map(options.unknownFlags, x => getFlagString(x[0], x[1], x[2]))
options.unknownFlags = @[]
else:
# For everything else, handle the flags that came before the command
# normally.
let unknownFlags = options.unknownFlags
options.unknownFlags = @[]
for flag in unknownFlags:
parseFlag(flag[1], flag[2], options, flag[0])
# Any unhandled flags?
if options.unknownFlags.len > 0:
let flag = options.unknownFlags[0]
raise newException(
NimbleError,
"Unknown option: " & getFlagString(flag[0], flag[1], flag[2])
)
proc parseCmdLine*(): Options = proc parseCmdLine*(): Options =
result = initOptions() result = initOptions()
@ -355,9 +444,11 @@ proc parseCmdLine*(): Options =
else: else:
parseArgument(key, result) parseArgument(key, result)
of cmdLongOption, cmdShortOption: of cmdLongOption, cmdShortOption:
parseFlag(key, val, result, kind) parseFlag(key, val, result, kind)
of cmdEnd: assert(false) # cannot happen of cmdEnd: assert(false) # cannot happen
handleUnknownFlags(result)
# Set verbosity level. # Set verbosity level.
setVerbosity(result.verbosity) setVerbosity(result.verbosity)
@ -373,6 +464,11 @@ proc parseCmdLine*(): Options =
if result.action.typ == actionNil and not result.showVersion: if result.action.typ == actionNil and not result.showVersion:
result.showHelp = true result.showHelp = true
if result.action.typ != actionNil and result.showVersion:
# We've got another command that should be handled. For example:
# nimble run foobar -v
result.showVersion = false
proc getProxy*(options: Options): Proxy = proc getProxy*(options: Options): Proxy =
## Returns ``nil`` if no proxy is specified. ## Returns ``nil`` if no proxy is specified.
var url = "" var url = ""
@ -384,6 +480,10 @@ proc getProxy*(options: Options): Proxy =
url = getEnv("http_proxy") url = getEnv("http_proxy")
elif existsEnv("https_proxy"): elif existsEnv("https_proxy"):
url = getEnv("https_proxy") url = getEnv("https_proxy")
elif existsEnv("HTTP_PROXY"):
url = getEnv("HTTP_PROXY")
elif existsEnv("HTTPS_PROXY"):
url = getEnv("HTTPS_PROXY")
except ValueError: except ValueError:
display("Warning:", "Unable to parse proxy from environment: " & display("Warning:", "Unable to parse proxy from environment: " &
getCurrentExceptionMsg(), Warning, HighPriority) getCurrentExceptionMsg(), Warning, HighPriority)
@ -404,5 +504,48 @@ proc briefClone*(options: Options): Options =
var newOptions = initOptions() var newOptions = initOptions()
newOptions.config = options.config newOptions.config = options.config
newOptions.nimbleData = options.nimbleData newOptions.nimbleData = options.nimbleData
newOptions.nimbleDir = options.nimbleDir
newOptions.forcePrompts = options.forcePrompts
newOptions.pkgInfoCache = options.pkgInfoCache newOptions.pkgInfoCache = options.pkgInfoCache
return newOptions return newOptions
proc shouldRemoveTmp*(options: Options, file: string): bool =
result = true
if options.verbosity <= DebugPriority:
let msg = "Not removing temporary path because of debug verbosity: " & file
display("Warning:", msg, Warning, MediumPriority)
return false
proc getCompilationFlags*(options: var Options): var seq[string] =
case options.action.typ
of actionBuild, actionDoc, actionCompile:
return options.action.compileOptions
of actionRun:
return options.action.compileFlags
else:
assert false
proc getCompilationFlags*(options: Options): seq[string] =
var opt = options
return opt.getCompilationFlags()
proc getCompilationBinary*(options: Options, pkgInfo: PackageInfo): Option[string] =
case options.action.typ
of actionBuild, actionDoc, actionCompile:
let file = options.action.file.changeFileExt("")
if file.len > 0:
return some(file)
of actionRun:
let optRunFile = options.action.runFile
let runFile =
if optRunFile.get("").len > 0:
optRunFile.get()
elif pkgInfo.bin.len == 1:
pkgInfo.bin[0]
else:
""
if runFile.len > 0:
return some(runFile.changeFileExt(ExeExt))
else:
discard

View file

@ -3,8 +3,7 @@
# Stdlib imports # Stdlib imports
import system except TResult import system except TResult
import parsecfg, json, streams, strutils, parseutils, os, sets, tables import hashes, json, strutils, os, sets, tables, httpclient
import httpclient
# Local imports # Local imports
import version, tools, common, options, cli, config import version, tools, common, options, cli, config
@ -107,8 +106,8 @@ proc requiredField(obj: JsonNode, name: string): string =
## Queries ``obj`` for the required ``name`` string. ## Queries ``obj`` for the required ``name`` string.
## ##
## Aborts execution if the field does not exist or is of invalid json type. ## Aborts execution if the field does not exist or is of invalid json type.
result = optionalField(obj, name, nil) result = optionalField(obj, name)
if result == nil: if result.len == 0:
raise newException(NimbleError, raise newException(NimbleError,
"Package in packages.json file does not contain a " & name & " field.") "Package in packages.json file does not contain a " & name & " field.")
@ -278,7 +277,7 @@ proc getPackage*(pkg: string, options: Options, resPkg: var Package): bool =
proc getPackageList*(options: Options): seq[Package] = proc getPackageList*(options: Options): seq[Package] =
## Returns the list of packages found in the downloaded packages.json files. ## Returns the list of packages found in the downloaded packages.json files.
result = @[] result = @[]
var namesAdded = initSet[string]() var namesAdded = initHashSet[string]()
for name, list in options.config.packageLists: for name, list in options.config.packageLists:
let packages = readPackageList(name, options) let packages = readPackageList(name, options)
for p in packages: for p in packages:
@ -313,14 +312,17 @@ proc findNimbleFile*(dir: string; error: bool): string =
# Return the path of the real .nimble file. # Return the path of the real .nimble file.
result = readNimbleLink(result).nimbleFilePath result = readNimbleLink(result).nimbleFilePath
if not fileExists(result): if not fileExists(result):
raiseNimbleError("The .nimble-link file is pointing to a missing" & let msg = "The .nimble-link file is pointing to a missing file: " & result
" file: " & result) let hintMsg =
"Remove '$1' or restore the file it points to." % dir
display("Warning:", msg, Warning, HighPriority)
display("Hint:", hintMsg, Warning, HighPriority)
proc getInstalledPkgsMin*(libsDir: string, options: Options): proc getInstalledPkgsMin*(libsDir: string, options: Options):
seq[tuple[pkginfo: PackageInfo, meta: MetaData]] = seq[tuple[pkginfo: PackageInfo, meta: MetaData]] =
## Gets a list of installed packages. The resulting package info is ## Gets a list of installed packages. The resulting package info is
## minimal. This has the advantage that it does not depend on the ## minimal. This has the advantage that it does not depend on the
## ``packageparser`` module, and so can be used by ``nimscriptsupport``. ## ``packageparser`` module, and so can be used by ``nimscriptwrapper``.
## ##
## ``libsDir`` is in most cases: ~/.nimble/pkgs/ (options.getPkgsDir) ## ``libsDir`` is in most cases: ~/.nimble/pkgs/ (options.getPkgsDir)
result = @[] result = @[]
@ -336,8 +338,17 @@ proc getInstalledPkgsMin*(libsDir: string, options: Options):
pkg.specialVersion = version pkg.specialVersion = version
pkg.isMinimal = true pkg.isMinimal = true
pkg.isInstalled = true pkg.isInstalled = true
pkg.isLinked = let nimbleFileDir = nimbleFile.splitFile().dir
cmpPaths(nimbleFile.splitFile().dir, path) != 0 pkg.isLinked = cmpPaths(nimbleFileDir, path) != 0
# Read the package's 'srcDir' (this is stored in the .nimble-link so
# we can easily grab it)
if pkg.isLinked:
let nimbleLinkPath = path / name.addFileExt("nimble-link")
let realSrcPath = readNimbleLink(nimbleLinkPath).packageDir
assert realSrcPath.startsWith(nimbleFileDir)
pkg.srcDir = realSrcPath.replace(nimbleFileDir)
pkg.srcDir.removePrefix(DirSep)
result.add((pkg, meta)) result.add((pkg, meta))
proc withinRange*(pkgInfo: PackageInfo, verRange: VersionRange): bool = proc withinRange*(pkgInfo: PackageInfo, verRange: VersionRange): bool =
@ -371,8 +382,7 @@ proc findPkg*(pkglist: seq[tuple[pkgInfo: PackageInfo, meta: MetaData]],
if cmpIgnoreStyle(pkg.pkginfo.name, dep.name) != 0 and if cmpIgnoreStyle(pkg.pkginfo.name, dep.name) != 0 and
cmpIgnoreStyle(pkg.meta.url, dep.name) != 0: continue cmpIgnoreStyle(pkg.meta.url, dep.name) != 0: continue
if withinRange(pkg.pkgInfo, dep.ver): if withinRange(pkg.pkgInfo, dep.ver):
let isNewer = (not r.version.isNil) and let isNewer = newVersion(r.version) < newVersion(pkg.pkginfo.version)
newVersion(r.version) < newVersion(pkg.pkginfo.version)
if not result or isNewer: if not result or isNewer:
r = pkg.pkginfo r = pkg.pkginfo
result = true result = true
@ -526,6 +536,15 @@ proc getPkgDest*(pkgInfo: PackageInfo, options: Options): string =
let pkgDestDir = options.getPkgsDir() / (pkgInfo.name & versionStr) let pkgDestDir = options.getPkgsDir() / (pkgInfo.name & versionStr)
return pkgDestDir return pkgDestDir
proc `==`*(pkg1: PackageInfo, pkg2: PackageInfo): bool =
if pkg1.name == pkg2.name and pkg1.myPath == pkg2.myPath:
return true
proc hash*(x: PackageInfo): Hash =
var h: Hash = 0
h = h !& hash(x.myPath)
result = !$h
when isMainModule: when isMainModule:
doAssert getNameVersion("/home/user/.nimble/libs/packagea-0.1") == doAssert getNameVersion("/home/user/.nimble/libs/packagea-0.1") ==
("packagea", "0.1") ("packagea", "0.1")

View file

@ -3,7 +3,13 @@
import os, strutils, sets, json import os, strutils, sets, json
# Local imports # Local imports
import cli, common, options, tools import cli, options, tools
when defined(windows):
import version
when not declared(initHashSet) or not declared(toHashSet):
import common
when defined(windows): when defined(windows):
# This is just for Win XP support. # This is just for Win XP support.
@ -81,7 +87,7 @@ proc saveNimbleMeta*(pkgDestDir, url, vcsRevision: string,
## ##
## isLink - Determines whether the installed package is a .nimble-link. ## isLink - Determines whether the installed package is a .nimble-link.
var nimblemeta = %{"url": %url} var nimblemeta = %{"url": %url}
if not vcsRevision.isNil: if vcsRevision.len > 0:
nimblemeta["vcsRevision"] = %vcsRevision nimblemeta["vcsRevision"] = %vcsRevision
let files = newJArray() let files = newJArray()
nimblemeta["files"] = files nimblemeta["files"] = files
@ -103,4 +109,4 @@ proc saveNimbleMeta*(pkgDestDir, pkgDir, vcsRevision, nimbleLinkPath: string) =
## pkgDir - The directory where the original package files are. ## pkgDir - The directory where the original package files are.
## For example: ~/projects/jester/ ## For example: ~/projects/jester/
saveNimbleMeta(pkgDestDir, "file://" & pkgDir, vcsRevision, saveNimbleMeta(pkgDestDir, "file://" & pkgDir, vcsRevision,
toSet[string]([nimbleLinkPath]), initSet[string](), true) toHashSet[string]([nimbleLinkPath]), initHashSet[string](), true)

View file

@ -1,12 +1,12 @@
# Copyright (C) Dominik Picheta. All rights reserved. # Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info. # BSD License. Look at license.txt for more info.
import parsecfg, json, streams, strutils, parseutils, os, tables, future import parsecfg, sets, streams, strutils, os, tables, sugar
from sequtils import apply, map from sequtils import apply, map
import version, tools, common, nimscriptsupport, options, packageinfo, cli import version, tools, common, nimscriptwrapper, options, packageinfo, cli
## Contains procedures for parsing .nimble files. Moved here from ``packageinfo`` ## Contains procedures for parsing .nimble files. Moved here from ``packageinfo``
## because it depends on ``nimscriptsupport`` (``nimscriptsupport`` also ## because it depends on ``nimscriptwrapper`` (``nimscriptwrapper`` also
## depends on other procedures in ``packageinfo``. ## depends on other procedures in ``packageinfo``.
type type
@ -212,7 +212,10 @@ proc multiSplit(s: string): seq[string] =
result.del(i) result.del(i)
# Huh, nothing to return? Return given input. # Huh, nothing to return? Return given input.
if len(result) < 1: if len(result) < 1:
return @[s] if s.strip().len != 0:
return @[s]
else:
return @[]
proc readPackageInfoFromNimble(path: string; result: var PackageInfo) = proc readPackageInfoFromNimble(path: string; result: var PackageInfo) =
var fs = newFileStream(path, fmRead) var fs = newFileStream(path, fmRead)
@ -253,12 +256,20 @@ proc readPackageInfoFromNimble(path: string; result: var PackageInfo) =
result.installExt.add(ev.value.multiSplit) result.installExt.add(ev.value.multiSplit)
of "bin": of "bin":
for i in ev.value.multiSplit: for i in ev.value.multiSplit:
if i.splitFile().ext == ".nim":
raise newException(NimbleError, "`bin` entry should not be a source file: " & i)
result.bin.add(i.addFileExt(ExeExt)) result.bin.add(i.addFileExt(ExeExt))
of "backend": of "backend":
result.backend = ev.value.toLowerAscii() result.backend = ev.value.toLowerAscii()
case result.backend.normalize case result.backend.normalize
of "javascript": result.backend = "js" of "javascript": result.backend = "js"
else: discard else: discard
of "beforehooks":
for i in ev.value.multiSplit:
result.preHooks.incl(i.normalize)
of "afterhooks":
for i in ev.value.multiSplit:
result.postHooks.incl(i.normalize)
else: else:
raise newException(NimbleError, "Invalid field: " & ev.key) raise newException(NimbleError, "Invalid field: " & ev.key)
of "deps", "dependencies": of "deps", "dependencies":
@ -277,6 +288,33 @@ proc readPackageInfoFromNimble(path: string; result: var PackageInfo) =
else: else:
raise newException(ValueError, "Cannot open package info: " & path) raise newException(ValueError, "Cannot open package info: " & path)
proc readPackageInfoFromNims(scriptName: string, options: Options,
result: var PackageInfo) =
let
iniFile = getIniFile(scriptName, options)
if iniFile.fileExists():
readPackageInfoFromNimble(iniFile, result)
proc inferInstallRules(pkgInfo: var PackageInfo, options: Options) =
# Binary packages shouldn't install .nim files by default.
# (As long as the package info doesn't explicitly specify what should be
# installed.)
let installInstructions =
pkgInfo.installDirs.len + pkgInfo.installExt.len + pkgInfo.installFiles.len
if installInstructions == 0 and pkgInfo.bin.len > 0:
pkgInfo.skipExt.add("nim")
# When a package doesn't specify a `srcDir` it's fair to assume that
# the .nim files are in the root of the package. So we can explicitly select
# them and prevent the installation of anything else. The user can always
# override this with `installFiles`.
if pkgInfo.srcDir == "":
if dirExists(pkgInfo.getRealDir() / pkgInfo.name):
pkgInfo.installDirs.add(pkgInfo.name)
if fileExists(pkgInfo.getRealDir() / pkgInfo.name.addFileExt("nim")):
pkgInfo.installFiles.add(pkgInfo.name.addFileExt("nim"))
proc readPackageInfo(nf: NimbleFile, options: Options, proc readPackageInfo(nf: NimbleFile, options: Options,
onlyMinimalInfo=false): PackageInfo = onlyMinimalInfo=false): PackageInfo =
## Reads package info from the specified Nimble file. ## Reads package info from the specified Nimble file.
@ -351,6 +389,9 @@ proc readPackageInfo(nf: NimbleFile, options: Options,
if version.kind == verSpecial: if version.kind == verSpecial:
result.specialVersion = minimalInfo.version result.specialVersion = minimalInfo.version
# Apply rules to infer which files should/shouldn't be installed. See #469.
inferInstallRules(result, options)
if not result.isMinimal: if not result.isMinimal:
options.pkgInfoCache[nf] = result options.pkgInfoCache[nf] = result
@ -446,6 +487,15 @@ proc toFullInfo*(pkg: PackageInfo, options: Options): PackageInfo =
else: else:
return pkg return pkg
proc getConcreteVersion*(pkgInfo: PackageInfo, options: Options): string =
## Returns a non-special version from the specified ``pkgInfo``. If the
## ``pkgInfo`` is minimal it looks it up and retrieves the concrete version.
result = pkgInfo.version
if pkgInfo.isMinimal:
let pkgInfo = pkgInfo.toFullInfo(options)
result = pkgInfo.version
assert(not newVersion(result).isSpecial)
when isMainModule: when isMainModule:
validatePackageName("foo_bar") validatePackageName("foo_bar")
validatePackageName("f_oo_b_a_r") validatePackageName("f_oo_b_a_r")

View file

@ -5,8 +5,8 @@
## nim-lang/packages automatically. ## nim-lang/packages automatically.
import system except TResult import system except TResult
import httpclient, base64, strutils, rdstdin, json, os, browsers, times, uri import httpclient, strutils, json, os, browsers, times, uri
import tools, common, cli, config, options import version, tools, common, cli, config, options
type type
Auth = object Auth = object
@ -62,7 +62,7 @@ proc getGithubAuth(o: Options): Auth =
# try to read from disk, if it cannot be found write a new one # try to read from disk, if it cannot be found write a new one
try: try:
let apiTokenFilePath = cfg.nimbleDir / ApiKeyFile let apiTokenFilePath = cfg.nimbleDir / ApiKeyFile
result.token = readFile(apiTokenFilePath) result.token = readFile(apiTokenFilePath).strip()
display("Info:", "Using GitHub API Token in file: " & apiTokenFilePath, display("Info:", "Using GitHub API Token in file: " & apiTokenFilePath,
priority = HighPriority) priority = HighPriority)
except IOError: except IOError:
@ -76,7 +76,7 @@ proc getGithubAuth(o: Options): Auth =
proc isCorrectFork(j: JsonNode): bool = proc isCorrectFork(j: JsonNode): bool =
# Check whether this is a fork of the nimble packages repo. # Check whether this is a fork of the nimble packages repo.
result = false result = false
if j{"fork"}.getBVal(): if j{"fork"}.getBool():
result = j{"parent"}{"full_name"}.getStr() == "nim-lang/packages" result = j{"parent"}{"full_name"}.getStr() == "nim-lang/packages"
proc forkExists(a: Auth): bool = proc forkExists(a: Auth): bool =
@ -94,11 +94,13 @@ proc createFork(a: Auth) =
raise newException(NimbleError, "Unable to create fork. Access token" & raise newException(NimbleError, "Unable to create fork. Access token" &
" might not have enough permissions.") " might not have enough permissions.")
proc createPullRequest(a: Auth, packageName, branch: string) = proc createPullRequest(a: Auth, packageName, branch: string): string =
display("Info", "Creating PR", priority = HighPriority) display("Info", "Creating PR", priority = HighPriority)
discard a.http.postContent(ReposUrl & "nim-lang/packages/pulls", var body = a.http.postContent(ReposUrl & "nim-lang/packages/pulls",
body="""{"title": "Add package $1", "head": "$2:$3", body="""{"title": "Add package $1", "head": "$2:$3",
"base": "master"}""" % [packageName, a.user, branch]) "base": "master"}""" % [packageName, a.user, branch])
var pr = parseJson(body)
return pr{"html_url"}.getStr()
proc `%`(s: openArray[string]): JsonNode = proc `%`(s: openArray[string]): JsonNode =
result = newJArray() result = newJArray()
@ -153,7 +155,7 @@ proc editJson(p: PackageInfo; url, tags, downloadMethod: string) =
proc publish*(p: PackageInfo, o: Options) = proc publish*(p: PackageInfo, o: Options) =
## Publishes the package p. ## Publishes the package p.
let auth = getGithubAuth(o) let auth = getGithubAuth(o)
var pkgsDir = getTempDir() / "nimble-packages-fork" var pkgsDir = getNimbleUserTempDir() / "nimble-packages-fork"
if not forkExists(auth): if not forkExists(auth):
createFork(auth) createFork(auth)
display("Info:", "Waiting 10s to let Github create a fork", display("Info:", "Waiting 10s to let Github create a fork",
@ -211,14 +213,17 @@ proc publish*(p: PackageInfo, o: Options) =
url = promptCustom("Github URL of " & p.name & "?", "") url = promptCustom("Github URL of " & p.name & "?", "")
if url.len == 0: userAborted() if url.len == 0: userAborted()
let tags = promptCustom("Whitespace separated list of tags?", "") let tags = promptCustom(
"Whitespace separated list of tags? (For example: web library wrapper)",
""
)
cd pkgsDir: cd pkgsDir:
editJson(p, url, tags, downloadMethod) editJson(p, url, tags, downloadMethod)
let branchName = "add-" & p.name & getTime().getGMTime().format("HHmm") let branchName = "add-" & p.name & getTime().utc.format("HHmm")
doCmd("git checkout -B " & branchName) doCmd("git checkout -B " & branchName)
doCmd("git commit packages.json -m \"Added package " & p.name & "\"") doCmd("git commit packages.json -m \"Added package " & p.name & "\"")
display("Pushing", "to remote of fork.", priority = HighPriority) display("Pushing", "to remote of fork.", priority = HighPriority)
doCmd("git push https://" & auth.token & "@github.com/" & auth.user & "/packages " & branchName) doCmd("git push https://" & auth.token & "@github.com/" & auth.user & "/packages " & branchName)
createPullRequest(auth, p.name, branchName) let prUrl = createPullRequest(auth, p.name, branchName)
display("Success:", "Pull request successful.", Success, HighPriority) display("Success:", "Pull request successful, check at " & prUrl , Success, HighPriority)

View file

@ -1,7 +1,7 @@
# Copyright (C) Dominik Picheta. All rights reserved. # Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info. # BSD License. Look at license.txt for more info.
import os, json import os, json, sets
import options, common, version, download, packageinfo import options, common, version, download, packageinfo
@ -58,7 +58,7 @@ proc removeRevDep*(nimbleData: JsonNode, pkg: PackageInfo) =
newData[key] = newVal newData[key] = newVal
nimbleData["reverseDeps"] = newData nimbleData["reverseDeps"] = newData
proc getRevDeps*(options: Options, pkg: PackageInfo): seq[PkgTuple] = proc getRevDepTups*(options: Options, pkg: PackageInfo): seq[PkgTuple] =
## Returns a list of *currently installed* reverse dependencies for `pkg`. ## Returns a list of *currently installed* reverse dependencies for `pkg`.
result = @[] result = @[]
let thisPkgsDep = let thisPkgsDep =
@ -76,6 +76,26 @@ proc getRevDeps*(options: Options, pkg: PackageInfo): seq[PkgTuple] =
result.add(pkgTup) result.add(pkgTup)
proc getRevDeps*(options: Options, pkg: PackageInfo): HashSet[PackageInfo] =
result.init()
let installedPkgs = getInstalledPkgsMin(options.getPkgsDir(), options)
for rdepTup in getRevDepTups(options, pkg):
for rdepInfo in findAllPkgs(installedPkgs, rdepTup):
result.incl rdepInfo
proc getAllRevDeps*(options: Options, pkg: PackageInfo, result: var HashSet[PackageInfo]) =
if pkg in result:
return
let installedPkgs = getInstalledPkgsMin(options.getPkgsDir(), options)
for rdepTup in getRevDepTups(options, pkg):
for rdepInfo in findAllPkgs(installedPkgs, rdepTup):
if rdepInfo in result:
continue
getAllRevDeps(options, rdepInfo, result)
result.incl pkg
when isMainModule: when isMainModule:
var nimbleData = %{"reverseDeps": newJObject()} var nimbleData = %{"reverseDeps": newJObject()}

View file

@ -3,7 +3,7 @@
# #
# Various miscellaneous utility functions reside here. # Various miscellaneous utility functions reside here.
import osproc, pegs, strutils, os, uri, sets, json, parseutils import osproc, pegs, strutils, os, uri, sets, json, parseutils
import version, common, cli import version, cli
proc extractBin(cmd: string): string = proc extractBin(cmd: string): string =
if cmd[0] == '"': if cmd[0] == '"':
@ -11,7 +11,7 @@ proc extractBin(cmd: string): string =
else: else:
return cmd.split(' ')[0] return cmd.split(' ')[0]
proc doCmd*(cmd: string, showOutput = false) = proc doCmd*(cmd: string, showOutput = false, showCmd = false) =
let bin = extractBin(cmd) let bin = extractBin(cmd)
if findExe(bin) == "": if findExe(bin) == "":
raise newException(NimbleError, "'" & bin & "' not in PATH.") raise newException(NimbleError, "'" & bin & "' not in PATH.")
@ -20,7 +20,10 @@ proc doCmd*(cmd: string, showOutput = false) =
stdout.flushFile() stdout.flushFile()
stderr.flushFile() stderr.flushFile()
displayDebug("Executing", cmd) if showCmd:
display("Executing", cmd, priority = MediumPriority)
else:
displayDebug("Executing", cmd)
if showOutput: if showOutput:
let exitCode = execCmd(cmd) let exitCode = execCmd(cmd)
displayDebug("Finished", "with exit code " & $exitCode) displayDebug("Finished", "with exit code " & $exitCode)
@ -87,7 +90,7 @@ proc changeRoot*(origRoot, newRoot, path: string): string =
## the trailing separator. This would cause this method to throw during package ## the trailing separator. This would cause this method to throw during package
## installation. ## installation.
if path.startsWith(origRoot) or path.samePaths(origRoot): if path.startsWith(origRoot) or path.samePaths(origRoot):
return newRoot / path[origRoot.len .. path.len-1] return newRoot / path.substr(origRoot.len, path.len-1)
else: else:
raise newException(ValueError, raise newException(ValueError,
"Cannot change root of path: Path does not begin with original root.") "Cannot change root of path: Path does not begin with original root.")
@ -106,6 +109,10 @@ proc copyDirD*(fro, to: string): seq[string] =
createDir(changeRoot(fro, to, path.splitFile.dir)) createDir(changeRoot(fro, to, path.splitFile.dir))
result.add copyFileD(path, changeRoot(fro, to, path)) result.add copyFileD(path, changeRoot(fro, to, path))
proc createDirD*(dir: string) =
display("Creating", "directory $#" % dir, priority = LowPriority)
createDir(dir)
proc getDownloadDirName*(uri: string, verRange: VersionRange): string = proc getDownloadDirName*(uri: string, verRange: VersionRange): string =
## Creates a directory name based on the specified ``uri`` (url) ## Creates a directory name based on the specified ``uri`` (url)
result = "" result = ""
@ -144,6 +151,15 @@ proc contains*(j: JsonNode, elem: tuple[key: string, val: JsonNode]): bool =
when not defined(windows): when not defined(windows):
from posix import getpid from posix import getpid
proc getProcessId*(): string =
when defined(windows):
proc GetCurrentProcessId(): int32 {.stdcall, dynlib: "kernel32",
importc: "GetCurrentProcessId".}
result = $GetCurrentProcessId()
else:
result = $getpid()
proc getNimbleTempDir*(): string = proc getNimbleTempDir*(): string =
## Returns a path to a temporary directory. ## Returns a path to a temporary directory.
## ##
@ -151,10 +167,18 @@ proc getNimbleTempDir*(): string =
## different for different runs of it. You have to make sure to create it ## different for different runs of it. You have to make sure to create it
## first. In release builds the directory will be removed when nimble finishes ## first. In release builds the directory will be removed when nimble finishes
## its work. ## its work.
result = getTempDir() / "nimble_" result = getTempDir() / "nimble_" & getProcessId()
when defined(windows):
proc GetCurrentProcessId(): int32 {.stdcall, dynlib: "kernel32", proc getNimbleUserTempDir*(): string =
importc: "GetCurrentProcessId".} ## Returns a path to a temporary directory.
result.add($GetCurrentProcessId()) ##
## The returned path will be the same for the duration of the process but
## different for different runs of it. You have to make sure to create it
## first. In release builds the directory will be removed when nimble finishes
## its work.
var tmpdir: string
if existsEnv("TMPDIR") and existsEnv("USER"):
tmpdir = joinPath(getEnv("TMPDIR"), getEnv("USER"))
else: else:
result.add($getpid()) tmpdir = getTempDir()
return tmpdir

View file

@ -39,8 +39,6 @@ proc `$`*(ver: Version): string {.borrow.}
proc hash*(ver: Version): Hash {.borrow.} proc hash*(ver: Version): Hash {.borrow.}
proc isNil*(ver: Version): bool {.borrow.}
proc newVersion*(ver: string): Version = proc newVersion*(ver: string): Version =
doAssert(ver.len == 0 or ver[0] in {'#', '\0'} + Digits, doAssert(ver.len == 0 or ver[0] in {'#', '\0'} + Digits,
"Wrong version: " & ver) "Wrong version: " & ver)
@ -95,6 +93,11 @@ proc `==`*(ver: Version, ver2: Version): bool =
else: else:
return false return false
proc cmp*(a, b: Version): int =
if a < b: -1
elif a > b: 1
else: 0
proc `<=`*(ver: Version, ver2: Version): bool = proc `<=`*(ver: Version, ver2: Version): bool =
return (ver == ver2) or (ver < ver2) return (ver == ver2) or (ver < ver2)
@ -132,46 +135,44 @@ proc contains*(ran: VersionRange, ver: Version): bool =
return withinRange(ver, ran) return withinRange(ver, ran)
proc makeRange*(version: string, op: string): VersionRange = proc makeRange*(version: string, op: string): VersionRange =
new(result)
if version == "": if version == "":
raise newException(ParseVersionError, raise newException(ParseVersionError,
"A version needs to accompany the operator.") "A version needs to accompany the operator.")
case op case op
of ">": of ">":
result.kind = verLater result = VersionRange(kind: verLater)
of "<": of "<":
result.kind = verEarlier result = VersionRange(kind: verEarlier)
of ">=": of ">=":
result.kind = verEqLater result = VersionRange(kind: verEqLater)
of "<=": of "<=":
result.kind = verEqEarlier result = VersionRange(kind: verEqEarlier)
of "": of "", "==":
result.kind = verEq result = VersionRange(kind: verEq)
else: else:
raise newException(ParseVersionError, "Invalid operator: " & op) raise newException(ParseVersionError, "Invalid operator: " & op)
result.ver = Version(version) result.ver = Version(version)
proc parseVersionRange*(s: string): VersionRange = proc parseVersionRange*(s: string): VersionRange =
# >= 1.5 & <= 1.8 # >= 1.5 & <= 1.8
new(result)
if s.len == 0: if s.len == 0:
result.kind = verAny result = VersionRange(kind: verAny)
return return
if s[0] == '#': if s[0] == '#':
result.kind = verSpecial result = VersionRange(kind: verSpecial)
result.spe = s.Version result.spe = s.Version
return return
var i = 0 var i = 0
var op = "" var op = ""
var version = "" var version = ""
while true: while i < s.len:
case s[i] case s[i]
of '>', '<', '=': of '>', '<', '=':
op.add(s[i]) op.add(s[i])
of '&': of '&':
result.kind = verIntersect result = VersionRange(kind: verIntersect)
result.verILeft = makeRange(version, op) result.verILeft = makeRange(version, op)
# Parse everything after & # Parse everything after &
@ -184,18 +185,14 @@ proc parseVersionRange*(s: string): VersionRange =
raise newException(ParseVersionError, raise newException(ParseVersionError,
"Having more than one `&` in a version range is pointless") "Having more than one `&` in a version range is pointless")
break return
of '0'..'9', '.': of '0'..'9', '.':
version.add(s[i]) version.add(s[i])
of '\0':
result = makeRange(version, op)
break
of ' ': of ' ':
# Make sure '0.9 8.03' is not allowed. # Make sure '0.9 8.03' is not allowed.
if version != "" and i < s.len: if version != "" and i < s.len - 1:
if s[i+1] in {'0'..'9', '.'}: if s[i+1] in {'0'..'9', '.'}:
raise newException(ParseVersionError, raise newException(ParseVersionError,
"Whitespace is not allowed in a version literal.") "Whitespace is not allowed in a version literal.")
@ -204,15 +201,16 @@ proc parseVersionRange*(s: string): VersionRange =
raise newException(ParseVersionError, raise newException(ParseVersionError,
"Unexpected char in version range '" & s & "': " & s[i]) "Unexpected char in version range '" & s & "': " & s[i])
inc(i) inc(i)
result = makeRange(version, op)
proc toVersionRange*(ver: Version): VersionRange = proc toVersionRange*(ver: Version): VersionRange =
## Converts a version to either a verEq or verSpecial VersionRange. ## Converts a version to either a verEq or verSpecial VersionRange.
new(result) new(result)
if ver.isSpecial: if ver.isSpecial:
result.kind = verSpecial result = VersionRange(kind: verSpecial)
result.spe = ver result.spe = ver
else: else:
result.kind = verEq result = VersionRange(kind: verEq)
result.ver = ver result.ver = ver
proc parseRequires*(req: string): PkgTuple = proc parseRequires*(req: string): PkgTuple =
@ -268,21 +266,18 @@ proc getSimpleString*(verRange: VersionRange): string =
result = "" result = ""
proc newVRAny*(): VersionRange = proc newVRAny*(): VersionRange =
new(result) result = VersionRange(kind: verAny)
result.kind = verAny
proc newVREarlier*(ver: string): VersionRange = proc newVREarlier*(ver: string): VersionRange =
new(result) result = VersionRange(kind: verEarlier)
result.kind = verEarlier
result.ver = newVersion(ver) result.ver = newVersion(ver)
proc newVREq*(ver: string): VersionRange = proc newVREq*(ver: string): VersionRange =
new(result) result = VersionRange(kind: verEq)
result.kind = verEq
result.ver = newVersion(ver) result.ver = newVersion(ver)
proc findLatest*(verRange: VersionRange, proc findLatest*(verRange: VersionRange,
versions: Table[Version, string]): tuple[ver: Version, tag: string] = versions: OrderedTable[Version, string]): tuple[ver: Version, tag: string] =
result = (newVersion(""), "") result = (newVersion(""), "")
for ver, tag in versions: for ver, tag in versions:
if not withinRange(ver, verRange): continue if not withinRange(ver, verRange): continue
@ -296,16 +291,17 @@ when isMainModule:
doAssert(newVersion("1.0") < newVersion("1.4")) doAssert(newVersion("1.0") < newVersion("1.4"))
doAssert(newVersion("1.0.1") > newVersion("1.0")) doAssert(newVersion("1.0.1") > newVersion("1.0"))
doAssert(newVersion("1.0.6") <= newVersion("1.0.6")) doAssert(newVersion("1.0.6") <= newVersion("1.0.6"))
#doAssert(not withinRange(newVersion("0.1.0"), parseVersionRange("> 0.1"))) doAssert(not withinRange(newVersion("0.1.0"), parseVersionRange("> 0.1")))
doAssert(not (newVersion("0.1.0") < newVersion("0.1"))) doAssert(not (newVersion("0.1.0") < newVersion("0.1")))
doAssert(not (newVersion("0.1.0") > newVersion("0.1"))) doAssert(not (newVersion("0.1.0") > newVersion("0.1")))
doAssert(newVersion("0.1.0") < newVersion("0.1.0.0.1")) doAssert(newVersion("0.1.0") < newVersion("0.1.0.0.1"))
doAssert(newVersion("0.1.0") <= newVersion("0.1")) doAssert(newVersion("0.1.0") <= newVersion("0.1"))
var inter1 = parseVersionRange(">= 1.0 & <= 1.5") var inter1 = parseVersionRange(">= 1.0 & <= 1.5")
doAssert(inter1.kind == verIntersect)
var inter2 = parseVersionRange("1.0") var inter2 = parseVersionRange("1.0")
doAssert(inter2.kind == verEq) doAssert(inter2.kind == verEq)
#echo(parseVersionRange(">= 0.8 0.9")) doAssert(parseVersionRange("== 3.4.2") == parseVersionRange("3.4.2"))
doAssert(not withinRange(newVersion("1.5.1"), inter1)) doAssert(not withinRange(newVersion("1.5.1"), inter1))
doAssert(withinRange(newVersion("1.0.2.3.4.5.6.7.8.9.10.11.12"), inter1)) doAssert(withinRange(newVersion("1.0.2.3.4.5.6.7.8.9.10.11.12"), inter1))
@ -319,8 +315,11 @@ when isMainModule:
doAssert(newVersion("") < newVersion("1.0.0")) doAssert(newVersion("") < newVersion("1.0.0"))
doAssert(newVersion("") < newVersion("0.1.0")) doAssert(newVersion("") < newVersion("0.1.0"))
var versions = toTable[Version, string]({newVersion("0.1.1"): "v0.1.1", var versions = toOrderedTable[Version, string]({
newVersion("0.2.3"): "v0.2.3", newVersion("0.5"): "v0.5"}) newVersion("0.1.1"): "v0.1.1",
newVersion("0.2.3"): "v0.2.3",
newVersion("0.5"): "v0.5"
})
doAssert findLatest(parseVersionRange(">= 0.1 & <= 0.4"), versions) == doAssert findLatest(parseVersionRange(">= 0.1 & <= 0.4"), versions) ==
(newVersion("0.2.3"), "v0.2.3") (newVersion("0.2.3"), "v0.2.3")
@ -356,4 +355,7 @@ when isMainModule:
doAssert toVersionRange(newVersion("#head")).kind == verSpecial doAssert toVersionRange(newVersion("#head")).kind == verSpecial
doAssert toVersionRange(newVersion("0.2.0")).kind == verEq doAssert toVersionRange(newVersion("0.2.0")).kind == verEq
# Something raised on IRC
doAssert newVersion("1") == newVersion("1.0")
echo("Everything works!") echo("Everything works!")

8
tests/.gitignore vendored
View file

@ -1,6 +1,10 @@
tester
/nimble-test
/buildDir
/binaryPackage/v1/binaryPackage /binaryPackage/v1/binaryPackage
/binaryPackage/v2/binaryPackage /binaryPackage/v2/binaryPackage
/develop/dependent/src/dependent /develop/dependent/src/dependent
/issue27/issue27
/issue206/issue/issue206bin /issue206/issue/issue206bin
/issue289/issue289 /issue289/issue289
/issue428/nimbleDir/ /issue428/nimbleDir/
@ -13,3 +17,7 @@
/testCommand/testsPass/tests/one /testCommand/testsPass/tests/one
/testCommand/testsPass/tests/three /testCommand/testsPass/tests/three
/testCommand/testsPass/tests/two /testCommand/testsPass/tests/two
/nimscript/nimscript
/packageStructure/validBinary/y
/testCommand/testsFail/tests/t2
/passNimFlags/passNimFlags

View file

@ -0,0 +1,10 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "Test package"
license = "BSD"
# Dependencies
requires "nim >= 0.12.1"

View file

@ -6,6 +6,7 @@ description = "hybrid"
license = "MIT" license = "MIT"
bin = @["hybrid"] bin = @["hybrid"]
installExt = @["nim"]
# Dependencies # Dependencies

View file

@ -0,0 +1,13 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "A new awesome nimble package"
license = "MIT"
srcDir = "src"
thisFieldDoesNotExist = "hello"
# Dependencies
requires "nim >= 0.20.0"

View file

@ -0,0 +1,15 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "A new awesome nimble package"
license = "MIT"
srcDir = "src"
# Dependencies
requires "nim >= 0.16.0"
requires "https://github.com/nimble-test/packagea#head",
"https://github.com/nimble-test/packagebin2"

View file

@ -0,0 +1,7 @@
# This is just an example to get you started. A typical library package
# exports the main API in this file. Note that you cannot rename this file
# but you can remove it if you wish.
proc add*(x, y: int): int =
## Adds two files together.
return x + y

View file

@ -0,0 +1,14 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "A new awesome nimble package"
license = "MIT"
srcDir = "src"
bin = @["issue564/issue564build"]
# Dependencies
requires "nim >= 0.16.0"

View file

@ -0,0 +1,5 @@
# This is just an example to get you started. A typical binary package
# uses this file as the main entry point of the application.
when isMainModule:
echo("Hello, World!")

View file

@ -0,0 +1,12 @@
# Package
version = "0.1.0"
author = "Author"
description = "dummy"
license = "MIT"
# Dependencies
requires "nim >= 0.17.0"
bin = @["test.nim"]

0
tests/issue597/test.nim Normal file
View file

View file

@ -0,0 +1,16 @@
# Package
version = "0.1.0"
author = "GT"
description = "Package for ensuring that issue #633 is resolved."
license = "MIT"
# Dependencies
requires "nim >= 0.19.6"
# to reproduce dependency 2 must be before 1
task testTask, "Test":
for i in 0 .. paramCount():
if paramStr(i) == "--testTask":
echo "Got it"

View file

@ -0,0 +1,12 @@
# Package
version = "0.1.0"
author = "Ivan Bobev"
description = "Package for ensuring that issue #678 is resolved."
license = "MIT"
# Dependencies
requires "nim >= 0.19.6"
# to reproduce dependency 2 must be before 1
requires "issue678_dependency_2", "issue678_dependency_1"

View file

@ -0,0 +1,19 @@
[
{
"name": "issue678_dependency_1",
"url": "https://github.com/nimble-test/issue678?subdir=dependency_1",
"method": "git",
"tags": [ "test" ],
"description":
"Both first and second level dependency of the issue678 package.",
"license": "MIT"
},
{
"name": "issue678_dependency_2",
"url": "https://github.com/nimble-test/issue678?subdir=dependency_2",
"method": "git",
"tags": [ "test" ],
"description": "First level dependency of the issue678 package.",
"license": "MIT"
}
]

View file

@ -0,0 +1,17 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "A new awesome nimble package"
license = "MIT"
srcDir = "src"
# Dependencies
requires "nim >= 0.16.0"
echo "hello"
echo "hello2"

View file

@ -0,0 +1,7 @@
# This is just an example to get you started. A typical library package
# exports the main API in this file. Note that you cannot rename this file
# but you can remove it if you wish.
proc add*(x, y: int): int =
## Adds two files together.
return x + y

View file

@ -0,0 +1,14 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "A new awesome nimble package"
license = "MIT"
srcDir = "src"
bin = @["nimbleVersionDefine"]
# Dependencies
requires "nim >= 0.16.0"

Binary file not shown.

View file

@ -0,0 +1,3 @@
when isMainModule:
const NimblePkgVersion {.strdefine.} = "Unknown"
echo(NimblePkgVersion)

View file

@ -2,7 +2,9 @@
version = "0.1.0" version = "0.1.0"
author = "Dominik Picheta" author = "Dominik Picheta"
description = "Test package" description = """Test package
with multi-line description
"""
license = "BSD" license = "BSD"
bin = @["nimscript"] bin = @["nimscript"]
@ -24,10 +26,13 @@ task cr, "Testing `nimble c -r nimscript.nim` via setCommand":
task repeated, "Testing `nimble c nimscript.nim` with repeated flags": task repeated, "Testing `nimble c nimscript.nim` with repeated flags":
--define: foo --define: foo
--define: bar --define: bar
--define: "quoted"
--define: "quoted\\\"with\\\"quotes"
setCommand "c", "nimscript.nim" setCommand "c", "nimscript.nim"
task api, "Testing nimscriptapi module functionality": task api, "Testing nimscriptapi module functionality":
echo(getPkgDir()) doAssert(findExe("nim").len != 0)
echo("PKG_DIR: ", getPkgDir())
before hooks: before hooks:
echo("First") echo("First")
@ -43,3 +48,15 @@ before hooks2:
task hooks2, "Testing the hooks again": task hooks2, "Testing the hooks again":
echo("Shouldn't happen") echo("Shouldn't happen")
before install:
echo("Before PkgDir: ", getPkgDir())
after install:
echo("After PkgDir: ", getPkgDir())
before build:
echo("Before build")
after build:
echo("After build")

View file

@ -0,0 +1,15 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "Correctly structured package myPkg. See #469."
license = "MIT"
# This is inferred implicitly by Nimble:
# installDirs = @["myPkg"]
# installFiles = @["myPkg.nim"]
# Dependencies
requires "nim >= 0.15.0"

View file

View file

@ -0,0 +1,13 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "Correctly structured package Y"
license = "MIT"
bin = @["y"]
# Dependencies
requires "nim >= 0.15.0"

View file

@ -0,0 +1 @@

View file

@ -5,6 +5,7 @@ author = "Dominik Picheta"
description = "Incorrectly structured package Y" description = "Incorrectly structured package Y"
license = "MIT" license = "MIT"
installExt = @["nim"]
bin = @["y"] bin = @["y"]
# Dependencies # Dependencies

View file

@ -0,0 +1 @@
when not defined(passNimIsWorking): {.error: "-d:passNimIsWorking wasn't passed to the compiler"}

View file

@ -0,0 +1,11 @@
# Package
version = "0.1.0"
author = "SolitudeSF"
description = "Test nimble install flag forwarding"
license = "BSD"
bin = @["passNimFlags"]
# Dependencies
requires "nim >= 0.13.0"

View file

@ -0,0 +1,25 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "Test package"
license = "BSD"
# Dependencies
requires "nim >= 0.12.1"
let
callNimble = getEnv("NIMBLE_TEST_BINARY_PATH")
doAssert callNimble.len != 0, "NIMBLE_TEST_BINARY_PATH not set"
task recurse, "Level 1":
echo 1
exec callNimble & " recurse2"
task recurse2, "Level 2":
echo 2
exec callNimble & " recurse3"
task recurse3, "Level 3":
echo 3

14
tests/run/run.nimble Normal file
View file

@ -0,0 +1,14 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "A new awesome nimble package"
license = "MIT"
srcDir = "src"
bin = @["run"]
# Dependencies
requires "nim >= 0.19.0"

4
tests/run/src/run.nim Normal file
View file

@ -0,0 +1,4 @@
import os
when isMainModule:
echo("Testing `nimble run`: ", commandLineParams())

View file

@ -0,0 +1,4 @@
version = "0.1.0"
author = "John Doe"
description = "Nimble Test"
license = "BSD"

View file

@ -0,0 +1,2 @@
import os
echo(getCurrentDir())

View file

@ -0,0 +1,4 @@
proc myFunc*() =
echo "Executing my func"

View file

@ -0,0 +1,4 @@
version = "0.1.0"
author = "John Doe"
description = "Nimble Test"
license = "BSD"

View file

@ -0,0 +1 @@
echo "Should be ignored"

View file

@ -0,0 +1 @@
echo "Should be ignored"

View file

@ -0,0 +1,7 @@
import testing123, unittest
test "can accept":
echo "First test"
myFunc()

View file

@ -1,6 +1,6 @@
# Copyright (C) Dominik Picheta. All rights reserved. # Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info. # BSD License. Look at license.txt for more info.
import osproc, streams, unittest, strutils, os, sequtils, future import osproc, unittest, strutils, os, sequtils, sugar, strformat
# TODO: Each test should start off with a clean slate. Currently installed # TODO: Each test should start off with a clean slate. Currently installed
# packages are shared between each test which causes a multitude of issues # packages are shared between each test which causes a multitude of issues
@ -10,6 +10,10 @@ var rootDir = getCurrentDir().parentDir()
var nimblePath = rootDir / "src" / addFileExt("nimble", ExeExt) var nimblePath = rootDir / "src" / addFileExt("nimble", ExeExt)
var installDir = rootDir / "tests" / "nimbleDir" var installDir = rootDir / "tests" / "nimbleDir"
const path = "../src/nimble" const path = "../src/nimble"
const stringNotFound = -1
# Set env var to propagate nimble binary path
putEnv("NIMBLE_TEST_BINARY_PATH", nimblePath)
# Clear nimble dir. # Clear nimble dir.
removeDir(installDir) removeDir(installDir)
@ -32,11 +36,23 @@ template cd*(dir: string, body: untyped) =
proc execNimble(args: varargs[string]): tuple[output: string, exitCode: int] = proc execNimble(args: varargs[string]): tuple[output: string, exitCode: int] =
var quotedArgs = @args var quotedArgs = @args
quotedArgs.insert("--nimbleDir:" & installDir)
quotedArgs.insert(nimblePath) quotedArgs.insert(nimblePath)
quotedArgs.add("--nimbleDir:" & installDir) quotedArgs = quotedArgs.map((x: string) => x.quoteShell)
quotedArgs = quoted_args.map((x: string) => ("\"" & x & "\""))
result = execCmdEx(quotedArgs.join(" ")) let path {.used.} = getCurrentDir().parentDir() / "src"
var cmd =
when not defined(windows):
"PATH=" & path & ":$PATH " & quotedArgs.join(" ")
else:
quotedArgs.join(" ")
when defined(macosx):
# TODO: Yeah, this is really specific to my machine but for my own sanity...
cmd = "DYLD_LIBRARY_PATH=/usr/local/opt/openssl@1.1/lib " & cmd
result = execCmdEx(cmd)
checkpoint(cmd)
checkpoint(result.output) checkpoint(result.output)
proc execNimbleYes(args: varargs[string]): tuple[output: string, exitCode: int]= proc execNimbleYes(args: varargs[string]): tuple[output: string, exitCode: int]=
@ -49,12 +65,67 @@ template verify(res: (string, int)) =
check r[1] == QuitSuccess check r[1] == QuitSuccess
proc processOutput(output: string): seq[string] = proc processOutput(output: string): seq[string] =
output.strip.splitLines().filter((x: string) => (x.len > 0)) output.strip.splitLines().filter(
(x: string) => (
x.len > 0 and
"Using env var NIM_LIB_PREFIX" notin x
)
)
proc inLines(lines: seq[string], line: string): bool = proc inLines(lines: seq[string], line: string): bool =
for i in lines: for i in lines:
if line.normalize in i.normalize: return true if line.normalize in i.normalize: return true
proc hasLineStartingWith(lines: seq[string], prefix: string): bool =
for line in lines:
if line.strip(trailing = false).startsWith(prefix):
return true
return false
test "issue 708":
cd "issue708":
# TODO: We need a way to filter out compiler messages from the messages
# written by our nimble scripts.
var (output, exitCode) = execNimble("install", "-y", "--verbose")
check exitCode == QuitSuccess
let lines = output.strip.processOutput()
check(inLines(lines, "hello"))
check(inLines(lines, "hello2"))
test "issue 564":
cd "issue564":
var (_, exitCode) = execNimble("build")
check exitCode == QuitSuccess
test "depsOnly + flag order test":
var (output, exitCode) = execNimble(
"--depsOnly", "install", "-y", "https://github.com/nimble-test/packagebin2"
)
check(not output.contains("Success: packagebin2 installed successfully."))
check exitCode == QuitSuccess
test "nimscript evaluation error message":
cd "invalidPackage":
var (output, exitCode) = execNimble("check")
let lines = output.strip.processOutput()
check(lines[^2].endsWith("Error: undeclared identifier: 'thisFieldDoesNotExist'"))
check exitCode == QuitFailure
test "caching of nims and ini detects changes":
cd "caching":
var (output, exitCode) = execNimble("dump")
check output.contains("0.1.0")
let
nfile = "caching.nimble"
writeFile(nfile, readFile(nfile).replace("0.1.0", "0.2.0"))
(output, exitCode) = execNimble("dump")
check output.contains("0.2.0")
writeFile(nfile, readFile(nfile).replace("0.2.0", "0.1.0"))
test "tasks can be called recursively":
cd "recursive":
check execNimble("recurse").exitCode == QuitSuccess
test "picks #head when looking for packages": test "picks #head when looking for packages":
cd "versionClashes" / "aporiaScenario": cd "versionClashes" / "aporiaScenario":
let (output, exitCode) = execNimble("install", "-y", "--verbose") let (output, exitCode) = execNimble("install", "-y", "--verbose")
@ -80,35 +151,38 @@ test "can build with #head and versioned package (#289)":
test "can validate package structure (#144)": test "can validate package structure (#144)":
# Test that no warnings are produced for correctly structured packages. # Test that no warnings are produced for correctly structured packages.
for package in ["a", "b", "c"]: for package in ["a", "b", "c", "validBinary", "softened"]:
cd "packageStructure/" & package: cd "packageStructure/" & package:
let (output, exitCode) = execNimble(["install", "-y"]) let (output, exitCode) = execNimble(["install", "-y"])
check exitCode == QuitSuccess check exitCode == QuitSuccess
let lines = output.strip.splitLines() let lines = output.strip.processOutput()
check(not inLines(lines, "warning")) check(not lines.hasLineStartingWith("Warning:"))
# Test that warnings are produced for the incorrectly structured packages. # Test that warnings are produced for the incorrectly structured packages.
for package in ["x", "y", "z"]: for package in ["x", "y", "z"]:
cd "packageStructure/" & package: cd "packageStructure/" & package:
let (output, exitCode) = execNimble(["install", "-y"]) let (output, exitCode) = execNimble(["install", "-y"])
check exitCode == QuitSuccess check exitCode == QuitSuccess
let lines = output.strip.splitLines() let lines = output.strip.processOutput()
checkpoint(output) checkpoint(output)
case package case package
of "x": of "x":
check inLines(lines, "Package 'x' has an incorrect structure. It should" & check lines.hasLineStartingWith(
" contain a single directory hierarchy for source files," & "Warning: Package 'x' has an incorrect structure. It should" &
" named 'x', but file 'foobar.nim' is in a directory named" & " contain a single directory hierarchy for source files," &
" 'incorrect' instead.") " named 'x', but file 'foobar.nim' is in a directory named" &
" 'incorrect' instead.")
of "y": of "y":
check inLines(lines, "Package 'y' has an incorrect structure. It should" & check lines.hasLineStartingWith(
" contain a single directory hierarchy for source files," & "Warning: Package 'y' has an incorrect structure. It should" &
" named 'ypkg', but file 'foobar.nim' is in a directory named" & " contain a single directory hierarchy for source files," &
" 'yWrong' instead.") " named 'ypkg', but file 'foobar.nim' is in a directory named" &
" 'yWrong' instead.")
of "z": of "z":
check inLines(lines, "Package 'z' has an incorrect structure. The top level" & check lines.hasLineStartingWith(
" of the package source directory should contain at most one module," & "Warning: Package 'z' has an incorrect structure. The top level" &
" named 'z.nim', but a file named 'incorrect.nim' was found.") " of the package source directory should contain at most one module," &
" named 'z.nim', but a file named 'incorrect.nim' was found.")
else: else:
assert false assert false
@ -132,7 +206,7 @@ test "issue 113 (uninstallation problems)":
# Try to remove c. # Try to remove c.
let (output, exitCode) = execNimble(["remove", "-y", "c"]) let (output, exitCode) = execNimble(["remove", "-y", "c"])
let lines = output.strip.splitLines() let lines = output.strip.processOutput()
check exitCode != QuitSuccess check exitCode != QuitSuccess
check inLines(lines, "cannot uninstall c (0.1.0) because b (0.1.0) depends on it") check inLines(lines, "cannot uninstall c (0.1.0) because b (0.1.0) depends on it")
@ -186,7 +260,7 @@ test "can refresh with custom urls":
let (output, exitCode) = execNimble(["refresh", "--verbose"]) let (output, exitCode) = execNimble(["refresh", "--verbose"])
checkpoint(output) checkpoint(output)
let lines = output.strip.splitLines() let lines = output.strip.processOutput()
check exitCode == QuitSuccess check exitCode == QuitSuccess
check inLines(lines, "config file at") check inLines(lines, "config file at")
check inLines(lines, "official package list") check inLines(lines, "official package list")
@ -201,9 +275,9 @@ test "can refresh with local package list":
[PackageList] [PackageList]
name = "local" name = "local"
path = "$1" path = "$1"
""".unindent % (getCurrentDir() / "issue368" / "packages.json")) """.unindent % (getCurrentDir() / "issue368" / "packages.json").replace("\\", "\\\\"))
let (output, exitCode) = execNimble(["refresh", "--verbose"]) let (output, exitCode) = execNimble(["refresh", "--verbose"])
let lines = output.strip.splitLines() let lines = output.strip.processOutput()
check inLines(lines, "config file at") check inLines(lines, "config file at")
check inLines(lines, "Copying") check inLines(lines, "Copying")
check inLines(lines, "Package list copied.") check inLines(lines, "Package list copied.")
@ -216,7 +290,7 @@ test "package list source required":
name = "local" name = "local"
""") """)
let (output, exitCode) = execNimble(["refresh", "--verbose"]) let (output, exitCode) = execNimble(["refresh", "--verbose"])
let lines = output.strip.splitLines() let lines = output.strip.processOutput()
check inLines(lines, "config file at") check inLines(lines, "config file at")
check inLines(lines, "Package list 'local' requires either url or path") check inLines(lines, "Package list 'local' requires either url or path")
check exitCode == QuitFailure check exitCode == QuitFailure
@ -230,69 +304,97 @@ test "package list can only have one source":
url = "http://nim-lang.org/nimble/packages.json" url = "http://nim-lang.org/nimble/packages.json"
""") """)
let (output, exitCode) = execNimble(["refresh", "--verbose"]) let (output, exitCode) = execNimble(["refresh", "--verbose"])
let lines = output.strip.splitLines() let lines = output.strip.processOutput()
check inLines(lines, "config file at") check inLines(lines, "config file at")
check inLines(lines, "Attempted to specify `url` and `path` for the same package list 'local'") check inLines(lines, "Attempted to specify `url` and `path` for the same package list 'local'")
check exitCode == QuitFailure check exitCode == QuitFailure
test "can install nimscript package": suite "nimscript":
cd "nimscript": test "can install nimscript package":
check execNimble(["install", "-y"]).exitCode == QuitSuccess cd "nimscript":
check execNimble(["install", "-y"]).exitCode == QuitSuccess
test "can execute nimscript tasks": test "before/after install pkg dirs are correct":
cd "nimscript": cd "nimscript":
let (output, exitCode) = execNimble("--verbose", "work") let (output, exitCode) = execNimble(["install", "-y"])
let lines = output.strip.splitLines() check exitCode == QuitSuccess
check exitCode == QuitSuccess check output.contains("Before build")
check lines[^1] == "10" check output.contains("After build")
let lines = output.strip.processOutput()
check lines[0].startsWith("Before PkgDir:")
check lines[0].endsWith("tests" / "nimscript")
check lines[^1].startsWith("After PkgDir:")
check lines[^1].endsWith("tests" / "nimbleDir" / "pkgs" / "nimscript-0.1.0")
test "can use nimscript's setCommand": test "before/after on build":
cd "nimscript": cd "nimscript":
let (output, exitCode) = execNimble("--verbose", "cTest") let (output, exitCode) = execNimble(["build"])
let lines = output.strip.splitLines() check exitCode == QuitSuccess
check exitCode == QuitSuccess check output.contains("Before build")
check "Execution finished".normalize in lines[^1].normalize check output.contains("After build")
test "can use nimscript's setCommand with flags": test "can execute nimscript tasks":
cd "nimscript": cd "nimscript":
let (output, exitCode) = execNimble("--debug", "cr") let (output, exitCode) = execNimble("--verbose", "work")
let lines = output.strip.splitLines() let lines = output.strip.processOutput()
check exitCode == QuitSuccess check exitCode == QuitSuccess
check inLines(lines, "Hello World") check lines[^1] == "10"
test "can use nimscript with repeated flags (issue #329)": test "can use nimscript's setCommand":
cd "nimscript": cd "nimscript":
let (output, exitCode) = execNimble("--debug", "repeated") let (output, exitCode) = execNimble("--verbose", "cTest")
let lines = output.strip.splitLines() let lines = output.strip.processOutput()
check exitCode == QuitSuccess check exitCode == QuitSuccess
var found = false check "Execution finished".normalize in lines[^1].normalize
for line in lines:
if line.contains("--define:foo"):
found = true
check found == true
test "can list nimscript tasks": test "can use nimscript's setCommand with flags":
cd "nimscript": cd "nimscript":
let (output, exitCode) = execNimble("tasks") let (output, exitCode) = execNimble("--debug", "cr")
check "work test description".normalize in output.normalize let lines = output.strip.processOutput()
check exitCode == QuitSuccess check exitCode == QuitSuccess
check inLines(lines, "Hello World")
test "can use pre/post hooks": test "can use nimscript with repeated flags (issue #329)":
cd "nimscript": cd "nimscript":
let (output, exitCode) = execNimble("hooks") let (output, exitCode) = execNimble("--debug", "repeated")
let lines = output.strip.splitLines() let lines = output.strip.processOutput()
check exitCode == QuitSuccess check exitCode == QuitSuccess
check inLines(lines, "First") var found = false
check inLines(lines, "middle") for line in lines:
check inLines(lines, "last") if line.contains("--define:foo"):
found = true
check found == true
test "pre hook can prevent action": test "can list nimscript tasks":
cd "nimscript": cd "nimscript":
let (output, exitCode) = execNimble("hooks2") let (output, exitCode) = execNimble("tasks")
let lines = output.strip.splitLines() check "work".normalize in output.normalize
check exitCode == QuitSuccess check "test description".normalize in output.normalize
check(not inLines(lines, "Shouldn't happen")) check exitCode == QuitSuccess
check inLines(lines, "Hook prevented further execution")
test "can use pre/post hooks":
cd "nimscript":
let (output, exitCode) = execNimble("hooks")
let lines = output.strip.processOutput()
check exitCode == QuitSuccess
check inLines(lines, "First")
check inLines(lines, "middle")
check inLines(lines, "last")
test "pre hook can prevent action":
cd "nimscript":
let (output, exitCode) = execNimble("hooks2")
let lines = output.strip.processOutput()
check exitCode == QuitSuccess
check(not inLines(lines, "Shouldn't happen"))
check inLines(lines, "Hook prevented further execution")
test "nimble script api":
cd "nimscript":
let (output, exitCode) = execNimble("api")
let lines = output.strip.processOutput()
check exitCode == QuitSuccess
check inLines(lines, "PKG_DIR: " & getCurrentDir())
test "can install packagebin2": test "can install packagebin2":
let args = ["install", "-y", "https://github.com/nimble-test/packagebin2.git"] let args = ["install", "-y", "https://github.com/nimble-test/packagebin2.git"]
@ -303,7 +405,7 @@ test "can reject same version dependencies":
"install", "-y", "https://github.com/nimble-test/packagebin.git") "install", "-y", "https://github.com/nimble-test/packagebin.git")
# We look at the error output here to avoid out-of-order problems caused by # We look at the error output here to avoid out-of-order problems caused by
# stderr output being generated and flushed without first flushing stdout # stderr output being generated and flushed without first flushing stdout
let ls = outp.strip.splitLines() let ls = outp.strip.processOutput()
check exitCode != QuitSuccess check exitCode != QuitSuccess
check "Cannot satisfy the dependency on PackageA 0.2.0 and PackageA 0.5.0" in check "Cannot satisfy the dependency on PackageA 0.2.0 and PackageA 0.5.0" in
ls[ls.len-1] ls[ls.len-1]
@ -326,20 +428,20 @@ test "issue #27":
test "issue #126": test "issue #126":
cd "issue126/a": cd "issue126/a":
let (output, exitCode) = execNimble("install", "-y") let (output, exitCode) = execNimble("install", "-y")
let lines = output.strip.splitLines() let lines = output.strip.processOutput()
check exitCode != QuitSuccess # TODO check exitCode != QuitSuccess # TODO
check inLines(lines, "issue-126 is an invalid package name: cannot contain '-'") check inLines(lines, "issue-126 is an invalid package name: cannot contain '-'")
cd "issue126/b": cd "issue126/b":
let (output1, exitCode1) = execNimble("install", "-y") let (output1, exitCode1) = execNimble("install", "-y")
let lines1 = output1.strip.splitLines() let lines1 = output1.strip.processOutput()
check exitCode1 != QuitSuccess check exitCode1 != QuitSuccess
check inLines(lines1, "The .nimble file name must match name specified inside") check inLines(lines1, "The .nimble file name must match name specified inside")
test "issue #108": test "issue #108":
cd "issue108": cd "issue108":
let (output, exitCode) = execNimble("build") let (output, exitCode) = execNimble("build")
let lines = output.strip.splitLines() let lines = output.strip.processOutput()
check exitCode != QuitSuccess check exitCode != QuitSuccess
check inLines(lines, "Nothing to build") check inLines(lines, "Nothing to build")
@ -382,13 +484,16 @@ test "issue #349":
proc checkName(name: string) = proc checkName(name: string) =
let (outp, code) = execNimble("init", "-y", name) let (outp, code) = execNimble("init", "-y", name)
let msg = outp.strip.splitLines() let msg = outp.strip.processOutput()
check code == QuitFailure check code == QuitFailure
check inLines(msg, check inLines(msg,
"\"$1\" is an invalid package name: reserved name" % name) "\"$1\" is an invalid package name: reserved name" % name)
removeFile(name.changeFileExt("nimble")) try:
removeDir("src") removeFile(name.changeFileExt("nimble"))
removeDir("tests") removeDir("src")
removeDir("tests")
except OSError:
discard
for reserved in reservedNames: for reserved in reservedNames:
checkName(reserved.toUpperAscii()) checkName(reserved.toUpperAscii())
@ -410,10 +515,9 @@ test "can uninstall":
block: block:
let (outp, exitCode) = execNimble("uninstall", "-y", "issue27b") let (outp, exitCode) = execNimble("uninstall", "-y", "issue27b")
let ls = outp.strip.splitLines() let ls = outp.strip.processOutput()
check exitCode != QuitSuccess check exitCode != QuitSuccess
check "Cannot uninstall issue27b (0.1.0) because issue27a (0.1.0) depends" & check inLines(ls, "Cannot uninstall issue27b (0.1.0) because issue27a (0.1.0) depends")
" on it" in ls[ls.len-1]
check execNimble("uninstall", "-y", "issue27").exitCode == QuitSuccess check execNimble("uninstall", "-y", "issue27").exitCode == QuitSuccess
check execNimble("uninstall", "-y", "issue27a").exitCode == QuitSuccess check execNimble("uninstall", "-y", "issue27a").exitCode == QuitSuccess
@ -484,6 +588,9 @@ test "can install diamond deps (#184)":
checkpoint(output) checkpoint(output)
check exitCode == 0 check exitCode == 0
test "issues #280 and #524":
check execNimble("install", "-y", "https://github.com/nimble-test/issue280and524.git").exitCode == 0
suite "can handle two binary versions": suite "can handle two binary versions":
setup: setup:
cd "binaryPackage/v1": cd "binaryPackage/v1":
@ -492,9 +599,15 @@ suite "can handle two binary versions":
cd "binaryPackage/v2": cd "binaryPackage/v2":
check execNimble("install", "-y").exitCode == QuitSuccess check execNimble("install", "-y").exitCode == QuitSuccess
var
cmd = installDir / "bin" / "binaryPackage"
when defined(windows):
cmd = "cmd /c " & cmd & ".cmd"
test "can execute v2": test "can execute v2":
let (output, exitCode) = let (output, exitCode) =
execCmdEx(installDir / "bin" / "binaryPackage".addFileExt(ExeExt)) execCmdEx(cmd)
check exitCode == QuitSuccess check exitCode == QuitSuccess
check output.strip() == "v2" check output.strip() == "v2"
@ -502,7 +615,7 @@ suite "can handle two binary versions":
check execNimble("remove", "binaryPackage@2.0", "-y").exitCode==QuitSuccess check execNimble("remove", "binaryPackage@2.0", "-y").exitCode==QuitSuccess
let (output, exitCode) = let (output, exitCode) =
execCmdEx(installDir / "bin" / "binaryPackage".addFileExt(ExeExt)) execCmdEx(cmd)
check exitCode == QuitSuccess check exitCode == QuitSuccess
check output.strip() == "v1" check output.strip() == "v1"
@ -510,7 +623,7 @@ suite "can handle two binary versions":
check execNimble("remove", "binaryPackage@1.0", "-y").exitCode==QuitSuccess check execNimble("remove", "binaryPackage@1.0", "-y").exitCode==QuitSuccess
let (output, exitCode) = let (output, exitCode) =
execCmdEx(installDir / "bin" / "binaryPackage".addFileExt(ExeExt)) execCmdEx(cmd)
check exitCode == QuitSuccess check exitCode == QuitSuccess
check output.strip() == "v2" check output.strip() == "v2"
@ -523,6 +636,12 @@ test "can pass args with spaces to Nim (#351)":
checkpoint output checkpoint output
check exitCode == QuitSuccess check exitCode == QuitSuccess
test "error if `bin` is a source file (#597)":
cd "issue597":
var (output, exitCode) = execNimble("build")
check exitCode != QuitSuccess
check output.contains("entry should not be a source file: test.nim")
suite "reverse dependencies": suite "reverse dependencies":
test "basic test": test "basic test":
cd "revdep/mydep": cd "revdep/mydep":
@ -534,6 +653,27 @@ suite "reverse dependencies":
verify execNimbleYes("remove", "pkgA") verify execNimbleYes("remove", "pkgA")
verify execNimbleYes("remove", "mydep") verify execNimbleYes("remove", "mydep")
test "revdep fail test":
cd "revdep/mydep":
verify execNimbleYes("install")
cd "revdep/pkgWithDep":
verify execNimbleYes("install")
let (output, exitCode) = execNimble("uninstall", "mydep")
checkpoint output
check output.processOutput.inLines("cannot uninstall mydep")
check exitCode == QuitFailure
test "revdep -i test":
cd "revdep/mydep":
verify execNimbleYes("install")
cd "revdep/pkgWithDep":
verify execNimbleYes("install")
verify execNimbleYes("remove", "mydep", "-i")
test "issue #373": test "issue #373":
cd "revdep/mydep": cd "revdep/mydep":
verify execNimbleYes("install") verify execNimbleYes("install")
@ -563,10 +703,10 @@ suite "develop feature":
let path = installDir / "pkgs" / "hybrid-#head" / "hybrid.nimble-link" let path = installDir / "pkgs" / "hybrid-#head" / "hybrid.nimble-link"
check fileExists(path) check fileExists(path)
let split = readFile(path).splitLines() let split = readFile(path).processOutput()
check split.len == 2 check split.len == 2
check split[0].endsWith("develop/hybrid/hybrid.nimble") check split[0].endsWith("develop" / "hybrid" / "hybrid.nimble")
check split[1].endsWith("develop/hybrid") check split[1].endsWith("develop" / "hybrid")
test "can develop with srcDir": test "can develop with srcDir":
cd "develop/srcdirtest": cd "develop/srcdirtest":
@ -578,13 +718,13 @@ suite "develop feature":
let path = installDir / "pkgs" / "srcdirtest-#head" / let path = installDir / "pkgs" / "srcdirtest-#head" /
"srcdirtest.nimble-link" "srcdirtest.nimble-link"
check fileExists(path) check fileExists(path)
let split = readFile(path).splitLines() let split = readFile(path).processOutput()
check split.len == 2 check split.len == 2
check split[0].endsWith("develop/srcdirtest/srcdirtest.nimble") check split[0].endsWith("develop" / "srcdirtest" / "srcdirtest.nimble")
check split[1].endsWith("develop/srcdirtest/src") check split[1].endsWith("develop" / "srcdirtest" / "src")
cd "develop/dependent": cd "develop/dependent":
let (output, exitCode) = execNimble("c", "-r", "src/dependent.nim") let (output, exitCode) = execNimble("c", "-r", "src" / "dependent.nim")
checkpoint output checkpoint output
check(output.processOutput.inLines("hello")) check(output.processOutput.inLines("hello"))
check exitCode == QuitSuccess check exitCode == QuitSuccess
@ -614,10 +754,20 @@ suite "develop feature":
check exitCode == QuitSuccess check exitCode == QuitSuccess
(output, exitCode) = execNimble("path", "srcdirtest") (output, exitCode) = execNimble("path", "srcdirtest")
checkpoint output checkpoint output
check exitCode == QuitSuccess check exitCode == QuitSuccess
check output.strip() == getCurrentDir() / "src" check output.strip() == getCurrentDir() / "src"
suite "path command":
test "can get correct path for srcDir (#531)":
check execNimble("uninstall", "srcdirtest", "-y").exitCode == QuitSuccess
cd "develop/srcdirtest":
let (_, exitCode) = execNimble("install", "-y")
check exitCode == QuitSuccess
let (output, _) = execNimble("path", "srcdirtest")
check output.strip() == installDir / "pkgs" / "srcdirtest-1.0"
suite "test command": suite "test command":
test "Runs passing unit tests": test "Runs passing unit tests":
cd "testCommand/testsPass": cd "testCommand/testsPass":
@ -642,6 +792,19 @@ suite "test command":
check exitCode == QuitSuccess check exitCode == QuitSuccess
check outp.processOutput.inLines("overriden") check outp.processOutput.inLines("overriden")
test "certain files are ignored":
cd "testCommand/testsIgnore":
let (outp, exitCode) = execNimble("test")
check exitCode == QuitSuccess
check(not outp.processOutput.inLines("Should be ignored"))
check outp.processOutput.inLines("First test")
test "CWD is root of package":
cd "testCommand/testsCWD":
let (outp, exitCode) = execNimble("test")
check exitCode == QuitSuccess
check outp.processOutput.inLines(getCurrentDir())
suite "check command": suite "check command":
test "can succeed package": test "can succeed package":
cd "binaryPackage/v1": cd "binaryPackage/v1":
@ -684,4 +847,199 @@ suite "multi":
test "can develop package from git subdir": test "can develop package from git subdir":
removeDir("nimble-test/multi") removeDir("nimble-test/multi")
let args = ["develop", "-y", "https://github.com/nimble-test/multi?subdir=beta"] let args = ["develop", "-y", "https://github.com/nimble-test/multi?subdir=beta"]
check execNimble(args).exitCode == QuitSuccess check execNimble(args).exitCode == QuitSuccess
suite "Module tests":
test "version":
cd "..":
check execCmdEx("nim c -r src/nimblepkg/version").exitCode == QuitSuccess
test "reversedeps":
cd "..":
check execCmdEx("nim c -r src/nimblepkg/reversedeps").exitCode == QuitSuccess
test "packageparser":
cd "..":
check execCmdEx("nim c -r src/nimblepkg/packageparser").exitCode == QuitSuccess
test "packageinfo":
cd "..":
check execCmdEx("nim c -r src/nimblepkg/packageinfo").exitCode == QuitSuccess
test "cli":
cd "..":
check execCmdEx("nim c -r src/nimblepkg/cli").exitCode == QuitSuccess
test "download":
cd "..":
check execCmdEx("nim c -r src/nimblepkg/download").exitCode == QuitSuccess
test "init does not overwrite existing files (#581)":
createDir("issue581/src")
cd "issue581":
const Src = "echo \"OK\""
writeFile("src/issue581.nim", Src)
check execNimbleYes("init").exitCode == QuitSuccess
check readFile("src/issue581.nim") == Src
removeDir("issue581")
test "remove skips packages with revDeps (#504)":
check execNimble("install", "nimboost@0.5.5", "nimfp@0.4.4", "-y").exitCode == QuitSuccess
var (output, exitCode) = execNimble("uninstall", "nimboost", "nimfp", "-n")
var lines = output.strip.processOutput()
check inLines(lines, "Cannot uninstall nimboost")
(output, exitCode) = execNimble("uninstall", "nimfp", "nimboost", "-y")
lines = output.strip.processOutput()
check (not inLines(lines, "Cannot uninstall nimboost"))
check execNimble("path", "nimboost").exitCode != QuitSuccess
check execNimble("path", "nimfp").exitCode != QuitSuccess
test "pass options to the compiler with `nimble install`":
cd "passNimFlags":
check execNimble("install", "--passNim:-d:passNimIsWorking").exitCode == QuitSuccess
test "do not install single dependency multiple times (#678)":
# for the test to be correct, the tested package and its dependencies must not
# exist in the local cache
removeDir("nimbleDir")
cd "issue678":
testRefresh():
writeFile(configFile, """
[PackageList]
name = "local"
path = "$1"
""".unindent % (getCurrentDir() / "packages.json").replace("\\", "\\\\"))
check execNimble(["refresh"]).exitCode == QuitSuccess
let (output, exitCode) = execNimble("install", "-y")
check exitCode == QuitSuccess
let index = output.find("issue678_dependency_1@0.1.0 already exists")
check index == stringNotFound
test "Passing command line arguments to a task (#633)":
cd "issue633":
var (output, exitCode) = execNimble("testTask --testTask")
check exitCode == QuitSuccess
check output.contains("Got it")
suite "nimble run":
test "Invalid binary":
cd "run":
var (output, exitCode) = execNimble(
"--debug", # Flag to enable debug verbosity in Nimble
"run", # Run command invokation
"blahblah", # The command to run
)
check exitCode == QuitFailure
check output.contains("Binary '$1' is not defined in 'run' package." %
"blahblah".changeFileExt(ExeExt))
test "Parameters passed to executable":
cd "run":
var (output, exitCode) = execNimble(
"--debug", # Flag to enable debug verbosity in Nimble
"run", # Run command invokation
"run", # The command to run
"--debug", # First argument passed to the executed command
"check" # Second argument passed to the executed command.
)
check exitCode == QuitSuccess
check output.contains("tests$1run$1$2 --debug check" %
[$DirSep, "run".changeFileExt(ExeExt)])
check output.contains("""Testing `nimble run`: @["--debug", "check"]""")
test "Parameters not passed to single executable":
cd "run":
var (output, exitCode) = execNimble(
"--debug", # Flag to enable debug verbosity in Nimble
"run", # Run command invokation
"--debug" # First argument passed to the executed command
)
check exitCode == QuitSuccess
check output.contains("tests$1run$1$2 --debug" %
[$DirSep, "run".changeFileExt(ExeExt)])
check output.contains("""Testing `nimble run`: @["--debug"]""")
test "Parameters passed to single executable":
cd "run":
var (output, exitCode) = execNimble(
"--debug", # Flag to enable debug verbosity in Nimble
"run", # Run command invokation
"--", # Flag to set run file to "" before next argument
"--debug", # First argument passed to the executed command
"check" # Second argument passed to the executed command.
)
check exitCode == QuitSuccess
check output.contains("tests$1run$1$2 --debug check" %
[$DirSep, "run".changeFileExt(ExeExt)])
check output.contains("""Testing `nimble run`: @["--debug", "check"]""")
test "Executable output is shown even when not debugging":
cd "run":
var (output, exitCode) =
execNimble("run", "run", "--option1", "arg1")
check exitCode == QuitSuccess
check output.contains("""Testing `nimble run`: @["--option1", "arg1"]""")
test "Quotes and whitespace are well handled":
cd "run":
var (output, exitCode) = execNimble(
"run", "run", "\"", "\'", "\t", "arg with spaces"
)
check exitCode == QuitSuccess
check output.contains(
"""Testing `nimble run`: @["\"", "\'", "\t", "arg with spaces"]"""
)
test "NimbleVersion is defined":
cd "nimbleVersionDefine":
var (output, exitCode) = execNimble("c", "-r", "src/nimbleVersionDefine.nim")
check output.contains("0.1.0")
check exitCode == QuitSuccess
var (output2, exitCode2) = execNimble("run", "nimbleVersionDefine")
check output2.contains("0.1.0")
check exitCode2 == QuitSuccess
test "issue 432":
cd "issue432":
check execNimble("install", "-y", "--depsOnly").exitCode == QuitSuccess
check execNimble("install", "-y", "--depsOnly").exitCode == QuitSuccess
test "compilation without warnings":
const buildDir = "./buildDir/"
const filesToBuild = [
"../src/nimble.nim",
"../src/nimblepkg/nimscriptapi.nim",
"./tester.nim",
]
proc execBuild(fileName: string): tuple[output: string, exitCode: int] =
result = execCmdEx(
fmt"nim c -o:{buildDir/fileName.splitFile.name} {fileName}")
proc checkOutput(output: string): uint =
const warningsToCheck = [
"[UnusedImport]",
"[Deprecated]",
"[XDeclaredButNotUsed]",
]
for line in output.splitLines():
for warning in warningsToCheck:
if line.find(warning) != stringNotFound:
once: checkpoint("Detected warnings:")
checkpoint(line)
inc(result)
removeDir(buildDir)
var linesWithWarningsCount: uint = 0
for file in filesToBuild:
let (output, exitCode) = execBuild(file)
check exitCode == QuitSuccess
linesWithWarningsCount += checkOutput(output)
check linesWithWarningsCount == 0