From c1699c1ca6b74518e48aadc311fde64328d1f281 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 14 Aug 2024 16:22:11 +0900 Subject: [PATCH] all: support Windows with Go 1.23 --- .github/workflows/test.yml | 6 +- 1.23_windows/internal/testenv/exec.go.patch | 5 + .../internal/testenv/testenv.go.patch | 5 + .../runtime/cgo/gcc_windows_amd64.c.patch | 19 +++ 1.23_windows/runtime/os_windows.go.patch | 118 ++++++++++++++++++ 1.23_windows/runtime/syscall_windows.go.patch | 7 ++ .../runtime/syscall_windows_test.go.patch | 5 + overlay.go | 36 +++++- 8 files changed, 195 insertions(+), 6 deletions(-) create mode 100644 1.23_windows/internal/testenv/exec.go.patch create mode 100644 1.23_windows/internal/testenv/testenv.go.patch create mode 100644 1.23_windows/runtime/cgo/gcc_windows_amd64.c.patch create mode 100644 1.23_windows/runtime/os_windows.go.patch create mode 100644 1.23_windows/runtime/syscall_windows.go.patch create mode 100644 1.23_windows/runtime/syscall_windows_test.go.patch diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 00e6cc3..75c5782 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -65,7 +65,6 @@ jobs: GOARCH=arm64 go run test.go -qemu -args="-test.run=^Test -test.v internal/runtime/atomic" - name: Test (amd64) - if: runner.os == 'Linux' || (runner.os == 'Windows' && !startsWith(matrix.go, '1.23.')) run: | go run test.go -args="-test.run=^Test -test.v fmt" go run test.go -args="-test.run=^Test -test.v internal/abi" @@ -97,13 +96,12 @@ jobs: go run test.go -args="-test.run=^Test -test.v runtime/internal/atomic" - name: Test (amd64, Go >=1.23) - if: (runner.os == 'Linux' || (runner.os == 'Windows' && !startsWith(matrix.go, '1.23.'))) && startsWith(matrix.go, '1.23.') + if: startsWith(matrix.go, '1.23.') run: | go run test.go -args="-test.run=^Test -test.v internal/runtime/atomic" - name: Test (amd64, runtime) # Skip runtime tests with Go 1.19 and Windows, as there is an issue (probably golang/go#51007 and golang/go#57455). - # For Go 1.23, Windows is not supported yet. - if: runner.os != 'Windows' || (!startsWith(matrix.go, '1.19.') && !startsWith(matrix.go, '1.23.')) + if: runner.os != 'Windows' || !startsWith(matrix.go, '1.19.') run: | go run test.go -args="-test.run=^Test -test.v -test.short runtime" diff --git a/1.23_windows/internal/testenv/exec.go.patch b/1.23_windows/internal/testenv/exec.go.patch new file mode 100644 index 0000000..a602b7b --- /dev/null +++ b/1.23_windows/internal/testenv/exec.go.patch @@ -0,0 +1,5 @@ +//--from +func tryExec() error { +//--to +func tryExec() error { + return fmt.Errorf("can't probe for exec support") diff --git a/1.23_windows/internal/testenv/testenv.go.patch b/1.23_windows/internal/testenv/testenv.go.patch new file mode 100644 index 0000000..f659d6e --- /dev/null +++ b/1.23_windows/internal/testenv/testenv.go.patch @@ -0,0 +1,5 @@ +//--from +func HasGoBuild() bool { +//--to +func HasGoBuild() bool { + return false diff --git a/1.23_windows/runtime/cgo/gcc_windows_amd64.c.patch b/1.23_windows/runtime/cgo/gcc_windows_amd64.c.patch new file mode 100644 index 0000000..a93766f --- /dev/null +++ b/1.23_windows/runtime/cgo/gcc_windows_amd64.c.patch @@ -0,0 +1,19 @@ +//--from +static void +threadentry(void *v) +{ +//--to +static int getproccount() { + static int proccount = 0; + if (!proccount) { + SYSTEM_INFO info; + GetSystemInfo(&info); + proccount = info.dwNumberOfProcessors; + } + return proccount; +} + +static void +threadentry(void *v) +{ + SetThreadAffinityMask(GetCurrentThread(), (1<= 0x80 { + isASCII = false + break + } + } + + if !isASCII { + var m uint32 + isConsole := stdcall2(_GetConsoleMode, handle, uintptr(unsafe.Pointer(&m))) != 0 + // If this is a console output, various non-unicode code pages can be in use. + // Use the dedicated WriteConsole call to ensure unicode is printed correctly. + if isConsole { + return int32(writeConsole(handle, buf, n)) + } + } + var written uint32 + stdcall5(_WriteFile, handle, uintptr(buf), uintptr(n), uintptr(unsafe.Pointer(&written)), 0) + return int32(written) +} +//--to +func write1(fd uintptr, buf unsafe.Pointer, n int32) int32 { + const ( + _STD_OUTPUT_HANDLE = ^uintptr(10) // -11 + _STD_ERROR_HANDLE = ^uintptr(11) // -12 + ) + var handle uintptr + switch fd { + case 1: + handle = stdcall1(_GetStdHandle, _STD_OUTPUT_HANDLE) + case 2: + handle = stdcall1(_GetStdHandle, _STD_ERROR_HANDLE) + default: + // assume fd is real windows handle. + handle = fd + } + if fd == 1 || fd == 2 { + // Note that handle is not used anyway. + return int32(writeConsole(handle, buf, n)) + } + var written uint32 + stdcall5(_WriteFile, handle, uintptr(buf), uintptr(n), uintptr(unsafe.Pointer(&written)), 0) + return int32(written) +} +//--from +func writeConsoleUTF16(handle uintptr, b []uint16) { + l := uint32(len(b)) + if l == 0 { + return + } + var written uint32 + stdcall5(_WriteConsoleW, + handle, + uintptr(unsafe.Pointer(&b[0])), + uintptr(l), + uintptr(unsafe.Pointer(&written)), + 0, + ) + return +} +//--to +func writeConsoleUTF16(handle uintptr, b []uint16) { + b = b[:len(b)+1] + b[len(b)-1] = 0 + l := uint32(len(b)) + if l <= 1 { + return + } + stdcall1(_OutputDebugStringW, + uintptr(unsafe.Pointer(&b[0])), + ) + return +} diff --git a/1.23_windows/runtime/syscall_windows.go.patch b/1.23_windows/runtime/syscall_windows.go.patch new file mode 100644 index 0000000..60881ed --- /dev/null +++ b/1.23_windows/runtime/syscall_windows.go.patch @@ -0,0 +1,7 @@ +//--from +func syscall_SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { +//--to +func syscall_SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("fn must not be 0 at SyscallN") + } diff --git a/1.23_windows/runtime/syscall_windows_test.go.patch b/1.23_windows/runtime/syscall_windows_test.go.patch new file mode 100644 index 0000000..dae88cd --- /dev/null +++ b/1.23_windows/runtime/syscall_windows_test.go.patch @@ -0,0 +1,5 @@ +//--from +func TestNumCPU(t *testing.T) { +//--to +func TestNumCPU(t *testing.T) { + t.Skip("creating a new process with os.Args[0] doesn't work in this environment") diff --git a/overlay.go b/overlay.go index 04ddf99..c10272d 100644 --- a/overlay.go +++ b/overlay.go @@ -114,6 +114,13 @@ func goVersion() string { return m[1] } +func goMajorMinorVersion() (int, int) { + tokens := strings.Split(goVersion(), ".") + major, _ := strconv.Atoi(tokens[0]) + minor, _ := strconv.Atoi(tokens[1]) + return major, minor +} + // GenOverlayJSON generates a JSON file for go-build's `-overlay` option. // GenOverlayJSON returns a JSON file content, or an error if generating it fails. // @@ -335,7 +342,27 @@ func init() { return nil, err } - if err := replace(tmpDir, replaces, "os", "exec_windows.go", `import ( + major, minor := goMajorMinorVersion() + if major != 1 { + return nil, fmt.Errorf("hitsumabushi: unexpected major version: %d", major) + } + if minor >= 23 { + if err := replace(tmpDir, replaces, "os", "exec_windows.go", `import ( + "errors" + "internal/syscall/windows" + "runtime" + "syscall" + "time" +)`, `import ( + "errors" + "runtime" + "syscall" + "time" +)`, cfg.os); err != nil { + return nil, err + } + } else { + if err := replace(tmpDir, replaces, "os", "exec_windows.go", `import ( "errors" "internal/syscall/windows" "runtime" @@ -349,7 +376,8 @@ func init() { "syscall" "time" )`, cfg.os); err != nil { - return nil, err + return nil, err + } } } @@ -538,6 +566,10 @@ func replace(tmpDir string, replaces map[string]string, pkg string, filename str } defer dst.Close() + if !strings.Contains(string(srcContent), old) { + return fmt.Errorf("hitsumabushi: replacing %s/%s failed: %s", pkg, filename, old) + } + replaced := strings.Replace(string(srcContent), old, new, 1) if string(srcContent) == replaced { return fmt.Errorf("hitsumabushi: replacing %s/%s failed: replacing result is the same", pkg, filename)