github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/buildtools/dotnet/package_reference.go (about)

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