| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| |
| // |
| // testfilerunner.m |
| // testObjects |
| // |
| // Created by Blaine Garst on 9/24/08. |
| // |
| |
| #import "testfilerunner.h" |
| #import <Foundation/Foundation.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <stdbool.h> |
| |
| bool Everything = false; // do it also with 3 levels of optimization |
| bool DoClang = false; |
| |
| static bool isDirectory(char *path); |
| static bool isExecutable(char *path); |
| static bool isYounger(char *source, char *binary); |
| static bool readErrorFile(char *buffer, const char *from); |
| |
| __strong char *gcstrcpy2(__strong const char *arg, char *endp) { |
| unsigned size = endp - arg + 1; |
| __strong char *result = NSAllocateCollectable(size, 0); |
| strncpy(result, arg, size); |
| result[size-1] = 0; |
| return result; |
| } |
| __strong char *gcstrcpy1(__strong char *arg) { |
| unsigned size = strlen(arg) + 1; |
| __strong char *result = NSAllocateCollectable(size, 0); |
| strncpy(result, arg, size); |
| result[size-1] = 0; |
| return result; |
| } |
| |
| @implementation TestFileExe |
| |
| @synthesize options, compileLine, shouldFail, binaryName, sourceName; |
| @synthesize generator; |
| @synthesize libraryPath, frameworkPath; |
| |
| - (NSString *)description { |
| NSMutableString *result = [NSMutableString new]; |
| if (shouldFail) [result appendString:@"fail"]; |
| for (id x in compileLine) { |
| [result appendString:[NSString stringWithFormat:@" %s", (char *)x]]; |
| } |
| return result; |
| } |
| |
| - (__strong char *)radar { |
| return generator.radar; |
| } |
| |
| - (bool) compileUnlessExists:(bool)skip { |
| if (shouldFail) { |
| printf("don't use this to compile anymore!\n"); |
| return false; |
| } |
| if (skip && isExecutable(binaryName) && !isYounger(sourceName, binaryName)) return true; |
| int argc = [compileLine count]; |
| char *argv[argc+1]; |
| for (int i = 0; i < argc; ++i) |
| argv[i] = (char *)[compileLine pointerAtIndex:i]; |
| argv[argc] = NULL; |
| pid_t child = fork(); |
| if (child == 0) { |
| execv(argv[0], argv); |
| exit(10); // shouldn't happen |
| } |
| if (child < 0) { |
| printf("fork failed\n"); |
| return false; |
| } |
| int status = 0; |
| pid_t deadchild = wait(&status); |
| if (deadchild != child) { |
| printf("wait got %d instead of %d\n", deadchild, child); |
| exit(1); |
| } |
| if (WEXITSTATUS(status) == 0) { |
| return true; |
| } |
| printf("run failed\n"); |
| return false; |
| } |
| |
| bool lookforIn(char *lookfor, const char *format, pid_t child) { |
| char buffer[512]; |
| char got[512]; |
| sprintf(buffer, format, child); |
| bool gotOutput = readErrorFile(got, buffer); |
| if (!gotOutput) { |
| printf("**** didn't get an output file %s to analyze!!??\n", buffer); |
| return false; |
| } |
| char *where = strstr(got, lookfor); |
| if (!where) { |
| printf("didn't find '%s' in output file %s\n", lookfor, buffer); |
| return false; |
| } |
| unlink(buffer); |
| return true; |
| } |
| |
| - (bool) compileWithExpectedFailure { |
| if (!shouldFail) { |
| printf("Why am I being called?\n"); |
| return false; |
| } |
| int argc = [compileLine count]; |
| char *argv[argc+1]; |
| for (int i = 0; i < argc; ++i) |
| argv[i] = (char *)[compileLine pointerAtIndex:i]; |
| argv[argc] = NULL; |
| pid_t child = fork(); |
| char buffer[512]; |
| if (child == 0) { |
| // in child |
| sprintf(buffer, "/tmp/errorfile_%d", getpid()); |
| close(1); |
| int fd = creat(buffer, 0777); |
| if (fd != 1) { |
| fprintf(stderr, "didn't open custom error file %s as 1, got %d\n", buffer, fd); |
| exit(1); |
| } |
| close(2); |
| dup(1); |
| int result = execv(argv[0], argv); |
| exit(10); |
| } |
| if (child < 0) { |
| printf("fork failed\n"); |
| return false; |
| } |
| int status = 0; |
| pid_t deadchild = wait(&status); |
| if (deadchild != child) { |
| printf("wait got %d instead of %d\n", deadchild, child); |
| exit(11); |
| } |
| if (WIFEXITED(status)) { |
| if (WEXITSTATUS(status) == 0) { |
| return false; |
| } |
| } |
| else { |
| printf("***** compiler borked/ICEd/died unexpectedly (status %x)\n", status); |
| return false; |
| } |
| char *error = generator.errorString; |
| |
| if (!error) return true; |
| #if 0 |
| char got[512]; |
| sprintf(buffer, "/tmp/errorfile_%d", child); |
| bool gotOutput = readErrorFile(got, buffer); |
| if (!gotOutput) { |
| printf("**** didn't get an error file %s to analyze!!??\n", buffer); |
| return false; |
| } |
| char *where = strstr(got, error); |
| if (!where) { |
| printf("didn't find '%s' in error file %s\n", error, buffer); |
| return false; |
| } |
| unlink(buffer); |
| #else |
| if (!lookforIn(error, "/tmp/errorfile_%d", child)) return false; |
| #endif |
| return true; |
| } |
| |
| - (bool) run { |
| if (shouldFail) return true; |
| if (sizeof(long) == 4 && options & Do64) { |
| return true; // skip 64-bit tests |
| } |
| int argc = 1; |
| char *argv[argc+1]; |
| argv[0] = binaryName; |
| argv[argc] = NULL; |
| pid_t child = fork(); |
| if (child == 0) { |
| // set up environment |
| char lpath[1024]; |
| char fpath[1024]; |
| char *myenv[3]; |
| int counter = 0; |
| if (libraryPath) { |
| sprintf(lpath, "DYLD_LIBRARY_PATH=%s", libraryPath); |
| myenv[counter++] = lpath; |
| } |
| if (frameworkPath) { |
| sprintf(fpath, "DYLD_FRAMEWORK_PATH=%s", frameworkPath); |
| myenv[counter++] = fpath; |
| } |
| myenv[counter] = NULL; |
| if (generator.warningString) { |
| // set up stdout/stderr |
| char outfile[1024]; |
| sprintf(outfile, "/tmp/stdout_%d", getpid()); |
| close(2); |
| close(1); |
| creat(outfile, 0700); |
| dup(1); |
| } |
| execve(argv[0], argv, myenv); |
| exit(10); // shouldn't happen |
| } |
| if (child < 0) { |
| printf("fork failed\n"); |
| return false; |
| } |
| int status = 0; |
| pid_t deadchild = wait(&status); |
| if (deadchild != child) { |
| printf("wait got %d instead of %d\n", deadchild, child); |
| exit(1); |
| } |
| if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { |
| if (generator.warningString) { |
| if (!lookforIn(generator.warningString, "/tmp/stdout_%d", child)) return false; |
| } |
| return true; |
| } |
| printf("**** run failed for %s\n", binaryName); |
| return false; |
| } |
| |
| @end |
| |
| @implementation TestFileExeGenerator |
| @synthesize filename, compilerPath, errorString; |
| @synthesize hasObjC, hasRR, hasGC, hasCPlusPlus, wantsC99, supposedToNotCompile, open, wants32, wants64; |
| @synthesize radar; |
| @synthesize warningString; |
| |
| - (void)setFilename:(__strong char *)name { |
| filename = gcstrcpy1(name); |
| } |
| - (void)setCompilerPath:(__strong char *)name { |
| compilerPath = gcstrcpy1(name); |
| } |
| |
| - (void)forMostThings:(NSMutableArray *)lines options:(int)options { |
| TestFileExe *item = nil; |
| item = [self lineForOptions:options]; |
| if (item) [lines addObject:item]; |
| item = [self lineForOptions:options|Do64]; |
| if (item) [lines addObject:item]; |
| item = [self lineForOptions:options|DoCPP]; |
| if (item) [lines addObject:item]; |
| item = [self lineForOptions:options|Do64|DoCPP]; |
| if (item) [lines addObject:item]; |
| } |
| |
| /* |
| DoDashG = (1 << 8), |
| DoDashO = (1 << 9), |
| DoDashOs = (1 << 10), |
| DoDashO2 = (1 << 11), |
| */ |
| |
| - (void)forAllThings:(NSMutableArray *)lines options:(int)options { |
| [self forMostThings:lines options:options]; |
| if (!Everything) { |
| return; |
| } |
| // now do it with three explicit optimization flags |
| [self forMostThings:lines options:options | DoDashO]; |
| [self forMostThings:lines options:options | DoDashOs]; |
| [self forMostThings:lines options:options | DoDashO2]; |
| } |
| |
| - (NSArray *)allLines { |
| NSMutableArray *result = [NSMutableArray new]; |
| TestFileExe *item = nil; |
| |
| int options = 0; |
| [self forAllThings:result options:0]; |
| [self forAllThings:result options:DoOBJC | DoRR]; |
| [self forAllThings:result options:DoOBJC | DoGC]; |
| [self forAllThings:result options:DoOBJC | DoGCRR]; |
| //[self forAllThings:result options:DoOBJC | DoRRGC]; |
| |
| return result; |
| } |
| |
| - (void)addLibrary:(const char *)dashLSomething { |
| if (!extraLibraries) { |
| extraLibraries = [NSPointerArray pointerArrayWithOptions: |
| NSPointerFunctionsStrongMemory | |
| NSPointerFunctionsCStringPersonality]; |
| } |
| [extraLibraries addPointer:(void *)dashLSomething]; |
| } |
| |
| - (TestFileExe *)lineForOptions:(int)options { // nil if no can do |
| if (hasObjC && !(options & DoOBJC)) return nil; |
| if (hasCPlusPlus && !(options & DoCPP)) return nil; |
| if (hasObjC) { |
| if (!hasGC && (options & (DoGC|DoGCRR))) return nil; // not smart enough |
| if (!hasRR && (options & (DoRR|DoRRGC))) return nil; |
| } |
| NSPointerArray *pa = [NSPointerArray pointerArrayWithOptions: |
| NSPointerFunctionsStrongMemory | |
| NSPointerFunctionsCStringPersonality]; |
| // construct path |
| char path[512]; |
| path[0] = 0; |
| if (!compilerPath) compilerPath = "/usr/bin"; |
| if (compilerPath) { |
| strcat(path, compilerPath); |
| strcat(path, "/"); |
| } |
| if (options & DoCPP) { |
| strcat(path, DoClang ? "clang++" : "g++-4.2"); |
| } |
| else { |
| strcat(path, DoClang ? "clang" : "gcc-4.2"); |
| } |
| [pa addPointer:gcstrcpy1(path)]; |
| if (options & DoOBJC) { |
| if (options & DoCPP) { |
| [pa addPointer:"-ObjC++"]; |
| } |
| else { |
| [pa addPointer:"-ObjC"]; |
| } |
| } |
| [pa addPointer:"-g"]; |
| if (options & DoDashO) [pa addPointer:"-O"]; |
| else if (options & DoDashO2) [pa addPointer:"-O2"]; |
| else if (options & DoDashOs) [pa addPointer:"-Os"]; |
| if (wantsC99 && (! (options & DoCPP))) { |
| [pa addPointer:"-std=c99"]; |
| [pa addPointer:"-fblocks"]; |
| } |
| [pa addPointer:"-arch"]; |
| [pa addPointer: (options & Do64) ? "x86_64" : "i386"]; |
| |
| if (options & DoOBJC) { |
| switch (options & (DoRR|DoGC|DoGCRR|DoRRGC)) { |
| case DoRR: |
| break; |
| case DoGC: |
| [pa addPointer:"-fobjc-gc-only"]; |
| break; |
| case DoGCRR: |
| [pa addPointer:"-fobjc-gc"]; |
| break; |
| case DoRRGC: |
| printf("DoRRGC unsupported right now\n"); |
| [pa addPointer:"-c"]; |
| return nil; |
| } |
| [pa addPointer:"-framework"]; |
| [pa addPointer:"Foundation"]; |
| } |
| [pa addPointer:gcstrcpy1(filename)]; |
| [pa addPointer:"-o"]; |
| |
| path[0] = 0; |
| strcat(path, filename); |
| strcat(path, "."); |
| strcat(path, (options & Do64) ? "64" : "32"); |
| if (options & DoOBJC) { |
| switch (options & (DoRR|DoGC|DoGCRR|DoRRGC)) { |
| case DoRR: strcat(path, "-rr"); break; |
| case DoGC: strcat(path, "-gconly"); break; |
| case DoGCRR: strcat(path, "-gcrr"); break; |
| case DoRRGC: strcat(path, "-rrgc"); break; |
| } |
| } |
| if (options & DoCPP) strcat(path, "++"); |
| if (options & DoDashO) strcat(path, "-O"); |
| else if (options & DoDashO2) strcat(path, "-O2"); |
| else if (options & DoDashOs) strcat(path, "-Os"); |
| if (wantsC99) strcat(path, "-C99"); |
| strcat(path, DoClang ? "-clang" : "-gcc"); |
| strcat(path, "-bin"); |
| TestFileExe *result = [TestFileExe new]; |
| result.binaryName = gcstrcpy1(path); // could snarf copy in pa |
| [pa addPointer:result.binaryName]; |
| for (id cString in extraLibraries) { |
| [pa addPointer:cString]; |
| } |
| |
| result.sourceName = gcstrcpy1(filename); // could snarf copy in pa |
| result.compileLine = pa; |
| result.options = options; |
| result.shouldFail = supposedToNotCompile; |
| result.generator = self; |
| return result; |
| } |
| |
| + (NSArray *)generatorsFromPath:(NSString *)path { |
| FILE *fp = fopen([path fileSystemRepresentation], "r"); |
| if (fp == NULL) return nil; |
| NSArray *result = [self generatorsFromFILE:fp]; |
| fclose(fp); |
| return result; |
| } |
| |
| #define LOOKFOR "CON" "FIG" |
| |
| char *__strong parseRadar(char *line) { |
| line = strstr(line, "rdar:"); // returns beginning |
| char *endp = line + strlen("rdar:"); |
| while (*endp && *endp != ' ' && *endp != '\n') |
| ++endp; |
| return gcstrcpy2(line, endp); |
| } |
| |
| - (void)parseLibraries:(const char *)line { |
| start: |
| line = strstr(line, "-l"); |
| char *endp = (char *)line + 2; |
| while (*endp && *endp != ' ' && *endp != '\n') |
| ++endp; |
| [self addLibrary:gcstrcpy2(line, endp)]; |
| if (strstr(endp, "-l")) { |
| line = endp; |
| goto start; |
| } |
| } |
| |
| + (TestFileExeGenerator *)generatorFromLine:(char *)line filename:(char *)filename { |
| TestFileExeGenerator *item = [TestFileExeGenerator new]; |
| item.filename = gcstrcpy1(filename); |
| if (strstr(line, "GC")) item.hasGC = true; |
| if (strstr(line, "RR")) item.hasRR = true; |
| if (strstr(line, "C++")) item.hasCPlusPlus = true; |
| if (strstr(line, "-C99")) { |
| item.wantsC99 = true; |
| } |
| if (strstr(line, "64")) item.wants64 = true; |
| if (strstr(line, "32")) item.wants32 = true; |
| if (strstr(line, "-l")) [item parseLibraries:line]; |
| if (strstr(line, "open")) item.open = true; |
| if (strstr(line, "FAIL")) item.supposedToNotCompile = true; // old |
| // compile time error |
| if (strstr(line, "error:")) { |
| item.supposedToNotCompile = true; |
| // zap newline |
| char *error = strstr(line, "error:") + strlen("error:"); |
| // make sure we have something before the newline |
| char *newline = strstr(error, "\n"); |
| if (newline && ((newline-error) > 1)) { |
| *newline = 0; |
| item.errorString = gcstrcpy1(strstr(line, "error:") + strlen("error: ")); |
| } |
| } |
| // run time warning |
| if (strstr(line, "runtime:")) { |
| // zap newline |
| char *error = strstr(line, "runtime:") + strlen("runtime:"); |
| // make sure we have something before the newline |
| char *newline = strstr(error, "\n"); |
| if (newline && ((newline-error) > 1)) { |
| *newline = 0; |
| item.warningString = gcstrcpy1(strstr(line, "runtime:") + strlen("runtime:")); |
| } |
| } |
| if (strstr(line, "rdar:")) item.radar = parseRadar(line); |
| if (item.hasGC || item.hasRR) item.hasObjC = true; |
| if (!item.wants32 && !item.wants64) { // give them both if they ask for neither |
| item.wants32 = item.wants64 = true; |
| } |
| return item; |
| } |
| |
| + (NSArray *)generatorsFromFILE:(FILE *)fp { |
| NSMutableArray *result = [NSMutableArray new]; |
| // pretend this is a grep LOOKFOR *.[cmCM][cmCM] input |
| // look for |
| // filename: ... LOOKFOR [GC] [RR] [C++] [FAIL ...] |
| char buf[512]; |
| while (fgets(buf, 512, fp)) { |
| char *config = strstr(buf, LOOKFOR); |
| if (!config) continue; |
| char *filename = buf; |
| char *end = strchr(buf, ':'); |
| *end = 0; |
| [result addObject:[self generatorFromLine:config filename:filename]]; |
| } |
| return result; |
| } |
| |
| + (TestFileExeGenerator *)generatorFromFilename:(char *)filename { |
| FILE *fp = fopen(filename, "r"); |
| if (!fp) { |
| printf("didn't open %s!!\n", filename); |
| return nil; |
| } |
| char buf[512]; |
| while (fgets(buf, 512, fp)) { |
| char *config = strstr(buf, LOOKFOR); |
| if (!config) continue; |
| fclose(fp); |
| return [self generatorFromLine:config filename:filename]; |
| } |
| fclose(fp); |
| // guess from filename |
| char *ext = strrchr(filename, '.'); |
| if (!ext) return nil; |
| TestFileExeGenerator *result = [TestFileExeGenerator new]; |
| result.filename = gcstrcpy1(filename); |
| if (!strncmp(ext, ".m", 2)) { |
| result.hasObjC = true; |
| result.hasRR = true; |
| result.hasGC = true; |
| } |
| else if (!strcmp(ext, ".c")) { |
| ; |
| } |
| else if (!strcmp(ext, ".M") || !strcmp(ext, ".mm")) { |
| result.hasObjC = true; |
| result.hasRR = true; |
| result.hasGC = true; |
| result.hasCPlusPlus = true; |
| } |
| else if (!strcmp(ext, ".cc") |
| || !strcmp(ext, ".cp") |
| || !strcmp(ext, ".cxx") |
| || !strcmp(ext, ".cpp") |
| || !strcmp(ext, ".CPP") |
| || !strcmp(ext, ".c++") |
| || !strcmp(ext, ".C")) { |
| result.hasCPlusPlus = true; |
| } |
| else { |
| printf("unknown extension, file %s ignored\n", filename); |
| result = nil; |
| } |
| return result; |
| |
| } |
| |
| - (NSString *)description { |
| return [NSString stringWithFormat:@"%s: %s%s%s%s%s%s", |
| filename, |
| LOOKFOR, |
| hasGC ? " GC" : "", |
| hasRR ? " RR" : "", |
| hasCPlusPlus ? " C++" : "", |
| wantsC99 ? "C99" : "", |
| supposedToNotCompile ? " FAIL" : ""]; |
| } |
| |
| @end |
| |
| void printDetails(NSArray *failures, const char *whatAreThey) { |
| if ([failures count]) { |
| NSMutableString *output = [NSMutableString new]; |
| printf("%s:\n", whatAreThey); |
| for (TestFileExe *line in failures) { |
| printf("%s", line.binaryName); |
| char *radar = line.generator.radar; |
| if (radar) |
| printf(" (due to %s?),", radar); |
| printf(" recompile via:\n%s\n\n", line.description.UTF8String); |
| } |
| printf("\n"); |
| } |
| } |
| |
| void help(const char *whoami) { |
| printf("Usage: %s [-fast] [-e] [-dyld librarypath] [gcc4.2dir] [-- | source1 ...]\n", whoami); |
| printf(" -fast don't recompile if binary younger than source\n"); |
| printf(" -open only run tests that are thought to still be unresolved\n"); |
| printf(" -clang use the clang and clang++ compilers\n"); |
| printf(" -e compile all variations also with -Os, -O2, -O3\n"); |
| printf(" -dyld p override DYLD_LIBRARY_PATH and DYLD_FRAMEWORK_PATH to p when running tests\n"); |
| printf(" <compilerpath> directory containing gcc-4.2 (or clang) that you wish to use to compile the tests\n"); |
| printf(" -- assume stdin is a grep CON" "FIG across the test sources\n"); |
| printf(" otherwise treat each remaining argument as a single test file source\n"); |
| printf("%s will compile and run individual test files under a variety of compilers, c, obj-c, c++, and objc++\n", whoami); |
| printf(" .c files are compiled with all four compilers\n"); |
| printf(" .m files are compiled with objc and objc++ compilers\n"); |
| printf(" .C files are compiled with c++ and objc++ compilers\n"); |
| printf(" .M files are compiled only with the objc++ compiler\n"); |
| printf("(actually all forms of extensions recognized by the compilers are honored, .cc, .c++ etc.)\n"); |
| printf("\nTest files should run to completion with no output and exit (return) 0 on success.\n"); |
| printf("Further they should be able to be compiled and run with GC on or off and by the C++ compilers\n"); |
| printf("A line containing the string CON" "FIG within the source enables restrictions to the above assumptions\n"); |
| printf("and other options.\n"); |
| printf("Following CON" "FIG the string\n"); |
| printf(" C++ restricts the test to only be run by c++ and objc++ compilers\n"); |
| printf(" GC restricts the test to only be compiled and run with GC on\n"); |
| printf(" RR (retain/release) restricts the test to only be compiled and run with GC off\n"); |
| printf("Additionally,\n"); |
| printf(" -C99 restricts the C versions of the test to -fstd=c99 -fblocks\n"); |
| printf(" -O adds the -O optimization level\n"); |
| printf(" -O2 adds the -O2 optimization level\n"); |
| printf(" -Os adds the -Os optimization level\n"); |
| printf("Files that are known to exhibit unresolved problems can provide the term \"open\" and this can"); |
| printf("in turn allow highlighting of fixes that have regressed as well as identify that fixes are now available.\n"); |
| printf("Files that exhibit known bugs may provide\n"); |
| printf(" rdar://whatever such that if they fail the rdar will get cited\n"); |
| printf("Files that are expected to fail to compile should provide, as their last token sequence,\n"); |
| printf(" error:\n"); |
| printf(" or error: substring to match.\n"); |
| printf("Files that are expected to produce a runtime error message should provide, as their last token sequence,\n"); |
| printf(" warning: string to match\n"); |
| printf("\n%s will compile and run all configurations of the test files and report a summary at the end. Good luck.\n", whoami); |
| printf(" Blaine Garst blaine@apple.com\n"); |
| } |
| |
| int main(int argc, char *argv[]) { |
| printf("running on %s-bit architecture\n", sizeof(long) == 4 ? "32" : "64"); |
| char *compilerDir = "/usr/bin"; |
| NSMutableArray *generators = [NSMutableArray new]; |
| bool doFast = false; |
| bool doStdin = false; |
| bool onlyOpen = false; |
| char *libraryPath = getenv("DYLD_LIBRARY_PATH"); |
| char *frameworkPath = getenv("DYLD_FRAMEWORK_PATH"); |
| // process options |
| while (argc > 1) { |
| if (!strcmp(argv[1], "-fast")) { |
| doFast = true; |
| --argc; |
| ++argv; |
| } |
| else if (!strcmp(argv[1], "-dyld")) { |
| doFast = true; |
| --argc; |
| ++argv; |
| frameworkPath = argv[1]; |
| libraryPath = argv[1]; |
| --argc; |
| ++argv; |
| } |
| else if (!strcmp(argv[1], "-open")) { |
| onlyOpen = true; |
| --argc; |
| ++argv; |
| } |
| else if (!strcmp(argv[1], "-clang")) { |
| DoClang = true; |
| --argc; |
| ++argv; |
| } |
| else if (!strcmp(argv[1], "-e")) { |
| Everything = true; |
| --argc; |
| ++argv; |
| } |
| else if (!strcmp(argv[1], "--")) { |
| doStdin = true; |
| --argc; |
| ++argv; |
| } |
| else if (!strcmp(argv[1], "-")) { |
| help(argv[0]); |
| return 1; |
| } |
| else if (argc > 1 && isDirectory(argv[1])) { |
| compilerDir = argv[1]; |
| ++argv; |
| --argc; |
| } |
| else |
| break; |
| } |
| // process remaining arguments, or stdin |
| if (argc == 1) { |
| if (doStdin) |
| generators = (NSMutableArray *)[TestFileExeGenerator generatorsFromFILE:stdin]; |
| else { |
| help(argv[0]); |
| return 1; |
| } |
| } |
| else while (argc > 1) { |
| TestFileExeGenerator *generator = [TestFileExeGenerator generatorFromFilename:argv[1]]; |
| if (generator) [generators addObject:generator]; |
| ++argv; |
| --argc; |
| } |
| // see if we can generate all possibilities |
| NSMutableArray *failureToCompile = [NSMutableArray new]; |
| NSMutableArray *failureToFailToCompile = [NSMutableArray new]; |
| NSMutableArray *failureToRun = [NSMutableArray new]; |
| NSMutableArray *successes = [NSMutableArray new]; |
| for (TestFileExeGenerator *generator in generators) { |
| //NSLog(@"got %@", generator); |
| if (onlyOpen && !generator.open) { |
| //printf("skipping resolved test %s\n", generator.filename); |
| continue; // skip closed if onlyOpen |
| } |
| if (!onlyOpen && generator.open) { |
| //printf("skipping open test %s\n", generator.filename); |
| continue; // skip open if not asked for onlyOpen |
| } |
| generator.compilerPath = compilerDir; |
| NSArray *tests = [generator allLines]; |
| for (TestFileExe *line in tests) { |
| line.frameworkPath = frameworkPath; // tell generators about it instead XXX |
| line.libraryPath = libraryPath; // tell generators about it instead XXX |
| if ([line shouldFail]) { |
| if (doFast) continue; // don't recompile & don't count as success |
| if ([line compileWithExpectedFailure]) { |
| [successes addObject:line]; |
| } |
| else |
| [failureToFailToCompile addObject:line]; |
| } |
| else if ([line compileUnlessExists:doFast]) { |
| if ([line run]) { |
| printf("%s ran successfully\n", line.binaryName); |
| [successes addObject:line]; |
| } |
| else { |
| [failureToRun addObject:line]; |
| } |
| } |
| else { |
| [failureToCompile addObject:line]; |
| } |
| } |
| } |
| 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", |
| [successes count], [failureToCompile count], [failureToFailToCompile count], [failureToRun count]); |
| printDetails(failureToCompile, "unexpected compile failures"); |
| printDetails(failureToFailToCompile, "should have failed to compile but didn't failures"); |
| printDetails(failureToRun, "run failures"); |
| |
| if (onlyOpen && [successes count]) { |
| NSMutableSet *radars = [NSMutableSet new]; |
| printf("The following tests ran successfully suggesting that they are now resolved:\n"); |
| for (TestFileExe *line in successes) { |
| printf("%s\n", line.binaryName); |
| if (line.radar) [radars addObject:line.generator]; |
| } |
| if ([radars count]) { |
| printf("The following radars may be resolved:\n"); |
| for (TestFileExeGenerator *line in radars) { |
| printf("%s\n", line.radar); |
| } |
| } |
| } |
| |
| return [failureToCompile count] + [failureToRun count]; |
| } |
| |
| #include <sys/stat.h> |
| |
| static bool isDirectory(char *path) { |
| struct stat statb; |
| int retval = stat(path, &statb); |
| if (retval != 0) return false; |
| if (statb.st_mode & S_IFDIR) return true; |
| return false; |
| } |
| |
| static bool isExecutable(char *path) { |
| struct stat statb; |
| int retval = stat(path, &statb); |
| if (retval != 0) return false; |
| if (!(statb.st_mode & S_IFREG)) return false; |
| if (statb.st_mode & S_IXUSR) return true; |
| return false; |
| } |
| |
| static bool isYounger(char *source, char *binary) { |
| struct stat statb; |
| int retval = stat(binary, &statb); |
| if (retval != 0) return true; // if doesn't exit, lie |
| |
| struct stat stata; |
| retval = stat(source, &stata); |
| if (retval != 0) return true; // we're hosed |
| // the greater the timeval the younger it is |
| if (stata.st_mtimespec.tv_sec > statb.st_mtimespec.tv_sec) return true; |
| if (stata.st_mtimespec.tv_nsec > statb.st_mtimespec.tv_nsec) return true; |
| return false; |
| } |
| |
| static bool readErrorFile(char *buffer, const char *from) { |
| int fd = open(from, 0); |
| if (fd < 0) { |
| printf("didn't open %s, (might not have been created?)\n", buffer); |
| return false; |
| } |
| int count = read(fd, buffer, 512); |
| if (count < 1) { |
| printf("read error on %s\n", buffer); |
| return false; |
| } |
| buffer[count-1] = 0; // zap newline |
| return true; |
| } |