-
Notifications
You must be signed in to change notification settings - Fork 644
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
made it possible to just give the path to an xcodeproject to appledoc.
it will try to do the right thing :D - it will open Xcode file and look for basic project settings and it will then build a docket and install it Signed-off-by: Dominik Pich <Dominik@pich.info>
- Loading branch information
Showing
4 changed files
with
320 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// | ||
// DDXcodeProjectFile.h | ||
// appledoc | ||
// | ||
// Created by Dominik Pich on 9/4/12. | ||
// Copyright (c) 2012 Gentle Bytes. All rights reserved. | ||
// | ||
|
||
#import <Foundation/Foundation.h> | ||
|
||
/** @file DDXcodeProjectFile.h */ | ||
|
||
/** | ||
* reads an xcode 4 Project file and provides an in memory representation | ||
* @warning WHICH cannot be saved at the moment | ||
* @warning Does only provide the fundamental project settings right now as that's all I need ATM :D | ||
*/ | ||
@interface DDXcodeProjectFile : NSObject | ||
|
||
@property(readonly) NSString *path; | ||
@property(readonly) NSDictionary *dictionary; | ||
|
||
@property(readonly) NSString *name; | ||
@property(readonly) NSString *minimumVersion; | ||
@property(readonly) NSString *company; | ||
@property(readonly) NSString *projectRoot; | ||
@property(readonly) NSString *classPrefix; | ||
@property(readonly) NSString *developmentRegion; | ||
|
||
/** | ||
* an array of 1-N dicts for all files found in the project. Each dictionary contains 'path' and 'type' | ||
* the path is resolved to an absolute path | ||
*/ | ||
@property(readonly) NSArray *files; | ||
|
||
+ (id)xcodeProjectFileWithPath:(NSString*)path error:(NSError**)pError; | ||
+ (id)xcodeProjectFileWithDictionary:(NSDictionary*)dict error:(NSError**)pError; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
// | ||
// DDXcodeProjectFile.m | ||
// appledoc | ||
// | ||
// Created by Dominik Pich on 9/4/12. | ||
// Copyright (c) 2012 Gentle Bytes. All rights reserved. | ||
// | ||
|
||
#import "DDXcodeProjectFile.h" | ||
|
||
@interface DDXcodeProjectFile () { | ||
NSMutableArray *_mutableFilesBuffer; | ||
} | ||
|
||
@property(readwrite) NSString *path; | ||
@property(readwrite) NSDictionary *dictionary; | ||
|
||
@property(readwrite) NSString *name; | ||
@property(readwrite) NSString *minimumVersion; | ||
@property(readwrite) NSString *projectRoot; | ||
@property(readwrite) NSString *company; | ||
@property(readwrite) NSString *classPrefix; | ||
@property(readwrite) NSString *developmentRegion; | ||
@property(readwrite) NSArray *files; | ||
|
||
- (BOOL)parse:(NSError**)pError; | ||
|
||
- (id)initWithPath:(NSString*)path; | ||
- (id)initWithName:(NSString*)name andDictionary:(NSDictionary*)dict; | ||
@end | ||
|
||
@implementation DDXcodeProjectFile | ||
|
||
#pragma mark - | ||
|
||
- (id)initWithPath:(NSString*)path { | ||
id pbxpath = nil; | ||
|
||
if([[path lastPathComponent] isEqualToString:@"project.pbxproj"]) { | ||
pbxpath = path; | ||
path = path.stringByDeletingLastPathComponent; | ||
} | ||
else { | ||
//path = path | ||
pbxpath = [path stringByAppendingPathComponent:@"project.pbxproj"]; | ||
} | ||
|
||
BOOL isDir = NO; | ||
NSAssert([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir], @"project doesnt exist"); | ||
NSAssert(isDir, @"project should be a directory"); | ||
NSAssert([[NSFileManager defaultManager] fileExistsAtPath:pbxpath isDirectory:nil], @"project has no pbx xml file"); | ||
|
||
if(self) { | ||
self.path = pbxpath; | ||
self.name = path.lastPathComponent.stringByDeletingPathExtension; | ||
} | ||
|
||
return self; | ||
} | ||
|
||
- (id)initWithName:(NSString*)name andDictionary:(NSDictionary*)dict { | ||
self = [super init]; | ||
|
||
if(self) { | ||
self.name = name; | ||
self.dictionary = dict; | ||
} | ||
|
||
return self; | ||
} | ||
|
||
+ (id)xcodeProjectFileWithPath:(NSString*)path error:(NSError**)pError { | ||
id file = [[[self class] alloc] initWithPath:path]; | ||
if(file) { | ||
if([file parse:pError]) { | ||
return file; | ||
} | ||
} | ||
return nil; | ||
} | ||
|
||
+ (id)xcodeProjectFileWithDictionary:(NSDictionary*)dict error:(NSError**)pError { | ||
id file = [[[self class] alloc] initWithDictionary:dict]; | ||
if(file) { | ||
if([file parse:pError]) { | ||
return file; | ||
} | ||
} | ||
return nil; | ||
} | ||
|
||
#pragma mark - main parse | ||
|
||
- (BOOL)parse:(NSError**)pError { | ||
NSAssert(self.path.length || self.dictionary.count, @"we should have a non-empty path or non-empty dictionary"); | ||
|
||
//get initial dictionary | ||
if(self.path.length) { | ||
self.dictionary = [NSDictionary dictionaryWithContentsOfFile:self.path]; | ||
if(!self.dictionary) { | ||
*pError = [NSError errorWithCode:0 description:@"DDXcodeProjectFile can't be parsed." reason:@"cannot make plist from file contents"]; | ||
return NO; | ||
} | ||
} | ||
if(!self.dictionary) { | ||
*pError = [NSError errorWithCode:0 description:@"DDXcodeProjectFile can't be parsed." reason:@"cannot make plist from file contents"]; | ||
return NO; | ||
} | ||
|
||
//get main objects dictionary | ||
NSDictionary *objects = self.dictionary[@"objects"]; | ||
if(![objects isKindOfClass:[NSDictionary class]]) { | ||
*pError = [NSError errorWithCode:1 description:@"DDXcodeProjectFile can't be parsed." reason:@"cannot find main objects dictionary"]; | ||
return NO; | ||
} | ||
|
||
//set up _mutableFilesBuffer which will get filled in the parsing process.. or not :D | ||
_mutableFilesBuffer = [NSMutableArray arrayWithCapacity:objects.count/2]; | ||
|
||
//handle each object that can appear in a method found via string to SEL | ||
for (NSDictionary *object in objects.allValues) { | ||
if(![object isKindOfClass:[NSDictionary class]] || !object[@"isa"]) { | ||
*pError = [NSError errorWithCode:1 description:@"DDXcodeProjectFile can't be parsed." reason:[NSString stringWithFormat:@"cannot handle object %@", object]]; | ||
return NO; | ||
} | ||
|
||
//get method by isa | ||
SEL method = [self methodNameForIsa:object[@"isa"]]; | ||
if(!method) { | ||
*pError = [NSError errorWithCode:1 description:@"DDXcodeProjectFile can't be parsed." reason:[NSString stringWithFormat:@"cannot handle isa %@. Selector: %@", object[@"isa"], NSStringFromSelector(method)]]; | ||
return NO; | ||
} | ||
|
||
//if it doesnt respond to the method, log it and continue for now | ||
if(![self respondsToSelector:method]) { | ||
NSLog(@"Warn: dont handle %@", NSStringFromSelector(method)); | ||
continue; | ||
} | ||
|
||
//call the reflected method | ||
#pragma clang diagnostic push | ||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks" | ||
NSError *suberror = [self performSelector:method withObject:object]; | ||
#pragma clang diagnostic pop | ||
|
||
//exit on error | ||
if(suberror) { | ||
*pError = suberror; | ||
return NO; | ||
} | ||
} | ||
|
||
//check mandatory... 1 file at least | ||
if( !_mutableFilesBuffer.count ) { | ||
*pError = [NSError errorWithCode:1 description:@"DDXcodeProjectFile can't be parsed." reason:@"cannot find manatory attributes in plist from file contents"]; | ||
return NO; | ||
} | ||
|
||
//resolve all | ||
NSError *suberror = [self resolveFiles:_mutableFilesBuffer]; | ||
if(suberror) { | ||
*pError = suberror; | ||
return NO; | ||
} | ||
|
||
//save buffer to property | ||
self.files = [NSArray arrayWithArray:_mutableFilesBuffer]; | ||
|
||
return YES; | ||
} | ||
|
||
- (SEL)methodNameForIsa:(NSString*)pbxIsa { | ||
NSString *name = [NSString stringWithFormat:@"parse%@:", pbxIsa]; | ||
return NSSelectorFromString(name); | ||
} | ||
|
||
- (NSError*)resolveFiles:(NSMutableArray*)files { | ||
|
||
return nil; | ||
} | ||
|
||
#pragma mark - parse individual objects | ||
|
||
//- (NSError*)parsePBXBuildFile:(NSDictionary*)dict { | ||
// return nil; | ||
//} | ||
//- (NSError*)parsePBXSourcesBuildPhase:(NSDictionary*)dict { | ||
// return nil; | ||
//} | ||
|
||
// we only care about those for now | ||
|
||
- (NSError*)parsePBXProject:(NSDictionary*)dict { | ||
if(!dict[@"attributes"]) { | ||
return [NSError errorWithCode:5 | ||
description:@"Can't parse project." | ||
reason:[NSString stringWithFormat:@"PBXProject without attributes: %@", dict]]; | ||
} | ||
|
||
self.classPrefix = dict[@"attributes"][@"CLASSPREFIX"]; | ||
self.company = dict[@"attributes"][@"ORGANIZATIONNAME"]; | ||
self.developmentRegion = dict[@"developmentRegion"]; | ||
self.minimumVersion = dict[@"compatibilityVersion"]; | ||
|
||
if(dict[@"projectRoot"]) { | ||
if(self.path.length) | ||
self.projectRoot = [self.path.stringByDeletingLastPathComponent.stringByDeletingLastPathComponent stringByAppendingPathComponent:dict[@"projectRoot"]]; | ||
else | ||
self.projectRoot = dict[@"projectRoot"]; | ||
} | ||
|
||
if(!self.classPrefix || !self.company || !self.developmentRegion || !self.minimumVersion || !self.projectRoot) { | ||
return [NSError errorWithCode:5 | ||
description:@"Can't parse project." | ||
reason:[NSString stringWithFormat:@"PBXProject missing mandatory attributes: %@", dict]]; | ||
} | ||
|
||
return nil; | ||
} | ||
|
||
- (NSError*)parsePBXFileReference:(NSDictionary*)dict { | ||
if(!dict[@"lastKnownFileType"] && dict[@"explicitFileType"]) { | ||
NSMutableDictionary *mdict = dict.mutableCopy; | ||
mdict[@"lastKnownFileType"] = dict[@"explicitFileType"]; | ||
dict = mdict; | ||
} | ||
|
||
if(!dict[@"path"] || !dict[@"lastKnownFileType"]) { | ||
return [NSError errorWithCode:5 | ||
description:@"Can't parse project." | ||
reason:[NSString stringWithFormat:@"PBXFileReference without path/type: %@", dict]]; | ||
} | ||
|
||
//add the path - TO BE resolved later :D | ||
[_mutableFilesBuffer addObject:@{@"path": dict[@"path"], @"type": dict[@"lastKnownFileType"]}]; | ||
|
||
return nil; //no error | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters