All Downloads are FREE. Search and download functionalities are using the official Maven repository.

name.remal.gradle_plugins.toolkit.build_logic.helpers.gradle Maven / Gradle / Ivy

import static org.gradle.api.attributes.Category.CATEGORY_ATTRIBUTE
import static org.gradle.api.attributes.Category.DOCUMENTATION
import static org.gradle.api.attributes.Category.ENFORCED_PLATFORM
import static org.gradle.api.attributes.Category.REGULAR_PLATFORM
import static org.gradle.api.attributes.Category.VERIFICATION

import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.security.MessageDigest
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
import org.gradle.util.GradleVersion
import org.json.JSONArray
import org.json.JSONObject
import org.json.JSONTokener
import org.jsoup.Jsoup
import org.jsoup.nodes.Document

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

buildscript {
    dependencies {
        classpath "org.json:json:20240303"
        classpath "org.jsoup:jsoup:1.18.1"
        classpath "com.vdurmont:semver4j:3.1.0"
    }
    repositories {
        mavenCentral()
    }
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

Object MUTEX = new Object[0]

project.ext.isBuildSrcProject = rootProject.name == 'buildSrc'

allprojects {
    project.ext.afterEvaluateOrNow = { Closure action ->
        if (project.state.executed) {
            project.configure(project, action)
        } else {
            project.afterEvaluate(action)
        }
    }

    project.ext.withPluginAfterEvaluateOrNow = { String pluginId, Closure action ->
        project.afterEvaluateOrNow {
            project.pluginManager.withPlugin(pluginId) {
                project.configure(project, action)
            }
        }
    }
}

project.ext.startWithWord = { CharSequence untypedString, CharSequence untypedNeedle ->
    if (untypedString == null || untypedNeedle == null) return false
    if (untypedNeedle.length() == 0) return true
    if (untypedString.length() == 0) return false
    String string = untypedString.toString()
    String needle = untypedNeedle.toString()
    if (string == needle) return true
    if (string.startsWith(needle)) {
        String remaining = string.substring(needle.length())
        char firstRemainingChar = remaining.charAt(0)
        char lastNeedleChar = needle.charAt(needle.length() - 1)
        if (Character.isDigit(lastNeedleChar)) {
            return !Character.isDigit(firstRemainingChar)
        } else if (Character.isLetter(lastNeedleChar)) {
            if (!Character.isLetter(lastNeedleChar)) {
                return true
            } else if (Character.isUpperCase(firstRemainingChar) && !Character.isUpperCase(lastNeedleChar)) {
                return true
            } else if (Character.isLowerCase(firstRemainingChar) && !Character.isLowerCase(lastNeedleChar)) {
                return true
            } else {
                return false
            }
        } else {
            return Character.isLetter(firstRemainingChar)
        }
    }
    return false
}

project.ext.isInstanceOf = { Object object, String parentClassName ->
    if (object == null) return false

    Class parentClass = Class.forName(parentClassName, true, object.class.classLoader)
    return parentClass.isInstance(object)
}

project.ext.disableTask = { Task task ->
    task.enabled = false
    task.onlyIf { false }
    task.dependsOn = []
    Iterator registeredFileProperties = task.inputs.registeredFileProperties.iterator()
    while (registeredFileProperties.hasNext()) {
        registeredFileProperties.next()
        registeredFileProperties.remove()
    }
}

project.ext.isVerificationTask = { Task task ->
    if (task instanceof VerificationTask
        || task instanceof AbstractTestTask
        || task instanceof ValidatePlugins
        || task instanceof JacocoCoverageVerification
    ) {
        return true
    }
    return false
}


List sourceSetGetConfigurationNameMethods = SourceSet.getMethods().findAll {
    it.name.startsWith('get')
        && it.name.endsWith('ConfigurationName')
        && it.returnType == String
        && it.parameterCount == 0
        && !Modifier.isStatic(it.modifiers)
}
project.ext.getSourceSetConfigurationNames = { SourceSet sourceSet ->
    return sourceSetGetConfigurationNameMethods.collect { it.invoke(sourceSet) }
        .findAll { it != null }
        .toSet()
}

allprojects {
    project.ext.getAllSourceSetConfigurations = { SourceSet sourceSet ->
        Collection sourceSetConfigurationNames = project.getSourceSetConfigurationNames(sourceSet)
        return project.configurations.matching { sourceSetConfigurationNames.contains(it.name) }
    }.memoize()

    DomainObjectSet allSourceSetsConfigurations = project.ext.allSourceSetsConfigurations = project.objects.domainObjectSet(Configuration)
    project.pluginManager.withPlugin('java') {
        project.sourceSets.all { SourceSet sourceSet ->
            project.getAllSourceSetConfigurations(sourceSet).all { allSourceSetsConfigurations.add(it) }
        }
    }
}


project.ext.getDependencyCategory = { Object dep ->
    AttributeContainer attributes = null
    if (dep instanceof ModuleDependency) {
        attributes = dep.attributes
    } else if (dep instanceof ResolvedDependencyResult) {
        attributes = dep.resolvedVariant.attributes
    } else if (dep instanceof UnresolvedDependencyResult) {
        attributes = dep.attempted.attributes
    } else if (dep instanceof Dependency) {
        return false
    } else {
        throw new GradleException("Unsupported dependency type: ${dep.class}")
    }

    Attribute categoryAttribute = attributes.keySet().find { attr ->
        if (attr instanceof String) {
            return attr == CATEGORY_ATTRIBUTE.name
        } else {
            return attr.name == CATEGORY_ATTRIBUTE.name
        }
    }
    if (categoryAttribute == null) {
        return null;
    }

    Object value = attributes.getAttribute(categoryAttribute)
    if (value instanceof String) {
        return value
    }
    return value.name
}

project.ext.isPlatformDependency = { Object dep ->
    String category = getDependencyCategory(dep)
    return [REGULAR_PLATFORM, ENFORCED_PLATFORM].contains(category)
}

project.ext.isEnforcedPlatformDependency = { Object dep ->
    String category = getDependencyCategory(dep)
    return [ENFORCED_PLATFORM].contains(category)
}

project.ext.isDocumentationDependency = { Object dep ->
    String category = getDependencyCategory(dep)
    return [DOCUMENTATION].contains(category)
}

project.ext.isVerificationDependency = { Object dep ->
    String category = getDependencyCategory(dep)
    return [VERIFICATION].contains(category)
}

project.ext.calculateBaseJavaPackageFor = { Project project ->
    String baseJavaPackage = project.ext.find('baseJavaPackage')
    if (baseJavaPackage != null) return baseJavaPackage

    List baseJavaPackageTokens = "${rootProject.group}:${rootProject.name}${project.path}".split(/[.:]+/)
        .findAll { !it.isEmpty() }
    int hiddenTokenPos = baseJavaPackageTokens.findIndexOf { it.contains('--') }
    if (hiddenTokenPos >= 0) {
        baseJavaPackageTokens = baseJavaPackageTokens.subList(0, hiddenTokenPos)
    }
    for (int index = 0; index < baseJavaPackageTokens.size() - 1; ++index) {
        String token = baseJavaPackageTokens.get(index)
        String nextToken = baseJavaPackageTokens.get(index + 1)
        if (token == nextToken) {
            baseJavaPackageTokens.remove(index)
            --index
        } else if ("${token}-root" == nextToken) {
            baseJavaPackageTokens.remove(index + 1)
            --index
        } else if (token == "${nextToken}-root") {
            baseJavaPackageTokens.remove(index)
            --index
        }
    }
    return baseJavaPackageTokens.join('.')
        .replaceAll(/[^\w.]+/, '_')
        .replaceAll(/_{2,}/, '_')
        .replaceAll(/_\./, '.')
        .replaceAll(/\._/, '.')
        .replaceAll(/\.{2,}/, '.')
        .replaceFirst(/^\./, '')
        .replaceFirst(/\.$/, '')
}.memoize()

allprojects {
    project.ext.calculateBaseJavaPackage = {
        return project.calculateBaseJavaPackageFor(project)
    }.memoize()

    project.ext.calculateJavaModuleName = {
        return project.findProperty('javaModuleName') ?: project.calculateBaseJavaPackage()
    }.memoize()
}


allprojects {
    Closure maybeRegisterTask = { String taskName, Closure configurer ->
        try {
            return tasks.named(taskName)
        } catch (UnknownTaskException ignore) {
            // do nothing
        }
        return tasks.register(taskName) { Task task -> configurer(task) }
    }

    Closure findSourceSet = { String sourceSetName, boolean isRequired = true ->
        if (isRequired) {
            return project.sourceSets.getByName(sourceSetName)
        } else {
            return project.sourceSets.findByName(sourceSetName)
        }
    }
    project.ext.registerResolveSourceSetCompileClasspathTask = { String sourceSetName, boolean isRequired = true ->
        String taskName = "resolve${sourceSetName.capitalize()}SourceSetCompileClasspath"
        TaskProvider taskProvider = maybeRegisterTask(taskName) { Task task ->
            task.ext.isRequired = false
            task.dependsOn(project.provider { findSourceSet(sourceSetName, task.isRequired)?.with { project.configurations[it.compileClasspathConfigurationName].allDependencies } ?: [] })
            task.doLast { findSourceSet(sourceSetName, task.isRequired)?.compileClasspath?.files }
        }
        if (isRequired) {
            taskProvider.configure { Task task ->
                task.ext.isRequired = true
            }
        }
        return taskProvider
    }
    project.ext.registerResolveSourceSetRuntimeClasspathTask = { String sourceSetName, boolean isRequired = true ->
        String taskName = "resolve${sourceSetName.capitalize()}SourceSetRuntimeClasspath"
        TaskProvider taskProvider = maybeRegisterTask(taskName) { Task task ->
            task.ext.isRequired = false
            task.dependsOn(project.provider { findSourceSet(sourceSetName, task.isRequired)?.with { project.configurations[it.runtimeClasspathConfigurationName].allDependencies } ?: [] })
            task.dependsOn(project.provider { findSourceSet(sourceSetName, task.isRequired)?.classesTaskName ?: [] })
            task.doLast { findSourceSet(sourceSetName, task.isRequired)?.runtimeClasspath?.files }
        }
        if (isRequired) {
            taskProvider.configure { Task task ->
                task.ext.isRequired = true
            }
        }
        return taskProvider
    }

    Closure findConfiguration = { String configurationName, boolean isRequired = true ->
        if (isRequired) {
            return project.configurations.getByName(configurationName)
        } else {
            return project.configurations.findByName(configurationName)
        }
    }
    project.ext.registerResolveConfigurationTask = { String configurationName, boolean isRequired = true ->
        String taskName = "resolve${configurationName.capitalize()}Configuration"
        TaskProvider taskProvider = maybeRegisterTask(taskName) { Task task ->
            task.ext.isRequired = false
            task.dependsOn(project.provider { findConfiguration(configurationName, task.isRequired)?.allDependencies ?: [] })
            task.doLast { findConfiguration(configurationName, task.isRequired)?.resolve() }
        }
        if (isRequired) {
            taskProvider.configure { Task task ->
                task.ext.isRequired = true
            }
        }
        return taskProvider
    }
}


Closure sha512hex = project.ext.sha512hex = { Object content ->
    if (content instanceof CharSequence) {
        content = content.toString().getBytes('UTF-8')
    }
    if (!(content instanceof byte[])) {
        throw new IllegalAccessException("content must be instance of CharSequence or byte[]")
    }

    MessageDigest md = MessageDigest.getInstance("SHA-512")
    byte[] hash = md.digest((byte[]) content)

    StringBuilder hex = new StringBuilder(hash.length * 2)
    for (byte b : hash) {
        hex.append(String.format("%02x", b))
    }
    return hex.toString()
}


File downloadedContentCacheDir = project.layout.buildDirectory.dir('downloaded-content-cache').get().asFile

Closure getCacheFileForDownloadedUrl = project.ext.getCacheFileForDownloadedUrl = { Object untypedUrl, String cacheFileNameSuffix = null ->
    URL url = project.uri(untypedUrl).toURL()

    if (cacheFileNameSuffix == null) {
        cacheFileNameSuffix = url.path.with { String path ->
            path = new File(path).getName()
            int dotPos = path.lastIndexOf('.')
            return dotPos >= 0 ? path.substring(dotPos) : ''
        }
    }

    return new File(downloadedContentCacheDir, sha512hex(url.toString()) + cacheFileNameSuffix)
}

Closure loadUrlContent = project.ext.loadUrlContent = { Object untypedUrl, String cacheFileNameSuffix = null ->
    synchronized (MUTEX) {
        File cacheFile = getCacheFileForDownloadedUrl(untypedUrl, cacheFileNameSuffix)
        if (cacheFile.exists()) {
            return cacheFile.bytes
        }

        URL url = project.uri(untypedUrl).toURL()
        URLConnection connection = url.openConnection()
        connection.connectTimeout = 5000
        connection.readTimeout = 15000
        connection.useCaches = false
        try {
            if (connection instanceof HttpURLConnection) {
                if (connection.responseCode != 200) {
                    throw new GradleException("$url returned ${connection.responseCode} status")
                }
            }

            byte[] content = connection.inputStream.bytes
            cacheFile.parentFile.mkdirs()
            cacheFile.bytes = content
            return content

        } finally {
            if (connection instanceof HttpURLConnection) {
                connection.disconnect()
            }
        }
    }
}

Closure loadTextUrlContent = project.ext.loadTextUrlContent = { Object untypedUrl, String encoding = 'UTF-8', String cacheFileNameSuffix = null ->
    byte[] content = loadUrlContent(untypedUrl, cacheFileNameSuffix)
    return new String(content, encoding)
}

Closure loadJsonFromUrl = project.ext.loadJsonFromUrl = { Object untypedUrl, String encoding = 'UTF-8' ->
    String content = loadTextUrlContent(untypedUrl, encoding, '.json')
    return new JSONTokener(content).nextValue()
}

Closure loadHtmlFromUrl = project.ext.loadHtmlFromUrl = { Object untypedUrl, String encoding = 'UTF-8' ->
    String content = loadTextUrlContent(untypedUrl, encoding, '.html')
    return Jsoup.parse(content, project.uri(untypedUrl).toString())
}

Closure> getAllGradleVersions = project.ext.getAllGradleVersions = {
    List result = []

    JSONArray versionObjects = loadJsonFromUrl('https://services.gradle.org/versions/all')
    for (JSONObject versionObject : versionObjects) {
        if (versionObject.optBoolean('snapshot')) continue
        if (versionObject.optBoolean('nightly')) continue
        if (versionObject.optBoolean('releaseNightly')) continue
        if (versionObject.optBoolean('broken')) continue
        if (!versionObject.optString('milestoneFor').isEmpty()) continue
        if (!versionObject.optBoolean('activeRc') && !versionObject.optString('rcFor').isEmpty()) continue

        String version = versionObject.getString('version')
        if (version.containsIgnoreCase('milestone')) continue
        if (version.startsWith('0.')) continue

        GradleVersion gradleVersion = GradleVersion.version(version)
        if (gradleVersion.baseVersion == gradleVersion) {
            result.add(gradleVersion)
        }
    }

    result.sort(Comparator.reverseOrder())

    return result
}.memoize()

Closure> getStableGradleVersions = project.ext.getStableGradleVersions = {
    return getAllGradleVersions().findAll { it.baseVersion == it }
}.memoize()

Closure> getMinorGradleVersions = project.ext.getMinorGradleVersions = {
    return getStableGradleVersions().findAll { it.version.count('.') == 1 }
}


Closure> getFilteredJavaRuntimeVersions = { Closure filter ->
    List result = []

    JSONArray versionObjects = loadJsonFromUrl('https://api.foojay.io/disco/v3.0/major_versions?ea=false&ga=true&discovery_scope_id=public&include_versions=false')['result']
    for (JSONObject versionObject : versionObjects) {
        if (versionObject.optBoolean('early_access_only')) continue
        if (versionObject.optString('release_status') == 'ea') continue
        if (!filter(versionObject)) continue

        int version = versionObject.getInt('major_version')
        if (version < 8) continue

        result.add(version)
    }

    result.sort(Comparator.reverseOrder())
    return result
}

Closure> getAllJavaRuntimeVersions = project.ext.getAllJavaRuntimeVersions = {
    return getFilteredJavaRuntimeVersions { true }
}.memoize()

Closure> getLtsJavaRuntimeVersions = project.ext.getLtsJavaRuntimeVersions = {
    return getFilteredJavaRuntimeVersions { JSONObject versionObject ->
        versionObject.getString('term_of_support') == 'LTS'
    }
}.memoize()


ConcurrentMap getGradleApiDependencyVersionCache = new ConcurrentHashMap<>()
allprojects {
    project.ext.getGradleApiDependencyVersion = { String notation ->
        return getGradleApiDependencyVersionCache.computeIfAbsent(notation) {
            String gradleApiVersion = project.configurations.projectDependencyConstraints
                .allDependencyConstraints
                .find { "${it.group}:${it.name}" == 'name.remal.gradle-api:gradle-api' }
                ?.version
            if (gradleApiVersion == null) throw new GradleException('Unknown Gradle API version')

            Configuration tempConf = project.configurations.detachedConfiguration(
                project.dependencies.create("name.remal.gradle-api:gradle-api:$gradleApiVersion")
            )

            String version = tempConf.resolvedConfiguration
                .resolvedArtifacts
                .collect { it.moduleVersion }
                .collect { it.id }
                .find { "${it.group}:*" == notation || "${it.group}:${it.name}" == notation }
                ?.version
            if (version == null) throw new GradleException("Gradle API dependency not found: $notation")
            return version
        }
    }
}


allprojects {
    project.ext.fatJarWithDependentTaskNames = [
        'classes',
        'jar',
        'sourcesJar',
        'javadocJar',
    ]

    Closure fatJarWithImpl = { Project otherProject ->
        otherProject.tasks.withType(Javadoc).configureEach { enabled = false }

        project.fatJarWithDependentTaskNames.forEach { String taskName ->
            tasks.matching { it.name == taskName }.configureEach {
                dependsOn(otherProject.tasks.matching { it.name == taskName })
            }
        }

        sourceSets.main.allSource.srcDirs(otherProject.sourceSets.main.allSource.srcDirs)
        otherProject.sourceSets.main.output.forEach { sourceSets.main.output.dir(it) }

        tasks.withType(AbstractCopyTask).configureEach {
            filesMatching(['**/package-info.class', '**/package-info.java']) {
                duplicatesStrategy(DuplicatesStrategy.EXCLUDE)
            }
        }

        tasks.withType(Javadoc).configureEach { Javadoc task ->
            task.dependsOn(project.provider { otherProject.registerResolveConfigurationTask('optionalHidden') })

            task.onlyIf {
                task.classpath += otherProject.configurations.optionalHidden
                otherProject.tasks.withType(Javadoc).forEach { Javadoc otherTask ->
                    task.classpath += otherTask.classpath
                }
                return true
            }

            Set processedPackageInfoResources = new LinkedHashSet<>()
            task.exclude { FileTreeElement element ->
                String path = element.path
                if ("/$path".endsWith('/package-info.class') || "/$path".endsWith('/package-info.java')) {
                    return !processedPackageInfoResources.add(path)
                }
            }

            task.source(
                project.provider {
                    otherProject.tasks.withType(Javadoc)
                        .collect { it.source }
                }
            )
        }
    }

    Set fatJarWithProjects = new LinkedHashSet<>()
    project.ext.fatJarWith = { Project otherProject ->
        if (!fatJarWithProjects.add(otherProject)) return
        project.withPluginAfterEvaluateOrNow('java') {
            otherProject.withPluginAfterEvaluateOrNow('java') {
                fatJarWithImpl(otherProject)
            }
        }
    }
}

Closure getClassLocationFile = project.ext.getClassLocationFile = { Class clazz ->
    URL location = clazz?.protectionDomain?.codeSource?.location
    if (location == null) {
        return null
    }

    try {
        return project.file(location)

    } catch (IllegalArgumentException ignored) {
        return null
    }
}

Closure addClassLocationFileToInputs = project.ext.addClassLocationFileToInputs = { Class clazz, TaskInputs inputs ->
    File file = getClassLocationFile(clazz)
    if (file == null) {
        // do nothing
    } else if (file.isDirectory()) {
        inputs.dir(file).optional()
    } else {
        inputs.file(file).optional()
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy