Newer
Older
dub_jkp / source / dub / init.d
  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.logging;
  12. import dub.package_ : PackageFormat, packageInfoFiles, defaultPackageFilename;
  13. import dub.recipe.packagerecipe;
  14. import dub.dependency;
  15.  
  16. import std.exception;
  17. import std.file;
  18. import std.format;
  19. import std.process : environment;
  20. import std.string;
  21.  
  22.  
  23. /** Initializes a new package in the given directory.
  24.  
  25. The given `root_path` will be checked for any of the files that will be
  26. created by this function. If any exist, an exception will be thrown before
  27. altering the directory.
  28.  
  29. Params:
  30. root_path = Directory in which to create the new package. If the
  31. directory doesn't exist, a new one will be created.
  32. deps = A set of extra dependencies to add to the package recipe. The
  33. associative array is expected to map from package name to package
  34. version.
  35. type = The type of package skeleton to create. Can currently be
  36. "minimal", "vibe.d" or "deimos"
  37. format = Format in which the recipe will be written (SDL / JSON)
  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(NativePath root_path, VersionRange[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),
  50. "The target directory already contains a '%s' %s. Aborting."
  51. .format(filename, filename.isDir ? "directory" : "file"));
  52. }
  53.  
  54. string username = getUserName();
  55.  
  56. PackageRecipe p;
  57. p.name = root_path.head.name.toLower();
  58. p.authors ~= username;
  59. // Use proprietary as conservative default, so that we don't announce a more
  60. // permissive license than actually chosen in case the dub.json wasn't updated.
  61. p.license = "proprietary";
  62. foreach (pack, v; deps) {
  63. p.buildSettings.dependencies[pack] = Dependency(v);
  64. }
  65.  
  66. //Check to see if a target directory needs to be created
  67. if (!root_path.empty) {
  68. ensureDirectory(root_path);
  69. }
  70.  
  71. //Make sure we do not overwrite anything accidentally
  72. foreach (fil; packageInfoFiles)
  73. enforceDoesNotExist(fil.filename);
  74.  
  75. auto files = ["source/", "views/", "public/", "dub.json"];
  76. foreach (fil; files)
  77. enforceDoesNotExist(fil);
  78.  
  79. void processRecipe()
  80. {
  81. if (recipe_callback)
  82. recipe_callback(p, format);
  83. }
  84.  
  85. switch (type) {
  86. default: break;
  87. case "minimal": initMinimalPackage(root_path, p, &processRecipe); break;
  88. case "vibe.d": initVibeDPackage(root_path, p, &processRecipe); break;
  89. case "deimos": initDeimosPackage(root_path, p, &processRecipe); break;
  90. }
  91.  
  92. writePackageRecipe(root_path ~ ("dub."~format.to!string), p);
  93. writeGitignore(root_path, p.name);
  94. }
  95.  
  96. alias RecipeCallback = void delegate(ref PackageRecipe, ref PackageFormat);
  97.  
  98. private void initMinimalPackage(NativePath root_path, ref PackageRecipe p, scope void delegate() pre_write_callback)
  99. {
  100. p.description = "A minimal D application.";
  101. pre_write_callback();
  102.  
  103. ensureDirectory(root_path ~ "source");
  104. write((root_path ~ "source/app.d").toNativeString(),
  105. q{import std.stdio;
  106.  
  107. void main()
  108. {
  109. writeln("Edit source/app.d to start your project.");
  110. }
  111. });
  112. }
  113.  
  114. private void initVibeDPackage(NativePath root_path, ref PackageRecipe p, scope void delegate() pre_write_callback)
  115. {
  116. if ("vibe-d" !in p.buildSettings.dependencies)
  117. p.buildSettings.dependencies["vibe-d"] = Dependency("~>0.9");
  118. p.description = "A simple vibe.d server application.";
  119. pre_write_callback();
  120.  
  121. ensureDirectory(root_path ~ "source");
  122. ensureDirectory(root_path ~ "views");
  123. ensureDirectory(root_path ~ "public");
  124. write((root_path ~ "source/app.d").toNativeString(),
  125. q{import vibe.vibe;
  126.  
  127. void main()
  128. {
  129. auto settings = new HTTPServerSettings;
  130. settings.port = 8080;
  131. settings.bindAddresses = ["::1", "127.0.0.1"];
  132. auto listener = listenHTTP(settings, &hello);
  133. scope (exit)
  134. {
  135. listener.stopListening();
  136. }
  137.  
  138. logInfo("Please open http://127.0.0.1:8080/ in your browser.");
  139. runApplication();
  140. }
  141.  
  142. void hello(HTTPServerRequest req, HTTPServerResponse res)
  143. {
  144. res.writeBody("Hello, World!");
  145. }
  146. });
  147. }
  148.  
  149. private void initDeimosPackage(NativePath root_path, ref PackageRecipe p, scope void delegate() pre_write_callback)
  150. {
  151. import dub.compilers.buildsettings : TargetType;
  152.  
  153. p.description = format("Deimos Bindings for "~p.name~".");
  154. p.buildSettings.importPaths[""] ~= ".";
  155. p.buildSettings.targetType = TargetType.sourceLibrary;
  156. pre_write_callback();
  157.  
  158. ensureDirectory(root_path ~ "C");
  159. ensureDirectory(root_path ~ "deimos");
  160. }
  161.  
  162. /**
  163. * Write the `.gitignore` file to the directory, if it does not already exists
  164. *
  165. * As `dub` is often used with `git`, adding a `.gitignore` is a nice touch for
  166. * most users. However, this file is not mandatory for `dub` to do its job,
  167. * so we do not depend on the content.
  168. * One important use case we need to support is people running `dub init` on
  169. * a GitHub-initialized repository. Those might already contain a `.gitignore`
  170. * (and a README and a LICENSE), thus we should not bail out if the file already
  171. * exists, just ignore it.
  172. *
  173. * Params:
  174. * root_path = The path to the directory hosting the project
  175. * pkg_name = Name of the package, to generate a list of binaries to ignore
  176. */
  177. private void writeGitignore(NativePath root_path, const(char)[] pkg_name)
  178. {
  179. auto full_path = (root_path ~ ".gitignore").toNativeString();
  180.  
  181. if (existsFile(full_path))
  182. return;
  183.  
  184. write(full_path,
  185. q"{.dub
  186. docs.json
  187. __dummy.html
  188. docs/
  189. /%1$s
  190. %1$s.so
  191. %1$s.dylib
  192. %1$s.dll
  193. %1$s.a
  194. %1$s.lib
  195. %1$s-test-*
  196. *.exe
  197. *.pdb
  198. *.o
  199. *.obj
  200. *.lst
  201. }".format(pkg_name));
  202. }
  203.  
  204. private string getUserName()
  205. {
  206. version (Windows)
  207. return environment.get("USERNAME", "Peter Parker");
  208. else version (Posix)
  209. {
  210. import core.sys.posix.pwd, core.sys.posix.unistd, core.stdc.string : strlen;
  211. import std.algorithm : splitter;
  212.  
  213. // Bionic doesn't have pw_gecos on ARM
  214. version(CRuntime_Bionic) {} else
  215. if (auto pw = getpwuid(getuid))
  216. {
  217. auto uinfo = pw.pw_gecos[0 .. strlen(pw.pw_gecos)].splitter(',');
  218. if (!uinfo.empty && uinfo.front.length)
  219. return uinfo.front.idup;
  220. }
  221. return environment.get("USER", "Peter Parker");
  222. }
  223. else
  224. static assert(0);
  225. }