Beispiel #1
0
        private void WriteLine(ILogItem item)
        {
            if (item == null)
            {
                throw new ArgumentNullException(nameof(item));
            }
            if (item.File == null)
            {
                return;
            }
            string fileFromWorkingDir = TypeForwardedToStringExtension.BackSlashToForwardSlash(item.File);

            if (!TypeForwardedToPathUtility.IsRelativePath(item.File))
            {
                fileFromWorkingDir = TypeForwardedToPathUtility.MakeRelativePath(EnvironmentContext.BaseDirectory, item.File);
            }
            List <LogItem> logsPerFile;

            if (!_logs.TryGetValue(fileFromWorkingDir, out logsPerFile))
            {
                logsPerFile = _logs[fileFromWorkingDir] = new List <LogItem>();
            }
            logsPerFile.Add(new LogItem
            {
                File     = TypeForwardedToStringExtension.BackSlashToForwardSlash(item.File),
                Line     = item.Line,
                LogLevel = item.LogLevel,
                Message  = item.Message,
                Phase    = item.Phase,
            });
        }
Beispiel #2
0
        private bool TryExportResourceFiles(IEnumerable <string> resourceNames, string outputDirectory, bool overwrite, string regexFilter = null)
        {
            if (string.IsNullOrEmpty(outputDirectory))
            {
                throw new ArgumentNullException(nameof(outputDirectory));
            }
            if (!resourceNames.Any())
            {
                return(false);
            }
            bool isEmpty = true;

            using (new LoggerPhaseScope("ExportResourceFiles", true))
                using (var templateResource = CreateTemplateResource(resourceNames))
                {
                    if (templateResource.IsEmpty)
                    {
                        Logger.Log(LogLevel.Warning, $"No resource found for [{TypeForwardedToStringExtension.ToDelimitedString(resourceNames)}].");
                    }
                    else
                    {
                        foreach (var pair in templateResource.GetResourceStreams(regexFilter))
                        {
                            var outputPath = Path.Combine(outputDirectory, pair.Key);
                            CopyResource(pair.Value, outputPath, overwrite);
                            Logger.Log(LogLevel.Verbose, $"File {pair.Key} copied to {outputPath}.");
                            isEmpty = false;
                        }
                    }

                    return(!isEmpty);
                }
        }
Beispiel #3
0
 public ExtractMetadataWorker(ExtractMetadataInputModel input, bool rebuild, bool useCompatibilityFileName)
 {
     _rawInput                  = input;
     _validInput                = ValidateInput(input);
     _rebuild                   = rebuild;
     _shouldSkipMarkup          = input.ShouldSkipMarkup;
     _preserveRawInlineComments = input.PreserveRawInlineComments;
     _filterConfigFile          = TypeForwardedToStringExtension.ToNormalizedFullPath(input.FilterConfigFile);
     _useCompatibilityFileName  = useCompatibilityFileName;
 }
Beispiel #4
0
        private static string GetRelativeResourceKey(string templateName, string relativePath)
        {
            if (string.IsNullOrEmpty(templateName))
            {
                return(relativePath);
            }

            // Make sure resource keys are combined using '/'
            return(TypeForwardedToStringExtension.ForwardSlashCombine(TypeForwardedToStringExtension.ToNormalizedPath(Path.GetDirectoryName(templateName)), relativePath));
        }
Beispiel #5
0
        private static string ExportModel(object model, string modelFileRelativePath, ExportSettings settings)
        {
            if (model == null)
            {
                return(null);
            }
            var outputFolder = settings.OutputFolder;

            string modelPath = Path.Combine(outputFolder ?? string.Empty, settings.PathRewriter(modelFileRelativePath));

            JsonUtility.Serialize(modelPath, model);
            return(TypeForwardedToStringExtension.ToDisplayPath(modelPath));
        }
Beispiel #6
0
        private void PrepareBuild(DocumentBuildContext context, IEnumerable <HostService> hostServices)
        {
            var incrementalContext = context.IncrementalBuildContext;
            var lbv = incrementalContext?.LastBuildVersionInfo;
            var cbv = incrementalContext?.CurrentBuildVersionInfo;

            if (ShouldTraceIncrementalInfo)
            {
                var ldg = lbv?.Dependency;
                var cdg = cbv.Dependency;
                if (ldg != null)
                {
                    // reregister dependency types from last dependency graph
                    using (new LoggerPhaseScope("RegisterDependencyTypeFromLastBuild", true))
                    {
                        cdg.RegisterDependencyType(ldg.DependencyTypes.Values);
                    }

                    // restore dependency graph from last dependency graph
                    using (new LoggerPhaseScope("ReportDependencyFromLastBuild", true))
                    {
                        cdg.ReportDependency(from r in ldg.ReportedBys
                                             from i in ldg.GetDependencyReportedBy(r)
                                             select i);
                    }
                }
            }
            foreach (var hostService in hostServices)
            {
                hostService.SourceFiles = context.AllSourceFiles;
                foreach (var m in hostService.Models)
                {
                    if (m.LocalPathFromRepoRoot == null)
                    {
                        m.LocalPathFromRepoRoot = TypeForwardedToStringExtension.ToDisplayPath(Path.Combine(m.BaseDir, m.File));
                    }
                    if (m.LocalPathFromRoot == null)
                    {
                        m.LocalPathFromRoot = TypeForwardedToStringExtension.ToDisplayPath(Path.Combine(m.BaseDir, m.File));
                    }
                }
                if (ShouldTraceIncrementalInfo)
                {
                    hostService.DependencyGraph = cbv.Dependency;
                    using (new LoggerPhaseScope("RegisterDependencyTypeFromProcessor", true))
                    {
                        RegisterDependencyType(hostService);
                    }
                }
            }
        }
Beispiel #7
0
        private static LoggerFileScope GetFileScope(ImmutableStack <string> parents)
        {
            if (!parents.IsEmpty)
            {
                var path = TypeForwardedToStringExtension.ToDisplayPath(parents.Peek());

                if (!string.IsNullOrEmpty(path))
                {
                    return(new LoggerFileScope(path));
                }
            }

            return(null);
        }
Beispiel #8
0
        private static void CopyFromCachedResult(BuildInfo buildInfo, IEnumerable <string> inputs, string outputFolder)
        {
            var outputFolderSource = buildInfo.OutputFolder;
            var relativeFiles      = buildInfo.RelatvieOutputFiles;

            if (relativeFiles == null)
            {
                Logger.Log(LogLevel.Warning, $"No metadata is generated for '{TypeForwardedToStringExtension.ToDelimitedString(inputs)}'.");
                return;
            }

            Logger.Log(LogLevel.Info, $"'{TypeForwardedToStringExtension.ToDelimitedString(inputs)}' keep up-to-date since '{buildInfo.TriggeredUtcTime.ToString()}', cached result from '{buildInfo.OutputFolder}' is used.");
            TypeForwardedToPathUtility.CopyFilesToFolder(relativeFiles.Select(s => Path.Combine(outputFolderSource, s)), outputFolderSource, outputFolder, true, s => Logger.Log(LogLevel.Info, s), null);
        }
Beispiel #9
0
        public void Exec(SubCommandRunningContext context)
        {
            string outputFolder = null;

            try
            {
                var config          = new DefaultConfigModel();
                var questionContext = new QuestionContext
                {
                    Quiet = _options.Quiet
                };
                foreach (var question in _selectorQuestions)
                {
                    question.Process(config, questionContext);
                }

                foreach (var question in _overallQuestion)
                {
                    question.Process(config, questionContext);
                }

                if (questionContext.ContainsMetadata)
                {
                    foreach (var question in _metadataQuestions)
                    {
                        question.Process(config, questionContext);
                    }
                }

                foreach (var question in _buildQuestions)
                {
                    question.Process(config, questionContext);
                }

                if (_options.OnlyConfigFile)
                {
                    GenerateConfigFile(_options.OutputFolder, config);
                }
                else
                {
                    outputFolder = TypeForwardedToStringExtension.ToDisplayPath(Path.GetFullPath(string.IsNullOrEmpty(_options.OutputFolder) ? DefaultOutputFolder : _options.OutputFolder));
                    GenerateSeedProject(outputFolder, config);
                }
            }
            catch (Exception e)
            {
                throw new DocfxInitException($"Error with init docfx project under \"{outputFolder}\" : {e.Message}", e);
            }
        }
Beispiel #10
0
        private static void GenerateConfigFile(string outputFolder, object config)
        {
            var path = TypeForwardedToStringExtension.ToDisplayPath(Path.Combine(outputFolder ?? string.Empty, ConfigName));

            if (File.Exists(path))
            {
                if (!ProcessOverwriteQuestion($"Config file \"{path}\" already exists, do you want to overwrite this file?"))
                {
                    return;
                }
            }

            SaveConfigFile(path, config);
            $"Successfully generated default docfx config file to {path}".WriteLineToConsole(ConsoleColor.Green);
        }
Beispiel #11
0
        public override Stream GetResourceStream(string name)
        {
            if (IsEmpty)
            {
                return(null);
            }

            // incase relative path is combined by backslash \
            if (!Names.Contains(TypeForwardedToStringExtension.ToNormalizedPath(name.Trim()), ResourceComparer))
            {
                return(null);
            }
            var filePath = Path.Combine(_directory, name);

            return(new FileStream(filePath, FileMode.Open, FileAccess.Read));
        }
Beispiel #12
0
 private static string ComputePluginHash(List <Assembly> assemblyList)
 {
     if (assemblyList?.Count > 0)
     {
         var builder = new StringBuilder();
         foreach (var item in
                  from assembly in assemblyList
                  select assembly.FullName + "@" + assembly.GetCustomAttribute <AssemblyFileVersionAttribute>()?.Version.ToString()
                  into item
                  orderby item
                  select item)
         {
             builder.AppendLine(item);
         }
         return(TypeForwardedToStringExtension.GetMd5String(builder.ToString()));
     }
     return(string.Empty);
 }
Beispiel #13
0
        public void SaveToCache(IEnumerable <string> inputProjects, IDictionary <string, List <string> > containedFiles, DateTime triggeredTime, string outputFolder, IList <string> fileRelativePaths, bool shouldSkipMarkup)
        {
            var       key          = TypeForwardedToStringExtension.GetNormalizedFullPathKey(inputProjects);
            DateTime  completeTime = DateTime.UtcNow;
            BuildInfo info         = new BuildInfo
            {
                InputFilesKey       = key,
                ContainedFiles      = containedFiles,
                TriggeredUtcTime    = triggeredTime,
                CompleteUtcTime     = completeTime,
                OutputFolder        = TypeForwardedToStringExtension.ToNormalizedFullPath(outputFolder),
                RelatvieOutputFiles = TypeForwardedToStringExtension.GetNormalizedPathList(fileRelativePaths),
                BuildAssembly       = AssemblyName,
                ShouldSkipMarkup    = shouldSkipMarkup
            };

            this.SaveConfig(key, info);
        }
Beispiel #14
0
        public override FileModel Load(FileAndType file, ImmutableDictionary <string, object> metadata)
        {
            switch (file.Type)
            {
            case DocumentType.Article:
                var filePath       = Path.Combine(file.BaseDir, file.File);
                var swaggerContent = File.ReadAllText(filePath);
                var swagger        = SwaggerJsonParser.Parse(swaggerContent);
                swagger.Metadata[DocumentTypeKey] = RestApiDocumentType;
                swagger.Raw = swaggerContent;
                CheckOperationId(swagger, file.File);

                var repoInfo = GitUtility.GetGitDetail(filePath);
                if (repoInfo != null)
                {
                    swagger.Metadata["source"] = new SourceDetail()
                    {
                        Remote = repoInfo
                    };
                }

                swagger.Metadata = MergeMetadata(swagger.Metadata, metadata);
                var vm = SwaggerModelConverter.FromSwaggerModel(swagger);
                var displayLocalPath = TypeForwardedToPathUtility.MakeRelativePath(EnvironmentContext.BaseDirectory, file.FullPath);

                return(new FileModel(file, vm, serializer: Environment.Is64BitProcess?null: new BinaryFormatter())
                {
                    Uids = new[] { new UidDefinition(vm.Uid, displayLocalPath) }
                    .Concat(from item in vm.Children select new UidDefinition(item.Uid, displayLocalPath))
                    .Concat(from tag in vm.Tags select new UidDefinition(tag.Uid, displayLocalPath)).ToImmutableArray(),
                    LocalPathFromRepoRoot = repoInfo?.RelativePath ?? TypeForwardedToStringExtension.ToDisplayPath(filePath),
                    LocalPathFromRoot = displayLocalPath
                });

            case DocumentType.Overwrite:
                // TODO: Refactor current behavior that overwrite file is read multiple times by multiple processors
                return(OverwriteDocumentReader.Read(file));

            default:
                throw new NotSupportedException();
            }
        }
Beispiel #15
0
        private static IEnumerable <string> GetTransitiveProjectReferences(ConcurrentDictionary <string, Project> projectCache, Project project)
        {
            var solution = project.Solution;

            foreach (var pr in project.ProjectReferences)
            {
                var refProject = solution.GetProject(pr.ProjectId);
                var path       = TypeForwardedToStringExtension.ToNormalizedFullPath(refProject.FilePath);
                if (projectCache.ContainsKey(path))
                {
                    yield return(path);
                }
                else
                {
                    foreach (var rpr in GetTransitiveProjectReferences(projectCache, refProject))
                    {
                        yield return(rpr);
                    }
                }
            }
        }
Beispiel #16
0
        public override FileModel Load(FileAndType file, ImmutableDictionary <string, object> metadata)
        {
            string uid = null;
            Dictionary <string, object> content = null;
            var metafile = Path.Combine(file.BaseDir, file.File.TrimEnd('.') + ".meta");

            if (File.Exists(metafile))
            {
                content = YamlUtility.Deserialize <Dictionary <string, object> >(metafile);
                if (content != null)
                {
                    foreach (var item in metadata)
                    {
                        if (!content.ContainsKey(item.Key))
                        {
                            content[item.Key] = item.Value;
                        }
                        if (item.Key == Constants.PropertyName.Uid)
                        {
                            uid = item.Value as string;
                        }
                    }
                }
            }
            if (content == null)
            {
                content = metadata.ToDictionary(p => p.Key, p => p.Value);
            }

            var filePath         = Path.Combine(file.BaseDir, file.File);
            var repoDetail       = GitUtility.GetGitDetail(filePath);
            var displayLocalPath = TypeForwardedToPathUtility.MakeRelativePath(EnvironmentContext.BaseDirectory, file.FullPath);

            return(new FileModel(file, content)
            {
                Uids = string.IsNullOrEmpty(uid) ? ImmutableArray <UidDefinition> .Empty : ImmutableArray <UidDefinition> .Empty.Add(new UidDefinition(uid, displayLocalPath)),
                LocalPathFromRepoRoot = repoDetail?.RelativePath ?? TypeForwardedToStringExtension.ToDisplayPath(Path.Combine(file.BaseDir, file.File)),
                LocalPathFromRoot = displayLocalPath
            });
        }
Beispiel #17
0
        public void ProcessTocWithCircularReferenceShouldFail()
        {
            var            referencedToc = _fileCreator.CreateFile($@"
- name: Topic
  href: TOC.md
", FileType.YamlToc, "sub1");
            var            subToc        = _fileCreator.CreateFile($@"
#Topic
##[ReferencedToc](Toc.yml)
", FileType.MarkdownToc, "sub1");
            var            content       = $@"
- name: Topic1
  href: {subToc}
";
            var            toc           = _fileCreator.CreateFile(content, FileType.YamlToc);
            FileCollection files         = new FileCollection(_inputFolder);

            files.Add(DocumentType.Article, new[] { toc, subToc });
            var e = Assert.Throws <DocumentException>(() => BuildDocument(files));

            Assert.Equal($"Circular reference to {TypeForwardedToStringExtension.ToDisplayPath(Path.GetFullPath(Path.Combine(_inputFolder, subToc)))} is found in {TypeForwardedToStringExtension.ToDisplayPath(Path.GetFullPath(Path.Combine(_inputFolder, referencedToc)))}", e.Message, true);
        }
Beispiel #18
0
        private static Task <Tuple <MetadataItem, bool> > GetProjectMetadataFromCacheAsync(Project project, Compilation compilation, string outputFolder, ProjectDocumentCache documentCache, bool forceRebuild, bool shouldSkipMarkup, bool preserveRawInlineComments, string filterConfigFile, IReadOnlyDictionary <Compilation, IEnumerable <IMethodSymbol> > extensionMethods, bool isReferencedProjectRebuilt)
        {
            var projectFilePath = project.FilePath;
            var k = documentCache.GetDocuments(projectFilePath);

            return(GetMetadataFromProjectLevelCacheAsync(
                       project,
                       new[] { projectFilePath, filterConfigFile },
                       s => Task.FromResult(forceRebuild || s.AreFilesModified(k.Concat(new string[] { filterConfigFile })) || isReferencedProjectRebuilt),
                       s => Task.FromResult(compilation),
                       s =>
            {
                return new Dictionary <string, List <string> > {
                    { TypeForwardedToStringExtension.ToNormalizedFullPath(s.FilePath), k.ToList() }
                };
            },
                       outputFolder,
                       preserveRawInlineComments,
                       shouldSkipMarkup,
                       filterConfigFile,
                       extensionMethods));
        }
Beispiel #19
0
        public static FileMapping ExpandFileMapping(string baseDirectory, FileMapping fileMapping)
        {
            if (fileMapping == null)
            {
                return(null);
            }

            if (fileMapping.Expanded)
            {
                return(fileMapping);
            }

            var expandedFileMapping = new FileMapping();

            foreach (var item in fileMapping.Items)
            {
                // Use local variable to avoid different items influencing each other
                var src     = Path.Combine(baseDirectory, item.SourceFolder ?? string.Empty);
                var options = GetMatchOptionsFromItem(item);
                var files   = FileGlob.GetFiles(src, item.Files, item.Exclude, options).ToArray();
                if (files.Length == 0)
                {
                    var currentSrcFullPath = string.IsNullOrEmpty(src) ? Directory.GetCurrentDirectory() : Path.GetFullPath(src);
                    Logger.LogInfo($"No files are found with glob pattern {TypeForwardedToStringExtension.ToDelimitedString(item.Files) ?? "<none>"}, excluding {TypeForwardedToStringExtension.ToDelimitedString(item.Exclude) ?? "<none>"}, under directory \"{currentSrcFullPath}\"");
                    CheckPatterns(item.Files);
                }
                expandedFileMapping.Add(
                    new FileMappingItem
                {
                    SourceFolder      = src,
                    Files             = new FileItems(files),
                    DestinationFolder = item.DestinationFolder
                });
            }

            expandedFileMapping.Expanded = true;
            return(expandedFileMapping);
        }
Beispiel #20
0
        public string GetTemplatesHash()
        {
            var sb = new StringBuilder();

            using (var templateResource = CreateTemplateResource(_templates))
                using (var md5 = MD5.Create())
                {
                    foreach (var name in from n in templateResource.Names ?? Enumerable.Empty <string>()
                             orderby n
                             select n)
                    {
                        sb.Append(name);
                        sb.Append(":");
                        sb.Append(
                            Convert.ToBase64String(
                                md5.ComputeHash(
                                    templateResource.GetResourceStream(
                                        name))));
                        sb.Append(";");
                    }
                }
            return(TypeForwardedToStringExtension.GetMd5String(sb.ToString()));
        }
Beispiel #21
0
        private Dictionary <string, string> GetListContent(XPathNavigator navigator, string xpath, string contentType, ITripleSlashCommentParserContext context)
        {
            var iterator = navigator.Select(xpath);
            var result   = new Dictionary <string, string>();

            if (iterator == null)
            {
                return(result);
            }
            foreach (XPathNavigator nav in iterator)
            {
                string name        = nav.GetAttribute("name", string.Empty);
                string description = GetXmlValue(nav);
                if (!string.IsNullOrEmpty(name))
                {
                    if (result.ContainsKey(name))
                    {
                        string path = context.Source.Remote != null?Path.Combine(context.Source.Remote.LocalWorkingDirectory, context.Source.Remote.RelativePath) : context.Source.Path;

                        Logger.LogWarning($"Duplicate {contentType} '{name}' found in comments, the latter one is ignored.", null, TypeForwardedToStringExtension.ToDisplayPath(path), context.Source.StartLine.ToString());
                    }
                    else
                    {
                        result.Add(name, description);
                    }
                }
            }

            return(result);
        }
Beispiel #22
0
        internal List <ManifestItem> Process(List <InternalManifestItem> manifest, DocumentBuildContext context, ApplyTemplateSettings settings, IDictionary <string, object> globals = null)
        {
            using (new LoggerPhaseScope("Apply Templates", true))
            {
                if (globals == null)
                {
                    globals = Tokens.ToDictionary(pair => pair.Key, pair => (object)pair.Value);
                }

                var documentTypes             = manifest.Select(s => s.DocumentType).Distinct();
                var notSupportedDocumentTypes = documentTypes.Where(s => s != "Resource" && _templateCollection[s] == null);
                if (notSupportedDocumentTypes.Any())
                {
                    Logger.LogWarning($"There is no template processing document type(s): {TypeForwardedToStringExtension.ToDelimitedString(notSupportedDocumentTypes)}");
                }
                Logger.LogInfo($"Applying templates to {manifest.Count} model(s)...");

                if (settings.Options.HasFlag(ApplyTemplateOptions.TransformDocument))
                {
                    var templatesInUse = documentTypes.Select(s => _templateCollection[s]).Where(s => s != null).ToList();
                    ProcessDependencies(settings.OutputFolder, templatesInUse);
                }
                else
                {
                    Logger.LogInfo("Dryrun, no template will be applied to the documents.");
                }

                var templateManifest = ProcessCore(manifest, context, settings, globals);
                return(templateManifest);
            }
        }
Beispiel #23
0
        public BuildInfo GetValidConfig(IEnumerable <string> inputProjects)
        {
            var key = TypeForwardedToStringExtension.GetNormalizedFullPathKey(inputProjects);

            return(GetConfig(key));
        }
Beispiel #24
0
 private static void FillProjectDependencyGraph(ConcurrentDictionary <string, Project> projectCache, ConcurrentDictionary <string, List <string> > projectDependencyGraph, Project project)
 {
     projectDependencyGraph.GetOrAdd(TypeForwardedToStringExtension.ToNormalizedFullPath(project.FilePath), GetTransitiveProjectReferences(projectCache, project).Distinct().ToList());
 }
Beispiel #25
0
        private async Task SaveAllMembersFromCacheAsync(IEnumerable <string> inputs, string outputFolder, bool forceRebuild)
        {
            var projectCache = new ConcurrentDictionary <string, Project>();
            // Project<=>Documents
            var      documentCache          = new ProjectDocumentCache();
            var      projectDependencyGraph = new ConcurrentDictionary <string, List <string> >();
            DateTime triggeredTime          = DateTime.UtcNow;
            var      solutions = inputs.Where(s => IsSupportedSolution(s));
            var      projects  = inputs.Where(s => IsSupportedProject(s));

            var sourceFiles = inputs.Where(s => IsSupportedSourceFile(s));

            // Exclude not supported files from inputs
            inputs = solutions.Concat(projects).Concat(sourceFiles);

            // Add filter config file into inputs and cache
            if (!string.IsNullOrEmpty(_filterConfigFile))
            {
                inputs = inputs.Concat(new string[] { _filterConfigFile });
                documentCache.AddDocument(_filterConfigFile, _filterConfigFile);
            }

            // No matter is incremental or not, we have to load solutions into memory
            await solutions.ForEachInParallelAsync(async path =>
            {
                documentCache.AddDocument(path, path);
                var solution = await GetSolutionAsync(path);
                if (solution != null)
                {
                    foreach (var project in solution.Projects)
                    {
                        var filePath = project.FilePath;

                        // If the project is csproj/vbproj, add to project dictionary, otherwise, ignore
                        if (IsSupportedProject(filePath))
                        {
                            projectCache.GetOrAdd(TypeForwardedToStringExtension.ToNormalizedFullPath(project.FilePath), s => project);
                        }
                        else
                        {
                            var value = string.Join(",", SupportedExtensions);
                            Logger.Log(LogLevel.Warning, $"Project {filePath} inside solution {path} is not supported, supported file extension are: {value}. The project will be ignored.");
                        }
                    }
                }
            }, 60);

            // Load additional projects out if it is not contained in expanded solution
            projects = projects.Except(projectCache.Keys).Distinct();

            await projects.ForEachInParallelAsync(async path =>
            {
                var project = await GetProjectAsync(path);
                if (project != null)
                {
                    projectCache.GetOrAdd(path, s => project);
                }
            }, 60);

            foreach (var item in projectCache)
            {
                var path    = item.Key;
                var project = item.Value;
                documentCache.AddDocument(path, path);
                documentCache.AddDocuments(path, project.Documents.Select(s => s.FilePath));
                documentCache.AddDocuments(path, project.MetadataReferences
                                           .Where(s => s is PortableExecutableReference)
                                           .Select(s => ((PortableExecutableReference)s).FilePath));
                FillProjectDependencyGraph(projectCache, projectDependencyGraph, project);
            }

            documentCache.AddDocuments(sourceFiles);

            // Incremental check for inputs as a whole:
            var applicationCache = ApplicationLevelCache.Get(inputs);

            if (!forceRebuild)
            {
                BuildInfo buildInfo = applicationCache.GetValidConfig(inputs);
                if (buildInfo != null && buildInfo.ShouldSkipMarkup == _shouldSkipMarkup)
                {
                    IncrementalCheck check = new IncrementalCheck(buildInfo);
                    // 1. Check if sln files/ project files and its contained documents/ source files are modified
                    var projectModified = check.AreFilesModified(documentCache.Documents);

                    if (!projectModified)
                    {
                        // 2. Check if documents/ assembly references are changed in a project
                        // e.g. <Compile Include="*.cs* /> and file added/deleted
                        foreach (var project in projectCache.Values)
                        {
                            var key = TypeForwardedToStringExtension.ToNormalizedFullPath(project.FilePath);
                            IEnumerable <string> currentContainedFiles = documentCache.GetDocuments(project.FilePath);
                            var previousDocumentCache = new ProjectDocumentCache(buildInfo.ContainedFiles);

                            IEnumerable <string> previousContainedFiles = previousDocumentCache.GetDocuments(project.FilePath);
                            if (previousContainedFiles != null && currentContainedFiles != null)
                            {
                                projectModified = !previousContainedFiles.SequenceEqual(currentContainedFiles);
                            }
                            else
                            {
                                // When one of them is not null, project is modified
                                if (!object.Equals(previousContainedFiles, currentContainedFiles))
                                {
                                    projectModified = true;
                                }
                            }
                            if (projectModified)
                            {
                                break;
                            }
                        }
                    }

                    if (!projectModified)
                    {
                        // Nothing modified, use the result in cache
                        try
                        {
                            CopyFromCachedResult(buildInfo, inputs, outputFolder);
                            return;
                        }
                        catch (Exception e)
                        {
                            Logger.Log(LogLevel.Warning, $"Unable to copy results from cache: {e.Message}. Rebuild starts.");
                        }
                    }
                }
            }

            // Build all the projects to get the output and save to cache
            List <MetadataItem> projectMetadataList = new List <MetadataItem>();
            ConcurrentDictionary <string, bool>        projectRebuildInfo = new ConcurrentDictionary <string, bool>();
            ConcurrentDictionary <string, Compilation> compilationCache   = await GetProjectCompilationAsync(projectCache);

            var extensionMethods = GetAllExtensionMethods(compilationCache.Values);

            foreach (var key in GetTopologicalSortedItems(projectDependencyGraph))
            {
                var dependencyRebuilt     = projectDependencyGraph[key].Any(r => projectRebuildInfo[r]);
                var projectMetadataResult = await GetProjectMetadataFromCacheAsync(projectCache[key], compilationCache[key], outputFolder, documentCache, forceRebuild, _shouldSkipMarkup, _preserveRawInlineComments, _filterConfigFile, extensionMethods, dependencyRebuilt);

                var projectMetadata = projectMetadataResult.Item1;
                if (projectMetadata != null)
                {
                    projectMetadataList.Add(projectMetadata);
                }
                projectRebuildInfo[key] = projectMetadataResult.Item2;
            }

            var csFiles = sourceFiles.Where(s => IsSupportedCSSourceFile(s));

            if (csFiles.Any())
            {
                var csContent     = string.Join(Environment.NewLine, csFiles.Select(s => File.ReadAllText(s)));
                var csCompilation = CompilationUtility.CreateCompilationFromCsharpCode(csContent);
                if (csCompilation != null)
                {
                    var csMetadata = await GetFileMetadataFromCacheAsync(csFiles, csCompilation, outputFolder, forceRebuild, _shouldSkipMarkup, _preserveRawInlineComments, _filterConfigFile, extensionMethods);

                    if (csMetadata != null)
                    {
                        projectMetadataList.Add(csMetadata.Item1);
                    }
                }
            }

            var vbFiles = sourceFiles.Where(s => IsSupportedVBSourceFile(s));

            if (vbFiles.Any())
            {
                var vbContent     = string.Join(Environment.NewLine, vbFiles.Select(s => File.ReadAllText(s)));
                var vbCompilation = CompilationUtility.CreateCompilationFromVBCode(vbContent);
                if (vbCompilation != null)
                {
                    var vbMetadata = await GetFileMetadataFromCacheAsync(vbFiles, vbCompilation, outputFolder, forceRebuild, _preserveRawInlineComments, _shouldSkipMarkup, _filterConfigFile, extensionMethods);

                    if (vbMetadata != null)
                    {
                        projectMetadataList.Add(vbMetadata.Item1);
                    }
                }
            }

            var allMemebers   = MergeYamlProjectMetadata(projectMetadataList);
            var allReferences = MergeYamlProjectReferences(projectMetadataList);

            if (allMemebers == null || allMemebers.Count == 0)
            {
                var value = TypeForwardedToStringExtension.ToDelimitedString(projectMetadataList.Select(s => s.Name));
                Logger.Log(LogLevel.Warning, $"No metadata is generated for {value}.");
                applicationCache.SaveToCache(inputs, null, triggeredTime, outputFolder, null, _shouldSkipMarkup);
            }
            else
            {
                // TODO: need an intermediate folder? when to clean it up?
                // Save output to output folder
                var outputFiles = ResolveAndExportYamlMetadata(allMemebers, allReferences, outputFolder, _validInput.IndexFileName, _validInput.TocFileName, _validInput.ApiFolderName, _preserveRawInlineComments, _shouldSkipMarkup, _rawInput.ExternalReferences, _useCompatibilityFileName);
                applicationCache.SaveToCache(inputs, documentCache.Cache, triggeredTime, outputFolder, outputFiles, _shouldSkipMarkup);
            }
        }
Beispiel #26
0
        private static ExtractMetadataInputModel ValidateInput(ExtractMetadataInputModel input)
        {
            if (input == null)
            {
                return(null);
            }

            if (input.Items == null || input.Items.Count == 0)
            {
                Logger.Log(LogLevel.Warning, "No source project or file to process, exiting...");
                return(null);
            }

            var items = new Dictionary <string, List <string> >();

            // 1. Input file should exists
            foreach (var pair in input.Items)
            {
                if (string.IsNullOrWhiteSpace(pair.Key))
                {
                    var value = string.Join(", ", pair.Value);
                    Logger.Log(LogLevel.Warning, $"Empty folder name is found: '{pair.Key}': '{value}'. It is not supported, skipping.");
                    continue;
                }

                // HashSet to guarantee the input file path is unique
                HashSet <string> validFilePath = new HashSet <string>();
                foreach (var inputFilePath in pair.Value)
                {
                    if (!string.IsNullOrEmpty(inputFilePath))
                    {
                        if (File.Exists(inputFilePath))
                        {
                            if (IsSupported(inputFilePath))
                            {
                                var path = TypeForwardedToStringExtension.ToNormalizedFullPath(inputFilePath);
                                validFilePath.Add(path);
                            }
                            else
                            {
                                var value = string.Join(",", SupportedExtensions);
                                Logger.Log(LogLevel.Warning, $"File {inputFilePath} is not supported, supported file extension are: {value}. The file will be ignored.");
                            }
                        }
                        else
                        {
                            Logger.Log(LogLevel.Warning, $"File {inputFilePath} does not exist, will be ignored.");
                        }
                    }
                }

                if (validFilePath.Count > 0)
                {
                    items.Add(pair.Key, validFilePath.ToList());
                }
            }

            if (items.Count > 0)
            {
                var clone = input.Clone();
                clone.Items = items;
                return(clone);
            }
            else
            {
                return(null);
            }
        }
Beispiel #27
0
        private Manifest BuildCore(DocumentBuildParameters parameters)
        {
            using (new LoggerPhaseScope(PhaseName, true))
            {
                Logger.LogInfo($"Max parallelism is {parameters.MaxParallelism}.");
                Directory.CreateDirectory(parameters.OutputBaseDir);
                var context = new DocumentBuildContext(
                    Path.Combine(Directory.GetCurrentDirectory(), parameters.OutputBaseDir),
                    parameters.Files.EnumerateFiles(),
                    parameters.ExternalReferencePackages,
                    parameters.XRefMaps,
                    parameters.MaxParallelism,
                    parameters.Files.DefaultBaseDir);
                if (ShouldTraceIncrementalInfo)
                {
                    context.IncrementalBuildContext = IncrementalBuildContext.Create(parameters, CurrentBuildInfo, LastBuildInfo, IntermediateFolder);
                    Logger.RegisterListener(context.IncrementalBuildContext.CurrentBuildVersionInfo.BuildMessage.GetListener());
                    if (context.IncrementalBuildContext.CanVersionIncremental)
                    {
                        context.IncrementalBuildContext.LoadChanges();
                        Logger.LogVerbose($"Before expanding dependency before build, changes: {JsonUtility.Serialize(context.IncrementalBuildContext.ChangeDict, Formatting.Indented)}");
                        var dependencyGraph = context.IncrementalBuildContext.LastBuildVersionInfo.Dependency;
                        context.IncrementalBuildContext.ExpandDependency(dependencyGraph, d => dependencyGraph.DependencyTypes[d.Type].Phase == BuildPhase.Build || dependencyGraph.DependencyTypes[d.Type].TriggerBuild);
                        Logger.LogVerbose($"After expanding dependency before build, changes: {JsonUtility.Serialize(context.IncrementalBuildContext.ChangeDict, Formatting.Indented)}");
                    }
                }

                Logger.LogVerbose("Start building document...");

                // Start building document...
                List <HostService> hostServices = null;
                try
                {
                    using (var templateProcessor = parameters.TemplateManager?.GetTemplateProcessor(context, parameters.MaxParallelism) ?? TemplateProcessor.DefaultProcessor)
                    {
                        IMarkdownService markdownService;
                        using (new LoggerPhaseScope("CreateMarkdownService", true))
                        {
                            markdownService = CreateMarkdownService(parameters, templateProcessor.Tokens.ToImmutableDictionary());
                        }

                        using (new LoggerPhaseScope("Load", true))
                        {
                            hostServices = GetInnerContexts(parameters, Processors, templateProcessor, markdownService, context).ToList();
                        }

                        var manifest = BuildCore(hostServices, context, parameters.VersionName).ToList();

                        // Use manifest from now on
                        using (new LoggerPhaseScope("UpdateContext", true))
                        {
                            UpdateContext(context);
                        }

                        // Run getOptions from Template
                        using (new LoggerPhaseScope("FeedOptions", true))
                        {
                            FeedOptions(manifest, context);
                        }

                        // Template can feed back xref map, actually, the anchor # location can only be determined in template
                        using (new LoggerPhaseScope("FeedXRefMap", true))
                        {
                            FeedXRefMap(manifest, context);
                        }

                        using (new LoggerPhaseScope("UpdateHref", true))
                        {
                            UpdateHref(manifest, context);
                        }

                        // Afterwards, m.Item.Model.Content is always IDictionary
                        using (new LoggerPhaseScope("ApplySystemMetadata", true))
                        {
                            ApplySystemMetadata(manifest, context);
                        }

                        // Register global variables after href are all updated
                        IDictionary <string, object> globalVariables;
                        using (new LoggerPhaseScope("FeedGlobalVariables", true))
                        {
                            globalVariables = FeedGlobalVariables(templateProcessor.Tokens, manifest, context);
                        }

                        // processor to add global variable to the model
                        foreach (var m in templateProcessor.Process(manifest.Select(s => s.Item).ToList(), context, parameters.ApplyTemplateSettings, globalVariables))
                        {
                            context.ManifestItems.Add(m);
                        }
                        return(new Manifest
                        {
                            Files = context.ManifestItems.ToList(),
                            Homepages = GetHomepages(context),
                            XRefMap = ExportXRefMap(parameters, context),
                            SourceBasePath = TypeForwardedToStringExtension.ToNormalizedPath(EnvironmentContext.BaseDirectory)
                        });
                    }
                }
                finally
                {
                    if (hostServices != null)
                    {
                        foreach (var item in hostServices)
                        {
                            Cleanup(item);
                            item.Dispose();
                        }
                    }
                }
            }
        }
Beispiel #28
0
        private static void GenerateSeedProject(string outputFolder, DefaultConfigModel config)
        {
            if (Directory.Exists(outputFolder))
            {
                if (!ProcessOverwriteQuestion($"Output folder \"{outputFolder}\" already exists. Do you still want to generate files into this folder? You can use -o command option to specify the folder name"))
                {
                    return;
                }
            }
            else
            {
                Directory.CreateDirectory(outputFolder);
            }

            // 1. Create default files
            var srcFolder     = Path.Combine(outputFolder, "src");
            var apiFolder     = Path.Combine(outputFolder, "api");
            var apidocFolder  = Path.Combine(outputFolder, "apidoc");
            var articleFolder = Path.Combine(outputFolder, "articles");
            var imageFolder   = Path.Combine(outputFolder, "images");
            var folders       = new string[] { srcFolder, apiFolder, apidocFolder, articleFolder, imageFolder };

            foreach (var folder in folders)
            {
                Directory.CreateDirectory(folder);
                $"Created folder {TypeForwardedToStringExtension.ToDisplayPath(folder)}".WriteLineToConsole(ConsoleColor.Gray);
            }

            // 2. Create default files
            // a. toc.yml
            // b. index.md
            // c. articles/toc.yml
            // d. articles/index.md
            // e. .gitignore
            // f. api/.gitignore
            // TODO: move api/index.md out to some other folder
            var tocYaml           = Tuple.Create("toc.yml", @"- name: Articles
  href: articles/
- name: Api Documentation
  href: api/
  homepage: api/index.md
");
            var indexMarkdownFile = Tuple.Create("index.md", @"# This is the **HOMEPAGE**.
Refer to [Markdown](http://daringfireball.net/projects/markdown/) for how to write markdown files.
## Quick Start Notes:
1. Add images to the *images* folder if the file is referencing an image.
");
            var apiTocFile        = Tuple.Create("api/toc.yml", @"- name: TO BE REPLACED
- href: index.md
");
            var apiIndexFile      = Tuple.Create("api/index.md", @"# PLACEHOLDER
TODO: Add .NET projects to the *src* folder and run `docfx` to generate **REAL** *API Documentation*!
");

            var articleTocFile      = Tuple.Create("articles/toc.yml", @"- name: Introduction
  href: intro.md
");
            var articleMarkdownFile = Tuple.Create("articles/intro.md", @"# Add your introductions here!
");
            var gitignore           = Tuple.Create(".gitignore", $@"###############
#    folder   #
###############
/**/DROP/
/**/TEMP/
/**/packages/
/**/bin/
/**/obj/
{config.Build.Destination}
");
            var apiGitignore        = Tuple.Create("api/.gitignore", $@"###############
#  temp file  #
###############
*.yml
");
            var files = new Tuple <string, string>[] { tocYaml, indexMarkdownFile, apiTocFile, apiIndexFile, articleTocFile, articleMarkdownFile, gitignore, apiGitignore };

            foreach (var file in files)
            {
                var filePath = Path.Combine(outputFolder, file.Item1);
                var content  = file.Item2;
                var dir      = Path.GetDirectoryName(filePath);
                if (!string.IsNullOrEmpty(dir))
                {
                    Directory.CreateDirectory(dir);
                }
                File.WriteAllText(filePath, content);
                $"Created File {TypeForwardedToStringExtension.ToDisplayPath(filePath)}".WriteLineToConsole(ConsoleColor.Gray);
            }

            // 2. Create docfx.json
            var path = Path.Combine(outputFolder ?? string.Empty, ConfigName);

            SaveConfigFile(path, config);
            $"Created config file {TypeForwardedToStringExtension.ToDisplayPath(path)}".WriteLineToConsole(ConsoleColor.Gray);
            $"Successfully generated default docfx project to {TypeForwardedToStringExtension.ToDisplayPath(outputFolder)}".WriteLineToConsole(ConsoleColor.Green);
            "Please run:".WriteLineToConsole(ConsoleColor.Gray);
            $"\tdocfx \"{TypeForwardedToStringExtension.ToDisplayPath(path)}\" --serve".WriteLineToConsole(ConsoleColor.White);
            "To generate a default docfx website.".WriteLineToConsole(ConsoleColor.Gray);
        }
Beispiel #29
0
 private Stream GetResourceStreamCore(string name)
 {
     // zip entry is case sensitive
     // incase relative path is combined by backslash \
     return(_zipped.GetEntry(TypeForwardedToStringExtension.ToNormalizedPath(name.Trim()))?.Open());
 }