authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-03-06 17:42:41-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-03-07 09:15:43-05:00
log189fba4390c3a748d33e0f72a305f0d6bdbfe7c0
tree5c3cc4c3393f5848041ee63df58f6e8e67a30449
parentccdef8c0fb0a80d38ce4f4ac0c57aa2b66bfcfc2
signaturelock-open Commit is signed but in an unrecognized format.

process headers tool


1 files changed, 627 insertions(+), 0 deletions(-)

libc/process_headers.zig created+627
......@@ -0,0 +1,627 @@
1// To get started, run this tool with no args and read the help message.
2//
3// The build systems of musl-libc and glibc require specifying a single target
4// architecture. Meanwhile, Zig supports out-of-the-box cross compilation for
5// every target. So the process to create libc headers that Zig ships is to use
6// this tool.
7// First, use the musl/glibc build systems to create installations of all the
8// targets in the `libc_targets` variable.
9// Next, run this tool to create a new directory which puts .h files into
10// <arch> subdirectories, with `generic` being files that apply to all architectures.
11// You'll then have to manually update Zig source repo with these new files.
12
13const std = @import("std");
14const builtin = @import("builtin");
15const Arch = builtin.Arch;
16const Abi = builtin.Abi;
17const Os = builtin.Os;
18const assert = std.debug.assert;
19
20const LibCTarget = struct {
21 name: []const u8,
22 zig_arch: ?@TagType(Arch),
23 zig_abi: ?Abi,
24};
25
26const libc_targets = []LibCTarget{
27 LibCTarget{
28 .name = "aarch64_be-linux-gnu",
29 .zig_arch = Arch.aarch64_be,
30 .zig_abi = Abi.gnu,
31 },
32 LibCTarget{
33 .name = "aarch64-linux-gnu",
34 .zig_arch = Arch.aarch64,
35 .zig_abi = Abi.gnu,
36 },
37 LibCTarget{
38 .name = "aarch64-linux-gnu-disable-multi-arch",
39 .zig_arch = Arch.aarch64,
40 .zig_abi = null,
41 },
42 LibCTarget{
43 .name = "alpha-linux-gnu",
44 .zig_arch = null,
45 .zig_abi = Abi.gnu,
46 },
47 LibCTarget{
48 .name = "armeb-linux-gnueabi",
49 .zig_arch = Arch.armeb,
50 .zig_abi = Abi.gnueabi,
51 },
52 LibCTarget{
53 .name = "armeb-linux-gnueabi-be8",
54 .zig_arch = Arch.armeb,
55 .zig_abi = null,
56 },
57 LibCTarget{
58 .name = "armeb-linux-gnueabihf",
59 .zig_arch = Arch.armeb,
60 .zig_abi = Abi.gnueabihf,
61 },
62 LibCTarget{
63 .name = "armeb-linux-gnueabihf-be8",
64 .zig_arch = Arch.armeb,
65 .zig_abi = null,
66 },
67 LibCTarget{
68 .name = "arm-linux-gnueabi",
69 .zig_arch = Arch.arm,
70 .zig_abi = Abi.gnueabi,
71 },
72 LibCTarget{
73 .name = "arm-linux-gnueabihf",
74 .zig_arch = Arch.arm,
75 .zig_abi = Abi.gnueabihf,
76 },
77 LibCTarget{
78 .name = "arm-linux-gnueabihf-v7a",
79 .zig_arch = Arch.arm,
80 .zig_abi = null,
81 },
82 LibCTarget{
83 .name = "arm-linux-gnueabihf-v7a-disable-multi-arch",
84 .zig_arch = null,
85 .zig_abi = null,
86 },
87 LibCTarget{
88 .name = "hppa-linux-gnu",
89 .zig_arch = null,
90 .zig_abi = Abi.gnu,
91 },
92 LibCTarget{
93 .name = "i486-linux-gnu",
94 .zig_arch = null,
95 .zig_abi = Abi.gnu,
96 },
97 LibCTarget{
98 .name = "i586-linux-gnu",
99 .zig_arch = null,
100 .zig_abi = Abi.gnu,
101 },
102 LibCTarget{
103 .name = "i686-gnu",
104 .zig_arch = Arch.i386,
105 .zig_abi = Abi.gnu,
106 },
107 LibCTarget{
108 .name = "i686-linux-gnu",
109 .zig_arch = Arch.i386,
110 .zig_abi = Abi.gnu,
111 },
112 LibCTarget{
113 .name = "i686-linux-gnu-disable-multi-arch",
114 .zig_arch = null,
115 .zig_abi = null,
116 },
117 LibCTarget{
118 .name = "i686-linux-gnu-enable-obsolete",
119 .zig_arch = Arch.i386,
120 .zig_abi = null,
121 },
122 LibCTarget{
123 .name = "i686-linux-gnu-static-pie",
124 .zig_arch = Arch.i386,
125 .zig_abi = null,
126 },
127 LibCTarget{
128 .name = "ia64-linux-gnu",
129 .zig_arch = null,
130 .zig_abi = null,
131 },
132 LibCTarget{
133 .name = "m68k-linux-gnu",
134 .zig_arch = null,
135 .zig_abi = null,
136 },
137 LibCTarget{
138 .name = "m68k-linux-gnu-coldfire",
139 .zig_arch = null,
140 .zig_abi = null,
141 },
142 LibCTarget{
143 .name = "m68k-linux-gnu-coldfire-soft",
144 .zig_arch = null,
145 .zig_abi = null,
146 },
147 LibCTarget{
148 .name = "microblazeel-linux-gnu",
149 .zig_arch = null,
150 .zig_abi = null,
151 },
152 LibCTarget{
153 .name = "microblaze-linux-gnu",
154 .zig_arch = null,
155 .zig_abi = null,
156 },
157 LibCTarget{
158 .name = "mips64el-linux-gnu-n32",
159 .zig_arch = Arch.mips64el,
160 .zig_abi = Abi.gnuabin32,
161 },
162 LibCTarget{
163 .name = "mips64el-linux-gnu-n32-nan2008",
164 .zig_arch = Arch.mips64el,
165 .zig_abi = null,
166 },
167 LibCTarget{
168 .name = "mips64el-linux-gnu-n32-nan2008-soft",
169 .zig_arch = Arch.mips64el,
170 .zig_abi = null,
171 },
172 LibCTarget{
173 .name = "mips64el-linux-gnu-n32-soft",
174 .zig_arch = Arch.mips64el,
175 .zig_abi = null,
176 },
177 LibCTarget{
178 .name = "mips64el-linux-gnu-n64",
179 .zig_arch = Arch.mips64el,
180 .zig_abi = Abi.gnuabi64,
181 },
182 LibCTarget{
183 .name = "mips64el-linux-gnu-n64-nan2008",
184 .zig_arch = Arch.mips64el,
185 .zig_abi = null,
186 },
187 LibCTarget{
188 .name = "mips64el-linux-gnu-n64-nan2008-soft",
189 .zig_arch = Arch.mips64el,
190 .zig_abi = null,
191 },
192 LibCTarget{
193 .name = "mips64el-linux-gnu-n64-soft",
194 .zig_arch = Arch.mips64el,
195 .zig_abi = null,
196 },
197 LibCTarget{
198 .name = "mips64-linux-gnu-n32",
199 .zig_arch = Arch.mips64,
200 .zig_abi = Abi.gnuabin32,
201 },
202 LibCTarget{
203 .name = "mips64-linux-gnu-n32-nan2008",
204 .zig_arch = Arch.mips64,
205 .zig_abi = null,
206 },
207 LibCTarget{
208 .name = "mips64-linux-gnu-n32-nan2008-soft",
209 .zig_arch = Arch.mips64,
210 .zig_abi = null,
211 },
212 LibCTarget{
213 .name = "mips64-linux-gnu-n32-soft",
214 .zig_arch = Arch.mips64,
215 .zig_abi = null,
216 },
217 LibCTarget{
218 .name = "mips64-linux-gnu-n64",
219 .zig_arch = Arch.mips64,
220 .zig_abi = Abi.gnuabi64,
221 },
222 LibCTarget{
223 .name = "mips64-linux-gnu-n64-nan2008",
224 .zig_arch = Arch.mips64,
225 .zig_abi = null,
226 },
227 LibCTarget{
228 .name = "mips64-linux-gnu-n64-nan2008-soft",
229 .zig_arch = Arch.mips64,
230 .zig_abi = null,
231 },
232 LibCTarget{
233 .name = "mips64-linux-gnu-n64-soft",
234 .zig_arch = Arch.mips64,
235 .zig_abi = null,
236 },
237 LibCTarget{
238 .name = "mipsel-linux-gnu",
239 .zig_arch = Arch.mipsel,
240 .zig_abi = Abi.gnu,
241 },
242 LibCTarget{
243 .name = "mipsel-linux-gnu-nan2008",
244 .zig_arch = Arch.mipsel,
245 .zig_abi = null,
246 },
247 LibCTarget{
248 .name = "mipsel-linux-gnu-nan2008-soft",
249 .zig_arch = Arch.mipsel,
250 .zig_abi = null,
251 },
252 LibCTarget{
253 .name = "mipsel-linux-gnu-soft",
254 .zig_arch = Arch.mipsel,
255 .zig_abi = null,
256 },
257 LibCTarget{
258 .name = "mips-linux-gnu",
259 .zig_arch = Arch.mips,
260 .zig_abi = Abi.gnu,
261 },
262 LibCTarget{
263 .name = "mips-linux-gnu-nan2008",
264 .zig_arch = Arch.mips,
265 .zig_abi = null,
266 },
267 LibCTarget{
268 .name = "mips-linux-gnu-nan2008-soft",
269 .zig_arch = Arch.mips,
270 .zig_abi = null,
271 },
272 LibCTarget{
273 .name = "mips-linux-gnu-soft",
274 .zig_arch = Arch.mips,
275 .zig_abi = null,
276 },
277 LibCTarget{
278 .name = "nios2-linux-gnu",
279 .zig_arch = Arch.nios2,
280 .zig_abi = Abi.gnu,
281 },
282 LibCTarget{
283 .name = "powerpc64le-linux-gnu",
284 .zig_arch = Arch.powerpc64le,
285 .zig_abi = Abi.gnu,
286 },
287 LibCTarget{
288 .name = "powerpc64-linux-gnu",
289 .zig_arch = Arch.powerpc64,
290 .zig_abi = Abi.gnu,
291 },
292 LibCTarget{
293 .name = "powerpc-linux-gnu",
294 .zig_arch = Arch.powerpc,
295 .zig_abi = Abi.gnu,
296 },
297 LibCTarget{
298 .name = "powerpc-linux-gnu-power4",
299 .zig_arch = Arch.powerpc,
300 .zig_abi = null,
301 },
302 LibCTarget{
303 .name = "powerpc-linux-gnu-soft",
304 .zig_arch = Arch.powerpc,
305 .zig_abi = null,
306 },
307 LibCTarget{
308 .name = "powerpc-linux-gnuspe",
309 .zig_arch = Arch.powerpc,
310 .zig_abi = null,
311 },
312 LibCTarget{
313 .name = "powerpc-linux-gnuspe-e500v1",
314 .zig_arch = Arch.powerpc,
315 .zig_abi = null,
316 },
317 LibCTarget{
318 .name = "riscv64-linux-gnu-rv64imac-lp64",
319 .zig_arch = Arch.riscv64,
320 .zig_abi = Abi.gnu,
321 },
322 LibCTarget{
323 .name = "riscv64-linux-gnu-rv64imafdc-lp64",
324 .zig_arch = Arch.riscv64,
325 .zig_abi = null,
326 },
327 LibCTarget{
328 .name = "riscv64-linux-gnu-rv64imafdc-lp64d",
329 .zig_arch = Arch.riscv64,
330 .zig_abi = null,
331 },
332 LibCTarget{
333 .name = "s390-linux-gnu",
334 .zig_arch = null,
335 .zig_abi = Abi.gnu,
336 },
337 LibCTarget{
338 .name = "s390x-linux-gnu",
339 .zig_arch = Arch.s390x,
340 .zig_abi = Abi.gnu,
341 },
342 LibCTarget{
343 .name = "sh3eb-linux-gnu",
344 .zig_arch = null,
345 .zig_abi = Abi.gnu,
346 },
347 LibCTarget{
348 .name = "sh3-linux-gnu",
349 .zig_arch = null,
350 .zig_abi = Abi.gnu,
351 },
352 LibCTarget{
353 .name = "sh4eb-linux-gnu",
354 .zig_arch = null,
355 .zig_abi = Abi.gnu,
356 },
357 LibCTarget{
358 .name = "sh4eb-linux-gnu-soft",
359 .zig_arch = null,
360 .zig_abi = null,
361 },
362 LibCTarget{
363 .name = "sh4-linux-gnu",
364 .zig_arch = null,
365 .zig_abi = Abi.gnu,
366 },
367 LibCTarget{
368 .name = "sh4-linux-gnu-soft",
369 .zig_arch = null,
370 .zig_abi = null,
371 },
372 LibCTarget{
373 .name = "sparc64-linux-gnu",
374 .zig_arch = Arch.sparc,
375 .zig_abi = Abi.gnu,
376 },
377 LibCTarget{
378 .name = "sparc64-linux-gnu-disable-multi-arch",
379 .zig_arch = Arch.sparc,
380 .zig_abi = null,
381 },
382 LibCTarget{
383 .name = "sparcv9-linux-gnu",
384 .zig_arch = Arch.sparcv9,
385 .zig_abi = Abi.gnu,
386 },
387 LibCTarget{
388 .name = "sparcv9-linux-gnu-disable-multi-arch",
389 .zig_arch = Arch.sparcv9,
390 .zig_abi = null,
391 },
392 LibCTarget{
393 .name = "x86_64-linux-gnu",
394 .zig_arch = Arch.x86_64,
395 .zig_abi = Abi.gnu,
396 },
397 LibCTarget{
398 .name = "x86_64-linux-gnu-disable-multi-arch",
399 .zig_arch = Arch.x86_64,
400 .zig_abi = null,
401 },
402 LibCTarget{
403 .name = "x86_64-linux-gnu-enable-obsolete",
404 .zig_arch = Arch.x86_64,
405 .zig_abi = null,
406 },
407 LibCTarget{
408 .name = "x86_64-linux-gnu-static-pie",
409 .zig_arch = Arch.x86_64,
410 .zig_abi = null,
411 },
412 LibCTarget{
413 .name = "x86_64-linux-gnu-x32",
414 .zig_arch = Arch.x86_64,
415 .zig_abi = Abi.gnux32,
416 },
417 LibCTarget{
418 .name = "x86_64-linux-gnu-x32-static-pie",
419 .zig_arch = Arch.x86_64,
420 .zig_abi = null,
421 },
422};
423
424const DestTarget = struct {
425 arch: @TagType(Arch),
426 os: Os,
427 abi: Abi,
428
429 fn hash(a: DestTarget) u32 {
430 return @enumToInt(a.arch) +%
431 (@enumToInt(a.os) *% u32(4202347608)) +%
432 (@enumToInt(a.abi) *% u32(4082223418));
433 }
434
435 fn eql(a: DestTarget, b: DestTarget) bool {
436 return a.arch == b.arch and
437 a.os == b.os and
438 a.abi == b.abi;
439 }
440};
441
442const Contents = struct {
443 bytes: []const u8,
444 hit_count: usize,
445 hash: []const u8,
446 is_generic: bool,
447
448 fn hitCountLessThan(lhs: *const Contents, rhs: *const Contents) bool {
449 return lhs.hit_count < rhs.hit_count;
450 }
451};
452
453const HashToContents = std.AutoHashMap([]const u8, Contents);
454const TargetToHash = std.HashMap(DestTarget, []const u8, DestTarget.hash, DestTarget.eql);
455const PathTable = std.AutoHashMap([]const u8, *TargetToHash);
456
457pub fn main() !void {
458 var direct_allocator = std.heap.DirectAllocator.init();
459 var arena = std.heap.ArenaAllocator.init(&direct_allocator.allocator);
460 const allocator = &arena.allocator;
461 const args = try std.os.argsAlloc(allocator);
462 var search_paths = std.ArrayList([]const u8).init(allocator);
463 var opt_out_dir: ?[]const u8 = null;
464
465 var arg_i: usize = 1;
466 while (arg_i < args.len) : (arg_i += 1) {
467 if (std.mem.eql(u8, args[arg_i], "--help"))
468 usageAndExit(args[0]);
469 if (arg_i + 1 >= args.len) {
470 std.debug.warn("expected argument after '{}'\n", args[arg_i]);
471 usageAndExit(args[0]);
472 }
473
474 if (std.mem.eql(u8, args[arg_i], "--search-path")) {
475 try search_paths.append(args[arg_i + 1]);
476 } else if (std.mem.eql(u8, args[arg_i], "--out")) {
477 assert(opt_out_dir == null);
478 opt_out_dir = args[arg_i + 1];
479 } else {
480 std.debug.warn("unrecognized argument: {}\n", args[arg_i]);
481 usageAndExit(args[0]);
482 }
483
484 arg_i += 1;
485 }
486
487 const out_dir = opt_out_dir orelse usageAndExit(args[0]);
488
489 var path_table = PathTable.init(allocator);
490 var hash_to_contents = HashToContents.init(allocator);
491 var max_bytes_saved: usize = 0;
492 var total_bytes: usize = 0;
493
494 var hasher = std.crypto.Sha256.init();
495
496 for (libc_targets) |libc_target| {
497 const dest_target = DestTarget{
498 .arch = libc_target.zig_arch orelse continue,
499 .abi = libc_target.zig_abi orelse continue,
500 .os = builtin.Os.linux,
501 };
502 search: for (search_paths.toSliceConst()) |search_path| {
503 const target_include_dir = try std.os.path.join(
504 allocator,
505 [][]const u8{ search_path, libc_target.name, "usr", "include" },
506 );
507 var dir_stack = std.ArrayList([]const u8).init(allocator);
508 try dir_stack.append(target_include_dir);
509
510 while (dir_stack.popOrNull()) |full_dir_name| {
511 var dir = std.os.Dir.open(allocator, full_dir_name) catch |err| switch (err) {
512 error.FileNotFound => continue :search,
513 error.AccessDenied => continue :search,
514 else => return err,
515 };
516 defer dir.close();
517
518 while (try dir.next()) |entry| {
519 const full_path = try std.os.path.join(allocator, [][]const u8{ full_dir_name, entry.name });
520 switch (entry.kind) {
521 std.os.Dir.Entry.Kind.Directory => try dir_stack.append(full_path),
522 std.os.Dir.Entry.Kind.File => {
523 const rel_path = try std.os.path.relative(allocator, target_include_dir, full_path);
524 const raw_bytes = try std.io.readFileAlloc(allocator, full_path);
525 const trimmed = std.mem.trim(u8, raw_bytes, " \r\n\t");
526 total_bytes += raw_bytes.len;
527 const hash = try allocator.alloc(u8, 32);
528 hasher.reset();
529 hasher.update(rel_path);
530 hasher.update(trimmed);
531 hasher.final(hash);
532 const gop = try hash_to_contents.getOrPut(hash);
533 if (gop.found_existing) {
534 max_bytes_saved += raw_bytes.len;
535 gop.kv.value.hit_count += 1;
536 std.debug.warn(
537 "duplicate: {} {} ({Bi2})\n",
538 libc_target.name,
539 rel_path,
540 raw_bytes.len,
541 );
542 } else {
543 gop.kv.value = Contents{
544 .bytes = trimmed,
545 .hit_count = 1,
546 .hash = hash,
547 .is_generic = false,
548 };
549 }
550 const path_gop = try path_table.getOrPut(rel_path);
551 const target_to_hash = if (path_gop.found_existing) path_gop.kv.value else blk: {
552 const ptr = try allocator.create(TargetToHash);
553 ptr.* = TargetToHash.init(allocator);
554 path_gop.kv.value = ptr;
555 break :blk ptr;
556 };
557 assert((try target_to_hash.put(dest_target, hash)) == null);
558 },
559 else => std.debug.warn("warning: weird file: {}\n", full_path),
560 }
561 }
562 }
563 break;
564 } else {
565 std.debug.warn("warning: libc target not found: {}\n", libc_target.name);
566 }
567 }
568 std.debug.warn("summary: {Bi2} could be reduced to {Bi2}\n", total_bytes, total_bytes - max_bytes_saved);
569 try std.os.makePath(allocator, out_dir);
570
571 var missed_opportunity_bytes: usize = 0;
572 // iterate path_table. for each path, put all the hashes into a list. sort by hit_count.
573 // the hash with the highest hit_count gets to be the "generic" one. everybody else
574 // gets their header in a separate arch directory.
575 var path_it = path_table.iterator();
576 while (path_it.next()) |path_kv| {
577 var contents_list = std.ArrayList(*Contents).init(allocator);
578 {
579 var hash_it = path_kv.value.iterator();
580 while (hash_it.next()) |hash_kv| {
581 const contents = &hash_to_contents.get(hash_kv.value).?.value;
582 try contents_list.append(contents);
583 }
584 }
585 std.sort.sort(*Contents, contents_list.toSlice(), Contents.hitCountLessThan);
586 var best_contents = contents_list.popOrNull().?;
587 if (best_contents.hit_count > 1) {
588 // worth it to make it generic
589 const full_path = try std.os.path.join(allocator, [][]const u8{ out_dir, "generic", path_kv.key });
590 try std.os.makePath(allocator, std.os.path.dirname(full_path).?);
591 try std.io.writeFile(full_path, best_contents.bytes);
592 best_contents.is_generic = true;
593 while (contents_list.popOrNull()) |contender| {
594 if (contender.hit_count > 1) {
595 const this_missed_bytes = contender.hit_count * contender.bytes.len;
596 missed_opportunity_bytes += this_missed_bytes;
597 std.debug.warn("Missed opportunity ({Bi2}): {}\n", this_missed_bytes, path_kv.key);
598 } else break;
599 }
600 }
601 var hash_it = path_kv.value.iterator();
602 while (hash_it.next()) |hash_kv| {
603 const contents = &hash_to_contents.get(hash_kv.value).?.value;
604 if (contents.is_generic) continue;
605
606 const dest_target = hash_kv.key;
607 const out_subpath = try std.fmt.allocPrint(
608 allocator,
609 "{}-{}-{}",
610 @tagName(dest_target.arch),
611 @tagName(dest_target.os),
612 @tagName(dest_target.abi),
613 );
614 const full_path = try std.os.path.join(allocator, [][]const u8{ out_dir, out_subpath, path_kv.key });
615 try std.os.makePath(allocator, std.os.path.dirname(full_path).?);
616 try std.io.writeFile(full_path, contents.bytes);
617 }
618 }
619}
620
621fn usageAndExit(arg0: []const u8) noreturn {
622 std.debug.warn("Usage: {} [--search-path <dir>] --out <dir>\n", arg0);
623 std.debug.warn("--search-path can be used any number of times.\n");
624 std.debug.warn(" subdirectories of search paths look like, e.g. x86_64-linux-gnu\n");
625 std.debug.warn("--out is a dir that will be created, and populated with the results\n");
626 std.os.exit(1);
627}