diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..37525e2 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,43 @@ +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/.gitignore b/.gitignore new file mode 100644 index 0000000..d8c8979 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.zig-cache +zig-out diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..7177203 --- /dev/null +++ b/build.zig @@ -0,0 +1,95 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const libssh2_dep = b.dependency("libssh2", .{ + .target = target, + .optimize = optimize, + }); + + const mbedtls_dep = b.dependency("mbedtls", .{ + .target = target, + .optimize = optimize, + }); + + const lib = b.addStaticLibrary(.{ + .name = "ssh2", + .target = target, + .optimize = optimize, + .link_libc = true, + }); + 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); + + 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 (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); + } + + b.installArtifact(lib); +} diff --git a/build.zig.zon b/build.zig.zon new file mode 100644 index 0000000..8f4b862 --- /dev/null +++ b/build.zig.zon @@ -0,0 +1,43 @@ +.{ + // 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. + .dependencies = .{ + .libssh2 = .{ + .url = "https://github.com/libssh2/libssh2/archive/refs/tags/libssh2-1.11.0.tar.gz", + .hash = "1220a0863be6190270168974107d04653087aacc89e72cd81914789cb7d84b744fda", + }, + .mbedtls = .{ + .url = "git+https://github.com/allyourcodebase/mbedtls.git#e4da72f6a8bedc883e34953514a3b62cbbd4f251", + .hash = "1220ffeef9740c79b6f62f6bf350f12a7b14768acf548cb47cd56c5ca58a15af7970", + }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/config/libssh2_config.h b/config/libssh2_config.h new file mode 100644 index 0000000..eb30d10 --- /dev/null +++ b/config/libssh2_config.h @@ -0,0 +1,10 @@ +#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 new file mode 100644 index 0000000..c8a3f67 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,24 @@ +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 new file mode 100644 index 0000000..ecfeade --- /dev/null +++ b/src/root.zig @@ -0,0 +1,10 @@ +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); +}