Source file src/cmd/dist/imports.go

     1  // Copyright 2012 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  // This file is forked from go/build/read.go.
     6  // (cmd/dist must not import go/build because we do not want it to be
     7  // sensitive to the specific version of go/build present in $GOROOT_BOOTSTRAP.)
     8  
     9  package main
    10  
    11  import (
    12  	"bufio"
    13  	"errors"
    14  	"fmt"
    15  	"io"
    16  	"path"
    17  	"path/filepath"
    18  	"strconv"
    19  	"strings"
    20  	"unicode/utf8"
    21  )
    22  
    23  type importReader struct {
    24  	b    *bufio.Reader
    25  	buf  []byte
    26  	peek byte
    27  	err  error
    28  	eof  bool
    29  	nerr int
    30  }
    31  
    32  func isIdent(c byte) bool {
    33  	return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf
    34  }
    35  
    36  var (
    37  	errSyntax = errors.New("syntax error")
    38  	errNUL    = errors.New("unexpected NUL in input")
    39  )
    40  
    41  // syntaxError records a syntax error, but only if an I/O error has not already been recorded.
    42  func (r *importReader) syntaxError() {
    43  	if r.err == nil {
    44  		r.err = errSyntax
    45  	}
    46  }
    47  
    48  // readByte reads the next byte from the input, saves it in buf, and returns it.
    49  // If an error occurs, readByte records the error in r.err and returns 0.
    50  func (r *importReader) readByte() byte {
    51  	c, err := r.b.ReadByte()
    52  	if err == nil {
    53  		r.buf = append(r.buf, c)
    54  		if c == 0 {
    55  			err = errNUL
    56  		}
    57  	}
    58  	if err != nil {
    59  		if err == io.EOF {
    60  			r.eof = true
    61  		} else if r.err == nil {
    62  			r.err = err
    63  		}
    64  		c = 0
    65  	}
    66  	return c
    67  }
    68  
    69  // peekByte returns the next byte from the input reader but does not advance beyond it.
    70  // If skipSpace is set, peekByte skips leading spaces and comments.
    71  func (r *importReader) peekByte(skipSpace bool) byte {
    72  	if r.err != nil {
    73  		if r.nerr++; r.nerr > 10000 {
    74  			panic("go/build: import reader looping")
    75  		}
    76  		return 0
    77  	}
    78  
    79  	// Use r.peek as first input byte.
    80  	// Don't just return r.peek here: it might have been left by peekByte(false)
    81  	// and this might be peekByte(true).
    82  	c := r.peek
    83  	if c == 0 {
    84  		c = r.readByte()
    85  	}
    86  	for r.err == nil && !r.eof {
    87  		if skipSpace {
    88  			// For the purposes of this reader, semicolons are never necessary to
    89  			// understand the input and are treated as spaces.
    90  			switch c {
    91  			case ' ', '\f', '\t', '\r', '\n', ';':
    92  				c = r.readByte()
    93  				continue
    94  
    95  			case '/':
    96  				c = r.readByte()
    97  				if c == '/' {
    98  					for c != '\n' && r.err == nil && !r.eof {
    99  						c = r.readByte()
   100  					}
   101  				} else if c == '*' {
   102  					var c1 byte
   103  					for (c != '*' || c1 != '/') && r.err == nil {
   104  						if r.eof {
   105  							r.syntaxError()
   106  						}
   107  						c, c1 = c1, r.readByte()
   108  					}
   109  				} else {
   110  					r.syntaxError()
   111  				}
   112  				c = r.readByte()
   113  				continue
   114  			}
   115  		}
   116  		break
   117  	}
   118  	r.peek = c
   119  	return r.peek
   120  }
   121  
   122  // nextByte is like peekByte but advances beyond the returned byte.
   123  func (r *importReader) nextByte(skipSpace bool) byte {
   124  	c := r.peekByte(skipSpace)
   125  	r.peek = 0
   126  	return c
   127  }
   128  
   129  // readKeyword reads the given keyword from the input.
   130  // If the keyword is not present, readKeyword records a syntax error.
   131  func (r *importReader) readKeyword(kw string) {
   132  	r.peekByte(true)
   133  	for i := 0; i < len(kw); i++ {
   134  		if r.nextByte(false) != kw[i] {
   135  			r.syntaxError()
   136  			return
   137  		}
   138  	}
   139  	if isIdent(r.peekByte(false)) {
   140  		r.syntaxError()
   141  	}
   142  }
   143  
   144  // readIdent reads an identifier from the input.
   145  // If an identifier is not present, readIdent records a syntax error.
   146  func (r *importReader) readIdent() {
   147  	c := r.peekByte(true)
   148  	if !isIdent(c) {
   149  		r.syntaxError()
   150  		return
   151  	}
   152  	for isIdent(r.peekByte(false)) {
   153  		r.peek = 0
   154  	}
   155  }
   156  
   157  // readString reads a quoted string literal from the input.
   158  // If an identifier is not present, readString records a syntax error.
   159  func (r *importReader) readString(save *[]string) {
   160  	switch r.nextByte(true) {
   161  	case '`':
   162  		start := len(r.buf) - 1
   163  		for r.err == nil {
   164  			if r.nextByte(false) == '`' {
   165  				if save != nil {
   166  					*save = append(*save, string(r.buf[start:]))
   167  				}
   168  				break
   169  			}
   170  			if r.eof {
   171  				r.syntaxError()
   172  			}
   173  		}
   174  	case '"':
   175  		start := len(r.buf) - 1
   176  		for r.err == nil {
   177  			c := r.nextByte(false)
   178  			if c == '"' {
   179  				if save != nil {
   180  					*save = append(*save, string(r.buf[start:]))
   181  				}
   182  				break
   183  			}
   184  			if r.eof || c == '\n' {
   185  				r.syntaxError()
   186  			}
   187  			if c == '\\' {
   188  				r.nextByte(false)
   189  			}
   190  		}
   191  	default:
   192  		r.syntaxError()
   193  	}
   194  }
   195  
   196  // readImport reads an import clause - optional identifier followed by quoted string -
   197  // from the input.
   198  func (r *importReader) readImport(imports *[]string) {
   199  	c := r.peekByte(true)
   200  	if c == '.' {
   201  		r.peek = 0
   202  	} else if isIdent(c) {
   203  		r.readIdent()
   204  	}
   205  	r.readString(imports)
   206  }
   207  
   208  // readComments is like ioutil.ReadAll, except that it only reads the leading
   209  // block of comments in the file.
   210  func readComments(f io.Reader) ([]byte, error) {
   211  	r := &importReader{b: bufio.NewReader(f)}
   212  	r.peekByte(true)
   213  	if r.err == nil && !r.eof {
   214  		// Didn't reach EOF, so must have found a non-space byte. Remove it.
   215  		r.buf = r.buf[:len(r.buf)-1]
   216  	}
   217  	return r.buf, r.err
   218  }
   219  
   220  // readimports returns the imports found in the named file.
   221  func readimports(file string) []string {
   222  	var imports []string
   223  	r := &importReader{b: bufio.NewReader(strings.NewReader(readfile(file)))}
   224  	r.readKeyword("package")
   225  	r.readIdent()
   226  	for r.peekByte(true) == 'i' {
   227  		r.readKeyword("import")
   228  		if r.peekByte(true) == '(' {
   229  			r.nextByte(false)
   230  			for r.peekByte(true) != ')' && r.err == nil {
   231  				r.readImport(&imports)
   232  			}
   233  			r.nextByte(false)
   234  		} else {
   235  			r.readImport(&imports)
   236  		}
   237  	}
   238  
   239  	for i := range imports {
   240  		unquoted, err := strconv.Unquote(imports[i])
   241  		if err != nil {
   242  			fatalf("reading imports from %s: %v", file, err)
   243  		}
   244  		imports[i] = unquoted
   245  	}
   246  
   247  	return imports
   248  }
   249  
   250  // resolveVendor returns a unique package path imported with the given import
   251  // path from srcDir.
   252  //
   253  // resolveVendor assumes that a package is vendored if and only if its first
   254  // path component contains a dot. If a package is vendored, its import path
   255  // is returned with a "vendor" or "cmd/vendor" prefix, depending on srcDir.
   256  // Otherwise, the import path is returned verbatim.
   257  func resolveVendor(imp, srcDir string) string {
   258  	var first string
   259  	if i := strings.Index(imp, "/"); i < 0 {
   260  		first = imp
   261  	} else {
   262  		first = imp[:i]
   263  	}
   264  	isStandard := !strings.Contains(first, ".")
   265  	if isStandard {
   266  		return imp
   267  	}
   268  
   269  	if strings.HasPrefix(srcDir, filepath.Join(goroot, "src", "cmd")) {
   270  		return path.Join("cmd", "vendor", imp)
   271  	} else if strings.HasPrefix(srcDir, filepath.Join(goroot, "src")) {
   272  		return path.Join("vendor", imp)
   273  	} else {
   274  		panic(fmt.Sprintf("srcDir %q not in GOOROT/src", srcDir))
   275  	}
   276  }
   277  

View as plain text