Blaine Garst | 86d0ba4 | 2010-08-04 23:34:21 +0000 | [diff] [blame] | 1 | // |
| 2 | // The LLVM Compiler Infrastructure |
| 3 | // |
| 4 | // This file is distributed under the University of Illinois Open Source |
| 5 | // License. See LICENSE.TXT for details. |
| 6 | |
| 7 | // |
| 8 | // testfilerunner.m |
| 9 | // testObjects |
| 10 | // |
| 11 | // Created by Blaine Garst on 9/24/08. |
| 12 | // |
| 13 | |
| 14 | #import "testfilerunner.h" |
| 15 | #import <Foundation/Foundation.h> |
| 16 | #include <stdio.h> |
| 17 | #include <unistd.h> |
| 18 | #include <fcntl.h> |
| 19 | #include <string.h> |
| 20 | #include <stdlib.h> |
| 21 | #include <stdbool.h> |
| 22 | |
| 23 | bool Everything = false; // do it also with 3 levels of optimization |
| 24 | bool DoClang = false; |
| 25 | |
| 26 | static bool isDirectory(char *path); |
| 27 | static bool isExecutable(char *path); |
| 28 | static bool isYounger(char *source, char *binary); |
| 29 | static bool readErrorFile(char *buffer, const char *from); |
| 30 | |
| 31 | __strong char *gcstrcpy2(__strong const char *arg, char *endp) { |
| 32 | unsigned size = endp - arg + 1; |
| 33 | __strong char *result = NSAllocateCollectable(size, 0); |
| 34 | strncpy(result, arg, size); |
| 35 | result[size-1] = 0; |
| 36 | return result; |
| 37 | } |
| 38 | __strong char *gcstrcpy1(__strong char *arg) { |
| 39 | unsigned size = strlen(arg) + 1; |
| 40 | __strong char *result = NSAllocateCollectable(size, 0); |
| 41 | strncpy(result, arg, size); |
| 42 | result[size-1] = 0; |
| 43 | return result; |
| 44 | } |
| 45 | |
| 46 | @implementation TestFileExe |
| 47 | |
| 48 | @synthesize options, compileLine, shouldFail, binaryName, sourceName; |
| 49 | @synthesize generator; |
| 50 | @synthesize libraryPath, frameworkPath; |
| 51 | |
| 52 | - (NSString *)description { |
| 53 | NSMutableString *result = [NSMutableString new]; |
| 54 | if (shouldFail) [result appendString:@"fail"]; |
| 55 | for (id x in compileLine) { |
| 56 | [result appendString:[NSString stringWithFormat:@" %s", (char *)x]]; |
| 57 | } |
| 58 | return result; |
| 59 | } |
| 60 | |
| 61 | - (__strong char *)radar { |
| 62 | return generator.radar; |
| 63 | } |
| 64 | |
| 65 | - (bool) compileUnlessExists:(bool)skip { |
| 66 | if (shouldFail) { |
| 67 | printf("don't use this to compile anymore!\n"); |
| 68 | return false; |
| 69 | } |
| 70 | if (skip && isExecutable(binaryName) && !isYounger(sourceName, binaryName)) return true; |
| 71 | int argc = [compileLine count]; |
| 72 | char *argv[argc+1]; |
| 73 | for (int i = 0; i < argc; ++i) |
| 74 | argv[i] = (char *)[compileLine pointerAtIndex:i]; |
| 75 | argv[argc] = NULL; |
| 76 | pid_t child = fork(); |
| 77 | if (child == 0) { |
| 78 | execv(argv[0], argv); |
| 79 | exit(10); // shouldn't happen |
| 80 | } |
| 81 | if (child < 0) { |
| 82 | printf("fork failed\n"); |
| 83 | return false; |
| 84 | } |
| 85 | int status = 0; |
| 86 | pid_t deadchild = wait(&status); |
| 87 | if (deadchild != child) { |
| 88 | printf("wait got %d instead of %d\n", deadchild, child); |
| 89 | exit(1); |
| 90 | } |
| 91 | if (WEXITSTATUS(status) == 0) { |
| 92 | return true; |
| 93 | } |
| 94 | printf("run failed\n"); |
| 95 | return false; |
| 96 | } |
| 97 | |
| 98 | bool lookforIn(char *lookfor, const char *format, pid_t child) { |
| 99 | char buffer[512]; |
| 100 | char got[512]; |
| 101 | sprintf(buffer, format, child); |
| 102 | bool gotOutput = readErrorFile(got, buffer); |
| 103 | if (!gotOutput) { |
| 104 | printf("**** didn't get an output file %s to analyze!!??\n", buffer); |
| 105 | return false; |
| 106 | } |
| 107 | char *where = strstr(got, lookfor); |
| 108 | if (!where) { |
| 109 | printf("didn't find '%s' in output file %s\n", lookfor, buffer); |
| 110 | return false; |
| 111 | } |
| 112 | unlink(buffer); |
| 113 | return true; |
| 114 | } |
| 115 | |
| 116 | - (bool) compileWithExpectedFailure { |
| 117 | if (!shouldFail) { |
| 118 | printf("Why am I being called?\n"); |
| 119 | return false; |
| 120 | } |
| 121 | int argc = [compileLine count]; |
| 122 | char *argv[argc+1]; |
| 123 | for (int i = 0; i < argc; ++i) |
| 124 | argv[i] = (char *)[compileLine pointerAtIndex:i]; |
| 125 | argv[argc] = NULL; |
| 126 | pid_t child = fork(); |
| 127 | char buffer[512]; |
| 128 | if (child == 0) { |
| 129 | // in child |
| 130 | sprintf(buffer, "/tmp/errorfile_%d", getpid()); |
| 131 | close(1); |
| 132 | int fd = creat(buffer, 0777); |
| 133 | if (fd != 1) { |
| 134 | fprintf(stderr, "didn't open custom error file %s as 1, got %d\n", buffer, fd); |
| 135 | exit(1); |
| 136 | } |
| 137 | close(2); |
| 138 | dup(1); |
| 139 | int result = execv(argv[0], argv); |
| 140 | exit(10); |
| 141 | } |
| 142 | if (child < 0) { |
| 143 | printf("fork failed\n"); |
| 144 | return false; |
| 145 | } |
| 146 | int status = 0; |
| 147 | pid_t deadchild = wait(&status); |
| 148 | if (deadchild != child) { |
| 149 | printf("wait got %d instead of %d\n", deadchild, child); |
| 150 | exit(11); |
| 151 | } |
| 152 | if (WIFEXITED(status)) { |
| 153 | if (WEXITSTATUS(status) == 0) { |
| 154 | return false; |
| 155 | } |
| 156 | } |
| 157 | else { |
| 158 | printf("***** compiler borked/ICEd/died unexpectedly (status %x)\n", status); |
| 159 | return false; |
| 160 | } |
| 161 | char *error = generator.errorString; |
| 162 | |
| 163 | if (!error) return true; |
| 164 | #if 0 |
| 165 | char got[512]; |
| 166 | sprintf(buffer, "/tmp/errorfile_%d", child); |
| 167 | bool gotOutput = readErrorFile(got, buffer); |
| 168 | if (!gotOutput) { |
| 169 | printf("**** didn't get an error file %s to analyze!!??\n", buffer); |
| 170 | return false; |
| 171 | } |
| 172 | char *where = strstr(got, error); |
| 173 | if (!where) { |
| 174 | printf("didn't find '%s' in error file %s\n", error, buffer); |
| 175 | return false; |
| 176 | } |
| 177 | unlink(buffer); |
| 178 | #else |
| 179 | if (!lookforIn(error, "/tmp/errorfile_%d", child)) return false; |
| 180 | #endif |
| 181 | return true; |
| 182 | } |
| 183 | |
| 184 | - (bool) run { |
| 185 | if (shouldFail) return true; |
| 186 | if (sizeof(long) == 4 && options & Do64) { |
| 187 | return true; // skip 64-bit tests |
| 188 | } |
| 189 | int argc = 1; |
| 190 | char *argv[argc+1]; |
| 191 | argv[0] = binaryName; |
| 192 | argv[argc] = NULL; |
| 193 | pid_t child = fork(); |
| 194 | if (child == 0) { |
| 195 | // set up environment |
| 196 | char lpath[1024]; |
| 197 | char fpath[1024]; |
| 198 | char *myenv[3]; |
| 199 | int counter = 0; |
| 200 | if (libraryPath) { |
| 201 | sprintf(lpath, "DYLD_LIBRARY_PATH=%s", libraryPath); |
| 202 | myenv[counter++] = lpath; |
| 203 | } |
| 204 | if (frameworkPath) { |
| 205 | sprintf(fpath, "DYLD_FRAMEWORK_PATH=%s", frameworkPath); |
| 206 | myenv[counter++] = fpath; |
| 207 | } |
| 208 | myenv[counter] = NULL; |
| 209 | if (generator.warningString) { |
| 210 | // set up stdout/stderr |
| 211 | char outfile[1024]; |
| 212 | sprintf(outfile, "/tmp/stdout_%d", getpid()); |
| 213 | close(2); |
| 214 | close(1); |
| 215 | creat(outfile, 0700); |
| 216 | dup(1); |
| 217 | } |
| 218 | execve(argv[0], argv, myenv); |
| 219 | exit(10); // shouldn't happen |
| 220 | } |
| 221 | if (child < 0) { |
| 222 | printf("fork failed\n"); |
| 223 | return false; |
| 224 | } |
| 225 | int status = 0; |
| 226 | pid_t deadchild = wait(&status); |
| 227 | if (deadchild != child) { |
| 228 | printf("wait got %d instead of %d\n", deadchild, child); |
| 229 | exit(1); |
| 230 | } |
| 231 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { |
| 232 | if (generator.warningString) { |
| 233 | if (!lookforIn(generator.warningString, "/tmp/stdout_%d", child)) return false; |
| 234 | } |
| 235 | return true; |
| 236 | } |
| 237 | printf("**** run failed for %s\n", binaryName); |
| 238 | return false; |
| 239 | } |
| 240 | |
| 241 | @end |
| 242 | |
| 243 | @implementation TestFileExeGenerator |
| 244 | @synthesize filename, compilerPath, errorString; |
| 245 | @synthesize hasObjC, hasRR, hasGC, hasCPlusPlus, wantsC99, supposedToNotCompile, open, wants32, wants64; |
| 246 | @synthesize radar; |
| 247 | @synthesize warningString; |
| 248 | |
| 249 | - (void)setFilename:(__strong char *)name { |
| 250 | filename = gcstrcpy1(name); |
| 251 | } |
| 252 | - (void)setCompilerPath:(__strong char *)name { |
| 253 | compilerPath = gcstrcpy1(name); |
| 254 | } |
| 255 | |
| 256 | - (void)forMostThings:(NSMutableArray *)lines options:(int)options { |
| 257 | TestFileExe *item = nil; |
| 258 | item = [self lineForOptions:options]; |
| 259 | if (item) [lines addObject:item]; |
| 260 | item = [self lineForOptions:options|Do64]; |
| 261 | if (item) [lines addObject:item]; |
| 262 | item = [self lineForOptions:options|DoCPP]; |
| 263 | if (item) [lines addObject:item]; |
| 264 | item = [self lineForOptions:options|Do64|DoCPP]; |
| 265 | if (item) [lines addObject:item]; |
| 266 | } |
| 267 | |
| 268 | /* |
| 269 | DoDashG = (1 << 8), |
| 270 | DoDashO = (1 << 9), |
| 271 | DoDashOs = (1 << 10), |
| 272 | DoDashO2 = (1 << 11), |
| 273 | */ |
| 274 | |
| 275 | - (void)forAllThings:(NSMutableArray *)lines options:(int)options { |
| 276 | [self forMostThings:lines options:options]; |
| 277 | if (!Everything) { |
| 278 | return; |
| 279 | } |
| 280 | // now do it with three explicit optimization flags |
| 281 | [self forMostThings:lines options:options | DoDashO]; |
| 282 | [self forMostThings:lines options:options | DoDashOs]; |
| 283 | [self forMostThings:lines options:options | DoDashO2]; |
| 284 | } |
| 285 | |
| 286 | - (NSArray *)allLines { |
| 287 | NSMutableArray *result = [NSMutableArray new]; |
| 288 | TestFileExe *item = nil; |
| 289 | |
| 290 | int options = 0; |
| 291 | [self forAllThings:result options:0]; |
| 292 | [self forAllThings:result options:DoOBJC | DoRR]; |
| 293 | [self forAllThings:result options:DoOBJC | DoGC]; |
| 294 | [self forAllThings:result options:DoOBJC | DoGCRR]; |
| 295 | //[self forAllThings:result options:DoOBJC | DoRRGC]; |
| 296 | |
| 297 | return result; |
| 298 | } |
| 299 | |
| 300 | - (void)addLibrary:(const char *)dashLSomething { |
| 301 | if (!extraLibraries) { |
| 302 | extraLibraries = [NSPointerArray pointerArrayWithOptions: |
| 303 | NSPointerFunctionsStrongMemory | |
| 304 | NSPointerFunctionsCStringPersonality]; |
| 305 | } |
| 306 | [extraLibraries addPointer:(void *)dashLSomething]; |
| 307 | } |
| 308 | |
| 309 | - (TestFileExe *)lineForOptions:(int)options { // nil if no can do |
| 310 | if (hasObjC && !(options & DoOBJC)) return nil; |
| 311 | if (hasCPlusPlus && !(options & DoCPP)) return nil; |
| 312 | if (hasObjC) { |
| 313 | if (!hasGC && (options & (DoGC|DoGCRR))) return nil; // not smart enough |
| 314 | if (!hasRR && (options & (DoRR|DoRRGC))) return nil; |
| 315 | } |
| 316 | NSPointerArray *pa = [NSPointerArray pointerArrayWithOptions: |
| 317 | NSPointerFunctionsStrongMemory | |
| 318 | NSPointerFunctionsCStringPersonality]; |
| 319 | // construct path |
| 320 | char path[512]; |
| 321 | path[0] = 0; |
| 322 | if (!compilerPath) compilerPath = "/usr/bin"; |
| 323 | if (compilerPath) { |
| 324 | strcat(path, compilerPath); |
| 325 | strcat(path, "/"); |
| 326 | } |
| 327 | if (options & DoCPP) { |
| 328 | strcat(path, DoClang ? "clang++" : "g++-4.2"); |
| 329 | } |
| 330 | else { |
| 331 | strcat(path, DoClang ? "clang" : "gcc-4.2"); |
| 332 | } |
| 333 | [pa addPointer:gcstrcpy1(path)]; |
| 334 | if (options & DoOBJC) { |
| 335 | if (options & DoCPP) { |
| 336 | [pa addPointer:"-ObjC++"]; |
| 337 | } |
| 338 | else { |
| 339 | [pa addPointer:"-ObjC"]; |
| 340 | } |
| 341 | } |
| 342 | [pa addPointer:"-g"]; |
| 343 | if (options & DoDashO) [pa addPointer:"-O"]; |
| 344 | else if (options & DoDashO2) [pa addPointer:"-O2"]; |
| 345 | else if (options & DoDashOs) [pa addPointer:"-Os"]; |
| 346 | if (wantsC99 && (! (options & DoCPP))) { |
| 347 | [pa addPointer:"-std=c99"]; |
| 348 | [pa addPointer:"-fblocks"]; |
| 349 | } |
| 350 | [pa addPointer:"-arch"]; |
| 351 | [pa addPointer: (options & Do64) ? "x86_64" : "i386"]; |
| 352 | |
| 353 | if (options & DoOBJC) { |
| 354 | switch (options & (DoRR|DoGC|DoGCRR|DoRRGC)) { |
| 355 | case DoRR: |
| 356 | break; |
| 357 | case DoGC: |
| 358 | [pa addPointer:"-fobjc-gc-only"]; |
| 359 | break; |
| 360 | case DoGCRR: |
| 361 | [pa addPointer:"-fobjc-gc"]; |
| 362 | break; |
| 363 | case DoRRGC: |
| 364 | printf("DoRRGC unsupported right now\n"); |
| 365 | [pa addPointer:"-c"]; |
| 366 | return nil; |
| 367 | } |
| 368 | [pa addPointer:"-framework"]; |
| 369 | [pa addPointer:"Foundation"]; |
| 370 | } |
| 371 | [pa addPointer:gcstrcpy1(filename)]; |
| 372 | [pa addPointer:"-o"]; |
| 373 | |
| 374 | path[0] = 0; |
| 375 | strcat(path, filename); |
| 376 | strcat(path, "."); |
| 377 | strcat(path, (options & Do64) ? "64" : "32"); |
| 378 | if (options & DoOBJC) { |
| 379 | switch (options & (DoRR|DoGC|DoGCRR|DoRRGC)) { |
| 380 | case DoRR: strcat(path, "-rr"); break; |
| 381 | case DoGC: strcat(path, "-gconly"); break; |
| 382 | case DoGCRR: strcat(path, "-gcrr"); break; |
| 383 | case DoRRGC: strcat(path, "-rrgc"); break; |
| 384 | } |
| 385 | } |
| 386 | if (options & DoCPP) strcat(path, "++"); |
| 387 | if (options & DoDashO) strcat(path, "-O"); |
| 388 | else if (options & DoDashO2) strcat(path, "-O2"); |
| 389 | else if (options & DoDashOs) strcat(path, "-Os"); |
| 390 | if (wantsC99) strcat(path, "-C99"); |
| 391 | strcat(path, DoClang ? "-clang" : "-gcc"); |
| 392 | strcat(path, "-bin"); |
| 393 | TestFileExe *result = [TestFileExe new]; |
| 394 | result.binaryName = gcstrcpy1(path); // could snarf copy in pa |
| 395 | [pa addPointer:result.binaryName]; |
| 396 | for (id cString in extraLibraries) { |
| 397 | [pa addPointer:cString]; |
| 398 | } |
| 399 | |
| 400 | result.sourceName = gcstrcpy1(filename); // could snarf copy in pa |
| 401 | result.compileLine = pa; |
| 402 | result.options = options; |
| 403 | result.shouldFail = supposedToNotCompile; |
| 404 | result.generator = self; |
| 405 | return result; |
| 406 | } |
| 407 | |
| 408 | + (NSArray *)generatorsFromPath:(NSString *)path { |
| 409 | FILE *fp = fopen([path fileSystemRepresentation], "r"); |
| 410 | if (fp == NULL) return nil; |
| 411 | NSArray *result = [self generatorsFromFILE:fp]; |
| 412 | fclose(fp); |
| 413 | return result; |
| 414 | } |
| 415 | |
| 416 | #define LOOKFOR "CON" "FIG" |
| 417 | |
| 418 | char *__strong parseRadar(char *line) { |
| 419 | line = strstr(line, "rdar:"); // returns beginning |
| 420 | char *endp = line + strlen("rdar:"); |
| 421 | while (*endp && *endp != ' ' && *endp != '\n') |
| 422 | ++endp; |
| 423 | return gcstrcpy2(line, endp); |
| 424 | } |
| 425 | |
| 426 | - (void)parseLibraries:(const char *)line { |
| 427 | start: |
| 428 | line = strstr(line, "-l"); |
| 429 | char *endp = (char *)line + 2; |
| 430 | while (*endp && *endp != ' ' && *endp != '\n') |
| 431 | ++endp; |
| 432 | [self addLibrary:gcstrcpy2(line, endp)]; |
| 433 | if (strstr(endp, "-l")) { |
| 434 | line = endp; |
| 435 | goto start; |
| 436 | } |
| 437 | } |
| 438 | |
| 439 | + (TestFileExeGenerator *)generatorFromLine:(char *)line filename:(char *)filename { |
| 440 | TestFileExeGenerator *item = [TestFileExeGenerator new]; |
| 441 | item.filename = gcstrcpy1(filename); |
| 442 | if (strstr(line, "GC")) item.hasGC = true; |
| 443 | if (strstr(line, "RR")) item.hasRR = true; |
| 444 | if (strstr(line, "C++")) item.hasCPlusPlus = true; |
| 445 | if (strstr(line, "-C99")) { |
| 446 | item.wantsC99 = true; |
| 447 | } |
| 448 | if (strstr(line, "64")) item.wants64 = true; |
| 449 | if (strstr(line, "32")) item.wants32 = true; |
| 450 | if (strstr(line, "-l")) [item parseLibraries:line]; |
| 451 | if (strstr(line, "open")) item.open = true; |
| 452 | if (strstr(line, "FAIL")) item.supposedToNotCompile = true; // old |
| 453 | // compile time error |
| 454 | if (strstr(line, "error:")) { |
| 455 | item.supposedToNotCompile = true; |
| 456 | // zap newline |
| 457 | char *error = strstr(line, "error:") + strlen("error:"); |
| 458 | // make sure we have something before the newline |
| 459 | char *newline = strstr(error, "\n"); |
| 460 | if (newline && ((newline-error) > 1)) { |
| 461 | *newline = 0; |
| 462 | item.errorString = gcstrcpy1(strstr(line, "error:") + strlen("error: ")); |
| 463 | } |
| 464 | } |
| 465 | // run time warning |
| 466 | if (strstr(line, "runtime:")) { |
| 467 | // zap newline |
| 468 | char *error = strstr(line, "runtime:") + strlen("runtime:"); |
| 469 | // make sure we have something before the newline |
| 470 | char *newline = strstr(error, "\n"); |
| 471 | if (newline && ((newline-error) > 1)) { |
| 472 | *newline = 0; |
| 473 | item.warningString = gcstrcpy1(strstr(line, "runtime:") + strlen("runtime:")); |
| 474 | } |
| 475 | } |
| 476 | if (strstr(line, "rdar:")) item.radar = parseRadar(line); |
| 477 | if (item.hasGC || item.hasRR) item.hasObjC = true; |
| 478 | if (!item.wants32 && !item.wants64) { // give them both if they ask for neither |
| 479 | item.wants32 = item.wants64 = true; |
| 480 | } |
| 481 | return item; |
| 482 | } |
| 483 | |
| 484 | + (NSArray *)generatorsFromFILE:(FILE *)fp { |
| 485 | NSMutableArray *result = [NSMutableArray new]; |
| 486 | // pretend this is a grep LOOKFOR *.[cmCM][cmCM] input |
| 487 | // look for |
| 488 | // filename: ... LOOKFOR [GC] [RR] [C++] [FAIL ...] |
| 489 | char buf[512]; |
| 490 | while (fgets(buf, 512, fp)) { |
| 491 | char *config = strstr(buf, LOOKFOR); |
| 492 | if (!config) continue; |
| 493 | char *filename = buf; |
| 494 | char *end = strchr(buf, ':'); |
| 495 | *end = 0; |
| 496 | [result addObject:[self generatorFromLine:config filename:filename]]; |
| 497 | } |
| 498 | return result; |
| 499 | } |
| 500 | |
| 501 | + (TestFileExeGenerator *)generatorFromFilename:(char *)filename { |
| 502 | FILE *fp = fopen(filename, "r"); |
| 503 | if (!fp) { |
| 504 | printf("didn't open %s!!\n", filename); |
| 505 | return nil; |
| 506 | } |
| 507 | char buf[512]; |
| 508 | while (fgets(buf, 512, fp)) { |
| 509 | char *config = strstr(buf, LOOKFOR); |
| 510 | if (!config) continue; |
| 511 | fclose(fp); |
| 512 | return [self generatorFromLine:config filename:filename]; |
| 513 | } |
| 514 | fclose(fp); |
| 515 | // guess from filename |
| 516 | char *ext = strrchr(filename, '.'); |
| 517 | if (!ext) return nil; |
| 518 | TestFileExeGenerator *result = [TestFileExeGenerator new]; |
| 519 | result.filename = gcstrcpy1(filename); |
| 520 | if (!strncmp(ext, ".m", 2)) { |
| 521 | result.hasObjC = true; |
| 522 | result.hasRR = true; |
| 523 | result.hasGC = true; |
| 524 | } |
| 525 | else if (!strcmp(ext, ".c")) { |
| 526 | ; |
| 527 | } |
| 528 | else if (!strcmp(ext, ".M") || !strcmp(ext, ".mm")) { |
| 529 | result.hasObjC = true; |
| 530 | result.hasRR = true; |
| 531 | result.hasGC = true; |
| 532 | result.hasCPlusPlus = true; |
| 533 | } |
| 534 | else if (!strcmp(ext, ".cc") |
| 535 | || !strcmp(ext, ".cp") |
| 536 | || !strcmp(ext, ".cxx") |
| 537 | || !strcmp(ext, ".cpp") |
| 538 | || !strcmp(ext, ".CPP") |
| 539 | || !strcmp(ext, ".c++") |
| 540 | || !strcmp(ext, ".C")) { |
| 541 | result.hasCPlusPlus = true; |
| 542 | } |
| 543 | else { |
| 544 | printf("unknown extension, file %s ignored\n", filename); |
| 545 | result = nil; |
| 546 | } |
| 547 | return result; |
| 548 | |
| 549 | } |
| 550 | |
| 551 | - (NSString *)description { |
| 552 | return [NSString stringWithFormat:@"%s: %s%s%s%s%s%s", |
| 553 | filename, |
| 554 | LOOKFOR, |
| 555 | hasGC ? " GC" : "", |
| 556 | hasRR ? " RR" : "", |
| 557 | hasCPlusPlus ? " C++" : "", |
| 558 | wantsC99 ? "C99" : "", |
| 559 | supposedToNotCompile ? " FAIL" : ""]; |
| 560 | } |
| 561 | |
| 562 | @end |
| 563 | |
| 564 | void printDetails(NSArray *failures, const char *whatAreThey) { |
| 565 | if ([failures count]) { |
| 566 | NSMutableString *output = [NSMutableString new]; |
| 567 | printf("%s:\n", whatAreThey); |
| 568 | for (TestFileExe *line in failures) { |
| 569 | printf("%s", line.binaryName); |
| 570 | char *radar = line.generator.radar; |
| 571 | if (radar) |
| 572 | printf(" (due to %s?),", radar); |
| 573 | printf(" recompile via:\n%s\n\n", line.description.UTF8String); |
| 574 | } |
| 575 | printf("\n"); |
| 576 | } |
| 577 | } |
| 578 | |
| 579 | void help(const char *whoami) { |
| 580 | printf("Usage: %s [-fast] [-e] [-dyld librarypath] [gcc4.2dir] [-- | source1 ...]\n", whoami); |
| 581 | printf(" -fast don't recompile if binary younger than source\n"); |
| 582 | printf(" -open only run tests that are thought to still be unresolved\n"); |
| 583 | printf(" -clang use the clang and clang++ compilers\n"); |
| 584 | printf(" -e compile all variations also with -Os, -O2, -O3\n"); |
| 585 | printf(" -dyld p override DYLD_LIBRARY_PATH and DYLD_FRAMEWORK_PATH to p when running tests\n"); |
| 586 | printf(" <compilerpath> directory containing gcc-4.2 (or clang) that you wish to use to compile the tests\n"); |
| 587 | printf(" -- assume stdin is a grep CON" "FIG across the test sources\n"); |
| 588 | printf(" otherwise treat each remaining argument as a single test file source\n"); |
| 589 | printf("%s will compile and run individual test files under a variety of compilers, c, obj-c, c++, and objc++\n", whoami); |
| 590 | printf(" .c files are compiled with all four compilers\n"); |
| 591 | printf(" .m files are compiled with objc and objc++ compilers\n"); |
| 592 | printf(" .C files are compiled with c++ and objc++ compilers\n"); |
| 593 | printf(" .M files are compiled only with the objc++ compiler\n"); |
| 594 | printf("(actually all forms of extensions recognized by the compilers are honored, .cc, .c++ etc.)\n"); |
| 595 | printf("\nTest files should run to completion with no output and exit (return) 0 on success.\n"); |
| 596 | printf("Further they should be able to be compiled and run with GC on or off and by the C++ compilers\n"); |
| 597 | printf("A line containing the string CON" "FIG within the source enables restrictions to the above assumptions\n"); |
| 598 | printf("and other options.\n"); |
| 599 | printf("Following CON" "FIG the string\n"); |
| 600 | printf(" C++ restricts the test to only be run by c++ and objc++ compilers\n"); |
| 601 | printf(" GC restricts the test to only be compiled and run with GC on\n"); |
| 602 | printf(" RR (retain/release) restricts the test to only be compiled and run with GC off\n"); |
| 603 | printf("Additionally,\n"); |
| 604 | printf(" -C99 restricts the C versions of the test to -fstd=c99 -fblocks\n"); |
| 605 | printf(" -O adds the -O optimization level\n"); |
| 606 | printf(" -O2 adds the -O2 optimization level\n"); |
| 607 | printf(" -Os adds the -Os optimization level\n"); |
| 608 | printf("Files that are known to exhibit unresolved problems can provide the term \"open\" and this can"); |
| 609 | printf("in turn allow highlighting of fixes that have regressed as well as identify that fixes are now available.\n"); |
| 610 | printf("Files that exhibit known bugs may provide\n"); |
| 611 | printf(" rdar://whatever such that if they fail the rdar will get cited\n"); |
| 612 | printf("Files that are expected to fail to compile should provide, as their last token sequence,\n"); |
| 613 | printf(" error:\n"); |
| 614 | printf(" or error: substring to match.\n"); |
| 615 | printf("Files that are expected to produce a runtime error message should provide, as their last token sequence,\n"); |
| 616 | printf(" warning: string to match\n"); |
| 617 | printf("\n%s will compile and run all configurations of the test files and report a summary at the end. Good luck.\n", whoami); |
| 618 | printf(" Blaine Garst blaine@apple.com\n"); |
| 619 | } |
| 620 | |
| 621 | int main(int argc, char *argv[]) { |
| 622 | printf("running on %s-bit architecture\n", sizeof(long) == 4 ? "32" : "64"); |
| 623 | char *compilerDir = "/usr/bin"; |
| 624 | NSMutableArray *generators = [NSMutableArray new]; |
| 625 | bool doFast = false; |
| 626 | bool doStdin = false; |
| 627 | bool onlyOpen = false; |
| 628 | char *libraryPath = getenv("DYLD_LIBRARY_PATH"); |
| 629 | char *frameworkPath = getenv("DYLD_FRAMEWORK_PATH"); |
| 630 | // process options |
| 631 | while (argc > 1) { |
| 632 | if (!strcmp(argv[1], "-fast")) { |
| 633 | doFast = true; |
| 634 | --argc; |
| 635 | ++argv; |
| 636 | } |
| 637 | else if (!strcmp(argv[1], "-dyld")) { |
| 638 | doFast = true; |
| 639 | --argc; |
| 640 | ++argv; |
| 641 | frameworkPath = argv[1]; |
| 642 | libraryPath = argv[1]; |
| 643 | --argc; |
| 644 | ++argv; |
| 645 | } |
| 646 | else if (!strcmp(argv[1], "-open")) { |
| 647 | onlyOpen = true; |
| 648 | --argc; |
| 649 | ++argv; |
| 650 | } |
| 651 | else if (!strcmp(argv[1], "-clang")) { |
| 652 | DoClang = true; |
| 653 | --argc; |
| 654 | ++argv; |
| 655 | } |
| 656 | else if (!strcmp(argv[1], "-e")) { |
| 657 | Everything = true; |
| 658 | --argc; |
| 659 | ++argv; |
| 660 | } |
| 661 | else if (!strcmp(argv[1], "--")) { |
| 662 | doStdin = true; |
| 663 | --argc; |
| 664 | ++argv; |
| 665 | } |
| 666 | else if (!strcmp(argv[1], "-")) { |
| 667 | help(argv[0]); |
| 668 | return 1; |
| 669 | } |
| 670 | else if (argc > 1 && isDirectory(argv[1])) { |
| 671 | compilerDir = argv[1]; |
| 672 | ++argv; |
| 673 | --argc; |
| 674 | } |
| 675 | else |
| 676 | break; |
| 677 | } |
| 678 | // process remaining arguments, or stdin |
| 679 | if (argc == 1) { |
| 680 | if (doStdin) |
| 681 | generators = (NSMutableArray *)[TestFileExeGenerator generatorsFromFILE:stdin]; |
| 682 | else { |
| 683 | help(argv[0]); |
| 684 | return 1; |
| 685 | } |
| 686 | } |
| 687 | else while (argc > 1) { |
| 688 | TestFileExeGenerator *generator = [TestFileExeGenerator generatorFromFilename:argv[1]]; |
| 689 | if (generator) [generators addObject:generator]; |
| 690 | ++argv; |
| 691 | --argc; |
| 692 | } |
| 693 | // see if we can generate all possibilities |
| 694 | NSMutableArray *failureToCompile = [NSMutableArray new]; |
| 695 | NSMutableArray *failureToFailToCompile = [NSMutableArray new]; |
| 696 | NSMutableArray *failureToRun = [NSMutableArray new]; |
| 697 | NSMutableArray *successes = [NSMutableArray new]; |
| 698 | for (TestFileExeGenerator *generator in generators) { |
| 699 | //NSLog(@"got %@", generator); |
| 700 | if (onlyOpen && !generator.open) { |
| 701 | //printf("skipping resolved test %s\n", generator.filename); |
| 702 | continue; // skip closed if onlyOpen |
| 703 | } |
| 704 | if (!onlyOpen && generator.open) { |
| 705 | //printf("skipping open test %s\n", generator.filename); |
| 706 | continue; // skip open if not asked for onlyOpen |
| 707 | } |
| 708 | generator.compilerPath = compilerDir; |
| 709 | NSArray *tests = [generator allLines]; |
| 710 | for (TestFileExe *line in tests) { |
| 711 | line.frameworkPath = frameworkPath; // tell generators about it instead XXX |
| 712 | line.libraryPath = libraryPath; // tell generators about it instead XXX |
| 713 | if ([line shouldFail]) { |
| 714 | if (doFast) continue; // don't recompile & don't count as success |
| 715 | if ([line compileWithExpectedFailure]) { |
| 716 | [successes addObject:line]; |
| 717 | } |
| 718 | else |
| 719 | [failureToFailToCompile addObject:line]; |
| 720 | } |
| 721 | else if ([line compileUnlessExists:doFast]) { |
| 722 | if ([line run]) { |
| 723 | printf("%s ran successfully\n", line.binaryName); |
| 724 | [successes addObject:line]; |
| 725 | } |
| 726 | else { |
| 727 | [failureToRun addObject:line]; |
| 728 | } |
| 729 | } |
| 730 | else { |
| 731 | [failureToCompile addObject:line]; |
| 732 | } |
| 733 | } |
| 734 | } |
| 735 | printf("\n--- results ---\n\n%lu successes\n%lu unexpected compile failures\n%lu failure to fail to compile errors\n%lu run failures\n", |
| 736 | [successes count], [failureToCompile count], [failureToFailToCompile count], [failureToRun count]); |
| 737 | printDetails(failureToCompile, "unexpected compile failures"); |
| 738 | printDetails(failureToFailToCompile, "should have failed to compile but didn't failures"); |
| 739 | printDetails(failureToRun, "run failures"); |
| 740 | |
| 741 | if (onlyOpen && [successes count]) { |
| 742 | NSMutableSet *radars = [NSMutableSet new]; |
| 743 | printf("The following tests ran successfully suggesting that they are now resolved:\n"); |
| 744 | for (TestFileExe *line in successes) { |
| 745 | printf("%s\n", line.binaryName); |
| 746 | if (line.radar) [radars addObject:line.generator]; |
| 747 | } |
| 748 | if ([radars count]) { |
| 749 | printf("The following radars may be resolved:\n"); |
| 750 | for (TestFileExeGenerator *line in radars) { |
| 751 | printf("%s\n", line.radar); |
| 752 | } |
| 753 | } |
| 754 | } |
| 755 | |
| 756 | return [failureToCompile count] + [failureToRun count]; |
| 757 | } |
| 758 | |
| 759 | #include <sys/stat.h> |
| 760 | |
| 761 | static bool isDirectory(char *path) { |
| 762 | struct stat statb; |
| 763 | int retval = stat(path, &statb); |
| 764 | if (retval != 0) return false; |
| 765 | if (statb.st_mode & S_IFDIR) return true; |
| 766 | return false; |
| 767 | } |
| 768 | |
| 769 | static bool isExecutable(char *path) { |
| 770 | struct stat statb; |
| 771 | int retval = stat(path, &statb); |
| 772 | if (retval != 0) return false; |
| 773 | if (!(statb.st_mode & S_IFREG)) return false; |
| 774 | if (statb.st_mode & S_IXUSR) return true; |
| 775 | return false; |
| 776 | } |
| 777 | |
| 778 | static bool isYounger(char *source, char *binary) { |
| 779 | struct stat statb; |
| 780 | int retval = stat(binary, &statb); |
| 781 | if (retval != 0) return true; // if doesn't exit, lie |
| 782 | |
| 783 | struct stat stata; |
| 784 | retval = stat(source, &stata); |
| 785 | if (retval != 0) return true; // we're hosed |
| 786 | // the greater the timeval the younger it is |
| 787 | if (stata.st_mtimespec.tv_sec > statb.st_mtimespec.tv_sec) return true; |
| 788 | if (stata.st_mtimespec.tv_nsec > statb.st_mtimespec.tv_nsec) return true; |
| 789 | return false; |
| 790 | } |
| 791 | |
| 792 | static bool readErrorFile(char *buffer, const char *from) { |
| 793 | int fd = open(from, 0); |
| 794 | if (fd < 0) { |
| 795 | printf("didn't open %s, (might not have been created?)\n", buffer); |
| 796 | return false; |
| 797 | } |
| 798 | int count = read(fd, buffer, 512); |
| 799 | if (count < 1) { |
| 800 | printf("read error on %s\n", buffer); |
| 801 | return false; |
| 802 | } |
| 803 | buffer[count-1] = 0; // zap newline |
| 804 | return true; |
| 805 | } |