Home | History | Annotate | Download | only in BlocksRuntime
      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 (a] 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 }
    806