@@ -89,7 +89,7 @@ func (n *node) isLicense() bool {
89
89
return n .role == licenseNode
90
90
}
91
91
92
- // Return the value of the license field.
92
+ // license returns the value of the license field.
93
93
// See also reconstructedLicenseString()
94
94
func (n * node ) license () * string {
95
95
if ! n .isLicense () {
@@ -167,7 +167,7 @@ func (n *node) reconstructedLicenseString() *string {
167
167
return nil
168
168
}
169
169
170
- // Sort an array of license and license reference nodes alphabetically based
170
+ // sortLicenses sorts an array of license and license reference nodes alphabetically based
171
171
// on their reconstructedLicenseString() representation. The sort function does not expect
172
172
// expression nodes, but if one is in the nodes list, it will sort to the end.
173
173
func sortLicenses (nodes []* node ) {
@@ -186,29 +186,51 @@ func sortLicenses(nodes []*node) {
186
186
187
187
// ---------------------- Comparator Methods ----------------------
188
188
189
- // Return true if two licenses are compatible; otherwise, false.
189
+ // licensesAreCompatible returns true if two licenses are compatible; otherwise, false.
190
+ // Two licenses are compatible if they are the same license or if they are in the same
191
+ // license group and they meet one of the following rules:
192
+ //
193
+ // * both licenses have the `hasPlus` flag set to true
194
+ // * the first license has the `hasPlus` flag and the second license is in the first license's range or greater
195
+ // * the second license has the `hasPlus` flag and the first license is in the second license's range or greater
196
+ // * both licenses are in the same range
190
197
func (nodes * nodePair ) licensesAreCompatible () bool {
198
+ // checking ranges is expensive, so check for simple cases first
191
199
if ! nodes .firstNode .isLicense () || ! nodes .secondNode .isLicense () {
192
200
return false
193
201
}
202
+ if ! nodes .exceptionsAreCompatible () {
203
+ return false
204
+ }
205
+ if nodes .licensesExactlyEqual () {
206
+ return true
207
+ }
208
+
209
+ // simple cases don't apply, so check license ranges
210
+ // NOTE: Ranges are organized into groups (referred to as license groups) of the same base license (e.g. GPL).
211
+ // Groups have sub-groups of license versions (referred to as the range) where each member is considered
212
+ // to be the same version (e.g. {GPL-2.0, GPL-2.0-only}). The sub-groups are in ascending order within
213
+ // the license group, such that the first sub-group is considered to be less than the second sub-group,
214
+ // and so on. (e.g. {{GPL-1.0}, {GPL-2.0, GPL-2.0-only}} implies {GPL-1.0} < {GPL-2.0, GPL-2.0-only}).
194
215
if nodes .secondNode .hasPlus () {
195
216
if nodes .firstNode .hasPlus () {
196
- // first+, second+
217
+ // first+, second+ just need to be in same range group
197
218
return nodes .rangesAreCompatible ()
198
219
}
199
- // first, second+
220
+ // first, second+ requires first to be in range of second
200
221
return nodes .identifierInRange ()
201
222
}
202
223
// else secondNode does not have plus
203
224
if nodes .firstNode .hasPlus () {
204
- // first+, second
225
+ // first+, second requires second to be in range of first
205
226
revNodes := & nodePair {firstNode : nodes .secondNode , secondNode : nodes .firstNode }
206
227
return revNodes .identifierInRange ()
207
228
}
208
- // first, second
209
- return nodes .licensesExactlyEqual ()
229
+ // first, second requires both to be in same range group
230
+ return nodes .rangesEqual ()
210
231
}
211
232
233
+ // licenseRefsAreCompatible returns true if two license references are compatible; otherwise, false.
212
234
func (nodes * nodePair ) licenseRefsAreCompatible () bool {
213
235
if ! nodes .firstNode .isLicenseRef () || ! nodes .secondNode .isLicenseRef () {
214
236
return false
@@ -222,13 +244,9 @@ func (nodes *nodePair) licenseRefsAreCompatible() bool {
222
244
return compatible
223
245
}
224
246
225
- // Return true if two licenses are compatible in the context of their ranges; otherwise, false.
247
+ // licenseRefsAreCompatible returns true if two licenses are in the same license group (e.g. all "GPL" licenses are in the same
248
+ // license group); otherwise, false.
226
249
func (nodes * nodePair ) rangesAreCompatible () bool {
227
- if nodes .licensesExactlyEqual () {
228
- // licenses specify ranges exactly the same (e.g. Apache-1.0+, Apache-1.0+)
229
- return true
230
- }
231
-
232
250
firstNode := * nodes .firstNode
233
251
secondNode := * nodes .secondNode
234
252
@@ -238,7 +256,7 @@ func (nodes *nodePair) rangesAreCompatible() bool {
238
256
// When both licenses allow later versions (i.e. hasPlus==true), being in the same license
239
257
// group is sufficient for compatibility, as long as, any exception is also compatible
240
258
// Example: All Apache licenses (e.g. Apache-1.0, Apache-2.0) are in the same license group
241
- return sameLicenseGroup (firstRange , secondRange ) && nodes . exceptionsAreCompatible ()
259
+ return sameLicenseGroup (firstRange , secondRange )
242
260
}
243
261
244
262
// identifierInRange returns true if the (first) simple license is in range of the (second)
@@ -247,14 +265,7 @@ func (nodes *nodePair) identifierInRange() bool {
247
265
simpleLicense := nodes .firstNode
248
266
plusLicense := nodes .secondNode
249
267
250
- if ! compareGT (simpleLicense , plusLicense ) && ! compareEQ (simpleLicense , plusLicense ) {
251
- return false
252
- }
253
-
254
- // With simpleLicense >= plusLicense, licenses are compatible, as long as, any exception
255
- // is also compatible
256
- return nodes .exceptionsAreCompatible ()
257
-
268
+ return compareGT (simpleLicense , plusLicense ) || compareEQ (simpleLicense , plusLicense )
258
269
}
259
270
260
271
// exceptionsAreCompatible returns true if neither license has an exception or they have
@@ -274,10 +285,15 @@ func (nodes *nodePair) exceptionsAreCompatible() bool {
274
285
}
275
286
276
287
return * nodes .firstNode .exception () == * nodes .secondNode .exception ()
288
+ }
277
289
290
+ // rangesEqual returns true if the licenses are in the same range; otherwise, false
291
+ // (e.g. GPL-2.0-only == GPL-2.0)
292
+ func (nodes * nodePair ) rangesEqual () bool {
293
+ return compareEQ (nodes .firstNode , nodes .secondNode )
278
294
}
279
295
280
- // Return true if the licenses are the same; otherwise, false
296
+ // licensesExactlyEqual returns true if the licenses are the same; otherwise, false
281
297
func (nodes * nodePair ) licensesExactlyEqual () bool {
282
298
return strings .EqualFold (* nodes .firstNode .reconstructedLicenseString (), * nodes .secondNode .reconstructedLicenseString ())
283
299
}
0 commit comments