androidmk.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  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. scope mkparser.Scope
  31. module *bpparser.Module
  32. mkPos scanner.Position // Position of the last handled line in the makefile
  33. bpPos scanner.Position // Position of the last emitted line to the blueprint file
  34. inModule bool
  35. }
  36. func (f *bpFile) insertComment(s string) {
  37. f.comments = append(f.comments, &bpparser.CommentGroup{
  38. Comments: []*bpparser.Comment{
  39. &bpparser.Comment{
  40. Comment: []string{s},
  41. Slash: f.bpPos,
  42. },
  43. },
  44. })
  45. f.bpPos.Offset += len(s)
  46. }
  47. func (f *bpFile) insertExtraComment(s string) {
  48. f.insertComment(s)
  49. f.bpPos.Line++
  50. }
  51. // records that the given node failed to be converted and includes an explanatory message
  52. func (f *bpFile) errorf(failedNode mkparser.Node, message string, args ...interface{}) {
  53. orig := failedNode.Dump()
  54. message = fmt.Sprintf(message, args...)
  55. f.addErrorText(fmt.Sprintf("// ANDROIDMK TRANSLATION ERROR: %s", message))
  56. lines := strings.Split(orig, "\n")
  57. for _, l := range lines {
  58. f.insertExtraComment("// " + l)
  59. }
  60. }
  61. // records that something unexpected occurred
  62. func (f *bpFile) warnf(message string, args ...interface{}) {
  63. message = fmt.Sprintf(message, args...)
  64. f.addErrorText(fmt.Sprintf("// ANDROIDMK TRANSLATION WARNING: %s", message))
  65. }
  66. // adds the given error message as-is to the bottom of the (in-progress) file
  67. func (f *bpFile) addErrorText(message string) {
  68. f.insertExtraComment(message)
  69. }
  70. func (f *bpFile) setMkPos(pos, end scanner.Position) {
  71. // It is unusual but not forbidden for pos.Line to be smaller than f.mkPos.Line
  72. // For example:
  73. //
  74. // if true # this line is emitted 1st
  75. // if true # this line is emitted 2nd
  76. // some-target: some-file # this line is emitted 3rd
  77. // echo doing something # this recipe is emitted 6th
  78. // endif #some comment # this endif is emitted 4th; this comment is part of the recipe
  79. // echo doing more stuff # this is part of the recipe
  80. // endif # this endif is emitted 5th
  81. //
  82. // However, if pos.Line < f.mkPos.Line, we treat it as though it were equal
  83. if pos.Line >= f.mkPos.Line {
  84. f.bpPos.Line += (pos.Line - f.mkPos.Line)
  85. f.mkPos = end
  86. }
  87. }
  88. type conditional struct {
  89. cond string
  90. eq bool
  91. }
  92. func ConvertFile(filename string, buffer *bytes.Buffer) (string, []error) {
  93. p := mkparser.NewParser(filename, buffer)
  94. nodes, errs := p.Parse()
  95. if len(errs) > 0 {
  96. return "", errs
  97. }
  98. file := &bpFile{
  99. scope: androidScope(),
  100. localAssignments: make(map[string]*bpparser.Property),
  101. globalAssignments: make(map[string]*bpparser.Expression),
  102. }
  103. var conds []*conditional
  104. var assignmentCond *conditional
  105. var tree *bpparser.File
  106. for _, node := range nodes {
  107. file.setMkPos(p.Unpack(node.Pos()), p.Unpack(node.End()))
  108. switch x := node.(type) {
  109. case *mkparser.Comment:
  110. file.insertComment("//" + x.Comment)
  111. case *mkparser.Assignment:
  112. handleAssignment(file, x, assignmentCond)
  113. case *mkparser.Directive:
  114. switch x.Name {
  115. case "include", "-include":
  116. module, ok := mapIncludePath(x.Args.Value(file.scope))
  117. if !ok {
  118. file.errorf(x, "unsupported include")
  119. continue
  120. }
  121. switch module {
  122. case clear_vars:
  123. resetModule(file)
  124. case include_ignored:
  125. // subdirs are already automatically included in Soong
  126. continue
  127. default:
  128. handleModuleConditionals(file, x, conds)
  129. makeModule(file, module)
  130. }
  131. case "ifeq", "ifneq", "ifdef", "ifndef":
  132. args := x.Args.Dump()
  133. eq := x.Name == "ifeq" || x.Name == "ifdef"
  134. if _, ok := conditionalTranslations[args]; ok {
  135. newCond := conditional{args, eq}
  136. conds = append(conds, &newCond)
  137. if file.inModule {
  138. if assignmentCond == nil {
  139. assignmentCond = &newCond
  140. } else {
  141. file.errorf(x, "unsupported nested conditional in module")
  142. }
  143. }
  144. } else {
  145. file.errorf(x, "unsupported conditional")
  146. conds = append(conds, nil)
  147. continue
  148. }
  149. case "else":
  150. if len(conds) == 0 {
  151. file.errorf(x, "missing if before else")
  152. continue
  153. } else if conds[len(conds)-1] == nil {
  154. file.errorf(x, "else from unsupported conditional")
  155. continue
  156. }
  157. conds[len(conds)-1].eq = !conds[len(conds)-1].eq
  158. case "endif":
  159. if len(conds) == 0 {
  160. file.errorf(x, "missing if before endif")
  161. continue
  162. } else if conds[len(conds)-1] == nil {
  163. file.errorf(x, "endif from unsupported conditional")
  164. conds = conds[:len(conds)-1]
  165. } else {
  166. if assignmentCond == conds[len(conds)-1] {
  167. assignmentCond = nil
  168. }
  169. conds = conds[:len(conds)-1]
  170. }
  171. default:
  172. file.errorf(x, "unsupported directive")
  173. continue
  174. }
  175. default:
  176. file.errorf(x, "unsupported line")
  177. }
  178. }
  179. tree = &bpparser.File{
  180. Defs: file.defs,
  181. Comments: file.comments,
  182. }
  183. // check for common supported but undesirable structures and clean them up
  184. fixer := bpfix.NewFixer(tree)
  185. fixedTree, fixerErr := fixer.Fix(bpfix.NewFixRequest().AddAll())
  186. if fixerErr != nil {
  187. errs = append(errs, fixerErr)
  188. } else {
  189. tree = fixedTree
  190. }
  191. out, err := bpparser.Print(tree)
  192. if err != nil {
  193. errs = append(errs, err)
  194. return "", errs
  195. }
  196. return string(out), errs
  197. }
  198. func handleAssignment(file *bpFile, assignment *mkparser.Assignment, c *conditional) {
  199. if !assignment.Name.Const() {
  200. file.errorf(assignment, "unsupported non-const variable name")
  201. return
  202. }
  203. if assignment.Target != nil {
  204. file.errorf(assignment, "unsupported target assignment")
  205. return
  206. }
  207. name := assignment.Name.Value(nil)
  208. prefix := ""
  209. if strings.HasPrefix(name, "LOCAL_") {
  210. for _, x := range propertyPrefixes {
  211. if strings.HasSuffix(name, "_"+x.mk) {
  212. name = strings.TrimSuffix(name, "_"+x.mk)
  213. prefix = x.bp
  214. break
  215. }
  216. }
  217. if c != nil {
  218. if prefix != "" {
  219. file.errorf(assignment, "prefix assignment inside conditional, skipping conditional")
  220. } else {
  221. var ok bool
  222. if prefix, ok = conditionalTranslations[c.cond][c.eq]; !ok {
  223. panic("unknown conditional")
  224. }
  225. }
  226. }
  227. } else {
  228. if c != nil {
  229. eq := "eq"
  230. if !c.eq {
  231. eq = "neq"
  232. }
  233. file.errorf(assignment, "conditional %s %s on global assignment", eq, c.cond)
  234. }
  235. }
  236. appendVariable := assignment.Type == "+="
  237. var err error
  238. if prop, ok := rewriteProperties[name]; ok {
  239. err = prop(variableAssignmentContext{file, prefix, assignment.Value, appendVariable})
  240. } else {
  241. switch {
  242. case name == "LOCAL_ARM_MODE":
  243. // This is a hack to get the LOCAL_ARM_MODE value inside
  244. // of an arch: { arm: {} } block.
  245. armModeAssign := assignment
  246. armModeAssign.Name = mkparser.SimpleMakeString("LOCAL_ARM_MODE_HACK_arm", assignment.Name.Pos())
  247. handleAssignment(file, armModeAssign, c)
  248. case strings.HasPrefix(name, "LOCAL_"):
  249. file.errorf(assignment, "unsupported assignment to %s", name)
  250. return
  251. default:
  252. var val bpparser.Expression
  253. val, err = makeVariableToBlueprint(file, assignment.Value, bpparser.ListType)
  254. if err == nil {
  255. err = setVariable(file, appendVariable, prefix, name, val, false)
  256. }
  257. }
  258. }
  259. if err != nil {
  260. file.errorf(assignment, err.Error())
  261. }
  262. }
  263. func handleModuleConditionals(file *bpFile, directive *mkparser.Directive, conds []*conditional) {
  264. for _, c := range conds {
  265. if c == nil {
  266. continue
  267. }
  268. if _, ok := conditionalTranslations[c.cond]; !ok {
  269. panic("unknown conditional " + c.cond)
  270. }
  271. disabledPrefix := conditionalTranslations[c.cond][!c.eq]
  272. // Create a fake assignment with enabled = false
  273. val, err := makeVariableToBlueprint(file, mkparser.SimpleMakeString("false", mkparser.NoPos), bpparser.BoolType)
  274. if err == nil {
  275. err = setVariable(file, false, disabledPrefix, "enabled", val, true)
  276. }
  277. if err != nil {
  278. file.errorf(directive, err.Error())
  279. }
  280. }
  281. }
  282. func makeModule(file *bpFile, t string) {
  283. file.module.Type = t
  284. file.module.TypePos = file.module.LBracePos
  285. file.module.RBracePos = file.bpPos
  286. file.defs = append(file.defs, file.module)
  287. file.inModule = false
  288. }
  289. func resetModule(file *bpFile) {
  290. file.module = &bpparser.Module{}
  291. file.module.LBracePos = file.bpPos
  292. file.localAssignments = make(map[string]*bpparser.Property)
  293. file.inModule = true
  294. }
  295. func makeVariableToBlueprint(file *bpFile, val *mkparser.MakeString,
  296. typ bpparser.Type) (bpparser.Expression, error) {
  297. var exp bpparser.Expression
  298. var err error
  299. switch typ {
  300. case bpparser.ListType:
  301. exp, err = makeToListExpression(val, file.scope)
  302. case bpparser.StringType:
  303. exp, err = makeToStringExpression(val, file.scope)
  304. case bpparser.BoolType:
  305. exp, err = makeToBoolExpression(val)
  306. default:
  307. panic("unknown type")
  308. }
  309. if err != nil {
  310. return nil, err
  311. }
  312. return exp, nil
  313. }
  314. func setVariable(file *bpFile, plusequals bool, prefix, name string, value bpparser.Expression, local bool) error {
  315. if prefix != "" {
  316. name = prefix + "." + name
  317. }
  318. pos := file.bpPos
  319. var oldValue *bpparser.Expression
  320. if local {
  321. oldProp := file.localAssignments[name]
  322. if oldProp != nil {
  323. oldValue = &oldProp.Value
  324. }
  325. } else {
  326. oldValue = file.globalAssignments[name]
  327. }
  328. if local {
  329. if oldValue != nil && plusequals {
  330. val, err := addValues(*oldValue, value)
  331. if err != nil {
  332. return fmt.Errorf("unsupported addition: %s", err.Error())
  333. }
  334. val.(*bpparser.Operator).OperatorPos = pos
  335. *oldValue = val
  336. } else {
  337. names := strings.Split(name, ".")
  338. if file.module == nil {
  339. file.warnf("No 'include $(CLEAR_VARS)' detected before first assignment; clearing vars now")
  340. resetModule(file)
  341. }
  342. container := &file.module.Properties
  343. for i, n := range names[:len(names)-1] {
  344. fqn := strings.Join(names[0:i+1], ".")
  345. prop := file.localAssignments[fqn]
  346. if prop == nil {
  347. prop = &bpparser.Property{
  348. Name: n,
  349. NamePos: pos,
  350. Value: &bpparser.Map{
  351. Properties: []*bpparser.Property{},
  352. },
  353. }
  354. file.localAssignments[fqn] = prop
  355. *container = append(*container, prop)
  356. }
  357. container = &prop.Value.(*bpparser.Map).Properties
  358. }
  359. prop := &bpparser.Property{
  360. Name: names[len(names)-1],
  361. NamePos: pos,
  362. Value: value,
  363. }
  364. file.localAssignments[name] = prop
  365. *container = append(*container, prop)
  366. }
  367. } else {
  368. if oldValue != nil && plusequals {
  369. a := &bpparser.Assignment{
  370. Name: name,
  371. NamePos: pos,
  372. Value: value,
  373. OrigValue: value,
  374. EqualsPos: pos,
  375. Assigner: "+=",
  376. }
  377. file.defs = append(file.defs, a)
  378. } else {
  379. a := &bpparser.Assignment{
  380. Name: name,
  381. NamePos: pos,
  382. Value: value,
  383. OrigValue: value,
  384. EqualsPos: pos,
  385. Assigner: "=",
  386. }
  387. file.globalAssignments[name] = &a.Value
  388. file.defs = append(file.defs, a)
  389. }
  390. }
  391. return nil
  392. }