diff --git a/.gitignore b/.gitignore index 611c591..3ba8cb3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ build/ *.gen.kt .vscode /.kotlin +/.konan *.log /library/src/jvmMain/java diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f176836..21eb532 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,4 +1,5 @@ import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmRun +import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink val GENERATED_FILE_PREFIX: String = "// Generated on build in build.gradle.kts\n" @@ -102,3 +103,23 @@ tasks.matching { it.name.startsWith("compileKotlin") }.all { tasks.withType { jvmArgs("--enable-native-access=ALL-UNNAMED") } + +tasks.withType { + val link_task: KotlinNativeLink = this + + finalizedBy( tasks.create("${name}Finalise") { + doFirst { + val patch_command: String = System.getenv("KOTLIN_BINARY_PATCH_COMMAND")?.takeIf { it.isNotBlank() } ?: return@doFirst + + for (dir in link_task.outputs.files) { + for (file in dir.listFiles().orEmpty()) { + if (!file.isFile) { + continue + } + + Runtime.getRuntime().exec(arrayOf(patch_command, file.absolutePath)).waitFor() + } + } + } + } ) +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..86a0cca --- /dev/null +++ b/flake.nix @@ -0,0 +1,80 @@ +{ + description = "SpMp server development environment"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + custom_nixpkgs.url = "github:toasterofbread/nixpkgs/2fce494a8413016b5630cb593ffb9a0a1867274a"; + }; + + outputs = { self, nixpkgs, custom_nixpkgs, ... }: + let + system = "x86_64-linux"; + in + { + devShells."${system}".default = + let + pkgs = import nixpkgs { + inherit system; + }; + custom_pkgs = import custom_nixpkgs { + inherit system; + }; + in + pkgs.mkShell { + packages = with pkgs; [ + fish + jdk21 + jdk22 + pkg-config + cmake + jextract + (custom_pkgs.zeromq-kotlin-native.override { enableDrafts = true; }) + mpv + libayatana-appindicator + gtk3 + curl + (custom_pkgs.kotlin-native-toolchain-env.override { x86 = true; }) + git + + # Runtime + patchelf + glibc + libgcc.lib + ]; + + JAVA_21_HOME = "${pkgs.jdk21_headless}/lib/openjdk"; + JAVA_22_HOME = "${pkgs.jdk22}/lib/openjdk"; + JAVA_HOME = "${pkgs.jdk21_headless}/lib/openjdk"; + JEXTRACT_PATH = "${pkgs.jextract}/bin/jextract"; + + KOTLIN_BINARY_PATCH_COMMAND = "patchkotlinbinary"; + + shellHook = '' + # Add NIX_LDFLAGS to LD_LIBRARY_PATH + lib_paths=($(echo $NIX_LDFLAGS | grep -oP '(?<=-rpath\s| -L)[^ ]+')) + lib_paths_str=$(IFS=:; echo "''${lib_paths[*]}") + export LD_LIBRARY_PATH="$lib_paths_str:$LD_LIBRARY_PATH" + + # Add glibc/include to C_INCLUDE_PATH + export C_INCLUDE_PATH="${pkgs.glibc.dev}/include:$C_INCLUDE_PATH" + + echo "Using KJna development environment" + echo "JAVA_HOME=$JAVA_HOME" + + export KONAN_DATA_DIR=$(pwd)/.konan + + mkdir -p $KONAN_DATA_DIR + cp -asfT ${custom_pkgs.kotlin-native-toolchain-env} $KONAN_DATA_DIR + + mkdir $KONAN_DATA_DIR/bin + export PATH="$KONAN_DATA_DIR/bin:$PATH" + + PATCH_KOTLIN_BINARY_SCRIPT="patchelf --set-interpreter \$(cat \$NIX_CC/nix-support/dynamic-linker) --set-rpath $KONAN_DATA_DIR/dependencies/x86_64-unknown-linux-gnu-gcc-8.3.0-glibc-2.19-kernel-4.9-2/x86_64-unknown-linux-gnu/sysroot/lib64 \$1" + echo "$PATCH_KOTLIN_BINARY_SCRIPT" > $KONAN_DATA_DIR/bin/$KOTLIN_BINARY_PATCH_COMMAND + chmod +x $KONAN_DATA_DIR/bin/$KOTLIN_BINARY_PATCH_COMMAND + + chmod -R u+w $KONAN_DATA_DIR + ''; + }; + }; +} diff --git a/gradle.properties b/gradle.properties index e16993b..885205f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,6 +5,7 @@ org.gradle.jvmargs=-Xmx4g -Xms1g org.gradle.caching=true kotlin.mpp.applyDefaultHierarchyTemplate=false kotlin.mpp.enableCInteropCommonization=true +kotlin.native.cacheKind.linuxX64=none # Dependencies kotlin.version=2.0.0 @@ -13,6 +14,7 @@ clikt.version=4.4.0 mediasession.version=0.1.1 ytm.version=0.2.1 ktor.version=3.0.0-beta-1 -kjna.version=06023e1f27 +kjna.version=0.0.3 -kotlin.native.cacheKind.linuxX64=none +# Nix +org.gradle.java.installations.fromEnv=JAVA_21_HOME,JAVA_22_HOME diff --git a/library/build.gradle.kts b/library/build.gradle.kts index a8052b5..27eee01 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -79,9 +79,9 @@ kotlin { exclude_functions += listOf("g_clear_handle_id", "_g_log_fallback_handler", "_g_signals_destroy") } - libraries += Static.pkgConfig(Platform.LINUX_X86, null, "ayatana-appindicator3-0.1", libs = true).mapNotNull { if (it.startsWith("-l")) it.drop(2) else null } + libraries += Static.pkgConfig(Platform.getCurrent(), null, "ayatana-appindicator3-0.1", libs = true).mapNotNull { if (it.startsWith("-l")) it.drop(2) else null } - for (cflag in Static.pkgConfig(Platform.LINUX_X86, null, "ayatana-appindicator3-0.1", cflags = true)) { + for (cflag in Static.pkgConfig(Platform.getCurrent(), null, "ayatana-appindicator3-0.1", cflags = true)) { if (cflag.startsWith("-I")) { include_dirs += listOf(cflag.drop(2)) } @@ -307,9 +307,10 @@ enum class CinteropLibraries { val cflags: List = Static.pkgConfig(platform, deps_directory, getPackageName(), cflags = true) settings.compilerOpts(cflags) - val default_include_dirs: List = + val default_include_dirs: List = ( if (platform.is_linux) listOf("/usr/include", "/usr/include/${platform.arch.libdir_name}").map { File(it) } else emptyList() + ) + System.getenv("CMAKE_INCLUDE_PATH").orEmpty().split(":").map { File(it) } fun addHeaderFile(path: String) { var file: File? = null @@ -352,7 +353,8 @@ enum class CinteropLibraries { } } - val libs_dir: File = deps_directory.resolve("lib") + val deps_libs_dir: File = deps_directory.resolve("lib") + val lib_dirs: List = listOf(deps_libs_dir) + System.getenv("LD_LIBRARY_PATH").orEmpty().split(":").map { File(it) } val lib_filenames: List = when (this) { @@ -361,9 +363,8 @@ enum class CinteropLibraries { } for (filename in lib_filenames) { - val lib_file: File = libs_dir.resolve(filename) - if (!lib_file.isFile) { - println("WARNING: Could not find library file '$lib_file' in $libs_dir") + if (lib_dirs.none { it.resolve(filename).isFile }) { + println("WARNING: Could not find library file '$filename' in $deps_libs_dir or LD_LIBRARY_PATH") } } @@ -386,7 +387,7 @@ enum class CinteropLibraries { def_file.writeText(""" staticLibraries = ${lib_filenames.joinToString(" ")} - libraryPaths = ${libs_dir.absolutePath} + libraryPaths = ${lib_dirs.map { it.absolutePath }.joinToString(" ")} linkerOpts = ${linker_opts.joinToString(" ")} """.trimIndent()) @@ -532,17 +533,19 @@ object Static { val process_builder: ProcessBuilder = ProcessBuilder( listOfNotNull( - "pkg-config", + findExecutable("pkg-config"), if (cflags) "--cflags" else null, if (libs) "--libs" else null ) + package_names ) - process_builder.environment()["PKG_CONFIG_PATH"] = + process_builder.environment()["PKG_CONFIG_PATH"] = ( + System.getenv("PKG_CONFIG_PATH").orEmpty() + listOfNotNull( deps_directory?.resolve("pkgconfig")?.takeIf { it.isDirectory }?.absolutePath, platform.arch.PKG_CONFIG_PATH, System.getenv("SPMS_LIB")?.plus("/pkgconfig") ).joinToString(":") + ) process_builder.environment()["PKG_CONFIG_ALLOW_SYSTEM_LIBS"] = "1" val process: Process = process_builder.start() @@ -559,4 +562,15 @@ object Static { } } } } + + private fun findExecutable(name: String): String { + for (dir in System.getenv("PATH")?.split(":").orEmpty()) { + val file: File = File(dir).resolve(name) + if (file.isFile) { + return file.absolutePath + } + } + + return name + } }