diff --git a/CHANGELOG.md b/CHANGELOG.md index edb5d99..fb95c32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ -------------------- ### Features and improvements ### + - Added support for dub init to take a list of dependencies. (by Colin Grogan) + - Example usage (dub init myProj logger vibe-d gfm --type=vibe.d) + - Dub will then try to get the latest version number for each of these dependencies from code.dlang.org and automatically add them to the dependencies section of dub.json. + - If it cant find the dependant package name, it will ignore it, + - Current functionality is preserved whereby project type can be determined by using [vibe.d, deimos or minimal] after package name. (So example above would be dub init myProj vibe.d logger vibe-d gfm). + - Preferrable to use --type however, as this should be removed for next version. v0.9.22 - 2014-09-22 -------------------- diff --git a/source/dub/commandline.d b/source/dub/commandline.d index d553d8b..6571982 100644 --- a/source/dub/commandline.d +++ b/source/dub/commandline.d @@ -298,32 +298,51 @@ /******************************************************************************/ class InitCommand : Command { + private{ + string m_buildType = "minimal"; + } this() { this.name = "init"; - this.argumentsPattern = "[ []]"; + this.argumentsPattern = "[ [...]]"; this.description = "Initializes an empty package skeleton"; this.helpText = [ - "Initializes an empty package of the specified type in the given directory. By default, the current working dirctory is used. Available types:", - "", - "minimal - a simple \"hello world\" project with no dependencies (default)", - "vibe.d - minimal HTTP server based on vibe.d", - "deimos - standard skeleton for a static C binding library" + "Initializes an empty package of the specified type in the given directory. By default, the current working dirctory is used." ]; } override void prepare(scope CommandArgs args) { + args.getopt("t|type", &m_buildType, [ + "Set the type of project to generate. Available types:", + "", + "minimal - simple \"hello world\" project (default)", + "vibe.d - minimal HTTP server based on vibe.d", + "deimos - skeleton for C header bindings", + ]); } override int execute(Dub dub, string[] free_args, string[] app_args) { - string dir, type = "minimal"; + string dir; enforceUsage(app_args.empty, "Unexpected application arguments."); - enforceUsage(free_args.length <= 2, "Too many arguments."); - if (free_args.length >= 1) dir = free_args[0]; - if (free_args.length >= 2) type = free_args[1]; - dub.createEmptyPackage(Path(dir), type); + if (free_args.length) + { + dir = free_args[0]; + free_args = free_args[1 .. $]; + } + //TODO: Remove this block in next version + // Checks if argument uses current method of specifying project type. + if (free_args.length) + { + if (["vibe.d", "deimos", "minimal"].canFind(free_args[0])) + { + m_buildType = free_args[0]; + free_args = free_args[1 .. $]; + logInfo("Deprecated use of init type. Use --type=[vibe.d | deimos | minimal] in future."); + } + } + dub.createEmptyPackage(Path(dir), free_args, m_buildType); return 0; } } diff --git a/source/dub/dub.d b/source/dub/dub.d index 6b48aba..bb78e5f 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -603,14 +603,32 @@ m_packageManager.removeSearchPath(makeAbsolute(path), system ? LocalPackageType.system : LocalPackageType.user); } - void createEmptyPackage(Path path, string type) + void createEmptyPackage(Path path, string[] deps, string type) { if( !path.absolute() ) path = m_rootPath ~ path; path.normalize(); if (m_dryRun) return; + string[string] depVers; + string[] notFound; // keep track of any failed packages in here + foreach(ps; this.m_packageSuppliers){ + foreach(dep; deps){ + try{ + auto versionStrings = ps.getVersions(dep); + depVers[dep] = versionStrings[$-1].toString; + } catch(Exception e){ + notFound ~= dep; + } + } + } + if(notFound.length > 1){ + throw new Exception(format("Couldn't find packages: %-(%s, %).", notFound)); + } + else if(notFound.length == 1){ + throw new Exception(format("Couldn't find package: %-(%s, %).", notFound)); + } - initPackage(path, type); + initPackage(path, depVers, type); //Act smug to the user. logInfo("Successfully created an empty project in '%s'.", path.toNativeString()); diff --git a/source/dub/init.d b/source/dub/init.d index 877851d..1e9bd83 100644 --- a/source/dub/init.d +++ b/source/dub/init.d @@ -19,7 +19,7 @@ import std.string; -void initPackage(Path root_path, string type) +void initPackage(Path root_path, string[string] deps, string type) { void enforceDoesNotExist(string filename) { enforce(!existsFile(root_path ~ filename), "The target directory already contains a '"~filename~"' file. Aborting."); @@ -41,16 +41,16 @@ switch (type) { default: throw new Exception("Unknown package init type: "~type); - case "minimal": initMinimalPackage(root_path); break; - case "vibe.d": initVibeDPackage(root_path); break; - case "deimos": initDeimosPackage(root_path); break; + case "minimal": initMinimalPackage(root_path, deps); break; + case "vibe.d": initVibeDPackage(root_path, deps); break; + case "deimos": initDeimosPackage(root_path, deps); break; } writeGitignore(root_path); } -void initMinimalPackage(Path root_path) +void initMinimalPackage(Path root_path, string[string] deps) { - writePackageJson(root_path, "A minimal D application."); + writePackageJson(root_path, "A minimal D application.", deps); createDirectory(root_path ~ "source"); write((root_path ~ "source/app.d").toNativeString(), q{import std.stdio; @@ -62,10 +62,13 @@ }); } -void initVibeDPackage(Path root_path) +void initVibeDPackage(Path root_path, string[string] deps) { + if("vibe-d" !in deps) + deps["vibe-d"] = "~>0.7.19"; + writePackageJson(root_path, "A simple vibe.d server application.", - ["vibe-d": "~>0.7.19"], ["versions": `["VibeDefaultMain"]`]); + deps, ["versions": `["VibeDefaultMain"]`]); createDirectory(root_path ~ "source"); createDirectory(root_path ~ "views"); createDirectory(root_path ~ "public"); @@ -89,11 +92,11 @@ }); } -void initDeimosPackage(Path root_path) +void initDeimosPackage(Path root_path, string[string] deps) { auto name = root_path.head.toString().toLower(); writePackageJson(root_path, "Deimos Bindings for "~name~".", - null, ["targetType": `"sourceLibrary"`, "importPaths": `["."]`]); + deps, ["targetType": `"sourceLibrary"`, "importPaths": `["."]`]); createDirectory(root_path ~ "C"); createDirectory(root_path ~ "deimos"); } diff --git a/source/dub/internal/utils.d b/source/dub/internal/utils.d index e71be4e..766f039 100644 --- a/source/dub/internal/utils.d +++ b/source/dub/internal/utils.d @@ -225,3 +225,18 @@ auto idx = distMap.countUntil!(a => a <= distance); return (idx == -1) ? null : array[idx]; } + +/** + Searches for close matches to input in range. R must be a range of strings + Note: Sorts the strings range. Use std.range.indexed to avoid this... + */ +auto fuzzySearch(R)(R strings, string input){ + import std.algorithm : levenshteinDistance, schwartzSort, partition3; + import std.traits : isSomeString; + import std.range : ElementType; + + static assert(isSomeString!(ElementType!R), "Cannot call fuzzy search on non string rang"); + immutable threshold = input.length / 4; + return strings.partition3!((a, b) => a.length + threshold < b.length)(input)[1] + .schwartzSort!(p => levenshteinDistance(input.toUpper, p.toUpper)); +} diff --git a/test/0-init-fail/.gitignore b/test/0-init-fail/.gitignore new file mode 100644 index 0000000..433d266 --- /dev/null +++ b/test/0-init-fail/.gitignore @@ -0,0 +1,5 @@ +.dub +docs.json +__dummy.html +*.o +*.obj diff --git a/test/0-init-fail/0-init-fail.sh b/test/0-init-fail/0-init-fail.sh new file mode 100755 index 0000000..11fadf4 --- /dev/null +++ b/test/0-init-fail/0-init-fail.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +packname="0-init-fail-pack" +deps="logger PACKAGE_DONT_EXIST" # would be very unlucky if it does exist... + +$DUB init $packname $deps + +function cleanup { + rm -rf $packname +} + +if [ -e $packname/dub.json ]; then # package is there, it should have failed + cleanup + exit 1 +fi +exit 0 diff --git a/test/0-init-fail/dub.json b/test/0-init-fail/dub.json new file mode 100644 index 0000000..6e6605b --- /dev/null +++ b/test/0-init-fail/dub.json @@ -0,0 +1,8 @@ +{ + "name": "0-init-fail", + "description": "A minimal D application.", + "copyright": "Copyright © 2014, colin", + "authors": ["colin"], + "dependencies": { + } +} diff --git a/test/0-init-fail/source/app.d b/test/0-init-fail/source/app.d new file mode 100644 index 0000000..0569360 --- /dev/null +++ b/test/0-init-fail/source/app.d @@ -0,0 +1,10 @@ +import std.stdio; + +import std.process : execute; +int main(string[] args) +{ + writefln("Executing init test - fail"); + auto script = args[0] ~ ".sh"; + auto dubInit = execute(script); + return dubInit.status; +} diff --git a/test/0-init-multi/.gitignore b/test/0-init-multi/.gitignore new file mode 100644 index 0000000..433d266 --- /dev/null +++ b/test/0-init-multi/.gitignore @@ -0,0 +1,5 @@ +.dub +docs.json +__dummy.html +*.o +*.obj diff --git a/test/0-init-multi/0-init-multi.sh b/test/0-init-multi/0-init-multi.sh new file mode 100755 index 0000000..54b151e --- /dev/null +++ b/test/0-init-multi/0-init-multi.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +packname="0-init-multi-pack" +deps="openssl logger" +type="vibe.d" + +$DUB init $packname $deps --type=$type + +function cleanup { + rm -rf $packname +} + +if [ ! -e $packname/dub.json ]; then # it failed, exit 1 + exit 1 +else # check if resulting dub.json has all dependancies in tow + deps="$deps vibe-d"; + IFS=" " read -a arr <<< "$deps" + for ele in "${arr[@]}" + do + if [ `grep -c "$ele" $packname/dub.json` -ne 1 ]; then #something went wrong + echo "$ele not in $packname/dub.json" + cleanup + exit 1 + fi + done + cleanup + exit 0 + +fi diff --git a/test/0-init-multi/dub.json b/test/0-init-multi/dub.json new file mode 100644 index 0000000..752ff18 --- /dev/null +++ b/test/0-init-multi/dub.json @@ -0,0 +1,8 @@ +{ + "name": "0-init-multi", + "description": "A minimal D application.", + "copyright": "Copyright © 2014, colin", + "authors": ["colin"], + "dependencies": { + } +} diff --git a/test/0-init-multi/source/app.d b/test/0-init-multi/source/app.d new file mode 100644 index 0000000..acd9893 --- /dev/null +++ b/test/0-init-multi/source/app.d @@ -0,0 +1,10 @@ +import std.stdio; + +import std.process : execute; +int main(string[] args) +{ + writefln("Executing init test - multi"); + auto script = args[0] ~ ".sh"; + auto dubInit = execute(script); + return dubInit.status; +} diff --git a/test/0-init-simple/.gitignore b/test/0-init-simple/.gitignore new file mode 100644 index 0000000..433d266 --- /dev/null +++ b/test/0-init-simple/.gitignore @@ -0,0 +1,5 @@ +.dub +docs.json +__dummy.html +*.o +*.obj diff --git a/test/0-init-simple/0-init-simple.sh b/test/0-init-simple/0-init-simple.sh new file mode 100755 index 0000000..b5f9227 --- /dev/null +++ b/test/0-init-simple/0-init-simple.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +packname="0-init-simple-pack" + +$DUB init $packname + +function cleanup { + rm -rf $packname +} + +if [ ! -e $packname/dub.json ]; then # it failed + cleanup + exit 1 +fi +cleanup +exit 0 diff --git a/test/0-init-simple/dub.json b/test/0-init-simple/dub.json new file mode 100644 index 0000000..276804f --- /dev/null +++ b/test/0-init-simple/dub.json @@ -0,0 +1,8 @@ +{ + "name": "0-init-simple", + "description": "A minimal D application.", + "copyright": "Copyright © 2014, colin", + "authors": ["colin"], + "dependencies": { + } +} diff --git a/test/0-init-simple/source/app.d b/test/0-init-simple/source/app.d new file mode 100644 index 0000000..2bd9e78 --- /dev/null +++ b/test/0-init-simple/source/app.d @@ -0,0 +1,10 @@ +import std.stdio; + +import std.process : execute; +int main(string[] args) +{ + writefln("Executing init test - simple"); + auto script = args[0] ~ ".sh"; + auto dubInit = execute(script); + return dubInit.status; +}