package interp
import (
"go/ast"
"go/build"
"go/parser"
"path"
"path/filepath"
"strconv"
"strings"
)
func (interp *Interpreter ) buildOk (ctx *build .Context , name , src string ) (bool , error ) {
f , err := parser .ParseFile (interp .fset , name , src , parser .PackageClauseOnly |parser .ParseComments )
if err != nil {
return false , err
}
for _ , g := range f .Comments {
for _ , line := range strings .Split (strings .TrimSpace (g .Text ()), "\n" ) {
if !buildLineOk (ctx , line ) {
return false , nil
}
}
}
setYaegiTags (ctx , f .Comments )
return true , nil
}
func buildLineOk(ctx *build .Context , line string ) (ok bool ) {
if len (line ) < 7 || line [:7 ] != "+build " {
return true
}
options := strings .Split (strings .TrimSpace (line [6 :]), " " )
for _ , o := range options {
if ok = buildOptionOk (ctx , o ); ok {
break
}
}
return ok
}
func buildOptionOk(ctx *build .Context , tag string ) bool {
for _ , t := range strings .Split (tag , "," ) {
if !buildTagOk (ctx , t ) {
return false
}
}
return true
}
func buildTagOk(ctx *build .Context , s string ) (r bool ) {
not := s [0 ] == '!'
if not {
s = s [1 :]
}
switch {
case contains (ctx .BuildTags , s ):
r = true
case s == ctx .GOOS :
r = true
case s == ctx .GOARCH :
r = true
case len (s ) > 4 && s [:4 ] == "go1." :
if n , err := strconv .Atoi (s [4 :]); err != nil {
r = false
} else {
r = goMinorVersion (ctx ) >= n
}
}
if not {
r = !r
}
return
}
func setYaegiTags(ctx *build .Context , comments []*ast .CommentGroup ) {
for _ , g := range comments {
for _ , line := range strings .Split (strings .TrimSpace (g .Text ()), "\n" ) {
if len (line ) < 11 || line [:11 ] != "yaegi:tags " {
continue
}
tags := strings .Split (strings .TrimSpace (line [10 :]), " " )
for _ , tag := range tags {
if !contains (ctx .BuildTags , tag ) {
ctx .BuildTags = append (ctx .BuildTags , tag )
}
}
}
}
}
func contains(tags []string , tag string ) bool {
for _ , t := range tags {
if t == tag {
return true
}
}
return false
}
func goMinorVersion(ctx *build .Context ) int {
current := ctx .ReleaseTags [len (ctx .ReleaseTags )-1 ]
v := strings .Split (current , "." )
if len (v ) < 2 {
panic ("unsupported Go version: " + current )
}
m , err := strconv .Atoi (v [1 ])
if err != nil {
panic ("unsupported Go version: " + current )
}
return m
}
func skipFile(ctx *build .Context , p string , skipTest bool ) bool {
if !strings .HasSuffix (p , ".go" ) {
return true
}
p = strings .TrimSuffix (path .Base (p ), ".go" )
if pp := filepath .Base (p ); strings .HasPrefix (pp , "_" ) || strings .HasPrefix (pp , "." ) {
return true
}
if skipTest && strings .HasSuffix (p , "_test" ) {
return true
}
i := strings .Index (p , "_" )
if i < 0 {
return false
}
a := strings .Split (p [i +1 :], "_" )
last := len (a ) - 1
if last -1 >= 0 {
switch x , y := a [last -1 ], a [last ]; {
case x == ctx .GOOS :
if knownArch [y ] {
return y != ctx .GOARCH
}
return false
case knownOs [x ] && knownArch [y ]:
return true
case knownArch [y ] && y != ctx .GOARCH :
return true
default :
return false
}
}
if x := a [last ]; knownOs [x ] && x != ctx .GOOS || knownArch [x ] && x != ctx .GOARCH {
return true
}
return false
}
var knownOs = map [string ]bool {
"aix" : true ,
"android" : true ,
"darwin" : true ,
"dragonfly" : true ,
"freebsd" : true ,
"illumos" : true ,
"ios" : true ,
"js" : true ,
"linux" : true ,
"netbsd" : true ,
"openbsd" : true ,
"plan9" : true ,
"solaris" : true ,
"wasip1" : true ,
"windows" : true ,
}
var knownArch = map [string ]bool {
"386" : true ,
"amd64" : true ,
"arm" : true ,
"arm64" : true ,
"loong64" : true ,
"mips" : true ,
"mips64" : true ,
"mips64le" : true ,
"mipsle" : true ,
"ppc64" : true ,
"ppc64le" : true ,
"s390x" : true ,
"wasm" : true ,
}
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .