Skip to content

Commit 9002163

Browse files
committed
feat(spdx): Optionally set licenseConcluded to the effective license
Previously, only ORT's `concludedLicense` from a package curation was taken into account. However, if solely detected license findings were cleared via license finding curations from package configurations, that did not have any impact at all until now. To address this, use the effective license with a custom license view of the declared and detected licenses as a fallback if no concluded license is set. Finally, the `licenseConcluded` should only be set if (human) clearance work was involved, so only set it if it differs from the `licenseDeclared`. If `licenseDeclared` already was correct from the start, then this needs to be "acknowledged" by manually setting the concluded license to the same expression. Signed-off-by: Sebastian Schuberth <sebastian@doubleopen.org>
1 parent cda6c80 commit 9002163

4 files changed

Lines changed: 43 additions & 14 deletions

File tree

plugins/reporters/spdx/src/funTest/kotlin/SpdxDocumentReporterFunTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ private fun TestConfiguration.generateReport(
153153
creationInfoOrganization = "some creation info organization",
154154
documentComment = "some document comment",
155155
documentName = documentName,
156+
autoConcludeToEffectiveLicense = false,
156157
fileInformationEnabled = fileInformationEnabled,
157158
outputFileFormats = listOf(format)
158159
)

plugins/reporters/spdx/src/main/kotlin/Extensions.kt

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import org.ossreviewtoolkit.model.Hash
3030
import org.ossreviewtoolkit.model.Identifier
3131
import org.ossreviewtoolkit.model.KnownProvenance
3232
import org.ossreviewtoolkit.model.LicenseFinding
33+
import org.ossreviewtoolkit.model.LicenseSource
3334
import org.ossreviewtoolkit.model.OrtResult
3435
import org.ossreviewtoolkit.model.Package
3536
import org.ossreviewtoolkit.model.Provenance
@@ -45,6 +46,7 @@ import org.ossreviewtoolkit.model.licenses.ResolvedLicenseInfo
4546
import org.ossreviewtoolkit.model.utils.FindingCurationMatcher
4647
import org.ossreviewtoolkit.model.utils.prependedPath
4748
import org.ossreviewtoolkit.plugins.licensefactproviders.api.LicenseFactProvider
49+
import org.ossreviewtoolkit.utils.common.enumSetOf
4850
import org.ossreviewtoolkit.utils.common.replaceCredentialsInUri
4951
import org.ossreviewtoolkit.utils.spdx.SpdxConstants
5052
import org.ossreviewtoolkit.utils.spdx.SpdxLicense
@@ -148,7 +150,8 @@ internal enum class SpdxPackageType(val infix: String, val suffix: String = "")
148150
internal fun Package.toSpdxPackage(
149151
type: SpdxPackageType,
150152
licenseInfoResolver: LicenseInfoResolver,
151-
ortResult: OrtResult
153+
ortResult: OrtResult,
154+
autoConcludeToEffectiveLicense: Boolean
152155
): SpdxPackage {
153156
val packageVerificationCode = ortResult.getPackageVerificationCode(id, type)?.let {
154157
SpdxPackageVerificationCode(packageVerificationCodeValue = it)
@@ -158,14 +161,25 @@ internal fun Package.toSpdxPackage(
158161
.applyChoices(ortResult.getPackageLicenseChoices(id))
159162
.applyChoices(ortResult.getRepositoryLicenseChoices())
160163

161-
val effectiveLicense = resolvedLicenseInfo.effectiveLicense(
162-
LicenseView.CONCLUDED_OR_DECLARED_AND_DETECTED,
163-
ortResult.getPackageLicenseChoices(id),
164-
ortResult.getRepositoryLicenseChoices()
165-
)
166-
167164
val licenseDeclared = resolvedLicenseInfo.mainLicense()?.simplify()
168165

166+
val (licenseConcluded, licenseComments) = if (concludedLicense == null && autoConcludeToEffectiveLicense) {
167+
// Do not use `CONCLUDED_OR_DECLARED_AND_DETECTED` here to support explicitly setting the `concludedLicense` to
168+
// the `licenseDeclared` in order to acknowledge the latter and record it as the license concluded in SPDX.
169+
val licenseView = LicenseView(enumSetOf(LicenseSource.DECLARED, LicenseSource.DETECTED))
170+
val effectiveLicense = resolvedLicenseInfo.effectiveLicense(licenseView).takeIf { it != licenseDeclared }
171+
172+
effectiveLicense to "licenseConcluded has been set to the effective license."
173+
} else {
174+
val effectiveLicense = resolvedLicenseInfo.effectiveLicense(
175+
LicenseView.CONCLUDED_OR_DECLARED_AND_DETECTED,
176+
ortResult.getPackageLicenseChoices(id),
177+
ortResult.getRepositoryLicenseChoices()
178+
)
179+
180+
concludedLicense to "effectiveLicense: ${effectiveLicense?.toString().nullOrBlankToSpdxNone()}"
181+
}
182+
169183
return SpdxPackage(
170184
spdxId = id.toSpdxId(type),
171185
checksums = when (type) {
@@ -184,17 +198,17 @@ internal fun Package.toSpdxPackage(
184198
externalRefs = if (type == SpdxPackageType.PROJECT) emptyList() else toSpdxExternalReferences(),
185199
filesAnalyzed = packageVerificationCode != null,
186200
homepage = homepageUrl.nullOrBlankToSpdxNone(),
187-
licenseComments = "effectiveLicense: ${effectiveLicense?.toString().nullOrBlankToSpdxNone()}",
201+
licenseComments = licenseComments,
188202
licenseConcluded = when (type) {
189203
// Clear the concluded license as it might need to be different for the source artifact.
190204
SpdxPackageType.SOURCE_PACKAGE -> SpdxConstants.NOASSERTION
191205

192206
// Clear the concluded license as it might need to be different for the VCS location.
193207
SpdxPackageType.VCS_PACKAGE -> SpdxConstants.NOASSERTION
194208

195-
SpdxPackageType.PROJECT -> concludedLicense.nullOrBlankToSpdxNoassertionOrNone()
209+
SpdxPackageType.PROJECT -> licenseConcluded.nullOrBlankToSpdxNoassertionOrNone()
196210

197-
else -> concludedLicense.nullOrBlankToSpdxNoassertionOrNone()
211+
else -> licenseConcluded.nullOrBlankToSpdxNoassertionOrNone()
198212
},
199213
licenseDeclared = licenseDeclared
200214
?.sorted()

plugins/reporters/spdx/src/main/kotlin/SpdxDocumentModelMapper.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ internal object SpdxDocumentModelMapper {
9494
val spdxProjectPackage = project.toPackage().toSpdxPackage(
9595
SpdxPackageType.PROJECT,
9696
licenseInfoResolver,
97-
ortResult
97+
ortResult,
98+
config.autoConcludeToEffectiveLicense
9899
).copy(hasFiles = filesForProject.map { it.spdxId })
99100

100101
ortResult.getDependencies(
@@ -114,7 +115,8 @@ internal object SpdxDocumentModelMapper {
114115
val binaryPackage = pkg.toSpdxPackage(
115116
SpdxPackageType.BINARY_PACKAGE,
116117
licenseInfoResolver,
117-
ortResult
118+
ortResult,
119+
config.autoConcludeToEffectiveLicense
118120
)
119121

120122
ortResult.getDependencies(
@@ -138,7 +140,8 @@ internal object SpdxDocumentModelMapper {
138140
val vcsPackage = pkg.toSpdxPackage(
139141
SpdxPackageType.VCS_PACKAGE,
140142
licenseInfoResolver,
141-
ortResult
143+
ortResult,
144+
config.autoConcludeToEffectiveLicense
142145
).copy(hasFiles = filesForPackage.map { it.spdxId })
143146

144147
val vcsPackageRelationShip = SpdxRelationship(
@@ -163,7 +166,8 @@ internal object SpdxDocumentModelMapper {
163166
val sourceArtifactPackage = pkg.toSpdxPackage(
164167
SpdxPackageType.SOURCE_PACKAGE,
165168
licenseInfoResolver,
166-
ortResult
169+
ortResult,
170+
config.autoConcludeToEffectiveLicense
167171
).copy(hasFiles = filesForPackage.map { it.spdxId })
168172

169173
val sourceArtifactPackageRelationship = SpdxRelationship(

plugins/reporters/spdx/src/main/kotlin/SpdxDocumentReporter.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import org.ossreviewtoolkit.utils.spdx.SpdxConstants.LICENSE_REF_PREFIX
3636
import org.ossreviewtoolkit.utils.spdxdocument.SpdxModelMapper.FileFormat
3737
import org.ossreviewtoolkit.utils.spdxdocument.model.SPDX_VERSION_2_2
3838
import org.ossreviewtoolkit.utils.spdxdocument.model.SpdxDocument
39+
import org.ossreviewtoolkit.utils.spdxdocument.model.SpdxPackage
3940
import org.ossreviewtoolkit.utils.spdxexpression.SpdxCompoundExpression
4041
import org.ossreviewtoolkit.utils.spdxexpression.SpdxExpression
4142
import org.ossreviewtoolkit.utils.spdxexpression.SpdxLicenseWithExceptionExpression
@@ -86,6 +87,15 @@ data class SpdxDocumentReporterConfig(
8687
@OrtPluginOption(aliases = ["document.name"])
8788
val documentName: String?,
8889

90+
/**
91+
* Set the [SpdxPackage.licenseConcluded] to the effective license as determined by ORT if there is no concluded
92+
* license from a package curation. This is useful in workflows where it is discouraged to set a concluded license
93+
* as it unconditionally overwrites any other license, in instead liense findings should be curated to make the
94+
* effective license match the license conclusion.
95+
*/
96+
@OrtPluginOption(defaultValue = "false")
97+
val autoConcludeToEffectiveLicense: Boolean,
98+
8999
/**
90100
* Toggle whether the output document should contain information on file granularity about files containing
91101
* findings.

0 commit comments

Comments
 (0)