Merge branch 'golang:master' into master

This commit is contained in:
Patryk Chelmecki 2023-05-17 22:44:58 +02:00 committed by GitHub
commit cc69ab9be3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1110 changed files with 20825 additions and 14921 deletions

View File

@ -1 +1,2 @@
pkg errors, var ErrUnsupported error #41198
pkg net/http, method (*ProtocolError) Is(error) bool #41198

2
api/next/54451.txt Normal file
View File

@ -0,0 +1,2 @@
pkg io/fs, func FormatDirEntry(DirEntry) string #54451
pkg io/fs, func FormatFileInfo(FileInfo) string #54451

View File

@ -47,7 +47,6 @@ pkg log/slog, func Error(string, ...interface{}) #56345
pkg log/slog, func ErrorCtx(context.Context, string, ...interface{}) #56345
pkg log/slog, func Float64(string, float64) Attr #56345
pkg log/slog, func Float64Value(float64) Value #56345
pkg log/slog, func Group(string, ...Attr) Attr #56345
pkg log/slog, func GroupValue(...Attr) Value #56345
pkg log/slog, func Info(string, ...interface{}) #56345
pkg log/slog, func InfoCtx(context.Context, string, ...interface{}) #56345
@ -58,10 +57,8 @@ pkg log/slog, func IntValue(int) Value #56345
pkg log/slog, func Log(context.Context, Level, string, ...interface{}) #56345
pkg log/slog, func LogAttrs(context.Context, Level, string, ...Attr) #56345
pkg log/slog, func New(Handler) *Logger #56345
pkg log/slog, func NewJSONHandler(io.Writer) *JSONHandler #56345
pkg log/slog, func NewLogLogger(Handler, Level) *log.Logger #56345
pkg log/slog, func NewRecord(time.Time, Level, string, uintptr) Record #56345
pkg log/slog, func NewTextHandler(io.Writer) *TextHandler #56345
pkg log/slog, func SetDefault(*Logger) #56345
pkg log/slog, func String(string, string) Attr #56345
pkg log/slog, func StringValue(string) Value #56345
@ -105,8 +102,6 @@ pkg log/slog, method (*TextHandler) WithAttrs([]Attr) Handler #56345
pkg log/slog, method (*TextHandler) WithGroup(string) Handler #56345
pkg log/slog, method (Attr) Equal(Attr) bool #56345
pkg log/slog, method (Attr) String() string #56345
pkg log/slog, method (HandlerOptions) NewJSONHandler(io.Writer) *JSONHandler #56345
pkg log/slog, method (HandlerOptions) NewTextHandler(io.Writer) *TextHandler #56345
pkg log/slog, method (Kind) String() string #56345
pkg log/slog, method (Level) Level() Level #56345
pkg log/slog, method (Level) MarshalJSON() ([]uint8, error) #56345

1
api/next/59204.txt Normal file
View File

@ -0,0 +1 @@
pkg log/slog, func Group(string, ...interface{}) Attr #59204

2
api/next/59339.txt Normal file
View File

@ -0,0 +1,2 @@
pkg log/slog, func NewJSONHandler(io.Writer, *HandlerOptions) *JSONHandler #59339
pkg log/slog, func NewTextHandler(io.Writer, *HandlerOptions) *TextHandler #59339

View File

@ -31,8 +31,21 @@ Do not send CLs removing the interior tags from such phrases.
<h2 id="ports">Ports</h2>
<h3 id="wasip1">WebAssembly System Interface</h3>
<p><!-- https://go.dev/issue/58141 -->
Go 1.21 adds an experimental port to the <a href="https://wasi.dev/">
WebAssembly System Interface (WASI)</a>, Preview 1
(<code>GOOS=wasip1</code>, <code>GOARCH=wasm</code>).
</p>
<p>
TODO: complete this section, or delete if not needed
As a result of the addition of the new <code>GOOS</code> value
"<code>wasip1</code>", Go files named <code>*_wasip1.go</code>
will now be <a href="/pkg/go/build/#hdr-Build_Constraints">ignored
by Go tools</a> except when that GOOS value is being used. If you
have existing filenames matching that pattern, you will need to
rename them.
</p>
<h2 id="tools">Tools</h2>
@ -105,6 +118,18 @@ Do not send CLs removing the interior tags from such phrases.
</dd>
</dl>
<dl id="reflect"><dt><a href="/pkg/reflect/">reflect</a></dt>
<dd>
<p><!-- CL 408826, CL 413474 -->
In Go 1.21, <a href="/pkg/reflect/#ValueOf"><code>ValueOf</code></a>
no longer forces its argument to be allocated on the heap, allowing
a <code>Value</code>'s content to be allocated on the stack. Most
operations on a <code>Value</code> also allow the underlying value
to be stack allocated.
</p>
</dd>
</dl>
<dl id="sync"><dt><a href="/pkg/sync/">sync</a></dt>
<dd>
<p><!-- https://go.dev/issue/56102, CL 451356 -->

View File

@ -24,8 +24,8 @@
# in the CL match the update.bash in the CL.
# Versions to use.
CODE=2022g
DATA=2022g
CODE=2023c
DATA=2023c
set -e

Binary file not shown.

View File

@ -1,340 +0,0 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
// +build ignore
// This program can be used as go_android_GOARCH_exec by the Go tool.
// It executes binaries on an android device using adb.
package main
import (
"errors"
"fmt"
"go/build"
"io"
"log"
"os"
"os/exec"
"os/signal"
"path/filepath"
"runtime"
"strconv"
"strings"
"syscall"
)
func run(args ...string) (string, error) {
cmd := adbCmd(args...)
buf := new(strings.Builder)
cmd.Stdout = io.MultiWriter(os.Stdout, buf)
// If the adb subprocess somehow hangs, go test will kill this wrapper
// and wait for our os.Stderr (and os.Stdout) to close as a result.
// However, if the os.Stderr (or os.Stdout) file descriptors are
// passed on, the hanging adb subprocess will hold them open and
// go test will hang forever.
//
// Avoid that by wrapping stderr, breaking the short circuit and
// forcing cmd.Run to use another pipe and goroutine to pass
// along stderr from adb.
cmd.Stderr = struct{ io.Writer }{os.Stderr}
err := cmd.Run()
if err != nil {
return "", fmt.Errorf("adb %s: %v", strings.Join(args, " "), err)
}
return buf.String(), nil
}
func adb(args ...string) error {
if out, err := adbCmd(args...).CombinedOutput(); err != nil {
fmt.Fprintf(os.Stderr, "adb %s\n%s", strings.Join(args, " "), out)
return err
}
return nil
}
func adbCmd(args ...string) *exec.Cmd {
if flags := os.Getenv("GOANDROID_ADB_FLAGS"); flags != "" {
args = append(strings.Split(flags, " "), args...)
}
return exec.Command("adb", args...)
}
const (
deviceRoot = "/data/local/tmp/go_android_exec"
deviceGoroot = deviceRoot + "/goroot"
)
func main() {
log.SetFlags(0)
log.SetPrefix("go_android_exec: ")
exitCode, err := runMain()
if err != nil {
log.Fatal(err)
}
os.Exit(exitCode)
}
func runMain() (int, error) {
// Concurrent use of adb is flaky, so serialize adb commands.
// See https://github.com/golang/go/issues/23795 or
// https://issuetracker.google.com/issues/73230216.
lockPath := filepath.Join(os.TempDir(), "go_android_exec-adb-lock")
lock, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
return 0, err
}
defer lock.Close()
if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX); err != nil {
return 0, err
}
// In case we're booting a device or emulator alongside all.bash, wait for
// it to be ready. adb wait-for-device is not enough, we have to
// wait for sys.boot_completed.
if err := adb("wait-for-device", "exec-out", "while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;"); err != nil {
return 0, err
}
// Done once per make.bash.
if err := adbCopyGoroot(); err != nil {
return 0, err
}
// Prepare a temporary directory that will be cleaned up at the end.
// Binary names can conflict.
// E.g. template.test from the {html,text}/template packages.
binName := filepath.Base(os.Args[1])
deviceGotmp := fmt.Sprintf(deviceRoot+"/%s-%d", binName, os.Getpid())
deviceGopath := deviceGotmp + "/gopath"
defer adb("exec-out", "rm", "-rf", deviceGotmp) // Clean up.
// Determine the package by examining the current working
// directory, which will look something like
// "$GOROOT/src/mime/multipart" or "$GOPATH/src/golang.org/x/mobile".
// We extract everything after the $GOROOT or $GOPATH to run on the
// same relative directory on the target device.
subdir, inGoRoot, err := subdir()
if err != nil {
return 0, err
}
deviceCwd := filepath.Join(deviceGopath, subdir)
if inGoRoot {
deviceCwd = filepath.Join(deviceGoroot, subdir)
} else {
if err := adb("exec-out", "mkdir", "-p", deviceCwd); err != nil {
return 0, err
}
if err := adbCopyTree(deviceCwd, subdir); err != nil {
return 0, err
}
// Copy .go files from the package.
goFiles, err := filepath.Glob("*.go")
if err != nil {
return 0, err
}
if len(goFiles) > 0 {
args := append(append([]string{"push"}, goFiles...), deviceCwd)
if err := adb(args...); err != nil {
return 0, err
}
}
}
deviceBin := fmt.Sprintf("%s/%s", deviceGotmp, binName)
if err := adb("push", os.Args[1], deviceBin); err != nil {
return 0, err
}
// Forward SIGQUIT from the go command to show backtraces from
// the binary instead of from this wrapper.
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGQUIT)
go func() {
for range quit {
// We don't have the PID of the running process; use the
// binary name instead.
adb("exec-out", "killall -QUIT "+binName)
}
}()
// In light of
// https://code.google.com/p/android/issues/detail?id=3254
// dont trust the exitcode of adb. Instead, append the exitcode to
// the output and parse it from there.
const exitstr = "exitcode="
cmd := `export TMPDIR="` + deviceGotmp + `"` +
`; export GOROOT="` + deviceGoroot + `"` +
`; export GOPATH="` + deviceGopath + `"` +
`; export CGO_ENABLED=0` +
`; export GOPROXY=` + os.Getenv("GOPROXY") +
`; export GOCACHE="` + deviceRoot + `/gocache"` +
`; export PATH=$PATH:"` + deviceGoroot + `/bin"` +
`; cd "` + deviceCwd + `"` +
"; '" + deviceBin + "' " + strings.Join(os.Args[2:], " ") +
"; echo -n " + exitstr + "$?"
output, err := run("exec-out", cmd)
signal.Reset(syscall.SIGQUIT)
close(quit)
if err != nil {
return 0, err
}
exitIdx := strings.LastIndex(output, exitstr)
if exitIdx == -1 {
return 0, fmt.Errorf("no exit code: %q", output)
}
code, err := strconv.Atoi(output[exitIdx+len(exitstr):])
if err != nil {
return 0, fmt.Errorf("bad exit code: %v", err)
}
return code, nil
}
// subdir determines the package based on the current working directory,
// and returns the path to the package source relative to $GOROOT (or $GOPATH).
func subdir() (pkgpath string, underGoRoot bool, err error) {
cwd, err := os.Getwd()
if err != nil {
return "", false, err
}
cwd, err = filepath.EvalSymlinks(cwd)
if err != nil {
return "", false, err
}
goroot, err := filepath.EvalSymlinks(runtime.GOROOT())
if err != nil {
return "", false, err
}
if subdir, err := filepath.Rel(goroot, cwd); err == nil {
if !strings.Contains(subdir, "..") {
return subdir, true, nil
}
}
for _, p := range filepath.SplitList(build.Default.GOPATH) {
pabs, err := filepath.EvalSymlinks(p)
if err != nil {
return "", false, err
}
if subdir, err := filepath.Rel(pabs, cwd); err == nil {
if !strings.Contains(subdir, "..") {
return subdir, false, nil
}
}
}
return "", false, fmt.Errorf("the current path %q is not in either GOROOT(%q) or GOPATH(%q)",
cwd, runtime.GOROOT(), build.Default.GOPATH)
}
// adbCopyTree copies testdata, go.mod, go.sum files from subdir
// and from parent directories all the way up to the root of subdir.
// go.mod and go.sum files are needed for the go tool modules queries,
// and the testdata directories for tests. It is common for tests to
// reach out into testdata from parent packages.
func adbCopyTree(deviceCwd, subdir string) error {
dir := ""
for {
for _, path := range []string{"testdata", "go.mod", "go.sum"} {
path := filepath.Join(dir, path)
if _, err := os.Stat(path); err != nil {
continue
}
devicePath := filepath.Join(deviceCwd, dir)
if err := adb("exec-out", "mkdir", "-p", devicePath); err != nil {
return err
}
if err := adb("push", path, devicePath); err != nil {
return err
}
}
if subdir == "." {
break
}
subdir = filepath.Dir(subdir)
dir = filepath.Join(dir, "..")
}
return nil
}
// adbCopyGoroot clears deviceRoot for previous versions of GOROOT, GOPATH
// and temporary data. Then, it copies relevant parts of GOROOT to the device,
// including the go tool built for android.
// A lock file ensures this only happens once, even with concurrent exec
// wrappers.
func adbCopyGoroot() error {
// Also known by cmd/dist. The bootstrap command deletes the file.
statPath := filepath.Join(os.TempDir(), "go_android_exec-adb-sync-status")
stat, err := os.OpenFile(statPath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
return err
}
defer stat.Close()
// Serialize check and copying.
if err := syscall.Flock(int(stat.Fd()), syscall.LOCK_EX); err != nil {
return err
}
s, err := io.ReadAll(stat)
if err != nil {
return err
}
if string(s) == "done" {
return nil
}
// Delete GOROOT, GOPATH and any leftover test data.
if err := adb("exec-out", "rm", "-rf", deviceRoot); err != nil {
return err
}
deviceBin := filepath.Join(deviceGoroot, "bin")
if err := adb("exec-out", "mkdir", "-p", deviceBin); err != nil {
return err
}
goroot := runtime.GOROOT()
// Build go for android.
goCmd := filepath.Join(goroot, "bin", "go")
tmpGo, err := os.CreateTemp("", "go_android_exec-cmd-go-*")
if err != nil {
return err
}
tmpGo.Close()
defer os.Remove(tmpGo.Name())
if out, err := exec.Command(goCmd, "build", "-o", tmpGo.Name(), "cmd/go").CombinedOutput(); err != nil {
return fmt.Errorf("failed to build go tool for device: %s\n%v", out, err)
}
deviceGo := filepath.Join(deviceBin, "go")
if err := adb("push", tmpGo.Name(), deviceGo); err != nil {
return err
}
for _, dir := range []string{"src", "test", "lib", "api"} {
if err := adb("push", filepath.Join(goroot, dir), filepath.Join(deviceGoroot)); err != nil {
return err
}
}
// Copy only the relevant from pkg.
if err := adb("exec-out", "mkdir", "-p", filepath.Join(deviceGoroot, "pkg", "tool")); err != nil {
return err
}
if err := adb("push", filepath.Join(goroot, "pkg", "include"), filepath.Join(deviceGoroot, "pkg")); err != nil {
return err
}
runtimea, err := exec.Command(goCmd, "list", "-f", "{{.Target}}", "runtime").Output()
pkgdir := filepath.Dir(string(runtimea))
if pkgdir == "" {
return errors.New("could not find android pkg dir")
}
if err := adb("push", pkgdir, filepath.Join(deviceGoroot, "pkg")); err != nil {
return err
}
tooldir := filepath.Join(goroot, "pkg", "tool", filepath.Base(pkgdir))
if err := adb("push", tooldir, filepath.Join(deviceGoroot, "pkg", "tool")); err != nil {
return err
}
if _, err := stat.Write([]byte("done")); err != nil {
return err
}
return nil
}

View File

@ -1,58 +0,0 @@
#!/usr/bin/env bash
# Copyright 2010 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
# This is a small script for executing go binaries on the android platform.
#
# example:
# ./a 5.out foo bar baz
#
# The script exports the local values of GOARCH, GOTRACEBACK and GOGC
# to the android environment.
#
# Known issues:
# The script fails unless the last character output by the program is "\n"
#
# TODO(kaib): add gdb bridge support
exp ()
{
if [ ${!1} ]; then
echo "export $1=\"${!1}\"; "
fi
}
# adb does not correctly return the exit value of the executed program. use this
# wrapper to manually extract the exit value
rloc=/data/local/tmp/retval
rsize=$(adb shell "ls -l $rloc"|tr -s ' '|cut -d' ' -f4)
rcheck=38
if [ "$rsize" != "$rcheck" ]; then
# echo "debug: retval size incorrect want $rcheck, got $rsize. uploading"
echo >/tmp/adb.retval '#!/system/bin/sh
"$@"
echo RETVAL: $?'
adb push /tmp/adb.retval $rloc >/dev/null 2>&1
adb shell chmod 755 $rloc
fi
# run the main binary
if [ "-g" == "$1" ]; then
adb forward tcp:$2 tcp:$2
args=$(echo $*| cut -d' ' -f4-)
adb push $3 /data/local/tmp/$3 >/dev/null 2>&1
adb shell "$(exp GOARCH) $(exp GOTRACEBACK) $(exp GOGC) \
gdbserver :$2 /data/local/tmp/retval /data/local/tmp/$3 $args" \
2>&1|tr -d '\r' |tee /tmp/adb.out|grep -v RETVAL
else
if [ "$*" != "$1" ]; then
args=$(echo $*| cut -d' ' -f2-)
fi
adb push $1 /data/local/tmp/$1 >/dev/null 2>&1
adb shell "$(exp GOARCH) $(exp GOTRACEBACK) $(exp GOGC) \
/data/local/tmp/retval /data/local/tmp/$1 $args" \
2>&1|tr -d '\r' |tee /tmp/adb.out|grep -v RETVAL
fi
exit $(grep RETVAL /tmp/adb.out|tr -d '\n\r'| cut -d' ' -f2)

View File

@ -1,9 +0,0 @@
package cgotest
import (
"testing"
"misc/cgo/test/issue9026"
)
func test9026(t *testing.T) { issue9026.Test(t) }

View File

@ -1,11 +1,6 @@
// Module misc contains tests and binaries that pertain to specific build modes
// (cgo) and platforms (Android and iOS).
//
// The 'run' scripts in ../src execute these tests and binaries, which need to
// be in a module in order to build and run successfully in module mode.
// (Otherwise, they lack well-defined import paths, and module mode unlike
// GOPATH mode does not synthesize import paths from the absolute working
// directory.)
// Module misc contains binaries that pertain to specific platforms
// (Android, iOS, and WebAssembly), as well as some miscellaneous
// tests and tools.
module misc
go 1.21

View File

@ -0,0 +1,76 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !(windows || js || wasip1)
package main
import (
"regexp"
"strings"
"testing"
)
func TestExitCodeFilter(t *testing.T) {
// Write text to the filter one character at a time.
var out strings.Builder
f, exitStr := newExitCodeFilter(&out)
// Embed a "fake" exit code in the middle to check that we don't get caught on it.
pre := "abc" + exitStr + "123def"
text := pre + exitStr + `1`
for i := 0; i < len(text); i++ {
_, err := f.Write([]byte{text[i]})
if err != nil {
t.Fatal(err)
}
}
// The "pre" output should all have been flushed already.
if want, got := pre, out.String(); want != got {
t.Errorf("filter should have already flushed %q, but flushed %q", want, got)
}
code, err := f.Finish()
if err != nil {
t.Fatal(err)
}
// Nothing more should have been written to out.
if want, got := pre, out.String(); want != got {
t.Errorf("want output %q, got %q", want, got)
}
if want := 1; want != code {
t.Errorf("want exit code %d, got %d", want, code)
}
}
func TestExitCodeMissing(t *testing.T) {
var wantErr *regexp.Regexp
check := func(text string) {
t.Helper()
var out strings.Builder
f, exitStr := newExitCodeFilter(&out)
if want := "exitcode="; want != exitStr {
t.Fatalf("test assumes exitStr will be %q, but got %q", want, exitStr)
}
f.Write([]byte(text))
_, err := f.Finish()
// We should get a no exit code error
if err == nil || !wantErr.MatchString(err.Error()) {
t.Errorf("want error matching %s, got %s", wantErr, err)
}
// And it should flush all output (even if it looks
// like we may be getting an exit code)
if got := out.String(); text != got {
t.Errorf("want full output %q, got %q", text, got)
}
}
wantErr = regexp.MustCompile("^no exit code")
check("abc")
check("exitcode")
check("exitcode=")
check("exitcode=123\n")
wantErr = regexp.MustCompile("^bad exit code: .* value out of range")
check("exitcode=999999999999999999999999")
}

View File

@ -0,0 +1,526 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This wrapper uses syscall.Flock to prevent concurrent adb commands,
// so for now it only builds on platforms that support that system call.
// TODO(#33974): use a more portable library for file locking.
//go:build darwin || dragonfly || freebsd || illumos || linux || netbsd || openbsd
// This program can be used as go_android_GOARCH_exec by the Go tool.
// It executes binaries on an android device using adb.
package main
import (
"bytes"
"errors"
"fmt"
"io"
"log"
"os"
"os/exec"
"os/signal"
"path"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"sync"
"syscall"
)
func adbRun(args string) (int, error) {
// The exit code of adb is often wrong. In theory it was fixed in 2016
// (https://code.google.com/p/android/issues/detail?id=3254), but it's
// still broken on our builders in 2023. Instead, append the exitcode to
// the output and parse it from there.
filter, exitStr := newExitCodeFilter(os.Stdout)
args += "; echo -n " + exitStr + "$?"
cmd := adbCmd("exec-out", args)
cmd.Stdout = filter
// If the adb subprocess somehow hangs, go test will kill this wrapper
// and wait for our os.Stderr (and os.Stdout) to close as a result.
// However, if the os.Stderr (or os.Stdout) file descriptors are
// passed on, the hanging adb subprocess will hold them open and
// go test will hang forever.
//
// Avoid that by wrapping stderr, breaking the short circuit and
// forcing cmd.Run to use another pipe and goroutine to pass
// along stderr from adb.
cmd.Stderr = struct{ io.Writer }{os.Stderr}
err := cmd.Run()
// Before we process err, flush any further output and get the exit code.
exitCode, err2 := filter.Finish()
if err != nil {
return 0, fmt.Errorf("adb exec-out %s: %v", args, err)
}
return exitCode, err2
}
func adb(args ...string) error {
if out, err := adbCmd(args...).CombinedOutput(); err != nil {
fmt.Fprintf(os.Stderr, "adb %s\n%s", strings.Join(args, " "), out)
return err
}
return nil
}
func adbCmd(args ...string) *exec.Cmd {
if flags := os.Getenv("GOANDROID_ADB_FLAGS"); flags != "" {
args = append(strings.Split(flags, " "), args...)
}
return exec.Command("adb", args...)
}
const (
deviceRoot = "/data/local/tmp/go_android_exec"
deviceGoroot = deviceRoot + "/goroot"
)
func main() {
log.SetFlags(0)
log.SetPrefix("go_android_exec: ")
exitCode, err := runMain()
if err != nil {
log.Fatal(err)
}
os.Exit(exitCode)
}
func runMain() (int, error) {
// Concurrent use of adb is flaky, so serialize adb commands.
// See https://github.com/golang/go/issues/23795 or
// https://issuetracker.google.com/issues/73230216.
lockPath := filepath.Join(os.TempDir(), "go_android_exec-adb-lock")
lock, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
return 0, err
}
defer lock.Close()
if err := syscall.Flock(int(lock.Fd()), syscall.LOCK_EX); err != nil {
return 0, err
}
// In case we're booting a device or emulator alongside all.bash, wait for
// it to be ready. adb wait-for-device is not enough, we have to
// wait for sys.boot_completed.
if err := adb("wait-for-device", "exec-out", "while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done;"); err != nil {
return 0, err
}
// Done once per make.bash.
if err := adbCopyGoroot(); err != nil {
return 0, err
}
// Prepare a temporary directory that will be cleaned up at the end.
// Binary names can conflict.
// E.g. template.test from the {html,text}/template packages.
binName := filepath.Base(os.Args[1])
deviceGotmp := fmt.Sprintf(deviceRoot+"/%s-%d", binName, os.Getpid())
deviceGopath := deviceGotmp + "/gopath"
defer adb("exec-out", "rm", "-rf", deviceGotmp) // Clean up.
// Determine the package by examining the current working
// directory, which will look something like
// "$GOROOT/src/mime/multipart" or "$GOPATH/src/golang.org/x/mobile".
// We extract everything after the $GOROOT or $GOPATH to run on the
// same relative directory on the target device.
importPath, isStd, modPath, modDir, err := pkgPath()
if err != nil {
return 0, err
}
var deviceCwd string
if isStd {
// Note that we use path.Join here instead of filepath.Join:
// The device paths should be slash-separated even if the go_android_exec
// wrapper itself is compiled for Windows.
deviceCwd = path.Join(deviceGoroot, "src", importPath)
} else {
deviceCwd = path.Join(deviceGopath, "src", importPath)
if modDir != "" {
// In module mode, the user may reasonably expect the entire module
// to be present. Copy it over.
deviceModDir := path.Join(deviceGopath, "src", modPath)
if err := adb("exec-out", "mkdir", "-p", path.Dir(deviceModDir)); err != nil {
return 0, err
}
// We use a single recursive 'adb push' of the module root instead of
// walking the tree and copying it piecewise. If the directory tree
// contains nested modules this could push a lot of unnecessary contents,
// but for the golang.org/x repos it seems to be significantly (~2x)
// faster than copying one file at a time (via filepath.WalkDir),
// apparently due to high latency in 'adb' commands.
if err := adb("push", modDir, deviceModDir); err != nil {
return 0, err
}
} else {
if err := adb("exec-out", "mkdir", "-p", deviceCwd); err != nil {
return 0, err
}
if err := adbCopyTree(deviceCwd, importPath); err != nil {
return 0, err
}
// Copy .go files from the package.
goFiles, err := filepath.Glob("*.go")
if err != nil {
return 0, err
}
if len(goFiles) > 0 {
args := append(append([]string{"push"}, goFiles...), deviceCwd)
if err := adb(args...); err != nil {
return 0, err
}
}
}
}
deviceBin := fmt.Sprintf("%s/%s", deviceGotmp, binName)
if err := adb("push", os.Args[1], deviceBin); err != nil {
return 0, err
}
// Forward SIGQUIT from the go command to show backtraces from
// the binary instead of from this wrapper.
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGQUIT)
go func() {
for range quit {
// We don't have the PID of the running process; use the
// binary name instead.
adb("exec-out", "killall -QUIT "+binName)
}
}()
cmd := `export TMPDIR="` + deviceGotmp + `"` +
`; export GOROOT="` + deviceGoroot + `"` +
`; export GOPATH="` + deviceGopath + `"` +
`; export CGO_ENABLED=0` +
`; export GOPROXY=` + os.Getenv("GOPROXY") +
`; export GOCACHE="` + deviceRoot + `/gocache"` +
`; export PATH="` + deviceGoroot + `/bin":$PATH` +
`; cd "` + deviceCwd + `"` +
"; '" + deviceBin + "' " + strings.Join(os.Args[2:], " ")
code, err := adbRun(cmd)
signal.Reset(syscall.SIGQUIT)
close(quit)
return code, err
}
type exitCodeFilter struct {
w io.Writer // Pass through to w
exitRe *regexp.Regexp
buf bytes.Buffer
}
func newExitCodeFilter(w io.Writer) (*exitCodeFilter, string) {
const exitStr = "exitcode="
// Build a regexp that matches any prefix of the exit string at the end of
// the input. We do it this way to avoid assuming anything about the
// subcommand output (e.g., it might not be \n-terminated).
var exitReStr strings.Builder
for i := 1; i <= len(exitStr); i++ {
fmt.Fprintf(&exitReStr, "%s$|", exitStr[:i])
}
// Finally, match the exit string along with an exit code.
// This is the only case we use a group, and we'll use this
// group to extract the numeric code.
fmt.Fprintf(&exitReStr, "%s([0-9]+)$", exitStr)
exitRe := regexp.MustCompile(exitReStr.String())
return &exitCodeFilter{w: w, exitRe: exitRe}, exitStr
}
func (f *exitCodeFilter) Write(data []byte) (int, error) {
n := len(data)
f.buf.Write(data)
// Flush to w until a potential match of exitRe
b := f.buf.Bytes()
match := f.exitRe.FindIndex(b)
if match == nil {
// Flush all of the buffer.
_, err := f.w.Write(b)
f.buf.Reset()
if err != nil {
return n, err
}
} else {
// Flush up to the beginning of the (potential) match.
_, err := f.w.Write(b[:match[0]])
f.buf.Next(match[0])
if err != nil {
return n, err
}
}
return n, nil
}
func (f *exitCodeFilter) Finish() (int, error) {
// f.buf could be empty, contain a partial match of exitRe, or
// contain a full match.
b := f.buf.Bytes()
defer f.buf.Reset()
match := f.exitRe.FindSubmatch(b)
if len(match) < 2 || match[1] == nil {
// Not a full match. Flush.
if _, err := f.w.Write(b); err != nil {
return 0, err
}
return 0, fmt.Errorf("no exit code (in %q)", string(b))
}
// Parse the exit code.
code, err := strconv.Atoi(string(match[1]))
if err != nil {
// Something is malformed. Flush.
if _, err := f.w.Write(b); err != nil {
return 0, err
}
return 0, fmt.Errorf("bad exit code: %v (in %q)", err, string(b))
}
return code, nil
}
// pkgPath determines the package import path of the current working directory,
// and indicates whether it is
// and returns the path to the package source relative to $GOROOT (or $GOPATH).
func pkgPath() (importPath string, isStd bool, modPath, modDir string, err error) {
errorf := func(format string, args ...any) (string, bool, string, string, error) {
return "", false, "", "", fmt.Errorf(format, args...)
}
goTool, err := goTool()
if err != nil {
return errorf("%w", err)
}
cmd := exec.Command(goTool, "list", "-e", "-f", "{{.ImportPath}}:{{.Standard}}{{with .Module}}:{{.Path}}:{{.Dir}}{{end}}", ".")
out, err := cmd.Output()
if err != nil {
if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 {
return errorf("%v: %s", cmd, ee.Stderr)
}
return errorf("%v: %w", cmd, err)
}
parts := strings.SplitN(string(bytes.TrimSpace(out)), ":", 4)
if len(parts) < 2 {
return errorf("%v: missing ':' in output: %q", cmd, out)
}
importPath = parts[0]
if importPath == "" || importPath == "." {
return errorf("current directory does not have a Go import path")
}
isStd, err = strconv.ParseBool(parts[1])
if err != nil {
return errorf("%v: non-boolean .Standard in output: %q", cmd, out)
}
if len(parts) >= 4 {
modPath = parts[2]
modDir = parts[3]
}
return importPath, isStd, modPath, modDir, nil
}
// adbCopyTree copies testdata, go.mod, go.sum files from subdir
// and from parent directories all the way up to the root of subdir.
// go.mod and go.sum files are needed for the go tool modules queries,
// and the testdata directories for tests. It is common for tests to
// reach out into testdata from parent packages.
func adbCopyTree(deviceCwd, subdir string) error {
dir := ""
for {
for _, name := range []string{"testdata", "go.mod", "go.sum"} {
hostPath := filepath.Join(dir, name)
if _, err := os.Stat(hostPath); err != nil {
continue
}
devicePath := path.Join(deviceCwd, dir)
if err := adb("exec-out", "mkdir", "-p", devicePath); err != nil {
return err
}
if err := adb("push", hostPath, devicePath); err != nil {
return err
}
}
if subdir == "." {
break
}
subdir = filepath.Dir(subdir)
dir = path.Join(dir, "..")
}
return nil
}
// adbCopyGoroot clears deviceRoot for previous versions of GOROOT, GOPATH
// and temporary data. Then, it copies relevant parts of GOROOT to the device,
// including the go tool built for android.
// A lock file ensures this only happens once, even with concurrent exec
// wrappers.
func adbCopyGoroot() error {
goTool, err := goTool()
if err != nil {
return err
}
cmd := exec.Command(goTool, "version")
cmd.Stderr = os.Stderr
out, err := cmd.Output()
if err != nil {
return fmt.Errorf("%v: %w", cmd, err)
}
goVersion := string(out)
// Also known by cmd/dist. The bootstrap command deletes the file.
statPath := filepath.Join(os.TempDir(), "go_android_exec-adb-sync-status")
stat, err := os.OpenFile(statPath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
return err
}
defer stat.Close()
// Serialize check and copying.
if err := syscall.Flock(int(stat.Fd()), syscall.LOCK_EX); err != nil {
return err
}
s, err := io.ReadAll(stat)
if err != nil {
return err
}
if string(s) == goVersion {
return nil
}
goroot, err := findGoroot()
if err != nil {
return err
}
// Delete the device's GOROOT, GOPATH and any leftover test data,
// and recreate GOROOT.
if err := adb("exec-out", "rm", "-rf", deviceRoot); err != nil {
return err
}
// Build Go for Android.
cmd = exec.Command(goTool, "install", "cmd")
out, err = cmd.CombinedOutput()
if err != nil {
if len(bytes.TrimSpace(out)) > 0 {
log.Printf("\n%s", out)
}
return fmt.Errorf("%v: %w", cmd, err)
}
if err := adb("exec-out", "mkdir", "-p", deviceGoroot); err != nil {
return err
}
// Copy the Android tools from the relevant bin subdirectory to GOROOT/bin.
cmd = exec.Command(goTool, "list", "-f", "{{.Target}}", "cmd/go")
cmd.Stderr = os.Stderr
out, err = cmd.Output()
if err != nil {
return fmt.Errorf("%v: %w", cmd, err)
}
platformBin := filepath.Dir(string(bytes.TrimSpace(out)))
if platformBin == "." {
return errors.New("failed to locate cmd/go for target platform")
}
if err := adb("push", platformBin, path.Join(deviceGoroot, "bin")); err != nil {
return err
}
// Copy only the relevant subdirectories from pkg: pkg/include and the
// platform-native binaries in pkg/tool.
if err := adb("exec-out", "mkdir", "-p", path.Join(deviceGoroot, "pkg", "tool")); err != nil {
return err
}
if err := adb("push", filepath.Join(goroot, "pkg", "include"), path.Join(deviceGoroot, "pkg", "include")); err != nil {
return err
}
cmd = exec.Command(goTool, "list", "-f", "{{.Target}}", "cmd/compile")
cmd.Stderr = os.Stderr
out, err = cmd.Output()
if err != nil {
return fmt.Errorf("%v: %w", cmd, err)
}
platformToolDir := filepath.Dir(string(bytes.TrimSpace(out)))
if platformToolDir == "." {
return errors.New("failed to locate cmd/compile for target platform")
}
relToolDir, err := filepath.Rel(filepath.Join(goroot), platformToolDir)
if err != nil {
return err
}
if err := adb("push", platformToolDir, path.Join(deviceGoroot, relToolDir)); err != nil {
return err
}
// Copy all other files from GOROOT.
dirents, err := os.ReadDir(goroot)
if err != nil {
return err
}
for _, de := range dirents {
switch de.Name() {
case "bin", "pkg":
// We already created GOROOT/bin and GOROOT/pkg above; skip those.
continue
}
if err := adb("push", filepath.Join(goroot, de.Name()), path.Join(deviceGoroot, de.Name())); err != nil {
return err
}
}
if _, err := stat.WriteString(goVersion); err != nil {
return err
}
return nil
}
func findGoroot() (string, error) {
gorootOnce.Do(func() {
// If runtime.GOROOT reports a non-empty path, assume that it is valid.
// (It may be empty if this binary was built with -trimpath.)
gorootPath = runtime.GOROOT()
if gorootPath != "" {
return
}
// runtime.GOROOT is empty — perhaps go_android_exec was built with
// -trimpath and GOROOT is unset. Try 'go env GOROOT' as a fallback,
// assuming that the 'go' command in $PATH is the correct one.
cmd := exec.Command("go", "env", "GOROOT")
cmd.Stderr = os.Stderr
out, err := cmd.Output()
if err != nil {
gorootErr = fmt.Errorf("%v: %w", cmd, err)
}
gorootPath = string(bytes.TrimSpace(out))
if gorootPath == "" {
gorootErr = errors.New("GOROOT not found")
}
})
return gorootPath, gorootErr
}
func goTool() (string, error) {
goroot, err := findGoroot()
if err != nil {
return "", err
}
return filepath.Join(goroot, "bin", "go"), nil
}
var (
gorootOnce sync.Once
gorootPath string
gorootErr error
)

View File

@ -1,11 +0,0 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package callback
type GoCallback struct{}
func (p *GoCallback) Run() string {
return "GoCallback.Run"
}

View File

@ -1,33 +0,0 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package callback
import (
"testing"
)
func TestCall(t *testing.T) {
c := NewCaller()
cb := NewCallback()
c.SetCallback(cb)
s := c.Call()
if s != "Callback::run" {
t.Errorf("unexpected string from Call: %q", s)
}
c.DelCallback()
}
func TestCallback(t *testing.T) {
c := NewCaller()
cb := NewDirectorCallback(&GoCallback{})
c.SetCallback(cb)
s := c.Call()
if s != "GoCallback.Run" {
t.Errorf("unexpected string from Call with callback: %q", s)
}
c.DelCallback()
DeleteDirectorCallback(cb)
}

View File

@ -1,15 +0,0 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file is here just to cause problems.
// file.swig turns into a file also named file.go.
// Make sure cmd/go keeps them separate
// when both are passed to cgo.
package file
//int F(void) { return 1; }
import "C"
func F() int { return int(C.F()) }

View File

@ -1,28 +0,0 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package file
import "testing"
// Open this file itself and verify that the first few characters are
// as expected.
func TestRead(t *testing.T) {
f := Fopen("file_test.go", "r")
if f.Swigcptr() == 0 {
t.Fatal("fopen failed")
}
if Fgetc(f) != '/' || Fgetc(f) != '/' || Fgetc(f) != ' ' || Fgetc(f) != 'C' {
t.Error("read unexpected characters")
}
if Fclose(f) != 0 {
t.Error("fclose failed")
}
}
func TestF(t *testing.T) {
if x := F(); x != 1 {
t.Fatalf("x = %d, want 1", x)
}
}

View File

@ -4,11 +4,17 @@
# license that can be found in the LICENSE file.
case "$GOWASIRUNTIME" in
"wasmedge")
exec wasmedge --dir=/ --env PWD="$PWD" "$1" "${@:2}"
;;
"wasmer")
exec wasmer run --dir=/ --env PWD="$PWD" "$1" -- "${@:2}"
;;
"wasmtime")
exec wasmtime run --dir=/ --env PWD="$PWD" --max-wasm-stack 1048576 "$1" -- "${@:2}"
;;
"wazero" | "")
exec wazero run -mount /:/ -env-inherit -cachedir "${TMPDIR}"/wazero "$1" "${@:2}"
exec wazero run -mount /:/ -env-inherit -cachedir "${TMPDIR:-/tmp}"/wazero "$1" "${@:2}"
;;
*)
echo "Unknown Go WASI runtime specified: $GOWASIRUNTIME"

View File

@ -607,6 +607,10 @@ func (fi headerFileInfo) Mode() (mode fs.FileMode) {
return mode
}
func (fi headerFileInfo) String() string {
return fs.FormatFileInfo(fi)
}
// sysStat, if non-nil, populates h from system-dependent fields of fi.
var sysStat func(fi fs.FileInfo, h *Header) error

View File

@ -780,6 +780,10 @@ func (f *fileListEntry) ModTime() time.Time {
func (f *fileListEntry) Info() (fs.FileInfo, error) { return f, nil }
func (f *fileListEntry) String() string {
return fs.FormatDirEntry(f)
}
// toValidName coerces name to be a valid name for fs.FS.Open.
func toValidName(name string) string {
name = strings.ReplaceAll(name, `\`, `/`)

View File

@ -190,6 +190,10 @@ func (fi headerFileInfo) Sys() any { return fi.fh }
func (fi headerFileInfo) Info() (fs.FileInfo, error) { return fi, nil }
func (fi headerFileInfo) String() string {
return fs.FormatFileInfo(fi)
}
// FileInfoHeader creates a partially-populated FileHeader from an
// fs.FileInfo.
// Because fs.FileInfo's Name method returns only the base name of

View File

@ -10,7 +10,6 @@ import (
"bufio"
"bytes"
"encoding/json"
"flag"
"fmt"
"go/ast"
"go/build"
@ -46,8 +45,7 @@ func goCmd() string {
return "go"
}
// contexts are the default contexts which are scanned, unless
// overridden by the -contexts flag.
// contexts are the default contexts which are scanned.
var contexts = []*build.Context{
{GOOS: "linux", GOARCH: "386", CgoEnabled: true},
{GOOS: "linux", GOARCH: "386"},
@ -96,25 +94,6 @@ func contextName(c *build.Context) string {
return s
}
func parseContext(c string) *build.Context {
parts := strings.Split(c, "-")
if len(parts) < 2 {
log.Fatalf("bad context: %q", c)
}
bc := &build.Context{
GOOS: parts[0],
GOARCH: parts[1],
}
if len(parts) == 3 {
if parts[2] == "cgo" {
bc.CgoEnabled = true
} else {
log.Fatalf("bad context: %q", c)
}
}
return bc
}
var internalPkg = regexp.MustCompile(`(^|/)internal($|/)`)
var exitCode = 0
@ -152,12 +131,7 @@ func Check(t *testing.T) {
var featureCtx = make(map[string]map[string]bool) // feature -> context name -> true
for _, w := range walkers {
pkgNames := w.stdPackages
if flag.NArg() > 0 {
pkgNames = flag.Args()
}
for _, name := range pkgNames {
for _, name := range w.stdPackages {
pkg, err := w.import_(name)
if _, nogo := err.(*build.NoGoError); nogo {
continue
@ -197,16 +171,15 @@ func Check(t *testing.T) {
for _, file := range checkFiles {
required = append(required, fileFeatures(file, needApproval(file))...)
}
var optional []string
for _, file := range nextFiles {
optional = append(optional, fileFeatures(file, true)...)
required = append(required, fileFeatures(file, true)...)
}
exception := fileFeatures(filepath.Join(testenv.GOROOT(t), "api/except.txt"), false)
if exitCode == 1 {
t.Errorf("API database problems found")
}
if !compareAPI(bw, features, required, optional, exception, false) {
if !compareAPI(bw, features, required, exception) {
t.Errorf("API differences found")
}
}
@ -252,12 +225,11 @@ func portRemoved(feature string) bool {
strings.Contains(feature, "(darwin-386-cgo)")
}
func compareAPI(w io.Writer, features, required, optional, exception []string, allowAdd bool) (ok bool) {
func compareAPI(w io.Writer, features, required, exception []string) (ok bool) {
ok = true
optionalSet := set(optional)
exceptionSet := set(exception)
featureSet := set(features)
exceptionSet := set(exception)
sort.Strings(features)
sort.Strings(required)
@ -268,7 +240,7 @@ func compareAPI(w io.Writer, features, required, optional, exception []string, a
return s
}
for len(required) > 0 || len(features) > 0 {
for len(features) > 0 || len(required) > 0 {
switch {
case len(features) == 0 || (len(required) > 0 && required[0] < features[0]):
feature := take(&required)
@ -289,33 +261,15 @@ func compareAPI(w io.Writer, features, required, optional, exception []string, a
}
case len(required) == 0 || (len(features) > 0 && required[0] > features[0]):
newFeature := take(&features)
if optionalSet[newFeature] {
// Known added feature to the upcoming release.
// Delete it from the map so we can detect any upcoming features
// which were never seen. (so we can clean up the nextFile)
delete(optionalSet, newFeature)
} else {
fmt.Fprintf(w, "+%s\n", newFeature)
if !allowAdd {
ok = false // we're in lock-down mode for next release
}
}
fmt.Fprintf(w, "+%s\n", newFeature)
ok = false // feature not in api/next/*
default:
take(&required)
take(&features)
}
}
// In next file, but not in API.
var missing []string
for feature := range optionalSet {
missing = append(missing, feature)
}
sort.Strings(missing)
for _, feature := range missing {
fmt.Fprintf(w, "±%s\n", feature)
}
return
return ok
}
// aliasReplacer applies type aliases to earlier API files,

View File

@ -20,33 +20,12 @@ import (
var flagCheck = flag.Bool("check", false, "run API checks")
func TestMain(m *testing.M) {
if !testenv.HasExec() {
os.Stdout.WriteString("skipping test: platform cannot exec")
os.Exit(0)
}
if !testenv.HasGoBuild() {
os.Stdout.WriteString("skipping test: platform cannot 'go build' to import std packages")
os.Exit(0)
}
flag.Parse()
for _, c := range contexts {
c.Compiler = build.Default.Compiler
}
build.Default.GOROOT = testenv.GOROOT(nil)
// Warm up the import cache in parallel.
var wg sync.WaitGroup
for _, context := range contexts {
context := context
wg.Add(1)
go func() {
defer wg.Done()
_ = NewWalker(context, filepath.Join(testenv.GOROOT(nil), "src"))
}()
}
wg.Wait()
os.Exit(m.Run())
}
@ -59,6 +38,9 @@ func TestGolden(t *testing.T) {
// slow, not worth repeating in -check
t.Skip("skipping with -check set")
}
testenv.MustHaveGoBuild(t)
td, err := os.Open("testdata/src/pkg")
if err != nil {
t.Fatal(err)
@ -115,16 +97,23 @@ func TestGolden(t *testing.T) {
func TestCompareAPI(t *testing.T) {
tests := []struct {
name string
features, required, optional, exception []string
ok bool // want
out string // want
name string
features, required, exception []string
ok bool // want
out string // want
}{
{
name: "equal",
features: []string{"A", "B", "C"},
required: []string{"A", "B", "C"},
ok: true,
out: "",
},
{
name: "feature added",
features: []string{"A", "B", "C", "D", "E", "F"},
required: []string{"B", "D"},
ok: true,
ok: false,
out: "+A\n+C\n+E\n+F\n",
},
{
@ -134,42 +123,52 @@ func TestCompareAPI(t *testing.T) {
ok: false,
out: "-B\n",
},
{
name: "feature added then removed",
features: []string{"A", "C"},
optional: []string{"B"},
required: []string{"A", "C"},
ok: true,
out: "±B\n",
},
{
name: "exception removal",
required: []string{"A", "B", "C"},
features: []string{"A", "C"},
required: []string{"A", "B", "C"},
exception: []string{"B"},
ok: true,
out: "",
},
// Test that a feature required on a subset of ports is implicitly satisfied
// by the same feature being implemented on all ports. That is, it shouldn't
// say "pkg syscall (darwin-amd64), type RawSockaddrInet6 struct" is missing.
// See https://go.dev/issue/4303.
{
// https://golang.org/issue/4303
name: "contexts reconverging",
required: []string{
"A",
"pkg syscall (darwin-amd64), type RawSockaddrInet6 struct",
},
name: "contexts reconverging after api/next/* update",
features: []string{
"A",
"pkg syscall, type RawSockaddrInet6 struct",
},
required: []string{
"A",
"pkg syscall (darwin-amd64), type RawSockaddrInet6 struct", // api/go1.n.txt
"pkg syscall, type RawSockaddrInet6 struct", // api/next/n.txt
},
ok: true,
out: "",
},
{
name: "contexts reconverging before api/next/* update",
features: []string{
"A",
"pkg syscall, type RawSockaddrInet6 struct",
},
required: []string{
"A",
"pkg syscall (darwin-amd64), type RawSockaddrInet6 struct",
},
ok: false,
out: "+pkg syscall, type RawSockaddrInet6 struct\n",
},
}
for _, tt := range tests {
buf := new(strings.Builder)
gotok := compareAPI(buf, tt.features, tt.required, tt.optional, tt.exception, true)
if gotok != tt.ok {
t.Errorf("%s: ok = %v; want %v", tt.name, gotok, tt.ok)
gotOK := compareAPI(buf, tt.features, tt.required, tt.exception)
if gotOK != tt.ok {
t.Errorf("%s: ok = %v; want %v", tt.name, gotOK, tt.ok)
}
if got := buf.String(); got != tt.out {
t.Errorf("%s: output differs\nGOT:\n%s\nWANT:\n%s", tt.name, got, tt.out)
@ -215,6 +214,20 @@ func TestIssue21181(t *testing.T) {
// slow, not worth repeating in -check
t.Skip("skipping with -check set")
}
testenv.MustHaveGoBuild(t)
// Warm up the import cache in parallel.
var wg sync.WaitGroup
for _, context := range contexts {
context := context
wg.Add(1)
go func() {
defer wg.Done()
_ = NewWalker(context, filepath.Join(testenv.GOROOT(nil), "src"))
}()
}
wg.Wait()
for _, context := range contexts {
w := NewWalker(context, "testdata/src/issue21181")
pkg, err := w.import_("p")
@ -231,6 +244,7 @@ func TestIssue29837(t *testing.T) {
// slow, not worth repeating in -check
t.Skip("skipping with -check set")
}
testenv.MustHaveGoBuild(t)
for _, context := range contexts {
w := NewWalker(context, "testdata/src/issue29837")
_, err := w.ImportFrom("p", "", 0)
@ -245,6 +259,7 @@ func TestIssue41358(t *testing.T) {
// slow, not worth repeating in -check
t.Skip("skipping with -check set")
}
testenv.MustHaveGoBuild(t)
context := new(build.Context)
*context = build.Default
context.Dir = filepath.Join(testenv.GOROOT(t), "src")
@ -261,5 +276,6 @@ func TestCheck(t *testing.T) {
if !*flagCheck {
t.Skip("-check not specified")
}
testenv.MustHaveGoBuild(t)
Check(t)
}

View File

@ -9,6 +9,7 @@ package main
import (
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/scanner"
"go/token"
@ -62,29 +63,48 @@ func (f *File) ParseGo(abspath string, src []byte) {
// In ast1, find the import "C" line and get any extra C preamble.
sawC := false
for _, decl := range ast1.Decls {
d, ok := decl.(*ast.GenDecl)
if !ok {
continue
}
for _, spec := range d.Specs {
s, ok := spec.(*ast.ImportSpec)
if !ok || s.Path.Value != `"C"` {
continue
switch decl := decl.(type) {
case *ast.GenDecl:
for _, spec := range decl.Specs {
s, ok := spec.(*ast.ImportSpec)
if !ok || s.Path.Value != `"C"` {
continue
}
sawC = true
if s.Name != nil {
error_(s.Path.Pos(), `cannot rename import "C"`)
}
cg := s.Doc
if cg == nil && len(decl.Specs) == 1 {
cg = decl.Doc
}
if cg != nil {
f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), abspath)
f.Preamble += commentText(cg) + "\n"
f.Preamble += "#line 1 \"cgo-generated-wrapper\"\n"
}
}
sawC = true
if s.Name != nil {
error_(s.Path.Pos(), `cannot rename import "C"`)
}
cg := s.Doc
if cg == nil && len(d.Specs) == 1 {
cg = d.Doc
}
if cg != nil {
f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), abspath)
f.Preamble += commentText(cg) + "\n"
f.Preamble += "#line 1 \"cgo-generated-wrapper\"\n"
case *ast.FuncDecl:
// Also, reject attempts to declare methods on C.T or *C.T.
// (The generated code would otherwise accept this
// invalid input; see issue #57926.)
if decl.Recv != nil && len(decl.Recv.List) > 0 {
recvType := decl.Recv.List[0].Type
if recvType != nil {
t := recvType
if star, ok := unparen(t).(*ast.StarExpr); ok {
t = star.X
}
if sel, ok := unparen(t).(*ast.SelectorExpr); ok {
var buf strings.Builder
format.Node(&buf, fset, recvType)
error_(sel.Pos(), `cannot define new methods on non-local type %s`, &buf)
}
}
}
}
}
if !sawC {
error_(ast1.Package, `cannot find import "C"`)
@ -542,3 +562,11 @@ func (f *File) walk(x interface{}, context astContext, visit func(*File, interfa
}
}
}
// If x is of the form (T), unparen returns unparen(T), otherwise it returns x.
func unparen(x ast.Expr) ast.Expr {
if p, isParen := x.(*ast.ParenExpr); isParen {
x = unparen(p.X)
}
return x
}

View File

@ -26,7 +26,7 @@ declared in the preamble may be used, even if they start with a
lower-case letter. Exception: static variables in the preamble may
not be referenced from Go code; static functions are permitted.
See $GOROOT/misc/cgo/stdio and $GOROOT/misc/cgo/gmp for examples. See
See $GOROOT/cmd/cgo/internal/teststdio and $GOROOT/misc/cgo/gmp for examples. See
"C? Go? Cgo!" for an introduction to using cgo:
https://golang.org/doc/articles/c_go_cgo.html.

View File

@ -0,0 +1,153 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package swig
import (
"cmd/internal/quoted"
"internal/testenv"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync"
"testing"
)
func TestStdio(t *testing.T) {
testenv.MustHaveCGO(t)
mustHaveSwig(t)
run(t, "testdata/stdio", false)
}
func TestCall(t *testing.T) {
testenv.MustHaveCGO(t)
mustHaveSwig(t)
mustHaveCxx(t)
run(t, "testdata/callback", false, "Call")
t.Run("lto", func(t *testing.T) { run(t, "testdata/callback", true, "Call") })
}
func TestCallback(t *testing.T) {
testenv.MustHaveCGO(t)
mustHaveSwig(t)
mustHaveCxx(t)
run(t, "testdata/callback", false, "Callback")
t.Run("lto", func(t *testing.T) { run(t, "testdata/callback", true, "Callback") })
}
func run(t *testing.T, dir string, lto bool, args ...string) {
runArgs := append([]string{"run", "."}, args...)
cmd := exec.Command("go", runArgs...)
cmd.Dir = dir
if lto {
const cflags = "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option"
cmd.Env = append(cmd.Environ(),
"CGO_CFLAGS="+cflags,
"CGO_CXXFLAGS="+cflags,
"CGO_LDFLAGS="+cflags)
}
out, err := cmd.CombinedOutput()
if string(out) != "OK\n" {
t.Errorf("%s", string(out))
}
if err != nil {
t.Errorf("%s", err)
}
}
func mustHaveCxx(t *testing.T) {
// Ask the go tool for the CXX it's configured to use.
cxx, err := exec.Command("go", "env", "CXX").CombinedOutput()
if err != nil {
t.Fatalf("go env CXX failed: %s", err)
}
args, err := quoted.Split(string(cxx))
if err != nil {
t.Skipf("could not parse 'go env CXX' output %q: %s", string(cxx), err)
}
if len(args) == 0 {
t.Skip("no C++ compiler")
}
testenv.MustHaveExecPath(t, string(args[0]))
}
var (
swigOnce sync.Once
haveSwig bool
)
func mustHaveSwig(t *testing.T) {
swigOnce.Do(func() {
mustHaveSwigOnce(t)
haveSwig = true
})
// The first call will skip t with a nice message. On later calls, we just skip.
if !haveSwig {
t.Skip("swig not found")
}
}
func mustHaveSwigOnce(t *testing.T) {
swig, err := exec.LookPath("swig")
if err != nil {
t.Skipf("swig not in PATH: %s", err)
}
// Check that swig was installed with Go support by checking
// that a go directory exists inside the swiglib directory.
// See https://golang.org/issue/23469.
output, err := exec.Command(swig, "-go", "-swiglib").Output()
if err != nil {
t.Skip("swig is missing Go support")
}
swigDir := strings.TrimSpace(string(output))
_, err = os.Stat(filepath.Join(swigDir, "go"))
if err != nil {
t.Skip("swig is missing Go support")
}
// Check that swig has a new enough version.
// See https://golang.org/issue/22858.
out, err := exec.Command(swig, "-version").CombinedOutput()
if err != nil {
t.Skipf("failed to get swig version:%s\n%s", err, string(out))
}
re := regexp.MustCompile(`[vV]ersion +(\d+)([.]\d+)?([.]\d+)?`)
matches := re.FindSubmatch(out)
if matches == nil {
// Can't find version number; hope for the best.
t.Logf("failed to find swig version, continuing")
return
}
var parseError error
atoi := func(s string) int {
x, err := strconv.Atoi(s)
if err != nil && parseError == nil {
parseError = err
}
return x
}
var major, minor, patch int
major = atoi(string(matches[1]))
if len(matches[2]) > 0 {
minor = atoi(string(matches[2][1:]))
}
if len(matches[3]) > 0 {
patch = atoi(string(matches[3][1:]))
}
if parseError != nil {
t.Logf("error parsing swig version %q, continuing anyway: %s", string(matches[0]), parseError)
return
}
t.Logf("found swig version %d.%d.%d", major, minor, patch)
if major < 3 || (major == 3 && minor == 0 && patch < 6) {
t.Skip("test requires swig 3.0.6 or later")
}
}

View File

@ -6,7 +6,7 @@
// included in the package.
#include <string>
#include "callback.h"
#include "main.h"
std::string Caller::call() {
if (callback_ != 0)

View File

@ -0,0 +1,60 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"os"
)
func main() {
if len(os.Args) != 2 {
fatal("usage: callback testname")
}
switch os.Args[1] {
default:
fatal("unknown test %q", os.Args[1])
case "Call":
testCall()
case "Callback":
testCallback()
}
println("OK")
}
func fatal(f string, args ...any) {
fmt.Fprintln(os.Stderr, fmt.Sprintf(f, args...))
os.Exit(1)
}
type GoCallback struct{}
func (p *GoCallback) Run() string {
return "GoCallback.Run"
}
func testCall() {
c := NewCaller()
cb := NewCallback()
c.SetCallback(cb)
s := c.Call()
if s != "Callback::run" {
fatal("unexpected string from Call: %q", s)
}
c.DelCallback()
}
func testCallback() {
c := NewCaller()
cb := NewDirectorCallback(&GoCallback{})
c.SetCallback(cb)
s := c.Call()
if s != "GoCallback.Run" {
fatal("unexpected string from Call with callback: %q", s)
}
c.DelCallback()
DeleteDirectorCallback(cb)
}

View File

@ -8,11 +8,11 @@
%{
#include <string>
#include "callback.h"
#include "main.h"
%}
%include "std_string.i"
%feature("director");
%include "callback.h"
%include "main.h"

View File

@ -0,0 +1,45 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file is here just to cause problems.
// main.swig turns into a file also named main.go.
// Make sure cmd/go keeps them separate
// when both are passed to cgo.
package main
//int F(void) { return 1; }
import "C"
import (
"fmt"
"os"
)
func F() int { return int(C.F()) }
func main() {
if x := int(C.F()); x != 1 {
fatal("x = %d, want 1", x)
}
// Open this file itself and verify that the first few characters are
// as expected.
f := Fopen("main.go", "r")
if f.Swigcptr() == 0 {
fatal("fopen failed")
}
if Fgetc(f) != '/' || Fgetc(f) != '/' || Fgetc(f) != ' ' || Fgetc(f) != 'C' {
fatal("read unexpected characters")
}
if Fclose(f) != 0 {
fatal("fclose failed")
}
println("OK")
}
func fatal(f string, args ...any) {
fmt.Fprintln(os.Stderr, fmt.Sprintf(f, args...))
os.Exit(1)
}

View File

@ -211,7 +211,7 @@ func testCallbackCallers(t *testing.T) {
}
// In module mode, this package has a fully-qualified import path.
// Remove it if present.
fname = strings.TrimPrefix(fname, "misc/cgo/")
fname = strings.TrimPrefix(fname, "cmd/cgo/internal/")
namei := ""
if i < len(name) {

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build cgo
package cgotest
import (

View File

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build cgo
package cgotest
import "testing"

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux && freebsd && openbsd
//go:build linux
package cgotest

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !windows
//go:build cgo && !windows
package cgotest

View File

@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build cgo
package cgotest
import (
"testing"
"misc/cgo/test/gcc68255"
"cmd/cgo/internal/test/gcc68255"
)
func testGCC68255(t *testing.T) {

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !windows
//go:build cgo && !windows
// Issue 18146: pthread_create failure during syscall.Exec.

View File

@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build cgo
// Test that we can have two identical cgo packages in a single binary.
// No runtime test; just make sure it compiles.
package cgotest
import (
_ "misc/cgo/test/issue23555a"
_ "misc/cgo/test/issue23555b"
_ "cmd/cgo/internal/test/issue23555a"
_ "cmd/cgo/internal/test/issue23555b"
)

View File

@ -2,16 +2,18 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build cgo
package cgotest
import (
"testing"
"misc/cgo/test/issue24161arg"
"misc/cgo/test/issue24161e0"
"misc/cgo/test/issue24161e1"
"misc/cgo/test/issue24161e2"
"misc/cgo/test/issue24161res"
"cmd/cgo/internal/test/issue24161arg"
"cmd/cgo/internal/test/issue24161e0"
"cmd/cgo/internal/test/issue24161e1"
"cmd/cgo/internal/test/issue24161e2"
"cmd/cgo/internal/test/issue24161res"
)
func Test24161Arg(t *testing.T) {

View File

@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build cgo
// Issue 26430: incomplete typedef leads to inconsistent typedefs error.
// No runtime test; just make sure it compiles.
package cgotest
import _ "misc/cgo/test/issue26430"
import _ "cmd/cgo/internal/test/issue26430"

View File

@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build cgo
// Issue 26743: typedef of uint leads to inconsistent typedefs error.
// No runtime test; just make sure it compiles.
package cgotest
import _ "misc/cgo/test/issue26743"
import _ "cmd/cgo/internal/test/issue26743"

View File

@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build cgo
// Failed to resolve typedefs consistently.
// No runtime test; just make sure it compiles.
package cgotest
import "misc/cgo/test/issue27340"
import "cmd/cgo/internal/test/issue27340"
var issue27340Var = issue27340.Issue27340GoFunc

View File

@ -2,11 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !windows
//go:build cgo && !windows
// Issue 29563: internal linker fails on duplicate weak symbols.
// No runtime test; just make sure it compiles.
package cgotest
import _ "misc/cgo/test/issue29563"
import _ "cmd/cgo/internal/test/issue29563"

View File

@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build cgo
// Issue 30527: function call rewriting casts untyped
// constants to int because of ":=" usage.
package cgotest
import "misc/cgo/test/issue30527"
import "cmd/cgo/internal/test/issue30527"
func issue30527G() {
issue30527.G(nil)

View File

@ -10,7 +10,7 @@ package cgotest
import "C"
import (
"misc/cgo/test/issue41761a"
"cmd/cgo/internal/test/issue41761a"
"testing"
)

Some files were not shown because too many files have changed in this diff Show More