1 #!/usr/bin/env dub 2 /+ dub.sdl: 3 name "download_llvm" 4 +/ 5 module download_llvm; 6 7 enum downloadPath = "tmp"; 8 9 enum Architecture 10 { 11 bit64 = 64, 12 bit32 = 32, 13 x86_64 = bit64, 14 x86 = bit32, 15 x86_mscoff = bit32, 16 } 17 18 version (Windows) 19 { 20 enum llvmArchives = [ 21 64: [ 22 "10.0.0": "LLVM-10.0.0-win64.exe", 23 "9.0.0": "LLVM-9.0.0-win64.exe" 24 ], 25 26 32: [ 27 "10.0.0": "LLVM-10.0.0-win32.exe", 28 "9.0.0": "LLVM-9.0.0-win32.exe" 29 ] 30 ]; 31 32 enum llvmUrls = [ 33 "10.0.0": "https://github.com/llvm/llvm-project/releases/download/llvmorg-%s/%s", 34 "9.0.0": "https://releases.llvm.org/%s/%s", 35 ]; 36 } 37 38 version (OSX) 39 { 40 enum llvmArchives = [ 41 64: [ 42 "10.0.0": "clang+llvm-10.0.0-x86_64-apple-darwin.tar.xz", 43 "9.0.0": "clang+llvm-9.0.0-x86_64-darwin-apple.tar.xz", 44 "8.0.0": "clang+llvm-8.0.0-x86_64-apple-darwin.tar.xz", 45 "6.0.0": "clang+llvm-6.0.0-x86_64-apple-darwin.tar.xz" 46 ] 47 ]; 48 49 enum dstepLLVMArchives = [ 50 64: [ 51 "10.0.0": "llvm-10.0.0-macos-x86_64.tar.xz" 52 ] 53 ]; 54 55 enum llvmUrls = [ 56 "10.0.0": "https://github.com/llvm/llvm-project/releases/download/llvmorg-%s/%s", 57 "9.0.0": "https://releases.llvm.org/%s/%s", 58 "8.0.0": "https://releases.llvm.org/%s/%s", 59 "6.0.0": "https://releases.llvm.org/%s/%s" 60 ]; 61 62 enum dstepLLVMUrls = [ 63 "10.0.0": "https://github.com/jacob-carlborg/llvm-project/releases/download/dstep-%s/%s" 64 ]; 65 } 66 67 else version (linux) 68 { 69 enum llvmArchives = [0: ["":""]]; 70 71 enum dstepLLVMArchives = [ 72 64: [ 73 "10.0.0": "llvm-10.0.0-linux-x86_64.tar.xz" 74 ] 75 ]; 76 77 enum llvmUrls = ["": ""]; 78 79 enum dstepLLVMUrls = [ 80 "10.0.0": "https://github.com/jacob-carlborg/llvm-project/releases/download/dstep-%s/%s" 81 ]; 82 } 83 84 struct Config 85 { 86 bool dstepLLVM = false; 87 Architecture architecture = defaultArchitecture; 88 } 89 90 void main(string[] args) 91 { 92 import std.getopt : getopt; 93 94 Config config; 95 96 auto helpInfo = getopt( 97 args, 98 "dstep", &config.dstepLLVM, 99 "arch", &config.architecture 100 ); 101 102 downloadLLVM(config); 103 104 if (!shouldInstallUsingPackageManager(config)) 105 extractArchive(config); 106 } 107 108 bool shouldInstallUsingPackageManager(Config config) 109 { 110 version (linux) 111 return !config.dstepLLVM; 112 113 else 114 return false; 115 } 116 117 void installUsingPackageManager() 118 { 119 import std.format : format; 120 121 const llvmMajorVersion = .llvmMajorVersion == "6" ? "6.0" : .llvmMajorVersion; 122 123 executeShell("curl -L https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -"); 124 125 const repo = format!"deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-%s main"(llvmMajorVersion); 126 execute("sudo", "add-apt-repository", "-y", repo); 127 execute("sudo", "apt-get", "-q", "update"); 128 129 const libclangPackage = format!"libclang-%s-dev"(llvmMajorVersion); 130 execute("sudo", "apt-get", "install", "-y", libclangPackage); 131 } 132 133 void downloadLLVM(Config config) 134 { 135 import std.file : exists, mkdirRecurse; 136 import std.net.curl : download; 137 import std.path : buildPath; 138 import std.stdio : writefln; 139 140 if (shouldInstallUsingPackageManager(config)) 141 { 142 installUsingPackageManager(); 143 return; 144 } 145 146 auto archivePath = buildPath(downloadPath, llvmArchive(config)); 147 148 if (!exists(archivePath)) 149 { 150 writefln("Downloading LLVM %s to %s", llvmVersion, archivePath); 151 mkdirRecurse(downloadPath); 152 download(llvmUrl(config), archivePath); 153 } 154 155 else 156 writefln("LLVM %s already exists", llvmVersion); 157 } 158 159 string componentsToStrip(Config config) 160 { 161 if (config.dstepLLVM) 162 { 163 version (OSX) 164 return "4"; 165 else 166 return "3"; 167 } 168 else 169 return "1"; 170 } 171 172 void extractArchive(Config config) 173 { 174 import std.path : buildPath; 175 import std.stdio : writefln; 176 import std.file : mkdirRecurse; 177 178 auto archivePath = buildPath(downloadPath, llvmArchive(config)); 179 auto targetPath = buildPath(downloadPath, "clang"); 180 181 writefln("Extracting %s to %s", archivePath, targetPath); 182 183 mkdirRecurse(targetPath); 184 185 version (Posix) 186 execute("tar", "xf", archivePath, "-C", targetPath, 187 "--strip-components=" ~ componentsToStrip(config)); 188 else 189 execute("7z", "x", archivePath, "-y", "-o" ~ targetPath); 190 } 191 192 string llvmVersion() 193 { 194 import std.process : environment; 195 196 return environment.get("LLVM_VERSION", "10.0.0"); 197 } 198 199 string llvmMajorVersion() 200 { 201 import std..string : split; 202 203 return llvmVersion.split('.')[0]; 204 } 205 206 string llvmUrl(Config config) 207 { 208 import std.format : format; 209 210 const baseUrls = config.dstepLLVM ? dstepLLVMUrls : llvmUrls; 211 const baseUrl = baseUrls.tryGet(llvmVersion); 212 213 return format(baseUrl, llvmVersion, llvmArchive(config)); 214 } 215 216 string llvmArchive(Config config) 217 { 218 if (config.dstepLLVM) 219 return dstepLLVMArchive(config); 220 221 return archive(llvmArchives, config); 222 } 223 224 string dstepLLVMArchive(Config config) 225 { 226 return archive(dstepLLVMArchives, config); 227 } 228 229 string archive(string[string][int] archives, Config config) 230 { 231 return archives.tryGet(config.architecture).tryGet(llvmVersion); 232 } 233 234 Architecture defaultArchitecture() 235 { 236 version (X86_64) 237 return Architecture.bit64; 238 else version (X86) 239 return Architecture.bit32; 240 else 241 static assert("unsupported architecture"); 242 } 243 244 void execute(const string[] args ...) 245 { 246 import std.process : spawnProcess, wait; 247 import std.array : join; 248 249 if (spawnProcess(args).wait() != 0) 250 throw new Exception("Failed to execute command: " ~ args.join(' ')); 251 } 252 253 void executeShell(string command) 254 { 255 import std.process : spawnShell, wait; 256 257 if (spawnShell(command).wait() != 0) 258 throw new Exception("Failed to execute command: " ~ command); 259 } 260 261 inout(V) tryGet(K, V)(inout(V[K]) aa, K key) 262 { 263 import std.format : format; 264 265 if (auto value = key in aa) 266 return *value; 267 else 268 { 269 auto message = format("The key '%s' did not exist in the associative " ~ 270 "array: %s", key, aa 271 ); 272 273 throw new Exception(message); 274 } 275 }