1 /** 2 Package skeleton initialization code. 3 4 Copyright: © 2013-2016 rejectedsoftware e.K. 5 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 6 Authors: Sönke Ludwig 7 */ 8 module dub.init; 9 10 import dub.internal.vibecompat.core.file; 11 import dub.internal.vibecompat.core.log; 12 import dub.package_ : PackageFormat, packageInfoFiles, defaultPackageFilename; 13 import dub.recipe.packagerecipe; 14 import dub.dependency; 15 16 import std.datetime; 17 import std.exception; 18 import std.file; 19 import std.format; 20 import std.process; 21 import std.string; 22 23 24 /** Intializes a new package in the given directory. 25 26 The given `root_path` will be checked for any of the files that will be 27 created by this function. If any exist, an exception will be thrown before 28 altering the directory. 29 30 Params: 31 root_path = Directory in which to create the new package. If the 32 directory doesn't exist, a new one will be created. 33 deps = A set of extra dependencies to add to the package recipe. The 34 associative array is expected to map from package name to package 35 version. 36 type = The type of package skeleton to create. Can currently be 37 "minimal", "vibe.d" or "deimos" 38 recipe_callback = Optional callback that can be used to customize the 39 package recipe and the file format used to store it prior to 40 writing it to disk. 41 */ 42 void initPackage(Path root_path, string[string] deps, string type, 43 PackageFormat format, scope RecipeCallback recipe_callback = null) 44 { 45 import std.conv : to; 46 import dub.recipe.io : writePackageRecipe; 47 48 void enforceDoesNotExist(string filename) { 49 enforce(!existsFile(root_path ~ filename), "The target directory already contains a '"~filename~"' file. Aborting."); 50 } 51 52 string username = getUserName(); 53 54 PackageRecipe p; 55 p.name = root_path.head.toString().toLower(); 56 p.authors ~= username; 57 p.license = "proprietary"; 58 p.copyright = .format("Copyright © %s, %s", Clock.currTime().year, username); 59 foreach (pack, v; deps) { 60 import std.ascii : isDigit; 61 p.buildSettings.dependencies[pack] = Dependency(v); 62 } 63 64 //Check to see if a target directory needs to be created 65 if (!root_path.empty) { 66 if (!existsFile(root_path)) 67 createDirectory(root_path); 68 } 69 70 //Make sure we do not overwrite anything accidentally 71 foreach (fil; packageInfoFiles) 72 enforceDoesNotExist(fil.filename); 73 74 auto files = ["source/", "views/", "public/", "dub.json", ".gitignore"]; 75 foreach (fil; files) 76 enforceDoesNotExist(fil); 77 78 void processRecipe() 79 { 80 if (recipe_callback) 81 recipe_callback(p, format); 82 } 83 84 switch (type) { 85 default: throw new Exception("Unknown package init type: "~type); 86 case "minimal": initMinimalPackage(root_path, p, &processRecipe); break; 87 case "vibe.d": initVibeDPackage(root_path, p, &processRecipe); break; 88 case "deimos": initDeimosPackage(root_path, p, &processRecipe); break; 89 } 90 91 writePackageRecipe(root_path ~ ("dub."~format.to!string), p); 92 writeGitignore(root_path); 93 } 94 95 alias RecipeCallback = void delegate(ref PackageRecipe, ref PackageFormat); 96 97 private void initMinimalPackage(Path root_path, ref PackageRecipe p, scope void delegate() pre_write_callback) 98 { 99 p.description = "A minimal D application."; 100 pre_write_callback(); 101 102 createDirectory(root_path ~ "source"); 103 write((root_path ~ "source/app.d").toNativeString(), 104 q{import std.stdio; 105 106 void main() 107 { 108 writeln("Edit source/app.d to start your project."); 109 } 110 }); 111 } 112 113 private void initVibeDPackage(Path root_path, ref PackageRecipe p, scope void delegate() pre_write_callback) 114 { 115 if ("vibe-d" !in p.buildSettings.dependencies) 116 p.buildSettings.dependencies["vibe-d"] = Dependency("~>0.7.28"); 117 p.description = "A simple vibe.d server application."; 118 p.buildSettings.versions[""] ~= "VibeDefaultMain"; 119 pre_write_callback(); 120 121 createDirectory(root_path ~ "source"); 122 createDirectory(root_path ~ "views"); 123 createDirectory(root_path ~ "public"); 124 write((root_path ~ "source/app.d").toNativeString(), 125 q{import vibe.d; 126 127 shared static this() 128 { 129 auto settings = new HTTPServerSettings; 130 settings.port = 8080; 131 settings.bindAddresses = ["::1", "127.0.0.1"]; 132 listenHTTP(settings, &hello); 133 134 logInfo("Please open http://127.0.0.1:8080/ in your browser."); 135 } 136 137 void hello(HTTPServerRequest req, HTTPServerResponse res) 138 { 139 res.writeBody("Hello, World!"); 140 } 141 }); 142 } 143 144 private void initDeimosPackage(Path root_path, ref PackageRecipe p, scope void delegate() pre_write_callback) 145 { 146 import dub.compilers.buildsettings : TargetType; 147 148 auto name = root_path.head.toString().toLower(); 149 p.description = format("Deimos Bindings for "~p.name~"."); 150 p.buildSettings.importPaths[""] ~= "."; 151 p.buildSettings.targetType = TargetType.sourceLibrary; 152 pre_write_callback(); 153 154 createDirectory(root_path ~ "C"); 155 createDirectory(root_path ~ "deimos"); 156 } 157 158 private void writeGitignore(Path root_path) 159 { 160 write((root_path ~ ".gitignore").toNativeString(), 161 ".dub\ndocs.json\n__dummy.html\n*.o\n*.obj\n__test__*__\n"); 162 } 163 164 private string getUserName() 165 { 166 version (Windows) 167 return environment.get("USERNAME", "Peter Parker"); 168 else version (Posix) 169 { 170 import core.sys.posix.pwd, core.sys.posix.unistd, core.stdc.string : strlen; 171 import std.algorithm : splitter; 172 173 if (auto pw = getpwuid(getuid)) 174 { 175 auto uinfo = pw.pw_gecos[0 .. strlen(pw.pw_gecos)].splitter(','); 176 if (!uinfo.empty && uinfo.front.length) 177 return uinfo.front.idup; 178 } 179 return environment.get("USER", "Peter Parker"); 180 } 181 else 182 static assert(0); 183 }