// recursive static void ReportProjectReferences(ProjectReferenceNode source, PackageSpec project, NuGetFramework rootNuGetFramework, DependencyGraphSpec dependencyGraph, int indentLevel, HashSet <Node> vertices, HashSet <Edge> edges) { const int INDENT_SIZE = 2; NuGetFramework nearest = project.GetNearestFrameworkMatching(rootNuGetFramework); // TODO: This is done be caller....delete //var projectReference = new ProjectReferenceNode(project.FilePath, project.Version.ToString(), nearest); //projects.Add(projectReference); // indent shows levels of the graph Console.Write(new string(' ', indentLevel * INDENT_SIZE)); Console.WriteLine($"{project.RestoreMetadata.ProjectUniqueName}, v{project.Version}, ({nearest.GetShortFolderName()})"); ProjectRestoreMetadataFrameworkInfo resolvedTargetFramework = project.RestoreMetadata.TargetFrameworks.Single(tf => nearest.Equals(tf.FrameworkName)); // project references of a targetFramework foreach (ProjectRestoreReference projectRestoreReference in resolvedTargetFramework.ProjectReferences) { // TODO: PrivateAssets, ExcludeAssets, IncludeAssets //dependency.ProjectPath //dependency.ProjectUniqueName PackageSpec projectDependency = dependencyGraph.GetProjectSpec(projectRestoreReference.ProjectUniqueName); var projectDependencyReference = new ProjectReferenceNode( projectDependency.FilePath, projectDependency.Version.ToString(), projectDependency.GetNearestFrameworkMatching(rootNuGetFramework)); vertices.Add(projectDependencyReference); edges.Add(new Edge(source, projectDependencyReference)); //Console.WriteLine($"({projectDependency.Name}, {Path.GetFileName(projectDependency.FilePath)}) is a project reference of ({project.Name}, {Path.GetFileName(project.FilePath)}"); // recursive ReportProjectReferences(projectDependencyReference, projectDependency, rootNuGetFramework, dependencyGraph, indentLevel + 1, vertices, edges); } }
// TODO: Should we use framework here // // 1. Create dgspec.json via .NET Core CLI: GenerateRestoreGraphFile target // 2. Load dgspec.json into DependencyGraphSpec instance/object // For each SDK project (ProjectStyle.PackageReference) in the graph spec // 3. Get assets file (project.assets.json) for the project // 4. Construct LockFile from assets file for the project static void AnalyzeProject(string projectPath, string framework) { // Graph input var vertices = new HashSet <Node>(); var edges = new HashSet <Edge>(); var rootNuGetFramework = NuGetFramework.ParseFolder(framework); // TODO: optional filter // TODO: HACK...kan fjernes if (string.IsNullOrEmpty(projectPath)) { var rootPath = GetRepoRootPath(); //projectPath = Path.Combine(rootPath, "DotnetDependencies.sln"); projectPath = Path.Combine( Path.Combine( Path.Combine(rootPath, "src"), "Deps.CLI"), "Deps.CLI.csproj"); } // 'package graph' is a better word // Load dependency graph via nuget client tools build into msbuild (.NET Core CLI, .NET Core SDK) // TODO: opsatning af packageSources var dependencyGraphService = new DependencyGraphService(); var dependencyGraph = dependencyGraphService.GenerateDependencyGraph(projectPath); // We only support MSBuild style <PackageReference> SDK projects, where project.assets.json (lock file) is // generated in the RestoreOutputPath folder if (dependencyGraph.Projects.Any(p => p.RestoreMetadata.ProjectStyle != ProjectStyle.PackageReference)) { throw new InvalidOperationException("Only SDK projects are supported."); } PackageSpec projectRoot = dependencyGraph.Projects.Single(p => Path.GetFileName(p.FilePath) == Path.GetFileName(projectPath)); var rootNode = new ProjectReferenceNode(projectPath, projectRoot.Version.ToString(), rootNuGetFramework); vertices.Add(rootNode); var projectRootTargetFramework = projectRoot.TargetFrameworks.FirstOrDefault(tf => rootNuGetFramework.Equals(tf.FrameworkName)); if (projectRootTargetFramework == null) { throw new InvalidOperationException( $"The root project does not define the TargetFramework {rootNuGetFramework}"); } //Console.WriteLine($"project: {Path.GetFileName(project.FilePath)}"); //Console.WriteLine($"project.BaseDirectory: {Path.GetFileName(project.BaseDirectory)}"); //Console.WriteLine($"project.Name: {project.Name}"); //Console.WriteLine($"project.TargetFrameworks: {string.Join(", ", project.TargetFrameworks.Select(tfm => tfm.FrameworkName.GetShortFolderName()))}"); //Console.WriteLine($"project.Version: {project.Version}"); //Console.WriteLine($"project.RestoreMetadata.ProjectName: {project.RestoreMetadata.ProjectName}"); //Console.WriteLine($"project.RestoreMetadata.Sources: {string.Join(", ", project.RestoreMetadata.Sources)}"); //Console.WriteLine($"project.Dependencies: {string.Join(", ", project.Dependencies)}"); // TODO: Path. Unique name etc... Console.WriteLine($"Name: {projectRoot.Name}"); Console.WriteLine($"Version: {projectRoot.Version} ({rootNuGetFramework.GetShortFolderName()})"); Console.WriteLine($"Framework: {rootNuGetFramework.DotNetFrameworkName}"); Console.WriteLine($"Framework moniker: {rootNuGetFramework.GetShortFolderName()}"); Console.WriteLine(); Console.WriteLine("resolve project reference dependency graph"); Console.WriteLine(); // TODO: Path. Unique name etc... Console.WriteLine($"{projectRoot.Name}, v{projectRoot.Version} ({rootNuGetFramework.GetShortFolderName()})"); // // First resolve project reference dependency graph // foreach (ProjectRestoreMetadataFrameworkInfo targetFramework in projectRoot.RestoreMetadata.TargetFrameworks) { // only want project references defined for the TargetFramework if (!rootNuGetFramework.Equals(targetFramework.FrameworkName)) { continue; } // project references of a targetFramework foreach (var projectRestoreReference in targetFramework.ProjectReferences) { // TODO: PrivateAssets, ExcludeAssets, IncludeAssets //dependency.ProjectPath //dependency.ProjectUniqueName PackageSpec projectDependency = dependencyGraph.GetProjectSpec(projectRestoreReference.ProjectUniqueName); //Console.WriteLine($"({project.Name}, {Path.GetFileName(project.FilePath)}) is a project reference of ({projectRoot.Name}, {Path.GetFileName(projectRoot.FilePath)}"); NuGetFramework nearest = projectDependency.GetNearestFrameworkMatching(rootNuGetFramework); var projectReference = new ProjectReferenceNode(projectDependency.FilePath, projectDependency.Version.ToString(), nearest); vertices.Add(projectReference); edges.Add(new Edge(rootNode, projectReference)); ReportProjectReferences(rootNode, projectDependency, rootNuGetFramework, dependencyGraph, 1, vertices, edges); } } // TODO: What about ProjectReferences...are project references converted to package specs in dgspec file??? YES, // and available via dependencyGraph.Projects and dependencyGraph.GetProjectSpec // TODO: project Closure is part of graph all ready // closure[0] is the root // closure[1..m] contains project references IReadOnlyList <PackageSpec> projectClosure = dependencyGraph.GetClosure(projectRoot.RestoreMetadata.ProjectUniqueName); Debug.Assert(ReferenceEquals(projectRoot, projectClosure[0])); // // Second, resolve package reference dependency graph of each project // Console.WriteLine(); Console.WriteLine("resolve package reference dependency graph"); Console.WriteLine(); // for each csproj with PackageReference style (i.e. SDK csproj) foreach (PackageSpec project in dependencyGraph.Projects) { // TODO: Maybe just use the project.assets.json created by .NET Core SDK tooling // Generate lock file: A lock file has the package dependency graph for the project/solution/repo // that includes both the direct as well as transitive dependencies. var lockFileService = new LockFileService(); LockFile lockFile = lockFileService.GetLockFile(project.FilePath, project.RestoreMetadata.OutputPath); // TODO: This could change our code...we can get at the project references inside one single loop //{ // // TODO: For debugging // var projectDirectory = Path.GetDirectoryName(lockFile.PackageSpec.RestoreMetadata.ProjectPath); // // full path to the referenced msbuild files (csproj files) // var projectReferences = lockFile.Libraries // .Where(library => library.MSBuildProject != null) // .Select(library => Path.GetFullPath(Path.Combine(projectDirectory, library.MSBuildProject))) // .ToArray(); // if (projectReferences.Length > 0) // { // Console.WriteLine($"project references {string.Join(", ", projectReferences)}"); // } //} NuGetFramework nearest = project.GetNearestFrameworkMatching(rootNuGetFramework); TargetFrameworkInformation resolvedTargetFramework = project.TargetFrameworks.Single(tf => nearest.Equals(tf.FrameworkName)); // for the root should this resolve to the rootNuGetFramework Console.WriteLine($"{project.Name}, v{project.Version}, ({resolvedTargetFramework.FrameworkName.GetShortFolderName()})"); // Find the transitive closure for this tfm LockFileTarget lockFileTargetFramework = lockFile.Targets.FirstOrDefault(t => t.TargetFramework.Equals(resolvedTargetFramework.FrameworkName)); if (lockFileTargetFramework != null) { // For each direct dependency foreach (LibraryDependency directDependency in resolvedTargetFramework.Dependencies) { // Find the _resolved_ package reference LockFileTargetLibrary resolvedPackageReference = lockFileTargetFramework.Libraries.FirstOrDefault(library => library.Name == directDependency.Name); // Show transitive dependencies of this direct dependency ReportLockFilePackageDependencies(resolvedPackageReference, lockFileTargetFramework, 1); } } } }