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 15package main 16 17import ( 18 "fmt" 19 "go/ast" 20 "go/build" 21 "go/token" 22 "os" 23 "path/filepath" 24 "strings" 25) 26 27type fileInfo struct { 28 filename string 29 ext ext 30 header []byte 31 fset *token.FileSet 32 parsed *ast.File 33 parseErr error 34 matched bool 35 isCgo bool 36 pkg string 37 imports []fileImport 38 embeds []fileEmbed 39} 40 41type ext int 42 43const ( 44 goExt ext = iota 45 cExt 46 cxxExt 47 objcExt 48 objcxxExt 49 sExt 50 hExt 51) 52 53type fileImport struct { 54 path string 55 pos token.Pos 56 doc *ast.CommentGroup 57} 58 59type fileEmbed struct { 60 pattern string 61 pos token.Position 62} 63 64type archiveSrcs struct { 65 goSrcs, cSrcs, cxxSrcs, objcSrcs, objcxxSrcs, sSrcs, hSrcs []fileInfo 66} 67 68// filterAndSplitFiles filters files using build constraints and collates 69// them by extension. 70func filterAndSplitFiles(fileNames []string) (archiveSrcs, error) { 71 var res archiveSrcs 72 for _, s := range fileNames { 73 src, err := readFileInfo(build.Default, s) 74 if err != nil { 75 return archiveSrcs{}, err 76 } 77 if !src.matched { 78 continue 79 } 80 var srcs *[]fileInfo 81 switch src.ext { 82 case goExt: 83 srcs = &res.goSrcs 84 case cExt: 85 srcs = &res.cSrcs 86 case cxxExt: 87 srcs = &res.cxxSrcs 88 case objcExt: 89 srcs = &res.objcSrcs 90 case objcxxExt: 91 srcs = &res.objcxxSrcs 92 case sExt: 93 srcs = &res.sSrcs 94 case hExt: 95 srcs = &res.hSrcs 96 } 97 *srcs = append(*srcs, src) 98 } 99 return res, nil 100} 101 102// readFileInfo applies build constraints to an input file and returns whether 103// it should be compiled. 104func readFileInfo(bctx build.Context, input string) (fileInfo, error) { 105 fi := fileInfo{filename: input} 106 if ext := filepath.Ext(input); ext == ".C" { 107 fi.ext = cxxExt 108 } else { 109 switch strings.ToLower(ext) { 110 case ".go": 111 fi.ext = goExt 112 case ".c": 113 fi.ext = cExt 114 case ".cc", ".cxx", ".cpp": 115 fi.ext = cxxExt 116 case ".m": 117 fi.ext = objcExt 118 case ".mm": 119 fi.ext = objcxxExt 120 case ".s": 121 fi.ext = sExt 122 case ".h", ".hh", ".hpp", ".hxx": 123 fi.ext = hExt 124 default: 125 return fileInfo{}, fmt.Errorf("unrecognized file extension: %s", ext) 126 } 127 } 128 129 dir, base := filepath.Split(input) 130 // Check build constraints on non-cgo files. 131 // Skip cgo files, since they get rejected (due to leading '_') and won't 132 // have any build constraints anyway. 133 if strings.HasPrefix(base, "_cgo") { 134 fi.matched = true 135 } else { 136 match, err := bctx.MatchFile(dir, base) 137 if err != nil { 138 return fi, err 139 } 140 fi.matched = match 141 } 142 // If it's not a go file, there's nothing more to read. 143 if fi.ext != goExt { 144 return fi, nil 145 } 146 147 // Scan the file for imports and embeds. 148 f, err := os.Open(input) 149 if err != nil { 150 return fileInfo{}, err 151 } 152 defer f.Close() 153 fi.fset = token.NewFileSet() 154 if err := readGoInfo(f, &fi); err != nil { 155 return fileInfo{}, err 156 } 157 158 // Exclude cgo files if cgo is not enabled. 159 for _, imp := range fi.imports { 160 if imp.path == "C" { 161 fi.isCgo = true 162 break 163 } 164 } 165 fi.matched = fi.matched && (bctx.CgoEnabled || !fi.isCgo) 166 167 return fi, nil 168} 169