From a0f77be2887cc69ea43991dfb5a58934eb539c65 Mon Sep 17 00:00:00 2001 From: jmmv Date: Tue, 31 Jul 2018 09:10:59 -0700 Subject: Break out xcode-locator's main function into smaller chunks. This is intended to be a refactoring without functional changes for my own sanity in understanding what this code does. Except I couldn't avoid applying some visual improvements to the usage message. RELNOTES: None. PiperOrigin-RevId: 206771539 --- tools/osx/xcode_locator.m | 170 +++++++++++++++++++++++++++------------------- 1 file changed, 99 insertions(+), 71 deletions(-) (limited to 'tools') diff --git a/tools/osx/xcode_locator.m b/tools/osx/xcode_locator.m index 65401006bf..dc967693c3 100644 --- a/tools/osx/xcode_locator.m +++ b/tools/osx/xcode_locator.m @@ -107,6 +107,100 @@ static NSString *ExpandVersion(NSString *version) { return version; } +// Searches for all available Xcodes in the system and returns a dictionary that +// maps version identifiers of any form (X, X.Y, and X.Y.Z) to the directory +// where the Xcode bundle lives. +// +// If there is a problem locating the Xcodes, prints an error message and +// returns nil. +static NSMutableDictionary *FindXcodes() __attribute((ns_returns_retained)) { + NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + CFErrorRef cfError; + NSArray *array = CFBridgingRelease(LSCopyApplicationURLsForBundleIdentifier( + CFSTR("com.apple.dt.Xcode"), &cfError)); + if (array == nil) { + NSError *nsError = (__bridge NSError *)cfError; + fprintf(stderr, "error: %s\n", nsError.description.UTF8String); + return nil; + } + for (NSURL *url in array) { + NSBundle *bundle = [NSBundle bundleWithURL:url]; + if (!bundle) { + fprintf(stderr, "error: Unable to open bundle at URL: %s\n", + url.description.UTF8String); + return nil; + } + NSString *version = bundle.infoDictionary[@"CFBundleShortVersionString"]; + if (!version) { + fprintf(stderr, "error: Unable to extract CFBundleShortVersionString " + "from URL: %s\n", url.description.UTF8String); + return nil; + } + version = ExpandVersion(version); + NSURL *developerDir = + [url URLByAppendingPathComponent:@"Contents/Developer"]; + XcodeVersionEntry *entry = + [[XcodeVersionEntry alloc] initWithVersion:version url:developerDir]; + AddEntryToDictionary(entry, dict); + } + return dict; +} + +// Prints out the located Xcodes as a set of lines where each line contains the +// list of versions for a given Xcode and its location on disk. +static void DumpAsVersionsOnly(FILE *output, NSMutableDictionary *dict) { + NSSet *distinctValues = [[NSSet alloc] initWithArray:[dict allValues]]; + NSMutableDictionary *aliasDict = [[NSMutableDictionary alloc] init]; + for (XcodeVersionEntry *value in distinctValues) { + NSString *versionString = value.version; + if (aliasDict[versionString] == nil) { + aliasDict[versionString] = [[NSMutableSet alloc] init]; + } + [aliasDict[versionString] + addObjectsFromArray:[dict allKeysForObject:value]]; + } + for (NSString *version in aliasDict) { + XcodeVersionEntry *entry = dict[version]; + fprintf(output, "%s:%s:%s\n", + version.UTF8String, + [[aliasDict[version] allObjects] + componentsJoinedByString: @","].UTF8String, + entry.url.fileSystemRepresentation); + } +} + +// Prints out the located Xcodes in JSON format. +static void DumpAsJson(FILE *output, NSMutableDictionary *dict) { + fprintf(output, "{\n"); + for (NSString *version in dict) { + XcodeVersionEntry *entry = dict[version]; + fprintf(output, "\t\"%s\": \"%s\",\n", + version.UTF8String, entry.url.fileSystemRepresentation); + } + fprintf(output, "}\n"); +} + +// Dumps usage information. +static void usage(FILE *output) { + fprintf( + output, + "xcode-locator [-v|]" + "\n\n" + "Given a version number or partial version number in x.y.z format, " + "will attempt to return the path to the appropriate developer " + "directory." + "\n\n" + "Omitting a version number will list all available versions in JSON " + "format, alongside their paths." + "\n\n" + "Passing -v will list all available fully-specified version numbers " + "along with their possible aliases and their developer directory, " + "each on a new line. For example:" + "\n\n" + "7.3.1:7,7.3,7.3.1:/Applications/Xcode.app/Contents/Developer" + "\n"); +} + int main(int argc, const char * argv[]) { @autoreleasepool { NSString *versionArg = nil; @@ -129,56 +223,14 @@ int main(int argc, const char * argv[]) { } } if (versionArg == nil) { - fprintf( - stderr, - "xcode-locator [-v|]" - "\n\n" - "Given a version number, or partial version number in x.y.z format, " - "will attempt to return the path to the appropriate developer " - "directory." - "\n" - "Omitting a version number will list all available versions in JSON " - "format, alongside their paths." - "\n" - "Passing -v will list all available fully-specified version numbers " - "along with their possible aliases and their developer directory, " - "each on a new line." - "\n" - "For example: " - "'7.3.1:7,7.3,7.3.1:/Applications/Xcode.app/Contents/Developer'." - "\n"); + usage(stderr); return 1; } - NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; - CFErrorRef cfError; - NSArray *array = CFBridgingRelease(LSCopyApplicationURLsForBundleIdentifier( - CFSTR("com.apple.dt.Xcode"), &cfError)); - if (array == nil) { - NSError *nsError = (__bridge NSError *)cfError; - fprintf(stderr, "error: %s\n", nsError.description.UTF8String); + NSMutableDictionary *dict = FindXcodes(); + if (dict == nil) { return 1; } - for (NSURL *url in array) { - NSBundle *bundle = [NSBundle bundleWithURL:url]; - if (!bundle) { - fprintf(stderr, "error: Unable to open bundle at URL: %s\n", - url.description.UTF8String); - return 1; - } - NSString *version = bundle.infoDictionary[@"CFBundleShortVersionString"]; - if (!version) { - fprintf(stderr, "error: Unable to extract CFBundleShortVersionString " - "from URL: %s\n", url.description.UTF8String); - return 1; - } - version = ExpandVersion(version); - NSURL *developerDir = - [url URLByAppendingPathComponent:@"Contents/Developer"]; - XcodeVersionEntry *entry = - [[XcodeVersionEntry alloc] initWithVersion:version url:developerDir]; - AddEntryToDictionary(entry, dict); - } XcodeVersionEntry *entry = [dict objectForKey:versionArg]; if (entry) { @@ -187,33 +239,9 @@ int main(int argc, const char * argv[]) { } if (versionsOnly) { - NSSet *distinctValues = [[NSSet alloc] initWithArray:[dict allValues]]; - NSMutableDictionary *aliasDict = [[NSMutableDictionary alloc] init]; - for (XcodeVersionEntry *value in distinctValues) { - NSString *versionString = value.version; - if (aliasDict[versionString] == nil) { - aliasDict[versionString] = [[NSMutableSet alloc] init]; - } - [aliasDict[versionString] - addObjectsFromArray:[dict allKeysForObject:value]]; - } - for (NSString *version in aliasDict) { - XcodeVersionEntry *entry = dict[version]; - printf("%s:%s:%s\n", - version.UTF8String, - [[aliasDict[version] allObjects] - componentsJoinedByString: @","].UTF8String, - entry.url.fileSystemRepresentation); - } + DumpAsVersionsOnly(stdout, dict); } else { - // Print out list in json format. - printf("{\n"); - for (NSString *version in dict) { - XcodeVersionEntry *entry = dict[version]; - printf("\t\"%s\": \"%s\",\n", - version.UTF8String, entry.url.fileSystemRepresentation); - } - printf("}\n"); + DumpAsJson(stdout, dict); } return ([@"" isEqualToString:versionArg] ? 0 : 1); } -- cgit v1.2.3