From e50c80e75423bf3f3c2a1af6ead3704f770df11e Mon Sep 17 00:00:00 2001 From: Kramer Hampton Date: Mon, 29 Sep 2025 07:10:45 -0400 Subject: [PATCH 1/2] feat(zig-0.15): clean up build process and update to Zig 0.15 --- .github/workflows/build.yml | 43 -------- .github/workflows/ci.yaml | 36 +++++++ .gitignore | 1 + LICENSE | 21 ++++ README.md | 43 +++++++- build.zig | 204 ++++++++++++++++++++++-------------- build.zig.zon | 42 ++------ config/libssh2_config.h | 10 -- src/main.zig | 24 ----- src/root.zig | 10 -- 10 files changed, 235 insertions(+), 199 deletions(-) delete mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/ci.yaml create mode 100644 LICENSE delete mode 100644 config/libssh2_config.h delete mode 100644 src/main.zig delete mode 100644 src/root.zig diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 37525e2..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Build and Test -on: - push: - branches: [main] - pull_request: - branches: [main] - workflow_dispatch: - -jobs: - build: - strategy: - fail-fast: false - matrix: - os: [windows-latest, ubuntu-latest, macos-latest] - target: [ - native, - x86_64-linux-gnu, - x86_64-linux-musl, - x86_64-windows, - aarch64-macos, - aarch64-linux-gnu, - aarch64-linux-musl - # TODO: bsd - ] - optimize: [ - Debug, - ReleaseSafe, - ReleaseFast, - ReleaseSmall - ] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v2 - with: - submodules: recursive - - - name: Setup Zig - uses: mlugg/setup-zig@v1 - with: - version: 0.13.0 - - - name: Build - run: zig build -Dtarget=${{ matrix.target }} -Doptimize=${{ matrix.optimize }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..e717d94 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,36 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + build: + strategy: + fail-fast: false + matrix: + zig-version: [master] + os: [ubuntu-latest, macos-latest, windows-latest] + include: + - zig-version: "0.15.1" + os: ubuntu-latest + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Zig + uses: mlugg/setup-zig@v2 + with: + version: ${{ matrix.zig-version }} + + - name: Check Formatting + run: zig fmt --ast-check --check . + + - name: Build + run: zig build -Dstrip=true -Dzlib=true --summary all diff --git a/.gitignore b/.gitignore index d8c8979..8c9d17e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .zig-cache +zig-cache zig-out diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..019c51f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md index 6a1aeea..4af5feb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,44 @@ +[![CI](https://github.com/hamptokr/zig-libssh2/actions/workflows/ci.yaml/badge.svg)](https://github.com/hamptokr/zig-libssh2/actions) + # libssh2 -libssh2 packaged with Zig +This is [libssh2](https://github.com/libssh2/libssh2), packaged for [Zig](https://ziglang.org/). + +## Installation + +First, update your `build.zig.zon`: + +``` +# Initialize a `zig build` project if you haven't already +zig init +zig fetch --save git+https://github.com/hamptokr/zig-libssh2.git#libssh2-1.11.1 +``` + +You can then import `libssh2` in your `build.zig` with: + +```zig +const libssh2_dependency = b.dependency("libssh2", .{ + .target = target, + .optimize = optimize, +}); +your_exe.linkLibrary(libssh2_dependency.artifact("ssh2")); +``` + +## Build Options + +`libssh2` offers a few options which you can control like so: + +```zig +const libssh2_dependency = b.dependency("libssh2", .{ + .target = target, + .optimize = optimize, + .zlib = true, // links to zlib for payload compression if enabled (default=true) + .strip = true, // Strip debug information (default=false) + .linkage = .static, // Whether to link statically or dynamically (default=static) + .@"crypto-backend" = .auto, // auto will to default to wincng on windows, openssl everywhere else. (default=auto) +}); +``` + +``` + +``` diff --git a/build.zig b/build.zig index 7177203..7312774 100644 --- a/build.zig +++ b/build.zig @@ -1,95 +1,141 @@ const std = @import("std"); +const version: std.SemanticVersion = .{ .major = 1, .minor = 11, .patch = 1 }; + +const CryptoBackend = enum { + auto, + openssl, + mbedtls, + libgcrypt, + wincng, +}; + pub fn build(b: *std.Build) void { + const upstream = b.dependency("libssh2", .{}); const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); - const libssh2_dep = b.dependency("libssh2", .{ - .target = target, - .optimize = optimize, - }); + const linkage = b.option(std.builtin.LinkMode, "linkage", "Link mode") orelse .static; + const strip = b.option(bool, "strip", "Omit debug information"); + const pic = b.option(bool, "pie", "Produce Position Independent Code"); + + const crypto_choice = b.option(CryptoBackend, "crypto-backend", "Crypto backend: auto|openssl|mbedtls|libgcrypt|wincng") orelse .auto; + const zlib = b.option(bool, "zlib", "Enable SSH payload compression (links zlib)") orelse false; + + const is_windows = target.result.os.tag == .windows; + const mbedtls = crypto_choice == .mbedtls; + const openssl = (crypto_choice == .auto and !is_windows) or crypto_choice == .openssl; + const wincng = (crypto_choice == .auto and is_windows) or crypto_choice == .wincng; + const libgcrypt = crypto_choice == .libgcrypt; - const mbedtls_dep = b.dependency("mbedtls", .{ - .target = target, - .optimize = optimize, + const config_header = b.addConfigHeader(.{ + .style = .{ + .cmake = upstream.path("src/libssh2_config_cmake.h.in"), + }, + .include_path = "libssh2_config.h", + }, .{ + .LIBSSH2_API = switch (target.result.os.tag) { + .windows => "__declspec(dllexport)", + else => "", + }, + .LIBSSH2_HAVE_ZLIB = zlib, + .HAVE_SYS_UIO_H = !is_windows, + .HAVE_WRITEV = !is_windows, + .HAVE_SYS_SOCKET_H = !is_windows, + .HAVE_NETINET_IN_H = !is_windows, + .HAVE_ARPA_INET_H = !is_windows, + .HAVE_SYS_TYPES_H = !is_windows, + .HAVE_INTTYPES_H = true, + .HAVE_STDINT_H = true, }); - const lib = b.addStaticLibrary(.{ + const ssh2_lib = b.addLibrary(.{ + .version = version, .name = "ssh2", - .target = target, - .optimize = optimize, - .link_libc = true, + .linkage = linkage, + .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + .link_libc = true, + .strip = strip, + .pic = pic, + }), }); - lib.addIncludePath(libssh2_dep.path("include")); - lib.linkLibrary(mbedtls_dep.artifact("mbedtls")); - lib.addCSourceFiles(.{ - .root = libssh2_dep.path("src"), - .flags = &.{}, - .files = &.{ - "channel.c", - "comp.c", - "crypt.c", - "hostkey.c", - "kex.c", - "mac.c", - "misc.c", - "packet.c", - "publickey.c", - "scp.c", - "session.c", - "sftp.c", - "userauth.c", - "transport.c", - "version.c", - "knownhost.c", - "agent.c", - "mbedtls.c", - "pem.c", - "keepalive.c", - "global.c", - "blowfish.c", - "bcrypt_pbkdf.c", - "agent_win.c", - }, - }); - lib.installHeader(b.path("config/libssh2_config.h"), "libssh2_config.h"); - lib.installHeadersDirectory(libssh2_dep.path("include"), ".", .{}); - lib.defineCMacro("LIBSSH2_MBEDTLS", null); + b.installArtifact(ssh2_lib); + ssh2_lib.installHeadersDirectory(upstream.path("include"), "", .{}); + ssh2_lib.root_module.addConfigHeader(config_header); + ssh2_lib.root_module.addIncludePath(upstream.path("include")); + ssh2_lib.root_module.addCMacro("HAVE_CONFIG_H", "1"); + ssh2_lib.root_module.addCSourceFiles(.{ .files = ssh2_src, .root = upstream.path(""), .flags = ssh2_flags }); - if (target.result.os.tag == .windows) { - lib.defineCMacro("_CRT_SECURE_NO_DEPRECATE", "1"); - lib.defineCMacro("HAVE_LIBCRYPT32", null); - lib.defineCMacro("HAVE_WINSOCK2_H", null); - lib.defineCMacro("HAVE_IOCTLSOCKET", null); - lib.defineCMacro("HAVE_SELECT", null); - lib.defineCMacro("LIBSSH2_DH_GEX_NEW", "1"); + if (mbedtls) { + ssh2_lib.root_module.addCSourceFile(.{ .file = upstream.path("src/mbedtls.c"), .flags = ssh2_flags }); + ssh2_lib.root_module.addCMacro("LIBSSH2_MBEDTLS", "1"); + ssh2_lib.linkSystemLibrary("mbedtls"); + ssh2_lib.linkSystemLibrary("mbedcrypto"); + ssh2_lib.linkSystemLibrary("mbedx509"); + } - if (target.result.isGnu()) { - lib.defineCMacro("HAVE_UNISTD_H", null); - lib.defineCMacro("HAVE_INTTYPES_H", null); - lib.defineCMacro("HAVE_SYS_TIME_H", null); - lib.defineCMacro("HAVE_GETTIMEOFDAY", null); - } - } else { - lib.defineCMacro("HAVE_UNISTD_H", null); - lib.defineCMacro("HAVE_INTTYPES_H", null); - lib.defineCMacro("HAVE_STDLIB_H", null); - lib.defineCMacro("HAVE_SYS_SELECT_H", null); - lib.defineCMacro("HAVE_SYS_UIO_H", null); - lib.defineCMacro("HAVE_SYS_SOCKET_H", null); - lib.defineCMacro("HAVE_SYS_IOCTL_H", null); - lib.defineCMacro("HAVE_SYS_TIME_H", null); - lib.defineCMacro("HAVE_SYS_UN_H", null); - lib.defineCMacro("HAVE_LONGLONG", null); - lib.defineCMacro("HAVE_GETTIMEOFDAY", null); - lib.defineCMacro("HAVE_INET_ADDR", null); - lib.defineCMacro("HAVE_POLL", null); - lib.defineCMacro("HAVE_SELECT", null); - lib.defineCMacro("HAVE_SOCKET", null); - lib.defineCMacro("HAVE_STRTOLL", null); - lib.defineCMacro("HAVE_SNPRINTF", null); - lib.defineCMacro("HAVE_O_NONBLOCK", null); + if (openssl) { + ssh2_lib.root_module.addCSourceFile(.{ .file = upstream.path("src/openssl.c"), .flags = ssh2_flags }); + ssh2_lib.root_module.addCMacro("LIBSSH2_OPENSSL", "1"); + ssh2_lib.linkSystemLibrary("ssl"); + ssh2_lib.linkSystemLibrary("crypto"); + } + + if (wincng) { + ssh2_lib.root_module.addCSourceFile(.{ .file = upstream.path("src/wincng.c"), .flags = ssh2_flags }); + ssh2_lib.root_module.addCMacro("LIBSSH2_WINCNG", "1"); + // Windows system libs (zig handles names) + ssh2_lib.linkSystemLibrary("bcrypt"); + ssh2_lib.linkSystemLibrary("ncrypt"); + } + + if (libgcrypt) { + ssh2_lib.root_module.addCSourceFile(.{ .file = upstream.path("src/libgcrypt.c"), .flags = ssh2_flags }); + ssh2_lib.root_module.addCMacro("LIBSSH2_LIBGCRYPT", "1"); + ssh2_lib.linkSystemLibrary("gcrypt"); } - b.installArtifact(lib); + if (zlib) { + if (b.systemIntegrationOption("zlib", .{})) { + ssh2_lib.root_module.linkSystemLibrary("zlib", .{}); + } else if (b.lazyDependency("zlib", .{ + .target = target, + .optimize = optimize, + })) |zlib_dependency| { + ssh2_lib.root_module.linkLibrary(zlib_dependency.artifact("z")); + } + } } + +pub const ssh2_src: []const []const u8 = &.{ + "src/agent.c", + "src/bcrypt_pbkdf.c", + "src/blowfish.c", + "src/chacha.c", + "src/channel.c", + "src/cipher-chachapoly.c", + "src/comp.c", + "src/crypt.c", + "src/global.c", + "src/hostkey.c", + "src/keepalive.c", + "src/kex.c", + "src/knownhost.c", + "src/mac.c", + "src/misc.c", + "src/packet.c", + "src/pem.c", + "src/poly1305.c", + "src/publickey.c", + "src/scp.c", + "src/session.c", + "src/sftp.c", + "src/transport.c", + "src/userauth.c", + "src/userauth_kbd_packet.c", + "src/version.c", +}; + +pub const ssh2_flags: []const []const u8 = &.{}; diff --git a/build.zig.zon b/build.zig.zon index 8f4b862..a180f8c 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,43 +1,21 @@ .{ - // This is the default name used by packages depending on this one. For - // example, when a user runs `zig fetch --save `, this field is used - // as the key in the `dependencies` table. Although the user can choose a - // different name, most users will stick with this provided value. - // - // It is redundant to include "zig" in this name because it is already - // within the Zig package namespace. - .name = "libssh2", - - // This is a [Semantic Version](https://semver.org/). - // In a future version of Zig it will be used for package deduplication. - .version = "1.11.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. + .name = .libssh2, + .version = "1.11.1", + .fingerprint = 0x57bdea04475e0726, // Changing this has security and trust implications. + .minimum_zig_version = "0.15.1", .dependencies = .{ .libssh2 = .{ - .url = "https://github.com/libssh2/libssh2/archive/refs/tags/libssh2-1.11.0.tar.gz", - .hash = "1220a0863be6190270168974107d04653087aacc89e72cd81914789cb7d84b744fda", + .url = "https://github.com/libssh2/libssh2/archive/refs/tags/libssh2-1.11.1.tar.gz", + .hash = "N-V-__8AAATRLQBu_rNy4X2UK6RtcdYK_yAzkV6OcCqgo5aK", }, - .mbedtls = .{ - .url = "git+https://github.com/allyourcodebase/mbedtls.git#e4da72f6a8bedc883e34953514a3b62cbbd4f251", - .hash = "1220ffeef9740c79b6f62f6bf350f12a7b14768acf548cb47cd56c5ca58a15af7970", + .zlib = .{ + .url = "git+https://github.com/allyourcodebase/zlib.git#61e7df7e996ec5a5f13a653db3c419adb340d6ef", + .hash = "zlib-1.3.1-ZZQ7lbYMAAB1hTSOKSXAKAgHsfDcyWNH_37ojw5WSpgR", + .lazy = true, }, }, .paths = .{ "build.zig", "build.zig.zon", - "src", - // For example... - //"LICENSE", - //"README.md", }, } diff --git a/config/libssh2_config.h b/config/libssh2_config.h deleted file mode 100644 index eb30d10..0000000 --- a/config/libssh2_config.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef LIBSSH2_CONFIG_H -#define LIBSSH2_CONFIG_H - -#ifdef WIN32 -#include -#include -#include -#endif - -#endif diff --git a/src/main.zig b/src/main.zig deleted file mode 100644 index c8a3f67..0000000 --- a/src/main.zig +++ /dev/null @@ -1,24 +0,0 @@ -const std = @import("std"); - -pub fn main() !void { - // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`) - std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); - - // stdout is for the actual output of your application, for example if you - // are implementing gzip, then only the compressed bytes should be sent to - // stdout, not any debugging messages. - const stdout_file = std.io.getStdOut().writer(); - var bw = std.io.bufferedWriter(stdout_file); - const stdout = bw.writer(); - - try stdout.print("Run `zig build test` to run the tests.\n", .{}); - - try bw.flush(); // don't forget to flush! -} - -test "simple test" { - var list = std.ArrayList(i32).init(std.testing.allocator); - defer list.deinit(); // try commenting this out and see if zig detects the memory leak! - try list.append(42); - try std.testing.expectEqual(@as(i32, 42), list.pop()); -} diff --git a/src/root.zig b/src/root.zig deleted file mode 100644 index ecfeade..0000000 --- a/src/root.zig +++ /dev/null @@ -1,10 +0,0 @@ -const std = @import("std"); -const testing = std.testing; - -export fn add(a: i32, b: i32) i32 { - return a + b; -} - -test "basic add functionality" { - try testing.expect(add(3, 7) == 10); -} From b77d4d8ef835294521c9c6d31ad0bae06550826c Mon Sep 17 00:00:00 2001 From: Kramer Hampton Date: Mon, 29 Sep 2025 07:20:23 -0400 Subject: [PATCH 2/2] fix(windows): use correct eol for Windows, update README link --- .gitattributes | 2 ++ README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..515471b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.zig text eol=lf +*.zon text eol=lf diff --git a/README.md b/README.md index 4af5feb..e59ff06 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ First, update your `build.zig.zon`: ``` # Initialize a `zig build` project if you haven't already zig init -zig fetch --save git+https://github.com/hamptokr/zig-libssh2.git#libssh2-1.11.1 +zig fetch --save git+https://github.com/allyourcodebase/libssh2.git#libssh2-1.11.1 ``` You can then import `libssh2` in your `build.zig` with: