Пример #1
0
func (ctx *Context) setPackage(dir, canonical, local, gopath string, status Status) *Package {
	at := 0
	vMiddle := "/" + pathos.SlashToImportPath(ctx.VendorDiscoverFolder) + "/"
	vStart := pathos.SlashToImportPath(ctx.VendorDiscoverFolder) + "/"
	switch {
	case strings.Contains(canonical, vMiddle):
		at = strings.LastIndex(canonical, vMiddle) + len(vMiddle)
	case strings.HasPrefix(canonical, vStart):
		at = strings.LastIndex(canonical, vStart) + len(vStart)
	}
	if at > 0 {
		canonical = canonical[at:]
		if status == StatusUnknown {
			status = StatusVendor
		}
	}
	if status == StatusUnknown {
		if vp := ctx.VendorFilePackageLocal(local); vp != nil {
			status = StatusVendor
			canonical = vp.Path
		}
	}
	if status == StatusUnknown && strings.HasPrefix(canonical, ctx.RootImportPath) {
		status = StatusLocal
	}
	pkg := &Package{
		Dir:       dir,
		Canonical: canonical,
		Local:     local,
		Gopath:    gopath,
		Status:    status,
	}
	ctx.Package[local] = pkg
	return pkg
}
Пример #2
0
func (ctx *Context) setPackage(dir, canonical, local, gopath string, status Status) *Package {
	at := 0
	vMiddle := "/" + pathos.SlashToImportPath(ctx.VendorDiscoverFolder) + "/"
	vStart := pathos.SlashToImportPath(ctx.VendorDiscoverFolder) + "/"
	switch {
	case strings.Contains(canonical, vMiddle):
		at = strings.LastIndex(canonical, vMiddle) + len(vMiddle)
	case strings.HasPrefix(canonical, vStart):
		at = strings.LastIndex(canonical, vStart) + len(vStart)
	}

	originDir := dir
	inVendor := false
	if at > 0 {
		canonical = canonical[at:]
		inVendor = true
		if status == StatusUnknown {
			p := path.Join(ctx.RootImportPath, ctx.VendorDiscoverFolder)
			if strings.HasPrefix(local, p) {
				status = StatusVendor
				od, _, err := ctx.findImportDir("", canonical)
				if err == nil {
					originDir = od
				}
			}
		}
	}
	if status == StatusUnknown && inVendor == false {
		if vp := ctx.VendorFilePackageLocal(local); vp != nil {
			// This will only be hit if the imported package is in the vendor
			// file, present in GOPATH, but not in vendor folder.
			status = StatusExternal
			inVendor = true
			canonical = vp.Path
			origin := vp.Origin
			if len(origin) == 0 {
				origin = canonical
			}
			od, _, err := ctx.findImportDir("", origin)
			if err == nil {
				originDir = od
			}
		}
	}
	if status == StatusUnknown && strings.HasPrefix(canonical, ctx.RootImportPath) {
		status = StatusLocal
	}
	pkg := &Package{
		OriginDir: originDir,
		Dir:       dir,
		Canonical: canonical,
		Local:     local,
		Gopath:    gopath,
		Status:    status,
		inVendor:  inVendor,
	}
	ctx.Package[local] = pkg
	return pkg
}
Пример #3
0
// findImportPath takes a absolute directory and returns the import path and go path.
func (ctx *Context) findImportPath(dir string) (importPath, gopath string, err error) {
	for _, gopath := range ctx.GopathList {
		if pathos.FileHasPrefix(dir, gopath) {
			importPath = pathos.FileTrimPrefix(dir, gopath)
			importPath = pathos.SlashToImportPath(importPath)
			return importPath, gopath, nil
		}
	}
	return "", "", ErrNotInGOPATH{dir}
}
Пример #4
0
func vendorFileFindLocal(vf *vendorfile.File, root, gopath, importPath string) *vendorfile.Package {
	local := pathos.SlashToImportPath(pathos.FileTrimPrefix(root, gopath)) // "/co1" = /file/src/co1, /file/src
	local = strings.TrimPrefix(strings.TrimPrefix(importPath, local), "/")
	for _, pkg := range vf.Package {
		if pkg.Remove {
			continue
		}
		if pkg.Local == local {
			return pkg
		}
	}
	return nil
}
Пример #5
0
func readVendorFile(vendorFilePath string) (*vendorfile.File, error) {
	vf := &vendorfile.File{}
	f, err := os.Open(vendorFilePath)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	err = vf.Unmarshal(f)
	if err != nil {
		return nil, err
	}
	// Determine if local field is relative to GOPATH or vendor file.
	// Change to relative to vendor file as needed.
	folder, _ := filepath.Split(vendorFilePath)
	relToFile := 0
	relToGOPATH := 0
	for _, pkg := range vf.Package {
		p := filepath.Join(folder, pathos.SlashToFilepath(pkg.Local))
		_, err := os.Stat(p)
		if os.IsNotExist(err) {
			relToGOPATH++
			continue
		}
		relToFile++
	}
	if relToFile > relToGOPATH || len(vf.Package) == 0 {
		return vf, nil
	}

	gopathList := strings.Split(os.Getenv("GOPATH"), string(os.PathListSeparator))
	gopath := ""
	for _, gp := range gopathList {
		if pathos.FileHasPrefix(folder, gp) {
			gopath = gp
			break
		}
	}
	if len(gopath) == 0 {
		return vf, nil
	}
	prefix := pathos.SlashToImportPath(pathos.FileTrimPrefix(folder, filepath.Join(gopath, "src")))
	prefix = strings.TrimPrefix(prefix, "/")
	for _, pkg := range vf.Package {
		pkg.Local = strings.TrimPrefix(pkg.Local, prefix)
	}

	return vf, nil
}
Пример #6
0
// addFileImports is called from loadPackage and resolveUnknown.
func (ctx *Context) addFileImports(pathname, gopath string) error {
	dir, filenameExt := filepath.Split(pathname)
	importPath := pathos.FileTrimPrefix(dir, gopath)
	importPath = pathos.SlashToImportPath(importPath)
	importPath = strings.TrimPrefix(importPath, "/")
	importPath = strings.TrimSuffix(importPath, "/")

	if strings.HasSuffix(pathname, ".go") == false {
		return nil
	}
	f, err := parser.ParseFile(token.NewFileSet(), pathname, nil, parser.ImportsOnly|parser.ParseComments)
	if err != nil {
		return err
	}

	tags, err := ctx.getFileTags(pathname, f)
	if err != nil {
		return err
	}

	pkg, found := ctx.Package[importPath]
	if !found {
		status := StatusUnknown
		if f.Name.Name == "main" {
			status = StatusProgram
		}
		pkg = ctx.setPackage(dir, importPath, importPath, gopath, status)
		ctx.Package[importPath] = pkg
	}
	if pkg.Status != StatusLocal && pkg.Status != StatusProgram {
		for _, tag := range tags {
			for _, ignore := range ctx.ignoreTag {
				if tag == ignore {
					pkg.ignoreFile = append(pkg.ignoreFile, filenameExt)
					return nil
				}
			}
		}
	}
	pf := &File{
		Package: pkg,
		Path:    pathname,
		Imports: make([]string, len(f.Imports)),
	}
	pkg.Files = append(pkg.Files, pf)
	for i := range f.Imports {
		imp := f.Imports[i].Path.Value
		imp, err = strconv.Unquote(imp)
		if err != nil {
			return err
		}
		if strings.HasPrefix(imp, "./") {
			imp = path.Join(importPath, imp)
		}
		pf.Imports[i] = imp
		err = ctx.addSingleImport(pkg.Dir, imp)
		if err != nil {
			return err
		}
	}

	// Record any import comment for file.
	var ic *ast.Comment
	if f.Name != nil {
		pos := f.Name.Pos()
	big:
		// Find the next comment after the package name.
		for _, cblock := range f.Comments {
			for _, c := range cblock.List {
				if c.Pos() > pos {
					ic = c
					break big
				}
			}
		}
	}
	if ic != nil {
		// If it starts with the import text, assume it is the import comment and remove.
		if index := strings.Index(ic.Text, " import "); index > 0 && index < 5 {
			q := strings.TrimSpace(ic.Text[index+len(" import "):])
			pf.ImportComment, err = strconv.Unquote(q)
			if err != nil {
				pf.ImportComment = q
			}
		}
	}

	return nil
}
Пример #7
0
// addFileImports is called from loadPackage and resolveUnknown.
func (ctx *Context) addFileImports(pathname, gopath string) error {
	dir, filenameExt := filepath.Split(pathname)
	importPath := pathos.FileTrimPrefix(dir, gopath)
	importPath = pathos.SlashToImportPath(importPath)
	importPath = strings.TrimPrefix(importPath, "/")
	importPath = strings.TrimSuffix(importPath, "/")

	if strings.HasSuffix(pathname, ".go") == false {
		return nil
	}
	f, err := parser.ParseFile(token.NewFileSet(), pathname, nil, parser.ImportsOnly|parser.ParseComments)
	if err != nil {
		return err
	}

	filename := filenameExt[:len(filenameExt)-3]

	filenameParts := strings.Split(filename, "_")
	tags := make([]string, 0)
	for i, part := range filenameParts {
		if i == 0 {
			continue
		}
		tags = append(tags, part)
	}
	const buildPrefix = "// +build "
	for _, cc := range f.Comments {
		for _, c := range cc.List {
			if strings.HasPrefix(c.Text, buildPrefix) {
				text := strings.TrimPrefix(c.Text, buildPrefix)
				ss := strings.Fields(text)
				for _, s := range ss {
					tags = append(tags, strings.Split(s, ",")...)
				}
			}
		}
	}

	pkg, found := ctx.Package[importPath]
	if !found {
		status := StatusUnknown
		if f.Name.Name == "main" {
			status = StatusProgram
		}
		pkg = ctx.setPackage(dir, importPath, importPath, gopath, status)
		ctx.Package[importPath] = pkg
	}
	for _, tag := range tags {
		for _, ignore := range ctx.ignoreTag {
			if tag == ignore {
				pkg.ignoreFile = append(pkg.ignoreFile, filenameExt)
				return nil
			}
		}
	}
	pf := &File{
		Package: pkg,
		Path:    pathname,
		Imports: make([]string, len(f.Imports)),
	}
	pkg.Files = append(pkg.Files, pf)
	for i := range f.Imports {
		imp := f.Imports[i].Path.Value
		imp, err = strconv.Unquote(imp)
		if err != nil {
			return err
		}
		if strings.HasPrefix(imp, "./") {
			imp = path.Join(importPath, imp)
		}
		pf.Imports[i] = imp
		err = ctx.addSingleImport(pkg.Dir, imp)
		if err != nil {
			return err
		}
	}

	return nil
}
Пример #8
0
// AddImport adds the package to the context. The vendorFolder is where the
// package should be added to relative to the project root.
func (ctx *Context) ModifyImport(sourcePath string, mod Modify) error {
	var err error
	if !ctx.loaded || ctx.dirty {
		err = ctx.loadPackage()
		if err != nil {
			return err
		}
	}
	// Determine canonical and local import paths.
	sourcePath = pathos.SlashToImportPath(sourcePath)
	canonicalImportPath, err := ctx.findCanonicalPath(sourcePath)
	if err != nil {
		if mod != Remove {
			return err
		}
		if _, is := err.(ErrNotInGOPATH); !is {
			return err
		}
	}
	// If the import is already vendored, ensure we have the local path and not
	// the canonical path.
	localImportPath := sourcePath
	if vendPkg := ctx.VendorFilePackagePath(localImportPath); vendPkg != nil {
		localImportPath = path.Join(ctx.RootImportPath, ctx.RootToVendorFile, vendPkg.Path)
	}

	dprintf("AI: %s, L: %s, C: %s\n", sourcePath, localImportPath, canonicalImportPath)

	// Does the local import exist?
	//   If so either update or just return.
	//   If not find the disk path from the canonical path, copy locally and rewrite (if needed).
	pkg, foundPkg := ctx.Package[localImportPath]
	if !foundPkg {
		err = ctx.addSingleImport("", canonicalImportPath)
		if err != nil {
			return err
		}
		pkg, foundPkg = ctx.Package[canonicalImportPath]
		// Find by canonical path if stored by different local path.
		if !foundPkg {
			for _, p := range ctx.Package {
				if canonicalImportPath == p.Canonical {
					foundPkg = true
					pkg = p
					break
				}
			}
		}
		if !foundPkg {
			panic(fmt.Sprintf("Package %q should be listed internally but is not.", canonicalImportPath))
		}
	}

	localExists, err := hasGoFileInFolder(filepath.Join(ctx.RootDir, ctx.VendorFolder, pathos.SlashToFilepath(canonicalImportPath)))
	if err != nil {
		return err
	}
	if mod == Add && localExists {
		return ErrPackageExists{path.Join(ctx.RootImportPath, ctx.VendorFolder, canonicalImportPath)}
	}
	switch mod {
	case Add:
		return ctx.modifyAdd(pkg)
	case AddUpdate:
		return ctx.modifyAdd(pkg)
	case Update:
		return ctx.modifyAdd(pkg)
	case Remove:
		return ctx.modifyRemove(pkg)
	default:
		panic("mod switch: case not handled")
	}
}
Пример #9
0
// NewContext creates new context from a given root folder and vendor file path.
// The vendorFolder is where vendor packages should be placed.
func NewContext(root, vendorFilePathRel, vendorFolder string, rewriteImports bool) (*Context, error) {
	dprintf("CTX: %s\n", root)
	vendorFilePath := filepath.Join(root, vendorFilePathRel)
	vf, err := readVendorFile(vendorFilePath)
	if err != nil {
		if !os.IsNotExist(err) {
			return nil, err
		}
		vf = &vendorfile.File{}
	}

	// Get GOROOT. First check ENV, then run "go env" and find the GOROOT line.
	goroot := os.Getenv("GOROOT")
	if len(goroot) == 0 {
		// If GOROOT is not set, get from go cmd.
		cmd := exec.Command("go", "env")
		var goEnv []byte
		goEnv, err = cmd.CombinedOutput()
		if err != nil {
			return nil, err
		}
		const gorootLookFor = `GOROOT=`
		for _, line := range strings.Split(string(goEnv), "\n") {
			if strings.HasPrefix(line, gorootLookFor) == false {
				continue
			}
			goroot = strings.TrimPrefix(line, gorootLookFor)
			goroot, err = strconv.Unquote(goroot)
			if err != nil {
				return nil, err
			}
			break
		}
	}
	if goroot == "" {
		return nil, ErrMissingGOROOT
	}
	goroot = filepath.Join(goroot, "src")

	// Get the GOPATHs. Prepend the GOROOT to the list.
	all := os.Getenv("GOPATH")
	if len(all) == 0 {
		return nil, ErrMissingGOPATH
	}
	gopathList := filepath.SplitList(all)
	gopathGoroot := make([]string, 0, len(gopathList)+1)
	gopathGoroot = append(gopathGoroot, goroot)
	for _, gopath := range gopathList {
		gopathGoroot = append(gopathGoroot, filepath.Join(gopath, "src")+string(filepath.Separator))
	}

	rootToVendorFile, _ := filepath.Split(vendorFilePathRel)

	vendorFileDir, _ := filepath.Split(vendorFilePath)
	vendorFolderRel, err := filepath.Rel(vendorFileDir, filepath.Join(root, vendorFolder))
	if err != nil {
		return nil, err
	}
	vendorFileToFolder := pathos.SlashToImportPath(vendorFolderRel)

	ctx := &Context{
		RootDir:    root,
		GopathList: gopathGoroot,
		Goroot:     goroot,

		VendorFile:         vf,
		VendorFilePath:     vendorFilePath,
		VendorFolder:       vendorFolder,
		VendorFileToFolder: vendorFileToFolder,
		RootToVendorFile:   pathos.SlashToImportPath(rootToVendorFile),

		VendorDiscoverFolder: "vendor",

		Package: make(map[string]*Package),

		RewriteRule: make(map[string]string, 3),

		rewriteImports: rewriteImports,
	}

	ctx.RootImportPath, ctx.RootGopath, err = ctx.findImportPath(root)
	if err != nil {
		return nil, err
	}

	ctx.IgnoreBuild(vf.Ignore)

	return ctx, nil
}
Пример #10
0
// AddImport adds the package to the context. The vendorFolder is where the
// package should be added to relative to the project root.
func (ctx *Context) ModifyImport(sourcePath string, mod Modify) error {
	var err error
	if !ctx.loaded || ctx.dirty {
		err = ctx.loadPackage()
		if err != nil {
			return err
		}
	}
	tree := strings.HasSuffix(sourcePath, TreeSuffix)
	sourcePath = strings.TrimSuffix(sourcePath, TreeSuffix)

	// Determine canonical and local import paths.
	sourcePath = pathos.SlashToImportPath(sourcePath)
	canonicalImportPath, err := ctx.findCanonicalPath(sourcePath)
	if err != nil {
		if mod != Remove {
			return err
		}
		if _, is := err.(ErrNotInGOPATH); !is {
			return err
		}
	}
	// If the import is already vendored, ensure we have the local path and not
	// the canonical path.
	localImportPath := sourcePath
	if vendPkg := ctx.VendorFilePackagePath(localImportPath); vendPkg != nil {
		localImportPath = path.Join(ctx.RootImportPath, ctx.RootToVendorFile, vendPkg.Path)
	}

	dprintf("AI: %s, L: %s, C: %s\n", sourcePath, localImportPath, canonicalImportPath)

	// Does the local import exist?
	//   If so either update or just return.
	//   If not find the disk path from the canonical path, copy locally and rewrite (if needed).
	pkg, foundPkg := ctx.Package[localImportPath]
	if !foundPkg {
		err = ctx.addSingleImport("", canonicalImportPath)
		if err != nil {
			return err
		}
		pkg, foundPkg = ctx.Package[canonicalImportPath]
		// Find by canonical path if stored by different local path.
		if !foundPkg {
			for _, p := range ctx.Package {
				if canonicalImportPath == p.Canonical {
					foundPkg = true
					pkg = p
					break
				}
			}
		}
		if !foundPkg {
			panic(fmt.Sprintf("Package %q should be listed internally but is not.", canonicalImportPath))
		}
	}

	// Do not support setting "tree" on Remove.
	if tree && mod != Remove {
		pkg.Tree = true
	}

	// A restriction where packages cannot live inside a tree package.
	if mod != Remove {
		if pkg.Tree {
			children := ctx.findPackageChild(pkg)
			if len(children) > 0 {
				return ErrTreeChildren{path: pkg.Canonical, children: children}
			}
		}
		treeParents := ctx.findPackageParentTree(pkg)
		if len(treeParents) > 0 {
			return ErrTreeParents{path: pkg.Canonical, parents: treeParents}
		}
	}

	// TODO (DT): figure out how to upgrade a non-tree package to a tree package with correct checks.
	localExists, err := hasGoFileInFolder(filepath.Join(ctx.RootDir, ctx.VendorFolder, pathos.SlashToFilepath(canonicalImportPath)))
	if err != nil {
		return err
	}
	if mod == Add && localExists {
		return ErrPackageExists{path.Join(ctx.RootImportPath, ctx.VendorFolder, canonicalImportPath)}
	}
	dprintf("stage 2: begin!\n")
	switch mod {
	case Add:
		return ctx.modifyAdd(pkg)
	case AddUpdate:
		return ctx.modifyAdd(pkg)
	case Update:
		return ctx.modifyAdd(pkg)
	case Remove:
		return ctx.modifyRemove(pkg)
	case Fetch:
		return ctx.modifyFetch(pkg)
	default:
		panic("mod switch: case not handled")
	}
}
Пример #11
0
// addFileImports is called from loadPackage and resolveUnknown.
func (ctx *Context) addFileImports(pathname, gopath string) error {
	dir, filenameExt := filepath.Split(pathname)
	importPath := pathos.FileTrimPrefix(dir, gopath)
	importPath = pathos.SlashToImportPath(importPath)
	importPath = strings.TrimPrefix(importPath, "/")
	importPath = strings.TrimSuffix(importPath, "/")

	if strings.HasSuffix(pathname, ".go") == false {
		return nil
	}
	f, err := parser.ParseFile(token.NewFileSet(), pathname, nil, parser.ImportsOnly|parser.ParseComments)
	if err != nil {
		return err
	}

	tags, err := ctx.getFileTags(pathname, f)
	if err != nil {
		return err
	}

	pkg, found := ctx.Package[importPath]
	if !found {
		status := StatusUnknown
		if f.Name.Name == "main" {
			status = StatusProgram
		}
		pkg = ctx.setPackage(dir, importPath, importPath, gopath, status)
		ctx.Package[importPath] = pkg
	}
	if pkg.Status != StatusLocal && pkg.Status != StatusProgram {
		for _, tag := range tags {
			for _, ignore := range ctx.ignoreTag {
				if tag == ignore {
					pkg.ignoreFile = append(pkg.ignoreFile, filenameExt)
					return nil
				}
			}
		}
	}
	pf := &File{
		Package: pkg,
		Path:    pathname,
		Imports: make([]string, len(f.Imports)),
	}
	pkg.Files = append(pkg.Files, pf)
	for i := range f.Imports {
		imp := f.Imports[i].Path.Value
		imp, err = strconv.Unquote(imp)
		if err != nil {
			return err
		}
		if strings.HasPrefix(imp, "./") {
			imp = path.Join(importPath, imp)
		}
		pf.Imports[i] = imp
		err = ctx.addSingleImport(pkg.Dir, imp)
		if err != nil {
			return err
		}
	}

	return nil
}