JPS Build: Temporary solution for creating IDEA artifacts settings
This script traverses all Gradle copy task, build virtual files model and generates IDEA artifacts configuration based on this. Unfortunately this is not fully working and needs manual fixing, so the actual artifacts configurations are commited by hand for now. By the way, this script also visits copy task's internals which leads that Gradle generates manifests in `build` folders during IDEA import. This files are referenced in artifacts configurations. So, despite the fact that artifacts configurations created by this script is not used, we need to run them to generate required manifests. This solution will be replaced with DSL that produces both Gradle copy tasks and IDEA artifacts configuration which is coming soon.
This commit is contained in:
@@ -38,4 +38,5 @@ build/
|
||||
.idea/compiler.xml
|
||||
.idea/inspectionProfiles/profiles_settings.xml
|
||||
.idea/.name
|
||||
.idea/artifacts/dist_auto_reference_dont_use.xml
|
||||
kotlin-ultimate/
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import proguard.gradle.ProGuardTask
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.jetbrains.kotlin.buildUtils.idea.*
|
||||
import org.jetbrains.gradle.ext.ActionDelegationConfig.TestRunner.*
|
||||
|
||||
buildscript {
|
||||
@@ -853,6 +854,10 @@ if (isJpsBuildEnabled && System.getProperty("idea.active") != null) {
|
||||
rootProject.idea {
|
||||
project {
|
||||
settings {
|
||||
ideArtifacts {
|
||||
generateIdeArtifacts(rootProject, this@ideArtifacts)
|
||||
}
|
||||
|
||||
compiler {
|
||||
processHeapSize = 2000
|
||||
addNotNullAssertions = true
|
||||
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package idea
|
||||
|
||||
import groovy.lang.Closure
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.Transformer
|
||||
import org.gradle.api.file.ContentFilterable
|
||||
import org.gradle.api.file.DuplicatesStrategy
|
||||
import org.gradle.api.file.FileCopyDetails
|
||||
import org.gradle.api.file.RelativePath
|
||||
import java.io.File
|
||||
import java.io.FilterReader
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
|
||||
class DistCopyDetailsMock(val context: DistModelBuildContext, srcName: String) : FileCopyDetails {
|
||||
private var relativePath = RelativePath(true, srcName)
|
||||
lateinit var lastAction: Action<in FileCopyDetails>
|
||||
|
||||
class E : Exception() {
|
||||
// skip stack trace filling
|
||||
override fun fillInStackTrace(): Throwable = this
|
||||
}
|
||||
|
||||
fun logUnsupported(methodName: String): Nothing {
|
||||
context.logUnsupported("COPY ACTION FileCopyDetails mock: $methodName", lastAction)
|
||||
throw E()
|
||||
}
|
||||
|
||||
override fun setDuplicatesStrategy(strategy: DuplicatesStrategy) {
|
||||
logUnsupported("setDuplicatesStrategy")
|
||||
}
|
||||
|
||||
override fun getSourcePath(): String {
|
||||
logUnsupported("getSourcePath")
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
logUnsupported("getName")
|
||||
}
|
||||
|
||||
override fun getSize(): Long {
|
||||
logUnsupported("getSize")
|
||||
}
|
||||
|
||||
override fun getRelativePath(): RelativePath = relativePath
|
||||
|
||||
override fun getRelativeSourcePath(): RelativePath {
|
||||
logUnsupported("getRelativeSourcePath")
|
||||
}
|
||||
|
||||
override fun expand(properties: MutableMap<String, *>): ContentFilterable {
|
||||
logUnsupported("expand")
|
||||
}
|
||||
|
||||
override fun getMode(): Int {
|
||||
logUnsupported("getMode")
|
||||
}
|
||||
|
||||
override fun getSourceName(): String {
|
||||
logUnsupported("getSourceName")
|
||||
}
|
||||
|
||||
override fun filter(properties: MutableMap<String, *>, filterType: Class<out FilterReader>): ContentFilterable {
|
||||
logUnsupported("filter")
|
||||
}
|
||||
|
||||
override fun filter(filterType: Class<out FilterReader>): ContentFilterable {
|
||||
logUnsupported("filter")
|
||||
}
|
||||
|
||||
override fun filter(closure: Closure<*>): ContentFilterable {
|
||||
logUnsupported("filter")
|
||||
}
|
||||
|
||||
override fun filter(transformer: Transformer<String, String>): ContentFilterable {
|
||||
logUnsupported("filter")
|
||||
}
|
||||
|
||||
override fun getFile(): File {
|
||||
logUnsupported("getFile")
|
||||
}
|
||||
|
||||
override fun setMode(mode: Int) {
|
||||
logUnsupported("setMode")
|
||||
}
|
||||
|
||||
override fun copyTo(output: OutputStream) {
|
||||
logUnsupported("copyTo")
|
||||
}
|
||||
|
||||
override fun copyTo(target: File): Boolean {
|
||||
logUnsupported("copyTo")
|
||||
}
|
||||
|
||||
override fun open(): InputStream {
|
||||
logUnsupported("open")
|
||||
}
|
||||
|
||||
override fun setRelativePath(path: RelativePath) {
|
||||
relativePath = path
|
||||
}
|
||||
|
||||
override fun getPath(): String {
|
||||
logUnsupported("getPath")
|
||||
}
|
||||
|
||||
override fun isDirectory(): Boolean {
|
||||
logUnsupported("isDirectory")
|
||||
}
|
||||
|
||||
override fun getDuplicatesStrategy(): DuplicatesStrategy {
|
||||
logUnsupported("getDuplicatesStrategy")
|
||||
}
|
||||
|
||||
override fun setName(name: String) {
|
||||
logUnsupported("setName")
|
||||
}
|
||||
|
||||
override fun getLastModified(): Long {
|
||||
logUnsupported("getLastModified")
|
||||
}
|
||||
|
||||
override fun setPath(path: String) {
|
||||
logUnsupported("setPath")
|
||||
}
|
||||
|
||||
override fun exclude() {
|
||||
logUnsupported("exclude")
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+96
@@ -0,0 +1,96 @@
|
||||
package org.jetbrains.kotlin.buildUtils.idea
|
||||
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.file.FileCopyDetails
|
||||
import java.io.File
|
||||
import java.io.PrintWriter
|
||||
|
||||
|
||||
class DistVFile(
|
||||
val parent: DistVFile?,
|
||||
val name: String,
|
||||
val file: File = File(parent!!.file, name)
|
||||
) {
|
||||
val child = mutableMapOf<String, DistVFile>()
|
||||
|
||||
val contents = mutableSetOf<DistContentElement>()
|
||||
|
||||
override fun toString(): String = name
|
||||
|
||||
val hasContents: Boolean = file.exists() || contents.isNotEmpty()
|
||||
|
||||
fun relativePath(path: String): DistVFile {
|
||||
val pathComponents = path.split(File.separatorChar)
|
||||
return pathComponents.fold(this) { parent: DistVFile, childName: String ->
|
||||
try {
|
||||
parent.getOrCreateChild(childName)
|
||||
} catch (t: Throwable) {
|
||||
throw Exception("Error while processing path `$path`, components: `$pathComponents`, element: `$childName`", t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getOrCreateChild(name: String): DistVFile = child.getOrPut(name) {
|
||||
DistVFile(this, name)
|
||||
}
|
||||
|
||||
fun addContents(contents: DistContentElement) {
|
||||
this.contents.add(contents)
|
||||
}
|
||||
|
||||
fun removeAll(matcher: (String) -> Boolean) {
|
||||
child.keys.filter(matcher).forEach {
|
||||
child.remove(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun printTree(p: PrintWriter, depth: String = "") {
|
||||
p.println("$depth${file.path} ${if (file.exists()) "EXISTED" else ""}:")
|
||||
contents.forEach {
|
||||
p.println("$depth $it")
|
||||
}
|
||||
child.values.forEach {
|
||||
it.printTree(p, "$depth ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class DistContentElement()
|
||||
|
||||
class DistCopy(
|
||||
target: DistVFile,
|
||||
val src: DistVFile,
|
||||
val customTargetName: String? = null,
|
||||
val copyActions: Collection<Action<in FileCopyDetails>> = listOf()
|
||||
) : DistContentElement() {
|
||||
init {
|
||||
target.addContents(this)
|
||||
}
|
||||
|
||||
override fun toString(): String =
|
||||
"COPY OF ${src.file}" +
|
||||
if (customTargetName != null) " -> $customTargetName" else ""
|
||||
}
|
||||
|
||||
class DistModuleOutput(parent: DistVFile, val projectId: String) : DistContentElement() {
|
||||
init {
|
||||
parent.addContents(this)
|
||||
}
|
||||
|
||||
override fun toString(): String = "COMPILE OUTPUT $projectId"
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as DistModuleOutput
|
||||
|
||||
if (projectId != other.projectId) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return projectId.hashCode()
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package idea
|
||||
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.file.FileCopyDetails
|
||||
import org.jetbrains.kotlin.buildUtils.idea.DistVFile
|
||||
import org.jetbrains.kotlin.buildUtils.idea.logger
|
||||
|
||||
/**
|
||||
* Used for logging and nesting properties
|
||||
*/
|
||||
class DistModelBuildContext(
|
||||
val parent: DistModelBuildContext?,
|
||||
val kind: String,
|
||||
val title: String,
|
||||
val report: Appendable? = parent?.report,
|
||||
val shade: Boolean = parent?.shade ?: false
|
||||
) {
|
||||
val logEnabled = false
|
||||
val allCopyActions = mutableSetOf<Action<in FileCopyDetails>>()
|
||||
|
||||
var destination: DistVFile? = parent?.destination // todo: don't nest destination between tasks visiting
|
||||
val logPrefix: String = if (parent != null) "${parent.logPrefix}-" else ""
|
||||
|
||||
init {
|
||||
report?.appendln(toString())
|
||||
if (parent != null) {
|
||||
allCopyActions.addAll(parent.allCopyActions)
|
||||
}
|
||||
}
|
||||
|
||||
fun log(kind: String, title: String = "", print: Boolean = false) {
|
||||
if (logEnabled) {
|
||||
report?.appendln("$logPrefix- $kind $title")
|
||||
if (print) {
|
||||
logger.error("$kind $title, while visiting:")
|
||||
var p = this
|
||||
while (p.parent != null) {
|
||||
logger.error(" - ${p.kind} ${p.title}")
|
||||
p = p.parent!!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun logUnsupported(kind: String, obj: Any? = null) {
|
||||
val objInfo = if (obj != null) {
|
||||
val javaClass = obj.javaClass
|
||||
val superclass = javaClass.superclass as Class<*>
|
||||
"$obj [$javaClass extends $superclass implements ${javaClass.interfaces.map { it.canonicalName }}]"
|
||||
} else ""
|
||||
|
||||
log("UNSUPPORTED $kind", objInfo, true)
|
||||
}
|
||||
|
||||
override fun toString() = "$logPrefix $kind $title"
|
||||
|
||||
inline fun child(
|
||||
kind: String,
|
||||
title: String = "",
|
||||
shade: Boolean = false,
|
||||
body: (DistModelBuildContext) -> Unit = {}
|
||||
): DistModelBuildContext {
|
||||
val result = DistModelBuildContext(this, kind, title, shade = shade)
|
||||
body(result)
|
||||
return result
|
||||
}
|
||||
|
||||
fun addCopyActions(allCopyActions: Collection<Action<in FileCopyDetails>>) {
|
||||
allCopyActions.forEach {
|
||||
log("COPY ACTION", "$it")
|
||||
}
|
||||
|
||||
this.allCopyActions.addAll(allCopyActions)
|
||||
}
|
||||
}
|
||||
|
||||
+314
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.buildUtils.idea
|
||||
|
||||
import IntelliJInstrumentCodeTask
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import idea.DistCopyDetailsMock
|
||||
import idea.DistModelBuildContext
|
||||
import org.codehaus.groovy.runtime.GStringImpl
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.file.FileVisitDetails
|
||||
import org.gradle.api.file.FileVisitor
|
||||
import org.gradle.api.file.SourceDirectorySet
|
||||
import org.gradle.api.internal.file.*
|
||||
import org.gradle.api.internal.file.archive.ZipFileTree
|
||||
import org.gradle.api.internal.file.collections.*
|
||||
import org.gradle.api.internal.file.copy.CopySpecInternal
|
||||
import org.gradle.api.internal.file.copy.DefaultCopySpec
|
||||
import org.gradle.api.internal.file.copy.DestinationRootCopySpec
|
||||
import org.gradle.api.internal.file.copy.SingleParentCopySpec
|
||||
import org.gradle.api.tasks.AbstractCopyTask
|
||||
import org.gradle.api.tasks.Copy
|
||||
import org.gradle.api.tasks.SourceSetOutput
|
||||
import org.gradle.api.tasks.Sync
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask
|
||||
import org.gradle.api.tasks.compile.AbstractCompile
|
||||
import org.gradle.internal.file.PathToFileResolver
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import java.io.File
|
||||
import java.io.PrintWriter
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
open class DistModelBuilder(val rootProject: Project, pw: PrintWriter) {
|
||||
val rootCtx = DistModelBuildContext(null, "ROOT", "dist", pw)
|
||||
val visited = mutableMapOf<Task, DistModelBuildContext>()
|
||||
val vfsRoot = DistVFile(null, "<root>", File(""))
|
||||
val refs = mutableSetOf<DistVFile>()
|
||||
|
||||
fun visitInstrumentTask(it: IntelliJInstrumentCodeTask): DistModelBuildContext = visited.getOrPut(it) {
|
||||
val ctx = rootCtx.child("INSTRUMENT", it.path)
|
||||
ctx.setDest(it.output!!.path)
|
||||
processSourcePath(it.originalClassesDirs, ctx)
|
||||
val dest = ctx.destination
|
||||
if (dest != null) {
|
||||
DistModuleOutput(dest, it.project.path)
|
||||
}
|
||||
|
||||
ctx
|
||||
}
|
||||
|
||||
fun visitCompileTask(it: AbstractCompile): DistModelBuildContext = visited.getOrPut(it) {
|
||||
val ctx = rootCtx.child("COMPILE", it.path)
|
||||
ctx.setDest(it.destinationDir.path)
|
||||
val dest = ctx.destination
|
||||
if (dest != null) DistModuleOutput(dest, it.project.path)
|
||||
else ctx.logUnsupported("Cannot add contents: destination is unknown", it)
|
||||
|
||||
ctx
|
||||
}
|
||||
|
||||
fun visitCopyTask(
|
||||
copy: AbstractCopyTask,
|
||||
shade: Boolean = false
|
||||
): DistModelBuildContext = visited.getOrPut(copy) {
|
||||
val context = rootCtx.child("COPY", copy.path, shade)
|
||||
|
||||
|
||||
val rootSpec = copy.rootSpec
|
||||
|
||||
when (copy) {
|
||||
is Copy -> context.setDest(copy.destinationDir.path)
|
||||
is Sync -> context.setDest(copy.destinationDir.path)
|
||||
is AbstractArchiveTask -> context.setDest(copy.archivePath.path)
|
||||
}
|
||||
|
||||
when (copy) {
|
||||
is ShadowJar -> copy.configurations.forEach {
|
||||
processSourcePath(it, context)
|
||||
}
|
||||
}
|
||||
|
||||
processCopySpec(rootSpec, context)
|
||||
|
||||
|
||||
context
|
||||
}
|
||||
|
||||
fun processCopySpec(spec: CopySpecInternal, ctx: DistModelBuildContext) {
|
||||
spec.children.forEach {
|
||||
when (it) {
|
||||
is DestinationRootCopySpec -> ctx.child("DESTINATION ROOT COPY SPEC") { newCtx ->
|
||||
newCtx.setDest(getRelativePath(it.destinationDir.path))
|
||||
processCopySpec(it, newCtx)
|
||||
}
|
||||
is DefaultCopySpec -> ctx.child("DEFAULT COPY SPEC") { newCtx ->
|
||||
val buildRootResolver = it.buildRootResolver()
|
||||
ctx.addCopyActions(buildRootResolver.allCopyActions)
|
||||
newCtx.setDest(buildRootResolver.destPath.getFile(ctx.destination!!.file).path)
|
||||
processCopySpec(it, newCtx)
|
||||
it.includes
|
||||
|
||||
newCtx.child("SINGE PARENT COPY SPEC") { child ->
|
||||
it.sourcePaths.forEach {
|
||||
processSourcePath(it, child)
|
||||
}
|
||||
}
|
||||
}
|
||||
is SingleParentCopySpec -> ctx.child("OTHER SINGE PARENT COPY SPEC") { child ->
|
||||
it.sourcePaths.forEach {
|
||||
processSourcePath(it, child)
|
||||
}
|
||||
}
|
||||
is CopySpecInternal -> processCopySpec(it, ctx)
|
||||
else -> ctx.logUnsupported("CopySpec", spec)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun processSourcePath(sourcePath: Any?, ctx: DistModelBuildContext) {
|
||||
when {
|
||||
sourcePath == null -> Unit
|
||||
sourcePath is Jar -> ctx.child("JAR") { child ->
|
||||
child.addCopyOf(sourcePath.archivePath.path)
|
||||
}
|
||||
sourcePath is SourceSetOutput -> ctx.child("COMPILE") { child ->
|
||||
sourcePath.classesDirs.files.forEach {
|
||||
child.addCopyOf(it.path)
|
||||
}
|
||||
}
|
||||
sourcePath is Configuration -> {
|
||||
ctx.child("CONFIGURATION") { child ->
|
||||
sourcePath.resolve().forEach {
|
||||
child.addCopyOf(it.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
sourcePath is SourceDirectorySet -> {
|
||||
ctx.child("SOURCES") { child ->
|
||||
sourcePath.srcDirs.forEach {
|
||||
child.addCopyOf(it.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
sourcePath is MinimalFileSet -> ctx.child("MINIMAL FILE SET (${sourcePath.javaClass.simpleName})") { child ->
|
||||
sourcePath.files.forEach {
|
||||
processSourcePath(it, child)
|
||||
}
|
||||
}
|
||||
sourcePath is MinimalFileTree -> ctx.child("MINIMAL FILE TREE (${sourcePath.javaClass.simpleName})") { child ->
|
||||
sourcePath.visit(object : FileVisitor {
|
||||
override fun visitDir(dirDetails: FileVisitDetails) {
|
||||
processSourcePath(dirDetails.file, child)
|
||||
}
|
||||
|
||||
override fun visitFile(fileDetails: FileVisitDetails) {
|
||||
processSourcePath(fileDetails.file, child)
|
||||
}
|
||||
})
|
||||
}
|
||||
sourcePath is FileTreeAdapter && sourcePath.tree is MapFileTree -> ctx.child("FILE TREE ADAPTER OF MAP FILE TREE (${sourcePath.javaClass.simpleName})") { child ->
|
||||
sourcePath.visitContents(object : FileCollectionResolveContext {
|
||||
override fun add(element: Any): FileCollectionResolveContext {
|
||||
processSourcePath(element, child)
|
||||
return this
|
||||
}
|
||||
|
||||
override fun newContext(): ResolvableFileCollectionResolveContext {
|
||||
error("not supported")
|
||||
}
|
||||
|
||||
override fun push(fileResolver: PathToFileResolver): FileCollectionResolveContext {
|
||||
return this
|
||||
}
|
||||
})
|
||||
}
|
||||
sourcePath is CompositeFileCollection -> ctx.child("COMPOSITE FILE COLLECTION") { child ->
|
||||
sourcePath.visitRootElements(object : FileCollectionVisitor {
|
||||
override fun visitDirectoryTree(directoryTree: DirectoryFileTree) {
|
||||
child.child("DIR TREE") {
|
||||
it.addCopyOf(directoryTree.dir.path)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitTree(fileTree: FileTreeInternal) {
|
||||
child.child("TREE") {
|
||||
processSourcePath(fileTree, it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitCollection(fileCollection: FileCollectionInternal) {
|
||||
processSourcePath(fileCollection, child)
|
||||
}
|
||||
})
|
||||
}
|
||||
sourcePath is FileTreeAdapter && sourcePath.tree is ZipFileTree -> ctx.child("ZIP FILE TREE ADAPTER") { child ->
|
||||
val tree = sourcePath.tree
|
||||
val field = tree.javaClass.declaredFields.find { it.name == "zipFile" }!!
|
||||
field.isAccessible = true
|
||||
val zipFile = field.get(tree) as File
|
||||
|
||||
child.addCopyOf(zipFile.path)
|
||||
}
|
||||
sourcePath is FileTreeInternal -> ctx.child("FILE TREE INTERNAL") { child ->
|
||||
// todo: preserve or warn about filtering
|
||||
sourcePath.visitTreeOrBackingFile(object : FileVisitor {
|
||||
override fun visitFile(fileDetails: FileVisitDetails) {
|
||||
child.addCopyOf(fileDetails.file.path)
|
||||
}
|
||||
|
||||
override fun visitDir(dirDetails: FileVisitDetails) {
|
||||
child.addCopyOf(dirDetails.file.path)
|
||||
}
|
||||
})
|
||||
}
|
||||
sourcePath is FileCollection -> ctx.child("OTHER FILE COLLECTION (${sourcePath.javaClass})") { child ->
|
||||
try {
|
||||
sourcePath.files.forEach {
|
||||
child.addCopyOf(it.path)
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
child.logUnsupported("FILE COLLECTION (${t.message})", sourcePath)
|
||||
}
|
||||
}
|
||||
sourcePath is String || sourcePath is GStringImpl -> ctx.child("STRING") { child ->
|
||||
child.addCopyOf(sourcePath.toString())
|
||||
}
|
||||
sourcePath is Callable<*> -> ctx.child("CALLABLE") { child ->
|
||||
processSourcePath(sourcePath.call(), child)
|
||||
}
|
||||
sourcePath is Collection<*> -> ctx.child("COLLECTION") { child ->
|
||||
sourcePath.forEach {
|
||||
processSourcePath(it, child)
|
||||
}
|
||||
}
|
||||
sourcePath is Copy -> ctx.child("COPY OUTPUT") { child ->
|
||||
val src = visitCopyTask(sourcePath).destination
|
||||
if (src != null) child.addCopyOf(src)
|
||||
// else it is added to `it`, because destination is inhereted by context
|
||||
}
|
||||
sourcePath is File -> ctx.child("FILE ${sourcePath.path}") { child ->
|
||||
child.addCopyOf(sourcePath.path)
|
||||
}
|
||||
else -> ctx.logUnsupported("SOURCE PATH", sourcePath)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun DistModelBuildContext.addCopyOf(
|
||||
src: String,
|
||||
body: (src: DistVFile, target: DistVFile) -> Unit = { _, _ -> Unit }
|
||||
) {
|
||||
addCopyOf(requirePath(src), body)
|
||||
}
|
||||
|
||||
fun DistModelBuildContext.transformName(srcName: String): String? {
|
||||
val detailsMock = DistCopyDetailsMock(this, srcName)
|
||||
allCopyActions.forEach {
|
||||
detailsMock.lastAction = it
|
||||
try {
|
||||
it.execute(detailsMock)
|
||||
} catch (t: DistCopyDetailsMock.E) {
|
||||
// skip
|
||||
}
|
||||
}
|
||||
val name1 = detailsMock.relativePath.lastName
|
||||
return if (name1.endsWith(".jar")) transformJarName(name1) else name1
|
||||
}
|
||||
|
||||
// todo: investigate why allCopyActions not working
|
||||
open fun transformJarName(name: String): String = name
|
||||
|
||||
inline fun DistModelBuildContext.addCopyOf(
|
||||
src: DistVFile,
|
||||
body: (src: DistVFile, target: DistVFile) -> Unit = { _, _ -> Unit }
|
||||
) {
|
||||
|
||||
val destination = destination
|
||||
if (destination != null) {
|
||||
body(src, destination)
|
||||
val customTargetName = transformName(src.name)
|
||||
DistCopy(destination, src, customTargetName)
|
||||
log("+DistCopy", "${getRelativePath(src.file.path)} -> ${getRelativePath(destination.file.path)}/$customTargetName")
|
||||
} else logUnsupported("Cannot add copy of `$src`: destination is unknown")
|
||||
}
|
||||
|
||||
fun DistModelBuildContext.setDest(path: String) {
|
||||
destination = vfsRoot.relativePath(path)
|
||||
log("INTO", getRelativePath(path))
|
||||
}
|
||||
|
||||
fun checkRefs() {
|
||||
refs.forEach {
|
||||
if (!it.hasContents && it.contents.isEmpty() && it.file.path.contains("${File.pathSeparator}build${File.pathSeparator}")) {
|
||||
logger.error("UNRESOLVED ${it.file}")
|
||||
it.contents.forEach {
|
||||
logger.error("+ ${it}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getRelativePath(path: String) = path.replace(rootProject.projectDir.path, "$")
|
||||
|
||||
fun requirePath(targetPath: String): DistVFile {
|
||||
val target = vfsRoot.relativePath(targetPath)
|
||||
if (!File(targetPath).exists()) refs.add(target)
|
||||
return target
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.buildUtils.idea
|
||||
|
||||
class DistModelFlattener() {
|
||||
val stack = mutableSetOf<DistVFile>()
|
||||
val common = mutableSetOf<DistVFile>()
|
||||
|
||||
fun DistVFile.flatten(): DistVFile {
|
||||
val new = DistVFile(parent, name, file)
|
||||
copyFlattenedContentsTo(new)
|
||||
return new
|
||||
}
|
||||
|
||||
private fun DistVFile.copyFlattenedContentsTo(new: DistVFile, inJar: Boolean = false) {
|
||||
if (!stack.add(this)) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
contents.forEach {
|
||||
if (!shouldSkip(new, it)) {
|
||||
when (it) {
|
||||
is DistCopy -> {
|
||||
val srcName = it.customTargetName ?: it.src.name
|
||||
if (it.src.file.exists()) {
|
||||
DistCopy(new, it.src, srcName)
|
||||
}
|
||||
|
||||
if (!inJar && srcName.endsWith(".jar")) {
|
||||
val newChild = new.getOrCreateChild(srcName)
|
||||
it.src.copyFlattenedContentsTo(newChild, inJar = true)
|
||||
} else {
|
||||
it.src.copyFlattenedContentsTo(new, inJar)
|
||||
}
|
||||
}
|
||||
is DistModuleOutput -> DistModuleOutput(new, it.projectId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
child.values.forEach { oldChild ->
|
||||
if (inJar) {
|
||||
val newChild =
|
||||
if (oldChild.name.endsWith(".jar")) new
|
||||
else new.getOrCreateChild(oldChild.name)
|
||||
oldChild.copyFlattenedContentsTo(newChild, inJar = true)
|
||||
} else {
|
||||
val newChild = new.getOrCreateChild(oldChild.name)
|
||||
oldChild.copyFlattenedContentsTo(newChild)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
stack.remove(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldSkip(
|
||||
new: DistVFile,
|
||||
content: DistContentElement
|
||||
) =
|
||||
new.name == "kotlin-jps-plugin.jar" && content is DistCopy && content.customTargetName == "kotlin-compiler-runner.jar"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.buildUtils.idea
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.plugins.ide.idea.model.IdeaModel
|
||||
import org.jetbrains.gradle.ext.ArtifactType
|
||||
import org.jetbrains.gradle.ext.RecursiveArtifact
|
||||
|
||||
class DistModelIdeaArtifactBuilder(val rootProject: Project) {
|
||||
fun RecursiveArtifact.addFiles(vFile: DistVFile, inJar: Boolean = false) {
|
||||
val files = mutableSetOf<String>()
|
||||
|
||||
vFile.contents.forEach {
|
||||
when (it) {
|
||||
is DistCopy -> {
|
||||
val file = it.src.file
|
||||
when {
|
||||
inJar && file.name.endsWith(".jar") -> extractedDirectory(file.path)
|
||||
file.isDirectory -> {
|
||||
files.add(file.name)
|
||||
directoryContent(file.path)
|
||||
}
|
||||
else -> {
|
||||
files.add(file.name)
|
||||
file(file.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
is DistModuleOutput -> {
|
||||
val name = it.ideaModuleName
|
||||
|
||||
if (name.result != null) moduleOutput(name.result + "_main")
|
||||
else logger.warn("Cannot find idea module name for project `${it.projectId}`: ${name.error}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vFile.child.values.forEach {
|
||||
if (it.name !in files) {
|
||||
when {
|
||||
it.name.endsWith(".jar") -> archive(it.name).addFiles(it, true)
|
||||
else -> directory(it.name).addFiles(it, inJar)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Result<T: Any>(val result: T? = null, val error: String? = null)
|
||||
|
||||
val DistModuleOutput.ideaModuleName: Result<String>
|
||||
get() {
|
||||
val findProject = rootProject.findProject(projectId) ?: return Result(error = "cannot find gradle project $projectId")
|
||||
val idea = findProject.extensions?.findByName("idea") as? IdeaModel ?: return Result(error = "cannot find idea model for gradle project $projectId")
|
||||
val name = idea.module?.name ?: return Result(error = "idea model for project $projectId has no module name")
|
||||
return Result(name)
|
||||
}
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.buildUtils.idea
|
||||
|
||||
import IntelliJInstrumentCodeTask
|
||||
import org.gradle.api.NamedDomainObjectContainer
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.AbstractCopyTask
|
||||
import org.gradle.api.tasks.compile.AbstractCompile
|
||||
import org.jetbrains.gradle.ext.TopLevelArtifact
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Temporary solution for configuring IDEA artifacts based on Gradle copy tasks configurations.
|
||||
* This should be replaced with DSL that produces both Gradle copy tasks and IDEA artifacts configuration.
|
||||
*
|
||||
* TODO: remove this package when DSL described above will be implemented
|
||||
*/
|
||||
fun generateIdeArtifacts(rootProject: Project, artifactsFactory: NamedDomainObjectContainer<TopLevelArtifact>) {
|
||||
val reportsDir = File(path(rootProject.buildDir.path, "reports", "idea-artifacts-cfg"))
|
||||
reportsDir.mkdirs()
|
||||
val projectDir = rootProject.projectDir
|
||||
|
||||
File(reportsDir, "01-visitor.report.txt").printWriter().use { visitorReport ->
|
||||
val modelBuilder = object : DistModelBuilder(rootProject, visitorReport) {
|
||||
// todo: investigate why allCopyActions not working
|
||||
override fun transformJarName(name: String): String {
|
||||
val name1 = name.replace(Regex("-${java.util.regex.Pattern.quote(rootProject.version.toString())}"), "")
|
||||
|
||||
val name2 = when (name1) {
|
||||
"kotlin-runtime-common.jar" -> "kotlin-runtime.jar"
|
||||
"kotlin-compiler-before-proguard.jar" -> "kotlin-compiler.jar"
|
||||
"kotlin-main-kts-before-proguard.jar" -> "kotlin-main-kts.jar"
|
||||
"kotlin-allopen-compiler-plugin.jar" -> "allopen-compiler-plugin.jar"
|
||||
"kotlin-noarg-compiler-plugin.jar" -> "noarg-compiler-plugin.jar"
|
||||
"kotlin-sam-with-receiver-compiler-plugin.jar" -> "sam-with-receiver-compiler-plugin.jar"
|
||||
"kotlin-android-extensions-runtime.jar" -> "android-extensions-runtime.jar"
|
||||
else -> name1
|
||||
}
|
||||
|
||||
val name3 = name2.removePrefix("dist-")
|
||||
|
||||
return name3
|
||||
}
|
||||
}
|
||||
|
||||
fun visitAllTasks(project: Project) {
|
||||
project.tasks.forEach {
|
||||
try {
|
||||
when {
|
||||
it is AbstractCopyTask -> modelBuilder.visitCopyTask(it)
|
||||
it is AbstractCompile -> modelBuilder.visitCompileTask(it)
|
||||
it is IntelliJInstrumentCodeTask -> modelBuilder.visitInstrumentTask(it)
|
||||
it.name == "stripMetadata" -> {
|
||||
modelBuilder.rootCtx.log(
|
||||
"STRIP METADATA",
|
||||
"${it.inputs.files.singleFile} -> ${it.outputs.files.singleFile}"
|
||||
)
|
||||
|
||||
DistCopy(
|
||||
modelBuilder.requirePath(it.outputs.files.singleFile.path),
|
||||
modelBuilder.requirePath(it.inputs.files.singleFile.path)
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
logger.error("Error while visiting `$it`", t)
|
||||
}
|
||||
}
|
||||
|
||||
project.subprojects.forEach {
|
||||
visitAllTasks(it)
|
||||
}
|
||||
}
|
||||
|
||||
visitAllTasks(rootProject)
|
||||
|
||||
// proguard
|
||||
DistCopy(
|
||||
target = modelBuilder.requirePath(
|
||||
path(
|
||||
projectDir.path,
|
||||
"libraries",
|
||||
"reflect",
|
||||
"build",
|
||||
"libs",
|
||||
"kotlin-reflect-proguard.jar"
|
||||
)
|
||||
),
|
||||
src = modelBuilder.requirePath(path(projectDir.path, "libraries", "reflect", "build", "libs", "kotlin-reflect-shadow.jar"))
|
||||
)
|
||||
|
||||
File(reportsDir, "02-vfs.txt").printWriter().use {
|
||||
modelBuilder.vfsRoot.printTree(it)
|
||||
}
|
||||
modelBuilder.checkRefs()
|
||||
|
||||
with(DistModelFlattener()) {
|
||||
with(DistModelIdeaArtifactBuilder(rootProject)) {
|
||||
File(reportsDir, "03-flattened-vfs.txt").printWriter().use { report ->
|
||||
fun getFlattenned(vfsPath: String): DistVFile =
|
||||
modelBuilder.vfsRoot.relativePath(path(projectDir.path, vfsPath))
|
||||
.flatten()
|
||||
|
||||
val all = getFlattenned("dist")
|
||||
all.child["artifacts"]
|
||||
?.removeAll { it != "ideaPlugin" }
|
||||
all.child["artifacts"]
|
||||
?.child?.get("ideaPlugin")
|
||||
?.child?.get("Kotlin")
|
||||
?.removeAll { it != "kotlinc" && it != "lib" }
|
||||
all.removeAll { it.endsWith(".zip") }
|
||||
all.printTree(report)
|
||||
|
||||
val dist = artifactsFactory.create("dist_auto_reference_dont_use")
|
||||
dist.addFiles(all)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun path(vararg components: String) = components.joinToString(File.separator)
|
||||
|
||||
internal val logger = LoggerFactory.getLogger("ide-artifacts")
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import org.gradle.api.NamedDomainObjectContainer
|
||||
import org.gradle.api.plugins.ExtensionAware
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.plugins.ide.idea.model.IdeaProject
|
||||
@@ -28,3 +29,6 @@ fun DefaultRunConfigurationContainer.junit(name: String, block: JUnit.() -> Unit
|
||||
|
||||
fun DefaultRunConfigurationContainer.application(name: String, block: Application.() -> Unit) =
|
||||
create(name, Application::class.java, block)
|
||||
|
||||
fun ProjectSettings.ideArtifacts(block: NamedDomainObjectContainer<org.jetbrains.gradle.ext.TopLevelArtifact>.() -> Unit) =
|
||||
(this@ideArtifacts as ExtensionAware).extensions.configure("ideArtifacts", block)
|
||||
|
||||
Reference in New Issue
Block a user