tirbofish/dropbear · commit
86408862b1a388f22d1b0d71a4e4dfd8dbc3b097
fix up magna-carta crate and magna-carta-plugin gradle plugin. it works pretty nicely now :)
Signature present but could not be verified.
Unverified
@@ -75,6 +75,10 @@ The dropbear engine uses Kotlin Multiplatform, which allows the cooked up produc KMP can support, which includes mobile, WASM and desktop. Because the editor is only available on desktop, the JVM is used to evaluate the scripts as it allows for hot-reloading (not made yet). +The dropbear engine does not support any Java code (for class scripting) as it won't be read by the magna-carta parser. +Java libraries are still fine, and java classes work, but cannot be annotated by the parser, and only works +on the JVM target. You can customise the build.gradle.kts file in your project to your own likings. + ## Documentation API documentation and articles are available at (todo) @@ -8,8 +8,6 @@ import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.tasks.* import org.gradle.internal.os.OperatingSystem -import java.io.File -import java.security.MessageDigest abstract class DownloadMagnaCartaToolTask: Download() { @get:Input @@ -26,37 +24,32 @@ abstract class DownloadMagnaCartaToolTask: Download() { val os = OperatingSystem.current() val arch = System.getProperty("os.arch") - val (fileName, url, expectedSha256) = when { - os.isLinux && arch == "amd64" -> Triple( + val (fileName, url) = when { + os.isLinux && arch == "amd64" -> arrayOf( "magna-carta-linux-x64", "https://github.com/4tkbytes/dropbear/releases/download/${toolVersion.get()}/magna-carta-linux-x64", - "88b3497ab7e787260aeb7f4d91fe46fa9f78ccfb32f841a27a693b824da4bc32" ) - os.isMacOsX && arch == "aarch64" -> Triple( + os.isMacOsX && arch == "aarch64" -> arrayOf( "magna-carta-macos-arm64", "https://github.com/4tkbytes/dropbear/releases/download/${toolVersion.get()}/magna-carta-macos-arm64", - "73ba95d193ab7ac5925324e4c3006c60e88bf0cb89b7a08786b75f2bfc038c10" ) - os.isMacOsX && (arch == "x86_64" || arch == "amd64") -> Triple( + os.isMacOsX && (arch == "x86_64" || arch == "amd64") -> arrayOf( "magna-carta-macos-x64", "https://github.com/4tkbytes/dropbear/releases/download/${toolVersion.get()}/magna-carta-macos-x64", - "012202ee74db0d9638033c8947b7f94d4b20be95b00f2ea181d892af20e226e2" ) - os.isWindows && arch == "aarch64" -> Triple( + os.isWindows && arch == "aarch64" -> arrayOf( "magna-carta-windows-arm64.exe", "https://github.com/4tkbytes/dropbear/releases/download/${toolVersion.get()}/magna-carta-windows-arm64.exe", - "639ab60a2693d82b548be1497ef25117a13b6e0c69fc45a19450fd10e2610d13" ) - os.isWindows && (arch == "x86_64" || arch == "amd64") -> Triple( + os.isWindows && (arch == "x86_64" || arch == "amd64") -> arrayOf( "magna-carta-windows-x64.exe", "https://github.com/4tkbytes/dropbear/releases/download/${toolVersion.get()}/magna-carta-windows-x64.exe", - "983880be951c9bda800a616739c4245f97e5ddb2818017d0d9c29ae62c0b9016" ) else -> throw GradleException("Unsupported OS/arch: $os / $arch") } val outputFile = outputDir.get().file(fileName).asFile - if (outputFile.exists() && verifyChecksum(outputFile, expectedSha256)) { + if (outputFile.exists()) { println("Using cached magna-carta tool: $outputFile") return } @@ -68,29 +61,11 @@ abstract class DownloadMagnaCartaToolTask: Download() { super.download() - if (!verifyChecksum(outputFile, expectedSha256)) { - outputFile.delete() - throw GradleException("Checksum verification failed for $fileName") - } - if (!os.isWindows) { outputFile.setExecutable(true) } } - private fun verifyChecksum(file: File, expectedSha256: String): Boolean { - val digest = MessageDigest.getInstance("SHA-256") - file.inputStream().use { input -> - val buffer = ByteArray(8192) - var bytesRead: Int - while (input.read(buffer).also { bytesRead = it } > 0) { - digest.update(buffer, 0, bytesRead) - } - } - val actualSha256 = digest.digest().joinToString("") { "%02x".format(it) } - return actualSha256 == expectedSha256 - } - private fun getToolFileName(): String { val os = OperatingSystem.current() val arch = System.getProperty("os.arch") @@ -10,7 +10,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget class MagnaCartaPlugin : Plugin<Project> { override fun apply(project: Project) { - val extension = project.extensions.create("magna-carta", MagnaCartaExtension::class) + project.extensions.create("magna-carta", MagnaCartaExtension::class) val downloadToolTask = project.tasks.register("downloadMagnaCartaTool", DownloadMagnaCartaToolTask::class) { toolVersion.set("magna-carta-v0.0.1") @@ -22,7 +22,7 @@ class MagnaCartaPlugin : Plugin<Project> { toolExecutable.set(downloadToolTask.flatMap { it.outputFile }) target.set("jvm") inputDir.set(project.projectDir.resolve("src")) - outputDir.set(project.layout.buildDirectory.dir("generated/magna-carta/jvm")) + outputDir.set(project.layout.buildDirectory.dir("magna-carta/jvmMain")) } val generateNativeTask = project.tasks.register("generateMagnaCartaNative", GenerateMagnaCartaTask::class) { @@ -30,7 +30,7 @@ class MagnaCartaPlugin : Plugin<Project> { toolExecutable.set(downloadToolTask.flatMap { it.outputFile }) target.set("native") inputDir.set(project.projectDir.resolve("src")) - outputDir.set(project.layout.buildDirectory.dir("generated/magna-carta/native")) + outputDir.set(project.layout.buildDirectory.dir("magna-carta/nativeLibMain")) } project.pluginManager.withPlugin("org.jetbrains.kotlin.multiplatform") { @@ -42,11 +42,11 @@ class MagnaCartaPlugin : Plugin<Project> { jvmMain.kotlin.srcDir(generateJvmTask.map { it.outputDir }) } - val nativeMain = findByName("nativeMain") ?: create("nativeMain") - nativeMain.kotlin.srcDir(generateNativeTask.map { it.outputDir }) + val nativeLibMain = findByName("nativeLibMain") ?: create("nativeLibMain") + nativeLibMain.kotlin.srcDir(generateNativeTask.map { it.outputDir }) kotlin.targets.withType(KotlinNativeTarget::class.java) { - compilations.getByName("main").defaultSourceSet.dependsOn(nativeMain) + compilations.getByName("main").defaultSourceSet.dependsOn(nativeLibMain) } } @@ -54,7 +54,17 @@ class MagnaCartaPlugin : Plugin<Project> { kotlin.targets.all { this.compilations.all { this.compileTaskProvider.configure { - this.dependsOn(generateJvmTask, generateNativeTask) + when (this.name) { + "compileKotlinNative" -> { + this.dependsOn(generateNativeTask) + } + "compileKotlinJvm" -> { + this.dependsOn(generateJvmTask) + } + else -> { + println("Unknown compilation task: ${this.name}") + } + } } } } @@ -14,11 +14,14 @@ impl Generator for KotlinJVMGenerator { writeln!( output, - "// Auto-generated by dropbear-engine with the magna-carta parser on {}", + "// Auto-generated by dropbear-engine with the magna-carta parser on {} UTC", local_time.format("%Y-%m-%d %H:%M:%S") )?; writeln!(output)?; + writeln!(output, "package com.dropbear.decl")?; + + writeln!(output)?; let mut imported_packages = std::collections::HashSet::new(); for item in manifest.items() { if let Some(last_dot) = item.fqcn().rfind('.') { @@ -4,10 +4,15 @@ use std::path::Path; pub mod jvm; pub mod native; -#[allow(dead_code)] +/// A trait that can generate code from a manifest. pub trait Generator { + /// Generate code from a manifest. + /// + /// # Returns + /// [`anyhow::Result<String>`] - The code from the manifest into that specific language. fn generate(&self, manifest: &ScriptManifest) -> anyhow::Result<String>; + /// Writes to a file using the std library. fn write_to_file( &self, manifest: &ScriptManifest, @@ -12,7 +12,7 @@ impl Generator for KotlinNativeGenerator { let local_time = Utc::now(); writeln!( output, - "// Auto-generated by dropbear-engine with the magna-carta parser on {}", + "// Auto-generated by dropbear-engine with the magna-carta parser on {} UTC", local_time.format("%Y-%m-%d %H:%M:%S") )?; writeln!( @@ -21,12 +21,17 @@ impl Generator for KotlinNativeGenerator { )?; writeln!(output)?; + writeln!(output, "package com.dropbear.decl")?; + writeln!(output)?; + writeln!(output, "import com.dropbear.DropbearEngine")?; writeln!(output, "import com.dropbear.EntityId")?; writeln!(output, "import com.dropbear.EntityRef")?; + writeln!(output, "import com.dropbear.System")?; writeln!(output, "import com.dropbear.ffi.NativeEngine")?; writeln!(output, "import kotlinx.cinterop.COpaquePointer")?; writeln!(output, "import kotlinx.cinterop.ExperimentalForeignApi")?; + writeln!(output, "import kotlin.experimental.ExperimentalNativeApi")?; writeln!(output, "import kotlin.native.CName")?; writeln!(output)?; @@ -11,14 +11,20 @@ struct Cli { #[arg(short, long, help = "Input directory containing Kotlin source files")] input: PathBuf, - #[arg(short, long, help = "Output directory for generated files")] - output: PathBuf, + #[arg(short, long, help = "Output directory for generated files (ignored if --stdout is used)")] + output: Option<PathBuf>, #[arg(short, long, help = "Target platform")] target: Target, + + #[arg(long, help = "Print generated manifest to stdout instead of writing to file")] + stdout: bool, + + #[arg(long, help = "Print manifest raw")] + raw: bool, } -#[derive(ValueEnum, Clone)] +#[derive(ValueEnum, Clone, Debug)] enum Target { Jvm, Native, @@ -27,6 +33,10 @@ enum Target { fn main() -> anyhow::Result<()> { let cli = Cli::parse(); + if !cli.stdout && cli.output.is_none() { + return Err(anyhow::anyhow!("--output is required unless --stdout is used")); + } + let mut processor = KotlinProcessor::new()?; let mut manifest = ScriptManifest::new(); @@ -36,21 +46,34 @@ fn main() -> anyhow::Result<()> { visit_kotlin_files(&cli.input, &mut processor, &mut manifest)?; - fs::create_dir_all(&cli.output)?; - - match cli.target { + let generated_content = match cli.target { Target::Jvm => { let generator = KotlinJVMGenerator; - let output_path = cli.output.join("RunnableRegistry.kt"); - generator.write_to_file(&manifest, &output_path)?; - println!("Generated JVM manifest at: {}", output_path.display()); + generator.generate(&manifest)? } Target::Native => { let generator = KotlinNativeGenerator; - let output_path = cli.output.join("ScriptManifest.kt"); - generator.write_to_file(&manifest, &output_path)?; - println!("Generated Native manifest at: {}", output_path.display()); + generator.generate(&manifest)? } + }; + + if cli.raw { + println!("{:#?}", manifest); + } + + if cli.stdout { + print!("{}", generated_content); + } else { + let output_dir = cli.output.unwrap(); + fs::create_dir_all(&output_dir)?; + + let filename = match cli.target { + Target::Jvm => "RunnableRegistry.kt", + Target::Native => "ScriptManifest.kt", + }; + let output_path = output_dir.join(filename); + fs::write(&output_path, generated_content)?; + println!("Generated {:?} manifest at: {}", cli.target, output_path.display()); } println!("Found {} script classes", manifest.items().len()); @@ -66,18 +89,18 @@ fn visit_kotlin_files( for entry in fs::read_dir(dir)? { let entry = entry?; let path = entry.path(); - + if path.is_dir() { visit_kotlin_files(&path, processor, manifest)?; } else if path.extension() == Some(std::ffi::OsStr::new("kt")) { let source_code = fs::read_to_string(&path)?; - + if let Some(item) = processor.process_file(&source_code, path.clone())? { manifest.add_item(item); } } } } - + Ok(()) }