Skip to content

Add Support for Example Code #1026

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
33 changes: 25 additions & 8 deletions src/nimble.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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?")
Expand All @@ -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,
Expand All @@ -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):
Expand All @@ -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)

Expand Down Expand Up @@ -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 == "":
Expand All @@ -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)
Expand Down Expand Up @@ -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:
Expand All @@ -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)

Expand Down
43 changes: 42 additions & 1 deletion src/nimblepkg/init.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import os, strutils
import os, strutils, std/options

import ./cli, ./tools

Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/nimblepkg/nimscriptapi.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -126,6 +127,7 @@ proc printPkgInfo(): string =
printIfLen srcDir
printIfLen binDir
printIfLen backend
printIfLen examplesDir

printSeqIfLen skipDirs
printSeqIfLen skipFiles
Expand Down
4 changes: 4 additions & 0 deletions src/nimblepkg/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
37 changes: 33 additions & 4 deletions src/nimblepkg/packageinfo.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions src/nimblepkg/packageinfotypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type
bin*: Table[string, string]
binDir*: string
srcDir*: string
examplesDir*: string
backend*: string
foreignDeps*: seq[string]
basicInfo*: PackageBasicInfo
Expand Down
1 change: 1 addition & 0 deletions src/nimblepkg/packageparser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down