Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create an interface for Kover tasks #338

Closed
mgroth0 opened this issue Apr 15, 2023 · 37 comments · Fixed by #564
Closed

Create an interface for Kover tasks #338

mgroth0 opened this issue Apr 15, 2023 · 37 comments · Fixed by #564
Assignees
Labels
Design needed Feature Feature request issue type S: ready for release Status: merged in the main branch

Comments

@mgroth0
Copy link

mgroth0 commented Apr 15, 2023

I am new to this plugin and I wanted to make certain tasks depend on Kover tasks. However, KoverHtmlTask and such are internal. This would be fine as long a superclass or interface were made public. But even AbstractKoverReportTask is internal. There is no straightforward way to do task.dependsOn(tasks.withType<KoverHtmlTask>()).

There are a couple of simple solutions. One is that you can make these tasks public and make the constructor internal. Another is that you can create a public interface that they extend from.

@mgroth0 mgroth0 added Feature Feature request issue type S: untriaged Status: issue reported but unprocessed labels Apr 15, 2023
@shanshin
Copy link
Collaborator

Hi,
it is not worth using task types for dependencies, because for example, in Android, several tasks with the same type can be created (in the future this may be added to regular JVM projects).

You may add a dependency to a Kover task by its name, for example
tasks.myTask { dependsOn(tasks.named("koverHtmlReport")) }

Names koverXmlReport, koverHtmlReport, koverVerify are stable for Kotlin/JVM or Kotlin/MPP, сonstants for the names of these tasks will be introduced in 0.7.0.

@shanshin shanshin added S: waiting for clarification Status: additional information required to proceed and removed S: untriaged Status: issue reported but unprocessed labels Apr 17, 2023
@mgroth0
Copy link
Author

mgroth0 commented Apr 18, 2023

Hey @shanshin. Thanks for the info. I disagree with your opinion on this.

Making task types public API is the norm. I've never encountered a gradle plugin before this that doesn't have public task classes, regardless how many tasks are created per project.

Depending on tasks using withType is more typesafe and preferred.

Configuring tasks using tasks.withType<X>.configureEach is also idiomatic.

It seems to me making task types public API has great benefit, no cost, and aligns better with idiomatic gradle usage.

@shanshin
Copy link
Collaborator

shanshin commented Apr 18, 2023

The described problem is solved by searching for a task by name (tasks.named("someName")), while it is also safe because if there is no task with that name, an error will be thrown.

The use of tasks.withType<X>.configureEach in the case of Kover tasks in most cases is not assumed, because all the configuration of the plug-in takes place in a single place - extensions of the project.

For rare cases, a search by name can be used, which is generally preferable, because in Android projects most often work will be carried out with a specific build variant and the corresponding task.

I've never encountered a gradle plugin before this that doesn't have public task classes, regardless how many tasks are created per project.

In fact, there are quite a lot of such tasks, for example, the most frequently used such task is check from the base plugin.

@shanshin
Copy link
Collaborator

In the future, some public classes may be added, however, it is necessary to understand what problem they will solve.

At the moment, the only known problem is defining dependencies between tasks, but it is easily solved with the names. For kts build scripts, we will add type-safe accessors for default reports so that users can write tasks.koverHtmlReport.

@mgroth0
Copy link
Author

mgroth0 commented Apr 19, 2023

tasks.named("someName") is less safe because it only throws an error at runtime, not compilation time. Compilation-time type safety is cleaner and more robust. Type safety is also reinenforced by IDE syntax highlighting, but finding by names is not.

If I want to reference "koverHtmlReport" in multiple places, I will likely want to define const val KOVER_HTML_REPORT_TASK_NAME = "koverHtmlReport" somewhere. But that's just extra clutter in my code where tasks.withType<KoverHTMLReport> would be more elegant and simple.

Checking if a task is a kover task based soley on the name will not work in when statements:

when (task) {
	is KotlinCompile, is Jar -> {
         ...
    }
    is JavaExec -> {
         ...
    }
    .name == KOVER_HTML_REPORT_TASK_NAME -> { // compilation error
         ...
    }
}

(at least for now, until one day KT-21661 is hopefully implemented )

Finding objects by type rather than name is also useful because if the hierarchical structure. If all kover tasks can implement an empty interface interface KoverTask: Task, then its easy to gather all kover tasks with tasks.withType<KoverTask>. KoverHtmlTask and KoverXmlTask can both implement a KoverReport interface (that is not implemented by KoverVerifyTask) if a user only wants to gather the tasks that's main purpose is to generate a report.

The kotlin-gradle-dsl is not available in all places. For example, it is not included by default in the gradle API when writing gradle plugins (and it can be challenging to include the kotlin-gradle-dsl as a dependency for gradle plugins.) So type-safe accessors won't solve this issue as broadly as public classes would.

Allowing users to use tasks.withType does not prevent the from using tasks.named. tasks.named, kotlin-gradle-dsl accessors, or other techniques can still be used to find tasks for a particular android build varient or similar.

check is a lifecycle task. AFAIK it does not do anything, it only depends on other tasks. I'd be curious if there are any tasks that actually do actions that don't have some public type it can be referenced, or at least narrowed down by.

I don't think new classes or code needs to be written (though creating a simple hierarchy with interfaces would be excellent). What issues would be introduced by simply removing the internal keyword for the existing task classes? The internal keyword could be added to the constructor and/or to any properties to protect the integrity of the classes if needed. As far as I can see, this change would have no effect on the rest of the plugin

Given how small of a change this is that there are identifiable benefits, I'm curious why there is such a need for justification. What costs are there to simply removing the internal keyword?

@qwwdfsad
Copy link
Member

qwwdfsad commented Apr 19, 2023

Given how small of a change this is that there are identifiable benefits, I'm curious why there is such a need for justification. What costs are there to simply removing the internal keyword?

It immediately becomes a part of public API which imposes quite a lot of restrictions:

  • API members should be properly documented
  • The API itself should be named properly, be consistent, and hack-free. E.g. now we can get away with val someTrickFlagAsWorkaround, but with public API we can't
  • It's part of API, thus extra care is required regarding API evolution.

Note that all these problems are solvable and are not arguments against your proposal, but rather an answer to "why it's not public by default" question.

To the original report -- thanks for the effort and for going the extra mile in explaining to us why this is a problem for Kover's users!
We'll discuss it internally and see what we can do to both solve your issue and keep our public API minimal

@shanshin
Copy link
Collaborator

tasks.named("someName") is less safe because it only throws an error at runtime, not compilation time.

This problem is solved by using constants with the task names: e.g. kotlinx.kover.api.KoverNames.HTML_REPORT_TASK_NAME for 0.6.1

What issues would be introduced by simply removing the internal keyword for the existing task classes?

Unfortunately in this case AbstractKoverReportTask becomes public, however these are implementation details. This class does not fit the hierarchy of reports described above (verify is also inherited from it), however, by making it public, we will need to maintain exactly such a hierarchy in the future for compatibility, which makes refactoring difficult.

Finding objects by type rather than name is also useful because if the hierarchical structure.

At this point, there may be discrepancies in the understanding of the correct hierarchy. After the implementation of #229, new tasks for printing "reports to logs" will appear, and verification will start working in a similar way after #339.

Allowing users to use tasks.withType does not prevent the from using tasks.named. tasks.named, kotlin-gradle-dsl accessors, or other techniques can still be used to find tasks for a particular android build varient or similar.

in this case, Android users can find an example of a solution with withType on the Internet and use configureEach to configure several tasks at once without realizing it.

If the concepts of default reports (always existing tasks with names koverHtmlReport, koverXmlReport etc) remain in the stable version , then it will be possible to add the interfaces like KoverDefaultHtmlReport. However, this requires additional design in the future.

@shanshin shanshin added Design needed S: postponed Status: work on the issue is not in the short term plans and removed S: waiting for clarification Status: additional information required to proceed labels Apr 19, 2023
@mgroth0
Copy link
Author

mgroth0 commented Apr 19, 2023

I appreciate the responses. I've never authored a public library before so it is helpful for me to learn about the types of design challenges you confront when considering what to make public API. Thanks both of you for for engaging with me and educating me on this!

One thought I had was Opt-in requirements. You could create:

@RequiresOptIn
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class IncubatingKoverAPI

And annotate the class with this. I think this could help in a few ways:

  • Indicates to users that incubating API may change. Library authors reserve every right to change or remove incubating features.
  • Justifies incubating API possibly not yet being documented
  • Users understand incubating API may not be completely robust or bug free and are required to consent to this

Opt-in requirements seems like a nice catch all solution that could address a number of the concerns you both raised above.

in this case, Android users can find an example of a solution with withType on the Internet and use configureEach to configure several tasks at once without realizing it.

I think this might be ok if it is consistent with other tasks and the norm in the Android ecosystem. I suspect a significant portion at least (if not the majority) of Android Gradle Plugin tasks are both public API (can work in withType) and also constructed with multiple instances per project (one per build varient). If this is the norm in the AGP, or even if its common enough that Android developers generally should know to look out for this, then perhaps its not your responsibility to safeguard android devs from making that mistake. I suspect even outside of the AGP, this may be common. For example, the class JavaCompile must have a seperate instance for each source set (like "main" and "test") within a project. I think syntax tasks.withType is explicit since "tasks" is plural that you may very well be configuring multiple tasks.

In the end I'll respect the design choices you make. Thank you for clarifying your reasoning.

@aSemy
Copy link
Contributor

aSemy commented Jun 1, 2023

I agree that making the tasks internal is highly unusual, and in the case of Groovy build scripts has no effect, since Groovy can ignore visibility modifiers.

// build.gradle

import kotlinx.kover.gradle.plugin.tasks.reports.KoverHtmlTask;

plugins {
  id "org.jetbrains.kotlin.jvm" version "1.8.21"
  id "org.jetbrains.kotlinx.kover" version "0.7.1"
}

afterEvaluate { // required because of https://github.com/Kotlin/kotlinx-kover/issues/394
  tasks.named("koverHtmlReport", KoverHtmlTask).map {
    it.reportDir
  }
}

Another usecase for exposing the task types is to be able to access the input/output properties in a typesafe way. For example, I might want to use the files that a Kover task generates as the input for another task https://docs.gradle.org/8.1.1/userguide/incremental_build.html#sec:task_input_output_side_effects. Because the classes are private then I can't do this.

@shanshin
Copy link
Collaborator

shanshin commented Jun 1, 2023

For example, I might want to use the files that a Kover task generates as the input for another task https://docs.gradle.org/8.1.1/userguide/incremental_build.html#sec:task_input_output_side_effects. Because the classes are private then I can't do this.

You may explicitly specify the report output directory and add a dependsOn("someKoverTask") - this will be enough for Gradle.

Until the DSL stabilizes, this will be more efficient than getting migration errors when changing the implementation of Kover tasks classes.

@aSemy
Copy link
Contributor

aSemy commented Jun 1, 2023

You're right, I shouldn't have said "I can't do this".

However, because the classes are private it makes it more difficult to discover this behaviour without digging through the source code or being very savvy with Gradle. It also makes it more difficult to perform actions on a tasks output, e.g. filtering the output

val someSpecificKoverFiles = tasks.someKoverTask.map { 
  it.reportDir.asFileTree.matching { include(...) }.files 
}

Also, as you have indicated you might refactor the task - so are you sure it's safe to use dependsOn("someKoverTask")? The task name or output could change without notice.

@shanshin
Copy link
Collaborator

shanshin commented Jun 1, 2023

and in the case of Groovy build scripts has no effect, since Groovy can ignore visibility modifiers.

Java reflection is also available, but the question here is not to exclude the technical possibility of accessing the class, but to ensure that users do not randomly start using implementation details for which we do not guarantee backward compatibility.

It also makes it more difficult to perform actions on a tasks output, e.g. filtering the output

This is a good use case, the outputs of the task can be listed in the public interface, but the inputs should not be specified when the configuration is carried out through the project extension.

Also, as you have indicated you might refactor the task - so are you sure it's safe to use dependsOn("someKoverTask")? The task name or output could change without notice.

This approach is still under development, but it seems to me that constants should be declared for Kover default tasks names, and for Android tasks it is necessary to create a public function like koverHtmlreportTaskName(buildVariantName)

@aSemy
Copy link
Contributor

aSemy commented Jun 1, 2023

This is a good use case, the outputs of the task can be listed in the public interface, but the inputs should not be specified when the configuration is carried out through the project extension.

I strongly disagree that Kover has to be so defensive with regards to protecting task properties. Manually modifying the inputs is sometimes necessary. To give one example from a different plugin: to help with integration tests.

These properties are already exposed to the public in Groovy scripts, so keeping the tasks as private just makes gradle.kts scripts inferior.

I disagree that Java reflection to access private code is comparable to Groovy - the former is complicated and the latter is easily accessible, and is even suggested via auto-completion.

The approach at the moment is encouraging using random implementation details. Because of using stringly-typed task names, inferring task outputs, and afterEvaluate {} right now I'm writing non-type-safe scripts that will degrade over time, and are missing out on unbelievably helpful functionality like @Deprecate, ReplaceWith, and @ReplacedBy.

This approach is still under development, but it seems to me that constants should be declared for Kover default tasks names, and for Android tasks it is necessary to create a public function like koverHtmlreportTaskName(buildVariantName)

I understand that dealing with Android is complicated, but thought of having to deal with a plugin that is breaking the norms that all other plugins follow is exhausting. Using Gradle is already difficult as it is. I shouldn't have to leave my IDE in order to figure out how Kover wants me to use Gradle.

@cldfzn
Copy link

cldfzn commented Dec 7, 2023

In addition, when writing custom plugins and reacting to other plugins being enabled. Having to guess the task name is not an effective solution. Being able to say "if this task is present in the task graph, finalize it with this task", so that a custom plugin can post process the report generated. With the current behavior I have to duplicate the logic this plugin uses to generate task names, so I can iterate through them to create the necessary dependencies and finalizers.

shanshin added a commit that referenced this issue Feb 19, 2024
- blocks kover and koverReports are merged
- added possibility of lazy configuration of Kover extensions
- removed the concept of default reports
- added the ability to create custom report variants
- Created interfaces for Kover tasks

Resolves #461
Resolves #410
Resolves #462
Resolves #463
Resolves #338
@shanshin shanshin reopened this Feb 20, 2024
@shanshin shanshin added S: ready for release Status: merged in the main branch and removed S: postponed Status: work on the issue is not in the short term plans labels Feb 20, 2024
@shanshin shanshin reopened this Mar 13, 2024
@shanshin
Copy link
Collaborator

shanshin commented Mar 13, 2024

Thanks!
Make sense to extend org.gradle.api.Task in KoverReport.

shanshin added a commit that referenced this issue Mar 13, 2024
To use pattern
```
tasks.withType<KoverReport>().configureEach {
    dependsOn("otherTask")
}
```
 it is necessary that all interfaces for Kover tasks inherit the `org.gradle.api.Task` interface

Resolves #338
shanshin added a commit that referenced this issue Mar 13, 2024
To use pattern
```
tasks.withType<KoverReport>().configureEach {
    dependsOn("otherTask")
}
```
 it is necessary that all interfaces for Kover tasks inherit the `org.gradle.api.Task` interface

Resolves #338
shanshin added a commit that referenced this issue Mar 13, 2024
To use pattern
```
tasks.withType<KoverReport>().configureEach {
    dependsOn("otherTask")
}
```
 it is necessary that all interfaces for Kover tasks inherit the `org.gradle.api.Task` interface

Resolves #338
PR #564
@shanshin shanshin reopened this Mar 13, 2024
@mgroth0
Copy link
Author

mgroth0 commented Mar 15, 2024

On a related note, how about exposing TaskProvider instances inside the configuration DSL?

In the Kotlin Multiplatform Gradle Plugin extension, there are public TaskProvider values. For an example see org.jetbrains.kotlin.gradle.plugin.mpp.NativeBinary.linkTask:

kotlin {
    macosArm64 {
        binaries.configureEach {
            // linkTaskProvider is a `TaskProvider<out KotlinNativeLink>` provided by the kotlin plugin
            myCustomBuildTask.dependsOn(linkTaskProvider)
            linkTaskProvider.configure {
                dependsOn(myCustomCheckTask)
            }
        }
    }
}

Having public values exposing TaskProvider instances for kover tasks might become increasingly useful as Kover begins to support more kotlin platforms.

If you want I can create a separate issue for this, but it falls within the same umbrella of having public APIs for getting references to Kover tasks.

@shanshin
Copy link
Collaborator

shanshin commented Mar 15, 2024

Could you clarify use case where TaskProvider needed, and the provider of which Kover tasks?

@mgroth0
Copy link
Author

mgroth0 commented Mar 15, 2024

Sure.

Use case: I might make my own custom aggregation task per platform for different forms of checks. For example:

abstract class MyCustomAndroidChecks: DefaultTask() {

}


val myCustomAndroidChecks by tasks.registering<MyCustomAndroidChecks>()

The purpose of MyCustomAndroidChecks might be to aggregate checks from different plugins. For example, I might aggregate kover with detekt or ktlint.

So, I might want to make myCustomAndroidChecks depend on specific tasks, such as koverVerifyDebug or something. I cant just make myCustomAndroidChecks depend on all instances of KoverVerifyReport because I do not want it to depend on the jvm variant.

It could also be the other way around. Like if I wanted the android variant koverVerifyDebug to depend on some specific android task that generates android code, for example.

So exposing TaskProviders in the way that kotlin multiplatform plugin does is really nice because it is a typesafe way to do this type of configuration without having to rely on string names for tasks, which can have all sorts of issues.

Which tasks:

Currently I use the koverVerify, koverHtmlReport, and koverPrintCoverage tasks, for both android and jvm-desktop varients.

@shanshin
Copy link
Collaborator

shanshin commented Mar 15, 2024

For Kotlin JVM project you already can use tasks.koverVerify, tasks.koverHtmlReport etc.

For Android projects we can add extensions like fun TaskContainer.koverVerify(variantName: String): TaskProvider<> to use it this way tasks.koverVerify("debug") (applicable for kts build scripts).
*However, I am not sure that this will be easy to implement, because the list of variants is determined at the evaluation stage and these tasks are created at the end of the stage, respectively, we can't use the function named("taskName")

@mgroth0
Copy link
Author

mgroth0 commented Mar 15, 2024

Thanks, I did not know about the existence of things like tasks.koverVerify. However, for me this is only working in a buildscript. I compile the kover gradle plugin as a normal dependency for my gradle plugin and for some reason the kotlin dsl accessor is not available. I do not fully understand how these accessors work, and why I cannot resolve them when using kover as a normal dependency. My plugin also has a dependency on the kotlin dsl so I can do things like tasks.registering but I still cannot resolve tasks.koverVerify for unknown reasons.

For the problem about android variants, what about exposing a DomainObjectSet or a similar live gradle collection for each variant? Each variant would have a reference to a TaskProvider for each task on the variant, and can also have a simple val variantName . This would work in the same way as org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget#binaries (and many other similar features of the kotlin gradle plugin)?

@shanshin
Copy link
Collaborator

shanshin commented Mar 15, 2024

This would work in the same way as org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget#binaries (and many other similar features of the kotlin gradle plugin)?

Comparing with the KMP plugin, you miss a small detail that in KMP there is both the creation of a target and access to its provider in one place.

E.g. for example

kotlin {
    macosArm64 {
        binaries.configureEach {
            someUsage(linkTaskProvider)
        }
    }
}

you take the task in the extension in which you actually create it.

When it comes to Kover + AGP, the build variants are specified in AGP, in Kover we cannot know in advance which variants exist and which do not (in some cases they may be created at the afterEvaluate stage).

Accordingly, even if, for example, we provide such a DSL

kover {
    reports {
        variant("debug") {
            htmlReportProvider: TaskProvider<KoverHtmlReport>
        }
    }
}

then it is necessary to write a custom implementation of TaskProvider, because the task will be created much later than the access to the provider will take place.


As a compromise option, I can suggest adding the variantName property to the KoverReport type, so that you can write such configurations

val koverVerifyTasks = tasks.matching {
    it is KoverVerifyReport && it.variantName in setOf("debug", "release")
}
or 
val koverVerifyTasks = tasks.withType(KoverVerifyReport::class.java).matching {
    it.variantName in setOf("debug", "release")
}

myTask.configure { 
    dependsOn(koverVerifyTasks)
}

@mgroth0
Copy link
Author

mgroth0 commented Mar 15, 2024

Do you know how I might be able to use tasks.koverVerify inside of my gradle plugin? I was hoping you might be able to point me in the right direction, just because I am curious about how exactly the kotlin dsl code is generated. But no worries if you aren't sure, as this isn't going to be a full solution for this issue anyway.


I am not quite understanding the issue you explain above. Let me propose this:

kover {
    variants.configureEach {
        verifyTaskProvider.configure {
        }
    }
}

Here, varients is live collection populated lazily. It shouldn't matter that it is created using information from the AGP configuration, or even if it is populated inside of a afterEvaluate call. Gradle collections are designed to be populated and configured lazily.

Is this not possible? Perhaps I am still missing something.

Ideally variantName would be a property of the Variant configuration object instead of the task. It would be located next to the task provider, not inside of the task.

This seems better than relying on avariantName inside the Task because then do not ever have to realize tasks.tasks.matching is best avoided.

@mgroth0
Copy link
Author

mgroth0 commented Mar 15, 2024

But I do see your point about Android variants not being hardcoded. This was my mistake. I see that it would be impossible to hardcode accessors for specific android variants.

@shanshin
Copy link
Collaborator

shanshin commented Mar 18, 2024

Do you know how I might be able to use tasks.koverVerify inside of my gradle plugin?

Unfortunately, your plugin will not be able to use this property, because this DSL is available only inside kts files.

Is this not possible? Perhaps I am still missing something.

Technically, this is possible, but there are several difficult points.
Firstly, there is currently no publicly available Variant domain object in Kover.

If we implement a collection of such objects, the case you have given is not very well suited for such an usage:
collection.configureEach { is intended primarily for configuring the element of the collection itself, its settings, but not getting the value of any properties from this element.
In addition, it is not always obvious that the action specified in configureEach is performed "sometime later" (or never at all) - this can lead to implicit errors if code is written there that must be executed at the evaluate stage.

And it doesn't seem to me that such code

kover {
    reportVariants.configureEach {
        if (variantName in setOf("release", "debug")) {
            myTask.configure { 
                dependsOn(htmlReportProvider)
            }
        }
    }
}

would be clearer than

val koverHtmlTasks = tasks.withType(KoverHtmlReport::class.java).matching {
    it.variantName in setOf("debug", "release")
}

myTask.configure { 
    dependsOn(koverHtmlTasks)
}

@mgroth0
Copy link
Author

mgroth0 commented Mar 18, 2024

You make a great point that configureEach would not be best if there were such a domain collection of variants. It might be better to use all, or an "implicit" all, which resolves these domain objects eagerly:

kover {
    reportVariants.all {
        if (variantName in setOf("release", "debug")) {
            myTask.configure { 
                dependsOn(htmlReportProvider)
            }
        }
    }
}
kover {
    reportVariants {
        if (variantName in setOf("release", "debug")) {
            myTask.configure { 
                dependsOn(htmlReportProvider)
            }
        }
    }
}

That is because like you say, it is unclear with configureEach if the configuration would be triggered for arbitrary domain objects.

This would match the kotlin multiplatform plugin DSL. All of these are technically possible:

binaries.configureEach { }
binaries.all { }
binaries { }

Though normally, we would only use binaries {} or binaries.all {}. I have not seen binaries.configureEach { } used.

As for matching, I think this is actually a performance issue not code cleanliness issue. I agree that this code looks clean:

val koverHtmlTasks = tasks.withType(KoverHtmlReport::class.java).matching {
    it.variantName in setOf("debug", "release")
}

myTask.configure { 
    dependsOn(koverHtmlTasks)
}

Though matching has some issues related to realizing instead of lazily constructing and configuring them. The gradle docs currently say matching is "OK, with issues", but this might not be entirely accurate as there are even more undocumented issues with matching. There is an open issue about it: gradle/gradle#26047

I believe that if there is any possible alternative to matching, that using the alternative would be correct in in order to have better task configuration avoidance?

@shanshin
Copy link
Collaborator

Yes, matching has some problems, but the effect on performance will be negligible because in the example I mentioned, withType is used as in the Gradle recommendation.
Since in your case you wanted to take almost all variants except JVM, then a minimum of unnecessary tasks will be created (for example, only for jvm and total)

@mgroth0
Copy link
Author

mgroth0 commented Mar 18, 2024

Agreed. Using withType greatly mitigates the performance issue. I'm just being a perfectionist 😮‍💨

@shanshin
Copy link
Collaborator

Well, is DSL like (in 0.8.0-Beta version)

kover {
    reports {
        for (variantName in setOf("release", "debug")) {
            variant(variantName) { 
                myTask.dependsOn(htmlReportProvider)
            }    
        }
    }      
}

suitable for you?

@mgroth0
Copy link
Author

mgroth0 commented Mar 18, 2024

I will need to get the variant for each Kotlin Multiplatform target. I am hopeful this library will eventually support all targets (I know it might still be a while). But if it ever does, I imagine there will be many variants. Referencing them by string will not seem very stable.

@mgroth0
Copy link
Author

mgroth0 commented Mar 18, 2024

Some ideas:

  • a Variant domain collection, as we have discussed above
  • variant(target) which takes a org.jetbrains.kotlin.gradle.plugin.KotlinTarget as a parameter

Is my understanding correct that there would be a variant per target, not per source set? As in, there would never be a "koverCommon" variant, correct?

@shanshin
Copy link
Collaborator

It's about current needs.
Supporting all multiplatform targets is a huge task that will take a very long time to develop. It is not known what DSL will be then, and whether it will be a separate Gradle plugin.
Therefore, it is worth not to go so far.

@mgroth0
Copy link
Author

mgroth0 commented Mar 18, 2024

Absolutely understandable. I think if kover task interfaces now extend from Task, this issue can be closed. We can tackle other things in new issues at a later date. Thank you for taking all of my thoughts into consideration.

@shanshin
Copy link
Collaborator

shanshin commented Mar 18, 2024

Thanks!

I will still add variantName property to the KoverReport interface.
This may not be an ideal option, but right now there is no other way to filter Kover tasks other than analyzing the task name and this is being implemented quickly.

@shanshin
Copy link
Collaborator

Fixed in 0.8.0-Beta2

@shanshin
Copy link
Collaborator

Relates #587

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design needed Feature Feature request issue type S: ready for release Status: merged in the main branch
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants