contextutil

package
v0.0.15 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 9, 2022 License: BSD-3-Clause, MIT Imports: 11 Imported by: 0

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultProjectTombstones = []string{
	".git",
	"go.mod",
	"go.work",
	"glide.yaml",
	"Gopkg.toml",
}

DefaultProjectTombstones are the files used by FindProjectRoot to determine the root of a project.

Functions

func ContainingDirectory

func ContainingDirectory(ctxt *build.Context, child, stopAt string, tombstones ...string) (string, error)

ContainingDirectory finds the parent directory of child containing an entry named by tombstones. The child directory must be absolute.

The stopAt argument is optional and is used to abort the search early. If specified it must be an absolute path and a parent of the child directory.

// This should return ctxt.GOPATH+"/src/github.com/charlievieth/buildutil"
// since it contains a "go.mod" file.
ContainingDirectory(
	ctxt,
	ctxt.GOPATH+"/src",
	ctxt.GOPATH+"/src/github.com/charlievieth/buildutil/contextutil",
	"go.mod",
)

func FindProjectRoot

func FindProjectRoot(ctxt *build.Context, path string, extra ...string) (string, error)

FindProjectRoot finds the root directory of the project containing path, which can be a file or a directory. The project root is determined to be the directory containing any entry with a name in DefaultProjectTombstones. The extra argument specifies additional tombstone names (".svn").

The build.Context is used for file system access and to limit the search space if path is a child of GOROOT or GOPATH. Otherwise, all parent directories of path are searched.

If path is not absolute it is joined with build.Context.Dir (if set) or the current working directory.

os.ErrNotExist is returned if the project directory was not found.

func HasSubdir

func HasSubdir(ctxt *build.Context, root, dir string) (rel string, ok bool)

HasSubdir calls ctxt.HasSubdir (if not nil) or else uses the local file system to answer the question. It is significantly faster than the default build.Context.hasSubdir() method as it tries to avoid calls to filepath.EvalSymlinks.

func HasSubdirFunc added in v0.0.15

func HasSubdirFunc(ctxt *build.Context) func(root, dir string) (rel string, ok bool)

HasSubdirFunc returns a function that can be used for build.Context.HasSubdir field and is significantly faster than the default implementation.

func ScopedContext

func ScopedContext(orig *build.Context, pkgdirs ...string) (*build.Context, error)

ScopedContext returns a build.Context with a ReadDir that is scoped to the directories listed by pkgdirs and the GOROOT. That is, ReadDir when called with an ancestor of pkgdirs will only return immediate ancestors (that lead to the pkgdirs). When called with any of the pkgdirs, GOROOT, or any of their children all results are returned (same as ioutil.ReadDir).

A scoped context is designed to limit the search scope of tools that walk the entire GOPATH (e.g. "golang.org/x/tools/refactor/rename"), which can greatly speed up processing time.

// In the below example we limit the search path to "/go/src/pkg/buildutil".
ctxt, _ := ScopedContext(&build.Default, "/go/src/pkg/buildutil")
ctxt.ReadDir("/go")                               // => ["src"]
ctxt.ReadDir("/go/src")                           // => ["pkg"]
ctxt.ReadDir("/go/src/pkg")                       // => ["buildutil"]
ctxt.ReadDir("/go/src/pkg/buildutil")             // => [ALL ENTRIES]
ctxt.ReadDir("/go/src/pkg/buildutil/contextutil") // => [ALL ENTRIES]
Example
package main

import (
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/charlievieth/buildutil/contextutil"
)

func printReadDir(ctxt *build.Context, path string) {
	// Remove leading temp directory
	name := strings.TrimPrefix(filepath.ToSlash(path),
		filepath.ToSlash(ctxt.GOPATH)+"/")

	fmt.Printf("ReadDir(%q)\n", name)
	fis, err := ctxt.ReadDir(path)
	if err != nil {
		if !os.IsNotExist(err) {
			panic(err)
		}
		fmt.Printf("  open %s: %s\n", name, os.ErrNotExist)
		return
	}
	for _, fi := range fis {
		if fi.IsDir() {
			fmt.Printf("  %s/\n", fi.Name())
		} else {
			fmt.Printf("  %s\n", fi.Name())
		}
	}
}

func main() {
	// Create a fake GOPATH and populate it with some files
	//
	// 	src
	// 	└── p
	// 	    ├── p1
	// 	    │   ├── c1
	// 	    │   │    ├── fc1.go
	// 	    │   │    └── fc2.go
	// 	    │   ├── f1.go
	// 	    │   └── f2.go
	// 	    └── p2
	// 	        └── nope.go
	//
	gopath, err := ioutil.TempDir("", "contextutil.*")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(gopath)

	pkg1 := filepath.Join(gopath, "src/p/p1")
	sub1 := filepath.Join(gopath, "src/p/p1/c1") // subdirectory of pkg1
	pkg2 := filepath.Join(gopath, "src/p/p2")
	for _, name := range []string{
		filepath.Join(pkg1, "f1.go"),
		filepath.Join(pkg1, "f2.go"),
		filepath.Join(sub1, "fc1.go"),
		filepath.Join(sub1, "fc2.go"),
		filepath.Join(pkg2, "nope.go"),
	} {
		if err := os.MkdirAll(filepath.Dir(name), 0755); err != nil {
			panic(err)
		}
		err := os.WriteFile(name, []byte(name), 0644)
		if err != nil {
			panic(err)
		}
	}

	// Scope context to path pkg1 ("$GOPATH/src/p/p1")
	orig := build.Default
	orig.GOPATH = gopath
	ctxt, err := contextutil.ScopedContext(&orig, pkg1)
	if err != nil {
		panic(err)
	}

	// Reading $GOPATH/src/p will only return "p1" since
	// that is what we scoped the Context to.
	printReadDir(ctxt, filepath.Dir(pkg1))

	// Reading p1 or any of it's subdirectories returns
	// all results (same as ioutil.ReadDir())
	printReadDir(ctxt, pkg1)
	printReadDir(ctxt, sub1)

	// Reading a directory outside of the scope returns no results.
	printReadDir(ctxt, pkg2)

}
Output:

ReadDir("src/p")
  p1/
ReadDir("src/p/p1")
  c1/
  f1.go
  f2.go
ReadDir("src/p/p1/c1")
  fc1.go
  fc2.go
ReadDir("src/p/p2")
  open src/p/p2: file does not exist

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL