Skip to content

Commit

Permalink
Merge pull request #61 from apposed/debug-log-to-file
Browse files Browse the repository at this point in the history
When in debug mode, log also to a file
  • Loading branch information
hinerm authored Dec 11, 2024
2 parents 5f57315 + 2b2e962 commit 7c68d40
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 20 deletions.
1 change: 1 addition & 0 deletions src/commonMain/kotlin/file.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ expect class File(rawPath: String) {
val length : Long
fun ls(): List<File>
fun lines(): List<String>
fun write(s: String)
fun mv(dest:File): Boolean
fun rm() : Boolean
fun rmdir() : Boolean
Expand Down
17 changes: 15 additions & 2 deletions src/commonMain/kotlin/logging.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import platform.posix.exit
private const val EXIT_CODE_ON_FAIL = 20

var debugMode = getenv("DEBUG") !in listOf(null, "", "0", "false", "FALSE")
var logFilePath = getenv("JAUNCH_LOGFILE") ?: "jaunch.log"

private var logFile: File? = null

fun debug(vararg args: Any) { if (debugMode) report("DEBUG", *args) }

Expand Down Expand Up @@ -37,8 +40,18 @@ fun fail(message: String): Nothing {
}

private fun report(prefix: String, vararg args: Any) {
printlnErr(buildString {
val s = buildString {
append("[$prefix] ")
args.forEach { append(it) }
})
}
printlnErr(s)
if (debugMode) {
val log = logFile ?: File(logFilePath)
if (logFile == null) {
// Overwrite any log file from previous run.
if (log.exists) log.rm()
logFile = log
}
log.write("$s$NL")
}
}
11 changes: 8 additions & 3 deletions src/commonMain/kotlin/main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,9 @@ fun main(args: Array<String>) {
}

// Finally, execute all the directives! \^_^/
executeDirectives(nonGlobalDirectives, launchDirectives, runtimes, userArgs,
argsInContext)
executeDirectives(nonGlobalDirectives, launchDirectives, runtimes, argsInContext)

debugBanner("JAUNCH CONFIGURATION COMPLETE")
}

// -- Program flow functions --
Expand All @@ -128,6 +129,11 @@ private fun parseArguments(args: Array<String>): Pair<File?, List<String>> {
// Enable debug mode when --debug flag is present.
debugMode = inputArgs.contains("--debug")

// Note: We need to let parseArguments set the debugMode
// flag in response to the --debug argument being passed.
// So we wait until now to emit this initial debugging bookend message.
debugBanner("PROCEEDING WITH JAUNCH CONFIGURATION")

debug("executable -> ", executable ?: "<null>")
debug("inputArgs -> ", inputArgs)

Expand Down Expand Up @@ -531,7 +537,6 @@ private fun executeDirectives(
configDirectives: List<String>,
launchDirectives: List<String>,
runtimes: List<RuntimeConfig>,
userArgs: ProgramArgs,
argsInContext: Map<String, ProgramArgs>
) {
debugBanner("EXECUTING DIRECTIVES")
Expand Down
20 changes: 16 additions & 4 deletions src/posixMain/kotlin/file.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ actual class File actual constructor(private val rawPath: String) {
actual fun ls(): List<File> {
if (!isDirectory) throw IllegalArgumentException("Not a directory: $path")

val directory = opendir(path) ?: throw IllegalArgumentException("Failed to open directory")
val directory = opendir(path) ?: throw IllegalArgumentException("Failed to open directory: $path")
val files = mutableListOf<File>()

try {
Expand All @@ -55,7 +55,7 @@ actual class File actual constructor(private val rawPath: String) {
actual fun lines(): List<String> {
val lines = mutableListOf<String>()
memScoped {
val file = fopen(path, "r") ?: throw RuntimeException("Failed to open file")
val file = fopen(path, "r") ?: throw RuntimeException("Failed to open file: $this")
try {
while (true) {
val buffer = ByteArray(BUFFER_SIZE)
Expand All @@ -70,8 +70,16 @@ actual class File actual constructor(private val rawPath: String) {
return lines
}

override fun toString(): String {
return path
@OptIn(ExperimentalForeignApi::class)
actual fun write(s: String) {
val file = fopen(path, "a") ?:
throw RuntimeException("Failed to open file: $this")
try {
fputs(s, file)
}
finally {
fclose(file)
}
}

@OptIn(ExperimentalForeignApi::class)
Expand All @@ -94,6 +102,10 @@ actual class File actual constructor(private val rawPath: String) {
return rmdir(path) == 0
}
}

override fun toString(): String {
return path
}
}

@OptIn(ExperimentalForeignApi::class)
Expand Down
77 changes: 66 additions & 11 deletions src/windowsMain/kotlin/file.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ actual class File actual constructor(private val rawPath: String) {

actual val isRoot: Boolean =
// Is it a drive letter plus backslash (e.g. `C:\`)?
path.length == 3 && (path[0] in 'a'..'z' || path[0] in 'A'..'Z') && path[1] == ':' && path[2] == '\\'
path.length == 3 &&
(path[0] in 'a'..'z' || path[0] in 'A'..'Z') &&
path[1] == ':' && path[2] == '\\'

@OptIn(ExperimentalForeignApi::class)
actual val length: Long get() {
Expand Down Expand Up @@ -82,7 +84,7 @@ actual class File actual constructor(private val rawPath: String) {
content.append(buffer.decodeToString(0, readCount));
}
} else {
printlnErr("Error reading file: ${GetLastError()}")
printlnErr("Error reading file '$this': ${lastError()}")
}
}
} while (bytesRead.value > 0U)
Expand All @@ -94,8 +96,33 @@ actual class File actual constructor(private val rawPath: String) {
return lines
}

override fun toString(): String {
return path
@OptIn(ExperimentalForeignApi::class)
actual fun write(s: String) {
val handle = openFile(path, write = true) ?:
throw RuntimeException("Failed to open file: $this")
try {
memScoped {
val bytes = s.encodeToByteArray()
bytes.usePinned { pinnedBytes ->
val bytesWritten = alloc<UIntVar>()

val result = WriteFile(
handle,
pinnedBytes.addressOf(0).reinterpret(),
bytes.size.toUInt(),
bytesWritten.ptr,
null
)

if (result == 0) { // 0 indicates failure in Windows
throw RuntimeException("Error writing to file '$this': ${lastError()}")
}
}
}
}
finally {
CloseHandle(handle)
}
}

@OptIn(ExperimentalForeignApi::class)
Expand All @@ -122,25 +149,29 @@ actual class File actual constructor(private val rawPath: String) {
}
}

override fun toString(): String {
return path
}

private fun isMode(modeBits: Int): Boolean {
val attrs = GetFileAttributesA(path)
return attrs != INVALID_FILE_ATTRIBUTES && (attrs.toInt() and modeBits) != 0
}

@OptIn(ExperimentalForeignApi::class)
private fun openFile(path: String): HANDLE? {
private fun openFile(path: String, write: Boolean = false): HANDLE? {
val fileHandle = CreateFileW(
path,
GENERIC_READ,
FILE_SHARE_READ.toUInt(),
if (write) FILE_APPEND_DATA.toUInt() else GENERIC_READ,
if (write) FILE_SHARE_WRITE.toUInt() else FILE_SHARE_READ.toUInt(),
null,
OPEN_EXISTING.toUInt(),
if (write) OPEN_ALWAYS.toUInt() else OPEN_EXISTING.toUInt(),
FILE_ATTRIBUTE_NORMAL.toUInt(),
null
)

if (fileHandle == INVALID_HANDLE_VALUE) {
warn("Error opening file: ${GetLastError()}")
warn("Error opening file '$this': ${lastError()}")
return null
}
return fileHandle
Expand All @@ -150,7 +181,7 @@ actual class File actual constructor(private val rawPath: String) {
private fun fileSize(fileHandle: HANDLE): Long? = memScoped {
val fileSize = alloc<LARGE_INTEGER>()
if (GetFileSizeEx(fileHandle, fileSize.ptr) == 0) {
warn("Error getting file size: ${GetLastError()}")
warn("Error getting size of file '$this': ${lastError()}")
return null
}
return fileSize.QuadPart
Expand All @@ -173,7 +204,31 @@ private fun canonicalize(path: String): String {
memScoped {
val buffer = allocArray<UShortVar>(bufferLength)
val fullPathLength = GetFullPathName!!(p.wcstr.ptr, bufferLength.toUInt(), buffer, null)
if (fullPathLength == 0u) throw RuntimeException("Failed to get full path: ${GetLastError()}")
if (fullPathLength == 0u) throw RuntimeException("Failed to get full path of '$path': ${lastError()}")
return buffer.toKString()
}
}

/** Converts Windows numeric error codes to human-friendly strings. */
@OptIn(ExperimentalForeignApi::class)
private fun lastError(): String {
memScoped {
val errorCode = GetLastError()
val buffer = alloc<CPointerVar<WCHARVar>>()

val messageLength = FormatMessageW(
(FORMAT_MESSAGE_ALLOCATE_BUFFER or FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_IGNORE_INSERTS).toUInt(),
null,
errorCode,
0.toUInt(), // Default language
buffer.ptr.reinterpret(),
0.toUInt(),
null
)
val errorMessage = if (messageLength > 0U) buffer.value!!.toKString().trim() else "Unknown error"

if (messageLength > 0U) LocalFree(buffer.value)

return errorMessage;
}
}

0 comments on commit 7c68d40

Please sign in to comment.