Skip to content

Commit 78db455

Browse files
committed
get Windows building locally
1 parent d25f392 commit 78db455

File tree

11 files changed

+369
-118
lines changed

11 files changed

+369
-118
lines changed

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
kotlin-version = "2.2.0"
33
dokka-version = "2.0.0"
44

5-
aws-kotlin-repo-tools-version = "0.4.38"
5+
aws-kotlin-repo-tools-version = "0.4.50"
66

77
# libs
88
coroutines-version = "1.10.2"

runtime/build.gradle.kts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ subprojects {
2828
plugin(libraries.plugins.aws.kotlin.repo.tools.kmp.get().pluginId)
2929
}
3030

31-
println("Project $name: hasNative=$hasNative, NATIVE_ENABLED=$NATIVE_ENABLED")
32-
3331
// Apply KMP configuration from build plugin
3432
configureKmpTargets()
3533

@@ -131,6 +129,20 @@ subprojects {
131129
// for details.
132130
duplicatesStrategy = DuplicatesStrategy.WARN
133131
}
132+
133+
tasks.withType<AbstractTestTask> {
134+
if (this is Test) {
135+
useJUnitPlatform()
136+
}
137+
138+
testLogging {
139+
events("passed", "skipped", "failed")
140+
showStandardStreams = true
141+
showStackTraces = true
142+
showExceptions = true
143+
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
144+
}
145+
}
134146
}
135147

136148
// configureIosSimulatorTasks()

runtime/runtime-core/build.gradle.kts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
7-
86
plugins {
97
alias(libs.plugins.kotlinx.serialization)
108
}
@@ -57,14 +55,21 @@ kotlin {
5755
}
5856
}
5957

60-
targets.withType<KotlinNativeTarget> {
58+
59+
mingwX64 {
6160
compilations["main"].cinterops {
62-
val interopDir = "$projectDir/native/src/nativeInterop/cinterop"
63-
create("environ") {
64-
includeDirs(interopDir)
65-
packageName("aws.smithy.platform.posix")
66-
headers(listOf("$interopDir/environ.h"))
61+
create("winver") {
62+
definitionFile.set(file("native/interop/winver.def"))
63+
includeDirs("C:\\msys64\\mingw64\\include")
6764
}
6865
}
66+
67+
// TODO clean up
68+
val compilerArgs = listOf(
69+
"-Xverbose-phases=Linker", // Enable verbose linking phase from the compiler
70+
"-linker-option",
71+
"-v",
72+
)
73+
compilerOptions.freeCompilerArgs.addAll(compilerArgs)
6974
}
7075
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package aws.smithy.kotlin.runtime.net
2+
3+
import kotlinx.cinterop.UnsafeNumber
4+
import kotlinx.cinterop.alloc
5+
import kotlinx.cinterop.allocPointerTo
6+
import kotlinx.cinterop.invoke
7+
import kotlinx.cinterop.memScoped
8+
import kotlinx.cinterop.pointed
9+
import kotlinx.cinterop.ptr
10+
import kotlinx.cinterop.refTo
11+
import kotlinx.cinterop.reinterpret
12+
import kotlinx.cinterop.toKString
13+
import kotlinx.cinterop.value
14+
import platform.posix.AF_INET
15+
import platform.posix.AF_INET6
16+
import platform.posix.AF_UNSPEC
17+
import platform.posix.SOCK_STREAM
18+
import platform.posix.WSADATA
19+
import platform.posix.memcpy
20+
import platform.posix.sockaddr
21+
import platform.posix.sockaddr_in
22+
import platform.windows.AI_PASSIVE
23+
import platform.windows.WSACleanup
24+
import platform.windows.WSAStartup
25+
import platform.windows.addrinfo
26+
import platform.windows.freeaddrinfo
27+
import platform.windows.gai_strerror
28+
import platform.windows.getaddrinfo
29+
import platform.windows.sockaddr_in6
30+
31+
internal actual object DefaultHostResolver : HostResolver {
32+
actual override suspend fun resolve(hostname: String): List<HostAddress> = memScoped {
33+
// Version format specified in https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-wsastartup
34+
val wsaMajorVersion = 1u
35+
val wsaMinorVersion = 1u
36+
val wsaVersion = (wsaMajorVersion shl 8 or wsaMinorVersion).toUShort()
37+
38+
val wsaInfo = alloc<WSADATA>()
39+
val wsaResult = WSAStartup(wsaVersion, wsaInfo.ptr)
40+
check(wsaResult == 0) { "Failed to initialize Windows Sockets (error code $wsaResult)" }
41+
42+
try {
43+
44+
val hints = alloc<addrinfo>().apply {
45+
ai_family = AF_UNSPEC // Allow both IPv4 and IPv6
46+
ai_socktype = SOCK_STREAM // TCP stream sockets
47+
ai_flags = AI_PASSIVE // For wildcard IP address
48+
}
49+
50+
val result = allocPointerTo<addrinfo>()
51+
52+
try {
53+
// Perform the DNS lookup
54+
val status = getaddrinfo(hostname, null, hints.ptr, result.ptr)
55+
check(status == 0) { "Failed to resolve host $hostname: ${gai_strerror?.invoke(status)?.toKString()}" }
56+
57+
return generateSequence(result.value) { it.pointed.ai_next }
58+
.map { it.pointed.ai_addr!!.pointed.toIpAddr() }
59+
.map { HostAddress(hostname, it) }
60+
.toList()
61+
} finally {
62+
freeaddrinfo(result.value)
63+
}
64+
} finally {
65+
WSACleanup()
66+
}
67+
}
68+
69+
@OptIn(UnsafeNumber::class)
70+
private fun sockaddr.toIpAddr(): IpAddr {
71+
val (size, addrPtr, constructor) = when (sa_family.toInt()) {
72+
AF_INET -> Triple(
73+
4,
74+
reinterpret<sockaddr_in>().sin_addr.ptr,
75+
{ bytes: ByteArray -> IpV4Addr(bytes) },
76+
)
77+
AF_INET6 -> Triple(
78+
16,
79+
reinterpret<sockaddr_in6>().sin6_addr.ptr,
80+
{ bytes: ByteArray -> IpV6Addr(bytes) },
81+
)
82+
else -> throw IllegalArgumentException("Unsupported sockaddr family $sa_family")
83+
}
84+
85+
val ipBytes = ByteArray(size)
86+
memcpy(ipBytes.refTo(0), addrPtr, size.toULong())
87+
return constructor(ipBytes)
88+
}
89+
90+
actual override fun reportFailure(addr: HostAddress) {
91+
// No-op, same as JVM implementation
92+
}
93+
94+
actual override fun purgeCache(addr: HostAddress?) {
95+
// No-op, same as JVM implementation
96+
}
97+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package aws.smithy.kotlin.runtime.util
2+
3+
import kotlinx.cinterop.*
4+
import platform.posix.environ
5+
import platform.posix.memcpy
6+
import aws.smithy.kotlin.native.winver.*
7+
8+
internal actual val rawEnvironmentVariables: CPointer<CPointerVarOf<CPointer<ByteVarOf<Byte>>>>? = environ
9+
10+
public actual object SystemDefaultProvider : SystemDefaultProviderBase() {
11+
actual override val filePathSeparator: String = "\\"
12+
actual override fun osInfo(): OperatingSystem = OperatingSystem(OsFamily.Windows, osVersionFromKernel())
13+
}
14+
15+
// The functions below are adapted from C++ SDK:
16+
// https://github.com/aws/aws-sdk-cpp/blob/0e6085bf0dd9a1cb1f27d101c4cf2db6ade6f307/src/aws-cpp-sdk-core/source/platform/windows/OSVersionInfo.cpp#L49-L106
17+
18+
private val wordHexFormat = HexFormat {
19+
upperCase = false
20+
number {
21+
removeLeadingZeros = true
22+
minLength = 4
23+
}
24+
}
25+
26+
private data class LangCodePage(
27+
val language: UShort,
28+
val codePage: UShort,
29+
)
30+
31+
private fun osVersionFromKernel(): String? = memScoped {
32+
withFileVersionInfo("Kernel32.dll") { versionInfoPtr ->
33+
getLangCodePage(versionInfoPtr)?.let { langCodePage ->
34+
getProductVersion(versionInfoPtr, langCodePage)
35+
}
36+
}
37+
}
38+
39+
private inline fun <R> withFileVersionInfo(fileName: String, block: (CPointer<ByteVarOf<Byte>>) -> R?): R? {
40+
val blobSize = GetFileVersionInfoSizeW(fileName, null)
41+
val blob = ByteArray(blobSize.convert())
42+
blob.usePinned { pinned ->
43+
val result = GetFileVersionInfoW(fileName, 0u, blobSize, pinned.addressOf(0))
44+
return if (result == 0) {
45+
null
46+
} else {
47+
block(pinned.addressOf(0))
48+
}
49+
}
50+
}
51+
52+
private fun MemScope.getLangCodePage(versionInfoPtr: CPointer<ByteVarOf<Byte>>): LangCodePage? {
53+
// Get _any_ language pack and codepage since they should all have the same version
54+
val langAndCodePagePtr = alloc<COpaquePointerVar>()
55+
val codePageSize = alloc<UIntVar>()
56+
val result = VerQueryValueW(
57+
versionInfoPtr,
58+
"""\VarFileInfo\Translation""",
59+
langAndCodePagePtr.ptr,
60+
codePageSize.ptr,
61+
)
62+
63+
return if (result == 0) {
64+
null
65+
} else {
66+
val langAndCodePage = langAndCodePagePtr.value!!.reinterpret<UIntVar>().pointed.value
67+
val language = (langAndCodePage and 0x0000ffffu).toUShort() // low WORD
68+
val codePage = (langAndCodePage and 0xffff0000u shr 16).toUShort() // high WORD
69+
LangCodePage(language, codePage)
70+
}
71+
}
72+
73+
private fun MemScope.getProductVersion(versionInfoPtr: CPointer<ByteVarOf<Byte>>, langCodePage: LangCodePage): String? {
74+
val versionId = buildString {
75+
// Something like: \StringFileInfo\04090fb0\ProductVersion
76+
append("""\StringFileInfo\""")
77+
append(langCodePage.language.toHexString(wordHexFormat))
78+
append(langCodePage.codePage.toHexString(wordHexFormat))
79+
append("""\ProductVersion""")
80+
}
81+
82+
// Get the block corresponding to versionId
83+
val block = alloc<COpaquePointerVar>()
84+
val blockSize = alloc<UIntVar>()
85+
val result = VerQueryValueW(versionInfoPtr, versionId, block.ptr, blockSize.ptr)
86+
87+
return if (result == 0) {
88+
null
89+
} else {
90+
// Copy the bytes into a Kotlin byte array
91+
val blockBytes = ByteArray(blockSize.value.convert())
92+
blockBytes.usePinned { pinned ->
93+
memcpy(pinned.addressOf(0), block.value!!.reinterpret<ByteVar>(), blockSize.value.convert())
94+
}
95+
blockBytes.decodeToString()
96+
}
97+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package aws.smithy.kotlin.runtime.util
6+
7+
import kotlinx.coroutines.test.runTest
8+
import kotlin.test.Test
9+
import kotlin.test.assertEquals
10+
import kotlin.test.assertNotNull
11+
12+
class SystemPlatformProviderMingwTest {
13+
@Test
14+
fun testOsInfo() = runTest {
15+
val osInfo = PlatformProvider.System.osInfo()
16+
println(osInfo)
17+
assertEquals(OsFamily.Windows, osInfo.family)
18+
assertNotNull(osInfo.version)
19+
}
20+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package = aws.smithy.kotlin.native.winver
2+
headers = windows.h
3+
compilerOpts = \
4+
-DUNICODE \
5+
-DWINVER=0x0601 \
6+
-D_WIN32_WINNT=0x0601 \
7+
-DWINAPI_FAMILY=3 \
8+
-DOEMRESOURCE \
9+
-Wno-incompatible-pointer-types \
10+
-Wno-deprecated-declarations
11+
staticLibraries = libversion.a
12+
libraryPaths = C:\\msys64\\mingw64\\lib

0 commit comments

Comments
 (0)