diff options
Diffstat (limited to 'lib/std/Build/InstallDirStep.zig')
| -rw-r--r-- | lib/std/Build/InstallDirStep.zig | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/lib/std/Build/InstallDirStep.zig b/lib/std/Build/InstallDirStep.zig new file mode 100644 index 0000000000..41dbb3e35a --- /dev/null +++ b/lib/std/Build/InstallDirStep.zig @@ -0,0 +1,93 @@ +const std = @import("../std.zig"); +const mem = std.mem; +const fs = std.fs; +const Step = std.Build.Step; +const InstallDir = std.Build.InstallDir; +const InstallDirStep = @This(); +const log = std.log; + +step: Step, +builder: *std.Build, +options: Options, +/// This is used by the build system when a file being installed comes from one +/// package but is being installed by another. +override_source_builder: ?*std.Build = null, + +pub const base_id = .install_dir; + +pub const Options = struct { + source_dir: []const u8, + install_dir: InstallDir, + install_subdir: []const u8, + /// File paths which end in any of these suffixes will be excluded + /// from being installed. + exclude_extensions: []const []const u8 = &.{}, + /// File paths which end in any of these suffixes will result in + /// empty files being installed. This is mainly intended for large + /// test.zig files in order to prevent needless installation bloat. + /// However if the files were not present at all, then + /// `@import("test.zig")` would be a compile error. + blank_extensions: []const []const u8 = &.{}, + + fn dupe(self: Options, b: *std.Build) Options { + return .{ + .source_dir = b.dupe(self.source_dir), + .install_dir = self.install_dir.dupe(b), + .install_subdir = b.dupe(self.install_subdir), + .exclude_extensions = b.dupeStrings(self.exclude_extensions), + .blank_extensions = b.dupeStrings(self.blank_extensions), + }; + } +}; + +pub fn init( + builder: *std.Build, + options: Options, +) InstallDirStep { + builder.pushInstalledFile(options.install_dir, options.install_subdir); + return InstallDirStep{ + .builder = builder, + .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make), + .options = options.dupe(builder), + }; +} + +fn make(step: *Step) !void { + const self = @fieldParentPtr(InstallDirStep, "step", step); + const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir); + const src_builder = self.override_source_builder orelse self.builder; + const full_src_dir = src_builder.pathFromRoot(self.options.source_dir); + var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| { + log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{ + full_src_dir, @errorName(err), + }); + return error.StepFailed; + }; + defer src_dir.close(); + var it = try src_dir.walk(self.builder.allocator); + next_entry: while (try it.next()) |entry| { + for (self.options.exclude_extensions) |ext| { + if (mem.endsWith(u8, entry.path, ext)) { + continue :next_entry; + } + } + + const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path }); + const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path }); + + switch (entry.kind) { + .Directory => try fs.cwd().makePath(dest_path), + .File => { + for (self.options.blank_extensions) |ext| { + if (mem.endsWith(u8, entry.path, ext)) { + try self.builder.truncateFile(dest_path); + continue :next_entry; + } + } + + try self.builder.updateFile(full_path, dest_path); + }, + else => continue, + } + } +} |
