diff --git a/Applications/ycode/AppController.h b/Applications/ycode/AppController.h index e7010c0..729b9df 100644 --- a/Applications/ycode/AppController.h +++ b/Applications/ycode/AppController.h @@ -61,6 +61,14 @@ */ - (NSString *) getProjectNameFromUser; +/** + * Saves the active project. + */ +- (IBAction) saveDocument: (id)sender; +- (IBAction) saveDocumentAs: (id)sender; +- (IBAction) saveDocumentTo: (id)sender; +- (IBAction) saveAllDocuments: (id)sender; + /** * Determines whether the application should terminate. */ @@ -87,6 +95,12 @@ */ - (IBAction) openProject: (id)sender; +/** + * Standard document menu entry points. + */ +- (IBAction) newDocument: (id)sender; +- (IBAction) openDocument: (id)sender; + @end #endif diff --git a/Applications/ycode/AppController.m b/Applications/ycode/AppController.m index 2fd1d74..12621c4 100644 --- a/Applications/ycode/AppController.m +++ b/Applications/ycode/AppController.m @@ -45,10 +45,15 @@ - (void) dealloc - (void) awakeFromNib { + [self updateApplicationMenuName]; + [self connectDocumentMenuActions]; } - (void) applicationDidFinishLaunching: (NSNotification *)aNotif { + [self updateApplicationMenuName]; + [self connectDocumentMenuActions]; + // Create and show the main window if (!windowController) { windowController = [[YCodeWindowController alloc] init]; @@ -75,8 +80,9 @@ - (BOOL) applicationShouldTerminate: (id)sender if (result == NSAlertThirdButtonReturn) { return NO; // Cancel } else if (result == NSAlertFirstButtonReturn) { - // Save before quitting - // TODO: Implement save functionality + if (![self saveActiveProjectShowingPanel:NO]) { + return NO; + } } } @@ -112,6 +118,16 @@ - (IBAction) newProject: (id)sender } } +- (IBAction)newDocument:(id)sender +{ + [self newProject:sender]; +} + +- (IBAction)openDocument:(id)sender +{ + [self openProject:sender]; +} + - (void) createNewProjectAtURL:(NSURL *)projectURL { // Show project type selection dialog @@ -151,8 +167,7 @@ - (void) createNewProjectAtURL:(NSURL *)projectURL [windowController setProject:newProject]; [windowController showWindow:self]; - // Save the project immediately - [newProject saveProjectToPath:projectPath]; + [[NSDocumentController sharedDocumentController] addDocument:newProject]; } else { NSAlert *errorAlert = [[NSAlert alloc] init]; [errorAlert setMessageText:@"Project Creation Failed"]; @@ -163,6 +178,69 @@ - (void) createNewProjectAtURL:(NSURL *)projectURL } } +- (IBAction)saveDocument:(id)sender +{ + [self saveActiveProjectShowingPanel:NO]; +} + +- (IBAction)saveDocumentAs:(id)sender +{ + [self saveActiveProjectShowingPanel:YES]; +} + +- (IBAction)saveDocumentTo:(id)sender +{ + [self saveActiveProjectShowingPanel:YES]; +} + +- (IBAction)saveAllDocuments:(id)sender +{ + [self saveActiveProjectShowingPanel:NO]; +} + +- (BOOL)saveActiveProjectShowingPanel:(BOOL)showPanel +{ + YCodeProject *project = [windowController project]; + NSString *projectPath = [project projectPath]; + + if (project == nil) { + NSBeep(); + return NO; + } + + if (showPanel || projectPath == nil || [projectPath length] == 0) { + NSSavePanel *panel = [NSSavePanel savePanel]; + NSString *name = @"Project.xcodeproj"; + + if (projectPath != nil && [projectPath length] > 0) { + name = [projectPath lastPathComponent]; + } + + [panel setTitle:@"Save Project"]; + [panel setNameFieldStringValue:name]; + [panel setAllowedFileTypes:[NSArray arrayWithObject:@"xcodeproj"]]; + + if ([panel runModal] != NSModalResponseOK) { + return NO; + } + + projectPath = [[panel URL] path]; + } + + if (![project saveProjectToPath:projectPath]) { + NSAlert *alert = [[NSAlert alloc] init]; + [alert setMessageText:@"Unable to save project"]; + [alert setInformativeText:@"The project could not be saved to the selected location."]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + RELEASE(alert); + return NO; + } + + [[windowController window] setTitle:[[project projectPath] lastPathComponent]]; + return YES; +} + - (NSString *) getProjectNameFromUser { NSAlert *alert = [[NSAlert alloc] init]; @@ -212,6 +290,110 @@ - (BOOL) application: (NSApplication *)application return NO; } +- (void)updateApplicationMenuName +{ + NSString *applicationName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"ApplicationName"]; + NSMenu *mainMenu = [NSApp mainMenu]; + NSMenuItem *applicationItem = nil; + NSMenu *applicationMenu = nil; + + if (applicationName == nil || [applicationName length] == 0) { + applicationName = [[NSProcessInfo processInfo] processName]; + } + + if (mainMenu == nil || [[mainMenu itemArray] count] == 0) { + return; + } + + applicationItem = [[mainMenu itemArray] objectAtIndex:0]; + applicationMenu = [applicationItem submenu]; + + [applicationItem setTitle:applicationName]; + if (applicationMenu != nil) { + NSMenuItem *infoItem = nil; + + [applicationMenu setTitle:applicationName]; + if ([[applicationMenu itemArray] count] > 0) { + infoItem = [[applicationMenu itemArray] objectAtIndex:0]; + if ([[infoItem title] isEqualToString:@"Info"]) { + [infoItem setTitle:applicationName]; + [[infoItem submenu] setTitle:applicationName]; + } + } + + [self updateApplicationNameItemsInMenu:applicationMenu + appName:applicationName]; + } +} + +- (void)updateApplicationNameItemsInMenu:(NSMenu *)menu appName:(NSString *)applicationName +{ + NSEnumerator *enumerator = nil; + NSMenuItem *item = nil; + + if (menu == nil) { + return; + } + + enumerator = [[menu itemArray] objectEnumerator]; + while ((item = [enumerator nextObject]) != nil) { + if ([[item title] isEqualToString:@"Hide"]) { + [item setTitle:[NSString stringWithFormat:@"Hide %@", applicationName]]; + } else if ([[item title] isEqualToString:@"Quit"]) { + [item setTitle:[NSString stringWithFormat:@"Quit %@", applicationName]]; + } + + [self updateApplicationNameItemsInMenu:[item submenu] + appName:applicationName]; + } +} + +- (void)connectDocumentMenuActions +{ + NSMenu *mainMenu = [NSApp mainMenu]; + NSEnumerator *enumerator = nil; + NSMenuItem *item = nil; + + if (mainMenu == nil) { + return; + } + + enumerator = [[mainMenu itemArray] objectEnumerator]; + while ((item = [enumerator nextObject]) != nil) { + [self connectDocumentMenuActionsInMenu:[item submenu]]; + } +} + +- (void)connectDocumentMenuActionsInMenu:(NSMenu *)menu +{ + NSEnumerator *enumerator = nil; + NSMenuItem *item = nil; + + if (menu == nil) { + return; + } + + enumerator = [[menu itemArray] objectEnumerator]; + while ((item = [enumerator nextObject]) != nil) { + SEL action = [item action]; + + if (action == @selector(newDocument:)) { + [item setTarget:self]; + [item setAction:@selector(newProject:)]; + } else if (action == @selector(openDocument:)) { + [item setTarget:self]; + [item setAction:@selector(openProject:)]; + } else if (action == @selector(saveDocument:) || + action == @selector(saveDocumentAs:) || + action == @selector(saveDocumentTo:) || + action == @selector(saveAllDocuments:)) { + [item setTarget:self]; + } + + [self connectDocumentMenuActionsInMenu:[item submenu]]; + } +} + - (void) showPrefPanel: (id)sender { // TODO: Implement preferences panel diff --git a/Applications/ycode/GNUmakefile b/Applications/ycode/GNUmakefile index e7b0476..d2055d7 100644 --- a/Applications/ycode/GNUmakefile +++ b/Applications/ycode/GNUmakefile @@ -25,7 +25,7 @@ include $(GNUSTEP_MAKEFILES)/common.make VERSION = 0.1 PACKAGE_NAME = Ycode APP_NAME = Ycode -Ycode_APPLICATION_ICON = +Ycode_APPLICATION_ICON = Ycode.tiff # # Libraries and Frameworks @@ -41,6 +41,7 @@ ADDITIONAL_INCLUDE_DIRS += -I../XCode # Resource files # Ycode_RESOURCE_FILES = \ +Resources/Ycode.tiff \ Resources/Ycode.gorm \ diff --git a/Applications/ycode/Resources/Ycode.tiff b/Applications/ycode/Resources/Ycode.tiff index 2a5fee5..3a69445 100644 Binary files a/Applications/ycode/Resources/Ycode.tiff and b/Applications/ycode/Resources/Ycode.tiff differ diff --git a/Applications/ycode/YCodeBuildSystem.h b/Applications/ycode/YCodeBuildSystem.h index dc8f0fd..bb4edde 100644 --- a/Applications/ycode/YCodeBuildSystem.h +++ b/Applications/ycode/YCodeBuildSystem.h @@ -81,7 +81,7 @@ typedef enum { /** * Initialization */ -- (instancetype)initWithProject:(YCodeProject *)project; +- (id)initWithProject:(YCodeProject *)project; /** * Project association @@ -148,4 +148,4 @@ typedef enum { @end -#endif // _YCODEBUILDSYSTEM_H_ \ No newline at end of file +#endif // _YCODEBUILDSYSTEM_H_ diff --git a/Applications/ycode/YCodeBuildSystem.m b/Applications/ycode/YCodeBuildSystem.m index 071148a..d775f00 100644 --- a/Applications/ycode/YCodeBuildSystem.m +++ b/Applications/ycode/YCodeBuildSystem.m @@ -27,7 +27,7 @@ @implementation YCodeBuildSystem -- (instancetype)initWithProject:(YCodeProject *)project +- (id)initWithProject:(YCodeProject *)project { self = [super init]; if (self) { @@ -69,7 +69,8 @@ - (void)setupDefaultEnvironment // Add common build tools to PATH if needed NSString *path = [environment objectForKey:@"PATH"]; - NSArray *additionalPaths = @[@"/usr/local/bin", @"/opt/local/bin", @"/usr/bin", @"/bin"]; + NSArray *additionalPaths = [NSArray arrayWithObjects: + @"/usr/local/bin", @"/opt/local/bin", @"/usr/bin", @"/bin", nil]; for (NSString *additionalPath in additionalPaths) { if (![path containsString:additionalPath]) { @@ -321,7 +322,7 @@ - (BOOL)buildWithXcode - (BOOL)buildWithMake { - return [self runCommand:@"make" withArguments:@[]]; + return [self runCommand:@"make" withArguments:[NSArray array]]; } - (BOOL)buildWithGNUMake @@ -350,14 +351,14 @@ - (BOOL)buildWithCMake [fileManager createDirectoryAtPath:buildDir withIntermediateDirectories:YES attributes:nil error:nil]; // Configure - NSArray *configureArgs = @[@"-S", @".", @"-B", @"build"]; + NSArray *configureArgs = [NSArray arrayWithObjects:@"-S", @".", @"-B", @"build", nil]; if (![self runCommand:@"cmake" withArguments:configureArgs]) { return NO; } } // Build - NSArray *buildArgs = @[@"--build", @"build"]; + NSArray *buildArgs = [NSArray arrayWithObjects:@"--build", @"build", nil]; return [self runCommand:@"cmake" withArguments:buildArgs]; } @@ -380,17 +381,18 @@ - (BOOL)cleanWithXcode - (BOOL)cleanWithMake { - return [self runCommand:@"make" withArguments:@[@"clean"]]; + return [self runCommand:@"make" withArguments:[NSArray arrayWithObject:@"clean"]]; } - (BOOL)cleanWithGNUMake { - return [self runCommand:@"make" withArguments:@[@"clean"]]; + return [self runCommand:@"make" withArguments:[NSArray arrayWithObject:@"clean"]]; } - (BOOL)cleanWithCMake { - return [self runCommand:@"cmake" withArguments:@[@"--build", @"build", @"--target", @"clean"]]; + return [self runCommand:@"cmake" withArguments: + [NSArray arrayWithObjects:@"--build", @"build", @"--target", @"clean", nil]]; } - (BOOL)testWithXcode @@ -412,7 +414,7 @@ - (BOOL)testWithXcode - (BOOL)testWithMake { - return [self runCommand:@"make" withArguments:@[@"test"]]; + return [self runCommand:@"make" withArguments:[NSArray arrayWithObject:@"test"]]; } - (BOOL)archiveWithXcode @@ -434,7 +436,7 @@ - (BOOL)archiveWithXcode - (BOOL)archiveWithMake { - return [self runCommand:@"make" withArguments:@[@"install"]]; + return [self runCommand:@"make" withArguments:[NSArray arrayWithObject:@"install"]]; } #pragma mark - Command Execution @@ -498,13 +500,12 @@ - (NSString *)findExecutable { // Look for built executable in common locations NSString *projectDir = [_project projectDirectoryPath]; - NSArray *searchPaths = @[ + NSArray *searchPaths = [NSArray arrayWithObjects: [projectDir stringByAppendingPathComponent:@"build/Debug"], [projectDir stringByAppendingPathComponent:@"build/Release"], [projectDir stringByAppendingPathComponent:@"build"], [projectDir stringByAppendingPathComponent:@"obj"], - projectDir - ]; + projectDir, nil]; NSFileManager *fileManager = [NSFileManager defaultManager]; @@ -534,7 +535,7 @@ - (NSString *)findExecutable - (BOOL)runExecutable:(NSString *)executablePath { - return [self runCommand:executablePath withArguments:@[]]; + return [self runCommand:executablePath withArguments:[NSArray array]]; } #pragma mark - Task Notifications @@ -698,4 +699,4 @@ - (BOOL)hasGNUmakefile return [[NSFileManager defaultManager] fileExistsAtPath:gnumakefilePath]; } -@end \ No newline at end of file +@end diff --git a/Applications/ycode/YCodeProject.h b/Applications/ycode/YCodeProject.h index 5982541..379141f 100644 --- a/Applications/ycode/YCodeProject.h +++ b/Applications/ycode/YCodeProject.h @@ -53,6 +53,8 @@ NSMutableDictionary *_fileWatchers; } ++ (YCodeProject *)createNewProjectAtPath:(NSString *)path name:(NSString *)name type:(NSString *)type; + /** * Project access */ @@ -110,4 +112,4 @@ @end -#endif // _YCODEPROJECT_H_ \ No newline at end of file +#endif // _YCODEPROJECT_H_ diff --git a/Applications/ycode/YCodeProject.m b/Applications/ycode/YCodeProject.m index b971b8a..4a893b9 100644 --- a/Applications/ycode/YCodeProject.m +++ b/Applications/ycode/YCodeProject.m @@ -38,6 +38,128 @@ #import #import #import +#import +#import + +static NSMutableDictionary * +YCodeBuildSettingsForProduct(NSString *productName) +{ + NSMutableDictionary *settings = [NSMutableDictionary dictionary]; + + [settings setObject:@"macosx" forKey:@"SDKROOT"]; + [settings setObject:@"$(inherited)" forKey:@"HEADER_SEARCH_PATHS"]; + [settings setObject:@"$(inherited)" forKey:@"LIBRARY_SEARCH_PATHS"]; + [settings setObject:productName forKey:@"PRODUCT_NAME"]; + [settings setObject:@"YES" forKey:@"GCC_ENABLE_OBJC_EXCEPTIONS"]; + + return settings; +} + +static XCConfigurationList * +YCodeConfigurationList(NSString *productName) +{ + XCBuildConfiguration *debug = nil; + XCBuildConfiguration *release = nil; + XCConfigurationList *list = nil; + NSMutableDictionary *debugSettings = nil; + NSMutableDictionary *releaseSettings = nil; + NSMutableArray *configs = nil; + + debugSettings = YCodeBuildSettingsForProduct(productName); + [debugSettings setObject:@"YES" forKey:@"GCC_GENERATE_DEBUGGING_SYMBOLS"]; + [debugSettings setObject:@"0" forKey:@"GCC_OPTIMIZATION_LEVEL"]; + + releaseSettings = [NSMutableDictionary dictionaryWithDictionary: + YCodeBuildSettingsForProduct(productName)]; + [releaseSettings setObject:@"NO" forKey:@"GCC_GENERATE_DEBUGGING_SYMBOLS"]; + [releaseSettings setObject:@"s" forKey:@"GCC_OPTIMIZATION_LEVEL"]; + + debug = AUTORELEASE([[XCBuildConfiguration alloc] + initWithName:@"Debug" + buildSettings:debugSettings]); + release = AUTORELEASE([[XCBuildConfiguration alloc] + initWithName:@"Release" + buildSettings:releaseSettings]); + configs = [NSMutableArray arrayWithObjects:debug, release, nil]; + + list = AUTORELEASE([[XCConfigurationList alloc] initWithConfigurations:configs]); + [list setDefaultConfigurationName:@"Debug"]; + [list setDefaultConfigurationIsVisible:@"0"]; + + return list; +} + +static PBXBuildFile * +YCodeBuildFile(PBXFileReference *fileRef) +{ + PBXBuildFile *buildFile = AUTORELEASE([[PBXBuildFile alloc] init]); + [buildFile setFileRef:fileRef]; + return buildFile; +} + +static PBXFileReference * +YCodeFileReference(NSString *path) +{ + PBXFileReference *fileRef = AUTORELEASE([[PBXFileReference alloc] + initWithPath:path]); + [fileRef setSourceTree:@""]; + return fileRef; +} + +static PBXBuildPhase * +YCodeBuildPhase(Class phaseClass, NSMutableArray *files, PBXNativeTarget *target, + NSString *name) +{ + PBXBuildPhase *phase = AUTORELEASE([[phaseClass alloc] + initWithFiles:files + buildActionMask:@"2147483647" + runOnlyForDeployment:@"0" + target:target + name:name]); + return phase; +} + +static NSString * +YCodeProjectFilePathForPath(NSString *path) +{ + if ([[path pathExtension] isEqualToString:@"pbxproj"]) { + return path; + } + + if ([[path pathExtension] isEqualToString:@"pcproj"]) { + NSString *name = [[path lastPathComponent] stringByDeletingPathExtension]; + return [[path stringByAppendingPathComponent: + [NSString stringWithFormat:@"%@.xcodeproj", name]] + stringByAppendingPathComponent:@"project.pbxproj"]; + } + + if ([[path pathExtension] isEqualToString:@"xcodeproj"]) { + return [path stringByAppendingPathComponent:@"project.pbxproj"]; + } + + return [[path stringByAppendingPathExtension:@"xcodeproj"] + stringByAppendingPathComponent:@"project.pbxproj"]; +} + +static NSString * +YCodeProjectPackagePathForPath(NSString *path) +{ + if ([[path pathExtension] isEqualToString:@"pbxproj"]) { + return [path stringByDeletingLastPathComponent]; + } + + if ([[path pathExtension] isEqualToString:@"pcproj"]) { + NSString *name = [[path lastPathComponent] stringByDeletingPathExtension]; + return [path stringByAppendingPathComponent: + [NSString stringWithFormat:@"%@.xcodeproj", name]]; + } + + if ([[path pathExtension] isEqualToString:@"xcodeproj"]) { + return path; + } + + return [path stringByAppendingPathExtension:@"xcodeproj"]; +} @implementation YCodeProject @@ -71,9 +193,20 @@ + (YCodeProject *)createNewProjectAtPath:(NSString *)path name:(NSString *)name - (BOOL)createXcodeProjectWithName:(NSString *)name atPath:(NSString *)path { - // Create basic Xcode project structure NSString *projectFile = [NSString stringWithFormat:@"%@.xcodeproj", name]; NSString *projectPath = [path stringByAppendingPathComponent:projectFile]; + NSString *projectPBXPath = [projectPath stringByAppendingPathComponent:@"project.pbxproj"]; + NSString *productPath = [NSString stringWithFormat:@"%@.app", name]; + PBXFileReference *mainFile = nil; + PBXFileReference *productReference = nil; + PBXBuildFile *mainBuildFile = nil; + PBXSourcesBuildPhase *sourcesPhase = nil; + PBXResourcesBuildPhase *resourcesPhase = nil; + PBXFrameworksBuildPhase *frameworksPhase = nil; + NSMutableArray *mainChildren = nil; + NSMutableArray *productChildren = nil; + NSMutableArray *targets = nil; + NSMutableArray *buildPhases = nil; NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager createDirectoryAtPath:projectPath withIntermediateDirectories:YES attributes:nil error:nil]) { @@ -86,32 +219,72 @@ - (BOOL)createXcodeProjectWithName:(NSString *)name atPath:(NSString *)path [pbxProject setProjectRoot:@""]; [pbxProject setCompatibilityVersion:@"Xcode 15.0"]; [pbxProject setDevelopmentRegion:@"en"]; + [pbxProject setKnownRegions:[NSMutableArray arrayWithObjects:@"en", @"Base", nil]]; + [pbxProject setBuildConfigurationList:YCodeConfigurationList(name)]; // Create main group PBXGroup *mainGroup = [[PBXGroup alloc] init]; [mainGroup setName:name]; + [mainGroup setSourceTree:@""]; [pbxProject setMainGroup:mainGroup]; + + PBXGroup *productsGroup = [[PBXGroup alloc] init]; + [productsGroup setName:@"Products"]; + [productsGroup setSourceTree:@""]; + [pbxProject setProductRefGroup:productsGroup]; // Create a simple target PBXNativeTarget *target = [[PBXNativeTarget alloc] init]; [target setName:name]; [target setProductName:name]; - // Note: addTarget method may not be available in this XCode framework version - // [pbxProject addTarget:target]; + [target setProductType:@"com.apple.product-type.application"]; + [target setBuildConfigurationList:YCodeConfigurationList(name)]; + [target setDependencies:[NSMutableArray array]]; + [target setBuildRules:[NSMutableArray array]]; + + productReference = YCodeFileReference(productPath); + [productReference setExplicitFileType:@"wrapper.application"]; + [productReference setSourceTree:@"BUILT_PRODUCTS_DIR"]; + [target setProductReference:productReference]; + + mainFile = YCodeFileReference(@"main.m"); + mainBuildFile = YCodeBuildFile(mainFile); + + sourcesPhase = (PBXSourcesBuildPhase *)YCodeBuildPhase([PBXSourcesBuildPhase class], + [NSMutableArray arrayWithObject:mainBuildFile], target, @"Sources"); + resourcesPhase = (PBXResourcesBuildPhase *)YCodeBuildPhase([PBXResourcesBuildPhase class], + [NSMutableArray array], target, @"Resources"); + frameworksPhase = (PBXFrameworksBuildPhase *)YCodeBuildPhase([PBXFrameworksBuildPhase class], + [NSMutableArray array], target, @"Frameworks"); + + buildPhases = [NSMutableArray arrayWithObjects: + sourcesPhase, resourcesPhase, frameworksPhase, nil]; + [target setBuildPhases:buildPhases]; + + mainChildren = [NSMutableArray arrayWithObjects:mainFile, productsGroup, nil]; + productChildren = [NSMutableArray arrayWithObject:productReference]; + [mainGroup setChildren:mainChildren]; + [productsGroup setChildren:productChildren]; + + targets = [NSMutableArray arrayWithObject:target]; + [pbxProject setTargets:targets]; // Create container - PBXContainer *container = [[PBXContainer alloc] init]; - // Note: setProject method may not be available in this XCode framework version - // [container setProject:pbxProject]; + PBXContainer *container = [[PBXContainer alloc] initWithRootObject:pbxProject]; + [container setFilename:projectPBXPath]; + [container setParameter:projectPath]; + [pbxProject setContainer:container]; [self setProject:pbxProject]; [self setContainer:container]; // Create basic main.m file [self createMainFileAtPath:path]; + [self saveProjectToPath:projectPath]; RELEASE(pbxProject); RELEASE(mainGroup); + RELEASE(productsGroup); RELEASE(target); RELEASE(container); @@ -123,6 +296,8 @@ - (BOOL)createProjectCenterProjectWithName:(NSString *)name atPath:(NSString *)p // Create ProjectCenter project file NSString *projectFile = [NSString stringWithFormat:@"%@.pcproj", name]; NSString *projectPath = [path stringByAppendingPathComponent:projectFile]; + NSString *projectInfoPath = [projectPath stringByAppendingPathComponent:@"PC.project"]; + NSFileManager *fileManager = [NSFileManager defaultManager]; NSMutableDictionary *projectDict = [NSMutableDictionary dictionary]; [projectDict setObject:name forKey:@"PROJECT_NAME"]; @@ -133,11 +308,18 @@ - (BOOL)createProjectCenterProjectWithName:(NSString *)name atPath:(NSString *)p [projectDict setObject:[NSArray array] forKey:@"LOCALIZED_RESOURCES"]; [projectDict setObject:[NSArray array] forKey:@"LIBRARIES"]; - BOOL success = [projectDict writeToFile:projectPath atomically:YES]; + if (![fileManager createDirectoryAtPath:projectPath + withIntermediateDirectories:YES + attributes:nil + error:nil]) { + return NO; + } + + BOOL success = [projectDict writeToFile:projectInfoPath atomically:YES]; if (success) { // Load the project we just created - PBXContainer *container = [self convertProjectCenterProject:projectDict fromPath:path]; + [self convertProjectCenterProject:projectDict fromPath:projectPath]; // Create basic main.m file [self createMainFileAtPath:path]; @@ -178,11 +360,36 @@ - (void)createGNUMakefileAtPath:(NSString *)path withName:(NSString *)name - (PBXContainer *)convertProjectCenterProject:(NSDictionary *)projectDict fromPath:(NSString *)path { - // Convert ProjectCenter project to internal PBX representation - return [self loadProjectCenterProject:projectDict fromPath:path]; + PBXContainer *container = [self loadProjectCenterProject:projectDict fromPath:path]; + + if (container != nil) { + [self setContainer:container]; + [self setProject:[container rootObject]]; + [self setProjectPath:path]; + } + + return container; +} + +- (PBXContainer *)loadProjectCenterProject:(NSDictionary *)projectDict fromPath:(NSString *)path +{ + PBXContainer *container = [self convertProjectCenterProject:projectDict]; + NSString *projectName = [projectDict objectForKey:@"PROJECT_NAME"]; + NSString *projectPath = nil; + + if (projectName == nil) { + projectName = [path lastPathComponent]; + } + + projectPath = [path stringByAppendingPathComponent: + [NSString stringWithFormat:@"%@.xcodeproj", projectName]]; + [container setFilename:[projectPath stringByAppendingPathComponent:@"project.pbxproj"]]; + [container setParameter:projectPath]; + + return container; } -- (instancetype)init +- (id)init { self = [super init]; if (self) { @@ -225,17 +432,33 @@ - (NSString *)windowNibName - (BOOL)readFromURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError **)outError { NSString *path = [url path]; + BOOL success = NO; if ([[path pathExtension] isEqualToString:@"xcodeproj"]) { - return [self loadXcodeProjectAtPath:path]; + success = [self loadXcodeProjectAtPath:path]; } else if ([[path pathExtension] isEqualToString:@"pcproj"]) { - return [self loadProjectCenterProjectAtPath:path]; + success = [self loadProjectCenterProjectAtPath:path]; + } else { + if (outError) { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"Unsupported project format" + forKey:NSLocalizedDescriptionKey]; + *outError = [NSError errorWithDomain:NSCocoaErrorDomain + code:NSFileReadUnsupportedSchemeError + userInfo:userInfo]; + } + return NO; } - + + if (success) { + return YES; + } + if (outError) { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"The selected project could not be opened" + forKey:NSLocalizedDescriptionKey]; *outError = [NSError errorWithDomain:NSCocoaErrorDomain - code:NSFileReadUnsupportedSchemeError - userInfo:@{NSLocalizedDescriptionKey: @"Unsupported project format"}]; + code:NSFileReadUnknownError + userInfo:userInfo]; } return NO; } @@ -244,14 +467,27 @@ - (BOOL)writeToURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError **)ou { if (_container == nil) { if (outError) { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"No project container to save" + forKey:NSLocalizedDescriptionKey]; *outError = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError - userInfo:@{NSLocalizedDescriptionKey: @"No project container to save"}]; + userInfo:userInfo]; } return NO; } - return [self saveProjectToPath:[url path]]; + if (![self saveProjectToPath:[url path]]) { + if (outError) { + NSDictionary *userInfo = [NSDictionary dictionaryWithObject:@"The project could not be saved" + forKey:NSLocalizedDescriptionKey]; + *outError = [NSError errorWithDomain:NSCocoaErrorDomain + code:NSFileWriteUnknownError + userInfo:userInfo]; + } + return NO; + } + + return YES; } #pragma mark - Project Loading @@ -259,13 +495,16 @@ - (BOOL)writeToURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError **)ou - (BOOL)loadXcodeProjectAtPath:(NSString *)path { @try { - PBXCoder *coder = [[PBXCoder alloc] initWithProjectFile:path]; + NSString *projectFile = YCodeProjectFilePathForPath(path); + PBXCoder *coder = [[PBXCoder alloc] initWithProjectFile:projectFile]; if (coder) { PBXContainer *container = [coder unarchive]; if (container) { [self setContainer:container]; [self setProject:[container rootObject]]; - [self setProjectPath:path]; + [self setProjectPath:YCodeProjectPackagePathForPath(path)]; + [container setFilename:projectFile]; + [container setParameter:[self projectPath]]; // Start watching files [self startWatchingFiles]; @@ -306,6 +545,22 @@ - (PBXContainer *)convertProjectCenterProject:(NSDictionary *)projectDict { // This is a simplified conversion - you might want to use the pc2xc logic NSString *projectName = [projectDict objectForKey:@"PROJECT_NAME"]; + PBXNativeTarget *target = nil; + PBXGroup *mainGroup = nil; + PBXGroup *productsGroup = nil; + PBXFileReference *productReference = nil; + NSMutableArray *children = nil; + NSMutableArray *productChildren = nil; + NSMutableArray *targets = nil; + NSMutableArray *buildPhases = nil; + PBXSourcesBuildPhase *sourcesPhase = nil; + PBXResourcesBuildPhase *resourcesPhase = nil; + PBXFrameworksBuildPhase *frameworksPhase = nil; + NSString *productPath = nil; + + if (projectName == nil) { + projectName = [[self projectDirectoryPath] lastPathComponent]; + } // Create project structure PBXProject *project = [[PBXProject alloc] init]; @@ -313,40 +568,109 @@ - (PBXContainer *)convertProjectCenterProject:(NSDictionary *)projectDict [project setProjectRoot:@""]; [project setCompatibilityVersion:@"Xcode 15.0"]; [project setDevelopmentRegion:@"en"]; + [project setKnownRegions:[NSMutableArray arrayWithObjects:@"en", @"Base", nil]]; + [project setBuildConfigurationList:YCodeConfigurationList(projectName)]; // Create main group - PBXGroup *mainGroup = [[PBXGroup alloc] init]; + mainGroup = [[PBXGroup alloc] init]; [mainGroup setName:projectName]; + [mainGroup setSourceTree:@""]; [project setMainGroup:mainGroup]; + + productsGroup = [[PBXGroup alloc] init]; + [productsGroup setName:@"Products"]; + [productsGroup setSourceTree:@""]; + [project setProductRefGroup:productsGroup]; + + target = [[PBXNativeTarget alloc] init]; + [target setName:projectName]; + [target setProductName:projectName]; + [target setProductType:@"com.apple.product-type.application"]; + [target setBuildConfigurationList:YCodeConfigurationList(projectName)]; + [target setDependencies:[NSMutableArray array]]; + [target setBuildRules:[NSMutableArray array]]; + + productPath = [NSString stringWithFormat:@"%@.app", projectName]; + productReference = YCodeFileReference(productPath); + [productReference setExplicitFileType:@"wrapper.application"]; + [productReference setSourceTree:@"BUILT_PRODUCTS_DIR"]; + [target setProductReference:productReference]; + + sourcesPhase = (PBXSourcesBuildPhase *)YCodeBuildPhase([PBXSourcesBuildPhase class], + [NSMutableArray array], target, @"Sources"); + resourcesPhase = (PBXResourcesBuildPhase *)YCodeBuildPhase([PBXResourcesBuildPhase class], + [NSMutableArray array], target, @"Resources"); + frameworksPhase = (PBXFrameworksBuildPhase *)YCodeBuildPhase([PBXFrameworksBuildPhase class], + [NSMutableArray array], target, @"Frameworks"); + buildPhases = [NSMutableArray arrayWithObjects: + sourcesPhase, resourcesPhase, frameworksPhase, nil]; + [target setBuildPhases:buildPhases]; + + children = [NSMutableArray arrayWithObject:productsGroup]; + productChildren = [NSMutableArray arrayWithObject:productReference]; + [mainGroup setChildren:children]; + [productsGroup setChildren:productChildren]; + + targets = [NSMutableArray arrayWithObject:target]; + [project setTargets:targets]; // Add files to groups // ... (implementation details for file organization) PBXContainer *container = [[PBXContainer alloc] initWithRootObject:project]; + [project setContainer:container]; + + RELEASE(project); + RELEASE(mainGroup); + RELEASE(productsGroup); + RELEASE(target); - return container; + return AUTORELEASE(container); } #pragma mark - Project Saving - (BOOL)saveProjectToPath:(NSString *)path { + NSString *originalPath = path; + NSString *projectPath = YCodeProjectPackagePathForPath(path); + NSString *projectFile = nil; + BOOL isDirectory = NO; + if (!_container) { return NO; } + + if ([[NSFileManager defaultManager] fileExistsAtPath:projectPath isDirectory:&isDirectory] && !isDirectory) { + return NO; + } + + if (![[NSFileManager defaultManager] fileExistsAtPath:projectPath]) { + if (![[NSFileManager defaultManager] createDirectoryAtPath:projectPath + withIntermediateDirectories:YES + attributes:nil + error:nil]) { + return NO; + } + } @try { - NSString *projectFile = [path stringByAppendingPathComponent:@"project.pbxproj"]; - // For now, just create a simple placeholder save - // This would need proper PBXCoder implementation - NSString *placeholder = @"// Project placeholder"; - BOOL success = [placeholder writeToFile:projectFile - atomically:YES - encoding:NSUTF8StringEncoding - error:nil]; + projectFile = [projectPath stringByAppendingPathComponent:@"project.pbxproj"]; + [_container setParameter:projectPath]; + [_container setFilename:projectFile]; + + if (_project) { + [_project setContainer:_container]; + } + + BOOL success = [_container save]; if (success) { - [self setProjectPath:path]; + if ([[originalPath pathExtension] isEqualToString:@"pcproj"]) { + [self setProjectPath:originalPath]; + } else { + [self setProjectPath:projectPath]; + } } return success; @@ -455,12 +779,17 @@ - (void)addFilesToProject:(NSArray *)filePaths while ((filePath = [enumerator nextObject]) != nil) { PBXFileReference *fileRef = [[PBXFileReference alloc] initWithPath:filePath]; - if ([mainGroup respondsToSelector:@selector(addChild:)]) { - [mainGroup addChild:fileRef]; + NSMutableArray *children = [mainGroup children]; + + if (children == nil) { + children = [NSMutableArray array]; + [mainGroup setChildren:children]; } + [children addObject:fileRef]; // Add to appropriate build phase based on file type [self addFileToBuildPhases:fileRef]; + RELEASE(fileRef); } // Notify navigator of changes @@ -483,11 +812,16 @@ - (BOOL)addGroupToProject:(NSString *)groupName [newGroup setName:groupName]; PBXGroup *mainGroup = [_project mainGroup]; - if ([mainGroup respondsToSelector:@selector(addChild:)]) { - [mainGroup addChild:newGroup]; + NSMutableArray *children = [mainGroup children]; + + if (children == nil) { + children = [NSMutableArray array]; + [mainGroup setChildren:children]; } + [children addObject:newGroup]; [_navigatorController projectDidChange]; + RELEASE(newGroup); return YES; } @@ -500,29 +834,40 @@ - (void)addFileToBuildPhases:(PBXFileReference *)fileRef PBXTarget *target; while ((target = [targetEnum nextObject]) != nil) { + NSEnumerator *phaseEnum = [[target buildPhases] objectEnumerator]; + PBXBuildPhase *phase = nil; + PBXBuildPhase *destinationPhase = nil; + if ([extension isEqualToString:@"m"] || [extension isEqualToString:@"mm"] || [extension isEqualToString:@"c"] || [extension isEqualToString:@"cpp"]) { - // Add to sources build phase - PBXSourcesBuildPhase *sourcesPhase = [target respondsToSelector:@selector(sourcesBuildPhase)] ? [target sourcesBuildPhase] : nil; - if (sourcesPhase) { - PBXBuildFile *buildFile = [[PBXBuildFile alloc] init]; - [buildFile setFileRef:fileRef]; - if ([sourcesPhase respondsToSelector:@selector(addFile:)]) { - [sourcesPhase addFile:buildFile]; + while ((phase = [phaseEnum nextObject]) != nil) { + if ([phase isKindOfClass:[PBXSourcesBuildPhase class]]) { + destinationPhase = phase; + break; } } } else if ([extension isEqualToString:@"xib"] || [extension isEqualToString:@"nib"] || [extension isEqualToString:@"plist"] || [extension isEqualToString:@"png"]) { - // Add to resources build phase - PBXResourcesBuildPhase *resourcesPhase = [target respondsToSelector:@selector(resourcesBuildPhase)] ? [target resourcesBuildPhase] : nil; - if (resourcesPhase) { - PBXBuildFile *buildFile = [[PBXBuildFile alloc] init]; - [buildFile setFileRef:fileRef]; - if ([resourcesPhase respondsToSelector:@selector(addFile:)]) { - [resourcesPhase addFile:buildFile]; + while ((phase = [phaseEnum nextObject]) != nil) { + if ([phase isKindOfClass:[PBXResourcesBuildPhase class]]) { + destinationPhase = phase; + break; } } } + + if (destinationPhase != nil) { + PBXBuildFile *buildFile = [[PBXBuildFile alloc] init]; + NSMutableArray *files = [destinationPhase files]; + + if (files == nil) { + files = [NSMutableArray array]; + [destinationPhase setFiles:files]; + } + [buildFile setFileRef:fileRef]; + [files addObject:buildFile]; + RELEASE(buildFile); + } } } @@ -558,4 +903,4 @@ - (void)stopWatchingFiles NSLog(@"Stopped watching files"); } -@end \ No newline at end of file +@end diff --git a/Applications/ycode/YCodeWindowController.m b/Applications/ycode/YCodeWindowController.m index 9ed295d..2a9f005 100644 --- a/Applications/ycode/YCodeWindowController.m +++ b/Applications/ycode/YCodeWindowController.m @@ -90,9 +90,12 @@ - (void)openProject:(NSString *)projectPath if ([project readFromURL:projectURL ofType:@"xcodeproj" error:&error]) { [self setProject:project]; } else { + NSString *message = (error != nil) + ? [error localizedDescription] + : @"The selected project could not be opened."; NSAlert *alert = [[NSAlert alloc] init]; [alert setMessageText:@"Unable to open project"]; - [alert setInformativeText:[error localizedDescription]]; + [alert setInformativeText:message]; [alert runModal]; RELEASE(alert); } diff --git a/Applications/ycode/YcodeInfo.plist b/Applications/ycode/YcodeInfo.plist index b12b4b7..6cf3878 100644 --- a/Applications/ycode/YcodeInfo.plist +++ b/Applications/ycode/YcodeInfo.plist @@ -7,7 +7,8 @@ CopyrightDescription = "Released under ..."; FullVersionID = "0.1"; NSExecutable = Ycode; + NSIcon = "Ycode.tiff"; NSMainNibFile = "Ycode.gorm"; NSPrincipalClass = NSApplication; NSRole = Application; -} \ No newline at end of file +} diff --git a/README.md b/README.md index 30fd15e..a17e715 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,22 @@ and it should build the project. If it doesn't then submit a bug. Currently GNUstep can only build projects for macOS. Once support for UIKit and other frameworks are available, those will be added. -#### 1.2.1 Plans for the future +#### 1.2.1 CocoaPods + +The library can also be consumed from CocoaPods on macOS: + +``` +pod 'XCode', :git => 'https://github.com/gnustep/libs-xcode.git' +``` + +The pod builds the Objective-C sources without ARC and exposes the public +headers under the `XCode` module-style include path, for example: + +``` +#import +``` + +#### 1.2.2 Plans for the future * Create delegate which will provide a way for the library to execute callbacks into the caller so that information can be shown. @@ -37,7 +52,7 @@ other frameworks are available, those will be added. which target should be built. * Add support for translated to .pcproj files. -#### 1.2.2 Documentation +#### 1.2.3 Documentation I am working on a manual as well as gsdocs for the code so that the library is properly documented. There is a manual for buildtool.plist settings and other diff --git a/XCode.podspec b/XCode.podspec new file mode 100644 index 0000000..f49acc8 --- /dev/null +++ b/XCode.podspec @@ -0,0 +1,30 @@ +Pod::Spec.new do |s| + s.name = 'XCode' + s.version = '0.5.0' + s.summary = 'GNUstep Xcode project parsing and build library.' + s.description = <<-DESC +The GNUstep Xcode Library parses Xcode projects and can drive builds for +Xcode project files. + DESC + s.homepage = 'https://github.com/gnustep/libs-xcode' + s.license = { :type => 'LGPL-2.0-or-later', :text => File.read('COPYING.LIB') } + s.author = { 'GNUstep' => 'bug-gnustep@gnu.org' } + s.source = { :git => 'https://github.com/gnustep/libs-xcode.git', :tag => s.version.to_s } + + s.osx.deployment_target = '10.13' + s.requires_arc = false + s.frameworks = 'Foundation' + + s.source_files = 'XCode/*.{h,m}' + s.exclude_files = 'XCode/setenv.m' + s.public_header_files = 'XCode/*.h' + s.header_mappings_dir = 'XCode' + + s.resource_bundles = { + 'XCode' => [ + 'XCode/Resources/Framework-mapping.plist', + 'XCode/Resources/language-codes.plist', + 'XCode/Resources/create-dummy-class.sh' + ] + } +end diff --git a/XCode/GSXCBuildDatabase.h b/XCode/GSXCBuildDatabase.h index c8df71c..cb1c341 100644 --- a/XCode/GSXCBuildDatabase.h +++ b/XCode/GSXCBuildDatabase.h @@ -38,17 +38,17 @@ /** * Creates a record from the contents of the given file. */ -+ (instancetype) recordWithContentsOfFile: (NSString *)path; ++ (id) recordWithContentsOfFile: (NSString *)path; /** * Initializes the record with the contents of the given file. */ -- (instancetype) initWithContentsOfFile: (NSString *)path; +- (id) initWithContentsOfFile: (NSString *)path; /** * Initializes the record with the given dictionary. */ -- (instancetype) initWithDictionary: (NSDictionary *)dict; +- (id) initWithDictionary: (NSDictionary *)dict; /** * Returns the dictionary for this record. @@ -69,17 +69,17 @@ /** * Initializes the file record with the given dictionary. */ -- (instancetype) initWithDictonary: (NSDictionary *)dict; +- (id) initWithDictonary: (NSDictionary *)dict; /** * Initializes the file record with the given build file and path. */ -- (instancetype) initWithFile: (PBXBuildFile *)f path: (NSString *)path; +- (id) initWithFile: (PBXBuildFile *)f path: (NSString *)path; /** * Creates a file record with the given build file and path. */ -+ (instancetype) recordWithBuildFile: (PBXBuildFile *)f path: (NSString *)path; ++ (id) recordWithBuildFile: (PBXBuildFile *)f path: (NSString *)path; /** * Sets the file name for this record. @@ -127,12 +127,12 @@ /** * Creates a build database for the given target. */ -+ (instancetype) buildDatabaseWithTarget: (PBXTarget *)target; ++ (id) buildDatabaseWithTarget: (PBXTarget *)target; /** * Initializes the build database with the given target. */ -- (instancetype) initWithTarget: (PBXTarget *)target; +- (id) initWithTarget: (PBXTarget *)target; /** * Sets the target for this database. diff --git a/XCode/GSXCBuildDatabase.m b/XCode/GSXCBuildDatabase.m index ccc8a32..c615572 100644 --- a/XCode/GSXCBuildDatabase.m +++ b/XCode/GSXCBuildDatabase.m @@ -37,18 +37,18 @@ @implementation GSXCRecord : NSObject -+ (instancetype) recordWithContentsOfFile: (NSString *)path ++ (id) recordWithContentsOfFile: (NSString *)path { return [[self alloc] initWithContentsOfFile: path]; } -- (instancetype) initWithContentsOfFile: (NSString *)path +- (id) initWithContentsOfFile: (NSString *)path { NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile: path]; return [self initWithDictionary: dict]; } -- (instancetype) initWithDictionary: (NSDictionary *)dict +- (id) initWithDictionary: (NSDictionary *)dict { self = [super init]; if (self != nil) @@ -58,7 +58,7 @@ - (instancetype) initWithDictionary: (NSDictionary *)dict return self; } -- (instancetype) init +- (id) init { self = [super init]; if (self != nil) @@ -92,7 +92,7 @@ - (NSDictionary *) dictionary @implementation GSXCFileRecord : GSXCRecord -- (instancetype) initWithDictonary: (NSDictionary *)dict +- (id) initWithDictonary: (NSDictionary *)dict { self = [super initWithDictionary: dict]; if (self != nil) @@ -108,7 +108,7 @@ - (instancetype) initWithDictonary: (NSDictionary *)dict return self; } -- (instancetype) initWithFile: (PBXBuildFile *)f path: (NSString *)path +- (id) initWithFile: (PBXBuildFile *)f path: (NSString *)path { self = [super init]; if (self != nil) @@ -165,7 +165,7 @@ - (instancetype) initWithFile: (PBXBuildFile *)f path: (NSString *)path return self; } -+ (instancetype) recordWithBuildFile: (PBXBuildFile *)f path: (NSString *)path ++ (id) recordWithBuildFile: (PBXBuildFile *)f path: (NSString *)path { return AUTORELEASE( [[self alloc] initWithFile: f path: path] ); } @@ -229,7 +229,7 @@ - (PBXFileReference *) fileReference @implementation GSXCBuildDatabase : NSObject -+ (instancetype) buildDatabaseWithTarget: (PBXTarget *)target ++ (id) buildDatabaseWithTarget: (PBXTarget *)target { return AUTORELEASE( [[self alloc] initWithTarget: target] ); } @@ -286,7 +286,7 @@ - (BOOL) _construct return YES; } -- (instancetype) init +- (id) init { self = [super init]; if (self != nil) @@ -297,7 +297,7 @@ - (instancetype) init return self; } -- (instancetype) initWithTarget: (PBXTarget *)target +- (id) initWithTarget: (PBXTarget *)target { self = [super init]; if (self != nil) diff --git a/XCode/GSXCBuildOperation.h b/XCode/GSXCBuildOperation.h index ca3d445..2141b98 100644 --- a/XCode/GSXCBuildOperation.h +++ b/XCode/GSXCBuildOperation.h @@ -42,12 +42,12 @@ extern "C" { /** * Creates an operation with the given file. */ -+ (instancetype) operationWithFile: (PBXBuildFile *)file; ++ (id) operationWithFile: (PBXBuildFile *)file; /** * Initializes the operation with the given file. */ -- (instancetype) initWithFile: (PBXBuildFile *)file; +- (id) initWithFile: (PBXBuildFile *)file; /** * Returns the file for this operation. diff --git a/XCode/GSXCBuildOperation.m b/XCode/GSXCBuildOperation.m index 24d8634..1f4ca1a 100644 --- a/XCode/GSXCBuildOperation.m +++ b/XCode/GSXCBuildOperation.m @@ -27,12 +27,12 @@ @implementation GSXCBuildOperation -+ (instancetype) operationWithFile: (PBXBuildFile *)file ++ (id) operationWithFile: (PBXBuildFile *)file { return AUTORELEASE([[self alloc] initWithFile: file]); } -- (instancetype) initWithFile: (PBXBuildFile *)file +- (id) initWithFile: (PBXBuildFile *)file { if ((self = [super init]) != nil) { diff --git a/XCode/GSXCCommon.h b/XCode/GSXCCommon.h index a9f44c0..2bde76a 100644 --- a/XCode/GSXCCommon.h +++ b/XCode/GSXCCommon.h @@ -22,10 +22,53 @@ Boston, MA 02110 USA. */ +#import + #import "GSXCColors.h" #import "GSXCBuildContext.h" #import "xcsystem.h" +#ifndef ASSIGN +#define ASSIGN(object, value) \ + do \ + { \ + id gsxcNewValue = (id)(value); \ + id gsxcOldValue = (id)(object); \ + if (gsxcNewValue != gsxcOldValue) \ + { \ + (object) = [gsxcNewValue retain]; \ + [gsxcOldValue release]; \ + } \ + } \ + while (0) +#endif + +#ifndef RELEASE +#define RELEASE(object) [(object) release] +#endif + +#ifndef AUTORELEASE +#define AUTORELEASE(object) [(object) autorelease] +#endif + +#ifndef DESTROY +#define DESTROY(object) \ + do \ + { \ + [(object) release]; \ + (object) = nil; \ + } \ + while (0) +#endif + +#ifndef NSDebugLog +#ifdef DEBUG +#define NSDebugLog(format, ...) NSLog(format, ##__VA_ARGS__) +#else +#define NSDebugLog(format, ...) +#endif +#endif + // product types... #define LIBRARY_TYPE @"com.apple.product-type.library.static" #define FRAMEWORK_TYPE @"com.apple.product-type.framework" @@ -34,4 +77,3 @@ #define TOOL_TYPE @"com.apple.product-type.tool" #define APPLICATION_TYPE @"com.apple.product-type.application" #define TEST_TYPE @"com.apple.product-type.bundle.unit-test" - diff --git a/XCode/GSXCGenerator.h b/XCode/GSXCGenerator.h index df3cd1b..68c3c81 100644 --- a/XCode/GSXCGenerator.h +++ b/XCode/GSXCGenerator.h @@ -35,7 +35,7 @@ /** * Initializes the generator with the given target. */ -- (instancetype) initWithTarget: (PBXTarget *)target; +- (id) initWithTarget: (PBXTarget *)target; /** * Returns the target for this generator. diff --git a/XCode/GSXCGenerator.m b/XCode/GSXCGenerator.m index 88e3d71..7f12ef1 100644 --- a/XCode/GSXCGenerator.m +++ b/XCode/GSXCGenerator.m @@ -9,7 +9,7 @@ @implementation GSXCGenerator -- (instancetype) initWithTarget: (PBXTarget *)target +- (id) initWithTarget: (PBXTarget *)target { self = [super init]; if (self != nil) diff --git a/XCode/NSObject+KeyExtraction.h b/XCode/NSObject+KeyExtraction.h index dd8419f..a34bfc4 100644 --- a/XCode/NSObject+KeyExtraction.h +++ b/XCode/NSObject+KeyExtraction.h @@ -37,9 +37,9 @@ - (NSArray *) keysForObject: (id)object; /** - * Returns a dictionary of recursive keys and values for the given object. + * Returns recursive plist values for the given object. */ -- (NSDictionary *) recursiveKeysAndValuesForObject: (id)object; +- (id) recursiveKeysAndValuesForObject: (id)object; /** * Returns all keys and values for this object. diff --git a/XCode/NSObject+KeyExtraction.m b/XCode/NSObject+KeyExtraction.m index 0f5bd53..7300aa9 100644 --- a/XCode/NSObject+KeyExtraction.m +++ b/XCode/NSObject+KeyExtraction.m @@ -278,7 +278,9 @@ + (NSArray *) skippedKeys { return [NSArray arrayWithObjects: @"context", // @"buildConfigurationList", @"buildConfigurations", @"array", @"valueforKey", @"objectatIndexedSubscript", @"totalFiles", - @"filename", @"currentFile", @"parameter", @"showEnvVarsInLog", nil]; + @"filename", @"currentFile", @"parameter", @"showEnvVarsInLog", + @"target", @"project", @"container", @"database", + @"minimizedProjectReferenceProxies", nil]; } - (NSArray *) keysForObject: (id)object @@ -303,10 +305,18 @@ - (NSArray *) keysForObject: (id)object return result; } -- (NSDictionary *) recursiveKeysAndValuesForObject: (id)object +- (id) recursiveKeysAndValuesForObject: (id)object { NSMutableDictionary *keysAndValues = nil; + if ([object isKindOfClass: [NSString class]] + || [object isKindOfClass: [NSNumber class]] + || [object isKindOfClass: [NSData class]] + || [object isKindOfClass: [NSDate class]]) + { + return object; + } + if (object && [object isKindOfClass: [NSNull class]] == NO) { NSArray *properties = [self keysForObject: object]; diff --git a/XCode/PBXBuildPhase.h b/XCode/PBXBuildPhase.h index 2a2c3d8..cdbab5d 100644 --- a/XCode/PBXBuildPhase.h +++ b/XCode/PBXBuildPhase.h @@ -44,7 +44,7 @@ /** * Initializes the build phase with the given parameters. */ -- (instancetype) initWithFiles: (NSMutableArray *)files +- (id) initWithFiles: (NSMutableArray *)files buildActionMask: (NSString *)buildActionMask runOnlyForDeployment: (NSString *)runOnlyForDeployment target: (PBXNativeTarget *)target diff --git a/XCode/PBXBuildPhase.m b/XCode/PBXBuildPhase.m index 920318e..b020c7f 100644 --- a/XCode/PBXBuildPhase.m +++ b/XCode/PBXBuildPhase.m @@ -27,7 +27,7 @@ @implementation PBXBuildPhase -- (instancetype) initWithFiles: (NSMutableArray *)files +- (id) initWithFiles: (NSMutableArray *)files buildActionMask: (NSString *)buildActionMask runOnlyForDeployment: (NSString *)runOnlyForDeployment target: (PBXNativeTarget *)target diff --git a/XCode/PBXCoder.h b/XCode/PBXCoder.h index 1a3f30a..d6079a5 100644 --- a/XCode/PBXCoder.h +++ b/XCode/PBXCoder.h @@ -45,17 +45,17 @@ /** * Unarchives a project from the given file. */ -+ (instancetype) unarchiveWithProjectFile: (NSString *)name; ++ (id) unarchiveWithProjectFile: (NSString *)name; /** * Initializes the coder with the given project file. */ -- (instancetype) initWithProjectFile: (NSString *)name; +- (id) initWithProjectFile: (NSString *)name; /** * Initializes the coder with the contents of the given file. */ -- (instancetype) initWithContentsOfFile: (NSString *)name; +- (id) initWithContentsOfFile: (NSString *)name; /** * Unarchives the project. @@ -101,12 +101,12 @@ /** * Archives the project with the given root object. */ -+ (instancetype) archiveWithRootObject: (id)obj; ++ (id) archiveWithRootObject: (id)obj; /** * Initializes the coder with the given root object. */ -- (instancetype) initWithRootObject: (id)obj; +- (id) initWithRootObject: (id)obj; /** * Archives the project. diff --git a/XCode/PBXCoder.m b/XCode/PBXCoder.m index 00ce6a9..c3b537d 100644 --- a/XCode/PBXCoder.m +++ b/XCode/PBXCoder.m @@ -49,12 +49,12 @@ - (void) setDelegate: (XCAbstractDelegate *)delegate } // Methods for unarchiving a pbxproj file... -+ (instancetype) unarchiveWithProjectFile: (NSString *)name ++ (id) unarchiveWithProjectFile: (NSString *)name { return AUTORELEASE([[self alloc] initWithProjectFile: name]); } -- (instancetype) initWithContentsOfFile: (NSString *)name +- (id) initWithContentsOfFile: (NSString *)name { if((self = [super init]) != nil) { @@ -79,7 +79,7 @@ - (instancetype) initWithContentsOfFile: (NSString *)name return self; } -- (instancetype) initWithProjectFile: (NSString *)name +- (id) initWithProjectFile: (NSString *)name { NSString *newName = [name stringByAppendingPathComponent: @"project.pbxproj"]; return [self initWithContentsOfFile: newName]; @@ -258,7 +258,7 @@ + (id) archiveWithRootObject: (id)root return [coder archive]; } -- (instancetype) initWithRootObject: (id)root +- (id) initWithRootObject: (id)root { self = [super init]; if (self != nil) diff --git a/XCode/PBXCommon.h b/XCode/PBXCommon.h index aac8a7a..61714e0 100644 --- a/XCode/PBXCommon.h +++ b/XCode/PBXCommon.h @@ -22,6 +22,4 @@ Boston, MA 02110 USA. */ -#include -#include #import "GSXCCommon.h" diff --git a/XCode/PBXContainer.h b/XCode/PBXContainer.h index 513bdb6..71bca3c 100644 --- a/XCode/PBXContainer.h +++ b/XCode/PBXContainer.h @@ -46,7 +46,7 @@ /** * Initializes the container with the given root object. */ -- (instancetype) initWithRootObject: (id)object; +- (id) initWithRootObject: (id)object; /** * Sets the workspace includes path. diff --git a/XCode/PBXContainer.m b/XCode/PBXContainer.m index b8f47e1..0809eec 100644 --- a/XCode/PBXContainer.m +++ b/XCode/PBXContainer.m @@ -29,7 +29,7 @@ @implementation PBXContainer -- (instancetype) init +- (id) init { self = [super init]; @@ -50,7 +50,7 @@ - (instancetype) init return self; } -- (instancetype) initWithRootObject: (id)object +- (id) initWithRootObject: (id)object { self = [self init]; if (self != nil) diff --git a/XCode/PBXFileReference.h b/XCode/PBXFileReference.h index 7bf94b9..2b9538c 100644 --- a/XCode/PBXFileReference.h +++ b/XCode/PBXFileReference.h @@ -68,7 +68,7 @@ /** * Initializes the file reference with the given path. */ -- (instancetype) initWithPath: (NSString *)path; +- (id) initWithPath: (NSString *)path; /** * Sets the total number of files. diff --git a/XCode/PBXFileReference.m b/XCode/PBXFileReference.m index 2edb49d..2764880 100644 --- a/XCode/PBXFileReference.m +++ b/XCode/PBXFileReference.m @@ -158,7 +158,7 @@ + (NSString *) extForFileType: (NSString *)type return result; } -- (instancetype) initWithPath: (NSString *)path +- (id) initWithPath: (NSString *)path { self = [super init]; if (self != nil) diff --git a/XCode/PBXGroup.m b/XCode/PBXGroup.m index c550ed6..40fdc7a 100644 --- a/XCode/PBXGroup.m +++ b/XCode/PBXGroup.m @@ -27,7 +27,7 @@ @implementation PBXGroup -- (instancetype) init +- (id) init { self = [super init]; if (self != nil) diff --git a/XCode/PBXProject.m b/XCode/PBXProject.m index 3be66e4..9820ecb 100644 --- a/XCode/PBXProject.m +++ b/XCode/PBXProject.m @@ -147,7 +147,7 @@ - (NSMutableArray *) arrangedTargets @implementation PBXProject -- (instancetype) init +- (id) init { self = [super init]; diff --git a/XCode/PBXResourcesBuildPhase.m b/XCode/PBXResourcesBuildPhase.m index c95a2d4..e8f6eb6 100644 --- a/XCode/PBXResourcesBuildPhase.m +++ b/XCode/PBXResourcesBuildPhase.m @@ -179,7 +179,7 @@ - (BOOL) resizeIconAtPath: (NSString *)sourcePath return status == 0; } -- (instancetype) init +- (id) init { self = [super init]; if (self != nil) diff --git a/XCode/PBXSourcesBuildPhase.m b/XCode/PBXSourcesBuildPhase.m index 92f179d..b9633e9 100644 --- a/XCode/PBXSourcesBuildPhase.m +++ b/XCode/PBXSourcesBuildPhase.m @@ -33,7 +33,7 @@ @implementation PBXSourcesBuildPhase -- (instancetype) init +- (id) init { if ((self = [super init]) != nil) { diff --git a/XCode/XCBuildConfiguration.h b/XCode/XCBuildConfiguration.h index 340fc88..89bdc9d 100644 --- a/XCode/XCBuildConfiguration.h +++ b/XCode/XCBuildConfiguration.h @@ -41,13 +41,13 @@ /** * Initializes the build configuration with the given name and build settings. */ -- (instancetype) initWithName: (NSString *)theName +- (id) initWithName: (NSString *)theName buildSettings: (NSMutableDictionary *)settings; /** * Initializes the build configuration with the given name. */ -- (instancetype) initWithName: (NSString *)theName; +- (id) initWithName: (NSString *)theName; /** * Returns the build settings for this configuration. diff --git a/XCode/XCBuildConfiguration.m b/XCode/XCBuildConfiguration.m index 38668dc..7a501d9 100644 --- a/XCode/XCBuildConfiguration.m +++ b/XCode/XCBuildConfiguration.m @@ -34,7 +34,7 @@ @implementation XCBuildConfiguration -- (instancetype) initWithName: (NSString *)theName +- (id) initWithName: (NSString *)theName buildSettings: (NSMutableDictionary *)settings { self = [super init]; @@ -46,7 +46,7 @@ - (instancetype) initWithName: (NSString *)theName return self; } -- (instancetype) initWithName: (NSString *)theName +- (id) initWithName: (NSString *)theName { NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithObject: @"macosx" forKey: @"SDKROOT"]; @@ -54,7 +54,7 @@ - (instancetype) initWithName: (NSString *)theName buildSettings: settings]; } -- (instancetype) init +- (id) init { self = [super init]; if (self != nil) diff --git a/XCode/XCConfigurationList.h b/XCode/XCConfigurationList.h index 6baabca..1aab3fb 100644 --- a/XCode/XCConfigurationList.h +++ b/XCode/XCConfigurationList.h @@ -40,7 +40,7 @@ /** * Initializes the configuration list with the given configurations. */ -- (instancetype) initWithConfigurations: (NSMutableArray *)configs; +- (id) initWithConfigurations: (NSMutableArray *)configs; /** * Returns whether the default configuration is visible. diff --git a/XCode/XCConfigurationList.m b/XCode/XCConfigurationList.m index 33f85af..f83d551 100644 --- a/XCode/XCConfigurationList.m +++ b/XCode/XCConfigurationList.m @@ -28,7 +28,7 @@ @implementation XCConfigurationList -- (instancetype) initWithConfigurations: (NSMutableArray *)configs +- (id) initWithConfigurations: (NSMutableArray *)configs { self = [super init]; if (self != nil) @@ -39,7 +39,7 @@ - (instancetype) initWithConfigurations: (NSMutableArray *)configs } /* -- (instancetype) init +- (id) init { return [self initWithConfigurations: [NSMutableArray array]]; } diff --git a/XCode/XCFileRef.h b/XCode/XCFileRef.h index 951e3d9..cc19463 100644 --- a/XCode/XCFileRef.h +++ b/XCode/XCFileRef.h @@ -37,7 +37,7 @@ /** * Creates a new file reference instance. */ -+ (instancetype) fileRef; ++ (id) fileRef; /** * Returns the workspace includes path. diff --git a/XCode/XCFileRef.m b/XCode/XCFileRef.m index b646838..32844c9 100644 --- a/XCode/XCFileRef.m +++ b/XCode/XCFileRef.m @@ -34,12 +34,12 @@ @implementation XCFileRef -+ (instancetype) fileRef ++ (id) fileRef { return AUTORELEASE([[self alloc] init]); } -- (instancetype) init +- (id) init { self = [super init]; diff --git a/XCode/XCWorkspace.h b/XCode/XCWorkspace.h index 03ed8bf..87c80ac 100644 --- a/XCode/XCWorkspace.h +++ b/XCode/XCWorkspace.h @@ -37,7 +37,7 @@ /** * Creates a new workspace instance. */ -+ (instancetype) workspace; ++ (id) workspace; /** * Returns the version of this workspace. diff --git a/XCode/XCWorkspace.m b/XCode/XCWorkspace.m index 15d28ab..6ef866a 100644 --- a/XCode/XCWorkspace.m +++ b/XCode/XCWorkspace.m @@ -36,12 +36,12 @@ @implementation XCWorkspace -+ (instancetype) workspace ++ (id) workspace { return AUTORELEASE([[self alloc] init]); } -- (instancetype) init +- (id) init { self = [super init]; diff --git a/XCode/XCWorkspaceParser.h b/XCode/XCWorkspaceParser.h index 0ce0c17..d7a39be 100644 --- a/XCode/XCWorkspaceParser.h +++ b/XCode/XCWorkspaceParser.h @@ -35,17 +35,17 @@ /** * Parses the workspace from the given file. */ -+ (instancetype) parseWorkspaceFile: (NSString *)file; ++ (id) parseWorkspaceFile: (NSString *)file; /** * Parses the workspace from the given directory. */ -+ (instancetype) parseWorkspaceDirectory: (NSString *)dir; ++ (id) parseWorkspaceDirectory: (NSString *)dir; /** * Initializes the parser with the contents of the given file. */ -- (instancetype) initWithContentsOfFile: (NSString *)file; +- (id) initWithContentsOfFile: (NSString *)file; /** * Returns the parsed workspace. diff --git a/XCode/XCWorkspaceParser.m b/XCode/XCWorkspaceParser.m index a8b1944..0d7302d 100644 --- a/XCode/XCWorkspaceParser.m +++ b/XCode/XCWorkspaceParser.m @@ -34,7 +34,7 @@ @implementation XCWorkspaceParser -- (instancetype) initWithContentsOfFile: (NSString *)file +- (id) initWithContentsOfFile: (NSString *)file { ASSIGN(_filename, file); if ((self = [super init]) != nil) @@ -66,12 +66,12 @@ - (instancetype) initWithContentsOfFile: (NSString *)file return self; } -+ (instancetype) parseWorkspaceFile: (NSString *)file ++ (id) parseWorkspaceFile: (NSString *)file { return AUTORELEASE([[self alloc] initWithContentsOfFile: file]); } -+ (instancetype) parseWorkspaceDirectory: (NSString *)dir ++ (id) parseWorkspaceDirectory: (NSString *)dir { NSString *datafile = [dir stringByAppendingPathComponent: @"contents.xcworkspacedata"]; return [self parseWorkspaceFile: datafile];