mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 21:17:03 +00:00
XcodeCapp now uses both .m and .h shadow files in addition of the general include .h. Seems to fix most of the problems + refactoring
This commit is contained in:
+289
-255
@@ -39,8 +39,8 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
@synthesize supportFileLevelAPI;
|
||||
@synthesize isUsingFileLevelAPI;
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization
|
||||
|
||||
#pragma mark - Initialization
|
||||
|
||||
/*!
|
||||
Initialize the AppController
|
||||
@@ -48,7 +48,7 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
if (self)
|
||||
{
|
||||
errorList = [NSMutableArray arrayWithCapacity:10];
|
||||
@@ -57,7 +57,7 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
parserPath = [[NSBundle mainBundle] pathForResource:@"parser" ofType:@"j"];
|
||||
lastEventId = [[NSUserDefaults standardUserDefaults] objectForKey:@"lastEventId"];
|
||||
appStartedTimestamp = [NSDate date];
|
||||
|
||||
|
||||
[self setIsListening:NO];
|
||||
[self setIsUsingFileLevelAPI:NO];
|
||||
|
||||
@@ -65,11 +65,11 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
SInt32 versionMinor = 0;
|
||||
Gestalt(gestaltSystemVersionMajor, &versionMajor);
|
||||
Gestalt(gestaltSystemVersionMinor, &versionMinor);
|
||||
|
||||
|
||||
[self setSupportFileLevelAPI:versionMajor >= 10 && versionMinor >= 7];
|
||||
// Uncomment to simulate 10.6 mode
|
||||
// [self setSupportFileLevelAPI:NO];
|
||||
|
||||
|
||||
[self configure];
|
||||
|
||||
if([fm fileExistsAtPath:[@"~/.bash_profile" stringByExpandingTildeInPath]])
|
||||
@@ -91,21 +91,21 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
profilePath = @"";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)start
|
||||
{
|
||||
{
|
||||
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"XCCReopenLastProject"])
|
||||
return;
|
||||
|
||||
NSString *lastOpenedPath = [[NSUserDefaults standardUserDefaults] objectForKey:@"LastOpenedPath"];
|
||||
|
||||
|
||||
if (lastOpenedPath)
|
||||
{
|
||||
if ([fm fileExistsAtPath:lastOpenedPath])
|
||||
{
|
||||
{
|
||||
[self listenProjectAtPath:[NSString stringWithFormat:@"%@/", lastOpenedPath]];
|
||||
}
|
||||
else
|
||||
@@ -115,6 +115,135 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Project Management
|
||||
|
||||
/*!
|
||||
Check if .XcodeSupport needs to be initialized.
|
||||
If not needed, check that all J files are mirrored. If no,
|
||||
then launch conversion for missing mirrored h files
|
||||
@return YES or NO
|
||||
*/
|
||||
- (BOOL)prepareXCodeSupportProject
|
||||
{
|
||||
XCodeSupportProjectName = [NSString stringWithFormat:@"%@.xcodeproj/", currentProjectName];
|
||||
XCodeTemplatePBXPath = [[NSBundle mainBundle] pathForResource:@"project.pbxproj" ofType:@"sample"];
|
||||
XCodeSupportProject = [NSURL URLWithString:XCodeSupportProjectName relativeToURL:currentProjectURL];
|
||||
XCodeSupportProjectSources = [NSURL URLWithString:@".XcodeSupport/" relativeToURL:currentProjectURL];
|
||||
XCodeSupportPBXPath = [NSString stringWithFormat:@"%@/project.pbxproj", [XCodeSupportProject path]];
|
||||
PBXModifierScriptPath = [[NSBundle mainBundle] pathForResource:@"pbxprojModifier" ofType:@"py"];
|
||||
|
||||
|
||||
//[fm removeItemAtURL:XCodeSupportProjectSources error:nil];
|
||||
//[fm removeItemAtURL:XCodeSupportProject error:nil];
|
||||
|
||||
// create the template project if it doesn't exist
|
||||
if (![fm fileExistsAtPath:[XCodeSupportProjectSources path]])
|
||||
{
|
||||
NSLog(@"prepareXCodeSupportProject: Xcode support folder created at: %@", [XCodeSupportProject path]);
|
||||
[fm createDirectoryAtPath:[XCodeSupportProject path] withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
|
||||
DLog(@"prepareXCodeSupportProject: Copying project.pbxproj from %@ to %@", XCodeTemplatePBXPath, [XCodeSupportProject path]);
|
||||
[fm copyItemAtPath:XCodeTemplatePBXPath toPath:XCodeSupportPBXPath error:nil];
|
||||
|
||||
DLog(@"prepareXCodeSupportProject: Reading the content of the project.pbxproj");
|
||||
NSMutableString *PBXContent = [NSMutableString stringWithContentsOfFile:XCodeSupportPBXPath encoding:NSUTF8StringEncoding error:nil];
|
||||
|
||||
[PBXContent writeToFile:XCodeSupportPBXPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
||||
DLog(@"prepareXCodeSupportProject: PBX file adapted to the project");
|
||||
|
||||
[self createXcodeSupportProjectSourcesDirIfNecessary];
|
||||
return NO;
|
||||
}
|
||||
|
||||
[self createXcodeSupportProjectSourcesDirIfNecessary];
|
||||
return YES;
|
||||
}
|
||||
|
||||
/*!
|
||||
Create the .XcodeSupport/Sources folder if necessary.
|
||||
*/
|
||||
- (void)createXcodeSupportProjectSourcesDirIfNecessary
|
||||
{
|
||||
if ([fm fileExistsAtPath:[XCodeSupportProjectSources path]])
|
||||
return;
|
||||
|
||||
DLog(@"createXcodeSupportProjectSourcesDirIfNecessary: Creating source folder %@", [XCodeSupportProjectSources path]);
|
||||
[fm createDirectoryAtPath:[XCodeSupportProjectSources path] withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initialize the creation of the .XcodeSupport project. This
|
||||
Operation is threaded
|
||||
@param arguments Thread arguments (not used)
|
||||
@param shouldNotify is YES, XCCDidPopulateProjectNotification will be send
|
||||
*/
|
||||
- (void)populateXCodeProject:(NSNumber *)shouldNotify
|
||||
{
|
||||
if ([shouldNotify boolValue])
|
||||
[delegate performSelector:@selector(growlWithTitle:message:) withObject:@"Loading project" withObject:[currentProjectURL path]];
|
||||
|
||||
NSArray *subdpaths = [fm subpathsAtPath:[currentProjectURL path]];
|
||||
|
||||
for (NSString *p in subdpaths)
|
||||
{
|
||||
NSString *filePath = [NSString stringWithFormat:@"%@/%@", [currentProjectURL path], p];
|
||||
|
||||
BOOL isDir = NO;
|
||||
[fm fileExistsAtPath:filePath isDirectory:&isDir];
|
||||
|
||||
if (isDir || ![self isObjJFile:filePath] || [self isPathMatchingIgnoredPaths:filePath])
|
||||
continue;
|
||||
|
||||
NSURL *eventualShadow = [self shadowHeaderURLForSourceURL:[NSURL fileURLWithPath:filePath]];
|
||||
|
||||
if (![fm fileExistsAtPath:[eventualShadow path]])
|
||||
{
|
||||
DLog(@"populateXCodeProject: Computing missing shadow file for %@", filePath);
|
||||
[self handleFileModification:filePath notify:NO];
|
||||
}
|
||||
}
|
||||
|
||||
if ([shouldNotify boolValue])
|
||||
{
|
||||
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:currentProjectURL, @"URL", nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:XCCDidPopulateProjectNotification object:self userInfo:info];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Start all needed processes for listening to a given path
|
||||
@param path The folder path to listen to
|
||||
*/
|
||||
- (void)listenProjectAtPath:(NSString *)path
|
||||
{
|
||||
NSMutableString *tempName = [NSMutableString stringWithString:[path lastPathComponent]];
|
||||
|
||||
currentProjectURL = [NSURL fileURLWithPath:path];
|
||||
|
||||
[tempName replaceOccurrencesOfString:@" "
|
||||
withString:@"_"
|
||||
options:NSCaseInsensitiveSearch
|
||||
range:NSMakeRange(0, [tempName length])];
|
||||
currentProjectName = [NSString stringWithString:tempName];
|
||||
|
||||
[self computeIgnoredPaths];
|
||||
|
||||
BOOL isProjectReady = [self prepareXCodeSupportProject];
|
||||
|
||||
[NSThread detachNewThreadSelector:@selector(populateXCodeProject:) toTarget:self withObject:[NSNumber numberWithBool:!isProjectReady]];
|
||||
|
||||
[self initializeEventStreamWithPath:[currentProjectURL path]];
|
||||
|
||||
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:path, @"path", [NSNumber numberWithInt:(isProjectReady) ? 1 : 0], @"ready", nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:XCCListeningStartNotification object:self userInfo:info];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setObject:[currentProjectURL path] forKey:@"LastOpenedPath"];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Event Stream
|
||||
|
||||
/*!
|
||||
Initializes the FSEvent stream
|
||||
@param aPath the path of the folder to listen
|
||||
@@ -123,18 +252,18 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
{
|
||||
if ([self isListening])
|
||||
return;
|
||||
|
||||
|
||||
[self stopEventStream];
|
||||
|
||||
|
||||
NSMutableArray *pathsToWatch = [NSMutableArray arrayWithObject:aPath];
|
||||
void *appPointer = (void *)self;
|
||||
FSEventStreamContext context = {0, appPointer, NULL, NULL, NULL};
|
||||
CFTimeInterval latency = 2.0;
|
||||
FSEventStreamCreateFlags flags = 0;
|
||||
|
||||
|
||||
if (supportsFileBasedListening)
|
||||
{
|
||||
DLog(@"Initializing the FSEventStream at file level (clean)");
|
||||
DLog(@"initializeEventStreamWithPath: Initializing the FSEventStream at file level (clean)");
|
||||
flags = kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents;
|
||||
}
|
||||
else
|
||||
@@ -142,10 +271,10 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
NSLog(@"Initializing the FSEventStream at folder level (dirty)");
|
||||
flags = kFSEventStreamCreateFlagUseCFTypes;
|
||||
}
|
||||
|
||||
|
||||
// add symlinked directories
|
||||
NSArray *fileList = [fm contentsOfDirectoryAtPath:aPath error:nil];
|
||||
|
||||
|
||||
for (NSString *node in fileList)
|
||||
{
|
||||
NSDictionary *attributes = [fm attributesOfItemAtPath:aPath error:nil];
|
||||
@@ -160,19 +289,15 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
stream = FSEventStreamCreate(NULL, &fsevents_callback, &context, (CFArrayRef) pathsToWatch,
|
||||
[lastEventId unsignedLongLongValue], latency, flags);
|
||||
|
||||
|
||||
FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
FSEventStreamStart(stream);
|
||||
[self setIsListening:YES];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Utilities
|
||||
|
||||
/*!
|
||||
Stop listening the FSEvent stream if active
|
||||
*/
|
||||
@@ -210,7 +335,7 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
{
|
||||
if (![self supportFileLevelAPI])
|
||||
{
|
||||
DLog(@"System doesn't support file level API");
|
||||
DLog(@"configure: System doesn't support file level API");
|
||||
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:2] forKey:@"XCCAPIMode"];
|
||||
}
|
||||
|
||||
@@ -229,7 +354,7 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
|
||||
if (supportsFileBasedListening)
|
||||
{
|
||||
DLog(@"using 10.7+ mode listening (clean)");
|
||||
DLog(@"configure: using 10.7+ mode listening (clean)");
|
||||
|
||||
[self setCurrentAPIMode:@"File level (Lion)"];
|
||||
[self setIsUsingFileLevelAPI:YES];
|
||||
@@ -237,7 +362,7 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
}
|
||||
else
|
||||
{
|
||||
DLog(@"using 10.6 mode listening (dirty)");
|
||||
DLog(@"configure: using 10.6 mode listening (dirty)");
|
||||
reactToInodeModification = NO;
|
||||
[self setCurrentAPIMode:@"Folder level (Snow Leopard)"];
|
||||
[self setIsUsingFileLevelAPI:NO];
|
||||
@@ -273,6 +398,9 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Shell Helpers
|
||||
|
||||
/*!
|
||||
Run a NSTask with the given arguments
|
||||
@param arguments NSArray containing the NSTask arguments
|
||||
@@ -284,22 +412,25 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
NSData *stdOut;
|
||||
NSString *response;
|
||||
NSNumber *status;
|
||||
|
||||
|
||||
task = [[NSTask alloc] init];
|
||||
|
||||
|
||||
[task setLaunchPath: @"/bin/bash"];
|
||||
[task setArguments: arguments];
|
||||
[task setStandardOutput:[NSPipe pipe]];
|
||||
[task launch];
|
||||
[task waitUntilExit];
|
||||
|
||||
|
||||
stdOut = [[[task standardOutput] fileHandleForReading] availableData];
|
||||
response = [[NSString alloc] initWithData:stdOut encoding:NSUTF8StringEncoding];
|
||||
status = [NSNumber numberWithInt:[task terminationStatus]];
|
||||
|
||||
|
||||
return [NSArray arrayWithObjects:status, response, nil];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Event Handlers
|
||||
|
||||
/*!
|
||||
Handle a file modification. If it's a .J or XIB or NIB, it will
|
||||
perform the according conversion. If it's .xcodecapp-ignore, it will
|
||||
@@ -315,7 +446,7 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
if ([self isPathMatchingIgnoredPaths:fullPath] || ![fm fileExistsAtPath:fullPath])
|
||||
return;
|
||||
|
||||
DLog(@"Parsing modified file: %@", fullPath);
|
||||
DLog(@"handleFileModification:notify: Parsing modified file: %@", fullPath);
|
||||
|
||||
NSArray *arguments = nil;
|
||||
NSArray *PBXArguments = nil;
|
||||
@@ -324,16 +455,19 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
NSString *response = nil;
|
||||
NSNumber *status = [NSNumber numberWithInt:0];
|
||||
NSString *splitPath = [fullPath substringFromIndex:[[currentProjectURL path] length] + 1];
|
||||
NSString *shadowPath = [[self shadowURLForSourceURL:[NSURL URLWithString:[fullPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]] path];
|
||||
|
||||
DLog(@"Shadow path: %@", shadowPath);
|
||||
|
||||
NSURL *encodedURL = [NSURL URLWithString:[fullPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
|
||||
NSURL *shadowHeaderURL = [self shadowHeaderURLForSourceURL:encodedURL];
|
||||
NSURL *shadowImplementationURL = [self shadowImplementationURLForSourceURL:encodedURL];
|
||||
|
||||
DLog(@"handleFileModification:notify: Shadow header path: %@", shadowHeaderURL);
|
||||
DLog(@"handleFileModification:notify: Shadow implementation path: %@", shadowImplementationURL);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:XCCConversionStartNotification object:self];
|
||||
|
||||
if ([self isXIBFile:fullPath])
|
||||
{
|
||||
arguments = [NSArray arrayWithObjects: @"-c",
|
||||
[NSString stringWithFormat:@"(%@; nib2cib '%@';) 2>&1", profilePath, fullPath],@"",nil];
|
||||
[NSString stringWithFormat:@"(%@; nib2cib '%@';) 2>&1", profilePath, fullPath],@"",nil];
|
||||
|
||||
successTitle = @"XIB converted";
|
||||
successMsg = splitPath;
|
||||
@@ -341,19 +475,20 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
else if ([self isObjJFile:fullPath])
|
||||
{
|
||||
arguments = [NSArray arrayWithObjects: @"-c",
|
||||
[NSString stringWithFormat:@"(%@; objj '%@' '%@' '%@';) 2>&1",
|
||||
profilePath,
|
||||
parserPath,
|
||||
fullPath,
|
||||
shadowPath],
|
||||
@"",nil];
|
||||
[NSString stringWithFormat:@"(%@; objj '%@' '%@' '%@' '%@';) 2>&1",
|
||||
profilePath,
|
||||
parserPath,
|
||||
fullPath,
|
||||
[shadowHeaderURL path],
|
||||
[shadowImplementationURL path]],nil];
|
||||
|
||||
PBXArguments = [NSArray arrayWithObjects: @"-c",
|
||||
[NSString stringWithFormat:@"(%@; python %@ add '%@' '%@' '%@' '%@') 2>&1",
|
||||
[NSString stringWithFormat:@"(%@; python %@ add '%@' '%@' '%@' '%@' '%@') 2>&1",
|
||||
profilePath,
|
||||
PBXModifierScriptPath,
|
||||
XCodeSupportPBXPath,
|
||||
shadowPath,
|
||||
[shadowHeaderURL path],
|
||||
[shadowImplementationURL path],
|
||||
fullPath,
|
||||
[currentProjectURL path]],nil];
|
||||
|
||||
@@ -371,22 +506,22 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
// Run the task and get the response if needed
|
||||
if (arguments)
|
||||
{
|
||||
DLog(@"Running conversion task...");
|
||||
DLog(@"handleFileModification:notify: Running conversion task...");
|
||||
NSArray *statusInfo = [self runTask:arguments];
|
||||
|
||||
|
||||
status = [statusInfo objectAtIndex:0];
|
||||
response = [statusInfo objectAtIndex:1];
|
||||
|
||||
DLog(@"Conversion task result/response: %@/%@", status, response);
|
||||
|
||||
DLog(@"handleFileModification:notify: Conversion task result/response: %@/%@", status, response);
|
||||
}
|
||||
|
||||
if (PBXArguments)
|
||||
{
|
||||
DLog(@"Running update PBX task...");
|
||||
DLog(@"handleFileModification:notify: Running update PBX task...");
|
||||
NSArray *statusInfo = [self runTask:PBXArguments];
|
||||
status = [statusInfo objectAtIndex:0];
|
||||
response = [statusInfo objectAtIndex:1];
|
||||
DLog(@"Update PBX Task result/response: %@/%@", status, response);
|
||||
DLog(@"handleFileModification:notify: Update PBX Task result/response: %@/%@", status, response);
|
||||
}
|
||||
|
||||
if ([status intValue] == 0 && shouldNotify)
|
||||
@@ -397,12 +532,12 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
{
|
||||
if (response)
|
||||
[errorList addObject:response];
|
||||
|
||||
|
||||
[delegate performSelector:@selector(growlWithTitle:message:) withObject:@"Error processing file" withObject:splitPath];
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:XCCConversionStopNotification object:self];
|
||||
DLog(@"Processed: %@", fullPath);
|
||||
DLog(@"handleFileModification:notify: Processed: %@", fullPath);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -416,37 +551,18 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
{
|
||||
if ([self isPathMatchingIgnoredPaths:fullPath] || [fm fileExistsAtPath:fullPath])
|
||||
return;
|
||||
|
||||
|
||||
NSString *encodedPath = [fullPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
if ([self isObjJFile:fullPath])
|
||||
{
|
||||
NSString *shadowPath = [[self shadowURLForSourceURL:[NSURL URLWithString:[fullPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]] path];
|
||||
|
||||
DLog(@"Removing shadow file: %@", shadowPath);
|
||||
[fm removeItemAtPath:shadowPath error:nil];
|
||||
|
||||
DLog(@"Removing PBX reference task...");
|
||||
|
||||
#if DEBUG
|
||||
NSArray *PBXArguments = [NSArray arrayWithObjects: @"-c",
|
||||
[NSString stringWithFormat:@"(%@; python %@ remove '%@' '%@' '%@' '%@') 2>&1",
|
||||
profilePath,
|
||||
PBXModifierScriptPath,
|
||||
XCodeSupportPBXPath,
|
||||
shadowPath,
|
||||
fullPath,
|
||||
[currentProjectURL path]],nil];
|
||||
NSArray *statusInfo = [self runTask:PBXArguments];
|
||||
NSNumber *status = [statusInfo objectAtIndex:0];
|
||||
NSString *response = [statusInfo objectAtIndex:1];
|
||||
#endif
|
||||
DLog(@"PBX Reference removal status/response: %@/%@", status, response);
|
||||
}
|
||||
[self cleanUpShadowsRelatedToSourceURL:[NSURL URLWithString:encodedPath]];
|
||||
else if ([self isXCCIgnoreFile:fullPath])
|
||||
{
|
||||
[self computeIgnoredPaths];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Source Files Management
|
||||
|
||||
/*!
|
||||
Check if given full path is Objective-J file
|
||||
@param path the path to check
|
||||
@@ -480,111 +596,20 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Check if .XcodeSupport needs to be initialized.
|
||||
If not needed, check that all J files are mirrored. If no,
|
||||
then launch conversion for missing mirrored h files
|
||||
@return YES or NO
|
||||
*/
|
||||
- (BOOL)prepareXCodeSupportProject
|
||||
{
|
||||
XCodeSupportProjectName = [NSString stringWithFormat:@"%@.xcodeproj/", currentProjectName];
|
||||
XCodeTemplatePBXPath = [[NSBundle mainBundle] pathForResource:@"project.pbxproj" ofType:@"sample"];
|
||||
XCodeSupportProject = [NSURL URLWithString:XCodeSupportProjectName relativeToURL:currentProjectURL];
|
||||
XCodeSupportProjectSources = [NSURL URLWithString:@".XcodeSupport/" relativeToURL:currentProjectURL];
|
||||
XCodeSupportPBXPath = [NSString stringWithFormat:@"%@/project.pbxproj", [XCodeSupportProject path]];
|
||||
PBXModifierScriptPath = [[NSBundle mainBundle] pathForResource:@"pbxprojModifier" ofType:@"py"];
|
||||
|
||||
|
||||
//[fm removeItemAtURL:XCodeSupportProjectSources error:nil];
|
||||
//[fm removeItemAtURL:XCodeSupportProject error:nil];
|
||||
|
||||
// create the template project if it doesn't exist
|
||||
if (![fm fileExistsAtPath:[XCodeSupportProjectSources path]])
|
||||
{
|
||||
NSLog(@"Xcode support folder created at: %@", [XCodeSupportProject path]);
|
||||
[fm createDirectoryAtPath:[XCodeSupportProject path] withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
|
||||
DLog(@"Copying project.pbxproj from %@ to %@", XCodeTemplatePBXPath, [XCodeSupportProject path]);
|
||||
[fm copyItemAtPath:XCodeTemplatePBXPath toPath:XCodeSupportPBXPath error:nil];
|
||||
|
||||
DLog(@"Reading the content of the project.pbxproj");
|
||||
NSMutableString *PBXContent = [NSMutableString stringWithContentsOfFile:XCodeSupportPBXPath encoding:NSUTF8StringEncoding error:nil];
|
||||
|
||||
[PBXContent writeToFile:XCodeSupportPBXPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
||||
DLog(@"PBX file adapted to the project");
|
||||
|
||||
[self createXcodeSupportProjectSourcesDirIfNecessary];
|
||||
return NO;
|
||||
}
|
||||
|
||||
[self createXcodeSupportProjectSourcesDirIfNecessary];
|
||||
return YES;
|
||||
}
|
||||
#pragma mark - Shadow Files Management
|
||||
|
||||
/*!
|
||||
Create the .XcodeSupport/Sources folder if necessary.
|
||||
*/
|
||||
- (void)createXcodeSupportProjectSourcesDirIfNecessary
|
||||
{
|
||||
if ([fm fileExistsAtPath:[XCodeSupportProjectSources path]])
|
||||
return;
|
||||
|
||||
DLog(@"Creating source folder %@", [XCodeSupportProjectSources path]);
|
||||
[fm createDirectoryAtPath:[XCodeSupportProjectSources path] withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initialize the creation of the .XcodeSupport project. This
|
||||
Operation is threaded
|
||||
@param arguments Thread arguments (not used)
|
||||
@param shouldNotify is YES, XCCDidPopulateProjectNotification will be send
|
||||
*/
|
||||
- (void)populateXCodeProject:(NSNumber *)shouldNotify
|
||||
{
|
||||
if ([shouldNotify boolValue])
|
||||
[delegate performSelector:@selector(growlWithTitle:message:) withObject:@"Loading project" withObject:[currentProjectURL path]];
|
||||
|
||||
NSArray *subdpaths = [fm subpathsAtPath:[currentProjectURL path]];
|
||||
|
||||
for (NSString *p in subdpaths)
|
||||
{
|
||||
NSString *filePath = [NSString stringWithFormat:@"%@/%@", [currentProjectURL path], p];
|
||||
|
||||
BOOL isDir = NO;
|
||||
[fm fileExistsAtPath:filePath isDirectory:&isDir];
|
||||
|
||||
if (isDir || ![self isObjJFile:filePath] || [self isPathMatchingIgnoredPaths:filePath])
|
||||
continue;
|
||||
|
||||
NSURL *eventualShadow = [self shadowURLForSourceURL:[NSURL fileURLWithPath:filePath]];
|
||||
|
||||
if (![fm fileExistsAtPath:[eventualShadow path]])
|
||||
{
|
||||
DLog(@"Computing missing shadow file for %@", filePath);
|
||||
[self handleFileModification:filePath notify:NO];
|
||||
}
|
||||
}
|
||||
|
||||
if ([shouldNotify boolValue])
|
||||
{
|
||||
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:currentProjectURL, @"URL", nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:XCCDidPopulateProjectNotification object:self userInfo:info];
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Compute the mirorred (shadow) name for a given path
|
||||
Compute the mirorred (shadow) header file name for a given path
|
||||
@param aSourceURL the origin path
|
||||
@return NSURL representing the shadow path
|
||||
@return NSURL representing the shadow URL for the header file
|
||||
*/
|
||||
- (NSURL *)shadowURLForSourceURL:(NSURL*)aSourceURL
|
||||
- (NSURL *)shadowHeaderURLForSourceURL:(NSURL*)aSourceURL
|
||||
{
|
||||
if (!aSourceURL)
|
||||
[NSException raise:NSInvalidArgumentException format:@"shadowURLForSourceURL: aSource URL must not be null"];
|
||||
[NSException raise:NSInvalidArgumentException format:@"shadowHeaderURLForSourceURL: aSource URL must not be null"];
|
||||
|
||||
NSMutableString *flattenedPath = [NSMutableString stringWithString:[aSourceURL path]];
|
||||
|
||||
|
||||
// Replace "_" with a substring that is unlikely to be in a filename
|
||||
[flattenedPath replaceOccurrencesOfString:@"_"
|
||||
withString:@"≤‹°∞°›≥"
|
||||
@@ -596,12 +621,33 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
options:0
|
||||
range:NSMakeRange(0, [flattenedPath length])];
|
||||
|
||||
DLog(@"Flattened path: %@", flattenedPath);
|
||||
NSString *basename = [NSString stringWithFormat:@"%@.m", [[flattenedPath stringByDeletingPathExtension] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
DLog(@"shadowHeaderURLForSourceURL: Flattened path: %@", flattenedPath);
|
||||
NSString *basename = [NSString stringWithFormat:@"%@.h", [[flattenedPath stringByDeletingPathExtension] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
return [NSURL URLWithString:basename relativeToURL:XCodeSupportProjectSources];
|
||||
}
|
||||
|
||||
/*!
|
||||
Compute the mirorred (shadow) implementation file name for a given path
|
||||
@param aSourceURL the origin path
|
||||
@return NSURL representing the shadow URL for the implementation file
|
||||
*/
|
||||
- (NSURL *)shadowImplementationURLForSourceURL:(NSURL*)aSourceURL
|
||||
{
|
||||
if (!aSourceURL)
|
||||
[NSException raise:NSInvalidArgumentException format:@"shadowImplementationURLForSourceURL: aSource URL must not be null"];
|
||||
|
||||
NSURL *shadowHeaderURL = [self shadowHeaderURLForSourceURL:aSourceURL];
|
||||
NSURL *shadowImplPath = [[shadowHeaderURL URLByDeletingPathExtension] URLByAppendingPathExtension:@"m"];
|
||||
|
||||
return shadowImplPath;
|
||||
}
|
||||
|
||||
/*!
|
||||
Compute the Cappuccino source file that is related to the given shadow header file URL
|
||||
@param aString the origin path
|
||||
@return NSURL representing the URL for the related Cappuccino source file
|
||||
*/
|
||||
- (NSURL *)sourceURLForShadowName:(NSString *)aString
|
||||
{
|
||||
NSMutableString * unshadowedPath = [NSMutableString stringWithString:aString];
|
||||
@@ -612,11 +658,11 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
range:NSMakeRange(0, [unshadowedPath length])];
|
||||
|
||||
[unshadowedPath replaceOccurrencesOfString:@"≤‹°∞°›≥"
|
||||
withString:@"_"
|
||||
options:0
|
||||
range:NSMakeRange(0, [unshadowedPath length])];
|
||||
withString:@"_"
|
||||
options:0
|
||||
range:NSMakeRange(0, [unshadowedPath length])];
|
||||
|
||||
[unshadowedPath replaceOccurrencesOfString:@".m"
|
||||
[unshadowedPath replaceOccurrencesOfString:@".h"
|
||||
withString:@".j"
|
||||
options:0
|
||||
range:NSMakeRange(0, [unshadowedPath length])];
|
||||
@@ -624,6 +670,64 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
return [NSURL fileURLWithPath:[NSString stringWithString:unshadowedPath]];
|
||||
}
|
||||
|
||||
/*!
|
||||
Clean up any shadow files and PBX entries related to given the Cappuccino source file URL
|
||||
@param anURL the Cappuccino source file URL
|
||||
*/
|
||||
- (void)cleanUpShadowsRelatedToSourceURL:(NSURL *)anURL
|
||||
{
|
||||
NSURL *shadowHeaderURL = [self shadowHeaderURLForSourceURL:anURL];
|
||||
NSURL *shadowImplementationURL = [self shadowImplementationURLForSourceURL:anURL];
|
||||
|
||||
DLog(@"cleanUpShadowsRelatedToSourceURL: Removing shadow header file: %@", shadowHeaderURL);
|
||||
[fm removeItemAtURL:shadowHeaderURL error:nil];
|
||||
|
||||
DLog(@"cleanUpShadowsRelatedToSourceURL: Removing shadow implementation file: %@", shadowImplementationURL);
|
||||
[fm removeItemAtURL:shadowImplementationURL error:nil];
|
||||
|
||||
DLog(@"cleanUpShadowsRelatedToSourceURL:Removing PBX reference task...");
|
||||
NSArray *PBXArguments = [NSArray arrayWithObjects: @"-c",
|
||||
[NSString stringWithFormat:@"(%@; python %@ remove '%@' '%@' '%@' '%@' '%@') 2>&1",
|
||||
profilePath,
|
||||
PBXModifierScriptPath,
|
||||
XCodeSupportPBXPath,
|
||||
[shadowHeaderURL path],
|
||||
[shadowImplementationURL path],
|
||||
[anURL path],
|
||||
[currentProjectURL path]],nil];
|
||||
|
||||
NSArray *statusInfo = [self runTask:PBXArguments];
|
||||
NSNumber *status = [statusInfo objectAtIndex:0];
|
||||
NSString *response = [statusInfo objectAtIndex:1];
|
||||
DLog(@"cleanUpShadowsRelatedToSourceURL: PBX Reference removal status/response: %@/%@", status, response);
|
||||
}
|
||||
|
||||
/*!
|
||||
Clean the support folder according to files present in given path
|
||||
*/
|
||||
- (void)tidyShadowedFiles
|
||||
{
|
||||
NSArray *subpaths = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[XCodeSupportProjectSources path] error:NULL];
|
||||
|
||||
for (NSString *subpath in subpaths)
|
||||
{
|
||||
if ([subpath pathExtension] != @".h" || [subpath lastPathComponent] == @"xcc_general_include.h")
|
||||
continue;
|
||||
|
||||
NSURL *unshadowed = [self sourceURLForShadowName:subpath];
|
||||
|
||||
if (![fm fileExistsAtPath:[unshadowed path]])
|
||||
{
|
||||
[self cleanUpShadowsRelatedToSourceURL:unshadowed];
|
||||
|
||||
if (![self supportFileLevelAPI] && [self respondsToSelector:@selector(updateLastModificationDate:forPath:)])
|
||||
[self performSelector:@selector(updateLastModificationDate:forPath:) withObject:nil withObject:unshadowed];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - XCC Ignore management
|
||||
|
||||
/*!
|
||||
Compute the ignored paths according to any existing
|
||||
@@ -645,7 +749,7 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
[ignoredFilePaths addObject:pattern];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[ignoredFilePaths addObject:@"*/.git/*"];
|
||||
[ignoredFilePaths addObject:@"*/.svn/*"];
|
||||
[ignoredFilePaths addObject:@"*/.hg/*"];
|
||||
@@ -656,7 +760,7 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
[ignoredFilePaths addObject:@"*main.j"];
|
||||
[ignoredFilePaths addObject:@"*.xcodeproj/*"];
|
||||
[ignoredFilePaths addObject:@"*.DS_Store"];
|
||||
|
||||
|
||||
NSLog(@"Ignoring file paths: %@", ignoredFilePaths);
|
||||
}
|
||||
|
||||
@@ -706,76 +810,6 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
return NO;
|
||||
}
|
||||
|
||||
/*!
|
||||
Start all needed processes for listening to a given path
|
||||
@param path The folder path to listen to
|
||||
*/
|
||||
- (void)listenProjectAtPath:(NSString *)path
|
||||
{
|
||||
NSMutableString *tempName = [NSMutableString stringWithString:[path lastPathComponent]];
|
||||
|
||||
currentProjectURL = [NSURL fileURLWithPath:path];
|
||||
|
||||
[tempName replaceOccurrencesOfString:@" "
|
||||
withString:@"_"
|
||||
options:NSCaseInsensitiveSearch
|
||||
range:NSMakeRange(0, [tempName length])];
|
||||
currentProjectName = [NSString stringWithString:tempName];
|
||||
|
||||
[self computeIgnoredPaths];
|
||||
|
||||
BOOL isProjectReady = [self prepareXCodeSupportProject];
|
||||
|
||||
[NSThread detachNewThreadSelector:@selector(populateXCodeProject:) toTarget:self withObject:[NSNumber numberWithBool:!isProjectReady]];
|
||||
|
||||
[self initializeEventStreamWithPath:[currentProjectURL path]];
|
||||
|
||||
NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:path, @"path", [NSNumber numberWithInt:(isProjectReady) ? 1 : 0], @"ready", nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:XCCListeningStartNotification object:self userInfo:info];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setObject:[currentProjectURL path] forKey:@"LastOpenedPath"];
|
||||
}
|
||||
|
||||
/*!
|
||||
Clean the support folder according to files present in given path
|
||||
*/
|
||||
- (void)tidyShadowedFiles
|
||||
{
|
||||
NSArray *subpaths = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[XCodeSupportProjectSources path] error:NULL];
|
||||
|
||||
for (NSString *subpath in subpaths)
|
||||
{
|
||||
NSString *unshadowed = [[self sourceURLForShadowName:subpath] path];
|
||||
NSString *shadowFullPath = [NSString stringWithFormat:@"%@/%@", [XCodeSupportProjectSources path], subpath];
|
||||
|
||||
if (![fm fileExistsAtPath:unshadowed])
|
||||
{
|
||||
DLog(@"cleaning shadow file: %@", subpath);
|
||||
[fm removeItemAtPath:shadowFullPath error:nil];
|
||||
|
||||
#if DEBUG
|
||||
NSArray *PBXArguments = [NSArray arrayWithObjects: @"-c",
|
||||
[NSString stringWithFormat:@"(%@; python %@ remove '%@' '%@' '%@' '%@') 2>&1",
|
||||
profilePath,
|
||||
PBXModifierScriptPath,
|
||||
XCodeSupportPBXPath,
|
||||
shadowFullPath,
|
||||
unshadowed,
|
||||
[currentProjectURL path]],nil];
|
||||
|
||||
DLog(@"Cleaning PBX reference task...");
|
||||
NSArray *statusInfo = [self runTask:PBXArguments];
|
||||
NSNumber *status = [statusInfo objectAtIndex:0];
|
||||
NSString *response = [statusInfo objectAtIndex:1];
|
||||
#endif
|
||||
DLog(@"PBX Reference cleaning status/response: %@/%@", status, response);
|
||||
|
||||
if (![self supportFileLevelAPI] && [self respondsToSelector:@selector(updateLastModificationDate:forPath:)])
|
||||
[self performSelector:@selector(updateLastModificationDate:forPath:) withObject:nil withObject:unshadowed];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -786,7 +820,7 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
if (!pathModificationDates)
|
||||
{
|
||||
pathModificationDates = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"pathModificationDates"] mutableCopy];
|
||||
|
||||
|
||||
if (!pathModificationDates)
|
||||
pathModificationDates = [NSMutableDictionary new];
|
||||
}
|
||||
@@ -804,7 +838,7 @@ NSString * const XCCListeningStartNotification = @"XCCListeningStartNotification
|
||||
if (!pathModificationDates)
|
||||
{
|
||||
pathModificationDates = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:@"pathModificationDates"] mutableCopy];
|
||||
|
||||
|
||||
if (!pathModificationDates)
|
||||
pathModificationDates = [NSMutableDictionary new];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user