From ef79764555c693219ece5ed2645fab35190839e7 Mon Sep 17 00:00:00 2001 From: Martin Nonnenmacher Date: Sun, 22 Sep 2024 14:17:03 +0200 Subject: [PATCH 1/7] feat(plugins-api): Make `OrtPluginOption.defaultValue` optional Make the `defaultValue` property of the `OrtPluginOption` annotation optional. As annotation properties cannot be nullable, define a constant to mark if the default value should be ignored. This is a preparation for adding more properties to the annotation. Signed-off-by: Martin Nonnenmacher --- plugins/api/src/main/kotlin/OrtPluginOption.kt | 8 ++++++-- plugins/compiler/src/main/kotlin/PluginSpecFactory.kt | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/api/src/main/kotlin/OrtPluginOption.kt b/plugins/api/src/main/kotlin/OrtPluginOption.kt index 0e83454b6bca3..c9f11da161080 100644 --- a/plugins/api/src/main/kotlin/OrtPluginOption.kt +++ b/plugins/api/src/main/kotlin/OrtPluginOption.kt @@ -28,5 +28,9 @@ annotation class OrtPluginOption( /** * The default value of the option. */ - val defaultValue: String -) + val defaultValue: String = NO_DEFAULT_VALUE +) { + companion object { + const val NO_DEFAULT_VALUE = "[NO_DEFAULT_VALUE]" + } +} diff --git a/plugins/compiler/src/main/kotlin/PluginSpecFactory.kt b/plugins/compiler/src/main/kotlin/PluginSpecFactory.kt index 96005bbd8d8c7..4dbe947d42ae1 100644 --- a/plugins/compiler/src/main/kotlin/PluginSpecFactory.kt +++ b/plugins/compiler/src/main/kotlin/PluginSpecFactory.kt @@ -167,7 +167,7 @@ class PluginSpecFactory { ) } - val defaultValue = annotation?.defaultValue + val defaultValue = annotation?.defaultValue?.takeIf { it != OrtPluginOption.NO_DEFAULT_VALUE } PluginOption( name = param.name?.asString().orEmpty(), From 4a1bf4554a50cc3b9f1d05804a393d84408c0acf Mon Sep 17 00:00:00 2001 From: Martin Nonnenmacher Date: Sun, 22 Sep 2024 14:37:56 +0200 Subject: [PATCH 2/7] feat(plugins-api): Add a way to configure plugin option aliases Add the property `OrtPluginOption.aliases` to define optional aliases for configuration options. This can be used to rename options in a backward-compatible way. Signed-off-by: Martin Nonnenmacher --- .../api/src/main/kotlin/OrtPluginOption.kt | 8 +++- .../api/src/main/kotlin/PluginDescriptor.kt | 5 ++ .../src/main/kotlin/JsonSpecGenerator.kt | 6 +++ .../src/main/kotlin/PluginFactoryGenerator.kt | 48 +++++++++++++------ .../src/main/kotlin/PluginSpecFactory.kt | 1 + 5 files changed, 53 insertions(+), 15 deletions(-) diff --git a/plugins/api/src/main/kotlin/OrtPluginOption.kt b/plugins/api/src/main/kotlin/OrtPluginOption.kt index c9f11da161080..1e789beda09a4 100644 --- a/plugins/api/src/main/kotlin/OrtPluginOption.kt +++ b/plugins/api/src/main/kotlin/OrtPluginOption.kt @@ -28,7 +28,13 @@ annotation class OrtPluginOption( /** * The default value of the option. */ - val defaultValue: String = NO_DEFAULT_VALUE + val defaultValue: String = NO_DEFAULT_VALUE, + + /** + * A list of alternative names for the option. This can be used to make renaming options backward-compatible. + * Aliases are tried in the order they are defined until a value is found. + */ + val aliases: Array = [] ) { companion object { const val NO_DEFAULT_VALUE = "[NO_DEFAULT_VALUE]" diff --git a/plugins/api/src/main/kotlin/PluginDescriptor.kt b/plugins/api/src/main/kotlin/PluginDescriptor.kt index 2d6d0d6fc2919..a5859a65a7b0b 100644 --- a/plugins/api/src/main/kotlin/PluginDescriptor.kt +++ b/plugins/api/src/main/kotlin/PluginDescriptor.kt @@ -91,6 +91,11 @@ data class PluginOption( */ val defaultValue: String?, + /** + * A list of alternative names for the option. + */ + val aliases: List, + /** * Whether the option is required. */ diff --git a/plugins/compiler/src/main/kotlin/JsonSpecGenerator.kt b/plugins/compiler/src/main/kotlin/JsonSpecGenerator.kt index c3dc70a441129..4facfdb8abbd1 100644 --- a/plugins/compiler/src/main/kotlin/JsonSpecGenerator.kt +++ b/plugins/compiler/src/main/kotlin/JsonSpecGenerator.kt @@ -24,6 +24,7 @@ import com.google.devtools.ksp.processing.Dependencies import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json +import kotlinx.serialization.json.addAll import kotlinx.serialization.json.addJsonObject import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.encodeToStream @@ -51,6 +52,11 @@ class JsonSpecGenerator(private val codeGenerator: CodeGenerator) { put("type", it.type.name) put("description", it.description) put("default", it.defaultValue) + + putJsonArray("aliases") { + addAll(it.aliases) + } + put("isRequired", it.isRequired) } } diff --git a/plugins/compiler/src/main/kotlin/PluginFactoryGenerator.kt b/plugins/compiler/src/main/kotlin/PluginFactoryGenerator.kt index ebc8369593556..ec74fca9ef9fb 100644 --- a/plugins/compiler/src/main/kotlin/PluginFactoryGenerator.kt +++ b/plugins/compiler/src/main/kotlin/PluginFactoryGenerator.kt @@ -109,17 +109,26 @@ class PluginFactoryGenerator(private val codeGenerator: CodeGenerator) { pluginOptions.forEach { option -> add(" ${option.name} = ") + fun readOption(name: String) = + when (option.type) { + PluginOptionType.BOOLEAN -> add("config.options[%S]?.toBooleanStrict()", name) + PluginOptionType.INTEGER -> add("config.options[%S]?.toInt()", name) + PluginOptionType.LONG -> add("config.options[%S]?.toLong()", name) + PluginOptionType.SECRET -> add("config.secrets[%S]?.let { %T(it) }", name, Secret::class) + PluginOptionType.STRING -> add("config.options[%S]", name) + PluginOptionType.STRING_LIST -> add( + "config.options[%S]?.split(\",\")?.map { it.trim() }", + name + ) + } + // Add code to read the option from the options or secrets maps based on its type. - when (option.type) { - PluginOptionType.BOOLEAN -> add("config.options[%S]?.toBooleanStrict()", option.name) - PluginOptionType.INTEGER -> add("config.options[%S]?.toInt()", option.name) - PluginOptionType.LONG -> add("config.options[%S]?.toLong()", option.name) - PluginOptionType.SECRET -> add("config.secrets[%S]?.let { %T(it) }", option.name, Secret::class) - PluginOptionType.STRING -> add("config.options[%S]", option.name) - PluginOptionType.STRING_LIST -> add( - "config.options[%S]?.split(\",\")?.map { it.trim() }", - option.name - ) + readOption(option.name) + + // Add code to handle aliases. + option.aliases.forEach { alias -> + add(" ?: ") + readOption(alias) } // Add the default value if present. @@ -175,16 +184,27 @@ class PluginFactoryGenerator(private val codeGenerator: CodeGenerator) { | description = %S, | type = %T.%L, | defaultValue = %S, - | isRequired = %L - | ), - | + | aliases = listOf( """.trimMargin(), PluginOption::class, it.name, it.description, PluginOptionType::class, it.type.name, - it.defaultValue, + it.defaultValue + ) + + it.aliases.forEach { alias -> + add(" %S,", alias) + } + + add( + """ + | ), + | isRequired = %L + | ), + | + """.trimMargin(), it.isRequired ) } diff --git a/plugins/compiler/src/main/kotlin/PluginSpecFactory.kt b/plugins/compiler/src/main/kotlin/PluginSpecFactory.kt index 4dbe947d42ae1..6944d52bf12ac 100644 --- a/plugins/compiler/src/main/kotlin/PluginSpecFactory.kt +++ b/plugins/compiler/src/main/kotlin/PluginSpecFactory.kt @@ -174,6 +174,7 @@ class PluginSpecFactory { description = prop.docString?.trim().orEmpty(), type = type, defaultValue = defaultValue, + aliases = annotation?.aliases?.asList().orEmpty(), isRequired = !paramType.isMarkedNullable && defaultValue == null ) } From 7667027179c108a886b096fe9c6bf64825d0aa74 Mon Sep 17 00:00:00 2001 From: Martin Nonnenmacher Date: Sun, 22 Sep 2024 15:05:55 +0200 Subject: [PATCH 3/7] fix(plugins-api): Fix handling of default values for string list options Signed-off-by: Martin Nonnenmacher --- .../compiler/src/main/kotlin/PluginFactoryGenerator.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/compiler/src/main/kotlin/PluginFactoryGenerator.kt b/plugins/compiler/src/main/kotlin/PluginFactoryGenerator.kt index ec74fca9ef9fb..42753b95e815b 100644 --- a/plugins/compiler/src/main/kotlin/PluginFactoryGenerator.kt +++ b/plugins/compiler/src/main/kotlin/PluginFactoryGenerator.kt @@ -139,7 +139,15 @@ class PluginFactoryGenerator(private val codeGenerator: CodeGenerator) { PluginOptionType.LONG -> add(" ?: %LL", defaultValue.toLong()) PluginOptionType.SECRET -> add(" ?: %T(%S)", Secret::class, defaultValue) PluginOptionType.STRING -> add(" ?: %S", defaultValue) - PluginOptionType.STRING_LIST -> add(" ?: %S", defaultValue) + PluginOptionType.STRING_LIST -> { + add(" ?: listOf(") + + defaultValue.split(",").forEach { value -> + add("%S,", value.trim()) + } + + add(")") + } } } From e8f0e6cb240124d7dc3047242c9b89298f9b47d9 Mon Sep 17 00:00:00 2001 From: Martin Nonnenmacher Date: Wed, 4 Sep 2024 23:35:36 +0200 Subject: [PATCH 4/7] refactor(asciidoc): Make `AsciiDocTemplateReporter` abstract Convert `AsciiDocTemplateReporter` to an abstract class. This allows to simplify the constructor by making `backend` an abstract property. Signed-off-by: Martin Nonnenmacher --- .../asciidoc/src/main/kotlin/AsciiDocTemplateReporter.kt | 4 +++- .../asciidoc/src/main/kotlin/DocBookTemplateReporter.kt | 5 ++++- .../asciidoc/src/main/kotlin/HtmlTemplateReporter.kt | 5 ++++- .../asciidoc/src/main/kotlin/ManPageTemplateReporter.kt | 5 ++++- .../asciidoc/src/main/kotlin/PdfTemplateReporter.kt | 5 ++++- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/plugins/reporters/asciidoc/src/main/kotlin/AsciiDocTemplateReporter.kt b/plugins/reporters/asciidoc/src/main/kotlin/AsciiDocTemplateReporter.kt index 6b1639da2c4e5..c8d6d61d2c6e1 100644 --- a/plugins/reporters/asciidoc/src/main/kotlin/AsciiDocTemplateReporter.kt +++ b/plugins/reporters/asciidoc/src/main/kotlin/AsciiDocTemplateReporter.kt @@ -42,7 +42,7 @@ import org.ossreviewtoolkit.utils.ort.createOrtTempDir * [3]: https://github.com/asciidoctor/asciidoctorj * [4]: https://docs.asciidoctor.org/asciidoctor/latest/convert/available */ -open class AsciiDocTemplateReporter(private val backend: String, override val type: String) : Reporter { +abstract class AsciiDocTemplateReporter : Reporter { companion object { private const val ASCII_DOC_FILE_PREFIX = "AsciiDoc_" private const val ASCII_DOC_FILE_EXTENSION = "adoc" @@ -53,6 +53,8 @@ open class AsciiDocTemplateReporter(private val backend: String, override val ty private const val DEFECT_TEMPLATE_ID = "defect_report" } + protected abstract val backend: String + private val templateProcessor = FreemarkerTemplateProcessor( ASCII_DOC_TEMPLATE_DIRECTORY, ASCII_DOC_FILE_PREFIX, diff --git a/plugins/reporters/asciidoc/src/main/kotlin/DocBookTemplateReporter.kt b/plugins/reporters/asciidoc/src/main/kotlin/DocBookTemplateReporter.kt index 5e65a55cf7c00..4346e25dec818 100644 --- a/plugins/reporters/asciidoc/src/main/kotlin/DocBookTemplateReporter.kt +++ b/plugins/reporters/asciidoc/src/main/kotlin/DocBookTemplateReporter.kt @@ -27,4 +27,7 @@ import org.ossreviewtoolkit.reporter.Reporter * [1]: https://docbook.org * [2]: https://freemarker.apache.org */ -class DocBookTemplateReporter : AsciiDocTemplateReporter("docbook", "DocBookTemplate") +class DocBookTemplateReporter : AsciiDocTemplateReporter() { + override val backend = "docbook" + override val type = "DocBookTemplate" +} diff --git a/plugins/reporters/asciidoc/src/main/kotlin/HtmlTemplateReporter.kt b/plugins/reporters/asciidoc/src/main/kotlin/HtmlTemplateReporter.kt index 35b1ababc18fd..d5cc60a7c075c 100644 --- a/plugins/reporters/asciidoc/src/main/kotlin/HtmlTemplateReporter.kt +++ b/plugins/reporters/asciidoc/src/main/kotlin/HtmlTemplateReporter.kt @@ -26,4 +26,7 @@ import org.ossreviewtoolkit.reporter.Reporter * * [1]: https://freemarker.apache.org */ -class HtmlTemplateReporter : AsciiDocTemplateReporter("html", "HtmlTemplate") +class HtmlTemplateReporter : AsciiDocTemplateReporter() { + override val backend = "html" + override val type = "HtmlTemplate" +} diff --git a/plugins/reporters/asciidoc/src/main/kotlin/ManPageTemplateReporter.kt b/plugins/reporters/asciidoc/src/main/kotlin/ManPageTemplateReporter.kt index 7510e821796a7..038eebad2830c 100644 --- a/plugins/reporters/asciidoc/src/main/kotlin/ManPageTemplateReporter.kt +++ b/plugins/reporters/asciidoc/src/main/kotlin/ManPageTemplateReporter.kt @@ -26,4 +26,7 @@ import org.ossreviewtoolkit.reporter.Reporter * * [1]: https://freemarker.apache.org */ -class ManPageTemplateReporter : AsciiDocTemplateReporter("manpage", "ManPageTemplate") +class ManPageTemplateReporter : AsciiDocTemplateReporter() { + override val backend = "manpage" + override val type = "ManPageTemplate" +} diff --git a/plugins/reporters/asciidoc/src/main/kotlin/PdfTemplateReporter.kt b/plugins/reporters/asciidoc/src/main/kotlin/PdfTemplateReporter.kt index 9960870a39882..dda709c352b2b 100644 --- a/plugins/reporters/asciidoc/src/main/kotlin/PdfTemplateReporter.kt +++ b/plugins/reporters/asciidoc/src/main/kotlin/PdfTemplateReporter.kt @@ -52,12 +52,15 @@ import org.ossreviewtoolkit.reporter.Reporter * [4]: https://github.com/asciidoctor/asciidoctorj-pdf * [5]: https://docs.asciidoctor.org/pdf-converter/latest/theme/ */ -class PdfTemplateReporter : AsciiDocTemplateReporter("pdf", "PdfTemplate") { +class PdfTemplateReporter : AsciiDocTemplateReporter() { companion object { private const val OPTION_PDF_THEME_FILE = "pdf.theme.file" private const val OPTION_PDF_FONTS_DIR = "pdf.fonts.dir" } + override val backend = "pdf" + override val type = "PdfTemplate" + override fun processTemplateOptions(outputDir: File, options: MutableMap): Attributes = Attributes.builder().apply { val pdfTheme = options.remove(OPTION_PDF_THEME_FILE)?.let { option -> From ae90dd094809d57cf956a20027a39068590d2e01 Mon Sep 17 00:00:00 2001 From: Martin Nonnenmacher Date: Thu, 5 Sep 2024 01:08:01 +0200 Subject: [PATCH 5/7] refactor(reporter): Migrate to new plugin API Migrate the reporters to the new plugin API. With the reporter API reporter implementations get their configuration options via the `generateReport` function. With the new plugin API the configuration options are provided as a constructor parameter. With this change both ways are supported to allow for a step-by-step migration in the following commits. Signed-off-by: Martin Nonnenmacher --- cli/src/funTest/kotlin/ExamplesFunTest.kt | 7 ++-- integrations/completions/ort-completion.fish | 2 +- .../src/main/kotlin/ReporterCommand.kt | 21 +++++++----- plugins/reporters/aosd/build.gradle.kts | 4 ++- .../aosd/src/main/kotlin/Aosd2Reporter.kt | 13 +++++-- .../org.ossreviewtoolkit.reporter.Reporter | 1 - plugins/reporters/asciidoc/build.gradle.kts | 4 ++- .../main/kotlin/DocBookTemplateReporter.kt | 12 +++++-- .../src/main/kotlin/HtmlTemplateReporter.kt | 12 +++++-- .../main/kotlin/ManPageTemplateReporter.kt | 12 +++++-- .../src/main/kotlin/PdfTemplateReporter.kt | 12 +++++-- .../org.ossreviewtoolkit.reporter.Reporter | 4 --- plugins/reporters/ctrlx/build.gradle.kts | 4 ++- .../main/kotlin/CtrlXAutomationReporter.kt | 13 +++++-- .../org.ossreviewtoolkit.reporter.Reporter | 1 - plugins/reporters/cyclonedx/build.gradle.kts | 4 ++- .../src/main/kotlin/CycloneDxReporter.kt | 13 +++++-- .../org.ossreviewtoolkit.reporter.Reporter | 1 - .../evaluated-model/build.gradle.kts | 4 ++- .../src/main/kotlin/EvaluatedModelReporter.kt | 13 +++++-- .../org.ossreviewtoolkit.reporter.Reporter | 1 - plugins/reporters/fossid/build.gradle.kts | 4 ++- .../fossid/src/main/kotlin/FossIdReporter.kt | 13 +++++-- .../src/main/kotlin/FossIdSnippetReporter.kt | 15 +++++--- .../org.ossreviewtoolkit.reporter.Reporter | 2 -- plugins/reporters/freemarker/build.gradle.kts | 4 ++- .../main/kotlin/PlainTextTemplateReporter.kt | 14 ++++++-- .../org.ossreviewtoolkit.reporter.Reporter | 1 - plugins/reporters/gitlab/build.gradle.kts | 4 ++- .../main/kotlin/GitLabLicenseModelReporter.kt | 14 ++++++-- .../org.ossreviewtoolkit.reporter.Reporter | 1 - plugins/reporters/opossum/build.gradle.kts | 4 ++- .../src/main/kotlin/OpossumReporter.kt | 12 +++++-- .../org.ossreviewtoolkit.reporter.Reporter | 1 - plugins/reporters/spdx/build.gradle.kts | 4 ++- .../src/main/kotlin/SpdxDocumentReporter.kt | 13 +++++-- .../org.ossreviewtoolkit.reporter.Reporter | 1 - .../reporters/static-html/build.gradle.kts | 4 ++- .../src/main/kotlin/StaticHtmlReporter.kt | 13 +++++-- .../org.ossreviewtoolkit.reporter.Reporter | 1 - .../reporters/trustsource/build.gradle.kts | 4 ++- .../src/main/kotlin/TrustSourceReporter.kt | 13 +++++-- .../org.ossreviewtoolkit.reporter.Reporter | 1 - plugins/reporters/web-app/build.gradle.kts | 4 ++- .../web-app/src/main/kotlin/WebAppReporter.kt | 12 +++++-- .../org.ossreviewtoolkit.reporter.Reporter | 1 - reporter/build.gradle.kts | 1 + reporter/src/main/kotlin/Reporter.kt | 9 +---- reporter/src/main/kotlin/ReporterFactory.kt | 34 +++++++++++++++++++ 49 files changed, 263 insertions(+), 99 deletions(-) delete mode 100644 plugins/reporters/aosd/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter delete mode 100644 plugins/reporters/asciidoc/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter delete mode 100644 plugins/reporters/ctrlx/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter delete mode 100644 plugins/reporters/cyclonedx/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter delete mode 100644 plugins/reporters/evaluated-model/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter delete mode 100644 plugins/reporters/fossid/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter delete mode 100644 plugins/reporters/freemarker/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter delete mode 100644 plugins/reporters/gitlab/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter delete mode 100644 plugins/reporters/opossum/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter delete mode 100644 plugins/reporters/spdx/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter delete mode 100644 plugins/reporters/static-html/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter delete mode 100644 plugins/reporters/trustsource/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter delete mode 100644 plugins/reporters/web-app/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter create mode 100644 reporter/src/main/kotlin/ReporterFactory.kt diff --git a/cli/src/funTest/kotlin/ExamplesFunTest.kt b/cli/src/funTest/kotlin/ExamplesFunTest.kt index 054fa0d980ba4..d57deca20e395 100644 --- a/cli/src/funTest/kotlin/ExamplesFunTest.kt +++ b/cli/src/funTest/kotlin/ExamplesFunTest.kt @@ -58,8 +58,9 @@ import org.ossreviewtoolkit.model.config.SendMailConfiguration import org.ossreviewtoolkit.model.licenses.LicenseClassifications import org.ossreviewtoolkit.model.readValue import org.ossreviewtoolkit.notifier.Notifier +import org.ossreviewtoolkit.plugins.api.PluginConfig import org.ossreviewtoolkit.reporter.HowToFixTextProvider -import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput import org.ossreviewtoolkit.utils.ort.ORT_PACKAGE_CURATIONS_FILENAME import org.ossreviewtoolkit.utils.ort.ORT_REPO_CONFIG_FILENAME @@ -125,12 +126,12 @@ class ExamplesFunTest : StringSpec({ } "The Asciidoctor PDF theme file is a valid" { - val reporter = Reporter.ALL.getValue("PdfTemplate") + val reporter = ReporterFactory.ALL.getValue("PdfTemplate") val outputDir = tempdir() takeExampleFile("asciidoctor-pdf-theme.yml") - val report = reporter.generateReport( + val report = reporter.create(PluginConfig()).generateReport( ReporterInput(OrtResult.EMPTY), outputDir, PluginConfiguration( diff --git a/integrations/completions/ort-completion.fish b/integrations/completions/ort-completion.fish index 006865519993f..c8f40cb85a8c8 100644 --- a/integrations/completions/ort-completion.fish +++ b/integrations/completions/ort-completion.fish @@ -141,7 +141,7 @@ complete -c ort -f -n __fish_use_subcommand -a report -d 'Present Analyzer, Scan ## Options for report complete -c ort -n "__fish_seen_subcommand_from report" -l ort-file -s i -r -F -d 'The ORT result file to use.' complete -c ort -n "__fish_seen_subcommand_from report" -l output-dir -s o -r -F -d 'The output directory to store the generated reports in.' -complete -c ort -n "__fish_seen_subcommand_from report" -l report-formats -s f -r -d 'A comma-separated list of report formats to generate, any of [AOSD2, CtrlXAutomation, CycloneDx, DocBookTemplate, EvaluatedModel, FossId, FossIdSnippet, GitLabLicenseModel, HtmlTemplate, ManPageTemplate, Opossum, PdfTemplate, PlainTextTemplate, SpdxDocument, StaticHtml, TrustSource, WebApp].' +complete -c ort -n "__fish_seen_subcommand_from report" -l report-formats -s f -r -d 'A comma-separated list of report formats to generate, any of [AOSD2, CtrlXAutomation, CycloneDX, DocBookTemplate, EvaluatedModel, FossID, FossIdSnippet, GitLabLicenseModel, HtmlTemplate, ManPageTemplate, Opossum, PdfTemplate, PlainTextTemplate, SpdxDocument, StaticHTML, TrustSource, WebApp].' complete -c ort -n "__fish_seen_subcommand_from report" -l copyright-garbage-file -r -F -d 'A file containing copyright statements which are marked as garbage. This can make the output inconsistent with the evaluator output but is useful when testing copyright garbage.' complete -c ort -n "__fish_seen_subcommand_from report" -l custom-license-texts-dir -r -F -d 'A directory which maps custom license IDs to license texts. It should contain one text file per license with the license ID as the filename. A custom license text is used only if its ID has a \'LicenseRef-\' prefix and if the respective license text is not known by ORT.' complete -c ort -n "__fish_seen_subcommand_from report" -l how-to-fix-text-provider-script -r -F -d 'The path to a Kotlin script which returns an instance of a \'HowToFixTextProvider\'. That provider injects how-to-fix texts in Markdown format for ORT issues.' diff --git a/plugins/commands/reporter/src/main/kotlin/ReporterCommand.kt b/plugins/commands/reporter/src/main/kotlin/ReporterCommand.kt index 0973045f9ca55..b72c7a5f262c6 100644 --- a/plugins/commands/reporter/src/main/kotlin/ReporterCommand.kt +++ b/plugins/commands/reporter/src/main/kotlin/ReporterCommand.kt @@ -53,6 +53,7 @@ import org.ossreviewtoolkit.model.licenses.orEmpty import org.ossreviewtoolkit.model.readValue import org.ossreviewtoolkit.model.readValueOrDefault import org.ossreviewtoolkit.model.utils.DefaultResolutionProvider +import org.ossreviewtoolkit.plugins.api.PluginConfig import org.ossreviewtoolkit.plugins.commands.api.OrtCommand import org.ossreviewtoolkit.plugins.commands.api.utils.configurationGroup import org.ossreviewtoolkit.plugins.commands.api.utils.inputGroup @@ -63,7 +64,7 @@ import org.ossreviewtoolkit.plugins.packageconfigurationproviders.api.SimplePack import org.ossreviewtoolkit.plugins.packageconfigurationproviders.dir.DirPackageConfigurationProvider import org.ossreviewtoolkit.reporter.DefaultLicenseTextProvider import org.ossreviewtoolkit.reporter.HowToFixTextProvider -import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput import org.ossreviewtoolkit.utils.common.collectMessages import org.ossreviewtoolkit.utils.common.expandTilde @@ -103,9 +104,10 @@ class ReporterCommand : OrtCommand( private val reportFormats by option( "--report-formats", "-f", - help = "A comma-separated list of report formats to generate, any of ${Reporter.ALL.keys}." + help = "A comma-separated list of report formats to generate, any of ${ReporterFactory.ALL.keys}." ).convert { name -> - Reporter.ALL[name] ?: throw BadParameterValue("Report formats must be one or more of ${Reporter.ALL.keys}.") + ReporterFactory.ALL[name] + ?: throw BadParameterValue("Report formats must be one or more of ${ReporterFactory.ALL.keys}.") }.split(",").required().outputGroup() private val copyrightGarbageFile by option( @@ -191,8 +193,8 @@ class ReporterCommand : OrtCommand( "format, and the value is an arbitrary key-value pair. For example: " + "-O PlainTextTemplate=template.id=NOTICE_SUMMARY" ).splitPair().convert { (format, option) -> - require(format in Reporter.ALL.keys) { - "Report formats must be one or more of ${Reporter.ALL.keys}." + require(format in ReporterFactory.ALL.keys) { + "Report formats must be one or more of ${ReporterFactory.ALL.keys}." } format to Pair(option.substringBefore("="), option.substringAfter("=", "")) @@ -288,11 +290,12 @@ class ReporterCommand : OrtCommand( reportFormats.map { reporter -> async { val threadName = Thread.currentThread().name - echo("Generating the '${reporter.type}' report in thread '$threadName'...") + echo("Generating the '${reporter.descriptor.id}' report in thread '$threadName'...") reporter to measureTimedValue { - val options = reportConfigMap[reporter.type] ?: PluginConfiguration.EMPTY - reporter.generateReport(input, outputDir, options) + val options = reportConfigMap[reporter.descriptor.id] ?: PluginConfiguration.EMPTY + reporter.create(PluginConfig(options.options, options.secrets)) + .generateReport(input, outputDir, options) } } }.awaitAll() @@ -302,7 +305,7 @@ class ReporterCommand : OrtCommand( var failureCount = 0 reportDurationMap.value.forEach { (reporter, timedValue) -> - val name = reporter.type + val name = reporter.descriptor.id val fileResults = timedValue.value fileResults.forEach { fileResult -> diff --git a/plugins/reporters/aosd/build.gradle.kts b/plugins/reporters/aosd/build.gradle.kts index 5563f6ff448ad..335999df0fdf8 100644 --- a/plugins/reporters/aosd/build.gradle.kts +++ b/plugins/reporters/aosd/build.gradle.kts @@ -19,7 +19,7 @@ plugins { // Apply precompiled plugins. - id("ort-library-conventions") + id("ort-plugin-conventions") // Apply third-party plugins. alias(libs.plugins.kotlinSerialization) @@ -29,6 +29,8 @@ dependencies { api(projects.model) api(projects.reporter) + ksp(projects.reporter) + implementation(projects.utils.spdxUtils) implementation(libs.kotlinx.serialization.core) diff --git a/plugins/reporters/aosd/src/main/kotlin/Aosd2Reporter.kt b/plugins/reporters/aosd/src/main/kotlin/Aosd2Reporter.kt index 0e96ee017e338..fefe06e25529c 100644 --- a/plugins/reporters/aosd/src/main/kotlin/Aosd2Reporter.kt +++ b/plugins/reporters/aosd/src/main/kotlin/Aosd2Reporter.kt @@ -29,14 +29,21 @@ import org.ossreviewtoolkit.model.RemoteArtifact import org.ossreviewtoolkit.model.RepositoryProvenance import org.ossreviewtoolkit.model.config.PluginConfiguration import org.ossreviewtoolkit.model.licenses.LicenseView +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.plugins.reporters.aosd.AOSD2.ExternalDependency import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput import org.ossreviewtoolkit.utils.spdx.SpdxLicense -class Aosd2Reporter : Reporter { - override val type = "AOSD2" - +@OrtPlugin( + id = "AOSD2", + displayName = "Audi Open Source Diagnostics 2 Reporter", + description = "A reporter for the Audi Open Source Diagnostics 2 (AOSD2) format.", + factory = ReporterFactory::class +) +class Aosd2Reporter(override val descriptor: PluginDescriptor = Aosd2ReporterFactory.descriptor) : Reporter { override fun generateReport( input: ReporterInput, outputDir: File, diff --git a/plugins/reporters/aosd/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter b/plugins/reporters/aosd/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter deleted file mode 100644 index ecdd70ef85615..0000000000000 --- a/plugins/reporters/aosd/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter +++ /dev/null @@ -1 +0,0 @@ -org.ossreviewtoolkit.plugins.reporters.aosd.Aosd2Reporter diff --git a/plugins/reporters/asciidoc/build.gradle.kts b/plugins/reporters/asciidoc/build.gradle.kts index bee3f5e73a0cf..7d20b3334f46c 100644 --- a/plugins/reporters/asciidoc/build.gradle.kts +++ b/plugins/reporters/asciidoc/build.gradle.kts @@ -19,12 +19,14 @@ plugins { // Apply precompiled plugins. - id("ort-library-conventions") + id("ort-plugin-conventions") } dependencies { api(projects.reporter) + ksp(projects.reporter) + implementation(projects.model) implementation(projects.plugins.reporters.freemarkerReporter) implementation(projects.utils.commonUtils) diff --git a/plugins/reporters/asciidoc/src/main/kotlin/DocBookTemplateReporter.kt b/plugins/reporters/asciidoc/src/main/kotlin/DocBookTemplateReporter.kt index 4346e25dec818..1082b25f29d90 100644 --- a/plugins/reporters/asciidoc/src/main/kotlin/DocBookTemplateReporter.kt +++ b/plugins/reporters/asciidoc/src/main/kotlin/DocBookTemplateReporter.kt @@ -19,7 +19,10 @@ package org.ossreviewtoolkit.plugins.reporters.asciidoc +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory /** * A [Reporter] that creates [DocBook][1] files from [Apache Freemarker][2] templates. @@ -27,7 +30,12 @@ import org.ossreviewtoolkit.reporter.Reporter * [1]: https://docbook.org * [2]: https://freemarker.apache.org */ -class DocBookTemplateReporter : AsciiDocTemplateReporter() { +@OrtPlugin( + displayName = "DocBook Template Reporter", + description = "Generates DocBook files from Apache Freemarker templates.", + factory = ReporterFactory::class +) +class DocBookTemplateReporter(override val descriptor: PluginDescriptor = DocBookTemplateReporterFactory.descriptor) : + AsciiDocTemplateReporter() { override val backend = "docbook" - override val type = "DocBookTemplate" } diff --git a/plugins/reporters/asciidoc/src/main/kotlin/HtmlTemplateReporter.kt b/plugins/reporters/asciidoc/src/main/kotlin/HtmlTemplateReporter.kt index d5cc60a7c075c..26fac22a68fea 100644 --- a/plugins/reporters/asciidoc/src/main/kotlin/HtmlTemplateReporter.kt +++ b/plugins/reporters/asciidoc/src/main/kotlin/HtmlTemplateReporter.kt @@ -19,14 +19,22 @@ package org.ossreviewtoolkit.plugins.reporters.asciidoc +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory /** * A [Reporter] that creates HTML files from [Apache Freemarker][1] templates. * * [1]: https://freemarker.apache.org */ -class HtmlTemplateReporter : AsciiDocTemplateReporter() { +@OrtPlugin( + displayName = "HTML Template Reporter", + description = "Generates HTML files from Apache Freemarker templates.", + factory = ReporterFactory::class +) +class HtmlTemplateReporter(override val descriptor: PluginDescriptor = HtmlTemplateReporterFactory.descriptor) : + AsciiDocTemplateReporter() { override val backend = "html" - override val type = "HtmlTemplate" } diff --git a/plugins/reporters/asciidoc/src/main/kotlin/ManPageTemplateReporter.kt b/plugins/reporters/asciidoc/src/main/kotlin/ManPageTemplateReporter.kt index 038eebad2830c..710d9ae474e05 100644 --- a/plugins/reporters/asciidoc/src/main/kotlin/ManPageTemplateReporter.kt +++ b/plugins/reporters/asciidoc/src/main/kotlin/ManPageTemplateReporter.kt @@ -19,14 +19,22 @@ package org.ossreviewtoolkit.plugins.reporters.asciidoc +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory /** * A [Reporter] that creates man pages from [Apache Freemarker][1] templates. * * [1]: https://freemarker.apache.org */ -class ManPageTemplateReporter : AsciiDocTemplateReporter() { +@OrtPlugin( + displayName = "Man Page Template Reporter", + description = "Generates manpages files from Apache Freemarker templates.", + factory = ReporterFactory::class +) +class ManPageTemplateReporter(override val descriptor: PluginDescriptor = ManPageTemplateReporterFactory.descriptor) : + AsciiDocTemplateReporter() { override val backend = "manpage" - override val type = "ManPageTemplate" } diff --git a/plugins/reporters/asciidoc/src/main/kotlin/PdfTemplateReporter.kt b/plugins/reporters/asciidoc/src/main/kotlin/PdfTemplateReporter.kt index dda709c352b2b..92c8a2d5b0303 100644 --- a/plugins/reporters/asciidoc/src/main/kotlin/PdfTemplateReporter.kt +++ b/plugins/reporters/asciidoc/src/main/kotlin/PdfTemplateReporter.kt @@ -23,7 +23,10 @@ import java.io.File import org.asciidoctor.Attributes +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory /** * A [Reporter] that creates PDF files using a combination of [Apache Freemarker][1] templates and [AsciiDoc][2] @@ -52,14 +55,19 @@ import org.ossreviewtoolkit.reporter.Reporter * [4]: https://github.com/asciidoctor/asciidoctorj-pdf * [5]: https://docs.asciidoctor.org/pdf-converter/latest/theme/ */ -class PdfTemplateReporter : AsciiDocTemplateReporter() { +@OrtPlugin( + displayName = "PDF Template Reporter", + description = "Generates PDF files from Apache Freemarker templates.", + factory = ReporterFactory::class +) +class PdfTemplateReporter(override val descriptor: PluginDescriptor = PdfTemplateReporterFactory.descriptor) : + AsciiDocTemplateReporter() { companion object { private const val OPTION_PDF_THEME_FILE = "pdf.theme.file" private const val OPTION_PDF_FONTS_DIR = "pdf.fonts.dir" } override val backend = "pdf" - override val type = "PdfTemplate" override fun processTemplateOptions(outputDir: File, options: MutableMap): Attributes = Attributes.builder().apply { diff --git a/plugins/reporters/asciidoc/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter b/plugins/reporters/asciidoc/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter deleted file mode 100644 index 912dc850c2100..0000000000000 --- a/plugins/reporters/asciidoc/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter +++ /dev/null @@ -1,4 +0,0 @@ -org.ossreviewtoolkit.plugins.reporters.asciidoc.DocBookTemplateReporter -org.ossreviewtoolkit.plugins.reporters.asciidoc.HtmlTemplateReporter -org.ossreviewtoolkit.plugins.reporters.asciidoc.ManPageTemplateReporter -org.ossreviewtoolkit.plugins.reporters.asciidoc.PdfTemplateReporter diff --git a/plugins/reporters/ctrlx/build.gradle.kts b/plugins/reporters/ctrlx/build.gradle.kts index 1835d6cc3014a..2e0ea9260424f 100644 --- a/plugins/reporters/ctrlx/build.gradle.kts +++ b/plugins/reporters/ctrlx/build.gradle.kts @@ -19,7 +19,7 @@ plugins { // Apply precompiled plugins. - id("ort-library-conventions") + id("ort-plugin-conventions") // Apply third-party plugins. alias(libs.plugins.kotlinSerialization) @@ -29,6 +29,8 @@ dependencies { api(projects.reporter) api(projects.model) + ksp(projects.reporter) + implementation(projects.utils.spdxUtils) implementation(libs.kotlinx.serialization.core) diff --git a/plugins/reporters/ctrlx/src/main/kotlin/CtrlXAutomationReporter.kt b/plugins/reporters/ctrlx/src/main/kotlin/CtrlXAutomationReporter.kt index 3cb0592c70fad..819dae0970a30 100644 --- a/plugins/reporters/ctrlx/src/main/kotlin/CtrlXAutomationReporter.kt +++ b/plugins/reporters/ctrlx/src/main/kotlin/CtrlXAutomationReporter.kt @@ -26,12 +26,21 @@ import kotlinx.serialization.json.encodeToStream import org.ossreviewtoolkit.model.config.PluginConfiguration import org.ossreviewtoolkit.model.licenses.LicenseView +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput import org.ossreviewtoolkit.utils.spdx.SpdxConstants import org.ossreviewtoolkit.utils.spdx.SpdxLicense -class CtrlXAutomationReporter : Reporter { +@OrtPlugin( + displayName = "CtrlX Automation Reporter", + description = "A reporter for the ctrlX Automation format.", + factory = ReporterFactory::class +) +class CtrlXAutomationReporter(override val descriptor: PluginDescriptor = CtrlXAutomationReporterFactory.descriptor) : + Reporter { companion object { const val REPORT_FILENAME = "fossinfo.json" @@ -44,8 +53,6 @@ class CtrlXAutomationReporter : Reporter { ) } - override val type = "CtrlXAutomation" - override fun generateReport( input: ReporterInput, outputDir: File, diff --git a/plugins/reporters/ctrlx/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter b/plugins/reporters/ctrlx/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter deleted file mode 100644 index d677c1f746191..0000000000000 --- a/plugins/reporters/ctrlx/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter +++ /dev/null @@ -1 +0,0 @@ -org.ossreviewtoolkit.plugins.reporters.ctrlx.CtrlXAutomationReporter diff --git a/plugins/reporters/cyclonedx/build.gradle.kts b/plugins/reporters/cyclonedx/build.gradle.kts index 0921f9b160289..be8460ddf8949 100644 --- a/plugins/reporters/cyclonedx/build.gradle.kts +++ b/plugins/reporters/cyclonedx/build.gradle.kts @@ -19,7 +19,7 @@ plugins { // Apply precompiled plugins. - id("ort-library-conventions") + id("ort-plugin-conventions") } dependencies { @@ -27,6 +27,8 @@ dependencies { api(libs.cyclonedx) + ksp(projects.reporter) + implementation(projects.model) implementation(projects.utils.commonUtils) implementation(projects.utils.ortUtils) diff --git a/plugins/reporters/cyclonedx/src/main/kotlin/CycloneDxReporter.kt b/plugins/reporters/cyclonedx/src/main/kotlin/CycloneDxReporter.kt index a9b779f3947aa..c2d5f731ff19f 100644 --- a/plugins/reporters/cyclonedx/src/main/kotlin/CycloneDxReporter.kt +++ b/plugins/reporters/cyclonedx/src/main/kotlin/CycloneDxReporter.kt @@ -49,7 +49,10 @@ import org.ossreviewtoolkit.model.config.PluginConfiguration import org.ossreviewtoolkit.model.licenses.ResolvedLicenseInfo import org.ossreviewtoolkit.model.utils.toPurl import org.ossreviewtoolkit.model.vulnerabilities.Vulnerability +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput import org.ossreviewtoolkit.utils.common.alsoIfNull import org.ossreviewtoolkit.utils.ort.Environment @@ -69,7 +72,13 @@ import org.ossreviewtoolkit.utils.spdx.SpdxLicense * - *output.file.formats*: A comma-separated list of (case-insensitive) output formats to export to. Supported are XML * and JSON. */ -class CycloneDxReporter : Reporter { +@OrtPlugin( + id = "CycloneDX", + displayName = "CycloneDX Reporter", + description = "Creates software bills of materials (SBOM) in the CycloneDX format.", + factory = ReporterFactory::class +) +class CycloneDxReporter(override val descriptor: PluginDescriptor = CycloneDxReporterFactory.descriptor) : Reporter { companion object { val DEFAULT_SCHEMA_VERSION = Version.VERSION_15 val DEFAULT_DATA_LICENSE = SpdxLicense.CC0_1_0 @@ -82,8 +91,6 @@ class CycloneDxReporter : Reporter { const val OPTION_OUTPUT_FILE_FORMATS = "output.file.formats" } - override val type = "CycloneDx" - private fun Bom.addExternalReference(type: ExternalReference.Type, url: String, comment: String? = null) { if (url.isBlank()) return diff --git a/plugins/reporters/cyclonedx/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter b/plugins/reporters/cyclonedx/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter deleted file mode 100644 index 8909f3bde8bb1..0000000000000 --- a/plugins/reporters/cyclonedx/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter +++ /dev/null @@ -1 +0,0 @@ -org.ossreviewtoolkit.plugins.reporters.cyclonedx.CycloneDxReporter diff --git a/plugins/reporters/evaluated-model/build.gradle.kts b/plugins/reporters/evaluated-model/build.gradle.kts index 6eb0c04ee1a77..b8a135d85995b 100644 --- a/plugins/reporters/evaluated-model/build.gradle.kts +++ b/plugins/reporters/evaluated-model/build.gradle.kts @@ -19,7 +19,7 @@ plugins { // Apply precompiled plugins. - id("ort-library-conventions") + id("ort-plugin-conventions") } dependencies { @@ -30,6 +30,8 @@ dependencies { api(libs.jackson.annotations) api(libs.jackson.databind) + ksp(projects.reporter) + implementation(projects.utils.ortUtils) implementation(libs.jackson.core) diff --git a/plugins/reporters/evaluated-model/src/main/kotlin/EvaluatedModelReporter.kt b/plugins/reporters/evaluated-model/src/main/kotlin/EvaluatedModelReporter.kt index 8e2538e9783a5..0b5ac3e4fffb3 100644 --- a/plugins/reporters/evaluated-model/src/main/kotlin/EvaluatedModelReporter.kt +++ b/plugins/reporters/evaluated-model/src/main/kotlin/EvaluatedModelReporter.kt @@ -23,7 +23,10 @@ import java.io.File import org.ossreviewtoolkit.model.FileFormat import org.ossreviewtoolkit.model.config.PluginConfiguration +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput /** @@ -34,15 +37,19 @@ import org.ossreviewtoolkit.reporter.ReporterInput * - *deduplicateDependencyTree*: Controls whether subtrees occurring multiple times in the dependency tree are * stripped. */ -class EvaluatedModelReporter : Reporter { +@OrtPlugin( + displayName = "Evaluated Model Reporter", + description = "Generates an evaluated model of the ORT result.", + factory = ReporterFactory::class +) +class EvaluatedModelReporter(override val descriptor: PluginDescriptor = EvaluatedModelReporterFactory.descriptor) : + Reporter { companion object { const val OPTION_OUTPUT_FILE_FORMATS = "output.file.formats" const val OPTION_DEDUPLICATE_DEPENDENCY_TREE = "deduplicateDependencyTree" } - override val type = "EvaluatedModel" - override fun generateReport( input: ReporterInput, outputDir: File, diff --git a/plugins/reporters/evaluated-model/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter b/plugins/reporters/evaluated-model/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter deleted file mode 100644 index 7f7d89a09f970..0000000000000 --- a/plugins/reporters/evaluated-model/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter +++ /dev/null @@ -1 +0,0 @@ -org.ossreviewtoolkit.plugins.reporters.evaluatedmodel.EvaluatedModelReporter diff --git a/plugins/reporters/fossid/build.gradle.kts b/plugins/reporters/fossid/build.gradle.kts index f84c8fd8e6127..4af6d394e6e78 100644 --- a/plugins/reporters/fossid/build.gradle.kts +++ b/plugins/reporters/fossid/build.gradle.kts @@ -19,12 +19,14 @@ plugins { // Apply precompiled plugins. - id("ort-library-conventions") + id("ort-plugin-conventions") } dependencies { api(projects.reporter) + ksp(projects.reporter) + implementation(projects.clients.fossidWebappClient) implementation(projects.model) implementation(projects.plugins.reporters.asciidocReporter) diff --git a/plugins/reporters/fossid/src/main/kotlin/FossIdReporter.kt b/plugins/reporters/fossid/src/main/kotlin/FossIdReporter.kt index dc6ab30353a87..eeeec79914030 100644 --- a/plugins/reporters/fossid/src/main/kotlin/FossIdReporter.kt +++ b/plugins/reporters/fossid/src/main/kotlin/FossIdReporter.kt @@ -33,13 +33,22 @@ import org.ossreviewtoolkit.clients.fossid.model.report.ReportType import org.ossreviewtoolkit.clients.fossid.model.report.SelectionType import org.ossreviewtoolkit.model.ScanResult import org.ossreviewtoolkit.model.config.PluginConfiguration +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput import org.ossreviewtoolkit.utils.common.collectMessages import org.ossreviewtoolkit.utils.ort.runBlocking import org.ossreviewtoolkit.utils.ort.showStackTrace -class FossIdReporter : Reporter { +@OrtPlugin( + id = "FossID", + displayName = "FossID Reporter", + description = "Export reports from FossID.", + factory = ReporterFactory::class +) +class FossIdReporter(override val descriptor: PluginDescriptor = FossIdReporterFactory.descriptor) : Reporter { companion object { /** Name of the configuration property for the server URL. */ const val SERVER_URL_PROPERTY = "serverUrl" @@ -67,8 +76,6 @@ class FossIdReporter : Reporter { const val SCAN_CODE_KEY = "scancode" } - override val type = "FossId" - override fun generateReport( input: ReporterInput, outputDir: File, diff --git a/plugins/reporters/fossid/src/main/kotlin/FossIdSnippetReporter.kt b/plugins/reporters/fossid/src/main/kotlin/FossIdSnippetReporter.kt index 6c0a200ed20a8..eea0b48450b7f 100644 --- a/plugins/reporters/fossid/src/main/kotlin/FossIdSnippetReporter.kt +++ b/plugins/reporters/fossid/src/main/kotlin/FossIdSnippetReporter.kt @@ -22,20 +22,27 @@ package org.ossreviewtoolkit.plugins.reporters.fossid import java.io.File import org.ossreviewtoolkit.model.config.PluginConfiguration +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.plugins.reporters.asciidoc.HtmlTemplateReporter import org.ossreviewtoolkit.plugins.reporters.freemarker.FreemarkerTemplateProcessor import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput -class FossIdSnippetReporter : Reporter by delegateReporter { +@OrtPlugin( + displayName = "FossID Snippet Reporter", + description = "Generates a detailed report of the FossID snippet findings.", + factory = ReporterFactory::class +) +class FossIdSnippetReporter(override val descriptor: PluginDescriptor = FossIdSnippetReporterFactory.descriptor) : + Reporter by delegateReporter { companion object { private const val TEMPLATE_NAME = "fossid_snippet" - private val delegateReporter = HtmlTemplateReporter() + private val delegateReporter = HtmlTemplateReporter(FossIdSnippetReporterFactory.descriptor) } - override val type = "FossIdSnippet" - override fun generateReport( input: ReporterInput, outputDir: File, diff --git a/plugins/reporters/fossid/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter b/plugins/reporters/fossid/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter deleted file mode 100644 index d4a524b43f72f..0000000000000 --- a/plugins/reporters/fossid/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter +++ /dev/null @@ -1,2 +0,0 @@ -org.ossreviewtoolkit.plugins.reporters.fossid.FossIdReporter -org.ossreviewtoolkit.plugins.reporters.fossid.FossIdSnippetReporter diff --git a/plugins/reporters/freemarker/build.gradle.kts b/plugins/reporters/freemarker/build.gradle.kts index 252b12bc5bd63..49267cd19279d 100644 --- a/plugins/reporters/freemarker/build.gradle.kts +++ b/plugins/reporters/freemarker/build.gradle.kts @@ -19,7 +19,7 @@ plugins { // Apply precompiled plugins. - id("ort-library-conventions") + id("ort-plugin-conventions") } dependencies { @@ -27,6 +27,8 @@ dependencies { api(projects.reporter) api(projects.utils.spdxUtils) + ksp(projects.reporter) + implementation(projects.utils.commonUtils) implementation(projects.utils.ortUtils) diff --git a/plugins/reporters/freemarker/src/main/kotlin/PlainTextTemplateReporter.kt b/plugins/reporters/freemarker/src/main/kotlin/PlainTextTemplateReporter.kt index 13aecdd3b601b..45bc77f9dc609 100644 --- a/plugins/reporters/freemarker/src/main/kotlin/PlainTextTemplateReporter.kt +++ b/plugins/reporters/freemarker/src/main/kotlin/PlainTextTemplateReporter.kt @@ -22,7 +22,10 @@ package org.ossreviewtoolkit.plugins.reporters.freemarker import java.io.File import org.ossreviewtoolkit.model.config.PluginConfiguration +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput /** @@ -38,15 +41,20 @@ import org.ossreviewtoolkit.reporter.ReporterInput * * [1]: https://freemarker.apache.org */ -class PlainTextTemplateReporter : Reporter { +@OrtPlugin( + displayName = "Plain Text Template Reporter", + description = "Generates plain text files using Apache Freemarker templates.", + factory = ReporterFactory::class +) +class PlainTextTemplateReporter( + override val descriptor: PluginDescriptor = PlainTextTemplateReporterFactory.descriptor +) : Reporter { companion object { private const val TEMPLATE_DIRECTORY = "plain-text" private const val DEFAULT_TEMPLATE_ID = "NOTICE_DEFAULT" } - override val type = "PlainTextTemplate" - private val templateProcessor = FreemarkerTemplateProcessor(TEMPLATE_DIRECTORY) override fun generateReport( diff --git a/plugins/reporters/freemarker/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter b/plugins/reporters/freemarker/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter deleted file mode 100644 index 1966ed7fc8f62..0000000000000 --- a/plugins/reporters/freemarker/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter +++ /dev/null @@ -1 +0,0 @@ -org.ossreviewtoolkit.plugins.reporters.freemarker.PlainTextTemplateReporter diff --git a/plugins/reporters/gitlab/build.gradle.kts b/plugins/reporters/gitlab/build.gradle.kts index e63f388fda69b..0c7d1253710ad 100644 --- a/plugins/reporters/gitlab/build.gradle.kts +++ b/plugins/reporters/gitlab/build.gradle.kts @@ -19,7 +19,7 @@ plugins { // Apply precompiled plugins. - id("ort-library-conventions") + id("ort-plugin-conventions") // Apply third-party plugins. alias(libs.plugins.kotlinSerialization) @@ -28,6 +28,8 @@ plugins { dependencies { api(projects.reporter) + ksp(projects.reporter) + implementation(projects.model) implementation(projects.utils.commonUtils) implementation(projects.utils.ortUtils) diff --git a/plugins/reporters/gitlab/src/main/kotlin/GitLabLicenseModelReporter.kt b/plugins/reporters/gitlab/src/main/kotlin/GitLabLicenseModelReporter.kt index aa7a79dfc665d..78e21ec5955ff 100644 --- a/plugins/reporters/gitlab/src/main/kotlin/GitLabLicenseModelReporter.kt +++ b/plugins/reporters/gitlab/src/main/kotlin/GitLabLicenseModelReporter.kt @@ -26,7 +26,10 @@ import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonNamingStrategy import org.ossreviewtoolkit.model.config.PluginConfiguration +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput /** @@ -38,7 +41,14 @@ import org.ossreviewtoolkit.reporter.ReporterInput * This reporter supports the following options: * - *skip.excluded*: Set to 'true' to omit excluded packages in the report. Defaults to 'false'. */ -class GitLabLicenseModelReporter : Reporter { +@OrtPlugin( + displayName = "GitLab License Model Reporter", + description = "Creates YAML documents according to the GitLab license model schema version 2.1.", + factory = ReporterFactory::class +) +class GitLabLicenseModelReporter( + override val descriptor: PluginDescriptor = GitLabLicenseModelReporterFactory.descriptor +) : Reporter { companion object { const val OPTION_SKIP_EXCLUDED = "skip.excluded" @@ -50,8 +60,6 @@ class GitLabLicenseModelReporter : Reporter { } } - override val type = "GitLabLicenseModel" - private val reportFilename = "gl-license-scanning-report.json" override fun generateReport( diff --git a/plugins/reporters/gitlab/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter b/plugins/reporters/gitlab/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter deleted file mode 100644 index f4e1a6f1b3ea2..0000000000000 --- a/plugins/reporters/gitlab/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter +++ /dev/null @@ -1 +0,0 @@ -org.ossreviewtoolkit.plugins.reporters.gitlab.GitLabLicenseModelReporter diff --git a/plugins/reporters/opossum/build.gradle.kts b/plugins/reporters/opossum/build.gradle.kts index 61bb0ef70134f..d62c71a1f0a53 100644 --- a/plugins/reporters/opossum/build.gradle.kts +++ b/plugins/reporters/opossum/build.gradle.kts @@ -19,12 +19,14 @@ plugins { // Apply precompiled plugins. - id("ort-library-conventions") + id("ort-plugin-conventions") } dependencies { api(projects.reporter) + ksp(projects.reporter) + implementation(projects.downloader) implementation(projects.model) implementation(projects.utils.commonUtils) diff --git a/plugins/reporters/opossum/src/main/kotlin/OpossumReporter.kt b/plugins/reporters/opossum/src/main/kotlin/OpossumReporter.kt index 56fc6de4b3ca1..46e8143e1473d 100644 --- a/plugins/reporters/opossum/src/main/kotlin/OpossumReporter.kt +++ b/plugins/reporters/opossum/src/main/kotlin/OpossumReporter.kt @@ -45,7 +45,10 @@ import org.ossreviewtoolkit.model.VcsInfo import org.ossreviewtoolkit.model.config.PluginConfiguration import org.ossreviewtoolkit.model.utils.getPurlType import org.ossreviewtoolkit.model.utils.toPurl +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput import org.ossreviewtoolkit.utils.common.packZip import org.ossreviewtoolkit.utils.ort.createOrtTempDir @@ -79,7 +82,12 @@ internal fun resolvePath(pieces: List) = pieces.reduce { right, left -> * This reporter supports the following option: * - *scanner.maxDepth*: The depth to which the full file level scanner information is added */ -class OpossumReporter : Reporter { +@OrtPlugin( + displayName = "Opossum Reporter", + description = "Generates a report in the Opossum format.", + factory = ReporterFactory::class +) +class OpossumReporter(override val descriptor: PluginDescriptor = OpossumReporterFactory.descriptor) : Reporter { companion object { const val OPTION_SCANNER_MAX_DEPTH = "scanner.maxDepth" } @@ -488,8 +496,6 @@ class OpossumReporter : Reporter { } } - override val type = "Opossum" - private fun writeReport(outputFile: File, opossumInput: OpossumInput) { val jsonFile = createOrtTempDir().resolve("input.json") JsonMapper().setSerializationInclusion(Include.NON_NULL).writeValue(jsonFile, opossumInput.toJson()) diff --git a/plugins/reporters/opossum/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter b/plugins/reporters/opossum/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter deleted file mode 100644 index 20bc9ea731a50..0000000000000 --- a/plugins/reporters/opossum/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter +++ /dev/null @@ -1 +0,0 @@ -org.ossreviewtoolkit.plugins.reporters.opossum.OpossumReporter diff --git a/plugins/reporters/spdx/build.gradle.kts b/plugins/reporters/spdx/build.gradle.kts index 4b9097f16026c..2695e2fd696fb 100644 --- a/plugins/reporters/spdx/build.gradle.kts +++ b/plugins/reporters/spdx/build.gradle.kts @@ -19,12 +19,14 @@ plugins { // Apply precompiled plugins. - id("ort-library-conventions") + id("ort-plugin-conventions") } dependencies { api(projects.reporter) + ksp(projects.reporter) + implementation(projects.model) implementation(projects.utils.commonUtils) implementation(projects.utils.ortUtils) diff --git a/plugins/reporters/spdx/src/main/kotlin/SpdxDocumentReporter.kt b/plugins/reporters/spdx/src/main/kotlin/SpdxDocumentReporter.kt index 6db635459fd74..bab015a203c61 100644 --- a/plugins/reporters/spdx/src/main/kotlin/SpdxDocumentReporter.kt +++ b/plugins/reporters/spdx/src/main/kotlin/SpdxDocumentReporter.kt @@ -24,7 +24,10 @@ import java.io.File import org.apache.logging.log4j.kotlin.logger import org.ossreviewtoolkit.model.config.PluginConfiguration +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput import org.ossreviewtoolkit.utils.spdx.SpdxCompoundExpression import org.ossreviewtoolkit.utils.spdx.SpdxConstants.LICENSE_REF_PREFIX @@ -50,7 +53,13 @@ import org.ossreviewtoolkit.utils.spdx.model.SpdxDocument * - *file.information.enabled*: Toggle whether the output document should contain information on file granularity * about files containing findings. */ -class SpdxDocumentReporter : Reporter { +@OrtPlugin( + displayName = "SPDX Document Reporter", + description = "Creates SPDX documents in YAML and JSON format.", + factory = ReporterFactory::class +) +class SpdxDocumentReporter(override val descriptor: PluginDescriptor = SpdxDocumentReporterFactory.descriptor) : + Reporter { companion object { const val REPORT_BASE_FILENAME = "bom.spdx" @@ -65,8 +74,6 @@ class SpdxDocumentReporter : Reporter { private const val DOCUMENT_NAME_DEFAULT_VALUE = "Unnamed document" } - override val type = "SpdxDocument" - override fun generateReport( input: ReporterInput, outputDir: File, diff --git a/plugins/reporters/spdx/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter b/plugins/reporters/spdx/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter deleted file mode 100644 index 159b6cabb2b6c..0000000000000 --- a/plugins/reporters/spdx/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter +++ /dev/null @@ -1 +0,0 @@ -org.ossreviewtoolkit.plugins.reporters.spdx.SpdxDocumentReporter diff --git a/plugins/reporters/static-html/build.gradle.kts b/plugins/reporters/static-html/build.gradle.kts index 2856ae68f2445..dc6cb70327fa2 100644 --- a/plugins/reporters/static-html/build.gradle.kts +++ b/plugins/reporters/static-html/build.gradle.kts @@ -19,12 +19,14 @@ plugins { // Apply precompiled plugins. - id("ort-library-conventions") + id("ort-plugin-conventions") } dependencies { api(projects.reporter) + ksp(projects.reporter) + implementation(projects.downloader) implementation(projects.model) implementation(projects.utils.commonUtils) diff --git a/plugins/reporters/static-html/src/main/kotlin/StaticHtmlReporter.kt b/plugins/reporters/static-html/src/main/kotlin/StaticHtmlReporter.kt index 4f8398003f460..4df71cd3d5d40 100644 --- a/plugins/reporters/static-html/src/main/kotlin/StaticHtmlReporter.kt +++ b/plugins/reporters/static-html/src/main/kotlin/StaticHtmlReporter.kt @@ -46,7 +46,10 @@ import org.ossreviewtoolkit.model.config.RepositoryConfiguration import org.ossreviewtoolkit.model.config.ScopeExclude import org.ossreviewtoolkit.model.licenses.ResolvedLicenseLocation import org.ossreviewtoolkit.model.yamlMapper +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput import org.ossreviewtoolkit.utils.common.isValidUri import org.ossreviewtoolkit.utils.common.joinNonBlank @@ -63,9 +66,13 @@ import org.ossreviewtoolkit.utils.spdx.SpdxLicenseWithExceptionExpression private const val RULE_VIOLATION_TABLE_ID = "rule-violation-summary" @Suppress("LargeClass", "TooManyFunctions") -class StaticHtmlReporter : Reporter { - override val type = "StaticHtml" - +@OrtPlugin( + id = "StaticHTML", + displayName = "Static HTML Reporter", + description = "Generates a static HTML report.", + factory = ReporterFactory::class +) +class StaticHtmlReporter(override val descriptor: PluginDescriptor = StaticHtmlReporterFactory.descriptor) : Reporter { private val reportFilename = "scan-report.html" private val css = javaClass.getResource("/static-html-reporter.css").readText() private val licensesSha1 = mutableMapOf() diff --git a/plugins/reporters/static-html/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter b/plugins/reporters/static-html/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter deleted file mode 100644 index cf42334fd0a44..0000000000000 --- a/plugins/reporters/static-html/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter +++ /dev/null @@ -1 +0,0 @@ -org.ossreviewtoolkit.plugins.reporters.statichtml.StaticHtmlReporter diff --git a/plugins/reporters/trustsource/build.gradle.kts b/plugins/reporters/trustsource/build.gradle.kts index 7d91541e9f549..5089b89ae5ddd 100644 --- a/plugins/reporters/trustsource/build.gradle.kts +++ b/plugins/reporters/trustsource/build.gradle.kts @@ -19,7 +19,7 @@ plugins { // Apply precompiled plugins. - id("ort-library-conventions") + id("ort-plugin-conventions") // Apply third-party plugins. alias(libs.plugins.kotlinSerialization) @@ -28,6 +28,8 @@ plugins { dependencies { api(projects.reporter) + ksp(projects.reporter) + implementation(projects.downloader) implementation(projects.model) implementation(projects.utils.commonUtils) diff --git a/plugins/reporters/trustsource/src/main/kotlin/TrustSourceReporter.kt b/plugins/reporters/trustsource/src/main/kotlin/TrustSourceReporter.kt index 172f3472ce3b5..bd0cfa9bee14c 100644 --- a/plugins/reporters/trustsource/src/main/kotlin/TrustSourceReporter.kt +++ b/plugins/reporters/trustsource/src/main/kotlin/TrustSourceReporter.kt @@ -27,16 +27,23 @@ import kotlinx.serialization.json.encodeToStream import org.ossreviewtoolkit.model.DependencyNode import org.ossreviewtoolkit.model.config.PluginConfiguration import org.ossreviewtoolkit.model.licenses.LicenseView +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput -class TrustSourceReporter : Reporter { +@OrtPlugin( + displayName = "TrustSource Reporter", + description = "Generates a report in the TrustSource JSON format.", + factory = ReporterFactory::class +) +class TrustSourceReporter(override val descriptor: PluginDescriptor = TrustSourceReporterFactory.descriptor) : + Reporter { companion object { val JSON = Json { encodeDefaults = false } } - override val type = "TrustSource" - private val reportFilename = "trustsource-report.json" override fun generateReport( diff --git a/plugins/reporters/trustsource/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter b/plugins/reporters/trustsource/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter deleted file mode 100644 index 023526110a1b0..0000000000000 --- a/plugins/reporters/trustsource/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter +++ /dev/null @@ -1 +0,0 @@ -org.ossreviewtoolkit.plugins.reporters.trustsource.TrustSourceReporter diff --git a/plugins/reporters/web-app/build.gradle.kts b/plugins/reporters/web-app/build.gradle.kts index 8f0c04163748e..aca37ea8fc59f 100644 --- a/plugins/reporters/web-app/build.gradle.kts +++ b/plugins/reporters/web-app/build.gradle.kts @@ -19,7 +19,7 @@ plugins { // Apply precompiled plugins. - id("ort-library-conventions") + id("ort-plugin-conventions") } val generatedResourcesDir = layout.buildDirectory.dir("generated-resources/main") @@ -39,6 +39,8 @@ sourceSets.main.get().output.dir(mapOf("builtBy" to copyWebAppTemplate), generat dependencies { api(projects.reporter) + ksp(projects.reporter) + implementation(projects.plugins.reporters.evaluatedModelReporter) implementation(libs.commonsCompress) diff --git a/plugins/reporters/web-app/src/main/kotlin/WebAppReporter.kt b/plugins/reporters/web-app/src/main/kotlin/WebAppReporter.kt index ff358577d38e3..aa23ed9a6ee06 100644 --- a/plugins/reporters/web-app/src/main/kotlin/WebAppReporter.kt +++ b/plugins/reporters/web-app/src/main/kotlin/WebAppReporter.kt @@ -30,8 +30,11 @@ import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream import org.apache.commons.compress.compressors.gzip.GzipParameters import org.ossreviewtoolkit.model.config.PluginConfiguration +import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.plugins.reporters.evaluatedmodel.EvaluatedModel import org.ossreviewtoolkit.reporter.Reporter +import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput private const val PLACEHOLDER = "ORT_REPORT_DATA_PLACEHOLDER" @@ -43,13 +46,16 @@ private const val PLACEHOLDER = "ORT_REPORT_DATA_PLACEHOLDER" * - *deduplicateDependencyTree*: Controls whether subtrees occurring multiple times in the dependency tree are * stripped. */ -class WebAppReporter : Reporter { +@OrtPlugin( + displayName = "WebApp Reporter", + description = "Generates a web application to browse an ORT result interactively.", + factory = ReporterFactory::class +) +class WebAppReporter(override val descriptor: PluginDescriptor = WebAppReporterFactory.descriptor) : Reporter { companion object { const val OPTION_DEDUPLICATE_DEPENDENCY_TREE = "deduplicateDependencyTree" } - override val type = "WebApp" - private val reportFilename = "scan-report-web-app.html" override fun generateReport( diff --git a/plugins/reporters/web-app/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter b/plugins/reporters/web-app/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter deleted file mode 100644 index 594ae8ef37028..0000000000000 --- a/plugins/reporters/web-app/src/main/resources/META-INF/services/org.ossreviewtoolkit.reporter.Reporter +++ /dev/null @@ -1 +0,0 @@ -org.ossreviewtoolkit.plugins.reporters.webapp.WebAppReporter diff --git a/reporter/build.gradle.kts b/reporter/build.gradle.kts index a08d2060133a9..cf9dc32ea3478 100644 --- a/reporter/build.gradle.kts +++ b/reporter/build.gradle.kts @@ -27,6 +27,7 @@ plugins { dependencies { api(projects.model) + api(projects.plugins.api) implementation(projects.utils.scriptingUtils) implementation(projects.utils.spdxUtils) diff --git a/reporter/src/main/kotlin/Reporter.kt b/reporter/src/main/kotlin/Reporter.kt index 580eac22837aa..77e451b74ed15 100644 --- a/reporter/src/main/kotlin/Reporter.kt +++ b/reporter/src/main/kotlin/Reporter.kt @@ -23,19 +23,12 @@ import java.io.File import org.ossreviewtoolkit.model.OrtResult import org.ossreviewtoolkit.model.config.PluginConfiguration -import org.ossreviewtoolkit.utils.common.Plugin +import org.ossreviewtoolkit.plugins.api.Plugin /** * A reporter that creates a human-readable report from a given [OrtResult]. */ interface Reporter : Plugin { - companion object { - /** - * All [reporters][Reporter] available in the classpath, associated by their names. - */ - val ALL by lazy { Plugin.getAll() } - } - /** * Generate a report for the provided [input] and write the generated file(s) to the [outputDir]. If and how the * [input] data is used depends on the specific reporter implementation, taking into account any format-specific diff --git a/reporter/src/main/kotlin/ReporterFactory.kt b/reporter/src/main/kotlin/ReporterFactory.kt new file mode 100644 index 0000000000000..9d350a3620bad --- /dev/null +++ b/reporter/src/main/kotlin/ReporterFactory.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 The ORT Project Authors (see ) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * License-Filename: LICENSE + */ + +package org.ossreviewtoolkit.reporter + +import org.ossreviewtoolkit.plugins.api.PluginFactory + +/** + * A factory interface for creating [Reporter] instances. + */ +interface ReporterFactory : PluginFactory { + companion object { + /** + * All [reporter factories][ReporterFactory] available in the classpath, associated by their ids. + */ + val ALL by lazy { PluginFactory.getAll() } + } +} From 02b921d0c78057d9e7b87dfd0a906bbdd1b6401d Mon Sep 17 00:00:00 2001 From: Martin Nonnenmacher Date: Sun, 22 Sep 2024 15:03:42 +0200 Subject: [PATCH 6/7] refactor(cyclonedx): Use a plugin config class Add a config class for the reporter and use it instead of the options passed to the `generateReport` function. Signed-off-by: Martin Nonnenmacher --- .../kotlin/CycloneDxReporterFunTest.kt | 48 +++++----- .../src/main/kotlin/CycloneDxReporter.kt | 95 ++++++++++++------- 2 files changed, 84 insertions(+), 59 deletions(-) diff --git a/plugins/reporters/cyclonedx/src/funTest/kotlin/CycloneDxReporterFunTest.kt b/plugins/reporters/cyclonedx/src/funTest/kotlin/CycloneDxReporterFunTest.kt index fddc678a26ef0..640e46c8f790c 100644 --- a/plugins/reporters/cyclonedx/src/funTest/kotlin/CycloneDxReporterFunTest.kt +++ b/plugins/reporters/cyclonedx/src/funTest/kotlin/CycloneDxReporterFunTest.kt @@ -39,7 +39,7 @@ import org.cyclonedx.parsers.JsonParser import org.cyclonedx.parsers.XmlParser import org.ossreviewtoolkit.model.OrtResult -import org.ossreviewtoolkit.model.config.PluginConfiguration +import org.ossreviewtoolkit.plugins.api.PluginConfig import org.ossreviewtoolkit.plugins.reporters.cyclonedx.CycloneDxReporter.Companion.REPORT_BASE_FILENAME import org.ossreviewtoolkit.reporter.ORT_RESULT import org.ossreviewtoolkit.reporter.ORT_RESULT_WITH_VULNERABILITIES @@ -49,15 +49,11 @@ import org.ossreviewtoolkit.utils.common.normalizeLineBreaks import org.ossreviewtoolkit.utils.test.getAssetAsString class CycloneDxReporterFunTest : WordSpec({ - val defaultSchemaVersion = CycloneDxReporter.DEFAULT_SCHEMA_VERSION.versionString + val defaultSchemaVersion = DEFAULT_SCHEMA_VERSION.versionString val outputDir = tempdir() - fun CycloneDxReporter.generateReport(result: OrtResult, options: Options): List> = - generateReport( - ReporterInput(result), - outputDir, - PluginConfiguration(options) - ) + fun generateReport(result: OrtResult, options: Options): List> = + CycloneDxReporterFactory().create(PluginConfig(options)).generateReport(ReporterInput(result), outputDir) "BOM generation with single option" should { val optionSingle = mapOf("single.bom" to "true") @@ -65,7 +61,7 @@ class CycloneDxReporterFunTest : WordSpec({ "create just one file" { val jsonOptions = optionSingle + mapOf("output.file.formats" to "json") - val bomFileResults = CycloneDxReporter().generateReport(ORT_RESULT, jsonOptions) + val bomFileResults = generateReport(ORT_RESULT, jsonOptions) bomFileResults.shouldBeSingleton { it shouldBeSuccess outputDir.resolve("$REPORT_BASE_FILENAME.json") @@ -75,13 +71,13 @@ class CycloneDxReporterFunTest : WordSpec({ "be valid XML according to schema version $defaultSchemaVersion" { val xmlOptions = optionSingle + mapOf("output.file.formats" to "xml") - val bomFileResults = CycloneDxReporter().generateReport(ORT_RESULT, xmlOptions) + val bomFileResults = generateReport(ORT_RESULT, xmlOptions) bomFileResults.shouldBeSingleton { it shouldBeSuccess { bomFile -> bomFile shouldBe aFile() bomFile shouldNotBe emptyFile() - XmlParser().validate(bomFile, CycloneDxReporter.DEFAULT_SCHEMA_VERSION) should beEmpty() + XmlParser().validate(bomFile, DEFAULT_SCHEMA_VERSION) should beEmpty() } } } @@ -90,7 +86,7 @@ class CycloneDxReporterFunTest : WordSpec({ val expectedBom = getAssetAsString("cyclonedx-reporter-expected-result.xml") val xmlOptions = optionSingle + mapOf("output.file.formats" to "xml") - val bomFileResults = CycloneDxReporter().generateReport(ORT_RESULT_WITH_VULNERABILITIES, xmlOptions) + val bomFileResults = generateReport(ORT_RESULT_WITH_VULNERABILITIES, xmlOptions) bomFileResults.shouldBeSingleton { it shouldBeSuccess { bomFile -> @@ -106,13 +102,13 @@ class CycloneDxReporterFunTest : WordSpec({ "be valid JSON according to schema version $defaultSchemaVersion" { val jsonOptions = optionSingle + mapOf("output.file.formats" to "json") - val bomFileResults = CycloneDxReporter().generateReport(ORT_RESULT_WITH_VULNERABILITIES, jsonOptions) + val bomFileResults = generateReport(ORT_RESULT_WITH_VULNERABILITIES, jsonOptions) bomFileResults.shouldBeSingleton { it shouldBeSuccess { bomFile -> bomFile shouldBe aFile() bomFile shouldNotBe emptyFile() - JsonParser().validate(bomFile, CycloneDxReporter.DEFAULT_SCHEMA_VERSION) should beEmpty() + JsonParser().validate(bomFile, DEFAULT_SCHEMA_VERSION) should beEmpty() } } } @@ -121,7 +117,7 @@ class CycloneDxReporterFunTest : WordSpec({ val expectedBom = getAssetAsString("cyclonedx-reporter-expected-result.json") val jsonOptions = optionSingle + mapOf("output.file.formats" to "json") - val bomFileResults = CycloneDxReporter().generateReport(ORT_RESULT_WITH_VULNERABILITIES, jsonOptions) + val bomFileResults = generateReport(ORT_RESULT_WITH_VULNERABILITIES, jsonOptions) bomFileResults.shouldBeSingleton { it shouldBeSuccess { bomFile -> @@ -141,7 +137,7 @@ class CycloneDxReporterFunTest : WordSpec({ "create one file per project" { val jsonOptions = optionMulti + mapOf("output.file.formats" to "json") - val bomFileResults = CycloneDxReporter().generateReport(ORT_RESULT_WITH_VULNERABILITIES, jsonOptions) + val bomFileResults = generateReport(ORT_RESULT_WITH_VULNERABILITIES, jsonOptions) bomFileResults shouldHaveSize 2 bomFileResults.forAll { it.shouldBeSuccess() } @@ -150,50 +146,50 @@ class CycloneDxReporterFunTest : WordSpec({ "generate valid XML files according to schema version $defaultSchemaVersion" { val xmlOptions = optionMulti + mapOf("output.file.formats" to "xml") - val (bomFileResultWithFindings, bomFileResultWithoutFindings) = CycloneDxReporter() - .generateReport(ORT_RESULT_WITH_VULNERABILITIES, xmlOptions).also { + val (bomFileResultWithFindings, bomFileResultWithoutFindings) = + generateReport(ORT_RESULT_WITH_VULNERABILITIES, xmlOptions).also { it shouldHaveSize 2 } bomFileResultWithFindings shouldBeSuccess { bomFile -> bomFile shouldBe aFile() bomFile shouldNotBe emptyFile() - XmlParser().validate(bomFile, CycloneDxReporter.DEFAULT_SCHEMA_VERSION) should beEmpty() + XmlParser().validate(bomFile, DEFAULT_SCHEMA_VERSION) should beEmpty() } bomFileResultWithoutFindings shouldBeSuccess { bomFile -> bomFile shouldBe aFile() bomFile shouldNotBe emptyFile() - XmlParser().validate(bomFile, CycloneDxReporter.DEFAULT_SCHEMA_VERSION) should beEmpty() + XmlParser().validate(bomFile, DEFAULT_SCHEMA_VERSION) should beEmpty() } } "generate valid JSON files according to schema version $defaultSchemaVersion" { val jsonOptions = optionMulti + mapOf("output.file.formats" to "json") - val (bomFileResultWithFindings, bomFileResultWithoutFindings) = CycloneDxReporter() - .generateReport(ORT_RESULT_WITH_VULNERABILITIES, jsonOptions).also { + val (bomFileResultWithFindings, bomFileResultWithoutFindings) = + generateReport(ORT_RESULT_WITH_VULNERABILITIES, jsonOptions).also { it shouldHaveSize 2 } bomFileResultWithFindings shouldBeSuccess { bomFile -> bomFile shouldBe aFile() bomFile shouldNotBe emptyFile() - JsonParser().validate(bomFile, CycloneDxReporter.DEFAULT_SCHEMA_VERSION) should beEmpty() + JsonParser().validate(bomFile, DEFAULT_SCHEMA_VERSION) should beEmpty() } bomFileResultWithoutFindings shouldBeSuccess { bomFile -> bomFile shouldBe aFile() bomFile shouldNotBe emptyFile() - JsonParser().validate(bomFile, CycloneDxReporter.DEFAULT_SCHEMA_VERSION) should beEmpty() + JsonParser().validate(bomFile, DEFAULT_SCHEMA_VERSION) should beEmpty() } } "generate expected JSON files" { val jsonOptions = optionMulti + mapOf("output.file.formats" to "json") - val (bomFileResultWithFindings, bomFileResultWithoutFindings) = CycloneDxReporter() - .generateReport(ORT_RESULT, jsonOptions).also { + val (bomFileResultWithFindings, bomFileResultWithoutFindings) = + generateReport(ORT_RESULT, jsonOptions).also { it shouldHaveSize 2 } diff --git a/plugins/reporters/cyclonedx/src/main/kotlin/CycloneDxReporter.kt b/plugins/reporters/cyclonedx/src/main/kotlin/CycloneDxReporter.kt index c2d5f731ff19f..95971fa46de60 100644 --- a/plugins/reporters/cyclonedx/src/main/kotlin/CycloneDxReporter.kt +++ b/plugins/reporters/cyclonedx/src/main/kotlin/CycloneDxReporter.kt @@ -50,6 +50,7 @@ import org.ossreviewtoolkit.model.licenses.ResolvedLicenseInfo import org.ossreviewtoolkit.model.utils.toPurl import org.ossreviewtoolkit.model.vulnerabilities.Vulnerability import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.OrtPluginOption import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter import org.ossreviewtoolkit.reporter.ReporterFactory @@ -60,17 +61,51 @@ import org.ossreviewtoolkit.utils.ort.ORT_FULL_NAME import org.ossreviewtoolkit.utils.ort.ORT_NAME import org.ossreviewtoolkit.utils.spdx.SpdxLicense +internal const val DEFAULT_SCHEMA_VERSION_NAME = "1.5" +internal val DEFAULT_SCHEMA_VERSION = Version.entries.single { it.versionString == DEFAULT_SCHEMA_VERSION_NAME } + +data class CycloneDxReporterConfig( + /** + * The CycloneDX schema version to use. Defaults to "1.5". + */ + @OrtPluginOption( + defaultValue = DEFAULT_SCHEMA_VERSION_NAME, // Version.VERSION_15.versionString + aliases = ["schema.version"] + ) + val schemaVersion: String, + + /** + * The license for the data contained in the report. Defaults to "CC0-1.0". + */ + @OrtPluginOption( + defaultValue = "CC0-1.0", + aliases = ["data.license"] + ) + val dataLicense: String, + + /** + * If true (the default), a single SBOM for all projects is created; if set to false, separate SBOMs are created for + * each project. + */ + @OrtPluginOption( + defaultValue = "true", + aliases = ["single.bom"] + ) + val singleBom: Boolean, + + /** + * A comma-separated list of (case-insensitive) output formats to export to. Supported are XML and JSON. + */ + @OrtPluginOption( + defaultValue = "XML", + aliases = ["output.file.formats"] + ) + val outputFileFormats: List +) + /** * A [Reporter] that creates software bills of materials (SBOM) in the [CycloneDX](https://cyclonedx.org) format. For * each [Project] contained in the ORT result a separate SBOM is created. - * - * This reporter supports the following options: - * - *data.license*: The license for the data contained in the report. Defaults to [DEFAULT_DATA_LICENSE]. - * - *schema.version*: The CycloneDX schema version to use. Defaults to [DEFAULT_SCHEMA_VERSION]. - * - *single.bom*: If true (the default), a single SBOM for all projects is created; if set to false, separate SBOMs are - * created for each project. - * - *output.file.formats*: A comma-separated list of (case-insensitive) output formats to export to. Supported are XML - * and JSON. */ @OrtPlugin( id = "CycloneDX", @@ -78,17 +113,12 @@ import org.ossreviewtoolkit.utils.spdx.SpdxLicense description = "Creates software bills of materials (SBOM) in the CycloneDX format.", factory = ReporterFactory::class ) -class CycloneDxReporter(override val descriptor: PluginDescriptor = CycloneDxReporterFactory.descriptor) : Reporter { +class CycloneDxReporter( + override val descriptor: PluginDescriptor = CycloneDxReporterFactory.descriptor, + private val config: CycloneDxReporterConfig +) : Reporter { companion object { - val DEFAULT_SCHEMA_VERSION = Version.VERSION_15 - val DEFAULT_DATA_LICENSE = SpdxLicense.CC0_1_0 - const val REPORT_BASE_FILENAME = "bom.cyclonedx" - - const val OPTION_SCHEMA_VERSION = "schema.version" - const val OPTION_DATA_LICENSE = "data.license" - const val OPTION_SINGLE_BOM = "single.bom" - const val OPTION_OUTPUT_FILE_FORMATS = "output.file.formats" } private fun Bom.addExternalReference(type: ExternalReference.Type, url: String, comment: String? = null) { @@ -152,20 +182,19 @@ class CycloneDxReporter(override val descriptor: PluginDescriptor = CycloneDxRep val packages = input.ortResult.getPackages(omitExcluded = true).sortedBy { it.metadata.id } val schemaVersion = Version.entries.find { - it.versionString == config.options[OPTION_SCHEMA_VERSION] - } ?: DEFAULT_SCHEMA_VERSION - - val dataLicense = config.options[OPTION_DATA_LICENSE] ?: DEFAULT_DATA_LICENSE.id - val createSingleBom = config.options[OPTION_SINGLE_BOM]?.toBooleanStrictOrNull() ?: true - - val outputFileExtensions = config.options[OPTION_OUTPUT_FILE_FORMATS] - ?.split(",") - ?.mapNotNullTo(mutableSetOf()) { - val extension = it.trim().lowercase() - extension.toFormat().alsoIfNull { - logger.warn { "No CycloneDX format supports the '$extension' extension." } - } - } ?: setOf(Format.XML) + it.versionString == this.config.schemaVersion + } ?: throw IllegalArgumentException("Unsupported CycloneDX schema version '${this.config.schemaVersion}'.") + + val outputFileExtensions = this.config.outputFileFormats.mapNotNullTo(mutableSetOf()) { + val extension = it.trim().lowercase() + extension.toFormat().alsoIfNull { + logger.warn { "No CycloneDX format supports the '$extension' extension." } + } + } + + require(outputFileExtensions.isNotEmpty()) { + "No valid CycloneDX output formats specified." + } val metadata = Metadata().apply { timestamp = Date() @@ -179,10 +208,10 @@ class CycloneDxReporter(override val descriptor: PluginDescriptor = CycloneDxRep ) } - licenses = LicenseChoice().apply { expression = Expression(dataLicense) } + licenses = LicenseChoice().apply { expression = Expression(this@CycloneDxReporter.config.dataLicense) } } - if (createSingleBom) { + if (this.config.singleBom) { val bom = Bom().apply { serialNumber = "urn:uuid:${UUID.randomUUID()}" this.metadata = metadata From aa855475973b2da012659c325e7b582fd2eae802 Mon Sep 17 00:00:00 2001 From: Martin Nonnenmacher Date: Sun, 22 Sep 2024 16:04:47 +0200 Subject: [PATCH 7/7] refactor(evaluatedmodel): Use a plugin config class Add a config class for the reporter and use it instead of the options passed to the `generateReport` function. Signed-off-by: Martin Nonnenmacher --- .../kotlin/EvaluatedModelReporterFunTest.kt | 6 +-- .../src/main/kotlin/EvaluatedModelReporter.kt | 41 ++++++++++++------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/plugins/reporters/evaluated-model/src/funTest/kotlin/EvaluatedModelReporterFunTest.kt b/plugins/reporters/evaluated-model/src/funTest/kotlin/EvaluatedModelReporterFunTest.kt index 80361b725cc62..ecfe20f6cae5e 100644 --- a/plugins/reporters/evaluated-model/src/funTest/kotlin/EvaluatedModelReporterFunTest.kt +++ b/plugins/reporters/evaluated-model/src/funTest/kotlin/EvaluatedModelReporterFunTest.kt @@ -26,7 +26,7 @@ import io.kotest.matchers.shouldBe import org.ossreviewtoolkit.model.FileFormat import org.ossreviewtoolkit.model.OrtResult -import org.ossreviewtoolkit.model.config.PluginConfiguration +import org.ossreviewtoolkit.plugins.api.PluginConfig import org.ossreviewtoolkit.reporter.ReporterInput import org.ossreviewtoolkit.utils.common.normalizeLineBreaks import org.ossreviewtoolkit.utils.test.getAssetAsString @@ -54,7 +54,7 @@ class EvaluatedModelReporterFunTest : WordSpec({ val ortResult = readOrtResult("src/funTest/assets/reporter-test-input.yml") val options = mapOf( EvaluatedModelReporter.OPTION_OUTPUT_FILE_FORMATS to FileFormat.YAML.fileExtension, - EvaluatedModelReporter.OPTION_DEDUPLICATE_DEPENDENCY_TREE to "True" + EvaluatedModelReporter.OPTION_DEDUPLICATE_DEPENDENCY_TREE to "true" ) generateReport(ortResult, options) shouldBe expectedResult @@ -70,6 +70,6 @@ private fun TestConfiguration.generateReport(ortResult: OrtResult, options: Map< val outputDir = tempdir() - return EvaluatedModelReporter().generateReport(input, outputDir, PluginConfiguration(options)) + return EvaluatedModelReporterFactory().create(PluginConfig(options)).generateReport(input, outputDir) .single().getOrThrow().readText().normalizeLineBreaks() } diff --git a/plugins/reporters/evaluated-model/src/main/kotlin/EvaluatedModelReporter.kt b/plugins/reporters/evaluated-model/src/main/kotlin/EvaluatedModelReporter.kt index 0b5ac3e4fffb3..4ef54b45844da 100644 --- a/plugins/reporters/evaluated-model/src/main/kotlin/EvaluatedModelReporter.kt +++ b/plugins/reporters/evaluated-model/src/main/kotlin/EvaluatedModelReporter.kt @@ -24,26 +24,43 @@ import java.io.File import org.ossreviewtoolkit.model.FileFormat import org.ossreviewtoolkit.model.config.PluginConfiguration import org.ossreviewtoolkit.plugins.api.OrtPlugin +import org.ossreviewtoolkit.plugins.api.OrtPluginOption import org.ossreviewtoolkit.plugins.api.PluginDescriptor import org.ossreviewtoolkit.reporter.Reporter import org.ossreviewtoolkit.reporter.ReporterFactory import org.ossreviewtoolkit.reporter.ReporterInput +data class EvaluatedModelReporterConfig( + /** + * Controls whether subtrees occurring multiple times in the dependency tree are stripped. + */ + @OrtPluginOption( + defaultValue = "false" + ) + val deduplicateDependencyTree: Boolean, + + /** + * The list of file formats to generate, defaults to JSON. Supported formats are JSON and YAML. + */ + @OrtPluginOption( + defaultValue = "JSON", + aliases = ["output.file.formats"] + ) + val outputFileFormats: List +) + /** * A [Reporter] that generates an [EvaluatedModel]. - * - * This reporter supports the following options: - * - *output.file.formats*: The list of [FileFormat]s to generate, defaults to [FileFormat.JSON]. - * - *deduplicateDependencyTree*: Controls whether subtrees occurring multiple times in the dependency tree are - * stripped. */ @OrtPlugin( displayName = "Evaluated Model Reporter", description = "Generates an evaluated model of the ORT result.", factory = ReporterFactory::class ) -class EvaluatedModelReporter(override val descriptor: PluginDescriptor = EvaluatedModelReporterFactory.descriptor) : - Reporter { +class EvaluatedModelReporter( + override val descriptor: PluginDescriptor = EvaluatedModelReporterFactory.descriptor, + private val config: EvaluatedModelReporterConfig +) : Reporter { companion object { const val OPTION_OUTPUT_FILE_FORMATS = "output.file.formats" @@ -55,15 +72,9 @@ class EvaluatedModelReporter(override val descriptor: PluginDescriptor = Evaluat outputDir: File, config: PluginConfiguration ): List> { - val evaluatedModel = EvaluatedModel.create( - input, - config.options[OPTION_DEDUPLICATE_DEPENDENCY_TREE].toBoolean() - ) + val evaluatedModel = EvaluatedModel.create(input, this.config.deduplicateDependencyTree) - val outputFileFormats = config.options[OPTION_OUTPUT_FILE_FORMATS] - ?.split(',') - ?.mapTo(mutableSetOf()) { FileFormat.forExtension(it) } - ?: setOf(FileFormat.JSON) + val outputFileFormats = this.config.outputFileFormats.map { FileFormat.forExtension(it) } return outputFileFormats.map { fileFormat -> runCatching {