From e377a98815da04a2885c50d6455b35ced5172334 Mon Sep 17 00:00:00 2001 From: Sebastian Sellmair Date: Fri, 23 Feb 2024 11:33:03 +0100 Subject: [PATCH] [ObjCExport] Fe10: Implement test with exported and non exported klib dependencies KT-65670 --- .../Fe10ObjCExportHeaderGenerator.kt | 12 +- .../backend/konan/testUtils/Fe10Utils.kt | 18 +- .../konan/testUtils/HeaderGenerator.kt | 16 ++ .../konan/testUtils/testDependencies.kt | 21 +++ ...CExportDependenciesHeaderGeneratorTest.kt} | 22 ++- .../!exportedAndNotExportedDependency.h | 171 ++++++++++++++++++ .../exportedAndNotExportedDependency/Foo.kt | 2 + .../testDependencies/ReadMe.md | 3 + .../testLibraryA/build.gradle.kts | 3 + .../testLibraryA/gradle.properties | 3 + .../src/nativeMain/kotlin/MyLibraryA.kt | 10 + .../testLibraryB/build.gradle.kts | 3 + .../testLibraryB/gradle.properties | 3 + .../src/nativeMain/kotlin/MyLibraryB.kt | 10 + .../kotlin/common-configuration.gradle.kts | 2 +- ...t-header-generator-test-library.gradle.kts | 22 +++ .../kotlin/objcExportHeaderGeneratorTest.kt | 49 +++++ settings.gradle | 4 + 18 files changed, 361 insertions(+), 13 deletions(-) create mode 100644 native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/testDependencies.kt rename native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/{ObjCDependenciesTypesTest.kt => ObjCExportDependenciesHeaderGeneratorTest.kt} (65%) create mode 100644 native/objcexport-header-generator/testData/dependencies/exportedAndNotExportedDependency/!exportedAndNotExportedDependency.h create mode 100644 native/objcexport-header-generator/testData/dependencies/exportedAndNotExportedDependency/Foo.kt create mode 100644 native/objcexport-header-generator/testDependencies/ReadMe.md create mode 100644 native/objcexport-header-generator/testDependencies/testLibraryA/build.gradle.kts create mode 100644 native/objcexport-header-generator/testDependencies/testLibraryA/gradle.properties create mode 100644 native/objcexport-header-generator/testDependencies/testLibraryA/src/nativeMain/kotlin/MyLibraryA.kt create mode 100644 native/objcexport-header-generator/testDependencies/testLibraryB/build.gradle.kts create mode 100644 native/objcexport-header-generator/testDependencies/testLibraryB/gradle.properties create mode 100644 native/objcexport-header-generator/testDependencies/testLibraryB/src/nativeMain/kotlin/MyLibraryB.kt create mode 100644 repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/objc-export-header-generator-test-library.gradle.kts diff --git a/native/objcexport-header-generator/impl/k1/test/org/jetbrains/kotlin/backend/konan/testUtils/Fe10ObjCExportHeaderGenerator.kt b/native/objcexport-header-generator/impl/k1/test/org/jetbrains/kotlin/backend/konan/testUtils/Fe10ObjCExportHeaderGenerator.kt index 574eee0d4c1..33c1f4b2468 100644 --- a/native/objcexport-header-generator/impl/k1/test/org/jetbrains/kotlin/backend/konan/testUtils/Fe10ObjCExportHeaderGenerator.kt +++ b/native/objcexport-header-generator/impl/k1/test/org/jetbrains/kotlin/backend/konan/testUtils/Fe10ObjCExportHeaderGenerator.kt @@ -11,6 +11,8 @@ import org.jetbrains.kotlin.backend.konan.UnitSuspendFunctionObjCExport import org.jetbrains.kotlin.backend.konan.objcexport.* import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.tooling.core.closure import org.junit.jupiter.api.extension.AfterEachCallback import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.api.extension.ParameterContext @@ -59,14 +61,18 @@ private class Fe10HeaderGeneratorImpl(private val disposable: Disposable) : Head val environment: KotlinCoreEnvironment = createKotlinCoreEnvironment(disposable) val kotlinFiles = root.walkTopDown().filter { it.isFile }.filter { it.extension == "kt" }.toList() - val moduleDescriptors = setOf(createModuleDescriptor(environment, kotlinFiles)) + val moduleDescriptors = setOf(createModuleDescriptor(environment, kotlinFiles, configuration.dependencies)) val mapper = ObjCExportMapper( unitSuspendFunctionExport = UnitSuspendFunctionObjCExport.DEFAULT ) + val exportedModuleDescriptors = moduleDescriptors + moduleDescriptors + .closure { it.allDependencyModules } + .filter { it.name.asStringStripSpecialMarkers() in configuration.exportedDependencyModuleNames } + val namer = ObjCExportNamerImpl( - moduleDescriptors = moduleDescriptors, + moduleDescriptors = exportedModuleDescriptors, builtIns = DefaultBuiltIns.Instance, mapper = mapper, problemCollector = ObjCExportProblemCollector.SILENT, @@ -76,7 +82,7 @@ private class Fe10HeaderGeneratorImpl(private val disposable: Disposable) : Head ) return ObjCExportHeaderGeneratorImpl( - moduleDescriptors = moduleDescriptors.toList(), + moduleDescriptors = exportedModuleDescriptors.toList(), mapper = mapper, namer = namer, problemCollector = ObjCExportProblemCollector.SILENT, diff --git a/native/objcexport-header-generator/impl/k1/test/org/jetbrains/kotlin/backend/konan/testUtils/Fe10Utils.kt b/native/objcexport-header-generator/impl/k1/test/org/jetbrains/kotlin/backend/konan/testUtils/Fe10Utils.kt index 58f08f1bafc..d9f285309fa 100644 --- a/native/objcexport-header-generator/impl/k1/test/org/jetbrains/kotlin/backend/konan/testUtils/Fe10Utils.kt +++ b/native/objcexport-header-generator/impl/k1/test/org/jetbrains/kotlin/backend/konan/testUtils/Fe10Utils.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.storage.LockBasedStorageManager import org.jetbrains.kotlin.test.KotlinTestUtils import org.jetbrains.kotlin.test.util.KtTestUtil import java.io.File +import java.nio.file.Path fun createModuleDescriptor( environment: KotlinCoreEnvironment, @@ -51,9 +52,8 @@ fun createModuleDescriptor( fun createModuleDescriptor( environment: KotlinCoreEnvironment, kotlinFiles: List, + dependencyKlibs: List = emptyList(), ): ModuleDescriptor { - val psiFactory = KtPsiFactory(environment.project) - val kotlinPsiFiles = kotlinFiles.map { file -> psiFactory.createFile(file.name, KtTestUtil.doLoadFile(file)) } val klibFactory = KlibMetadataFactories(::KonanBuiltIns, DynamicTypeDeserializer) @@ -64,6 +64,15 @@ fun createModuleDescriptor( packageAccessHandler = null ).also { it.setDependencies(it) } + val dependencyKlibDescriptors = dependencyKlibs.map { dependencyKlib -> + klibFactory.DefaultDeserializedDescriptorFactory.createDescriptorAndNewBuiltIns( + library = resolveSingleFileKlib(org.jetbrains.kotlin.konan.file.File(dependencyKlib)), + languageVersionSettings = createLanguageVersionSettings(), + storageManager = LockBasedStorageManager.NO_LOCKS, + packageAccessHandler = null, + ).also { it.setDependencies(it, stdlibModuleDescriptor) } + } + val moduleDescriptor = ModuleDescriptorImpl( moduleName = Name.special(""), storageManager = LockBasedStorageManager.NO_LOCKS, @@ -78,7 +87,7 @@ fun createModuleDescriptor( moduleDescriptor.setDependencies( ModuleDependenciesImpl( - allDependencies = listOf(moduleDescriptor, stdlibModuleDescriptor), + allDependencies = listOf(moduleDescriptor, stdlibModuleDescriptor) + dependencyKlibDescriptors, modulesWhoseInternalsAreVisible = emptySet(), directExpectedByDependencies = emptyList(), allExpectedByDependencies = emptySet() @@ -87,6 +96,9 @@ fun createModuleDescriptor( val projectContext = ProjectContext(environment.project, "test project context") + val psiFactory = KtPsiFactory(environment.project) + val kotlinPsiFiles = kotlinFiles.map { file -> psiFactory.createFile(file.name, KtTestUtil.doLoadFile(file)) } + return FakeTopDownAnalyzerFacadeForNative.analyzeFilesWithGivenTrace( files = kotlinPsiFiles, trace = NoScopeRecordCliBindingTrace(), diff --git a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/HeaderGenerator.kt b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/HeaderGenerator.kt index d7e81b294a8..5f2010b0797 100644 --- a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/HeaderGenerator.kt +++ b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/HeaderGenerator.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.konan.testUtils import org.jetbrains.kotlin.backend.konan.objcexport.ObjCHeader import java.io.File +import java.nio.file.Path interface HeaderGenerator { @@ -18,7 +19,22 @@ interface HeaderGenerator { * We do not generate them by default to keep test data easier to read. */ val generateBaseDeclarationStubs: Boolean = false, + + /** + * List of paths pointing to .klib files that can be used as dependency for the compiler when generating + * the header for the given source files. + * + * Some of those dependencies can also be exported, see [exportedDependencyModuleNames] + */ + val dependencies: List = listOf(), + + /** + * Any dependency listed in [dependencies] which module name is present in this set is considered 'exported' and + * will result in the entire public API surface of the said library to be translated in the header + */ + val exportedDependencyModuleNames: Set = emptySet(), ) + fun generateHeaders(root: File, configuration: Configuration = Configuration()): ObjCHeader } \ No newline at end of file diff --git a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/testDependencies.kt b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/testDependencies.kt new file mode 100644 index 00000000000..580b592a18c --- /dev/null +++ b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/testUtils/testDependencies.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.backend.konan.testUtils + +import java.io.File +import kotlin.io.path.Path + +val testDependencyKlibs = System.getProperty("testDependencyKlibs").orEmpty() + .split(File.pathSeparator) + .map(::Path) + +val testLibraryAKlibFile + get() = testDependencyKlibs.firstOrNull { it.contains(Path("testLibraryA")) } + ?: error("Missing 'testLibraryA' in 'testDependencyKlibs' System Property") + +val testLibraryBKlibFile + get() = testDependencyKlibs.firstOrNull { it.contains(Path("testLibraryB")) } + ?: error("Missing 'testLibraryB' in 'testDependencyKlibs' System Property") \ No newline at end of file diff --git a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCDependenciesTypesTest.kt b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportDependenciesHeaderGeneratorTest.kt similarity index 65% rename from native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCDependenciesTypesTest.kt rename to native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportDependenciesHeaderGeneratorTest.kt index c12899d1a6f..2c5783eb9bd 100644 --- a/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCDependenciesTypesTest.kt +++ b/native/objcexport-header-generator/test/org/jetbrains/kotlin/backend/konan/tests/ObjCExportDependenciesHeaderGeneratorTest.kt @@ -5,9 +5,7 @@ package org.jetbrains.kotlin.backend.konan.tests -import org.jetbrains.kotlin.backend.konan.testUtils.HeaderGenerator -import org.jetbrains.kotlin.backend.konan.testUtils.TodoAnalysisApi -import org.jetbrains.kotlin.backend.konan.testUtils.dependenciesDir +import org.jetbrains.kotlin.backend.konan.testUtils.* import org.jetbrains.kotlin.test.KotlinTestUtils import org.junit.jupiter.api.Test import java.io.File @@ -21,7 +19,7 @@ import kotlin.test.fail * - stubs orders * - depth of traversing (some types must be skipped */ -class ObjCDependenciesTypesTest( +class ObjCExportDependenciesHeaderGeneratorTest( private val generator: HeaderGenerator, ) { @@ -54,9 +52,21 @@ class ObjCDependenciesTypesTest( doTest(dependenciesDir.resolve("implementIterator")) } - private fun doTest(root: File) { + @Test + fun `test - exportedAndNotExportedDependency`() { + doTest( + dependenciesDir.resolve("exportedAndNotExportedDependency"), configuration = HeaderGenerator.Configuration( + frameworkName = "MyApp", + generateBaseDeclarationStubs = true, + dependencies = listOf(testLibraryAKlibFile, testLibraryBKlibFile), + exportedDependencyModuleNames = setOf("org.jetbrains.kotlin:testLibraryA") + ) + ) + } + + private fun doTest(root: File, configuration: HeaderGenerator.Configuration = HeaderGenerator.Configuration()) { if (!root.isDirectory) fail("Expected ${root.absolutePath} to be directory") - val generatedHeaders = generator.generateHeaders(root, HeaderGenerator.Configuration()).toString() + val generatedHeaders = generator.generateHeaders(root, configuration).toString() KotlinTestUtils.assertEqualsToFile(root.resolve("!${root.nameWithoutExtension}.h"), generatedHeaders) } } \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/dependencies/exportedAndNotExportedDependency/!exportedAndNotExportedDependency.h b/native/objcexport-header-generator/testData/dependencies/exportedAndNotExportedDependency/!exportedAndNotExportedDependency.h new file mode 100644 index 00000000000..29b7dd2e60f --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/exportedAndNotExportedDependency/!exportedAndNotExportedDependency.h @@ -0,0 +1,171 @@ +#import +#import +#import +#import +#import +#import +#import + +@class MyAppMyLibraryA, MyAppTLBMyLibraryB; + +NS_ASSUME_NONNULL_BEGIN +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-warning-option" +#pragma clang diagnostic ignored "-Wincompatible-property-type" +#pragma clang diagnostic ignored "-Wnullability" + +#pragma push_macro("_Nullable_result") +#if !__has_feature(nullability_nullable_result) +#undef _Nullable_result +#define _Nullable_result _Nullable +#endif + +__attribute__((swift_name("KotlinBase"))) +@interface MyAppBase : NSObject +- (instancetype)init __attribute__((unavailable)); ++ (instancetype)new __attribute__((unavailable)); ++ (void)initialize __attribute__((objc_requires_super)); +@end + +@interface MyAppBase (MyAppBaseCopying) +@end + +__attribute__((swift_name("KotlinMutableSet"))) +@interface MyAppMutableSet : NSMutableSet +@end + +__attribute__((swift_name("KotlinMutableDictionary"))) +@interface MyAppMutableDictionary : NSMutableDictionary +@end + +@interface NSError (NSErrorMyAppKotlinException) +@property (readonly) id _Nullable kotlinException; +@end + +__attribute__((swift_name("KotlinNumber"))) +@interface MyAppNumber : NSNumber +- (instancetype)initWithChar:(char)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedChar:(unsigned char)value __attribute__((unavailable)); +- (instancetype)initWithShort:(short)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedShort:(unsigned short)value __attribute__((unavailable)); +- (instancetype)initWithInt:(int)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedInt:(unsigned int)value __attribute__((unavailable)); +- (instancetype)initWithLong:(long)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedLong:(unsigned long)value __attribute__((unavailable)); +- (instancetype)initWithLongLong:(long long)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedLongLong:(unsigned long long)value __attribute__((unavailable)); +- (instancetype)initWithFloat:(float)value __attribute__((unavailable)); +- (instancetype)initWithDouble:(double)value __attribute__((unavailable)); +- (instancetype)initWithBool:(BOOL)value __attribute__((unavailable)); +- (instancetype)initWithInteger:(NSInteger)value __attribute__((unavailable)); +- (instancetype)initWithUnsignedInteger:(NSUInteger)value __attribute__((unavailable)); ++ (instancetype)numberWithChar:(char)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedChar:(unsigned char)value __attribute__((unavailable)); ++ (instancetype)numberWithShort:(short)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedShort:(unsigned short)value __attribute__((unavailable)); ++ (instancetype)numberWithInt:(int)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedInt:(unsigned int)value __attribute__((unavailable)); ++ (instancetype)numberWithLong:(long)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedLong:(unsigned long)value __attribute__((unavailable)); ++ (instancetype)numberWithLongLong:(long long)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedLongLong:(unsigned long long)value __attribute__((unavailable)); ++ (instancetype)numberWithFloat:(float)value __attribute__((unavailable)); ++ (instancetype)numberWithDouble:(double)value __attribute__((unavailable)); ++ (instancetype)numberWithBool:(BOOL)value __attribute__((unavailable)); ++ (instancetype)numberWithInteger:(NSInteger)value __attribute__((unavailable)); ++ (instancetype)numberWithUnsignedInteger:(NSUInteger)value __attribute__((unavailable)); +@end + +__attribute__((swift_name("KotlinByte"))) +@interface MyAppByte : MyAppNumber +- (instancetype)initWithChar:(char)value; ++ (instancetype)numberWithChar:(char)value; +@end + +__attribute__((swift_name("KotlinUByte"))) +@interface MyAppUByte : MyAppNumber +- (instancetype)initWithUnsignedChar:(unsigned char)value; ++ (instancetype)numberWithUnsignedChar:(unsigned char)value; +@end + +__attribute__((swift_name("KotlinShort"))) +@interface MyAppShort : MyAppNumber +- (instancetype)initWithShort:(short)value; ++ (instancetype)numberWithShort:(short)value; +@end + +__attribute__((swift_name("KotlinUShort"))) +@interface MyAppUShort : MyAppNumber +- (instancetype)initWithUnsignedShort:(unsigned short)value; ++ (instancetype)numberWithUnsignedShort:(unsigned short)value; +@end + +__attribute__((swift_name("KotlinInt"))) +@interface MyAppInt : MyAppNumber +- (instancetype)initWithInt:(int)value; ++ (instancetype)numberWithInt:(int)value; +@end + +__attribute__((swift_name("KotlinUInt"))) +@interface MyAppUInt : MyAppNumber +- (instancetype)initWithUnsignedInt:(unsigned int)value; ++ (instancetype)numberWithUnsignedInt:(unsigned int)value; +@end + +__attribute__((swift_name("KotlinLong"))) +@interface MyAppLong : MyAppNumber +- (instancetype)initWithLongLong:(long long)value; ++ (instancetype)numberWithLongLong:(long long)value; +@end + +__attribute__((swift_name("KotlinULong"))) +@interface MyAppULong : MyAppNumber +- (instancetype)initWithUnsignedLongLong:(unsigned long long)value; ++ (instancetype)numberWithUnsignedLongLong:(unsigned long long)value; +@end + +__attribute__((swift_name("KotlinFloat"))) +@interface MyAppFloat : MyAppNumber +- (instancetype)initWithFloat:(float)value; ++ (instancetype)numberWithFloat:(float)value; +@end + +__attribute__((swift_name("KotlinDouble"))) +@interface MyAppDouble : MyAppNumber +- (instancetype)initWithDouble:(double)value; ++ (instancetype)numberWithDouble:(double)value; +@end + +__attribute__((swift_name("KotlinBoolean"))) +@interface MyAppBoolean : MyAppNumber +- (instancetype)initWithBool:(BOOL)value; ++ (instancetype)numberWithBool:(BOOL)value; +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("MyLibraryA"))) +@interface MyAppMyLibraryA : MyAppBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (int32_t)returnInt __attribute__((swift_name("returnInt()"))); +- (MyAppMyLibraryA *)returnMe __attribute__((swift_name("returnMe()"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("FooKt"))) +@interface MyAppFooKt : MyAppBase ++ (MyAppTLBMyLibraryB *)foo __attribute__((swift_name("foo()"))); +@end + +__attribute__((objc_subclassing_restricted)) +__attribute__((swift_name("TLBMyLibraryB"))) +@interface MyAppTLBMyLibraryB : MyAppBase +- (instancetype)init __attribute__((swift_name("init()"))) __attribute__((objc_designated_initializer)); ++ (instancetype)new __attribute__((availability(swift, unavailable, message="use object initializers instead"))); +- (int32_t)returnInt __attribute__((swift_name("returnInt()"))); +- (MyAppTLBMyLibraryB *)returnMe __attribute__((swift_name("returnMe()"))); +@end + +#pragma pop_macro("_Nullable_result") +#pragma clang diagnostic pop +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/native/objcexport-header-generator/testData/dependencies/exportedAndNotExportedDependency/Foo.kt b/native/objcexport-header-generator/testData/dependencies/exportedAndNotExportedDependency/Foo.kt new file mode 100644 index 00000000000..e5266f3c554 --- /dev/null +++ b/native/objcexport-header-generator/testData/dependencies/exportedAndNotExportedDependency/Foo.kt @@ -0,0 +1,2 @@ +// LibraryA is exported, LibraryB will just be referenced in this return type */ +fun foo(): MyLibraryB = error("stub") \ No newline at end of file diff --git a/native/objcexport-header-generator/testDependencies/ReadMe.md b/native/objcexport-header-generator/testDependencies/ReadMe.md new file mode 100644 index 00000000000..f16d42cf264 --- /dev/null +++ b/native/objcexport-header-generator/testDependencies/ReadMe.md @@ -0,0 +1,3 @@ +# Test Dependencies for ObjC Export tests: +Those dependencies will be built and the klibs can be used for the header generator to run tests against. +This should emulate the situation of having 'real life' dependencies in your project. \ No newline at end of file diff --git a/native/objcexport-header-generator/testDependencies/testLibraryA/build.gradle.kts b/native/objcexport-header-generator/testDependencies/testLibraryA/build.gradle.kts new file mode 100644 index 00000000000..4e658db5031 --- /dev/null +++ b/native/objcexport-header-generator/testDependencies/testLibraryA/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + id("objc-export-header-generator-test-library") +} diff --git a/native/objcexport-header-generator/testDependencies/testLibraryA/gradle.properties b/native/objcexport-header-generator/testDependencies/testLibraryA/gradle.properties new file mode 100644 index 00000000000..443e8757632 --- /dev/null +++ b/native/objcexport-header-generator/testDependencies/testLibraryA/gradle.properties @@ -0,0 +1,3 @@ +# https://youtrack.jetbrains.com/issue/KT-65985 +kotlin.native.toolchain.enabled=false +kotlin.native.distribution.downloadFromMaven=false \ No newline at end of file diff --git a/native/objcexport-header-generator/testDependencies/testLibraryA/src/nativeMain/kotlin/MyLibraryA.kt b/native/objcexport-header-generator/testDependencies/testLibraryA/src/nativeMain/kotlin/MyLibraryA.kt new file mode 100644 index 00000000000..49098714699 --- /dev/null +++ b/native/objcexport-header-generator/testDependencies/testLibraryA/src/nativeMain/kotlin/MyLibraryA.kt @@ -0,0 +1,10 @@ +@file:Suppress("unused") +/* +* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. +* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. +*/ + +class MyLibraryA { + fun returnInt(): Int = 42 + fun returnMe(): MyLibraryA = this +} \ No newline at end of file diff --git a/native/objcexport-header-generator/testDependencies/testLibraryB/build.gradle.kts b/native/objcexport-header-generator/testDependencies/testLibraryB/build.gradle.kts new file mode 100644 index 00000000000..4e658db5031 --- /dev/null +++ b/native/objcexport-header-generator/testDependencies/testLibraryB/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + id("objc-export-header-generator-test-library") +} diff --git a/native/objcexport-header-generator/testDependencies/testLibraryB/gradle.properties b/native/objcexport-header-generator/testDependencies/testLibraryB/gradle.properties new file mode 100644 index 00000000000..54f7f30e30c --- /dev/null +++ b/native/objcexport-header-generator/testDependencies/testLibraryB/gradle.properties @@ -0,0 +1,3 @@ +# https://youtrack.jetbrains.com/issue/KT-65985 +kotlin.native.toolchain.enabled=false +kotlin.native.distribution.downloadFromMaven=false diff --git a/native/objcexport-header-generator/testDependencies/testLibraryB/src/nativeMain/kotlin/MyLibraryB.kt b/native/objcexport-header-generator/testDependencies/testLibraryB/src/nativeMain/kotlin/MyLibraryB.kt new file mode 100644 index 00000000000..18a8ba3b049 --- /dev/null +++ b/native/objcexport-header-generator/testDependencies/testLibraryB/src/nativeMain/kotlin/MyLibraryB.kt @@ -0,0 +1,10 @@ +@file:Suppress("unused") +/* +* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. +* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. +*/ + +class MyLibraryB { + fun returnInt(): Int = 42 + fun returnMe(): MyLibraryB = this +} \ No newline at end of file diff --git a/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/common-configuration.gradle.kts b/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/common-configuration.gradle.kts index 33fa24eed9e..50231e81c8e 100644 --- a/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/common-configuration.gradle.kts +++ b/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/common-configuration.gradle.kts @@ -167,7 +167,7 @@ fun Project.configureKotlinCompilationOptions() { // This is a workaround for KT-50876, but with no clear explanation why doFirst is used. // However, KGP with Native targets is used in the native-xctest project, and this code fails with // The value for property 'freeCompilerArgs' is final and cannot be changed any further. - if (project.path != ":native:kotlin-test-native-xctest") { + if (project.path != ":native:kotlin-test-native-xctest" && !project.path.startsWith(":native:objcexport-header-generator")) { doFirst { if (!useAbsolutePathsInKlib) { @Suppress("DEPRECATION") diff --git a/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/objc-export-header-generator-test-library.gradle.kts b/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/objc-export-header-generator-test-library.gradle.kts new file mode 100644 index 00000000000..ecb29c1d963 --- /dev/null +++ b/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/objc-export-header-generator-test-library.gradle.kts @@ -0,0 +1,22 @@ +/** + * Used in for modules in 'native/objcexport-heade-generator/testDependencies. + * Such libraries can build klibs that can then later be used for running objc export tests against + */ +plugins { + kotlin("multiplatform") +} + +/* +Depends on https://youtrack.jetbrains.com/issue/KT-65985 + */ +providers.systemProperty("kotlin.internal.native.test.nativeHome").orNull?.let { nativeHome -> + extensions.extraProperties.set("kotlin.native.home", nativeHome) +} + +kotlin { + macosArm64() + macosX64() + linuxX64() + linuxArm64() + mingwX64() +} diff --git a/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/objcExportHeaderGeneratorTest.kt b/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/objcExportHeaderGeneratorTest.kt index de8ff0b9dbc..03ceaaddc9d 100644 --- a/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/objcExportHeaderGeneratorTest.kt +++ b/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/objcExportHeaderGeneratorTest.kt @@ -1,5 +1,16 @@ import org.gradle.api.Project +import org.gradle.api.attributes.Category +import org.gradle.api.attributes.Usage +import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.testing.Test +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.named +import org.gradle.kotlin.dsl.project +import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages +import org.jetbrains.kotlin.konan.target.HostManager +import java.io.File /* * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. @@ -19,11 +30,49 @@ fun Project.objCExportHeaderGeneratorTest( tag = null, requirePlatformLibs = false, ) { + /** + * Setup klib dependencies that can be used in tests: + * The resolved klibs will be available as classpath under the `testDependencyKlibs` System property. + */ + run { + /* Configuration to resolve klibs for the current host */ + val testDependencyKlibs = configurations.maybeCreate("testDependencyKlibs").also { configuration -> + configuration.attributes { + attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_API)) + attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY)) + attribute(KotlinPlatformType.attribute, KotlinPlatformType.native) + attribute(KotlinNativeTarget.konanTargetAttribute, HostManager.host.name) + } + + dependencies { + configuration(project(":native:objcexport-header-generator:testLibraryA")) + configuration(project(":native:objcexport-header-generator:testLibraryB")) + } + } + + /* Create a classpath (list of file paths) that will be exposed as System property */ + val testDependencyKlibsClasspath = testDependencyKlibs.incoming.files.elements.map { elements -> + elements.joinToString(File.pathSeparator) { location -> location.asFile.absolutePath } + } + + doFirst { + systemProperty("testDependencyKlibs", testDependencyKlibsClasspath.get()) + } + + /* Add dependency files as inputs to this test task */ + inputs.files(testDependencyKlibs).withPathSensitivity(PathSensitivity.RELATIVE) + } + useJUnitPlatform() enableJunit5ExtensionsAutodetection() + + /* Special 'Kotlin in Fleet' flag that can switch test mode to 'local development' */ systemProperty("kif.local", project.providers.gradleProperty("kif.local").isPresent) + + /* Tests will show this displayName as an additional tag (e.g., to differentiate between K1 and AA tests) */ if (testDisplayNameTag != null) { systemProperty("testDisplayName.tag", testDisplayNameTag) } + configure() } diff --git a/settings.gradle b/settings.gradle index 1e719db820b..eee3cee708e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -122,6 +122,8 @@ include ":benchmarks", ":native:objcexport-header-generator", ":native:objcexport-header-generator-k1", ":native:objcexport-header-generator-analysis-api", + ":native:objcexport-header-generator:testLibraryA", + ":native:objcexport-header-generator:testLibraryB", ":core:compiler.common", ":core:compiler.common.jvm", ":core:compiler.common.js", @@ -724,6 +726,8 @@ project(":native:kotlin-klib-commonizer-api").projectDir = "$rootDir/native/comm project(':native:kotlin-klib-commonizer-embeddable').projectDir = "$rootDir/native/commonizer-embeddable" as File project(':native:objcexport-header-generator-k1').projectDir = "$rootDir/native/objcexport-header-generator/impl/k1" as File project(':native:objcexport-header-generator-analysis-api').projectDir = "$rootDir/native/objcexport-header-generator/impl/analysis-api" as File +project(':native:objcexport-header-generator:testLibraryA').projectDir = "$rootDir/native/objcexport-header-generator/testDependencies/testLibraryA" as File +project(':native:objcexport-header-generator:testLibraryB').projectDir = "$rootDir/native/objcexport-header-generator/testDependencies/testLibraryB" as File project(':plugins:android-extensions-compiler').projectDir = "$rootDir/plugins/android-extensions/android-extensions-compiler" as File project(':kotlin-android-extensions').projectDir = "$rootDir/prepare/android-extensions-compiler-gradle" as File project(':kotlin-parcelize-compiler').projectDir = "$rootDir/prepare/parcelize-compiler-gradle" as File