@@ -24,6 +24,7 @@ import (
24
24
"go/build"
25
25
"go/parser"
26
26
"go/token"
27
+ "io"
27
28
"log"
28
29
"os"
29
30
"path/filepath"
60
61
"linux/arm64" , "linux/ppc64le" ,
61
62
"linux/s390x" , "darwin/386" ,
62
63
}
64
+ darwinPlatString = "darwin/386,darwin/amd64"
65
+ windowsPlatString = "windows/386,windows/amd64"
63
66
)
64
67
65
68
type analyzer struct {
@@ -69,6 +72,7 @@ type analyzer struct {
69
72
failed bool
70
73
platform string
71
74
donePaths map [string ]interface {}
75
+ errors []string
72
76
}
73
77
74
78
func newAnalyzer (platform string ) * analyzer {
@@ -120,11 +124,19 @@ func (a *analyzer) handleError(err error) {
120
124
return
121
125
}
122
126
}
123
- // TODO(rmmh): dedup errors across platforms?
124
- fmt .Fprintf (os .Stderr , "%sERROR(%s) %s\n " , logPrefix , a .platform , err )
127
+ a .errors = append (a .errors , err .Error ())
128
+ if * serial {
129
+ fmt .Fprintf (os .Stderr , "%sERROR(%s) %s\n " , logPrefix , a .platform , err )
130
+ }
125
131
a .failed = true
126
132
}
127
133
134
+ func (a * analyzer ) dumpAndResetErrors () []string {
135
+ es := a .errors
136
+ a .errors = nil
137
+ return es
138
+ }
139
+
128
140
// collect extracts test metadata from a file.
129
141
func (a * analyzer ) collect (dir string ) {
130
142
if _ , ok := a .donePaths [dir ]; ok {
@@ -262,6 +274,51 @@ func (c *collector) handlePath(path string, info os.FileInfo, err error) error {
262
274
return nil
263
275
}
264
276
277
+ type analyzerResult struct {
278
+ platform string
279
+ dir string
280
+ errors []string
281
+ }
282
+
283
+ func dedupeErrors (out io.Writer , results chan analyzerResult , nDirs , nPlatforms int ) {
284
+ pkgRes := make (map [string ][]analyzerResult )
285
+ for done := 0 ; done < nDirs ; {
286
+ res := <- results
287
+ pkgRes [res .dir ] = append (pkgRes [res .dir ], res )
288
+ if len (pkgRes [res .dir ]) != nPlatforms {
289
+ continue // expect more results for dir
290
+ }
291
+ done ++
292
+ // Collect list of platforms for each error
293
+ errPlats := map [string ][]string {}
294
+ for _ , res := range pkgRes [res .dir ] {
295
+ for _ , err := range res .errors {
296
+ errPlats [err ] = append (errPlats [err ], res .platform )
297
+ }
298
+ }
299
+ // Print each error (in the same order!) once.
300
+ for _ , res := range pkgRes [res .dir ] {
301
+ for _ , err := range res .errors {
302
+ if errPlats [err ] == nil {
303
+ continue // already printed
304
+ }
305
+ sort .Strings (errPlats [err ])
306
+ plats := strings .Join (errPlats [err ], "," )
307
+ if len (errPlats [err ]) == len (crossPlatforms ) {
308
+ plats = "all"
309
+ } else if plats == darwinPlatString {
310
+ plats = "darwin"
311
+ } else if plats == windowsPlatString {
312
+ plats = "windows"
313
+ }
314
+ fmt .Fprintf (out , "%sERROR(%s) %s\n " , logPrefix , plats , err )
315
+ delete (errPlats , err )
316
+ }
317
+ }
318
+ delete (pkgRes , res .dir )
319
+ }
320
+ }
321
+
265
322
func main () {
266
323
flag .Parse ()
267
324
args := flag .Args ()
@@ -296,6 +353,15 @@ func main() {
296
353
var processedDirs int64
297
354
var currentWork int64 // (dir_index << 8) | platform_index
298
355
statuses := make ([]int , len (ps ))
356
+ var results chan analyzerResult
357
+ if ! * serial {
358
+ results = make (chan analyzerResult )
359
+ wg .Add (1 )
360
+ go func () {
361
+ dedupeErrors (os .Stderr , results , len (c .dirs ), len (ps ))
362
+ wg .Done ()
363
+ }()
364
+ }
299
365
for i , p := range ps {
300
366
wg .Add (1 )
301
367
fn := func (i int , p string ) {
@@ -305,6 +371,9 @@ func main() {
305
371
a .collect (dir )
306
372
atomic .AddInt64 (& processedDirs , 1 )
307
373
atomic .StoreInt64 (& currentWork , int64 (n << 8 | i ))
374
+ if results != nil {
375
+ results <- analyzerResult {p , dir , a .dumpAndResetErrors ()}
376
+ }
308
377
}
309
378
if a .failed {
310
379
statuses [i ] = 1
0 commit comments