public void Build(IList <DocumentBuildParameters> parameters, string outputDirectory) { if (parameters == null) { throw new ArgumentNullException(nameof(parameters)); } if (parameters.Count == 0) { throw new ArgumentException("Parameters are empty.", nameof(parameters)); } var markdownServiceProvider = CompositionContainer.GetExport <IMarkdownServiceProvider>(_container, parameters[0].MarkdownEngineName); if (markdownServiceProvider == null) { Logger.LogError($"Unable to find markdown engine: {parameters[0].MarkdownEngineName}"); throw new DocfxException($"Unable to find markdown engine: {parameters[0].MarkdownEngineName}"); } Logger.LogInfo($"Markdown engine is {parameters[0].MarkdownEngineName}"); var logCodesLogListener = new LogCodesLogListener(); Logger.RegisterListener(logCodesLogListener); // Load schema driven processor from template var sdps = LoadSchemaDrivenDocumentProcessors(parameters[0]).ToList(); if (sdps.Count > 0) { Logger.LogInfo($"{sdps.Count()} schema driven document processor plug-in(s) loaded."); Processors = Processors.Union(sdps); } BuildInfo lastBuildInfo = null; var currentBuildInfo = new BuildInfo { BuildStartTime = DateTime.UtcNow, DocfxVersion = EnvironmentContext.Version, }; try { lastBuildInfo = BuildInfo.Load(_intermediateFolder, true); currentBuildInfo.CommitFromSHA = _commitFromSHA; currentBuildInfo.CommitToSHA = _commitToSHA; if (_intermediateFolder != null) { currentBuildInfo.PluginHash = ComputePluginHash(_assemblyList); currentBuildInfo.TemplateHash = _templateHash; if (!_cleanupCacheHistory && lastBuildInfo != null) { // Reuse the directory for last incremental if cleanup is disabled currentBuildInfo.DirectoryName = lastBuildInfo.DirectoryName; } else { currentBuildInfo.DirectoryName = IncrementalUtility.CreateRandomDirectory(Environment.ExpandEnvironmentVariables(_intermediateFolder)); } } _postProcessorsManager.IncrementalInitialize(_intermediateFolder, currentBuildInfo, lastBuildInfo, parameters[0].ForcePostProcess, parameters[0].MaxParallelism); var manifests = new List <Manifest>(); bool transformDocument = false; if (parameters.All(p => p.Files.Count == 0)) { Logger.LogWarning( $"No file found, nothing will be generated. Please make sure docfx.json is correctly configured.", code: WarningCodes.Build.EmptyInputFiles); } var noContentFound = true; var emptyContentGroups = new List <string>(); foreach (var parameter in parameters) { if (parameter.CustomLinkResolver != null) { if (_container.TryGetExport(parameter.CustomLinkResolver, out ICustomHrefGenerator chg)) { parameter.ApplyTemplateSettings.HrefGenerator = chg; } else { Logger.LogWarning($"Custom href generator({parameter.CustomLinkResolver}) is not found."); } } FileAbstractLayerBuilder falBuilder; if (_intermediateFolder == null) { falBuilder = FileAbstractLayerBuilder.Default .ReadFromRealFileSystem(EnvironmentContext.BaseDirectory) .WriteToRealFileSystem(parameter.OutputBaseDir); } else { falBuilder = FileAbstractLayerBuilder.Default .ReadFromRealFileSystem(EnvironmentContext.BaseDirectory) .WriteToLink(Path.Combine(_intermediateFolder, currentBuildInfo.DirectoryName)); } if (!string.IsNullOrEmpty(parameter.FALName)) { if (_container.TryGetExport <IInputFileAbstractLayerBuilderProvider>( parameter.FALName, out var provider)) { falBuilder = provider.Create(falBuilder, parameter); } else { Logger.LogWarning($"Input fal builder provider not found, name: {parameter.FALName}."); } } EnvironmentContext.FileAbstractLayerImpl = falBuilder.Create(); if (parameter.ApplyTemplateSettings.TransformDocument) { transformDocument = true; } if (parameter.Files.Count == 0) { manifests.Add(new Manifest()); } else { if (!parameter.Files.EnumerateFiles().Any(s => s.Type == DocumentType.Article)) { if (!string.IsNullOrEmpty(parameter.GroupInfo?.Name)) { emptyContentGroups.Add(parameter.GroupInfo.Name); } } else { noContentFound = false; } parameter.Metadata = _postProcessorsManager.PrepareMetadata(parameter.Metadata); if (!string.IsNullOrEmpty(parameter.VersionName)) { Logger.LogInfo($"Start building for version: {parameter.VersionName}"); } using (new LoggerPhaseScope("BuildCore")) { manifests.Add(BuildCore(parameter, markdownServiceProvider, currentBuildInfo, lastBuildInfo)); } } } if (noContentFound) { Logger.LogWarning( $"No content file found. Please make sure the content section of docfx.json is correctly configured.", code: WarningCodes.Build.EmptyInputContents); } else if (emptyContentGroups.Count > 0) { Logger.LogWarning( $"No content file found in group: {string.Join(",", emptyContentGroups)}. Please make sure the content section of docfx.json is correctly configured.", code: WarningCodes.Build.EmptyInputContents); } using (new LoggerPhaseScope("Postprocess", LogLevel.Verbose)) { var generatedManifest = ManifestUtility.MergeManifest(manifests); generatedManifest.SitemapOptions = parameters.FirstOrDefault()?.SitemapOptions; ManifestUtility.RemoveDuplicateOutputFiles(generatedManifest.Files); ManifestUtility.ApplyLogCodes(generatedManifest.Files, logCodesLogListener.Codes); EnvironmentContext.FileAbstractLayerImpl = FileAbstractLayerBuilder.Default .ReadFromManifest(generatedManifest, parameters[0].OutputBaseDir) .WriteToManifest(generatedManifest, parameters[0].OutputBaseDir) .Create(); using (new PerformanceScope("Process")) { _postProcessorsManager.Process(generatedManifest, outputDirectory); } using (new PerformanceScope("Dereference")) { if (parameters[0].KeepFileLink) { var count = (from f in generatedManifest.Files from o in f.OutputFiles select o.Value into v where v.LinkToPath != null select v).Count(); if (count > 0) { Logger.LogInfo($"Skip dereferencing {count} files."); } } else { generatedManifest.Dereference(parameters[0].OutputBaseDir, parameters[0].MaxParallelism); } } using (new PerformanceScope("SaveManifest")) { // Save to manifest.json EnvironmentContext.FileAbstractLayerImpl = FileAbstractLayerBuilder.Default .ReadFromRealFileSystem(parameters[0].OutputBaseDir) .WriteToRealFileSystem(parameters[0].OutputBaseDir) .Create(); SaveManifest(generatedManifest); } using (new PerformanceScope("Cleanup")) { EnvironmentContext.FileAbstractLayerImpl = null; // overwrite intermediate cache files if (_intermediateFolder != null && transformDocument) { try { if (Logger.WarningCount >= Logger.WarningThrottling) { currentBuildInfo.IsValid = false; currentBuildInfo.Message = $"Warning count {Logger.WarningCount} exceeds throttling {Logger.WarningThrottling}"; } currentBuildInfo.Save(_intermediateFolder); if (_cleanupCacheHistory) { ClearCacheExcept(currentBuildInfo.DirectoryName); } } catch (Exception ex) { Logger.LogWarning($"Error happened while saving cache. Message: {ex.Message}."); } } } } } catch { // Leave cache folder there as it contains historical data // exceptions happens in this build does not corrupt the cache theoretically // however the cache file created by this build will never be cleaned up with DisableIncrementalFolderCleanup option if (_intermediateFolder != null && _cleanupCacheHistory) { ClearCacheExcept(lastBuildInfo?.DirectoryName); } throw; } finally { Logger.UnregisterListener(logCodesLogListener); } }
internal Manifest BuildCore(DocumentBuildParameters parameter, IMarkdownServiceProvider markdownServiceProvider, BuildInfo currentBuildInfo, BuildInfo lastBuildInfo) { using (var builder = new SingleDocumentBuilder { CurrentBuildInfo = currentBuildInfo, LastBuildInfo = lastBuildInfo, IntermediateFolder = _intermediateFolder, MetadataValidators = MetadataValidators.Concat(GetMetadataRules(parameter)).ToList(), Processors = Processors, MarkdownServiceProvider = markdownServiceProvider, }) { return(builder.Build(parameter)); } }