Compare commits

...

437 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
Dominik Picheta
6a2da5627c Version 0.8.10. 2018-02-23 23:15:12 +00:00
Dominik Picheta
cf1b83792d Run travis tests against 0.17.2 instead of 0.17.0. 2018-02-23 22:44:57 +00:00
Dominik Picheta
7e3058657c Fixes problem with subdirs and 'develop'. Documents package urls. 2018-02-23 22:43:28 +00:00
Dominik Picheta
25ffe273f7 Implements #421. 2018-02-23 22:27:14 +00:00
Dominik Picheta
7d3f3f9127 Fixes #445. 2018-02-21 20:22:32 +00:00
Dominik Picheta
383e4fd7bc Merges #450 manually with some adjustments. 2018-02-21 19:06:42 +00:00
Dominik Picheta
0a205d3868
Merge pull request #454 from yglukhov/path-fix
Fixed nimble path
2018-01-30 15:07:11 +00:00
Yuriy Glukhov
d00f59708a Fixed nimble path 2018-01-30 17:01:31 +02:00
Dominik Picheta
2b9215256b Show friendly error message when stdlib is outdated. 2018-01-27 15:37:51 +00:00
Dominik Picheta
fd84b139bd Fixes breaking changes in 0.18.0. 2018-01-27 15:37:51 +00:00
Ganesh Viswanathan
5dc0404dee Fix for #398 (#451)
* Fix for #398

* Updated fix for #398
2018-01-15 11:17:40 +00:00
Dominik Picheta
3dae26447e Improve project structure docs. 2018-01-14 16:01:51 +00:00
Dominik Picheta
6354132959 Implement nimLibPrefix config var and add better messages for it. 2018-01-09 22:32:08 +00:00
Dominik Picheta
c271435a84 Lower priority of messages from NimScript parser. 2018-01-09 21:57:39 +00:00
Dominik Picheta
182893c529 Show better error when standard library cannot be found. 2018-01-09 21:56:12 +00:00
Dominik Picheta
c8cd1d9286
Merge pull request #448 from genotrance/#280
Fix for #280
2018-01-08 13:20:49 +00:00
Ganesh Viswanathan
4770556939 Fix for #280 2018-01-08 00:37:20 -06:00
Dominik Picheta
133003ef54
Merge pull request #447 from nc-x/446
Fix #446
2018-01-05 14:45:49 +00:00
Neelesh Chandola
3cc9c4b2f5
Fix #446 2018-01-05 18:52:57 +05:30
Dominik Picheta
18da3c8b4c
Merge pull request #442 from antizealot1337/issue349
Prevent reserved names on Windows from being package names
2017-12-22 17:43:54 +00:00
Chris
74856a8708 Prevent reserved names on Windows from being package names.
Fixes #349
2017-12-22 11:31:33 -05:00
Xiao-Yong
e32e2cdcbe Initialize nimbleDir option from the environment variable NIMBLE_DIR (#417)
* Initialize options from the environment variable NIMBLE_DIR

* Tests for the existence of project.nim file

Matches nim's command line handling of 'nim c project'
The error message also states the files being tested.

* Fix deprecated warnings (#420)

* Fix deprecated warning

* Fix deprecation warnings

* Initialize options from the environment variable NIMBLE_DIR

* inform user about using NIMBLE_DIR env
2017-12-21 23:43:59 +00:00
Fabian Keller
209fca53e7 Terminate copy iteration when hitting in-place nimbleDir (#429)
* terminate copy iteration when hitting in-place nimbleDir; fixes #428

* investigate travis issue

* more investigations

* more investigations

* fix nim version issue
2017-12-21 23:15:03 +00:00
Dominik Picheta
0e09f69334
Merge pull request #431 from bluenote10/clean_working_dir
gitignore files produced by tests
2017-12-21 23:12:43 +00:00
antizealot1337
4cc1d44bdd Reuse -y to mean accept defaults for init command for #288 (#437)
* Reuse -y to mean accept defaults for init command

* Change cli.promptCustom to accept a ForcePrompt value and delegate prompting responsiblity to it

* Add prompt for different license selection

* Remove extra spaces in category in promptCustom and promptList

* Fix bug that always returned MIT

* Add LGPL3 to array of licenses

* Change to not prompt at all for package name

* Change to not prompt for author if acquired from vcs and moved before prompting for version

* Reduce excess indenting

* Create tools.writeContents to contain file writting functionality and use it to write the nimble file

* Create package src directory and add it to the nimble file

* Write an initial source file

* Create directory and files needed for testing

* Add package created message and change priority of nimble file creation

* Rearrange args for promptCustom and add another promptCustom function that doesn't accept ForcePrompt

* Add package type prompt, remove writeContents proc, rename initial test file, provide alternate initial file contents based on type, and other minor improvements
2017-12-21 23:11:02 +00:00
Fabian Keller
3d0d1fe9a4 gitignore files produced by tests 2017-11-13 20:30:00 +01:00
Daniil Yarancev
f852198724 Fix deprecated warnings (#420)
* Fix deprecated warning

* Fix deprecation warnings
2017-11-01 19:07:40 +00:00
Dominik Picheta
35327876c0 Merge pull request #418 from jxy/autoext
Tests for the existence of project.nim file
2017-10-23 22:15:30 +01:00
Xiao-Yong Jin
8ca1cc0957 Tests for the existence of project.nim file
Matches nim's command line handling of 'nim c project'
The error message also states the files being tested.
2017-10-23 15:51:32 -05:00
Xiao-Yong
4992707e8b Put "nimblepkg/nimscriptapi.nim" under nimbleDir, fix #363 (#416)
* Put "nimblepkg/nimscriptapi.nim" under nimbleDir, fix #363

By default, the module will be under
  $HOME/.nimble/nimblecache/nimblepkg/nimscriptapi.nim
and the following directory will be in the searchPaths
  $HOME/.nimble/nimblecache/

* Put "nimblepkg/nimscriptapi.nim" under nimbleDir, fix #363

By default, the module will be under
  $HOME/.nimble/nimblecache/nimblepkg/nimscriptapi.nim
and the following directory will be in the searchPaths
  $HOME/.nimble/nimblecache/
2017-10-19 18:58:50 +01:00
Dominik Picheta
c7b97bb206 Implements check command. 2017-10-15 15:28:29 +01:00
Dominik Picheta
968e1a20be Merge pull request #411 from Calinou/readme-tweaks
Improve the readme
2017-10-04 11:17:42 +01:00
Hugo Locurcio
7164fca394 Improve the readme
- Improve grammar and spelling
- Capitalize Nimble
- Fix the link to docs/distros.html
- Switch all supporting links to HTTPS
- Link to the license file directly
2017-10-04 10:45:39 +02:00
Dominik Picheta
a02a1431e2 Simplify implementation of path for Nimble links. 2017-09-30 14:16:57 +01:00
Dominik Picheta
596d17804f Merge branch 'fix-403' of https://github.com/yglukhov/nimble into yglukhov-fix-403 2017-09-30 13:51:02 +01:00
Dominik Picheta
fc6912a139 Verify that GIT hash was retrieved correctly during compilation. 2017-09-30 13:49:59 +01:00
Yuriy Glukhov
a247047089 Fixed nondeterminism in version sorting 2017-09-20 18:18:22 +03:00
Yuriy Glukhov
88b4a9ed8a Introduced NimbleLink object with its read/write routines 2017-09-09 23:33:31 +03:00
Yuriy Glukhov
9068e8e43a Added a test 2017-09-09 23:15:08 +03:00
Yuriy Glukhov
5a739b4c03 Fixes #403 2017-09-08 17:44:04 +03:00
Dominik Picheta
fc81b5b58e Update repo info docs in readme. 2017-09-06 22:35:09 +01:00
Dominik Picheta
5b82e06d00 Version 0.8.8. 2017-09-03 18:26:17 +01:00
Dominik Picheta
d9b174b7df Work around NimScript eval problems by caching better. 2017-09-03 17:44:15 +01:00
Dominik Picheta
301a366dd2 Refactoring and many fixes to PR #378. (Broken)
This commit is broken due to a cleanup issue with NimScript eval.
2017-09-03 17:37:52 +01:00
Dominik Picheta
6a851f5b87 Merge branch 'master' of https://github.com/Nycto/nimble into Nycto-master 2017-09-03 15:16:04 +01:00
Dominik Picheta
92e9ec5e59 Fixes #373. Implements #287. Fixes #271. 2017-09-02 23:19:03 +01:00
Dominik Picheta
3c1e669eaa Only consider #head to be newest not other special versions.
Refs #311.
2017-09-02 15:39:57 +01:00
Dominik Picheta
9d0b978845 Fixes readme formatting. 2017-08-28 23:18:46 +01:00
Dominik Picheta
43489da1f2 Added implementation details about .nimble-link files to readme. 2017-08-28 23:03:49 +01:00
Dominik Picheta
cd01b1e221 Merge pull request #396 from FedericoCeratto/patch-2
Add completion for zsh
2017-08-24 21:02:29 +01:00
Federico Ceratto
555a10dcb8 Add completion for zsh 2017-08-23 18:44:50 +01:00
Dominik Picheta
acbce88bac Fixes cloning repo for develop command. 2017-08-19 22:03:45 +01:00
Dominik Picheta
bdfb681824 Fixes #290. 2017-08-19 21:22:48 +01:00
Dominik Picheta
c46e3dcd4d Speeds up the list -i command. 2017-08-19 21:02:15 +01:00
Dominik Picheta
3bdce8d332 Fixes removal of linked packages (and speeds up removal). 2017-08-19 20:56:29 +01:00
Dominik Picheta
f746f4f0f3 Add docs for nimble develop. 2017-08-18 23:20:30 +01:00
Dominik Picheta
d72045ddc5 Attempt to fix tests. 2017-08-16 23:35:52 +01:00
Dominik Picheta
97dc0ffb45 Implements basic support for building with .nimble-link'ed packages. 2017-08-16 22:22:00 +01:00
Dominik Picheta
5f1de1e4ff Don't fail immediately when user doesn't want to load packages.json. 2017-08-16 22:20:24 +01:00
Dominik Picheta
a5f325f032 Small refactoring. 2017-08-16 19:51:22 +01:00
Dominik Picheta
788f93a4e7 Merge pull request #391 from subsetpark/bugfix-path-values
Don't normalize package list paths. That strips meaningful characters.
2017-08-13 22:44:00 +01:00
Zach Smith
1ef7e49350 Don't normalize package list paths. That strips meaningful characters. 2017-08-13 17:41:56 -04:00
Dominik Picheta
9f0aae0432 Check for Windows compilation in the tester. 2017-08-13 15:11:38 +01:00
Dominik Picheta
56dd401831 Implements develop command. Refs #240. 2017-08-13 15:04:40 +01:00
Dominik Picheta
84d63c8988 Fixes compilation on Windows. 2017-08-13 15:02:09 +01:00
Dominik Picheta
bc6ce4b9ea Add missing binaryPackage test files. 2017-08-13 00:06:23 +01:00
Dominik Picheta
d1eae2f1a0 Fixes #351. 2017-08-12 22:01:47 +01:00
Dominik Picheta
ee34150d70 Fixes #331. 2017-08-12 20:48:17 +01:00
Dominik Picheta
79b78ff781 Add diamond_deps tests for #184. 2017-08-12 15:59:30 +01:00
Dominik Picheta
c379a79910 Bump to v0.8.7 (devel). 2017-08-12 14:52:11 +01:00
Dominik Picheta
23e2932be8 Tester improvements. 2017-08-12 14:51:31 +01:00
Dominik Picheta
612c084688 Improvements to #385. 2017-08-12 14:50:32 +01:00
Dominik Picheta
4050683a9c Merge branch 'out-of-mem' of https://github.com/yglukhov/nimble into yglukhov-out-of-mem 2017-08-12 12:42:31 +01:00
Dominik Picheta
b47f174748 Merge pull request #348 from antizealot1337/seqstringarg
Change buildFromDir to accept a seq[string] instead of a string.
2017-08-12 11:18:41 +01:00
Dominik Picheta
6b46dc1b31 Merge pull request #387 from TiberiumN/master
Removed deprecated warnings, also tiny refactoring
2017-08-10 20:43:31 +01:00
Daniil Yarancev
29c9cf8ce7 Removed deprecated warnings, also tiny refactoring 2017-08-10 12:51:16 +03:00
Yuriy Glukhov
06a94ba28f Lower memory consumption 2017-08-07 17:22:47 +03:00
Dominik Picheta
ebf4eace39 Bump travis to Nim 0.17.0 2017-08-01 19:11:21 +01:00
Dominik Picheta
d253652afc Merge pull request #383 from waylon531/master
Added better url detection to publish
2017-07-29 12:02:10 +01:00
waylon531
4a78953fc6
Added better url detection to publish
Publish will now look at the hostname part of the ssh url to figure out
what git server to use. This definitely works with both github and
gitlab, and adds gitlab support to `nimble publish`.
2017-07-25 17:10:05 -07:00
Yuriy Glukhov
e756a14c15 Fixed branch checkout and handling of branch names with dashes. (#379)
* Fixed branch checkout and  handling of branch names with dashes.

* Added assert
2017-07-19 22:42:28 +01:00
Nycto
254658ee5d Add test target 2017-07-18 09:26:52 -07:00
Dominik Picheta
10a38a3c90 Merge pull request #371 from subsetpark/local-json
Allow specifying local json package list files
2017-07-11 19:30:14 +01:00
Dominik Picheta
82ff6134bc Merge pull request #372 from subsetpark/explicit-structure
More explicit instructions about conventional package structure
2017-06-27 18:25:40 +01:00
Zach Smith
34cc9b958f Restore Hybrids heading to readme 2017-06-26 20:29:36 -04:00
Zach Smith
2a7e1f132c Code Review from @dom96 2017-06-26 17:12:46 -04:00
Zach Smith
2718a8e9d5 Use path != "" instead of len check 2017-06-25 17:40:06 -04:00
Zach Smith
f2836e5a5c Roll back downloadFile upgrade 2017-06-25 17:27:56 -04:00
Zach Smith
c89ca099a2 Allow locally stored package list files to be specified in config (#368) 2017-06-25 17:10:08 -04:00
Zach Smith
183fed527b Make conventional structure more explicit in structure warnings and readme 2017-06-25 16:31:34 -04:00
Dominik Picheta
0314df706f Merge pull request #370 from couven92/gitignore
Add Windows and VCC artifacts to gitignore
2017-06-24 13:37:12 +01:00
Fredrik Høisæther Rasch
9878279769 Add Windows and VCC artifacts to gitignore 2017-06-24 13:10:08 +02:00
Dominik Picheta
528886b24d Merge pull request #365 from jonafato/fix-publishing-packages-link
Fix publishing packages header and table of contents link
2017-06-17 14:27:24 +01:00
Jon Banafato
8a1fde9693 Fix publishing packages header and table of contents link 2017-06-15 00:01:02 -04:00
Dominik Picheta
18a860ff14 Merge pull request #364 from jxy/patch-1
nimble accepts a .nimble file that is a link
2017-06-12 17:36:30 +01:00
Xiao-Yong
a69d7563f4 nimble accepts a .nimble file that is a link 2017-06-07 23:32:42 -05:00
Dominik Picheta
f67a214bae Merge pull request #362 from ephja/patch-2
fix typo
2017-06-05 10:23:11 +01:00
ephja
659fcf194f fix typo 2017-06-05 11:16:23 +02:00
Dominik Picheta
5adfe30155 Version 0.8.6. 2017-05-05 16:47:17 +01:00
Chris
8555982512 Change buildFromDir to accept a seq[string] instead of a string.
Fixes #334
2017-05-05 11:31:51 -04:00
Dominik Picheta
4a71ccbbd4 Improve getNimPrefixDir to support choosenim and env var.
See comment for more information. This commit also adds support
for an environment variable so that users have a workaround
when Nimble cannot find the Nim stdlib.
2017-05-05 12:36:54 +01:00
antizealot1337
30d6aaf966 Change to pass extra parameters to compiler (#347)
* Change to pass extra parameters to compiler

* Add extra space in from of path argument

* Fix missing space that caused some nimble commands to fail
2017-05-05 12:04:01 +01:00
Dominik Picheta
cfe68bb441 Don't check Nim version when init'ing config (breaks .babel dir). 2017-05-02 14:01:02 +01:00
Dominik Picheta
5d5825a179 Bump version to 0.8.5. 2017-04-30 21:39:08 +01:00
Dominik Picheta
d9f2e6c49e Implement --noColor option. 2017-04-30 15:40:46 +01:00
Dominik Picheta
c1ffe62a68 Allow doAction to show help/version instead of CLI parser. 2017-04-28 16:52:34 +02:00
Dominik Picheta
9e6db081db Merge pull request #345 from samdmarshall/fix-srcdir-path-exception
Modifying how package install path is validated
2017-04-25 21:23:37 +02:00
Samantha Marshall
05f0de4492
Adding tests to fix for #338
Test:
  * Added new test case for issue #338, where package installation would fail when the `srcDir` path ended in a directory separator.
2017-04-24 13:45:31 -04:00
Samantha Marshall
b7201c81a4
Modifying how package install path is validated
Fix:
  * This addresses a bug where nimble will throw an exception in the middle of installation due to trying to evaluate paths that are not normalized. This was fixed by adding some additional validation checks that involve comparing paths by normalizing them first. [#338]
2017-04-22 13:33:49 -04:00
Dominik Picheta
e62084a2a7 Merge pull request #343 from andreaferretti/fix305
Fixed https://github.com/nim-lang/nimble/issues/305
2017-04-07 19:53:55 +02:00
Andrea Ferretti
b98b697bea Fixed test 2017-04-07 19:46:52 +02:00
Dominik Picheta
43cbc028e4 Merge pull request #342 from andreaferretti/fix329
Fixed https://github.com/nim-lang/nimble/issues/339
2017-04-07 18:20:47 +02:00
Andrea Ferretti
745a7863d2 Fixed https://github.com/nim-lang/nimble/issues/305 2017-04-07 15:32:31 +02:00
Andrea Ferretti
2e9c50e487 Fixed https://github.com/nim-lang/nimble/issues/339 2017-04-07 15:09:13 +02:00
Dominik Picheta
c3c845f632 Merge pull request #337 from samdmarshall/github-api-token-env-var
Access GitHub API token through environment variable
2017-03-13 19:52:00 +01:00
Samantha Marshall
0d516b483e
fixing casing 2017-03-13 14:47:16 -04:00
Samantha Marshall
ad869f9df6
fixing typo and adding message for retrieving API Token from file 2017-03-13 13:38:32 -04:00
Samantha Marshall
47f5691c0b
adding new display message about where the token is coming from 2017-03-13 13:18:27 -04:00
Samantha Marshall
26344e2083
adding on-screen delay for new message 2017-03-11 14:54:48 -05:00
Samantha Marshall
32ed21695b
adding support for an environment variable 2017-03-11 14:51:54 -05:00
Dominik Picheta
8e143a097d Change name of file where publish token is saved. 2017-02-22 18:17:18 +01:00
Dominik Picheta
82dcadeb46 Merge pull request #336 from Jeff-Ciesielski/jeffc/cache_creds
Add credential caching
2017-02-22 18:16:33 +01:00
Jeff Ciesielski
c13a1c8977 Add credential caching
This change stores the user's github API token in ~/.nimble/api_token for
ease of publishing.

closes https://github.com/nim-lang/nimble/issues/307
2017-02-21 22:26:20 -05:00
Dominik Picheta
195c67826c Merge pull request #333 from andreaferretti/master
Fixed #329
2017-02-10 17:29:52 +01:00
Andrea Ferretti
83731a9b32 Fixed #329 2017-02-10 16:53:27 +01:00
Dominik Picheta
0a280aa6dd Version 0.8.4 2017-01-29 21:31:31 +01:00
Dominik Picheta
c4d294ca9b Fixes #319. 2017-01-29 20:47:47 +01:00
Dominik Picheta
4c99e3b6eb Fixes #321. 2017-01-29 16:58:53 +01:00
Dominik Picheta
015233a8b6 Travis: Upgrade to Nim 0.16.0. 2017-01-29 16:16:17 +01:00
Dominik Picheta
64518698e2 Travis: Use ubuntu 14.04 instead of 12.04. 2017-01-29 16:07:23 +01:00
Dominik Picheta
f479213ad8 Fixes #323. 2017-01-29 15:57:15 +01:00
Dominik Picheta
27b970885a Merge pull request #325 from samdmarshall/master
whooops, missed part two of #324
2017-01-28 18:34:53 +01:00
Samantha Marshall
f613458780
whooops, missed part two of this changeset 2017-01-28 10:47:04 -05:00
Dominik Picheta
6836b337ef Merge pull request #324 from samdmarshall/master
updating git command to fetch the url of the remote origin
2017-01-28 12:39:19 +01:00
Samantha Marshall
caae0a3cbd
updating git command to fetch the url of the remote origin 2017-01-27 20:25:25 -05:00
Dominik Picheta
ee07fb83e7 Always overwrite temporary nimscriptapi.nim in /tmp. 2017-01-11 00:10:40 +01:00
Dominik Picheta
69efef5f4b Fixes #313. Fixes #314. Binary packages installation issues fixed. 2017-01-10 23:53:46 +01:00
Dominik Picheta
97c67e64a4 Fixes #312. 2017-01-09 20:28:17 +01:00
Dominik Picheta
756beb6b5e Increment to v0.8.3. 2017-01-09 20:28:03 +01:00
Dominik Picheta
690fcf04a3 Add 0.8.2 changelog. 2017-01-08 18:41:00 +01:00
Dominik Picheta
297d3651ae Clarify install instructions to mention koch. 2017-01-08 18:38:29 +01:00
Dominik Picheta
c37420d507 Bump to v0.8.2. 2017-01-08 18:34:34 +01:00
Dominik Picheta
f847344784 Add missing test files. 2017-01-08 18:16:56 +01:00
Dominik Picheta
d78af3acf1 Fixes aporia install. See nim-lang/aporia#136. Refs #311. 2017-01-08 18:15:36 +01:00
Dominik Picheta
08f5032781 Small changelog adjustment. 2017-01-05 21:32:01 +00:00
Dominik Picheta
8a3c661b98 Add info about external dependency feature to changelog. 2017-01-05 21:28:52 +00:00
Dominik Picheta
4f175749e4 Don't search for nimscriptapi overrides. Fixes #306. 2017-01-04 16:05:43 +00:00
Dominik Picheta
17f1467e5d Add missing test files. 2017-01-03 20:04:48 +00:00
Dominik Picheta
0628911758 Show output for the compiler/docgen commands. Fixes #303. 2017-01-03 19:38:34 +00:00
Dominik Picheta
dda0c39e34 Fixes #304. 2017-01-03 19:25:36 +00:00
Dominik Picheta
b629048249 Just check for nimdistros instead of adding an additional define. 2017-01-03 19:00:39 +00:00
Dominik Picheta
fcbb3de783 More intelligent definition of nimbledistros.
This fixes issues with using latest Nimble in an
0.15.2 environment.

Squashed commit of the following:

commit 4c5f791873b7965a25b9dbdb29a96b38a065c478
Author: Dominik Picheta <dominikpicheta@gmail.com>
Date:   Tue Jan 3 18:41:30 2017 +0000

    Disable output in tester.

commit 9f4af9685a36fbebc5a71b6d39130c2d2b30bc05
Author: Dominik Picheta <dominikpicheta@gmail.com>
Date:   Tue Jan 3 18:30:38 2017 +0000

    Attempt at fixing tests.

commit d0de031d1ce11be0f106eb6d92885b6833ce95b0
Author: Dominik Picheta <dominikpicheta@gmail.com>
Date:   Tue Jan 3 18:16:40 2017 +0000

    Testing.
2017-01-03 18:41:51 +00:00
Dominik Picheta
4d7b3081a8 Fixes tests. 2017-01-03 18:02:55 +00:00
Dominik Picheta
2f45eab060 Fixes #301. 2017-01-03 17:43:59 +00:00
Dominik Picheta
040189dfca Define new symbol to support distros in older Nimble versions. 2017-01-03 17:06:18 +00:00
Dominik Picheta
1c982a7e5f Use display for external deps and document this new feature. 2017-01-03 16:56:44 +00:00
Dominik Picheta
8120ac02c8 Add foreign deps to nimble.nimble. 2017-01-03 14:41:08 +00:00
Andreas Rumpf
2bec00a9f3 Merge branch 'master' into native-pkg-support 2017-01-02 20:16:19 +01:00
Dominik Picheta
df640de6c8 Bump to 0.8.0 and fill out changelog for this release. 2017-01-01 17:47:14 +00:00
Dominik Picheta
d25c8e29d4 Prevent crash when symlink already exists in $nimbleDir/bin. 2017-01-01 17:14:12 +00:00
Dominik Picheta
d70526fa90 Improve error when fork can't be created for publishing.
References #284.
2017-01-01 16:34:03 +00:00
Dominik Picheta
6b175056df Fixes #236. 2017-01-01 15:57:06 +00:00
Dominik Picheta
db68bc575a Merge pull request #297 from luked99/master
Remove quotes around "test"
2016-12-31 12:37:41 +00:00
Araq
6368ccb0df Merge branch 'master' into native-pkg-support 2016-12-31 02:12:00 +01:00
Araq
ad21bb039f removed distros.nim; now part of Nim's stdlib 2016-12-31 02:09:14 +01:00
Luke Diamand
56f547f52e Remove quotes around "test"
The example for adding a test section uses quotes around the
test target, "test".

However, this doesn't work, as it's not a valid identifier and
nimble fails with:

    Error: identifier expected, but found '`"test" Task`'.

Remove the quotes in the example.
2016-12-30 18:43:29 +00:00
Dominik Picheta
850304ffcb Fixes #189. 2016-12-30 12:43:52 +00:00
Dominik Picheta
0c70b67a0a Merge pull request #296 from nigredo-tori/better-dump
Change the way `dump` locates the project file
2016-12-30 10:51:14 +00:00
Dmitry Polienko
871252d202 Update usage message 2016-12-30 15:06:20 +07:00
Dmitry Polienko
039bc0e1c1 Change dump to use fuzzy matching on argument 2016-12-30 15:06:20 +07:00
Dominik Picheta
3d4d751a48 Fixes #247. 2016-12-29 15:19:26 +00:00
Dominik Picheta
767aaf56d0 Improves readme and adds info about repo branches. 2016-12-29 14:31:05 +00:00
Dominik Picheta
f4bad0f18b Merge pull request #295 from nigredo-tori/fix-tags
Fix git ls-remote parsing
2016-12-29 13:04:21 +00:00
Dmitry Polienko
4332fb45cb Fix git ls-remote parsing 2016-12-29 17:57:13 +07:00
Dominik Picheta
ff82fc2536 Fixes #286. 2016-12-28 15:51:14 +00:00
Andreas Rumpf
26755c0183 hotfix: make nimble compile with latest compiler version 2016-12-28 00:08:05 +01:00
Dominik Picheta
736ed97974 Fixes tests once and for all. 2016-12-27 16:28:45 +00:00
Dominik Picheta
87f4ad106a Fix the tests correctly this time (hopefully). 2016-12-27 15:51:05 +00:00
Dominik Picheta
40e3abb911 Attempt to workaround Nim 0.15.2 problem for tests. 2016-12-27 15:42:01 +00:00
Dominik Picheta
53856db8ff Fixes tests. 2016-12-27 13:54:54 +00:00
Dominik Picheta
432c91b938 Renamed myVersion to version and version to specialVersion. 2016-12-27 13:27:08 +00:00
Dominik Picheta
2886059817 Fixes #289. 2016-12-27 12:19:13 +00:00
Dominik Picheta
d192de6511 Fixes version conflicts when building between special&non-special.
Refs #289. Aporia still cannot be installed. In addition,
the myVersion vs. version should be refactored into version vs.
specialVersion.
2016-12-27 00:42:05 +00:00
Dominik Picheta
8453a4e2de Add missing test files. 2016-12-26 23:59:51 +00:00
Dominik Picheta
95a29197ee Add tests for #144. 2016-12-26 23:03:01 +00:00
Dominik Picheta
f29a25a10d Implements #144. Warnings are now issued for incorrect namespacing. 2016-12-26 18:09:19 +00:00
Dominik Picheta
380cb46da8 Fixes tests on case sensitive file systems. 2016-12-23 19:15:32 +01:00
Dominik Picheta
1d0f28e321 Preserve special versions in package installations. Ref #88. 2016-12-23 19:09:54 +01:00
Dominik Picheta
2a42052099 Merge pull request #282 from yglukhov/fix-install-from-source
Fixed installation from git dir
2016-12-23 16:35:49 +01:00
Araq
e813aa6448 Merge branch 'master' into native-pkg-support 2016-12-23 16:01:37 +01:00
Araq
b3b4c6343f moved new feature to separate 'distros.nim' module 2016-12-23 16:01:10 +01:00
Araq
5fef5c577e make tests work on Windows again 2016-12-23 15:58:21 +01:00
Dominik Picheta
8d51fc4c2f Many additions/improvements to readme. Fixes #221. Fixes #246. 2016-12-23 12:42:08 +01:00
Dominik Picheta
87aab62762 Fixes ToC in readme. 2016-12-23 11:17:41 +01:00
Dominik Picheta
e457b54d09 Small output adjustment. 2016-12-22 23:12:41 +01:00
Dominik Picheta
8a16603ca7 Use inLines in tester more to fix failures. 2016-12-22 21:54:32 +01:00
Dominik Picheta
da3b38ff60 Fixes tests. 2016-12-22 21:43:39 +01:00
Dominik Picheta
36e7bfba19 Use the cli module everywhere. 2016-12-22 17:12:45 +01:00
Dominik Picheta
8f34336e91 Add hints for NimbleErrors which help the user fix them. 2016-12-22 16:49:24 +01:00
Dominik Picheta
c0f2bd03b1 More use of the cli module. 2016-12-22 16:30:24 +01:00
Dominik Picheta
a111d5f3b9 Implement promptCustom in cli and use it for init command. 2016-12-22 16:13:36 +01:00
Dominik Picheta
9b67f424e6 The path command no longer looks for .babel or .nims files. 2016-12-22 15:38:53 +01:00
Dominik Picheta
34af12a326 Use cli.display for path command. 2016-12-22 15:36:18 +01:00
Dominik Picheta
971dc300bb Usability fixes and output improvements for compile command. 2016-12-22 15:18:46 +01:00
Dominik Picheta
cb1248a9b4 Fix --nimbledir for c command and separate flags into actions. 2016-12-22 14:38:22 +01:00
Araq
f972236ed9 outline of how the support for native package managers should look like 2016-12-22 12:41:20 +01:00
Dominik Picheta
a04848060b Use cli for remove command messages. Improve prompt. 2016-12-22 11:29:48 +01:00
Dominik Picheta
22929add07 Spice up the prompts. 2016-12-21 22:56:39 +01:00
Dominik Picheta
9be27cd665 Display suppressed messages tip only when an error occurred. 2016-12-21 22:18:11 +01:00
Dominik Picheta
17ddbc3214 Add colons to non-verb categories in cli.display. 2016-12-21 22:11:47 +01:00
Dominik Picheta
87567161b8 All output from install command is now using the cli module. 2016-12-21 21:45:43 +01:00
Dominik Picheta
87d6f85aea Implement debug messages and wrap exec output in them. 2016-12-21 20:09:53 +01:00
Dominik Picheta
5fe69b389f Improvements to existing CLI messages. 2016-12-21 00:17:56 +01:00
Dominik Picheta
e3f833d61c More replacements of 'echo' to CLI's 'display'. 2016-12-21 00:08:42 +01:00
Dominik Picheta
60aa57be24 NimbleError is now captured in debug mode as well as release mode. 2016-12-20 23:40:26 +01:00
Dominik Picheta
c16c0b8864 Use CLI in packageparser. 2016-12-20 23:25:56 +01:00
Dominik Picheta
3791f8a09a Switch to cli in processDeps. 2016-12-20 22:46:07 +01:00
Dominik Picheta
dcc36814c2 Fixed tests. 2016-12-20 21:26:50 +01:00
Dominik Picheta
25a53eb4dc Raise an error if 'refresh' does not download anything successfully. 2016-12-20 21:14:00 +01:00
Dominik Picheta
effbfbc6a1 Refresh command now supports a package list name as argument. 2016-12-20 20:58:57 +01:00
Dominik Picheta
ac352aa6b2 Rename 'update' proc to 'refresh'. 2016-12-20 20:49:39 +01:00
Dominik Picheta
2dab010c23 Implement --verbosity switch. 2016-12-20 00:01:39 +01:00
Dominik Picheta
8ebc4bb7c1 Implement message suppression. 2016-12-19 23:49:41 +01:00
Dominik Picheta
dcbbb8b62e Switch 'refresh' command to new CLI module. 2016-12-19 23:35:27 +01:00
Dominik Picheta
abb9d79ea4 Parse command line args first so that -v doesn't read config. 2016-12-19 22:28:19 +01:00
Dominik Picheta
3c22ffd848 Implements a nimblepkg/cli module which formats output messages. 2016-12-19 21:42:02 +01:00
Dominik Picheta
a7913b5d23 Improves tester. 2016-12-15 23:35:09 +01:00
Dominik Picheta
d42a07e0f7 Fixes nimbledata.json being read from default nimbleDir. 2016-12-15 23:31:45 +01:00
Dominik Picheta
934c047068 Remove mentions of the old .nimble format from readme. Fixes #262. 2016-12-13 22:45:33 +01:00
Dominik Picheta
4276b7237d Merge pull request #273 from nim-lang/ARAQ-fix-warnings
fixes a 'break search loop' bug; got rid of most compiler warnings
2016-12-12 20:17:29 +01:00
Yuriy Glukhov
de59767f43 Fixed installation from git dir 2016-12-10 11:39:32 +02:00
Araq
82868ea818 get rid of unnecessary cyclic module dependency 2016-11-24 00:45:47 +01:00
Araq
b3f793ae0d get rid of unnecessary cyclic module dependency 2016-11-24 00:16:05 +01:00
Dominik Picheta
0d15faeef1 Merge pull request #279 from crohlfs/master
Fix issues with spaces in bin path
2016-11-21 18:53:32 +01:00
Chris Rohlfs
bcbb85c5e4 Fix 'compile' not working when spaces are in bin path 2016-11-22 01:41:40 +13:00
Chris Rohlfs
4fa4404df8 Fix install not working when spaces are in bin path 2016-11-21 21:14:28 +13:00
Dominik Picheta
e6057a0084 Fixes travis build.
Squashed commit of the following:

commit 8811389f5071831b4fd55fa0c978767d722a4ed0
Author: Dominik Picheta <dominikpicheta@gmail.com>
Date:   Tue Nov 15 21:24:03 2016 +0100

    Cleans up troibobasbfdb

commit 283e10b43517e1c03a277bdf0dd39ea017421d53
Author: Dominik Picheta <dominikpicheta@gmail.com>
Date:   Tue Nov 15 21:12:03 2016 +0100

    Too bad bash isn't style insensitive.

commit 3cbf069230306c2977fe37f811c803b9a55b4ccf
Author: Dominik Picheta <dominikpicheta@gmail.com>
Date:   Tue Nov 15 21:09:27 2016 +0100

    Attempt to add -lrt to linker flags.

commit 0f3cbfabd9f9b2cf8c9ba9709175384d2f440ffd
Author: Dominik Picheta <dominikpicheta@gmail.com>
Date:   Tue Nov 15 20:58:52 2016 +0100

    Print ldd version.

commit badc29b79d6cc4f0770c483ac63079b0a3e9bd6c
Author: Dominik Picheta <dominikpicheta@gmail.com>
Date:   Tue Nov 15 20:50:35 2016 +0100

    Attempt to debug.
2016-11-15 21:30:34 +01:00
Dominik Picheta
180653216c Bump to v0.7.11. 2016-11-11 11:40:40 +00:00
Dominik Picheta
8a1efc275e Fixes build (missing bootstrap.sh). Now builds against Nim 0.15.2. 2016-11-11 11:35:50 +00:00
Dominik Picheta
0ce718dea7 Merge pull request #275 from yglukhov/patch-2
Fixed #274
2016-11-11 11:27:29 +00:00
Yuriy Glukhov
68b1b67733 Fixed #274 2016-11-09 22:45:45 +02:00
Araq
ee3ad4353e fixes a 'break search loop' bug; got rid of most compiler warnings 2016-11-06 23:06:37 +01:00
Araq
a0670e9620 finish migration to new compiler API 2016-11-06 19:58:37 +01:00
Araq
ba46f95feb cleanup tester and introduce safeMoveFile for Windows 2016-11-06 19:58:20 +01:00
Andreas Rumpf
15db5da8a1 Merge pull request #272 from nim-lang/ARAQ-newcompilerapi3
make Nimble compile with the lastest version of the compiler API
2016-11-06 17:30:36 +01:00
Araq
417136e57e make Nimble compiler with the lastest version of the compiler API 2016-11-05 01:17:05 +01:00
Andreas Rumpf
58959f207e Merge pull request #269 from nim-lang/araq-compiler-api2
make Nimble compile with the changed compiler API
2016-11-01 00:27:33 +01:00
Araq
9c81e28343 Merge branch 'master' into araq-compiler-api2 2016-11-01 00:21:51 +01:00
Araq
4ce6f2b395 make Nimble compile with the changed compiler API 2016-10-31 20:40:21 +01:00
Dominik Picheta
06b942680b Remove spammy message about nimscriptapi.nim. 2016-10-26 21:14:18 +02:00
Dominik Picheta
d854bc64c8 Fix import inside nimble.nimble when Nimble is installed. 2016-10-26 20:16:35 +02:00
Dominik Picheta
2eec391732 Fixes getInstalledPkgs returning minimal package information.
This was introduced in commit 2cf03313fd.
2016-10-26 20:10:17 +02:00
119 changed files with 5904 additions and 1756 deletions

26
.gitignore vendored
View file

@ -6,9 +6,29 @@ 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
*.exe
*.dll
# VCC compiler and linker artifacts
*.ilk
*.pdb
# Editors and IDEs project files and folders
.vscode
# VCS artifacts
*.orig
# Test procedure artifacts
nimble_*.nims

View file

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

View file

@ -3,6 +3,265 @@
# 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
The first release of 2018! Another fairly big release containing 40 commits.
This release fixes many
issues, with most being fixed by our brilliant contributors. Thanks a lot
everyone!
One big new feature is the new support for multiple Nimble packages in a single
Git/Hg repository. You can now specify ``?subdir=<dir>`` at the end of your
repo's URL and Nimble will know to look in ``<dir>`` for your package.
* **Implemented support for multi-package repos.** See
[#421](https://github.com/nim-lang/nimble/issues/421) for the relevant issue.
* **Better error message when the user has an outdated stdlib version that confuses Nimble**
* **The validity of a Nimble package can now be checked using the new ``check`` command**
* Nimble no longer silently ignores an erroneous '@' in for example
``nimble install compiler@``.
* Issues with the ``nimble path`` command have been fixed.
* The ``nimble publish`` command has been improved and stabilised.
* Messages for the ``NIM_LIB_PREFIX`` env var have been improved.
* ``before install`` is now called when packages are installed by their name.
See [#280](https://github.com/nim-lang/nimble/issues/280).
* Fixed issue with ``nimble init``. See [#446](https://github.com/nim-lang/nimble/issues/446).
* Nimble now rejects [reserved names on Windows](https://github.com/nim-lang/nimble/commit/74856a87084b73451254555b2c20ad932cf84270).
* The ``NIMBLE_DIR`` environment variable is now supported, in addition to the
command line flag and config setting.
* The ``init`` command has been improved significantly.
----
Full changelog: https://github.com/nim-lang/nimble/compare/v0.8.8...v0.8.10
## 0.8.8 - 03/09/2017
This is a relatively big release containing 57 commits, with multiple new
features and many bug fixes.
* **Implemented the `develop` command.** See
[readme](https://github.com/nim-lang/nimble#nimble-develop) for details.
* **Implemented a default `test` task** for packages that don't define it.
* **Lowered the memory consumption** in cases where a package contained many files.
* Nimble now accepts .nimble symlinks.
* Locally stored package list files can now be specified in the Nimble config.
* Fixed branch checkout and handling of branch names with dashes.
* Improved URL detection in ``publish`` feature.
* Fixed many issues related to binary management. Packages are now resymlinked
when an newer version is removed.
([#331](https://github.com/nim-lang/nimble/issues/331))
* Fixed issues with CLI arg passing to the Nim compiler.
([#351](https://github.com/nim-lang/nimble/issues/351))
* Improved performance of ``list -i`` command.
* Fixed issue where warnings weren't suppressed for some commands.
([#290](https://github.com/nim-lang/nimble/issues/290))
* Special versions other than `#head` are no longer considered to be newest.
* Improves the reverse dependency lookup by cross checking it with the
installed list of packages.
([#287](https://github.com/nim-lang/nimble/issues/287))
----
Full changelog: https://github.com/nim-lang/nimble/compare/v0.8.6...v0.8.8
## 0.8.6 - 05/05/2017
Yet another point release which includes various bug fixes and improvements.
* Improves heuristic for finding Nim standard library to support choosenim
installations and adds ability to override it via ``NIM_LIB_PREFIX``
environment variable.
* Implement ``--noColor`` option to remove color from the output.
* Fixes bug when ``srcDir`` contains trailing slash.
* Fixes failure when ``-d`` flag is passed to ``c`` command.
* Show raw output for certain commands.
* GitHub API token can now be specified via the ``NIMBLE_GITHUB_API_TOKEN``
environment variable.
* GitHub API token is now stored in ``~/.nimble/api_token`` so that it
doesn't need to be specified each time.
* Fixes multiple flags not being passed in Nimble task.
----
Full changelog: https://github.com/nim-lang/nimble/compare/v0.8.4...v0.8.6
## 0.8.4 - 29/01/2017
Another bug fix release which resolves problems related to stale nimscriptapi
files in /tmp/, no compilation output when ``nimble build`` fails, and issues
with the new package validation on Windows.
----
Full changelog: https://github.com/nim-lang/nimble/compare/v0.8.2...v0.8.4
## 0.8.2 - 08/01/2017
This is a small bug fix release which resolves problems with the installation
of Aporia (and likely other Nimble packages).
## 0.8.0 - 05/01/2017
This is a large release containing multiple new features and many bug fixes.
* Implemented a completely new output system.
* Supports different message types and priorities. Each is differently
encoded using a color and a brightness.
* The amount of messages shown can be changed by using the new ``--verbose``
and ``--debug`` flags, by default only high priority messages are shown.
* Duplicate warnings are filtered out to prevent too much noise.
* Package namespaces are now validated. You will see a warning whenever an
incorrectly namespaced package is read by Nimble, this can occur either
during installation or when the installed package database is being loaded.
The namespacing rules are described in Nimble's
[readme](https://github.com/nim-lang/nimble#libraries).
**Consider these warnings to be unstable, if you see something that you
think is incorrect please report it**.
* Special version dependencies are now installed into a directory with that
special version in its name. For example, ``compiler@#head`` will be installed
into ``~/.nimble/pkgs/compiler-#head``. This reduces the amount of redundant
installs. See [#88](https://github.com/nim-lang/nimble/issues/88) for
more information.
* External dependencies can now be specified in .nimble files. Nimble doesn't
install these, but does instruct the user on how they can be installed.
More information about this feature can be found in the
[readme](https://github.com/nim-lang/nimble#external-dependencies).
* Nimble now supports package aliases in the packages.json files.
* Fixed regression that caused transitive dependencies to not be installed.
* Fixed problem with ``install`` command when a ``src`` directory is present
in the current directory.
* Improved quoting of process execution arguments.
* Many improvements to custom ``--nimbleDir`` handling. All commands should now
support it correctly.
* Running ``nimble -v`` will no longer read the Nimble config before displaying
the version.
* Refresh command now supports a package list name as argument.
* Fixes issues with symlinks not being removed correctly.
* Changed the way the ``dump`` command locates the .nimble file.
----
Full changelog: https://github.com/nim-lang/nimble/compare/v0.7.10...v0.8.0
Full list of issues which have been closed: https://github.com/nim-lang/nimble/issues?utf8=%E2%9C%93&q=is%3Aissue+closed%3A%222016-10-09+..+2017-01-05%22+
## 0.7.10 - 09/10/2016 ## 0.7.10 - 09/10/2016
This release includes multiple bug fixes. This release includes multiple bug fixes.

View file

@ -1,18 +1,25 @@
import src/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"
task tests, "Run the Nimble tester!": when defined(nimdistros):
import distros
if detectOs(Ubuntu):
foreignDep "libssl-dev"
else:
foreignDep "openssl"
task test, "Run the Nimble tester!":
withDir "tests": withDir "tests":
exec "nim c -r tester" exec "nim c -r tester"

51
nimble.zsh-completion Normal file
View file

@ -0,0 +1,51 @@
#compdef nimble
_nimble() {
local line
_arguments -C \
'1: :(install init publish uninstall build c cc js doc doc2 refresh search list tasks path dump develop)' \
'*::options:->options' \
'(--version)--version[show version]' \
'(--help)--help[show help]' \
'(-)--help[display help information]' \
'(-)--version[display version information]' \
'(-y --accept)'{-y,--accept}'[accept all interactive prompts]' \
{-n,--reject}'[reject all interactive prompts]' \
'--ver[Query remote server for package version information when searching or listing packages]' \
'--nimbleDir dirname[Set the Nimble directory]' \
'(-d --depsOnly)'{-d,--depsOnly}'[Install only dependencies]'
if [ $#line -eq 0 ]; then
# if the command line is empty and "nimble tasks" is successfull, add custom tasks
tasks=$(nimble tasks)
if [ $? -eq 0 ]; then
compadd - $(echo $tasks | cut -f1 -d" " | tr '\n' ' ')
fi
fi
case $line[1] in
install)
_nimble_installable_packages
;;
uninstall|path|dump)
_nimble_installed_packages
;;
init|publish|build|refresh|search|tasks)
(( ret )) && _message 'no more arguments'
;;
*)
(( ret )) && _message 'no more arguments'
;;
esac
}
function _nimble_installable_packages {
compadd - $(nimble list 2> /dev/null | grep -v '^ ' | tr -d ':')
}
function _nimble_installed_packages {
compadd - $(nimble list -i 2> /dev/null | grep ']$' | cut -d' ' -f1)
}
_nimble "$@"

View file

@ -1,7 +1,7 @@
# Nimble [![Build Status](https://travis-ci.org/nim-lang/nimble.svg?branch=master)](https://travis-ci.org/nim-lang/nimble) # Nimble [![Build Status](https://travis-ci.org/nim-lang/nimble.svg?branch=master)](https://travis-ci.org/nim-lang/nimble)
Nimble is a *beta*-grade *package manager* for the [Nim programming Nimble is a *beta*-grade *package manager* for the [Nim programming
language](http://nim-lang.org). language](https://nim-lang.org).
Interested in learning **how to create a package**? Skip directly to that section Interested in learning **how to create a package**? Skip directly to that section
[here](#creating-packages). [here](#creating-packages).
@ -10,12 +10,12 @@ Interested in learning **how to create a package**? Skip directly to that sectio
- [Requirements](#requirements) - [Requirements](#requirements)
- [Installation](#installation) - [Installation](#installation)
- [Nimble's folder structure and packages](#nimbles-folder-structure-and-packages)
- [Nimble usage](#nimble-usage) - [Nimble usage](#nimble-usage)
- [nimble refresh](#nimble-refresh) - [nimble refresh](#nimble-refresh)
- [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)
@ -26,14 +26,17 @@ Interested in learning **how to create a package**? Skip directly to that sectio
- [nimble dump](#nimble-dump) - [nimble dump](#nimble-dump)
- [Configuration](#configuration) - [Configuration](#configuration)
- [Creating Packages](#creating-packages) - [Creating Packages](#creating-packages)
- [The new NimScript format](#the-new-nimscript-format) - [Project structure](#project-structure)
- [Tests](#tests)
- [Libraries](#libraries) - [Libraries](#libraries)
- [Binary packages](#binary-packages) - [Binary packages](#binary-packages)
- [Hybrids](#hybrids) - [Hybrids](#hybrids)
- [Dependencies](#dependencies) - [Dependencies](#dependencies)
- [External dependencies](#external-dependencies)
- [Nim compiler](#nim-compiler) - [Nim compiler](#nim-compiler)
- [Versions](#versions) - [Versions](#versions)
- [Submitting your package to the package list.](#submitting-your-package-to-the-package-list) - [Releasing a new version](#releasing-a-new-version)
- [Publishing packages](#publishing-packages)
- [.nimble reference](#nimble-reference) - [.nimble reference](#nimble-reference)
- [[Package]](#package) - [[Package]](#package)
- [Required](#required) - [Required](#required)
@ -41,28 +44,29 @@ Interested in learning **how to create a package**? Skip directly to that sectio
- [[Deps]/[Dependencies]](#depsdependencies) - [[Deps]/[Dependencies]](#depsdependencies)
- [Optional](#optional) - [Optional](#optional)
- [Troubleshooting](#troubleshooting) - [Troubleshooting](#troubleshooting)
- [Nimble's folder structure and packages](#nimbles-folder-structure-and-packages)
- [Repository information](#repository-information)
- [Contribution](#contribution) - [Contribution](#contribution)
- [About](#about) - [About](#about)
## Requirements ## Requirements
Nimble has some runtime dependencies on external tools, these tools are Nimble has some runtime dependencies on external tools, these tools are used to
used to download Nimble packages. download Nimble packages. For instance, if a package is hosted on
For [GitHub](https://github.com), you need to have [git](https://www.git-scm.com)
instance, if a package is hosted on [Github](https://github.com) you require to installed and added to your environment ``PATH``. Same goes for
have [git](http://www.git-scm.com) installed and added to your environment [Mercurial](http://mercurial.selenic.com) repositories on
``PATH``. Same goes for [Mercurial](http://mercurial.selenic.com) repositories [Bitbucket](https://bitbucket.org). Nimble packages are typically hosted in Git
on [Bitbucket](https://bitbucket.org). Nimble packages are typically hosted in repositories so you may be able to get away without installing Mercurial.
Git repositories so you may be able to get away without installing Mercurial.
**Warning:** Ensure that you have a fairly recent version of Git installed. **Warning:** Ensure that you have a fairly recent version of Git installed.
If the version is less recent than 1.9.0 then Nimble may have trouble using it. If the version is older than 1.9.0, then Nimble may have trouble using it.
See [this issue](https://github.com/nim-lang/nimble/issues/105) for more See [this issue](https://github.com/nim-lang/nimble/issues/105) for more
info. information.
## Installation ## Installation
Nimble is now bundled with [Nim](http://nim-lang.org) Nimble is now bundled with [Nim](https://nim-lang.org)
(since Nim version 0.15.0). (since Nim version 0.15.0).
This means that you should have Nimble installed already, as long as you have This means that you should have Nimble installed already, as long as you have
the latest version of Nim installed as well. Because of this **you likely do the latest version of Nim installed as well. Because of this **you likely do
@ -71,48 +75,36 @@ 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
``install_nimble.nims`` script included in the Nim distribution and itself.
[repository](https://github.com/nim-lang/Nim/blob/devel/install_nimble.nims).
Simply execute this to install Nimble. ### Using koch
The ``koch`` tool is included in the Nim distribution and
[repository](https://github.com/nim-lang/Nim/blob/devel/koch.nim).
Simply navigate to the location of your Nim installation and execute the
following command to compile and install Nimble.
``` ```
nim e install_nimble.nims ./koch 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's folder structure and packages
Nimble stores everything that has been installed in ``~/.nimble`` on Unix systems
and in your ``$home/.nimble`` on Windows. Libraries are stored in
``$nimbleDir/pkgs``, and binaries are stored in ``$nimbleDir/bin``. Most Nimble
packages will provide ``.nim`` files and some documentation. The Nim
compiler is aware of Nimble and will automatically find the modules so you can
``import modulename`` and have that working without additional setup.
However, some Nimble packages can provide additional tools or commands. If you
don't add their location (``$nimbleDir/bin``) to your ``$PATH`` they will not
work properly and you won't be able to run them.
## Nimble usage ## Nimble usage
@ -139,6 +131,20 @@ a third-party package list.
Package lists can be specified in Nimble's config. Take a look at the Package lists can be specified in Nimble's config. Take a look at the
config section below to see how to do this. config section below to see how to do this.
### nimble check
The ``check`` command will read your package's .nimble file. It will then
verify that the package's structure is valid.
Example:
$ nimble check
Error: Package 'x' has an incorrect structure. It should contain a single directory hierarchy for source files, named 'x', but file 'foobar.nim' is in a directory named 'incorrect' instead. This will be an error in the future.
Hint: If 'incorrect' contains source files for building 'x', rename it to 'x'. Otherwise, prevent its installation by adding `skipDirs = @["incorrect"]` to the .nimble file.
Failure: Validation failed
### nimble install ### nimble install
The ``install`` command will download and install a package. You need to pass The ``install`` command will download and install a package. You need to pass
@ -153,9 +159,9 @@ Example:
nake installed successfully nake installed successfully
Nimble always fetches and installs the latest version of a package. Note that Nimble always fetches and installs the latest version of a package. Note that
latest version is defined as the latest tagged version in the git (or hg) latest version is defined as the latest tagged version in the Git (or Mercurial)
repository, if the package has no tagged versions then the latest commit in the repository, if the package has no tagged versions then the latest commit in the
remote repository will be installed. If you already have that version installed remote repository will be installed. If you already have that version installed,
Nimble will ask you whether you wish it to overwrite your local copy. Nimble will ask you whether you wish it to overwrite your local copy.
You can force Nimble to download the latest commit from the package's repo, for You can force Nimble to download the latest commit from the package's repo, for
@ -163,15 +169,16 @@ example:
$ nimble install nimgame@#head $ nimble install nimgame@#head
This is of course git specific, for hg 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
@ -179,14 +186,46 @@ the current working directory. This can be useful for developers who are testing
locally their ``.nimble`` files before submitting them to the official package locally their ``.nimble`` files before submitting them to the official package
list. See the [Creating Packages](#creating-packages) section for more info on this. list. See the [Creating Packages](#creating-packages) section for more info on this.
A URL to a repository can also be specified, Nimble will automatically detect #### Package URLs
the type of the repository that the url points to and install it.
A valid URL to a Git or Merurial repository can also be specified, Nimble will
automatically detect the type of the repository that the url points to and
install it.
For repositories containing the Nimble package in a subdirectory, you can
instruct Nimble about the location of your package using the ``?subdir=<path>``
query parameter. For example:
$ nimble install https://github.com/nimble-test/multi?subdir=alpha
### nimble develop
The ``develop`` command allows you to link an existing copy of a package into
your installation directory. This is so that when developing a package you
don't need to keep reinstalling it for every single change.
$ cd ~/projects/jester
$ nimble develop
Any packages depending on ``jester`` will now use the code in
``~/projects/jester``.
If you specify a package name to this command, Nimble will clone it into the
current working directory.
$ nimble develop jester
The ``jester`` package will be cloned into ``./jester`` and it will be linked
to your installation directory.
Just as with the ``install`` command, a package URL may also be specified
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:
@ -195,9 +234,17 @@ Similar to the ``install`` command you can specify a version range, for example:
### nimble build ### nimble build
The ``build`` command is mostly used by developers who want to test building The ``build`` command is mostly used by developers who want to test building
their ``.nimble`` package. This command will build the package in debug mode, their ``.nimble`` package. This command will build the package with default
without installing anything. The ``install`` command will build the package flags, i.e. a debug build which includes stack traces but no GDB debug
in release mode instead. information. The ``install`` command will build the package in release mode
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
@ -213,7 +260,7 @@ the command ``c`` or ``compile`` is specified. The more specific ``js``, ``cc``,
The ``list`` command will display the known list of packages available for The ``list`` command will display the known list of packages available for
Nimble. An optional ``--ver`` parameter can be specified to tell Nimble to Nimble. An optional ``--ver`` parameter can be specified to tell Nimble to
query remote git repositories for the list of versions of the packages and to query remote Git repositories for the list of versions of the packages and to
then print the versions. Please note however that this can be slow as each then print the versions. Please note however that this can be slow as each
package must be queried separately. package must be queried separately.
@ -241,8 +288,8 @@ substrings). Example:
Searches are case insensitive. Searches are case insensitive.
An optional ``--ver`` parameter can be specified to tell Nimble to An optional ``--ver`` parameter can be specified to tell Nimble to
query remote git repositories for the list of versions of the packages and to query remote Git repositories for the list of versions of the packages and
then print the versions. Please note however that this can be slow as each then print the versions. However, please note that this can be slow as each
package must be queried separately. package must be queried separately.
### nimble path ### nimble path
@ -266,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.
@ -276,11 +323,11 @@ 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
For a nimble package in the current working directory, list the tasks which that For a Nimble package in the current working directory, list the tasks which that
package defines. This is only supported for packages utilising the new package defines. This is only supported for packages utilising the new
nimscript .nimble files. nimscript .nimble files.
@ -305,22 +352,27 @@ nimbleDir = r"C:\Nimble\"
[PackageList] [PackageList]
name = "CustomPackages" name = "CustomPackages"
url = "http://mydomain.org/packages.json" url = "http://mydomain.org/packages.json"
[PackageList]
name = "Local project packages"
path = r"C:\Projects\Nim\packages.json"
``` ```
You can currently configure the following in this file: You can currently configure the following in this file:
* ``nimbleDir`` - The directory which nimble uses for package installation. * ``nimbleDir`` - The directory which Nimble uses for package installation.
**Default:** ``~/.nimble/`` **Default:** ``~/.nimble/``
* ``chcp`` - Whether to change the current code page when executing Nim * ``chcp`` - Whether to change the current code page when executing Nim
application packages. If ``true`` this will add ``chcp 65001`` to the application packages. If ``true`` this will add ``chcp 65001`` to the
.cmd stubs generated in ``~/.nimble/bin/``. .cmd stubs generated in ``~/.nimble/bin/``.
**Default:** ``true`` **Default:** ``true``
* ``[PackageList]`` + ``name`` + ``url`` - You can use this section to specify * ``[PackageList]`` + ``name`` + (``url``|``path``) - You can use this section to specify
a new custom package list. Multiple package lists can be specified. Nimble a new custom package list. Multiple package lists can be specified. Nimble
defaults to the "Official" package list, you can override it by specifying defaults to the "Official" package list, you can override it by specifying
a ``[PackageList]`` section named "official". Multiple URLs can be specified a ``[PackageList]`` section named "official". Multiple URLs can be specified
under each section, Nimble will try each in succession if under each section, Nimble will try each in succession if
downloading from the first fails. downloading from the first fails. Alternately, ``path`` can specify a
local file path to copy a package list .json file from.
* ``cloneUsingHttps`` - Whether to replace any ``git://`` inside URLs with * ``cloneUsingHttps`` - Whether to replace any ``git://`` inside URLs with
``https://``. ``https://``.
**Default: true** **Default: true**
@ -328,69 +380,32 @@ You can currently configure the following in this file:
Nimble will also attempt to read the ``http_proxy`` and ``https_proxy`` Nimble will also attempt to read the ``http_proxy`` and ``https_proxy``
environment variables. environment variables.
**Default: ""** **Default: ""**
* ``nimLibPrefix`` - Specifies the Nim standard library prefix to help Nimble
find the Nim standard library.
**Default: ""**
## Creating Packages ## Creating Packages
Nimble works on git repositories as its primary source of packages. Its list of Nimble works on Git repositories as its primary source of packages. Its list of
packages is stored in a JSON file which is freely accessible in the packages is stored in a JSON file which is freely accessible in the
[nim-lang/packages repository](https://github.com/nim-lang/packages). [nim-lang/packages repository](https://github.com/nim-lang/packages).
This JSON file provides nimble with the required Git URL to clone the package This JSON file provides Nimble with the required Git URL to clone the package
and install it. Installation and build instructions are contained inside a and install it. Installation and build instructions are contained inside a
ini-style file with the ``.nimble`` file extension. The nimble file shares the file with the ``.nimble`` file extension. The Nimble file shares the
package's name, i.e. a package package's name, i.e. a package
named "foobar" should have a corresponding ``foobar.nimble`` file. named "foobar" should have a corresponding ``foobar.nimble`` file.
These files specify information about the package including its name, author, These files specify information about the package including its author,
license, dependencies and more. Without one Nimble is not able to install license, dependencies and more. Without one, Nimble is not able to install
a package. 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.
**Note:** As of version 0.7.0, the ``init`` command generates .nimble files
using the new NimScript format. Take a look at the next section for more
information.
A bare minimum .nimble file follows: A bare minimum .nimble file follows:
```ini ```ini
[Package]
name = "ProjectName"
version = "0.1.0"
author = "Your Name"
description = "Example .nimble file."
license = "MIT"
[Deps]
Requires: "nim >= 0.10.0"
```
You may omit the dependencies entirely, but specifying the lowest version
of the Nim compiler required is recommended.
Nimble currently supports installation of packages from a local directory, a
git repository and a mercurial repository. The .nimble file must be present in
the root of the directory or repository being installed.
### The new NimScript format
**Warning:** This feature is still very experimental. You are encouraged to
try it, but be aware that it may change significantly in the future or
may even be removed completely!
Version 0.7.0 of Nimble introduces support for evaluating .nimble files
as Nim code. This means that you can define metadata about your package
using the Nim programming language.
Because of Nim's flexibility the definitions remain declarative. With the added
ability of using the Nim language to enrich your package specification.
For example, you can define dependencies for specific platforms using Nim's
``when`` statement.
The ini example above looks like this in the NimScript format:
```nim
# Package # Package
version = "0.1.0" version = "0.1.0"
@ -403,7 +418,30 @@ license = "MIT"
requires "nim >= 0.10.0" requires "nim >= 0.10.0"
``` ```
The format is indeed very similar to the ini format. Another great feature You may omit the dependencies entirely, but specifying the lowest version
of the Nim compiler required is recommended.
You can also specify multiple dependencies like so:
```
# Deps
requires "nim >= 0.10.0", "foobar >= 0.1.0"
requires "fizzbuzz >= 1.0"
requires "https://github.com/user/pkg#5a54b5e"
```
Nimble currently supports installation of packages from a local directory, a
Git repository and a mercurial repository. The .nimble file must be present in
the root of the directory or repository being installed.
The .nimble file is very flexible because it is interpreted using NimScript.
Because of Nim's flexibility the definitions remain declarative. With the added
ability of using the Nim language to enrich your package specification.
For example, you can define dependencies for specific platforms using Nim's
``when`` statement.
Another great feature
is the ability to define custom Nimble package-specific commands. These are is the ability to define custom Nimble package-specific commands. These are
defined in the .nimble files of course. defined in the .nimble files of course.
@ -422,7 +460,7 @@ Hello World!
You can place any Nim code inside these tasks. As long as that code does not You can place any Nim code inside these tasks. As long as that code does not
access the FFI. The ``nimscript`` access the FFI. The ``nimscript``
[module](http://nim-lang.org/docs/nimscript.html) in Nim's standard library defines [module](https://nim-lang.org/docs/nimscript.html) in Nim's standard library defines
additional functionality such as the ability to execute external processes additional functionality such as the ability to execute external processes
which makes this feature very powerful. which makes this feature very powerful.
@ -455,33 +493,135 @@ also return ``false`` from these blocks to stop further execution.
The ``nimscriptapi.nim`` module specifies this and includes other definitions The ``nimscriptapi.nim`` module specifies this and includes other definitions
which are also useful. Take a look at it for more information. which are also useful. Take a look at it for more information.
### Project structure
For a package named "foobar", the recommended project structure is the following:
```
. # The root directory of the project
├── LICENSE
├── README.md
├── foobar.nimble # The project .nimble file
└── src
└── foobar.nim # Imported via `import foobar`
└── tests # Contains the tests
├── config.nims
├── tfoo1.nim # First test
└── tfoo2.nim # Second test
```
Note that the .nimble file needs to be in the project's root directory. This
directory structure will be created if you run ``nimble init`` inside a
``foobar`` directory.
**Warning:** When source files are placed in a ``src`` directory, the
.nimble file must contain a ``srcDir = "src"`` directive. The ``nimble init``
command takes care of that for you.
When introducing more modules into your package, you should place them in a
separate directory named ``foobar`` (i.e. your package's name). For example:
```
. # The root directory of the project
├── ...
├── foobar.nimble # The project .nimble file
├── src
│ ├── foobar
│ │ ├── utils.nim # Imported via `import foobar/utils`
│ │ └── common.nim # Imported via `import foobar/common`
│ └── foobar.nim # Imported via `import foobar`
└── ...
```
#### Private modules
You may wish to hide certain modules in your package from the users. Create a
``private`` directory for that purpose. For example:
```
. # The root directory of the project
├── ...
├── foobar.nimble # The project .nimble file
├── src
│ ├── foobar
│ │ ├── private
│ │ │ └── hidden.nim # Imported via `import foobar/private/hidden`
│ │ ├── utils.nim # Imported via `import foobar/utils`
│ │ └── common.nim # Imported via `import foobar/common`
│ └── foobar.nim # Imported via `import foobar`
└── ...
```
#### Tests
A common problem that arises with tests is the fact that they need to import
the associated package. But the package is in the parent directory. This can
be solved in a few different ways:
* Expect that the package has been installed locally into your
``~/.nimble`` directory.
* Use a simple path modification to resolve the package properly.
The latter is highly recommended. Reinstalling the package to test an actively
changing code base is a massive pain.
To modify the path for your tests only, simply add a ``nim.cfg`` file into
your ``tests`` directory with the following contents:
```
--path:"../src/"
```
Nimble offers a pre-defined ``test`` task which compiles and runs all files
in the ``tests`` directory beginning with 't' in their filename.
You may wish to override this ``test`` task in your ``.nimble`` file. This
is particularly useful when you have a single test suite program. Just add
the following to your ``.nimble`` file to override the default ``test`` task.
```nim
task test, "Runs the test suite":
exec "nim c -r tests/tester"
```
Running ``nimble test`` will now use the ``test`` task you have defined.
### Libraries ### Libraries
Library packages are likely the most popular form of Nimble packages. They are Library packages are likely the most popular form of Nimble packages. They are
meant to be used by other library packages or the ultimate binary packages. meant to be used by other library or binary packages.
When nimble installs a library it will copy all the files in the package When Nimble installs a library, it will copy all of its files
into ``$nimbleDir/pkgs/pkgname-ver``. It's up to the package creator to make sure into ``$nimbleDir/pkgs/pkgname-ver``. It's up to the package creator to make sure
that the package directory layout is correct, this is so that users of the that the package directory layout is correct, this is so that users of the
package can correctly import the package. package can correctly import the package.
By convention, it is suggested that the layout be as follows. The directory It is suggested that the layout be as follows. The directory layout is
layout is determined by the nature of your package, that is, whether your determined by the nature of your package, that is, whether your package exposes
package exposes only one module or multiple modules. only one module or multiple modules.
If your package exposes only a single module, then that module should be If your package exposes only a single module, then that module should be
present in the root directory (the directory with the .nimble file) of your git present in the root directory (the directory with the .nimble file) of your Git
repository, it is recommended that in this case you name that module whatever repository, and should be named whatever your package's name is. A good example
your package's name is. A good example of this is the of this is the [jester](https://github.com/dom96/jester) package which exposes
[jester](https://github.com/dom96/jester) package which exposes the ``jester`` the ``jester`` module. In this case the jester package is imported with
module. In this case the jester package is imported with ``import jester``. ``import jester``.
If your package exposes multiple modules then the modules should be in a If your package exposes multiple modules then the modules should be in a
``PackageName`` directory. This will allow for a certain measure of isolation ``PackageName`` directory. This will allow for a certain measure of isolation
from other packages which expose modules with the same names. In this case from other packages which expose modules with the same names. In this case
the package's modules will be imported with ``import PackageName/module``. the package's modules will be imported with ``import PackageName/module``.
You are free to combine the two approaches described. Here's a simple example multi-module library package called `kool`:
```
.
├── kool
│   ├── useful.nim
│   └── also_useful.nim
└── kool.nimble
```
In regards to modules which you do **not** wish to be exposed. You should place In regards to modules which you do **not** wish to be exposed. You should place
them in a ``PackageName/private`` directory. Your modules may then import these them in a ``PackageName/private`` directory. Your modules may then import these
@ -490,9 +630,9 @@ structure may be enforced in the future.
All files and folders in the directory of where the .nimble file resides will be All files and folders in the directory of where the .nimble file resides will be
copied as-is, you can however skip some directories or files by setting copied as-is, you can however skip some directories or files by setting
the ``SkipDirs``, ``SkipFiles`` or ``SkipExt`` options in your .nimble file. the ``skipDirs``, ``skipFiles`` or ``skipExt`` options in your .nimble file.
Directories and files can also be specified on a *whitelist* basis, if you Directories and files can also be specified on a *whitelist* basis, if you
specify either of ``InstallDirs``, ``InstallFiles`` or ``InstallExt`` then specify either of ``installDirs``, ``installFiles`` or ``installExt`` then
Nimble will **only** install the files specified. Nimble will **only** install the files specified.
### Binary packages ### Binary packages
@ -502,68 +642,109 @@ A package is automatically a binary package as soon as it sets at least one
``bin`` value, like so: ``bin`` value, like so:
```ini ```ini
bin = "main" # NimScript config expects a seq instead, e.g. @["main"] bin = @["main"]
``` ```
In this case when ``nimble install`` is invoked, nimble will build the ``main.nim`` In this case when ``nimble install`` is invoked, Nimble will build the ``main.nim``
file, copy it into ``$nimbleDir/pkgs/pkgname-ver/`` and subsequently create a file, copy it into ``$nimbleDir/pkgs/pkgname-ver/`` and subsequently create a
symlink to the binary in ``$nimbleDir/bin/``. On Windows a stub .bat file is symlink to the binary in ``$nimbleDir/bin/``. On Windows a stub .cmd file is
created instead. created instead.
Other files will be copied in the same way as they are for library packages. Other files will be copied in the same way as they are for library packages.
Binary packages should not install .nim files so you should include Binary packages should not install .nim files so include ``skipExt = @["nim"]``
``SkipExt = "nim"`` in your .nimble file, unless you intend for your package to in your .nimble file, unless you intend for your package to be a binary/library
be a binary/library combo which is fine. combo.
Dependencies are automatically installed before building. Before publishing your Dependencies are automatically installed before building.
package you should ensure that the dependencies you specified are correct. It's a good idea to test that the dependencies you specified are correct by
You can do this 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
One thing to note about library and binary package hybrids is that your binary One thing to note about binary packages that contain source files aside from
may share the name of the package. This will mean that you will the one(s) specified in `bin` (or that also expose multiple library modules, as
not be able to put your .nim files in a ``pkgname`` directory. The reason you above) is that a binary may share the name of the package: this will mean
will not be able to do this is because binaries on some operating systems that you will not be able to put your additional .nim files in a ``pkgname``
do not have an extension so they will clash with a directory of the same name. directory. The reason for this is that binaries on some operating systems do
not have an extension, so they will clash with a directory of the same name.
The current If this is the case, you should place your additional .nim files in a directory
convention to get around this problem is to append ``pkg`` to the name as is with `pkg` appended after the name of the project. For instance, if you were
done for nimble. building a binary named `project`, you would put any additional source files in
a directory called `projectpkg`. From within project.nim you would then import
those modules namespaced with `projectpkg/`.
### Dependencies ### Dependencies
Dependencies are specified under the ``[Deps]`` section in a nimble file. Dependencies are specified using the ``requires`` function. For example:
The ``requires`` key field is used to specify them. For example:
```ini ```
[Deps] # Dependencies
Requires: "nim >= 0.10.0, jester > 0.1 & <= 0.5" requires "nim >= 0.10.0", "jester > 0.1 & <= 0.5"
``` ```
Dependency lists support version ranges. These versions may either be a concrete Dependency lists support version ranges. These versions may either be a concrete
version like ``0.1``, or they may contain any of the less-than (``<``), version like ``0.1``, or they may contain any of the less-than (``<``),
greater-than (``>``), less-than-or-equal-to (``<=``) and greater-than-or-equal-to greater-than (``>``), less-than-or-equal-to (``<=``) and greater-than-or-equal-to
(``>=``). Two version ranges may be combined using the ``&`` operator for example: (``>=``) operators.
``> 0.2 & < 1.0`` which will install a package with the version greater than 0.2 Two version ranges may be combined using the ``&`` operator, for example
``> 0.2 & < 1.0``, which will install a package with the version greater than 0.2
and less than 1.0. and less than 1.0.
Specifying a concrete version as a dependency is not a good idea because your Specifying a concrete version as a dependency is not a good idea because your
package may end up depending on two different versions of the same package. package may end up depending on two different versions of the same package.
If this happens Nimble will refuse to install the package. Similarly you should If this happens, Nimble will refuse to install the package.
not specify an upper-bound as this can lead to a similar issue.
In addition to versions you may also specify git/hg tags, branches and commits. In addition to versions you may also specify Git/Mercurial tags, branches and commits.
These have to be concrete however. This is done with the ``#`` character, Although these have to be specific; ranges of commits are not supported.
This is done with the ``#`` character,
for example: ``jester#head``. Which will make your package depend on the for example: ``jester#head``. Which will make your package depend on the
latest commit of Jester. latest commit of Jester.
#### External dependencies
**Warning:** This feature is brand new in Nimble v0.8.0. Breaking changes
related to it are more likely to be introduced than for any other Nimble
features.
Starting with Nimble v0.8.0, you can now specify external dependencies. These
are dependencies which are not managed by Nimble and can only be installed via
your system's package manager or downloaded manually via the internet.
As an example, to specify a dependency on openssl you may put this in your
.nimble file:
```nim
when defined(nimdistros):
import distros
if detectOs(Ubuntu):
foreignDep "libssl-dev"
else:
foreignDep "openssl"
```
The ``when`` branch is important to support installation using older versions
of Nimble.
The [distros module](https://nim-lang.org/docs/distros.html) in Nim's
standard library contains a list of all the supported Operating Systems and
Linux distributions.
With this inside your .nimble file, Nimble will output the following after
installing your package (on macOS):
```
Hint: This package requires some external dependencies.
Hint: To install them you may be able to run:
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.
@ -573,18 +754,39 @@ It resolves the dependencies and feeds the path of each package to
the compiler so that it knows precisely which version to use. the compiler so that it knows precisely which version to use.
This means that you can safely compile using the compiler when developing your 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
repository's *tags*. repository's *tags*.
When installing a package which needs to be downloaded, after the download is When installing a package which needs to be downloaded, after the download is
complete and if the package is distributed through a VCS, nimble will check the complete and if the package is distributed through a VCS, Nimble will check the
cloned repository's tags list. If no tags exist, nimble will simply install the cloned repository's tags list. If no tags exist, Nimble will simply install the
HEAD (or tip in mercurial) of the repository. If tags exist, nimble will attempt HEAD (or tip in Mercurial) of the repository. If tags exist, Nimble will attempt
to look for tags which resemble versions (e.g. v0.1) and will then find the to look for tags which resemble versions (e.g. v0.1) and will then find the
latest version out of the available tags, once it does so it will install the latest version out of the available tags, once it does so it will install the
package after checking out the latest version. package after checking out the latest version.
@ -592,11 +794,56 @@ package after checking out the latest version.
You can force the installation of the HEAD of the repository by specifying You can force the installation of the HEAD of the repository by specifying
``#head`` after the package name in your dependency list. ``#head`` after the package name in your dependency list.
## Submitting your package to the package list. #### Releasing a new version
Nimble's packages list is stored on github and everyone is encouraged to add Version releases are done by creating a tag in your Git or Mercurial
their own packages to it! Take a look at repository.
[nim-lang/packages](https://github.com/nim-lang/packages) to learn more.
Whenever you want to release a new version, you should remember to first
increment the version in your ``.nimble`` file and commit your changes. Only
after that is done should you tag the release.
To summarise, the steps for release are:
* Increment the version in your ``.nimble`` file.
* Commit your changes.
* Tag your release, by for example running ``git tag v0.2.0``.
* Push your tags and commits.
Once the new tag is in the remote repository, Nimble will be able to detect
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 isn't a requirement. But doing so allows people to associate
a specific name to a URL pointing to your package. This mapping is stored
in an official packages repository located
[here](https://github.com/nim-lang/packages).
This repository contains a ``packages.json`` file which lists all the published
packages. It contains a set of package names with associated metadata. You
can read more about this metadata in the
[readme for the packages repository](https://github.com/nim-lang/packages#readme).
To publish your package you need to fork that repository, and add an entry
into the ``packages.json`` file for your package. Then create a pull request
with your changes. **You only need to do this
once**.
Nimble includes a ``publish`` command which does this for you automatically.
## .nimble reference ## .nimble reference
@ -613,25 +860,25 @@ their own packages to it! Take a look at
#### Optional #### Optional
* ``SkipDirs`` - A list of directory names which should be skipped during * ``skipDirs`` - A list of directory names which should be skipped during
installation, separated by commas. installation, separated by commas.
* ``SkipFiles`` - A list of file names which should be skipped during * ``skipFiles`` - A list of file names which should be skipped during
installation, separated by commas. installation, separated by commas.
* ``SkipExt`` - A list of file extensions which should be skipped during * ``skipExt`` - A list of file extensions which should be skipped during
installation, the extensions should be specified without a leading ``.`` and installation, the extensions should be specified without a leading ``.`` and
should be separated by commas. should be separated by commas.
* ``InstallDirs`` - A list of directories which should exclusively be installed, * ``installDirs`` - A list of directories which should exclusively be installed,
if this option is specified nothing else will be installed except the dirs if this option is specified nothing else will be installed except the dirs
listed here, the files listed in ``InstallFiles``, the files which share the listed here, the files listed in ``installFiles``, the files which share the
extensions listed in ``InstallExt``, the .nimble file and the binary extensions listed in ``installExt``, the .nimble file and the binary
(if ``bin`` is specified). Separated by commas. (if ``bin`` is specified). Separated by commas.
* ``InstallFiles`` - A list of files which should be exclusively installed, * ``installFiles`` - A list of files which should be exclusively installed,
this complements ``InstallDirs`` and ``InstallExt``. Only the files listed this complements ``installDirs`` and ``installExt``. Only the files listed
here, directories listed in ``InstallDirs``, files which share the extension here, directories listed in ``installDirs``, files which share the extension
listed in ``InstallExt``, the .nimble file and the binary (if ``bin`` is listed in ``installExt``, the .nimble file and the binary (if ``bin`` is
specified) will be installed. Separated by commas. specified) will be installed. Separated by commas.
* ``InstallExt`` - A list of file extensions which should be exclusively * ``installExt`` - A list of file extensions which should be exclusively
installed, this complements ``InstallDirs`` and ``InstallFiles``. installed, this complements ``installDirs`` and ``installFiles``.
Separated by commas. Separated by commas.
* ``srcDir`` - Specifies the directory which contains the .nim source files. * ``srcDir`` - Specifies the directory which contains the .nim source files.
**Default**: The directory in which the .nimble file resides; i.e. root dir of **Default**: The directory in which the .nimble file resides; i.e. root dir of
@ -642,7 +889,7 @@ their own packages to it! Take a look at
root dir of the package. root dir of the package.
* ``bin`` - A list of files which should be built separated by commas with * ``bin`` - A list of files which should be built separated by commas with
no file extension required. This option turns your package into a *binary no file extension required. This option turns your package into a *binary
package*, nimble will build the files specified and install them appropriately. package*, Nimble will build the files specified and install them appropriately.
* ``backend`` - Specifies the backend which will be used to build the files * ``backend`` - Specifies the backend which will be used to build the files
listed in ``bin``. Possible values include: ``c``, ``cc``, ``cpp``, ``objc``, listed in ``bin``. Possible values include: ``c``, ``cc``, ``cpp``, ``objc``,
``js``. ``js``.
@ -657,27 +904,94 @@ their own packages to it! Take a look at
**Example**: ``nim >= 0.10.0, jester``; with this value your package will **Example**: ``nim >= 0.10.0, jester``; with this value your package will
depend on ``nim`` version 0.10.0 or greater and on any version of ``jester``. depend on ``nim`` version 0.10.0 or greater and on any version of ``jester``.
## Nimble's folder structure and packages
Nimble stores everything that has been installed in ``~/.nimble`` on Unix systems
and in your ``$home/.nimble`` on Windows. Libraries are stored in
``$nimbleDir/pkgs``, and binaries are stored in ``$nimbleDir/bin``. Most Nimble
packages will provide ``.nim`` files and some documentation. The Nim
compiler is aware of Nimble and will automatically find the modules so you can
``import modulename`` and have that working without additional setup.
However, some Nimble packages can provide additional tools or commands. If you
don't add their location (``$nimbleDir/bin``) to your ``$PATH`` they will not
work properly and you won't be able to run them.
## Troubleshooting ## Troubleshooting
* ```SSL support is not available. Cannot connect over SSL. [HttpRequestError]``` * ```SSL support is not available. Cannot connect over SSL. [HttpRequestError]```
Make sure that nimble is configured to run with SSL, adding a ```-d:ssl``` Make sure that Nimble is configured to run with SSL, adding a ```-d:ssl```
flag to the file ```src/nimble.nim.cfg```. 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``
Make sure that you are running at least version 0.16.0 of Nim (or the latest nightly).
* ``Error: cannot open '/home/user/.nimble/lib/system.nim'.``
Nimble cannot find the Nim standard library. This is considered a bug so
please report it. As a workaround you can set the ``NIM_LIB_PREFIX`` environment
variable to the directory where ``lib/system.nim`` (and other standard library
files) are found. Alternatively you can also configure this in Nimble's
config file.
## Repository information
This repository has two main branches: ``master`` and ``stable``.
The ``master`` branch is...
* default
* bleeding edge
* tested to compile with a pinned (close to HEAD) commit of Nim
The ``stable`` branch is...
* installed by ``koch tools``/``koch nimble``
* relatively stable
* should compile with Nim HEAD as well as the latest Nim version
Note: The travis build only tests whether Nimble works with the latest Nim
version.
A new Nim release (via ``koch xz``) will always bundle the ``stable`` branch.
## Contribution ## Contribution
If you would like to help, feel free to fork and make any additions you see fit If you would like to help, feel free to fork and make any additions you see fit
and then send a pull request. and then send a pull request.
If you have any questions about the project you can ask me directly on github, If you have any questions about the project, you can ask me directly on GitHub,
ask on the Nim [forum](http://forum.nim-lang.org), or ask on Freenode in ask on the Nim [forum](https://forum.nim-lang.org), or ask on Freenode in
the #nim channel. the #nim channel.
## Implementation details
### .nimble-link
These files are created by Nimble when using the ``develop`` command. They
are very simple and contain two lines.
**The first line:** Always a path to the `.nimble` file.
**The second line:** Always a path to the Nimble package's source code. Usually
``$pkgDir/src``, depending on what ``srcDir`` is set to.
The paths written by Nimble are **always** absolute. But Nimble (and the
Nim compiler) also supports relative paths, which will be read relative to
the `.nimble-link` file.
## About ## About
Nimble has been written by [Dominik Picheta](http://picheta.me/) with help from Nimble has been written by [Dominik Picheta](https://picheta.me/) with help from
a number of a number of
[contributors](https://github.com/nim-lang/nimble/graphs/contributors). [contributors](https://github.com/nim-lang/nimble/graphs/contributors).
It is licensed under the BSD license (Look at license.txt for more info). It is licensed under the 3-clause BSD license, see [license.txt](license.txt)
for more information.

File diff suppressed because it is too large Load diff

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

293
src/nimblepkg/cli.nim Normal file
View file

@ -0,0 +1,293 @@
# Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info.
#
# Rough rules/philosophy for the messages that Nimble displays are the following:
# - Green is only shown when the requested operation is successful.
# - Blue can be used to emphasise certain keywords, for example actions such
# as "Downloading" or "Reading".
# - Red is used when the requested operation fails with an error.
# - Yellow is used for warnings.
#
# - Dim for LowPriority.
# - Bright for HighPriority.
# - Normal for MediumPriority.
import terminal, sets, strutils
import version
when not declared(initHashSet):
import common
type
CLI* = ref object
level: Priority
warnings: HashSet[(string, string)]
suppressionCount: int ## Amount of messages which were not shown.
showColor: bool ## Whether messages should be colored.
suppressMessages: bool ## Whether Warning, Message and Success messages
## should be suppressed, useful for
## commands like `dump` whose output should be
## machine readable.
Priority* = enum
DebugPriority, LowPriority, MediumPriority, HighPriority
DisplayType* = enum
Error, Warning, Message, Success
ForcePrompt* = enum
dontForcePrompt, forcePromptYes, forcePromptNo
const
longestCategory = len("Downloading")
foregrounds: array[Error .. Success, ForegroundColor] =
[fgRed, fgYellow, fgCyan, fgGreen]
styles: array[DebugPriority .. HighPriority, set[Style]] =
[{styleDim}, {styleDim}, {}, {styleBright}]
proc newCLI(): CLI =
result = CLI(
level: HighPriority,
warnings: initHashSet[(string, string)](),
suppressionCount: 0,
showColor: true,
suppressMessages: false
)
var globalCLI = newCLI()
proc calculateCategoryOffset(category: string): int =
assert category.len <= longestCategory
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,
priority: Priority) =
if isSuppressed(displayType):
return
# Calculate how much the `category` must be offset to align along a center
# line.
let offset = calculateCategoryOffset(category)
# Display the category.
let text = "$1$2 " % [spaces(offset), category]
if globalCLI.showColor:
if priority != DebugPriority:
setForegroundColor(stdout, foregrounds[displayType])
writeStyled(text, styles[priority])
resetAttributes()
else:
stdout.write(text)
proc displayLine(category, line: string, displayType: DisplayType,
priority: Priority) =
if isSuppressed(displayType):
return
displayCategory(category, displayType, priority)
# Display the message.
echo(line)
proc display*(category, msg: string, displayType = Message,
priority = MediumPriority) =
# Multiple warnings containing the same messages should not be shown.
let warningPair = (category, msg)
if displayType == Warning:
if warningPair in globalCLI.warnings:
return
else:
globalCLI.warnings.incl(warningPair)
# Suppress this message if its priority isn't high enough.
# TODO: Per-priority suppression counts?
if priority < globalCLI.level:
if priority != DebugPriority:
globalCLI.suppressionCount.inc
return
# Display each line in the message.
var i = 0
for line in msg.splitLines():
if len(line) == 0: continue
displayLine(if i == 0: category else: "...", line, displayType, priority)
i.inc
proc displayDebug*(category, msg: string) =
## Convenience for displaying debug messages.
display(category, msg, priority = DebugPriority)
proc displayDebug*(msg: string) =
## Convenience for displaying debug messages with a default category.
displayDebug("Debug:", msg)
proc displayTip*() =
## Called just before Nimble exits. Shows some tips for the user, for example
## the amount of messages that were suppressed and how to show them.
if globalCLI.suppressionCount > 0:
let msg = "$1 messages have been suppressed, use --verbose to show them." %
$globalCLI.suppressionCount
display("Tip:", msg, Warning, HighPriority)
proc prompt*(forcePrompts: ForcePrompt, question: string): bool =
case forcePrompts
of forcePromptYes:
display("Prompt:", question & " -> [forced yes]", Warning, HighPriority)
return true
of forcePromptNo:
display("Prompt:", question & " -> [forced no]", Warning, HighPriority)
return false
of dontForcePrompt:
displayLine("Prompt:", question & " [y/N]", Warning, HighPriority)
displayCategory("Answer:", Warning, HighPriority)
let yn = stdin.readLine()
case yn.normalize
of "y", "yes":
return true
of "n", "no":
return false
else:
return false
proc promptCustom*(forcePrompts: ForcePrompt, question, default: string): string =
case forcePrompts:
of forcePromptYes:
display("Prompt:", question & " -> [forced " & default & "]", Warning,
HighPriority)
return default
else:
if default == "":
display("Prompt:", question, Warning, HighPriority)
displayCategory("Answer:", Warning, HighPriority)
let user = stdin.readLine()
if user.len == 0: return promptCustom(forcePrompts, question, default)
else: return user
else:
display("Prompt:", question & " [" & default & "]", Warning, HighPriority)
displayCategory("Answer:", Warning, HighPriority)
let user = stdin.readLine()
if user == "": return default
else: return user
proc promptCustom*(question, default: string): string =
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 =
case forcePrompts:
of forcePromptYes:
result = args[0]
display("Prompt:", question & " -> [forced " & result & "]", Warning,
HighPriority)
else:
if isatty(stdout):
return promptListInteractive(question, args)
else:
return promptListFallback(question, args)
proc setVerbosity*(level: Priority) =
globalCLI.level = level
proc setShowColor*(val: bool) =
globalCLI.showColor = val
proc setSuppressMessages*(val: bool) =
globalCLI.suppressMessages = val
when isMainModule:
display("Reading", "config file at /Users/dom/.config/nimble/nimble.ini",
priority = LowPriority)
display("Reading", "official package list",
priority = LowPriority)
display("Downloading", "daemonize v0.0.2 using Git",
priority = HighPriority)
display("Warning", "dashes in package names will be deprecated", Warning,
priority = HighPriority)
display("Error", """Unable to read package info for /Users/dom/.nimble/pkgs/nimble-0.7.11
Reading as ini file failed with:
Invalid section: .
Evaluating as NimScript file failed with:
Users/dom/.nimble/pkgs/nimble-0.7.11/nimble.nimble(3, 23) Error: cannot open 'src/nimblepkg/common'.
""", Error, HighPriority)

View file

@ -1,3 +1,4 @@
# 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.
# #
# Various miscellaneous common types reside here, to avoid problems with # Various miscellaneous common types reside here, to avoid problems with
@ -7,20 +8,24 @@ when not defined(nimscript):
import sets import sets
import version import version
export version.NimbleError
type type
BuildFailed* = object of NimbleError BuildFailed* = object of NimbleError
PackageInfo* = object PackageInfo* = object
mypath*: string ## The path of this .nimble file myPath*: string ## The path of this .nimble file
isNimScript*: bool ## Determines if this pkg info was read from a nims file isNimScript*: bool ## Determines if this pkg info was read from a nims file
isMinimal*: bool isMinimal*: bool
isInstalled*: bool ## Determines if the pkg this info belongs to is installed isInstalled*: bool ## Determines if the pkg this info belongs to is installed
isLinked*: bool ## Determines if the pkg this info belongs to has been linked via `develop`
postHooks*: HashSet[string] ## Useful to know so that Nimble doesn't execHook unnecessarily postHooks*: HashSet[string] ## Useful to know so that Nimble doesn't execHook unnecessarily
preHooks*: HashSet[string] preHooks*: HashSet[string]
name*: string name*: string
## 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'.
## If in doubt, use `getConcreteVersion` instead.
version*: string version*: string
specialVersion*: string ## Either `myVersion` or a special version such as #head.
author*: string author*: string
description*: string description*: string
license*: string license*: string
@ -35,6 +40,39 @@ when not defined(nimscript):
binDir*: string binDir*: string
srcDir*: string srcDir*: string
backend*: string backend*: string
foreignDeps*: seq[string]
## Same as quit(QuitSuccess), but allows cleanup.
NimbleQuit* = ref object of Exception
proc raiseNimbleError*(msg: string, hint = "") =
var exc = newException(NimbleError, msg)
exc.hint = hint
raise exc
proc getOutputInfo*(err: ref NimbleError): (string, string) =
var error = ""
var hint = ""
error = err.msg
when not defined(release):
let stackTrace = getStackTrace(err)
error = stackTrace & "\n\n" & error
if not err.isNil:
hint = err.hint
return (error, hint)
const const
nimbleVersion* = "0.7.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,26 +1,25 @@
# 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 import version, cli
type type
Config* = object Config* = object
nimbleDir*: string nimbleDir*: string
chcp*: bool # Whether to change the code page in .cmd files on Win. chcp*: bool # Whether to change the code page in .cmd files on Win.
packageLists*: Table[string, PackageList] ## URLs to packages.json files packageLists*: Table[string, PackageList] ## Names -> packages.json files
cloneUsingHttps*: bool # Whether to replace git:// for https:// cloneUsingHttps*: bool # Whether to replace git:// for https://
httpProxy*: Uri # Proxy for package list downloads. httpProxy*: Uri # Proxy for package list downloads.
nimLibPrefix*: string # Nim stdlib prefix.
PackageList* = object PackageList* = object
name*: string name*: string
urls*: seq[string] urls*: seq[string]
path*: string
proc initConfig(): Config = proc initConfig(): Config =
if getNimrodVersion() > newVersion("0.9.6"):
result.nimbleDir = getHomeDir() / ".nimble" result.nimbleDir = getHomeDir() / ".nimble"
else:
result.nimbleDir = getHomeDir() / ".babel"
result.httpProxy = initUri() result.httpProxy = initUri()
@ -35,9 +34,12 @@ proc initConfig(): Config =
]) ])
result.packageLists["official"] = defaultPkgList result.packageLists["official"] = defaultPkgList
result.nimLibPrefix = ""
proc initPackageList(): PackageList = proc initPackageList(): PackageList =
result.name = "" result.name = ""
result.urls = @[] result.urls = @[]
result.path = ""
proc addCurrentPkgList(config: var Config, currentPackageList: PackageList) = proc addCurrentPkgList(config: var Config, currentPackageList: PackageList) =
if currentPackageList.name.len > 0: if currentPackageList.name.len > 0:
@ -50,13 +52,14 @@ proc parseConfig*(): Config =
var f = newFileStream(confFile, fmRead) var f = newFileStream(confFile, fmRead)
if f == nil: if f == nil:
# Try the old deprecated babel.ini # Try the old deprecated babel.ini
# TODO: This can be removed.
confFile = getConfigDir() / "babel" / "babel.ini" confFile = getConfigDir() / "babel" / "babel.ini"
f = newFileStream(confFile, fmRead) f = newFileStream(confFile, fmRead)
if f != nil: if f != nil:
echo("[Warning] Using deprecated config file at ", confFile) display("Warning", "Using deprecated config file at " & confFile,
Warning, HighPriority)
if f != nil: if f != nil:
echo("Reading from config file at ", confFile) display("Reading", "config file at " & confFile, priority = LowPriority)
var p: CfgParser var p: CfgParser
open(p, f, confFile) open(p, f, confFile)
var currentSection = "" var currentSection = ""
@ -65,6 +68,11 @@ proc parseConfig*(): Config =
var e = next(p) var e = next(p)
case e.kind case e.kind
of cfgEof: of cfgEof:
if currentSection.len > 0:
if currentPackageList.urls.len == 0 and currentPackageList.path == "":
raise newException(NimbleError, "Package list '$1' requires either url or path" % currentPackageList.name)
if currentPackageList.urls.len > 0 and currentPackageList.path != "":
raise newException(NimbleError, "Attempted to specify `url` and `path` for the same package list '$1'" % currentPackageList.name)
addCurrentPkgList(result, currentPackageList) addCurrentPkgList(result, currentPackageList)
break break
of cfgSectionStart: of cfgSectionStart:
@ -98,6 +106,16 @@ proc parseConfig*(): Config =
of "packagelist": of "packagelist":
currentPackageList.urls.add(e.value) currentPackageList.urls.add(e.value)
else: assert false else: assert false
of "path":
case currentSection.normalize
of "packagelist":
if currentPackageList.path != "":
raise newException(NimbleError, "Attempted to specify more than one `path` for the same package list.")
else:
currentPackageList.path = e.value
else: assert false
of "nimlibprefix":
result.nimLibPrefix = e.value
else: else:
raise newException(NimbleError, "Unable to parse config file:" & raise newException(NimbleError, "Unable to parse config file:" &
" Unknown key: " & e.key) " Unknown key: " & e.key)

View file

@ -1,15 +1,16 @@
# 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 parseutils, os, osproc, strutils, tables, pegs import parseutils, os, osproc, strutils, tables, pegs, uri
import packageinfo, packageparser, version, tools, common, options, cli
import packageinfo, packageparser, version, tools, common, options 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)
@ -91,8 +93,10 @@ proc getTagsListRemote*(url: string, meth: DownloadMethod): seq[string] =
raise newException(OSError, "Unable to query remote tags for " & url & raise newException(OSError, "Unable to query remote tags for " & url &
". Git returned: " & output) ". Git returned: " & output)
for i in output.splitLines(): for i in output.splitLines():
if i == "": continue let refStart = i.find("refs/tags/")
let start = i.find("refs/tags/")+"refs/tags/".len # git outputs warnings, empty lines, etc
if refStart == -1: continue
let start = refStart+"refs/tags/".len
let tag = i[start .. i.len-1] let tag = i[start .. i.len-1]
if not tag.endswith("^{}"): result.add(tag) if not tag.endswith("^{}"): result.add(tag)
@ -100,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
@ -116,12 +127,12 @@ proc getDownloadMethod*(meth: string): DownloadMethod =
else: else:
raise newException(NimbleError, "Invalid download method: " & meth) raise newException(NimbleError, "Invalid download method: " & meth)
proc getHeadName*(meth: DownloadMethod): string = proc getHeadName*(meth: DownloadMethod): Version =
## Returns the name of the download method specific head. i.e. for git ## Returns the name of the download method specific head. i.e. for git
## it's ``head`` for hg it's ``tip``. ## it's ``head`` for hg it's ``tip``.
case meth case meth
of DownloadMethod.git: "head" of DownloadMethod.git: newVersion("#head")
of DownloadMethod.hg: "tip" of DownloadMethod.hg: newVersion("#tip")
proc checkUrlType*(url: string): DownloadMethod = proc checkUrlType*(url: string): DownloadMethod =
## Determines the download method based on the URL. ## Determines the download method based on the URL.
@ -130,19 +141,29 @@ proc checkUrlType*(url: string): DownloadMethod =
elif doCmdEx("hg identify " & url).exitCode == QuitSuccess: elif doCmdEx("hg identify " & url).exitCode == QuitSuccess:
return DownloadMethod.hg return DownloadMethod.hg
else: else:
raise newException(NimbleError, "Unable to identify url.") raise newException(NimbleError, "Unable to identify url: " & url)
proc getUrlData*(url: string): (string, Table[string, string]) =
var uri = parseUri(url)
# TODO: use uri.parseQuery once it lands... this code is quick and dirty.
var subdir = ""
if uri.query.startsWith("subdir="):
subdir = uri.query[7 .. ^1]
uri.query = ""
return ($uri, {"subdir": subdir}.toTable())
proc isURL*(name: string): bool = proc isURL*(name: string): bool =
name.startsWith(peg" @'://' ") name.startsWith(peg" @'://' ")
proc doDownload*(url: string, downloadDir: string, verRange: VersionRange, proc doDownload(url: string, downloadDir: string, verRange: VersionRange,
downMethod: DownloadMethod, options: Options): VersionRange = downMethod: DownloadMethod,
options: Options): Version =
## Downloads the repository specified by ``url`` using the specified download ## Downloads the repository specified by ``url`` using the specified download
## method. ## method.
## ##
## Returns the version of the repository which has been downloaded. ## Returns the version of the repository which has been downloaded.
template getLatestByTag(meth: stmt): stmt {.dirty, immediate.} = template getLatestByTag(meth: untyped) {.dirty.} =
echo("Found tags...")
# Find latest version that fits our ``verRange``. # Find latest version that fits our ``verRange``.
var latest = findLatest(verRange, versions) var latest = findLatest(verRange, versions)
## Note: HEAD is not used when verRange.kind is verAny. This is ## Note: HEAD is not used when verRange.kind is verAny. This is
@ -150,62 +171,103 @@ proc doDownload*(url: string, downloadDir: string, verRange: VersionRange,
# If no tagged versions satisfy our range latest.tag will be "". # If no tagged versions satisfy our range latest.tag will be "".
# We still clone in that scenario because we want to try HEAD in that case. # We still clone in that scenario because we want to try HEAD in that case.
# https://github.com/nimrod-code/nimble/issues/22 # https://github.com/nim-lang/nimble/issues/22
meth meth
if $latest.ver != "": if $latest.ver != "":
result = parseVersionRange($latest.ver) result = latest.ver
else:
# Result should already be set to #head here.
assert(not result.isNil)
proc verifyClone() =
## Makes sure that the downloaded package's version satisfies the requested
## version range.
let pkginfo = getPkgInfo(downloadDir, options)
if pkginfo.version.newVersion notin verRange:
raise newException(NimbleError,
"Downloaded package's version does not satisfy requested version " &
"range: wanted $1 got $2." %
[$verRange, $pkginfo.version])
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 == newSpecial(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.
doCheckout(downMethod, downloadDir, $verRange.spe) # `spe` starts with '#', trim it.
result = verRange doAssert(($verRange.spe)[0] == '#')
doCheckout(downMethod, downloadDir, substr($verRange.spe, 1))
result = verRange.spe
else: else:
case downMethod case downMethod
of DownloadMethod.git: of DownloadMethod.git:
# For Git we have to query the repo remotely for its tags. This is # For Git we have to query the repo remotely for its tags. This is
# necessary as cloning with a --depth of 1 removes all tag info. # necessary as cloning with a --depth of 1 removes all tag info.
result = parseVersionRange("#head") result = getHeadName(downMethod)
let versions = getTagsListRemote(url, downMethod).getVersionList() let versions = getTagsListRemote(url, downMethod).getVersionList()
if versions.len > 0: if versions.len > 0:
getLatestByTag: getLatestByTag:
echo("Cloning latest tagged version: ", latest.tag) display("Cloning", "latest tagged version: " & latest.tag,
doClone(downMethod, url, downloadDir, latest.tag) priority = MediumPriority)
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.
verifyClone()
of DownloadMethod.hg: of DownloadMethod.hg:
doClone(downMethod, url, downloadDir) doClone(downMethod, url, downloadDir, onlyTip = not options.forceFullClone)
result = parseVersionRange("#tip") result = getHeadName(downMethod)
let versions = getTagsList(downloadDir, downMethod).getVersionList() let versions = getTagsList(downloadDir, downMethod).getVersionList()
if versions.len > 0: if versions.len > 0:
getLatestByTag: getLatestByTag:
echo("Switching to latest tagged version: ", latest.tag) display("Switching", "to latest tagged version: " & latest.tag,
priority = MediumPriority)
doCheckout(downMethod, downloadDir, latest.tag) doCheckout(downMethod, downloadDir, latest.tag)
verifyClone() proc downloadPkg*(url: string, verRange: VersionRange,
downMethod: DownloadMethod,
subdir: string,
options: Options,
downloadPath = ""): (string, Version) =
## Downloads the repository as specified by ``url`` and ``verRange`` using
## the download method specified.
##
## If `downloadPath` isn't specified a location in /tmp/ will be used.
##
## Returns the directory where it was downloaded (subdir is appended) and
## the concrete version which was downloaded.
let downloadDir =
if downloadPath == "":
(getNimbleTempDir() / getDownloadDirName(url, verRange))
else:
downloadPath
createDir(downloadDir)
var modUrl =
if url.startsWith("git://") and options.config.cloneUsingHttps:
"https://" & url[6 .. ^1]
else: url
# Fixes issue #204
# github + https + trailing url slash causes a
# checkout/ls-remote to fail with Repository not found
if modUrl.contains("github.com") and modUrl.endswith("/"):
modUrl = modUrl[0 .. ^2]
if subdir.len > 0:
display("Downloading", "$1 using $2 (subdir is '$3')" %
[modUrl, $downMethod, subdir],
priority = HighPriority)
else:
display("Downloading", "$1 using $2" % [modUrl, $downMethod],
priority = HighPriority)
result = (
downloadDir / subdir,
doDownload(modUrl, downloadDir, verRange, downMethod, options)
)
if verRange.kind != verSpecial:
## Makes sure that the downloaded package's version satisfies the requested
## version range.
let pkginfo = getPkgInfo(result[0], options)
if pkginfo.version.newVersion notin verRange:
raise newException(NimbleError,
"Downloaded package's version does not satisfy requested version " &
"range: wanted $1 got $2." %
[$verRange, $pkginfo.version])
proc echoPackageVersions*(pkg: Package) = proc echoPackageVersions*(pkg: Package) =
let downMethod = pkg.downloadMethod.getDownloadMethod() let downMethod = pkg.downloadMethod.getDownloadMethod()
@ -214,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:
@ -229,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.
@ -19,26 +25,172 @@ var
installExt*, bin*: seq[string] = @[] ## Nimble metadata. installExt*, bin*: seq[string] = @[] ## Nimble metadata.
requiresData*: seq[string] = @[] ## The package's dependencies. requiresData*: seq[string] = @[] ## The package's dependencies.
foreignDeps*: seq[string] = @[] ## The foreign dependencies. Only
## 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

@ -0,0 +1,76 @@
# Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info.
import os, strutils, sets
import packageparser, common, packageinfo, options, nimscriptwrapper, cli,
version
proc execHook*(options: Options, hookAction: ActionType, before: bool): bool =
## Returns whether to continue.
result = true
# For certain commands hooks should not be evaluated.
if hookAction in noHookActions:
return
var nimbleFile = ""
try:
nimbleFile = findNimbleFile(getCurrentDir(), true)
except NimbleError: return true
# PackageInfos are cached so we can read them as many times as we want.
let pkgInfo = getPkgInfoFromFile(nimbleFile, options)
let actionName =
if hookAction == actionCustom: options.action.command
else: ($hookAction)[6 .. ^1]
let hookExists =
if before: actionName.normalize in pkgInfo.preHooks
else: actionName.normalize in pkgInfo.postHooks
if pkgInfo.isNimScript and hookExists:
let res = execHook(nimbleFile, actionName, before, options)
if res.success:
result = res.retVal
proc execCustom*(options: Options,
execResult: var ExecutionResult[bool],
failFast = true): bool =
## Executes the custom command using the nimscript backend.
##
## If failFast is true then exceptions will be raised when something is wrong.
## Otherwise this function will just return false.
# Custom command. Attempt to call a NimScript task.
let nimbleFile = findNimbleFile(getCurrentDir(), true)
if not nimbleFile.isNimScript(options) and failFast:
writeHelp()
execResult = execTask(nimbleFile, options.action.command, options)
if not execResult.success:
if not failFast:
return
raiseNimbleError(msg = "Could not find task $1 in $2" %
[options.action.command, nimbleFile],
hint = "Run `nimble --help` and/or `nimble tasks` for" &
" a list of possible commands.")
if execResult.command.normalize == "nop":
display("Warning:", "Using `setCommand 'nop'` is not necessary.", Warning,
HighPriority)
return
if not execHook(options, actionCustom, false):
return
return true
proc getOptionsForCommand*(execResult: ExecutionResult,
options: Options): Options =
## Creates an Options object for the requested command.
var newOptions = options.briefClone()
parseCommand(execResult.command, newOptions)
for arg in execResult.arguments:
parseArgument(arg, newOptions)
for flag, vals in execResult.flags:
for val in vals:
parseFlag(flag, val, newOptions)
return newOptions

View file

@ -1,432 +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/lists, compiler/nimconf
from compiler/scriptconfig import setupVM
from compiler/idents import getIdent
from compiler/astalgo import strTableGet
import compiler/options as compiler_options
import common, version, options, packageinfo
import os, strutils, strtabs, times, osproc, sets
type
ExecutionResult*[T] = object
success*: bool
command*: string
arguments*: seq[string]
flags*: StringTableRef
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)]")
proc setupVM(module: PSym; scriptName: string,
flags: StringTableRef): 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 :)
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 result: seq[string] = @[]
for kind, path in walkDir(dir):
if kind in filter: result.add path
setResult(a, result)
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:
flags[a.getString 0] = a.getString 1
proc findNimscriptApi(options: Options): string =
## Returns the directory containing ``nimscriptapi.nim`` or an empty string
## if it cannot be found.
result = ""
# Try finding it in exe's path
if fileExists(getAppDir() / "nimblepkg" / "nimscriptapi.nim"):
result = getAppDir()
if result.len == 0:
let pkgs = getInstalledPkgsMin(options.getPkgsDir(), options)
var pkg: PackageInfo
if pkgs.findPkg(("nimble", newVRAny()), pkg):
let pkgDir = pkg.getRealDir()
if fileExists(pkgDir / "nimblepkg" / "nimscriptapi.nim"):
result = pkgDir
proc getNimPrefixDir(): string = splitPath(findExe("nim")).head.parentDir
proc execScript(scriptName: string, flags: StringTableRef, options: Options) =
## Executes the specified script.
##
## No clean up is performed and must be done manually!
if "nimblepkg/nimscriptapi" notin compiler_options.implicitIncludes:
compiler_options.implicitIncludes.add("nimblepkg/nimscriptapi")
# Ensure the compiler can find its standard library #220.
compiler_options.gPrefixDir = getNimPrefixDir()
let pkgName = scriptName.splitFile.name
# Ensure that "nimblepkg/nimscriptapi" is in the PATH.
let nimscriptApiPath = findNimscriptApi(options)
if nimscriptApiPath.len > 0:
echo("Using custom nimscriptapi.nim defined in ",
nimscriptApiPath / "nimblepkg")
appendStr(searchPaths, nimscriptApiPath)
else:
let tmpNimscriptApiPath = getTempDir() / "nimblepkg" / "nimscriptapi.nim"
createDir(tmpNimscriptApiPath.splitFile.dir)
if not existsFile(tmpNimscriptApiPath):
writeFile(tmpNimscriptApiPath, nimscriptApi)
appendStr(searchPaths, getTempDir())
initDefines()
loadConfigs(DefaultConfig)
passes.gIncludeFile = includeModule
passes.gImportModule = importModule
defineSymbol("nimscript")
defineSymbol("nimconfig")
defineSymbol("nimble")
registerPass(semPass)
registerPass(evalPass)
appendStr(searchPaths, compiler_options.libpath)
var m = makeModule(scriptName)
incl(m.flags, sfMainModule)
vm.globalCtx = setupVM(m, 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)
compileSystemModule()
processModule(m, llStreamOpen(scriptName, fmRead), nil)
proc cleanup() =
# ensure everything can be called again:
compiler_options.gProjectName = ""
compiler_options.command = ""
resetAllModulesHard()
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:
echo(previousMsg)
if output.normalize.startsWith("error"):
raise newException(NimbleError, output)
previousMsg = output
compiler_options.command = internalCmd
# Execute the nimscript file.
execScript(scriptName, nil, options)
# 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(thisModule: PSym, ident: string): PSym =
result = thisModule.tab.strTableGet(getIdent(ident))
if result.isNil:
raise newException(NimbleError, "Ident not found: " & ident)
template trivialField(field) =
result.field = getGlobal(getSym(thisModule, astToStr field))
template trivialFieldSeq(field) =
result.field.add getGlobalAsSeq(getSym(thisModule, astToStr field))
# Grab the module Sym for .nimble file (nimscriptapi is included in it).
let idx = fileInfoIdx(scriptName)
let thisModule = getModule(idx)
assert(not thisModule.isNil)
assert thisModule.kind == skModule
# keep reasonable default:
let name = getGlobal(thisModule.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
extractRequires(getSym(thisModule, "requiresData"), result.requires)
let binSeq = getGlobalAsSeq(getSym(thisModule, "bin"))
for i in binSeq:
result.bin.add(i.addFileExt(ExeExt))
let backend = getGlobal(getSym(thisModule, "backend"))
if backend.len == 0:
result.backend = "c"
elif cmpIgnoreStyle(backend, "javascript") == 0:
result.backend = "js"
else:
result.backend = backend.toLower()
# 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 = newStringTable()
compiler_options.command = internalCmd
echo("Executing task ", taskName, " in ", scriptName)
execScript(scriptName, result.flags, options)
# Explicitly execute the task procedure, instead of relying on hack.
let idx = fileInfoIdx(scriptName)
let thisModule = getModule(idx)
assert thisModule.kind == skModule
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 = newStringTable()
compiler_options.command = internalCmd
let hookName =
if before: actionName.toLower & "Before"
else: actionName.toLower & "After"
echo("Attempting to execute hook ", hookName, " in ", scriptName)
execScript(scriptName, result.flags, options)
# Explicitly execute the task procedure, instead of relying on hack.
let idx = fileInfoIdx(scriptName)
let thisModule = getModule(idx)
assert thisModule.kind == skModule
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")
execScript(scriptName, nil, options)
# TODO: 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,79 +1,122 @@
# 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 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
verbosity*: cli.Priority
action*: Action action*: Action
config*: Config config*: Config
nimbleData*: JsonNode ## Nimbledata.json nimbleData*: JsonNode ## Nimbledata.json
pkgInfoCache*: TableRef[string, PackageInfo] pkgInfoCache*: TableRef[string, PackageInfo]
showHelp*: bool
showVersion*: bool
noColor*: 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,
actionCustom, actionTasks, actionVersion actionDoc, actionCustom, actionTasks, actionDevelop, actionCheck,
actionRun
Action* = object Action* = object
case typ*: ActionType case typ*: ActionType
of actionNil, actionList, actionBuild, actionPublish, actionTasks, of actionNil, actionList, actionPublish, actionTasks, actionCheck: nil
actionVersion: nil
of actionRefresh: of actionRefresh:
optionalURL*: string # Overrides default package list. optionalURL*: string # Overrides default package list.
of actionInstall, actionPath, actionUninstall: of actionInstall, actionPath, actionUninstall, actionDevelop:
packages*: seq[PkgTuple] # Optional only for actionInstall. packages*: seq[PkgTuple] # Optional only for actionInstall
# 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
of actionCompile: vcsOption*: string
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]
flags*: StringTableRef flags*: StringTableRef
ForcePrompt* = enum
dontForcePrompt, forcePromptYes, forcePromptNo
const const
help* = """ help* = """
Usage: nimble COMMAND [opts] Usage: nimble COMMAND [opts]
Commands: Commands:
install [pkgname, ...] Installs a list of packages. install [pkgname, ...] Installs a list of packages.
init [pkgname] Initializes a new Nimble project. [-d, --depsOnly] Install only dependencies.
[-p, --passNim] Forward specified flag to compiler.
develop [pkgname, ...] Clones a list of packages for development.
Symlinks the cloned packages or any package
in the current working directory.
check Verifies the validity of a package in the
current working directory.
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
[-c, --continue] Don't stop execution on a failed test.
doc, doc2 [opts, ...] f.nim Builds documentation for a file inside a
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
can be optionally specified. can be optionally specified.
search [--ver] pkg/tag Searches for a specified package. Search is search pkg/tag Searches for a specified package. Search is
performed by tag and by name. performed by tag and by name.
list [--ver] Lists all packages. [--ver] Query remote server for package version.
list Lists all packages.
[--ver] Query remote server for package version.
[-i, --installed] Lists all installed packages. [-i, --installed] Lists all installed packages.
tasks Lists the tasks specified in the Nimble tasks Lists the tasks specified in the Nimble
package's Nimble file. package's Nimble file.
path pkgname ... Shows absolute path to the installed packages path pkgname ... Shows absolute path to the installed packages
specified. specified.
dump [pkgname] Outputs Nimble package information for dump [pkgname] Outputs Nimble package information for
external tools. external tools. The argument can be a
.nimble file, a project directory or
the name of an installed package.
Options: Options:
-h, --help Print this help message. -h, --help Print this help message.
@ -82,31 +125,47 @@ Options:
-n, --reject Reject all interactive prompts. -n, --reject Reject all interactive prompts.
--ver Query remote server for package version --ver Query remote server for package version
information when searching or listing packages information when searching or listing packages
--nimbleDir dirname Set the Nimble directory. --nimbleDir:dirname Set the Nimble directory.
-d --depsOnly Install only dependencies. --verbose Show all non-debug output.
--debug Show all output including debug messages.
--noColor Don't colorise output.
For more information read the Github readme: For more information read the Github readme:
https://github.com/nim-lang/nimble#readme https://github.com/nim-lang/nimble#readme
""" """
proc writeHelp*() = const noHookActions* = {actionCheck}
proc writeHelp*(quit=true) =
echo(help) echo(help)
quit(QuitSuccess) if quit:
raise NimbleQuit(msg: "")
proc writeVersion*() =
echo("nimble v$# compiled at $# $#" %
[nimbleVersion, CompileDate, CompileTime])
const execResult = gorgeEx("git rev-parse HEAD")
when execResult[0].len > 0 and execResult[1] == QuitSuccess:
echo "git hash: ", execResult[0]
else:
{.warning: "Couldn't determine GIT hash: " & execResult[0].}
echo "git hash: couldn't determine git hash"
raise NimbleQuit(msg: "")
proc parseActionType*(action: string): ActionType = proc parseActionType*(action: string): ActionType =
case action.normalize()
of "install", "path":
case action.normalize() case action.normalize()
of "install": of "install":
result = actionInstall result = actionInstall
of "path": of "path":
result = actionPath result = actionPath
else:
discard
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":
result = actionDoc
of "init": of "init":
result = actionInit result = actionInit
of "dump": of "dump":
@ -123,6 +182,10 @@ proc parseActionType*(action: string): ActionType =
result = actionPublish result = actionPublish
of "tasks": of "tasks":
result = actionTasks result = actionTasks
of "develop":
result = actionDevelop
of "check":
result = actionCheck
else: else:
result = actionCustom result = actionCustom
@ -131,67 +194,67 @@ proc initAction*(options: var Options, key: string) =
## `key`. ## `key`.
let keyNorm = key.normalize() let keyNorm = key.normalize()
case options.action.typ case options.action.typ
of actionInstall, actionPath: of actionInstall, actionPath, actionDevelop, actionUninstall:
options.action.packages = @[] options.action.packages = @[]
of actionCompile: options.action.passNimFlags = @[]
of actionCompile, actionDoc, actionBuild:
options.action.compileOptions = @[] options.action.compileOptions = @[]
options.action.file = "" options.action.file = ""
if keyNorm == "c" or keyNorm == "compile": options.action.backend = "" if keyNorm == "c" or keyNorm == "compile": options.action.backend = ""
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:
options.action.search = @[] options.action.search = @[]
of actionUninstall:
options.action.packages = @[]
of actionCustom: of actionCustom:
options.action.command = key options.action.command = key
options.action.arguments = @[] options.action.arguments = @[]
options.action.flags = newStringTable() options.action.flags = newStringTable()
of actionBuild, actionPublish, actionList, actionTasks, of actionPublish, actionList, actionTasks, actionCheck, actionRun,
actionNil, actionVersion: discard actionNil: discard
proc prompt*(options: Options, question: string): bool = proc prompt*(options: Options, question: string): bool =
## Asks an interactive question and returns the result. ## Asks an interactive question and returns the result.
## ##
## The proc will return immediately without asking the user if the global ## The proc will return immediately without asking the user if the global
## forcePrompts has a value different than dontForcePrompt. ## forcePrompts has a value different than dontForcePrompt.
case options.forcePrompts return prompt(options.forcePrompts, question)
of forcePromptYes:
echo(question & " -> [forced yes]")
return true
of forcePromptNo:
echo(question & " -> [forced no]")
return false
of dontForcePrompt:
echo(question & " [y/N]")
let yn = stdin.readLine()
case yn.normalize
of "y", "yes":
return true
of "n", "no":
return false
else:
return false
proc renameBabelToNimble(options: Options) {.deprecated.} = proc promptCustom*(options: Options, question, default: string): string =
let babelDir = getHomeDir() / ".babel" ## Asks an interactive question and returns the result.
let nimbleDir = getHomeDir() / ".nimble" ##
if dirExists(babelDir): ## The proc will return "default" without asking the user if the global
if options.prompt("Found deprecated babel package directory, would you " & ## forcePrompts is forcePromptYes.
"like to rename it to nimble?"): return promptCustom(options.forcePrompts, question, default)
copyDir(babelDir, nimbleDir)
copyFile(babelDir / "babeldata.json", nimbleDir / "nimbledata.json")
removeDir(babelDir) proc promptList*(options: Options, question: string, args: openarray[string]): string =
removeFile(nimbleDir / "babeldata.json") ## Asks an interactive question and returns the result.
##
## The proc will return one of the provided args. If not prompting the first
## options is selected.
return promptList(options.forcePrompts, question, args)
proc getNimbleDir*(options: Options): string = proc getNimbleDir*(options: Options): string =
expandTilde(options.config.nimbleDir) result = options.config.nimbleDir
if options.nimbleDir.len != 0:
# --nimbleDir:<dir> takes priority...
result = options.nimbleDir
else:
# ...followed by the environment variable.
let env = getEnv("NIMBLE_DIR")
if env.len != 0:
display("Warning:", "Using the environment variable: NIMBLE_DIR='" &
env & "'", Warning)
result = env
return expandTilde(result)
proc getPkgsDir*(options: Options): string = proc getPkgsDir*(options: Options): string =
options.getNimbleDir() / "pkgs" options.getNimbleDir() / "pkgs"
@ -200,20 +263,27 @@ 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:
assert false assert false
of actionInstall, actionPath, actionUninstall: of actionInstall, actionPath, actionDevelop, actionUninstall:
# Parse pkg@verRange # Parse pkg@verRange
if '@' in key: if '@' in key:
let i = find(key, '@') let i = find(key, '@')
let pkgTup = (key[0 .. i-1], let (pkgName, pkgVer) = (key[0 .. i-1], key[i+1 .. key.len-1])
key[i+1 .. key.len-1].parseVersionRange()) if pkgVer.len == 0:
result.action.packages.add(pkgTup) raise newException(NimbleError, "Version range expected after '@'.")
result.action.packages.add((pkgName, pkgVer.parseVersionRange()))
else: else:
result.action.packages.add((key, VersionRange(kind: verAny))) result.action.packages.add((key, VersionRange(kind: verAny)))
of actionRefresh: of actionRefresh:
@ -222,64 +292,150 @@ 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: of actionCompile, actionDoc:
result.action.file = key result.action.file = key
of actionList, actionBuild, actionPublish: of actionList, actionPublish:
writeHelp() 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 parseFlag*(flag, val: string, result: var Options) = proc getFlagString(kind: CmdLineKind, flag, val: string): string =
case result.action.typ let prefix =
of actionCompile: case kind
of cmdShortOption: "-"
of cmdLongOption: "--"
else: ""
if val == "": if val == "":
result.action.compileOptions.add("--" & flag) return prefix & flag
else: else:
result.action.compileOptions.add("--" & flag & ":" & val) return prefix & flag & ":" & val
of actionCustom:
result.action.flags[flag] = val proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) =
else:
# TODO: These should not be global. let f = flag.normalize()
case flag.normalize()
of "help", "h": writeHelp() # Global flags.
of "version", "v": var isGlobalFlag = true
assert result.action.typ == actionNil case f
result.action.typ = actionVersion of "help", "h": result.showHelp = true
of "version", "v": result.showVersion = true
of "accept", "y": result.forcePrompts = forcePromptYes of "accept", "y": result.forcePrompts = forcePromptYes
of "reject", "n": result.forcePrompts = forcePromptNo of "reject", "n": result.forcePrompts = forcePromptNo
of "ver": result.queryVersions = true of "nimbledir": result.nimbleDir = val
of "nimbledir": result.config.nimbleDir = val # overrides option from file of "verbose": result.verbosity = LowPriority
of "installed", "i": result.queryInstalled = true of "debug": result.verbosity = DebugPriority
of "depsonly", "d": result.depsOnly = true of "nocolor": result.noColor = true
of "disablevalidation": result.disableValidation = true
else: isGlobalFlag = false
var wasFlagHandled = true
# Action-specific flags.
case result.action.typ
of actionSearch, actionList:
case f
of "installed", "i":
result.queryInstalled = true
of "ver":
result.queryVersions = true
else: else:
raise newException(NimbleError, "Unknown option: --" & flag) 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 and not isGlobalFlag:
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(
action: Action(typ: actionNil),
proc parseMisc(): Options = pkgInfoCache: newTable[string, PackageInfo](),
result = initOptions() verbosity: HighPriority,
result.config = parseConfig() noColor: not isatty(stdout)
)
proc parseMisc(options: var Options) =
# Load nimbledata.json # Load nimbledata.json
let nimbledataFilename = result.getNimbleDir() / "nimbledata.json" let nimbledataFilename = options.getNimbleDir() / "nimbledata.json"
if fileExists(nimbledataFilename): if fileExists(nimbledataFilename):
try: try:
result.nimbleData = parseFile(nimbledataFilename) options.nimbleData = parseFile(nimbledataFilename)
except: except:
raise newException(NimbleError, "Couldn't parse nimbledata.json file " & raise newException(NimbleError, "Couldn't parse nimbledata.json file " &
"located at " & nimbledataFilename) "located at " & nimbledataFilename)
else: else:
result.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 = parseMisc() result = initOptions()
# Parse command line params first. A simple `--version` shouldn't require
# a config to be parsed.
for kind, key, val in getOpt(): for kind, key, val in getOpt():
case kind case kind
of cmdArgument: of cmdArgument:
@ -288,15 +444,30 @@ proc parseCmdLine*(): Options =
else: else:
parseArgument(key, result) parseArgument(key, result)
of cmdLongOption, cmdShortOption: of cmdLongOption, cmdShortOption:
parseFlag(key, val, result) parseFlag(key, val, result, kind)
of cmdEnd: assert(false) # cannot happen of cmdEnd: assert(false) # cannot happen
if result.action.typ == actionNil:
writeHelp()
# TODO: Remove this after a couple of versions. handleUnknownFlags(result)
if getNimrodVersion() > newVersion("0.9.6"):
# Rename deprecated babel dir. # Set verbosity level.
renameBabelToNimble(result) setVerbosity(result.verbosity)
# Set whether color should be shown.
setShowColor(not result.noColor)
# Parse config.
result.config = parseConfig()
# Parse other things, for example the nimbledata.json file.
parseMisc(result)
if result.action.typ == actionNil and not result.showVersion:
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.
@ -309,9 +480,13 @@ 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:
echo("WARNING: Unable to parse proxy from environment: ", display("Warning:", "Unable to parse proxy from environment: " &
getCurrentExceptionMsg()) getCurrentExceptionMsg(), Warning, HighPriority)
if url.len > 0: if url.len > 0:
var parsed = parseUri(url) var parsed = parseUri(url)
@ -323,3 +498,54 @@ proc getProxy*(options: Options): Proxy =
return newProxy($parsed, auth) return newProxy($parsed, auth)
else: else:
return nil return nil
proc briefClone*(options: Options): Options =
## Clones the few important fields and creates a new Options object.
var newOptions = initOptions()
newOptions.config = options.config
newOptions.nimbleData = options.nimbleData
newOptions.nimbleDir = options.nimbleDir
newOptions.forcePrompts = options.forcePrompts
newOptions.pkgInfoCache = options.pkgInfoCache
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

@ -1,10 +1,15 @@
# 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, sets, tables
import version, tools, common, options # Stdlib imports
import system except TResult
import hashes, json, strutils, os, sets, tables, httpclient
# Local imports
import version, tools, common, options, cli, config
type type
Package* = object Package* = object ## Definition of package from packages.json.
# Required fields in a package. # Required fields in a package.
name*: string name*: string
url*: string # Download location. url*: string # Download location.
@ -16,12 +21,18 @@ type
version*: string version*: string
dvcsTag*: string dvcsTag*: string
web*: string # Info url for humans. web*: string # Info url for humans.
alias*: string ## A name of another package, that this package aliases.
MetaData* = object MetaData* = object
url*: string url*: string
NimbleLink* = object
nimbleFilePath*: string
packageDir*: string
proc initPackageInfo*(path: string): PackageInfo = proc initPackageInfo*(path: string): PackageInfo =
result.mypath = path result.myPath = path
result.specialVersion = ""
result.preHooks.init() result.preHooks.init()
result.postHooks.init() result.postHooks.init()
# reasonable default: # reasonable default:
@ -37,6 +48,7 @@ proc initPackageInfo*(path: string): PackageInfo =
result.installFiles = @[] result.installFiles = @[]
result.installExt = @[] result.installExt = @[]
result.requires = @[] result.requires = @[]
result.foreignDeps = @[]
result.bin = @[] result.bin = @[]
result.srcDir = "" result.srcDir = ""
result.binDir = "" result.binDir = ""
@ -57,22 +69,24 @@ proc getNameVersion*(pkgpath: string): tuple[name, version: string] =
## ##
## Also works for file paths like: ## Also works for file paths like:
## ``/home/user/.nimble/pkgs/package-0.1/package.nimble`` ## ``/home/user/.nimble/pkgs/package-0.1/package.nimble``
if pkgPath.splitFile.ext in [".nimble", ".nimble-link", ".babel"]:
if pkgPath.splitFile.ext == ".nimble" or pkgPath.splitFile.ext == ".babel":
return getNameVersion(pkgPath.splitPath.head) return getNameVersion(pkgPath.splitPath.head)
result.name = "" result.name = ""
result.version = "" result.version = ""
let tail = pkgpath.splitPath.tail let tail = pkgpath.splitPath.tail
if '-' notin tail:
const specialSeparator = "-#"
var sepIdx = tail.find(specialSeparator)
if sepIdx == -1:
sepIdx = tail.rfind('-')
if sepIdx == -1:
result.name = tail result.name = tail
return return
for i in countdown(tail.len-1, 0): result.name = tail[0 .. sepIdx - 1]
if tail[i] == '-': result.version = tail.substr(sepIdx + 1)
result.name = tail[0 .. i-1]
result.version = tail[i+1 .. tail.len-1]
break
proc optionalField(obj: JsonNode, name: string, default = ""): string = proc optionalField(obj: JsonNode, name: string, default = ""): string =
## Queries ``obj`` for the optional ``name`` string. ## Queries ``obj`` for the optional ``name`` string.
@ -92,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.")
@ -102,6 +116,10 @@ proc fromJson(obj: JSonNode): Package =
## ##
## Aborts execution if the JSON node doesn't contain the required fields. ## Aborts execution if the JSON node doesn't contain the required fields.
result.name = obj.requiredField("name") result.name = obj.requiredField("name")
if obj.hasKey("alias"):
result.alias = obj.requiredField("alias")
else:
result.alias = ""
result.version = obj.optionalField("version") result.version = obj.optionalField("version")
result.url = obj.requiredField("url") result.url = obj.requiredField("url")
result.downloadMethod = obj.requiredField("method") result.downloadMethod = obj.requiredField("method")
@ -116,43 +134,152 @@ proc fromJson(obj: JSonNode): Package =
proc readMetaData*(path: string): MetaData = proc readMetaData*(path: string): MetaData =
## Reads the metadata present in ``~/.nimble/pkgs/pkg-0.1/nimblemeta.json`` ## Reads the metadata present in ``~/.nimble/pkgs/pkg-0.1/nimblemeta.json``
var bmeta = path / "nimblemeta.json" var bmeta = path / "nimblemeta.json"
if not existsFile(bmeta):
bmeta = path / "babelmeta.json"
if existsFile(bmeta):
echo("WARNING: using deprecated babelmeta.json file in " & path)
if not existsFile(bmeta): if not existsFile(bmeta):
result.url = "" result.url = ""
echo("WARNING: No nimblemeta.json file found in " & path) display("Warning:", "No nimblemeta.json file found in " & path,
Warning, HighPriority)
return return
# TODO: Make this an error. # TODO: Make this an error.
let cont = readFile(bmeta) let cont = readFile(bmeta)
let jsonmeta = parseJson(cont) let jsonmeta = parseJson(cont)
result.url = jsonmeta["url"].str result.url = jsonmeta["url"].str
proc getPackage*(pkg: string, options: Options, proc readNimbleLink*(nimbleLinkPath: string): NimbleLink =
resPkg: var Package): bool = let s = readFile(nimbleLinkPath).splitLines()
result.nimbleFilePath = s[0]
result.packageDir = s[1]
proc writeNimbleLink*(nimbleLinkPath: string, contents: NimbleLink) =
let c = contents.nimbleFilePath & "\n" & contents.packageDir
writeFile(nimbleLinkPath, c)
proc needsRefresh*(options: Options): bool =
## Determines whether a ``nimble refresh`` is needed.
##
## In the future this will check a stored time stamp to determine how long
## ago the package list was refreshed.
result = true
for name, list in options.config.packageLists:
if fileExists(options.getNimbleDir() / "packages_" & name & ".json"):
result = false
proc validatePackagesList(path: string): bool =
## Determines whether package list at ``path`` is valid.
try:
let pkgList = parseFile(path)
if pkgList.kind == JArray:
if pkgList.len == 0:
display("Warning:", path & " contains no packages.", Warning,
HighPriority)
return true
except ValueError, JsonParsingError:
return false
proc fetchList*(list: PackageList, options: Options) =
## Downloads or copies the specified package list and saves it in $nimbleDir.
let verb = if list.urls.len > 0: "Downloading" else: "Copying"
display(verb, list.name & " package list", priority = HighPriority)
var
lastError = ""
copyFromPath = ""
if list.urls.len > 0:
for i in 0 ..< list.urls.len:
let url = list.urls[i]
display("Trying", url)
let tempPath = options.getNimbleDir() / "packages_temp.json"
# Grab the proxy
let proxy = getProxy(options)
if not proxy.isNil:
var maskedUrl = proxy.url
if maskedUrl.password.len > 0: maskedUrl.password = "***"
display("Connecting", "to proxy at " & $maskedUrl,
priority = LowPriority)
try:
let client = newHttpClient(proxy = proxy)
client.downloadFile(url, tempPath)
except:
let message = "Could not download: " & getCurrentExceptionMsg()
display("Warning:", message, Warning)
lastError = message
continue
if not validatePackagesList(tempPath):
lastError = "Downloaded packages.json file is invalid"
display("Warning:", lastError & ", discarding.", Warning)
continue
copyFromPath = tempPath
display("Success", "Package list downloaded.", Success, HighPriority)
lastError = ""
break
elif list.path != "":
if not validatePackagesList(list.path):
lastError = "Copied packages.json file is invalid"
display("Warning:", lastError & ", discarding.", Warning)
else:
copyFromPath = list.path
display("Success", "Package list copied.", Success, HighPriority)
if lastError.len != 0:
raise newException(NimbleError, "Refresh failed\n" & lastError)
if copyFromPath.len > 0:
copyFile(copyFromPath,
options.getNimbleDir() / "packages_$1.json" % list.name.toLowerAscii())
proc readPackageList(name: string, options: Options): JsonNode =
# If packages.json is not present ask the user if they want to download it.
if needsRefresh(options):
if options.prompt("No local packages.json found, download it from " &
"internet?"):
for name, list in options.config.packageLists:
fetchList(list, options)
else:
# The user might not need a package list for now. So let's try
# going further.
return newJArray()
return parseFile(options.getNimbleDir() / "packages_" &
name.toLowerAscii() & ".json")
proc getPackage*(pkg: string, options: Options, resPkg: var Package): bool
proc resolveAlias(pkg: Package, options: Options): Package =
result = pkg
# Resolve alias.
if pkg.alias.len > 0:
display("Warning:", "The $1 package has been renamed to $2" %
[pkg.name, pkg.alias], Warning, HighPriority)
if not getPackage(pkg.alias, options, result):
raise newException(NimbleError, "Alias for package not found: " &
pkg.alias)
proc getPackage*(pkg: string, options: Options, resPkg: var Package): bool =
## Searches any packages.json files defined in ``options.config.packageLists`` ## Searches any packages.json files defined in ``options.config.packageLists``
## Saves the found package into ``resPkg``. ## Saves the found package into ``resPkg``.
## ##
## Pass in ``pkg`` the name of the package you are searching for. As ## Pass in ``pkg`` the name of the package you are searching for. As
## convenience the proc returns a boolean specifying if the ``resPkg`` was ## convenience the proc returns a boolean specifying if the ``resPkg`` was
## successfully filled with good data. ## successfully filled with good data.
##
## Aliases are handled and resolved.
for name, list in options.config.packageLists: for name, list in options.config.packageLists:
echo("Searching in \"", name, "\" package list...") display("Reading", "$1 package list" % name, priority = LowPriority)
let packages = parseFile(options.getNimbleDir() / let packages = readPackageList(name, options)
"packages_" & name.toLower() & ".json")
for p in packages: for p in packages:
if normalize(p["name"].str) == normalize(pkg): if normalize(p["name"].str) == normalize(pkg):
resPkg = p.fromJson() resPkg = p.fromJson()
resPkg = resolveAlias(resPkg, options)
return true return true
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 = parseFile(options.getNimbleDir() / let packages = readPackageList(name, options)
"packages_" & name.toLower() & ".json")
for p in packages: for p in packages:
let pkg: Package = p.fromJson() let pkg: Package = p.fromJson()
if pkg.name notin namesAdded: if pkg.name notin namesAdded:
@ -163,10 +290,10 @@ proc findNimbleFile*(dir: string; error: bool): string =
result = "" result = ""
var hits = 0 var hits = 0
for kind, path in walkDir(dir): for kind, path in walkDir(dir):
if kind == pcFile: if kind in {pcFile, pcLinkToFile}:
let ext = path.splitFile.ext let ext = path.splitFile.ext
case ext case ext
of ".babel", ".nimble": of ".babel", ".nimble", ".nimble-link":
result = path result = path
inc hits inc hits
else: discard else: discard
@ -176,33 +303,73 @@ proc findNimbleFile*(dir: string; error: bool): string =
elif hits == 0: elif hits == 0:
if error: if error:
raise newException(NimbleError, raise newException(NimbleError,
"Specified directory does not contain a .nimble file.") "Specified directory ($1) does not contain a .nimble file." % dir)
else: else:
# TODO: Abstract logging. display("Warning:", "No .nimble or .nimble-link file found for " &
echo("WARNING: No .nimble file found for ", dir) dir, Warning, HighPriority)
if result.splitFile.ext == ".nimble-link":
# Return the path of the real .nimble file.
result = readNimbleLink(result).nimbleFilePath
if not fileExists(result):
let msg = "The .nimble-link file is pointing to a missing 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/ ## ``libsDir`` is in most cases: ~/.nimble/pkgs/ (options.getPkgsDir)
result = @[] result = @[]
for kind, path in walkDir(libsDir): for kind, path in walkDir(libsDir):
if kind == pcDir: if kind == pcDir:
let nimbleFile = findNimbleFile(path, false) let nimbleFile = findNimbleFile(path, false)
if nimbleFile != "": if nimbleFile != "":
let meta = readMetaData(path) let meta = readMetaData(path)
let (name, version) = getNameVersion(nimbleFile) let (name, version) = getNameVersion(path)
var pkg = initPackageInfo(nimbleFile) var pkg = initPackageInfo(nimbleFile)
pkg.name = name pkg.name = name
pkg.version = version pkg.version = version
pkg.specialVersion = version
pkg.isMinimal = true pkg.isMinimal = true
pkg.isInstalled = true pkg.isInstalled = true
let nimbleFileDir = nimbleFile.splitFile().dir
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 findPkg*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]], proc withinRange*(pkgInfo: PackageInfo, verRange: VersionRange): bool =
## Determines whether the specified package's version is within the
## specified range. The check works with ordinary versions as well as
## special ones.
return withinRange(newVersion(pkgInfo.version), verRange) or
withinRange(newVersion(pkgInfo.specialVersion), verRange)
proc resolveAlias*(dep: PkgTuple, options: Options): PkgTuple =
## Looks up the specified ``dep.name`` in the packages.json files to resolve
## a potential alias into the package's real name.
result = dep
var pkg: Package
# TODO: This needs better caching.
if getPackage(dep.name, options, pkg):
# The resulting ``pkg`` will contain the resolved name or the original if
# no alias is present.
result.name = pkg.name
proc findPkg*(pkglist: seq[tuple[pkgInfo: PackageInfo, meta: MetaData]],
dep: PkgTuple, dep: PkgTuple,
r: var PackageInfo): bool = r: var PackageInfo): bool =
## Searches ``pkglist`` for a package of which version is within the range ## Searches ``pkglist`` for a package of which version is within the range
@ -214,27 +381,27 @@ proc findPkg*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]],
for pkg in pkglist: for pkg in pkglist:
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(newVersion(pkg.pkginfo.version), dep.ver): if withinRange(pkg.pkgInfo, dep.ver):
if not result or newVersion(r.version) < newVersion(pkg.pkginfo.version): let isNewer = newVersion(r.version) < newVersion(pkg.pkginfo.version)
if not result or isNewer:
r = pkg.pkginfo r = pkg.pkginfo
result = true result = true
proc findAllPkgs*(pkglist: seq[tuple[pkginfo: PackageInfo, meta: MetaData]], proc findAllPkgs*(pkglist: seq[tuple[pkgInfo: PackageInfo, meta: MetaData]],
dep: PkgTuple): seq[PackageInfo] = dep: PkgTuple): seq[PackageInfo] =
## Searches ``pkglist`` for packages of which version is within the range ## Searches ``pkglist`` for packages of which version is within the range
## of ``dep.ver``. This is similar to ``findPkg`` but returns multiple ## of ``dep.ver``. This is similar to ``findPkg`` but returns multiple
## packages if multiple are found. ## packages if multiple are found.
result = @[] result = @[]
for pkg in pkglist: for pkg in pkglist:
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(newVersion(pkg.pkginfo.version), dep.ver): if withinRange(pkg.pkgInfo, dep.ver):
result.add pkg.pkginfo result.add pkg.pkginfo
proc getRealDir*(pkgInfo: PackageInfo): string = proc getRealDir*(pkgInfo: PackageInfo): string =
## Returns the ``pkgInfo.srcDir`` or the .mypath directory if package does ## Returns the directory containing the package source files.
## not specify the src dir. if pkgInfo.srcDir != "" and (not pkgInfo.isInstalled or pkgInfo.isLinked):
if pkgInfo.srcDir != "" and not pkgInfo.isInstalled:
result = pkgInfo.mypath.splitFile.dir / pkgInfo.srcDir result = pkgInfo.mypath.splitFile.dir / pkgInfo.srcDir
else: else:
result = pkgInfo.mypath.splitFile.dir result = pkgInfo.mypath.splitFile.dir
@ -248,6 +415,9 @@ proc getOutputDir*(pkgInfo: PackageInfo, bin: string): string =
proc echoPackage*(pkg: Package) = proc echoPackage*(pkg: Package) =
echo(pkg.name & ":") echo(pkg.name & ":")
if pkg.alias.len > 0:
echo(" Alias for ", pkg.alias)
else:
echo(" url: " & pkg.url & " (" & pkg.downloadMethod & ")") echo(" url: " & pkg.url & " (" & pkg.downloadMethod & ")")
echo(" tags: " & pkg.tags.join(", ")) echo(" tags: " & pkg.tags.join(", "))
echo(" description: " & pkg.description) echo(" description: " & pkg.description)
@ -262,26 +432,118 @@ proc getDownloadDirName*(pkg: Package, verRange: VersionRange): string =
result.add "_" result.add "_"
result.add verSimple result.add verSimple
proc needsRefresh*(options: Options): bool = proc checkInstallFile(pkgInfo: PackageInfo,
## Determines whether a ``nimble refresh`` is needed. origDir, file: string): bool =
## ## Checks whether ``file`` should be installed.
## In the future this will check a stored time stamp to determine how long ## ``True`` means file should be skipped.
## ago the package list was refreshed.
result = true
for name, list in options.config.packageLists:
if fileExists(options.getNimbleDir() / "packages_" & name & ".json"):
result = false
proc validatePackagesList*(path: string): bool = for ignoreFile in pkgInfo.skipFiles:
## Determines whether package list at ``path`` is valid. if ignoreFile.endswith("nimble"):
try: raise newException(NimbleError, ignoreFile & " must be installed.")
let pkgList = parseFile(path) if samePaths(file, origDir / ignoreFile):
if pkgList.kind == JArray: result = true
if pkgList.len == 0: break
echo("WARNING: ", path, " contains no packages.")
for ignoreExt in pkgInfo.skipExt:
if file.splitFile.ext == ('.' & ignoreExt):
result = true
break
if file.splitFile().name[0] == '.': result = true
proc checkInstallDir(pkgInfo: PackageInfo,
origDir, dir: string): bool =
## Determines whether ``dir`` should be installed.
## ``True`` means dir should be skipped.
for ignoreDir in pkgInfo.skipDirs:
if samePaths(dir, origDir / ignoreDir):
result = true
break
let thisDir = splitPath(dir).tail
assert thisDir != ""
if thisDir[0] == '.': result = true
if thisDir == "nimcache": result = true
proc iterFilesWithExt(dir: string, pkgInfo: PackageInfo,
action: proc (f: string)) =
## Runs `action` for each filename of the files that have a whitelisted
## file extension.
for kind, path in walkDir(dir):
if kind == pcDir:
iterFilesWithExt(path, pkgInfo, action)
else:
if path.splitFile.ext.substr(1) in pkgInfo.installExt:
action(path)
proc iterFilesInDir(dir: string, action: proc (f: string)) =
## Runs `action` for each file in ``dir`` and any
## subdirectories that are in it.
for kind, path in walkDir(dir):
if kind == pcDir:
iterFilesInDir(path, action)
else:
action(path)
proc iterInstallFiles*(realDir: string, pkgInfo: PackageInfo,
options: Options, action: proc (f: string)) =
## Runs `action` for each file within the ``realDir`` that should be
## installed.
let whitelistMode =
pkgInfo.installDirs.len != 0 or
pkgInfo.installFiles.len != 0 or
pkgInfo.installExt.len != 0
if whitelistMode:
for file in pkgInfo.installFiles:
let src = realDir / file
if not src.existsFile():
if options.prompt("Missing file " & src & ". Continue?"):
continue
else:
raise NimbleQuit(msg: "")
action(src)
for dir in pkgInfo.installDirs:
# TODO: Allow skipping files inside dirs?
let src = realDir / dir
if not src.existsDir():
if options.prompt("Missing directory " & src & ". Continue?"):
continue
else:
raise NimbleQuit(msg: "")
iterFilesInDir(src, action)
iterFilesWithExt(realDir, pkgInfo, action)
else:
for kind, file in walkDir(realDir):
if kind == pcDir:
let skip = pkgInfo.checkInstallDir(realDir, file)
if skip: continue
# we also have to stop recursing if we reach an in-place nimbleDir
if file == options.getNimbleDir().expandFilename(): continue
iterInstallFiles(file, pkgInfo, options, action)
else:
let skip = pkgInfo.checkInstallFile(realDir, file)
if skip: continue
action(file)
proc getPkgDest*(pkgInfo: PackageInfo, options: Options): string =
let versionStr = '-' & pkgInfo.specialVersion
let pkgDestDir = options.getPkgsDir() / (pkgInfo.name & versionStr)
return pkgDestDir
proc `==`*(pkg1: PackageInfo, pkg2: PackageInfo): bool =
if pkg1.name == pkg2.name and pkg1.myPath == pkg2.myPath:
return true return true
except ValueError, JsonParsingError:
return false 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") ==
@ -290,14 +552,12 @@ when isMainModule:
("package-a", "0.1") ("package-a", "0.1")
doAssert getNameVersion("/home/user/.nimble/libs/package-a-0.1/package.nimble") == doAssert getNameVersion("/home/user/.nimble/libs/package-a-0.1/package.nimble") ==
("package-a", "0.1") ("package-a", "0.1")
doAssert getNameVersion("/home/user/.nimble/libs/package-#head") ==
validatePackageName("foo_bar") ("package", "#head")
validatePackageName("f_oo_b_a_r") doAssert getNameVersion("/home/user/.nimble/libs/package-#branch-with-dashes") ==
try: ("package", "#branch-with-dashes")
validatePackageName("foo__bar") # readPackageInfo (and possibly more) depends on this not raising.
assert false doAssert getNameVersion("/home/user/.nimble/libs/package") == ("package", "")
except NimbleError:
assert true
doAssert toValidPackageName("foo__bar") == "foo_bar" doAssert toValidPackageName("foo__bar") == "foo_bar"
doAssert toValidPackageName("jhbasdh!£$@%#^_&*_()qwe") == "jhbasdh_qwe" doAssert toValidPackageName("jhbasdh!£$@%#^_&*_()qwe") == "jhbasdh_qwe"

View file

@ -0,0 +1,112 @@
# Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info.
import os, strutils, sets, json
# Local imports
import cli, options, tools
when defined(windows):
import version
when not declared(initHashSet) or not declared(toHashSet):
import common
when defined(windows):
# This is just for Win XP support.
# TODO: Drop XP support?
from winlean import WINBOOL, DWORD
type
OSVERSIONINFO* {.final, pure.} = object
dwOSVersionInfoSize*: DWORD
dwMajorVersion*: DWORD
dwMinorVersion*: DWORD
dwBuildNumber*: DWORD
dwPlatformId*: DWORD
szCSDVersion*: array[0..127, char]
proc GetVersionExA*(VersionInformation: var OSVERSIONINFO): WINBOOL{.stdcall,
dynlib: "kernel32", importc: "GetVersionExA".}
proc setupBinSymlink*(symlinkDest, symlinkFilename: string,
options: Options): seq[string] =
result = @[]
let currentPerms = getFilePermissions(symlinkDest)
setFilePermissions(symlinkDest, currentPerms + {fpUserExec})
when defined(unix):
display("Creating", "symlink: $1 -> $2" %
[symlinkDest, symlinkFilename], priority = MediumPriority)
if existsFile(symlinkFilename):
let msg = "Symlink already exists in $1. Replacing." % symlinkFilename
display("Warning:", msg, Warning, HighPriority)
removeFile(symlinkFilename)
createSymlink(symlinkDest, symlinkFilename)
result.add symlinkFilename.extractFilename
elif defined(windows):
# There is a bug on XP, described here:
# http://stackoverflow.com/questions/2182568/batch-script-is-not-executed-if-chcp-was-called
# But this workaround brakes code page on newer systems, so we need to detect OS version
var osver = OSVERSIONINFO()
osver.dwOSVersionInfoSize = cast[DWORD](sizeof(OSVERSIONINFO))
if GetVersionExA(osver) == WINBOOL(0):
raise newException(NimbleError,
"Can't detect OS version: GetVersionExA call failed")
let fixChcp = osver.dwMajorVersion <= 5
# Create cmd.exe/powershell stub.
let dest = symlinkFilename.changeFileExt("cmd")
display("Creating", "stub: $1 -> $2" % [symlinkDest, dest],
priority = MediumPriority)
var contents = "@"
if options.config.chcp:
if fixChcp:
contents.add "chcp 65001 > nul && "
else: contents.add "chcp 65001 > nul\n@"
contents.add "\"" & symlinkDest & "\" %*\n"
writeFile(dest, contents)
result.add dest.extractFilename
# For bash on Windows (Cygwin/Git bash).
let bashDest = dest.changeFileExt("")
display("Creating", "Cygwin stub: $1 -> $2" %
[symlinkDest, bashDest], priority = MediumPriority)
writeFile(bashDest, "\"" & symlinkDest & "\" \"$@\"\n")
result.add bashDest.extractFilename
else:
{.error: "Sorry, your platform is not supported.".}
proc saveNimbleMeta*(pkgDestDir, url, vcsRevision: string,
filesInstalled, bins: HashSet[string],
isLink: bool = false) =
## Saves the specified data into a ``nimblemeta.json`` file inside
## ``pkgDestDir``.
##
## filesInstalled - A list of absolute paths to files which have been
## installed.
## bins - A list of binary filenames which have been installed for this
## package.
##
## isLink - Determines whether the installed package is a .nimble-link.
var nimblemeta = %{"url": %url}
if vcsRevision.len > 0:
nimblemeta["vcsRevision"] = %vcsRevision
let files = newJArray()
nimblemeta["files"] = files
for file in filesInstalled:
files.add(%changeRoot(pkgDestDir, "", file))
let binaries = newJArray()
nimblemeta["binaries"] = binaries
for bin in bins:
binaries.add(%bin)
nimblemeta["isLink"] = %isLink
writeFile(pkgDestDir / "nimblemeta.json", $nimblemeta)
proc saveNimbleMeta*(pkgDestDir, pkgDir, vcsRevision, nimbleLinkPath: string) =
## Overload of saveNimbleMeta for linked (.nimble-link) packages.
##
## pkgDestDir - The directory where the package has been installed.
## For example: ~/.nimble/pkgs/jester-#head/
##
## pkgDir - The directory where the original package files are.
## For example: ~/projects/jester/
saveNimbleMeta(pkgDestDir, "file://" & pkgDir, vcsRevision,
toHashSet[string]([nimbleLinkPath]), initHashSet[string](), true)

View file

@ -1,31 +1,56 @@
# 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 import parsecfg, sets, streams, strutils, os, tables, sugar
import version, tools, common, nimscriptsupport, options, packageinfo from sequtils import apply, map
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``.
when not declared(system.map):
from sequtils import map
type type
NimbleFile* = string NimbleFile* = string
ValidationError* = object of NimbleError ValidationError* = object of NimbleError
warnInstalled*: bool # Determines whether to show a warning for installed pkgs warnInstalled*: bool # Determines whether to show a warning for installed pkgs
warnAll*: bool
proc newValidationError(msg: string, warnInstalled: bool): ref ValidationError = const reservedNames = [
"CON",
"PRN",
"AUX",
"NUL",
"COM1",
"COM2",
"COM3",
"COM4",
"COM5",
"COM6",
"COM7",
"COM8",
"COM9",
"LPT1",
"LPT2",
"LPT3",
"LPT4",
"LPT5",
"LPT6",
"LPT7",
"LPT8",
"LPT9",
]
proc newValidationError(msg: string, warnInstalled: bool,
hint: string, warnAll: bool): ref ValidationError =
result = newException(ValidationError, msg) result = newException(ValidationError, msg)
result.warnInstalled = warnInstalled result.warnInstalled = warnInstalled
result.warnAll = warnAll
result.hint = hint
proc raiseNewValidationError(msg: string, warnInstalled: bool) = proc raiseNewValidationError(msg: string, warnInstalled: bool,
if warnInstalled: hint: string = "", warnAll = false) =
# TODO: We warn everywhere for now. Raise the error in the next version. raise newValidationError(msg, warnInstalled, hint, warnAll)
echo("WARNING: ", msg, ". Will be an error in next version!")
else:
raise newValidationError(msg, warnInstalled)
proc validatePackageName*(name: string) = proc validatePackageName*(name: string) =
## Raises an error if specified package name contains invalid characters. ## Raises an error if specified package name contains invalid characters.
@ -54,6 +79,13 @@ proc validatePackageName*(name: string) =
else: else:
prevWasUnderscore = false prevWasUnderscore = false
if name.endsWith("pkg"):
raiseNewValidationError("\"$1\" is an invalid package name: cannot end" &
" with \"pkg\"" % name, false)
if name.toUpperAscii() in reservedNames:
raiseNewValidationError(
"\"$1\" is an invalid package name: reserved name" % name, false)
proc validateVersion*(ver: string) = proc validateVersion*(ver: string) =
for c in ver: for c in ver:
if c notin ({'.'} + Digits): if c notin ({'.'} + Digits):
@ -61,7 +93,74 @@ proc validateVersion*(ver: string) =
"Version may only consist of numbers and the '.' character " & "Version may only consist of numbers and the '.' character " &
"but found '" & c & "'.", false) "but found '" & c & "'.", false)
proc validatePackageInfo(pkgInfo: PackageInfo, path: string) = proc validatePackageStructure(pkgInfo: PackageInfo, options: Options) =
## This ensures that a package's source code does not leak into
## another package's namespace.
## https://github.com/nim-lang/nimble/issues/144
let
realDir = pkgInfo.getRealDir()
normalizedBinNames = pkgInfo.bin.map(
(x) => x.changeFileExt("").toLowerAscii()
)
correctDir =
if pkgInfo.name.toLowerAscii() in normalizedBinNames:
pkgInfo.name & "pkg"
else:
pkgInfo.name
proc onFile(path: string) =
# Remove the root to leave only the package subdirectories.
# ~/package-0.1/package/utils.nim -> package/utils.nim.
var trailPath = changeRoot(realDir, "", path)
if trailPath.startsWith(DirSep): trailPath = trailPath[1 .. ^1]
let (dir, file, ext) = trailPath.splitFile
# We're only interested in nim files, because only they can pollute our
# namespace.
if ext != (ExtSep & "nim"):
return
if dir.len == 0:
if file != pkgInfo.name:
# A source file was found in the top level of srcDir that doesn't share
# a name with the package.
let
msg = ("Package '$1' has an incorrect structure. " &
"The top level of the package source directory " &
"should contain at most one module, " &
"named '$2', but a file named '$3' was found. This " &
"will be an error in the future.") %
[pkgInfo.name, pkgInfo.name & ext, file & ext]
hint = ("If this is the primary source file in the package, " &
"rename it to '$1'. If it's a source file required by " &
"the main module, or if it is one of several " &
"modules exposed by '$4', then move it into a '$2' subdirectory. " &
"If it's a test file or otherwise not required " &
"to build the the package '$1', prevent its installation " &
"by adding `skipFiles = @[\"$3\"]` to the .nimble file. See " &
"https://github.com/nim-lang/nimble#libraries for more info.") %
[pkgInfo.name & ext, correctDir & DirSep, file & ext, pkgInfo.name]
raiseNewValidationError(msg, true, hint, true)
else:
assert(not pkgInfo.isMinimal)
# On Windows `pkgInfo.bin` has a .exe extension, so we need to normalize.
if not (dir.startsWith(correctDir & DirSep) or dir == correctDir):
let
msg = ("Package '$2' has an incorrect structure. " &
"It should contain a single directory hierarchy " &
"for source files, named '$3', but file '$1' " &
"is in a directory named '$4' instead. " &
"This will be an error in the future.") %
[file & ext, pkgInfo.name, correctDir, dir]
hint = ("If '$1' contains source files for building '$2', rename it " &
"to '$3'. Otherwise, prevent its installation " &
"by adding `skipDirs = @[\"$1\"]` to the .nimble file.") %
[dir, pkgInfo.name, correctDir]
raiseNewValidationError(msg, true, hint, true)
iterInstallFiles(realDir, pkgInfo, options, onFile)
proc validatePackageInfo(pkgInfo: PackageInfo, options: Options) =
let path = pkgInfo.myPath
if pkgInfo.name == "": if pkgInfo.name == "":
raiseNewValidationError("Incorrect .nimble file: " & path & raiseNewValidationError("Incorrect .nimble file: " & path &
" does not contain a name field.", false) " does not contain a name field.", false)
@ -88,14 +187,16 @@ proc validatePackageInfo(pkgInfo: PackageInfo, path: string) =
raiseNewValidationError("'" & pkgInfo.backend & raiseNewValidationError("'" & pkgInfo.backend &
"' is an invalid backend.", false) "' is an invalid backend.", false)
validateVersion(pkgInfo.version) validatePackageStructure(pkginfo, options)
proc nimScriptHint*(pkgInfo: PackageInfo) = proc nimScriptHint*(pkgInfo: PackageInfo) =
if not pkgInfo.isNimScript: if not pkgInfo.isNimScript:
# TODO: Turn this into a warning. display("Warning:", "The .nimble file for this project could make use of " &
# TODO: Add a URL explaining more. "additional features, if converted into the new NimScript format." &
echo("NOTE: The .nimble file for this project could make use of " & "\nFor more details see:" &
"additional features, if converted into the new NimScript format.") "https://github.com/nim-lang/nimble#creating-packages",
Warning, HighPriority)
proc multiSplit(s: string): seq[string] = proc multiSplit(s: string): seq[string] =
## Returns ``s`` split by newline and comma characters. ## Returns ``s`` split by newline and comma characters.
@ -105,13 +206,16 @@ proc multiSplit(s: string): seq[string] =
## done no entries are found in the list, the proc returns a sequence with ## done no entries are found in the list, the proc returns a sequence with
## the original string as the only entry. ## the original string as the only entry.
result = split(s, {char(0x0A), char(0x0D), ','}) result = split(s, {char(0x0A), char(0x0D), ','})
map(result, proc(x: var string) = x = x.strip()) apply(result, proc(x: var string) = x = x.strip())
for i in countdown(result.len()-1, 0): for i in countdown(result.len()-1, 0):
if len(result[i]) < 1: if len(result[i]) < 1:
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:
if s.strip().len != 0:
return @[s] 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)
@ -152,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.toLower() 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":
@ -176,7 +288,34 @@ 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 readPackageInfo*(nf: NimbleFile, options: Options, 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,
onlyMinimalInfo=false): PackageInfo = onlyMinimalInfo=false): PackageInfo =
## Reads package info from the specified Nimble file. ## Reads package info from the specified Nimble file.
## ##
@ -186,17 +325,20 @@ proc readPackageInfo*(nf: NimbleFile, options: Options,
## If both fail then returns an error. ## If both fail then returns an error.
## ##
## When ``onlyMinimalInfo`` is true, only the `name` and `version` fields are ## When ``onlyMinimalInfo`` is true, only the `name` and `version` fields are
## populated. The isNimScript field can also be relied on. ## populated. The ``isNimScript`` field can also be relied on.
## ##
## This version uses a cache stored in ``options``, so calling it multiple ## This version uses a cache stored in ``options``, so calling it multiple
## times on the same ``nf`` shouldn't require re-evaluation of the Nimble ## times on the same ``nf`` shouldn't require re-evaluation of the Nimble
## file. ## file.
assert fileExists(nf)
# Check the cache. # Check the cache.
if options.pkgInfoCache.hasKey(nf): if options.pkgInfoCache.hasKey(nf):
return options.pkgInfoCache[nf] return options.pkgInfoCache[nf]
result = initPackageInfo(nf) result = initPackageInfo(nf)
let minimalInfo = getNameVersion(nf)
validatePackageName(nf.splitFile.name) validatePackageName(nf.splitFile.name)
@ -212,61 +354,155 @@ proc readPackageInfo*(nf: NimbleFile, options: Options,
if not success: if not success:
if onlyMinimalInfo: if onlyMinimalInfo:
let tmp = getNameVersion(nf) result.name = minimalInfo.name
result.name = tmp.name result.version = minimalInfo.version
result.version = tmp.version
result.isNimScript = true result.isNimScript = true
result.isMinimal = true result.isMinimal = true
# It's possible this proc will receive a .nimble-link file eventually,
# I added this assert to hopefully make this error clear for everyone.
let msg = "No version detected. Received nimble-link?"
assert result.version.len > 0, msg
else: else:
try: try:
readPackageInfoFromNims(nf, options, result) readPackageInfoFromNims(nf, options, result)
result.isNimScript = true result.isNimScript = true
except NimbleError: except NimbleError as exc:
if exc.hint.len > 0:
raise
let msg = "Could not read package info file in " & nf & ";\n" & let msg = "Could not read package info file in " & nf & ";\n" &
" Reading as ini file failed with: \n" & " Reading as ini file failed with: \n" &
" " & iniError.msg & ".\n" & " " & iniError.msg & ".\n" &
" Evaluating as NimScript file failed with: \n" & " Evaluating as NimScript file failed with: \n" &
" " & getCurrentExceptionMsg() & "." " " & exc.msg & "."
raise newException(NimbleError, msg) raise newException(NimbleError, msg)
validatePackageInfo(result, nf) # By default specialVersion is the same as version.
result.specialVersion = result.version
# Only attempt to read a special version if `nf` is inside the $nimbleDir.
if nf.startsWith(options.getNimbleDir()):
# The package directory name may include a "special" version
# (example #head). If so, it is given higher priority and therefore
# overwrites the .nimble file's version.
let version = parseVersionRange(minimalInfo.version)
if version.kind == verSpecial:
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
# Validate the rest of the package info last.
if not options.disableValidation:
validateVersion(result.version)
validatePackageInfo(result, options)
proc validate*(file: NimbleFile, options: Options,
error: var ValidationError, pkgInfo: var PackageInfo): bool =
try:
pkgInfo = readPackageInfo(file, options)
except ValidationError as exc:
error = exc[]
return false
return true
proc getPkgInfoFromFile*(file: NimbleFile, options: Options): PackageInfo =
## Reads the specified .nimble file and returns its data as a PackageInfo
## object. Any validation errors are handled and displayed as warnings.
try:
result = readPackageInfo(file, options)
except ValidationError:
let exc = (ref ValidationError)(getCurrentException())
if exc.warnAll:
display("Warning:", exc.msg, Warning, HighPriority)
display("Hint:", exc.hint, Warning, HighPriority)
else:
raise
proc getPkgInfo*(dir: string, options: Options): PackageInfo = proc getPkgInfo*(dir: string, options: Options): PackageInfo =
## Find the .nimble file in ``dir`` and parses it, returning a PackageInfo. ## Find the .nimble file in ``dir`` and parses it, returning a PackageInfo.
let nimbleFile = findNimbleFile(dir, true) let nimbleFile = findNimbleFile(dir, true)
result = readPackageInfo(nimbleFile, options) return getPkgInfoFromFile(nimbleFile, options)
proc getInstalledPkgs*(libsDir: string, options: Options): proc getInstalledPkgs*(libsDir: string, options: Options):
seq[tuple[pkginfo: PackageInfo, meta: MetaData]] = seq[tuple[pkginfo: PackageInfo, meta: MetaData]] =
## Gets a list of installed packages. ## Gets a list of installed packages.
## ##
## ``libsDir`` is in most cases: ~/.nimble/pkgs/ ## ``libsDir`` is in most cases: ~/.nimble/pkgs/
const
readErrorMsg = "Installed package '$1@$2' is outdated or corrupt."
validationErrorMsg = readErrorMsg & "\nPackage did not pass validation: $3"
hintMsg = "The corrupted package will need to be removed manually. To fix" &
" this error message, remove $1."
proc createErrorMsg(tmplt, path, msg: string): string =
let (name, version) = getNameVersion(path)
return tmplt % [name, version, msg]
display("Loading", "list of installed packages", priority = MediumPriority)
result = @[] result = @[]
for kind, path in walkDir(libsDir): for kind, path in walkDir(libsDir):
if kind == pcDir: if kind == pcDir:
let nimbleFile = findNimbleFile(path, false) let nimbleFile = findNimbleFile(path, false)
if nimbleFile != "": if nimbleFile != "":
let meta = readMetaData(path) let meta = readMetaData(path)
var pkg: PackageInfo
try: try:
var pkg = readPackageInfo(nimbleFile, options, true) pkg = readPackageInfo(nimbleFile, options, onlyMinimalInfo=false)
pkg.isInstalled = true
result.add((pkg, meta))
except ValidationError: except ValidationError:
let exc = (ref ValidationError)(getCurrentException()) let exc = (ref ValidationError)(getCurrentException())
if exc.warnInstalled: exc.msg = createErrorMsg(validationErrorMsg, path, exc.msg)
echo("WARNING: Unable to read package info for " & path & "\n" & exc.hint = hintMsg % path
" Package did not pass validation: " & exc.msg) if exc.warnInstalled or exc.warnAll:
display("Warning:", exc.msg, Warning, HighPriority)
# Don't show hints here because they are only useful for package
# owners.
else: else:
exc.msg = "Unable to read package info for " & path & "\n" &
" Package did not pass validation: " & exc.msg
raise exc raise exc
except: except:
let exc = getCurrentException() let tmplt = readErrorMsg & "\nMore info: $3"
exc.msg = "Unable to read package info for " & path & "\n" & let msg = createErrorMsg(tmplt, path, getCurrentException().msg)
" Error: " & exc.msg var exc = newException(NimbleError, msg)
exc.hint = hintMsg % path
raise exc raise exc
pkg.isInstalled = true
pkg.isLinked =
cmpPaths(nimbleFile.splitFile().dir, path) != 0
result.add((pkg, meta))
proc isNimScript*(nf: string, options: Options): bool = proc isNimScript*(nf: string, options: Options): bool =
result = readPackageInfo(nf, options).isNimScript result = readPackageInfo(nf, options).isNimScript
proc toFullInfo*(pkg: PackageInfo, options: Options): PackageInfo =
if pkg.isMinimal:
result = getPkgInfoFromFile(pkg.mypath, options)
result.isInstalled = pkg.isInstalled
result.isLinked = pkg.isLinked
else:
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:
validatePackageName("foo_bar")
validatePackageName("f_oo_b_a_r")
try:
validatePackageName("foo__bar")
assert false
except NimbleError:
assert true
echo("Everything passed!")

View file

@ -4,63 +4,103 @@
## Implements 'nimble publish' to create a pull request against ## Implements 'nimble publish' to create a pull request against
## nim-lang/packages automatically. ## nim-lang/packages automatically.
import httpclient, base64, strutils, rdstdin, json, os, browsers, times, uri import system except TResult
import tools, common import httpclient, strutils, json, os, browsers, times, uri
import version, tools, common, cli, config, options
type type
Auth = object Auth = object
user: string user: string
pw: string token: string ## Github access token
token: string ## base64 encoding of user:pw http: HttpClient ## http client for doing API requests
const
ApiKeyFile = "github_api_token"
ApiTokenEnvironmentVariable = "NIMBLE_GITHUB_API_TOKEN"
ReposUrl = "https://api.github.com/repos/"
proc userAborted() = proc userAborted() =
raise newException(NimbleError, "User aborted the process.") raise newException(NimbleError, "User aborted the process.")
proc createHeaders(a: Auth): string = proc createHeaders(a: Auth) =
(("Authorization: token $1\c\L" % a.token) & a.http.headers = newHttpHeaders({
"Content-Type: application/x-www-form-urlencoded\c\L" & "Authorization": "token $1" % a.token,
"Accept: */*\c\L") "Content-Type": "application/x-www-form-urlencoded",
"Accept": "*/*"
})
proc getGithubAuth(): Auth = proc requestNewToken(cfg: Config): string =
echo("Please create a new personal access token on Github in order to " & display("Info:", "Please create a new personal access token on Github in" &
"allow Nimble to fork the packages repository.") " order to allow Nimble to fork the packages repository.",
priority = HighPriority)
display("Hint:", "Make sure to give the access token access to public repos" &
" (public_repo scope)!", Warning, HighPriority)
sleep(5000) sleep(5000)
echo("Your default browser should open with the following URL: " & display("Info:", "Your default browser should open with the following URL: " &
"https://github.com/settings/tokens/new") "https://github.com/settings/tokens/new", priority = HighPriority)
sleep(3000) sleep(3000)
openDefaultBrowser("https://github.com/settings/tokens/new") openDefaultBrowser("https://github.com/settings/tokens/new")
result.token = readLineFromStdin("Personal access token: ").strip() let token = promptCustom("Personal access token?", "").strip()
let resp = getContent("https://api.github.com/user", # inform the user that their token will be written to disk
extraHeaders=createHeaders(result)).parseJson() let tokenWritePath = cfg.nimbleDir / ApiKeyFile
display("Info:", "Writing access token to file:" & tokenWritePath,
priority = HighPriority)
writeFile(tokenWritePath, token)
sleep(3000)
return token
proc getGithubAuth(o: Options): Auth =
let cfg = o.config
result.http = newHttpClient(proxy = getProxy(o))
# always prefer the environment variable to asking for a new one
if existsEnv(ApiTokenEnvironmentVariable):
result.token = getEnv(ApiTokenEnvironmentVariable)
display("Info:", "Using the '" & ApiTokenEnvironmentVariable &
"' environment variable for the GitHub API Token.",
priority = HighPriority)
else:
# try to read from disk, if it cannot be found write a new one
try:
let apiTokenFilePath = cfg.nimbleDir / ApiKeyFile
result.token = readFile(apiTokenFilePath).strip()
display("Info:", "Using GitHub API Token in file: " & apiTokenFilePath,
priority = HighPriority)
except IOError:
result.token = requestNewToken(cfg)
createHeaders(result)
let resp = result.http.getContent("https://api.github.com/user").parseJson()
result.user = resp["login"].str result.user = resp["login"].str
echo("Successfully verified as ", result.user) display("Success:", "Verified as " & result.user, Success, HighPriority)
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 =
try: try:
let x = getContent("https://api.github.com/repos/" & a.user & "/packages", let x = a.http.getContent(ReposUrl & a.user & "/packages")
extraHeaders=createHeaders(a))
let j = parseJson(x) let j = parseJson(x)
result = isCorrectFork(j) result = isCorrectFork(j)
except JsonParsingError, IOError: except JsonParsingError, IOError:
result = false result = false
proc createFork(a: Auth) = proc createFork(a: Auth) =
discard postContent("https://api.github.com/repos/nim-lang/packages/forks", try:
extraHeaders=createHeaders(a)) discard a.http.postContent(ReposUrl & "nim-lang/packages/forks")
except HttpRequestError:
raise newException(NimbleError, "Unable to create fork. Access token" &
" might not have enough permissions.")
proc createPullRequest(a: Auth, packageName, branch: string) = proc createPullRequest(a: Auth, packageName, branch: string): string =
echo("Creating PR") display("Info", "Creating PR", priority = HighPriority)
discard postContent("https://api.github.com/repos/nim-lang/packages/pulls", var body = a.http.postContent(ReposUrl & "nim-lang/packages/pulls",
extraHeaders=createHeaders(a),
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()
@ -101,48 +141,43 @@ proc cleanupWhitespace(s: string): string =
proc editJson(p: PackageInfo; url, tags, downloadMethod: string) = proc editJson(p: PackageInfo; url, tags, downloadMethod: string) =
var contents = parseFile("packages.json") var contents = parseFile("packages.json")
doAssert contents.kind == JArray doAssert contents.kind == JArray
contents.add(%{ contents.add(%*{
"name": %p.name, "name": p.name,
"url": %url, "url": url,
"method": %downloadMethod, "method": downloadMethod,
"tags": %tags.split(), "tags": tags.split(),
"description": %p.description, "description": p.description,
"license": %p.license, "license": p.license,
"web": %url}) "web": url
})
writeFile("packages.json", contents.pretty.cleanupWhitespace) writeFile("packages.json", contents.pretty.cleanupWhitespace)
proc getPackageOriginUrl(a: Auth): string = proc publish*(p: PackageInfo, o: Options) =
## Adds 'user:pw' to the URL so that the user is not asked *again* for it.
## We need this for 'git push'.
let (output, exitCode) = doCmdEx("git config --get remote.origin.url")
result = "origin"
if exitCode == 0:
result = output.string.strip
if result.endsWith(".git"): result.setLen(result.len - 4)
if result.startsWith("https://"):
result = "https://" & a.user & ':' & a.pw & '@' &
result["https://".len .. ^1]
proc publish*(p: PackageInfo) =
## Publishes the package p. ## Publishes the package p.
let auth = getGithubAuth() 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)
echo "waiting 10s to let Github create a fork ..." display("Info:", "Waiting 10s to let Github create a fork",
priority = HighPriority)
os.sleep(10_000) os.sleep(10_000)
echo "... done" display("Info:", "Finished waiting", priority = LowPriority)
if dirExists(pkgsDir): if dirExists(pkgsDir):
echo("Removing old packages fork git directory.") display("Removing", "old packages fork git directory.",
priority = LowPriority)
removeDir(pkgsDir) removeDir(pkgsDir)
echo "Cloning packages into: ", pkgsDir createDir(pkgsDir)
doCmd("git clone git@github.com:" & auth.user & "/packages " & pkgsDir)
# Make sure to update the clone.
echo("Updating the fork...")
cd pkgsDir: cd pkgsDir:
# Avoid git clone to prevent token from being stored in repo
# https://github.com/blog/1270-easier-builds-and-deployments-using-git-over-https-and-oauth
display("Copying", "packages fork into: " & pkgsDir, priority = HighPriority)
doCmd("git init")
doCmd("git pull https://github.com/" & auth.user & "/packages")
# Make sure to update the fork
display("Updating", "the fork", priority = HighPriority)
doCmd("git pull https://github.com/nim-lang/packages.git master") doCmd("git pull https://github.com/nim-lang/packages.git master")
doCmd("git push origin master") doCmd("git push https://" & auth.token & "@github.com/" & auth.user & "/packages master")
if not dirExists(pkgsDir): if not dirExists(pkgsDir):
raise newException(NimbleError, raise newException(NimbleError,
@ -157,7 +192,7 @@ proc publish*(p: PackageInfo) =
var url = "" var url = ""
var downloadMethod = "" var downloadMethod = ""
if dirExists(os.getCurrentDir() / ".git"): if dirExists(os.getCurrentDir() / ".git"):
let (output, exitCode) = doCmdEx("git config --get remote.origin.url") let (output, exitCode) = doCmdEx("git ls-remote --get-url")
if exitCode == 0: if exitCode == 0:
url = output.string.strip url = output.string.strip
if url.endsWith(".git"): url.setLen(url.len - 4) if url.endsWith(".git"): url.setLen(url.len - 4)
@ -166,7 +201,7 @@ proc publish*(p: PackageInfo) =
if parsed.scheme == "": if parsed.scheme == "":
# Assuming that we got an ssh write/read URL. # Assuming that we got an ssh write/read URL.
let sshUrl = parseUri("ssh://" & url) let sshUrl = parseUri("ssh://" & url)
url = "https://github.com/" & sshUrl.port & sshUrl.path url = "https://" & sshUrl.hostname & "/" & sshUrl.port & sshUrl.path
elif dirExists(os.getCurrentDir() / ".hg"): elif dirExists(os.getCurrentDir() / ".hg"):
downloadMethod = "hg" downloadMethod = "hg"
# TODO: Retrieve URL from hg. # TODO: Retrieve URL from hg.
@ -175,22 +210,20 @@ proc publish*(p: PackageInfo) =
"No .git nor .hg directory found. Stopping.") "No .git nor .hg directory found. Stopping.")
if url.len == 0: if url.len == 0:
url = readLineFromStdin("Github URL of " & p.name & ": ") url = promptCustom("Github URL of " & p.name & "?", "")
if url.len == 0: userAborted() if url.len == 0: userAborted()
let tags = readLineFromStdin("Please enter a 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 & "\"")
echo("Pushing to remote of fork.") display("Pushing", "to remote of fork.", priority = HighPriority)
doCmd("git push " & getPackageOriginUrl(auth) & " " & 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)
echo "Pull request successful." display("Success:", "Pull request successful, check at " & prUrl , Success, HighPriority)
when isMainModule:
import packageinfo
var p = getPkgInfo(getCurrentDir())
publish(p)

View file

@ -0,0 +1,135 @@
# Copyright (C) Dominik Picheta. All rights reserved.
# BSD License. Look at license.txt for more info.
import os, json, sets
import options, common, version, download, packageinfo
proc saveNimbleData*(options: Options) =
# TODO: This file should probably be locked.
writeFile(options.getNimbleDir() / "nimbledata.json",
pretty(options.nimbleData))
proc addRevDep*(nimbleData: JsonNode, dep: tuple[name, version: string],
pkg: PackageInfo) =
# Add a record which specifies that `pkg` has a dependency on `dep`, i.e.
# the reverse dependency of `dep` is `pkg`.
if not nimbleData["reverseDeps"].hasKey(dep.name):
nimbleData["reverseDeps"][dep.name] = newJObject()
if not nimbleData["reverseDeps"][dep.name].hasKey(dep.version):
nimbleData["reverseDeps"][dep.name][dep.version] = newJArray()
let revDep = %{ "name": %pkg.name, "version": %pkg.specialVersion}
let thisDep = nimbleData["reverseDeps"][dep.name][dep.version]
if revDep notin thisDep:
thisDep.add revDep
proc removeRevDep*(nimbleData: JsonNode, pkg: PackageInfo) =
## Removes ``pkg`` from the reverse dependencies of every package.
assert(not pkg.isMinimal)
proc remove(pkg: PackageInfo, depTup: PkgTuple, thisDep: JsonNode) =
for ver, val in thisDep:
if ver.newVersion in depTup.ver:
var newVal = newJArray()
for revDep in val:
if not (revDep["name"].str == pkg.name and
revDep["version"].str == pkg.specialVersion):
newVal.add revDep
thisDep[ver] = newVal
for depTup in pkg.requires:
if depTup.name.isURL():
# We sadly must go through everything in this case...
for key, val in nimbleData["reverseDeps"]:
remove(pkg, depTup, val)
else:
let thisDep = nimbleData{"reverseDeps", depTup.name}
if thisDep.isNil: continue
remove(pkg, depTup, thisDep)
# Clean up empty objects/arrays
var newData = newJObject()
for key, val in nimbleData["reverseDeps"]:
if val.len != 0:
var newVal = newJObject()
for ver, elem in val:
if elem.len != 0:
newVal[ver] = elem
if newVal.len != 0:
newData[key] = newVal
nimbleData["reverseDeps"] = newData
proc getRevDepTups*(options: Options, pkg: PackageInfo): seq[PkgTuple] =
## Returns a list of *currently installed* reverse dependencies for `pkg`.
result = @[]
let thisPkgsDep =
options.nimbleData["reverseDeps"]{pkg.name}{pkg.specialVersion}
if not thisPkgsDep.isNil:
let pkgList = getInstalledPkgsMin(options.getPkgsDir(), options)
for pkg in thisPkgsDep:
let pkgTup = (
name: pkg["name"].getStr(),
ver: parseVersionRange(pkg["version"].getStr())
)
var pkgInfo: PackageInfo
if not findPkg(pkgList, pkgTup, pkgInfo):
continue
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:
var nimbleData = %{"reverseDeps": newJObject()}
let nimforum1 = PackageInfo(
isMinimal: false,
name: "nimforum",
specialVersion: "0.1.0",
requires: @[("jester", parseVersionRange("0.1.0")),
("captcha", parseVersionRange("1.0.0")),
("auth", parseVersionRange("#head"))]
)
let nimforum2 = PackageInfo(isMinimal: false, name: "nimforum", specialVersion: "0.2.0")
let play = PackageInfo(isMinimal: false, name: "play", specialVersion: "#head")
nimbleData.addRevDep(("jester", "0.1.0"), nimforum1)
nimbleData.addRevDep(("jester", "0.1.0"), play)
nimbleData.addRevDep(("captcha", "1.0.0"), nimforum1)
nimbleData.addRevDep(("auth", "#head"), nimforum1)
nimbleData.addRevDep(("captcha", "1.0.0"), nimforum2)
nimbleData.addRevDep(("auth", "#head"), nimforum2)
doAssert nimbleData["reverseDeps"]["jester"]["0.1.0"].len == 2
doAssert nimbleData["reverseDeps"]["captcha"]["1.0.0"].len == 2
doAssert nimbleData["reverseDeps"]["auth"]["#head"].len == 2
block:
nimbleData.removeRevDep(nimforum1)
let jester = nimbleData["reverseDeps"]["jester"]["0.1.0"][0]
doAssert jester["name"].getStr() == play.name
doAssert jester["version"].getStr() == play.specialVersion
let captcha = nimbleData["reverseDeps"]["captcha"]["1.0.0"][0]
doAssert captcha["name"].getStr() == nimforum2.name
doAssert captcha["version"].getStr() == nimforum2.specialVersion
echo("Everything works!")

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, packageinfo, common 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) = 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,11 +20,26 @@ proc doCmd*(cmd: string) =
stdout.flushFile() stdout.flushFile()
stderr.flushFile() stderr.flushFile()
if showCmd:
display("Executing", cmd, priority = MediumPriority)
else:
displayDebug("Executing", cmd)
if showOutput:
let exitCode = execCmd(cmd) let exitCode = execCmd(cmd)
displayDebug("Finished", "with exit code " & $exitCode)
if exitCode != QuitSuccess:
raise newException(NimbleError,
"Execution failed with exit code $1\nCommand: $2" %
[$exitCode, cmd])
else:
let (output, exitCode) = execCmdEx(cmd)
displayDebug("Finished", "with exit code " & $exitCode)
displayDebug("Output", output)
if exitCode != QuitSuccess: if exitCode != QuitSuccess:
raise newException(NimbleError, raise newException(NimbleError,
"Execution failed with exit code " & $exitCode) "Execution failed with exit code $1\nCommand: $2\nOutput: $3" %
[$exitCode, cmd, output])
proc doCmdEx*(cmd: string): tuple[output: TaintedString, exitCode: int] = proc doCmdEx*(cmd: string): tuple[output: TaintedString, exitCode: int] =
let bin = extractBin(cmd) let bin = extractBin(cmd)
@ -32,7 +47,7 @@ proc doCmdEx*(cmd: string): tuple[output: TaintedString, exitCode: int] =
raise newException(NimbleError, "'" & bin & "' not in PATH.") raise newException(NimbleError, "'" & bin & "' not in PATH.")
return execCmdEx(cmd) return execCmdEx(cmd)
template cd*(dir: string, body: stmt) = template cd*(dir: string, body: untyped) =
## Sets the current dir to ``dir``, executes ``body`` and restores the ## Sets the current dir to ``dir``, executes ``body`` and restores the
## previous working dir. ## previous working dir.
let lastDir = getCurrentDir() let lastDir = getCurrentDir()
@ -50,7 +65,7 @@ proc getNimrodVersion*: Version =
let vOutput = doCmdEx('"' & nimBin & "\" -v").output let vOutput = doCmdEx('"' & nimBin & "\" -v").output
var matches: array[0..MaxSubpatterns, string] var matches: array[0..MaxSubpatterns, string]
if vOutput.find(peg"'Version'\s{(\d+\.)+\d}", matches) == -1: if vOutput.find(peg"'Version'\s{(\d+\.)+\d}", matches) == -1:
quit("Couldn't find Nim version.", QuitFailure) raise newException(NimbleError, "Couldn't find Nim version.")
newVersion(matches[0]) newVersion(matches[0])
proc samePaths*(p1, p2: string): bool = proc samePaths*(p1, p2: string): bool =
@ -67,26 +82,37 @@ proc changeRoot*(origRoot, newRoot, path: string): string =
## newRoot: /home/test/ ## newRoot: /home/test/
## path: /home/dom/bar/blah/2/foo.txt ## path: /home/dom/bar/blah/2/foo.txt
## Return value -> /home/test/bar/blah/2/foo.txt ## Return value -> /home/test/bar/blah/2/foo.txt
if path.startsWith(origRoot):
return newRoot / path[origRoot.len .. path.len-1] ## The additional check of `path.samePaths(origRoot)` is necessary to prevent
## a regression, where by ending the `srcDir` defintion in a nimble file in a
## trailing separator would cause the `path.startsWith(origRoot)` evaluation to
## fail because of the value of `origRoot` would be longer than `path` due to
## the trailing separator. This would cause this method to throw during package
## installation.
if path.startsWith(origRoot) or path.samePaths(origRoot):
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.")
proc copyFileD*(fro, to: string): string = proc copyFileD*(fro, to: string): string =
## Returns the destination (``to``). ## Returns the destination (``to``).
echo(fro, " -> ", to) display("Copying", "file $# to $#" % [fro, to], priority = LowPriority)
copyFileWithPermissions(fro, to) copyFileWithPermissions(fro, to)
result = to result = to
proc copyDirD*(fro, to: string): seq[string] = proc copyDirD*(fro, to: string): seq[string] =
## Returns the filenames of the files in the directory that were copied. ## Returns the filenames of the files in the directory that were copied.
result = @[] result = @[]
echo("Copying directory: ", fro, " -> ", to) display("Copying", "directory $# to $#" % [fro, to], priority = LowPriority)
for path in walkDirRec(fro): for path in walkDirRec(fro):
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 = ""
@ -122,3 +148,37 @@ proc contains*(j: JsonNode, elem: tuple[key: string, val: JsonNode]): bool =
for key, val in pairs(j): for key, val in pairs(j):
if key == elem.key and val == elem.val: if key == elem.key and val == elem.val:
return true return true
when not defined(windows):
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 =
## Returns a path to a temporary directory.
##
## 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.
result = getTempDir() / "nimble_" & getProcessId()
proc getNimbleUserTempDir*(): string =
## Returns a path to a temporary directory.
##
## 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:
tmpdir = getTempDir()
return tmpdir

View file

@ -5,7 +5,6 @@
import strutils, tables, hashes, parseutils import strutils, tables, hashes, parseutils
type type
Version* = distinct string Version* = distinct string
Special* = distinct string
VersionRangeEnum* = enum VersionRangeEnum* = enum
verLater, # > V verLater, # > V
@ -23,7 +22,7 @@ type
of verLater, verEarlier, verEqLater, verEqEarlier, verEq: of verLater, verEarlier, verEqLater, verEqEarlier, verEq:
ver*: Version ver*: Version
of verSpecial: of verSpecial:
spe*: Special spe*: Version
of verIntersect: of verIntersect:
verILeft, verIRight: VersionRange verILeft, verIRight: VersionRange
of verAny: of verAny:
@ -34,19 +33,32 @@ type
ParseVersionError* = object of ValueError ParseVersionError* = object of ValueError
NimbleError* = object of Exception NimbleError* = object of Exception
hint*: string
proc newVersion*(ver: string): Version = return Version(ver)
proc newSpecial*(spe: string): Special = return Special(spe)
proc `$`*(ver: Version): string {.borrow.} proc `$`*(ver: Version): string {.borrow.}
proc hash*(ver: Version): THash {.borrow.} proc hash*(ver: Version): Hash {.borrow.}
proc `$`*(ver: Special): string {.borrow.} proc newVersion*(ver: string): Version =
doAssert(ver.len == 0 or ver[0] in {'#', '\0'} + Digits,
"Wrong version: " & ver)
return Version(ver)
proc hash*(ver: Special): THash {.borrow.} proc isSpecial*(ver: Version): bool =
return ($ver).len > 0 and ($ver)[0] == '#'
proc `<`*(ver: Version, ver2: Version): bool = proc `<`*(ver: Version, ver2: Version): bool =
# Handling for special versions such as "#head" or "#branch".
if ver.isSpecial or ver2.isSpecial:
# TODO: This may need to be reverted. See #311.
if ver2.isSpecial and ($ver2).normalize == "#head":
return ($ver).normalize != "#head"
if not ver2.isSpecial:
# `#aa111 < 1.1`
return ($ver).normalize != "#head"
# Handling for normal versions such as "0.1.0" or "1.0".
var sVer = string(ver).split('.') var sVer = string(ver).split('.')
var sVer2 = string(ver2).split('.') var sVer2 = string(ver2).split('.')
for i in 0..max(sVer.len, sVer2.len)-1: for i in 0..max(sVer.len, sVer2.len)-1:
@ -64,6 +76,9 @@ proc `<`*(ver: Version, ver2: Version): bool =
return false return false
proc `==`*(ver: Version, ver2: Version): bool = proc `==`*(ver: Version, ver2: Version): bool =
if ver.isSpecial or ver2.isSpecial:
return ($ver).toLowerAscii() == ($ver2).toLowerAscii()
var sVer = string(ver).split('.') var sVer = string(ver).split('.')
var sVer2 = string(ver2).split('.') var sVer2 = string(ver2).split('.')
for i in 0..max(sVer.len, sVer2.len)-1: for i in 0..max(sVer.len, sVer2.len)-1:
@ -78,8 +93,10 @@ proc `==`*(ver: Version, ver2: Version): bool =
else: else:
return false return false
proc `==`*(spe: Special, spe2: Special): bool = proc cmp*(a, b: Version): int =
return ($spe).toLower() == ($spe2).toLower() 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)
@ -108,64 +125,54 @@ proc withinRange*(ver: Version, ran: VersionRange): bool =
of verEq: of verEq:
return ver == ran.ver return ver == ran.ver
of verSpecial: of verSpecial:
return false return ver == ran.spe
of verIntersect: of verIntersect:
return withinRange(ver, ran.verILeft) and withinRange(ver, ran.verIRight) return withinRange(ver, ran.verILeft) and withinRange(ver, ran.verIRight)
of verAny: of verAny:
return true return true
proc withinRange*(spe: Special, ran: VersionRange): bool =
case ran.kind
of verLater, verEarlier, verEqLater, verEqEarlier, verEq, verIntersect:
return false
of verSpecial:
return spe == ran.spe
of verAny:
return true
proc contains*(ran: VersionRange, ver: Version): bool = proc contains*(ran: VersionRange, ver: Version): bool =
return withinRange(ver, ran) return withinRange(ver, ran)
proc contains*(ran: VersionRange, spe: Special): bool =
return withinRange(spe, 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:
result = VersionRange(kind: verAny)
return
if s[0] == '#': if s[0] == '#':
result.kind = verSpecial result = VersionRange(kind: verSpecial)
result.spe = s[1 .. s.len-1].Special 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 &
@ -178,27 +185,33 @@ 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.")
else: else:
raise newException(ParseVersionError, raise newException(ParseVersionError,
"Unexpected char in version range: " & s[i]) "Unexpected char in version range '" & s & "': " & s[i])
inc(i) inc(i)
result = makeRange(version, op)
proc toVersionRange*(ver: Version): VersionRange =
## Converts a version to either a verEq or verSpecial VersionRange.
new(result)
if ver.isSpecial:
result = VersionRange(kind: verSpecial)
result.spe = ver
else:
result = VersionRange(kind: verEq)
result.ver = ver
proc parseRequires*(req: string): PkgTuple = proc parseRequires*(req: string): PkgTuple =
try: try:
@ -230,7 +243,7 @@ proc `$`*(verRange: VersionRange): string =
of verEq: of verEq:
result = "" result = ""
of verSpecial: of verSpecial:
return "#" & $verRange.spe return $verRange.spe
of verIntersect: of verIntersect:
return $verRange.verILeft & " & " & $verRange.verIRight return $verRange.verILeft & " & " & $verRange.verIRight
of verAny: of verAny:
@ -253,41 +266,42 @@ 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
if ver > result.ver: if ver > result.ver:
result = (ver, tag) result = (ver, tag)
proc `$`*(dep: PkgTuple): string =
return dep.name & "@" & $dep.ver
when isMainModule: 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))
@ -301,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")
@ -311,13 +328,34 @@ when isMainModule:
#doAssert newVersion("0.1-rc1") < newVersion("0.1") #doAssert newVersion("0.1-rc1") < newVersion("0.1")
# Special tests # Special tests
doAssert newSpecial("ab26sgdt362") != newSpecial("ab26saggdt362") doAssert newVersion("#ab26sgdt362") != newVersion("#qwersaggdt362")
doAssert newSpecial("ab26saggdt362") == newSpecial("ab26saggdt362") doAssert newVersion("#ab26saggdt362") == newVersion("#ab26saggdt362")
doAssert newSpecial("head") == newSpecial("HEAD") doAssert newVersion("#head") == newVersion("#HEAD")
doAssert newSpecial("head") == newSpecial("head") doAssert newVersion("#head") == newVersion("#head")
var sp = parseVersionRange("#ab26sgdt362") var sp = parseVersionRange("#ab26sgdt362")
doAssert newSpecial("ab26sgdt362") in sp doAssert newVersion("#ab26sgdt362") in sp
doAssert newSpecial("ab26saggdt362") notin sp doAssert newVersion("#ab26saggdt362") notin sp
doAssert newVersion("#head") in parseVersionRange("#head")
# We assume that #head > 0.1.0, in practice this shouldn't be a problem.
doAssert(newVersion("#head") > newVersion("0.1.0"))
doAssert(not(newVersion("#head") > newVersion("#head")))
doAssert(withinRange(newVersion("#head"), parseVersionRange(">= 0.5.0")))
doAssert newVersion("#a111") < newVersion("#head")
# We assume that all other special versions are not higher than a normal
# version.
doAssert newVersion("#a111") < newVersion("1.1")
# An empty version range should give verAny
doAssert parseVersionRange("").kind == verAny
# toVersionRange tests
doAssert toVersionRange(newVersion("#head")).kind == verSpecial
doAssert toVersionRange(newVersion("0.2.0")).kind == verEq
# Something raised on IRC
doAssert newVersion("1") == newVersion("1.0")
echo("Everything works!") echo("Everything works!")

23
tests/.gitignore vendored Normal file
View file

@ -0,0 +1,23 @@
tester
/nimble-test
/buildDir
/binaryPackage/v1/binaryPackage
/binaryPackage/v2/binaryPackage
/develop/dependent/src/dependent
/issue27/issue27
/issue206/issue/issue206bin
/issue289/issue289
/issue428/nimbleDir/
/nimbleDir/
/packageStructure/c/c
/packageStructure/y/y
/testCommand/testOverride/myTester
/testCommand/testsFail/tests/a
/testCommand/testsFail/tests/b
/testCommand/testsPass/tests/one
/testCommand/testsPass/tests/three
/testCommand/testsPass/tests/two
/nimscript/nimscript
/packageStructure/validBinary/y
/testCommand/testsFail/tests/t2
/passNimFlags/passNimFlags

View file

@ -0,0 +1 @@
echo("v1")

View file

@ -0,0 +1,12 @@
# Package
version = "1.0"
author = "Dominik Picheta"
description = "binary"
license = "MIT"
bin = @["binaryPackage"]
# Dependencies
requires "nim >= 0.15.3"

View file

@ -0,0 +1 @@
echo("v2")

View file

@ -0,0 +1,12 @@
# Package
version = "2.0"
author = "Dominik Picheta"
description = "binary"
license = "MIT"
bin = @["binaryPackage"]
# Dependencies
requires "nim >= 0.15.3"

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

@ -0,0 +1 @@
echo("hello")

View file

@ -0,0 +1,14 @@
# Package
version = "1.0"
author = "Dominik Picheta"
description = "binary"
license = "MIT"
bin = @["binary"]
skipExt = @["nim"]
# Dependencies
requires "nim >= 0.16.0"

View file

@ -0,0 +1,12 @@
# Package
version = "1.0"
author = "Dominik Picheta"
description = "dependent"
license = "MIT"
srcDir = "src"
# Dependencies
requires "nim >= 0.16.0", "srcdirtest"

View file

@ -0,0 +1,3 @@
import srcdirtest
doAssert foo() == "correct"

View file

@ -0,0 +1 @@
echo("hello")

View file

@ -0,0 +1,13 @@
# Package
version = "1.0"
author = "Dominik Picheta"
description = "hybrid"
license = "MIT"
bin = @["hybrid"]
installExt = @["nim"]
# Dependencies
requires "nim >= 0.16.0"

View file

@ -0,0 +1,4 @@
proc foo*(): string =
return "correct"
echo("hello")

View file

@ -0,0 +1,12 @@
# Package
version = "1.0"
author = "Dominik Picheta"
description = "srcdir"
license = "MIT"
srcDir = "src"
# Dependencies
requires "nim >= 0.16.0"

View file

@ -0,0 +1,14 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "a"
license = "MIT"
# Dependencies
requires "nim >= 0.15.3", "b", "c"
task test, "test":
echo("hello")

View file

@ -0,0 +1,11 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "b"
license = "MIT"
# Dependencies
requires "nim >= 0.15.3", "d"

View file

@ -0,0 +1,11 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "c"
license = "MIT"
# Dependencies
requires "nim >= 0.15.3", "d"

View file

@ -0,0 +1,11 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "d"
license = "MIT"
# Dependencies
requires "nim >= 0.15.3"

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 @@
echo 42

View file

@ -0,0 +1,14 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "Package reproducing issues depending on #head and concrete version of the same package."
license = "MIT"
bin = @["issue289"]
# Dependencies
requires "nim >= 0.15.0", "https://github.com/nimble-test/packagea.git 0.6.0"
requires "https://github.com/nimble-test/packagea.git#head"

View file

@ -0,0 +1,11 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "Package for ensuring that issue #304 is resolved."
license = "MIT"
# Dependencies
requires "nim >= 0.15.3"

View file

@ -0,0 +1,7 @@
# Package
version = "0.1.0"
author = "Samantha Marshall"
description = "test case to validate successful install when `srcDir` value ends in a directory separator"
license = "MIT"
srcDir = "src/"

View file

View file

@ -0,0 +1,14 @@
[
{
"name": "discordnim",
"url": "https://github.com/Krognol/discordnim",
"method": "git",
"tags": [
"library",
"discord"
],
"description": "Discord library for Nim",
"license": "MIT",
"web": "https://github.com/Krognol/discordnim"
}
]

View file

@ -0,0 +1,10 @@
# Package
version = "0.1.0"
author = "Author"
description = "dummy"
license = "MIT"
# Dependencies
requires "nim >= 0.17.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"]
@ -11,7 +13,7 @@ bin = @["nimscript"]
requires "nim >= 0.12.1" requires "nim >= 0.12.1"
task test, "test description": task work, "test description":
echo(5+5) echo(5+5)
task c_test, "Testing `setCommand \"c\", \"nimscript.nim\"`": task c_test, "Testing `setCommand \"c\", \"nimscript.nim\"`":
@ -21,8 +23,16 @@ task cr, "Testing `nimble c -r nimscript.nim` via setCommand":
--r --r
setCommand "c", "nimscript.nim" setCommand "c", "nimscript.nim"
task repeated, "Testing `nimble c nimscript.nim` with repeated flags":
--define: foo
--define: bar
--define: "quoted"
--define: "quoted\\\"with\\\"quotes"
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")
@ -38,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 @@

View file

@ -0,0 +1,11 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "Correctly structured package A"
license = "MIT"
# Dependencies
requires "nim >= 0.15.0"

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,11 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "Correctly structured package B"
license = "MIT"
# Dependencies
requires "nim >= 0.15.0"

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@

View file

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

View file

@ -0,0 +1 @@

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

@ -0,0 +1,11 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "Incorrectly structured package X."
license = "MIT"
# Dependencies
requires "nim >= 0.15.0"

View file

View file

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

View file

@ -0,0 +1 @@

View file

View file

@ -0,0 +1,11 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "Incorrect package structure Z."
license = "MIT"
# Dependencies
requires "nim >= 0.15.0"

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

View file

@ -0,0 +1,10 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "Random dep"
license = "MIT"
# Dependencies
requires "nim >= 0.15.0"

View file

@ -0,0 +1,11 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "Correctly structured package A"
license = "MIT"
# Dependencies
requires "nim >= 0.15.0"

View file

@ -0,0 +1,11 @@
# Package
version = "0.1.0"
author = "Dominik Picheta"
description = "Correctly structured package A"
license = "MIT"
# Dependencies
requires "nim >= 0.15.0", "mydep"

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 @@
echo("overriden")

View file

@ -0,0 +1,9 @@
version = "0.1.0"
author = "John Doe"
description = "Nimble Test"
license = "BSD"
skipFiles = @["myTester.nim"]
task test, "Custom tester":
exec "nim c -r myTester.nim"

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())

Some files were not shown because too many files have changed in this diff Show more