Skip to content

Commit

Permalink
Add support for reading xcodeproj files
Browse files Browse the repository at this point in the history
  • Loading branch information
Zach Drayer authored and sorbits committed Sep 3, 2012
1 parent 9f5aa0b commit 55c0109
Show file tree
Hide file tree
Showing 9 changed files with 293 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@
[submodule "PlugIns/dialog"]
path = PlugIns/dialog
url = https://github.com/textmate/dialog.git
[submodule "vendor/XcodeEditor/vendor"]
path = vendor/XcodeEditor/vendor
url = https://github.com/textmate/xcode-editor.git
2 changes: 2 additions & 0 deletions Frameworks/OakFileBrowser/src/OakFileBrowser.mm
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ - (void)loadFileBrowserOptions;
return [NSURL fileURLWithPath:parentPath isDirectory:YES];
else if([[url scheme] isEqualToString:@"scm"])
return [NSURL fileURLWithPath:[url path] isDirectory:YES];
else if([[url scheme] isEqualToString:@"xcodeproj"])
return [NSURL fileURLWithPath:parentPath isDirectory:YES];
else
return [[[NSURL alloc] initWithScheme:[url scheme] host:[url host] path:parentPath] autorelease];
}
Expand Down
5 changes: 4 additions & 1 deletion Frameworks/OakFileBrowser/src/io/FSDataSource.mm
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// #import "FSBundlesDataSource.h"
#import "FSSCMDataSource.h"
#import "FSSearchDataSource.h"
#import "FSXcodeProjectDataSource.h"
#import <OakAppKit/OakAppKit.h>
#import <OakAppKit/OakSound.h>
#import <OakFoundation/OakFoundation.h>
Expand All @@ -19,7 +20,9 @@
{
FSDataSource* res = nil;
NSString* scheme = [anURL scheme];
if([scheme isEqualToString:@"file"])
if([scheme isEqualToString:@"xcodeproj"])
res = [[[FSXcodeProjectDataSource alloc] initWithURL:anURL options:someOptions] autorelease];
else if([scheme isEqualToString:@"file"])
res = [[[FSDirectoryDataSource alloc] initWithURL:anURL options:someOptions] autorelease];
else if([scheme isEqualToString:@"computer"])
res = [[[FSVolumesDataSource alloc] initWithURL:anURL options:someOptions] autorelease];
Expand Down
3 changes: 3 additions & 0 deletions Frameworks/OakFileBrowser/src/io/FSDirectoryDataSource.mm
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ @implementation FSFileItem
sort_as_directory = !(flags & path::flag::package);
treat_as_directory = !(flags & (path::flag::package|path::flag::hidden_volume));
target = "file://localhost" + encode::url_part(path, "/") + "/";

if(path::extension(path) == ".xcodeproj")
target = "xcodeproj://localhost" + encode::url_part(path, "/") + "/";
}
else if(entry->d_type == DT_REG)
{
Expand Down
9 changes: 9 additions & 0 deletions Frameworks/OakFileBrowser/src/io/FSXcodeProjectDataSource.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#import "FSDataSource.h"

@interface FSXcodeProjectDataSource : FSDataSource
{
NSMutableDictionary* _projects;
NSString* _developerDirectoryPath;
}
- (id)initWithURL:(NSURL*)anURL options:(NSUInteger)someOptions;
@end
267 changes: 267 additions & 0 deletions Frameworks/OakFileBrowser/src/io/FSXcodeProjectDataSource.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
#import "FSXcodeProjectDataSource.h"
#import "FSItem.h"
#import <XcodeEditor/XCProject.h>
#import <XcodeEditor/XCGroup.h>
#import <XcodeEditor/XCSourceFile.h>
#import <XcodeEditor/XCTarget.h>
#import <XcodeEditor/XCBuildConfigurationList.h>
#import <XcodeEditor/XcodeGroupMember.h>
#import <XcodeEditor/XcodeSourceFileType.h>
#import <OakFoundation/NSArray Additions.h>
#import <OakFoundation/NSString Additions.h>
#import <OakAppKit/OakFileIconImage.h>
#import <io/path.h>
#import <io/exec.h>

static BOOL isFaultyProductGroup(XCGroup* group)
{
return ([group groupMemberType] == PBXGroup && [[group alias] isEqualToString:@"Products"]);
}

static NSURL* pathURLWithBaseAndRelativePath(NSString* basePath, NSString* relativePath)
{
return [NSURL fileURLWithPath:[[basePath stringByAppendingPathComponent:relativePath] stringByResolvingSymlinksInPath]];
}

static NSString *caseSensitiveMatchInArrayForString(NSArray *array, NSString *stringToMatch)
{
for (NSString *possibleString in array)
{
if (![stringToMatch caseInsensitiveCompare:possibleString])
return [[possibleString copy] autorelease];
}
return nil;
}

#pragma mark -

@interface FSXcodeProjectDataSource (Private)
- (FSItem*)itemForFrameworkAtPath:(NSString*)path;
- (NSArray*)itemsForDirectoryAtPath:(NSString*)path;

- (FSItem*)itemForProject:(XCProject*)project atURL:(NSURL*)anURL;
- (NSArray*)itemsForGroup:(XCGroup*)group withBasePath:(NSString*)basePath inProject:(XCProject*)project;

- (NSString*)frameworkPathForSDKRoot:(NSString*)SDKRoot;
@end

#pragma mark -

@implementation FSXcodeProjectDataSource
- (id)initWithURL:(NSURL*)anURL options:(NSUInteger)someOptions
{
if((self = [super init])) {
_projects = [[NSMutableDictionary alloc] init];

NSString* xcodeprojPath = [anURL path];
if ([xcodeprojPath existsAsPath])
{
XCProject* project = [XCProject projectWithFilePath:xcodeprojPath];

self.rootItem = [self itemForProject:project atURL:[NSURL fileURLWithPath:xcodeprojPath]];

[_projects setObject:project forKey:[NSURL fileURLWithPath:xcodeprojPath]];
}
else
self.rootItem = [FSItem itemWithURL:anURL];
}
return self;
}

- (void) dealloc
{
[_projects release];
[_developerDirectoryPath release];

[super dealloc];
}

#pragma mark -

- (FSItem*)itemForFrameworkAtPath:(NSString*)path
{
FSItem* item = [FSItem itemWithURL:[NSURL fileURLWithPath:path]];

NSMutableArray* results = [NSMutableArray array];
for (NSString* file in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil])
{
if ([file hasPrefix:@"_CodeSignature"])
continue;

NSString* workingPath = [path stringByAppendingPathComponent:file];
if (![workingPath isDirectory])
continue;

FSItem* item = [FSItem itemWithURL:[NSURL fileURLWithPath:workingPath]];
item.children = [self itemsForDirectoryAtPath:[item path]];
[results addObject:item];
}
item.children = results;

return item;
}

- (NSArray*)itemsForDirectoryAtPath:(NSString*)path
{
NSMutableArray* results = [NSMutableArray array];
for (NSString* file in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil])
{
file = [path stringByAppendingPathComponent:file];

FSItem* item = [FSItem itemWithURL:[NSURL fileURLWithPath:file]];
if ([file isDirectory])
item.children = [self itemsForDirectoryAtPath:file];
[results addObject:item];
}
return [[results copy] autorelease];
}

#pragma mark -

- (FSItem*)itemForProject:(XCProject*)project atURL:(NSURL*)anURL
{
FSItem* item = [FSItem itemWithURL:anURL];
item.name = [[[project filePath] lastPathComponent] stringByDeletingPathExtension];
item.url = [anURL URLByDeletingLastPathComponent];

NSMutableArray* results = [NSMutableArray array];
NSString* basePath = [[project filePath] stringByDeletingLastPathComponent];
for (XCGroup* group in [project rootGroups])
{
if (isFaultyProductGroup(group))
continue;

FSItem* item = [FSItem itemWithURL:pathURLWithBaseAndRelativePath(basePath, [group pathRelativeToProjectRoot])];
item.name = [anURL lastPathComponent];
item.children = [self itemsForGroup:group withBasePath:basePath inProject:project];
[results addObject:item];
}
item.children = results;
return item;
}

- (NSArray*)itemsForGroup:(XCGroup*)group withBasePath:(NSString*)basePath inProject:(XCProject*)project
{
NSMutableArray* results = [NSMutableArray array];
for (id<XcodeGroupMember> member in [group members])
{
NSURL* itemURL = pathURLWithBaseAndRelativePath(basePath, [member pathRelativeToProjectRoot]);
if (![[itemURL path] existsAsPath])
itemURL = pathURLWithBaseAndRelativePath(basePath, [[group pathRelativeToProjectRoot] stringByAppendingPathComponent:[member pathRelativeToProjectRoot]]);
if (![[itemURL path] existsAsPath])
itemURL = pathURLWithBaseAndRelativePath(basePath, [[group pathRelativeToProjectRoot] stringByAppendingPathComponent:[member displayName]]);
if (![[itemURL path] existsAsPath])
itemURL = [NSURL fileURLWithPath:basePath];

if ([[[member pathRelativeToProjectRoot] pathExtension] isEqualToString:@"xcodeproj"])
{
if ([[itemURL path] existsAsPath])
{
XCProject* project = [XCProject projectWithFilePath:[itemURL path]];
[results addObject:[self itemForProject:project atURL:itemURL]];

[_projects setObject:project forKey:itemURL];
}
else
[results addObject:[FSItem itemWithURL:itemURL]];
}
else
{
FSItem* item = [FSItem itemWithURL:itemURL];
item.name = member.displayName;

if ([member groupMemberType] == PBXGroup)
{
if (isFaultyProductGroup(member))
continue;
item.children = [self itemsForGroup:member withBasePath:basePath inProject:project];
}
else
{
if (([(XCSourceFile* )member type] == Framework || [[member pathRelativeToProjectRoot] hasSuffix:@"dylib"]))
{
NSArray* targets = [project targets];
if (![targets count])
continue;

if (![_developerDirectoryPath length])
{
std::string xcodePath = io::exec("/usr/bin/xcode-select", "--print-path", NULL);
xcodePath = xcodePath.substr(0, (xcodePath.length() - 1));

_developerDirectoryPath = [[NSString stringWithCxxString:xcodePath] copy];
}

XCTarget* target = [targets objectAtIndex:0];
XCBuildConfigurationList* targetBuildConfiguration = [target defaultConfiguration];
XCBuildConfigurationList* projectBuildConfiguration = [project defaultConfiguration];

NSString* SDKRoot = [targetBuildConfiguration valueForKey:@"SDKROOT"];
if (!SDKRoot.length)
SDKRoot = [projectBuildConfiguration valueForKey:@"SDKROOT"];

if (!SDKRoot.length)
SDKRoot = @"macosx";

NSString* frameworkPath = [self frameworkPathForSDKRoot:SDKRoot];

frameworkPath = [frameworkPath stringByAppendingPathComponent:[member pathRelativeToProjectRoot]];
if ([frameworkPath hasSuffix:@"framework"])
item = [self itemForFrameworkAtPath:frameworkPath];
else
item.url = [NSURL fileURLWithPath:frameworkPath];
}
}
item.icon = [OakFileIconImage fileIconImageWithPath:[[item url] path] size:NSMakeSize(16, 16)];
[results addObject:item];
}
}
return [[results copy] autorelease];
}

#pragma mark -

- (NSString*)frameworkPathForSDKRoot:(NSString*)SDKRoot
{
NSString *frameworkPath = nil;
if ([SDKRoot rangeOfString:@"iphoneos" options:(NSCaseInsensitiveSearch | NSAnchoredSearch) range:NSMakeRange(0, SDKRoot.length)].location != NSNotFound)
frameworkPath = [_developerDirectoryPath stringByAppendingPathComponent:@"/Platforms/iPhoneOS.platform/Developer/SDKs/"];
else if ([SDKRoot rangeOfString:@"mac" options:(NSCaseInsensitiveSearch | NSAnchoredSearch) range:NSMakeRange(0, SDKRoot.length)].location != NSNotFound)
frameworkPath = [_developerDirectoryPath stringByAppendingPathComponent:@"/Platforms/MacOSX.platform/Developer/SDKs/"];

NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:frameworkPath error:nil];
NSString *workingSDKRoot = caseSensitiveMatchInArrayForString(files, [SDKRoot stringByAppendingString:@".sdk"]);

// if an SDK version was specified directly, use it
if (workingSDKRoot)
return [frameworkPath stringByAppendingPathComponent:workingSDKRoot];

NSInteger index = NSNotFound;
if ([SDKRoot isEqualToString:@"iphoneos"])
index = @"iphoneos".length;
else if ([SDKRoot isEqualToString:@"macosx"])
index = @"macosx".length;

if (index == NSNotFound)
return nil;

files = [files sortedArrayUsingComparator:^(id one, id two) {
CGFloat oneFloat = [[one substringFromIndex:index] floatValue];
CGFloat twoFloat = [[two substringFromIndex:index] floatValue];
if (oneFloat > twoFloat)
return (NSComparisonResult)NSOrderedDescending;
if (oneFloat < twoFloat)
return (NSComparisonResult)NSOrderedAscending;
return (NSComparisonResult)NSOrderedSame;
}];

return [frameworkPath stringByAppendingPathComponent:[files lastObject]];
}

#pragma mark -

- (BOOL)outlineView:(NSOutlineView*)anOutlineView acceptDrop:(id <NSDraggingInfo>)info item:(FSItem*)item childIndex:(NSInteger)childIndex
{
return NO;
}
@end
2 changes: 1 addition & 1 deletion Frameworks/OakFileBrowser/target
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
SOURCES = src/**/*.{cc,mm}
EXPORT = src/OakFileBrowser.h
CP_Resources = resources/* gfx/**/*
LINK += text cf document editor io regexp settings ns plist OakAppKit OakFoundation scm OakSystem Preferences
LINK += text cf document editor io regexp settings ns plist OakAppKit OakFoundation scm OakSystem Preferences XcodeEditor
FRAMEWORKS = Cocoa Carbon SystemConfiguration
3 changes: 3 additions & 0 deletions vendor/XcodeEditor/target
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SOURCES = vendor/Source/Main/**/*.m
EXPORT = vendor/Source/Main/**/*.h
FRAMEWORKS = Cocoa
1 change: 1 addition & 0 deletions vendor/XcodeEditor/vendor
Submodule vendor added at 16b06c

0 comments on commit 55c0109

Please sign in to comment.