master
1//! This script updates the .c, .h, .s, and .S files that make up the start
2//! files such as crt1.o. Not to be confused with
3//! https://github.com/ziglang/glibc-abi-tool/ which updates the `abilists`
4//! file.
5//!
6//! Example usage:
7//! `zig run ../tools/update_glibc.zig -- ~/Downloads/glibc ..`
8
9const std = @import("std");
10const mem = std.mem;
11const log = std.log;
12const fs = std.fs;
13
14const exempt_files = [_][]const u8{
15 // This file is maintained by a separate project and does not come from glibc.
16 "abilists",
17
18 // Generated files.
19 "include/libc-modules.h",
20 "include/config.h",
21
22 // These are easier to maintain like this, without updating to the abi-note.c
23 // that glibc did upstream.
24 "csu/abi-tag.h",
25 "csu/abi-note.S",
26
27 // We have patched these files to require fewer includes.
28 "stdlib/at_quick_exit.c",
29 "stdlib/atexit.c",
30 "sysdeps/pthread/pthread_atfork.c",
31};
32
33const exempt_extensions = [_][]const u8{
34 // These are the start files we use when targeting glibc <= 2.33.
35 "-2.33.S",
36 "-2.33.c",
37};
38
39pub fn main() !void {
40 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
41 defer arena_instance.deinit();
42 const arena = arena_instance.allocator();
43
44 const args = try std.process.argsAlloc(arena);
45 const glibc_src_path = args[1];
46 const zig_src_path = args[2];
47
48 const dest_dir_path = try std.fmt.allocPrint(arena, "{s}/lib/libc/glibc", .{zig_src_path});
49
50 var dest_dir = fs.cwd().openDir(dest_dir_path, .{ .iterate = true }) catch |err| {
51 fatal("unable to open destination directory '{s}': {s}", .{
52 dest_dir_path, @errorName(err),
53 });
54 };
55 defer dest_dir.close();
56
57 var glibc_src_dir = try fs.cwd().openDir(glibc_src_path, .{});
58 defer glibc_src_dir.close();
59
60 // Copy updated files from upstream.
61 {
62 var walker = try dest_dir.walk(arena);
63 defer walker.deinit();
64
65 walk: while (try walker.next()) |entry| {
66 if (entry.kind != .file) continue;
67 if (mem.startsWith(u8, entry.basename, ".")) continue;
68 for (exempt_files) |p| {
69 if (mem.eql(u8, entry.path, p)) continue :walk;
70 }
71 for (exempt_extensions) |ext| {
72 if (mem.endsWith(u8, entry.path, ext)) continue :walk;
73 }
74
75 glibc_src_dir.copyFile(entry.path, dest_dir, entry.path, .{}) catch |err| {
76 log.warn("unable to copy '{s}/{s}' to '{s}/{s}': {s}", .{
77 glibc_src_path, entry.path,
78 dest_dir_path, entry.path,
79 @errorName(err),
80 });
81 if (err == error.FileNotFound) {
82 try dest_dir.deleteFile(entry.path);
83 }
84 };
85 }
86 }
87
88 // Warn about duplicated files inside glibc/include/* that can be omitted
89 // because they are already in generic-glibc/*.
90
91 var include_dir = dest_dir.openDir("include", .{ .iterate = true }) catch |err| {
92 fatal("unable to open directory '{s}/include': {s}", .{
93 dest_dir_path, @errorName(err),
94 });
95 };
96 defer include_dir.close();
97
98 const generic_glibc_path = try std.fmt.allocPrint(
99 arena,
100 "{s}/lib/libc/include/generic-glibc",
101 .{zig_src_path},
102 );
103 var generic_glibc_dir = try fs.cwd().openDir(generic_glibc_path, .{});
104 defer generic_glibc_dir.close();
105
106 var walker = try include_dir.walk(arena);
107 defer walker.deinit();
108
109 walk: while (try walker.next()) |entry| {
110 if (entry.kind != .file) continue;
111 if (mem.startsWith(u8, entry.basename, ".")) continue;
112 for (exempt_files) |p| {
113 if (mem.eql(u8, entry.path, p)) continue :walk;
114 }
115
116 const max_file_size = 10 * 1024 * 1024;
117
118 const generic_glibc_contents = generic_glibc_dir.readFileAlloc(
119 entry.path,
120 arena,
121 .limited(max_file_size),
122 ) catch |err| switch (err) {
123 error.FileNotFound => continue,
124 else => |e| fatal("unable to load '{s}/include/{s}': {s}", .{
125 generic_glibc_path, entry.path, @errorName(e),
126 }),
127 };
128 const glibc_include_contents = include_dir.readFileAlloc(
129 entry.path,
130 arena,
131 .limited(max_file_size),
132 ) catch |err| {
133 fatal("unable to load '{s}/include/{s}': {s}", .{
134 dest_dir_path, entry.path, @errorName(err),
135 });
136 };
137
138 const whitespace = " \r\n\t";
139 const generic_glibc_trimmed = mem.trim(u8, generic_glibc_contents, whitespace);
140 const glibc_include_trimmed = mem.trim(u8, glibc_include_contents, whitespace);
141 if (mem.eql(u8, generic_glibc_trimmed, glibc_include_trimmed)) {
142 log.warn("same contents: '{s}/include/{s}' and '{s}/include/{s}'", .{
143 generic_glibc_path, entry.path,
144 dest_dir_path, entry.path,
145 });
146 }
147 }
148}
149
150fn fatal(comptime format: []const u8, args: anytype) noreturn {
151 log.err(format, args);
152 std.process.exit(1);
153}