rule.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. /* Copyright 2018 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 rule provides tools for editing Bazel build files. It is intended to
  13. // be a more powerful replacement for
  14. // github.com/bazelbuild/buildtools/build.Rule, adapted for Gazelle's usage. It
  15. // is language agnostic, but it may be used for language-specific rules by
  16. // providing configuration.
  17. //
  18. // File is the primary interface to this package. A File represents an
  19. // individual build file. It comprises a list of Rules and a list of Loads.
  20. // Rules and Loads may be inserted, modified, or deleted. When all changes
  21. // are done, File.Save() may be called to write changes back to a file.
  22. package rule
  23. import (
  24. "fmt"
  25. "io/ioutil"
  26. "os"
  27. "path/filepath"
  28. "sort"
  29. "strings"
  30. bzl "github.com/bazelbuild/buildtools/build"
  31. bt "github.com/bazelbuild/buildtools/tables"
  32. )
  33. // File provides editing functionality for a build file. You can create a
  34. // new file with EmptyFile or load an existing file with LoadFile. After
  35. // changes have been made, call Save to write changes back to a file.
  36. type File struct {
  37. // File is the underlying build file syntax tree. Some editing operations
  38. // may modify this, but editing is not complete until Sync() is called.
  39. File *bzl.File
  40. // function is the underlying syntax tree of a bzl file function.
  41. // This is used for editing the bzl file function specified by the
  42. // update-repos -to_macro option.
  43. function *function
  44. // Pkg is the Bazel package this build file defines.
  45. Pkg string
  46. // Path is the file system path to the build file (same as File.Path).
  47. Path string
  48. // DefName is the name of the function definition this File refers to
  49. // if loaded with LoadMacroFile or a similar function. Normally empty.
  50. DefName string
  51. // Directives is a list of configuration directives found in top-level
  52. // comments in the file. This should not be modified after the file is read.
  53. Directives []Directive
  54. // Loads is a list of load statements within the file. This should not
  55. // be modified directly; use Load methods instead.
  56. Loads []*Load
  57. // Rules is a list of rules within the file (or function calls that look like
  58. // rules). This should not be modified directly; use Rule methods instead.
  59. Rules []*Rule
  60. }
  61. // EmptyFile creates a File wrapped around an empty syntax tree.
  62. func EmptyFile(path, pkg string) *File {
  63. return &File{
  64. File: &bzl.File{Path: path, Type: bzl.TypeBuild},
  65. Path: path,
  66. Pkg: pkg,
  67. }
  68. }
  69. // LoadFile loads a build file from disk, parses it, and scans for rules and
  70. // load statements. The syntax tree within the returned File will be modified
  71. // by editing methods.
  72. //
  73. // This function returns I/O and parse errors without modification. It's safe
  74. // to use os.IsNotExist and similar predicates.
  75. func LoadFile(path, pkg string) (*File, error) {
  76. data, err := ioutil.ReadFile(path)
  77. if err != nil {
  78. return nil, err
  79. }
  80. return LoadData(path, pkg, data)
  81. }
  82. // LoadWorkspaceFile is similar to LoadFile but parses the file as a WORKSPACE
  83. // file.
  84. func LoadWorkspaceFile(path, pkg string) (*File, error) {
  85. data, err := ioutil.ReadFile(path)
  86. if err != nil {
  87. return nil, err
  88. }
  89. return LoadWorkspaceData(path, pkg, data)
  90. }
  91. // LoadMacroFile loads a bzl file from disk, parses it, then scans for the load
  92. // statements and the rules called from the given Starlark function. If there is
  93. // no matching function name, then a new function with that name will be created.
  94. // The function's syntax tree will be returned within File and can be modified by
  95. // Sync and Save calls.
  96. func LoadMacroFile(path, pkg, defName string) (*File, error) {
  97. data, err := ioutil.ReadFile(path)
  98. if err != nil {
  99. return nil, err
  100. }
  101. return LoadMacroData(path, pkg, defName, data)
  102. }
  103. // EmptyMacroFile creates a bzl file at the given path and within the file creates
  104. // a Starlark function with the provided name. The function can then be modified
  105. // by Sync and Save calls.
  106. func EmptyMacroFile(path, pkg, defName string) (*File, error) {
  107. _, err := os.Create(path)
  108. if err != nil {
  109. return nil, err
  110. }
  111. return LoadMacroData(path, pkg, defName, nil)
  112. }
  113. // LoadData parses a build file from a byte slice and scans it for rules and
  114. // load statements. The syntax tree within the returned File will be modified
  115. // by editing methods.
  116. func LoadData(path, pkg string, data []byte) (*File, error) {
  117. ast, err := bzl.ParseBuild(path, data)
  118. if err != nil {
  119. return nil, err
  120. }
  121. return ScanAST(pkg, ast), nil
  122. }
  123. // LoadWorkspaceData is similar to LoadData but parses the data as a
  124. // WORKSPACE file.
  125. func LoadWorkspaceData(path, pkg string, data []byte) (*File, error) {
  126. ast, err := bzl.ParseWorkspace(path, data)
  127. if err != nil {
  128. return nil, err
  129. }
  130. return ScanAST(pkg, ast), nil
  131. }
  132. // LoadMacroData parses a bzl file from a byte slice and scans for the load
  133. // statements and the rules called from the given Starlark function. If there is
  134. // no matching function name, then a new function will be created, and added to the
  135. // File the next time Sync is called. The function's syntax tree will be returned
  136. // within File and can be modified by Sync and Save calls.
  137. func LoadMacroData(path, pkg, defName string, data []byte) (*File, error) {
  138. ast, err := bzl.ParseBzl(path, data)
  139. if err != nil {
  140. return nil, err
  141. }
  142. return ScanASTBody(pkg, defName, ast), nil
  143. }
  144. // ScanAST creates a File wrapped around the given syntax tree. This tree
  145. // will be modified by editing methods.
  146. func ScanAST(pkg string, bzlFile *bzl.File) *File {
  147. return ScanASTBody(pkg, "", bzlFile)
  148. }
  149. type function struct {
  150. stmt *bzl.DefStmt
  151. inserted, hasPass bool
  152. }
  153. // ScanASTBody creates a File wrapped around the given syntax tree. It will also
  154. // scan the AST for a function matching the given defName, and if the function
  155. // does not exist it will create a new one and mark it to be added to the File
  156. // the next time Sync is called.
  157. func ScanASTBody(pkg, defName string, bzlFile *bzl.File) *File {
  158. f := &File{
  159. File: bzlFile,
  160. Pkg: pkg,
  161. Path: bzlFile.Path,
  162. DefName: defName,
  163. }
  164. var defStmt *bzl.DefStmt
  165. f.Rules, f.Loads, defStmt = scanExprs(defName, bzlFile.Stmt)
  166. if defStmt != nil {
  167. f.Rules, _, _ = scanExprs("", defStmt.Body)
  168. f.function = &function{
  169. stmt: defStmt,
  170. inserted: true,
  171. }
  172. if len(defStmt.Body) == 1 {
  173. if v, ok := defStmt.Body[0].(*bzl.BranchStmt); ok && v.Token == "pass" {
  174. f.function.hasPass = true
  175. }
  176. }
  177. } else if defName != "" {
  178. f.function = &function{
  179. stmt: &bzl.DefStmt{Name: defName},
  180. inserted: false,
  181. }
  182. }
  183. if f.function != nil {
  184. f.Directives = ParseDirectivesFromMacro(f.function.stmt)
  185. } else {
  186. f.Directives = ParseDirectives(bzlFile)
  187. }
  188. return f
  189. }
  190. func scanExprs(defName string, stmt []bzl.Expr) (rules []*Rule, loads []*Load, fn *bzl.DefStmt) {
  191. for i, expr := range stmt {
  192. switch expr := expr.(type) {
  193. case *bzl.LoadStmt:
  194. l := loadFromExpr(i, expr)
  195. loads = append(loads, l)
  196. case *bzl.CallExpr:
  197. if r := ruleFromExpr(i, expr); r != nil {
  198. rules = append(rules, r)
  199. }
  200. case *bzl.DefStmt:
  201. if expr.Name == defName {
  202. fn = expr
  203. }
  204. }
  205. }
  206. return rules, loads, fn
  207. }
  208. // MatchBuildFileName looks for a file in files that has a name from names.
  209. // If there is at least one matching file, a path will be returned by joining
  210. // dir and the first matching name. If there are no matching files, the
  211. // empty string is returned.
  212. func MatchBuildFileName(dir string, names []string, files []os.FileInfo) string {
  213. for _, name := range names {
  214. for _, fi := range files {
  215. if fi.Name() == name && !fi.IsDir() {
  216. return filepath.Join(dir, name)
  217. }
  218. }
  219. }
  220. return ""
  221. }
  222. // SyncMacroFile syncs the file's syntax tree with another file's. This is
  223. // useful for keeping multiple macro definitions from the same .bzl file in sync.
  224. func (f *File) SyncMacroFile(from *File) {
  225. fromFunc := *from.function.stmt
  226. _, _, toFunc := scanExprs(from.function.stmt.Name, f.File.Stmt)
  227. if toFunc != nil {
  228. *toFunc = fromFunc
  229. } else {
  230. f.File.Stmt = append(f.File.Stmt, &fromFunc)
  231. }
  232. }
  233. // MacroName returns the name of the macro function that this file is editing,
  234. // or an empty string if a macro function is not being edited.
  235. func (f *File) MacroName() string {
  236. if f.function != nil && f.function.stmt != nil {
  237. return f.function.stmt.Name
  238. }
  239. return ""
  240. }
  241. // Sync writes all changes back to the wrapped syntax tree. This should be
  242. // called after editing operations, before reading the syntax tree again.
  243. func (f *File) Sync() {
  244. var loadInserts, loadDeletes, loadStmts []*stmt
  245. var r, w int
  246. for r, w = 0, 0; r < len(f.Loads); r++ {
  247. s := f.Loads[r]
  248. s.sync()
  249. if s.deleted {
  250. loadDeletes = append(loadDeletes, &s.stmt)
  251. continue
  252. }
  253. if s.inserted {
  254. loadInserts = append(loadInserts, &s.stmt)
  255. s.inserted = false
  256. } else {
  257. loadStmts = append(loadStmts, &s.stmt)
  258. }
  259. f.Loads[w] = s
  260. w++
  261. }
  262. f.Loads = f.Loads[:w]
  263. var ruleInserts, ruleDeletes, ruleStmts []*stmt
  264. for r, w = 0, 0; r < len(f.Rules); r++ {
  265. s := f.Rules[r]
  266. s.sync()
  267. if s.deleted {
  268. ruleDeletes = append(ruleDeletes, &s.stmt)
  269. continue
  270. }
  271. if s.inserted {
  272. ruleInserts = append(ruleInserts, &s.stmt)
  273. s.inserted = false
  274. } else {
  275. ruleStmts = append(ruleStmts, &s.stmt)
  276. }
  277. f.Rules[w] = s
  278. w++
  279. }
  280. f.Rules = f.Rules[:w]
  281. if f.function == nil {
  282. deletes := append(ruleDeletes, loadDeletes...)
  283. inserts := append(ruleInserts, loadInserts...)
  284. stmts := append(ruleStmts, loadStmts...)
  285. updateStmt(&f.File.Stmt, inserts, deletes, stmts)
  286. } else {
  287. updateStmt(&f.File.Stmt, loadInserts, loadDeletes, loadStmts)
  288. if f.function.hasPass && len(ruleInserts) > 0 {
  289. f.function.stmt.Body = []bzl.Expr{}
  290. f.function.hasPass = false
  291. }
  292. updateStmt(&f.function.stmt.Body, ruleInserts, ruleDeletes, ruleStmts)
  293. if len(f.function.stmt.Body) == 0 {
  294. f.function.stmt.Body = append(f.function.stmt.Body, &bzl.BranchStmt{Token: "pass"})
  295. f.function.hasPass = true
  296. }
  297. if !f.function.inserted {
  298. f.File.Stmt = append(f.File.Stmt, f.function.stmt)
  299. f.function.inserted = true
  300. }
  301. }
  302. }
  303. func updateStmt(oldStmt *[]bzl.Expr, inserts, deletes, stmts []*stmt) {
  304. sort.Stable(byIndex(deletes))
  305. sort.Stable(byIndex(inserts))
  306. sort.Stable(byIndex(stmts))
  307. newStmt := make([]bzl.Expr, 0, len(*oldStmt)-len(deletes)+len(inserts))
  308. var ii, di, si int
  309. for i, stmt := range *oldStmt {
  310. for ii < len(inserts) && inserts[ii].index == i {
  311. inserts[ii].index = len(newStmt)
  312. newStmt = append(newStmt, inserts[ii].expr)
  313. ii++
  314. }
  315. if di < len(deletes) && deletes[di].index == i {
  316. di++
  317. continue
  318. }
  319. if si < len(stmts) && stmts[si].expr == stmt {
  320. stmts[si].index = len(newStmt)
  321. si++
  322. }
  323. newStmt = append(newStmt, stmt)
  324. }
  325. for ii < len(inserts) {
  326. inserts[ii].index = len(newStmt)
  327. newStmt = append(newStmt, inserts[ii].expr)
  328. ii++
  329. }
  330. *oldStmt = newStmt
  331. }
  332. // Format formats the build file in a form that can be written to disk.
  333. // This method calls Sync internally.
  334. func (f *File) Format() []byte {
  335. f.Sync()
  336. return bzl.Format(f.File)
  337. }
  338. // Save writes the build file to disk. This method calls Sync internally.
  339. func (f *File) Save(path string) error {
  340. f.Sync()
  341. data := bzl.Format(f.File)
  342. return ioutil.WriteFile(path, data, 0666)
  343. }
  344. // HasDefaultVisibility returns whether the File contains a "package" rule with
  345. // a "default_visibility" attribute. Rules generated by Gazelle should not
  346. // have their own visibility attributes if this is the case.
  347. func (f *File) HasDefaultVisibility() bool {
  348. for _, r := range f.Rules {
  349. if r.Kind() == "package" && r.Attr("default_visibility") != nil {
  350. return true
  351. }
  352. }
  353. return false
  354. }
  355. type stmt struct {
  356. index int
  357. deleted, inserted, updated bool
  358. expr bzl.Expr
  359. }
  360. // Index returns the index for this statement within the build file. For
  361. // inserted rules, this is where the rule will be inserted (rules with the
  362. // same index will be inserted in the order Insert was called). For existing
  363. // rules, this is the index of the original statement.
  364. func (s *stmt) Index() int { return s.index }
  365. // Delete marks this statement for deletion. It will be removed from the
  366. // syntax tree when File.Sync is called.
  367. func (s *stmt) Delete() { s.deleted = true }
  368. type byIndex []*stmt
  369. func (s byIndex) Len() int {
  370. return len(s)
  371. }
  372. func (s byIndex) Less(i, j int) bool {
  373. return s[i].index < s[j].index
  374. }
  375. func (s byIndex) Swap(i, j int) {
  376. s[i], s[j] = s[j], s[i]
  377. }
  378. // identPair represents one symbol, with or without remapping, in a load
  379. // statement within a build file.
  380. type identPair struct {
  381. to, from *bzl.Ident
  382. }
  383. // Load represents a load statement within a build file.
  384. type Load struct {
  385. stmt
  386. name string
  387. symbols map[string]identPair
  388. }
  389. // NewLoad creates a new, empty load statement for the given file name.
  390. func NewLoad(name string) *Load {
  391. return &Load{
  392. stmt: stmt{
  393. expr: &bzl.LoadStmt{
  394. Module: &bzl.StringExpr{Value: name},
  395. ForceCompact: true,
  396. },
  397. },
  398. name: name,
  399. symbols: make(map[string]identPair),
  400. }
  401. }
  402. func loadFromExpr(index int, loadStmt *bzl.LoadStmt) *Load {
  403. l := &Load{
  404. stmt: stmt{index: index, expr: loadStmt},
  405. name: loadStmt.Module.Value,
  406. symbols: make(map[string]identPair),
  407. }
  408. for i := range loadStmt.From {
  409. to, from := loadStmt.To[i], loadStmt.From[i]
  410. l.symbols[to.Name] = identPair{to: to, from: from}
  411. }
  412. return l
  413. }
  414. // Name returns the name of the file this statement loads.
  415. func (l *Load) Name() string {
  416. return l.name
  417. }
  418. // Symbols returns a list of symbols this statement loads.
  419. func (l *Load) Symbols() []string {
  420. syms := make([]string, 0, len(l.symbols))
  421. for sym := range l.symbols {
  422. syms = append(syms, sym)
  423. }
  424. sort.Strings(syms)
  425. return syms
  426. }
  427. // Has returns true if sym is loaded by this statement.
  428. func (l *Load) Has(sym string) bool {
  429. _, ok := l.symbols[sym]
  430. return ok
  431. }
  432. // Add inserts a new symbol into the load statement. This has no effect if
  433. // the symbol is already loaded. Symbols will be sorted, so the order
  434. // doesn't matter.
  435. func (l *Load) Add(sym string) {
  436. if _, ok := l.symbols[sym]; !ok {
  437. i := &bzl.Ident{Name: sym}
  438. l.symbols[sym] = identPair{to: i, from: i}
  439. l.updated = true
  440. }
  441. }
  442. // Remove deletes a symbol from the load statement. This has no effect if
  443. // the symbol is not loaded.
  444. func (l *Load) Remove(sym string) {
  445. if _, ok := l.symbols[sym]; ok {
  446. delete(l.symbols, sym)
  447. l.updated = true
  448. }
  449. }
  450. // IsEmpty returns whether this statement loads any symbols.
  451. func (l *Load) IsEmpty() bool {
  452. return len(l.symbols) == 0
  453. }
  454. // Insert marks this statement for insertion at the given index. If multiple
  455. // statements are inserted at the same index, they will be inserted in the
  456. // order Insert is called.
  457. func (l *Load) Insert(f *File, index int) {
  458. l.index = index
  459. l.inserted = true
  460. f.Loads = append(f.Loads, l)
  461. }
  462. func (l *Load) sync() {
  463. if !l.updated {
  464. return
  465. }
  466. l.updated = false
  467. // args1 and args2 are two different sort groups based on whether a remap of the identifier is present.
  468. var args1, args2, args []string
  469. for sym, pair := range l.symbols {
  470. if pair.from.Name == pair.to.Name {
  471. args1 = append(args1, sym)
  472. } else {
  473. args2 = append(args2, sym)
  474. }
  475. }
  476. sort.Strings(args1)
  477. sort.Strings(args2)
  478. args = append(args, args1...)
  479. args = append(args, args2...)
  480. loadStmt := l.expr.(*bzl.LoadStmt)
  481. loadStmt.Module.Value = l.name
  482. loadStmt.From = make([]*bzl.Ident, 0, len(args))
  483. loadStmt.To = make([]*bzl.Ident, 0, len(args))
  484. for _, sym := range args {
  485. pair := l.symbols[sym]
  486. loadStmt.From = append(loadStmt.From, pair.from)
  487. loadStmt.To = append(loadStmt.To, pair.to)
  488. if pair.from.Name != pair.to.Name {
  489. loadStmt.ForceCompact = false
  490. }
  491. }
  492. }
  493. // Rule represents a rule statement within a build file.
  494. type Rule struct {
  495. stmt
  496. kind string
  497. args []bzl.Expr
  498. attrs map[string]*bzl.AssignExpr
  499. private map[string]interface{}
  500. }
  501. // NewRule creates a new, empty rule with the given kind and name.
  502. func NewRule(kind, name string) *Rule {
  503. nameAttr := &bzl.AssignExpr{
  504. LHS: &bzl.Ident{Name: "name"},
  505. RHS: &bzl.StringExpr{Value: name},
  506. Op: "=",
  507. }
  508. r := &Rule{
  509. stmt: stmt{
  510. expr: &bzl.CallExpr{
  511. X: &bzl.Ident{Name: kind},
  512. List: []bzl.Expr{nameAttr},
  513. },
  514. },
  515. kind: kind,
  516. attrs: map[string]*bzl.AssignExpr{"name": nameAttr},
  517. private: map[string]interface{}{},
  518. }
  519. return r
  520. }
  521. func ruleFromExpr(index int, expr bzl.Expr) *Rule {
  522. call, ok := expr.(*bzl.CallExpr)
  523. if !ok {
  524. return nil
  525. }
  526. x, ok := call.X.(*bzl.Ident)
  527. if !ok {
  528. return nil
  529. }
  530. kind := x.Name
  531. var args []bzl.Expr
  532. attrs := make(map[string]*bzl.AssignExpr)
  533. for _, arg := range call.List {
  534. if attr, ok := arg.(*bzl.AssignExpr); ok {
  535. key := attr.LHS.(*bzl.Ident) // required by parser
  536. attrs[key.Name] = attr
  537. } else {
  538. args = append(args, arg)
  539. }
  540. }
  541. return &Rule{
  542. stmt: stmt{
  543. index: index,
  544. expr: call,
  545. },
  546. kind: kind,
  547. args: args,
  548. attrs: attrs,
  549. private: map[string]interface{}{},
  550. }
  551. }
  552. // ShouldKeep returns whether the rule is marked with a "# keep" comment. Rules
  553. // that are kept should not be modified. This does not check whether
  554. // subexpressions within the rule should be kept.
  555. func (r *Rule) ShouldKeep() bool {
  556. return ShouldKeep(r.expr)
  557. }
  558. // Kind returns the kind of rule this is (for example, "go_library").
  559. func (r *Rule) Kind() string {
  560. return r.kind
  561. }
  562. // SetKind changes the kind of rule this is.
  563. func (r *Rule) SetKind(kind string) {
  564. r.kind = kind
  565. r.updated = true
  566. }
  567. // Name returns the value of the rule's "name" attribute if it is a string
  568. // or "" if the attribute does not exist or is not a string.
  569. func (r *Rule) Name() string {
  570. return r.AttrString("name")
  571. }
  572. // SetName sets the value of the rule's "name" attribute.
  573. func (r *Rule) SetName(name string) {
  574. r.SetAttr("name", name)
  575. }
  576. // AttrKeys returns a sorted list of attribute keys used in this rule.
  577. func (r *Rule) AttrKeys() []string {
  578. keys := make([]string, 0, len(r.attrs))
  579. for k := range r.attrs {
  580. keys = append(keys, k)
  581. }
  582. sort.SliceStable(keys, func(i, j int) bool {
  583. if cmp := bt.NamePriority[keys[i]] - bt.NamePriority[keys[j]]; cmp != 0 {
  584. return cmp < 0
  585. }
  586. return keys[i] < keys[j]
  587. })
  588. return keys
  589. }
  590. // Attr returns the value of the named attribute. nil is returned when the
  591. // attribute is not set.
  592. func (r *Rule) Attr(key string) bzl.Expr {
  593. attr, ok := r.attrs[key]
  594. if !ok {
  595. return nil
  596. }
  597. return attr.RHS
  598. }
  599. // AttrString returns the value of the named attribute if it is a scalar string.
  600. // "" is returned if the attribute is not set or is not a string.
  601. func (r *Rule) AttrString(key string) string {
  602. attr, ok := r.attrs[key]
  603. if !ok {
  604. return ""
  605. }
  606. str, ok := attr.RHS.(*bzl.StringExpr)
  607. if !ok {
  608. return ""
  609. }
  610. return str.Value
  611. }
  612. // AttrStrings returns the string values of an attribute if it is a list.
  613. // nil is returned if the attribute is not set or is not a list. Non-string
  614. // values within the list won't be returned.
  615. func (r *Rule) AttrStrings(key string) []string {
  616. attr, ok := r.attrs[key]
  617. if !ok {
  618. return nil
  619. }
  620. list, ok := attr.RHS.(*bzl.ListExpr)
  621. if !ok {
  622. return nil
  623. }
  624. strs := make([]string, 0, len(list.List))
  625. for _, e := range list.List {
  626. if str, ok := e.(*bzl.StringExpr); ok {
  627. strs = append(strs, str.Value)
  628. }
  629. }
  630. return strs
  631. }
  632. // DelAttr removes the named attribute from the rule.
  633. func (r *Rule) DelAttr(key string) {
  634. delete(r.attrs, key)
  635. r.updated = true
  636. }
  637. // SetAttr adds or replaces the named attribute with an expression produced
  638. // by ExprFromValue.
  639. func (r *Rule) SetAttr(key string, value interface{}) {
  640. rhs := ExprFromValue(value)
  641. if attr, ok := r.attrs[key]; ok {
  642. attr.RHS = rhs
  643. } else {
  644. r.attrs[key] = &bzl.AssignExpr{
  645. LHS: &bzl.Ident{Name: key},
  646. RHS: rhs,
  647. Op: "=",
  648. }
  649. }
  650. r.updated = true
  651. }
  652. // PrivateAttrKeys returns a sorted list of private attribute names.
  653. func (r *Rule) PrivateAttrKeys() []string {
  654. keys := make([]string, 0, len(r.private))
  655. for k := range r.private {
  656. keys = append(keys, k)
  657. }
  658. sort.Strings(keys)
  659. return keys
  660. }
  661. // PrivateAttr return the private value associated with a key.
  662. func (r *Rule) PrivateAttr(key string) interface{} {
  663. return r.private[key]
  664. }
  665. // SetPrivateAttr associates a value with a key. Unlike SetAttr, this value
  666. // is not converted to a build syntax tree and will not be written to a build
  667. // file.
  668. func (r *Rule) SetPrivateAttr(key string, value interface{}) {
  669. r.private[key] = value
  670. }
  671. // Args returns positional arguments passed to a rule.
  672. func (r *Rule) Args() []bzl.Expr {
  673. return r.args
  674. }
  675. // Insert marks this statement for insertion at the end of the file. Multiple
  676. // statements will be inserted in the order Insert is called.
  677. func (r *Rule) Insert(f *File) {
  678. // TODO(jayconrod): should rules always be inserted at the end? Should there
  679. // be some sort order?
  680. var stmt []bzl.Expr
  681. if f.function == nil {
  682. stmt = f.File.Stmt
  683. } else {
  684. stmt = f.function.stmt.Body
  685. }
  686. r.index = len(stmt)
  687. r.inserted = true
  688. f.Rules = append(f.Rules, r)
  689. }
  690. // IsEmpty returns true when the rule contains none of the attributes in attrs
  691. // for its kind. attrs should contain attributes that make the rule buildable
  692. // like srcs or deps and not descriptive attributes like name or visibility.
  693. func (r *Rule) IsEmpty(info KindInfo) bool {
  694. if info.NonEmptyAttrs == nil {
  695. return false
  696. }
  697. for k := range info.NonEmptyAttrs {
  698. if _, ok := r.attrs[k]; ok {
  699. return false
  700. }
  701. }
  702. return true
  703. }
  704. func (r *Rule) sync() {
  705. if !r.updated {
  706. return
  707. }
  708. r.updated = false
  709. for _, k := range []string{"srcs", "deps"} {
  710. if attr, ok := r.attrs[k]; ok {
  711. bzl.Walk(attr.RHS, sortExprLabels)
  712. }
  713. }
  714. call := r.expr.(*bzl.CallExpr)
  715. call.X.(*bzl.Ident).Name = r.kind
  716. if len(r.attrs) > 1 {
  717. call.ForceMultiLine = true
  718. }
  719. list := make([]bzl.Expr, 0, len(r.args)+len(r.attrs))
  720. list = append(list, r.args...)
  721. for _, attr := range r.attrs {
  722. list = append(list, attr)
  723. }
  724. sortedAttrs := list[len(r.args):]
  725. key := func(e bzl.Expr) string { return e.(*bzl.AssignExpr).LHS.(*bzl.Ident).Name }
  726. sort.SliceStable(sortedAttrs, func(i, j int) bool {
  727. ki := key(sortedAttrs[i])
  728. kj := key(sortedAttrs[j])
  729. if cmp := bt.NamePriority[ki] - bt.NamePriority[kj]; cmp != 0 {
  730. return cmp < 0
  731. }
  732. return ki < kj
  733. })
  734. call.List = list
  735. r.updated = false
  736. }
  737. // ShouldKeep returns whether e is marked with a "# keep" comment. Kept
  738. // expressions should not be removed or modified.
  739. func ShouldKeep(e bzl.Expr) bool {
  740. for _, c := range append(e.Comment().Before, e.Comment().Suffix...) {
  741. text := strings.TrimSpace(strings.TrimPrefix(c.Token, "#"))
  742. if text == "keep" {
  743. return true
  744. }
  745. }
  746. return false
  747. }
  748. // CheckInternalVisibility overrides the given visibility if the package is
  749. // internal.
  750. func CheckInternalVisibility(rel, visibility string) string {
  751. if i := strings.LastIndex(rel, "/internal/"); i >= 0 {
  752. visibility = fmt.Sprintf("//%s:__subpackages__", rel[:i])
  753. } else if strings.HasPrefix(rel, "internal/") {
  754. visibility = "//:__subpackages__"
  755. }
  756. return visibility
  757. }
  758. type byAttrName []KeyValue
  759. var _ sort.Interface = byAttrName{}
  760. func (s byAttrName) Len() int {
  761. return len(s)
  762. }
  763. func (s byAttrName) Less(i, j int) bool {
  764. if cmp := bt.NamePriority[s[i].Key] - bt.NamePriority[s[j].Key]; cmp != 0 {
  765. return cmp < 0
  766. }
  767. return s[i].Key < s[j].Key
  768. }
  769. func (s byAttrName) Swap(i, j int) {
  770. s[i], s[j] = s[j], s[i]
  771. }