mirror of https://github.com/golang/go.git
Merge branch 'golang:master' into master
This commit is contained in:
commit
cc69ab9be3
|
|
@ -1 +1,2 @@
|
|||
pkg errors, var ErrUnsupported error #41198
|
||||
pkg net/http, method (*ProtocolError) Is(error) bool #41198
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
pkg io/fs, func FormatDirEntry(DirEntry) string #54451
|
||||
pkg io/fs, func FormatFileInfo(FileInfo) string #54451
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
pkg log/slog, func Group(string, ...interface{}) Attr #59204
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
pkg log/slog, func NewJSONHandler(io.Writer, *HandlerOptions) *JSONHandler #59339
|
||||
pkg log/slog, func NewTextHandler(io.Writer, *HandlerOptions) *TextHandler #59339
|
||||
|
|
@ -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 -->
|
||||
|
|
|
|||
|
|
@ -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.
|
|
@ -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
|
||||
}
|
||||
58
misc/arm/a
58
misc/arm/a
|
|
@ -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)
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package cgotest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"misc/cgo/test/issue9026"
|
||||
)
|
||||
|
||||
func test9026(t *testing.T) { issue9026.Test(t) }
|
||||
11
misc/go.mod
11
misc/go.mod
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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()) }
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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, `\`, `/`)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
// included in the package.
|
||||
|
||||
#include <string>
|
||||
#include "callback.h"
|
||||
#include "main.h"
|
||||
|
||||
std::string Caller::call() {
|
||||
if (callback_ != 0)
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -8,11 +8,11 @@
|
|||
|
||||
%{
|
||||
#include <string>
|
||||
#include "callback.h"
|
||||
#include "main.h"
|
||||
%}
|
||||
|
||||
%include "std_string.i"
|
||||
|
||||
%feature("director");
|
||||
|
||||
%include "callback.h"
|
||||
%include "main.h"
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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 (
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
@ -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"
|
||||
)
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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"
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
Loading…
Reference in New Issue