diff --git a/src/nimble.nim b/src/nimble.nim index 2d66714a6..8117bd9fa 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -157,14 +157,23 @@ proc buildFromDir(pkgInfo: PackageInfo, paths: HashSet[string], ## Builds a package as specified by ``pkgInfo``. # Handle pre-`build` hook. let - realDir = pkgInfo.getRealDir() pkgDir = pkgInfo.myPath.parentDir() + realDir = + if options.example: + pkgInfo.getRealExamplesDir() + else: + pkgInfo.getRealDir() + bin = + if options.example: + pkgInfo.getExampleBin() + else: + pkgInfo.bin cd pkgDir: # Make sure `execHook` executes the correct .nimble file. if not execHook(options, actionBuild, true): raise nimbleError("Pre-hook prevented further execution.") - if pkgInfo.bin.len == 0: + if bin.len == 0: raise nimbleError( "Nothing to build. Did you specify a module to build using the" & " `bin` key in your .nimble file?") @@ -181,6 +190,8 @@ proc buildFromDir(pkgInfo: PackageInfo, paths: HashSet[string], if options.verbosity == SilentPriority: # Hide Nim warnings args.add("--warnings:off") + if options.example: + args.add("--path:" & pkgInfo.srcDir.quoteShell) let binToBuild = # Only build binaries specified by user if any, but only if top-level package, @@ -189,13 +200,13 @@ proc buildFromDir(pkgInfo: PackageInfo, paths: HashSet[string], options.getCompilationBinary(pkgInfo).get("") else: "" - for bin, src in pkgInfo.bin: + for bin, src in bin: # Check if this is the only binary that we want to build. if binToBuild.len != 0 and binToBuild != bin: if bin.extractFilename().changeFileExt("") != binToBuild: continue - let outputDir = pkgInfo.getOutputDir("") + let outputDir = pkgInfo.getOutputDir("", options.example) if dirExists(outputDir): if fileExists(outputDir / bin): if not pkgInfo.needsRebuild(outputDir / bin, realDir, options): @@ -206,7 +217,7 @@ proc buildFromDir(pkgInfo: PackageInfo, paths: HashSet[string], else: createDir(outputDir) - let outputOpt = "-o:" & pkgInfo.getOutputDir(bin).quoteShell + let outputOpt = "-o:" & pkgInfo.getOutputDir(bin, options.example).quoteShell display("Building", "$1/$2 using $3 backend" % [pkginfo.basicInfo.name, bin, pkgInfo.backend], priority = HighPriority) @@ -724,7 +735,11 @@ proc clean(options: Options) = proc execBackend(pkgInfo: PackageInfo, options: Options) = let - bin = options.getCompilationBinary(pkgInfo).get("") + bin = + if options.example: + pkgInfo.getRealExamplesDir() / options.getCompilationBinary(pkgInfo).get("") + else: + options.getCompilationBinary(pkgInfo).get("") binDotNim = bin.addFileExt("nim") if bin == "": @@ -750,6 +765,8 @@ proc execBackend(pkgInfo: PackageInfo, options: Options) = if options.verbosity == SilentPriority: # Hide Nim warnings args.add("--warnings:off") + if options.example: + args.add("--path:" & pkgInfo.srcDir.quoteShell) for option in options.getCompilationFlags(): args.add(option.quoteShell) @@ -1918,7 +1935,7 @@ proc run(options: Options) = if binary.len == 0: raise nimbleError("Please specify a binary to run") - if binary notin pkgInfo.bin: + if not options.example and binary notin pkgInfo.bin: raise nimbleError(binaryNotDefinedInPkgMsg(binary, pkgInfo.basicInfo.name)) if pkgInfo.isLink: @@ -1927,7 +1944,7 @@ proc run(options: Options) = elif options.getCompilationFlags.len > 0: displayWarning(ignoringCompilationFlagsMsg) - let binaryPath = pkgInfo.getOutputDir(binary) + let binaryPath = pkgInfo.getOutputDir(binary, options.example) let cmd = quoteShellCommand(binaryPath & options.action.runFlags) displayDebug("Executing", cmd) diff --git a/src/nimblepkg/init.nim b/src/nimblepkg/init.nim index a3b5b6538..4c6531155 100644 --- a/src/nimblepkg/init.nim +++ b/src/nimblepkg/init.nim @@ -1,4 +1,4 @@ -import os, strutils +import os, strutils, std/options import ./cli, ./tools @@ -20,6 +20,36 @@ proc writeExampleIfNonExistent(file: string, content: string) = display("Info:", "File " & file & " already exists, did not write " & "example code", priority = HighPriority) +proc exampleCode(info: PkgInitInfo): Option[string] = + case info.pkgType + of "library": + result = some(""" +# You may create as many example files as you want. Example file names should +# be valid Nim module names. +# +# To run this example, simply execute `nimble run --example example1`. + +import $1 + +echo "One plus one equals ", $$add(1, 1) + +""" % info.pkgName) + + of "hybrid": + result = some(""" +# You may create as many example files as you want. Example file names should +# be valid Nim module names. +# +# To run this example, simply execute `nimble run --example example1`. + +import $1pkg/submodule + +echo getWelcomeMessage() + +""" % info.pkgName) + else: + discard + proc createPkgStructure*(info: PkgInitInfo, pkgRoot: string) = # Create source directory createDirD(pkgRoot / info.pkgSrcDir) @@ -154,6 +184,17 @@ test "correct welcome": else: assert false, "Invalid package type specified." + # Create examples directory and dummy example + let pkgExamplesDir = "examples" + let pkgExampleCode = exampleCode(info) + if pkgExampleCode.isSome(): + let pkgExampleCode = pkgExampleCode.get("") + let pkgExamplesPath = pkgRoot / pkgExamplesDir + + createDirD(pkgExamplesPath) + nimbleFileOptions.add("# examplesDir = $1 # Uncomment to change the name of the examples directory\n" % pkgExamplesDir.escape()) + writeExampleIfNonExistent(pkgExamplesPath / "example1".addFileExt(".nim"), pkgExampleCode) + # Write the nimble file let nimbleFile = pkgRoot / info.pkgName.changeFileExt("nimble") writeFile(nimbleFile, """# Package diff --git a/src/nimblepkg/nimscriptapi.nim b/src/nimblepkg/nimscriptapi.nim index a8adcb95e..01cb2e400 100644 --- a/src/nimblepkg/nimscriptapi.nim +++ b/src/nimblepkg/nimscriptapi.nim @@ -24,6 +24,7 @@ var srcDir*: string ## The package's source directory. binDir*: string ## The package's binary directory. backend*: string ## The package's backend. + examplesDir*: string ## The package's examples directory. skipDirs*, skipFiles*, skipExt*, installDirs*, installFiles*, installExt*, bin*: seq[string] = @[] ## Nimble metadata. @@ -126,6 +127,7 @@ proc printPkgInfo(): string = printIfLen srcDir printIfLen binDir printIfLen backend + printIfLen examplesDir printSeqIfLen skipDirs printSeqIfLen skipFiles diff --git a/src/nimblepkg/options.nim b/src/nimblepkg/options.nim index 449a8fc44..fdf2385b0 100644 --- a/src/nimblepkg/options.nim +++ b/src/nimblepkg/options.nim @@ -31,6 +31,7 @@ type showVersion*: bool offline*: bool noColor*: bool + example*: bool disableValidation*: bool continueTestsOnFailure*: bool ## Whether packages' repos should always be downloaded with their history. @@ -56,6 +57,7 @@ type actionUninstall, actionCompile, actionDoc, actionCustom, actionTasks, actionDevelop, actionCheck, actionLock, actionRun, actionSync, actionSetup, actionClean, actionDeps + DevelopActionType* = enum datAdd, datRemoveByPath, datRemoveByName, datInclude, datExclude @@ -223,6 +225,7 @@ Nimble Options: --silent Hide all Nimble and Nim output --verbose Show all non-debug output. --debug Show all output including debug messages. + --example Build/run an example instead of a package. --offline Don't use network. --noColor Don't colorise output. --noSSLCheck Don't check SSL certificates. @@ -517,6 +520,7 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) = of "tarballs", "t": result.enableTarballs = true of "package", "p": result.package = val of "lock-file": result.lockFileName = val + of "example": result.example = true else: isGlobalFlag = false var wasFlagHandled = true diff --git a/src/nimblepkg/packageinfo.nim b/src/nimblepkg/packageinfo.nim index e2a299014..0fbc38265 100644 --- a/src/nimblepkg/packageinfo.nim +++ b/src/nimblepkg/packageinfo.nim @@ -367,15 +367,30 @@ proc getRealDir*(pkgInfo: PackageInfo): string = else: result = pkgInfo.getNimbleFileDir() -proc getOutputDir*(pkgInfo: PackageInfo, bin: string): string = +proc getOutputDir*(pkgInfo: PackageInfo, bin: string, example = false): string = ## Returns a binary output dir for the package. - if pkgInfo.binDir != "": - result = pkgInfo.getNimbleFileDir() / pkgInfo.binDir / bin + if example and pkgInfo.examplesDir != "": + if pkgInfo.binDir != "": + result = pkgInfo.getNimbleFileDir() / pkgInfo.binDir / pkgInfo.examplesDir / bin + else: + result = pkgInfo.mypath.splitFile.dir / pkgInfo.examplesDir / bin else: - result = pkgInfo.mypath.splitFile.dir / bin + if pkgInfo.binDir != "": + result = pkgInfo.getNimbleFileDir() / pkgInfo.binDir / bin + else: + result = pkgInfo.mypath.splitFile.dir / bin if bin.len != 0 and dirExists(result): result &= ".out" +proc getRealExamplesDir*(pkgInfo: PackageInfo): string = + ## Returns the directory containing the example source files. + ## If no example directory was specified in the package info, "examples" + ## is returned. + if pkgInfo.examplesDir != "" and (not pkgInfo.isInstalled or pkgInfo.isLink): + result = pkgInfo.getNimbleFileDir() / pkgInfo.examplesDir + elif not pkgInfo.isInstalled or pkgInfo.isLink: + result = "examples" + proc echoPackage*(pkg: Package) = echo(pkg.name & ":") if pkg.alias.len > 0: @@ -530,6 +545,20 @@ proc hash*(x: PackageInfo): Hash = proc getNameAndVersion*(pkgInfo: PackageInfo): string = &"{pkgInfo.basicInfo.name}@{pkgInfo.basicInfo.version}" +proc getExampleBin*(pkgInfo: PackageInfo): Table[string,string] = + let examplesDir = pkgInfo.getRealExamplesDir() + if examplesDir == "": + raise nimbleError("Cannot find example files", hint="Was 'examplesDir' defined in '$1' ?" % pkgInfo.myPath) + + if not examplesDir.dirExists(): + raise nimbleError("Examples directory not found: $1" % examplesDir) + + for kind, path in walkDir(examplesDir): + if kind in {pcFile, pcLinkToFile}: + let (_, name, ext) = path.splitFile() + if ext == ".nim": + result[name] = name + when isMainModule: import unittest diff --git a/src/nimblepkg/packageinfotypes.nim b/src/nimblepkg/packageinfotypes.nim index 482ab1c3a..ed4c99e48 100644 --- a/src/nimblepkg/packageinfotypes.nim +++ b/src/nimblepkg/packageinfotypes.nim @@ -58,6 +58,7 @@ type bin*: Table[string, string] binDir*: string srcDir*: string + examplesDir*: string backend*: string foreignDeps*: seq[string] basicInfo*: PackageBasicInfo diff --git a/src/nimblepkg/packageparser.nim b/src/nimblepkg/packageparser.nim index 8b051fe65..d76eb1c5d 100644 --- a/src/nimblepkg/packageparser.nim +++ b/src/nimblepkg/packageparser.nim @@ -238,6 +238,7 @@ proc readPackageInfoFromNimble(path: string; result: var PackageInfo) = of "license": result.license = ev.value of "srcdir": result.srcDir = ev.value of "bindir": result.binDir = ev.value + of "examplesdir": result.examplesDir = ev.value of "skipdirs": result.skipDirs.add(ev.value.multiSplit) of "skipfiles":