5b5dae9249
Include new project as build logic included build. Such change will allow us to start migration into build convention plugins by splitting buildSrc logic into subprojects.
462 lines
16 KiB
Kotlin
462 lines
16 KiB
Kotlin
@file:Suppress("PropertyName", "HasPlatformType", "UnstableApiUsage")
|
|
|
|
import org.gradle.internal.os.OperatingSystem
|
|
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
|
|
import java.io.Closeable
|
|
import java.io.OutputStreamWriter
|
|
import java.net.URI
|
|
import java.text.SimpleDateFormat
|
|
import java.time.Duration
|
|
import java.time.Instant
|
|
import java.util.*
|
|
import javax.xml.stream.XMLOutputFactory
|
|
|
|
plugins {
|
|
base
|
|
}
|
|
|
|
val intellijReleaseType: String by rootProject.extra
|
|
val intellijVersion = rootProject.extra["versions.intellijSdk"] as String
|
|
val intellijVersionForIde = rootProject.intellijSdkVersionForIde()
|
|
val asmVersion = rootProject.findProperty("versions.jar.asm-all") as String?
|
|
val androidStudioRelease = rootProject.findProperty("versions.androidStudioRelease") as String?
|
|
val androidStudioBuild = rootProject.findProperty("versions.androidStudioBuild") as String?
|
|
val intellijSeparateSdks: Boolean by rootProject.extra
|
|
|
|
fun checkIntellijVersion(intellijVersion: String) {
|
|
val intellijVersionDelimiterIndex = intellijVersion.indexOfAny(charArrayOf('.', '-'))
|
|
if (intellijVersionDelimiterIndex == -1) {
|
|
error("Invalid IDEA version $intellijVersion")
|
|
}
|
|
}
|
|
checkIntellijVersion(intellijVersion)
|
|
intellijVersionForIde?.let { checkIntellijVersion(it) }
|
|
|
|
logger.info("intellijVersion: $intellijVersion")
|
|
logger.info("intellijVersionForIde: $intellijVersionForIde")
|
|
logger.info("androidStudioRelease: $androidStudioRelease")
|
|
logger.info("androidStudioBuild: $androidStudioBuild")
|
|
logger.info("intellijSeparateSdks: $intellijSeparateSdks")
|
|
|
|
val androidStudioOs by lazy {
|
|
when {
|
|
OperatingSystem.current().isWindows -> "windows"
|
|
OperatingSystem.current().isMacOsX -> "mac"
|
|
OperatingSystem.current().isLinux -> "linux"
|
|
else -> {
|
|
logger.error("Unknown operating system for android tools: ${OperatingSystem.current().name}")
|
|
""
|
|
}
|
|
}
|
|
}
|
|
|
|
repositories {
|
|
if (androidStudioRelease != null) {
|
|
ivy {
|
|
url = URI("https://dl.google.com/dl/android/studio/ide-zips/$androidStudioRelease")
|
|
|
|
patternLayout {
|
|
artifact("[artifact]-[revision]-$androidStudioOs.[ext]")
|
|
}
|
|
|
|
metadataSources {
|
|
artifact()
|
|
}
|
|
}
|
|
}
|
|
|
|
maven("https://www.jetbrains.com/intellij-repository/$intellijReleaseType")
|
|
maven("https://plugins.jetbrains.com/maven")
|
|
maven("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies")
|
|
}
|
|
|
|
val intellij by configurations.creating
|
|
val intellijForIde by configurations.creating
|
|
val androidStudio by configurations.creating
|
|
val sources by configurations.creating
|
|
val sourcesForIde by configurations.creating
|
|
val jpsStandalone by configurations.creating
|
|
val jpsStandaloneForIde by configurations.creating
|
|
val intellijCore by configurations.creating
|
|
val intellijCoreForIde by configurations.creating
|
|
|
|
/**
|
|
* Special repository for annotations.jar required for idea runtime only.
|
|
*
|
|
* See IntellijDependenciesKt.intellijRuntimeAnnotations for more details.
|
|
*/
|
|
val intellijRuntimeAnnotations = "intellij-runtime-annotations"
|
|
|
|
val dependenciesDir = (findProperty("kotlin.build.dependencies.dir") as String?)?.let(::File)
|
|
?: rootProject.gradle.gradleUserHomeDir.resolve("kotlin-build-dependencies")
|
|
|
|
val customDepsRepoDir = dependenciesDir.resolve("repo")
|
|
|
|
val customDepsOrg: String by rootProject.extra
|
|
val repoDir = File(customDepsRepoDir, customDepsOrg)
|
|
|
|
dependencies {
|
|
if (androidStudioRelease != null) {
|
|
val extension = if (androidStudioOs == "linux")
|
|
"tar.gz"
|
|
else
|
|
"zip"
|
|
|
|
androidStudio("google:android-studio-ide:$androidStudioBuild@$extension")
|
|
} else {
|
|
intellij("com.jetbrains.intellij.idea:ideaIC:$intellijVersion")
|
|
intellijVersionForIde?.let { intellijForIde("com.jetbrains.intellij.idea:ideaIC:$it") }
|
|
}
|
|
|
|
if (asmVersion != null) {
|
|
sources("org.jetbrains.intellij.deps:asm-all:$asmVersion:sources@jar")
|
|
}
|
|
|
|
sources("com.jetbrains.intellij.idea:ideaIC:$intellijVersion:sources@jar")
|
|
intellijVersionForIde?.let { sourcesForIde("com.jetbrains.intellij.idea:ideaIC:$it:sources@jar") }
|
|
jpsStandalone("com.jetbrains.intellij.idea:jps-standalone:$intellijVersion")
|
|
intellijVersionForIde?.let { jpsStandaloneForIde("com.jetbrains.intellij.idea:jps-standalone:$it") }
|
|
intellijCore("com.jetbrains.intellij.idea:intellij-core:$intellijVersion")
|
|
intellijVersionForIde?.let { intellijCoreForIde("com.jetbrains.intellij.idea:intellij-core:$it") }
|
|
}
|
|
|
|
fun prepareDeps(
|
|
intellij: Configuration,
|
|
intellijCore: Configuration,
|
|
sources: Configuration,
|
|
jpsStandalone: Configuration,
|
|
intellijVersion: String
|
|
) {
|
|
val makeIntellijCore = buildIvyRepositoryTask(intellijCore, customDepsOrg, customDepsRepoDir)
|
|
|
|
val makeIntellijAnnotations = tasks.register("makeIntellijAnnotations${intellij.name.replaceFirstChar(Char::uppercase)}", Copy::class) {
|
|
dependsOn(makeIntellijCore)
|
|
|
|
val intellijCoreRepo = CleanableStore[repoDir.resolve("intellij-core").absolutePath][intellijVersion].use()
|
|
from(intellijCoreRepo.resolve("artifacts/annotations.jar"))
|
|
|
|
val annotationsStore = CleanableStore[repoDir.resolve(intellijRuntimeAnnotations).absolutePath]
|
|
val targetDir = annotationsStore[intellijVersion].use()
|
|
into(targetDir)
|
|
|
|
val ivyFile = File(targetDir, "$intellijRuntimeAnnotations.ivy.xml")
|
|
outputs.files(ivyFile)
|
|
|
|
doFirst {
|
|
annotationsStore.cleanStore()
|
|
}
|
|
|
|
doLast {
|
|
writeIvyXml(
|
|
customDepsOrg,
|
|
intellijRuntimeAnnotations,
|
|
intellijVersion,
|
|
intellijRuntimeAnnotations,
|
|
targetDir,
|
|
targetDir,
|
|
targetDir,
|
|
allowAnnotations = true
|
|
)
|
|
}
|
|
}
|
|
|
|
val mergeSources = tasks.create("mergeSources${intellij.name.replaceFirstChar(Char::uppercase)}", Jar::class.java) {
|
|
dependsOn(sources)
|
|
isPreserveFileTimestamps = false
|
|
isReproducibleFileOrder = true
|
|
isZip64 = true
|
|
if (!kotlinBuildProperties.isTeamcityBuild) {
|
|
from(provider { sources.map(::zipTree) })
|
|
}
|
|
destinationDirectory.set(File(repoDir, sources.name))
|
|
archiveBaseName.set("intellij")
|
|
archiveClassifier.set("sources")
|
|
archiveVersion.set(intellijVersion)
|
|
}
|
|
|
|
val sourcesFile = mergeSources.outputs.files.singleFile
|
|
|
|
val makeIde = if (androidStudioBuild != null) {
|
|
buildIvyRepositoryTask(
|
|
androidStudio,
|
|
customDepsOrg,
|
|
customDepsRepoDir,
|
|
if (androidStudioOs == "mac")
|
|
::skipContentsDirectory
|
|
else
|
|
::skipToplevelDirectory
|
|
)
|
|
} else {
|
|
val task = buildIvyRepositoryTask(intellij, customDepsOrg, customDepsRepoDir, null, sourcesFile)
|
|
|
|
task.configure {
|
|
dependsOn(mergeSources)
|
|
}
|
|
|
|
task
|
|
}
|
|
|
|
val buildJpsStandalone = buildIvyRepositoryTask(jpsStandalone, customDepsOrg, customDepsRepoDir, null, sourcesFile)
|
|
|
|
tasks.named("build") {
|
|
dependsOn(
|
|
makeIntellijCore,
|
|
makeIde,
|
|
buildJpsStandalone,
|
|
makeIntellijAnnotations
|
|
)
|
|
}
|
|
}
|
|
|
|
when(kotlinBuildProperties.getOrNull("attachedIntellijVersion")) {
|
|
null -> {}
|
|
"master" -> {} // for intellij/kt-master, intellij maven artifacts are used instead of manual unpacked dependencies
|
|
else -> {
|
|
val intellijVersionForIde = intellijVersionForIde
|
|
?: error("intellijVersionForIde should not be null as attachedIntellijVersion is present")
|
|
prepareDeps(intellijForIde, intellijCoreForIde, sourcesForIde, jpsStandaloneForIde, intellijVersionForIde)
|
|
}
|
|
}
|
|
|
|
|
|
tasks.named<Delete>("clean") {
|
|
delete(customDepsRepoDir)
|
|
}
|
|
|
|
fun buildIvyRepositoryTask(
|
|
configuration: Configuration,
|
|
organization: String,
|
|
repoDirectory: File,
|
|
pathRemap: ((String) -> String)? = null,
|
|
sources: File? = null
|
|
): TaskProvider<Task> {
|
|
fun ResolvedArtifact.storeDirectory(): CleanableStore =
|
|
CleanableStore[repoDirectory.resolve("$organization/${moduleVersion.id.name}").absolutePath]
|
|
|
|
fun ResolvedArtifact.moduleDirectory(): File =
|
|
storeDirectory()[moduleVersion.id.version].use()
|
|
|
|
return tasks.register("buildIvyRepositoryFor${configuration.name.replaceFirstChar(Char::uppercase)}") {
|
|
dependsOn(configuration)
|
|
inputs.files(configuration)
|
|
|
|
outputs.upToDateWhen {
|
|
val repoMarker = configuration.resolvedConfiguration.resolvedArtifacts.single().moduleDirectory().resolve(".marker")
|
|
repoMarker.exists()
|
|
}
|
|
|
|
doFirst {
|
|
val artifact = configuration.resolvedConfiguration.resolvedArtifacts.single()
|
|
val moduleDirectory = artifact.moduleDirectory()
|
|
|
|
artifact.storeDirectory().cleanStore()
|
|
|
|
val repoMarker = File(moduleDirectory, ".marker")
|
|
if (repoMarker.exists()) {
|
|
logger.info("Path ${repoMarker.absolutePath} already exists, skipping unpacking.")
|
|
return@doFirst
|
|
}
|
|
|
|
with(artifact) {
|
|
val artifactsDirectory = File(moduleDirectory, "artifacts")
|
|
logger.info("Unpacking ${file.name} into ${artifactsDirectory.absolutePath}")
|
|
copy {
|
|
val fileTree = when (extension) {
|
|
"tar.gz" -> tarTree(file)
|
|
"zip" -> zipTree(file)
|
|
else -> error("Unsupported artifact extension: $extension")
|
|
}
|
|
|
|
from(
|
|
fileTree.matching {
|
|
exclude("**/plugins/Kotlin/**")
|
|
}
|
|
)
|
|
|
|
into(artifactsDirectory)
|
|
|
|
if (pathRemap != null) {
|
|
eachFile {
|
|
path = pathRemap(path)
|
|
}
|
|
}
|
|
|
|
includeEmptyDirs = false
|
|
}
|
|
|
|
writeIvyXml(
|
|
organization,
|
|
moduleVersion.id.name,
|
|
moduleVersion.id.version,
|
|
moduleVersion.id.name,
|
|
File(artifactsDirectory, "lib"),
|
|
File(artifactsDirectory, "lib"),
|
|
File(moduleDirectory, "ivy"),
|
|
*listOfNotNull(sources).toTypedArray()
|
|
)
|
|
|
|
val pluginsDirectory = File(artifactsDirectory, "plugins")
|
|
if (pluginsDirectory.exists()) {
|
|
file(File(artifactsDirectory, "plugins"))
|
|
.listFiles { file: File -> file.isDirectory }
|
|
.forEach {
|
|
writeIvyXml(
|
|
organization,
|
|
it.name,
|
|
moduleVersion.id.version,
|
|
it.name,
|
|
File(it, "lib"),
|
|
File(it, "lib"),
|
|
File(moduleDirectory, "ivy"),
|
|
*listOfNotNull(sources).toTypedArray()
|
|
)
|
|
}
|
|
}
|
|
|
|
repoMarker.createNewFile()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fun CleanableStore.cleanStore() = cleanDir(Instant.now().minus(Duration.ofDays(30)))
|
|
|
|
fun writeIvyXml(
|
|
organization: String,
|
|
moduleName: String,
|
|
version: String,
|
|
fileName: String,
|
|
baseDir: File,
|
|
artifactDir: File,
|
|
targetDir: File,
|
|
vararg sourcesJar: File,
|
|
allowAnnotations: Boolean = false
|
|
) {
|
|
fun shouldIncludeIntellijJar(jar: File) =
|
|
jar.isFile
|
|
&& jar.extension == "jar"
|
|
&& !jar.name.startsWith("kotlin-")
|
|
&& (allowAnnotations || jar.name != "annotations.jar") // see comments for [intellijAnnotations] above
|
|
|
|
val ivyFile = targetDir.resolve("$fileName.ivy.xml")
|
|
ivyFile.parentFile.mkdirs()
|
|
with(XMLWriter(ivyFile.writer())) {
|
|
document("UTF-8", "1.0") {
|
|
element("ivy-module") {
|
|
attribute("version", "2.0")
|
|
attribute("xmlns:m", "http://ant.apache.org/ivy/maven")
|
|
|
|
emptyElement("info") {
|
|
attributes(
|
|
"organisation" to organization,
|
|
"module" to moduleName,
|
|
"revision" to version,
|
|
"publication" to SimpleDateFormat("yyyyMMddHHmmss").format(Date())
|
|
)
|
|
}
|
|
|
|
element("configurations") {
|
|
listOf("default", "sources").forEach { configurationName ->
|
|
emptyElement("conf") {
|
|
attributes("name" to configurationName, "visibility" to "public")
|
|
}
|
|
}
|
|
}
|
|
|
|
element("publications") {
|
|
artifactDir.listFiles()
|
|
?.filter(::shouldIncludeIntellijJar)
|
|
?.sortedBy { it.name.lowercase() }
|
|
?.forEach { jarFile ->
|
|
val relativeName = jarFile.toRelativeString(baseDir).removeSuffix(".jar")
|
|
emptyElement("artifact") {
|
|
attributes(
|
|
"name" to relativeName,
|
|
"type" to "jar",
|
|
"ext" to "jar",
|
|
"conf" to "default"
|
|
)
|
|
}
|
|
}
|
|
|
|
sourcesJar.forEach { jarFile ->
|
|
emptyElement("artifact") {
|
|
val sourcesArtifactName = jarFile.name.substringBefore("-$version")
|
|
attributes(
|
|
"name" to sourcesArtifactName,
|
|
"type" to "jar",
|
|
"ext" to "jar",
|
|
"conf" to "sources",
|
|
"m:classifier" to "sources"
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
close()
|
|
}
|
|
}
|
|
|
|
fun skipToplevelDirectory(path: String) = path.substringAfter('/')
|
|
|
|
fun skipContentsDirectory(path: String) = path.substringAfter("Contents/")
|
|
|
|
fun Project.intellijSdkVersionForIde(): String? {
|
|
val majorVersion = kotlinBuildProperties.getOrNull("attachedIntellijVersion") as? String ?: return null
|
|
return rootProject.findProperty("versions.intellijSdk.forIde.$majorVersion") as? String
|
|
}
|
|
|
|
class XMLWriter(private val outputStreamWriter: OutputStreamWriter) : Closeable {
|
|
|
|
private val xmlStreamWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(outputStreamWriter)
|
|
|
|
private var depth = 0
|
|
private val indent = " "
|
|
|
|
fun document(encoding: String, version: String, init: XMLWriter.() -> Unit) = apply {
|
|
xmlStreamWriter.writeStartDocument(encoding, version)
|
|
|
|
init()
|
|
|
|
xmlStreamWriter.writeEndDocument()
|
|
}
|
|
|
|
fun element(name: String, init: XMLWriter.() -> Unit) = apply {
|
|
writeIndent()
|
|
xmlStreamWriter.writeStartElement(name)
|
|
depth += 1
|
|
|
|
init()
|
|
|
|
depth -= 1
|
|
writeIndent()
|
|
xmlStreamWriter.writeEndElement()
|
|
}
|
|
|
|
fun emptyElement(name: String, init: XMLWriter.() -> Unit) = apply {
|
|
writeIndent()
|
|
xmlStreamWriter.writeEmptyElement(name)
|
|
init()
|
|
}
|
|
|
|
fun attribute(name: String, value: String): Unit = xmlStreamWriter.writeAttribute(name, value)
|
|
|
|
fun attributes(vararg attributes: Pair<String, String>) {
|
|
attributes.forEach { attribute(it.first, it.second) }
|
|
}
|
|
|
|
private fun writeIndent() {
|
|
xmlStreamWriter.writeCharacters("\n")
|
|
repeat(depth) {
|
|
xmlStreamWriter.writeCharacters(indent)
|
|
}
|
|
}
|
|
|
|
override fun close() {
|
|
xmlStreamWriter.flush()
|
|
xmlStreamWriter.close()
|
|
outputStreamWriter.close()
|
|
}
|
|
} |