Skip to content

Commit

Permalink
handle updated ziget to get Windows schannel support
Browse files Browse the repository at this point in the history
  • Loading branch information
marler8997 committed Feb 20, 2021
1 parent 639d31a commit ec3c3d6
Show file tree
Hide file tree
Showing 2 changed files with 248 additions and 42 deletions.
61 changes: 19 additions & 42 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,20 @@ fn unwrapOptionalBool(optionalBool: ?bool) bool {
}

pub fn build(b: *Builder) !void {
const ssl_backend: enum { openssl, iguana } = init: {
const openssl = unwrapOptionalBool(b.option(bool, "openssl", "enable OpenSSL backend"));
const iguana = unwrapOptionalBool(b.option(bool, "iguana", "enable IguanaTLS backend"));
if (openssl and iguana) {
std.log.err("both '-Dopenssl' and '-Diguana' cannot be enabled at the same time", .{});
std.os.exit(1);
}
if (openssl) break :init .openssl;
if (iguana) break :init .iguana;
const ziget_repo = try getGitRepo(b.allocator, "https://github.com/marler8997/ziget");

const zigetbuild = @import("zigetbuild.zig");
// TODO: with @tryImport this will become something like
// const zigetbuild = @tryImport("zigetbuild") orelse {
// // TODO: recompile build.zig and add zigetbuild as package
// };
//

std.log.err("please enable an ssl backend with either '-Dopenssl' or '-Diguana'", .{});
const ssl_backend = zigetbuild.getSslBackend(b) orelse {
std.debug.print("error: an SSL backend must be enabled with one of:\n", .{});
inline for (zigetbuild.ssl_backends) |field, i| {
std.debug.print(" -D{s}\n", .{field.name});
}
std.os.exit(1);
};

Expand All @@ -35,38 +38,12 @@ pub fn build(b: *Builder) !void {
const exe = b.addExecutable("zigup", "zigup.zig");
exe.setTarget(target);
exe.setBuildMode(mode);

//
// TODO: figure out how to use ziget's build.zig file
//
const ziget_repo = try getGitRepo(b.allocator, "https://github.com/marler8997/ziget");
const ssl_pkg = init: { switch (ssl_backend) {
.openssl => {
// these libraries are required for openssl
exe.linkSystemLibrary("c");
exe.linkSystemLibrary("ssl");
exe.linkSystemLibrary("crypto");
break :init Pkg {
.name = "ssl",
.path = try join(b, &[_][]const u8 {ziget_repo, "openssl", "ssl.zig"}),
};
},
.iguana => {
const iguana_repo = try getGitRepo(b.allocator, "https://github.com/alexnask/iguanaTLS");
const iguana_index_file = try join(b, &[_][]const u8 {iguana_repo, "src", "main.zig"});
break :init Pkg {
.name = "ssl",
.path = try join(b, &[_][]const u8 {ziget_repo, "iguana", "ssl.zig"}),
.dependencies = &[_]Pkg {
.{ .name = "iguana", .path = iguana_index_file },
},
};
}
}};
exe.addPackage(.{
exe.addPackage(Pkg {
.name = "ziget",
.path = try join(b, &[_][]const u8 {ziget_repo, "ziget.zig"}),
.dependencies = &[_]Pkg { ssl_pkg },
.path = try join(b, &[_][]const u8 { ziget_repo, "ziget.zig" }),
.dependencies = &[_]Pkg {
try zigetbuild.addSslBackend(exe, ssl_backend, ziget_repo),
},
});
exe.install();

Expand All @@ -81,7 +58,7 @@ fn join(b: *Builder, parts: []const []const u8) ![]const u8 {
return try std.fs.path.join(b.allocator, parts);
}

fn getGitRepo(allocator: *std.mem.Allocator, url: []const u8) ![]const u8 {
pub fn getGitRepo(allocator: *std.mem.Allocator, url: []const u8) ![]const u8 {
const repo_path = init: {
const cwd = try std.process.getCwdAlloc(allocator);
defer allocator.free(cwd);
Expand Down
229 changes: 229 additions & 0 deletions zigetbuild.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
//
// NOTE: this build.zig file is copied directly from the ziget repo
// this copy can be removed if @tryImport is accepted
// see https://github.com/ziglang/zig/pull/8033
//
const std = @import("std");
const Builder = std.build.Builder;
const Pkg = std.build.Pkg;

pub fn build(b: *Builder) !void {
const optional_ssl_backend = getSslBackend(b);

const target = b.standardTargetOptions(.{});
const mode = b.standardReleaseOptions();

const exe = b.addExecutable("ziget", "ziget-cmdline.zig");
exe.setTarget(target);
exe.single_threaded = true;
exe.setBuildMode(mode);
exe.addPackage(
if (optional_ssl_backend) |ssl_backend| try addSslBackend(exe, ssl_backend, ".")
else Pkg { .name = "ssl", .path = "nossl/ssl.zig" }
);
exe.install();

const run_cmd = exe.run();
run_cmd.step.dependOn(b.getInstallStep());

const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);

addTests(b, target, mode);
}

fn addTests(b: *Builder, target: std.zig.CrossTarget, mode: std.builtin.Mode) void {
const test_exe = b.addExecutable("test", "test.zig");
test_exe.setTarget(target);
test_exe.setBuildMode(mode);

const test_step = b.step("test", "Run all the 'Enabled' tests");
inline for (ssl_backends) |field, i| {
const enum_value = @field(SslBackend, field.name);
const enabled_by_default =
if (enum_value == .wolfssl) false
else if (enum_value == .schannel and std.builtin.os.tag != .windows) false
else true;
addTest(test_step, test_exe, field.name, enabled_by_default);
}
addTest(test_step, test_exe, "nossl", true);
}

fn addTest(test_step: *std.build.Step, test_exe: *std.build.LibExeObjStep, comptime backend_name: []const u8, comptime enabled_by_default: bool) void {
const b = test_exe.builder;
const run_cmd = test_exe.run();
run_cmd.addArg(backend_name);
run_cmd.step.dependOn(b.getInstallStep());

const enabled_prefix = if (enabled_by_default) "Enabled " else "Disabled";
const test_backend_step = b.step("test-" ++ backend_name,
enabled_prefix ++ ": test ziget with the '" ++ backend_name ++ "' ssl backend");
test_backend_step.dependOn(&run_cmd.step);

if (enabled_by_default) {
test_step.dependOn(&run_cmd.step);
}
}

pub fn unwrapOptionalBool(optionalBool: ?bool) bool {
if (optionalBool) |b| return b;
return false;
}

pub const SslBackend = enum {
openssl,
wolfssl,
iguana,
schannel,
};
pub const ssl_backends = @typeInfo(SslBackend).Enum.fields;

pub fn getSslBackend(b: *Builder) ?SslBackend {

var backend: ?SslBackend = null;

var backend_infos : [ssl_backends.len]struct {
enabled: bool,
name: []const u8,
} = undefined;
var backend_enabled_count: u32 = 0;
inline for (ssl_backends) |field, i| {
const enabled = unwrapOptionalBool(b.option(bool, field.name, "enable ssl backend: " ++ field.name));
if (enabled) {
backend = @field(SslBackend, field.name);
backend_enabled_count += 1;
}
backend_infos[i] = .{
.enabled = enabled,
.name = field.name,
};
}
if (backend_enabled_count > 1) {
std.log.err("only one ssl backend may be enabled, can't provide these options at the same time:", .{});
for (backend_infos) |info| {
if (info.enabled) {
std.log.err(" -D{s}", .{info.name});
}
}
std.os.exit(1);
}
return backend;
}

//
// NOTE: the ziget_repo argument is here so this function can be used by other projects, not just this repo
//
pub fn addSslBackend(step: *std.build.LibExeObjStep, backend: SslBackend, ziget_repo: []const u8) !Pkg {
const b = step.builder;
switch (backend) {
.openssl => {
step.linkSystemLibrary("c");
if (std.builtin.os.tag == .windows) {
step.linkSystemLibrary("libcrypto");
step.linkSystemLibrary("libssl");
try setupOpensslWindows(step);
} else {
step.linkSystemLibrary("crypto");
step.linkSystemLibrary("ssl");
}
return Pkg {
.name = "ssl",
.path = try std.fs.path.join(b.allocator, &[_][]const u8 { ziget_repo, "openssl/ssl.zig" }),
};
},
.wolfssl => {
std.log.err("-Dwolfssl is not implemented", .{});
std.os.exit(1);
},
.iguana => {
const iguana_index_file = try getGitRepoFile(b.allocator,
"https://github.com/alexnask/iguanaTLS",
"src" ++ std.fs.path.sep_str ++ "main.zig");
return Pkg {
.name = "ssl",
.path = try std.fs.path.join(b.allocator, &[_][]const u8 { ziget_repo, "iguana", "ssl.zig" }),
.dependencies = &[_]Pkg {
.{ .name = "iguana", .path = iguana_index_file },
},
};
},
.schannel => {
{
// NOTE: for now I'm using msspi from https://github.com/deemru/msspi
// I'll probably port this to Zig at some point
// Once I do remove this build config
// NOTE: I tested using this commit: 7338760a4a2c6fb80c47b24a2abba32d5fc40635 tagged at version 0.1.42
const msspi_repo = try getGitRepo(b.allocator, "https://github.com/deemru/msspi");
const msspi_src_dir = try std.fs.path.join(b.allocator, &[_][]const u8 { msspi_repo, "src" });
const msspi_main_cpp = try std.fs.path.join(b.allocator, &[_][]const u8 { msspi_src_dir, "msspi.cpp" });
const msspi_third_party_include = try std.fs.path.join(b.allocator, &[_][]const u8 { msspi_repo, "third_party", "cprocsp", "include" });
step.addCSourceFile(msspi_main_cpp, &[_][]const u8 { });
step.addIncludeDir(msspi_src_dir);
step.addIncludeDir(msspi_third_party_include);
step.linkLibC();
step.linkSystemLibrary("ws2_32");
step.linkSystemLibrary("crypt32");
step.linkSystemLibrary("advapi32");
}
// TODO: this will be needed if/when msspi is ported to Zig
//const zigwin32_index_file = try getGitRepoFile(b.allocator,
// "https://github.com/marlersoft/zigwin32",
// "src" ++ std.fs.path.sep_str ++ "win32.zig");
return Pkg {
.name = "ssl",
.path = try std.fs.path.join(b.allocator, &[_][]const u8 { ziget_repo, "schannel", "ssl.zig" }),
//.dependencies = &[_]Pkg {
// .{ .name = "win32", .path = zigwin32_index_file },
//},
};
}
}
}

pub fn setupOpensslWindows(step: *std.build.LibExeObjStep) !void {
const b = step.builder;
const openssl_path = b.option([]const u8, "openssl-path", "path to openssl (for Windows)") orelse {
std.debug.print("Error: -Dopenssl on windows requires -Dopenssl-path=DIR to be specified\n", .{});
std.os.exit(1);
};
// NOTE: right now these files are hardcoded to the files expected when installing SSL via
// this web page: https://slproweb.com/products/Win32OpenSSL.html and installed using
// this exe installer: https://slproweb.com/download/Win64OpenSSL-1_1_1g.exe
step.addIncludeDir(try std.fs.path.join(b.allocator, &[_][]const u8 {openssl_path, "include"}));
step.addLibPath(try std.fs.path.join(b.allocator, &[_][]const u8 {openssl_path, "lib"}));
// install dlls to the same directory as executable
for ([_][]const u8 {"libcrypto-1_1-x64.dll", "libssl-1_1-x64.dll"}) |dll| {
step.step.dependOn(
&b.addInstallFileWithDir(
try std.fs.path.join(b.allocator, &[_][]const u8 {openssl_path, dll}),
.Bin,
dll,
).step
);
}
}

pub fn getGitRepo(allocator: *std.mem.Allocator, url: []const u8) ![]const u8 {
const repo_path = init: {
const cwd = try std.process.getCwdAlloc(allocator);
defer allocator.free(cwd);
break :init try std.fs.path.join(allocator,
&[_][]const u8{ std.fs.path.dirname(cwd).?, std.fs.path.basename(url) }
);
};
errdefer allocator.free(repo_path);

std.fs.accessAbsolute(repo_path, std.fs.File.OpenFlags { .read = true }) catch |err| {
std.debug.print("Error: repository '{s}' does not exist\n", .{repo_path});
std.debug.print(" Run the following to clone it:\n", .{});
std.debug.print(" git clone {s} {s}\n", .{url, repo_path});
std.os.exit(1);
};
return repo_path;
}

pub fn getGitRepoFile(allocator: *std.mem.Allocator, url: []const u8, index_sub_path: []const u8) ![]const u8 {
const repo_path = try getGitRepo(allocator, url);
defer allocator.free(repo_path);
return try std.fs.path.join(allocator, &[_][]const u8 { repo_path, index_sub_path });
}

0 comments on commit ec3c3d6

Please sign in to comment.