From 7d5428be19b5a958a677a5b7363889974b73b809 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Wed, 23 Dec 2015 22:40:14 +0000 Subject: [PATCH] Improved the init command. Fixes #96. --- src/nimble.nim | 105 +++++++++++++++++++---------- src/nimblepkg/nim.cfg | 2 + src/nimblepkg/nimscriptsupport.nim | 2 + src/nimblepkg/packageinfo.nim | 63 ++++++++++++++--- 4 files changed, 130 insertions(+), 42 deletions(-) create mode 100644 src/nimblepkg/nim.cfg diff --git a/src/nimble.nim b/src/nimble.nim index b06268e..97ed2e5 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -151,6 +151,18 @@ proc prompt(options: Options, question: string): bool = else: return false +proc promptCustom(question, default: string): string = + if default == "": + stdout.write(question, ": ") + let user = stdin.readLine() + if user.len == 0: return promptCustom(question, default) + else: return user + else: + stdout.write(question, " [", default, "]: ") + let user = stdin.readLine() + if user == "": return default + else: return user + proc renameBabelToNimble(options: Options) {.deprecated.} = let babelDir = getHomeDir() / ".babel" let nimbleDir = getHomeDir() / ".nimble" @@ -884,13 +896,6 @@ proc listPaths(options: Options) = raise newException(NimbleError, "At least one of the specified packages was not found") -proc guessAuthor(): string = - if dirExists(os.getCurrentDir() / ".git"): - let (output, exitCode) = doCmdEx("git config user.name") - if exitCode == 0: - return output.string.strip - return "Anonymous" - proc join(x: seq[PkgTuple]; y: string): string = if x.len == 0: return "" result = x[0][0] & " " & $x[0][1] @@ -899,7 +904,7 @@ proc join(x: seq[PkgTuple]; y: string): string = result.add x[i][0] & " " & $x[i][1] proc dump(options: Options) = - let proj = addFileExt(options.action.projName, NimsExt) + let proj = addFileExt(options.action.projName, "nimble") let p = if fileExists(proj): readPackageInfo(proj) else: getPkgInfo(os.getCurrentDir()) echo "name: ", p.name.escape @@ -920,44 +925,76 @@ proc dump(options: Options) = echo "backend: ", p.backend.escape proc init(options: Options) = - echo("Initializing new Nimble project!") - var - pkgName, fName: string = "" - outFile: File + var nimbleFile: string = "" + echo("In order to initialise a new Nimble package, I will need to ask you\n" & + "some questions. Default values are shown in square brackets, press\n" & + "enter to use them.") + + # Ask for package name. if options.action.projName != "": - pkgName = options.action.projName - fName = pkgName & NimsExt - if (existsFile(os.getCurrentDir() / fName)): - raise newException(NimbleError, "Already have a nimscript file.") + let pkgName = options.action.projName + nimbleFile = pkgName.changeFileExt("nimble") else: - echo("Enter a project name for this (blank to use working directory), " & - "Ctrl-C to abort:") - pkgName = readline(stdin) - if pkgName == "": - pkgName = os.getCurrentDir().splitPath.tail - if pkgName == "": - raise newException(NimbleError, "Could not get default file path.") - fName = pkgName & NimsExt + var pkgName = os.getCurrentDir().splitPath.tail.toValidPackageName() + pkgName = promptCustom("Enter package name", pkgName) + nimbleFile = pkgName.changeFileExt("nimble") - # Now need to write out .nimble file with projName and other details + validatePackageName(nimbleFile.changeFileExt("")) - if (not existsFile(os.getCurrentDir() / fName) and - open(f=outFile, filename = fName, mode = fmWrite)): + if existsFile(os.getCurrentDir() / nimbleFile): + raise newException(NimbleError, "Nimble file already exists.") + + # Ask for package version. + let pkgVersion = promptCustom("Enter intial version of package", "0.1.0") + validateVersion(pkgVersion) + + # Ask for package author + var defaultAuthor = "Anonymous" + if findExe("git") != "": + let (name, exitCode) = doCmdEx("git config --global user.name") + if exitCode == QuitSuccess and name.len > 0: + defaultAuthor = name.strip() + elif defaultAuthor == "Anonymous" and findExe("hg") != "": + let (name, exitCode) = doCmdEx("hg config ui.username") + if exitCode == QuitSuccess and name.len > 0: + defaultAuthor = name.strip() + let pkgAuthor = promptCustom("Enter your name", defaultAuthor) + + # Ask for description + let pkgDesc = promptCustom("Enter package description", "") + + # Ask for license + # TODO: Provide selection of licenses, or select random default license. + let pkgLicense = promptCustom("Enter package license", "MIT") + + # Ask for Nim dependency + let nimDepDef = getNimrodVersion() + let pkgNimDep = promptCustom("Enter lowest supported Nim version", $nimDepDef) + validateVersion(pkgNimDep) + + # Now generate the .nimble file. + if existsFile(os.getCurrentDir() / nimbleFile): + raise newException(NimbleError, + "Looks like a Nimble file has already been created.") + + var outFile: File + if open(f = outFile, filename = nimbleFile, mode = fmWrite): outFile.writeLine """# Package -version = "1.0.0" -author = $1 -description = "New Nimble project for Nim" -license = "MIT" +version = $# +author = $# +description = $# +license = $# # Dependencies -requires "nim >= 0.11.2" -""" % guessAuthor().escape() +requires "nim >= $#" +""" % [pkgVersion.escape(), pkgAuthor.escape(), pkgDesc.escape(), + pkgLicense.escape(), pkgNimDep] close(outFile) else: - raise newException(NimbleError, "Unable to open file " & fName & + raise newException(NimbleError, "Unable to open file " & nimbleFile & " for writing: " & osErrorMsg(osLastError())) proc uninstall(options: Options) = diff --git a/src/nimblepkg/nim.cfg b/src/nimblepkg/nim.cfg new file mode 100644 index 0000000..d7edcd3 --- /dev/null +++ b/src/nimblepkg/nim.cfg @@ -0,0 +1,2 @@ +--path:"$nim/" +--path:"$lib/packages/docutils" \ No newline at end of file diff --git a/src/nimblepkg/nimscriptsupport.nim b/src/nimblepkg/nimscriptsupport.nim index 7a343e8..a5dda4c 100644 --- a/src/nimblepkg/nimscriptsupport.nim +++ b/src/nimblepkg/nimscriptsupport.nim @@ -86,6 +86,8 @@ proc cleanup() = # ensure everything can be called again: resetAllModulesHard() clearPasses() + msgs.gErrorMax = 1 + msgs.writeLnHook = nil vm.globalCtx = nil initDefines() diff --git a/src/nimblepkg/packageinfo.nim b/src/nimblepkg/packageinfo.nim index 71d4c92..8823adf 100644 --- a/src/nimblepkg/packageinfo.nim +++ b/src/nimblepkg/packageinfo.nim @@ -6,9 +6,6 @@ import version, tools, nimbletypes, nimscriptsupport when not declared(system.map): from sequtils import map -const - NimsExt* = ".nims" - type Package* = object # Required fields in a package. @@ -48,6 +45,47 @@ proc initPackageInfo(path: string): PackageInfo = result.binDir = "" result.backend = "c" +proc validatePackageName*(name: string) = + ## Raises an error if specified package name contains invalid characters. + ## + ## A valid package name is one which is a valid nim module name. So only + ## underscores, letters and numbers allowed. + if name.len == 0: return + + if name[0] in {'0'..'9'}: + raise newException(NimbleError, + "Invalid package name: cannot beging with " & name[0]) + + var prevWasUnderscore = false + for c in name: + case c + of '_': + if prevWasUnderscore: + raise newException(NimbleError, + "Invalid package name: cannot contain \"__\"") + prevWasUnderscore = true + of AllChars - IdentChars: + raise newException(NimbleError, + "Invalid package name: cannot contain '$1'" % $c) + else: + prevWasUnderscore = false + +proc toValidPackageName*(name: string): string = + result = "" + for c in name: + case c + of '_', '-': + if result[^1] != '_': result.add('_') + of AllChars - IdentChars - {'-'}: discard + else: result.add(c) + +proc validateVersion*(ver: string) = + for c in ver: + if c notin ({'.'} + Digits): + raise newException(NimbleError, + "Version may only consist of numbers and the '.' character " & + "but found '" & c & "'.") + proc validatePackageInfo(pkgInfo: PackageInfo, path: string) = if pkgInfo.name == "": raise newException(NimbleError, "Incorrect .nimble file: " & path & @@ -67,11 +105,7 @@ proc validatePackageInfo(pkgInfo: PackageInfo, path: string) = if pkgInfo.backend notin ["c", "cc", "objc", "cpp", "js"]: raise newException(NimbleError, "'" & pkgInfo.backend & "' is an invalid backend.") - for c in pkgInfo.version: - if c notin ({'.'} + Digits): - raise newException(NimbleError, - "Version may only consist of numbers and the '.' character " & - "but found '" & c & "'.") + validateVersion(pkgInfo.version) if not pkgInfo.isNimScript: # TODO: Turn this into a warning. @@ -395,3 +429,16 @@ when isMainModule: ("packagea", "0.1") doAssert getNameVersion("/home/user/.nimble/libs/package-a-0.1") == ("package-a", "0.1") + + validatePackageName("foo_bar") + validatePackageName("f_oo_b_a_r") + try: + validatePackageName("foo__bar") + assert false + except NimbleError: + assert true + + doAssert toValidPackageName("foo__bar") == "foo_bar" + doAssert toValidPackageName("jhbasdh!£$@%#^_&*_()qwe") == "jhbasdh_qwe" + + echo("All tests passed!") \ No newline at end of file