diff --git a/settings.gradle b/settings.gradle index abe51da00eb..d30f69357ab 100644 --- a/settings.gradle +++ b/settings.gradle @@ -24,3 +24,4 @@ include ':runtime' include ':common' include ':backend.native:tests' include ':tools:helpers' +include ':tools:gradle-plugin' diff --git a/tools/gradle-plugin/build.gradle b/tools/gradle-plugin/build.gradle new file mode 100644 index 00000000000..8b86039b439 --- /dev/null +++ b/tools/gradle-plugin/build.gradle @@ -0,0 +1,74 @@ +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: "java" +apply plugin: "kotlin" + +// TODO: move this code to top level (the same for backend.native) +// Copied from backend.native +allprojects { + repositories { + maven { + url 'http://oss.sonatype.org/content/repositories/snapshots' + } + } + + configurations.all { + // kotlin-compiler module includes Kotlin runtime bundled; + // make Gradle aware of this to avoid multiple Kotlin runtimes in classpath: + resolutionStrategy.dependencySubstitution { + substitute module('org.jetbrains.kotlin:kotlin-runtime') with module(kotlinCompilerModule) + substitute module('org.jetbrains.kotlin:kotlin-stdlib') with module(kotlinCompilerModule) + substitute module('org.jetbrains.kotlin:kotlin-reflect') with module(kotlinCompilerModule) + } + // TODO: probably we should use kotlin-compiler without bundled runtime + } +} + +repositories { + mavenCentral() +} + +configurations { + konanCompiler +} + +evaluationDependsOn(':tools:helpers') +dependencies { + // TODO: do we need it? + konanCompiler project(':backend.native') + + compile project(':tools:helpers') + compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + compile gradleApi() +} + +jar { + dependsOn(':tools:helpers:jar') + from (rootProject.findProject(':tools:helpers').sourceSets.main.output) +} + +task dist(type: Copy) { + dependsOn(jar) + destinationDir project.file('dist') + from jar.outputs.files +} + +task copy_to_samples(type: Copy) { + dependsOn(dist) + destinationDir rootProject.file('samples') + from dist.outputs.files +} + +clean { + doFirst { + delete project.file('dist') + } +} \ No newline at end of file diff --git a/tools/gradle-plugin/src/main/kotlin/CompilerDownloadTask.kt b/tools/gradle-plugin/src/main/kotlin/CompilerDownloadTask.kt new file mode 100644 index 00000000000..8d1ac58f93f --- /dev/null +++ b/tools/gradle-plugin/src/main/kotlin/CompilerDownloadTask.kt @@ -0,0 +1,42 @@ +package org.jetbrains.kotlin.gradle.plugin + +import org.gradle.api.DefaultTask +import org.gradle.api.GradleScriptException +import org.gradle.api.tasks.TaskAction +import org.jetbrains.kotlin.konan.DependencyDownloader +import java.io.File + +open class CompilerDownloadTask: DefaultTask() { + + internal companion object { + internal const val DOWNLOAD_URL = "http://download.jetbrains.com/kotlin/native" + + internal val KONAN_NAME = "kotlin-native-${simpleOsName()}-${KonanPlugin.KONAN_VERSION}" + private val KONAN_PARENT_DIR = "${System.getProperty("user.home")}/.konan" + + internal fun simpleOsName(): String { + val osName = System.getProperty("os.name") + return when (osName) { + "Mac OS X" -> "macos" + "Linux" -> "linux" + else -> throw IllegalStateException("Unsupported platform: $osName") + } + } + } + + @TaskAction + fun downloadAndExtract() { + if (project.hasProperty(KonanPlugin.KONAN_HOME_PROPERTY_NAME)) { + val konanHome = project.property(KonanPlugin.KONAN_HOME_PROPERTY_NAME) + logger.info("Use a user-defined compiler path: $konanHome") + return + } + try { + logger.info("Downloading Kotlin Native compiler from $DOWNLOAD_URL into $KONAN_PARENT_DIR") + DependencyDownloader(File(KONAN_PARENT_DIR), DOWNLOAD_URL, listOf(KONAN_NAME)).run() + project.extensions.extraProperties.set(KonanPlugin.KONAN_HOME_PROPERTY_NAME, "$KONAN_PARENT_DIR/$KONAN_NAME") + } catch (e: RuntimeException) { + throw GradleScriptException("Cannot download Kotlin Native compiler", e) + } + } +} \ No newline at end of file diff --git a/tools/gradle-plugin/src/main/kotlin/KonanCompilerConfig.kt b/tools/gradle-plugin/src/main/kotlin/KonanCompilerConfig.kt new file mode 100644 index 00000000000..2b6192c1e1e --- /dev/null +++ b/tools/gradle-plugin/src/main/kotlin/KonanCompilerConfig.kt @@ -0,0 +1,203 @@ +package org.jetbrains.kotlin.gradle.plugin + +import org.gradle.api.Named +import org.gradle.api.file.FileCollection +import org.gradle.api.internal.project.ProjectInternal +import java.io.File + +/** + * We can the following: + * + * konanArtifacts { + * + * artifactName1 { + * + * inputDir "path/to/files" + * inputFiles "files" "to" "be" "compiled" + * + * outputDir "path/to/output/dir" + * + * library "path/to/library" + * library File("Library") + * + * nativeLibrary "path/to/library" + * nativeLibrary File("Library") + * + * noStdLib + * noLink + * enableOptimization + * + * linkerOpts "linker" "args" + * target "target" + * + * languageVersion "version" + * apiVersion "version" + * + * } + * + * artifactName2 { + * + * extends artifactName1 + * + * inputDir "someDir" + * outputDir "someDir" + * } + * + * } + + */ +// TODO: check debug outputs +// TODO: Use +=/-= syntax for libraries and inputFiles +open class KonanCompilerConfig( + val configName: String, + val project: ProjectInternal, + taskNamePrefix: String = "compileKonan"): Named { + + override fun getName() = configName + + val compilationTask: KonanCompileTask = project.tasks.create( + "$taskNamePrefix${configName.capitalize()}", + KonanCompileTask::class.java + ) { it.initialize(this@KonanCompilerConfig.name) } + + protected fun String.toAbsolutePath() = project.file(this).canonicalPath + + // DSL methods -------------------------------------------------- + + // TODO: Check if we copied all data or not + fun extendsFrom(anotherConfig: KonanCompilerConfig) = with(compilationTask) { + val anotherTask = anotherConfig.compilationTask + + outputDir(anotherTask.outputDir.absolutePath) + + linkerOpts(anotherTask.linkerOpts) + + anotherTask.target?.let { target(it) } + anotherTask.languageVersion?.let { languageVersion(it) } + anotherTask.apiVersion?.let { apiVersion(it) } + + if (anotherTask.noStdLib) noStdLib() + if (anotherTask.noLink) noLink() + if (anotherTask.noMain) noMain() + if (anotherTask.enableOptimization) enableOptimization() + if (anotherTask.enableAssertions) enableAssertions() + } + + fun useInterop(interopConfig: KonanInteropConfig) { + var generateStubsTask = interopConfig.generateStubsTask + var compileStubsTask = interopConfig.compileStubsTask + + compilationTask.dependsOn(compileStubsTask) + compilationTask.dependsOn(generateStubsTask) + + linkerOpts(generateStubsTask.linkerOpts) + libraries(project.fileTree(compileStubsTask.outputDir).apply { + builtBy(compileStubsTask) + include("**/*.bc") + }) + nativeLibraries(project.fileTree(generateStubsTask.libsDir).apply { + builtBy(generateStubsTask) + include("**/*.bc") + }) + } + fun useInterop(interop: String) = useInterop(project.konanInteropContainer.getByName(interop)) + + // DSL. Input/output files + + fun inputDir(dir: String) = with(compilationTask) { + inputFiles.add(project.fileTree(dir)) + } + fun inputFiles(vararg files: Any) = with(compilationTask) { + inputFiles.add(project.files(files)) + } + fun inputFiles(files: FileCollection) = compilationTask.inputFiles.add(files) + fun inputFiles(files: Collection) = compilationTask.inputFiles.addAll(files) + + + fun outputDir(dir: Any) = with(compilationTask) { + outputDir = project.file(dir) + } + + // DSL. Libraries. + + fun library(lib: Any) = libraries(lib) + fun libraries(vararg libs: Any) = with(compilationTask) { + libraries.add(project.files(*libs)) + } + fun libraries(libs: FileCollection) = with(compilationTask) { + libraries.add(libs) + } + + fun nativeLibrary(lib: Any) = nativeLibraries(lib) + fun nativeLibraries(vararg libs: Any) = with(compilationTask) { + nativeLibraries.add(project.files(*libs)) + } + fun nativeLibraries(libs: FileCollection) = with(compilationTask) { + nativeLibraries.add(libs) + } + + // DSL. Other parameters. + + fun linkerOpts(args: List) = linkerOpts(*args.toTypedArray()) + fun linkerOpts(vararg args: String) = with(compilationTask) { + linkerOpts.addAll(args) + } + + fun target(tgt: String) = with(compilationTask) { + target = tgt + } + + fun languageVersion(version: String) = with(compilationTask) { + languageVersion = version + } + + fun apiVersion(version: String) = with(compilationTask) { + apiVersion = version + } + + fun noStdLib() = with(compilationTask) { + noStdLib = true + } + + fun noLink() = with(compilationTask) { + noLink = true + } + + fun noMain() = with(compilationTask) { + noMain = true + } + + fun enableOptimization() = with(compilationTask) { + enableOptimization = true + } + + fun enableAssertions() = with(compilationTask) { + enableAssertions = true + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/gradle-plugin/src/main/kotlin/KonanCompilerTask.kt b/tools/gradle-plugin/src/main/kotlin/KonanCompilerTask.kt new file mode 100644 index 00000000000..cece69fd793 --- /dev/null +++ b/tools/gradle-plugin/src/main/kotlin/KonanCompilerTask.kt @@ -0,0 +1,142 @@ +package org.jetbrains.kotlin.gradle.plugin + +import org.gradle.api.DefaultTask +import org.gradle.api.file.FileCollection +import org.gradle.api.tasks.* +import java.io.File + +// TODO: form groups for tasks +// TODO: Make the task class nested for config with properties accessible for outer users. +open class KonanCompileTask: DefaultTask() { + + companion object { + const val COMPILER_MAIN = "org.jetbrains.kotlin.cli.bc.K2NativeKt" + } + + val COMPILER_JVM_ARGS: List + get() = listOf("-Dkonan.home=${project.konanHome}", "-Djava.library.path=${project.konanHome}/konan/nativelib") + val COMPILER_CLASSPATH: String + get() = "${project.konanHome}/konan/lib/" + + // Output artifact -------------------------------------------------------- + + protected lateinit var artifactName: String + + @OutputDirectory + lateinit var outputDir: File + internal set + + internal fun initialize(artifactName: String) { + dependsOn(project.konanCompilerDownloadTask) + this.artifactName = artifactName + outputDir = project.file("${project.konanCompilerOutputDir}/$artifactName") + } + + val artifactPath: String + get() = "${outputDir.absolutePath}/$artifactName.${ if (noLink) "bc" else "kexe" }" + + // Other compilation parameters ------------------------------------------- + + @InputFiles var inputFiles = mutableSetOf() + internal set + + @InputFiles var libraries = mutableSetOf() + internal set + @InputFiles var nativeLibraries = mutableSetOf() + internal set + + @Input var linkerOpts = mutableListOf() + internal set + + @Input var noStdLib = false + internal set + @Input var noLink = false + internal set + @Input var noMain = false + internal set + @Input var enableOptimization = false + internal set + @Input var enableAssertions = false + internal set + + @Optional @Input var target : String? = null + internal set + @Optional @Input var languageVersion : String? = null + internal set + @Optional @Input var apiVersion : String? = null + internal set + + // Useful extensions and functions --------------------------------------- + + protected fun MutableList.addArg(parameter: String, value: String) { + add(parameter) + add(value) + } + + protected fun MutableList.addArgIfNotNull(parameter: String, value: String?) { + if (value != null) { + addArg(parameter, value) + } + } + + protected fun MutableList.addKey(key: String, enabled: Boolean) { + if (enabled) { + add(key) + } + } + + protected fun MutableList.addFileArgs(parameter: String, values: FileCollection) { + values.files.forEach { + addArg(parameter, it.canonicalPath) + } + } + + protected fun MutableList.addFileArgs(parameter: String, values: Collection) { + values.forEach { + addFileArgs(parameter, it) + } + } + + // Task action ------------------------------------------------------------ + + protected fun buildArgs() = mutableListOf().apply { + addArg("-output", artifactPath) + + addFileArgs("-library", libraries) + addFileArgs("-nativelibrary", nativeLibraries) + + linkerOpts.forEach { + addArg("-linkerArgs", it) + } + + addArgIfNotNull("-target", target) + addArgIfNotNull("-language-version", languageVersion) + addArgIfNotNull("-api-version", apiVersion) + + addKey("-nostdlib", noStdLib) + addKey("-nolink", noLink) + addKey("-nomain", noMain) + addKey("-opt", enableOptimization) + addKey("-ea", enableAssertions) + + inputFiles.forEach { + it.files.filter { it.name.endsWith(".kt") }.mapTo(this) { it.canonicalPath } + } + } + + @TaskAction + fun compile() { + project.file(outputDir).mkdirs() + + // TODO: Use compiler service. + project.javaexec { + with(it) { + main = COMPILER_MAIN + classpath = project.fileTree(COMPILER_CLASSPATH).apply { include("*.jar") } + jvmArgs(COMPILER_JVM_ARGS) + args(buildArgs()) + } + } + } + +} diff --git a/tools/gradle-plugin/src/main/kotlin/KonanInteropConfig.kt b/tools/gradle-plugin/src/main/kotlin/KonanInteropConfig.kt new file mode 100644 index 00000000000..14b8ad66504 --- /dev/null +++ b/tools/gradle-plugin/src/main/kotlin/KonanInteropConfig.kt @@ -0,0 +1,99 @@ +package org.jetbrains.kotlin.gradle.plugin + +import org.gradle.api.Named +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.file.FileCollection +import org.gradle.api.internal.project.ProjectInternal + +/** + * What we can: + * + * konanInterop { + * foo { + * defFile + * pkg + * target + * compilerOpts + * linkerOpts + * headers + * includeDirs + * linkFiles + * } + * + * // TODO: add configuration for konan compiler + * } + */ + +open class KonanInteropConfig( + val configName: String, + val project: ProjectInternal +): Named { + + override fun getName() = configName + + // Child tasks ------------------------------------------------------------ + + // Task to process the library and generate stubs + val generateStubsTask: KonanInteropTask = project.tasks.create( + "gen${name.capitalize()}InteropStubs", + KonanInteropTask::class.java + ) + + // Config and task to compile *.kt stubs in a *.bc library + internal val compileStubsConfig = KonanCompilerConfig("${name}InteropStubs", project, "compile").apply { + compilationTask.dependsOn(generateStubsTask) + outputDir(generateStubsTask.stubsDir.absolutePath) + noLink() + inputFiles(project.fileTree(generateStubsTask.stubsDir).apply { builtBy(generateStubsTask) }) + } + val compileStubsTask = compileStubsConfig.compilationTask + + // DSL methods ------------------------------------------------------------ + + fun defFile(file: Any) = with(generateStubsTask) { + defFile = project.file(file) + } + + fun pkg(value: String) = with(generateStubsTask) { + pkg = value + } + + fun target(value: String) = with(generateStubsTask) { + target = value + } + + fun compilerOpts(vararg values: String) = with(generateStubsTask) { + compilerOpts.addAll(values) + } + + fun header(file: Any) = headers(file) + fun headers(vararg files: Any) = with(generateStubsTask) { + headers.add(project.files(files)) + } + fun headers(files: FileCollection) = with(generateStubsTask) { + headers.add(files) + } + + + fun includeDirs(vararg values: String) = with(generateStubsTask) { + compilerOpts.addAll(values.map { "-I$it" }) + } + + fun linker(value: String) = with(generateStubsTask) { + linker = value + } + + fun linkerOpts(vararg values: String) = linkerOpts(values.toList()) + fun linkerOpts(values: List) = with(generateStubsTask) { + linkerOpts.addAll(values) + } + + fun link(vararg files: Any) = with(generateStubsTask) { + linkFiles.add(project.files(files)) + } + fun link(files: FileCollection) = with(generateStubsTask) { + linkFiles.add(files) + } + +} diff --git a/tools/gradle-plugin/src/main/kotlin/KonanInteropTask.kt b/tools/gradle-plugin/src/main/kotlin/KonanInteropTask.kt new file mode 100644 index 00000000000..01ed823fc94 --- /dev/null +++ b/tools/gradle-plugin/src/main/kotlin/KonanInteropTask.kt @@ -0,0 +1,102 @@ +package org.jetbrains.kotlin.gradle.plugin + +import org.gradle.api.DefaultTask +import org.gradle.api.Task +import org.gradle.api.file.FileCollection +import org.gradle.api.tasks.* +import java.io.File + + +open class KonanInteropTask: DefaultTask() { + + internal companion object { + const val INTEROP_MAIN = "org.jetbrains.kotlin.native.interop.gen.jvm.MainKt" + } + + init { + dependsOn(project.konanCompilerDownloadTask) + } + + internal val INTEROP_JVM_ARGS: List + get() = listOf("-Dkonan.home=${project.konanHome}", "-Djava.library.path=${project.konanHome}/konan/nativelib") + internal val INTEROP_CLASSPATH: String + get() = "${project.konanHome}/konan/lib/" + + + // Output directories ----------------------------------------------------- + + /** Directory with autogenerated interop stubs (*.kt) */ + @OutputDirectory + val stubsDir = project.file("${project.konanInteropStubsOutputDir}/$name") + + /** Directory with library bitcodes (*.bc) */ + @OutputDirectory + val libsDir = project.file("${project.konanInteropLibsOutputDir}/$name") + + // Interop stub generator parameters ------------------------------------- + + @Optional @InputFile var defFile: File? = null + internal set + @Optional @Input var target: String? = null + internal set + @Optional @Input var pkg: String? = null + internal set + @Optional @Input var linker: String? = null + internal set + + @Input var compilerOpts = mutableListOf() + internal set + @Input var linkerOpts = mutableListOf() + internal set + + // TODO: Check if we can use only one FileCollection instead of set. + @InputFiles var headers = mutableSetOf() + internal set + @InputFiles var linkFiles = mutableSetOf() + internal set + + @TaskAction + fun exec() { + project.javaexec { + with(it) { + main = INTEROP_MAIN + classpath = project.fileTree(INTEROP_CLASSPATH).apply { include("*.jar") } + jvmArgs(INTEROP_JVM_ARGS) + environment("LIBCLANG_DISABLE_CRASH_RECOVERY", "1") + environment("LD_LIBRARY_PATH", "${project.konanHome}/nativelib") + environment("DYLD_LIBRARY_PATH", "${project.konanHome}/nativelib") + + args(buildArgs()) + } + } + } + + protected fun buildArgs() = mutableListOf().apply { + add("-properties:${project.konanHome}/konan/konan.properties") + add("-flavor:native") + add("-generated:$stubsDir") + add("-natives:$libsDir") + + target?.let { add("-target:$target") } + defFile?.let { add("-def:$it") } + pkg?.let { add("-pkg:$it") } + linker?.let { add("-linker:$it") } + + headers.forEach { + add("-h:$it") + } + + compilerOpts.forEach { + add("-copt:$it") + } + + val linkerOpts = mutableListOf().apply { addAll(linkerOpts) } + linkFiles.forEach { + linkerOpts.addAll(it.files.map { it.canonicalPath }) + } + linkerOpts.forEach { + add("-lopt:$it") + } + } + +} diff --git a/tools/gradle-plugin/src/main/kotlin/KonanPlugin.kt b/tools/gradle-plugin/src/main/kotlin/KonanPlugin.kt new file mode 100644 index 00000000000..dac9d2fa084 --- /dev/null +++ b/tools/gradle-plugin/src/main/kotlin/KonanPlugin.kt @@ -0,0 +1,92 @@ +package org.jetbrains.kotlin.gradle.plugin + +import org.gradle.api.DefaultTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.internal.AbstractNamedDomainObjectContainer +import org.gradle.api.internal.project.ProjectInternal +import org.gradle.internal.reflect.Instantiator + +/** + * We use the follwing properties: + * kotlin.native.home - directory where compiler is located (aka dist in konan project output) + */ + +// konanHome extension is set by downloadKonanCompiler task. +internal val Project.konanHome: String + get() { + assert(extensions.extraProperties.has(KonanPlugin.KONAN_HOME_PROPERTY_NAME)) + return extensions.extraProperties.get(KonanPlugin.KONAN_HOME_PROPERTY_NAME).toString() + } + +internal val Project.konanBuildRoot get() = "${buildDir.canonicalPath}/konan" +internal val Project.konanCompilerOutputDir get() = "${konanBuildRoot}/bin" +internal val Project.konanInteropStubsOutputDir get() = "${konanBuildRoot}/interopStubs" +internal val Project.konanInteropLibsOutputDir get() = "${konanBuildRoot}/nativelibs" + +internal val Project.konanArtifactsContainer: KonanArtifactsContainer + get() = extensions.getByName(KonanPlugin.COMPILER_EXTENSION_NAME) as KonanArtifactsContainer +internal val Project.konanInteropContainer: KonanInteropContainer + get() = extensions.getByName(KonanPlugin.INTEROP_EXTENSION_NAME) as KonanInteropContainer + +internal val Project.konanCompilerDownloadTask get() = tasks.getByName(KonanPlugin.KONAN_DOWNLOAD_TASK_NAME) + +class KonanArtifactsContainer(val project: ProjectInternal): AbstractNamedDomainObjectContainer( + KonanCompilerConfig::class.java, + project.gradle.services.get(Instantiator::class.java)) { + + override fun doCreate(name: String): KonanCompilerConfig = + KonanCompilerConfig(name, project) +} + +class KonanInteropContainer(val project: ProjectInternal): AbstractNamedDomainObjectContainer( + KonanInteropConfig::class.java, + project.gradle.services.get(Instantiator::class.java)) { + + override fun doCreate(name: String): KonanInteropConfig = + KonanInteropConfig(name, project) +} + +class KonanPlugin: Plugin { + + companion object { + internal const val COMPILER_EXTENSION_NAME = "konanArtifacts" + internal const val INTEROP_EXTENSION_NAME = "konanInterop" + internal const val KONAN_HOME_PROPERTY_NAME = "kotlin.native.home" + internal const val KONAN_DOWNLOAD_TASK_NAME = "downloadKonanCompiler" + + // TODO: Move in config. + internal const val KONAN_VERSION = "0.1" + } + + /** + * Looks for task with given name in the given project. + * If such task isn't found, will create it. Returns created/found task. + */ + private fun getTask(project: ProjectInternal, name: String): Task { + var tasks = project.getTasksByName(name, false) + assert(tasks.size <= 1) + return if (tasks.isEmpty()) { + project.tasks.create(name, DefaultTask::class.java) + } else { + tasks.single() + } + } + + // TODO: Create default config? what about test sources? + override fun apply(project: ProjectInternal?) { + if (project == null) { return } + project.tasks.create(KONAN_DOWNLOAD_TASK_NAME, CompilerDownloadTask::class.java) + project.extensions.add(COMPILER_EXTENSION_NAME, KonanArtifactsContainer(project)) + project.extensions.add(INTEROP_EXTENSION_NAME, KonanInteropContainer(project)) + getTask(project, "clean").doLast { + project.delete(project.konanBuildRoot) + } + getTask(project, "build").apply { + dependsOn(project.tasks.withType(KonanCompileTask::class.java)) + dependsOn(project.tasks.withType(KonanInteropTask::class.java)) + } + } + +} diff --git a/tools/gradle-plugin/src/main/resources/META-INF/gradle-plugins/konan.properties b/tools/gradle-plugin/src/main/resources/META-INF/gradle-plugins/konan.properties new file mode 100644 index 00000000000..368d15f832e --- /dev/null +++ b/tools/gradle-plugin/src/main/resources/META-INF/gradle-plugins/konan.properties @@ -0,0 +1,2 @@ +# TODO: find a good name for the plugin +implementation-class=org.jetbrains.kotlin.gradle.plugin.KonanPlugin \ No newline at end of file diff --git a/tools/gradle-plugin/test-project/test-project.iml b/tools/gradle-plugin/test-project/test-project.iml deleted file mode 100644 index bcd22c033ca..00000000000 --- a/tools/gradle-plugin/test-project/test-project.iml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - -