1 module served.commands.dcd_update; 2 3 import served.extension; 4 import served.io.git_build; 5 import served.io.http_wrap; 6 import served.types; 7 8 import workspaced.api; 9 import workspaced.coms; 10 11 import rm.rf; 12 13 import std.algorithm : endsWith; 14 import std.format : format; 15 import std.json : JSONValue; 16 import std.path : baseName, buildPath, chainPath, isAbsolute; 17 18 import fs = std.file; 19 import io = std.stdio; 20 21 __gshared string dcdUpdateReason = null; 22 __gshared bool dcdUpdating; 23 @protocolNotification("served/updateDCD") 24 void updateDCD() 25 { 26 scope (exit) 27 { 28 dcdUpdating = false; 29 dcdUpdateReason = null; 30 } 31 32 if (dcdUpdateReason.length) 33 rpc.notifyMethod("coded/logInstall", "Installing DCD: " ~ dcdUpdateReason); 34 else 35 rpc.notifyMethod("coded/logInstall", "Installing DCD"); 36 37 string outputFolder = determineOutputFolder; 38 if (fs.exists(outputFolder)) 39 { 40 foreach (file; ["dcd", "DCD", "dcd-client", "dcd-server"]) 41 { 42 auto path = buildPath(outputFolder, file); 43 if (fs.exists(path)) 44 { 45 if (fs.isFile(path)) 46 fs.remove(path); 47 else 48 rmdirRecurseForce(path); 49 } 50 } 51 } 52 if (!fs.exists(outputFolder)) 53 fs.mkdirRecurse(outputFolder); 54 string ext = ""; 55 version (Windows) 56 ext = ".exe"; 57 string finalDestinationClient; 58 string finalDestinationServer; 59 60 bool success; 61 62 version (Windows) 63 enum latestVersionBroken = true; 64 else 65 enum latestVersionBroken = false; 66 67 // the latest version that has downloads available 68 enum bundledDCDVersion = "v0.13.5"; 69 70 bool compileFromSource = false; 71 version (DCDFromSource) 72 compileFromSource = true; 73 else 74 { 75 version (Windows) 76 { 77 // needed to check for 64 bit process compatibility on 32 bit binaries because of WoW64 78 import core.sys.windows.windows : GetNativeSystemInfo, SYSTEM_INFO, 79 PROCESSOR_ARCHITECTURE_INTEL; 80 81 SYSTEM_INFO sysInfo; 82 GetNativeSystemInfo(&sysInfo); 83 if (sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) // only 64 bit releases 84 compileFromSource = true; 85 } 86 87 if (!checkVersion(bundledDCDVersion, DCDComponent.latestKnownVersion)) 88 compileFromSource = true; 89 } 90 91 static if (latestVersionBroken) 92 compileFromSource = true; 93 94 string[] triedPaths; 95 96 if (compileFromSource) 97 { 98 success = compileDependency(outputFolder, "DCD", 99 "https://github.com/dlang-community/DCD.git", [ 100 [ 101 firstConfig.git.userPath, "submodule", "update", "--init", 102 "--recursive" 103 ], ["dub", "build", "--config=client"], 104 ["dub", "build", "--config=server"] 105 ]); 106 finalDestinationClient = buildPath(outputFolder, "DCD", "dcd-client" ~ ext); 107 if (!fs.exists(finalDestinationClient)) 108 finalDestinationClient = buildPath(outputFolder, "DCD", "bin", "dcd-client" ~ ext); 109 finalDestinationServer = buildPath(outputFolder, "DCD", "dcd-server" ~ ext); 110 if (!fs.exists(finalDestinationServer)) 111 finalDestinationServer = buildPath(outputFolder, "DCD", "bin", "dcd-server" ~ ext); 112 113 triedPaths = [ 114 "DCD/dcd-client" ~ ext, "DCD/dcd-server" ~ ext, "DCD/bin/dcd-client" ~ ext, 115 "DCD/bin/dcd-server" ~ ext 116 ]; 117 } 118 else 119 { 120 string url; 121 122 enum commonPrefix = "https://github.com/dlang-community/DCD/releases/download/" 123 ~ bundledDCDVersion ~ "/dcd-" ~ bundledDCDVersion; 124 125 version (Windows) 126 url = commonPrefix ~ "-windows-x86_64.zip"; 127 else version (linux) 128 url = commonPrefix ~ "-linux-x86_64.tar.gz"; 129 else version (OSX) 130 url = commonPrefix ~ "-osx-x86_64.tar.gz"; 131 else 132 static assert(false); 133 134 import std.process : pipeProcess, Redirect, Config, wait; 135 import std.zip : ZipArchive; 136 import core.thread : Fiber; 137 138 string destFile = buildPath(outputFolder, url.baseName); 139 140 try 141 { 142 rpc.notifyMethod("coded/logInstall", "Downloading from " ~ url ~ " to " ~ outputFolder); 143 144 if (fs.exists(destFile)) 145 rpc.notifyMethod("coded/logInstall", 146 "Zip file already exists! Trying to install existing zip."); 147 else 148 downloadFile(url, "Downloading DCD...", destFile); 149 150 rpc.notifyMethod("coded/logInstall", "Extracting download..."); 151 if (url.endsWith(".tar.gz")) 152 { 153 rpc.notifyMethod("coded/logInstall", "> tar xvfz " ~ url.baseName); 154 auto proc = pipeProcess(["tar", "xvfz", url.baseName], 155 Redirect.stdout | Redirect.stderrToStdout, null, Config.none, outputFolder); 156 foreach (line; proc.stdout.byLineCopy) 157 rpc.notifyMethod("coded/logInstall", line); 158 proc.pid.wait; 159 } 160 else if (url.endsWith(".zip")) 161 { 162 auto zip = new ZipArchive(fs.read(destFile)); 163 foreach (name, am; zip.directory) 164 { 165 if (name.isAbsolute) 166 name = "." ~ name; 167 zip.expand(am); 168 fs.write(chainPath(outputFolder, name), am.expandedData); 169 } 170 } 171 success = true; 172 173 finalDestinationClient = buildPath(outputFolder, "dcd-client" ~ ext); 174 finalDestinationServer = buildPath(outputFolder, "dcd-server" ~ ext); 175 176 if (!fs.exists(finalDestinationClient)) 177 finalDestinationClient = buildPath(outputFolder, "bin", "dcd-client" ~ ext); 178 if (!fs.exists(finalDestinationServer)) 179 finalDestinationServer = buildPath(outputFolder, "bin", "dcd-client" ~ ext); 180 181 triedPaths = [ 182 "dcd-client" ~ ext, "dcd-server" ~ ext, "bin/dcd-client" ~ ext, 183 "bin/dcd-server" ~ ext 184 ]; 185 } 186 catch (Exception e) 187 { 188 rpc.notifyMethod("coded/logInstall", "Failed installing: " ~ e.toString ~ "\n\n"); 189 rpc.notifyMethod("coded/logInstall", 190 "If you have troube downloading via code-d, try manually downloading the DCD archive and placing it in " 191 ~ destFile ~ "!"); 192 success = false; 193 } 194 } 195 196 if (success && (!fs.exists(finalDestinationClient) || !fs.exists(finalDestinationServer))) 197 { 198 rpc.notifyMethod("coded/logInstall", 199 "Successfully downloaded DCD, but could not find the executables."); 200 rpc.notifyMethod("coded/logInstall", 201 "Please open your user settings and insert the paths for dcd-client and dcd-server manually."); 202 rpc.notifyMethod("coded/logInstall", "Download base location: " ~ outputFolder); 203 rpc.notifyMethod("coded/logInstall", ""); 204 rpc.notifyMethod("coded/logInstall", format("Tried %(%s, %)", triedPaths)); 205 206 finalDestinationClient = "dcd-client"; 207 finalDestinationServer = "dcd-server"; 208 } 209 210 if (success) 211 { 212 dcdUpdating = false; 213 214 backend.globalConfiguration.set("dcd", "clientPath", finalDestinationClient); 215 backend.globalConfiguration.set("dcd", "serverPath", finalDestinationServer); 216 217 foreach (ref workspace; workspaces) 218 { 219 workspace.config.d.dcdClientPath = finalDestinationClient; 220 workspace.config.d.dcdServerPath = finalDestinationServer; 221 } 222 rpc.notifyMethod("coded/updateSetting", UpdateSettingParams("dcdClientPath", 223 JSONValue(finalDestinationClient), true)); 224 rpc.notifyMethod("coded/updateSetting", UpdateSettingParams("dcdServerPath", 225 JSONValue(finalDestinationServer), true)); 226 rpc.notifyMethod("coded/logInstall", "Successfully installed DCD"); 227 228 foreach (ref workspace; workspaces) 229 { 230 auto instance = backend.getInstance(workspace.folder.uri.uriToFile); 231 if (instance is null) 232 rpc.notifyMethod("coded/logInstall", 233 "Failed to find workspace to start DCD for " ~ workspace.folder.uri); 234 else 235 { 236 instance.config.set("dcd", "clientPath", finalDestinationClient); 237 instance.config.set("dcd", "serverPath", finalDestinationServer); 238 239 lazyStartDCDServer(instance, workspace.folder.uri); 240 } 241 } 242 } 243 }