github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/dist/imports.go (about) 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 dist 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 }