Skip to content

Commit

Permalink
Zig package manager-fication of
Browse files Browse the repository at this point in the history
  • Loading branch information
Durobot committed Mar 6, 2024
1 parent f69c0ed commit 7a9764f
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 17 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 Alexei Kireev
Copyright (c) 2023, 2024 Alexei Kireev

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,35 @@ PPM and PGM are very simple graphic file formats, but they are widespread enough

See https://en.wikipedia.org/wiki/Netpbm?useskin=vector, https://netpbm.sourceforge.net/doc/ppm.html.

Check out the tests in `export_ppm.zig` for examples of use.

**test_data_src** folder contains PNG images I used as test data, ignore them or use them however you want. I release them in public domain.

**export_ppm.zig** is licensed under [the MIT License](https://en.wikipedia.org/w/index.php?title=MIT_License&useskin=vector).
**export_ppm.zig** is licensed under [the MIT License](https://en.wikipedia.org/w/index.php?title=MIT_License&useskin=vector).

Just drop `export_ppm.zig` into your project and add `const eppm = @import("export_ppm.zig");`, or use the Zig package manager:

1. In your project's `build.zig.zon`, in `.dependencies`, add

```zig
.export_ppm =
.{
.url = "https://github.com/Durobot/export_ppm/archive/<GIT COMMIT HASH, 40 HEX DIGITS>.tar.gz",
.hash = "<ZIG PACKAGE HASH, 68 HEX DIGITS>" // Use arbitrary hash, get correct hash from the error
}
```

2. In your project's `build.zig`, in `pub fn build`, before `b.installArtifact(exe);`, add

```zig
const eppm = b.dependency("export_ppm",
.{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("export_ppm", eppm.module("export_ppm"));
```

3. Add `const eppm = @import("export_ppm");`in your source file(s).

4. Build your project with `zig build`, as you normally do.
56 changes: 56 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const std = @import("std");

// Although this function looks imperative, note that its job is to
// declaratively construct a build graph that will be executed by an external
// runner.
pub fn build(b: *std.Build) void
{
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});

// Standard optimization options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
// set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{});

_ = b.addModule("export_ppm", // package_name
.{
.root_source_file = .{ .path = "src/export_ppm.zig" },
.target = target,
.optimize = optimize,
});

const lib = b.addStaticLibrary(
.{
.name = "export_ppm",
// In this case the main source file is merely a path, however, in more
// complicated build scripts, this could be a generated file.
.root_source_file = .{ .path = "src/export_ppm.zig" },
.target = target,
.optimize = optimize,
});

// This declares intent for the library to be installed into the standard
// location when the user invokes the "install" step (the default step when
// running `zig build`).
b.installArtifact(lib);

// Creates a step for unit testing. This only builds the test executable
// but does not run it.
const lib_unit_tests = b.addTest(
.{
.root_source_file = .{ .path = "src/export_ppm.zig" },
.target = target,
.optimize = optimize,
});
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);

// Similar to creating the run step earlier, this exposes a `test` step to
// the `zig build --help` menu, providing a way for the user to request
// running the unit tests.
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_lib_unit_tests.step);
}
40 changes: 40 additions & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.{
.name = "export_ppm",
// This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication.
.version = "0.1.0",

// This field is optional.
// This is currently advisory only; Zig does not yet do anything
// with this value.
.minimum_zig_version = "0.12.0-dev.3097+5c0766b6c",

// This field is optional.
// Each dependency must either provide a `url` and `hash`, or a `path`.
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
// Once all dependencies are fetched, `zig build` no longer requires
// internet connectivity.
.dependencies = .{},

// Specifies the set of files and directories that are included in this package.
// Only files and directories listed here are included in the `hash` that
// is computed for this package.
// Paths are relative to the build root. Use the empty string (`""`) to refer to
// the build root itself.
// A directory listed here means that all files within, recursively, are included.
.paths =
.{
// This makes *all* files, recursively, included in this package. It is generally
// better to explicitly list the files and directories instead, to insure that
// fetching from tarballs, file system paths, and version control all result
// in the same contents hash.
//"",
// For example...
"build.zig",
"build.zig.zon",
"src",
"test_data_src",
"LICENSE",
"README.md",
},
}
32 changes: 17 additions & 15 deletions export_ppm.zig → src/export_ppm.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// MIT License
//
// Copyright (c) 2023 Alexei Kireev
// Copyright (c) 2023, 2024 Alexei Kireev
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -52,20 +52,21 @@ pub const ChannelSize = enum
/// clr_type - Image color type.
/// ch_size - How many bits is every image channel (R, G, B, Alpha) wide. 8 and 16 bits per channel
/// images are supported.
/// alloc8r - Allocator, used for small buffers used by the function.
/// allocr - Allocator, used to allocate small buffers used by the function. Those are freed before
/// the function returns.
pub fn exportBinaryPpm(fname: []const u8, img_data: []const u8, img_w: u32, img_h: u32,
clr_type: ColorType, ch_size: ChannelSize,
alloc8r: std.mem.Allocator) !void
allocr: std.mem.Allocator) !void
{
const fext = switch (clr_type)
{
.gray, .graya => ".pgm",
.rgb, .rgba => ".ppm"
.rgb, .rgba => ".ppm"
};
const magic_num = switch (clr_type)
{
.gray, .graya => "P5",
.rgb, .rgba => "P6"
.rgb, .rgba => "P6"
};
const maxval: u16 = switch (ch_size)
{
Expand All @@ -74,8 +75,8 @@ pub fn exportBinaryPpm(fname: []const u8, img_data: []const u8, img_w: u32, img_
};

// Allocate at least 64 bytes so that we can reuse the buffer for the text part of the file, see below
const tmp_buf = try alloc8r.alloc(u8, if (fname.len + fext.len > 64) fname.len + fext.len else 64);
defer alloc8r.free(tmp_buf);
const tmp_buf = try allocr.alloc(u8, if (fname.len + fext.len > 64) fname.len + fext.len else 64);
defer allocr.free(tmp_buf);
const full_fname_sl = try std.fmt.bufPrint(tmp_buf, "{s}{s}", .{ fname, fext });
// Whether the file will be created with read access -----v
var f = try std.fs.cwd().createFile(full_fname_sl, .{ .read = false });
Expand Down Expand Up @@ -105,8 +106,8 @@ pub fn exportBinaryPpm(fname: []const u8, img_data: []const u8, img_w: u32, img_
const compacted_row_num_pixel_bytes = img_w * bytes_pixel_wo_alpha; // Don't add +1 byte for filter type

// Allocate a buffer for one row of pixels without alpha channel
const compacted_buf = try alloc8r.alloc(u8, compacted_row_num_pixel_bytes);
defer alloc8r.free(compacted_buf);
const compacted_buf = try allocr.alloc(u8, compacted_row_num_pixel_bytes);
defer allocr.free(compacted_buf);

const bytes_per_pixel = bytes_per_chan * chan_num;
const row_num_bytes = img_w * bytes_per_pixel;
Expand Down Expand Up @@ -140,20 +141,21 @@ pub fn exportBinaryPpm(fname: []const u8, img_data: []const u8, img_w: u32, img_
/// clr_type - Image color type.
/// ch_size - How many bits is every image channel (R, G, B, Alpha) wide. 8 and 16 bits per channel
/// images are supported.
/// alloc8r - Allocator, used for small buffers used by the function.
/// allocr - Allocator, used to allocate small buffers used by the function. Those are freed before
/// the function returns.
pub fn exportAsciiPpm(fname: []const u8, img_data: []const u8, img_w: u32, img_h: u32,
clr_type: ColorType, ch_size: ChannelSize,
alloc8r: std.mem.Allocator) !void
allocr: std.mem.Allocator) !void
{
const fext = switch (clr_type)
{
.gray, .graya => ".pgm",
.rgb, .rgba => ".ppm"
.rgb, .rgba => ".ppm"
};
const magic_num = switch (clr_type)
{
.gray, .graya => "P2",
.rgb, .rgba => "P3"
.rgb, .rgba => "P3"
};
const maxval: u16 = switch (ch_size)
{
Expand All @@ -162,8 +164,8 @@ pub fn exportAsciiPpm(fname: []const u8, img_data: []const u8, img_w: u32, img_h
};

// Allocate at least 64 bytes so that we can reuse the buffer for the text part of the file, see below
const tmp_buf = try alloc8r.alloc(u8, if (fname.len + fext.len > 64) fname.len + fext.len else 64);
defer alloc8r.free(tmp_buf);
const tmp_buf = try allocr.alloc(u8, if (fname.len + fext.len > 64) fname.len + fext.len else 64);
defer allocr.free(tmp_buf);
const full_fname_sl = try std.fmt.bufPrint(tmp_buf, "{s}{s}", .{ fname, fext });
// Whether the file will be created with read access -----v
var f = try std.fs.cwd().createFile(full_fname_sl, .{ .read = false });
Expand Down

0 comments on commit 7a9764f

Please sign in to comment.