build.zig 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. const std = @import("std");
  2. const builtin = @import("builtin");
  3. const Builder = std.build.Builder;
  4. const Pkg = std.build.Pkg;
  5. const GitRepoStep = @import("GitRepoStep.zig");
  6. pub fn build(b: *Builder) !void {
  7. // ensure we always support -Dfetch regardless of backend
  8. _ = GitRepoStep.defaultFetchOption(b);
  9. const optional_ssl_backend = getSslBackend(b);
  10. const target = b.standardTargetOptions(.{});
  11. const mode = b.standardReleaseOptions();
  12. const exe = b.addExecutable("ziget", "ziget-cmdline.zig");
  13. exe.setTarget(target);
  14. exe.single_threaded = true;
  15. exe.setBuildMode(mode);
  16. exe.addPackage(
  17. if (optional_ssl_backend) |ssl_backend| try addSslBackend(exe, ssl_backend, ".")
  18. else Pkg { .name = "ssl", .path = .{ .path = "nossl/ssl.zig" } }
  19. );
  20. exe.install();
  21. const run_cmd = exe.run();
  22. run_cmd.step.dependOn(b.getInstallStep());
  23. const run_step = b.step("run", "Run the app");
  24. run_step.dependOn(&run_cmd.step);
  25. addTests(b, target, mode);
  26. }
  27. fn addTests(b: *Builder, target: std.zig.CrossTarget, mode: std.builtin.Mode) void {
  28. const test_exe = b.addExecutable("test", "test.zig");
  29. test_exe.setTarget(target);
  30. test_exe.setBuildMode(mode);
  31. const test_step = b.step("test", "Run all the 'Enabled' tests");
  32. inline for (ssl_backends) |field| {
  33. const enum_value = @field(SslBackend, field.name);
  34. const enabled_by_default =
  35. if (enum_value == .wolfssl) false
  36. else if (enum_value == .schannel and builtin.os.tag != .windows) false
  37. else true;
  38. addTest(test_step, test_exe, field.name, enabled_by_default);
  39. }
  40. addTest(test_step, test_exe, "nossl", true);
  41. }
  42. fn addTest(test_step: *std.build.Step, test_exe: *std.build.LibExeObjStep, comptime backend_name: []const u8, comptime enabled_by_default: bool) void {
  43. const b = test_exe.builder;
  44. const run_cmd = test_exe.run();
  45. run_cmd.addArg(backend_name);
  46. run_cmd.step.dependOn(b.getInstallStep());
  47. const enabled_prefix = if (enabled_by_default) "Enabled " else "Disabled";
  48. const test_backend_step = b.step("test-" ++ backend_name,
  49. enabled_prefix ++ ": test ziget with the '" ++ backend_name ++ "' ssl backend");
  50. test_backend_step.dependOn(&run_cmd.step);
  51. if (enabled_by_default) {
  52. test_step.dependOn(&run_cmd.step);
  53. }
  54. }
  55. pub fn unwrapOptionalBool(optionalBool: ?bool) bool {
  56. if (optionalBool) |b| return b;
  57. return false;
  58. }
  59. pub const SslBackend = enum {
  60. openssl,
  61. opensslstatic,
  62. wolfssl,
  63. iguana,
  64. schannel,
  65. };
  66. pub const ssl_backends = @typeInfo(SslBackend).Enum.fields;
  67. pub fn getSslBackend(b: *Builder) ?SslBackend {
  68. var backend: ?SslBackend = null;
  69. var backend_infos : [ssl_backends.len]struct {
  70. enabled: bool,
  71. name: []const u8,
  72. } = undefined;
  73. var backend_enabled_count: u32 = 0;
  74. inline for (ssl_backends) |field, i| {
  75. const enabled = unwrapOptionalBool(b.option(bool, field.name, "enable ssl backend: " ++ field.name));
  76. if (enabled) {
  77. backend = @field(SslBackend, field.name);
  78. backend_enabled_count += 1;
  79. }
  80. backend_infos[i] = .{
  81. .enabled = enabled,
  82. .name = field.name,
  83. };
  84. }
  85. if (backend_enabled_count > 1) {
  86. std.log.err("only one ssl backend may be enabled, can't provide these options at the same time:", .{});
  87. for (backend_infos) |info| {
  88. if (info.enabled) {
  89. std.log.err(" -D{s}", .{info.name});
  90. }
  91. }
  92. std.os.exit(1);
  93. }
  94. return backend;
  95. }
  96. //
  97. // NOTE: the ziget_repo argument is here so this function can be used by other projects, not just this repo
  98. //
  99. pub fn addSslBackend(step: *std.build.LibExeObjStep, backend: SslBackend, ziget_repo: []const u8) !Pkg {
  100. const b = step.builder;
  101. switch (backend) {
  102. .openssl => {
  103. step.linkSystemLibrary("c");
  104. if (builtin.os.tag == .windows) {
  105. step.linkSystemLibrary("libcrypto");
  106. step.linkSystemLibrary("libssl");
  107. try setupOpensslWindows(step);
  108. } else {
  109. step.linkSystemLibrary("crypto");
  110. step.linkSystemLibrary("ssl");
  111. }
  112. return Pkg {
  113. .name = "ssl",
  114. .path = .{ .path = try std.fs.path.join(b.allocator, &[_][]const u8 { ziget_repo, "openssl/ssl.zig" }) },
  115. };
  116. },
  117. .opensslstatic => {
  118. const openssl_repo = GitRepoStep.create(step.builder, .{
  119. .url = "https://github.com/openssl/openssl",
  120. .branch = "OpenSSL_1_1_1j",
  121. .sha = "52c587d60be67c337364b830dd3fdc15404a2f04",
  122. });
  123. // TODO: should we implement something to cache the configuration?
  124. // can the configure output be in a different directory?
  125. {
  126. const configure_openssl = std.build.RunStep.create(b, "configure openssl");
  127. configure_openssl.step.dependOn(&openssl_repo.step);
  128. configure_openssl.cwd = openssl_repo.getPath(&configure_openssl.step);
  129. configure_openssl.addArgs(&[_][]const u8 {
  130. "./config",
  131. // just a temporary path for now
  132. //"--openssl",
  133. //"/tmp/ziget-openssl-static-dir1",
  134. "-static",
  135. // just disable everything for now
  136. "no-threads",
  137. "no-shared",
  138. "no-asm",
  139. "no-sse2",
  140. "no-aria",
  141. "no-bf",
  142. "no-camellia",
  143. "no-cast",
  144. "no-des",
  145. "no-dh",
  146. "no-dsa",
  147. "no-ec",
  148. "no-idea",
  149. "no-md2",
  150. "no-mdc2",
  151. "no-rc2",
  152. "no-rc4",
  153. "no-rc5",
  154. "no-seed",
  155. "no-sm2",
  156. "no-sm3",
  157. "no-sm4",
  158. });
  159. configure_openssl.stdout_action = .{
  160. .expect_matches = &[_][]const u8 { "OpenSSL has been successfully configured" },
  161. };
  162. const make_openssl = std.build.RunStep.create(b, "configure openssl");
  163. make_openssl.cwd = configure_openssl.cwd;
  164. make_openssl.addArgs(&[_][]const u8 {
  165. "make",
  166. "include/openssl/opensslconf.h",
  167. "include/crypto/bn_conf.h",
  168. "include/crypto/dso_conf.h",
  169. });
  170. make_openssl.step.dependOn(&configure_openssl.step);
  171. step.step.dependOn(&make_openssl.step);
  172. }
  173. const openssl_repo_path_for_step = openssl_repo.getPath(&step.step);
  174. step.addIncludeDir(openssl_repo_path_for_step);
  175. step.addIncludeDir(try std.fs.path.join(b.allocator, &[_][]const u8 {
  176. openssl_repo_path_for_step, "include" }));
  177. step.addIncludeDir(try std.fs.path.join(b.allocator, &[_][]const u8 {
  178. openssl_repo_path_for_step, "crypto", "modes" }));
  179. const cflags = &[_][]const u8 {
  180. "-Wall",
  181. // TODO: is this the right way to do this? is it a config option?
  182. "-DOPENSSL_NO_ENGINE",
  183. // TODO: --openssldir doesn't seem to be setting this?
  184. "-DOPENSSLDIR=\"/tmp/ziget-openssl-static-dir2\"",
  185. };
  186. {
  187. const sources = @embedFile("openssl/sources");
  188. var source_lines = std.mem.split(u8, sources, "\n");
  189. while (source_lines.next()) |src| {
  190. if (src.len == 0 or src[0] == '#') continue;
  191. step.addCSourceFile(try std.fs.path.join(b.allocator, &[_][]const u8 {
  192. openssl_repo_path_for_step, src }), cflags);
  193. }
  194. }
  195. step.linkLibC();
  196. return Pkg {
  197. .name = "ssl",
  198. .path = .{ .path = try std.fs.path.join(b.allocator, &[_][]const u8 { ziget_repo, "openssl/ssl.zig" }) },
  199. };
  200. },
  201. .wolfssl => {
  202. std.log.err("-Dwolfssl is not implemented", .{});
  203. std.os.exit(1);
  204. },
  205. .iguana => {
  206. const iguana_repo = GitRepoStep.create(b, .{
  207. .url = "https://github.com/marler8997/iguanaTLS",
  208. .branch = null,
  209. .sha = "c1106fa6ecac1d51e8148cd47a8a4b99bb307af8",
  210. });
  211. step.step.dependOn(&iguana_repo.step);
  212. const iguana_repo_path = iguana_repo.getPath(&step.step);
  213. const iguana_index_file = try std.fs.path.join(b.allocator, &[_][]const u8 {iguana_repo_path, "src", "main.zig"});
  214. var p = Pkg {
  215. .name = "ssl",
  216. .path = .{ .path = try std.fs.path.join(b.allocator, &[_][]const u8 { ziget_repo, "iguana", "ssl.zig" }) },
  217. .dependencies = &[_]Pkg {
  218. .{ .name = "iguana", .path = .{ .path = iguana_index_file } },
  219. },
  220. };
  221. // NOTE: I don't know why I need to call dupePkg, I think this is a bug
  222. return b.dupePkg(p);
  223. },
  224. .schannel => {
  225. {
  226. // NOTE: for now I'm using msspi from https://github.com/deemru/msspi
  227. // I'll probably port this to Zig at some point
  228. // Once I do remove this build config
  229. // NOTE: I tested using this commit: 7338760a4a2c6fb80c47b24a2abba32d5fc40635 tagged at version 0.1.42
  230. const msspi_repo = GitRepoStep.create(b, .{
  231. .url = "https://github.com/deemru/msspi",
  232. .branch = "0.1.42",
  233. .sha = "7338760a4a2c6fb80c47b24a2abba32d5fc40635"
  234. });
  235. step.step.dependOn(&msspi_repo.step);
  236. const msspi_repo_path = msspi_repo.getPath(&step.step);
  237. const msspi_src_dir = try std.fs.path.join(b.allocator, &[_][]const u8 { msspi_repo_path, "src" });
  238. const msspi_main_cpp = try std.fs.path.join(b.allocator, &[_][]const u8 { msspi_src_dir, "msspi.cpp" });
  239. const msspi_third_party_include = try std.fs.path.join(b.allocator, &[_][]const u8 { msspi_repo_path, "third_party", "cprocsp", "include" });
  240. step.addCSourceFile(msspi_main_cpp, &[_][]const u8 { });
  241. step.addIncludeDir(msspi_src_dir);
  242. step.addIncludeDir(msspi_third_party_include);
  243. step.linkLibC();
  244. step.linkSystemLibrary("ws2_32");
  245. step.linkSystemLibrary("crypt32");
  246. step.linkSystemLibrary("advapi32");
  247. }
  248. // TODO: this will be needed if/when msspi is ported to Zig
  249. //const zigwin32_index_file = try getGitRepoFile(b.allocator,
  250. // "https://github.com/marlersoft/zigwin32",
  251. // "src" ++ std.fs.path.sep_str ++ "win32.zig");
  252. return Pkg {
  253. .name = "ssl",
  254. .path = .{ .path = try std.fs.path.join(b.allocator, &[_][]const u8 { ziget_repo, "schannel", "ssl.zig" }) },
  255. //.dependencies = &[_]Pkg {
  256. // .{ .name = "win32", .path = .{ .path = zigwin32_index_file } },
  257. //},
  258. };
  259. }
  260. }
  261. }
  262. pub fn setupOpensslWindows(step: *std.build.LibExeObjStep) !void {
  263. const b = step.builder;
  264. const openssl_path = b.option([]const u8, "openssl-path", "path to openssl (for Windows)") orelse {
  265. std.debug.print("Error: -Dopenssl on windows requires -Dopenssl-path=DIR to be specified\n", .{});
  266. std.os.exit(1);
  267. };
  268. // NOTE: right now these files are hardcoded to the files expected when installing SSL via
  269. // this web page: https://slproweb.com/products/Win32OpenSSL.html and installed using
  270. // this exe installer: https://slproweb.com/download/Win64OpenSSL-1_1_1g.exe
  271. step.addIncludeDir(try std.fs.path.join(b.allocator, &[_][]const u8 {openssl_path, "include"}));
  272. step.addLibPath(try std.fs.path.join(b.allocator, &[_][]const u8 {openssl_path, "lib"}));
  273. // install dlls to the same directory as executable
  274. for ([_][]const u8 {"libcrypto-1_1-x64.dll", "libssl-1_1-x64.dll"}) |dll| {
  275. step.step.dependOn(
  276. &b.addInstallFileWithDir(
  277. .{ .path = try std.fs.path.join(b.allocator, &[_][]const u8 {openssl_path, dll}) },
  278. .bin,
  279. dll,
  280. ).step
  281. );
  282. }
  283. }