コード例 #1
0
        private async Task SaveAllMembersFromCacheAsync()
        {
            var forceRebuild = _rebuild;
            var outputFolder = _outputFolder;

            var projectCache = new ConcurrentDictionary <string, AbstractProject>();

            // Project<=>Documents
            var      documentCache          = new ProjectDocumentCache();
            var      projectDependencyGraph = new ConcurrentDictionary <string, List <string> >();
            DateTime triggeredTime          = DateTime.UtcNow;

            // Exclude not supported files from inputs
            var cacheKey = GetCacheKey(_files.SelectMany(s => s.Value));

            Logger.LogInfo("Loading projects...");
            if (_files.TryGetValue(FileType.Solution, out var sln))
            {
                var solutions = sln.Select(s => s.NormalizedPath);
                // No matter is incremental or not, we have to load solutions into memory
                foreach (var path in solutions)
                {
                    using (new LoggerFileScope(path))
                    {
                        documentCache.AddDocument(path, path);
                        var solution = await GetSolutionAsync(path);

                        if (solution != null)
                        {
                            foreach (var project in solution.Projects)
                            {
                                var projectFile = new FileInformation(project.FilePath);

                                // If the project is supported, add to project dictionary, otherwise, ignore
                                if (projectFile.Type == FileType.Project)
                                {
                                    projectCache.GetOrAdd(projectFile.NormalizedPath,
                                                          s => _loader.Load(projectFile.NormalizedPath));
                                }
                                else
                                {
                                    Logger.LogWarning($"Project {projectFile.RawPath} inside solution {path} is ignored, supported projects are csproj, fsproj and vbproj.");
                                }
                            }
                        }
                    }
                }
            }

            if (_files.TryGetValue(FileType.Project, out var p))
            {
                foreach (var pp in p)
                {
                    GetProject(projectCache, pp.NormalizedPath);
                }
            }

            foreach (var item in projectCache)
            {
                var path    = item.Key;
                var project = item.Value;
                documentCache.AddDocument(path, path);
                if (project.HasDocuments)
                {
                    documentCache.AddDocuments(path, project.Documents.Select(s => s.FilePath));
                }
                else
                {
                    Logger.Log(LogLevel.Warning, $"Project '{project.FilePath}' does not contain any documents.");
                }
                documentCache.AddDocuments(path, project.PortableExecutableMetadataReferences);
                FillProjectDependencyGraph(projectCache, projectDependencyGraph, project);
            }

            var csFiles       = new List <string>();
            var vbFiles       = new List <string>();
            var assemblyFiles = new List <string>();

            if (_files.TryGetValue(FileType.CSSourceCode, out var cs))
            {
                csFiles.AddRange(cs.Select(s => s.NormalizedPath));
                documentCache.AddDocuments(csFiles);
            }

            if (_files.TryGetValue(FileType.VBSourceCode, out var vb))
            {
                vbFiles.AddRange(vb.Select(s => s.NormalizedPath));
                documentCache.AddDocuments(vbFiles);
            }

            if (_files.TryGetValue(FileType.Assembly, out var asm))
            {
                assemblyFiles.AddRange(asm.Select(s => s.NormalizedPath));
                documentCache.AddDocuments(assemblyFiles);
            }

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

            var options = _options;

            if (!forceRebuild)
            {
                var buildInfo = applicationCache.GetValidConfig(cacheKey);
                if (buildInfo != null)
                {
                    IncrementalCheck check = new IncrementalCheck(buildInfo);
                    if (!options.HasChanged(check, true))
                    {
                        // 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 = StringExtension.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, cacheKey, outputFolder);
                                return;
                            }
                            catch (Exception e)
                            {
                                Logger.Log(LogLevel.Warning, $"Unable to copy results from cache: {e.Message}. Rebuild starts.");
                            }
                        }
                    }
                }
            }

            Logger.LogInfo("Generating metadata for each project...");

            // 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, AbstractCompilation> compilationCache =
                await GetProjectCompilationAsync(projectCache);

            var roslynProjects = compilationCache.Values.OfType <RoslynCompilation>().Select(rc => rc.Compilation);

            options.RoslynExtensionMethods =
                RoslynIntermediateMetadataExtractor.GetAllExtensionMethodsFromCompilation(roslynProjects);
            foreach (var key in GetTopologicalSortedItems(projectDependencyGraph))
            {
                var dependencyRebuilt = projectDependencyGraph[key].Any(r => projectRebuildInfo[r]);
                var k          = documentCache.GetDocuments(key);
                var input      = new ProjectFileInputParameters(options, k, key, dependencyRebuilt);
                var controller = compilationCache[key].GetBuildController();

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

            if (csFiles.Count > 0)
            {
                var csContent     = string.Join(Environment.NewLine, csFiles.Select(s => File.ReadAllText(s)));
                var csCompilation = CompilationUtility.CreateCompilationFromCsharpCode(csContent);
                if (csCompilation != null)
                {
                    var input      = new SourceFileInputParameters(options, csFiles);
                    var controller = new RoslynSourceFileBuildController(csCompilation);

                    var csMetadata = GetMetadataFromProjectLevelCache(controller, input);
                    if (csMetadata != null)
                    {
                        projectMetadataList.Add(csMetadata.Item1);
                    }
                }
            }

            if (vbFiles.Count > 0)
            {
                var vbContent     = string.Join(Environment.NewLine, vbFiles.Select(s => File.ReadAllText(s)));
                var vbCompilation = CompilationUtility.CreateCompilationFromVBCode(vbContent);
                if (vbCompilation != null)
                {
                    var input      = new SourceFileInputParameters(options, vbFiles);
                    var controller = new RoslynSourceFileBuildController(vbCompilation);

                    var vbMetadata = GetMetadataFromProjectLevelCache(controller, input);
                    if (vbMetadata != null)
                    {
                        projectMetadataList.Add(vbMetadata.Item1);
                    }
                }
            }

            if (assemblyFiles.Count > 0)
            {
                var assemblyCompilation = CompilationUtility.CreateCompilationFromAssembly(
                    assemblyFiles.Concat(_references ?? Enumerable.Empty <string>()));
                if (assemblyCompilation != null)
                {
                    var commentFiles = (from file in assemblyFiles
                                        select Path.ChangeExtension(file, XmlCommentFileExtension) into xmlFile
                                        where File.Exists(xmlFile)
                                        select xmlFile).ToList();

                    var referencedAssemblyList = CompilationUtility.GetAssemblyFromAssemblyComplation(assemblyCompilation, assemblyFiles).ToList();
                    // TODO: why not merge with compilation's extension methods?
                    var assemblyExtension = RoslynIntermediateMetadataExtractor.GetAllExtensionMethodsFromAssembly(assemblyCompilation, referencedAssemblyList.Select(s => s.assembly));
                    options.RoslynExtensionMethods = assemblyExtension;
                    foreach (var(reference, assembly) in referencedAssemblyList)
                    {
                        var input      = new AssemblyFileInputParameters(options, reference.Display);
                        var controller = new RoslynSourceFileBuildController(assemblyCompilation, assembly);

                        var mta = GetMetadataFromProjectLevelCache(controller, input);

                        if (mta != null)
                        {
                            MergeCommentsHelper.MergeComments(mta.Item1, commentFiles);
                            projectMetadataList.Add(mta.Item1);
                        }
                    }
                }
            }

            Dictionary <string, MetadataItem>  allMembers;
            Dictionary <string, ReferenceItem> allReferences;

            using (new PerformanceScope("MergeMetadata"))
            {
                allMembers = MergeYamlProjectMetadata(projectMetadataList);
            }

            using (new PerformanceScope("MergeReference"))
            {
                allReferences = MergeYamlProjectReferences(projectMetadataList);
            }

            if (allMembers == null || allMembers.Count == 0)
            {
                var value = StringExtension.ToDelimitedString(projectMetadataList.Select(s => s.Name));
                Logger.Log(LogLevel.Warning, $"No metadata is generated for {value}.");
                applicationCache.SaveToCache(cacheKey, null, triggeredTime, outputFolder, null, options);
            }
            else
            {
                // TODO: need an intermediate folder? when to clean it up?
                // Save output to output folder
                List <string> outputFiles;
                using (new PerformanceScope("ResolveAndExport"))
                {
                    outputFiles = ResolveAndExportYamlMetadata(allMembers, allReferences, outputFolder, options.PreserveRawInlineComments, options.ShouldSkipMarkup, _useCompatibilityFileName).ToList();
                }

                applicationCache.SaveToCache(cacheKey, documentCache.Cache, triggeredTime, outputFolder, outputFiles, options);
            }
        }
コード例 #2
0
        public MetadataItem ExtractMetadata(IInputParameters parameters)
        {
            var extractor = new RoslynIntermediateMetadataExtractor(this);

            return(extractor.Extract(parameters));
        }