@@ -34,47 +34,66 @@ type ValidateLicensesOptions struct {
3434// Returns all the invalid licenses contained in the `licenses` argument.
3535func ValidateLicensesWithOptions (licenses []string , options ValidateLicensesOptions ) (bool , []string ) {
3636 // handle all other cases with parsing, which will cover both single and multiple licenses and expressions
37- valid := true
38- invalidLicenses := []string {}
37+ _ , invalidLicenses := ValidateAndNormalizeLicensesWithOptions (licenses , options )
38+ return len (invalidLicenses ) == 0 , invalidLicenses
39+ }
40+
41+ // ValidateAndNormalizeLicensesWithOptions checks if given licenses are valid according to SPDX.
42+ // Supports validation options as defined in ValidateLicensesOptions.
43+ // Returns all validated licenses in their normalized form as the first return value.
44+ // Returns any invalid licenses as the second return value.
45+ func ValidateAndNormalizeLicensesWithOptions (licenses []string , options ValidateLicensesOptions ) (normalizedLicenses , invalidLicenses []string ) {
46+ normalizedLicenses = []string {}
47+ invalidLicenses = []string {}
48+ seenNormalized := make (map [string ]struct {}, len (licenses ))
49+
50+ addNormalized := func (license string ) {
51+ if _ , ok := seenNormalized [license ]; ok {
52+ return
53+ }
54+ seenNormalized [license ] = struct {}{}
55+ normalizedLicenses = append (normalizedLicenses , license )
56+ }
57+
3958 for _ , license := range licenses {
4059 // MIT is the most common license, so check for it first before doing any processing to optimize for this case.
4160 // By putting the isMIT check here, we can avoid the overhead of parsing for the most common case of MIT.
4261 // Having it before trimming means that licenses with leading/trailing whitespace will not be validated
4362 // as MIT by isMIT, but will still be correctly identified using activeLicense. As this is uncommon, it
4463 // is an acceptable tradeoff to avoid the overhead of trimming for the more common case.
4564 if isMIT (license ) {
65+ addNormalized ("MIT" )
4666 continue
4767 }
4868
4969 license = strings .TrimSpace (license )
5070
5171 isAtomic := isAtomicLicense (license )
5272 if isAtomic {
53- if ok , _ := activeLicense (license ); ok {
73+ if ok , normalizedLicense := activeLicense (license ); ok {
74+ addNormalized (normalizedLicense )
5475 continue
5576 }
5677
57- if ok , _ := deprecatedLicense (license ); ok {
78+ if ok , normalizedLicense := deprecatedLicense (license ); ok {
5879 if options .FailDeprecatedLicenses {
59- valid = false
6080 invalidLicenses = append (invalidLicenses , license )
6181 continue
6282 }
83+ addNormalized (normalizedLicense )
6384 // if FailDeprecatedLicenses is false, then consider the deprecated license valid and continue
6485 continue
6586 }
6687
6788 if options .FailAllLicenseRefs {
6889 if strings .HasPrefix (license , "LicenseRef-" ) {
69- valid = false
7090 invalidLicenses = append (invalidLicenses , license )
7191 continue
7292 }
7393 }
7494
7595 if options .FailAllDocumentRefs {
7696 if strings .HasPrefix (license , "DocumentRef-" ) {
77- valid = false
7897 invalidLicenses = append (invalidLicenses , license )
7998 continue
8099 }
@@ -86,17 +105,18 @@ func ValidateLicensesWithOptions(licenses []string, options ValidateLicensesOpti
86105 if ! isAtomic {
87106 if hasException , licensePart , exceptionPart := isLicenseWithException (license ); hasException {
88107 // matches pattern "licensePart WITH exceptionPart", so validate both parts separately
89- if ok , _ := exceptionLicense (exceptionPart ); ok {
90- if ok , _ := activeLicense (licensePart ); ok {
108+ if ok , normalizedException := exceptionLicense (exceptionPart ); ok {
109+ if ok , normalizedLicense := activeLicense (licensePart ); ok {
110+ addNormalized (normalizedLicense + " WITH " + normalizedException )
91111 continue
92112 }
93113 if ! options .FailDeprecatedLicenses {
94- if ok , _ := deprecatedLicense (licensePart ); ok {
114+ if ok , normalizedLicense := deprecatedLicense (licensePart ); ok {
115+ addNormalized (normalizedLicense + " WITH " + normalizedException )
95116 continue
96117 }
97118 }
98119 }
99- valid = false
100120 invalidLicenses = append (invalidLicenses , license )
101121 continue
102122 }
@@ -105,19 +125,22 @@ func ValidateLicensesWithOptions(licenses []string, options ValidateLicensesOpti
105125 // all other non-atomic expressions are complex expressions with conjunctions (e.g. "MIT AND Apache-2.0"),
106126 // so fail if complex expressions are not allowed
107127 if options .FailComplexExpressions && ! isAtomic {
108- valid = false
109128 invalidLicenses = append (invalidLicenses , license )
110129 continue
111130 }
112131
113132 // need to parse if allowing any of LicenseRef, DocumentRef, or complex expressions to be able to determine
114133 // whether the license expression is valid
115- if _ , err := parse (license ); err != nil {
116- valid = false
134+ var parsedLicense * node
135+ var err error
136+ if parsedLicense , err = parse (license ); err != nil {
117137 invalidLicenses = append (invalidLicenses , license )
138+ } else {
139+ normalizedLicense := * parsedLicense .reconstructedLicenseString ()
140+ addNormalized (normalizedLicense )
118141 }
119142 }
120- return valid , invalidLicenses
143+ return normalizedLicenses , invalidLicenses
121144}
122145
123146// Satisfies determines if the allowed list of licenses satisfies the test license expression.
0 commit comments