Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include clang-tidy binary in the cache fingerprint #4

Merged
merged 2 commits into from
Oct 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ A fairly simple wrapper application around the clang-tidy executable. It will at

## Configuration

In order to keep the wrapper reasonably clean, the user will have to write a configuration file at the following location:
By default, the wrapper will look for the `clang-tidy` executable on the path. This can be changed by setting the `CLANG_TIDY_CACHE_BINARY` environment variable, or by writing a configuration file at the following location:

`~/.ctcache/config.json`

Expand Down
40 changes: 30 additions & 10 deletions caches/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/ejfitzgerald/clang-tidy-cache/utils"
"io"
"os"
"os/exec"
)

type Cacher interface {
Expand All @@ -15,15 +16,8 @@ type Cacher interface {
SaveEntry(digest []byte, content []byte) error
}

func computeDigestForConfigFile(projectRoot string) ([]byte, error) {
configFilePath, err := utils.FindInParents(projectRoot, ".clang-tidy")
if err != nil {
return nil, err
}

// compute the SHA of the configuration file
// read the contents of the file am hash it
f, err := os.Open(configFilePath)
func computeFileDigest(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
Expand All @@ -40,7 +34,26 @@ func computeDigestForConfigFile(projectRoot string) ([]byte, error) {
return digest, nil
}

func ComputeFingerPrint(invocation *clang.TidyInvocation, wd string, args []string) ([]byte, error) {
func computeDigestForConfigFile(projectRoot string) ([]byte, error) {
configFilePath, err := utils.FindInParents(projectRoot, ".clang-tidy")
if err != nil {
return nil, err
}

return computeFileDigest(configFilePath)
}

func computeDigestForClangTidyBinary(clangTidyPath string) ([]byte, error) {
// resolve to a full path: e.g. `clang-tidy` -> `/usr/local/bin/clang-tidy`
path, err := exec.LookPath(clangTidyPath)
if err != nil {
return nil, err
}

return computeFileDigest(path)
}

func ComputeFingerPrint(clangTidyPath string, invocation *clang.TidyInvocation, wd string, args []string) ([]byte, error) {

// extract the compilation target command flags from the database
targetFlags, err := clang.ExtractCompilationTarget(invocation.DatabaseRoot, invocation.TargetPath)
Expand All @@ -66,10 +79,17 @@ func ComputeFingerPrint(invocation *clang.TidyInvocation, wd string, args []stri
return nil, err
}

// we also need to include the clang-tidy binary since different version have different output
binaryDigest, err := computeDigestForClangTidyBinary(clangTidyPath)
if err != nil {
return nil, err
}

// combine all the digests to generate a unique fingerprint
hasher := sha256.New()
hasher.Write(preProcessedDigest)
hasher.Write(configDigest)
hasher.Write(binaryDigest)
fingerPrint := hasher.Sum(nil)

return fingerPrint, nil
Expand Down
41 changes: 35 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,33 +20,62 @@ type Configuration struct {
GcsConfig *caches.GcsConfiguration `json:"gcs,omitempty"`
}

func loadConfiguration() (*Configuration, error) {
func readConfigFile(cfg *Configuration) error {
usr, err := user.Current()
if err != nil {
return nil, err
return err
}

// define the configuration path
configPath := path.Join(usr.HomeDir, ".ctcache", "config.json")

// missing config file is fine: we simply use the defaults or env vars
if _, err := os.Stat(configPath); os.IsNotExist(err) {
return nil
}

// open the configuration file
jsonFile, err := os.Open(configPath)
if err != nil {
return nil, err
return err
}

// defer the closing of our jsonFile so that we can parse it later on
defer jsonFile.Close()

// read the contents
bytes, err := ioutil.ReadAll(jsonFile)
if err != nil {
return err
}

var cfg Configuration
err = json.Unmarshal(bytes, &cfg)
err = json.Unmarshal(bytes, cfg)
if err != nil {
return err
}

return nil
}

func readConfigEnv(cfg *Configuration) {
if envPath := os.Getenv("CLANG_TIDY_CACHE_BINARY"); len(envPath) > 0 {
cfg.ClangTidyPath = envPath
}
}

func loadConfiguration() (*Configuration, error) {
// lowest priority: built-in defaults
cfg := Configuration{ClangTidyPath: "clang-tidy"}

// higher priority: config file
err := readConfigFile(&cfg)
if err != nil {
return nil, err
}

// highest priority: environment variables
readConfigEnv(&cfg)

return &cfg, nil
}

Expand Down Expand Up @@ -123,7 +152,7 @@ func evaluateTidyCommand(cfg *Configuration, wd string, args []string, cache cac
invocation = other

// compute the finger print for the file
computedFingerPrint, err := caches.ComputeFingerPrint(invocation, wd, args)
computedFingerPrint, err := caches.ComputeFingerPrint(cfg.ClangTidyPath, invocation, wd, args)
if err != nil {
return err
}
Expand Down