1 /**
2  * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Jan 19, 2011
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module dvm.commands.DvmInstall;
8 
9 import std.algorithm : each, filter, find, map;
10 import std.array : array, join;
11 import std.exception : assumeUnique;
12 import std.file : append, exists, thisExePath;
13 import std.path : buildPath;
14 import std.stdio : writeln;
15 
16 import tango.io.device.File;
17 import tango.sys.HomeFolder;
18 
19 import Path = dvm.io.Path;
20 import dvm.commands.Command;
21 import dvm.dvm.Exceptions;
22 import dvm.dvm.ShellScript;
23 import dvm.dvm.Options;
24 import dvm.util.Util;
25 version (Windows) import DvmRegistry = dvm.util.DvmRegistry;
26 version (Windows) import dvm.util.Windows;
27 
28 class DvmInstall : Command
29 {
30     private
31     {
32         enum postInstallInstructions = import("post_install_instructions.txt");
33         enum failedInstallInstructions = import("failed_install_instructions.txt");
34 
35         version (Posix)
36             enum dvmScript = import("dvm.sh");
37 
38         else
39             enum dvmScript = import("dvm.bat");
40     }
41 
42     override void execute ()
43     {
44         install;
45     }
46 
47 private:
48 
49     void install ()
50     {
51         if (Path.exists(options.path.dvm))
52             return update;
53 
54         verbose("Installing dvm to: ", options.path.dvm);
55         createPaths;
56         copyExecutable;
57         writeScript;
58 
59         version (Posix)
60         {
61             setPermissions;
62             installBashInclude(createBashInclude);
63         }
64 
65         version (Windows)
66             setupRegistry;
67     }
68 
69     void update ()
70     {
71         createPaths;
72         copyExecutable;
73         writeScript;
74         setPermissions;
75 
76         version (Windows)
77             setupRegistry;
78     }
79 
80     void createPaths ()
81     {
82         verbose("Creating paths:");
83 
84         createPath(options.path.dvm);
85         createPath(options.path.archives);
86         createPath(Path.join(options.path.dvm, options.path.bin).assumeUnique);
87         createPath(options.path.compilers);
88         createPath(options.path.env);
89         createPath(options.path.scripts);
90 
91         verbose();
92     }
93 
94     void copyExecutable ()
95     {
96         verbose("Copying executable:");
97         verbose("thisExePath: ", thisExePath);
98         copy(thisExePath, options.path.dvmExecutable);
99     }
100 
101     void writeScript ()
102     {
103         verbose("Writing script to: ", options.path.dvmScript);
104         File.set(options.path.dvmScript, dvmScript);
105     }
106 
107     void setPermissions ()
108     {
109         verbose("Setting permissions:");
110         permission(options.path.dvmScript, "+x");
111         permission(options.path.dvmExecutable, "+x");
112     }
113 
114     version (Posix)
115         void installBashInclude (ShellScript sh)
116         {
117             static string defaultProfile()
118             {
119                 with(Shell) switch (Options.instance.shell)
120                 {
121                     case bash: return ".bash_profile";
122                     case zsh: return ".zprofile";
123                     default: throw new DvmException("Failed to identify a " ~
124                         "default shell profile file", __FILE__, __LINE__);
125                 }
126             }
127 
128             enum potentialShellProfileFiles = [
129                 [".bashrc", ".bash_profile"],
130                 [".zshrc", ".zprofile"]
131             ];
132 
133             const home = homeFolder.assumeUnique;
134             alias toFullPath = path => home.buildPath(path);
135 
136             auto existingPofilePaths = potentialShellProfileFiles
137                 .map!(files => files.map!toFullPath)
138                 .map!(files => files.find!exists)
139                 .filter!(files => !files.empty)
140                 .map!(files => files.front);
141 
142             auto profilePaths = existingPofilePaths.empty ?
143                 [home.buildPath(defaultProfile)] : existingPofilePaths.array;
144 
145             verbose("Installing dvm in the shell loading file(s): ", profilePaths.join(", "));
146             profilePaths.each!(path => append(path, sh.content));
147             writeln(postInstallInstructions);
148         }
149 
150     void createPath (string path)
151     {
152         verbose(options.indentation, path);
153         if(!Path.exists(path))
154             Path.createFolder(path);
155     }
156 
157     void permission (string path, string mode)
158     {
159         version (Posix)
160         {
161             verbose(options.indentation, "mode: " ~ mode);
162             verbose(options.indentation, "file: " ~ path, '\n');
163 
164             Path.permission(path, mode);
165         }
166     }
167 
168     void copy (string source, string destination)
169     {
170         verbose(options.indentation, "source: ", source);
171         verbose(options.indentation, "destination: ", destination, '\n');
172 
173         Path.copy(source, destination);
174     }
175 
176     ShellScript createBashInclude ()
177     {
178         auto sh = new ShellScript;
179         sh.nl.nl;
180         sh.comment("This loads DVM into a shell session.").nl;
181 
182         sh.ifFileIsNotEmpty(options.path.dvmScript, {
183             sh.source(options.path.dvmScript);
184         });
185 
186         return sh;
187     }
188 
189     version (Windows)
190         void setupRegistry ()
191         {
192             auto defaultCompilerPath = DvmRegistry.getDefaultCompilerPath();
193             DvmRegistry.updateEnvironment(options.path.binDir, defaultCompilerPath);
194             DvmRegistry.checkSystemPath();
195             broadcastSettingChange("Environment");
196             println("DVM has now been installed.");
197             println("To use dvm, you may need to open a new command prompt.");
198         }
199 }