Newer
Older
dub_jkp / source / dub / dub.d
  1. /**
  2. A package manager.
  3.  
  4. Copyright: © 2012 Matthias Dondorff
  5. License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
  6. Authors: Matthias Dondorff, Sönke Ludwig
  7. */
  8. module dub.dub;
  9.  
  10. import dub.compilers.compiler;
  11. import dub.dependency;
  12. import dub.installation;
  13. import dub.utils;
  14. import dub.registry;
  15. import dub.package_;
  16. import dub.packagemanager;
  17. import dub.packagesupplier;
  18. import dub.project;
  19. import dub.generators.generator;
  20.  
  21. import vibecompat.core.file;
  22. import vibecompat.core.log;
  23. import vibecompat.data.json;
  24. import vibecompat.inet.url;
  25.  
  26. // todo: cleanup imports.
  27. import std.algorithm;
  28. import std.array;
  29. import std.conv;
  30. import std.datetime;
  31. import std.exception;
  32. import std.file;
  33. import std.string;
  34. import std.typecons;
  35. import std.zip;
  36. import stdx.process;
  37.  
  38.  
  39.  
  40. /// The default supplier for packages, which is the registry
  41. /// hosted by vibed.org.
  42. PackageSupplier defaultPackageSupplier() {
  43. Url url = Url.parse("http://registry.vibed.org/");
  44. logDebug("Using the registry from %s", url);
  45. return new RegistryPS(url);
  46. }
  47.  
  48. /// The Dub class helps in getting the applications
  49. /// dependencies up and running. An instance manages one application.
  50. class Dub {
  51. private {
  52. Path m_cwd, m_tempPath;
  53. Path m_root;
  54. PackageSupplier m_packageSupplier;
  55. Path m_userDubPath, m_systemDubPath;
  56. Json m_systemConfig, m_userConfig;
  57. PackageManager m_packageManager;
  58. Project m_app;
  59. }
  60.  
  61. /// Initiales the package manager for the vibe application
  62. /// under root.
  63. this(PackageSupplier ps = defaultPackageSupplier())
  64. {
  65. m_cwd = Path(getcwd());
  66.  
  67. version(Windows){
  68. m_systemDubPath = Path(environment.get("ProgramData")) ~ "dub/";
  69. m_userDubPath = Path(environment.get("APPDATA")) ~ "dub/";
  70. m_tempPath = Path(environment.get("TEMP"));
  71. } else version(Posix){
  72. m_systemDubPath = Path("/var/lib/dub/");
  73. m_userDubPath = Path(environment.get("HOME")) ~ ".dub/";
  74. m_tempPath = Path("/tmp");
  75. }
  76. m_userConfig = jsonFromFile(m_userDubPath ~ "settings.json", true);
  77. m_systemConfig = jsonFromFile(m_systemDubPath ~ "settings.json", true);
  78.  
  79. m_packageSupplier = ps;
  80. m_packageManager = new PackageManager(m_systemDubPath ~ "packages/", m_userDubPath ~ "packages/");
  81. }
  82.  
  83. /// Returns the name listed in the package.json of the current
  84. /// application.
  85. @property string projectName() const { return m_app.name; }
  86.  
  87. @property Path projectPath() const { return m_root; }
  88.  
  89. @property string[] configurations() const { return m_app.configurations; }
  90.  
  91. @property inout(PackageManager) packageManager() inout { return m_packageManager; }
  92.  
  93. @property Path binaryPath() const { return m_app.binaryPath; }
  94.  
  95. void loadPackageFromCwd()
  96. {
  97. m_root = m_cwd;
  98. m_packageManager.projectPackagePath = m_root ~ ".dub/packages/";
  99. m_app = new Project(m_packageManager, m_root);
  100. }
  101.  
  102. string getDefaultConfiguration(BuildPlatform platform) const { return m_app.getDefaultConfiguration(platform); }
  103.  
  104. /// Lists all installed modules
  105. void list() {
  106. logInfo(m_app.info());
  107. }
  108.  
  109. /// Performs installation and uninstallation as necessary for
  110. /// the application.
  111. /// @param options bit combination of UpdateOptions
  112. bool update(UpdateOptions options) {
  113. Action[] actions = m_app.determineActions(m_packageSupplier, options);
  114. if( actions.length == 0 ) return true;
  115.  
  116. logInfo("The following changes could be performed:");
  117. bool conflictedOrFailed = false;
  118. foreach(Action a; actions) {
  119. logInfo(capitalize(to!string(a.type)) ~ ": " ~ a.packageId ~ ", version %s", a.vers);
  120. if( a.type == Action.Type.conflict || a.type == Action.Type.failure ) {
  121. logInfo("Issued by: ");
  122. conflictedOrFailed = true;
  123. foreach(string pkg, d; a.issuer)
  124. logInfo(" "~pkg~": %s", d);
  125. }
  126. }
  127.  
  128. if( conflictedOrFailed || options & UpdateOptions.JustAnnotate )
  129. return conflictedOrFailed;
  130.  
  131. // Uninstall first
  132.  
  133. // ??
  134. // foreach(Action a ; filter!((Action a) => a.type == Action.Type.Uninstall)(actions))
  135. // uninstall(a.packageId);
  136. // foreach(Action a; filter!((Action a) => a.type == Action.Type.InstallUpdate)(actions))
  137. // install(a.packageId, a.vers);
  138. foreach(Action a; actions)
  139. if(a.type == Action.Type.uninstall){
  140. assert(a.pack !is null, "No package specified for uninstall.");
  141. uninstall(a.pack);
  142. }
  143. foreach(Action a; actions)
  144. if(a.type == Action.Type.install)
  145. install(a.packageId, a.vers, a.location);
  146.  
  147. m_app.reinit();
  148. Action[] newActions = m_app.determineActions(m_packageSupplier, 0);
  149. if(newActions.length > 0) {
  150. logInfo("There are still some actions to perform:");
  151. foreach(Action a; newActions)
  152. logInfo("%s", a);
  153. }
  154. else
  155. logInfo("You are up to date");
  156.  
  157. return newActions.length == 0;
  158. }
  159.  
  160. /// Generate project files for a specified IDE.
  161. /// Any existing project files will be overridden.
  162. void generateProject(string ide, GeneratorSettings settings) {
  163. auto generator = createProjectGenerator(ide, m_app, m_packageManager);
  164. generator.generateProject(settings);
  165. }
  166. /// Creates a zip from the application.
  167. void createZip(string zipFile) {
  168. m_app.createZip(zipFile);
  169. }
  170.  
  171. /// Prints some information to the log.
  172. void info() {
  173. logInfo("Status for %s", m_root);
  174. logInfo("\n" ~ m_app.info());
  175. }
  176.  
  177. /// Gets all installed packages as a "packageId" = "version" associative array
  178. string[string] installedPackages() const { return m_app.installedPackagesIDs(); }
  179.  
  180. /// Installs the package matching the dependency into the application.
  181. /// @param addToApplication if true, this will also add an entry in the
  182. /// list of dependencies in the application's package.json
  183. void install(string packageId, const Dependency dep, InstallLocation location = InstallLocation.ProjectLocal)
  184. {
  185. auto pinfo = m_packageSupplier.packageJson(packageId, dep);
  186. string ver = pinfo["version"].get!string;
  187.  
  188. logInfo("Downloading %s %s...", packageId, ver);
  189.  
  190. logDebug("Acquiring package zip file");
  191. auto dload = m_root ~ ".dub/temp/downloads";
  192. auto tempfname = packageId ~ "-" ~ (ver.startsWith('~') ? ver[1 .. $] : ver) ~ ".zip";
  193. auto tempFile = m_tempPath ~ tempfname;
  194. if( existsFile(tempFile) ) removeFile(tempFile);
  195. m_packageSupplier.storePackage(tempFile, packageId, dep); // Q: continue on fail?
  196. scope(exit) removeFile(tempFile);
  197.  
  198. logInfo("Installing %s %s...", packageId, ver);
  199. m_packageManager.install(tempFile, pinfo, location);
  200. }
  201.  
  202. /// Uninstalls a given package from the list of installed modules.
  203. /// @removeFromApplication: if true, this will also remove an entry in the
  204. /// list of dependencies in the application's package.json
  205. void uninstall(in Package pack)
  206. {
  207. logInfo("Uninstalling %s in %s", pack.name, pack.path.toNativeString());
  208.  
  209. m_packageManager.uninstall(pack);
  210. }
  211.  
  212. void addLocalPackage(string path, string ver, bool system)
  213. {
  214. auto abs_path = Path(path);
  215. if( !abs_path.absolute ) abs_path = m_cwd ~ abs_path;
  216. m_packageManager.addLocalPackage(abs_path, Version(ver), system ? LocalPackageType.system : LocalPackageType.user);
  217. }
  218.  
  219. void removeLocalPackage(string path, bool system)
  220. {
  221. auto abs_path = Path(path);
  222. if( !abs_path.absolute ) abs_path = m_cwd ~ abs_path;
  223. m_packageManager.removeLocalPackage(abs_path, system ? LocalPackageType.system : LocalPackageType.user);
  224. }
  225.  
  226. void createEmptyPackage(Path path)
  227. {
  228. path.normalize();
  229.  
  230. //Check to see if a target directory needs to be created
  231. if( !path.empty ){
  232. if( !existsFile(path) )
  233. createDirectory(path);
  234. }
  235.  
  236. //Make sure we do not overwrite anything accidentally
  237. if( existsFile(path ~ PackageJsonFilename) ||
  238. existsFile(path ~ "source") ||
  239. existsFile(path ~ "views") ||
  240. existsFile(path ~ "public") )
  241. {
  242. throw new Exception("The current directory is not empty.\n");
  243. }
  244.  
  245. //raw strings must be unindented.
  246. immutable packageJson =
  247. `{
  248. "name": "`~(path.empty ? "my-project" : path.head.toString())~`",
  249. "description": "An example project skeleton",
  250. "homepage": "http://example.org",
  251. "copyright": "Copyright © 2000, Your Name",
  252. "authors": [
  253. "Your Name"
  254. ],
  255. "dependencies": {
  256. }
  257. }
  258. `;
  259. immutable appFile =
  260. `import std.stdio;
  261.  
  262. void main()
  263. {
  264. writeln("Edit source/app.d to start your project.");
  265. }
  266. `;
  267.  
  268. //Create the common directories.
  269. createDirectory(path ~ "source");
  270. createDirectory(path ~ "views");
  271. createDirectory(path ~ "public");
  272.  
  273. //Create the common files.
  274. openFile(path ~ PackageJsonFilename, FileMode.Append).write(packageJson);
  275. openFile(path ~ "source/app.d", FileMode.Append).write(appFile);
  276.  
  277. //Act smug to the user.
  278. logInfo("Successfully created an empty project in '"~path.toNativeString()~"'.");
  279. }
  280. }