Skip to content

Commit bc68d48

Browse files
authored
Merge pull request #970 from bobeff/feature/link-files
Add `develop --global` option
2 parents 519bb2b + 0e5580e commit bc68d48

File tree

7 files changed

+149
-41
lines changed

7 files changed

+149
-41
lines changed

readme.markdown

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,9 @@ packages for which the develop command is executed.
290290
* `--develop-file` - Changes the name of the develop file which to be
291291
manipulated. It is useful for creating a free develop file which is not
292292
associated with any project intended for inclusion in some other develop file.
293+
* `-g, --global` - Creates an old style link file in the special `links`
294+
directory. It is read by Nim to be able to use global develop mode packages.
295+
Nimble uses it as a global develop file if a local one does not exist.
293296

294297
The options for manipulation of the develop files could be given only when
295298
executing `develop` command from some package's directory unless

src/nimble.nim

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,21 @@ proc listTasks(options: Options) =
11661166

11671167
proc developAllDependencies(pkgInfo: PackageInfo, options: var Options)
11681168

1169+
proc saveLinkFile(pkgInfo: PackageInfo, options: Options) =
1170+
let
1171+
pkgName = pkgInfo.basicInfo.name
1172+
pkgLinkDir = options.getPkgsLinksDir / pkgName.getLinkFileDir
1173+
pkgLinkFilePath = pkgLinkDir / pkgName.getLinkFileName
1174+
pkgLinkFileContent = pkgInfo.myPath & "\n" & pkgInfo.getNimbleFileDir
1175+
1176+
if pkgLinkDir.dirExists and not options.prompt(
1177+
&"The link file for {pkgName} already exists. Overwrite?"):
1178+
return
1179+
1180+
pkgLinkDir.createDir
1181+
writeFile(pkgLinkFilePath, pkgLinkFileContent)
1182+
displaySuccess(pkgLinkFileSavedMsg(pkgLinkFilePath))
1183+
11691184
proc developFromDir(pkgInfo: PackageInfo, options: var Options) =
11701185
assert options.action.typ == actionDevelop,
11711186
"This procedure should be called only when executing develop sub-command."
@@ -1201,6 +1216,9 @@ proc developFromDir(pkgInfo: PackageInfo, options: var Options) =
12011216
# Dependencies need to be processed before the creation of the pkg dir.
12021217
discard processAllDependencies(pkgInfo, options)
12031218

1219+
if options.action.global:
1220+
saveLinkFile(pkgInfo, options)
1221+
12041222
displaySuccess(pkgSetupInDevModeMsg(pkgInfo.basicInfo.name, dir))
12051223

12061224
# Execute the post-develop hook.
@@ -1302,7 +1320,7 @@ proc updateSyncFile(dependentPkg: PackageInfo, options: Options)
13021320

13031321
proc updatePathsFile(pkgInfo: PackageInfo, options: Options) =
13041322
let paths = pkgInfo.getDependenciesPaths(options)
1305-
var pathsFileContent: string
1323+
var pathsFileContent = "--noNimblePath\n"
13061324
for path in paths:
13071325
pathsFileContent &= &"--path:{path.escape}\n"
13081326
var action = if fileExists(nimblePathsFileName): "updated" else: "generated"
@@ -1459,10 +1477,9 @@ proc check(options: Options) =
14591477
try:
14601478
let currentDir = getCurrentDir()
14611479
let pkgInfo = getPkgInfo(currentDir, options, true)
1462-
if currentDir.developFileExists:
1463-
validateDevelopFile(pkgInfo, options)
1464-
let dependencies = pkgInfo.processAllDependencies(options).toSeq
1465-
validateDevelopDependenciesVersionRanges(pkgInfo, dependencies, options)
1480+
validateDevelopFile(pkgInfo, options)
1481+
let dependencies = pkgInfo.processAllDependencies(options).toSeq
1482+
validateDevelopDependenciesVersionRanges(pkgInfo, dependencies, options)
14661483
displaySuccess(&"The package \"{pkgInfo.basicInfo.name}\" is valid.")
14671484
except CatchableError as error:
14681485
displayError(error)

src/nimblepkg/common.nim

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type
2424
const
2525
nimbleVersion* = "0.14.0"
2626
nimblePackagesDirName* = "pkgs2"
27+
nimblePackagesLinksDirName* ="links"
2728
nimbleBinariesDirName* = "bin"
2829

2930
proc newNimbleError*[ErrorType](msg: string, hint = "",
@@ -77,3 +78,9 @@ template cdNewDir*(dir: string, body: untyped) =
7778
createNewDir dir
7879
cd dir:
7980
body
81+
82+
proc getLinkFileDir*(pkgName: string): string =
83+
pkgName & "-#head"
84+
85+
proc getLinkFileName*(pkgName: string): string =
86+
pkgName & ".nimble-link"

src/nimblepkg/developfile.nim

Lines changed: 74 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ proc mergeFollowedDevFileData(lhs: var DevelopFileData, rhs: DevelopFileData,
307307
errors.collidingNames)
308308
309309
proc load(path: Path, dependentPkg: PackageInfo, options: Options,
310-
silentIfFileNotExists, raiseOnValidationErrors: bool):
310+
silentIfFileNotExists, raiseOnValidationErrors, loadGlobalDeps: bool):
311311
DevelopFileData
312312
313313
template load(dependentPkg: PackageInfo, args: varargs[untyped]):
@@ -318,8 +318,37 @@ template load(dependentPkg: PackageInfo, args: varargs[untyped]):
318318
dependentPkg.assertIsLoaded
319319
load(dependentPkg.getPkgDevFilePath, dependentPkg, args)
320320
321+
proc loadGlobalDependencies(result: var DevelopFileData,
322+
collidingNames: var CollidingNames,
323+
options: Options) =
324+
## Loads data from the `links` subdirectory in the Nimble cache. The links
325+
## in the cache are treated as paths in a global develop file used when a
326+
## local one does not exist.
327+
328+
for (kind, path) in walkDir(options.getPkgsLinksDir):
329+
if kind != pcDir:
330+
continue
331+
let (pkgName, _, _) = getNameVersionChecksum(path)
332+
let linkFilePath = path / pkgName.getLinkFileName
333+
if not linkFilePath.fileExists:
334+
displayWarning(&"Not found link file in \"{path}\".")
335+
continue
336+
let lines = linkFilePath.readFile.split("\n")
337+
if lines.len != 2:
338+
displayWarning(&"Invalid link file \"{linkFilePath}\".")
339+
continue
340+
let pkgPath = lines[1]
341+
let (pkgInfo, error) = validatePackage(pkgPath, options)
342+
if error == nil:
343+
let path = path.Path
344+
result.addPackage(pkgInfo, path, [path].toHashSet, collidingNames)
345+
else:
346+
displayWarning(
347+
&"Package \"{pkgName}\" at path \"{pkgPath}\" is invalid. Skipping it.")
348+
displayDetails(error.msg)
349+
321350
proc load(path: Path, dependentPkg: PackageInfo, options: Options,
322-
silentIfFileNotExists, raiseOnValidationErrors: bool):
351+
silentIfFileNotExists, raiseOnValidationErrors, loadGlobalDeps: bool):
323352
DevelopFileData =
324353
## Loads data from a develop file at path `path`.
325354
##
@@ -328,6 +357,10 @@ proc load(path: Path, dependentPkg: PackageInfo, options: Options,
328357
##
329358
## If `raiseOnValidationErrors` raises a `NimbleError` in the case some of the
330359
## contents of the develop file are invalid.
360+
##
361+
## If `loadGlobalDeps` then load the packages pointed by the link files in the
362+
## `links` directory in the Nimble cache instead of the once pointed by the
363+
## local develop file.
331364
##
332365
## Raises if the develop file or some of the included develop files:
333366
## - cannot be read.
@@ -343,9 +376,6 @@ proc load(path: Path, dependentPkg: PackageInfo, options: Options,
343376
result.path = path
344377
result.dependentPkg = dependentPkg
345378

346-
if silentIfFileNotExists and not path.fileExists:
347-
return
348-
349379
var
350380
errors {.global.}: ErrorsCollection
351381
visitedFiles {.global.}: HashSet[Path]
@@ -355,31 +385,38 @@ proc load(path: Path, dependentPkg: PackageInfo, options: Options,
355385
if dependentPkg.isLoaded:
356386
visitedPkgs.incl dependentPkg.getNimbleFileDir
357387

358-
try:
359-
fromJson(result.jsonData, parseFile(path), Joptions(allowExtraKeys: true))
360-
except ValueError as error:
361-
raise nimbleError(notAValidDevFileJsonMsg($path), details = error)
362-
363-
for depPath in result.jsonData.dependencies:
364-
let depPath = if depPath.isAbsolute:
365-
depPath.normalizedPath else: (path.splitFile.dir / depPath).normalizedPath
366-
let (pkgInfo, error) = validatePackage(depPath, options)
367-
if error == nil:
368-
result.addPackage(pkgInfo, path, [path].toHashSet, errors.collidingNames)
369-
else:
370-
errors.invalidPackages[depPath] = error
388+
if loadGlobalDeps:
389+
loadGlobalDependencies(result, errors.collidingNames, options)
390+
else:
391+
if silentIfFileNotExists and not path.fileExists:
392+
return
371393

372-
for inclPath in result.jsonData.includes:
373-
let inclPath = inclPath.normalizedPath
374-
if visitedFiles.contains(inclPath):
375-
continue
376-
var inclDevFileData = initDevelopFileData()
377394
try:
378-
inclDevFileData = load(inclPath, initPackageInfo(), options, false, false)
379-
except CatchableError as error:
380-
errors.invalidIncludeFiles[path] = error
381-
continue
382-
result.mergeIncludedDevFileData(inclDevFileData, errors)
395+
fromJson(result.jsonData, parseFile(path), Joptions(allowExtraKeys: true))
396+
except ValueError as error:
397+
raise nimbleError(notAValidDevFileJsonMsg($path), details = error)
398+
399+
for depPath in result.jsonData.dependencies:
400+
let depPath = if depPath.isAbsolute:
401+
depPath.normalizedPath else: (path.splitFile.dir / depPath).normalizedPath
402+
let (pkgInfo, error) = validatePackage(depPath, options)
403+
if error == nil:
404+
result.addPackage(pkgInfo, path, [path].toHashSet, errors.collidingNames)
405+
else:
406+
errors.invalidPackages[depPath] = error
407+
408+
for inclPath in result.jsonData.includes:
409+
let inclPath = inclPath.normalizedPath
410+
if visitedFiles.contains(inclPath):
411+
continue
412+
var inclDevFileData = initDevelopFileData()
413+
try:
414+
inclDevFileData = load(
415+
inclPath, initPackageInfo(), options, false, false, false)
416+
except CatchableError as error:
417+
errors.invalidIncludeFiles[path] = error
418+
continue
419+
result.mergeIncludedDevFileData(inclDevFileData, errors)
383420

384421
if result.dependentPkg.isLoaded and path.splitPath.tail == developFileName:
385422
# If this is a package develop file, but not a free one, for each of the
@@ -390,7 +427,7 @@ proc load(path: Path, dependentPkg: PackageInfo, options: Options,
390427
continue
391428
var followedPkgDevFileData = initDevelopFileData()
392429
try:
393-
followedPkgDevFileData = load(pkg[], options, true, false)
430+
followedPkgDevFileData = load(pkg[], options, true, false, false)
394431
except:
395432
# The errors will be accumulated in `errors` global variable and
396433
# reported by the `load` call which initiated the recursive process.
@@ -580,7 +617,7 @@ proc includeDevelopFile(data: var DevelopFileData, path: Path,
580617
581618
var inclFileData = initDevelopFileData()
582619
try:
583-
inclFileData = load(path, initPackageInfo(), options, false, true)
620+
inclFileData = load(path, initPackageInfo(), options, false, true, false)
584621
except CatchableError as error:
585622
displayError(failedToLoadFileMsg($path))
586623
displayDetails(error)
@@ -660,7 +697,7 @@ proc updateDevelopFile*(dependentPkg: PackageInfo, options: Options): bool =
660697
var
661698
hasError = false
662699
hasSuccessfulRemoves = false
663-
data = load(developFile, dependentPkg, options, true, true)
700+
data = load(developFile, dependentPkg, options, true, true, false)
664701
665702
defer:
666703
let writeEmpty = hasSuccessfulRemoves or
@@ -691,7 +728,8 @@ proc processDevelopDependencies*(dependentPkg: PackageInfo, options: Options):
691728
## Returns a sequence with the develop mode dependencies of the `dependentPkg`
692729
## and recursively all of their develop mode dependencies.
693730
694-
let data = load(dependentPkg, options, true, true)
731+
let loadGlobalDeps = not dependentPkg.getPkgDevFilePath.fileExists
732+
let data = load(dependentPkg, options, true, true, loadGlobalDeps)
695733
result = newSeqOfCap[PackageInfo](data.nameToPkg.len)
696734
for _, pkg in data.nameToPkg:
697735
result.add pkg[]
@@ -702,7 +740,8 @@ proc getDevelopDependencies*(dependentPkg: PackageInfo, options: Options):
702740
## mode dependencies of package `dependentPkg` and recursively all of their
703741
## develop mode dependencies.
704742
705-
let data = load(dependentPkg, options, true, true)
743+
let loadGlobalDeps = not dependentPkg.getPkgDevFilePath.fileExists
744+
let data = load(dependentPkg, options, true, true, loadGlobalDeps)
706745
return data.nameToPkg
707746
708747
type
@@ -908,6 +947,7 @@ proc validateDevelopFile*(dependentPkg: PackageInfo, options: Options) =
908947
## The procedure is used in the Nimble's `check` command to transitively
909948
## validate the contents of the develop files.
910949
911-
discard load(dependentPkg, options, true, true)
950+
let loadGlobalDeps = not dependentPkg.getPkgDevFilePath.fileExists
951+
discard load(dependentPkg, options, true, true, loadGlobalDeps)
912952
if dependentPkg.areLockedDepsLoaded:
913953
validateDevelopFileAgainstLockFile(dependentPkg, options)

src/nimblepkg/displaymessages.nim

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,6 @@ proc pkgAlreadyExistsInTheCacheMsg*(pkgInfo: PackageInfo): string =
157157
proc skipDownloadingInAlreadyExistingDirectoryMsg*(dir, name: string): string =
158158
&"The download directory \"{dir}\" already exists.\n" &
159159
&"Skipping the download of \"{name}\"."
160+
161+
proc pkgLinkFileSavedMsg*(path: string): string =
162+
&"Package link file \"{path}\" is saved."

src/nimblepkg/options.nim

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ type
7575
## Whether to put in develop mode also the dependencies of the packages
7676
## listed in the develop command.
7777
developFile*: string
78+
global*: bool
7879
of actionSearch:
7980
search*: seq[string] # Search string.
8081
of actionInit, actionDump:
@@ -109,7 +110,7 @@ Commands:
109110
executed in package's directory.
110111
[--with-dependencies] Puts in develop mode also the dependencies
111112
of the packages in the list or of the current
112-
directory package if the list is
113+
directory package if the list is empty.
113114
[--develop-file] Specifies the name of the develop file which
114115
to be manipulated. If not present creates it.
115116
[-p, --path path] Specifies the path whether the packages should
@@ -131,6 +132,11 @@ Commands:
131132
[-e, --exclude file] Excludes a develop file from a specified
132133
develop file or from `nimble.develop` if not
133134
specified and executed in package's directory.
135+
[-g, --global] Creates an old style link file in the special
136+
`links` directory. It is read by Nim to be
137+
able to use global develop mode packages.
138+
Nimble uses it as a global develop file if a
139+
local one does not exist.
134140
check Verifies the validity of a package in the
135141
current working directory.
136142
init [pkgname] Initializes a new Nimble project in the
@@ -315,6 +321,9 @@ proc getNimbleDir*(options: Options): string =
315321
proc getPkgsDir*(options: Options): string =
316322
options.getNimbleDir() / nimblePackagesDirName
317323

324+
proc getPkgsLinksDir*(options: Options): string =
325+
options.getNimbleDir() / nimblePackagesLinksDirName
326+
318327
proc getBinDir*(options: Options): string =
319328
options.getNimbleDir() / nimbleBinariesDirName
320329

@@ -563,6 +572,8 @@ proc parseFlag*(flag, val: string, result: var Options, kind = cmdLongOption) =
563572
raise nimbleError(multiplePathOptionsGivenMsg)
564573
of "with-dependencies":
565574
result.action.withDependencies = true
575+
of "global":
576+
result.action.global = true
566577
of "develop-file":
567578
if result.action.developFile.len == 0:
568579
result.action.developFile = val.normalizedPath

tests/tdevelopfeature.nim

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import unittest, os, strutils, strformat, json, sets
77
import testscommon, nimblepkg/displaymessages, nimblepkg/paths
88

9-
from nimblepkg/common import cd
9+
from nimblepkg/common import cd, getLinkFileName, getLinkFileDir
1010
from nimblepkg/developfile import developFileName, pkgFoundMoreThanOnceMsg
1111
from nimblepkg/version import newVersion, parseVersionRange
1212
from nimblepkg/nimbledatafile import nimbleDataFileName, NimbleDataJsonKeys
@@ -81,6 +81,33 @@ suite "develop feature":
8181
check lines.inLinesOrdered(pkgSetupInDevModeMsg(
8282
pkgBName, installDir / pkgBName))
8383

84+
test "can develop global":
85+
cleanDir installDir
86+
usePackageListFile &"develop/{pkgListFileName}":
87+
let dependencyPath = getCurrentDir() / "develop" / "dependency"
88+
89+
# Check that a link file can be created for a global develop dependency.
90+
cd dependencyPath:
91+
let (output, exitCode) = execNimble("develop", "--global")
92+
check exitCode == QuitSuccess
93+
var lines = output.processOutput
94+
let linkFilePath = installDir / "links" / depName.getLinkFileDir /
95+
depName.getLinkFileName
96+
check lines.inLinesOrdered(pkgLinkFileSavedMsg(linkFilePath))
97+
check lines.inLinesOrdered(pkgSetupInDevModeMsg(
98+
depName, dependencyPath))
99+
check linkFilePath.fileExists
100+
let linkFileLines = linkFilePath.readFile.split('\n')
101+
let expectedLinkNimbleFilePath = dependencyPath / depName & ".nimble"
102+
check linkFileLines[0] == expectedLinkNimbleFilePath
103+
check linkFileLines[1] == dependencyPath
104+
105+
# Check that a link file can be used for finding the dependency.
106+
cd "develop/dependent":
107+
cleanFile dependentPkgName.addFileExt(ExeExt)
108+
let (_, exitCode) = execNimble("run")
109+
check exitCode == QuitSuccess
110+
84111
test "cannot remove package with develop reverse dependency":
85112
cdCleanDir installDir:
86113
usePackageListFile &"../develop/{pkgListFileName}":

0 commit comments

Comments
 (0)