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}