package.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. /* Copyright 2017 The Bazel Authors. All rights reserved.
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package golang
  13. import (
  14. "fmt"
  15. "log"
  16. "path"
  17. "sort"
  18. "strings"
  19. "github.com/bazelbuild/bazel-gazelle/internal/config"
  20. "github.com/bazelbuild/bazel-gazelle/internal/language/proto"
  21. "github.com/bazelbuild/bazel-gazelle/internal/rule"
  22. )
  23. // goPackage contains metadata for a set of .go and .proto files that can be
  24. // used to generate Go rules.
  25. type goPackage struct {
  26. name, dir, rel string
  27. library, binary, test goTarget
  28. proto protoTarget
  29. hasTestdata bool
  30. importPath string
  31. }
  32. // goTarget contains information used to generate an individual Go rule
  33. // (library, binary, or test).
  34. type goTarget struct {
  35. sources, imports, copts, clinkopts platformStringsBuilder
  36. cgo bool
  37. }
  38. // protoTarget contains information used to generate a go_proto_library rule.
  39. type protoTarget struct {
  40. name string
  41. sources platformStringsBuilder
  42. imports platformStringsBuilder
  43. hasServices bool
  44. }
  45. // platformStringsBuilder is used to construct rule.PlatformStrings. Bazel
  46. // has some requirements for deps list (a dependency cannot appear in more
  47. // than one select expression; dependencies cannot be duplicated), so we need
  48. // to build these carefully.
  49. type platformStringsBuilder struct {
  50. strs map[string]platformStringInfo
  51. }
  52. // platformStringInfo contains information about a single string (source,
  53. // import, or option).
  54. type platformStringInfo struct {
  55. set platformStringSet
  56. oss map[string]bool
  57. archs map[string]bool
  58. platforms map[rule.Platform]bool
  59. }
  60. type platformStringSet int
  61. const (
  62. genericSet platformStringSet = iota
  63. osSet
  64. archSet
  65. platformSet
  66. )
  67. // addFile adds the file described by "info" to a target in the package "p" if
  68. // the file is buildable.
  69. //
  70. // "cgo" tells whether any ".go" file in the package contains cgo code. This
  71. // affects whether C files are added to targets.
  72. //
  73. // An error is returned if a file is buildable but invalid (for example, a
  74. // test .go file containing cgo code). Files that are not buildable will not
  75. // be added to any target (for example, .txt files).
  76. func (pkg *goPackage) addFile(c *config.Config, info fileInfo, cgo bool) error {
  77. switch {
  78. case info.ext == unknownExt || !cgo && (info.ext == cExt || info.ext == csExt):
  79. return nil
  80. case info.ext == protoExt:
  81. if proto.GetProtoConfig(c).Mode == proto.LegacyMode {
  82. // Only add files in legacy mode. This is used to generate a filegroup
  83. // that contains all protos. In order modes, we get the .proto files
  84. // from information emitted by the proto language extension.
  85. pkg.proto.addFile(c, info)
  86. }
  87. case info.isTest:
  88. if info.isCgo {
  89. return fmt.Errorf("%s: use of cgo in test not supported", info.path)
  90. }
  91. pkg.test.addFile(c, info)
  92. default:
  93. pkg.library.addFile(c, info)
  94. }
  95. return nil
  96. }
  97. // isCommand returns true if the package name is "main".
  98. func (pkg *goPackage) isCommand() bool {
  99. return pkg.name == "main"
  100. }
  101. // isBuildable returns true if anything in the package is buildable.
  102. // This is true if the package has Go code that satisfies build constraints
  103. // on any platform or has proto files not in legacy mode.
  104. func (pkg *goPackage) isBuildable(c *config.Config) bool {
  105. return pkg.firstGoFile() != "" || !pkg.proto.sources.isEmpty()
  106. }
  107. // firstGoFile returns the name of a .go file if the package contains at least
  108. // one .go file, or "" otherwise.
  109. func (pkg *goPackage) firstGoFile() string {
  110. goSrcs := []platformStringsBuilder{
  111. pkg.library.sources,
  112. pkg.binary.sources,
  113. pkg.test.sources,
  114. }
  115. for _, sb := range goSrcs {
  116. if sb.strs != nil {
  117. for s := range sb.strs {
  118. if strings.HasSuffix(s, ".go") {
  119. return s
  120. }
  121. }
  122. }
  123. }
  124. return ""
  125. }
  126. func (pkg *goPackage) haveCgo() bool {
  127. return pkg.library.cgo || pkg.binary.cgo || pkg.test.cgo
  128. }
  129. func (pkg *goPackage) inferImportPath(c *config.Config) error {
  130. if pkg.importPath != "" {
  131. log.Panic("importPath already set")
  132. }
  133. gc := getGoConfig(c)
  134. if !gc.prefixSet {
  135. return fmt.Errorf("%s: go prefix is not set, so importpath can't be determined for rules. Set a prefix with a '# gazelle:prefix' comment or with -go_prefix on the command line", pkg.dir)
  136. }
  137. pkg.importPath = inferImportPath(gc, pkg.rel)
  138. if pkg.rel == gc.prefixRel {
  139. pkg.importPath = gc.prefix
  140. } else {
  141. fromPrefixRel := strings.TrimPrefix(pkg.rel, gc.prefixRel+"/")
  142. pkg.importPath = path.Join(gc.prefix, fromPrefixRel)
  143. }
  144. return nil
  145. }
  146. func inferImportPath(gc *goConfig, rel string) string {
  147. if rel == gc.prefixRel {
  148. return gc.prefix
  149. } else {
  150. fromPrefixRel := strings.TrimPrefix(rel, gc.prefixRel+"/")
  151. return path.Join(gc.prefix, fromPrefixRel)
  152. }
  153. }
  154. func goProtoPackageName(pkg proto.Package) string {
  155. if value, ok := pkg.Options["go_package"]; ok {
  156. if strings.LastIndexByte(value, '/') == -1 {
  157. return value
  158. } else {
  159. if i := strings.LastIndexByte(value, ';'); i != -1 {
  160. return value[i+1:]
  161. } else {
  162. return path.Base(value)
  163. }
  164. }
  165. }
  166. return strings.Replace(pkg.Name, ".", "_", -1)
  167. }
  168. func goProtoImportPath(gc *goConfig, pkg proto.Package, rel string) string {
  169. if value, ok := pkg.Options["go_package"]; ok {
  170. if strings.LastIndexByte(value, '/') == -1 {
  171. return inferImportPath(gc, rel)
  172. } else if i := strings.LastIndexByte(value, ';'); i != -1 {
  173. return value[:i]
  174. } else {
  175. return value
  176. }
  177. }
  178. return inferImportPath(gc, rel)
  179. }
  180. func (t *goTarget) addFile(c *config.Config, info fileInfo) {
  181. t.cgo = t.cgo || info.isCgo
  182. add := getPlatformStringsAddFunction(c, info, nil)
  183. add(&t.sources, info.name)
  184. add(&t.imports, info.imports...)
  185. for _, copts := range info.copts {
  186. optAdd := add
  187. if len(copts.tags) > 0 {
  188. optAdd = getPlatformStringsAddFunction(c, info, copts.tags)
  189. }
  190. optAdd(&t.copts, copts.opts)
  191. }
  192. for _, clinkopts := range info.clinkopts {
  193. optAdd := add
  194. if len(clinkopts.tags) > 0 {
  195. optAdd = getPlatformStringsAddFunction(c, info, clinkopts.tags)
  196. }
  197. optAdd(&t.clinkopts, clinkopts.opts)
  198. }
  199. }
  200. func protoTargetFromProtoPackage(name string, pkg proto.Package) protoTarget {
  201. target := protoTarget{name: name}
  202. for f := range pkg.Files {
  203. target.sources.addGenericString(f)
  204. }
  205. for i := range pkg.Imports {
  206. target.imports.addGenericString(i)
  207. }
  208. target.hasServices = pkg.HasServices
  209. return target
  210. }
  211. func (t *protoTarget) addFile(c *config.Config, info fileInfo) {
  212. t.sources.addGenericString(info.name)
  213. for _, imp := range info.imports {
  214. t.imports.addGenericString(imp)
  215. }
  216. t.hasServices = t.hasServices || info.hasServices
  217. }
  218. // getPlatformStringsAddFunction returns a function used to add strings to
  219. // a *platformStringsBuilder under the same set of constraints. This is a
  220. // performance optimization to avoid evaluating constraints repeatedly.
  221. func getPlatformStringsAddFunction(c *config.Config, info fileInfo, cgoTags tagLine) func(sb *platformStringsBuilder, ss ...string) {
  222. isOSSpecific, isArchSpecific := isOSArchSpecific(info, cgoTags)
  223. switch {
  224. case !isOSSpecific && !isArchSpecific:
  225. if checkConstraints(c, "", "", info.goos, info.goarch, info.tags, cgoTags) {
  226. return func(sb *platformStringsBuilder, ss ...string) {
  227. for _, s := range ss {
  228. sb.addGenericString(s)
  229. }
  230. }
  231. }
  232. case isOSSpecific && !isArchSpecific:
  233. var osMatch []string
  234. for _, os := range rule.KnownOSs {
  235. if checkConstraints(c, os, "", info.goos, info.goarch, info.tags, cgoTags) {
  236. osMatch = append(osMatch, os)
  237. }
  238. }
  239. if len(osMatch) > 0 {
  240. return func(sb *platformStringsBuilder, ss ...string) {
  241. for _, s := range ss {
  242. sb.addOSString(s, osMatch)
  243. }
  244. }
  245. }
  246. case !isOSSpecific && isArchSpecific:
  247. var archMatch []string
  248. for _, arch := range rule.KnownArchs {
  249. if checkConstraints(c, "", arch, info.goos, info.goarch, info.tags, cgoTags) {
  250. archMatch = append(archMatch, arch)
  251. }
  252. }
  253. if len(archMatch) > 0 {
  254. return func(sb *platformStringsBuilder, ss ...string) {
  255. for _, s := range ss {
  256. sb.addArchString(s, archMatch)
  257. }
  258. }
  259. }
  260. default:
  261. var platformMatch []rule.Platform
  262. for _, platform := range rule.KnownPlatforms {
  263. if checkConstraints(c, platform.OS, platform.Arch, info.goos, info.goarch, info.tags, cgoTags) {
  264. platformMatch = append(platformMatch, platform)
  265. }
  266. }
  267. if len(platformMatch) > 0 {
  268. return func(sb *platformStringsBuilder, ss ...string) {
  269. for _, s := range ss {
  270. sb.addPlatformString(s, platformMatch)
  271. }
  272. }
  273. }
  274. }
  275. return func(_ *platformStringsBuilder, _ ...string) {}
  276. }
  277. func (sb *platformStringsBuilder) isEmpty() bool {
  278. return sb.strs == nil
  279. }
  280. func (sb *platformStringsBuilder) hasGo() bool {
  281. for s := range sb.strs {
  282. if strings.HasSuffix(s, ".go") {
  283. return true
  284. }
  285. }
  286. return false
  287. }
  288. func (sb *platformStringsBuilder) addGenericString(s string) {
  289. if sb.strs == nil {
  290. sb.strs = make(map[string]platformStringInfo)
  291. }
  292. sb.strs[s] = platformStringInfo{set: genericSet}
  293. }
  294. func (sb *platformStringsBuilder) addOSString(s string, oss []string) {
  295. if sb.strs == nil {
  296. sb.strs = make(map[string]platformStringInfo)
  297. }
  298. si, ok := sb.strs[s]
  299. if !ok {
  300. si.set = osSet
  301. si.oss = make(map[string]bool)
  302. }
  303. switch si.set {
  304. case genericSet:
  305. return
  306. case osSet:
  307. for _, os := range oss {
  308. si.oss[os] = true
  309. }
  310. default:
  311. si.convertToPlatforms()
  312. for _, os := range oss {
  313. for _, arch := range rule.KnownOSArchs[os] {
  314. si.platforms[rule.Platform{OS: os, Arch: arch}] = true
  315. }
  316. }
  317. }
  318. sb.strs[s] = si
  319. }
  320. func (sb *platformStringsBuilder) addArchString(s string, archs []string) {
  321. if sb.strs == nil {
  322. sb.strs = make(map[string]platformStringInfo)
  323. }
  324. si, ok := sb.strs[s]
  325. if !ok {
  326. si.set = archSet
  327. si.archs = make(map[string]bool)
  328. }
  329. switch si.set {
  330. case genericSet:
  331. return
  332. case archSet:
  333. for _, arch := range archs {
  334. si.archs[arch] = true
  335. }
  336. default:
  337. si.convertToPlatforms()
  338. for _, arch := range archs {
  339. for _, os := range rule.KnownArchOSs[arch] {
  340. si.platforms[rule.Platform{OS: os, Arch: arch}] = true
  341. }
  342. }
  343. }
  344. sb.strs[s] = si
  345. }
  346. func (sb *platformStringsBuilder) addPlatformString(s string, platforms []rule.Platform) {
  347. if sb.strs == nil {
  348. sb.strs = make(map[string]platformStringInfo)
  349. }
  350. si, ok := sb.strs[s]
  351. if !ok {
  352. si.set = platformSet
  353. si.platforms = make(map[rule.Platform]bool)
  354. }
  355. switch si.set {
  356. case genericSet:
  357. return
  358. default:
  359. si.convertToPlatforms()
  360. for _, p := range platforms {
  361. si.platforms[p] = true
  362. }
  363. }
  364. sb.strs[s] = si
  365. }
  366. func (sb *platformStringsBuilder) build() rule.PlatformStrings {
  367. var ps rule.PlatformStrings
  368. for s, si := range sb.strs {
  369. switch si.set {
  370. case genericSet:
  371. ps.Generic = append(ps.Generic, s)
  372. case osSet:
  373. if ps.OS == nil {
  374. ps.OS = make(map[string][]string)
  375. }
  376. for os := range si.oss {
  377. ps.OS[os] = append(ps.OS[os], s)
  378. }
  379. case archSet:
  380. if ps.Arch == nil {
  381. ps.Arch = make(map[string][]string)
  382. }
  383. for arch := range si.archs {
  384. ps.Arch[arch] = append(ps.Arch[arch], s)
  385. }
  386. case platformSet:
  387. if ps.Platform == nil {
  388. ps.Platform = make(map[rule.Platform][]string)
  389. }
  390. for p := range si.platforms {
  391. ps.Platform[p] = append(ps.Platform[p], s)
  392. }
  393. }
  394. }
  395. sort.Strings(ps.Generic)
  396. if ps.OS != nil {
  397. for _, ss := range ps.OS {
  398. sort.Strings(ss)
  399. }
  400. }
  401. if ps.Arch != nil {
  402. for _, ss := range ps.Arch {
  403. sort.Strings(ss)
  404. }
  405. }
  406. if ps.Platform != nil {
  407. for _, ss := range ps.Platform {
  408. sort.Strings(ss)
  409. }
  410. }
  411. return ps
  412. }
  413. func (sb *platformStringsBuilder) buildFlat() []string {
  414. strs := make([]string, 0, len(sb.strs))
  415. for s := range sb.strs {
  416. strs = append(strs, s)
  417. }
  418. sort.Strings(strs)
  419. return strs
  420. }
  421. func (si *platformStringInfo) convertToPlatforms() {
  422. switch si.set {
  423. case genericSet:
  424. log.Panic("cannot convert generic string to platforms")
  425. case platformSet:
  426. return
  427. case osSet:
  428. si.set = platformSet
  429. si.platforms = make(map[rule.Platform]bool)
  430. for os := range si.oss {
  431. for _, arch := range rule.KnownOSArchs[os] {
  432. si.platforms[rule.Platform{OS: os, Arch: arch}] = true
  433. }
  434. }
  435. si.oss = nil
  436. case archSet:
  437. si.set = platformSet
  438. si.platforms = make(map[rule.Platform]bool)
  439. for arch := range si.archs {
  440. for _, os := range rule.KnownArchOSs[arch] {
  441. si.platforms[rule.Platform{OS: os, Arch: arch}] = true
  442. }
  443. }
  444. si.archs = nil
  445. }
  446. }