1 /** 2 * Copyright: Copyright (c) 2010-2011 Jacob Carlborg. All rights reserved. 3 * Authors: Jacob Carlborg 4 * Version: Initial created: Nov 8, 2010 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 */ 7 module dvm.commands.Install; 8 9 import tango.core.Exception; 10 import tango.io.Stdout; 11 import tango.io.device.File; 12 import tango.net.http.HttpGet; 13 import tango.sys.Common; 14 import tango.sys.Environment; 15 import tango.sys.Process; 16 import tango.sys.win32.Types; 17 import tango.util.compress.Zip : extractArchive; 18 19 import mambo.util.Version; 20 21 import dvm.commands.Command; 22 import dvm.commands.DmcInstall; 23 import dvm.commands.DvmInstall; 24 import dvm.commands.Fetch; 25 import dvm.commands.Use; 26 import mambo.core._; 27 import dvm.dvm.Wrapper; 28 import dvm.dvm._; 29 import Path = dvm.io.Path; 30 import dvm.util.Util; 31 32 class Install : Fetch 33 { 34 private 35 { 36 string archivePath; 37 string tmpCompilerPath; 38 string installPath_; 39 string binDestination_; 40 Wrapper wrapper; 41 string ver; 42 } 43 44 this () 45 { 46 super("install", "Install one or many D versions."); 47 } 48 49 override void execute () 50 { 51 // special case for the installation of dvm itself or dmc 52 if (args.any() && (args.first == "dvm" || args.first == "dmc")) 53 { 54 if (args.first == "dvm") 55 (new DvmInstall).invoke(args); 56 57 else 58 (new DmcInstall).invoke(args); 59 60 return; 61 } 62 63 install; 64 } 65 66 void install (string ver = "") 67 { 68 if(ver == "") 69 ver = getDMDVersion(); 70 71 this.ver = ver; 72 73 auto filename = buildFilename(ver); 74 auto url = buildUrl(ver, filename); 75 76 archivePath = Path.join(options.path.archives, filename); 77 78 fetch(url, archivePath); 79 println("Installing: dmd-", ver); 80 81 unpack; 82 moveFiles; 83 installWrapper; 84 85 version (Posix) 86 setPermissions; 87 88 installEnvironment(createEnvironment); 89 90 if (options.tango) 91 installTango; 92 } 93 94 private: 95 96 void unpack () 97 { 98 tmpCompilerPath = Path.join(options.path.tmp, "dmd-" ~ ver); 99 verbose("Unpacking:"); 100 verbose(options.indentation, "source: ", archivePath); 101 verbose(options.indentation, "destination: ", tmpCompilerPath, '\n'); 102 extractArchive(archivePath, tmpCompilerPath); 103 } 104 105 void moveFiles () 106 { 107 auto dmd = ver.length > 0 && ver[0] == '2' ? "dmd2" : "dmd"; 108 auto root = Path.join(tmpCompilerPath, dmd); 109 auto platformRoot = Path.join(root, Options.platform); 110 111 if (!Path.exists(platformRoot)) 112 throw new DvmException(mambo.core..string.format(`The platform "{}" is not compatible with the compiler dmd {}`, Options.platform, ver), __FILE__, __LINE__); 113 114 auto binSource = getBinSource(platformRoot); 115 116 auto srcSource = Path.join(root, options.path.src); 117 auto srcDest = Path.join(installPath, options.path.src); 118 119 verbose("Moving:"); 120 121 foreach (path ; getLibSources(platformRoot)) 122 { 123 auto dest = Path.join(installPath, options.platform, path.destination); 124 Path.move(path.source, dest); 125 } 126 127 Path.move(binSource, binDestination); 128 Path.move(srcSource, srcDest); 129 } 130 131 void installWrapper () 132 { 133 wrapper.target = Path.join(binDestination, "dmd" ~ options.path.executableExtension); 134 wrapper.path = Path.join(options.path.dvm, options.path.bin, "dmd-") ~ ver; 135 136 version (Windows) 137 wrapper.path ~= ".bat"; 138 139 verbose("Installing wrapper: " ~ wrapper.path); 140 wrapper.write; 141 } 142 143 void setPermissions () 144 { 145 verbose("Setting permissions:"); 146 147 setExecutableIfExists(Path.join(binDestination, "ddemangle")); 148 setExecutableIfExists(Path.join(binDestination, "dman")); 149 setExecutableIfExists(Path.join(binDestination, "dmd")); 150 setExecutableIfExists(Path.join(binDestination, "dub")); 151 setExecutableIfExists(Path.join(binDestination, "dumpobj")); 152 setExecutableIfExists(Path.join(binDestination, "dustmite")); 153 setExecutableIfExists(Path.join(binDestination, "obj2asm")); 154 setExecutableIfExists(Path.join(binDestination, "rdmd")); 155 setExecutableIfExists(Path.join(binDestination, "shell")); 156 157 setExecutableIfExists(wrapper.path); 158 } 159 160 void installEnvironment (ShellScript sh) 161 { 162 sh.path = options.path.env; 163 Path.createPath(sh.path); 164 sh.path = Path.join(sh.path, "dmd-" ~ ver ~ options.path.scriptExtension); 165 166 verbose("Installing environment: ", sh.path); 167 sh.write; 168 } 169 170 ShellScript createEnvironment () 171 { 172 auto sh = new ShellScript; 173 sh.echoOff; 174 175 auto envPath = binDestination; 176 auto binPath = Path.join(options.path.dvm, options.path.bin); 177 178 version (Posix) 179 sh.exportPath("PATH", envPath, binPath, Sh.variable("PATH", false)); 180 181 version (Windows) 182 { 183 Path.native(envPath); 184 Path.native(binPath); 185 sh.exportPath("DVM", envPath, binPath).nl; 186 sh.exportPath("PATH", envPath, Sh.variable("PATH", false)); 187 } 188 189 return sh; 190 } 191 192 void installTango () 193 { 194 verbose("Installing Tango"); 195 196 fetchTango; 197 unpackTango; 198 setupTangoEnvironment; 199 buildTango; 200 moveTangoFiles; 201 patchDmdConfForTango; 202 } 203 204 void fetchTango () 205 { 206 enum tangoUrl = "http://dsource.org/projects/tango/changeset/head/trunk?old_path=%2F&format=zip"; 207 fetch(tangoUrl, options.path.tangoZip); 208 } 209 210 void unpackTango () 211 { 212 verbose("Unpacking:"); 213 verbose(options.indentation, "source: ", options.path.tangoZip); 214 verbose(options.indentation, "destination: ", options.path.tangoTmp, '\n'); 215 extractArchive(options.path.tangoZip, options.path.tangoUnarchived); 216 } 217 218 void setupTangoEnvironment () 219 { 220 verbose(format(`Installing "{}" as the temporary D compiler`, ver)); 221 auto path = Environment.get("PATH"); 222 path = binDestination ~ options.path.pathSeparator ~ path; 223 Environment.set("PATH", path); 224 } 225 226 void buildTango () 227 { 228 version (Posix) 229 { 230 verbose("Setting permission:"); 231 permission(options.path.tangoBob, "+x"); 232 } 233 234 verbose("Building Tango..."); 235 236 string[] tangoBuildOptions = ["-r=dmd"[], "-c=dmd", "-u", "-q", "-l=" ~ options.path.tangoLibName]; 237 238 version (Posix) 239 tangoBuildOptions ~= options.is64bit ? "-m=64" : "-m=32"; 240 241 auto process = new Process(true, options.path.tangoBob ~ tangoBuildOptions ~ "."[]); 242 process.workDir = options.path.tangoTmp; 243 process.execute; 244 245 auto result = process.wait; 246 247 if (options.verbose || result.reason != Process.Result.Exit) 248 { 249 println("Output of the Tango build:", "\n"); 250 Stdout.copy(process.stdout).flush; 251 println(); 252 println("Process ", process.programName, '(', process.pid, ')', " exited with:"); 253 println(options.indentation, "reason: ", result); 254 println(options.indentation, "status: ", result.status, "\n"); 255 } 256 } 257 258 void moveTangoFiles () 259 { 260 verbose("Moving:"); 261 262 auto importDest = Path.join(installPath, options.path.import_); 263 264 auto tangoSource = options.path.tangoSrc; 265 auto tangoDest = Path.join(importDest, "tango"); 266 267 268 auto objectSrc = options.path.tangoObject; 269 auto objectDest = Path.join(importDest, options.path.object_di); 270 271 auto vendorSrc = options.path.tangoVendor; 272 auto vendorDest = Path.join(importDest, options.path.std); 273 274 auto libPath = options.is64bit ? options.path.lib64 : options.path.lib32; 275 276 Path.move(options.path.tangoLib, Path.join(installPath, options.platform, libPath, options.path.tangoLibName ~ options.path.libExtension)); 277 Path.move(vendorSrc, vendorDest); 278 Path.move(tangoSource, tangoDest); 279 Path.move(objectSrc, objectDest); 280 } 281 282 void patchDmdConfForTango () 283 { 284 auto dmdConfPath = Path.join(binDestination, options.path.confName); 285 286 verbose("Patching: ", dmdConfPath); 287 288 string newInclude = "-I%@P%/../../import"; 289 string newArgs = " -defaultlib=tango -debuglib=tango -version=Tango"; 290 string content = cast(string) File.get(dmdConfPath); 291 292 string oldInclude1 = "-I%@P%/../../src/phobos"; 293 string oldInclude2 = "-I%@P%/../../src/druntime/import"; 294 version (Windows) 295 { 296 oldInclude1 = '"' ~ oldInclude1 ~ '"'; 297 oldInclude2 = '"' ~ oldInclude2 ~ '"'; 298 newInclude = '"' ~ newInclude ~ '"'; 299 } 300 301 auto src = newInclude ~ newArgs; 302 303 content = content.slashSafeSubstitute(oldInclude1, src); 304 content = content.slashSafeSubstitute(oldInclude2, ""); 305 306 File.set(dmdConfPath, content); 307 } 308 309 string installPath () 310 { 311 if (installPath_.length > 0) 312 return installPath_; 313 314 return installPath_ = Path.join(options.path.compilers, "dmd-" ~ ver); 315 } 316 317 void permission (string path, string mode) 318 { 319 version (Posix) 320 { 321 verbose(options.indentation, "mode: " ~ mode); 322 verbose(options.indentation, "file: " ~ path, '\n'); 323 324 Path.permission(path, mode); 325 } 326 } 327 328 SourceDestination[] getLibSources (string platformRoot) 329 { 330 SourceDestination[] paths; 331 332 if (auto path = getLibSource(platformRoot, options.path.lib)) 333 paths ~= path; 334 335 if (auto path = getLibSource(platformRoot, options.path.lib32)) 336 paths ~= path; 337 338 if (auto path = getLibSource(platformRoot, options.path.lib64)) 339 paths ~= path; 340 341 if (paths.isEmpty) 342 throw new DvmException("Could not find any library paths", __FILE__, __LINE__); 343 344 return paths; 345 } 346 347 SourceDestination getLibSource (string platformRoot, string libPath) 348 { 349 auto path = Path.join(platformRoot, libPath); 350 351 if (Path.exists(path)) 352 return SourceDestination(path, libPath); 353 354 else 355 return SourceDestination.invalid; 356 } 357 358 string getBinSource (string platformRoot) 359 { 360 string binPath = Path.join(platformRoot, options.path.bin); 361 362 if (Path.exists(binPath)) 363 return binPath; 364 365 if (options.is64bit) 366 { 367 binPath = Path.join(platformRoot, options.path.bin64); 368 369 if (Path.exists(binPath)) 370 return binPath; 371 372 else 373 throw new DvmException("There is no 64bit compiler available on this platform", __FILE__, __LINE__); 374 } 375 376 binPath = Path.join(platformRoot, options.path.bin32); 377 378 if (Path.exists(binPath)) 379 return binPath; 380 381 throw new DvmException("Could not find the binrary path: " ~ binPath, __FILE__, __LINE__); 382 } 383 384 void setExecutableIfExists (string path) 385 { 386 if (Path.exists(path)) 387 permission(path, "+x"); 388 } 389 390 void validateArguments (string errorMessage = null) 391 { 392 if (errorMessage.isEmpty) 393 errorMessage = "Cannot install a compiler without specifying a version"; 394 395 super.validateArguments(errorMessage); 396 } 397 398 struct SourceDestination 399 { 400 string source; 401 string destination; 402 403 bool isValid () 404 { 405 return source.any && destination.any; 406 } 407 408 bool opCast (T : bool) () 409 { 410 return isValid; 411 } 412 413 static SourceDestination invalid () 414 { 415 return SourceDestination(null, null); 416 } 417 } 418 419 string binDestination () 420 { 421 return binDestination_ = binDestination_.any ? binDestination_ : Path.join(installPath, options.platform, options.path.bin); 422 } 423 }