xref: /aosp_15_r20/external/spdx-tools/examples/6-licensediff/example_licensediff.go (revision ba677afa8f67bb56cbc794f4d0e378e0da058e16)
1// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2
3// Example for: *licensediff*, *tvloader*
4
5// This example demonstrates loading two SPDX tag-value files from disk into
6// memory, and generating a diff of the concluded licenses for Files in
7// Packages with matching IDs in each document.
8// This is generally only useful when run with two SPDX documents that
9// describe licenses for subsequent versions of the same set of files, AND if
10// they have the same identifier in both documents.
11// Run project: go run example_licensediff.go ../sample-docs/tv/hello.spdx  ../sample-docs/tv/hello-modified.spdx
12package main
13
14import (
15	"fmt"
16	"os"
17
18	"github.com/spdx/tools-golang/licensediff"
19	"github.com/spdx/tools-golang/spdx/v2_2"
20	"github.com/spdx/tools-golang/spdxlib"
21	"github.com/spdx/tools-golang/tvloader"
22)
23
24func main() {
25
26	// check that we've received the right number of arguments
27	args := os.Args
28	if len(args) != 3 {
29		fmt.Printf("Usage: %v <spdx-file-first> <spdx-file-second>\n", args[0])
30		fmt.Printf("  Load SPDX 2.2 tag-value files <spdx-file-first> and <spdx-file-second>,\n")
31		fmt.Printf("  run a diff between their concluded licenses, and print basic results.\n")
32		return
33	}
34
35	// open the first SPDX file
36	filenameFirst := args[1]
37	r, err := os.Open(filenameFirst)
38	if err != nil {
39		fmt.Printf("Error while opening %v for reading: %v", filenameFirst, err)
40		return
41	}
42	defer r.Close()
43
44	// try to load the first SPDX file's contents as a tag-value file, version 2.2
45	docFirst, err := tvloader.Load2_2(r)
46	if err != nil {
47		fmt.Printf("Error while parsing %v: %v", filenameFirst, err)
48		return
49	}
50	// check whether the SPDX file has at least one package that it describes
51	pkgIDsFirst, err := spdxlib.GetDescribedPackageIDs2_2(docFirst)
52	if err != nil {
53		fmt.Printf("Unable to get describe packages from first SPDX document: %v\n", err)
54		return
55	}
56
57	// if we got here, the file is now loaded into memory.
58	fmt.Printf("Successfully loaded first SPDX file %s\n", filenameFirst)
59
60	// open the second SPDX file
61	filenameSecond := args[2]
62	r, err = os.Open(filenameSecond)
63	if err != nil {
64		fmt.Printf("Error while opening %v for reading: %v", filenameSecond, err)
65		return
66	}
67	defer r.Close()
68
69	// try to load the second SPDX file's contents as a tag-value file, version 2.2
70	docSecond, err := tvloader.Load2_2(r)
71	if err != nil {
72		fmt.Printf("Error while parsing %v: %v", filenameSecond, err)
73		return
74	}
75	// check whether the SPDX file has at least one package that it describes
76	pkgIDsSecond, err := spdxlib.GetDescribedPackageIDs2_2(docSecond)
77	if err != nil {
78		fmt.Printf("Unable to get describe packages from second SPDX document: %v\n", err)
79		return
80	}
81
82	// if we got here, the file is now loaded into memory.
83	fmt.Printf("Successfully loaded second SPDX file %s\n\n", filenameSecond)
84
85	// compare the described packages from each Document, by SPDX ID
86	// go through the first set first, report if they aren't in the second set
87	for _, pkgID := range pkgIDsFirst {
88		fmt.Printf("================================\n")
89
90		var p1, p2 *v2_2.Package
91		var okFirst, okSecond bool
92		for _, pkg := range docFirst.Packages {
93			if pkg.PackageSPDXIdentifier == pkgID {
94				okFirst = true
95				p1 = pkg
96				break
97			}
98		}
99		if !okFirst {
100			fmt.Printf("Package %s has described relationship in first document but ID not found\n", string(pkgID))
101			continue
102		}
103
104		fmt.Printf("Package %s (%s)\n", string(pkgID), p1.PackageName)
105
106		for _, pkg := range docSecond.Packages {
107			if pkg.PackageSPDXIdentifier == pkgID {
108				okSecond = true
109				p2 = pkg
110				break
111			}
112		}
113		if !okSecond {
114			fmt.Printf("  Found in first document, not found in second\n")
115			continue
116		}
117
118		// now, run a diff between the two
119		pairs, err := licensediff.MakePairs2_2(p1, p2)
120		if err != nil {
121			fmt.Printf("  Error generating licensediff pairs: %v\n", err)
122			continue
123		}
124
125		// take the pairs and turn them into a more structured results set
126		resultSet, err := licensediff.MakeResults(pairs)
127		if err != nil {
128			fmt.Printf("  Error generating licensediff results set: %v\n", err)
129			continue
130		}
131
132		// print some information about the results
133		fmt.Printf("  Files in first only: %d\n", len(resultSet.InFirstOnly))
134		fmt.Printf("  Files in second only: %d\n", len(resultSet.InSecondOnly))
135		fmt.Printf("  Files in both with different licenses: %d\n", len(resultSet.InBothChanged))
136		fmt.Printf("  Files in both with same licenses: %d\n", len(resultSet.InBothSame))
137	}
138
139	// now report if there are any package IDs in the second set that aren't
140	// in the first
141	for _, pkgID := range pkgIDsSecond {
142		var p2 *v2_2.Package
143		var okFirst, okSecond bool
144		for _, pkg := range docSecond.Packages {
145			if pkg.PackageSPDXIdentifier == pkgID {
146				okSecond = true
147				p2 = pkg
148				break
149			}
150		}
151		if !okSecond {
152			fmt.Printf("================================\n")
153			fmt.Printf("Package %s has described relationship in second document but ID not found\n", string(pkgID))
154			continue
155		}
156
157		for _, pkg := range docFirst.Packages {
158			if pkg.PackageSPDXIdentifier == pkgID {
159				okFirst = true
160				break
161			}
162		}
163		if !okFirst {
164			fmt.Printf("================================\n")
165			fmt.Printf("Package %s (%s)\n", string(pkgID), p2.PackageName)
166			fmt.Printf("  Found in second document, not found in first\n")
167		}
168	}
169}
170