xref: /aosp_15_r20/external/spdx-tools/examples/4-search/example_search.go (revision ba677afa8f67bb56cbc794f4d0e378e0da058e16)
1// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2
3// Example for: *idsearcher*, *tvsaver*
4
5// This example demonstrates building an SPDX document for a directory's
6// contents (implicitly using *builder*); searching through that directory for
7// [SPDX short-form IDs](https://spdx.org/ids/); filling those IDs into the
8// document's Package and File license fields; and saving the resulting document
9// to disk.
10// Run project: go run example_search.go project2 ../../testdata/project2/folder test.spdx
11
12package main
13
14import (
15	"fmt"
16	"os"
17
18	"github.com/spdx/tools-golang/idsearcher"
19
20	"github.com/spdx/tools-golang/tvsaver"
21)
22
23func main() {
24
25	// check that we've received the right number of arguments
26	args := os.Args
27	if len(args) != 4 {
28		fmt.Printf("Usage: %v <package-name> <package-root-dir> <spdx-file-out>\n", args[0])
29		fmt.Printf("  Build a SPDX 2.2 document with one package called <package-name>;\n")
30		fmt.Printf("  create files with hashes corresponding to the files in <package-root-dir>;\n")
31		fmt.Printf("  search for SPDX short-form IDs, and use them to fill in license data\n")
32		fmt.Printf("  where possible; and save it out as a tag-value file to <spdx-file-out>.\n")
33		return
34	}
35
36	// get the command-line arguments
37	packageName := args[1]
38	packageRootDir := args[2]
39	fileOut := args[3]
40
41	// to use the SPDX idsearcher package, the first step is to define a
42	// idsearcher.Config2_2 struct. this config data can be reused, in case you
43	// are building SPDX documents for several directories in sequence.
44	config := &idsearcher.Config2_2{
45
46		// NamespacePrefix is a prefix that will be used to populate the
47		// mandatory DocumentNamespace field in the Creation Info section.
48		// Because it needs to be unique, the value that will be filled in
49		// for the document will have the package name and verification code
50		// appended to this prefix.
51		NamespacePrefix: "https://example.com/whatever/testdata-",
52
53		// CreatorType and Creator, from builder.Config2_2, are not needed for
54		// idsearcher.Config2_2. Because it is automated and doesn't assume
55		// further review, the following two Creator fields are filled in:
56		// Creator: Tool: github.com/spdx/tools-golang/builder
57		// Creator: Tool: github.com/spdx/tools-golang/idsearcher
58
59		// You can define one or more paths that should be ignored
60		// when walking through the directory. This is intended to omit files
61		// that are located within the package's directory, but which should
62		// be omitted from the SPDX document.
63		// This is directly passed through to builder, and uses the same
64		// format as shown in examples/3-build/example_build.go.
65		BuilderPathsIgnored: []string{
66
67			// ignore all files in the .git/ directory at the package root
68			"/.git/",
69
70			// ignore all files in all __pycache__/ directories, anywhere
71			// within the package directory tree
72			"**/__pycache__/",
73
74			// ignore the file with this specific path relative to the
75			// package root
76			"/.ignorefile",
77
78			// or ignore all files with this filename, anywhere within the
79			// package directory tree
80			"**/.DS_Store",
81		},
82
83		// Finally, SearcherPathsIgnored lists certain paths that should not be
84		// searched by idsearcher, even if those paths have Files present (and
85		// had files filled in by builder). This is useful, for instance, if
86		// your project has some directories or files with
87		// "SPDX-License-Identifier:" tags, but for one reason or another you
88		// want to exclude those files' tags from being picked up by the
89		// searcher.
90		// SearcherPathsIgnored uses the same format as BuilderPathsIgnored.
91		SearcherPathsIgnored: []string{
92
93			// Example for the Linux kernel: ignore the documentation file
94			// which explains how to use SPDX short-form IDs (and therefore
95			// has a bunch of "SPDX-License-Identifier:" tags that we wouldn't
96			// want to pick up).
97			"/Documentation/process/license-rules.rst",
98
99			// Similar example for the Linux kernel: ignore all files in the
100			// /LICENSES/ directory.
101			"/LICENSES/",
102		},
103	}
104
105	// now, when we actually ask idsearcher to walk through a directory and
106	// build an SPDX document, we need to give it three things:
107	//   - what to name the package; and
108	//   - where the directory is located on disk; and
109	//   - the config object we just defined.
110	// these are the same arguments needed for builder, and in fact they get
111	// passed through to builder (with the relevant data from the config
112	// object extracted behind the scenes).
113	doc, err := idsearcher.BuildIDsDocument2_2(packageName, packageRootDir, config)
114	if err != nil {
115		fmt.Printf("Error while building document: %v\n", err)
116		return
117	}
118
119	// if we got here, the document has been created.
120	// all file hashes and the package verification code have been filled in
121	// appropriately by builder.
122	// And, all files with "SPDX-License-Identifier:" tags have had their
123	// licenses extracted into LicenseInfoInFiles and LicenseConcluded for
124	// each file by idsearcher. The PackageLicenseInfoFromFiles field will
125	// also be filled in with all license identifiers.
126	fmt.Printf("Successfully created document and searched for IDs for package %s\n", packageName)
127
128	// NOTE that BuildIDsDocument does NOT do any validation of the license
129	// identifiers, to confirm that they are e.g. on the SPDX License List
130	// or in other appropriate format (e.g., LicenseRef-...)
131
132	// we can now save it to disk, using tvsaver.
133
134	// create a new file for writing
135	w, err := os.Create(fileOut)
136	if err != nil {
137		fmt.Printf("Error while opening %v for writing: %v\n", fileOut, err)
138		return
139	}
140	defer w.Close()
141
142	err = tvsaver.Save2_2(doc, w)
143	if err != nil {
144		fmt.Printf("Error while saving %v: %v", fileOut, err)
145		return
146	}
147
148	fmt.Printf("Successfully saved %v\n", fileOut)
149}
150