build: Fix racy write to finishedShards map

Change-Id: Ib27ea7adbb092d8a310004984c095b5e396da8dd
diff --git a/build/builder.go b/build/builder.go
index 77ee611..20a848d 100644
--- a/build/builder.go
+++ b/build/builder.go
@@ -94,6 +94,10 @@
 	finishedShards map[string]string
 }
 
+type finishedShard struct {
+	temp, final string
+}
+
 // SetDefaults sets reasonable default options.
 func (o *Options) SetDefaults() {
 	if o.CTags == "" {
@@ -267,7 +271,7 @@
 		b.building.Add(1)
 		go func() {
 			b.throttle <- 1
-			err := b.buildShard(todo, shard)
+			done, err := b.buildShard(todo, shard)
 			<-b.throttle
 
 			b.errMu.Lock()
@@ -275,13 +279,17 @@
 			if err != nil && b.buildError == nil {
 				b.buildError = err
 			}
+			b.finishedShards[done.temp] = done.final
 			b.building.Done()
 		}()
 	} else {
 		// No goroutines when we're not parallel. This
 		// simplifies memory profiling.
-		b.buildError = b.buildShard(todo, shard)
-
+		done, err := b.buildShard(todo, shard)
+		b.buildError = err
+		if err == nil {
+			b.finishedShards[done.temp] = done.final
+		}
 		if b.opts.MemProfile != "" {
 			// drop memory, and profile.
 			todo = nil
@@ -311,14 +319,14 @@
 	log.Printf("wrote mem profile %q", nm)
 }
 
-func (b *Builder) buildShard(todo []*zoekt.Document, nextShardNum int) error {
+func (b *Builder) buildShard(todo []*zoekt.Document, nextShardNum int) (*finishedShard, error) {
 	if b.opts.CTags == "" && b.opts.CTagsMustSucceed {
-		return fmt.Errorf("ctags binary not found, but CTagsMustSucceed set.")
+		return nil, fmt.Errorf("ctags binary not found, but CTagsMustSucceed set.")
 	}
 	if b.opts.CTags != "" {
 		err := ctagsAddSymbols(todo, b.opts.CTags, b.opts.NamespaceSandbox)
 		if b.opts.CTagsMustSucceed && err != nil {
-			return err
+			return nil, err
 		}
 		if err != nil {
 			log.Printf("ignoring %s error: %v", b.opts.CTags, err)
@@ -327,22 +335,19 @@
 
 	name, err := b.opts.shardName(nextShardNum)
 	if err != nil {
-		return err
+		return nil, err
 	}
 
 	shardBuilder, err := b.newShardBuilder()
 	if err != nil {
-		return err
+		return nil, err
 	}
 
 	for _, t := range todo {
 		shardBuilder.Add(*t)
 	}
 
-	if err := b.writeShard(name, shardBuilder); err != nil {
-		return err
-	}
-	return nil
+	return b.writeShard(name, shardBuilder)
 }
 
 func (b *Builder) newShardBuilder() (*zoekt.IndexBuilder, error) {
@@ -356,32 +361,31 @@
 	return shardBuilder, nil
 }
 
-func (b *Builder) writeShard(fn string, ib *zoekt.IndexBuilder) error {
+func (b *Builder) writeShard(fn string, ib *zoekt.IndexBuilder) (*finishedShard, error) {
 	dir := filepath.Dir(fn)
 	if err := os.MkdirAll(dir, 0700); err != nil {
-		return err
+		return nil, err
 	}
 
 	f, err := ioutil.TempFile(dir, filepath.Base(fn))
 	if err != nil {
-		return err
+		return nil, err
 	}
 
 	defer f.Close()
 	if err := ib.Write(f); err != nil {
-		return err
+		return nil, err
 	}
 	fi, err := f.Stat()
 	if err != nil {
-		return err
+		return nil, err
 	}
 	if err := f.Close(); err != nil {
-		return err
+		return nil, err
 	}
 
-	b.finishedShards[f.Name()] = fn
 	log.Printf("finished %s: %d index bytes (overhead %3.1f)", fn, fi.Size(),
 		float64(fi.Size())/float64(ib.ContentSize()+1))
 
-	return nil
+	return &finishedShard{f.Name(), fn}, nil
 }