Skip to content

Improved gradle buildscript #219

@solonovamax

Description

@solonovamax

Hi,

I took the buildscript listed in the README and improved it to be substantially less awful.
Not only was the buildscript that was already there broken due to refactors in gradle (using gradle internals), but the idea of just using

Instead of doing what is listed in the README, you can add a component metadata rule:

@CacheableRule
abstract class Brotli4JRule : ComponentMetadataRule {
    data class NativeVariant(val os: String, val arch: String, val classifier: String)

    private val nativeVariants = listOf(
        NativeVariant(OperatingSystemFamily.WINDOWS, Architectures.AARCH64.canonicalName, "windows-aarch64"),
        NativeVariant(OperatingSystemFamily.WINDOWS, Architectures.X86_64.canonicalName, "windows-x86_64"),
        NativeVariant(OperatingSystemFamily.MACOS, Architectures.X86_64.canonicalName, "osx-x86_64"),
        NativeVariant(OperatingSystemFamily.MACOS, Architectures.AARCH64.canonicalName, "osx-aarch64"),
        NativeVariant(OperatingSystemFamily.LINUX, Architectures.X86_64.canonicalName, "linux-x86_64"),
        NativeVariant(OperatingSystemFamily.LINUX, Architectures.AARCH64.canonicalName, "linux-aarch64"),
        NativeVariant(OperatingSystemFamily.LINUX, Architectures.ARM_V7.canonicalName, "linux-armv7"),
        NativeVariant(OperatingSystemFamily.LINUX, "s390x", "linux-s390x"),
        NativeVariant(OperatingSystemFamily.LINUX, "riscv64", "linux-riscv64"),
        NativeVariant(OperatingSystemFamily.LINUX, "ppc64le", "linux-ppc64le"),
    )

    @get:Inject
    abstract val objects: ObjectFactory

    override fun execute(context: ComponentMetadataContext) {
        listOf("compile", "runtime").forEach { base -> addVariant(context, base) }
    }

    private fun addVariant(context: ComponentMetadataContext, base: String) {
        val version = context.details.id.version

        nativeVariants.forEach { variant ->
            context.details.addVariant("${variant.classifier}-${base}", base) {
                attributes {
                    attributes.attribute(OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE, objects.named(variant.os))
                    attributes.attribute(MachineArchitecture.ARCHITECTURE_ATTRIBUTE, objects.named(variant.arch))
                }

                withFiles {
                    addFile("native-${variant.classifier}-${version}.jar", "../../native-${variant.classifier}/${version}/native-${variant.classifier}-${version}.jar")
                }
            }
        }

        context.details.addVariant("all-${base}", base) {
            attributes {
                attributes.attribute(OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE, objects.named("all"))
                attributes.attribute(MachineArchitecture.ARCHITECTURE_ATTRIBUTE, objects.named("all"))
            }

            withFiles {
                nativeVariants.forEach { variant ->
                    addFile("native-${variant.classifier}-${version}.jar", "../../native-${variant.classifier}/${version}/native-${variant.classifier}-${version}.jar")
                }
            }
        }

        context.details.withVariant(base) {
            this.withFiles {
                this.removeAllFiles()
            }
        }
    }
}

you can use this rule in one of two ways:

  1. in the settings script:
    dependencyResolutionManagement {
        components {
            withModule<Brotli4JRule>("com.aayushatharva.brotli4j:natives")
        }
    }
    
    gradle.beforeProject {
        val host = DefaultTargetMachineFactory(objects).host()
    
        configurations.configureEach {
            attributes {
                attribute(OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE, host.operatingSystemFamily)
                attribute(MachineArchitecture.ARCHITECTURE_ATTRIBUTE, host.architecture)
            }
        }
    }
  2. in the project buildscript:
    configurations.configureEach {
        attributes {
            val host = DefaultTargetMachineFactory(objects).host()
            attribute(OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE, host.operatingSystemFamily)
            attribute(MachineArchitecture.ARCHITECTURE_ATTRIBUTE, host.architecture)
        }
    }
    
    dependencies {
        components {
            withModule<Brotli4JRule>("com.aayushatharva.brotli4j:natives")
        }
    
        // ...
    }

then, you just add a dependency on com.aayushatharva.brotli4j:brotli4j and com.aayushatharva.brotli4j:natives. it hijacks the natives dependency to resolve the appropriate variant.

this would be significantly easier if all of those additional variants were all just different classifiers of the original jar, eg. in maven you might have natives-1.18.0.jar, natives-1.18.0-windows-aarch64.jar, natives-1.18.0-windows-x86_64.jar, etc. all under the com.aayushatharva.brotli4j:natives coordinates.

related: #142

to add support for shadow, you would then also include the following:

configurations.named { it.contains("shadow", true) }.configureEach { 
   attributes {
       attribute(OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE, objects.named("all"))
       attribute(MachineArchitecture.ARCHITECTURE_ATTRIBUTE, objects.named("all"))
   }
}

all of these were using the kotlin DSL, however it shouldn't be too difficult to translate to the groovy DSL.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions