PageRenderTime 78ms CodeModel.GetById 14ms app.highlight 60ms RepoModel.GetById 1ms app.codeStats 1ms

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