806 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Objective-C
		
	
	
	
			
		
		
	
	
			806 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Objective-C
		
	
	
	
//
 | 
						|
//                     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;
 | 
						|
}
 |