edit.go 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105
  1. /*
  2. Copyright 2016 Google Inc. All Rights Reserved.
  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. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. // Package edit provides high-level auxiliary functions for AST manipulation
  14. // on BUILD files.
  15. package edit
  16. import (
  17. "fmt"
  18. "os"
  19. "path"
  20. "path/filepath"
  21. "regexp"
  22. "sort"
  23. "strconv"
  24. "strings"
  25. "github.com/bazelbuild/buildtools/build"
  26. "github.com/bazelbuild/buildtools/tables"
  27. "github.com/bazelbuild/buildtools/wspace"
  28. )
  29. var (
  30. // ShortenLabelsFlag if true converts added labels to short form , e.g. //foo:bar => :bar
  31. ShortenLabelsFlag = true
  32. // DeleteWithComments if true a list attribute will be be deleted in ListDelete, even if there is a comment attached to it
  33. DeleteWithComments = true
  34. )
  35. // ParseLabel parses a Blaze label (eg. //devtools/buildozer:rule), and returns
  36. // the repo name ("" for the main repo), package (with leading slashes trimmed)
  37. // and rule name (e.g. ["", "devtools/buildozer", "rule"]).
  38. func ParseLabel(target string) (string, string, string) {
  39. repo := ""
  40. if strings.HasPrefix(target, "@") {
  41. target = strings.TrimLeft(target, "@")
  42. parts := strings.SplitN(target, "/", 2)
  43. if len(parts) == 1 {
  44. // "@foo" -> "foo", "", "foo" (ie @foo//:foo)
  45. return target, "", target
  46. }
  47. repo = parts[0]
  48. target = "/" + parts[1]
  49. }
  50. // TODO(bazel-team): check if the next line can now be deleted
  51. target = strings.TrimRight(target, ":") // labels can end with ':'
  52. parts := strings.SplitN(target, ":", 2)
  53. parts[0] = strings.TrimPrefix(parts[0], "//")
  54. if len(parts) == 1 {
  55. if strings.HasPrefix(target, "//") || tables.StripLabelLeadingSlashes {
  56. // "//absolute/pkg" -> "absolute/pkg", "pkg"
  57. return repo, parts[0], path.Base(parts[0])
  58. }
  59. // "relative/label" -> "", "relative/label"
  60. return repo, "", parts[0]
  61. }
  62. return repo, parts[0], parts[1]
  63. }
  64. // ShortenLabel rewrites labels to use the canonical form (the form
  65. // recommended by build-style). This behavior can be disabled using the
  66. // --noshorten_labels flag for projects that consistently use long-form labels.
  67. // "//foo/bar:bar" => "//foo/bar", or ":bar" when possible.
  68. func ShortenLabel(label string, pkg string) string {
  69. if !ShortenLabelsFlag {
  70. return label
  71. }
  72. if !strings.Contains(label, "//") {
  73. // It doesn't look like a long label, so we preserve it.
  74. return label
  75. }
  76. repo, labelPkg, rule := ParseLabel(label)
  77. if repo == "" && labelPkg == pkg { // local label
  78. return ":" + rule
  79. }
  80. slash := strings.LastIndex(labelPkg, "/")
  81. if (slash >= 0 && labelPkg[slash+1:] == rule) || labelPkg == rule {
  82. if repo == "" {
  83. return "//" + labelPkg
  84. }
  85. return "@" + repo + "//" + labelPkg
  86. }
  87. if strings.HasPrefix(label, "@") && repo == rule && labelPkg == "" {
  88. return "@" + repo
  89. }
  90. return label
  91. }
  92. // LabelsEqual returns true if label1 and label2 are equal. The function
  93. // takes care of the optional ":" prefix and differences between long-form
  94. // labels and local labels.
  95. func LabelsEqual(label1, label2, pkg string) bool {
  96. str1 := strings.TrimPrefix(ShortenLabel(label1, pkg), ":")
  97. str2 := strings.TrimPrefix(ShortenLabel(label2, pkg), ":")
  98. return str1 == str2
  99. }
  100. // isFile returns true if the path refers to a regular file after following
  101. // symlinks.
  102. func isFile(path string) bool {
  103. path, err := filepath.EvalSymlinks(path)
  104. if err != nil {
  105. return false
  106. }
  107. info, err := os.Stat(path)
  108. if err != nil {
  109. return false
  110. }
  111. return info.Mode().IsRegular()
  112. }
  113. // InterpretLabelForWorkspaceLocation returns the name of the BUILD file to
  114. // edit, the full package name, and the rule. It takes a workspace-rooted
  115. // directory to use.
  116. func InterpretLabelForWorkspaceLocation(root string, target string) (buildFile string, pkg string, rule string) {
  117. repo, pkg, rule := ParseLabel(target)
  118. rootDir, relativePath := wspace.FindWorkspaceRoot(root)
  119. if repo != "" {
  120. files, err := wspace.FindRepoBuildFiles(rootDir)
  121. if err == nil {
  122. if buildFile, ok := files[repo]; ok {
  123. return buildFile, pkg, rule
  124. }
  125. }
  126. // TODO(rodrigoq): report error for other repos
  127. }
  128. if strings.HasPrefix(target, "//") {
  129. buildFile = path.Join(rootDir, pkg, "BUILD")
  130. return
  131. }
  132. if isFile(pkg) {
  133. // allow operation on other files like WORKSPACE
  134. buildFile = pkg
  135. pkg = path.Join(relativePath, filepath.Dir(pkg))
  136. return
  137. }
  138. if pkg != "" {
  139. buildFile = pkg + "/BUILD"
  140. } else {
  141. buildFile = "BUILD"
  142. }
  143. pkg = path.Join(relativePath, pkg)
  144. return
  145. }
  146. // InterpretLabel returns the name of the BUILD file to edit, the full
  147. // package name, and the rule. It uses the pwd for resolving workspace file paths.
  148. func InterpretLabel(target string) (buildFile string, pkg string, rule string) {
  149. return InterpretLabelForWorkspaceLocation("", target)
  150. }
  151. // ExprToRule returns a Rule from an Expr.
  152. // The boolean is false iff the Expr is not a function call, or does not have
  153. // the expected kind.
  154. func ExprToRule(expr build.Expr, kind string) (*build.Rule, bool) {
  155. call, ok := expr.(*build.CallExpr)
  156. if !ok {
  157. return nil, false
  158. }
  159. k, ok := call.X.(*build.Ident)
  160. if !ok || k.Name != kind {
  161. return nil, false
  162. }
  163. return &build.Rule{call, ""}, true
  164. }
  165. // ExistingPackageDeclaration returns the package declaration, or nil if there is none.
  166. func ExistingPackageDeclaration(f *build.File) *build.Rule {
  167. for _, stmt := range f.Stmt {
  168. if rule, ok := ExprToRule(stmt, "package"); ok {
  169. return rule
  170. }
  171. }
  172. return nil
  173. }
  174. // PackageDeclaration returns the package declaration. If it doesn't
  175. // exist, it is created at the top of the BUILD file, after optional
  176. // docstring, comments, and load statements.
  177. func PackageDeclaration(f *build.File) *build.Rule {
  178. if pkg := ExistingPackageDeclaration(f); pkg != nil {
  179. return pkg
  180. }
  181. all := []build.Expr{}
  182. added := false
  183. call := &build.CallExpr{X: &build.Ident{Name: "package"}}
  184. for _, stmt := range f.Stmt {
  185. switch stmt.(type) {
  186. case *build.CommentBlock, *build.LoadStmt, *build.StringExpr:
  187. // Skip docstring, comments, and load statements to
  188. // find a place to insert the package declaration.
  189. default:
  190. if !added {
  191. all = append(all, call)
  192. added = true
  193. }
  194. }
  195. all = append(all, stmt)
  196. }
  197. if !added { // In case the file is empty.
  198. all = append(all, call)
  199. }
  200. f.Stmt = all
  201. return &build.Rule{call, ""}
  202. }
  203. // RemoveEmptyPackage removes empty package declarations from the file, i.e.:
  204. // package()
  205. // This might appear because of a buildozer transformation (e.g. when removing a package
  206. // attribute). Removing it is required for the file to be valid.
  207. func RemoveEmptyPackage(f *build.File) *build.File {
  208. var all []build.Expr
  209. for _, stmt := range f.Stmt {
  210. if call, ok := stmt.(*build.CallExpr); ok {
  211. functionName, ok := call.X.(*build.Ident)
  212. if ok && functionName.Name == "package" && len(call.List) == 0 {
  213. continue
  214. }
  215. }
  216. all = append(all, stmt)
  217. }
  218. return &build.File{Path: f.Path, Comments: f.Comments, Stmt: all, Type: build.TypeBuild}
  219. }
  220. // InsertAfter inserts an expression after index i.
  221. func InsertAfter(i int, stmt []build.Expr, expr build.Expr) []build.Expr {
  222. i = i + 1 // index after the element at i
  223. result := make([]build.Expr, len(stmt)+1)
  224. copy(result[0:i], stmt[0:i])
  225. result[i] = expr
  226. copy(result[i+1:], stmt[i:])
  227. return result
  228. }
  229. // IndexOfLast finds the index of the last expression of a specific kind.
  230. func IndexOfLast(stmt []build.Expr, Kind string) int {
  231. lastIndex := -1
  232. for i, s := range stmt {
  233. sAsCallExpr, ok := s.(*build.CallExpr)
  234. if !ok {
  235. continue
  236. }
  237. literal, ok := sAsCallExpr.X.(*build.Ident)
  238. if ok && literal.Name == Kind {
  239. lastIndex = i
  240. }
  241. }
  242. return lastIndex
  243. }
  244. // InsertAfterLastOfSameKind inserts an expression after the last expression of the same kind.
  245. func InsertAfterLastOfSameKind(stmt []build.Expr, expr *build.CallExpr) []build.Expr {
  246. index := IndexOfLast(stmt, expr.X.(*build.Ident).Name)
  247. if index == -1 {
  248. return InsertAtEnd(stmt, expr)
  249. }
  250. return InsertAfter(index, stmt, expr)
  251. }
  252. // InsertAtEnd inserts an expression at the end of a list, before trailing comments.
  253. func InsertAtEnd(stmt []build.Expr, expr build.Expr) []build.Expr {
  254. var i int
  255. for i = len(stmt) - 1; i >= 0; i-- {
  256. _, ok := stmt[i].(*build.CommentBlock)
  257. if !ok {
  258. break
  259. }
  260. }
  261. return InsertAfter(i, stmt, expr)
  262. }
  263. // FindRuleByName returns the rule in the file that has the given name.
  264. // If the name is "__pkg__", it returns the global package declaration.
  265. func FindRuleByName(f *build.File, name string) *build.Rule {
  266. if name == "__pkg__" {
  267. return PackageDeclaration(f)
  268. }
  269. _, rule := IndexOfRuleByName(f, name)
  270. return rule
  271. }
  272. // IndexOfRuleByName returns the index (in f.Stmt) of the CallExpr which defines a rule named `name`, or -1 if it doesn't exist.
  273. func IndexOfRuleByName(f *build.File, name string) (int, *build.Rule) {
  274. linenum := -1
  275. if strings.HasPrefix(name, "%") {
  276. // "%<LINENUM>" will match the rule which begins at LINENUM.
  277. // This is for convenience, "%" is not a valid character in bazel targets.
  278. if result, err := strconv.Atoi(name[1:]); err == nil {
  279. linenum = result
  280. }
  281. }
  282. for i, stmt := range f.Stmt {
  283. call, ok := stmt.(*build.CallExpr)
  284. if !ok {
  285. continue
  286. }
  287. r := f.Rule(call)
  288. start, _ := call.X.Span()
  289. if r.Name() == name || start.Line == linenum {
  290. return i, r
  291. }
  292. }
  293. return -1, nil
  294. }
  295. // FindExportedFile returns the first exports_files call which contains the
  296. // file 'name', or nil if not found
  297. func FindExportedFile(f *build.File, name string) *build.Rule {
  298. for _, r := range f.Rules("exports_files") {
  299. if len(r.Call.List) == 0 {
  300. continue
  301. }
  302. pkg := "" // Files are not affected by the package name
  303. if ListFind(r.Call.List[0], name, pkg) != nil {
  304. return r
  305. }
  306. }
  307. return nil
  308. }
  309. // DeleteRule returns the AST without the specified rule
  310. func DeleteRule(f *build.File, rule *build.Rule) *build.File {
  311. var all []build.Expr
  312. for _, stmt := range f.Stmt {
  313. if stmt == rule.Call {
  314. continue
  315. }
  316. all = append(all, stmt)
  317. }
  318. return &build.File{Path: f.Path, Comments: f.Comments, Stmt: all, Type: build.TypeBuild}
  319. }
  320. // DeleteRuleByName returns the AST without the rules that have the
  321. // given name.
  322. func DeleteRuleByName(f *build.File, name string) *build.File {
  323. var all []build.Expr
  324. for _, stmt := range f.Stmt {
  325. call, ok := stmt.(*build.CallExpr)
  326. if !ok {
  327. all = append(all, stmt)
  328. continue
  329. }
  330. r := f.Rule(call)
  331. if r.Name() != name {
  332. all = append(all, stmt)
  333. }
  334. }
  335. return &build.File{Path: f.Path, Comments: f.Comments, Stmt: all, Type: build.TypeBuild}
  336. }
  337. // DeleteRuleByKind removes the rules of the specified kind from the AST.
  338. // Returns an updated copy of f.
  339. func DeleteRuleByKind(f *build.File, kind string) *build.File {
  340. var all []build.Expr
  341. for _, stmt := range f.Stmt {
  342. call, ok := stmt.(*build.CallExpr)
  343. if !ok {
  344. all = append(all, stmt)
  345. continue
  346. }
  347. k, ok := call.X.(*build.Ident)
  348. if !ok || k.Name != kind {
  349. all = append(all, stmt)
  350. }
  351. }
  352. return &build.File{Path: f.Path, Comments: f.Comments, Stmt: all, Type: build.TypeBuild}
  353. }
  354. // AllLists returns all the lists concatenated in an expression.
  355. // For example, in: glob(["*.go"]) + [":rule"]
  356. // the function will return [[":rule"]].
  357. func AllLists(e build.Expr) []*build.ListExpr {
  358. switch e := e.(type) {
  359. case *build.ListExpr:
  360. return []*build.ListExpr{e}
  361. case *build.BinaryExpr:
  362. if e.Op == "+" {
  363. return append(AllLists(e.X), AllLists(e.Y)...)
  364. }
  365. }
  366. return nil
  367. }
  368. // AllSelects returns all the selects concatenated in an expression.
  369. func AllSelects(e build.Expr) []*build.CallExpr {
  370. switch e := e.(type) {
  371. case *build.BinaryExpr:
  372. if e.Op == "+" {
  373. return append(AllSelects(e.X), AllSelects(e.Y)...)
  374. }
  375. case *build.CallExpr:
  376. if x, ok := e.X.(*build.Ident); ok && x.Name == "select" {
  377. return []*build.CallExpr{e}
  378. }
  379. }
  380. return nil
  381. }
  382. // FirstList works in the same way as AllLists, except that it
  383. // returns only one list, or nil.
  384. func FirstList(e build.Expr) *build.ListExpr {
  385. switch e := e.(type) {
  386. case *build.ListExpr:
  387. return e
  388. case *build.BinaryExpr:
  389. if e.Op == "+" {
  390. li := FirstList(e.X)
  391. if li == nil {
  392. return FirstList(e.Y)
  393. }
  394. return li
  395. }
  396. }
  397. return nil
  398. }
  399. // AllStrings returns all the string literals concatenated in an expression.
  400. // For example, in: "foo" + x + "bar"
  401. // the function will return ["foo", "bar"].
  402. func AllStrings(e build.Expr) []*build.StringExpr {
  403. switch e := e.(type) {
  404. case *build.StringExpr:
  405. return []*build.StringExpr{e}
  406. case *build.BinaryExpr:
  407. if e.Op == "+" {
  408. return append(AllStrings(e.X), AllStrings(e.Y)...)
  409. }
  410. }
  411. return nil
  412. }
  413. // ListFind looks for a string in the list expression (which may be a
  414. // concatenation of lists). It returns the element if it is found. nil
  415. // otherwise.
  416. func ListFind(e build.Expr, item string, pkg string) *build.StringExpr {
  417. item = ShortenLabel(item, pkg)
  418. for _, li := range AllLists(e) {
  419. for _, elem := range li.List {
  420. str, ok := elem.(*build.StringExpr)
  421. if ok && LabelsEqual(str.Value, item, pkg) {
  422. return str
  423. }
  424. }
  425. }
  426. return nil
  427. }
  428. // hasComments returns whether the StringExpr literal has a comment attached to it.
  429. func hasComments(literal *build.StringExpr) bool {
  430. return len(literal.Before) > 0 || len(literal.Suffix) > 0
  431. }
  432. // ContainsComments returns whether the expr has a comment that includes str.
  433. func ContainsComments(expr build.Expr, str string) bool {
  434. str = strings.ToLower(str)
  435. com := expr.Comment()
  436. comments := append(com.Before, com.Suffix...)
  437. comments = append(comments, com.After...)
  438. for _, c := range comments {
  439. if strings.Contains(strings.ToLower(c.Token), str) {
  440. return true
  441. }
  442. }
  443. return false
  444. }
  445. // RemoveEmptySelectsAndConcatLists iterates the tree in order to turn
  446. // empty selects into empty lists and adjacent lists are concatenated
  447. func RemoveEmptySelectsAndConcatLists(e build.Expr) build.Expr {
  448. switch e := e.(type) {
  449. case *build.BinaryExpr:
  450. if e.Op == "+" {
  451. e.X = RemoveEmptySelectsAndConcatLists(e.X)
  452. e.Y = RemoveEmptySelectsAndConcatLists(e.Y)
  453. x, xIsList := e.X.(*build.ListExpr)
  454. y, yIsList := e.Y.(*build.ListExpr)
  455. if xIsList && yIsList {
  456. return &build.ListExpr{List: append(x.List, y.List...)}
  457. }
  458. if xIsList && len(x.List) == 0 {
  459. return e.Y
  460. }
  461. if yIsList && len(y.List) == 0 {
  462. return e.X
  463. }
  464. }
  465. case *build.CallExpr:
  466. if x, ok := e.X.(*build.Ident); ok && x.Name == "select" {
  467. if len(e.List) == 0 {
  468. return &build.ListExpr{List: []build.Expr{}}
  469. }
  470. if dict, ok := e.List[0].(*build.DictExpr); ok {
  471. for _, keyVal := range dict.List {
  472. if keyVal, ok := keyVal.(*build.KeyValueExpr); ok {
  473. val, ok := keyVal.Value.(*build.ListExpr)
  474. if !ok || len(val.List) > 0 {
  475. return e
  476. }
  477. } else {
  478. return e
  479. }
  480. }
  481. return &build.ListExpr{List: []build.Expr{}}
  482. }
  483. }
  484. }
  485. return e
  486. }
  487. // ComputeIntersection returns the intersection of the two lists given as parameters;
  488. // if the containing elements are not build.StringExpr, the result will be nil.
  489. func ComputeIntersection(list1, list2 []build.Expr) []build.Expr {
  490. if list1 == nil || list2 == nil {
  491. return nil
  492. }
  493. if len(list2) == 0 {
  494. return []build.Expr{}
  495. }
  496. i := 0
  497. for j, common := range list1 {
  498. if common, ok := common.(*build.StringExpr); ok {
  499. found := false
  500. for _, elem := range list2 {
  501. if str, ok := elem.(*build.StringExpr); ok {
  502. if str.Value == common.Value {
  503. found = true
  504. break
  505. }
  506. } else {
  507. return nil
  508. }
  509. }
  510. if found {
  511. list1[i] = list1[j]
  512. i++
  513. }
  514. } else {
  515. return nil
  516. }
  517. }
  518. return list1[:i]
  519. }
  520. // SelectListsIntersection returns the intersection of the lists of strings inside
  521. // the dictionary argument of the select expression given as a parameter
  522. func SelectListsIntersection(sel *build.CallExpr, pkg string) (intersection []build.Expr) {
  523. if len(sel.List) == 0 || len(sel.List) > 1 {
  524. return nil
  525. }
  526. dict, ok := sel.List[0].(*build.DictExpr)
  527. if !ok || len(dict.List) == 0 {
  528. return nil
  529. }
  530. if keyVal, ok := dict.List[0].(*build.KeyValueExpr); ok {
  531. if val, ok := keyVal.Value.(*build.ListExpr); ok {
  532. intersection = make([]build.Expr, len(val.List))
  533. copy(intersection, val.List)
  534. }
  535. }
  536. for _, keyVal := range dict.List[1:] {
  537. if keyVal, ok := keyVal.(*build.KeyValueExpr); ok {
  538. if val, ok := keyVal.Value.(*build.ListExpr); ok {
  539. intersection = ComputeIntersection(intersection, val.List)
  540. if len(intersection) == 0 {
  541. return intersection
  542. }
  543. } else {
  544. return nil
  545. }
  546. } else {
  547. return nil
  548. }
  549. }
  550. return intersection
  551. }
  552. // ResolveAttr extracts common elements of the lists inside select dictionaries
  553. // and adds them at attribute level rather than select level, as well as turns
  554. // empty selects into empty lists and concatenates adjacent lists
  555. func ResolveAttr(r *build.Rule, attr, pkg string) {
  556. var toExtract []build.Expr
  557. e := r.Attr(attr)
  558. if e == nil {
  559. return
  560. }
  561. for _, sel := range AllSelects(e) {
  562. intersection := SelectListsIntersection(sel, pkg)
  563. if intersection != nil {
  564. toExtract = append(toExtract, intersection...)
  565. }
  566. }
  567. for _, common := range toExtract {
  568. e = AddValueToList(e, pkg, common, false) // this will also remove them from selects
  569. }
  570. r.SetAttr(attr, RemoveEmptySelectsAndConcatLists(e))
  571. }
  572. // SelectDelete removes the item from all the lists which are values
  573. // in the dictionary of every select
  574. func SelectDelete(e build.Expr, item, pkg string, deleted **build.StringExpr) {
  575. for _, sel := range AllSelects(e) {
  576. if len(sel.List) == 0 {
  577. continue
  578. }
  579. if dict, ok := sel.List[0].(*build.DictExpr); ok {
  580. for _, keyVal := range dict.List {
  581. if keyVal, ok := keyVal.(*build.KeyValueExpr); ok {
  582. if val, ok := keyVal.Value.(*build.ListExpr); ok {
  583. RemoveFromList(val, item, pkg, deleted)
  584. }
  585. }
  586. }
  587. }
  588. }
  589. }
  590. // RemoveFromList removes one element from a ListExpr and stores
  591. // the deleted StringExpr at the address pointed by the last parameter
  592. func RemoveFromList(li *build.ListExpr, item, pkg string, deleted **build.StringExpr) {
  593. var all []build.Expr
  594. for _, elem := range li.List {
  595. if str, ok := elem.(*build.StringExpr); ok {
  596. if LabelsEqual(str.Value, item, pkg) && (DeleteWithComments || !hasComments(str)) {
  597. if deleted != nil {
  598. *deleted = str
  599. }
  600. continue
  601. }
  602. }
  603. all = append(all, elem)
  604. }
  605. li.List = all
  606. }
  607. // ListDelete deletes the item from a list expression in e and returns
  608. // the StringExpr deleted, or nil otherwise.
  609. func ListDelete(e build.Expr, item, pkg string) (deleted *build.StringExpr) {
  610. if unquoted, _, err := build.Unquote(item); err == nil {
  611. item = unquoted
  612. }
  613. deleted = nil
  614. item = ShortenLabel(item, pkg)
  615. for _, li := range AllLists(e) {
  616. RemoveFromList(li, item, pkg, &deleted)
  617. }
  618. SelectDelete(e, item, pkg, &deleted)
  619. return deleted
  620. }
  621. // ListAttributeDelete deletes string item from list attribute attr, deletes attr if empty,
  622. // and returns the StringExpr deleted, or nil otherwise.
  623. func ListAttributeDelete(rule *build.Rule, attr, item, pkg string) *build.StringExpr {
  624. deleted := ListDelete(rule.Attr(attr), item, pkg)
  625. if deleted != nil {
  626. if listExpr, ok := rule.Attr(attr).(*build.ListExpr); ok && len(listExpr.List) == 0 {
  627. rule.DelAttr(attr)
  628. }
  629. }
  630. return deleted
  631. }
  632. // ListReplace replaces old with value in all lists in e and returns a Boolean
  633. // to indicate whether the replacement was successful.
  634. func ListReplace(e build.Expr, old, value, pkg string) bool {
  635. replaced := false
  636. old = ShortenLabel(old, pkg)
  637. for _, li := range AllLists(e) {
  638. for k, elem := range li.List {
  639. str, ok := elem.(*build.StringExpr)
  640. if !ok || !LabelsEqual(str.Value, old, pkg) {
  641. continue
  642. }
  643. li.List[k] = &build.StringExpr{Value: ShortenLabel(value, pkg), Comments: *elem.Comment()}
  644. replaced = true
  645. }
  646. }
  647. return replaced
  648. }
  649. // ListSubstitute replaces strings matching a regular expression in all lists
  650. // in e and returns a Boolean to indicate whether the replacement was
  651. // successful.
  652. func ListSubstitute(e build.Expr, oldRegexp *regexp.Regexp, newTemplate string) bool {
  653. substituted := false
  654. for _, li := range AllLists(e) {
  655. for k, elem := range li.List {
  656. str, ok := elem.(*build.StringExpr)
  657. if !ok {
  658. continue
  659. }
  660. newValue, ok := stringSubstitute(str.Value, oldRegexp, newTemplate)
  661. if ok {
  662. li.List[k] = &build.StringExpr{Value: newValue, Comments: *elem.Comment()}
  663. substituted = true
  664. }
  665. }
  666. }
  667. return substituted
  668. }
  669. func stringSubstitute(oldValue string, oldRegexp *regexp.Regexp, newTemplate string) (string, bool) {
  670. match := oldRegexp.FindStringSubmatchIndex(oldValue)
  671. if match == nil {
  672. return oldValue, false
  673. }
  674. newValue := string(oldRegexp.ExpandString(nil, newTemplate, oldValue, match))
  675. if match[0] > 0 {
  676. newValue = oldValue[:match[0]] + newValue
  677. }
  678. if match[1] < len(oldValue) {
  679. newValue = newValue + oldValue[match[1]:]
  680. }
  681. return newValue, true
  682. }
  683. // isExprLessThan compares two Expr statements. Currently, only labels are supported.
  684. func isExprLessThan(x1, x2 build.Expr) bool {
  685. str1, ok1 := x1.(*build.StringExpr)
  686. str2, ok2 := x2.(*build.StringExpr)
  687. if ok1 != ok2 {
  688. return ok2
  689. }
  690. if ok1 && ok2 {
  691. // Labels starting with // are put at the end.
  692. pre1 := strings.HasPrefix(str1.Value, "//")
  693. pre2 := strings.HasPrefix(str2.Value, "//")
  694. if pre1 != pre2 {
  695. return pre2
  696. }
  697. return str1.Value < str2.Value
  698. }
  699. return false
  700. }
  701. func sortedInsert(list []build.Expr, item build.Expr) []build.Expr {
  702. i := 0
  703. for ; i < len(list); i++ {
  704. if isExprLessThan(item, list[i]) {
  705. break
  706. }
  707. }
  708. res := make([]build.Expr, 0, len(list)+1)
  709. res = append(res, list[:i]...)
  710. res = append(res, item)
  711. res = append(res, list[i:]...)
  712. return res
  713. }
  714. // attributeMustNotBeSorted returns true if the list in the attribute cannot be
  715. // sorted. For some attributes, it makes sense to try to do a sorted insert
  716. // (e.g. deps), even when buildifier will not sort it for conservative reasons.
  717. // For a few attributes, sorting will never make sense.
  718. func attributeMustNotBeSorted(rule, attr string) bool {
  719. // TODO(bazel-team): Come up with a more complete list.
  720. return attr == "args"
  721. }
  722. // getVariable returns the binary expression that assignes a variable to expr, if expr is
  723. // an identifier of a variable that vars contains a mapping for.
  724. func getVariable(expr build.Expr, vars *map[string]*build.AssignExpr) (varAssignment *build.AssignExpr) {
  725. if vars == nil {
  726. return nil
  727. }
  728. if literal, ok := expr.(*build.Ident); ok {
  729. if varAssignment = (*vars)[literal.Name]; varAssignment != nil {
  730. return varAssignment
  731. }
  732. }
  733. return nil
  734. }
  735. // AddValueToList adds a value to a list. If the expression is
  736. // not a list, a list with a single element is appended to the original
  737. // expression.
  738. func AddValueToList(oldList build.Expr, pkg string, item build.Expr, sorted bool) build.Expr {
  739. if oldList == nil {
  740. return &build.ListExpr{List: []build.Expr{item}}
  741. }
  742. str, ok := item.(*build.StringExpr)
  743. if ok {
  744. if ListFind(oldList, str.Value, pkg) != nil {
  745. // The value is already in the list.
  746. return oldList
  747. }
  748. SelectDelete(oldList, str.Value, pkg, nil)
  749. }
  750. li := FirstList(oldList)
  751. if li != nil {
  752. if sorted {
  753. li.List = sortedInsert(li.List, item)
  754. } else {
  755. li.List = append(li.List, item)
  756. }
  757. return oldList
  758. }
  759. list := &build.ListExpr{List: []build.Expr{item}}
  760. concat := &build.BinaryExpr{Op: "+", X: oldList, Y: list}
  761. return concat
  762. }
  763. // AddValueToListAttribute adds the given item to the list attribute identified by name and pkg.
  764. func AddValueToListAttribute(r *build.Rule, name string, pkg string, item build.Expr, vars *map[string]*build.AssignExpr) {
  765. old := r.Attr(name)
  766. sorted := !attributeMustNotBeSorted(r.Kind(), name)
  767. if varAssignment := getVariable(old, vars); varAssignment != nil {
  768. varAssignment.RHS = AddValueToList(varAssignment.RHS, pkg, item, sorted)
  769. } else {
  770. r.SetAttr(name, AddValueToList(old, pkg, item, sorted))
  771. }
  772. }
  773. // MoveAllListAttributeValues moves all values from list attribute oldAttr to newAttr,
  774. // and deletes oldAttr.
  775. func MoveAllListAttributeValues(rule *build.Rule, oldAttr, newAttr, pkg string, vars *map[string]*build.AssignExpr) error {
  776. if rule.Attr(oldAttr) == nil {
  777. return fmt.Errorf("no attribute %s found in %s", oldAttr, rule.Name())
  778. }
  779. if rule.Attr(newAttr) == nil {
  780. RenameAttribute(rule, oldAttr, newAttr)
  781. return nil
  782. }
  783. if listExpr, ok := rule.Attr(oldAttr).(*build.ListExpr); ok {
  784. for _, val := range listExpr.List {
  785. AddValueToListAttribute(rule, newAttr, pkg, val, vars)
  786. }
  787. rule.DelAttr(oldAttr)
  788. return nil
  789. }
  790. return fmt.Errorf("%s already exists and %s is not a simple list", newAttr, oldAttr)
  791. }
  792. // DictionarySet looks for the key in the dictionary expression. If value is not nil,
  793. // it replaces the current value with it. In all cases, it returns the current value.
  794. func DictionarySet(dict *build.DictExpr, key string, value build.Expr) build.Expr {
  795. for _, e := range dict.List {
  796. kv, _ := e.(*build.KeyValueExpr)
  797. if k, ok := kv.Key.(*build.StringExpr); ok && k.Value == key {
  798. if value != nil {
  799. kv.Value = value
  800. }
  801. return kv.Value
  802. }
  803. }
  804. if value != nil {
  805. kv := &build.KeyValueExpr{Key: &build.StringExpr{Value: key}, Value: value}
  806. dict.List = append(dict.List, kv)
  807. }
  808. return nil
  809. }
  810. // DictionaryGet looks for the key in the dictionary expression, and returns the
  811. // current value. If it is unset, it returns nil.
  812. func DictionaryGet(dict *build.DictExpr, key string) build.Expr {
  813. for _, e := range dict.List {
  814. kv, ok := e.(*build.KeyValueExpr)
  815. if !ok {
  816. continue
  817. }
  818. if k, ok := kv.Key.(*build.StringExpr); ok && k.Value == key {
  819. return kv.Value
  820. }
  821. }
  822. return nil
  823. }
  824. // DictionaryDelete looks for the key in the dictionary expression. If the key exists,
  825. // it removes the key-value pair and returns it. Otherwise it returns nil.
  826. func DictionaryDelete(dict *build.DictExpr, key string) (deleted build.Expr) {
  827. if unquoted, _, err := build.Unquote(key); err == nil {
  828. key = unquoted
  829. }
  830. deleted = nil
  831. var all []build.Expr
  832. for _, e := range dict.List {
  833. kv, _ := e.(*build.KeyValueExpr)
  834. if k, ok := kv.Key.(*build.StringExpr); ok {
  835. if k.Value == key {
  836. deleted = kv
  837. } else {
  838. all = append(all, e)
  839. }
  840. }
  841. }
  842. dict.List = all
  843. return deleted
  844. }
  845. // RenameAttribute renames an attribute in a rule.
  846. func RenameAttribute(r *build.Rule, oldName, newName string) error {
  847. if r.Attr(newName) != nil {
  848. return fmt.Errorf("attribute %s already exists in rule %s", newName, r.Name())
  849. }
  850. for _, kv := range r.Call.List {
  851. as, ok := kv.(*build.AssignExpr)
  852. if !ok {
  853. continue
  854. }
  855. k, ok := as.LHS.(*build.Ident)
  856. if !ok || k.Name != oldName {
  857. continue
  858. }
  859. k.Name = newName
  860. return nil
  861. }
  862. return fmt.Errorf("no attribute %s found in rule %s", oldName, r.Name())
  863. }
  864. // EditFunction is a wrapper around build.Edit. The callback is called only on
  865. // functions 'name'.
  866. func EditFunction(v build.Expr, name string, f func(x *build.CallExpr, stk []build.Expr) build.Expr) build.Expr {
  867. return build.Edit(v, func(expr build.Expr, stk []build.Expr) build.Expr {
  868. call, ok := expr.(*build.CallExpr)
  869. if !ok {
  870. return nil
  871. }
  872. fct, ok := call.X.(*build.Ident)
  873. if !ok || fct.Name != name {
  874. return nil
  875. }
  876. return f(call, stk)
  877. })
  878. }
  879. // UsedSymbols returns the set of symbols used in the BUILD file (variables, function names).
  880. func UsedSymbols(stmt build.Expr) map[string]bool {
  881. symbols := make(map[string]bool)
  882. build.Walk(stmt, func(expr build.Expr, stack []build.Expr) {
  883. // Don't traverse inside load statements
  884. if len(stack) > 0 {
  885. if _, ok := stack[len(stack)-1].(*build.LoadStmt); ok {
  886. return
  887. }
  888. }
  889. literal, ok := expr.(*build.Ident)
  890. if !ok {
  891. return
  892. }
  893. // Check if we are on the left-side of an assignment
  894. for _, e := range stack {
  895. if as, ok := e.(*build.AssignExpr); ok {
  896. if as.LHS == expr {
  897. return
  898. }
  899. }
  900. }
  901. symbols[literal.Name] = true
  902. })
  903. return symbols
  904. }
  905. // NewLoad creates a new LoadStmt node
  906. func NewLoad(location string, from, to []string) *build.LoadStmt {
  907. load := &build.LoadStmt{
  908. Module: &build.StringExpr{
  909. Value: location,
  910. },
  911. ForceCompact: true,
  912. }
  913. for i := range from {
  914. load.From = append(load.From, &build.Ident{Name: from[i]})
  915. load.To = append(load.To, &build.Ident{Name: to[i]})
  916. }
  917. return load
  918. }
  919. // AppendToLoad appends symbols to an existing load statement
  920. // Returns true if the statement was acually edited (if the required symbols haven't been
  921. // loaded yet)
  922. func AppendToLoad(load *build.LoadStmt, from, to []string) bool {
  923. symbolsToLoad := make(map[string]string)
  924. for i, s := range to {
  925. symbolsToLoad[s] = from[i]
  926. }
  927. for _, ident := range load.To {
  928. delete(symbolsToLoad, ident.Name) // Already loaded.
  929. }
  930. if len(symbolsToLoad) == 0 {
  931. return false
  932. }
  933. // Append the remaining loads to the load statement.
  934. sortedSymbols := []string{}
  935. for s := range symbolsToLoad {
  936. sortedSymbols = append(sortedSymbols, s)
  937. }
  938. sort.Strings(sortedSymbols)
  939. for _, s := range sortedSymbols {
  940. load.From = append(load.From, &build.Ident{Name: symbolsToLoad[s]})
  941. load.To = append(load.To, &build.Ident{Name: s})
  942. }
  943. return true
  944. }
  945. // appendLoad tries to find an existing load location and append symbols to it.
  946. func appendLoad(stmts []build.Expr, location string, from, to []string) bool {
  947. symbolsToLoad := make(map[string]string)
  948. for i, s := range to {
  949. symbolsToLoad[s] = from[i]
  950. }
  951. var lastLoad *build.LoadStmt
  952. for _, s := range stmts {
  953. load, ok := s.(*build.LoadStmt)
  954. if !ok {
  955. continue
  956. }
  957. if load.Module.Value != location {
  958. continue // Loads a different file.
  959. }
  960. for _, ident := range load.To {
  961. delete(symbolsToLoad, ident.Name) // Already loaded.
  962. }
  963. // Remember the last insert location, but potentially remove more symbols
  964. // that are already loaded in other subsequent calls.
  965. lastLoad = load
  966. }
  967. if lastLoad == nil {
  968. return false
  969. }
  970. // Append the remaining loads to the last load location.
  971. from = []string{}
  972. to = []string{}
  973. for t, f := range symbolsToLoad {
  974. from = append(from, f)
  975. to = append(to, t)
  976. }
  977. AppendToLoad(lastLoad, from, to)
  978. return true
  979. }
  980. // InsertLoad inserts a load statement at the top of the list of statements.
  981. // The load statement is constructed using a string location and two slices of from- and to-symbols.
  982. // The function panics if the slices aren't of the same lentgh. Symbols that are already loaded
  983. // from the given filepath are ignored. If stmts already contains a load for the
  984. // location in arguments, appends the symbols to load to it.
  985. func InsertLoad(stmts []build.Expr, location string, from, to []string) []build.Expr {
  986. if len(from) != len(to) {
  987. panic(fmt.Errorf("length mismatch: %v (from) and %v (to)", len(from), len(to)))
  988. }
  989. if appendLoad(stmts, location, from, to) {
  990. return stmts
  991. }
  992. load := NewLoad(location, from, to)
  993. var all []build.Expr
  994. added := false
  995. for i, stmt := range stmts {
  996. _, isComment := stmt.(*build.CommentBlock)
  997. _, isString := stmt.(*build.StringExpr)
  998. isDocString := isString && i == 0
  999. if isComment || isDocString || added {
  1000. all = append(all, stmt)
  1001. continue
  1002. }
  1003. all = append(all, load)
  1004. all = append(all, stmt)
  1005. added = true
  1006. }
  1007. if !added { // Empty file or just comments.
  1008. all = append(all, load)
  1009. }
  1010. return all
  1011. }