/core/externals/update-engine/externals/gdata-objectivec-client/Source/Tools/GenerateTargetNamespaceMacros/GenerateTargetNamespaceMacros.m

http://macfuse.googlecode.com/ · Objective C · 270 lines · 143 code · 60 blank · 67 comment · 28 complexity · cf00538eac78ab38e3631c662224c359 MD5 · raw file

  1. /* Copyright (c) 2008 Google Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. #import <Foundation/Foundation.h>
  16. #import <objc/runtime.h>
  17. // This tool takes the path to the GData framework, and searches it
  18. // for classes with the "GData" or "GTM" prefix.
  19. //
  20. // If any GData classes are found, it writes to the output path the contents
  21. // of the file GDataTargetNamespace.h, like
  22. //
  23. // #define GDataServiceBase _GDATA_NS_SYMBOL(GDataServiceBase)
  24. // #define GDataHTTPFetcher _GDATA_NS_SYMBOL(GDataHTTPFetcher)
  25. static int DoGDataClassSearch(NSBundle *targetBundle, NSString *outputPath);
  26. static NSArray *AllClassNamesNow(void);
  27. int main (int argc, const char * argv[]) {
  28. NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  29. // get the --path argument
  30. NSString *const kPathKey = @"-path";
  31. NSString *const kOutputPathKey = @"-output";
  32. NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  33. NSString *pathToBundle = [defaults stringForKey:kPathKey];
  34. NSString *pathToOutput = [defaults stringForKey:kOutputPathKey];
  35. int result = 1; // failure
  36. NSFileManager *fileMgr = [NSFileManager defaultManager];
  37. // the tool is in
  38. // GData/Source/Tools/GenerateTargetNamespaceMacros/build/Debug/GenerateTargetNamespaceMacros
  39. // the framework is in
  40. // GData/Source/build/Debug/GData.framework
  41. // or GData/Source/build/Release/GData.framework
  42. NSArray *args = [[NSProcessInfo processInfo] arguments];
  43. NSString *toolPath = [args objectAtIndex:0];
  44. NSString *toolDir = [toolPath stringByDeletingLastPathComponent];
  45. NSString *sourceDir = [toolDir stringByAppendingPathComponent:@"../../../.."];
  46. sourceDir = [sourceDir stringByStandardizingPath];
  47. if (pathToBundle == nil) {
  48. // no input path was specified; look for the framework in a usual build
  49. // location
  50. NSString *frameworkDir = [sourceDir stringByAppendingPathComponent:@"build/Debug/GData.framework"];
  51. if ([fileMgr fileExistsAtPath:frameworkDir]) {
  52. // found a built debug GData framework
  53. pathToBundle = frameworkDir;
  54. } else {
  55. frameworkDir = [sourceDir stringByAppendingPathComponent:@"build/Release/GData.framework"];
  56. if ([fileMgr fileExistsAtPath:frameworkDir]) {
  57. // found a built release GData framework
  58. pathToBundle = frameworkDir;
  59. }
  60. }
  61. }
  62. if (pathToBundle == nil) {
  63. // an input bundle is needed
  64. fprintf(stderr, "Usage: %s [--path /path/to/framework] [--output /path/to/output/file]\n", argv[0]);
  65. goto Done;
  66. }
  67. if (pathToOutput == nil) {
  68. // no output path was specified; default to the usual header path,
  69. // GData/Source/GDataTargetNamespace.h
  70. pathToOutput = [sourceDir stringByAppendingPathComponent:@"GDataTargetNamespace.h"];
  71. }
  72. NSBundle *targetBundle = [NSBundle bundleWithPath:pathToBundle];
  73. if (targetBundle == nil) {
  74. // could not make an NSBundle
  75. fprintf(stderr, "%s: No framework at path %s", argv[0],
  76. [pathToBundle UTF8String]);
  77. goto Done;
  78. }
  79. // generate the macros
  80. printf("Generating macros for bundle: %s\nOutput path: %s\n\n",
  81. [pathToBundle UTF8String], [pathToOutput UTF8String]);
  82. result = DoGDataClassSearch(targetBundle, pathToOutput);
  83. if (result == 0) {
  84. printf("Finished.\n");
  85. } else {
  86. printf("Failed.\n"); // DoGDataClassSearch writes error details to stderr
  87. }
  88. Done:
  89. [pool release];
  90. return result;
  91. }
  92. // DoGDataClassSearch finds all classes in the target bundle that begin with
  93. // GData. If it finds any, it writes to the output path the contents of
  94. // GDataTargetNamespace.h
  95. static int DoGDataClassSearch(NSBundle *targetBundle, NSString *outputPath) {
  96. // get all predefined classes so we can omit them after loading the
  97. // bundle's classes
  98. //
  99. // Note:
  100. // Remember that not all classes derive from NSObject, so we can't
  101. // call *any* methods on any of them.
  102. NSArray *initialClasses = AllClassNamesNow();
  103. // now load the bundle, get all the class names again, and omit the
  104. // class names that were pre-existing
  105. [targetBundle load];
  106. NSMutableArray *bundleClasses;
  107. bundleClasses = [NSMutableArray arrayWithArray:AllClassNamesNow()];
  108. [bundleClasses removeObjectsInArray:initialClasses];
  109. // search for class names that are in our bundle (excluding those
  110. // that loaded with our bundle but are located in other bundles), and that
  111. // begin with GData
  112. NSMutableArray *sharedNamesArray = [NSMutableArray array];
  113. NSEnumerator *nameEnum = [bundleClasses objectEnumerator];
  114. NSString *className;
  115. while ((className = [nameEnum nextObject]) != nil) {
  116. if ([className hasPrefix:@"GData"] || [className hasPrefix:@"GTM"]) {
  117. Class classObj = NSClassFromString(className);
  118. NSBundle *classBundle = [NSBundle bundleForClass:classObj];
  119. if (classBundle == targetBundle) {
  120. [sharedNamesArray addObject:className];
  121. }
  122. }
  123. }
  124. // if there are no class names remaining, we're done
  125. if ([sharedNamesArray count] == 0) {
  126. return 0; // successful exit
  127. }
  128. // sort the remaining class names, and build a list of macros
  129. SEL compareSel = @selector(caseInsensitiveCompare:);
  130. NSArray *sortedNames;
  131. sortedNames = [sharedNamesArray sortedArrayUsingSelector:compareSel];
  132. // the output will pad the macro definitions for easier skimming, like
  133. //
  134. // #define GDataAtomIcon _GDATA_NS_SYMBOL(GDataAtomIcon)
  135. // #define GDataAtomID _GDATA_NS_SYMBOL(GDataAtomID)
  136. // use KVC magic to find the longest class name
  137. int maxNameLen = [[sortedNames valueForKeyPath:@"@max.length"] intValue];
  138. NSString *const kSpaces = @" ";
  139. NSMutableString *nameListStr = [NSMutableString string];
  140. NSEnumerator *sortedEnum = [sortedNames objectEnumerator];
  141. NSString *name;
  142. while ((name = [sortedEnum nextObject]) != nil) {
  143. NSString *padding = [kSpaces substringToIndex:(maxNameLen - [name length])];
  144. [nameListStr appendFormat:@" #define %@%@ _GDATA_NS_SYMBOL(%@)\n",
  145. name, padding, name];
  146. }
  147. // write out the complete header file contents
  148. //
  149. // because of C-preprocessor argument evaluation rules, we need to create
  150. // "inner" macros to turn the arguments into their actual values
  151. //
  152. // we also generate GDATA_TARGET_NAMESPACE_STRING so code (particularly
  153. // unit tests) can explicitly refer to the prefix as a string at runtime
  154. NSString *const kTemplate =
  155. @"//\n// Makes the value of GDATA_TARGET_NAMESPACE a prefix for all GData class names\n//\n\n"
  156. "//\n// To avoid global namespace issues, define GDATA_TARGET_NAMESPACE to a short\n"
  157. "// string in your target if you are using the GData library in a shared-code\n"
  158. "// environment like a plug-in.\n"
  159. "//\n// For example: -DGDATA_TARGET_NAMESPACE=MyPlugin\n//\n\n"
  160. "//\n// %@\n//\n\n"
  161. "#if defined(__OBJC__) && defined(GDATA_TARGET_NAMESPACE)\n\n"
  162. " #define _GDATA_NS_SYMBOL_INNER(namespace, symbol) namespace ## _ ## symbol\n"
  163. " #define _GDATA_NS_SYMBOL_MIDDLE(namespace, symbol) _GDATA_NS_SYMBOL_INNER(namespace, symbol)\n"
  164. " #define _GDATA_NS_SYMBOL(symbol) _GDATA_NS_SYMBOL_MIDDLE(GDATA_TARGET_NAMESPACE, symbol)\n\n"
  165. " #define _GDATA_NS_STRING_INNER(namespace) #namespace\n"
  166. " #define _GDATA_NS_STRING_MIDDLE(namespace) _GDATA_NS_STRING_INNER(namespace)\n"
  167. " #define GDATA_TARGET_NAMESPACE_STRING _GDATA_NS_STRING_MIDDLE(GDATA_TARGET_NAMESPACE)\n\n"
  168. "%@\n"
  169. "#endif\n";
  170. NSString *versionInfo = [NSString stringWithFormat:@"%@ v. %@ (%d classes) %@",
  171. [targetBundle bundleIdentifier],
  172. [[targetBundle infoDictionary] objectForKey:@"CFBundleVersion"],
  173. (int) [sortedNames count],
  174. [NSDate date]];
  175. NSString *fileStr = [NSString stringWithFormat:kTemplate,
  176. versionInfo, nameListStr];
  177. NSError *error = nil;
  178. BOOL didWrite = [fileStr writeToFile:outputPath
  179. atomically:YES
  180. encoding:NSUTF8StringEncoding
  181. error:&error];
  182. if (!didWrite) {
  183. fprintf(stderr, "%s", [[error description] UTF8String]);
  184. return 1;
  185. }
  186. return 0;
  187. }
  188. // AllClassNamesNow returns all class names currently known to the Obj-C runtime
  189. static NSArray *AllClassNamesNow(void)
  190. {
  191. NSMutableArray *resultArray = [NSMutableArray array];
  192. int numClasses = 0;
  193. int newNumClasses = objc_getClassList(NULL, 0);
  194. Class *classes = NULL;
  195. // do the weird memory allocation dance suggested in objc-runtime.h
  196. while (numClasses < newNumClasses) {
  197. numClasses = newNumClasses;
  198. classes = realloc(classes, sizeof(Class) * numClasses);
  199. newNumClasses = objc_getClassList(classes, numClasses);
  200. }
  201. if (classes) {
  202. for (int index = 0; index < newNumClasses; ++index) {
  203. Class currClass = classes[index];
  204. NSString *className = NSStringFromClass(currClass);
  205. [resultArray addObject:className];
  206. }
  207. free(classes);
  208. }
  209. return resultArray;
  210. }