From 423e2d3490c2e1bf93c9b514eee166091a20543b Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Fri, 7 Dec 2012 21:05:30 +0000 Subject: [PATCH] Versioning support, changes to directory structure. --- babel.nim | 67 +++++++++++++++++++++++-------------------------- packageinfo.nim | 37 +++++++++++++++++++++------ readme.markdown | 26 ++++++++++--------- 3 files changed, 76 insertions(+), 54 deletions(-) diff --git a/babel.nim b/babel.nim index 3d29051..27a6002 100644 --- a/babel.nim +++ b/babel.nim @@ -97,10 +97,11 @@ proc update(url: string = defaultPackageURL) = echo("Done.") proc findBabelFile(dir: string): string = + result = "" for kind, path in walkDir(dir): if kind == pcFile and path.splitFile.ext == ".babel": - return path - return "" + if result != "": quit("Only one .babel file should be present in " & dir) + result = path proc copyFileD(fro, to: string) = echo(fro, " -> ", to) @@ -127,7 +128,7 @@ proc changeRoot(origRoot, newRoot, path: string): string = raise newException(EInvalidValue, "Cannot change root of path: Path does not begin with original root.") -proc copyFilesRec(origDir, currentDir: string, pkgInfo: TPackageInfo) = +proc copyFilesRec(origDir, currentDir, dest: string, pkgInfo: TPackageInfo) = for kind, file in walkDir(currentDir): if kind == pcDir: var skip = false @@ -142,9 +143,9 @@ proc copyFilesRec(origDir, currentDir: string, pkgInfo: TPackageInfo) = if skip: continue # Create the dir. - createDir(changeRoot(origDir, getLibsDir() / pkgInfo.name, file)) + createDir(changeRoot(origDir, dest, file)) - copyFilesRec(origDir, file, pkgInfo) + copyFilesRec(origDir, file, dest, pkgInfo) else: var skip = false if file.splitFile().name[0] == '.': skip = true @@ -155,46 +156,40 @@ proc copyFilesRec(origDir, currentDir: string, pkgInfo: TPackageInfo) = break if not skip: - copyFileD(file, changeRoot(origDir, getLibsDir() / pkgInfo.name, file)) + copyFileD(file, changeRoot(origDir, dest, file)) -proc installFromDir(dir: string) = +proc installFromDir(dir: string, latest: bool) = let babelFile = findBabelFile(dir) if babelFile == "": quit("Specified directory does not contain a .babel file.", QuitFailure) var pkgInfo = readPackageInfo(babelFile) - if not existsDir(dir / pkgInfo.name): - quit("Package modules should be placed in a " & pkgInfo.name & dirSep & - " directory.", QuitFailure) - - if not existsDir(getLibsDir() / pkgInfo.name): - createDir(getLibsDir() / pkgInfo.name) + let pkgDestDir = getLibsDir() / (pkgInfo.name & + (if latest: "" else: '-' & pkgInfo.version)) + if not existsDir(pkgDestDir): + createDir(pkgDestDir) else: if not prompt("Package already exists. Overwrite?"): quit(QuitSuccess) - removeDir(getLibsDir() / pkgInfo.name) - createDir(getLibsDir() / pkgInfo.name) + removeDir(pkgDestDir) + createDir(pkgDestDir) - # Find main project file. - let nimFile = dir / pkgInfo.name.addFileExt("nim") - let nimrodFile = dir / pkgInfo.name.addFileExt("nimrod") - if existsFile(nimFile) or existsFile(nimrodFile): - if existsFile(nimFile): - copyFileD(nimFile, changeRoot(dir, getLibsDir(), nimFile)) - pkgInfo.skipFiles.add(changeRoot(dir, "", nimFile)) - elif existsFile(nimrodFile): - copyFileD(nimrodFile, changeRoot(dir, getLibsDir(), nimrodFile)) - pkgInfo.skipFiles.add(changeRoot(dir, "", nimrodFile)) - else: - # TODO: Make this an error? Which can be overriden in .babel file? - echo("Warning: Could not find main package file.") - - copyFilesRec(dir / pkgInfo.name, dir / pkgInfo.name, pkgInfo) + copyFilesRec(dir, dir, pkgDestDir, pkgInfo) echo(pkgInfo.name & " installed successfully.") +proc doCmd(cmd: string) = + let exitCode = execCmd(cmd) + if exitCode != QuitSuccess: + quit("Execution failed with exit code " & $exitCode, QuitFailure) + +proc getDVCSTag(pkg: TPackage): string = + result = pkg.dvcsTag + if result == "": + result = pkg.version + proc install(packages: seq[String]) = if packages == @[]: - installFromDir(getCurrentDir()) + installFromDir(getCurrentDir(), false) else: if not existsFile(getBabelDir() / "packages.json"): quit("Please run babel update.", QuitFailure) @@ -202,15 +197,17 @@ proc install(packages: seq[String]) = var pkg: TPackage if getPackage(p, getBabelDir() / "packages.json", pkg): let downloadDir = (getTempDir() / "babel" / pkg.name) + let dvcsTag = getDVCSTag(pkg) case pkg.downloadMethod of "git": echo("Executing git...") removeDir(downloadDir) - let exitCode = execCmd("git clone " & pkg.url & " " & downloadDir) - if exitCode != QuitSuccess: - quit("Execution of git failed.", QuitFailure) + doCmd("git clone " & pkg.url & " " & downloadDir) + if dvcsTag != "": + doCmd("cd \"" & downloadDir & "\" && git checkout " & dvcsTag) else: quit("Unknown download method: " & pkg.downloadMethod, QuitFailure) - installFromDir(downloadDir) + + installFromDir(downloadDir, dvcsTag == "") else: quit("Package not found.", QuitFailure) diff --git a/packageinfo.nim b/packageinfo.nim index 4d86b94..1f06f52 100644 --- a/packageinfo.nim +++ b/packageinfo.nim @@ -11,7 +11,9 @@ type TPackage* = object name*: string + version*: string url*: string + dvcsTag*: string downloadMethod*: string tags*: seq[string] description*: string @@ -53,17 +55,36 @@ proc readPackageInfo*(path: string): TPackageInfo = else: quit("Cannot open package info: " & path, QuitFailure) +proc optionalField(obj: PJsonNode, name: string): string = + if existsKey(obj, name): + if obj[name].kind == JString: + return obj[name].str + else: + quit("Corrupted packages.json file. " & name & " field is of unexpected type.") + else: return "" + +proc requiredField(obj: PJsonNode, name: string): string = + if existsKey(obj, name): + if obj[name].kind == JString: + return obj[name].str + else: + quit("Corrupted packages.json file. " & name & " field is of unexpected type.") + else: + quit("Package in packages.json file does not contain a " & name & " field.") + proc getPackage*(pkg: string, packagesPath: string, resPkg: var TPackage): bool = let packages = parseFile(packagesPath) for p in packages: if p["name"].str != pkg: continue resPkg.name = pkg - resPkg.url = p["url"].str - resPkg.downloadMethod = p["method"].str + resPkg.url = p.requiredField("url") + resPkg.version = p.optionalField("version") + resPkg.downloadMethod = p.requiredField("method") + resPkg.dvcsTag = p.optionalField("dvcs-tag") resPkg.tags = @[] for t in p["tags"]: resPkg.tags.add(t.str) - resPkg.description = p["description"].str + resPkg.description = p.requiredField("description") return true return false @@ -72,13 +93,15 @@ proc getPackageList*(packagesPath: string): seq[TPackage] = let packages = parseFile(packagesPath) for p in packages: var pkg: TPackage - pkg.name = p["name"].str - pkg.url = p["url"].str - pkg.downloadMethod = p["method"].str + pkg.name = p.requiredField("name") + pkg.version = p.optionalField("version") + pkg.url = p.requiredField("url") + pkg.downloadMethod = p.requiredField("method") + pkg.dvcsTag = p.optionalField("dvcs-tag") pkg.tags = @[] for t in p["tags"]: pkg.tags.add(t.str) - pkg.description = p["description"].str + pkg.description = p.requiredField("description") result.add(pkg) proc echoPackage*(pkg: TPackage) = diff --git a/readme.markdown b/readme.markdown index 9889e4f..0c3587a 100644 --- a/readme.markdown +++ b/readme.markdown @@ -11,21 +11,23 @@ Babel stores everything that has been installed in ~/.babel on Unix systems and in your $home/.babel on Windows. Libraries are stored in $babelDir/libs. ## Libraries -Libraries may contain a ``ProjectName.nim`` file, this file will be copied -to ~/.babel/libs/ProjectName.nim allowing anyone to import it with -``import ProjectName``, it is recommended to include such a file, however -it's not a requirement. -All modules should be placed in a ``ProjectName/`` folder. The reason for -this is that the main project file can then import the modules that it needs -with confidence that the filename of those modules will not change after -installation. +By convention, if you have a single file with the same filename as your package +name, then you can include it in the same directory as the .babel file. +However, if you have other public modules whose names are quite common, +they should be included in a separate directory by the name of "PackageName", so +as to not pollute the namespace. This will mean that your main file can be +imported by simply writing ``import PackageName`` and all other public modules +can be imported by writing ``import PackageName/module``. This structure can be +seen being used by (jester)[https://github.com/dom96/jester]. All private modules should be placed, by convention, in -a ``private`` folder inside the ``ProjectName/`` folder, these are modules which -the user of your library should not be importing. All files and folders in -``ProjectName/`` will be copied as-is, you can however specify to skip some -directories or files in your .babel file. +a ``private`` folder, these are modules which +the user of your library should not be importing. + +All files and folders in the directory of where the .babel file resides will be +copied as-is, you can however skip some directories or files in your by setting +the 'SkipDirs' or 'SkipFiles' options in your .babel file. ## Example .babel file