Source file src/cmd/go/internal/base/path.go

     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  
     5  package base
     6  
     7  import (
     8  	"errors"
     9  	"io/fs"
    10  	"os"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strings"
    14  	"sync"
    15  )
    16  
    17  // UncachedCwd returns the current working directory.
    18  // Most callers should use Cwd, which caches the result for future use.
    19  // UncachedCwd is appropriate to call early in program startup before flag parsing,
    20  // because the -C flag may change the current directory.
    21  func UncachedCwd() string {
    22  	wd, err := os.Getwd()
    23  	if err != nil {
    24  		Fatalf("cannot determine current directory: %v", err)
    25  	}
    26  	return wd
    27  }
    28  
    29  var cwdOnce = sync.OnceValue(UncachedCwd)
    30  
    31  // Cwd returns the current working directory at the time of the first call.
    32  func Cwd() string {
    33  	return cwdOnce()
    34  }
    35  
    36  // ShortPath returns an absolute or relative name for path, whatever is shorter.
    37  // ShortPath should only be used when formatting paths for error messages.
    38  func ShortPath(path string) string {
    39  	if rel, err := filepath.Rel(Cwd(), path); err == nil && len(rel) < len(path) && sameFile(rel, path) {
    40  		return rel
    41  	}
    42  	return path
    43  }
    44  
    45  func sameFile(path1, path2 string) bool {
    46  	fi1, err1 := os.Stat(path1)
    47  	fi2, err2 := os.Stat(path2)
    48  	if err1 != nil || err2 != nil {
    49  		// If there were errors statting the files return false,
    50  		// unless both of the files don't exist.
    51  		return os.IsNotExist(err1) && os.IsNotExist(err2)
    52  	}
    53  	return os.SameFile(fi1, fi2)
    54  }
    55  
    56  // ShortPathError rewrites the path in err using base.ShortPath, if err is a wrapped PathError.
    57  func ShortPathError(err error) error {
    58  	if pe, ok := errors.AsType[*fs.PathError](err); ok {
    59  		pe.Path = ShortPath(pe.Path)
    60  	}
    61  	return err
    62  }
    63  
    64  // RelPaths returns a copy of paths with absolute paths
    65  // made relative to the current directory if they would be shorter.
    66  func RelPaths(paths []string) []string {
    67  	out := make([]string, 0, len(paths))
    68  	for _, p := range paths {
    69  		out = append(out, ShortPath(p))
    70  	}
    71  	return out
    72  }
    73  
    74  // IsTestFile reports whether the source file is a set of tests and should therefore
    75  // be excluded from coverage analysis.
    76  func IsTestFile(file string) bool {
    77  	// We don't cover tests, only the code they test.
    78  	return strings.HasSuffix(file, "_test.go")
    79  }
    80  
    81  // IsNull reports whether the path is a common name for the null device.
    82  // It returns true for /dev/null on Unix, or NUL (case-insensitive) on Windows.
    83  func IsNull(path string) bool {
    84  	if path == os.DevNull {
    85  		return true
    86  	}
    87  	if runtime.GOOS == "windows" {
    88  		if strings.EqualFold(path, "NUL") {
    89  			return true
    90  		}
    91  	}
    92  	return false
    93  }
    94  

View as plain text