aaschmid / gradle-cpd-plugin

Gradle plugin to find duplicate code using PMDs copy/paste detection (= CPD).
Apache License 2.0
95 stars 12 forks source link

Plugin doesn't support projects that use both Kotlin and Java #39

Closed JLLeitschuh closed 3 years ago

JLLeitschuh commented 5 years ago

I have quite a few projects that are slowly being migrated from Java to Kotlin. There doesn't seem to be a good way to support linting both Kotlin and Java since you can only specify one language.

aaschmid commented 5 years ago

This is correct. Reason is that on Cpd task can only run one langugage. However, you can create multiple Cpd tasks with differently configured language and source attributes. An example will follow...

JLLeitschuh commented 5 years ago

This is the snippet that I've been using utilizing the Gradle Kotlin DSL:

tasks.register<Cpd>("cpdKotlin") {
    language = "kotlin"
    exclude { it.file.extension.contains("java") }

    allprojects.forEach { project ->
        project.plugins.withType<JavaBasePlugin> {
            project.convention.getPlugin<JavaPluginConvention>().sourceSets.configureEach {
                allJava.srcDirTrees.forEach { this@register.source(it) }
            }
        }
    }
}

It would be nice if the plugin could detect mixed kotlin/java projects automatically and auto-generate the correct tasks for you.

aaschmid commented 5 years ago

That looks good from first view. Does it work for you? And let me think about your suggestion...

JLLeitschuh commented 5 years ago

This does work well for me. I've embedded it in my own build.

And let me think about your suggestion...

🤔 Would be awesome!

aaschmid commented 4 years ago

Hi @JLLeitschuh, thought about it but unfortunately without any clear result. Only thing I can currently think of is to create a mapping and create cpdCheck${LANG} tasks for every recognized and available LANG applied by a known plugin...

I have no clue if a lot people use this plugin for multiple languages, however some kind of auto detection could be a nice improvement...

giri-sh commented 4 years ago

Would it possible to provide an example using Gradle groovy DSL?

aaschmid commented 4 years ago

@grushler the one to one translation of example above would be

import de.aaschmid.gradle.plugins.cpd.Cpd
task cpdKotlin(type: Cpd) {
    language = "kotlin"
    exclude { it.file.extension.contains("java") }

    allprojects.forEach { project ->
        project.plugins.withType(JavaBasePlugin) { plugin ->
            project.sourceSets.forEach { ss -> ss.allJava.forEach { rootProject.cpdKotlin.source(it) } }
        }
    }
}

Disclaimer: not tested with a Kotlin project actually but don't hesitate to complain if it does not ;-)

Note: The rootProject need to be adjusted if your copy-paste-detection should be applied to a different project.

giri-sh commented 4 years ago

Thank you @aaschmid . I tested the provided code, but get below error (added with stacktrace) -

> Task :cpdKotlin FAILED
1 actionable task: 1 executed

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':cpdKotlin'.
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:38)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:73)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:49)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:49)
    at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:43)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:355)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:343)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:336)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:322)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:134)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:129)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:202)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:193)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:129)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
Caused by: groovy.lang.MissingPropertyException: No such property: extension for class: java.io.File
    at build_2p5hiyvlrsgqi2g70hxystd36$_run_closure17$_closure32.doCall(/Users/dev/workspace/test-api/build.gradle:320)
    at org.gradle.api.specs.internal.ClosureSpec.isSatisfiedBy(ClosureSpec.java:32)
    at org.gradle.api.specs.OrSpec.isSatisfiedBy(OrSpec.java:47)
    at org.gradle.api.specs.NotSpec.isSatisfiedBy(NotSpec.java:35)
    at org.gradle.api.internal.file.collections.AbstractSingletonFileTree.visit(AbstractSingletonFileTree.java:35)
    at org.gradle.api.internal.file.collections.FileTreeAdapter.visit(FileTreeAdapter.java:118)
    at org.gradle.api.internal.file.AbstractFileTree.isEmpty(AbstractFileTree.java:68)
    at org.gradle.api.internal.file.CompositeFileCollection.isEmpty(CompositeFileCollection.java:99)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:67)
    at org.gradle.api.internal.tasks.execution.ResolveBeforeExecutionOutputsTaskExecuter.execute(ResolveBeforeExecutionOutputsTaskExecuter.java:67)
    at org.gradle.api.internal.tasks.execution.StartSnapshotTaskInputsBuildOperationTaskExecuter.execute(StartSnapshotTaskInputsBuildOperationTaskExecuter.java:62)
    at org.gradle.api.internal.tasks.execution.ResolveAfterPreviousExecutionStateTaskExecuter.execute(ResolveAfterPreviousExecutionStateTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:94)
    at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:95)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
    ... 24 more

So I changed the exclude code and tried again - exclude { file("**/*.java") } But I get the output as > Task :cpdKotlin NO-SOURCE. Any changes that you would suggest?

Btw, my folder structure something like this - src -- main ---- java (contains all java files) ---- kotlin (contains all kotlin files)

aaschmid commented 4 years ago

@grushler Hm ... actually I need a real reproducer in order to investigate. For now maybe you could add some print of ss, ss.allJava and it as well as cpdKotlin.source and check the folders for their path and content. Note: For printing you may also need to use ...files in order to displaying a readable version...

Another working way should be:

task cpdKotlin(type: Cpd) {
    language = "kotlin"
    source = allprojects*.file("src/main/kotlin")
}

It's a bit ugly because it hard-codes src/main/kotlin but it is good for now IMO.

giri-sh commented 4 years ago

Thanks @aaschmid. That helps though there is a hardcoded path but seems to be ok to use as we usually never change the path. I will try out printing the source and verify if the location is being picked up as needed.

aaschmid commented 3 years ago

@JLLeitschuh any further thoughts on this?

JLLeitschuh commented 3 years ago

@aaschmid sorry, this is no longer a project I'm actively using. I've left the company I was working for that was using it.

aaschmid commented 3 years ago

Ok, so I will close this but anyone can reopen as soon as this is an issue anymore...