{"id":30898095,"url":"https://github.com/falsepattern/zanama","last_synced_at":"2025-09-09T01:04:59.564Z","repository":{"id":308443176,"uuid":"1029883415","full_name":"FalsePattern/zanama","owner":"FalsePattern","description":"A Zig to Java FFI bindings generator, inspired by JExtract.","archived":false,"fork":false,"pushed_at":"2025-08-05T23:13:34.000Z","size":136,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-08-06T01:08:15.437Z","etag":null,"topics":["bindings-generator","gradle-plugin","java-ffi","java-panama","zig-package"],"latest_commit_sha":null,"homepage":"https://git.falsepattern.com/falsepattern/zanama","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FalsePattern.png","metadata":{"files":{"readme":"README.MD","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-31T18:17:47.000Z","updated_at":"2025-08-05T23:13:37.000Z","dependencies_parsed_at":"2025-08-06T01:22:12.532Z","dependency_job_id":null,"html_url":"https://github.com/FalsePattern/zanama","commit_stats":null,"previous_names":["falsepattern/zanama"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/FalsePattern/zanama","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FalsePattern%2Fzanama","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FalsePattern%2Fzanama/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FalsePattern%2Fzanama/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FalsePattern%2Fzanama/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FalsePattern","download_url":"https://codeload.github.com/FalsePattern/zanama/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FalsePattern%2Fzanama/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274231709,"owners_count":25245855,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-08T02:00:09.813Z","response_time":121,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bindings-generator","gradle-plugin","java-ffi","java-panama","zig-package"],"created_at":"2025-09-09T01:04:54.167Z","updated_at":"2025-09-09T01:04:59.469Z","avatar_url":"https://github.com/FalsePattern.png","language":"Kotlin","readme":"# Zanama\n\nA Zig to Java FFI bindings generator, inspired by JExtract.\n\n## Usage \u003ca name=\"usage\"\u003e\u003c/a\u003e\n\n## Step 1: Add the gradle plugin to your repo \u003ca name=\"usage1\"\u003e\u003c/a\u003e\n\n```kotlin\nplugins {\n    id(\"com.falsepattern.zanama\") version \"ZANAMA_VERSION\"\n    id(\"com.falsepattern.zigbuild\") version \"ZIGBUILD_VERSION\" //Optional, but it's highly recommended you use this plugin for compiling zig via gradle.\n}\n```\n\n## Step 2: Create a zig build gradle task\n\n```kotlin\n// The directory where zig will output the generated json and binaries\nval zigPrefix = layout.buildDirectory.dir(\"zig-out\")\nval zigInstall = tasks.register\u003cZigBuildTask\u003e(\"zigInstall\") {\n    options {\n        steps.add(\"install\")\n    }\n    prefixDirectory = zigPrefix\n    clearPrefixDirectory = true\n    sourceFiles.from(\n        // Any files/directories that are referenced by your zig buildscript, as well as:\n        layout.buildDirectory.dir(\"zanama\"),\n    )\n    //This task provides the Zig side of the bindings generator\n    dependsOn(\"extractZanama\")\n}\n```\n\n## Step 3: Set up the zig build for generating the binaries and the json output\n\nThe following is a small example of how the zig build side of zanama can be used. This will require changes based on how your project is actually structured.\n\n### `build.zig.zon`:\n\n```zig\n.{\n    .dependencies = .{\n        .zanama = .{ .path = \"build/zanama\" }, //This path is generated by the extractZanama gradle task.\n    },\n}\n```\n\n### `build.zig`:\n\n```zig\nconst std = @import(\"std\");\nconst zanama = @import(\"zanama\");\n\n// This creates a single linux x86_64 baseline binary\npub fn build(b: *std.Build) void {\n    const optimize = b.standardOptimizeOption(.{});\n\n    // Zanama needs to be aware of itself as the dependency, as well as the main build instance\n    const zanama_dep = b.dependency(\"zanama\", .{});\n    const zb = zanama.Build.init(b, optimize, zanama_dep);\n\n    // Your code\n    const root_module = b.createModule(.{\n        .root_source_file = b.path(\"src/main/zig/root.zig\"),\n        .imports = \u0026.{\n            .{ .name = \"zanama\", .module = zanama_dep.module(\"api\") },\n        },\n    });\n\n    // Generating zanama bindings, as well as the libraries.\n    // There are other createZanamaLibs* functions, check the source code for more info\n    const libs = zb.createZanamaLibsQuery(\"root\", root_module, \u0026.{zanama.target.common.x86_64.linux.baseline});\n\n    // Putting the libraries and generated json into the prefix directory (specified in the gradle task)\n    const install_step = b.getInstallStep();\n    for (libs.artifacts) |artifact| {\n        install_step.dependOn(\u0026b.addInstallArtifact(artifact, .{\n            //Configured like this to make the gradle side simpler\n            .dest_dir = .{ .override = .lib },\n            .h_dir = .disabled,\n            .implib_dir = .disabled,\n            .pdb_dir = .disabled,\n        }).step);\n    }\n    const install_json = b.addInstallFile(libs.json, \"root.json\");\n    install_json.step.dependOn(libs.json_step);\n    install_step.dependOn(\u0026install_json.step);\n}\n\n```\n\n### `src/main/zig/root.zig`:\n\n```zig\nconst zanama = @import(\"zanama\");\n\npub const Bar = extern struct {\n    x: i32,\n    y: i32,\n    z: i32,\n\n    // Any function you want to call from Java must have the C calling convention.\n    pub fn foo(_: NonPacked) callconv(.c) void {\n\n    }\n};\n\n\ncomptime {\n    zanama.genBindings(\u0026.{\n        .{ .name = \"myproject.Bar\", .Struct = Bar },\n    }) catch unreachable;\n}\n\n```\n\n## Step 4: Processing the zig build outputs in gradle\n```kotlin\nval translateJavaSources = layout.buildDirectory.dir(\"generated/zanama_root\")\n\nval zigTranslate = tasks.register\u003cZanamaTranslate\u003e(\"zigTranslate\") {\n    // zigPrefix variable specified in step 2\n    from = zigPrefix.map { it.file(\"root.json\") }\n    into = translateJavaSources\n    //The package you want to put the generated code into. May create sub-packages for nested structs, but never above this package.\n    rootPkg = \"com.example.myproject.natives\"\n    //The prefix of the .name part in the zanam genBindings call for all the bindings you added\n    bindRoot = \"myproject\"\n    //The name of the \"main\" class of this translation unit. It contains shared type info used by all bindings in the json\n    className = \"root_z\"\n    dependsOn(zigInstall) // from step 2\n}\n\nsourceSets[\"main\"].java.srcDir(translateJavaSources)\n\ntasks.compileJava {\n    dependsOn(zigTranslate)\n}\n\n//You can either package the output DLLs into your program, include it in the generated jar file, or whatever else\ntasks.processResources {\n    dependsOn(zigInstall)\n    into(\"com/example/myproject/natives\") {\n        from(zigPrefix.map { it.dir(\"lib\") } )\n        include(\"*.dll\", \"*.so\", \"*.dylib\") // windows, linux, macos\n    }\n}\n\n//Add the generated libraries to your jar\n\n//Pull in the zanama runtime.\ndependencies {\n    implementation(\"com.falsepattern:zanama-rt:ZANAMA_VERSION\")\n}\n```\n\n## Step 5: Create the initializer class\n\nThe generated main class of the translation requires you to load the native library manually.\n\n`root_z_init.java`:\n```java\nclass root_z_init {\n    //You can use the unpacker for jar-bundled resources (CAREFUL: gradle's application `run` task DOES NOT run the jar, so createWithUnpacker won't work there! Make a custom gradle task that runs your jar if you want to use it)\n    //Alternatively, you can ship the natives already unpacked, and use NativeContext.create(Path)\n    //You can share a single NativeContext between all *_init classes as long as you package all the natives into the same directory\n    private static final NativeContext CTX = NativeContext.createWithUnpacker(Path.of(\"natives\"), \"com/example/myproject/natives/\");\n    \n    public static NativeContext.Lib createLib() {\n        // You can do this checked and handle the errors yourself, etc.\n        // For now, Zanama is not flexible enough for a better architecture, so this is fine.\n        \n        // Determining the library name is up to the user.\n        // You can use the Platform class as extra help for determining the OS/CPU.\n        var name = \"root-linux-x86_64-baseline\";\n        return CTX.loadUnchecked(root_z_init.class, name);\n    }\n}\n```\n\nAt this point, you should be able to use the generated zanama bindings to work with structs and call methods.\n\n\n## Notes/todos\n\n- Zanama is not meant for generating bindings to arbitrary libraries, and instead is designed as the \"inverse\" of JNI headers. This means that it should not be used for\nmass-binding random libraries, but instead done with \"pinhole\" bindings for code that you directly control. If you want to do mass bindings, use JExtract with C headers,\nit's way more robust for that use case.\n- Zanama currently uses global state. Once a library is loaded, it can never be unloaded until the process exits, or you unload the zanama classes and discard the arena (accessible with the alternative NativeContext methods)\n- Translation for struct default values are not yet implemented\n- Non-type/function constants (`pub const x: u32 = 123;`) crash the translator. This is a known issue.\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffalsepattern%2Fzanama","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffalsepattern%2Fzanama","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffalsepattern%2Fzanama/lists"}