Skip to content

Commit

Permalink
fix go 1.20 build and retract v1.0.7
Browse files Browse the repository at this point in the history
This commit fixes the build for Go 1.20 by removing use of the slices
package and retracts the v1.0.7 release.

It also changes the CI to test go1.20 (our minimum supported version)
so that this will not happen again in the future.
  • Loading branch information
charlievieth committed Jul 18, 2024
1 parent 861d712 commit d6ebdd1
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 37 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go: [1.21, 1.22]
go: ['1.20', '1.21', '1.22']
steps:
- uses: actions/checkout@v4
with:
Expand Down
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ test_build_solaris_amd64:

.PHONY: test_build_wasip1_wasm
test_build_wasip1_wasm:
GOOS=wasip1 GOARCH=wasm go test -c -o /dev/null
@# Ignore versions before 1.21
go version | grep -qE 'go1\.(20|1[0-9])' || \
GOOS=wasip1 GOARCH=wasm go test -c -o /dev/null

.PHONY: test_build_aix_ppc64
test_build_aix_ppc64:
Expand Down
39 changes: 22 additions & 17 deletions dirent_portable.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ package fastwalk
import (
"io/fs"
"os"
"slices"
"strings"
"sort"
"sync"

"github.com/charlievieth/fastwalk/internal/fmtdirent"
)

var _ DirEntry = (*portableDirent)(nil)
Expand All @@ -22,7 +23,7 @@ type portableDirent struct {
}

func (d *portableDirent) String() string {
return fs.FormatDirEntry(d)
return fmtdirent.FormatDirEntry(d)
}

func (d *portableDirent) Stat() (fs.FileInfo, error) {
Expand Down Expand Up @@ -72,52 +73,56 @@ func sortDirents(mode SortMode, dents []DirEntry) {
}
switch mode {
case SortLexical:
slices.SortFunc(dents, func(d1, d2 DirEntry) int {
return strings.Compare(d1.Name(), d2.Name())
sort.Slice(dents, func(i, j int) bool {
return dents[i].Name() < dents[j].Name()
})
case SortFilesFirst:
slices.SortFunc(dents, func(d1, d2 DirEntry) int {
sort.Slice(dents, func(i, j int) bool {
d1 := dents[i]
d2 := dents[j]
r1 := d1.Type().IsRegular()
r2 := d2.Type().IsRegular()
switch {
case r1 && !r2:
return -1
return true
case !r1 && r2:
return 1
return false
case !r1 && !r2:
// Both are not regular files: sort directories last
dd1 := d1.Type().IsDir()
dd2 := d2.Type().IsDir()
switch {
case !dd1 && dd2:
return -1
return true
case dd1 && !dd2:
return 1
return false
}
}
return strings.Compare(d1.Name(), d2.Name())
return d1.Name() < d2.Name()
})
case SortDirsFirst:
slices.SortFunc(dents, func(d1, d2 DirEntry) int {
sort.Slice(dents, func(i, j int) bool {
d1 := dents[i]
d2 := dents[j]
dd1 := d1.Type().IsDir()
dd2 := d2.Type().IsDir()
switch {
case dd1 && !dd2:
return -1
return true
case !dd1 && dd2:
return 1
return false
case !dd1 && !dd2:
// Both are not directories: sort regular files first
r1 := d1.Type().IsRegular()
r2 := d2.Type().IsRegular()
switch {
case r1 && !r2:
return -1
return true
case !r1 && r2:
return 1
return false
}
}
return strings.Compare(d1.Name(), d2.Name())
return d1.Name() < d2.Name()
})
}
}
4 changes: 3 additions & 1 deletion dirent_portable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"reflect"
"testing"
"time"

"github.com/charlievieth/fastwalk/internal/fmtdirent"
)

var _ DirEntry = dirEntry{}
Expand All @@ -25,7 +27,7 @@ func (de dirEntry) Info() (fs.FileInfo, error) { panic("not implemented") }
func (de dirEntry) Stat() (fs.FileInfo, error) { panic("not implemented") }

func (de dirEntry) String() string {
return fs.FormatDirEntry(de)
return fmtdirent.FormatDirEntry(de)
}

// NB: this must be kept in sync with the
Expand Down
39 changes: 22 additions & 17 deletions dirent_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ package fastwalk
import (
"io/fs"
"os"
"slices"
"strings"
"sort"
"sync"

"github.com/charlievieth/fastwalk/internal/fmtdirent"
)

type unixDirent struct {
Expand All @@ -21,7 +22,7 @@ type unixDirent struct {
func (d *unixDirent) Name() string { return d.name }
func (d *unixDirent) IsDir() bool { return d.typ.IsDir() }
func (d *unixDirent) Type() fs.FileMode { return d.typ }
func (d *unixDirent) String() string { return fs.FormatDirEntry(d) }
func (d *unixDirent) String() string { return fmtdirent.FormatDirEntry(d) }

func (d *unixDirent) Info() (fs.FileInfo, error) {
info := loadFileInfo(&d.info)
Expand Down Expand Up @@ -87,52 +88,56 @@ func sortDirents(mode SortMode, dents []*unixDirent) {
}
switch mode {
case SortLexical:
slices.SortFunc(dents, func(d1, d2 *unixDirent) int {
return strings.Compare(d1.name, d2.name)
sort.Slice(dents, func(i, j int) bool {
return dents[i].name < dents[j].name
})
case SortFilesFirst:
slices.SortFunc(dents, func(d1, d2 *unixDirent) int {
sort.Slice(dents, func(i, j int) bool {
d1 := dents[i]
d2 := dents[j]
r1 := d1.typ.IsRegular()
r2 := d2.typ.IsRegular()
switch {
case r1 && !r2:
return -1
return true
case !r1 && r2:
return 1
return false
case !r1 && !r2:
// Both are not regular files: sort directories last
dd1 := d1.typ.IsDir()
dd2 := d2.typ.IsDir()
switch {
case !dd1 && dd2:
return -1
return true
case dd1 && !dd2:
return 1
return false
}
}
return strings.Compare(d1.name, d2.name)
return d1.name < d2.name
})
case SortDirsFirst:
slices.SortFunc(dents, func(d1, d2 *unixDirent) int {
sort.Slice(dents, func(i, j int) bool {
d1 := dents[i]
d2 := dents[j]
dd1 := d1.typ.IsDir()
dd2 := d2.typ.IsDir()
switch {
case dd1 && !dd2:
return -1
return true
case !dd1 && dd2:
return 1
return false
case !dd1 && !dd2:
// Both are not directories: sort regular files first
r1 := d1.typ.IsRegular()
r2 := d2.typ.IsRegular()
switch {
case r1 && !r2:
return -1
return true
case !r1 && r2:
return 1
return false
}
}
return strings.Compare(d1.name, d2.name)
return d1.name < d2.name
})
}
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/charlievieth/fastwalk

go 1.20

retract v1.0.7 // Build broken on Go 1.20
31 changes: 31 additions & 0 deletions internal/fmtdirent/fmtdirent_go120.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//go:build !go1.21

package fmtdirent

import "io/fs"

// Backport fs.FormatDirEntry from go1.21

// FormatDirEntry returns a formatted version of dir for human readability.
// Implementations of [DirEntry] can call this from a String method.
// The outputs for a directory named subdir and a file named hello.go are:
//
// d subdir/
// - hello.go
func FormatDirEntry(dir fs.DirEntry) string {
name := dir.Name()
b := make([]byte, 0, 5+len(name))

// The Type method does not return any permission bits,
// so strip them from the string.
mode := dir.Type().String()
mode = mode[:len(mode)-9]

b = append(b, mode...)
b = append(b, ' ')
b = append(b, name...)
if dir.IsDir() {
b = append(b, '/')
}
return string(b)
}
118 changes: 118 additions & 0 deletions internal/fmtdirent/fmtdirent_go120_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//go:build !go1.21

// 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.

// Backport fs.FormatDirEntry tests from go1.21. We don't test
// the go1.21+ FormatDirEntry function since it just calls the
// stdlib and we don't want changes in its output to break our
// tests.

package fmtdirent_test

import (
. "io/fs"
"testing"
"time"

"github.com/charlievieth/fastwalk/internal/fmtdirent"
)

// formatTest implements FileInfo to test FormatFileInfo,
// and implements DirEntry to test FormatDirEntry.
type formatTest struct {
name string
size int64
mode FileMode
modTime time.Time
isDir bool
}

func (fs *formatTest) Name() string {
return fs.name
}

func (fs *formatTest) Size() int64 {
return fs.size
}

func (fs *formatTest) Mode() FileMode {
return fs.mode
}

func (fs *formatTest) ModTime() time.Time {
return fs.modTime
}

func (fs *formatTest) IsDir() bool {
return fs.isDir
}

func (fs *formatTest) Sys() any {
return nil
}

func (fs *formatTest) Type() FileMode {
return fs.mode.Type()
}

func (fs *formatTest) Info() (FileInfo, error) {
return fs, nil
}

var formatTests = []struct {
input formatTest
wantDirEntry string
}{
{
formatTest{
name: "hello.go",
size: 100,
mode: 0o644,
modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),
isDir: false,
},
"- hello.go",
},
{
formatTest{
name: "home/gopher",
size: 0,
mode: ModeDir | 0o755,
modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),
isDir: true,
},
"d home/gopher/",
},
{
formatTest{
name: "big",
size: 0x7fffffffffffffff,
mode: ModeIrregular | 0o644,
modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),
isDir: false,
},
"? big",
},
{
formatTest{
name: "small",
size: -0x8000000000000000,
mode: ModeSocket | ModeSetuid | 0o644,
modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),
isDir: false,
},
"S small",
},
}

func TestFormatDirEntry(t *testing.T) {
for i, test := range formatTests {
got := fmtdirent.FormatDirEntry(&test.input)
if got != test.wantDirEntry {
t.Errorf("%d: FormatDirEntry(%#v) = %q, want %q", i, test.input, got, test.wantDirEntry)
}
}

}
15 changes: 15 additions & 0 deletions internal/fmtdirent/fmtdirent_go121.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//go:build go1.21

package fmtdirent

import "io/fs"

// FormatDirEntry returns a formatted version of dir for human readability.
// Implementations of [DirEntry] can call this from a String method.
// The outputs for a directory named subdir and a file named hello.go are:
//
// d subdir/
// - hello.go
func FormatDirEntry(dir fs.DirEntry) string {
return fs.FormatDirEntry(dir)
}

0 comments on commit d6ebdd1

Please sign in to comment.