/buildtools/dotnet/package_reference.go

https://github.com/fossas/fossa-cli · Go · 130 lines · 106 code · 19 blank · 5 comment · 18 complexity · 81209d76fa6a0435f5fa1063c1632ba0 MD5 · raw file

  1. package dotnet
  2. import (
  3. "path/filepath"
  4. "regexp"
  5. "github.com/fossas/fossa-cli/files"
  6. "github.com/fossas/fossa-cli/graph"
  7. "github.com/fossas/fossa-cli/pkg"
  8. )
  9. type Manifest struct {
  10. PropertyGroup []PropertyGroup
  11. ItemGroup []ItemGroup
  12. }
  13. type PropertyGroup struct {
  14. RootNamespace string
  15. Version string
  16. }
  17. type ItemGroup struct {
  18. Reference []Reference
  19. PackageReference []Reference
  20. ProjectReference []Reference
  21. }
  22. type Reference struct {
  23. Include string `xml:",attr"`
  24. Version string `xml:"Version"`
  25. }
  26. func (m *Manifest) Name() string {
  27. for _, propertyGroup := range m.PropertyGroup {
  28. if propertyGroup.RootNamespace != "" {
  29. return propertyGroup.RootNamespace
  30. }
  31. }
  32. return ""
  33. }
  34. func (m *Manifest) Version() string {
  35. for _, propertyGroup := range m.PropertyGroup {
  36. if propertyGroup.Version != "" {
  37. return propertyGroup.Version
  38. }
  39. }
  40. return ""
  41. }
  42. func (m *Manifest) packages() []Reference {
  43. var references []Reference
  44. for _, itemGroup := range m.ItemGroup {
  45. references = append(references, itemGroup.PackageReference...)
  46. }
  47. return references
  48. }
  49. func (m *Manifest) projects() []Reference {
  50. var projects []Reference
  51. for _, itemGroup := range m.ItemGroup {
  52. projects = append(projects, itemGroup.ProjectReference...)
  53. }
  54. return projects
  55. }
  56. // IsPackageReferenceFile checks for a valid package reference file name.
  57. func IsPackageReferenceFile(filename string) bool {
  58. var xmlProj = regexp.MustCompile(`\.(cs|x|vb|db|fs)proj$`)
  59. return xmlProj.MatchString(filename)
  60. }
  61. // PackageReferenceGraph reads a package reference file and returns a dependency graph.
  62. func PackageReferenceGraph(file string) (graph.Deps, error) {
  63. imports := []pkg.Import{}
  64. dependencyMap := make(map[pkg.ID]pkg.Package)
  65. projects := make(map[string]Manifest)
  66. err := Projects(projects, file)
  67. if err != nil {
  68. return graph.Deps{}, nil
  69. }
  70. for _, project := range projects {
  71. for _, reference := range project.packages() {
  72. id := pkg.ID{
  73. Type: pkg.NuGet,
  74. Name: reference.Include,
  75. Revision: reference.Version,
  76. }
  77. imports = append(imports, pkg.Import{
  78. Target: reference.Include,
  79. Resolved: id,
  80. })
  81. dependencyMap[id] = pkg.Package{
  82. ID: id,
  83. }
  84. }
  85. }
  86. return graph.Deps{
  87. Direct: imports,
  88. Transitive: dependencyMap,
  89. }, nil
  90. }
  91. // Projects recursively discovers references to other manifest files from the base manifest file.
  92. func Projects(projects map[string]Manifest, projectFile string) error {
  93. // Break out of cycles.
  94. if _, ok := projects[projectFile]; ok {
  95. return nil
  96. }
  97. var manifest Manifest
  98. err := files.ReadXML(&manifest, projectFile)
  99. if err != nil {
  100. return err
  101. }
  102. projects[projectFile] = manifest
  103. // Get all project reference files and recurse.
  104. for _, reference := range manifest.projects() {
  105. err = Projects(projects, filepath.Join(filepath.Dir(projectFile), Path(reference.Include)))
  106. if err != nil {
  107. return err
  108. }
  109. }
  110. return nil
  111. }