Skip to content

Commit d6ebdd1

Browse files
committed
fix go 1.20 build and retract v1.0.7
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.
1 parent 861d712 commit d6ebdd1

9 files changed

Lines changed: 217 additions & 37 deletions

File tree

.github/workflows/linux.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
14-
go: [1.21, 1.22]
14+
go: ['1.20', '1.21', '1.22']
1515
steps:
1616
- uses: actions/checkout@v4
1717
with:

Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ test_build_solaris_amd64:
4747

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

5254
.PHONY: test_build_aix_ppc64
5355
test_build_aix_ppc64:

dirent_portable.go

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ package fastwalk
88
import (
99
"io/fs"
1010
"os"
11-
"slices"
12-
"strings"
11+
"sort"
1312
"sync"
13+
14+
"github.com/charlievieth/fastwalk/internal/fmtdirent"
1415
)
1516

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

2425
func (d *portableDirent) String() string {
25-
return fs.FormatDirEntry(d)
26+
return fmtdirent.FormatDirEntry(d)
2627
}
2728

2829
func (d *portableDirent) Stat() (fs.FileInfo, error) {
@@ -72,52 +73,56 @@ func sortDirents(mode SortMode, dents []DirEntry) {
7273
}
7374
switch mode {
7475
case SortLexical:
75-
slices.SortFunc(dents, func(d1, d2 DirEntry) int {
76-
return strings.Compare(d1.Name(), d2.Name())
76+
sort.Slice(dents, func(i, j int) bool {
77+
return dents[i].Name() < dents[j].Name()
7778
})
7879
case SortFilesFirst:
79-
slices.SortFunc(dents, func(d1, d2 DirEntry) int {
80+
sort.Slice(dents, func(i, j int) bool {
81+
d1 := dents[i]
82+
d2 := dents[j]
8083
r1 := d1.Type().IsRegular()
8184
r2 := d2.Type().IsRegular()
8285
switch {
8386
case r1 && !r2:
84-
return -1
87+
return true
8588
case !r1 && r2:
86-
return 1
89+
return false
8790
case !r1 && !r2:
8891
// Both are not regular files: sort directories last
8992
dd1 := d1.Type().IsDir()
9093
dd2 := d2.Type().IsDir()
9194
switch {
9295
case !dd1 && dd2:
93-
return -1
96+
return true
9497
case dd1 && !dd2:
95-
return 1
98+
return false
9699
}
97100
}
98-
return strings.Compare(d1.Name(), d2.Name())
101+
return d1.Name() < d2.Name()
99102
})
100103
case SortDirsFirst:
101-
slices.SortFunc(dents, func(d1, d2 DirEntry) int {
104+
sort.Slice(dents, func(i, j int) bool {
105+
d1 := dents[i]
106+
d2 := dents[j]
102107
dd1 := d1.Type().IsDir()
103108
dd2 := d2.Type().IsDir()
104109
switch {
105110
case dd1 && !dd2:
106-
return -1
111+
return true
107112
case !dd1 && dd2:
108-
return 1
113+
return false
109114
case !dd1 && !dd2:
110115
// Both are not directories: sort regular files first
111116
r1 := d1.Type().IsRegular()
112117
r2 := d2.Type().IsRegular()
113118
switch {
114119
case r1 && !r2:
115-
return -1
120+
return true
116121
case !r1 && r2:
117-
return 1
122+
return false
118123
}
119124
}
120-
return strings.Compare(d1.Name(), d2.Name())
125+
return d1.Name() < d2.Name()
121126
})
122127
}
123128
}

dirent_portable_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"reflect"
99
"testing"
1010
"time"
11+
12+
"github.com/charlievieth/fastwalk/internal/fmtdirent"
1113
)
1214

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

2729
func (de dirEntry) String() string {
28-
return fs.FormatDirEntry(de)
30+
return fmtdirent.FormatDirEntry(de)
2931
}
3032

3133
// NB: this must be kept in sync with the

dirent_unix.go

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ package fastwalk
55
import (
66
"io/fs"
77
"os"
8-
"slices"
9-
"strings"
8+
"sort"
109
"sync"
10+
11+
"github.com/charlievieth/fastwalk/internal/fmtdirent"
1112
)
1213

1314
type unixDirent struct {
@@ -21,7 +22,7 @@ type unixDirent struct {
2122
func (d *unixDirent) Name() string { return d.name }
2223
func (d *unixDirent) IsDir() bool { return d.typ.IsDir() }
2324
func (d *unixDirent) Type() fs.FileMode { return d.typ }
24-
func (d *unixDirent) String() string { return fs.FormatDirEntry(d) }
25+
func (d *unixDirent) String() string { return fmtdirent.FormatDirEntry(d) }
2526

2627
func (d *unixDirent) Info() (fs.FileInfo, error) {
2728
info := loadFileInfo(&d.info)
@@ -87,52 +88,56 @@ func sortDirents(mode SortMode, dents []*unixDirent) {
8788
}
8889
switch mode {
8990
case SortLexical:
90-
slices.SortFunc(dents, func(d1, d2 *unixDirent) int {
91-
return strings.Compare(d1.name, d2.name)
91+
sort.Slice(dents, func(i, j int) bool {
92+
return dents[i].name < dents[j].name
9293
})
9394
case SortFilesFirst:
94-
slices.SortFunc(dents, func(d1, d2 *unixDirent) int {
95+
sort.Slice(dents, func(i, j int) bool {
96+
d1 := dents[i]
97+
d2 := dents[j]
9598
r1 := d1.typ.IsRegular()
9699
r2 := d2.typ.IsRegular()
97100
switch {
98101
case r1 && !r2:
99-
return -1
102+
return true
100103
case !r1 && r2:
101-
return 1
104+
return false
102105
case !r1 && !r2:
103106
// Both are not regular files: sort directories last
104107
dd1 := d1.typ.IsDir()
105108
dd2 := d2.typ.IsDir()
106109
switch {
107110
case !dd1 && dd2:
108-
return -1
111+
return true
109112
case dd1 && !dd2:
110-
return 1
113+
return false
111114
}
112115
}
113-
return strings.Compare(d1.name, d2.name)
116+
return d1.name < d2.name
114117
})
115118
case SortDirsFirst:
116-
slices.SortFunc(dents, func(d1, d2 *unixDirent) int {
119+
sort.Slice(dents, func(i, j int) bool {
120+
d1 := dents[i]
121+
d2 := dents[j]
117122
dd1 := d1.typ.IsDir()
118123
dd2 := d2.typ.IsDir()
119124
switch {
120125
case dd1 && !dd2:
121-
return -1
126+
return true
122127
case !dd1 && dd2:
123-
return 1
128+
return false
124129
case !dd1 && !dd2:
125130
// Both are not directories: sort regular files first
126131
r1 := d1.typ.IsRegular()
127132
r2 := d2.typ.IsRegular()
128133
switch {
129134
case r1 && !r2:
130-
return -1
135+
return true
131136
case !r1 && r2:
132-
return 1
137+
return false
133138
}
134139
}
135-
return strings.Compare(d1.name, d2.name)
140+
return d1.name < d2.name
136141
})
137142
}
138143
}

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
module github.com/charlievieth/fastwalk
22

33
go 1.20
4+
5+
retract v1.0.7 // Build broken on Go 1.20
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//go:build !go1.21
2+
3+
package fmtdirent
4+
5+
import "io/fs"
6+
7+
// Backport fs.FormatDirEntry from go1.21
8+
9+
// FormatDirEntry returns a formatted version of dir for human readability.
10+
// Implementations of [DirEntry] can call this from a String method.
11+
// The outputs for a directory named subdir and a file named hello.go are:
12+
//
13+
// d subdir/
14+
// - hello.go
15+
func FormatDirEntry(dir fs.DirEntry) string {
16+
name := dir.Name()
17+
b := make([]byte, 0, 5+len(name))
18+
19+
// The Type method does not return any permission bits,
20+
// so strip them from the string.
21+
mode := dir.Type().String()
22+
mode = mode[:len(mode)-9]
23+
24+
b = append(b, mode...)
25+
b = append(b, ' ')
26+
b = append(b, name...)
27+
if dir.IsDir() {
28+
b = append(b, '/')
29+
}
30+
return string(b)
31+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//go:build !go1.21
2+
3+
// Copyright 2023 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
// Backport fs.FormatDirEntry tests from go1.21. We don't test
8+
// the go1.21+ FormatDirEntry function since it just calls the
9+
// stdlib and we don't want changes in its output to break our
10+
// tests.
11+
12+
package fmtdirent_test
13+
14+
import (
15+
. "io/fs"
16+
"testing"
17+
"time"
18+
19+
"github.com/charlievieth/fastwalk/internal/fmtdirent"
20+
)
21+
22+
// formatTest implements FileInfo to test FormatFileInfo,
23+
// and implements DirEntry to test FormatDirEntry.
24+
type formatTest struct {
25+
name string
26+
size int64
27+
mode FileMode
28+
modTime time.Time
29+
isDir bool
30+
}
31+
32+
func (fs *formatTest) Name() string {
33+
return fs.name
34+
}
35+
36+
func (fs *formatTest) Size() int64 {
37+
return fs.size
38+
}
39+
40+
func (fs *formatTest) Mode() FileMode {
41+
return fs.mode
42+
}
43+
44+
func (fs *formatTest) ModTime() time.Time {
45+
return fs.modTime
46+
}
47+
48+
func (fs *formatTest) IsDir() bool {
49+
return fs.isDir
50+
}
51+
52+
func (fs *formatTest) Sys() any {
53+
return nil
54+
}
55+
56+
func (fs *formatTest) Type() FileMode {
57+
return fs.mode.Type()
58+
}
59+
60+
func (fs *formatTest) Info() (FileInfo, error) {
61+
return fs, nil
62+
}
63+
64+
var formatTests = []struct {
65+
input formatTest
66+
wantDirEntry string
67+
}{
68+
{
69+
formatTest{
70+
name: "hello.go",
71+
size: 100,
72+
mode: 0o644,
73+
modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),
74+
isDir: false,
75+
},
76+
"- hello.go",
77+
},
78+
{
79+
formatTest{
80+
name: "home/gopher",
81+
size: 0,
82+
mode: ModeDir | 0o755,
83+
modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),
84+
isDir: true,
85+
},
86+
"d home/gopher/",
87+
},
88+
{
89+
formatTest{
90+
name: "big",
91+
size: 0x7fffffffffffffff,
92+
mode: ModeIrregular | 0o644,
93+
modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),
94+
isDir: false,
95+
},
96+
"? big",
97+
},
98+
{
99+
formatTest{
100+
name: "small",
101+
size: -0x8000000000000000,
102+
mode: ModeSocket | ModeSetuid | 0o644,
103+
modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),
104+
isDir: false,
105+
},
106+
"S small",
107+
},
108+
}
109+
110+
func TestFormatDirEntry(t *testing.T) {
111+
for i, test := range formatTests {
112+
got := fmtdirent.FormatDirEntry(&test.input)
113+
if got != test.wantDirEntry {
114+
t.Errorf("%d: FormatDirEntry(%#v) = %q, want %q", i, test.input, got, test.wantDirEntry)
115+
}
116+
}
117+
118+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//go:build go1.21
2+
3+
package fmtdirent
4+
5+
import "io/fs"
6+
7+
// FormatDirEntry returns a formatted version of dir for human readability.
8+
// Implementations of [DirEntry] can call this from a String method.
9+
// The outputs for a directory named subdir and a file named hello.go are:
10+
//
11+
// d subdir/
12+
// - hello.go
13+
func FormatDirEntry(dir fs.DirEntry) string {
14+
return fs.FormatDirEntry(dir)
15+
}

0 commit comments

Comments
 (0)