diff --git a/.github/workflows/zig-build.yaml b/.github/workflows/zig-build.yaml index be64846..d10cd30 100644 --- a/.github/workflows/zig-build.yaml +++ b/.github/workflows/zig-build.yaml @@ -14,6 +14,8 @@ jobs: with: version: 0.12.0 - uses: Hanaasagi/zig-action-cache@v1.1.5 + - name: Install fontconfig + run: apt update && apt install libfontconfig1 && ln -s /usr/lib/x86_64-linux-gnu/libfontconfig.so.1 /usr/local/lib/libfontconfig.so - name: Build project run: zig build --summary all - name: Run tests diff --git a/Dockerfile b/Dockerfile index 49edd80..dd1433b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ RUN apt-get update \ ca-certificates \ curl \ xz-utils \ - && curl https://ziglang.org/download/0.11.0/zig-linux-x86_64-0.11.0.tar.xz | tar -C /usr/local/ -xJ \ + && curl https://ziglang.org/download/0.12.0/zig-linux-x86_64-0.12.0.tar.xz | tar -C /usr/local/ -xJ \ && apt-get -y remove curl xz-utils \ && ln -s /usr/local/zig*/zig /usr/local/bin \ && rm -rf /var/lib/apt/lists/* diff --git a/README.md b/README.md index 03c34c1..21c95e0 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,9 @@ Building This is not fully `zig build` friendly, since it links to system libraries. Specifically, you need to have [fontconfig](https://www.freedesktop.org/wiki/Software/fontconfig/) -and dependencies installed. I initially went down that rabbit hole, but it's -kind of a mess that I don't need for what is really a personal project. +and dependencies installed. The include files are pulled from fontconfig 2.14.1, +which is the version in debian bullseye, but he system library still needs to +linked. To help with the build, a Dockerfile exists in this repository that can be used to create a docker image with the appropriate zig version and system libraries. diff --git a/build.zig b/build.zig index 3588a9a..2a31920 100644 --- a/build.zig +++ b/build.zig @@ -24,7 +24,9 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }); - configure(exe); + const fontconfig = b.dependency("fontconfig", .{}); + + configure(b, fontconfig, exe); // This declares intent for the executable to be installed into the // standard location when the user invokes the "install" step (the default @@ -62,7 +64,7 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }); - configure(unit_tests); + configure(b, fontconfig, unit_tests); const run_unit_tests = b.addRunArtifact(unit_tests); @@ -73,14 +75,17 @@ pub fn build(b: *std.Build) void { test_step.dependOn(&run_unit_tests.step); } -fn configure(object: anytype) void { +fn configure(b: *std.Build, fontconfig: *std.Build.Dependency, object: anytype) void { // object.linkage = .static; object.linkLibC(); // Fontconfig must be installed. Docker can also be used (see Dockerfile) - object.addSystemIncludePath("/usr/include"); + object.addSystemIncludePath(fontconfig.path("")); object.linkSystemLibrary("fontconfig"); // object.linkSystemLibrary("expat"); // fontconfig dependency - needed for static builds - object.addLibraryPath("/usr/lib"); - object.addCSourceFile("src/fontconfig.c", &[_][]const u8{"-std=c99"}); + object.addLibraryPath(.{ .cwd_relative = "/usr/lib" }); + object.addCSourceFile(.{ + .file = b.path("src/fontconfig.c"), + .flags = &[_][]const u8{"-std=c99"}, + }); } diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..2f5b43b --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,36 @@ +.{ + .name = "fontfinder", + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // 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 = .{ + .fontconfig = .{ + .url = "https://gitlab.freedesktop.org/fontconfig/fontconfig/-/archive/2.14.1/fontconfig-2.14.1.tar.gz", + .hash = "1220a54d89babad491b562dad3f2c73414c95ea2f46a4e7206f1e36be4632654458e", + }, + }, + .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", + //"LICENSE", + //"README.md", + }, +} diff --git a/src/fontconfig.zig b/src/fontconfig.zig index 3cf5c62..fe11c30 100644 --- a/src/fontconfig.zig +++ b/src/fontconfig.zig @@ -35,7 +35,7 @@ pub const FontList = struct { const Self = @This(); pub fn initCapacity(allocator: std.mem.Allocator, num: usize, pattern: *c.FcPattern, fontset: *c.FcFontSet) std.mem.Allocator.Error!Self { - var al = try std.ArrayList(Font).initCapacity(allocator, num); + const al = try std.ArrayList(Font).initCapacity(allocator, num); return Self{ .allocator = allocator, .list = al, @@ -192,7 +192,7 @@ pub const FontQuery = struct { var rc = std.ArrayList(RangeFont).init(self.allocator); defer rc.deinit(); - var previously_supported = blk: { + const previously_supported = blk: { if (!exclude_previous) break :blk null; var al = try std.ArrayList(bool).initCapacity(self.allocator, ending_codepoint - starting_codepoint); defer al.deinit(); @@ -267,7 +267,7 @@ test "Get fonts" { var fl = try fq.fontList(":regular:normal:spacing=100:slant=0"); defer fl.deinit(); try std.testing.expect(fl.list.items.len > 0); - var matched = blk: { + const matched = blk: { for (fl.list.items) |item| { log.debug("full_name: '{s}'", .{item.full_name}); if (std.mem.eql(u8, "DejaVu Sans Mono", item.full_name)) diff --git a/src/main.zig b/src/main.zig index 44d12f6..8967c83 100644 --- a/src/main.zig +++ b/src/main.zig @@ -52,7 +52,7 @@ pub fn main() !u8 { if (options.list_fonts) { var fq = fontconfig.FontQuery.init(allocator); defer fq.deinit(); - var fl = try fq.fontList(options.pattern); + const fl = try fq.fontList(options.pattern); var longest_family_name = @as(usize, 0); var longest_style_name = @as(usize, 0); for (fl.list.items) |f| { @@ -81,7 +81,7 @@ pub fn main() !u8 { var si = std.mem.splitScalar(u8, fo, ','); var fq = fontconfig.FontQuery.init(allocator); defer fq.deinit(); - var fl = try fq.fontList(options.pattern); + const fl = try fq.fontList(options.pattern); // This messes with data after, and we don't need to deinit anyway // defer fl.deinit(); var al = try std.ArrayList(fontconfig.Font).initCapacity(allocator, std.mem.count(u8, fo, ",") + 2); @@ -184,7 +184,7 @@ fn outputRange( ) !void { var fq = fontconfig.FontQuery.init(allocator); defer fq.deinit(); - var range_fonts = try fq.fontsForRange(starting_codepoint, ending_codepoint, fonts, exclude_previous); // do we want hard limits around this? + const range_fonts = try fq.fontsForRange(starting_codepoint, ending_codepoint, fonts, exclude_previous); // do we want hard limits around this? defer allocator.free(range_fonts); std.log.debug("Got {d} range fonts back from query", .{range_fonts.len}); @@ -323,7 +323,7 @@ test "command line parses with long name no equals" { try std.testing.expectEqualStrings("Latin-1", options.groups.?); } test "command line parses with long name equals" { - var log_level = std.testing.log_level; + const log_level = std.testing.log_level; defer std.testing.log_level = log_level; // std.testing.log_level = .debug; var it = try std.process.ArgIteratorGeneral(.{}).init(std.testing.allocator, "--groups=Latin-1"); @@ -339,7 +339,7 @@ test "Get ranges" { var fl = try fq.fontList(":regular:normal:spacing=100:slant=0"); defer fl.deinit(); try std.testing.expect(fl.list.items.len > 0); - var matched = blk: { + const matched = blk: { for (fl.list.items) |item| { std.log.debug("full_name: '{s}'", .{item.full_name}); if (std.mem.eql(u8, "DejaVu Sans Mono", item.full_name)) @@ -352,7 +352,7 @@ test "Get ranges" { var al = std.ArrayList(u8).init(std.testing.allocator); defer al.deinit(); const range_name = "Basic Latin"; - var matched_range = try blk: { + const matched_range = try blk: { var unicode_ranges = unicode.all_ranges(); while (unicode_ranges.next()) |range| { var it = std.mem.splitScalar(u8, range_name, ','); @@ -364,7 +364,7 @@ test "Get ranges" { } break :blk error.RangeNotFound; }; - var log_level = std.testing.log_level; + const log_level = std.testing.log_level; std.testing.log_level = .debug; defer std.testing.log_level = log_level; try outputRange(