1const InstallArtifact = @This();
2
3const std = @import("std");
4const Io = std.Io;
5const Configuration = std.Build.Configuration;
6const assert = std.debug.assert;
7
8const Step = @import("../Step.zig");
9const Maker = @import("../../Maker.zig");
10
11pub fn make(
12 install_artifact: *InstallArtifact,
13 step_index: Configuration.Step.Index,
14 maker: *Maker,
15 progress_node: std.Progress.Node,
16) Step.ExtendedMakeError!void {
17 _ = install_artifact;
18 _ = progress_node;
19 const step = maker.stepByIndex(step_index);
20 const conf = &maker.scanned_config.configuration;
21 const graph = maker.graph;
22 const gpa = maker.gpa;
23 const arena = graph.arena; // TODO don't leak into process arena
24 const io = graph.io;
25 const conf_step = step_index.ptr(conf);
26 const conf_ia = conf_step.extended.get(conf.extra).install_artifact;
27 const compile_step_index = conf_step.deps.get(conf).steps.slice[0];
28 const conf_comp_step = compile_step_index.ptr(conf);
29 const conf_comp = conf_comp_step.extended.get(conf.extra).compile;
30 const root_module = conf_comp.root_module.get(conf);
31 const target = root_module.resolved_target.get(conf).?.result.get(conf);
32
33 var all_cached = true;
34
35 if (conf_ia.bin_dir.value) |bin_dir| {
36 if (conf_comp.generated_bin.value) |generated_bin| {
37 const bin_sub_path = if (conf_ia.bin_sub_path.value) |s| s.slice(conf) else try std.zig.binNameAlloc(arena, .{
38 .root_name = conf_comp.root_name.slice(conf),
39 .cpu_arch = target.flags.cpu_arch.unwrap().?,
40 .os_tag = target.flags.os_tag.unwrap().?,
41 .ofmt = target.flags.object_format.unwrap().?,
42 .abi = target.flags.abi.unwrap().?,
43 .output_mode = conf_comp.flags3.kind.toOutputMode(),
44 .link_mode = conf_comp.flags2.linkage.unwrap(),
45 .version = v: {
46 const string = conf_comp.version.value orelse break :v null;
47 const slice = string.slice(conf);
48 break :v std.SemanticVersion.parse(slice) catch @panic("bad semver string");
49 },
50 });
51 const dest_dir = try maker.resolveInstallDir(arena, bin_dir);
52 const dest_path = try dest_dir.join(arena, bin_sub_path);
53 const src_path = maker.generatedPath(generated_bin).*;
54 const p = try maker.installPath(arena, src_path, dest_path, step_index);
55 all_cached = all_cached and p == .fresh;
56
57 if (conf_ia.flags.dylib_symlinks)
58 try maker.installSymLinks(arena, dest_path, compile_step_index, step_index);
59
60 const make_comp_step = maker.stepByIndex(compile_step_index);
61 const make_comp = &make_comp_step.extended.compile;
62 make_comp.installed_path = dest_path;
63 }
64 }
65
66 if (conf_ia.implib_dir.value) |implib_dir| {
67 if (conf_comp.generated_implib.value) |generated_implib| {
68 const p = try maker.installGenerated(arena, generated_implib, implib_dir, step_index);
69 all_cached = all_cached and p == .fresh;
70 }
71 }
72
73 if (conf_ia.pdb_dir.value) |pdb_dir| {
74 if (conf_comp.generated_pdb.value) |generated_pdb| {
75 const p = try maker.installGenerated(arena, generated_pdb, pdb_dir, step_index);
76 all_cached = all_cached and p == .fresh;
77 }
78 }
79
80 if (conf_ia.h_dir.value) |h_dir| {
81 const h_prefix = try maker.resolveInstallDir(arena, h_dir);
82
83 if (conf_comp.generated_h.value) |generated_h| {
84 const p = try maker.installGenerated(arena, generated_h, h_dir, step_index);
85 all_cached = all_cached and p == .fresh;
86 }
87
88 for (conf_comp.installed_headers.slice) |installation| switch (installation.get(conf.extra)) {
89 .file => |file| {
90 const src_path = try maker.resolveLazyPathIndex(arena, file.source, step_index);
91 const dest_path = try h_prefix.join(arena, file.dest_sub_path.slice(conf));
92 const p = try maker.installPath(arena, src_path, dest_path, step_index);
93 all_cached = all_cached and p == .fresh;
94 },
95 .directory => |dir| {
96 const src_dir_path = try maker.resolveLazyPathIndex(arena, dir.source, step_index);
97 const full_h_prefix = try h_prefix.join(arena, dir.dest_sub_path.slice(conf));
98
99 var src_dir = src_dir_path.root_dir.handle.openDir(io, src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| {
100 return step.fail(maker, "unable to open source directory {f}: {t}", .{ src_dir_path, err });
101 };
102 defer src_dir.close(io);
103
104 var it = try src_dir.walk(gpa);
105 defer it.deinit();
106 next_entry: while (it.next(io) catch |err| switch (err) {
107 error.Canceled, error.OutOfMemory => |e| return e,
108 else => |e| return step.fail(maker, "failed to iterate directory {f}: {t}", .{ src_dir_path, e }),
109 }) |entry| {
110 for (dir.exclude_extensions.slice) |ext| {
111 if (std.mem.endsWith(u8, entry.path, ext.slice(conf))) continue :next_entry;
112 }
113 if (dir.flags.include_extensions) {
114 for (dir.include_extensions.slice) |inc| {
115 if (std.mem.endsWith(u8, entry.path, inc.slice(conf))) break;
116 } else {
117 continue :next_entry;
118 }
119 }
120
121 const full_dest_path = try full_h_prefix.join(arena, entry.path);
122 switch (entry.kind) {
123 .directory => {
124 const p = try maker.installDir(arena, full_dest_path, step_index);
125 all_cached = all_cached and p == .existed;
126 },
127 .file => {
128 const entry_dir_path = try maker.resolveLazyPathIndex(arena, dir.source, step_index);
129 const entry_path = try entry_dir_path.join(arena, entry.path);
130 const p = try maker.installPath(arena, entry_path, full_dest_path, step_index);
131 all_cached = all_cached and p == .fresh;
132 },
133 else => continue,
134 }
135 }
136 },
137 };
138 }
139
140 step.result_cached = all_cached;
141}