merge.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  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
  13. import (
  14. "errors"
  15. "fmt"
  16. "log"
  17. "sort"
  18. bzl "github.com/bazelbuild/buildtools/build"
  19. )
  20. // MergeRules copies information from src into dst, usually discarding
  21. // information in dst when they have the same attributes.
  22. //
  23. // If dst is marked with a "# keep" comment, either above the rule or as
  24. // a suffix, nothing will be changed.
  25. //
  26. // If src has an attribute that is not in dst, it will be copied into dst.
  27. //
  28. // If src and dst have the same attribute and the attribute is mergeable and the
  29. // attribute in dst is not marked with a "# keep" comment, values in the dst
  30. // attribute not marked with a "# keep" comment will be dropped, and values from
  31. // src will be copied in.
  32. //
  33. // If dst has an attribute not in src, and the attribute is mergeable and not
  34. // marked with a "# keep" comment, values in the attribute not marked with
  35. // a "# keep" comment will be dropped. If the attribute is empty afterward,
  36. // it will be deleted.
  37. func MergeRules(src, dst *Rule, mergeable map[string]bool, filename string) {
  38. if dst.ShouldKeep() {
  39. return
  40. }
  41. // Process attributes that are in dst but not in src.
  42. for key, dstAttr := range dst.attrs {
  43. if _, ok := src.attrs[key]; ok || !mergeable[key] || ShouldKeep(dstAttr) {
  44. continue
  45. }
  46. dstValue := dstAttr.Y
  47. if mergedValue, err := mergeExprs(nil, dstValue); err != nil {
  48. start, end := dstValue.Span()
  49. log.Printf("%s:%d.%d-%d.%d: could not merge expression", filename, start.Line, start.LineRune, end.Line, end.LineRune)
  50. } else if mergedValue == nil {
  51. dst.DelAttr(key)
  52. } else {
  53. dst.SetAttr(key, mergedValue)
  54. }
  55. }
  56. // Merge attributes from src into dst.
  57. for key, srcAttr := range src.attrs {
  58. srcValue := srcAttr.Y
  59. if dstAttr, ok := dst.attrs[key]; !ok {
  60. dst.SetAttr(key, srcValue)
  61. } else if mergeable[key] && !ShouldKeep(dstAttr) {
  62. dstValue := dstAttr.Y
  63. if mergedValue, err := mergeExprs(srcValue, dstValue); err != nil {
  64. start, end := dstValue.Span()
  65. log.Printf("%s:%d.%d-%d.%d: could not merge expression", filename, start.Line, start.LineRune, end.Line, end.LineRune)
  66. } else {
  67. dst.SetAttr(key, mergedValue)
  68. }
  69. }
  70. }
  71. }
  72. // mergeExprs combines information from src and dst and returns a merged
  73. // expression. dst may be modified during this process. The returned expression
  74. // may be different from dst when a structural change is needed.
  75. //
  76. // The following kinds of expressions are recognized.
  77. //
  78. // * nil
  79. // * strings (can only be merged with strings)
  80. // * lists of strings
  81. // * a call to select with a dict argument. The dict keys must be strings,
  82. // and the values must be lists of strings.
  83. // * a list of strings combined with a select call using +. The list must
  84. // be the left operand.
  85. //
  86. // An error is returned if the expressions can't be merged, for example
  87. // because they are not in one of the above formats.
  88. func mergeExprs(src, dst bzl.Expr) (bzl.Expr, error) {
  89. if ShouldKeep(dst) {
  90. return nil, nil
  91. }
  92. if src == nil && (dst == nil || isScalar(dst)) {
  93. return nil, nil
  94. }
  95. if isScalar(src) {
  96. return src, nil
  97. }
  98. srcExprs, err := extractPlatformStringsExprs(src)
  99. if err != nil {
  100. return nil, err
  101. }
  102. dstExprs, err := extractPlatformStringsExprs(dst)
  103. if err != nil {
  104. return nil, err
  105. }
  106. mergedExprs, err := mergePlatformStringsExprs(srcExprs, dstExprs)
  107. if err != nil {
  108. return nil, err
  109. }
  110. return makePlatformStringsExpr(mergedExprs), nil
  111. }
  112. func mergePlatformStringsExprs(src, dst platformStringsExprs) (platformStringsExprs, error) {
  113. var ps platformStringsExprs
  114. var err error
  115. ps.generic = mergeList(src.generic, dst.generic)
  116. if ps.os, err = mergeDict(src.os, dst.os); err != nil {
  117. return platformStringsExprs{}, err
  118. }
  119. if ps.arch, err = mergeDict(src.arch, dst.arch); err != nil {
  120. return platformStringsExprs{}, err
  121. }
  122. if ps.platform, err = mergeDict(src.platform, dst.platform); err != nil {
  123. return platformStringsExprs{}, err
  124. }
  125. return ps, nil
  126. }
  127. func mergeList(src, dst *bzl.ListExpr) *bzl.ListExpr {
  128. if dst == nil {
  129. return src
  130. }
  131. if src == nil {
  132. src = &bzl.ListExpr{List: []bzl.Expr{}}
  133. }
  134. // Build a list of strings from the src list and keep matching strings
  135. // in the dst list. This preserves comments. Also keep anything with
  136. // a "# keep" comment, whether or not it's in the src list.
  137. srcSet := make(map[string]bool)
  138. for _, v := range src.List {
  139. if s := stringValue(v); s != "" {
  140. srcSet[s] = true
  141. }
  142. }
  143. var merged []bzl.Expr
  144. kept := make(map[string]bool)
  145. keepComment := false
  146. for _, v := range dst.List {
  147. s := stringValue(v)
  148. if keep := ShouldKeep(v); keep || srcSet[s] {
  149. keepComment = keepComment || keep
  150. merged = append(merged, v)
  151. if s != "" {
  152. kept[s] = true
  153. }
  154. }
  155. }
  156. // Add anything in the src list that wasn't kept.
  157. for _, v := range src.List {
  158. if s := stringValue(v); kept[s] {
  159. continue
  160. }
  161. merged = append(merged, v)
  162. }
  163. if len(merged) == 0 {
  164. return nil
  165. }
  166. return &bzl.ListExpr{
  167. List: merged,
  168. ForceMultiLine: src.ForceMultiLine || dst.ForceMultiLine || keepComment,
  169. }
  170. }
  171. func mergeDict(src, dst *bzl.DictExpr) (*bzl.DictExpr, error) {
  172. if dst == nil {
  173. return src, nil
  174. }
  175. if src == nil {
  176. src = &bzl.DictExpr{List: []bzl.Expr{}}
  177. }
  178. var entries []*dictEntry
  179. entryMap := make(map[string]*dictEntry)
  180. for _, kv := range dst.List {
  181. k, v, err := dictEntryKeyValue(kv)
  182. if err != nil {
  183. return nil, err
  184. }
  185. if _, ok := entryMap[k]; ok {
  186. return nil, fmt.Errorf("dst dict contains more than one case named %q", k)
  187. }
  188. e := &dictEntry{key: k, dstValue: v}
  189. entries = append(entries, e)
  190. entryMap[k] = e
  191. }
  192. for _, kv := range src.List {
  193. k, v, err := dictEntryKeyValue(kv)
  194. if err != nil {
  195. return nil, err
  196. }
  197. e, ok := entryMap[k]
  198. if !ok {
  199. e = &dictEntry{key: k}
  200. entries = append(entries, e)
  201. entryMap[k] = e
  202. }
  203. e.srcValue = v
  204. }
  205. keys := make([]string, 0, len(entries))
  206. haveDefault := false
  207. for _, e := range entries {
  208. e.mergedValue = mergeList(e.srcValue, e.dstValue)
  209. if e.key == "//conditions:default" {
  210. // Keep the default case, even if it's empty.
  211. haveDefault = true
  212. if e.mergedValue == nil {
  213. e.mergedValue = &bzl.ListExpr{}
  214. }
  215. } else if e.mergedValue != nil {
  216. keys = append(keys, e.key)
  217. }
  218. }
  219. if len(keys) == 0 && (!haveDefault || len(entryMap["//conditions:default"].mergedValue.List) == 0) {
  220. return nil, nil
  221. }
  222. sort.Strings(keys)
  223. // Always put the default case last.
  224. if haveDefault {
  225. keys = append(keys, "//conditions:default")
  226. }
  227. mergedEntries := make([]bzl.Expr, len(keys))
  228. for i, k := range keys {
  229. e := entryMap[k]
  230. mergedEntries[i] = &bzl.KeyValueExpr{
  231. Key: &bzl.StringExpr{Value: e.key},
  232. Value: e.mergedValue,
  233. }
  234. }
  235. return &bzl.DictExpr{List: mergedEntries, ForceMultiLine: true}, nil
  236. }
  237. type dictEntry struct {
  238. key string
  239. dstValue, srcValue, mergedValue *bzl.ListExpr
  240. }
  241. // SquashRules copies information from src into dst without discarding
  242. // information in dst. SquashRules detects duplicate elements in lists and
  243. // dictionaries, but it doesn't sort elements after squashing. If squashing
  244. // fails because the expression is not understood, an error is returned,
  245. // and neither rule is modified.
  246. func SquashRules(src, dst *Rule, filename string) error {
  247. if dst.ShouldKeep() {
  248. return nil
  249. }
  250. for key, srcAttr := range src.attrs {
  251. srcValue := srcAttr.Y
  252. if dstAttr, ok := dst.attrs[key]; !ok {
  253. dst.SetAttr(key, srcValue)
  254. } else if !ShouldKeep(dstAttr) {
  255. dstValue := dstAttr.Y
  256. if squashedValue, err := squashExprs(srcValue, dstValue); err != nil {
  257. start, end := dstValue.Span()
  258. return fmt.Errorf("%s:%d.%d-%d.%d: could not squash expression", filename, start.Line, start.LineRune, end.Line, end.LineRune)
  259. } else {
  260. dst.SetAttr(key, squashedValue)
  261. }
  262. }
  263. }
  264. dst.call.Comments.Before = append(dst.call.Comments.Before, src.call.Comments.Before...)
  265. dst.call.Comments.Suffix = append(dst.call.Comments.Suffix, src.call.Comments.Suffix...)
  266. dst.call.Comments.After = append(dst.call.Comments.After, src.call.Comments.After...)
  267. return nil
  268. }
  269. func squashExprs(src, dst bzl.Expr) (bzl.Expr, error) {
  270. if ShouldKeep(dst) {
  271. return dst, nil
  272. }
  273. if isScalar(dst) {
  274. // may lose src, but they should always be the same.
  275. return dst, nil
  276. }
  277. srcExprs, err := extractPlatformStringsExprs(src)
  278. if err != nil {
  279. return nil, err
  280. }
  281. dstExprs, err := extractPlatformStringsExprs(dst)
  282. if err != nil {
  283. return nil, err
  284. }
  285. squashedExprs, err := squashPlatformStringsExprs(srcExprs, dstExprs)
  286. if err != nil {
  287. return nil, err
  288. }
  289. return makePlatformStringsExpr(squashedExprs), nil
  290. }
  291. func squashPlatformStringsExprs(x, y platformStringsExprs) (platformStringsExprs, error) {
  292. var ps platformStringsExprs
  293. var err error
  294. if ps.generic, err = squashList(x.generic, y.generic); err != nil {
  295. return platformStringsExprs{}, err
  296. }
  297. if ps.os, err = squashDict(x.os, y.os); err != nil {
  298. return platformStringsExprs{}, err
  299. }
  300. if ps.arch, err = squashDict(x.arch, y.arch); err != nil {
  301. return platformStringsExprs{}, err
  302. }
  303. if ps.platform, err = squashDict(x.platform, y.platform); err != nil {
  304. return platformStringsExprs{}, err
  305. }
  306. return ps, nil
  307. }
  308. func squashList(x, y *bzl.ListExpr) (*bzl.ListExpr, error) {
  309. if x == nil {
  310. return y, nil
  311. }
  312. if y == nil {
  313. return x, nil
  314. }
  315. ls := makeListSquasher()
  316. for _, e := range x.List {
  317. s, ok := e.(*bzl.StringExpr)
  318. if !ok {
  319. return nil, errors.New("could not squash non-string")
  320. }
  321. ls.add(s)
  322. }
  323. for _, e := range y.List {
  324. s, ok := e.(*bzl.StringExpr)
  325. if !ok {
  326. return nil, errors.New("could not squash non-string")
  327. }
  328. ls.add(s)
  329. }
  330. squashed := ls.list()
  331. squashed.Comments.Before = append(x.Comments.Before, y.Comments.Before...)
  332. squashed.Comments.Suffix = append(x.Comments.Suffix, y.Comments.Suffix...)
  333. squashed.Comments.After = append(x.Comments.After, y.Comments.After...)
  334. return squashed, nil
  335. }
  336. func squashDict(x, y *bzl.DictExpr) (*bzl.DictExpr, error) {
  337. if x == nil {
  338. return y, nil
  339. }
  340. if y == nil {
  341. return x, nil
  342. }
  343. cases := make(map[string]*bzl.KeyValueExpr)
  344. addCase := func(e bzl.Expr) error {
  345. kv := e.(*bzl.KeyValueExpr)
  346. key, ok := kv.Key.(*bzl.StringExpr)
  347. if !ok {
  348. return errors.New("could not squash non-string dict key")
  349. }
  350. if _, ok := kv.Value.(*bzl.ListExpr); !ok {
  351. return errors.New("could not squash non-list dict value")
  352. }
  353. if c, ok := cases[key.Value]; ok {
  354. if sq, err := squashList(kv.Value.(*bzl.ListExpr), c.Value.(*bzl.ListExpr)); err != nil {
  355. return err
  356. } else {
  357. c.Value = sq
  358. }
  359. } else {
  360. kvCopy := *kv
  361. cases[key.Value] = &kvCopy
  362. }
  363. return nil
  364. }
  365. for _, e := range x.List {
  366. if err := addCase(e); err != nil {
  367. return nil, err
  368. }
  369. }
  370. for _, e := range y.List {
  371. if err := addCase(e); err != nil {
  372. return nil, err
  373. }
  374. }
  375. keys := make([]string, 0, len(cases))
  376. haveDefault := false
  377. for k := range cases {
  378. if k == "//conditions:default" {
  379. haveDefault = true
  380. continue
  381. }
  382. keys = append(keys, k)
  383. }
  384. sort.Strings(keys)
  385. if haveDefault {
  386. keys = append(keys, "//conditions:default") // must be last
  387. }
  388. squashed := *x
  389. squashed.Comments.Before = append(x.Comments.Before, y.Comments.Before...)
  390. squashed.Comments.Suffix = append(x.Comments.Suffix, y.Comments.Suffix...)
  391. squashed.Comments.After = append(x.Comments.After, y.Comments.After...)
  392. squashed.List = make([]bzl.Expr, 0, len(cases))
  393. for _, k := range keys {
  394. squashed.List = append(squashed.List, cases[k])
  395. }
  396. return &squashed, nil
  397. }
  398. // listSquasher builds a sorted, deduplicated list of string expressions. If
  399. // a string expression is added multiple times, comments are consolidated.
  400. // The original expressions are not modified.
  401. type listSquasher struct {
  402. unique map[string]*bzl.StringExpr
  403. seenComments map[elemComment]bool
  404. }
  405. type elemComment struct {
  406. elem, com string
  407. }
  408. func makeListSquasher() listSquasher {
  409. return listSquasher{
  410. unique: make(map[string]*bzl.StringExpr),
  411. seenComments: make(map[elemComment]bool),
  412. }
  413. }
  414. func (ls *listSquasher) add(s *bzl.StringExpr) {
  415. sCopy, ok := ls.unique[s.Value]
  416. if !ok {
  417. // Make a copy of s. We may modify it when we consolidate comments from
  418. // duplicate strings. We don't want to modify the original in case this
  419. // function fails (due to a later failed pattern match).
  420. sCopy = new(bzl.StringExpr)
  421. *sCopy = *s
  422. sCopy.Comments.Before = make([]bzl.Comment, 0, len(s.Comments.Before))
  423. sCopy.Comments.Suffix = make([]bzl.Comment, 0, len(s.Comments.Suffix))
  424. ls.unique[s.Value] = sCopy
  425. }
  426. for _, c := range s.Comment().Before {
  427. if key := (elemComment{s.Value, c.Token}); !ls.seenComments[key] {
  428. sCopy.Comments.Before = append(sCopy.Comments.Before, c)
  429. ls.seenComments[key] = true
  430. }
  431. }
  432. for _, c := range s.Comment().Suffix {
  433. if key := (elemComment{s.Value, c.Token}); !ls.seenComments[key] {
  434. sCopy.Comments.Suffix = append(sCopy.Comments.Suffix, c)
  435. ls.seenComments[key] = true
  436. }
  437. }
  438. }
  439. func (ls *listSquasher) list() *bzl.ListExpr {
  440. sortedExprs := make([]bzl.Expr, 0, len(ls.unique))
  441. for _, e := range ls.unique {
  442. sortedExprs = append(sortedExprs, e)
  443. }
  444. sort.Slice(sortedExprs, func(i, j int) bool {
  445. return sortedExprs[i].(*bzl.StringExpr).Value < sortedExprs[j].(*bzl.StringExpr).Value
  446. })
  447. return &bzl.ListExpr{List: sortedExprs}
  448. }