prokod / gradle-crossbuild-scala

Adds cross building functionality to Gradle for Scala based projects
Apache License 2.0
19 stars 4 forks source link

Feature 'dependency validation' #79

Open borissmidt opened 4 years ago

borissmidt commented 4 years ago

Hey i had written some code in my free time to validate if all dependencies are not depending on a scala version without a scala version tag. This might be usefull for companies that just started to cross compile there libs.

def traceDependency(DependencyResult d, List<DependencyResult> dfrom) {
    def deps = d.from.dependents
    if (deps.size() == 0 || d.from.toString().startsWith("project")) {
        dfrom.plus(d.from).reverse().join(" -> ")
    } else {
        deps.collect { traceDependency(it, dfrom.plus(d.from)) }.flatten()
    }

}

task validateScalaDependencies {
    doLast {
        def problems = project.subprojects.collect { subProject ->
            if (subProject.configurations.getNames().contains("runtimeClasspath")) {
                def dependencies = subProject.configurations.runtimeClasspath.incoming.getResolutionResult().getAllDependencies()

                def untaggedScalaDependenciesTraces = dependencies.findAll {
                    String[] groupNameVersion = it.from.toString().split(':')
                    String[] requestedGroupNameVersion = it.requested.toString().split(':')
                    //for dependnecies that depend on scala
                    (requestedGroupNameVersion[0] == "org.scala-lang"
                        // without a scala tag
                        && !(
                        groupNameVersion[1].endsWith("_2.12")
                            || groupNameVersion[1].endsWith("_2.11")
                            || groupNameVersion[1].endsWith("_2.10"))
                        //ignore projects
                        && !it.from.toString().startsWith("project")
                        //ignore scala libs
                        && groupNameVersion[0] != "org.scala-lang"
                    )
                }.collect {
                    "${it.from} traces: \n    -- ${traceDependency(it, [it.requested]).join("\n    -- ")}"
                }

                println("")
                if (untaggedScalaDependenciesTraces.size() != 0) {
                    println("${subProject.name} : CROSS COMPILATION ERRORS :(")
                    println("  * ${untaggedScalaDependenciesTraces.join("*")}")
                    return false
                } else {
                    println("${subProject.name} : IS OK :)")
                    return true
                }

            }
        }.contains(false)

        if(problems) {
            throw new GradleException("INVALID CROSS BUILD FAILED")
        }

    }
}
borissmidt commented 4 years ago

Maybe a small remark, your plugin would be more popular if it included the term scala in the tool. Its a bit weird to include gradle-crossbuild and get scala-crossbuild functionality :rofl:

prokod commented 4 years ago

@borissmidt HI :)

Yes at first I visioned it as a generic cross compilation plugin that has first specialization implemented: scala. But yea I agree that wishes and reality are apart now and we could make it search friendly. something like com.github.prokod.gradle-crossbuild-scala ?

Regarding your code ? I am not sure I have fully grasped your intent here, Can you please elaborate with some example ?

borissmidt commented 4 years ago

Yes the name sounds better or just scala-crossbuild-plugin (it doesn't matter much)

The validation taks does this. if a company make a artifact but was not crossbuilding yet they might have not added the scala version task. com.mycompany:helloworld:1.2.3. this lib would depend on a scala version. Thus any app depending on this lib doesn't know on what scala version it depends.

So this task gives you a trace that you have a 'scala dependency' that has no tags. So including com.mycompany:helloworld:1.2.3 in a cross-build project you an error. But including com.mycompany:helloworld_2.11:1.2.3 will fix the problem.

Otherwise you might upgrade your program or build a cross version lib that still has a dependency on an old scala version and fails at runtime. It also gives you a trace of the violating dependency to make it easier to fix. then looking at the full dependency tree.

You could even have com.mycompany:helloworld:1.2.3 and com.mycompany:helloworld_2.11:1.2.4 on the classpath at the same time. :scream:

some examples of a trace: would also result in an error:

-- project :health -> com.company:all-commons:2.0.0 -> com.company:commons-core:2.0.0 -> org.scala-lang:scala-library:2.11.8
-- project :health -> com.company:all-commons:2.0.0_2.11 -> com.company:commons-core:2.0.0 -> org.scala-lang:scala-library:2.11.8

I need to improve so it also fails in case of

-- project :health -> com.company:all-commons:2.0.0 -> com.company:commons-core_2.11:2.0.0 -> org.scala-lang:scala-library:2.11.8
prokod commented 4 years ago

@borissmidt nice one! :)

I am separating this from the plugin name update. This is lower priority currently IMO

borissmidt commented 4 years ago

Yea true, i still use this in a script like fashion. or copy paste it in the project i want to validate.