github.com/ajguerrer/rules_go@v0.20.3/go/tools/builders/filter.go (about) 1 // Copyright 2017 The Bazel Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main 16 17 import ( 18 "fmt" 19 "go/ast" 20 "go/build" 21 "go/parser" 22 "go/token" 23 "log" 24 "path/filepath" 25 "strconv" 26 "strings" 27 ) 28 29 type fileInfo struct { 30 filename string 31 ext ext 32 matched bool 33 isCgo bool 34 pkg string 35 imports []string 36 } 37 38 type ext int 39 40 const ( 41 goExt ext = iota 42 cExt 43 cxxExt 44 objcExt 45 objcxxExt 46 sExt 47 hExt 48 ) 49 50 type archiveSrcs struct { 51 goSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs []fileInfo 52 } 53 54 // filterAndSplitFiles filters files using build constraints and collates 55 // them by extension. 56 func filterAndSplitFiles(fileNames []string) (archiveSrcs, error) { 57 var res archiveSrcs 58 for _, s := range fileNames { 59 src, err := readFileInfo(build.Default, s, true) 60 if err != nil { 61 return archiveSrcs{}, err 62 } 63 if !src.matched { 64 continue 65 } 66 var srcs *[]fileInfo 67 switch src.ext { 68 case goExt: 69 srcs = &res.goSrcs 70 case cExt: 71 srcs = &res.cSrcs 72 case cxxExt: 73 srcs = &res.cxxSrcs 74 case objcExt: 75 srcs = &res.objcSrcs 76 case objcxxExt: 77 srcs = &res.objcxxSrcs 78 case sExt: 79 srcs = &res.sSrcs 80 case hExt: 81 srcs = &res.hSrcs 82 } 83 *srcs = append(*srcs, src) 84 } 85 return res, nil 86 } 87 88 // readFileInfo applies build constraints to an input file and returns whether 89 // it should be compiled. 90 func readFileInfo(bctx build.Context, input string, needPackage bool) (fileInfo, error) { 91 fi := fileInfo{filename: input} 92 if ext := filepath.Ext(input); ext == ".C" { 93 fi.ext = cxxExt 94 } else { 95 switch strings.ToLower(ext) { 96 case ".go": 97 fi.ext = goExt 98 case ".c": 99 fi.ext = cExt 100 case ".cc", ".cxx", ".cpp": 101 fi.ext = cxxExt 102 case ".m": 103 fi.ext = objcExt 104 case ".mm": 105 fi.ext = objcxxExt 106 case ".s": 107 fi.ext = sExt 108 case ".h", ".hh", ".hpp", ".hxx": 109 fi.ext = hExt 110 default: 111 return fileInfo{}, fmt.Errorf("unrecognized file extension: %s", ext) 112 } 113 } 114 115 dir, base := filepath.Split(input) 116 // Check build constraints on non-cgo files. 117 // Skip cgo files, since they get rejected (due to leading '_') and won't 118 // have any build constraints anyway. 119 if strings.HasPrefix(base, "_cgo") { 120 fi.matched = true 121 } else { 122 match, err := bctx.MatchFile(dir, base) 123 if err != nil { 124 return fi, err 125 } 126 fi.matched = match 127 } 128 // if we don't need the package, and we are cgo, no need to parse the file 129 if !needPackage && bctx.CgoEnabled { 130 return fi, nil 131 } 132 // if it's not a go file, there is no package or cgo 133 if !strings.HasSuffix(input, ".go") { 134 return fi, nil 135 } 136 137 // read the file header 138 fset := token.NewFileSet() 139 parsed, err := parser.ParseFile(fset, input, nil, parser.ImportsOnly) 140 if err != nil { 141 return fi, err 142 } 143 fi.pkg = parsed.Name.String() 144 145 for _, decl := range parsed.Decls { 146 d, ok := decl.(*ast.GenDecl) 147 if !ok { 148 continue 149 } 150 for _, dspec := range d.Specs { 151 spec, ok := dspec.(*ast.ImportSpec) 152 if !ok { 153 continue 154 } 155 imp, err := strconv.Unquote(spec.Path.Value) 156 if err != nil { 157 log.Panicf("%s: invalid string `%s`", input, spec.Path.Value) 158 } 159 if imp == "C" { 160 fi.isCgo = true 161 break 162 } 163 } 164 } 165 // matched if cgo is enabled or the file is not cgo 166 fi.matched = fi.matched && (bctx.CgoEnabled || !fi.isCgo) 167 168 for _, i := range parsed.Imports { 169 path, err := strconv.Unquote(i.Path.Value) 170 if err != nil { 171 return fi, err 172 } 173 fi.imports = append(fi.imports, path) 174 } 175 176 return fi, nil 177 }