Something went wrong. Try again.
Uploads documents to Paperless-ngx from the command line or from a Send to Paperless item in Nautilus.
Something went wrong. Try again.
paperless-upload build.zig
9.7 kB · 224 lines
Zig
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225// SPDX-FileCopyrightText: © 2025 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
const std = @import("std");const dbus_service_build = @import("dbus_service");
pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); const nautilus = b.option( bool, "nautilus", "Build the Nautilus extension (default: on Linux)", ) orelse (target.result.os.tag == .linux);
const deps = .{ .target = target, .optimize = optimize }; const paperless_dep = b.dependency("paperless", deps); const dbus_service_dep = b.dependency("dbus_service", deps); const mdbus_dep = b.dependency("mdbus", deps); const known_folders_dep = b.dependency("known_folders", .{});
// Bindings for org.freedesktop.Notifications, generated from the // introspection file by zig-dbus-service. const dbus_interfaces = dbus_service_build.addInterfaces(b, dbus_service_dep, &.{ b.path("dbus/org.freedesktop.Notifications.xml"), });
// What the command-line tool and the extension share. const core = b.addModule("core", .{ .root_source_file = b.path("src/root.zig"), .target = target, .optimize = optimize, .imports = &.{ .{ .name = "paperless", .module = paperless_dep.module("paperless") }, .{ .name = "known_folders", .module = known_folders_dep.module("known-folders") }, .{ .name = "mdbus", .module = mdbus_dep.module("mdbus") }, .{ .name = "dbus_service", .module = dbus_service_dep.module("dbus_service") }, .{ .name = "dbus_interfaces", .module = dbus_interfaces }, }, });
const exe = b.addExecutable(.{ .name = "paperless-upload", .root_module = b.createModule(.{ .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, .imports = &.{ .{ .name = "paperless", .module = paperless_dep.module("paperless") }, .{ .name = "core", .module = core }, }, }), }); b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe); run_cmd.addPassthruArgs(); const run_step = b.step("run", "Run the command-line tool"); run_step.dependOn(&run_cmd.step);
const test_step = b.step("test", "Run tests"); const check_step = b.step("check", "Compile everything without running it"); check_step.dependOn(&exe.step);
// `use_llvm` because the self-hosted backend emits no coverage // instrumentation, which leaves kcov and the fuzzer with nothing. const core_tests = b.addTest(.{ .name = "core-test", .root_module = core, .use_llvm = true, }); test_step.dependOn(&b.addRunArtifact(core_tests).step); check_step.dependOn(&core_tests.step);
const exe_tests = b.addTest(.{ .name = "paperless-upload-test", .root_module = exe.root_module, .use_llvm = true, }); test_step.dependOn(&b.addRunArtifact(exe_tests).step); check_step.dependOn(&exe_tests.step);
if (nautilus) { const gobject = addGObjectBindings(b, target, optimize);
const extension = b.addLibrary(.{ .name = "paperless-upload-nautilus", .linkage = .dynamic, .root_module = b.createModule(.{ .root_source_file = b.path("nautilus/extension.zig"), .target = target, .optimize = optimize, .link_libc = true, .imports = &.{ .{ .name = "paperless", .module = paperless_dep.module("paperless") }, .{ .name = "core", .module = core }, .{ .name = "glib", .module = gobject.get("glib2") }, .{ .name = "gobject", .module = gobject.get("gobject2") }, .{ .name = "gio", .module = gobject.get("gio2") }, .{ .name = "gdk", .module = gobject.get("gdk4") }, .{ .name = "gtk", .module = gobject.get("gtk4") }, .{ .name = "adw", .module = gobject.get("adw1") }, .{ .name = "nautilus", .module = gobject.get("nautilus4") }, }, }), // The self-hosted backend cannot yet produce a shared library // that a C program loads. .use_llvm = true, }); // Where Nautilus looks: NAUTILUS_4_EXTENSION_DIR on NixOS points at // this directory in the system profile. const install_extension = b.addInstallArtifact(extension, .{ .dest_dir = .{ .override = .{ .custom = "lib/nautilus/extensions-4" } }, }); b.getInstallStep().dependOn(&install_extension.step); check_step.dependOn(&extension.step); }}
/// The bindings zig-gobject generates, by module name, and the pkg-config/// packages each links. Generating Nautilus-4.1 and Adw-1 makes exactly/// these; a new one appearing is a missing-import error naming it.const gobject_modules = [_]struct { []const u8, []const []const u8 }{ .{ "glib2", &.{"glib-2.0"} }, .{ "gobject2", &.{"gobject-2.0"} }, .{ "gmodule2", &.{"gmodule-2.0"} }, .{ "gio2", &.{"gio-2.0"} }, .{ "cairo1", &.{"cairo-gobject"} }, .{ "freetype22", &.{} }, .{ "harfbuzz0", &.{ "harfbuzz", "harfbuzz-gobject" } }, .{ "pango1", &.{"pango"} }, .{ "pangocairo1", &.{"pangocairo"} }, .{ "gdkpixbuf2", &.{"gdk-pixbuf-2.0"} }, .{ "graphene1", &.{"graphene-gobject-1.0"} }, .{ "gdk4", &.{"gtk4"} }, .{ "gsk4", &.{"gtk4"} }, .{ "gtk4", &.{"gtk4"} }, .{ "adw1", &.{"libadwaita-1"} }, .{ "nautilus4", &.{"libnautilus-extension-4"} },};
const GObjectBindings = struct { modules: std.StringHashMapUnmanaged(*std.Build.Module),
fn get(self: GObjectBindings, name: []const u8) *std.Build.Module { return self.modules.get(name) orelse std.debug.panic("no binding module {s}", .{name}); }};
/// Generates Zig bindings for libnautilus-extension-4, libadwaita, GTK 4/// and everything they are built on, with zig-gobject's `translate-gir`,/// from the GIR files found on `GIR_PATH` -- which the Nix devshell and/// package set.////// The generated directory is itself a package, but a package's/// dependencies have to be known before the build graph runs, and this one/// is made by it. So the modules are made here instead. Each one imports/// every other, which is a superset of what the generated `build.zig` wires/// and saves keeping that graph here as well.fn addGObjectBindings( b: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode,) GObjectBindings { const io = b.graph.io;
// The configuration reads GIR_PATH and lists the directories it names, // neither of which Zig's configure cache keys on; without this a change // to either would be answered with a stale configuration. b.graph.poisonCache();
const codegen_dep = b.dependency("gobject_codegen", .{}); const translate_gir = b.addRunArtifact(codegen_dep.artifact("translate-gir")); const bindings = translate_gir.addPrefixedOutputDirectoryArg("--output-dir=", "bindings"); translate_gir.addPrefixedDirectoryArg("--gir-fixes-dir=", codegen_dep.path("gir-fixes")); translate_gir.addPrefixedDirectoryArg("--bindings-dir=", codegen_dep.path("binding-overrides")); translate_gir.addPrefixedDirectoryArg("--extensions-dir=", codegen_dep.path("extensions"));
const gir_files = b.addWriteFiles(); const gir_path = b.graph.environ_map.get("GIR_PATH") orelse { std.log.err("GIR_PATH is not set; it names the directories holding the GIR files of Nautilus, libadwaita, GTK and what they depend on (nix develop sets it)", .{}); std.process.exit(1); }; var gir_dirs = std.mem.tokenizeScalar(u8, gir_path, ':'); while (gir_dirs.next()) |dir_path| { var dir = std.Io.Dir.cwd().openDir(io, dir_path, .{ .iterate = true }) catch continue; defer dir.close(io); var it = dir.iterate(); while (it.next(io) catch null) |entry| { if (!std.mem.eql(u8, std.fs.path.extension(entry.name), ".gir")) continue; _ = gir_files.addCopyFile(.{ .cwd_relative = b.pathJoin(&.{ dir_path, entry.name }) }, entry.name); } } translate_gir.addPrefixedDirectoryArg("--gir-dir=", gir_files.getDirectory()); // translate-gir follows the dependencies of what it is asked for. translate_gir.addArgs(&.{ "Nautilus-4.1", "Adw-1" }); translate_gir.expectExitCode(0);
const compat = b.createModule(.{ .root_source_file = bindings.path(b, "src/compat/compat.zig"), .target = target, .optimize = optimize, });
var result: GObjectBindings = .{ .modules = .empty }; for (gobject_modules) |entry| { const name, const libraries = entry; const m = b.createModule(.{ .root_source_file = bindings.path(b, b.fmt("src/{s}/{s}.zig", .{ name, name })), .target = target, .optimize = optimize, .link_libc = true, }); for (libraries) |library| m.linkSystemLibrary(library, .{ .use_pkg_config = .yes }); m.addImport("compat", compat); result.modules.put(b.allocator, name, m) catch @panic("OOM"); } var it = result.modules.iterator(); while (it.next()) |a| { var jt = result.modules.iterator(); while (jt.next()) |c| a.value_ptr.*.addImport(c.key_ptr.*, c.value_ptr.*); } return result;}