1// Copyright 2017 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package cache
6
7import (
8	"fmt"
9	"os"
10	"path/filepath"
11	"sync"
12
13	"cmd/go/internal/base"
14	"cmd/go/internal/cfg"
15	"internal/goexperiment"
16)
17
18// Default returns the default cache to use.
19// It never returns nil.
20func Default() Cache {
21	defaultOnce.Do(initDefaultCache)
22	return defaultCache
23}
24
25var (
26	defaultOnce  sync.Once
27	defaultCache Cache
28)
29
30// cacheREADME is a message stored in a README in the cache directory.
31// Because the cache lives outside the normal Go trees, we leave the
32// README as a courtesy to explain where it came from.
33const cacheREADME = `This directory holds cached build artifacts from the Go build system.
34Run "go clean -cache" if the directory is getting too large.
35Run "go clean -fuzzcache" to delete the fuzz cache.
36See golang.org to learn more about Go.
37`
38
39// initDefaultCache does the work of finding the default cache
40// the first time Default is called.
41func initDefaultCache() {
42	dir, _ := DefaultDir()
43	if dir == "off" {
44		if defaultDirErr != nil {
45			base.Fatalf("build cache is required, but could not be located: %v", defaultDirErr)
46		}
47		base.Fatalf("build cache is disabled by GOCACHE=off, but required as of Go 1.12")
48	}
49	if err := os.MkdirAll(dir, 0777); err != nil {
50		base.Fatalf("failed to initialize build cache at %s: %s\n", dir, err)
51	}
52	if _, err := os.Stat(filepath.Join(dir, "README")); err != nil {
53		// Best effort.
54		os.WriteFile(filepath.Join(dir, "README"), []byte(cacheREADME), 0666)
55	}
56
57	diskCache, err := Open(dir)
58	if err != nil {
59		base.Fatalf("failed to initialize build cache at %s: %s\n", dir, err)
60	}
61
62	if v := cfg.Getenv("GOCACHEPROG"); v != "" && goexperiment.CacheProg {
63		defaultCache = startCacheProg(v, diskCache)
64	} else {
65		defaultCache = diskCache
66	}
67}
68
69var (
70	defaultDirOnce    sync.Once
71	defaultDir        string
72	defaultDirChanged bool // effective value differs from $GOCACHE
73	defaultDirErr     error
74)
75
76// DefaultDir returns the effective GOCACHE setting.
77// It returns "off" if the cache is disabled,
78// and reports whether the effective value differs from GOCACHE.
79func DefaultDir() (string, bool) {
80	// Save the result of the first call to DefaultDir for later use in
81	// initDefaultCache. cmd/go/main.go explicitly sets GOCACHE so that
82	// subprocesses will inherit it, but that means initDefaultCache can't
83	// otherwise distinguish between an explicit "off" and a UserCacheDir error.
84
85	defaultDirOnce.Do(func() {
86		defaultDir = cfg.Getenv("GOCACHE")
87		if defaultDir != "" {
88			defaultDirChanged = true
89			if filepath.IsAbs(defaultDir) || defaultDir == "off" {
90				return
91			}
92			defaultDir = "off"
93			defaultDirErr = fmt.Errorf("GOCACHE is not an absolute path")
94			return
95		}
96
97		// Compute default location.
98		dir, err := os.UserCacheDir()
99		if err != nil {
100			defaultDir = "off"
101			defaultDirChanged = true
102			defaultDirErr = fmt.Errorf("GOCACHE is not defined and %v", err)
103			return
104		}
105		defaultDir = filepath.Join(dir, "go-build")
106	})
107
108	return defaultDir, defaultDirChanged
109}
110