Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[K/N] Expose program name in runtime #5281

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions kotlin-native/backend.native/tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,10 @@ standaloneTest("throw_from_except_constr") {
source = "runtime/exceptions/throw_from_except_constr.kt"
}

standaloneTest("program_name") {
source = "runtime/program_name/runtime_program_name.kt"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file represents the legacy test infrastructure that we are actively trying to get rid of.

Please create a proper test class in
https://github.com/JetBrains/kotlin/tree/master/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ported the testcase to the new infrastructure.


tasks.register("vararg0", KonanLocalTest) {
source = "lower/vararg.kt"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class)

import kotlin.test.*
import kotlin.native.Platform

fun main(args: Array<String>) {
// Remove path and extension (.kexe or .exe)
val programFileName = Platform.programName.substringAfterLast("/").substringBeforeLast(".")

assertEquals("program_name", programFileName)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More tests are necessary. For example, the original issue mentions a nice use case:

Build multi-call binaries. For example, busybox is a single binary that contains many tools, and decides which tool to run based on how the symbolic link is called: https://busybox.net/downloads/BusyBox.html#usage (that's how it can be so small)

So checking a case with symbolic link would also be useful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added test for that by calling execv() in the C code. This allowed to test even more use-cases (no programName at all), while still covering the use-case of renaming/linking a binary.

2 changes: 2 additions & 0 deletions kotlin-native/runtime/src/launcher/cpp/launcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ using namespace kotlin;
//--- Setup args --------------------------------------------------------------//

OBJ_GETTER(setupArgs, int argc, const char** argv) {
kotlin::programName = argv[0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is argv[0] guaranteed to live long enough? What will happen if some code accesses kotlin::programName after the main function finishes?
Is argv guaranteed to always have at least one element?

Copy link
Contributor Author

@vonox7 vonox7 Apr 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. programName will now be set to null before argv leaves scope. I could not find a definite answer if it would be guaranteed to live long enough, so I opted for the safe choice.
  2. Theoretically it is guaranteed by the POSIX standard. However if it has 0 arguments, the code 1 line below would have crashed. I also added a testcase for argc=0, and added support for it via std::max(0, ...). So now kotlin executables don't crash on startup when they are launched in a non posix compatible way.


// The count is one less, because we skip argv[0] which is the binary name.
ObjHeader* result = AllocArrayInstance(theArrayTypeInfo, argc - 1, OBJ_RESULT);
ArrayHeader* array = result->array();
Expand Down
6 changes: 6 additions & 0 deletions kotlin-native/runtime/src/main/cpp/Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ bool kotlin::initializeGlobalRuntimeIfNeeded() noexcept {
return true;
}

const char* kotlin::programName = nullptr;

extern "C" {

RUNTIME_NOTHROW void AppendToInitializersTail(InitNode *next) {
Expand Down Expand Up @@ -335,6 +337,10 @@ KBoolean Konan_Platform_isFreezingEnabled() {
return kotlin::compiler::freezingChecksEnabled();
}

OBJ_GETTER0(Konan_Platform_getProgramName) {
RETURN_RESULT_OF(CreateStringFromCString, kotlin::programName)
}

bool Kotlin_memoryLeakCheckerEnabled() {
return g_checkLeaks;
}
Expand Down
3 changes: 3 additions & 0 deletions kotlin-native/runtime/src/main/cpp/Runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "Porting.h"
#include "Memory.h"
#include "KString.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems unused.


#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -50,6 +51,8 @@ namespace kotlin {
// Returns `true` if initialized.
bool initializeGlobalRuntimeIfNeeded() noexcept;

extern const char* programName;

}

#endif // RUNTIME_RUNTIME_H
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ public object Platform {
public val isFreezingEnabled: Boolean
get() = Platform_isFreezingEnabled()

/**
* Representation of the name used to invoke the program executable.
*/
public val programName: String
get() = Platform_getProgramName()
SvyatoslavScherbina marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@qurbonzoda could you please review the stdlib change?


/**
* If the memory leak checker is activated, by default `true` in debug mode, `false` in release.
* When memory leak checker is activated, and leak is detected during last Kotlin context
Expand Down Expand Up @@ -161,6 +167,9 @@ private external fun Platform_isDebugBinary(): Boolean
@GCUnsafeCall("Konan_Platform_isFreezingEnabled")
private external fun Platform_isFreezingEnabled(): Boolean

@GCUnsafeCall("Konan_Platform_getProgramName")
private external fun Platform_getProgramName(): String

@GCUnsafeCall("Konan_Platform_getMemoryLeakChecker")
private external fun Platform_getMemoryLeakChecker(): Boolean

Expand Down