/// <summary> /// Enumerates the assembly, folder and platform settings in the options /// to construct a per-service manifest that we will then process. Our preferred /// documentation source is for the 'net45' platform but if a subfolder does /// not exist under the root for this platform, we'll generate using the /// assemblies in the first subfolder we find. /// </summary> /// <remarks> /// Currently we only construct manifests for the assemblies we find in the /// preferred or first platform subfolder. Distinct assemblies that exist /// outside this folder do not get included. /// </remarks> /// <returns>Collections of service manifests to process</returns> private IList <GenerationManifest> ConstructGenerationManifests() { var platformSubfolders = Directory.GetDirectories(Options.SDKAssembliesRoot, "*", SearchOption.TopDirectoryOnly); var availablePlatforms = platformSubfolders.Select(Path.GetFileName).ToList(); if (!availablePlatforms.Any(ap => ap.Equals(Options.Platform, StringComparison.OrdinalIgnoreCase))) { Info("Warning: selected platform '{0}' is not available, switching to '{1}' for assembly discovery.", Options.Platform, availablePlatforms[0]); Options.Platform = availablePlatforms[0]; } var manifests = new List <GenerationManifest>(); // discover the matching service/core assemblies in the selected platform folder and // construct a processing manifest for each var assemblySourcePath = Path.Combine(Options.SDKAssembliesRoot, Options.Platform); if (Options.Verbose) { Info("Discovering assemblies in {0}", assemblySourcePath); } foreach (var service in Options.Services) { var namePattern = $"{GenerationManifest.AWSAssemblyNamePrefix}.{service}.dll"; var assemblies = Directory.GetFiles(assemblySourcePath, namePattern); foreach (var assembly in assemblies) { if (_assembliesToSkip.Any(toSkip => assembly.Contains(toSkip))) { continue; } // keep items as the root for content, but a further subfolder of the root namespace // will be added for the artifacts var artifactManifest = new GenerationManifest(assembly, Options.ComputedContentFolder, availablePlatforms, Options, true); manifests.Add(artifactManifest); } } return(manifests); }
public void GetHelpURL(FrameworkVersion version, out string url, out string target) { target = ""; url = null; if (IsSystemNamespace) { // msdn urls for generic collections are not predictable, so // we elect to output as simple text if (!this.IsGenericType) { target = "target=_new"; url = string.Format(NDocUtilities.MSDN_TYPE_URL_PATTERN, this.GetDisplayName(true).ToLower()); } } else if (IsAmazonNamespace) { // don't know if reference is to a type in folder for namespace of declaring type we're // processing, or in another namespace (folder), so jump to output root on the link url = string.Format("../{0}/{1}", GenerationManifest.OutputSubFolderFromNamespace(this.Namespace), FilenameGenerator.GenerateFilename(this)); } }
/// <summary> /// Enumerates the assembly, folder and platform settings in the options /// to construct a per-service manifest that we will then process. Our preferred /// documentation source is for the 'net45' platform but if a subfolder does /// not exist under the root for this platform, we'll generate using the /// assemblies in the first subfolder we find. /// </summary> /// <remarks> /// Currently we only construct manifests for the assemblies we find in the /// preferred or first platform subfolder. Distinct assemblies that exist /// outside this folder do not get included. /// </remarks> /// <returns>Collections of service manifests to process</returns> private IList<GenerationManifest> ConstructGenerationManifests() { var platformSubfolders = Directory.GetDirectories(Options.SDKAssembliesRoot, "*", SearchOption.TopDirectoryOnly); var availablePlatforms = platformSubfolders.Select(Path.GetFileName).ToList(); if (!availablePlatforms.Any(ap => ap.Equals(Options.Platform, StringComparison.OrdinalIgnoreCase))) { Info("Warning: selected platform '{0}' is not available, switching to '{1}' for assembly discovery.", Options.Platform, availablePlatforms[0]); Options.Platform = availablePlatforms[0]; } var manifests = new List<GenerationManifest>(); // discover the matching service/core assemblies in the selected platform folder and // construct a processing manifest for each var assemblySourcePath = Path.Combine(Options.SDKAssembliesRoot, Options.Platform); if (Options.Verbose) Info("Discovering assemblies in {0}", assemblySourcePath); foreach (var service in Options.Services) { var namePattern = string.Format("{0}.{1}.dll", GenerationManifest.AWSAssemblyNamePrefix, service); var assemblies = Directory.GetFiles(assemblySourcePath, namePattern); foreach (var a in assemblies) { // keep items as the root for content, but a further subfolder of the root namespace // will be added for the artifacts var artifactManifest = new GenerationManifest(a, Options.ComputedContentFolder, availablePlatforms, Options, true); manifests.Add(artifactManifest); } } return manifests; }
/// <summary> /// Runs the doc generator to produce or update a consistent documentation /// set for the SDK. /// </summary> /// <param name="options"></param> /// <returns>0 on successful completion</returns> public int Execute(GeneratorOptions options) { // this is just to record the run duration, so we can monitor and optimize // build-time perf _startTimeTicks = DateTime.Now.Ticks; Options = options; Trace.Listeners.Add(new ConditionalConsoleTraceListener(Options.Verbose)); if (Options.TestMode) { SetOptionsForTestMode(); } if (string.IsNullOrEmpty(Options.SDKAssembliesRoot)) { Info("ERROR: SDKAssembliesRoot option not set"); return(-1); } if (Options.Verbose) { Info("Starting generation with options:"); Info("...TestMode: {0}", Options.TestMode); Info("...Clean: {0}", Options.Clean); Info("...WriteStaticContent: {0}", Options.WriteStaticContent); Info("...WaitOnExit: {0}", Options.WaitOnExit); Info(""); Info("...SDKAssembliesRoot: {0}", Options.SDKAssembliesRoot); Info("...OutputFolder: {0}", Options.OutputFolder); Info("...Platform: {0}", Options.Platform); Info("...Services: {0}", string.Join(",", Options.Services)); Info("...CodeSamplesRootFolder: {0}", Options.CodeSamplesRootFolder); Info(""); } if (options.Clean) { FileUtilties.CleanFolder(options.OutputFolder, true); } if (!Directory.Exists(options.OutputFolder)) { Directory.CreateDirectory(options.OutputFolder); } // use the sdk root and primary platform to determine the set of // service manifests to process var manifests = ConstructGenerationManifests(); TOCWriter = new TOCWriter(options); GenerationManifest coreManifest = null; DeferredTypesProvider deferredTypes = new DeferredTypesProvider(null); foreach (var m in manifests) { if (m.ServiceName.Equals("Core", StringComparison.InvariantCultureIgnoreCase)) { coreManifest = m; continue; } m.Generate(deferredTypes, TOCWriter); } // now all service assemblies are processed, handle core plus any types in those assemblies that // we elected to defer until we processed core. coreManifest.ManifestAssemblyContext.SdkAssembly.DeferredTypesProvider = deferredTypes; coreManifest.Generate(null, TOCWriter); Info("Generating table of contents entries..."); TOCWriter.Write(); CopyVersionInfoManifest(); if (options.WriteStaticContent) { Info("Generating/copying static content:"); Info("...creating landing page"); var lpWriter = new LandingPageWriter(options); lpWriter.Write(); Info("...copying static resources"); var sourceLocation = Directory.GetParent(typeof(SdkDocGenerator).Assembly.Location).FullName; FileUtilties.FolderCopy(Path.Combine(sourceLocation, "output-files"), options.OutputFolder, true); } // Write out all the redirect rules for doc cross-linking. using (Stream stream = File.Open(Path.Combine(options.OutputFolder, SDKDocRedirectWriter.RedirectFileName), FileMode.Create)) { SDKDocRedirectWriter.Write(stream); } return(0); }
public string CreateReferenceHtml(bool fullTypeName) { string html; var nameOrFullName = fullTypeName ? (this.FullName ?? this.Name) : this.Name; if (this.IsGenericParameter) { html = this.JustName; } else if (this.IsGenericType) { var fixedName = nameOrFullName.Substring(0, nameOrFullName.IndexOf('`')); using (var writer = new StringWriter()) { writer.Write("<a"); string url; if (this.IsAmazonNamespace) { url = $"../{GenerationManifest.OutputSubFolderFromNamespace(this.Namespace)}/{FilenameGenerator.GenerateFilename(this)}"; } else if (this.IsSystemNamespace) { writer.Write(" target=_new"); var fixedMsdnName = this.Name.Replace('`', '-'); url = $"https://docs.microsoft.com/en-us/dotnet/api/{this.Namespace}.{fixedMsdnName}"; } else { throw new ApplicationException($"Type {this.FullName} is not a System or Amazon type, no idea how to handle its help URL"); } writer.Write(" href=\"{0}\"", url); writer.Write(">"); writer.Write(fixedName); writer.Write("</a>"); writer.Write("<"); var typeArguments = this.GenericTypeArguments(); for (int i = 0; i < typeArguments.Count; i++) { if (i != 0) { writer.Write(", "); } var typeArgument = typeArguments[i]; var argumentHtml = typeArgument.CreateReferenceHtml(fullTypeName); writer.Write(argumentHtml); } writer.Write(">"); html = writer.ToString(); } } else if (this.IsArray) { var elementType = this.GetElementType(); var elementTypeHtml = elementType.CreateReferenceHtml(fullTypeName); html = $"{elementTypeHtml}[]"; } else { string url, label, target; if (this.IsAmazonNamespace) { url = $"../{GenerationManifest.OutputSubFolderFromNamespace(this.Namespace)}/{FilenameGenerator.GenerateFilename(this)}"; label = nameOrFullName; target = string.Empty; } else if (this.IsSystemNamespace) { url = string.Format(NDocUtilities.MSDN_TYPE_URL_PATTERN, this.GetDisplayName(true).ToLower()); target = " target=_new"; label = nameOrFullName; } else { throw new ApplicationException($"Type {this.FullName} is not a System or Amazon type, no idea how to handle its help URL"); } html = $"<a{target} href=\"{url}\">{label}</a>"; } return(html); }
/// <summary> /// Runs the doc generator to produce or update a consistent documentation /// set for the SDK. /// </summary> /// <param name="options"></param> /// <returns>0 on successful completion</returns> public int Execute(GeneratorOptions options) { // this is just to record the run duration, so we can monitor and optimize // build-time perf _startTimeTicks = DateTime.Now.Ticks; Options = options; Trace.Listeners.Add(new ConditionalConsoleTraceListener(Options.Verbose)); if (Options.TestMode) { SetOptionsForTestMode(); } if (string.IsNullOrEmpty(Options.SDKAssembliesRoot)) { Info("ERROR: SDKAssembliesRoot option not set"); return(-1); } if (Options.Verbose) { Info("Starting generation with options:"); Info("...TestMode: {0}", Options.TestMode); Info("...Clean: {0}", Options.Clean); Info("...WriteStaticContent: {0}", Options.WriteStaticContent); Info("...WaitOnExit: {0}", Options.WaitOnExit); Info(""); Info("...SDKAssembliesRoot: {0}", Options.SDKAssembliesRoot); Info("...OutputFolder: {0}", Options.OutputFolder); Info("...Platform: {0}", Options.Platform); Info("...Services: {0}", string.Join(",", Options.Services)); Info("...CodeSamplesRootFolder: {0}", Options.CodeSamplesRootFolder); Info(""); } if (options.Clean) { FileUtilties.CleanFolder(options.OutputFolder, true); } if (!Directory.Exists(options.OutputFolder)) { Directory.CreateDirectory(options.OutputFolder); } // use the sdk root and primary platform to determine the set of // service manifests to process var manifests = ConstructGenerationManifests(); // We want to aggregate all types(such as AmazonS3Config) under Amazon namespace before generating docs for Core. // Currently, the doc generator will stomp over files with conflicting namespaces across dll. If we encounter manifest // for Core, stash all its types and process it last. List <string> namespacesToIgnore = new List <string> { "Amazon", "Amazon.Util", "Amazon.Runtime", "Amazon.Runtime.SharedInterfaces" }; GenerationManifest coreManifest = null; PartialTypeProvider additionalTypeProvider = new PartialTypeProvider(null); foreach (var m in manifests) { if (m.ServiceName.Equals("Core", StringComparison.InvariantCultureIgnoreCase)) { coreManifest = m; continue; } m.Generate(namespacesToIgnore); foreach (var namespaceName in namespacesToIgnore) { additionalTypeProvider.ProcessTypes(m.AssemblyWrapper.GetTypesForNamespace(namespaceName)); } } coreManifest.AssemblyWrapper.SetSecondaryTypeProvider(additionalTypeProvider); coreManifest.Generate(null); // finish up by outputting/updating the TOC and emitting the static doc framework content if requested // we try and generate the toc based on the .Net 4.5 platform by preference, falling back as necessary GenerateTableOfContents(manifests); CopyVersionInfoManifest(); if (options.WriteStaticContent) { Info("Generating/copying static content:"); Info("...creating landing page"); var lpWriter = new LandingPageWriter(options); lpWriter.Write(); Info("...copying static resources"); var sourceLocation = Directory.GetParent(typeof(SdkDocGenerator).Assembly.Location).FullName; FileUtilties.FolderCopy(Path.Combine(sourceLocation, "output-files"), options.OutputFolder, true); } // Write out all the redirect rules for doc cross-linking. using (Stream stream = File.Open(Path.Combine(options.OutputFolder, SDKDocRedirectWriter.RedirectFileName), FileMode.Create)) { SDKDocRedirectWriter.Write(stream); } return(0); }