summaryrefslogtreecommitdiffstats
path: root/src/cmd/dist/imports.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/dist/imports.go')
-rw-r--r--src/cmd/dist/imports.go276
1 files changed, 276 insertions, 0 deletions
diff --git a/src/cmd/dist/imports.go b/src/cmd/dist/imports.go
new file mode 100644
index 0000000..05dd84d
--- /dev/null
+++ b/src/cmd/dist/imports.go
@@ -0,0 +1,276 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file is forked from go/build/read.go.
+// (cmd/dist must not import go/build because we do not want it to be
+// sensitive to the specific version of go/build present in $GOROOT_BOOTSTRAP.)
+
+package main
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+ "path"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "unicode/utf8"
+)
+
+type importReader struct {
+ b *bufio.Reader
+ buf []byte
+ peek byte
+ err error
+ eof bool
+ nerr int
+}
+
+func isIdent(c byte) bool {
+ return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf
+}
+
+var (
+ errSyntax = errors.New("syntax error")
+ errNUL = errors.New("unexpected NUL in input")
+)
+
+// syntaxError records a syntax error, but only if an I/O error has not already been recorded.
+func (r *importReader) syntaxError() {
+ if r.err == nil {
+ r.err = errSyntax
+ }
+}
+
+// readByte reads the next byte from the input, saves it in buf, and returns it.
+// If an error occurs, readByte records the error in r.err and returns 0.
+func (r *importReader) readByte() byte {
+ c, err := r.b.ReadByte()
+ if err == nil {
+ r.buf = append(r.buf, c)
+ if c == 0 {
+ err = errNUL
+ }
+ }
+ if err != nil {
+ if err == io.EOF {
+ r.eof = true
+ } else if r.err == nil {
+ r.err = err
+ }
+ c = 0
+ }
+ return c
+}
+
+// peekByte returns the next byte from the input reader but does not advance beyond it.
+// If skipSpace is set, peekByte skips leading spaces and comments.
+func (r *importReader) peekByte(skipSpace bool) byte {
+ if r.err != nil {
+ if r.nerr++; r.nerr > 10000 {
+ panic("go/build: import reader looping")
+ }
+ return 0
+ }
+
+ // Use r.peek as first input byte.
+ // Don't just return r.peek here: it might have been left by peekByte(false)
+ // and this might be peekByte(true).
+ c := r.peek
+ if c == 0 {
+ c = r.readByte()
+ }
+ for r.err == nil && !r.eof {
+ if skipSpace {
+ // For the purposes of this reader, semicolons are never necessary to
+ // understand the input and are treated as spaces.
+ switch c {
+ case ' ', '\f', '\t', '\r', '\n', ';':
+ c = r.readByte()
+ continue
+
+ case '/':
+ c = r.readByte()
+ if c == '/' {
+ for c != '\n' && r.err == nil && !r.eof {
+ c = r.readByte()
+ }
+ } else if c == '*' {
+ var c1 byte
+ for (c != '*' || c1 != '/') && r.err == nil {
+ if r.eof {
+ r.syntaxError()
+ }
+ c, c1 = c1, r.readByte()
+ }
+ } else {
+ r.syntaxError()
+ }
+ c = r.readByte()
+ continue
+ }
+ }
+ break
+ }
+ r.peek = c
+ return r.peek
+}
+
+// nextByte is like peekByte but advances beyond the returned byte.
+func (r *importReader) nextByte(skipSpace bool) byte {
+ c := r.peekByte(skipSpace)
+ r.peek = 0
+ return c
+}
+
+// readKeyword reads the given keyword from the input.
+// If the keyword is not present, readKeyword records a syntax error.
+func (r *importReader) readKeyword(kw string) {
+ r.peekByte(true)
+ for i := 0; i < len(kw); i++ {
+ if r.nextByte(false) != kw[i] {
+ r.syntaxError()
+ return
+ }
+ }
+ if isIdent(r.peekByte(false)) {
+ r.syntaxError()
+ }
+}
+
+// readIdent reads an identifier from the input.
+// If an identifier is not present, readIdent records a syntax error.
+func (r *importReader) readIdent() {
+ c := r.peekByte(true)
+ if !isIdent(c) {
+ r.syntaxError()
+ return
+ }
+ for isIdent(r.peekByte(false)) {
+ r.peek = 0
+ }
+}
+
+// readString reads a quoted string literal from the input.
+// If an identifier is not present, readString records a syntax error.
+func (r *importReader) readString(save *[]string) {
+ switch r.nextByte(true) {
+ case '`':
+ start := len(r.buf) - 1
+ for r.err == nil {
+ if r.nextByte(false) == '`' {
+ if save != nil {
+ *save = append(*save, string(r.buf[start:]))
+ }
+ break
+ }
+ if r.eof {
+ r.syntaxError()
+ }
+ }
+ case '"':
+ start := len(r.buf) - 1
+ for r.err == nil {
+ c := r.nextByte(false)
+ if c == '"' {
+ if save != nil {
+ *save = append(*save, string(r.buf[start:]))
+ }
+ break
+ }
+ if r.eof || c == '\n' {
+ r.syntaxError()
+ }
+ if c == '\\' {
+ r.nextByte(false)
+ }
+ }
+ default:
+ r.syntaxError()
+ }
+}
+
+// readImport reads an import clause - optional identifier followed by quoted string -
+// from the input.
+func (r *importReader) readImport(imports *[]string) {
+ c := r.peekByte(true)
+ if c == '.' {
+ r.peek = 0
+ } else if isIdent(c) {
+ r.readIdent()
+ }
+ r.readString(imports)
+}
+
+// readComments is like ioutil.ReadAll, except that it only reads the leading
+// block of comments in the file.
+func readComments(f io.Reader) ([]byte, error) {
+ r := &importReader{b: bufio.NewReader(f)}
+ r.peekByte(true)
+ if r.err == nil && !r.eof {
+ // Didn't reach EOF, so must have found a non-space byte. Remove it.
+ r.buf = r.buf[:len(r.buf)-1]
+ }
+ return r.buf, r.err
+}
+
+// readimports returns the imports found in the named file.
+func readimports(file string) []string {
+ var imports []string
+ r := &importReader{b: bufio.NewReader(strings.NewReader(readfile(file)))}
+ r.readKeyword("package")
+ r.readIdent()
+ for r.peekByte(true) == 'i' {
+ r.readKeyword("import")
+ if r.peekByte(true) == '(' {
+ r.nextByte(false)
+ for r.peekByte(true) != ')' && r.err == nil {
+ r.readImport(&imports)
+ }
+ r.nextByte(false)
+ } else {
+ r.readImport(&imports)
+ }
+ }
+
+ for i := range imports {
+ unquoted, err := strconv.Unquote(imports[i])
+ if err != nil {
+ fatalf("reading imports from %s: %v", file, err)
+ }
+ imports[i] = unquoted
+ }
+
+ return imports
+}
+
+// resolveVendor returns a unique package path imported with the given import
+// path from srcDir.
+//
+// resolveVendor assumes that a package is vendored if and only if its first
+// path component contains a dot. If a package is vendored, its import path
+// is returned with a "vendor" or "cmd/vendor" prefix, depending on srcDir.
+// Otherwise, the import path is returned verbatim.
+func resolveVendor(imp, srcDir string) string {
+ var first string
+ if i := strings.Index(imp, "/"); i < 0 {
+ first = imp
+ } else {
+ first = imp[:i]
+ }
+ isStandard := !strings.Contains(first, ".")
+ if isStandard {
+ return imp
+ }
+
+ if strings.HasPrefix(srcDir, filepath.Join(goroot, "src", "cmd")) {
+ return path.Join("cmd", "vendor", imp)
+ } else if strings.HasPrefix(srcDir, filepath.Join(goroot, "src")) {
+ return path.Join("vendor", imp)
+ } else {
+ panic(fmt.Sprintf("srcDir %q not in GOOROT/src", srcDir))
+ }
+}