diff --git a/compiler/incremental-compilation-impl/incremental-compilation-impl.iml b/compiler/incremental-compilation-impl/incremental-compilation-impl.iml
index c317050d542..c6d4241d727 100644
--- a/compiler/incremental-compilation-impl/incremental-compilation-impl.iml
+++ b/compiler/incremental-compilation-impl/incremental-compilation-impl.iml
@@ -21,5 +21,6 @@
+
\ No newline at end of file
diff --git a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalJvmCompilerRunnerTest.kt b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalJvmCompilerRunnerTest.kt
index 24cf6f980f1..b219f00704f 100644
--- a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalJvmCompilerRunnerTest.kt
+++ b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/AbstractIncrementalJvmCompilerRunnerTest.kt
@@ -16,24 +16,66 @@
package org.jetbrains.kotlin.incremental
+import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.incremental.utils.TestCompilationResult
import org.jetbrains.kotlin.incremental.utils.TestICReporter
import org.jetbrains.kotlin.incremental.utils.TestMessageCollector
+import java.io.ByteArrayOutputStream
import java.io.File
+import javax.tools.ToolProvider
abstract class AbstractIncrementalJvmCompilerRunnerTest : AbstractIncrementalCompilerRunnerTestBase() {
override fun make(cacheDir: File, sourceRoots: Iterable, args: K2JVMCompilerArguments): TestCompilationResult {
val reporter = TestICReporter()
val messageCollector = TestMessageCollector()
makeIncrementally(cacheDir, sourceRoots, args, reporter = reporter, messageCollector = messageCollector)
- return TestCompilationResult(reporter, messageCollector)
+ val kotlinCompileResult = TestCompilationResult(reporter, messageCollector)
+ if (kotlinCompileResult.exitCode != ExitCode.OK) return kotlinCompileResult
+
+ val (javaExitCode, _, javaErrors) = compileJava(sourceRoots, args.destination!!)
+ return when (javaExitCode) {
+ ExitCode.OK -> kotlinCompileResult
+ else -> kotlinCompileResult.copy(exitCode = javaExitCode, compileErrors = javaErrors)
+ }
+ }
+
+ private fun compileJava(sourceRoots: Iterable, kotlinClassesPath: String): TestCompilationResult {
+ val javaSources = arrayListOf()
+ for (root in sourceRoots) {
+ javaSources.addAll(root.walk().filter { it.isFile && it.extension == "java" })
+ }
+ if (javaSources.isEmpty()) return TestCompilationResult(ExitCode.OK, emptyList(), emptyList())
+
+ val javaClasspath = compileClasspath + File.pathSeparator + kotlinClassesPath
+ val javaDestinationDir = File(workingDir, "java-classes").apply {
+ if (exists()) {
+ deleteRecursively()
+ }
+ mkdirs()
+ }
+ val args = arrayOf("-cp", javaClasspath,
+ "-d", javaDestinationDir.canonicalPath,
+ *javaSources.map { it.canonicalPath }.toTypedArray())
+
+ val err = ByteArrayOutputStream()
+ val javac = ToolProvider.getSystemJavaCompiler()
+ val rc = javac.run(null, null, err, *args)
+
+ val exitCode = if (rc == 0) ExitCode.OK else ExitCode.COMPILATION_ERROR
+ val errors = err.toString().split("\n")
+ return TestCompilationResult(exitCode, javaSources, errors)
}
override fun createCompilerArguments(destinationDir: File, testDir: File): K2JVMCompilerArguments =
K2JVMCompilerArguments().apply {
moduleName = testDir.name
destination = destinationDir.path
- classpathAsList = listOf(File(bootstrapKotlincLib, "kotlin-stdlib.jar"))
+ classpath = compileClasspath
}
+
+ private val compileClasspath =
+ listOf(File(bootstrapKotlincLib, "kotlin-stdlib.jar"))
+ .map { it.canonicalPath }
+ .joinToString(File.pathSeparator)
}
\ No newline at end of file
diff --git a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunnerTestGenerated.java b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunnerTestGenerated.java
index d26babc592b..38a966140fe 100644
--- a/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunnerTestGenerated.java
+++ b/compiler/incremental-compilation-impl/test/org/jetbrains/kotlin/incremental/IncrementalJvmCompilerRunnerTestGenerated.java
@@ -941,4 +941,247 @@ public class IncrementalJvmCompilerRunnerTestGenerated extends AbstractIncrement
doTest(fileName);
}
}
+
+ @TestMetadata("jps-plugin/testData/incremental/withJava")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class WithJava extends AbstractIncrementalJvmCompilerRunnerTest {
+ public void testAllFilesPresentInWithJava() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/incremental/withJava"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class ConvertBetweenJavaAndKotlin extends AbstractIncrementalJvmCompilerRunnerTest {
+ public void testAllFilesPresentInConvertBetweenJavaAndKotlin() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("javaToKotlin")
+ public void testJavaToKotlin() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin/javaToKotlin/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("javaToKotlinAndBack")
+ public void testJavaToKotlinAndBack() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin/javaToKotlinAndBack/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("javaToKotlinAndRemove")
+ public void testJavaToKotlinAndRemove() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin/javaToKotlinAndRemove/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("kotlinToJava")
+ public void testKotlinToJava() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin/kotlinToJava/");
+ doTest(fileName);
+ }
+ }
+
+ @TestMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class JavaUsedInKotlin extends AbstractIncrementalJvmCompilerRunnerTest {
+ public void testAllFilesPresentInJavaUsedInKotlin() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/incremental/withJava/javaUsedInKotlin"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("changeNotUsedSignature")
+ public void testChangeNotUsedSignature() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/changeNotUsedSignature/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("changeSignature")
+ public void testChangeSignature() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/changeSignature/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("constantChanged")
+ public void testConstantChanged() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/constantChanged/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("constantUnchanged")
+ public void testConstantUnchanged() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/constantUnchanged/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("enumEntryAdded")
+ public void testEnumEntryAdded() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryAdded/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("enumEntryRemoved")
+ public void testEnumEntryRemoved() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryRemoved/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("javaAndKotlinChangedSimultaneously")
+ public void testJavaAndKotlinChangedSimultaneously() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaAndKotlinChangedSimultaneously/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("javaFieldNullabilityChanged")
+ public void testJavaFieldNullabilityChanged() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaFieldNullabilityChanged/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("javaMethodParamNullabilityChanged")
+ public void testJavaMethodParamNullabilityChanged() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaMethodParamNullabilityChanged/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("javaMethodReturnTypeNullabilityChanged")
+ public void testJavaMethodReturnTypeNullabilityChanged() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaMethodReturnTypeNullabilityChanged/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("methodAddedInSuper")
+ public void testMethodAddedInSuper() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/methodAddedInSuper/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("methodRenamed")
+ public void testMethodRenamed() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/methodRenamed/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("notChangeSignature")
+ public void testNotChangeSignature() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/notChangeSignature/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/samConversions")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class SamConversions extends AbstractIncrementalJvmCompilerRunnerTest {
+ public void testAllFilesPresentInSamConversions() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/samConversions"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("methodAdded")
+ public void testMethodAdded() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/samConversions/methodAdded/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("methodSignatureChanged")
+ public void testMethodSignatureChanged() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/javaUsedInKotlin/samConversions/methodSignatureChanged/");
+ doTest(fileName);
+ }
+ }
+ }
+
+ @TestMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class KotlinUsedInJava extends AbstractIncrementalJvmCompilerRunnerTest {
+ @TestMetadata("addOptionalParameter")
+ public void testAddOptionalParameter() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/addOptionalParameter/");
+ doTest(fileName);
+ }
+
+ public void testAllFilesPresentInKotlinUsedInJava() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("jps-plugin/testData/incremental/withJava/kotlinUsedInJava"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("changeNotUsedSignature")
+ public void testChangeNotUsedSignature() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/changeNotUsedSignature/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("changeSignature")
+ public void testChangeSignature() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/changeSignature/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("constantChanged")
+ public void testConstantChanged() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/constantChanged/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("constantUnchanged")
+ public void testConstantUnchanged() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/constantUnchanged/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("funRenamed")
+ public void testFunRenamed() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/funRenamed/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("jvmFieldChanged")
+ public void testJvmFieldChanged() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/jvmFieldChanged/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("jvmFieldUnchanged")
+ public void testJvmFieldUnchanged() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/jvmFieldUnchanged/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("methodAddedInSuper")
+ public void testMethodAddedInSuper() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/methodAddedInSuper/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("notChangeSignature")
+ public void testNotChangeSignature() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/notChangeSignature/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("onlyTopLevelFunctionInFileRemoved")
+ public void testOnlyTopLevelFunctionInFileRemoved() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/onlyTopLevelFunctionInFileRemoved/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("packageFileAdded")
+ public void testPackageFileAdded() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/packageFileAdded/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("privateChanges")
+ public void testPrivateChanges() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/privateChanges/");
+ doTest(fileName);
+ }
+
+ @TestMetadata("propertyRenamed")
+ public void testPropertyRenamed() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("jps-plugin/testData/incremental/withJava/kotlinUsedInJava/propertyRenamed/");
+ doTest(fileName);
+ }
+ }
+ }
}
diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt
index 1925152c62a..96143ada344 100755
--- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt
+++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt
@@ -1250,6 +1250,7 @@ fun main(args: Array) {
testClass {
model("incremental/pureKotlin", extension = null, recursive = false)
model("incremental/inlineFunCallSite", extension = null, excludeParentDirs = true)
+ model("incremental/withJava", extension = null, excludeParentDirs = true)
}
testClass {
diff --git a/jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin/javaToKotlinAndRemove/gradle-build.log b/jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin/javaToKotlinAndRemove/gradle-build.log
new file mode 100644
index 00000000000..b43d089d40e
--- /dev/null
+++ b/jps-plugin/testData/incremental/withJava/convertBetweenJavaAndKotlin/javaToKotlinAndRemove/gradle-build.log
@@ -0,0 +1,14 @@
+================ Step #1 =================
+Compiling files:
+ src/TheClass.kt
+ src/other.kt
+End of files
+Exit code: OK
+------------------------------------------
+
+================ Step #2 =================
+
+Compiling files:
+End of files
+Exit code: OK
+------------------------------------------
diff --git a/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/constantUnchanged/gradle-build.log b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/constantUnchanged/gradle-build.log
new file mode 100644
index 00000000000..6d26dbae105
--- /dev/null
+++ b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/constantUnchanged/gradle-build.log
@@ -0,0 +1,7 @@
+================ Step #1 =================
+
+Compiling files:
+ src/JavaClass.java
+ src/usage.kt
+End of files
+Exit code: OK
diff --git a/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryAdded/gradle-build.log b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryAdded/gradle-build.log
index 6d78e93fdff..739c085f5d9 100644
--- a/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryAdded/gradle-build.log
+++ b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryAdded/gradle-build.log
@@ -1,35 +1,22 @@
================ Step #1 =================
-Cleaning output files:
- out/production/module/Enum.class
-End of files
-Exit code: NOTHING_DONE
-------------------------------------------
Compiling files:
src/Enum.java
-End of files
-Cleaning output files:
- out/production/module/META-INF/module.kotlin_module
- out/production/module/UseKt.class
-End of files
-Compiling files:
+ src/getRandomEnumEntry.kt
src/use.kt
+ src/useEnumImplicitly.kt
End of files
Exit code: ABORT
------------------------------------------
COMPILATION FAILED
-When expression must be exhaustive, add necessary 'C' branch or 'else' branch instead
+'when' expression must be exhaustive, add necessary 'C' branch or 'else' branch instead
================ Step #2 =================
-Cleaning output files:
- out/production/module/Enum.class
-End of files
-Compiling files:
- src/use.kt
-End of files
-Exit code: OK
-------------------------------------------
Compiling files:
src/Enum.java
-End of files
\ No newline at end of file
+ src/getRandomEnumEntry.kt
+ src/use.kt
+ src/useEnumImplicitly.kt
+End of files
+Exit code: OK
\ No newline at end of file
diff --git a/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryRemoved/gradle-build.log b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryRemoved/gradle-build.log
new file mode 100644
index 00000000000..b0607ab07a5
--- /dev/null
+++ b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/enumEntryRemoved/gradle-build.log
@@ -0,0 +1,22 @@
+================ Step #1 =================
+
+Compiling files:
+ src/Enum.java
+ src/getRandomEnumEntry.kt
+ src/use.kt
+ src/useEnumImplicitly.kt
+End of files
+Exit code: ABORT
+------------------------------------------
+COMPILATION FAILED
+Unresolved reference: C
+
+================ Step #2 =================
+
+Compiling files:
+ src/Enum.java
+ src/getRandomEnumEntry.kt
+ src/use.kt
+ src/useEnumImplicitly.kt
+End of files
+Exit code: OK
\ No newline at end of file
diff --git a/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaFieldNullabilityChanged/gradle-build.log b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaFieldNullabilityChanged/gradle-build.log
new file mode 100644
index 00000000000..5ada4a700d5
--- /dev/null
+++ b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/javaFieldNullabilityChanged/gradle-build.log
@@ -0,0 +1,7 @@
+================ Step #1 =================
+Compiling files:
+ src/useA.kt
+ src/AChild.kt
+End of files
+Exit code: OK
+------------------------------------------
\ No newline at end of file
diff --git a/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/notChangeSignature/gradle-build.log b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/notChangeSignature/gradle-build.log
new file mode 100644
index 00000000000..5b9396ca2b1
--- /dev/null
+++ b/jps-plugin/testData/incremental/withJava/javaUsedInKotlin/notChangeSignature/gradle-build.log
@@ -0,0 +1,6 @@
+================ Step #1 =================
+
+Compiling files:
+ src/JavaClass.java
+ src/usage.kt
+End of files