Skip to content

Commit

Permalink
all: support Windows with Go 1.23
Browse files Browse the repository at this point in the history
  • Loading branch information
hajimehoshi committed Aug 14, 2024
1 parent 8de20e4 commit c1699c1
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 6 deletions.
6 changes: 2 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
5 changes: 5 additions & 0 deletions 1.23_windows/internal/testenv/exec.go.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//--from
func tryExec() error {
//--to
func tryExec() error {
return fmt.Errorf("can't probe for exec support")
5 changes: 5 additions & 0 deletions 1.23_windows/internal/testenv/testenv.go.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//--from
func HasGoBuild() bool {
//--to
func HasGoBuild() bool {
return false
19 changes: 19 additions & 0 deletions 1.23_windows/runtime/cgo/gcc_windows_amd64.c.patch
Original file line number Diff line number Diff line change
@@ -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<<getproccount())-1);
118 changes: 118 additions & 0 deletions 1.23_windows/runtime/os_windows.go.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//--from
//go:cgo_import_dynamic runtime._GetConsoleMode GetConsoleMode%2 "kernel32.dll"
//--to
//--from
//go:cgo_import_dynamic runtime._SetConsoleCtrlHandler SetConsoleCtrlHandler%2 "kernel32.dll"
//--to
//--from
//go:cgo_import_dynamic runtime._WriteConsoleW WriteConsoleW%5 "kernel32.dll"
//--to
//go:cgo_import_dynamic runtime._OutputDebugStringW OutputDebugStringW%1 "kernel32.dll"
//--from
_GetConsoleMode,
//--to
//--from
_SetConsoleCtrlHandler,
//--to
//--from
_WriteConsoleW,
//--to
_OutputDebugStringW,
//--from
// We call these all the way here, late in init, so that malloc works
// for the callback functions these generate.
var fn any = ctrlHandler
ctrlHandlerPC := compileCallback(*efaceOf(&fn), true)
stdcall2(_SetConsoleCtrlHandler, ctrlHandlerPC, 1)
//--to
//--from
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
}
isASCII := true
b := (*[1 << 30]byte)(buf)[:n]
for _, x := range b {
if x >= 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
}
7 changes: 7 additions & 0 deletions 1.23_windows/runtime/syscall_windows.go.patch
Original file line number Diff line number Diff line change
@@ -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")
}
5 changes: 5 additions & 0 deletions 1.23_windows/runtime/syscall_windows_test.go.patch
Original file line number Diff line number Diff line change
@@ -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")
36 changes: 34 additions & 2 deletions overlay.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand Down Expand Up @@ -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"
Expand All @@ -349,7 +376,8 @@ func init() {
"syscall"
"time"
)`, cfg.os); err != nil {
return nil, err
return nil, err
}
}
}

Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit c1699c1

Please sign in to comment.