From a90ec84bee91de62c826272560ee3fbfe1d9b7ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Tue, 6 Oct 2026 23:28:29 +0200 Subject: [PATCH] fix(arch): narrow file names by go/build's suffix rule Assisted-by: GLM 5.3 Flash --- arch/arch.go | 41 +++++++++++++++++++++++++++++++---------- arch/arch_test.go | 31 +++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/arch/arch.go b/arch/arch.go index 3af6a91..4c183a1 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -24,20 +24,41 @@ const ( Unknown Arch = "" ) -// FromFilename guesses the target architecture from a source file name. Go -// assembly files conventionally carry a GOARCH suffix such as "_amd64.s", -// "_arm64.s", "_riscv64.s" or "_loong64.s". It returns Unknown when no suffix -// matches. +// FromFilename guesses the target architecture from a source file name, by +// go/build's goodOSArchFile rule: directories are stripped, the name is cut +// at its first dot, everything before the first underscore is ignored and a +// trailing _test segment is dropped; the name is per-architecture only when +// its final underscore segment is an architecture name (foo_amd64.s, +// sys_darwin_arm64.s, foo_amd64_test.s). Any other name narrows nothing: +// not one whose last segment merely contains an architecture name +// (x_loong64y.s), not one with no underscore at all (amd64.s, which +// go/build also keeps generic), and not one differing in case +// (foo_AMD64.s), because the segment match is case-sensitive as go/build's +// is. A name carrying an operating system alone (foo_linux.s) narrows by +// GOOS rather than architecture, and one carrying an architecture gasm +// does not assemble for (vlop_arm.s) reports Unknown so the caller's own +// port filter decides. It returns Unknown when the name narrows nothing. func FromFilename(name string) Arch { - lower := strings.ToLower(name) - switch { - case strings.Contains(lower, "_amd64"): + if i := strings.LastIndexByte(name, '/'); i >= 0 { + name = name[i+1:] + } + name, _, _ = strings.Cut(name, ".") + i := strings.Index(name, "_") + if i < 0 { + return Unknown + } + segs := strings.Split(name[i:], "_") + if last := len(segs) - 1; segs[last] == "test" { + segs = segs[:last] + } + switch segs[len(segs)-1] { + case "amd64": return AMD64 - case strings.Contains(lower, "_arm64"): + case "arm64": return ARM64 - case strings.Contains(lower, "_riscv64"), strings.Contains(lower, "_riscv"): + case "riscv64": return RISCV - case strings.Contains(lower, "_loong64"), strings.Contains(lower, "_loong"): + case "loong64": return LOONG64 default: return Unknown diff --git a/arch/arch_test.go b/arch/arch_test.go index c764031..92a048f 100644 --- a/arch/arch_test.go +++ b/arch/arch_test.go @@ -7,13 +7,40 @@ import "testing" func TestFromFilename(t *testing.T) { cases := map[string]Arch{ + // Positive: the final underscore segment is the architecture. "avx2_amd64.s": AMD64, "foo_arm64.s": ARM64, - "portable.s": Unknown, - "decode_ARM64.S": ARM64, "kernels_amd64.s": AMD64, "kernel_riscv64.s": RISCV, "kernel_loong64.s": LOONG64, + "decode_arm64.S": ARM64, // the extension is cut with the first dot + // OS before arch: the __ pair form. + "sys_darwin_arm64.s": ARM64, + "rt0_linux_amd64.s": AMD64, + // A trailing _test segment is dropped before the suffix rule. + "foo_amd64_test.s": AMD64, + // Trailing junk: a final segment that merely contains an + // architecture name narrows nothing, exactly as go/build's + // segment rule says. + "x_loong64y.s": Unknown, + "asm_amd64x.s": Unknown, // a real GOROOT name + "asm_darwin_arm64_gc.s": Unknown, // the trailing _gc segment is junk + "x_loong.s": Unknown, + "foo_amd64x_test.s": Unknown, + // Case sensitivity: the segment match is exact, as go/build's is. + "foo_AMD64.s": Unknown, + "decode_ARM64.S": Unknown, + // No underscore at all: generic whatever the stem says. + "amd64.s": Unknown, + "portable.s": Unknown, + // A GOOS-only name narrows by operating system, not architecture. + "foo_linux.s": Unknown, + // An architecture gasm does not assemble for: named, but Unknown. + "vlop_arm.s": Unknown, + "foo_riscv.s": Unknown, + // Directories are stripped first, whatever dots they carry. + "some/dir/kernel_loong64.s": LOONG64, + "/a.b/x_amd64.s": AMD64, } for name, want := range cases { if got := FromFilename(name); got != want {