1 module test; 2 3 import std.process; 4 import std.file : rmdirRecurse; 5 6 import Path = tango.io.Path; 7 8 import mambo.core._; 9 10 int main () 11 { 12 return TestRunner().run; 13 } 14 15 struct TestRunner 16 { 17 private string wd; 18 19 int run () 20 { 21 int result = 0; 22 auto matrix = setup(); 23 activate(matrix.clangs.first); 24 build(); 25 26 foreach (const clang ; matrix.clangs) 27 { 28 activate(clang); 29 println("Testing with libclang version ", clang.version_); 30 result += test(); 31 } 32 33 return result; 34 } 35 36 string workingDirectory () 37 { 38 import tango.sys.Environment; 39 40 if (wd.any) 41 return wd; 42 43 return wd = Environment.cwd.assumeUnique; 44 } 45 46 auto setup () 47 { 48 auto matrix = ClangMatrix(workingDirectory, clangBasePath); 49 matrix.downloadAll; 50 matrix.extractAll; 51 52 return matrix; 53 } 54 55 string clangBasePath () 56 { 57 return Path.join(workingDirectory, "clangs").assumeUnique; 58 } 59 60 void activate (const Clang clang) 61 { 62 auto src = Path.join(workingDirectory, clang.versionedLibclang); 63 auto dest = Path.join(workingDirectory, clang.libclang); 64 65 if (Path.exists(dest)) 66 Path.remove(dest); 67 68 Path.copy(src, dest); 69 } 70 71 int test () 72 { 73 auto result = execute("cucumber"); 74 75 if (result.status != 0) 76 println(result.output); 77 78 return result.status; 79 } 80 81 void build () 82 { 83 auto result = executeShell("dub build"); 84 85 if (result.status != 0) 86 { 87 println(result.output); 88 throw new Exception("Failed to build DStep"); 89 } 90 } 91 } 92 93 struct Clang 94 { 95 string version_; 96 string baseUrl; 97 string filename; 98 99 version (linux) 100 { 101 enum extension = ".so"; 102 enum prefix = "lib"; 103 } 104 105 else version (OSX) 106 { 107 enum extension = ".dylib"; 108 enum prefix = "lib"; 109 } 110 111 else version (Windows) 112 { 113 enum extension = ".dll"; 114 enum prefix = ""; 115 } 116 117 else version (FreeBSD) 118 { 119 enum extension = ".so"; 120 enum prefix = "lib"; 121 } 122 123 else 124 static assert(false, "Unsupported platform"); 125 126 string libclang () const 127 { 128 return Clang.prefix ~ "clang" ~ Clang.extension; 129 } 130 131 string versionedLibclang () const 132 { 133 return Clang.prefix ~ "clang-" ~ version_ ~ Clang.extension; 134 } 135 } 136 137 struct ClangMatrix 138 { 139 import Path = tango.io.Path; 140 141 private 142 { 143 string basePath; 144 string workingDirectory; 145 string clangPath_; 146 immutable Clang[] clangs; 147 } 148 149 150 this (string workingDirectory, string basePath) 151 { 152 clangs = getClangs(); 153 this.workingDirectory = workingDirectory; 154 this.basePath = basePath; 155 } 156 157 void downloadAll () 158 { 159 foreach (clang ; ClangMatrix.clangs) 160 { 161 if (libclangExists(clang)) 162 continue; 163 164 println("Downloading clang ", clang.version_); 165 Path.createPath(basePath); 166 download(clang); 167 } 168 } 169 170 void extractAll () 171 { 172 foreach (clang ; ClangMatrix.clangs) 173 { 174 if (libclangExists(clang)) 175 continue; 176 177 println("Extracting clang ", clang.version_); 178 extractArchive(clang); 179 extractLibclang(clang); 180 clean(); 181 } 182 } 183 184 private: 185 186 bool libclangExists (const ref Clang clang) 187 { 188 auto libclangPath = Path.join(workingDirectory, clang.versionedLibclang); 189 return Path.exists(libclangPath); 190 } 191 192 void download (const ref Clang clang) 193 { 194 auto url = clang.baseUrl ~ clang.filename; 195 auto dest = archivePath(clang.filename); 196 197 if (!Path.exists(dest)) 198 Http.download(url, dest); 199 } 200 201 void extractArchive (const ref Clang clang) 202 { 203 auto src = archivePath(clang.filename); 204 auto dest = clangPath(); 205 Path.createPath(dest); 206 207 auto result = execute(["tar", "--strip-components=1", "-C", dest, "-xf", src]); 208 209 if (result.status != 0) 210 throw new ProcessException("Failed to extract archive"); 211 } 212 213 string archivePath (string filename) 214 { 215 return Path.join(basePath, filename).assumeUnique; 216 } 217 218 string clangPath () 219 { 220 if (clangPath_.any) 221 return clangPath_; 222 223 return clangPath_ = Path.join(basePath, "clang").assumeUnique; 224 } 225 226 void extractLibclang (const ref Clang clang) 227 { 228 auto src = Path.join(clangPath, "lib", clang.libclang); 229 auto dest = Path.join(workingDirectory, clang.versionedLibclang); 230 231 Path.copy(src, dest); 232 } 233 234 void clean () 235 { 236 rmdirRecurse(clangPath); 237 } 238 239 immutable(Clang[]) getClangs () 240 { 241 version (FreeBSD) 242 { 243 version (D_LP64) 244 return [ 245 // Clang("3.7.1", "http://llvm.org/releases/3.7.1/", "clang+llvm-3.7.1-amd64-unknown-freebsd10.tar.xz"), 246 // Clang("3.7.0", "http://llvm.org/releases/3.7.0/", "clang+llvm-3.7.0-amd64-unknown-freebsd10.tar.xz"), 247 // Clang("3.6.2", "http://llvm.org/releases/3.6.2/", "clang+llvm-3.6.2-amd64-unknown-freebsd10.tar.xz"), 248 // Clang("3.6.1", "http://llvm.org/releases/3.6.1/", "clang+llvm-3.6.1-amd64-unknown-freebsd10.tar.xz"), 249 // Clang("3.6.0", "http://llvm.org/releases/3.6.0/", "clang+llvm-3.6.0-amd64-unknown-freebsd10.tar.xz"), 250 // Clang("3.5.0", "http://llvm.org/releases/3.5.0/", "clang+llvm-3.5.0-amd64-unknown-freebsd10.tar.xz"), 251 Clang("3.4", "http://llvm.org/releases/3.4/", "clang+llvm-3.4-amd64-unknown-freebsd9.2.tar.xz"), 252 Clang("3.3", "http://llvm.org/releases/3.3/", "clang+llvm-3.3-amd64-freebsd9.tar.xz"), 253 Clang("3.2", "http://llvm.org/releases/3.2/", "clang+llvm-3.2-amd64-freebsd9.tar.gz"), 254 Clang("3.1", "http://llvm.org/releases/3.1/", "clang+llvm-3.1-amd64-freebsd9.tar.bz2") 255 ]; 256 257 else 258 return [ 259 // Clang("3.7.1", "http://llvm.org/releases/3.7.1/", "clang+llvm-3.7.1-i386-unknown-freebsd10.tar.xz"), 260 // Clang("3.7.0", "http://llvm.org/releases/3.7.1/", "clang+llvm-3.7.0-i386-unknown-freebsd10.tar.xz"), 261 // Clang("3.6.2", "http://llvm.org/releases/3.6.2/", "clang+llvm-3.6.2-i386-unknown-freebsd10.tar.xz"), 262 // Clang("3.6.1", "http://llvm.org/releases/3.6.1/", "clang+llvm-3.6.1-i386-unknown-freebsd10.tar.xz"), 263 // Clang("3.6.0", "http://llvm.org/releases/3.6.0/", "clang+llvm-3.6.0-i386-unknown-freebsd10.tar.xz"), 264 // Clang("3.5.0", "http://llvm.org/releases/3.5.0/", "clang+llvm-3.5.0-i386-unknown-freebsd10.tar.xz"), 265 Clang("3.4", "http://llvm.org/releases/3.4/", "clang+llvm-3.4-i386-unknown-freebsd9.2.tar.xz"), 266 Clang("3.3", "http://llvm.org/releases/3.3/", "clang+llvm-3.3-i386-freebsd9.tar.xz"), 267 Clang("3.2", "http://llvm.org/releases/3.2/", "clang+llvm-3.2-i386-freebsd9.tar.gz"), 268 Clang("3.1", "http://llvm.org/releases/3.1/", "clang+llvm-3.1-i386-freebsd9.tar.bz2") 269 ]; 270 } 271 272 else version (linux) 273 { 274 if (System.isTravis) 275 { 276 return [ 277 // Clang("3.5.1", "http://llvm.org/releases/3.5.1/", "clang+llvm-3.5.1-x86_64-linux-gnu.tar.xz"), 278 // Clang("3.5.0", "http://llvm.org/releases/3.5.0/", "clang+llvm-3.5.0-x86_64-linux-gnu-ubuntu-14.04.tar.xz"), 279 Clang("3.4.2", "http://llvm.org/releases/3.4.2/", "clang+llvm-3.4.2-x86_64-unknown-ubuntu12.04.xz"), 280 Clang("3.4.1", "http://llvm.org/releases/3.4.1/", "clang+llvm-3.4.1-x86_64-unknown-ubuntu12.04.tar.xz"), 281 Clang("3.4", "http://llvm.org/releases/3.4/", "clang+llvm-3.4-x86_64-unknown-ubuntu12.04.tar.xz"), 282 Clang("3.3", "http://llvm.org/releases/3.3/", "clang+llvm-3.3-amd64-Ubuntu-12.04.2.tar.gz"), 283 Clang("3.2", "http://llvm.org/releases/3.2/", "clang+llvm-3.2-x86_64-linux-ubuntu-12.04.tar.gz"), 284 Clang("3.1", "http://llvm.org/releases/3.1/", "clang+llvm-3.1-x86_64-linux-ubuntu_12.04.tar.gz") 285 ]; 286 } 287 288 else if (System.isUbuntu) 289 { 290 version (D_LP64) 291 return [ 292 Clang("3.5.1", "http://llvm.org/releases/3.5.1/", "clang+llvm-3.5.1-x86_64-linux-gnu.tar.xz"), 293 Clang("3.5.0", "http://llvm.org/releases/3.5.0/", "clang+llvm-3.5.0-x86_64-linux-gnu-ubuntu-14.04.tar.xz"), 294 Clang("3.4.2", "http://llvm.org/releases/3.4.2/", "clang+llvm-3.4.2-x86_64-unknown-ubuntu12.04.xz"), 295 Clang("3.4.1", "http://llvm.org/releases/3.4.1/", "clang+llvm-3.4.1-x86_64-unknown-ubuntu12.04.tar.xz"), 296 Clang("3.4", "http://llvm.org/releases/3.4/", "clang+llvm-3.4-x86_64-unknown-ubuntu12.04.tar.xz"), 297 Clang("3.3", "http://llvm.org/releases/3.3/", "clang+llvm-3.3-amd64-Ubuntu-12.04.2.tar.gz"), 298 Clang("3.2", "http://llvm.org/releases/3.2/", "clang+llvm-3.2-x86_64-linux-ubuntu-12.04.tar.gz"), 299 Clang("3.1", "http://llvm.org/releases/3.1/", "clang+llvm-3.1-x86_64-linux-ubuntu_12.04.tar.gz") 300 ]; 301 else 302 return [ 303 Clang("3.2", "http://llvm.org/releases/3.2/", "clang+llvm-3.2-x86-linux-ubuntu-12.04.tar.gz"), 304 Clang("3.1", "http://llvm.org/releases/3.1/", "clang+llvm-3.1-x86-linux-ubuntu_12.04.tar.gz") 305 ]; 306 } 307 308 else if (System.isDebian) 309 { 310 version (D_LP64) 311 return [ 312 Clang("3.3", "http://llvm.org/releases/3.3/", "clang+llvm-3.3-amd64-debian6.tar.bz2") 313 ]; 314 else 315 return [ 316 Clang("3.3", "http://llvm.org/releases/3.3/", "clang+llvm-3.3-i386-debian6.tar.bz2"), 317 ]; 318 319 } 320 321 else if (System.isFedora) 322 { 323 version (D_LP64) 324 return [ 325 Clang("3.5.1", "http://llvm.org/releases/3.5.1/", "clang+llvm-3.5.1-x86_64-fedora20.tar.xz"), 326 Clang("3.5.0", "http://llvm.org/releases/3.5.0/", "clang+llvm-3.5.0-x86_64-fedora20.tar.xz"), 327 Clang("3.4", "http://llvm.org/releases/3.4/", "clang+llvm-3.4-x86_64-fedora19.tar.gz"), 328 Clang("3.3", "http://llvm.org/releases/3.3/", "clang+llvm-3.3-x86_64-fedora18.tar.bz2") 329 ]; 330 else 331 return [ 332 Clang("3.5.1", "http://llvm.org/releases/3.5.1/", "clang+llvm-3.5.1-i686-fedora20.tar.xz"), 333 Clang("3.5.0", "http://llvm.org/releases/3.5.0/", "clang+llvm-3.5.0-i686-fedora20.tar.xz"), 334 Clang("3.4.2", "http://llvm.org/releases/3.4.2/", "clang+llvm-3.4.2-i686-fedora20.xz"), 335 Clang("3.4.1", "http://llvm.org/releases/3.4.1/", "clang+llvm-3.4.1-i686-fedora20.tar.xz"), 336 Clang("3.4", "http://llvm.org/releases/3.4/", "clang+llvm-3.4-i686-fedora19.tar.gz"), 337 Clang("3.3", "http://llvm.org/releases/3.3/", "clang+llvm-3.3-i686-fedora18.tar.bz2") 338 ]; 339 } 340 341 else 342 throw new Exception("Current Linux distribution '" ~ System.update ~ "' is not supported"); 343 } 344 345 else version (OSX) 346 { 347 version (D_LP64) 348 { 349 if (System.isTravis) 350 return [ 351 Clang("3.7.0", "http://llvm.org/releases/3.7.0/", "clang+llvm-3.7.0-x86_64-apple-darwin.tar.xz"), 352 Clang("3.6.2", "http://llvm.org/releases/3.6.2/", "clang+llvm-3.6.2-x86_64-apple-darwin.tar.xz"), 353 Clang("3.6.1", "http://llvm.org/releases/3.6.1/", "clang+llvm-3.6.1-x86_64-apple-darwin.tar.xz"), 354 Clang("3.6.0", "http://llvm.org/releases/3.6.0/", "clang+llvm-3.6.0-x86_64-apple-darwin.tar.xz"), 355 Clang("3.5.0", "http://llvm.org/releases/3.5.0/", "clang+llvm-3.5.0-macosx-apple-darwin.tar.xz"), 356 // Clang("3.4.2", "http://llvm.org/releases/3.4.2/", "clang+llvm-3.4.2-x86_64-apple-darwin10.9.xz"), 357 // Clang("3.4.1", "http://llvm.org/releases/3.4.1/", "clang+llvm-3.4.1-x86_64-apple-darwin10.9.tar.xz"), 358 // Clang("3.4", "http://llvm.org/releases/3.4/", "clang+llvm-3.4-x86_64-apple-darwin10.9.tar.gz"), 359 Clang("3.3", "http://llvm.org/releases/3.3/", "clang+llvm-3.3-x86_64-apple-darwin12.tar.gz"), 360 Clang("3.2", "http://llvm.org/releases/3.2/", "clang+llvm-3.2-x86_64-apple-darwin11.tar.gz"), 361 Clang("3.1", "http://llvm.org/releases/3.1/", "clang+llvm-3.1-x86_64-apple-darwin11.tar.gz") 362 ]; 363 364 else 365 return [ 366 Clang("3.7.0", "http://llvm.org/releases/3.7.0/", "clang+llvm-3.7.0-x86_64-apple-darwin.tar.xz"), 367 Clang("3.6.2", "http://llvm.org/releases/3.6.2/", "clang+llvm-3.6.2-x86_64-apple-darwin.tar.xz"), 368 Clang("3.6.1", "http://llvm.org/releases/3.6.1/", "clang+llvm-3.6.1-x86_64-apple-darwin.tar.xz"), 369 Clang("3.6.0", "http://llvm.org/releases/3.6.0/", "clang+llvm-3.6.0-x86_64-apple-darwin.tar.xz"), 370 Clang("3.5.0", "http://llvm.org/releases/3.5.0/", "clang+llvm-3.5.0-macosx-apple-darwin.tar.xz"), 371 Clang("3.4.2", "http://llvm.org/releases/3.4.2/", "clang+llvm-3.4.2-x86_64-apple-darwin10.9.xz"), 372 // Clang("3.4.1", "http://llvm.org/releases/3.4.1/", "clang+llvm-3.4.1-x86_64-apple-darwin10.9.tar.xz"), 373 // Clang("3.4", "http://llvm.org/releases/3.4/", "clang+llvm-3.4-x86_64-apple-darwin10.9.tar.gz"), 374 Clang("3.3", "http://llvm.org/releases/3.3/", "clang+llvm-3.3-x86_64-apple-darwin12.tar.gz"), 375 Clang("3.2", "http://llvm.org/releases/3.2/", "clang+llvm-3.2-x86_64-apple-darwin11.tar.gz"), 376 Clang("3.1", "http://llvm.org/releases/3.1/", "clang+llvm-3.1-x86_64-apple-darwin11.tar.gz") 377 ]; 378 } 379 380 else 381 static assert(false, "Only 64bit versions of OS X are supported"); 382 } 383 384 else version (Windows) 385 { 386 return [ 387 Clang("3.5.0", "http://llvm.org/releases/3.5.0/", "LLVM-3.5.0-win32.exe"), 388 Clang("3.4.1", "http://llvm.org/releases/3.4.1/", "LLVM-3.4.1-win32.exe"), 389 Clang("3.4", "http://llvm.org/releases/3.4/", "LLVM-3.4-win32.exe") 390 ]; 391 } 392 393 else 394 static assert(false, "Unsupported platform"); 395 } 396 } 397 398 struct System 399 { 400 static: 401 402 version (D_LP64) 403 bool isTravis () 404 { 405 return environment.get("TRAVIS", "false") == "true"; 406 } 407 408 else 409 bool isTravis () 410 { 411 return false; 412 } 413 414 version (linux): 415 416 import core.sys.posix.sys.utsname; 417 418 private 419 { 420 utsname data_; 421 string update_; 422 string nodename_; 423 } 424 425 bool isFedora () 426 { 427 return nodename.contains("fedora"); 428 } 429 430 bool isUbuntu () 431 { 432 return nodename.contains("ubuntu"); 433 } 434 435 bool isDebian () 436 { 437 return nodename.contains("debian"); 438 } 439 440 private utsname data() 441 { 442 import std.exception; 443 444 if (data_ != data_.init) 445 return data_; 446 447 errnoEnforce(!uname(&data_)); 448 return data_; 449 } 450 451 string update () 452 { 453 if (update_.any) 454 return update_; 455 456 return update_ = data.update.ptr.toString.toLower; 457 } 458 459 string nodename () 460 { 461 if (nodename_.any) 462 return nodename_; 463 464 return nodename_ = data.nodename.ptr.toString.toLower; 465 } 466 } 467 468 struct Http 469 { 470 import tango.io.device.File; 471 import tango.io.model.IConduit; 472 import tango.net.device.Socket; 473 import tango.net.http.HttpGet; 474 import tango.net.http.HttpConst; 475 476 static: 477 478 void download (string url, string destination, float timeout = 30f, ProgressHandler progress = new CliProgressHandler) 479 { 480 auto data = download(url, timeout, progress); 481 writeFile(data, destination); 482 } 483 484 void[] download (string url, float timeout = 30f, ProgressHandler progress = new CliProgressHandler) 485 { 486 scope page = new HttpGet(url); 487 page.setTimeout(timeout); 488 auto buffer = page.open; 489 490 checkPageStatus(page, url); 491 492 auto contentLength = page.getResponseHeaders.getInt(HttpHeader.ContentLength); 493 494 enum width = 40; 495 int bytesLeft = contentLength; 496 int chunkSize = bytesLeft / width; 497 498 progress.start(contentLength, chunkSize, width); 499 500 while (bytesLeft > 0) 501 { 502 buffer.load(chunkSize > bytesLeft ? bytesLeft : chunkSize); 503 bytesLeft -= chunkSize; 504 progress(bytesLeft); 505 } 506 507 progress.end(); 508 509 return buffer.slice; 510 } 511 512 bool exists (string url) 513 { 514 scope resource = new HttpGet(url); 515 resource.open; 516 517 return resource.isResponseOK; 518 } 519 520 private: 521 522 void checkPageStatus (HttpGet page, string url) 523 { 524 import tango.core.Exception; 525 526 if (page.getStatus == 404) 527 throw new IOException(format(`The resource with URL "{}" could not be found.`, url)); 528 529 else if (!page.isResponseOK) 530 throw new IOException(format(`An unexpected error occurred. The resource "{}" responded with the message "{}" and the status code {}.`, url, page.getResponse.getReason, page.getResponse.getStatus)); 531 } 532 533 void writeFile (void[] data, string filename) 534 { 535 scope file = new File(filename, File.WriteCreate); 536 file.write(data); 537 } 538 } 539 540 abstract class ProgressHandler 541 { 542 void start (int length, int chunkSize, int width); 543 void opCall (int bytesLeft); 544 void end (); 545 } 546 547 class CliProgressHandler : ProgressHandler 548 { 549 private 550 { 551 int num; 552 int width; 553 int chunkSize; 554 int contentLength; 555 556 version (Posix) 557 enum 558 { 559 clearLine = "\033[1K", // clear backwards 560 saveCursor = "\0337", 561 restoreCursor = "\0338" 562 } 563 564 else 565 enum 566 { 567 clearLine = "\r", 568 saveCursor = "", 569 restoreCursor = "" 570 } 571 } 572 573 override void start (int contentLength, int chunkSize, int width) 574 { 575 this.chunkSize = chunkSize; 576 this.contentLength = contentLength; 577 this.width = width; 578 this.num = width; 579 580 print(saveCursor); 581 } 582 583 override void opCall (int bytesLeft) 584 { 585 int i = 0; 586 587 print(clearLine ~ restoreCursor ~ saveCursor); 588 print("["); 589 590 for ( ; i < (width - num); i++) 591 print("="); 592 593 print(">"); 594 595 for ( ; i < width; i++) 596 print(" "); 597 598 print("]"); 599 print(format(" {}/{} KB", (contentLength - bytesLeft) / 1024, contentLength / 1024).assumeUnique); 600 601 num--; 602 } 603 604 override void end () 605 { 606 println(restoreCursor); 607 println(); 608 } 609 }