androidmk.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. // Copyright 2017 Google Inc. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package androidmk
  15. import (
  16. "bytes"
  17. "fmt"
  18. "strings"
  19. "text/scanner"
  20. "android/soong/bpfix/bpfix"
  21. mkparser "android/soong/androidmk/parser"
  22. bpparser "github.com/google/blueprint/parser"
  23. )
  24. // TODO: non-expanded variables with expressions
  25. type bpFile struct {
  26. comments []*bpparser.CommentGroup
  27. defs []bpparser.Definition
  28. localAssignments map[string]*bpparser.Property
  29. globalAssignments map[string]*bpparser.Expression
  30. variableRenames map[string]string
  31. scope mkparser.Scope
  32. module *bpparser.Module
  33. mkPos scanner.Position // Position of the last handled line in the makefile
  34. bpPos scanner.Position // Position of the last emitted line to the blueprint file
  35. inModule bool
  36. }
  37. var invalidVariableStringToReplacement = map[string]string{
  38. "-": "_dash_",
  39. }
  40. // Fix steps that should only run in the androidmk tool, i.e. should only be applied to
  41. // newly-converted Android.bp files.
  42. var fixSteps = bpfix.FixStepsExtension{
  43. Name: "androidmk",
  44. Steps: []bpfix.FixStep{
  45. {
  46. Name: "RewriteRuntimeResourceOverlay",
  47. Fix: bpfix.RewriteRuntimeResourceOverlay,
  48. },
  49. },
  50. }
  51. func init() {
  52. bpfix.RegisterFixStepExtension(&fixSteps)
  53. }
  54. func (f *bpFile) insertComment(s string) {
  55. f.comments = append(f.comments, &bpparser.CommentGroup{
  56. Comments: []*bpparser.Comment{
  57. &bpparser.Comment{
  58. Comment: []string{s},
  59. Slash: f.bpPos,
  60. },
  61. },
  62. })
  63. f.bpPos.Offset += len(s)
  64. }
  65. func (f *bpFile) insertExtraComment(s string) {
  66. f.insertComment(s)
  67. f.bpPos.Line++
  68. }
  69. // records that the given node failed to be converted and includes an explanatory message
  70. func (f *bpFile) errorf(failedNode mkparser.Node, message string, args ...interface{}) {
  71. orig := failedNode.Dump()
  72. message = fmt.Sprintf(message, args...)
  73. f.addErrorText(fmt.Sprintf("// ANDROIDMK TRANSLATION ERROR: %s", message))
  74. lines := strings.Split(orig, "\n")
  75. for _, l := range lines {
  76. f.insertExtraComment("// " + l)
  77. }
  78. }
  79. // records that something unexpected occurred
  80. func (f *bpFile) warnf(message string, args ...interface{}) {
  81. message = fmt.Sprintf(message, args...)
  82. f.addErrorText(fmt.Sprintf("// ANDROIDMK TRANSLATION WARNING: %s", message))
  83. }
  84. // adds the given error message as-is to the bottom of the (in-progress) file
  85. func (f *bpFile) addErrorText(message string) {
  86. f.insertExtraComment(message)
  87. }
  88. func (f *bpFile) setMkPos(pos, end scanner.Position) {
  89. // It is unusual but not forbidden for pos.Line to be smaller than f.mkPos.Line
  90. // For example:
  91. //
  92. // if true # this line is emitted 1st
  93. // if true # this line is emitted 2nd
  94. // some-target: some-file # this line is emitted 3rd
  95. // echo doing something # this recipe is emitted 6th
  96. // endif #some comment # this endif is emitted 4th; this comment is part of the recipe
  97. // echo doing more stuff # this is part of the recipe
  98. // endif # this endif is emitted 5th
  99. //
  100. // However, if pos.Line < f.mkPos.Line, we treat it as though it were equal
  101. if pos.Line >= f.mkPos.Line {
  102. f.bpPos.Line += (pos.Line - f.mkPos.Line)
  103. f.mkPos = end
  104. }
  105. }
  106. type conditional struct {
  107. cond string
  108. eq bool
  109. }
  110. func ConvertFile(filename string, buffer *bytes.Buffer) (string, []error) {
  111. p := mkparser.NewParser(filename, buffer)
  112. nodes, errs := p.Parse()
  113. if len(errs) > 0 {
  114. return "", errs
  115. }
  116. file := &bpFile{
  117. scope: androidScope(),
  118. localAssignments: make(map[string]*bpparser.Property),
  119. globalAssignments: make(map[string]*bpparser.Expression),
  120. variableRenames: make(map[string]string),
  121. }
  122. var conds []*conditional
  123. var assignmentCond *conditional
  124. var tree *bpparser.File
  125. for _, node := range nodes {
  126. file.setMkPos(p.Unpack(node.Pos()), p.Unpack(node.End()))
  127. switch x := node.(type) {
  128. case *mkparser.Comment:
  129. // Split the comment on escaped newlines and then
  130. // add each chunk separately.
  131. chunks := strings.Split(x.Comment, "\\\n")
  132. file.insertComment("//" + chunks[0])
  133. for i := 1; i < len(chunks); i++ {
  134. file.bpPos.Line++
  135. file.insertComment("//" + chunks[i])
  136. }
  137. case *mkparser.Assignment:
  138. handleAssignment(file, x, assignmentCond)
  139. case *mkparser.Directive:
  140. switch x.Name {
  141. case "include", "-include":
  142. module, ok := mapIncludePath(x.Args.Value(file.scope))
  143. if !ok {
  144. file.errorf(x, "unsupported include")
  145. continue
  146. }
  147. switch module {
  148. case clearVarsPath:
  149. resetModule(file)
  150. case includeIgnoredPath:
  151. // subdirs are already automatically included in Soong
  152. continue
  153. default:
  154. handleModuleConditionals(file, x, conds)
  155. makeModule(file, module)
  156. }
  157. case "ifeq", "ifneq", "ifdef", "ifndef":
  158. args := x.Args.Dump()
  159. eq := x.Name == "ifeq" || x.Name == "ifdef"
  160. if _, ok := conditionalTranslations[args]; ok {
  161. newCond := conditional{args, eq}
  162. conds = append(conds, &newCond)
  163. if file.inModule {
  164. if assignmentCond == nil {
  165. assignmentCond = &newCond
  166. } else {
  167. file.errorf(x, "unsupported nested conditional in module")
  168. }
  169. }
  170. } else {
  171. file.errorf(x, "unsupported conditional")
  172. conds = append(conds, nil)
  173. continue
  174. }
  175. case "else":
  176. if len(conds) == 0 {
  177. file.errorf(x, "missing if before else")
  178. continue
  179. } else if conds[len(conds)-1] == nil {
  180. file.errorf(x, "else from unsupported conditional")
  181. continue
  182. }
  183. conds[len(conds)-1].eq = !conds[len(conds)-1].eq
  184. case "endif":
  185. if len(conds) == 0 {
  186. file.errorf(x, "missing if before endif")
  187. continue
  188. } else if conds[len(conds)-1] == nil {
  189. file.errorf(x, "endif from unsupported conditional")
  190. conds = conds[:len(conds)-1]
  191. } else {
  192. if assignmentCond == conds[len(conds)-1] {
  193. assignmentCond = nil
  194. }
  195. conds = conds[:len(conds)-1]
  196. }
  197. default:
  198. file.errorf(x, "unsupported directive")
  199. continue
  200. }
  201. default:
  202. file.errorf(x, "unsupported line")
  203. }
  204. }
  205. tree = &bpparser.File{
  206. Defs: file.defs,
  207. Comments: file.comments,
  208. }
  209. // check for common supported but undesirable structures and clean them up
  210. fixer := bpfix.NewFixer(tree)
  211. fixedTree, fixerErr := fixer.Fix(bpfix.NewFixRequest().AddAll())
  212. if fixerErr != nil {
  213. errs = append(errs, fixerErr)
  214. } else {
  215. tree = fixedTree
  216. }
  217. out, err := bpparser.Print(tree)
  218. if err != nil {
  219. errs = append(errs, err)
  220. return "", errs
  221. }
  222. return string(out), errs
  223. }
  224. func renameVariableWithInvalidCharacters(name string) string {
  225. renamed := ""
  226. for invalid, replacement := range invalidVariableStringToReplacement {
  227. if strings.Contains(name, invalid) {
  228. renamed = strings.ReplaceAll(name, invalid, replacement)
  229. }
  230. }
  231. return renamed
  232. }
  233. func invalidVariableStrings() string {
  234. invalidStrings := make([]string, 0, len(invalidVariableStringToReplacement))
  235. for s := range invalidVariableStringToReplacement {
  236. invalidStrings = append(invalidStrings, "\""+s+"\"")
  237. }
  238. return strings.Join(invalidStrings, ", ")
  239. }
  240. func handleAssignment(file *bpFile, assignment *mkparser.Assignment, c *conditional) {
  241. if !assignment.Name.Const() {
  242. file.errorf(assignment, "unsupported non-const variable name")
  243. return
  244. }
  245. if assignment.Target != nil {
  246. file.errorf(assignment, "unsupported target assignment")
  247. return
  248. }
  249. name := assignment.Name.Value(nil)
  250. prefix := ""
  251. if newName := renameVariableWithInvalidCharacters(name); newName != "" {
  252. file.warnf("Variable names cannot contain: %s. Renamed \"%s\" to \"%s\"", invalidVariableStrings(), name, newName)
  253. file.variableRenames[name] = newName
  254. name = newName
  255. }
  256. if strings.HasPrefix(name, "LOCAL_") {
  257. for _, x := range propertyPrefixes {
  258. if strings.HasSuffix(name, "_"+x.mk) {
  259. name = strings.TrimSuffix(name, "_"+x.mk)
  260. prefix = x.bp
  261. break
  262. }
  263. }
  264. if c != nil {
  265. if prefix != "" {
  266. file.errorf(assignment, "prefix assignment inside conditional, skipping conditional")
  267. } else {
  268. var ok bool
  269. if prefix, ok = conditionalTranslations[c.cond][c.eq]; !ok {
  270. panic("unknown conditional")
  271. }
  272. }
  273. }
  274. } else {
  275. if c != nil {
  276. eq := "eq"
  277. if !c.eq {
  278. eq = "neq"
  279. }
  280. file.errorf(assignment, "conditional %s %s on global assignment", eq, c.cond)
  281. }
  282. }
  283. appendVariable := assignment.Type == "+="
  284. var err error
  285. if prop, ok := rewriteProperties[name]; ok {
  286. err = prop(variableAssignmentContext{file, prefix, assignment.Value, appendVariable})
  287. } else {
  288. switch {
  289. case name == "LOCAL_ARM_MODE":
  290. // This is a hack to get the LOCAL_ARM_MODE value inside
  291. // of an arch: { arm: {} } block.
  292. armModeAssign := assignment
  293. armModeAssign.Name = mkparser.SimpleMakeString("LOCAL_ARM_MODE_HACK_arm", assignment.Name.Pos())
  294. handleAssignment(file, armModeAssign, c)
  295. case strings.HasPrefix(name, "LOCAL_"):
  296. file.errorf(assignment, "unsupported assignment to %s", name)
  297. return
  298. default:
  299. var val bpparser.Expression
  300. val, err = makeVariableToBlueprint(file, assignment.Value, bpparser.ListType)
  301. if err == nil {
  302. err = setVariable(file, appendVariable, prefix, name, val, false)
  303. }
  304. }
  305. }
  306. if err != nil {
  307. file.errorf(assignment, err.Error())
  308. }
  309. }
  310. func handleModuleConditionals(file *bpFile, directive *mkparser.Directive, conds []*conditional) {
  311. for _, c := range conds {
  312. if c == nil {
  313. continue
  314. }
  315. if _, ok := conditionalTranslations[c.cond]; !ok {
  316. panic("unknown conditional " + c.cond)
  317. }
  318. disabledPrefix := conditionalTranslations[c.cond][!c.eq]
  319. // Create a fake assignment with enabled = false
  320. val, err := makeVariableToBlueprint(file, mkparser.SimpleMakeString("false", mkparser.NoPos), bpparser.BoolType)
  321. if err == nil {
  322. err = setVariable(file, false, disabledPrefix, "enabled", val, true)
  323. }
  324. if err != nil {
  325. file.errorf(directive, err.Error())
  326. }
  327. }
  328. }
  329. func makeModule(file *bpFile, t string) {
  330. file.module.Type = t
  331. file.module.TypePos = file.module.LBracePos
  332. file.module.RBracePos = file.bpPos
  333. file.defs = append(file.defs, file.module)
  334. file.inModule = false
  335. }
  336. func resetModule(file *bpFile) {
  337. file.module = &bpparser.Module{}
  338. file.module.LBracePos = file.bpPos
  339. file.localAssignments = make(map[string]*bpparser.Property)
  340. file.inModule = true
  341. }
  342. func makeVariableToBlueprint(file *bpFile, val *mkparser.MakeString,
  343. typ bpparser.Type) (bpparser.Expression, error) {
  344. var exp bpparser.Expression
  345. var err error
  346. switch typ {
  347. case bpparser.ListType:
  348. exp, err = makeToListExpression(val, file)
  349. case bpparser.StringType:
  350. exp, err = makeToStringExpression(val, file)
  351. case bpparser.BoolType:
  352. exp, err = makeToBoolExpression(val, file)
  353. default:
  354. panic("unknown type")
  355. }
  356. if err != nil {
  357. return nil, err
  358. }
  359. return exp, nil
  360. }
  361. // If local is set to true, then the variable will be added as a part of the
  362. // variable at file.bpPos. For example, if file.bpPos references a module,
  363. // then calling this method will set a property on that module if local is set
  364. // to true. Otherwise, the Variable will be created at the root of the file.
  365. //
  366. // prefix should be populated with the top level value to be assigned, and
  367. // name with a sub-value. If prefix is empty, then name is the top level value.
  368. // For example, if prefix is "foo" and name is "bar" with a value of "baz", then
  369. // the following variable will be generated:
  370. //
  371. // foo {
  372. // bar: "baz"
  373. // }
  374. //
  375. // If prefix is the empty string and name is "foo" with a value of "bar", the
  376. // following variable will be generated (if it is a property):
  377. //
  378. // foo: "bar"
  379. func setVariable(file *bpFile, plusequals bool, prefix, name string, value bpparser.Expression, local bool) error {
  380. if prefix != "" {
  381. name = prefix + "." + name
  382. }
  383. pos := file.bpPos
  384. var oldValue *bpparser.Expression
  385. if local {
  386. oldProp := file.localAssignments[name]
  387. if oldProp != nil {
  388. oldValue = &oldProp.Value
  389. }
  390. } else {
  391. oldValue = file.globalAssignments[name]
  392. }
  393. if local {
  394. if oldValue != nil && plusequals {
  395. val, err := addValues(*oldValue, value)
  396. if err != nil {
  397. return fmt.Errorf("unsupported addition: %s", err.Error())
  398. }
  399. val.(*bpparser.Operator).OperatorPos = pos
  400. *oldValue = val
  401. } else {
  402. names := strings.Split(name, ".")
  403. if file.module == nil {
  404. file.warnf("No 'include $(CLEAR_VARS)' detected before first assignment; clearing vars now")
  405. resetModule(file)
  406. }
  407. container := &file.module.Properties
  408. for i, n := range names[:len(names)-1] {
  409. fqn := strings.Join(names[0:i+1], ".")
  410. prop := file.localAssignments[fqn]
  411. if prop == nil {
  412. prop = &bpparser.Property{
  413. Name: n,
  414. NamePos: pos,
  415. Value: &bpparser.Map{
  416. Properties: []*bpparser.Property{},
  417. },
  418. }
  419. file.localAssignments[fqn] = prop
  420. *container = append(*container, prop)
  421. }
  422. container = &prop.Value.(*bpparser.Map).Properties
  423. }
  424. prop := &bpparser.Property{
  425. Name: names[len(names)-1],
  426. NamePos: pos,
  427. Value: value,
  428. }
  429. file.localAssignments[name] = prop
  430. *container = append(*container, prop)
  431. }
  432. } else {
  433. if oldValue != nil && plusequals {
  434. a := &bpparser.Assignment{
  435. Name: name,
  436. NamePos: pos,
  437. Value: value,
  438. OrigValue: value,
  439. EqualsPos: pos,
  440. Assigner: "+=",
  441. }
  442. file.defs = append(file.defs, a)
  443. } else {
  444. if _, ok := file.globalAssignments[name]; ok {
  445. return fmt.Errorf("cannot assign a variable multiple times: \"%s\"", name)
  446. }
  447. a := &bpparser.Assignment{
  448. Name: name,
  449. NamePos: pos,
  450. Value: value,
  451. OrigValue: value,
  452. EqualsPos: pos,
  453. Assigner: "=",
  454. }
  455. file.globalAssignments[name] = &a.Value
  456. file.defs = append(file.defs, a)
  457. }
  458. }
  459. return nil
  460. }