1.15 specific files deleted.
1.16 specific files folded carefully into generic files, with goal of reducing diff with upstream.
Follows upstream 1.16 in making PathError etc. be aliases for the same errors in io/fs.

This fixes #2817 and lets us add io/ioutil to "make test-tinygo" on linux and mac.
Этот коммит содержится в:
Dan Kegel 2022-05-01 08:04:05 -07:00 коммит произвёл Ron Evans
родитель db389ba443
коммит e87cd23e87
15 изменённых файлов: 232 добавлений и 335 удалений

Просмотреть файл

@ -102,9 +102,9 @@ commands:
- run: make fmt-check
jobs:
test-llvm11-go115:
test-llvm11-go116:
docker:
- image: circleci/golang:1.15-buster
- image: circleci/golang:1.16-buster
steps:
- test-linux:
llvm: "11"
@ -118,5 +118,5 @@ jobs:
workflows:
test-all:
jobs:
- test-llvm11-go115
- test-llvm11-go116
- test-llvm12-go117

Просмотреть файл

@ -18,7 +18,7 @@ tarball. If you want to help with development of TinyGo itself, you should follo
LLVM, Clang and LLD are quite light on dependencies, requiring only standard
build tools to be built. Go is of course necessary to build TinyGo itself.
* Go (1.15+)
* Go (1.16+)
* Standard build tools (gcc/clang)
* git
* CMake

Просмотреть файл

@ -300,6 +300,7 @@ TEST_PACKAGES_LINUX := \
debug/dwarf \
debug/plan9obj \
io/fs \
io/ioutil \
testing/fstest
TEST_PACKAGES_DARWIN := $(TEST_PACKAGES_LINUX)

Просмотреть файл

@ -33,8 +33,8 @@ func NewConfig(options *compileopts.Options) (*compileopts.Config, error) {
if err != nil {
return nil, fmt.Errorf("could not read version from GOROOT (%v): %v", goroot, err)
}
if major != 1 || minor < 15 || minor > 18 {
return nil, fmt.Errorf("requires go version 1.15 through 1.18, got go%d.%d", major, minor)
if major != 1 || minor < 16 || minor > 18 {
return nil, fmt.Errorf("requires go version 1.16 through 1.18, got go%d.%d", major, minor)
}
clangHeaderPath := getClangHeaderPath(goenv.Get("TINYGOROOT"))

2
go.mod
Просмотреть файл

@ -1,6 +1,6 @@
module github.com/tinygo-org/tinygo
go 1.15
go 1.16
require (
github.com/aykevl/go-wasm v0.0.2-0.20211119014117-0761b1ddcd1a

Просмотреть файл

@ -1,6 +1,3 @@
//go:build go1.16
// +build go1.16
// Copyright 2016 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.

Просмотреть файл

@ -1,5 +1,5 @@
//go:build (go1.16 && baremetal) || (go1.16 && js) || (go1.16 && wasi) || (go1.16 && windows)
// +build go1.16,baremetal go1.16,js go1.16,wasi go1.16,windows
//go:build baremetal || js || wasi || windows
// +build baremetal js wasi windows
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style

Просмотреть файл

@ -1,12 +0,0 @@
//go:build !go1.16
// +build !go1.16
// Copyright 2009 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 os
func (f *File) Readdirnames(n int) (names []string, err error) {
return nil, ErrInvalid
}

Просмотреть файл

@ -1,21 +1,32 @@
// Copyright 2009 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 os
import (
"errors"
"io/fs"
"syscall"
)
// Portable analogs of some common system call errors.
//
// Errors returned from this package may be tested against these errors
// with errors.Is.
var (
ErrInvalid = errors.New("invalid argument")
ErrPermission = errors.New("permission denied")
ErrClosed = errors.New("file already closed")
// ErrInvalid indicates an invalid argument.
// Methods on File will return this error when the receiver is nil.
ErrInvalid = fs.ErrInvalid // "invalid argument"
ErrPermission = fs.ErrPermission // "permission denied"
ErrExist = fs.ErrExist // "file already exists"
ErrNotExist = fs.ErrNotExist // "file does not exist"
ErrClosed = fs.ErrClosed // "file already closed"
// Portable analogs of some common system call errors.
// Note that these are exported for use in the Filesystem interface.
ErrUnsupported = errors.New("operation not supported")
ErrNotImplemented = errors.New("operation not implemented")
ErrNotExist = errors.New("file not found")
ErrExist = errors.New("file exists")
)
// The following code is copied from the official implementation.
@ -46,6 +57,9 @@ func NewSyscallError(syscall string, err error) error {
return &SyscallError{syscall, err}
}
// PathError records an error and the operation and file path that caused it.
type PathError = fs.PathError
// SyscallError records an error from a specific system call.
type SyscallError struct {
Syscall string

Просмотреть файл

@ -12,6 +12,7 @@ package os
import (
"errors"
"io"
"io/fs"
"runtime"
"syscall"
)
@ -200,23 +201,6 @@ func (f *File) Truncate(size int64) error {
return &PathError{"truncate", f.name, ErrNotImplemented}
}
// PathError records an error and the operation and file path that caused it.
// TODO: PathError moved to io/fs in go 1.16 and left an alias in os/errors.go.
// Do the same once we drop support for go 1.15.
type PathError struct {
Op string
Path string
Err error
}
func (e *PathError) Error() string {
return e.Op + " " + e.Path + ": " + e.Err.Error()
}
func (e *PathError) Unwrap() error {
return e.Err
}
// LinkError records an error during a link or symlink or rename system call and
// the paths that caused it.
type LinkError struct {
@ -286,3 +270,146 @@ func UserHomeDir() (string, error) {
}
return "", errors.New(enverr + " is not defined")
}
type (
FileMode = fs.FileMode
FileInfo = fs.FileInfo
)
// The followings are copied from Go 1.16 or 1.17 official implementation:
// https://github.com/golang/go/blob/go1.16/src/os/file.go
// DirFS returns a file system (an fs.FS) for the tree of files rooted at the directory dir.
//
// Note that DirFS("/prefix") only guarantees that the Open calls it makes to the
// operating system will begin with "/prefix": DirFS("/prefix").Open("file") is the
// same as os.Open("/prefix/file"). So if /prefix/file is a symbolic link pointing outside
// the /prefix tree, then using DirFS does not stop the access any more than using
// os.Open does. DirFS is therefore not a general substitute for a chroot-style security
// mechanism when the directory tree contains arbitrary content.
func DirFS(dir string) fs.FS {
return dirFS(dir)
}
func containsAny(s, chars string) bool {
for i := 0; i < len(s); i++ {
for j := 0; j < len(chars); j++ {
if s[i] == chars[j] {
return true
}
}
}
return false
}
type dirFS string
func (dir dirFS) Open(name string) (fs.File, error) {
if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) {
return nil, &PathError{Op: "open", Path: name, Err: ErrInvalid}
}
f, err := Open(string(dir) + "/" + name)
if err != nil {
return nil, err // nil fs.File
}
return f, nil
}
func (dir dirFS) Stat(name string) (fs.FileInfo, error) {
if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) {
return nil, &PathError{Op: "stat", Path: name, Err: ErrInvalid}
}
f, err := Stat(string(dir) + "/" + name)
if err != nil {
return nil, err
}
return f, nil
}
// ReadFile reads the named file and returns the contents.
// A successful call returns err == nil, not err == EOF.
// Because ReadFile reads the whole file, it does not treat an EOF from Read
// as an error to be reported.
func ReadFile(name string) ([]byte, error) {
f, err := Open(name)
if err != nil {
return nil, err
}
defer f.Close()
var size int
if info, err := f.Stat(); err == nil {
size64 := info.Size()
if int64(int(size64)) == size64 {
size = int(size64)
}
}
size++ // one byte for final read at EOF
// If a file claims a small size, read at least 512 bytes.
// In particular, files in Linux's /proc claim size 0 but
// then do not work right if read in small pieces,
// so an initial read of 1 byte would not work correctly.
if size < 512 {
size = 512
}
data := make([]byte, 0, size)
for {
if len(data) >= cap(data) {
d := append(data[:cap(data)], 0)
data = d[:len(data)]
}
n, err := f.Read(data[len(data):cap(data)])
data = data[:len(data)+n]
if err != nil {
if err == io.EOF {
err = nil
}
return data, err
}
}
}
// WriteFile writes data to the named file, creating it if necessary.
// If the file does not exist, WriteFile creates it with permissions perm (before umask);
// otherwise WriteFile truncates it before writing, without changing permissions.
func WriteFile(name string, data []byte, perm FileMode) error {
f, err := OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, perm)
if err != nil {
return err
}
_, err = f.Write(data)
if err1 := f.Close(); err1 != nil && err == nil {
err = err1
}
return err
}
// The defined file mode bits are the most significant bits of the FileMode.
// The nine least-significant bits are the standard Unix rwxrwxrwx permissions.
// The values of these bits should be considered part of the public API and
// may be used in wire protocols or disk representations: they must not be
// changed, although new bits might be added.
const (
// The single letters are the abbreviations
// used by the String method's formatting.
ModeDir = fs.ModeDir // d: is a directory
ModeAppend = fs.ModeAppend // a: append-only
ModeExclusive = fs.ModeExclusive // l: exclusive use
ModeTemporary = fs.ModeTemporary // T: temporary file; Plan 9 only
ModeSymlink = fs.ModeSymlink // L: symbolic link
ModeDevice = fs.ModeDevice // D: device file
ModeNamedPipe = fs.ModeNamedPipe // p: named pipe (FIFO)
ModeSocket = fs.ModeSocket // S: Unix domain socket
ModeSetuid = fs.ModeSetuid // u: setuid
ModeSetgid = fs.ModeSetgid // g: setgid
ModeCharDevice = fs.ModeCharDevice // c: Unix character device, when ModeDevice is set
ModeSticky = fs.ModeSticky // t: sticky
ModeIrregular = fs.ModeIrregular // ?: non-regular file; nothing else is known about this file
// Mask for the type bits. For regular files, none will be set.
ModeType = fs.ModeType
ModePerm = fs.ModePerm // Unix permission bits, 0o777
)

Просмотреть файл

@ -1,153 +0,0 @@
//go:build go1.16
// +build go1.16
package os
import (
"io"
"io/fs"
"runtime"
)
type (
FileMode = fs.FileMode
FileInfo = fs.FileInfo
)
// The followings are copied from Go 1.16 or 1.17 official implementation:
// https://github.com/golang/go/blob/go1.16/src/os/file.go
// DirFS returns a file system (an fs.FS) for the tree of files rooted at the directory dir.
//
// Note that DirFS("/prefix") only guarantees that the Open calls it makes to the
// operating system will begin with "/prefix": DirFS("/prefix").Open("file") is the
// same as os.Open("/prefix/file"). So if /prefix/file is a symbolic link pointing outside
// the /prefix tree, then using DirFS does not stop the access any more than using
// os.Open does. DirFS is therefore not a general substitute for a chroot-style security
// mechanism when the directory tree contains arbitrary content.
func DirFS(dir string) fs.FS {
return dirFS(dir)
}
func containsAny(s, chars string) bool {
for i := 0; i < len(s); i++ {
for j := 0; j < len(chars); j++ {
if s[i] == chars[j] {
return true
}
}
}
return false
}
type dirFS string
func (dir dirFS) Open(name string) (fs.File, error) {
if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) {
return nil, &PathError{Op: "open", Path: name, Err: ErrInvalid}
}
f, err := Open(string(dir) + "/" + name)
if err != nil {
return nil, err // nil fs.File
}
return f, nil
}
func (dir dirFS) Stat(name string) (fs.FileInfo, error) {
if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) {
return nil, &PathError{Op: "stat", Path: name, Err: ErrInvalid}
}
f, err := Stat(string(dir) + "/" + name)
if err != nil {
return nil, err
}
return f, nil
}
// ReadFile reads the named file and returns the contents.
// A successful call returns err == nil, not err == EOF.
// Because ReadFile reads the whole file, it does not treat an EOF from Read
// as an error to be reported.
func ReadFile(name string) ([]byte, error) {
f, err := Open(name)
if err != nil {
return nil, err
}
defer f.Close()
var size int
if info, err := f.Stat(); err == nil {
size64 := info.Size()
if int64(int(size64)) == size64 {
size = int(size64)
}
}
size++ // one byte for final read at EOF
// If a file claims a small size, read at least 512 bytes.
// In particular, files in Linux's /proc claim size 0 but
// then do not work right if read in small pieces,
// so an initial read of 1 byte would not work correctly.
if size < 512 {
size = 512
}
data := make([]byte, 0, size)
for {
if len(data) >= cap(data) {
d := append(data[:cap(data)], 0)
data = d[:len(data)]
}
n, err := f.Read(data[len(data):cap(data)])
data = data[:len(data)+n]
if err != nil {
if err == io.EOF {
err = nil
}
return data, err
}
}
}
// WriteFile writes data to the named file, creating it if necessary.
// If the file does not exist, WriteFile creates it with permissions perm (before umask);
// otherwise WriteFile truncates it before writing, without changing permissions.
func WriteFile(name string, data []byte, perm FileMode) error {
f, err := OpenFile(name, O_WRONLY|O_CREATE|O_TRUNC, perm)
if err != nil {
return err
}
_, err = f.Write(data)
if err1 := f.Close(); err1 != nil && err == nil {
err = err1
}
return err
}
// The defined file mode bits are the most significant bits of the FileMode.
// The nine least-significant bits are the standard Unix rwxrwxrwx permissions.
// The values of these bits should be considered part of the public API and
// may be used in wire protocols or disk representations: they must not be
// changed, although new bits might be added.
const (
// The single letters are the abbreviations
// used by the String method's formatting.
ModeDir = fs.ModeDir // d: is a directory
ModeAppend = fs.ModeAppend // a: append-only
ModeExclusive = fs.ModeExclusive // l: exclusive use
ModeTemporary = fs.ModeTemporary // T: temporary file; Plan 9 only
ModeSymlink = fs.ModeSymlink // L: symbolic link
ModeDevice = fs.ModeDevice // D: device file
ModeNamedPipe = fs.ModeNamedPipe // p: named pipe (FIFO)
ModeSocket = fs.ModeSocket // S: Unix domain socket
ModeSetuid = fs.ModeSetuid // u: setuid
ModeSetgid = fs.ModeSetgid // g: setgid
ModeCharDevice = fs.ModeCharDevice // c: Unix character device, when ModeDevice is set
ModeSticky = fs.ModeSticky // t: sticky
ModeIrregular = fs.ModeIrregular // ?: non-regular file; nothing else is known about this file
// Mask for the type bits. For regular files, none will be set.
ModeType = fs.ModeType
ModePerm = fs.ModePerm // Unix permission bits, 0o777
)

Просмотреть файл

@ -1,72 +0,0 @@
// Copyright 2009 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 go1.16 && !baremetal && !js && !wasi
// +build go1.16,!baremetal,!js,!wasi
// DirFS tests copied verbatim from upstream os_test.go, and adjusted minimally to fit tinygo.
package os_test
import (
"io/fs"
"os"
. "os"
"path/filepath"
"runtime"
"testing"
"testing/fstest"
)
func TestDirFS(t *testing.T) {
if runtime.GOOS == "windows" {
t.Log("TODO: implement Readdir for Windows")
return
}
if err := fstest.TestFS(DirFS("./testdata/dirfs"), "a", "b", "dir/x"); err != nil {
t.Fatal(err)
}
// Test that Open does not accept backslash as separator.
d := DirFS(".")
_, err := d.Open(`testdata\dirfs`)
if err == nil {
t.Fatalf(`Open testdata\dirfs succeeded`)
}
}
func TestDirFSPathsValid(t *testing.T) {
if runtime.GOOS == "windows" {
t.Log("skipping on Windows")
return
}
// TODO: switch back to t.TempDir once it's implemented
d, err := MkdirTemp("", "TestDirFSPathsValid")
if err != nil {
t.Fatal(err)
}
defer Remove(d)
if err := os.WriteFile(filepath.Join(d, "control.txt"), []byte(string("Hello, world!")), 0644); err != nil {
t.Fatal(err)
}
defer Remove(filepath.Join(d, "control.txt"))
if err := os.WriteFile(filepath.Join(d, `e:xperi\ment.txt`), []byte(string("Hello, colon and backslash!")), 0644); err != nil {
t.Fatal(err)
}
defer Remove(filepath.Join(d, `e:xperi\ment.txt`))
fsys := os.DirFS(d)
err = fs.WalkDir(fsys, ".", func(path string, e fs.DirEntry, err error) error {
if fs.ValidPath(e.Name()) {
t.Logf("%q ok", e.Name())
} else {
t.Errorf("%q INVALID", e.Name())
}
return nil
})
if err != nil {
t.Fatal(err)
}
}

Просмотреть файл

@ -1,57 +0,0 @@
//go:build !go1.16
// +build !go1.16
package os
import "time"
// A FileInfo describes a file and is returned by Stat and Lstat.
type FileInfo interface {
Name() string // base name of the file
Size() int64 // length in bytes for regular files; system-dependent for others
Mode() FileMode // file mode bits
ModTime() time.Time // modification time
IsDir() bool // abbreviation for Mode().IsDir()
Sys() interface{} // underlying data source (can return nil)
}
type FileMode uint32
// Mode constants, copied from the mainline Go source
// https://github.com/golang/go/blob/4ce6a8e89668b87dce67e2f55802903d6eb9110a/src/os/types.go#L35-L63
const (
// The single letters are the abbreviations used by the String method's formatting.
ModeDir FileMode = 1 << (32 - 1 - iota) // d: is a directory
ModeAppend // a: append-only
ModeExclusive // l: exclusive use
ModeTemporary // T: temporary file; Plan 9 only
ModeSymlink // L: symbolic link
ModeDevice // D: device file
ModeNamedPipe // p: named pipe (FIFO)
ModeSocket // S: Unix domain socket
ModeSetuid // u: setuid
ModeSetgid // g: setgid
ModeCharDevice // c: Unix character device, when ModeDevice is set
ModeSticky // t: sticky
ModeIrregular // ?: non-regular file; nothing else is known about this file
// Mask for the type bits. For regular files, none will be set.
ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular
ModePerm FileMode = 0777 // Unix permission bits
)
// IsDir is a stub, always returning false
func (m FileMode) IsDir() bool {
return false
}
// IsRegular is a stub, always returning false
func (m FileMode) IsRegular() bool {
return false
}
// Perm is a stub, always returning 0.
func (m FileMode) Perm() FileMode {
return 0
}

Просмотреть файл

@ -4,6 +4,7 @@
package os_test
import (
"io/fs"
"io/ioutil"
. "os"
"path/filepath"
@ -11,6 +12,7 @@ import (
"strconv"
"strings"
"testing"
"testing/fstest"
"time"
)
@ -55,8 +57,7 @@ func TestStatBadDir(t *testing.T) {
dir := TempDir()
badDir := filepath.Join(dir, "not-exist/really-not-exist")
_, err := Stat(badDir)
// TODO: PathError moved to io/fs in go 1.16; fix next line once we drop go 1.15 support.
if pe, ok := err.(*PathError); !ok || !IsNotExist(err) || pe.Path != badDir {
if pe, ok := err.(*fs.PathError); !ok || !IsNotExist(err) || pe.Path != badDir {
t.Errorf("Mkdir error = %#v; want PathError for path %q satisifying IsNotExist", err, badDir)
}
}
@ -123,8 +124,7 @@ func TestRemove(t *testing.T) {
if err == nil {
t.Errorf("TestRemove: remove of nonexistent file did not fail")
} else {
// FIXME: once we drop go 1.15, switch this to fs.PathError
if pe, ok := err.(*PathError); !ok {
if pe, ok := err.(*fs.PathError); !ok {
t.Errorf("TestRemove: expected PathError, got err %q", err.Error())
} else {
if pe.Path != f {
@ -271,3 +271,55 @@ func TestUserHomeDir(t *testing.T) {
t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode())
}
}
func TestDirFS(t *testing.T) {
if runtime.GOOS == "windows" {
t.Log("TODO: implement Readdir for Windows")
return
}
if err := fstest.TestFS(DirFS("./testdata/dirfs"), "a", "b", "dir/x"); err != nil {
t.Fatal(err)
}
// Test that Open does not accept backslash as separator.
d := DirFS(".")
_, err := d.Open(`testdata\dirfs`)
if err == nil {
t.Fatalf(`Open testdata\dirfs succeeded`)
}
}
func TestDirFSPathsValid(t *testing.T) {
if runtime.GOOS == "windows" {
t.Log("skipping on Windows")
return
}
// TODO: switch back to t.TempDir once it's implemented
d, err := MkdirTemp("", "TestDirFSPathsValid")
if err != nil {
t.Fatal(err)
}
defer Remove(d)
if err := WriteFile(filepath.Join(d, "control.txt"), []byte(string("Hello, world!")), 0644); err != nil {
t.Fatal(err)
}
defer Remove(filepath.Join(d, "control.txt"))
if err := WriteFile(filepath.Join(d, `e:xperi\ment.txt`), []byte(string("Hello, colon and backslash!")), 0644); err != nil {
t.Fatal(err)
}
defer Remove(filepath.Join(d, `e:xperi\ment.txt`))
fsys := DirFS(d)
err = fs.WalkDir(fsys, ".", func(path string, e fs.DirEntry, err error) error {
if fs.ValidPath(e.Name()) {
t.Logf("%q ok", e.Name())
} else {
t.Errorf("%q INVALID", e.Name())
}
return nil
})
if err != nil {
t.Fatal(err)
}
}

Просмотреть файл

@ -9,6 +9,7 @@ package os_test
import (
"errors"
"io/fs"
. "os"
"path/filepath"
"regexp"
@ -157,8 +158,7 @@ func TestMkdirTempBadDir(t *testing.T) {
badDir := filepath.Join(dir, "not-exist")
_, err = MkdirTemp(badDir, "foo")
// TODO: when we drop support for go 1.15, PathError should move to fs
if pe, ok := err.(*PathError); !ok || !IsNotExist(err) || pe.Path != badDir {
if pe, ok := err.(*fs.PathError); !ok || !IsNotExist(err) || pe.Path != badDir {
t.Errorf("TempDir error = %#v; want PathError for path %q satisifying IsNotExist", err, badDir)
}
}