| ... | ... | @@ -1853,6 +1853,9 @@ pub fn resolveInputs( |
| 1853 | 1853 | var ld_script_bytes: std.ArrayList(u8) = .empty; |
| 1854 | 1854 | defer ld_script_bytes.deinit(gpa); |
| 1855 | 1855 | |
| 1856 | var archive_dedup: ArchiveDedupMap = .empty; |
| 1857 | defer archive_dedup.deinit(gpa); |
| 1858 | |
| 1856 | 1859 | var failed_libs: std.ArrayList(struct { |
| 1857 | 1860 | name: []const u8, |
| 1858 | 1861 | strategy: UnresolvedInput.SearchStrategy, |
| ... | ... | @@ -1892,6 +1895,7 @@ pub fn resolveInputs( |
| 1892 | 1895 | resolved_inputs, |
| 1893 | 1896 | &checked_paths, |
| 1894 | 1897 | &ld_script_bytes, |
| 1898 | &archive_dedup, |
| 1895 | 1899 | lib_directory, |
| 1896 | 1900 | name_query, |
| 1897 | 1901 | target, |
| ... | ... | @@ -1919,6 +1923,7 @@ pub fn resolveInputs( |
| 1919 | 1923 | resolved_inputs, |
| 1920 | 1924 | &checked_paths, |
| 1921 | 1925 | &ld_script_bytes, |
| 1926 | &archive_dedup, |
| 1922 | 1927 | lib_directory, |
| 1923 | 1928 | name_query, |
| 1924 | 1929 | target, |
| ... | ... | @@ -1947,6 +1952,7 @@ pub fn resolveInputs( |
| 1947 | 1952 | resolved_inputs, |
| 1948 | 1953 | &checked_paths, |
| 1949 | 1954 | &ld_script_bytes, |
| 1955 | &archive_dedup, |
| 1950 | 1956 | lib_directory, |
| 1951 | 1957 | name_query, |
| 1952 | 1958 | target, |
| ... | ... | @@ -1966,6 +1972,7 @@ pub fn resolveInputs( |
| 1966 | 1972 | resolved_inputs, |
| 1967 | 1973 | &checked_paths, |
| 1968 | 1974 | &ld_script_bytes, |
| 1975 | &archive_dedup, |
| 1969 | 1976 | lib_directory, |
| 1970 | 1977 | name_query, |
| 1971 | 1978 | target, |
| ... | ... | @@ -1997,6 +2004,7 @@ pub fn resolveInputs( |
| 1997 | 2004 | unresolved_inputs, |
| 1998 | 2005 | resolved_inputs, |
| 1999 | 2006 | &ld_script_bytes, |
| 2007 | &archive_dedup, |
| 2000 | 2008 | target, |
| 2001 | 2009 | .{ |
| 2002 | 2010 | .path = Path.initCwd(an.name), |
| ... | ... | @@ -2015,6 +2023,7 @@ pub fn resolveInputs( |
| 2015 | 2023 | unresolved_inputs, |
| 2016 | 2024 | resolved_inputs, |
| 2017 | 2025 | &ld_script_bytes, |
| 2026 | &archive_dedup, |
| 2018 | 2027 | target, |
| 2019 | 2028 | .{ |
| 2020 | 2029 | .path = .{ |
| ... | ... | @@ -2043,6 +2052,7 @@ pub fn resolveInputs( |
| 2043 | 2052 | unresolved_inputs, |
| 2044 | 2053 | resolved_inputs, |
| 2045 | 2054 | &ld_script_bytes, |
| 2055 | &archive_dedup, |
| 2046 | 2056 | target, |
| 2047 | 2057 | pq, |
| 2048 | 2058 | color, |
| ... | ... | @@ -2088,6 +2098,8 @@ fn resolveLibInput( |
| 2088 | 2098 | checked_paths: *std.ArrayList(u8), |
| 2089 | 2099 | /// Allocated via `gpa`. |
| 2090 | 2100 | ld_script_bytes: *std.ArrayList(u8), |
| 2101 | /// Allocated via `gpa`. |
| 2102 | archive_dedup: *ArchiveDedupMap, |
| 2091 | 2103 | lib_directory: Directory, |
| 2092 | 2104 | name_query: UnresolvedInput.NameQuery, |
| 2093 | 2105 | target: *const std.Target, |
| ... | ... | @@ -2095,6 +2107,7 @@ fn resolveLibInput( |
| 2095 | 2107 | color: std.zig.Color, |
| 2096 | 2108 | ) Allocator.Error!ResolveLibInputResult { |
| 2097 | 2109 | try resolved_inputs.ensureUnusedCapacity(gpa, 1); |
| 2110 | try archive_dedup.ensureUnusedCapacity(gpa, 1); |
| 2098 | 2111 | |
| 2099 | 2112 | const lib_name = name_query.name; |
| 2100 | 2113 | |
| ... | ... | @@ -2110,7 +2123,7 @@ fn resolveLibInput( |
| 2110 | 2123 | else => |e| fatal("unable to search for tbd library '{f}': {s}", .{ test_path, @errorName(e) }), |
| 2111 | 2124 | }; |
| 2112 | 2125 | errdefer file.close(io); |
| 2113 | | return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query); |
| 2126 | return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, name_query.query); |
| 2114 | 2127 | } |
| 2115 | 2128 | |
| 2116 | 2129 | { |
| ... | ... | @@ -2125,7 +2138,7 @@ fn resolveLibInput( |
| 2125 | 2138 | }), |
| 2126 | 2139 | }; |
| 2127 | 2140 | try checked_paths.print(gpa, "\n {f}", .{test_path}); |
| 2128 | | switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, target, .{ |
| 2141 | switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, .{ |
| 2129 | 2142 | .path = test_path, |
| 2130 | 2143 | .query = name_query.query, |
| 2131 | 2144 | }, link_mode, color)) { |
| ... | ... | @@ -2149,7 +2162,7 @@ fn resolveLibInput( |
| 2149 | 2162 | }), |
| 2150 | 2163 | }; |
| 2151 | 2164 | errdefer file.close(io); |
| 2152 | | return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query); |
| 2165 | return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, name_query.query); |
| 2153 | 2166 | } |
| 2154 | 2167 | |
| 2155 | 2168 | // In the case of MinGW, the main check will be .lib but we also need to |
| ... | ... | @@ -2165,26 +2178,61 @@ fn resolveLibInput( |
| 2165 | 2178 | else => |e| fatal("unable to search for static library '{f}': {s}", .{ test_path, @errorName(e) }), |
| 2166 | 2179 | }; |
| 2167 | 2180 | errdefer file.close(io); |
| 2168 | | return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query); |
| 2181 | return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, name_query.query); |
| 2169 | 2182 | } |
| 2170 | 2183 | |
| 2171 | 2184 | return .no_match; |
| 2172 | 2185 | } |
| 2173 | 2186 | |
| 2187 | /// Deduplicates static archive link inputs based on their path. This is done for efficiency, so |
| 2188 | /// that linker implementations do not need to open and scan the archive just to determine that they |
| 2189 | /// need not extract any objects. At the time of writing, it also helps avoid "multiple definitions |
| 2190 | /// of symbol" errors in incomplete linker implementations. |
| 2191 | /// |
| 2192 | /// Key is index into `resolved_inputs` of an `Input.archive`. |
| 2193 | /// |
| 2194 | /// Accessed through `ArchiveDedupAdapter`. |
| 2195 | /// |
| 2196 | const ArchiveDedupMap = std.array_hash_map.Custom(u32, void, void, true); |
| 2197 | /// Adapter for accessing `ArchiveDedupMap` with an effective key type of `Path`. |
| 2198 | const ArchiveDedupAdapter = struct { |
| 2199 | resolved_inputs: []const Input, |
| 2200 | pub fn hash(ctx: ArchiveDedupAdapter, path: Path) u32 { |
| 2201 | _ = ctx; |
| 2202 | return Path.TableAdapter.hash(.{}, path); |
| 2203 | } |
| 2204 | pub fn eql(ctx: ArchiveDedupAdapter, a_path: Path, b_input_index: u32, _: usize) bool { |
| 2205 | const b_path = ctx.resolved_inputs[b_input_index].archive.path; |
| 2206 | return a_path.eql(b_path); |
| 2207 | } |
| 2208 | }; |
| 2209 | |
| 2174 | 2210 | fn finishResolveLibInput( |
| 2211 | io: Io, |
| 2175 | 2212 | resolved_inputs: *std.ArrayList(Input), |
| 2213 | archive_dedup: *ArchiveDedupMap, |
| 2176 | 2214 | path: Path, |
| 2177 | 2215 | file: Io.File, |
| 2178 | 2216 | link_mode: std.lang.LinkMode, |
| 2179 | 2217 | query: UnresolvedInput.Query, |
| 2180 | 2218 | ) ResolveLibInputResult { |
| 2181 | 2219 | switch (link_mode) { |
| 2182 | | .static => resolved_inputs.appendAssumeCapacity(.{ .archive = .{ |
| 2183 | | .path = path, |
| 2184 | | .file = file, |
| 2185 | | .must_link = query.must_link, |
| 2186 | | .hidden = query.hidden, |
| 2187 | | } }), |
| 2220 | .static => { |
| 2221 | const ctx: ArchiveDedupAdapter = .{ .resolved_inputs = resolved_inputs.items }; |
| 2222 | const gop = archive_dedup.getOrPutAssumeCapacityAdapted(path, ctx); |
| 2223 | if (gop.found_existing) { |
| 2224 | // Ignore duplicate archive input |
| 2225 | file.close(io); |
| 2226 | return .ok; |
| 2227 | } |
| 2228 | gop.key_ptr.* = @intCast(resolved_inputs.items.len); |
| 2229 | resolved_inputs.appendAssumeCapacity(.{ .archive = .{ |
| 2230 | .path = path, |
| 2231 | .file = file, |
| 2232 | .must_link = query.must_link, |
| 2233 | .hidden = query.hidden, |
| 2234 | } }); |
| 2235 | }, |
| 2188 | 2236 | .dynamic => resolved_inputs.appendAssumeCapacity(.{ .dso = .{ |
| 2189 | 2237 | .path = path, |
| 2190 | 2238 | .file = file, |
| ... | ... | @@ -2206,13 +2254,15 @@ fn resolvePathInput( |
| 2206 | 2254 | resolved_inputs: *std.ArrayList(Input), |
| 2207 | 2255 | /// Allocated via `gpa`. |
| 2208 | 2256 | ld_script_bytes: *std.ArrayList(u8), |
| 2257 | /// Allocated via `gpa`. |
| 2258 | archive_dedup: *ArchiveDedupMap, |
| 2209 | 2259 | target: *const std.Target, |
| 2210 | 2260 | pq: UnresolvedInput.PathQuery, |
| 2211 | 2261 | color: std.zig.Color, |
| 2212 | 2262 | ) Allocator.Error!?ResolveLibInputResult { |
| 2213 | 2263 | switch (Compilation.classifyFileExt(pq.path.sub_path)) { |
| 2214 | | .static_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .static, color), |
| 2215 | | .shared_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .dynamic, color), |
| 2264 | .static_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, pq, .static, color), |
| 2265 | .shared_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, pq, .dynamic, color), |
| 2216 | 2266 | .object => { |
| 2217 | 2267 | var file = pq.path.root_dir.handle.openFile(io, pq.path.sub_path, .{}) catch |err| |
| 2218 | 2268 | fatal("failed to open object {f}: {s}", .{ pq.path, @errorName(err) }); |
| ... | ... | @@ -2249,12 +2299,15 @@ fn resolvePathInputLib( |
| 2249 | 2299 | resolved_inputs: *std.ArrayList(Input), |
| 2250 | 2300 | /// Allocated via `gpa`. |
| 2251 | 2301 | ld_script_bytes: *std.ArrayList(u8), |
| 2302 | /// Allocated via `gpa`. |
| 2303 | archive_dedup: *ArchiveDedupMap, |
| 2252 | 2304 | target: *const std.Target, |
| 2253 | 2305 | pq: UnresolvedInput.PathQuery, |
| 2254 | 2306 | link_mode: std.lang.LinkMode, |
| 2255 | 2307 | color: std.zig.Color, |
| 2256 | 2308 | ) Allocator.Error!ResolveLibInputResult { |
| 2257 | 2309 | try resolved_inputs.ensureUnusedCapacity(gpa, 1); |
| 2310 | try archive_dedup.ensureUnusedCapacity(gpa, 1); |
| 2258 | 2311 | |
| 2259 | 2312 | const test_path: Path = pq.path; |
| 2260 | 2313 | // In the case of shared libraries, they might actually be "linker scripts" |
| ... | ... | @@ -2279,7 +2332,7 @@ fn resolvePathInputLib( |
| 2279 | 2332 | mem.startsWith(u8, buf, std.elf.ARMAG_THIN)) |
| 2280 | 2333 | { |
| 2281 | 2334 | // Appears to be an ELF or archive file. |
| 2282 | | return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query); |
| 2335 | return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, pq.query); |
| 2283 | 2336 | } |
| 2284 | 2337 | const stat = file.stat(io) catch |err| |
| 2285 | 2338 | fatal("failed to stat {f}: {t}", .{ test_path, err }); |
| ... | ... | @@ -2349,7 +2402,7 @@ fn resolvePathInputLib( |
| 2349 | 2402 | }), |
| 2350 | 2403 | }; |
| 2351 | 2404 | errdefer file.close(io); |
| 2352 | | return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query); |
| 2405 | return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, pq.query); |
| 2353 | 2406 | } |
| 2354 | 2407 | |
| 2355 | 2408 | pub fn openObject(io: Io, path: Path, must_link: bool, hidden: bool) !Input.Object { |