Skip to content
Browse files

Implemented preparing for docset publishing. Closes #45.

The feature must be enabled by --package-docset and works by using the information from --docset-feed-url which is required by docsetutil when packaging. The result of using it is publish directory in output path with .xar and .atom files. If the .atom file is already there, it is updated if needed, otherwise it is created.
  • Loading branch information...
1 parent 65ab4bb commit a28b2ffaf931747769f0522fae0eb71058418c88 @tomaz committed
View
12 Application/GBAppledocApplication.m
@@ -30,7 +30,9 @@
static NSString *kGBArgCreateHTML = @"create-html";
static NSString *kGBArgCreateDocSet = @"create-docset";
static NSString *kGBArgInstallDocSet = @"install-docset";
+static NSString *kGBArgPublishDocSet = @"publish-docset";
static NSString *kGBArgKeepIntermediateFiles = @"keep-intermediate-files";
+
static NSString *kGBArgRepeatFirstParagraph = @"repeat-first-par";
static NSString *kGBArgKeepUndocumentedObjects = @"keep-undocumented-objects";
static NSString *kGBArgKeepUndocumentedMembers = @"keep-undocumented-members";
@@ -206,9 +208,11 @@ - (void)application:(DDCliApplication *)app willParseOptions:(DDGetoptLongParser
{ kGBArgCreateHTML, 'h', DDGetoptNoArgument },
{ kGBArgCreateDocSet, 'd', DDGetoptNoArgument },
{ kGBArgInstallDocSet, 'n', DDGetoptNoArgument },
+ { kGBArgPublishDocSet, 'u', DDGetoptNoArgument },
{ GBNoArg(kGBArgCreateHTML), 0, DDGetoptNoArgument },
{ GBNoArg(kGBArgCreateDocSet), 0, DDGetoptNoArgument },
{ GBNoArg(kGBArgInstallDocSet), 0, DDGetoptNoArgument },
+ { GBNoArg(kGBArgPublishDocSet), 0, DDGetoptNoArgument },
{ kGBArgKeepIntermediateFiles, 0, DDGetoptNoArgument },
{ kGBArgKeepUndocumentedObjects, 0, DDGetoptNoArgument },
@@ -429,9 +433,11 @@ - (void)setCompanyId:(NSString *)value { self.settings.companyIdentifier = value
- (void)setCreateHtml:(BOOL)value { self.settings.createHTML = value; }
- (void)setCreateDocset:(BOOL)value { self.settings.createDocSet = value; }
- (void)setInstallDocset:(BOOL)value { self.settings.installDocSet = value; }
+- (void)setPublishDocset:(BOOL)value { self.settings.publishDocSet = value; }
- (void)setNoCreateHtml:(BOOL)value { self.settings.createHTML = !value; }
- (void)setNoCreateDocset:(BOOL)value { self.settings.createDocSet = !value; }
- (void)setNoInstallDocset:(BOOL)value { self.settings.installDocSet = !value; }
+- (void)setNoPublishDocset:(BOOL)value { self.settings.publishDocSet = !value; }
- (void)setKeepIntermediateFiles:(BOOL)value { self.settings.keepIntermediateFiles = value;}
- (void)setKeepUndocumentedObjects:(BOOL)value { self.settings.keepUndocumentedObjects = value; }
@@ -533,6 +539,7 @@ - (void)printSettingsAndArguments:(NSArray *)arguments {
ddprintf(@"--%@ = %@\n", kGBArgCreateHTML, PRINT_BOOL(self.settings.createHTML));
ddprintf(@"--%@ = %@\n", kGBArgCreateDocSet, PRINT_BOOL(self.settings.createDocSet));
ddprintf(@"--%@ = %@\n", kGBArgInstallDocSet, PRINT_BOOL(self.settings.installDocSet));
+ ddprintf(@"--%@ = %@\n", kGBArgPublishDocSet, PRINT_BOOL(self.settings.publishDocSet));
ddprintf(@"--%@ = %@\n", kGBArgKeepIntermediateFiles, PRINT_BOOL(self.settings.keepIntermediateFiles));
ddprintf(@"--%@ = %@\n", kGBArgKeepUndocumentedObjects, PRINT_BOOL(self.settings.keepUndocumentedObjects));
ddprintf(@"--%@ = %@\n", kGBArgKeepUndocumentedMembers, PRINT_BOOL(self.settings.keepUndocumentedMembers));
@@ -580,8 +587,9 @@ - (void)printHelp {
ddprintf(@"\n");
ddprintf(@"OUTPUT GENERATION\n");
PRINT_USAGE(@"-h,", kGBArgCreateHTML, @"", @"[b] Create HTML");
- PRINT_USAGE(@"-d,", kGBArgCreateDocSet, @"", @"[b] Create HTML and documentation set");
- PRINT_USAGE(@"-n,", kGBArgInstallDocSet, @"", @"[b] Create HTML & DocSet and install DocSet to Xcode");
+ PRINT_USAGE(@"-d,", kGBArgCreateDocSet, @"", @"[b] Create documentation set");
+ PRINT_USAGE(@"-n,", kGBArgInstallDocSet, @"", @"[b] Install documentation set to Xcode");
+ PRINT_USAGE(@"-u,", kGBArgPublishDocSet, @"", @"[b] Prepare DocSet for publishing");
ddprintf(@"\n");
ddprintf(@"OPTIONS\n");
PRINT_USAGE(@" ", kGBArgKeepIntermediateFiles, @"", @"[b] Keep intermediate files in output path");
View
73 Application/GBApplicationSettingsProvider.h
@@ -110,6 +110,48 @@
/// @name Behavior handling
///---------------------------------------------------------------------------------------
+/* Indicates whether HTML files should be generated or not.
+
+ If `YES`, HTML files are generated in `outputPath` from parsed and processed data. If `NO`, input files are parsed and processed, but nothing is generated.
+
+ @see createDocSet
+ */
+@property (assign) BOOL createHTML;
+
+/** Specifies whether documentation set should be created from the HTML files.
+
+ If `YES`, HTML files from html subdirectory in `outputPath` are moved to proper subdirectory within docset output files, then helper files are generated from parsed data. Documentation set files are also indexed. If `NO`, HTML files are left in the output path.
+
+ @see createHtml
+ @see installDocSet
+ @see publishDocSet
+ */
+@property (assign) BOOL createDocSet;
+
+/** Specifies whether the documentation set should be installed or not.
+
+ If `YES`, temporary files used for indexing and removed, then documentation set bundle is created from the files from docset output path and is moved to `docsetInstallPath`. If `NO`, all documentation set files are left in output path.
+
+ @see createDocSet
+ @see publishDocSet
+ */
+@property (assign) BOOL installDocSet;
+
+/** Specifies whether the documentation set should be prepared for publishing or not.
+
+ If `YES`, installed documentation set is packaged for publishing - an atom feed is created and documentation set is archived. If the atom feed file is alreay found, it is updated with new information. Both, the feed and archived docset files are located within `outputPath`. If `NO`, documentation set is not prepared for publishing.
+
+ @see createDocSet
+ @see installDocSet
+ */
+@property (assign) BOOL publishDocSet;
+
+/** Specifies whether intermediate files should be kept in `outputPath` or not.
+
+ If `YES`, all intermediate files (i.e. HTML files and documentation set files) are kept in output path. If `NO`, only final results are kept. This setting not only affects how the files are being handled, it also affects performance. If intermediate files are not kept, appledoc moves files between various generation phases, otherwise it copies them. So it's prefferable to leave this option to `NO`. This option only affects output files, input source files are always left intact!
+ */
+@property (assign) BOOL keepIntermediateFiles;
+
/** Indicates whether the first paragraph needs to be repeated within method and property description or not.
If `YES`, first paragraph is repeated in members description, otherwise not.
@@ -192,37 +234,6 @@
*/
@property (assign) BOOL prefixMergedCategoriesSectionsWithCategoryName;
-/* Indicates whether HTML files should be generated or not.
-
- If `YES`, HTML files are generated in `outputPath` from parsed and processed data. If `NO`, input files are parsed and processed, but nothing is generated.
-
- @see createDocSet
- */
-@property (assign) BOOL createHTML;
-
-/** Specifies whether documentation set should be created from the HTML files.
-
- If `YES`, HTML files from html subdirectory in `outputPath` are moved to proper subdirectory within docset output files, then helper files are generated from parsed data. Documentation set files are also indexed. If `NO`, HTML files are left in the output path.
-
- @see createHtml
- @see installDocSet
- */
-@property (assign) BOOL createDocSet;
-
-/** Specifies whether the documentation set should be installed or not.
-
- If `YES`, temporary files used for indexing and removed, then documentation set bundle is created from the files from docset output path and is moved to `docsetInstallPath`. If `NO`, all documentation set files are left in output path.
-
- @see createDocSet
- */
-@property (assign) BOOL installDocSet;
-
-/** Specifies whether intermediate files should be kept in `outputPath` or not.
-
- If `YES`, all intermediate files (i.e. HTML files and documentation set files) are kept in output path. If `NO`, only final results are kept. This setting not only affects how the files are being handled, it also affects performance. If intermediate files are not kept, appledoc moves files between various generation phases, otherwise it copies them. So it's prefferable to leave this option to `NO`. This option only affects output files, input source files are always left intact!
- */
-@property (assign) BOOL keepIntermediateFiles;
-
///---------------------------------------------------------------------------------------
/// @name Warnings handling
///---------------------------------------------------------------------------------------
View
4 Application/GBApplicationSettingsProvider.m
@@ -53,7 +53,8 @@ - (id)init {
self.createHTML = YES;
self.createDocSet = YES;
- self.installDocSet = NO;
+ self.installDocSet = YES;
+ self.publishDocSet = NO;
self.repeatFirstParagraphForMemberDescription = YES;
self.keepIntermediateFiles = NO;
self.keepUndocumentedObjects = NO;
@@ -306,6 +307,7 @@ - (NSString *)description {
@synthesize createHTML;
@synthesize createDocSet;
@synthesize installDocSet;
+@synthesize publishDocSet;
@synthesize keepIntermediateFiles;
@synthesize warnOnMissingOutputPathArgument;
View
97 Generating/GBDocSetOutputGenerator.m
@@ -23,6 +23,7 @@ - (BOOL)processTokensXml:(NSError **)error;
- (BOOL)indexDocSet:(NSError **)error;
- (BOOL)removeTemporaryFiles:(NSError **)error;
- (BOOL)installDocSet:(NSError **)error;
+- (BOOL)publishDocSet:(NSError **)error;
- (BOOL)processTokensXmlForObjects:(NSArray *)objects type:(NSString *)type template:(NSString *)template index:(NSUInteger *)index error:(NSError **)error;
- (void)addTokensXmlModelObjectDataForObject:(GBModelBase *)object toData:(NSMutableDictionary *)data;
- (void)initializeSimplifiedObjects;
@@ -31,6 +32,7 @@ - (NSString *)tokenIdentifierForObject:(GBModelBase *)object;
@property (retain) NSArray *classes;
@property (retain) NSArray *categories;
@property (retain) NSArray *protocols;
+@property (readonly) NSString *docsetInstallationPath;
@property (readonly) NSMutableSet *temporaryFiles;
@end
@@ -60,6 +62,10 @@ - (BOOL)generateOutputWithStore:(id)store error:(NSError **)error {
// Install documentation set to Xcode.
if (!self.settings.installDocSet) return YES;
if (![self installDocSet:error]) return NO;
+
+ // Prepare documentation set for publishing.
+ if (!self.settings.publishDocSet) return YES;
+ if (![self publishDocSet:error]) return NO;
return YES;
}
@@ -208,27 +214,16 @@ - (BOOL)removeTemporaryFiles:(NSError **)error {
- (BOOL)installDocSet:(NSError **)error {
GBLogInfo(@"Installing DocSet...");
- // Prepare destination directory path and documentation set name.
- NSString *destDir = self.settings.docsetInstallPath;
- NSString *destSubDir = [self.settings.docsetBundleIdentifier stringByAppendingPathExtension:@"docset"];
-
// Prepare source and destination paths and file names.
NSString *sourceUserPath = self.outputUserPath;
- NSString *destUserPath = [destDir stringByAppendingPathComponent:destSubDir];
+ NSString *destUserPath = self.docsetInstallationPath;
NSString *sourcePath = [sourceUserPath stringByStandardizingPath];
- NSString *destPath = [destUserPath stringByStandardizingPath];
+ NSString *destPath = [destUserPath stringByStandardizingPath];\
- // Create destination directory and move files to it. If the destination directory alredy exists, remove it. Then create installation directory to make sure it's there the first time. Then move the docset files to the correct subdirectory.
+ // Create destination directory and move files to it.
GBLogVerbose(@"Moving DocSet files from '%@' to '%@'...", sourceUserPath, destUserPath);
- if ([self.fileManager fileExistsAtPath:destPath]) {
- GBLogDebug(@"Removing previous DocSet installation directory '%@'...", destUserPath);
- if (![self.fileManager removeItemAtPath:destPath error:error]) {
- GBLogWarn(@"Failed removing previous DocSet installation directory '%@'!", destUserPath);
- return NO;
- }
- }
- if (![self.fileManager createDirectoryAtPath:[destDir stringByStandardizingPath] withIntermediateDirectories:YES attributes:nil error:error]) {
- GBLogWarn(@"Failed creating DocSet installation parent directory '%@'!", destDir);
+ if (![self initializeDirectoryAtPath:destUserPath error:error]) {
+ GBLogWarn(@"Failed initializing DocSet installation directory '%@'!", destUserPath);
return NO;
}
if (![self copyOrMoveItemFromPath:sourcePath toPath:destPath error:error]) {
@@ -255,6 +250,59 @@ - (BOOL)installDocSet:(NSError **)error {
return YES;
}
+- (BOOL)publishDocSet:(NSError **)error {
+ GBLogInfo(@"Preparing DocSet for publishing...");
+ GBTask *task = [GBTask task];
+ task.reportIndividualLines = YES;
+
+ // Get the path to the installed documentation set and extract the name. Then replace the name's extension with .xar.
+ NSString *installedDocSetPath = self.docsetInstallationPath;
+ NSString *docsetBaseName = [[installedDocSetPath lastPathComponent] stringByDeletingPathExtension];
+ NSString *docsetName = [docsetBaseName stringByAppendingPathExtension:@"xar"];
+ NSString *atomName = [docsetBaseName stringByAppendingPathExtension:@"atom"];
+
+ // Prepare command line arguments for packaging.
+ NSString *outputDir = [self.settings.outputPath stringByAppendingPathComponent:@"publish"];
+ NSString *outputDocSetPath = [outputDir stringByAppendingPathComponent:docsetName];
+ NSString *outputAtomPath = [outputDir stringByAppendingPathComponent:atomName];
+ NSString *signer = self.settings.docsetCertificateSigner;
+ NSString *url = self.settings.docsetFeedURL;
+ if ([url length] == 0) GBLogWarn(@"--docset-feed-url is required for publishing DocSet; placeholder will be used in '%@'!", outputAtomPath);
+
+ // Create destination directory.
+ if (![self initializeDirectoryAtPath:outputDir preserve:[NSArray arrayWithObject:atomName] error:error]) {
+ GBLogWarn(@"Failed initializing DocSet publish directory '%@'!", outputDir);
+ return NO;
+ }
+
+ // Create command line arguments array.
+ NSMutableArray *args = [NSMutableArray array];
+ [args addObject:@"package"];
+ [args addObject:@"-output"];
+ [args addObject:[outputDocSetPath stringByStandardizingPath]];
+ [args addObject:@"-atom"];
+ [args addObject:[outputAtomPath stringByStandardizingPath]];
+ if ([signer length] > 0) {
+ [args addObject:@"-signid"];
+ [args addObject:signer];
+ }
+ if ([url length] > 0) {
+ [args addObject:@"-download-url"];
+ [args addObject:url];
+ }
+ [args addObject:installedDocSetPath];
+
+ BOOL result = [task runCommand:self.settings.docsetUtilPath arguments:args block:^(NSString *output, NSString *error) {
+ if (output) GBLogDebug(@"> %@", [output stringByTrimmingWhitespaceAndNewLine]);
+ if (error) GBLogError(@"!> %@", [error stringByTrimmingWhitespaceAndNewLine]);
+ }];
+ if (!result) {
+ if (error) *error = [NSError errorWithCode:GBErrorDocSetUtilIndexingFailed description:@"docsetutil failed to package the documentation set!" reason:task.lastStandardError];
+ return NO;
+ }
+ return YES;
+}
+
#pragma mark Helper methods
- (BOOL)processTokensXmlForObjects:(NSArray *)objects type:(NSString *)type template:(NSString *)template index:(NSUInteger *)index error:(NSError **)error {
@@ -391,12 +439,6 @@ - (NSArray *)simplifiedObjectsFromObjects:(NSArray *)objects value:(NSString *)v
return result;
}
-- (NSMutableSet *)temporaryFiles {
- static NSMutableSet *result = nil;
- if (!result) result = [[NSMutableSet alloc] init];
- return result;
-}
-
#pragma mark Overriden methods
- (NSString *)outputSubpath {
@@ -405,6 +447,17 @@ - (NSString *)outputSubpath {
#pragma mark Properties
+- (NSString *)docsetInstallationPath {
+ NSString *bundleSubDir = [self.settings.docsetBundleIdentifier stringByAppendingPathExtension:@"docset"];
+ return [self.settings.docsetInstallPath stringByAppendingPathComponent:bundleSubDir];
+}
+
+- (NSMutableSet *)temporaryFiles {
+ static NSMutableSet *result = nil;
+ if (!result) result = [[NSMutableSet alloc] init];
+ return result;
+}
+
@synthesize classes;
@synthesize categories;
@synthesize protocols;
View
25 Generating/GBOutputGenerator.h
@@ -43,6 +43,31 @@
/// @name Generation handling
///---------------------------------------------------------------------------------------
+/** Initializes the directory at the given path.
+
+ If the directory alreay exists, it is removed. Then a new one is created with all intermediate directories as needed. The result of the method is an empty directory at the given path. Sending this message has the same effect as sending `initializeDirectoryAtPath:preserve:error:` and passing `nil` for preserve argument.
+
+ @param path The path to initialize.
+ @param error If initialization fails, error is returned here.
+ @return Returns `YES` if initialization succeeds, `NO` otherwise.
+ @see initializeDirectoryAtPath:preserve:error:
+ */
+- (BOOL)initializeDirectoryAtPath:(NSString *)path error:(NSError **)error;
+
+/** Initializes the directory at the given path optionally preserving any number of files or subdirectories.
+
+ If the directory doesn't exist, it is created. Otherwise all files and subdirectories are removed, except for the given array of files or subdirectories to preserve. If the array is `nil` or empty, all files are removed. Preserve array should contain paths relative to the given path. The paths are not handled recursively - only first level items are preserved!
+
+ The result of the method is an empty directory at the given path containing only files or subdirectories from the given array.
+
+ @param path The path to initialize.
+ @param preserve An array of paths to preserve.
+ @param error If initialization fails, error is returned here.
+ @return Returns `YES` if initialization succeeds, `NO` otherwise.
+ @see initializeDirectoryAtPath:error:
+ */
+- (BOOL)initializeDirectoryAtPath:(NSString *)path preserve:(NSArray *)preserve error:(NSError **)error;
+
/** Copies all files from the templates path to the output path as defined in assigned `settings`, replicating the directory structure and stores all detected template files to `templateFiles` dictionary.
The method uses `[GBApplicationSettingsProvider templatesPath]` as the base path for templates and `[GBApplicationSettingsProvider outputPath]` as the base path for output. In both cases, `outputSubpath` is used to determine the source and destination subdirectories. It then copies all files from template path to the output path, including the whole directory structure. If any special template file is found at source path, it is not copied! Template files are identified by having a `-template` suffix followed by optional extension. For example `object-template.html`. As this message prepares the ground for actual generation, it should be sent before any other messages (i.e. before `generateOutput:`).
View
35 Generating/GBOutputGenerator.m
@@ -119,6 +119,41 @@ - (BOOL)copyTemplateFilesToOutputPath:(NSError **)error {
return YES;
}
+- (BOOL)initializeDirectoryAtPath:(NSString *)path error:(NSError **)error {
+ return [self initializeDirectoryAtPath:path preserve:nil error:error];
+}
+
+- (BOOL)initializeDirectoryAtPath:(NSString *)path preserve:(NSArray *)preserve error:(NSError **)error {
+ GBLogVerbose(@"Initializing directory at '%@'...", path);
+ NSString *standardized = [path stringByStandardizingPath];
+
+ // If no path is to be preserved, just use simple approach of removing path and recreating it later on... Otherwise delete all content except given one.
+ BOOL exists = [self.fileManager fileExistsAtPath:standardized];
+ if ([preserve count] == 0) {
+ if (exists) {
+ GBLogDebug(@"Removing existing directory...");
+ if (![self.fileManager removeItemAtPath:standardized error:error]) return NO;
+ }
+ } else if (exists) {
+ GBLogDebug(@"Enumerating directory contents...");
+ NSArray *contents = [self.fileManager contentsOfDirectoryAtPath:path error:error];
+ if (!contents && error && *error) return NO;
+ for (NSString *subpath in contents) {
+ if (![preserve containsObject:subpath]) {
+ GBLogDebug(@"Removing '%@'...", subpath);
+ if (![self.fileManager removeItemAtPath:[path stringByAppendingPathComponent:subpath] error:error]) return NO;
+ }
+ }
+ }
+
+ // Create the directory if it doesn't yet exist. Note that we rely on system to actually check if the directory exists, instead of the cached value from above. The cached value may change if we remove the directory. Although we could change the value too, it makes tool safer this way.
+ if (![self.fileManager fileExistsAtPath:standardized]) {
+ GBLogDebug(@"Creating directory...");
+ return [self.fileManager createDirectoryAtPath:standardized withIntermediateDirectories:YES attributes:nil error:error];
+ }
+ return YES;
+}
+
- (BOOL)copyOrMoveItemFromPath:(NSString *)source toPath:(NSString *)destination error:(NSError **)error {
BOOL copy = self.settings.keepIntermediateFiles;
GBLogDebug(@"%@ '%@' to '%@'...", copy ? @"Copying" : @"Moving", source, destination);
View
9 Testing/GBApplicationTesting.m
@@ -153,6 +153,15 @@ - (void)testInstallDocSet_shouldAssignValueToSettings {
assertThatBool(settings2.installDocSet, equalToBool(NO));
}
+- (void)testPublishDocSet_shouldAssignValueToSettings {
+ // setup & execute
+ GBApplicationSettingsProvider *settings1 = [self settingsByRunningWithArgs:@"--publish-docset", nil];
+ GBApplicationSettingsProvider *settings2 = [self settingsByRunningWithArgs:@"--no-publish-docset", nil];
+ // verify
+ assertThatBool(settings1.publishDocSet, equalToBool(YES));
+ assertThatBool(settings2.publishDocSet, equalToBool(NO));
+}
+
- (void)testKeepIntermediateFiles_shouldAssignValueToSettings {
// setup & execute
GBApplicationSettingsProvider *settings1 = [self settingsByRunningWithArgs:@"--keep-intermediate-files", nil];

0 comments on commit a28b2ff

Please sign in to comment.
Something went wrong with that request. Please try again.