mk2rbc.go 72 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195
  1. // Copyright 2021 Google LLC
  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. // Convert makefile containing device configuration to Starlark file
  15. // The conversion can handle the following constructs in a makefile:
  16. // - comments
  17. // - simple variable assignments
  18. // - $(call init-product,<file>)
  19. // - $(call inherit-product-if-exists
  20. // - if directives
  21. //
  22. // All other constructs are carried over to the output starlark file as comments.
  23. package mk2rbc
  24. import (
  25. "bytes"
  26. "fmt"
  27. "io"
  28. "io/fs"
  29. "io/ioutil"
  30. "os"
  31. "path/filepath"
  32. "regexp"
  33. "sort"
  34. "strconv"
  35. "strings"
  36. "text/scanner"
  37. mkparser "android/soong/androidmk/parser"
  38. )
  39. const (
  40. annotationCommentPrefix = "RBC#"
  41. baseUri = "//build/make/core:product_config.rbc"
  42. // The name of the struct exported by the product_config.rbc
  43. // that contains the functions and variables available to
  44. // product configuration Starlark files.
  45. baseName = "rblf"
  46. soongNsPrefix = "SOONG_CONFIG_"
  47. // And here are the functions and variables:
  48. cfnGetCfg = baseName + ".cfg"
  49. cfnMain = baseName + ".product_configuration"
  50. cfnBoardMain = baseName + ".board_configuration"
  51. cfnPrintVars = baseName + ".printvars"
  52. cfnInherit = baseName + ".inherit"
  53. cfnSetListDefault = baseName + ".setdefault"
  54. )
  55. const (
  56. soongConfigAppend = "soong_config_append"
  57. soongConfigAssign = "soong_config_set"
  58. )
  59. var knownFunctions = map[string]interface {
  60. parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr
  61. }{
  62. "abspath": &simpleCallParser{name: baseName + ".abspath", returnType: starlarkTypeString},
  63. "add-product-dex-preopt-module-config": &simpleCallParser{name: baseName + ".add_product_dex_preopt_module_config", returnType: starlarkTypeString, addHandle: true},
  64. "add_soong_config_namespace": &simpleCallParser{name: baseName + ".soong_config_namespace", returnType: starlarkTypeVoid, addGlobals: true},
  65. "add_soong_config_var_value": &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
  66. soongConfigAssign: &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
  67. soongConfigAppend: &simpleCallParser{name: baseName + ".soong_config_append", returnType: starlarkTypeVoid, addGlobals: true},
  68. "soong_config_get": &simpleCallParser{name: baseName + ".soong_config_get", returnType: starlarkTypeString, addGlobals: true},
  69. "add-to-product-copy-files-if-exists": &simpleCallParser{name: baseName + ".copy_if_exists", returnType: starlarkTypeList},
  70. "addprefix": &simpleCallParser{name: baseName + ".addprefix", returnType: starlarkTypeList},
  71. "addsuffix": &simpleCallParser{name: baseName + ".addsuffix", returnType: starlarkTypeList},
  72. "and": &andOrParser{isAnd: true},
  73. "copy-files": &simpleCallParser{name: baseName + ".copy_files", returnType: starlarkTypeList},
  74. "dir": &simpleCallParser{name: baseName + ".dir", returnType: starlarkTypeString},
  75. "dist-for-goals": &simpleCallParser{name: baseName + ".mkdist_for_goals", returnType: starlarkTypeVoid, addGlobals: true},
  76. "enforce-product-packages-exist": &simpleCallParser{name: baseName + ".enforce_product_packages_exist", returnType: starlarkTypeVoid, addHandle: true},
  77. "error": &makeControlFuncParser{name: baseName + ".mkerror"},
  78. "findstring": &simpleCallParser{name: baseName + ".findstring", returnType: starlarkTypeInt},
  79. "find-copy-subdir-files": &simpleCallParser{name: baseName + ".find_and_copy", returnType: starlarkTypeList},
  80. "filter": &simpleCallParser{name: baseName + ".filter", returnType: starlarkTypeList},
  81. "filter-out": &simpleCallParser{name: baseName + ".filter_out", returnType: starlarkTypeList},
  82. "firstword": &simpleCallParser{name: baseName + ".first_word", returnType: starlarkTypeString},
  83. "foreach": &foreachCallParser{},
  84. "if": &ifCallParser{},
  85. "info": &makeControlFuncParser{name: baseName + ".mkinfo"},
  86. "is-board-platform": &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
  87. "is-board-platform2": &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
  88. "is-board-platform-in-list": &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
  89. "is-board-platform-in-list2": &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
  90. "is-product-in-list": &isProductInListCallParser{},
  91. "is-vendor-board-platform": &isVendorBoardPlatformCallParser{},
  92. "is-vendor-board-qcom": &isVendorBoardQcomCallParser{},
  93. "lastword": &simpleCallParser{name: baseName + ".last_word", returnType: starlarkTypeString},
  94. "notdir": &simpleCallParser{name: baseName + ".notdir", returnType: starlarkTypeString},
  95. "math_max": &mathMaxOrMinCallParser{function: "max"},
  96. "math_min": &mathMaxOrMinCallParser{function: "min"},
  97. "math_gt_or_eq": &mathComparisonCallParser{op: ">="},
  98. "math_gt": &mathComparisonCallParser{op: ">"},
  99. "math_lt": &mathComparisonCallParser{op: "<"},
  100. "my-dir": &myDirCallParser{},
  101. "or": &andOrParser{isAnd: false},
  102. "patsubst": &substCallParser{fname: "patsubst"},
  103. "product-copy-files-by-pattern": &simpleCallParser{name: baseName + ".product_copy_files_by_pattern", returnType: starlarkTypeList},
  104. "require-artifacts-in-path": &simpleCallParser{name: baseName + ".require_artifacts_in_path", returnType: starlarkTypeVoid, addHandle: true},
  105. "require-artifacts-in-path-relaxed": &simpleCallParser{name: baseName + ".require_artifacts_in_path_relaxed", returnType: starlarkTypeVoid, addHandle: true},
  106. // TODO(asmundak): remove it once all calls are removed from configuration makefiles. see b/183161002
  107. "shell": &shellCallParser{},
  108. "sort": &simpleCallParser{name: baseName + ".mksort", returnType: starlarkTypeList},
  109. "strip": &simpleCallParser{name: baseName + ".mkstrip", returnType: starlarkTypeString},
  110. "subst": &substCallParser{fname: "subst"},
  111. "warning": &makeControlFuncParser{name: baseName + ".mkwarning"},
  112. "word": &wordCallParser{},
  113. "words": &wordsCallParser{},
  114. "wildcard": &simpleCallParser{name: baseName + ".expand_wildcard", returnType: starlarkTypeList},
  115. }
  116. // The same as knownFunctions, but returns a []starlarkNode instead of a starlarkExpr
  117. var knownNodeFunctions = map[string]interface {
  118. parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) []starlarkNode
  119. }{
  120. "eval": &evalNodeParser{},
  121. "if": &ifCallNodeParser{},
  122. "inherit-product": &inheritProductCallParser{loadAlways: true},
  123. "inherit-product-if-exists": &inheritProductCallParser{loadAlways: false},
  124. "foreach": &foreachCallNodeParser{},
  125. }
  126. // These look like variables, but are actually functions, and would give
  127. // undefined variable errors if we converted them as variables. Instead,
  128. // emit an error instead of converting them.
  129. var unsupportedFunctions = map[string]bool{
  130. "local-generated-sources-dir": true,
  131. "local-intermediates-dir": true,
  132. }
  133. // These are functions that we don't implement conversions for, but
  134. // we allow seeing their definitions in the product config files.
  135. var ignoredDefines = map[string]bool{
  136. "find-word-in-list": true, // internal macro
  137. "get-vendor-board-platforms": true, // internal macro, used by is-board-platform, etc.
  138. "is-android-codename": true, // unused by product config
  139. "is-android-codename-in-list": true, // unused by product config
  140. "is-chipset-in-board-platform": true, // unused by product config
  141. "is-chipset-prefix-in-board-platform": true, // unused by product config
  142. "is-not-board-platform": true, // defined but never used
  143. "is-platform-sdk-version-at-least": true, // unused by product config
  144. "match-prefix": true, // internal macro
  145. "match-word": true, // internal macro
  146. "match-word-in-list": true, // internal macro
  147. "tb-modules": true, // defined in hardware/amlogic/tb_modules/tb_detect.mk, unused
  148. }
  149. var identifierFullMatchRegex = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
  150. // Conversion request parameters
  151. type Request struct {
  152. MkFile string // file to convert
  153. Reader io.Reader // if set, read input from this stream instead
  154. OutputSuffix string // generated Starlark files suffix
  155. OutputDir string // if set, root of the output hierarchy
  156. ErrorLogger ErrorLogger
  157. TracedVariables []string // trace assignment to these variables
  158. TraceCalls bool
  159. SourceFS fs.FS
  160. MakefileFinder MakefileFinder
  161. }
  162. // ErrorLogger prints errors and gathers error statistics.
  163. // Its NewError function is called on every error encountered during the conversion.
  164. type ErrorLogger interface {
  165. NewError(el ErrorLocation, node mkparser.Node, text string, args ...interface{})
  166. }
  167. type ErrorLocation struct {
  168. MkFile string
  169. MkLine int
  170. }
  171. func (el ErrorLocation) String() string {
  172. return fmt.Sprintf("%s:%d", el.MkFile, el.MkLine)
  173. }
  174. // Derives module name for a given file. It is base name
  175. // (file name without suffix), with some characters replaced to make it a Starlark identifier
  176. func moduleNameForFile(mkFile string) string {
  177. base := strings.TrimSuffix(filepath.Base(mkFile), filepath.Ext(mkFile))
  178. // TODO(asmundak): what else can be in the product file names?
  179. return strings.NewReplacer("-", "_", ".", "_").Replace(base)
  180. }
  181. func cloneMakeString(mkString *mkparser.MakeString) *mkparser.MakeString {
  182. r := &mkparser.MakeString{StringPos: mkString.StringPos}
  183. r.Strings = append(r.Strings, mkString.Strings...)
  184. r.Variables = append(r.Variables, mkString.Variables...)
  185. return r
  186. }
  187. func isMakeControlFunc(s string) bool {
  188. return s == "error" || s == "warning" || s == "info"
  189. }
  190. // varAssignmentScope points to the last assignment for each variable
  191. // in the current block. It is used during the parsing to chain
  192. // the assignments to a variable together.
  193. type varAssignmentScope struct {
  194. outer *varAssignmentScope
  195. vars map[string]bool
  196. }
  197. // Starlark output generation context
  198. type generationContext struct {
  199. buf strings.Builder
  200. starScript *StarlarkScript
  201. indentLevel int
  202. inAssignment bool
  203. tracedCount int
  204. varAssignments *varAssignmentScope
  205. }
  206. func NewGenerateContext(ss *StarlarkScript) *generationContext {
  207. return &generationContext{
  208. starScript: ss,
  209. varAssignments: &varAssignmentScope{
  210. outer: nil,
  211. vars: make(map[string]bool),
  212. },
  213. }
  214. }
  215. func (gctx *generationContext) pushVariableAssignments() {
  216. va := &varAssignmentScope{
  217. outer: gctx.varAssignments,
  218. vars: make(map[string]bool),
  219. }
  220. gctx.varAssignments = va
  221. }
  222. func (gctx *generationContext) popVariableAssignments() {
  223. gctx.varAssignments = gctx.varAssignments.outer
  224. }
  225. func (gctx *generationContext) hasBeenAssigned(v variable) bool {
  226. for va := gctx.varAssignments; va != nil; va = va.outer {
  227. if _, ok := va.vars[v.name()]; ok {
  228. return true
  229. }
  230. }
  231. return false
  232. }
  233. func (gctx *generationContext) setHasBeenAssigned(v variable) {
  234. gctx.varAssignments.vars[v.name()] = true
  235. }
  236. // emit returns generated script
  237. func (gctx *generationContext) emit() string {
  238. ss := gctx.starScript
  239. // The emitted code has the following layout:
  240. // <initial comments>
  241. // preamble, i.e.,
  242. // load statement for the runtime support
  243. // load statement for each unique submodule pulled in by this one
  244. // def init(g, handle):
  245. // cfg = rblf.cfg(handle)
  246. // <statements>
  247. // <warning if conversion was not clean>
  248. iNode := len(ss.nodes)
  249. for i, node := range ss.nodes {
  250. if _, ok := node.(*commentNode); !ok {
  251. iNode = i
  252. break
  253. }
  254. node.emit(gctx)
  255. }
  256. gctx.emitPreamble()
  257. gctx.newLine()
  258. // The arguments passed to the init function are the global dictionary
  259. // ('g') and the product configuration dictionary ('cfg')
  260. gctx.write("def init(g, handle):")
  261. gctx.indentLevel++
  262. if gctx.starScript.traceCalls {
  263. gctx.newLine()
  264. gctx.writef(`print(">%s")`, gctx.starScript.mkFile)
  265. }
  266. gctx.newLine()
  267. gctx.writef("cfg = %s(handle)", cfnGetCfg)
  268. for _, node := range ss.nodes[iNode:] {
  269. node.emit(gctx)
  270. }
  271. if gctx.starScript.traceCalls {
  272. gctx.newLine()
  273. gctx.writef(`print("<%s")`, gctx.starScript.mkFile)
  274. }
  275. gctx.indentLevel--
  276. gctx.write("\n")
  277. return gctx.buf.String()
  278. }
  279. func (gctx *generationContext) emitPreamble() {
  280. gctx.newLine()
  281. gctx.writef("load(%q, %q)", baseUri, baseName)
  282. // Emit exactly one load statement for each URI.
  283. loadedSubConfigs := make(map[string]string)
  284. for _, mi := range gctx.starScript.inherited {
  285. uri := mi.path
  286. if m, ok := loadedSubConfigs[uri]; ok {
  287. // No need to emit load statement, but fix module name.
  288. mi.moduleLocalName = m
  289. continue
  290. }
  291. if mi.optional || mi.missing {
  292. uri += "|init"
  293. }
  294. gctx.newLine()
  295. gctx.writef("load(%q, %s = \"init\")", uri, mi.entryName())
  296. loadedSubConfigs[uri] = mi.moduleLocalName
  297. }
  298. gctx.write("\n")
  299. }
  300. func (gctx *generationContext) emitPass() {
  301. gctx.newLine()
  302. gctx.write("pass")
  303. }
  304. func (gctx *generationContext) write(ss ...string) {
  305. for _, s := range ss {
  306. gctx.buf.WriteString(s)
  307. }
  308. }
  309. func (gctx *generationContext) writef(format string, args ...interface{}) {
  310. gctx.write(fmt.Sprintf(format, args...))
  311. }
  312. func (gctx *generationContext) newLine() {
  313. if gctx.buf.Len() == 0 {
  314. return
  315. }
  316. gctx.write("\n")
  317. gctx.writef("%*s", 2*gctx.indentLevel, "")
  318. }
  319. func (gctx *generationContext) emitConversionError(el ErrorLocation, message string) {
  320. gctx.writef(`rblf.mk2rbc_error("%s", %q)`, el, message)
  321. }
  322. func (gctx *generationContext) emitLoadCheck(im inheritedModule) {
  323. if !im.needsLoadCheck() {
  324. return
  325. }
  326. gctx.newLine()
  327. gctx.writef("if not %s:", im.entryName())
  328. gctx.indentLevel++
  329. gctx.newLine()
  330. gctx.write(`rblf.mkerror("`, gctx.starScript.mkFile, `", "Cannot find %s" % (`)
  331. im.pathExpr().emit(gctx)
  332. gctx.write("))")
  333. gctx.indentLevel--
  334. }
  335. type knownVariable struct {
  336. name string
  337. class varClass
  338. valueType starlarkType
  339. }
  340. type knownVariables map[string]knownVariable
  341. func (pcv knownVariables) NewVariable(name string, varClass varClass, valueType starlarkType) {
  342. v, exists := pcv[name]
  343. if !exists {
  344. pcv[name] = knownVariable{name, varClass, valueType}
  345. return
  346. }
  347. // Conflict resolution:
  348. // * config class trumps everything
  349. // * any type trumps unknown type
  350. match := varClass == v.class
  351. if !match {
  352. if varClass == VarClassConfig {
  353. v.class = VarClassConfig
  354. match = true
  355. } else if v.class == VarClassConfig {
  356. match = true
  357. }
  358. }
  359. if valueType != v.valueType {
  360. if valueType != starlarkTypeUnknown {
  361. if v.valueType == starlarkTypeUnknown {
  362. v.valueType = valueType
  363. } else {
  364. match = false
  365. }
  366. }
  367. }
  368. if !match {
  369. fmt.Fprintf(os.Stderr, "cannot redefine %s as %v/%v (already defined as %v/%v)\n",
  370. name, varClass, valueType, v.class, v.valueType)
  371. }
  372. }
  373. // All known product variables.
  374. var KnownVariables = make(knownVariables)
  375. func init() {
  376. for _, kv := range []string{
  377. // Kernel-related variables that we know are lists.
  378. "BOARD_VENDOR_KERNEL_MODULES",
  379. "BOARD_VENDOR_RAMDISK_KERNEL_MODULES",
  380. "BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD",
  381. "BOARD_RECOVERY_KERNEL_MODULES",
  382. // Other variables we knwo are lists
  383. "ART_APEX_JARS",
  384. } {
  385. KnownVariables.NewVariable(kv, VarClassSoong, starlarkTypeList)
  386. }
  387. }
  388. // Information about the generated Starlark script.
  389. type StarlarkScript struct {
  390. mkFile string
  391. moduleName string
  392. mkPos scanner.Position
  393. nodes []starlarkNode
  394. inherited []*moduleInfo
  395. hasErrors bool
  396. traceCalls bool // print enter/exit each init function
  397. sourceFS fs.FS
  398. makefileFinder MakefileFinder
  399. nodeLocator func(pos mkparser.Pos) int
  400. }
  401. // parseContext holds the script we are generating and all the ephemeral data
  402. // needed during the parsing.
  403. type parseContext struct {
  404. script *StarlarkScript
  405. nodes []mkparser.Node // Makefile as parsed by mkparser
  406. currentNodeIndex int // Node in it we are processing
  407. ifNestLevel int
  408. moduleNameCount map[string]int // count of imported modules with given basename
  409. fatalError error
  410. outputSuffix string
  411. errorLogger ErrorLogger
  412. tracedVariables map[string]bool // variables to be traced in the generated script
  413. variables map[string]variable
  414. outputDir string
  415. dependentModules map[string]*moduleInfo
  416. soongNamespaces map[string]map[string]bool
  417. includeTops []string
  418. typeHints map[string]starlarkType
  419. atTopOfMakefile bool
  420. }
  421. func newParseContext(ss *StarlarkScript, nodes []mkparser.Node) *parseContext {
  422. predefined := []struct{ name, value string }{
  423. {"SRC_TARGET_DIR", filepath.Join("build", "make", "target")},
  424. {"LOCAL_PATH", filepath.Dir(ss.mkFile)},
  425. {"MAKEFILE_LIST", ss.mkFile},
  426. {"TOPDIR", ""}, // TOPDIR is just set to an empty string in cleanbuild.mk and core.mk
  427. // TODO(asmundak): maybe read it from build/make/core/envsetup.mk?
  428. {"TARGET_COPY_OUT_SYSTEM", "system"},
  429. {"TARGET_COPY_OUT_SYSTEM_OTHER", "system_other"},
  430. {"TARGET_COPY_OUT_DATA", "data"},
  431. {"TARGET_COPY_OUT_ASAN", filepath.Join("data", "asan")},
  432. {"TARGET_COPY_OUT_OEM", "oem"},
  433. {"TARGET_COPY_OUT_RAMDISK", "ramdisk"},
  434. {"TARGET_COPY_OUT_DEBUG_RAMDISK", "debug_ramdisk"},
  435. {"TARGET_COPY_OUT_VENDOR_DEBUG_RAMDISK", "vendor_debug_ramdisk"},
  436. {"TARGET_COPY_OUT_TEST_HARNESS_RAMDISK", "test_harness_ramdisk"},
  437. {"TARGET_COPY_OUT_ROOT", "root"},
  438. {"TARGET_COPY_OUT_RECOVERY", "recovery"},
  439. {"TARGET_COPY_OUT_VENDOR_RAMDISK", "vendor_ramdisk"},
  440. // TODO(asmundak): to process internal config files, we need the following variables:
  441. // TARGET_VENDOR
  442. // target_base_product
  443. //
  444. // the following utility variables are set in build/make/common/core.mk:
  445. {"empty", ""},
  446. {"space", " "},
  447. {"comma", ","},
  448. {"newline", "\n"},
  449. {"pound", "#"},
  450. {"backslash", "\\"},
  451. }
  452. ctx := &parseContext{
  453. script: ss,
  454. nodes: nodes,
  455. currentNodeIndex: 0,
  456. ifNestLevel: 0,
  457. moduleNameCount: make(map[string]int),
  458. variables: make(map[string]variable),
  459. dependentModules: make(map[string]*moduleInfo),
  460. soongNamespaces: make(map[string]map[string]bool),
  461. includeTops: []string{},
  462. typeHints: make(map[string]starlarkType),
  463. atTopOfMakefile: true,
  464. }
  465. for _, item := range predefined {
  466. ctx.variables[item.name] = &predefinedVariable{
  467. baseVariable: baseVariable{nam: item.name, typ: starlarkTypeString},
  468. value: &stringLiteralExpr{item.value},
  469. }
  470. }
  471. return ctx
  472. }
  473. func (ctx *parseContext) hasNodes() bool {
  474. return ctx.currentNodeIndex < len(ctx.nodes)
  475. }
  476. func (ctx *parseContext) getNode() mkparser.Node {
  477. if !ctx.hasNodes() {
  478. return nil
  479. }
  480. node := ctx.nodes[ctx.currentNodeIndex]
  481. ctx.currentNodeIndex++
  482. return node
  483. }
  484. func (ctx *parseContext) backNode() {
  485. if ctx.currentNodeIndex <= 0 {
  486. panic("Cannot back off")
  487. }
  488. ctx.currentNodeIndex--
  489. }
  490. func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) []starlarkNode {
  491. // Handle only simple variables
  492. if !a.Name.Const() || a.Target != nil {
  493. return []starlarkNode{ctx.newBadNode(a, "Only simple variables are handled")}
  494. }
  495. name := a.Name.Strings[0]
  496. // The `override` directive
  497. // override FOO :=
  498. // is parsed as an assignment to a variable named `override FOO`.
  499. // There are very few places where `override` is used, just flag it.
  500. if strings.HasPrefix(name, "override ") {
  501. return []starlarkNode{ctx.newBadNode(a, "cannot handle override directive")}
  502. }
  503. if name == ".KATI_READONLY" {
  504. // Skip assignments to .KATI_READONLY. If it was in the output file, it
  505. // would be an error because it would be sorted before the definition of
  506. // the variable it's trying to make readonly.
  507. return []starlarkNode{}
  508. }
  509. // Soong configuration
  510. if strings.HasPrefix(name, soongNsPrefix) {
  511. return ctx.handleSoongNsAssignment(strings.TrimPrefix(name, soongNsPrefix), a)
  512. }
  513. lhs := ctx.addVariable(name)
  514. if lhs == nil {
  515. return []starlarkNode{ctx.newBadNode(a, "unknown variable %s", name)}
  516. }
  517. _, isTraced := ctx.tracedVariables[lhs.name()]
  518. asgn := &assignmentNode{lhs: lhs, mkValue: a.Value, isTraced: isTraced, location: ctx.errorLocation(a)}
  519. if lhs.valueType() == starlarkTypeUnknown {
  520. // Try to divine variable type from the RHS
  521. asgn.value = ctx.parseMakeString(a, a.Value)
  522. inferred_type := asgn.value.typ()
  523. if inferred_type != starlarkTypeUnknown {
  524. lhs.setValueType(inferred_type)
  525. }
  526. }
  527. if lhs.valueType() == starlarkTypeList {
  528. xConcat, xBad := ctx.buildConcatExpr(a)
  529. if xBad != nil {
  530. asgn.value = xBad
  531. } else {
  532. switch len(xConcat.items) {
  533. case 0:
  534. asgn.value = &listExpr{}
  535. case 1:
  536. asgn.value = xConcat.items[0]
  537. default:
  538. asgn.value = xConcat
  539. }
  540. }
  541. } else {
  542. asgn.value = ctx.parseMakeString(a, a.Value)
  543. }
  544. if asgn.lhs.valueType() == starlarkTypeString &&
  545. asgn.value.typ() != starlarkTypeUnknown &&
  546. asgn.value.typ() != starlarkTypeString {
  547. asgn.value = &toStringExpr{expr: asgn.value}
  548. }
  549. switch a.Type {
  550. case "=", ":=":
  551. asgn.flavor = asgnSet
  552. case "+=":
  553. asgn.flavor = asgnAppend
  554. case "?=":
  555. asgn.flavor = asgnMaybeSet
  556. default:
  557. panic(fmt.Errorf("unexpected assignment type %s", a.Type))
  558. }
  559. return []starlarkNode{asgn}
  560. }
  561. func (ctx *parseContext) handleSoongNsAssignment(name string, asgn *mkparser.Assignment) []starlarkNode {
  562. val := ctx.parseMakeString(asgn, asgn.Value)
  563. if xBad, ok := val.(*badExpr); ok {
  564. return []starlarkNode{&exprNode{expr: xBad}}
  565. }
  566. // Unfortunately, Soong namespaces can be set up by directly setting corresponding Make
  567. // variables instead of via add_soong_config_namespace + add_soong_config_var_value.
  568. // Try to divine the call from the assignment as follows:
  569. if name == "NAMESPACES" {
  570. // Upon seeng
  571. // SOONG_CONFIG_NAMESPACES += foo
  572. // remember that there is a namespace `foo` and act as we saw
  573. // $(call add_soong_config_namespace,foo)
  574. s, ok := maybeString(val)
  575. if !ok {
  576. return []starlarkNode{ctx.newBadNode(asgn, "cannot handle variables in SOONG_CONFIG_NAMESPACES assignment, please use add_soong_config_namespace instead")}
  577. }
  578. result := make([]starlarkNode, 0)
  579. for _, ns := range strings.Fields(s) {
  580. ctx.addSoongNamespace(ns)
  581. result = append(result, &exprNode{&callExpr{
  582. name: baseName + ".soong_config_namespace",
  583. args: []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{ns}},
  584. returnType: starlarkTypeVoid,
  585. }})
  586. }
  587. return result
  588. } else {
  589. // Upon seeing
  590. // SOONG_CONFIG_x_y = v
  591. // find a namespace called `x` and act as if we encountered
  592. // $(call soong_config_set,x,y,v)
  593. // or check that `x_y` is a namespace, and then add the RHS of this assignment as variables in
  594. // it.
  595. // Emit an error in the ambiguous situation (namespaces `foo_bar` with a variable `baz`
  596. // and `foo` with a variable `bar_baz`.
  597. namespaceName := ""
  598. if ctx.hasSoongNamespace(name) {
  599. namespaceName = name
  600. }
  601. var varName string
  602. for pos, ch := range name {
  603. if !(ch == '_' && ctx.hasSoongNamespace(name[0:pos])) {
  604. continue
  605. }
  606. if namespaceName != "" {
  607. return []starlarkNode{ctx.newBadNode(asgn, "ambiguous soong namespace (may be either `%s` or `%s`)", namespaceName, name[0:pos])}
  608. }
  609. namespaceName = name[0:pos]
  610. varName = name[pos+1:]
  611. }
  612. if namespaceName == "" {
  613. return []starlarkNode{ctx.newBadNode(asgn, "cannot figure out Soong namespace, please use add_soong_config_var_value macro instead")}
  614. }
  615. if varName == "" {
  616. // Remember variables in this namespace
  617. s, ok := maybeString(val)
  618. if !ok {
  619. return []starlarkNode{ctx.newBadNode(asgn, "cannot handle variables in SOONG_CONFIG_ assignment, please use add_soong_config_var_value instead")}
  620. }
  621. ctx.updateSoongNamespace(asgn.Type != "+=", namespaceName, strings.Fields(s))
  622. return []starlarkNode{}
  623. }
  624. // Finally, handle assignment to a namespace variable
  625. if !ctx.hasNamespaceVar(namespaceName, varName) {
  626. return []starlarkNode{ctx.newBadNode(asgn, "no %s variable in %s namespace, please use add_soong_config_var_value instead", varName, namespaceName)}
  627. }
  628. fname := baseName + "." + soongConfigAssign
  629. if asgn.Type == "+=" {
  630. fname = baseName + "." + soongConfigAppend
  631. }
  632. return []starlarkNode{&exprNode{&callExpr{
  633. name: fname,
  634. args: []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{namespaceName}, &stringLiteralExpr{varName}, val},
  635. returnType: starlarkTypeVoid,
  636. }}}
  637. }
  638. }
  639. func (ctx *parseContext) buildConcatExpr(a *mkparser.Assignment) (*concatExpr, *badExpr) {
  640. xConcat := &concatExpr{}
  641. var xItemList *listExpr
  642. addToItemList := func(x ...starlarkExpr) {
  643. if xItemList == nil {
  644. xItemList = &listExpr{[]starlarkExpr{}}
  645. }
  646. xItemList.items = append(xItemList.items, x...)
  647. }
  648. finishItemList := func() {
  649. if xItemList != nil {
  650. xConcat.items = append(xConcat.items, xItemList)
  651. xItemList = nil
  652. }
  653. }
  654. items := a.Value.Words()
  655. for _, item := range items {
  656. // A function call in RHS is supposed to return a list, all other item
  657. // expressions return individual elements.
  658. switch x := ctx.parseMakeString(a, item).(type) {
  659. case *badExpr:
  660. return nil, x
  661. case *stringLiteralExpr:
  662. addToItemList(maybeConvertToStringList(x).(*listExpr).items...)
  663. default:
  664. switch x.typ() {
  665. case starlarkTypeList:
  666. finishItemList()
  667. xConcat.items = append(xConcat.items, x)
  668. case starlarkTypeString:
  669. finishItemList()
  670. xConcat.items = append(xConcat.items, &callExpr{
  671. object: x,
  672. name: "split",
  673. args: nil,
  674. returnType: starlarkTypeList,
  675. })
  676. default:
  677. addToItemList(x)
  678. }
  679. }
  680. }
  681. if xItemList != nil {
  682. xConcat.items = append(xConcat.items, xItemList)
  683. }
  684. return xConcat, nil
  685. }
  686. func (ctx *parseContext) newDependentModule(path string, optional bool) *moduleInfo {
  687. modulePath := ctx.loadedModulePath(path)
  688. if mi, ok := ctx.dependentModules[modulePath]; ok {
  689. mi.optional = mi.optional && optional
  690. return mi
  691. }
  692. moduleName := moduleNameForFile(path)
  693. moduleLocalName := "_" + moduleName
  694. n, found := ctx.moduleNameCount[moduleName]
  695. if found {
  696. moduleLocalName += fmt.Sprintf("%d", n)
  697. }
  698. ctx.moduleNameCount[moduleName] = n + 1
  699. _, err := fs.Stat(ctx.script.sourceFS, path)
  700. mi := &moduleInfo{
  701. path: modulePath,
  702. originalPath: path,
  703. moduleLocalName: moduleLocalName,
  704. optional: optional,
  705. missing: err != nil,
  706. }
  707. ctx.dependentModules[modulePath] = mi
  708. ctx.script.inherited = append(ctx.script.inherited, mi)
  709. return mi
  710. }
  711. func (ctx *parseContext) handleSubConfig(
  712. v mkparser.Node, pathExpr starlarkExpr, loadAlways bool, processModule func(inheritedModule) starlarkNode) []starlarkNode {
  713. // Allow seeing $(sort $(wildcard realPathExpr)) or $(wildcard realPathExpr)
  714. // because those are functionally the same as not having the sort/wildcard calls.
  715. if ce, ok := pathExpr.(*callExpr); ok && ce.name == "rblf.mksort" && len(ce.args) == 1 {
  716. if ce2, ok2 := ce.args[0].(*callExpr); ok2 && ce2.name == "rblf.expand_wildcard" && len(ce2.args) == 1 {
  717. pathExpr = ce2.args[0]
  718. }
  719. } else if ce2, ok2 := pathExpr.(*callExpr); ok2 && ce2.name == "rblf.expand_wildcard" && len(ce2.args) == 1 {
  720. pathExpr = ce2.args[0]
  721. }
  722. // In a simple case, the name of a module to inherit/include is known statically.
  723. if path, ok := maybeString(pathExpr); ok {
  724. // Note that even if this directive loads a module unconditionally, a module may be
  725. // absent without causing any harm if this directive is inside an if/else block.
  726. moduleShouldExist := loadAlways && ctx.ifNestLevel == 0
  727. if strings.Contains(path, "*") {
  728. if paths, err := fs.Glob(ctx.script.sourceFS, path); err == nil {
  729. sort.Strings(paths)
  730. result := make([]starlarkNode, 0)
  731. for _, p := range paths {
  732. mi := ctx.newDependentModule(p, !moduleShouldExist)
  733. result = append(result, processModule(inheritedStaticModule{mi, loadAlways}))
  734. }
  735. return result
  736. } else {
  737. return []starlarkNode{ctx.newBadNode(v, "cannot glob wildcard argument")}
  738. }
  739. } else {
  740. mi := ctx.newDependentModule(path, !moduleShouldExist)
  741. return []starlarkNode{processModule(inheritedStaticModule{mi, loadAlways})}
  742. }
  743. }
  744. // If module path references variables (e.g., $(v1)/foo/$(v2)/device-config.mk), find all the paths in the
  745. // source tree that may be a match and the corresponding variable values. For instance, if the source tree
  746. // contains vendor1/foo/abc/dev.mk and vendor2/foo/def/dev.mk, the first one will be inherited when
  747. // (v1, v2) == ('vendor1', 'abc'), and the second one when (v1, v2) == ('vendor2', 'def').
  748. // We then emit the code that loads all of them, e.g.:
  749. // load("//vendor1/foo/abc:dev.rbc", _dev1_init="init")
  750. // load("//vendor2/foo/def/dev.rbc", _dev2_init="init")
  751. // And then inherit it as follows:
  752. // _e = {
  753. // "vendor1/foo/abc/dev.mk": ("vendor1/foo/abc/dev", _dev1_init),
  754. // "vendor2/foo/def/dev.mk": ("vendor2/foo/def/dev", _dev_init2) }.get("%s/foo/%s/dev.mk" % (v1, v2))
  755. // if _e:
  756. // rblf.inherit(handle, _e[0], _e[1])
  757. //
  758. var matchingPaths []string
  759. var needsWarning = false
  760. if interpolate, ok := pathExpr.(*interpolateExpr); ok {
  761. pathPattern := []string{interpolate.chunks[0]}
  762. for _, chunk := range interpolate.chunks[1:] {
  763. if chunk != "" {
  764. pathPattern = append(pathPattern, chunk)
  765. }
  766. }
  767. if len(pathPattern) == 1 {
  768. pathPattern = append(pathPattern, "")
  769. }
  770. matchingPaths = ctx.findMatchingPaths(pathPattern)
  771. needsWarning = pathPattern[0] == "" && len(ctx.includeTops) == 0
  772. } else if len(ctx.includeTops) > 0 {
  773. matchingPaths = append(matchingPaths, ctx.findMatchingPaths([]string{"", ""})...)
  774. } else {
  775. return []starlarkNode{ctx.newBadNode(v, "inherit-product/include argument is too complex")}
  776. }
  777. // Safeguard against $(call inherit-product,$(PRODUCT_PATH))
  778. const maxMatchingFiles = 150
  779. if len(matchingPaths) > maxMatchingFiles {
  780. return []starlarkNode{ctx.newBadNode(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles)}
  781. }
  782. res := inheritedDynamicModule{pathExpr, []*moduleInfo{}, loadAlways, ctx.errorLocation(v), needsWarning}
  783. for _, p := range matchingPaths {
  784. // A product configuration files discovered dynamically may attempt to inherit
  785. // from another one which does not exist in this source tree. Prevent load errors
  786. // by always loading the dynamic files as optional.
  787. res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true))
  788. }
  789. return []starlarkNode{processModule(res)}
  790. }
  791. func (ctx *parseContext) findMatchingPaths(pattern []string) []string {
  792. files := ctx.script.makefileFinder.Find(".")
  793. if len(pattern) == 0 {
  794. return files
  795. }
  796. // Create regular expression from the pattern
  797. regexString := "^" + regexp.QuoteMeta(pattern[0])
  798. for _, s := range pattern[1:] {
  799. regexString += ".*" + regexp.QuoteMeta(s)
  800. }
  801. regexString += "$"
  802. rex := regexp.MustCompile(regexString)
  803. includeTopRegexString := ""
  804. if len(ctx.includeTops) > 0 {
  805. for i, top := range ctx.includeTops {
  806. if i > 0 {
  807. includeTopRegexString += "|"
  808. }
  809. includeTopRegexString += "^" + regexp.QuoteMeta(top)
  810. }
  811. } else {
  812. includeTopRegexString = ".*"
  813. }
  814. includeTopRegex := regexp.MustCompile(includeTopRegexString)
  815. // Now match
  816. var res []string
  817. for _, p := range files {
  818. if rex.MatchString(p) && includeTopRegex.MatchString(p) {
  819. res = append(res, p)
  820. }
  821. }
  822. return res
  823. }
  824. type inheritProductCallParser struct {
  825. loadAlways bool
  826. }
  827. func (p *inheritProductCallParser) parse(ctx *parseContext, v mkparser.Node, args *mkparser.MakeString) []starlarkNode {
  828. args.TrimLeftSpaces()
  829. args.TrimRightSpaces()
  830. pathExpr := ctx.parseMakeString(v, args)
  831. if _, ok := pathExpr.(*badExpr); ok {
  832. return []starlarkNode{ctx.newBadNode(v, "Unable to parse argument to inherit")}
  833. }
  834. return ctx.handleSubConfig(v, pathExpr, p.loadAlways, func(im inheritedModule) starlarkNode {
  835. return &inheritNode{im, p.loadAlways}
  836. })
  837. }
  838. func (ctx *parseContext) handleInclude(v *mkparser.Directive) []starlarkNode {
  839. loadAlways := v.Name[0] != '-'
  840. return ctx.handleSubConfig(v, ctx.parseMakeString(v, v.Args), loadAlways, func(im inheritedModule) starlarkNode {
  841. return &includeNode{im, loadAlways}
  842. })
  843. }
  844. func (ctx *parseContext) handleVariable(v *mkparser.Variable) []starlarkNode {
  845. // Handle:
  846. // $(call inherit-product,...)
  847. // $(call inherit-product-if-exists,...)
  848. // $(info xxx)
  849. // $(warning xxx)
  850. // $(error xxx)
  851. // $(call other-custom-functions,...)
  852. if name, args, ok := ctx.maybeParseFunctionCall(v, v.Name); ok {
  853. if kf, ok := knownNodeFunctions[name]; ok {
  854. return kf.parse(ctx, v, args)
  855. }
  856. }
  857. return []starlarkNode{&exprNode{expr: ctx.parseReference(v, v.Name)}}
  858. }
  859. func (ctx *parseContext) maybeHandleDefine(directive *mkparser.Directive) starlarkNode {
  860. macro_name := strings.Fields(directive.Args.Strings[0])[0]
  861. // Ignore the macros that we handle
  862. _, ignored := ignoredDefines[macro_name]
  863. _, known := knownFunctions[macro_name]
  864. if !ignored && !known {
  865. return ctx.newBadNode(directive, "define is not supported: %s", macro_name)
  866. }
  867. return nil
  868. }
  869. func (ctx *parseContext) handleIfBlock(ifDirective *mkparser.Directive) starlarkNode {
  870. ssSwitch := &switchNode{
  871. ssCases: []*switchCase{ctx.processBranch(ifDirective)},
  872. }
  873. for ctx.hasNodes() && ctx.fatalError == nil {
  874. node := ctx.getNode()
  875. switch x := node.(type) {
  876. case *mkparser.Directive:
  877. switch x.Name {
  878. case "else", "elifdef", "elifndef", "elifeq", "elifneq":
  879. ssSwitch.ssCases = append(ssSwitch.ssCases, ctx.processBranch(x))
  880. case "endif":
  881. return ssSwitch
  882. default:
  883. return ctx.newBadNode(node, "unexpected directive %s", x.Name)
  884. }
  885. default:
  886. return ctx.newBadNode(ifDirective, "unexpected statement")
  887. }
  888. }
  889. if ctx.fatalError == nil {
  890. ctx.fatalError = fmt.Errorf("no matching endif for %s", ifDirective.Dump())
  891. }
  892. return ctx.newBadNode(ifDirective, "no matching endif for %s", ifDirective.Dump())
  893. }
  894. // processBranch processes a single branch (if/elseif/else) until the next directive
  895. // on the same level.
  896. func (ctx *parseContext) processBranch(check *mkparser.Directive) *switchCase {
  897. block := &switchCase{gate: ctx.parseCondition(check)}
  898. defer func() {
  899. ctx.ifNestLevel--
  900. }()
  901. ctx.ifNestLevel++
  902. for ctx.hasNodes() {
  903. node := ctx.getNode()
  904. if d, ok := node.(*mkparser.Directive); ok {
  905. switch d.Name {
  906. case "else", "elifdef", "elifndef", "elifeq", "elifneq", "endif":
  907. ctx.backNode()
  908. return block
  909. }
  910. }
  911. block.nodes = append(block.nodes, ctx.handleSimpleStatement(node)...)
  912. }
  913. ctx.fatalError = fmt.Errorf("no matching endif for %s", check.Dump())
  914. return block
  915. }
  916. func (ctx *parseContext) parseCondition(check *mkparser.Directive) starlarkNode {
  917. switch check.Name {
  918. case "ifdef", "ifndef", "elifdef", "elifndef":
  919. if !check.Args.Const() {
  920. return ctx.newBadNode(check, "ifdef variable ref too complex: %s", check.Args.Dump())
  921. }
  922. v := NewVariableRefExpr(ctx.addVariable(check.Args.Strings[0]))
  923. if strings.HasSuffix(check.Name, "ndef") {
  924. v = &notExpr{v}
  925. }
  926. return &ifNode{
  927. isElif: strings.HasPrefix(check.Name, "elif"),
  928. expr: v,
  929. }
  930. case "ifeq", "ifneq", "elifeq", "elifneq":
  931. return &ifNode{
  932. isElif: strings.HasPrefix(check.Name, "elif"),
  933. expr: ctx.parseCompare(check),
  934. }
  935. case "else":
  936. return &elseNode{}
  937. default:
  938. panic(fmt.Errorf("%s: unknown directive: %s", ctx.script.mkFile, check.Dump()))
  939. }
  940. }
  941. func (ctx *parseContext) newBadExpr(node mkparser.Node, text string, args ...interface{}) starlarkExpr {
  942. if ctx.errorLogger != nil {
  943. ctx.errorLogger.NewError(ctx.errorLocation(node), node, text, args...)
  944. }
  945. ctx.script.hasErrors = true
  946. return &badExpr{errorLocation: ctx.errorLocation(node), message: fmt.Sprintf(text, args...)}
  947. }
  948. // records that the given node failed to be converted and includes an explanatory message
  949. func (ctx *parseContext) newBadNode(failedNode mkparser.Node, message string, args ...interface{}) starlarkNode {
  950. return &exprNode{ctx.newBadExpr(failedNode, message, args...)}
  951. }
  952. func (ctx *parseContext) parseCompare(cond *mkparser.Directive) starlarkExpr {
  953. // Strip outer parentheses
  954. mkArg := cloneMakeString(cond.Args)
  955. mkArg.Strings[0] = strings.TrimLeft(mkArg.Strings[0], "( ")
  956. n := len(mkArg.Strings)
  957. mkArg.Strings[n-1] = strings.TrimRight(mkArg.Strings[n-1], ") ")
  958. args := mkArg.Split(",")
  959. // TODO(asmundak): handle the case where the arguments are in quotes and space-separated
  960. if len(args) != 2 {
  961. return ctx.newBadExpr(cond, "ifeq/ifneq len(args) != 2 %s", cond.Dump())
  962. }
  963. args[0].TrimRightSpaces()
  964. args[1].TrimLeftSpaces()
  965. isEq := !strings.HasSuffix(cond.Name, "neq")
  966. xLeft := ctx.parseMakeString(cond, args[0])
  967. xRight := ctx.parseMakeString(cond, args[1])
  968. if bad, ok := xLeft.(*badExpr); ok {
  969. return bad
  970. }
  971. if bad, ok := xRight.(*badExpr); ok {
  972. return bad
  973. }
  974. if expr, ok := ctx.parseCompareSpecialCases(cond, xLeft, xRight); ok {
  975. return expr
  976. }
  977. var stringOperand string
  978. var otherOperand starlarkExpr
  979. if s, ok := maybeString(xLeft); ok {
  980. stringOperand = s
  981. otherOperand = xRight
  982. } else if s, ok := maybeString(xRight); ok {
  983. stringOperand = s
  984. otherOperand = xLeft
  985. }
  986. // If we've identified one of the operands as being a string literal, check
  987. // for some special cases we can do to simplify the resulting expression.
  988. if otherOperand != nil {
  989. if stringOperand == "" {
  990. if isEq {
  991. return negateExpr(otherOperand)
  992. } else {
  993. return otherOperand
  994. }
  995. }
  996. if stringOperand == "true" && otherOperand.typ() == starlarkTypeBool {
  997. if !isEq {
  998. return negateExpr(otherOperand)
  999. } else {
  1000. return otherOperand
  1001. }
  1002. }
  1003. if otherOperand.typ() == starlarkTypeList {
  1004. fields := strings.Fields(stringOperand)
  1005. elements := make([]starlarkExpr, len(fields))
  1006. for i, s := range fields {
  1007. elements[i] = &stringLiteralExpr{literal: s}
  1008. }
  1009. return &eqExpr{
  1010. left: otherOperand,
  1011. right: &listExpr{elements},
  1012. isEq: isEq,
  1013. }
  1014. }
  1015. if intOperand, err := strconv.Atoi(strings.TrimSpace(stringOperand)); err == nil && otherOperand.typ() == starlarkTypeInt {
  1016. return &eqExpr{
  1017. left: otherOperand,
  1018. right: &intLiteralExpr{literal: intOperand},
  1019. isEq: isEq,
  1020. }
  1021. }
  1022. }
  1023. return &eqExpr{left: xLeft, right: xRight, isEq: isEq}
  1024. }
  1025. // Given an if statement's directive and the left/right starlarkExprs,
  1026. // check if the starlarkExprs are one of a few hardcoded special cases
  1027. // that can be converted to a simpler equality expression than simply comparing
  1028. // the two.
  1029. func (ctx *parseContext) parseCompareSpecialCases(directive *mkparser.Directive, left starlarkExpr,
  1030. right starlarkExpr) (starlarkExpr, bool) {
  1031. isEq := !strings.HasSuffix(directive.Name, "neq")
  1032. // All the special cases require a call on one side and a
  1033. // string literal/variable on the other. Turn the left/right variables into
  1034. // call/value variables, and return false if that's not possible.
  1035. var value starlarkExpr = nil
  1036. call, ok := left.(*callExpr)
  1037. if ok {
  1038. switch right.(type) {
  1039. case *stringLiteralExpr, *variableRefExpr:
  1040. value = right
  1041. }
  1042. } else {
  1043. call, _ = right.(*callExpr)
  1044. switch left.(type) {
  1045. case *stringLiteralExpr, *variableRefExpr:
  1046. value = left
  1047. }
  1048. }
  1049. if call == nil || value == nil {
  1050. return nil, false
  1051. }
  1052. switch call.name {
  1053. case baseName + ".filter":
  1054. return ctx.parseCompareFilterFuncResult(directive, call, value, isEq)
  1055. case baseName + ".findstring":
  1056. return ctx.parseCheckFindstringFuncResult(directive, call, value, !isEq), true
  1057. case baseName + ".strip":
  1058. return ctx.parseCompareStripFuncResult(directive, call, value, !isEq), true
  1059. }
  1060. return nil, false
  1061. }
  1062. func (ctx *parseContext) parseCompareFilterFuncResult(cond *mkparser.Directive,
  1063. filterFuncCall *callExpr, xValue starlarkExpr, negate bool) (starlarkExpr, bool) {
  1064. // We handle:
  1065. // * ifeq/ifneq (,$(filter v1 v2 ..., EXPR) becomes if EXPR not in/in ["v1", "v2", ...]
  1066. // * ifeq/ifneq (,$(filter EXPR, v1 v2 ...) becomes if EXPR not in/in ["v1", "v2", ...]
  1067. if x, ok := xValue.(*stringLiteralExpr); !ok || x.literal != "" {
  1068. return nil, false
  1069. }
  1070. xPattern := filterFuncCall.args[0]
  1071. xText := filterFuncCall.args[1]
  1072. var xInList *stringLiteralExpr
  1073. var expr starlarkExpr
  1074. var ok bool
  1075. if xInList, ok = xPattern.(*stringLiteralExpr); ok && !strings.ContainsRune(xInList.literal, '%') && xText.typ() == starlarkTypeList {
  1076. expr = xText
  1077. } else if xInList, ok = xText.(*stringLiteralExpr); ok {
  1078. expr = xPattern
  1079. } else {
  1080. return nil, false
  1081. }
  1082. slExpr := newStringListExpr(strings.Fields(xInList.literal))
  1083. // Generate simpler code for the common cases:
  1084. if expr.typ() == starlarkTypeList {
  1085. if len(slExpr.items) == 1 {
  1086. // Checking that a string belongs to list
  1087. return &inExpr{isNot: negate, list: expr, expr: slExpr.items[0]}, true
  1088. } else {
  1089. return nil, false
  1090. }
  1091. } else if len(slExpr.items) == 1 {
  1092. return &eqExpr{left: expr, right: slExpr.items[0], isEq: !negate}, true
  1093. } else {
  1094. return &inExpr{isNot: negate, list: newStringListExpr(strings.Fields(xInList.literal)), expr: expr}, true
  1095. }
  1096. }
  1097. func (ctx *parseContext) parseCheckFindstringFuncResult(directive *mkparser.Directive,
  1098. xCall *callExpr, xValue starlarkExpr, negate bool) starlarkExpr {
  1099. if isEmptyString(xValue) {
  1100. return &eqExpr{
  1101. left: &callExpr{
  1102. object: xCall.args[1],
  1103. name: "find",
  1104. args: []starlarkExpr{xCall.args[0]},
  1105. returnType: starlarkTypeInt,
  1106. },
  1107. right: &intLiteralExpr{-1},
  1108. isEq: !negate,
  1109. }
  1110. } else if s, ok := maybeString(xValue); ok {
  1111. if s2, ok := maybeString(xCall.args[0]); ok && s == s2 {
  1112. return &eqExpr{
  1113. left: &callExpr{
  1114. object: xCall.args[1],
  1115. name: "find",
  1116. args: []starlarkExpr{xCall.args[0]},
  1117. returnType: starlarkTypeInt,
  1118. },
  1119. right: &intLiteralExpr{-1},
  1120. isEq: negate,
  1121. }
  1122. }
  1123. }
  1124. return ctx.newBadExpr(directive, "$(findstring) can only be compared to nothing or its first argument")
  1125. }
  1126. func (ctx *parseContext) parseCompareStripFuncResult(directive *mkparser.Directive,
  1127. xCall *callExpr, xValue starlarkExpr, negate bool) starlarkExpr {
  1128. if _, ok := xValue.(*stringLiteralExpr); !ok {
  1129. return ctx.newBadExpr(directive, "strip result can be compared only to string: %s", xValue)
  1130. }
  1131. return &eqExpr{
  1132. left: &callExpr{
  1133. name: "strip",
  1134. args: xCall.args,
  1135. returnType: starlarkTypeString,
  1136. },
  1137. right: xValue, isEq: !negate}
  1138. }
  1139. func (ctx *parseContext) maybeParseFunctionCall(node mkparser.Node, ref *mkparser.MakeString) (name string, args *mkparser.MakeString, ok bool) {
  1140. ref.TrimLeftSpaces()
  1141. ref.TrimRightSpaces()
  1142. words := ref.SplitN(" ", 2)
  1143. if !words[0].Const() {
  1144. return "", nil, false
  1145. }
  1146. name = words[0].Dump()
  1147. args = mkparser.SimpleMakeString("", words[0].Pos())
  1148. if len(words) >= 2 {
  1149. args = words[1]
  1150. }
  1151. args.TrimLeftSpaces()
  1152. if name == "call" {
  1153. words = args.SplitN(",", 2)
  1154. if words[0].Empty() || !words[0].Const() {
  1155. return "", nil, false
  1156. }
  1157. name = words[0].Dump()
  1158. if len(words) < 2 {
  1159. args = mkparser.SimpleMakeString("", words[0].Pos())
  1160. } else {
  1161. args = words[1]
  1162. }
  1163. }
  1164. ok = true
  1165. return
  1166. }
  1167. // parses $(...), returning an expression
  1168. func (ctx *parseContext) parseReference(node mkparser.Node, ref *mkparser.MakeString) starlarkExpr {
  1169. ref.TrimLeftSpaces()
  1170. ref.TrimRightSpaces()
  1171. refDump := ref.Dump()
  1172. // Handle only the case where the first (or only) word is constant
  1173. words := ref.SplitN(" ", 2)
  1174. if !words[0].Const() {
  1175. if len(words) == 1 {
  1176. expr := ctx.parseMakeString(node, ref)
  1177. return &callExpr{
  1178. object: &identifierExpr{"cfg"},
  1179. name: "get",
  1180. args: []starlarkExpr{
  1181. expr,
  1182. &callExpr{
  1183. object: &identifierExpr{"g"},
  1184. name: "get",
  1185. args: []starlarkExpr{
  1186. expr,
  1187. &stringLiteralExpr{literal: ""},
  1188. },
  1189. returnType: starlarkTypeUnknown,
  1190. },
  1191. },
  1192. returnType: starlarkTypeUnknown,
  1193. }
  1194. } else {
  1195. return ctx.newBadExpr(node, "reference is too complex: %s", refDump)
  1196. }
  1197. }
  1198. if name, _, ok := ctx.maybeParseFunctionCall(node, ref); ok {
  1199. if _, unsupported := unsupportedFunctions[name]; unsupported {
  1200. return ctx.newBadExpr(node, "%s is not supported", refDump)
  1201. }
  1202. }
  1203. // If it is a single word, it can be a simple variable
  1204. // reference or a function call
  1205. if len(words) == 1 && !isMakeControlFunc(refDump) && refDump != "shell" && refDump != "eval" {
  1206. if strings.HasPrefix(refDump, soongNsPrefix) {
  1207. // TODO (asmundak): if we find many, maybe handle them.
  1208. return ctx.newBadExpr(node, "SOONG_CONFIG_ variables cannot be referenced, use soong_config_get instead: %s", refDump)
  1209. }
  1210. // Handle substitution references: https://www.gnu.org/software/make/manual/html_node/Substitution-Refs.html
  1211. if strings.Contains(refDump, ":") {
  1212. parts := strings.SplitN(refDump, ":", 2)
  1213. substParts := strings.SplitN(parts[1], "=", 2)
  1214. if len(substParts) < 2 || strings.Count(substParts[0], "%") > 1 {
  1215. return ctx.newBadExpr(node, "Invalid substitution reference")
  1216. }
  1217. if !strings.Contains(substParts[0], "%") {
  1218. if strings.Contains(substParts[1], "%") {
  1219. return ctx.newBadExpr(node, "A substitution reference must have a %% in the \"before\" part of the substitution if it has one in the \"after\" part.")
  1220. }
  1221. substParts[0] = "%" + substParts[0]
  1222. substParts[1] = "%" + substParts[1]
  1223. }
  1224. v := ctx.addVariable(parts[0])
  1225. if v == nil {
  1226. return ctx.newBadExpr(node, "unknown variable %s", refDump)
  1227. }
  1228. return &callExpr{
  1229. name: baseName + ".mkpatsubst",
  1230. returnType: starlarkTypeString,
  1231. args: []starlarkExpr{
  1232. &stringLiteralExpr{literal: substParts[0]},
  1233. &stringLiteralExpr{literal: substParts[1]},
  1234. NewVariableRefExpr(v),
  1235. },
  1236. }
  1237. }
  1238. if v := ctx.addVariable(refDump); v != nil {
  1239. return NewVariableRefExpr(v)
  1240. }
  1241. return ctx.newBadExpr(node, "unknown variable %s", refDump)
  1242. }
  1243. if name, args, ok := ctx.maybeParseFunctionCall(node, ref); ok {
  1244. if kf, found := knownFunctions[name]; found {
  1245. return kf.parse(ctx, node, args)
  1246. } else {
  1247. return ctx.newBadExpr(node, "cannot handle invoking %s", name)
  1248. }
  1249. }
  1250. return ctx.newBadExpr(node, "cannot handle %s", refDump)
  1251. }
  1252. type simpleCallParser struct {
  1253. name string
  1254. returnType starlarkType
  1255. addGlobals bool
  1256. addHandle bool
  1257. }
  1258. func (p *simpleCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
  1259. expr := &callExpr{name: p.name, returnType: p.returnType}
  1260. if p.addGlobals {
  1261. expr.args = append(expr.args, &globalsExpr{})
  1262. }
  1263. if p.addHandle {
  1264. expr.args = append(expr.args, &identifierExpr{name: "handle"})
  1265. }
  1266. for _, arg := range args.Split(",") {
  1267. arg.TrimLeftSpaces()
  1268. arg.TrimRightSpaces()
  1269. x := ctx.parseMakeString(node, arg)
  1270. if xBad, ok := x.(*badExpr); ok {
  1271. return xBad
  1272. }
  1273. expr.args = append(expr.args, x)
  1274. }
  1275. return expr
  1276. }
  1277. type makeControlFuncParser struct {
  1278. name string
  1279. }
  1280. func (p *makeControlFuncParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
  1281. // Make control functions need special treatment as everything
  1282. // after the name is a single text argument
  1283. x := ctx.parseMakeString(node, args)
  1284. if xBad, ok := x.(*badExpr); ok {
  1285. return xBad
  1286. }
  1287. return &callExpr{
  1288. name: p.name,
  1289. args: []starlarkExpr{
  1290. &stringLiteralExpr{ctx.script.mkFile},
  1291. x,
  1292. },
  1293. returnType: starlarkTypeUnknown,
  1294. }
  1295. }
  1296. type shellCallParser struct{}
  1297. func (p *shellCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
  1298. // Shell functions need special treatment as everything
  1299. // after the name is a single text argument
  1300. x := ctx.parseMakeString(node, args)
  1301. if xBad, ok := x.(*badExpr); ok {
  1302. return xBad
  1303. }
  1304. return &callExpr{
  1305. name: baseName + ".shell",
  1306. args: []starlarkExpr{x},
  1307. returnType: starlarkTypeUnknown,
  1308. }
  1309. }
  1310. type myDirCallParser struct{}
  1311. func (p *myDirCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
  1312. if !args.Empty() {
  1313. return ctx.newBadExpr(node, "my-dir function cannot have any arguments passed to it.")
  1314. }
  1315. return &stringLiteralExpr{literal: filepath.Dir(ctx.script.mkFile)}
  1316. }
  1317. type andOrParser struct {
  1318. isAnd bool
  1319. }
  1320. func (p *andOrParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
  1321. if args.Empty() {
  1322. return ctx.newBadExpr(node, "and/or function must have at least 1 argument")
  1323. }
  1324. op := "or"
  1325. if p.isAnd {
  1326. op = "and"
  1327. }
  1328. argsParsed := make([]starlarkExpr, 0)
  1329. for _, arg := range args.Split(",") {
  1330. arg.TrimLeftSpaces()
  1331. arg.TrimRightSpaces()
  1332. x := ctx.parseMakeString(node, arg)
  1333. if xBad, ok := x.(*badExpr); ok {
  1334. return xBad
  1335. }
  1336. argsParsed = append(argsParsed, x)
  1337. }
  1338. typ := starlarkTypeUnknown
  1339. for _, arg := range argsParsed {
  1340. if typ != arg.typ() && arg.typ() != starlarkTypeUnknown && typ != starlarkTypeUnknown {
  1341. return ctx.newBadExpr(node, "Expected all arguments to $(or) or $(and) to have the same type, found %q and %q", typ.String(), arg.typ().String())
  1342. }
  1343. if arg.typ() != starlarkTypeUnknown {
  1344. typ = arg.typ()
  1345. }
  1346. }
  1347. result := argsParsed[0]
  1348. for _, arg := range argsParsed[1:] {
  1349. result = &binaryOpExpr{
  1350. left: result,
  1351. right: arg,
  1352. op: op,
  1353. returnType: typ,
  1354. }
  1355. }
  1356. return result
  1357. }
  1358. type isProductInListCallParser struct{}
  1359. func (p *isProductInListCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
  1360. if args.Empty() {
  1361. return ctx.newBadExpr(node, "is-product-in-list requires an argument")
  1362. }
  1363. return &inExpr{
  1364. expr: NewVariableRefExpr(ctx.addVariable("TARGET_PRODUCT")),
  1365. list: maybeConvertToStringList(ctx.parseMakeString(node, args)),
  1366. isNot: false,
  1367. }
  1368. }
  1369. type isVendorBoardPlatformCallParser struct{}
  1370. func (p *isVendorBoardPlatformCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
  1371. if args.Empty() || !identifierFullMatchRegex.MatchString(args.Dump()) {
  1372. return ctx.newBadExpr(node, "cannot handle non-constant argument to is-vendor-board-platform")
  1373. }
  1374. return &inExpr{
  1375. expr: NewVariableRefExpr(ctx.addVariable("TARGET_BOARD_PLATFORM")),
  1376. list: NewVariableRefExpr(ctx.addVariable(args.Dump() + "_BOARD_PLATFORMS")),
  1377. isNot: false,
  1378. }
  1379. }
  1380. type isVendorBoardQcomCallParser struct{}
  1381. func (p *isVendorBoardQcomCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
  1382. if !args.Empty() {
  1383. return ctx.newBadExpr(node, "is-vendor-board-qcom does not accept any arguments")
  1384. }
  1385. return &inExpr{
  1386. expr: NewVariableRefExpr(ctx.addVariable("TARGET_BOARD_PLATFORM")),
  1387. list: NewVariableRefExpr(ctx.addVariable("QCOM_BOARD_PLATFORMS")),
  1388. isNot: false,
  1389. }
  1390. }
  1391. type substCallParser struct {
  1392. fname string
  1393. }
  1394. func (p *substCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
  1395. words := args.Split(",")
  1396. if len(words) != 3 {
  1397. return ctx.newBadExpr(node, "%s function should have 3 arguments", p.fname)
  1398. }
  1399. from := ctx.parseMakeString(node, words[0])
  1400. if xBad, ok := from.(*badExpr); ok {
  1401. return xBad
  1402. }
  1403. to := ctx.parseMakeString(node, words[1])
  1404. if xBad, ok := to.(*badExpr); ok {
  1405. return xBad
  1406. }
  1407. words[2].TrimLeftSpaces()
  1408. words[2].TrimRightSpaces()
  1409. obj := ctx.parseMakeString(node, words[2])
  1410. typ := obj.typ()
  1411. if typ == starlarkTypeString && p.fname == "subst" {
  1412. // Optimization: if it's $(subst from, to, string), emit string.replace(from, to)
  1413. return &callExpr{
  1414. object: obj,
  1415. name: "replace",
  1416. args: []starlarkExpr{from, to},
  1417. returnType: typ,
  1418. }
  1419. }
  1420. return &callExpr{
  1421. name: baseName + ".mk" + p.fname,
  1422. args: []starlarkExpr{from, to, obj},
  1423. returnType: obj.typ(),
  1424. }
  1425. }
  1426. type ifCallParser struct{}
  1427. func (p *ifCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
  1428. words := args.Split(",")
  1429. if len(words) != 2 && len(words) != 3 {
  1430. return ctx.newBadExpr(node, "if function should have 2 or 3 arguments, found "+strconv.Itoa(len(words)))
  1431. }
  1432. condition := ctx.parseMakeString(node, words[0])
  1433. ifTrue := ctx.parseMakeString(node, words[1])
  1434. var ifFalse starlarkExpr
  1435. if len(words) == 3 {
  1436. ifFalse = ctx.parseMakeString(node, words[2])
  1437. } else {
  1438. switch ifTrue.typ() {
  1439. case starlarkTypeList:
  1440. ifFalse = &listExpr{items: []starlarkExpr{}}
  1441. case starlarkTypeInt:
  1442. ifFalse = &intLiteralExpr{literal: 0}
  1443. case starlarkTypeBool:
  1444. ifFalse = &boolLiteralExpr{literal: false}
  1445. default:
  1446. ifFalse = &stringLiteralExpr{literal: ""}
  1447. }
  1448. }
  1449. return &ifExpr{
  1450. condition,
  1451. ifTrue,
  1452. ifFalse,
  1453. }
  1454. }
  1455. type ifCallNodeParser struct{}
  1456. func (p *ifCallNodeParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) []starlarkNode {
  1457. words := args.Split(",")
  1458. if len(words) != 2 && len(words) != 3 {
  1459. return []starlarkNode{ctx.newBadNode(node, "if function should have 2 or 3 arguments, found "+strconv.Itoa(len(words)))}
  1460. }
  1461. ifn := &ifNode{expr: ctx.parseMakeString(node, words[0])}
  1462. cases := []*switchCase{
  1463. {
  1464. gate: ifn,
  1465. nodes: ctx.parseNodeMakeString(node, words[1]),
  1466. },
  1467. }
  1468. if len(words) == 3 {
  1469. cases = append(cases, &switchCase{
  1470. gate: &elseNode{},
  1471. nodes: ctx.parseNodeMakeString(node, words[2]),
  1472. })
  1473. }
  1474. if len(cases) == 2 {
  1475. if len(cases[1].nodes) == 0 {
  1476. // Remove else branch if it has no contents
  1477. cases = cases[:1]
  1478. } else if len(cases[0].nodes) == 0 {
  1479. // If the if branch has no contents but the else does,
  1480. // move them to the if and negate its condition
  1481. ifn.expr = negateExpr(ifn.expr)
  1482. cases[0].nodes = cases[1].nodes
  1483. cases = cases[:1]
  1484. }
  1485. }
  1486. return []starlarkNode{&switchNode{ssCases: cases}}
  1487. }
  1488. type foreachCallParser struct{}
  1489. func (p *foreachCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
  1490. words := args.Split(",")
  1491. if len(words) != 3 {
  1492. return ctx.newBadExpr(node, "foreach function should have 3 arguments, found "+strconv.Itoa(len(words)))
  1493. }
  1494. if !words[0].Const() || words[0].Empty() || !identifierFullMatchRegex.MatchString(words[0].Strings[0]) {
  1495. return ctx.newBadExpr(node, "first argument to foreach function must be a simple string identifier")
  1496. }
  1497. loopVarName := words[0].Strings[0]
  1498. list := ctx.parseMakeString(node, words[1])
  1499. action := ctx.parseMakeString(node, words[2]).transform(func(expr starlarkExpr) starlarkExpr {
  1500. if varRefExpr, ok := expr.(*variableRefExpr); ok && varRefExpr.ref.name() == loopVarName {
  1501. return &identifierExpr{loopVarName}
  1502. }
  1503. return nil
  1504. })
  1505. if list.typ() != starlarkTypeList {
  1506. list = &callExpr{
  1507. name: baseName + ".words",
  1508. returnType: starlarkTypeList,
  1509. args: []starlarkExpr{list},
  1510. }
  1511. }
  1512. var result starlarkExpr = &foreachExpr{
  1513. varName: loopVarName,
  1514. list: list,
  1515. action: action,
  1516. }
  1517. if action.typ() == starlarkTypeList {
  1518. result = &callExpr{
  1519. name: baseName + ".flatten_2d_list",
  1520. args: []starlarkExpr{result},
  1521. returnType: starlarkTypeList,
  1522. }
  1523. }
  1524. return result
  1525. }
  1526. func transformNode(node starlarkNode, transformer func(expr starlarkExpr) starlarkExpr) {
  1527. switch a := node.(type) {
  1528. case *ifNode:
  1529. a.expr = a.expr.transform(transformer)
  1530. case *switchCase:
  1531. transformNode(a.gate, transformer)
  1532. for _, n := range a.nodes {
  1533. transformNode(n, transformer)
  1534. }
  1535. case *switchNode:
  1536. for _, n := range a.ssCases {
  1537. transformNode(n, transformer)
  1538. }
  1539. case *exprNode:
  1540. a.expr = a.expr.transform(transformer)
  1541. case *assignmentNode:
  1542. a.value = a.value.transform(transformer)
  1543. case *foreachNode:
  1544. a.list = a.list.transform(transformer)
  1545. for _, n := range a.actions {
  1546. transformNode(n, transformer)
  1547. }
  1548. case *inheritNode:
  1549. if b, ok := a.module.(inheritedDynamicModule); ok {
  1550. b.path = b.path.transform(transformer)
  1551. a.module = b
  1552. }
  1553. case *includeNode:
  1554. if b, ok := a.module.(inheritedDynamicModule); ok {
  1555. b.path = b.path.transform(transformer)
  1556. a.module = b
  1557. }
  1558. }
  1559. }
  1560. type foreachCallNodeParser struct{}
  1561. func (p *foreachCallNodeParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) []starlarkNode {
  1562. words := args.Split(",")
  1563. if len(words) != 3 {
  1564. return []starlarkNode{ctx.newBadNode(node, "foreach function should have 3 arguments, found "+strconv.Itoa(len(words)))}
  1565. }
  1566. if !words[0].Const() || words[0].Empty() || !identifierFullMatchRegex.MatchString(words[0].Strings[0]) {
  1567. return []starlarkNode{ctx.newBadNode(node, "first argument to foreach function must be a simple string identifier")}
  1568. }
  1569. loopVarName := words[0].Strings[0]
  1570. list := ctx.parseMakeString(node, words[1])
  1571. if list.typ() != starlarkTypeList {
  1572. list = &callExpr{
  1573. name: baseName + ".words",
  1574. returnType: starlarkTypeList,
  1575. args: []starlarkExpr{list},
  1576. }
  1577. }
  1578. actions := ctx.parseNodeMakeString(node, words[2])
  1579. // TODO(colefaust): Replace transforming code with something more elegant
  1580. for _, action := range actions {
  1581. transformNode(action, func(expr starlarkExpr) starlarkExpr {
  1582. if varRefExpr, ok := expr.(*variableRefExpr); ok && varRefExpr.ref.name() == loopVarName {
  1583. return &identifierExpr{loopVarName}
  1584. }
  1585. return nil
  1586. })
  1587. }
  1588. return []starlarkNode{&foreachNode{
  1589. varName: loopVarName,
  1590. list: list,
  1591. actions: actions,
  1592. }}
  1593. }
  1594. type wordCallParser struct{}
  1595. func (p *wordCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
  1596. words := args.Split(",")
  1597. if len(words) != 2 {
  1598. return ctx.newBadExpr(node, "word function should have 2 arguments")
  1599. }
  1600. var index = 0
  1601. if words[0].Const() {
  1602. if i, err := strconv.Atoi(strings.TrimSpace(words[0].Strings[0])); err == nil {
  1603. index = i
  1604. }
  1605. }
  1606. if index < 1 {
  1607. return ctx.newBadExpr(node, "word index should be constant positive integer")
  1608. }
  1609. words[1].TrimLeftSpaces()
  1610. words[1].TrimRightSpaces()
  1611. array := ctx.parseMakeString(node, words[1])
  1612. if bad, ok := array.(*badExpr); ok {
  1613. return bad
  1614. }
  1615. if array.typ() != starlarkTypeList {
  1616. array = &callExpr{
  1617. name: baseName + ".words",
  1618. args: []starlarkExpr{array},
  1619. returnType: starlarkTypeList,
  1620. }
  1621. }
  1622. return &indexExpr{array, &intLiteralExpr{index - 1}}
  1623. }
  1624. type wordsCallParser struct{}
  1625. func (p *wordsCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
  1626. args.TrimLeftSpaces()
  1627. args.TrimRightSpaces()
  1628. array := ctx.parseMakeString(node, args)
  1629. if bad, ok := array.(*badExpr); ok {
  1630. return bad
  1631. }
  1632. if array.typ() != starlarkTypeList {
  1633. array = &callExpr{
  1634. name: baseName + ".words",
  1635. args: []starlarkExpr{array},
  1636. returnType: starlarkTypeList,
  1637. }
  1638. }
  1639. return &callExpr{
  1640. name: "len",
  1641. args: []starlarkExpr{array},
  1642. returnType: starlarkTypeInt,
  1643. }
  1644. }
  1645. func parseIntegerArguments(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString, expectedArgs int) ([]starlarkExpr, error) {
  1646. parsedArgs := make([]starlarkExpr, 0)
  1647. for _, arg := range args.Split(",") {
  1648. expr := ctx.parseMakeString(node, arg)
  1649. if expr.typ() == starlarkTypeList {
  1650. return nil, fmt.Errorf("argument to math argument has type list, which cannot be converted to int")
  1651. }
  1652. if s, ok := maybeString(expr); ok {
  1653. intVal, err := strconv.Atoi(strings.TrimSpace(s))
  1654. if err != nil {
  1655. return nil, err
  1656. }
  1657. expr = &intLiteralExpr{literal: intVal}
  1658. } else if expr.typ() != starlarkTypeInt {
  1659. expr = &callExpr{
  1660. name: "int",
  1661. args: []starlarkExpr{expr},
  1662. returnType: starlarkTypeInt,
  1663. }
  1664. }
  1665. parsedArgs = append(parsedArgs, expr)
  1666. }
  1667. if len(parsedArgs) != expectedArgs {
  1668. return nil, fmt.Errorf("function should have %d arguments", expectedArgs)
  1669. }
  1670. return parsedArgs, nil
  1671. }
  1672. type mathComparisonCallParser struct {
  1673. op string
  1674. }
  1675. func (p *mathComparisonCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
  1676. parsedArgs, err := parseIntegerArguments(ctx, node, args, 2)
  1677. if err != nil {
  1678. return ctx.newBadExpr(node, err.Error())
  1679. }
  1680. return &binaryOpExpr{
  1681. left: parsedArgs[0],
  1682. right: parsedArgs[1],
  1683. op: p.op,
  1684. returnType: starlarkTypeBool,
  1685. }
  1686. }
  1687. type mathMaxOrMinCallParser struct {
  1688. function string
  1689. }
  1690. func (p *mathMaxOrMinCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
  1691. parsedArgs, err := parseIntegerArguments(ctx, node, args, 2)
  1692. if err != nil {
  1693. return ctx.newBadExpr(node, err.Error())
  1694. }
  1695. return &callExpr{
  1696. object: nil,
  1697. name: p.function,
  1698. args: parsedArgs,
  1699. returnType: starlarkTypeInt,
  1700. }
  1701. }
  1702. type evalNodeParser struct{}
  1703. func (p *evalNodeParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) []starlarkNode {
  1704. parser := mkparser.NewParser("Eval expression", strings.NewReader(args.Dump()))
  1705. nodes, errs := parser.Parse()
  1706. if errs != nil {
  1707. return []starlarkNode{ctx.newBadNode(node, "Unable to parse eval statement")}
  1708. }
  1709. if len(nodes) == 0 {
  1710. return []starlarkNode{}
  1711. } else if len(nodes) == 1 {
  1712. switch n := nodes[0].(type) {
  1713. case *mkparser.Assignment:
  1714. if n.Name.Const() {
  1715. return ctx.handleAssignment(n)
  1716. }
  1717. case *mkparser.Comment:
  1718. return []starlarkNode{&commentNode{strings.TrimSpace("#" + n.Comment)}}
  1719. case *mkparser.Directive:
  1720. if n.Name == "include" || n.Name == "-include" {
  1721. return ctx.handleInclude(n)
  1722. }
  1723. case *mkparser.Variable:
  1724. // Technically inherit-product(-if-exists) don't need to be put inside
  1725. // an eval, but some makefiles do it, presumably because they copy+pasted
  1726. // from a $(eval include ...)
  1727. if name, _, ok := ctx.maybeParseFunctionCall(n, n.Name); ok {
  1728. if name == "inherit-product" || name == "inherit-product-if-exists" {
  1729. return ctx.handleVariable(n)
  1730. }
  1731. }
  1732. }
  1733. }
  1734. return []starlarkNode{ctx.newBadNode(node, "Eval expression too complex; only assignments, comments, includes, and inherit-products are supported")}
  1735. }
  1736. func (ctx *parseContext) parseMakeString(node mkparser.Node, mk *mkparser.MakeString) starlarkExpr {
  1737. if mk.Const() {
  1738. return &stringLiteralExpr{mk.Dump()}
  1739. }
  1740. if mkRef, ok := mk.SingleVariable(); ok {
  1741. return ctx.parseReference(node, mkRef)
  1742. }
  1743. // If we reached here, it's neither string literal nor a simple variable,
  1744. // we need a full-blown interpolation node that will generate
  1745. // "a%b%c" % (X, Y) for a$(X)b$(Y)c
  1746. parts := make([]starlarkExpr, len(mk.Variables)+len(mk.Strings))
  1747. for i := 0; i < len(parts); i++ {
  1748. if i%2 == 0 {
  1749. parts[i] = &stringLiteralExpr{literal: mk.Strings[i/2]}
  1750. } else {
  1751. parts[i] = ctx.parseReference(node, mk.Variables[i/2].Name)
  1752. if x, ok := parts[i].(*badExpr); ok {
  1753. return x
  1754. }
  1755. }
  1756. }
  1757. return NewInterpolateExpr(parts)
  1758. }
  1759. func (ctx *parseContext) parseNodeMakeString(node mkparser.Node, mk *mkparser.MakeString) []starlarkNode {
  1760. // Discard any constant values in the make string, as they would be top level
  1761. // string literals and do nothing.
  1762. result := make([]starlarkNode, 0, len(mk.Variables))
  1763. for i := range mk.Variables {
  1764. result = append(result, ctx.handleVariable(&mk.Variables[i])...)
  1765. }
  1766. return result
  1767. }
  1768. // Handles the statements whose treatment is the same in all contexts: comment,
  1769. // assignment, variable (which is a macro call in reality) and all constructs that
  1770. // do not handle in any context ('define directive and any unrecognized stuff).
  1771. func (ctx *parseContext) handleSimpleStatement(node mkparser.Node) []starlarkNode {
  1772. var result []starlarkNode
  1773. switch x := node.(type) {
  1774. case *mkparser.Comment:
  1775. if n, handled := ctx.maybeHandleAnnotation(x); handled && n != nil {
  1776. result = []starlarkNode{n}
  1777. } else if !handled {
  1778. result = []starlarkNode{&commentNode{strings.TrimSpace("#" + x.Comment)}}
  1779. }
  1780. case *mkparser.Assignment:
  1781. result = ctx.handleAssignment(x)
  1782. case *mkparser.Variable:
  1783. result = ctx.handleVariable(x)
  1784. case *mkparser.Directive:
  1785. switch x.Name {
  1786. case "define":
  1787. if res := ctx.maybeHandleDefine(x); res != nil {
  1788. result = []starlarkNode{res}
  1789. }
  1790. case "include", "-include":
  1791. result = ctx.handleInclude(x)
  1792. case "ifeq", "ifneq", "ifdef", "ifndef":
  1793. result = []starlarkNode{ctx.handleIfBlock(x)}
  1794. default:
  1795. result = []starlarkNode{ctx.newBadNode(x, "unexpected directive %s", x.Name)}
  1796. }
  1797. default:
  1798. result = []starlarkNode{ctx.newBadNode(x, "unsupported line %s", strings.ReplaceAll(x.Dump(), "\n", "\n#"))}
  1799. }
  1800. // Clear the includeTops after each non-comment statement
  1801. // so that include annotations placed on certain statements don't apply
  1802. // globally for the rest of the makefile was well.
  1803. if _, wasComment := node.(*mkparser.Comment); !wasComment {
  1804. ctx.atTopOfMakefile = false
  1805. ctx.includeTops = []string{}
  1806. }
  1807. if result == nil {
  1808. result = []starlarkNode{}
  1809. }
  1810. return result
  1811. }
  1812. // The types allowed in a type_hint
  1813. var typeHintMap = map[string]starlarkType{
  1814. "string": starlarkTypeString,
  1815. "list": starlarkTypeList,
  1816. }
  1817. // Processes annotation. An annotation is a comment that starts with #RBC# and provides
  1818. // a conversion hint -- say, where to look for the dynamically calculated inherit/include
  1819. // paths. Returns true if the comment was a successfully-handled annotation.
  1820. func (ctx *parseContext) maybeHandleAnnotation(cnode *mkparser.Comment) (starlarkNode, bool) {
  1821. maybeTrim := func(s, prefix string) (string, bool) {
  1822. if strings.HasPrefix(s, prefix) {
  1823. return strings.TrimSpace(strings.TrimPrefix(s, prefix)), true
  1824. }
  1825. return s, false
  1826. }
  1827. annotation, ok := maybeTrim(cnode.Comment, annotationCommentPrefix)
  1828. if !ok {
  1829. return nil, false
  1830. }
  1831. if p, ok := maybeTrim(annotation, "include_top"); ok {
  1832. // Don't allow duplicate include tops, because then we will generate
  1833. // invalid starlark code. (duplicate keys in the _entry dictionary)
  1834. for _, top := range ctx.includeTops {
  1835. if top == p {
  1836. return nil, true
  1837. }
  1838. }
  1839. ctx.includeTops = append(ctx.includeTops, p)
  1840. return nil, true
  1841. } else if p, ok := maybeTrim(annotation, "type_hint"); ok {
  1842. // Type hints must come at the beginning the file, to avoid confusion
  1843. // if a type hint was specified later and thus only takes effect for half
  1844. // of the file.
  1845. if !ctx.atTopOfMakefile {
  1846. return ctx.newBadNode(cnode, "type_hint annotations must come before the first Makefile statement"), true
  1847. }
  1848. parts := strings.Fields(p)
  1849. if len(parts) <= 1 {
  1850. return ctx.newBadNode(cnode, "Invalid type_hint annotation: %s. Must be a variable type followed by a list of variables of that type", p), true
  1851. }
  1852. var varType starlarkType
  1853. if varType, ok = typeHintMap[parts[0]]; !ok {
  1854. varType = starlarkTypeUnknown
  1855. }
  1856. if varType == starlarkTypeUnknown {
  1857. return ctx.newBadNode(cnode, "Invalid type_hint annotation. Only list/string types are accepted, found %s", parts[0]), true
  1858. }
  1859. for _, name := range parts[1:] {
  1860. // Don't allow duplicate type hints
  1861. if _, ok := ctx.typeHints[name]; ok {
  1862. return ctx.newBadNode(cnode, "Duplicate type hint for variable %s", name), true
  1863. }
  1864. ctx.typeHints[name] = varType
  1865. }
  1866. return nil, true
  1867. }
  1868. return ctx.newBadNode(cnode, "unsupported annotation %s", cnode.Comment), true
  1869. }
  1870. func (ctx *parseContext) loadedModulePath(path string) string {
  1871. // During the transition to Roboleaf some of the product configuration files
  1872. // will be converted and checked in while the others will be generated on the fly
  1873. // and run. The runner (rbcrun application) accommodates this by allowing three
  1874. // different ways to specify the loaded file location:
  1875. // 1) load(":<file>",...) loads <file> from the same directory
  1876. // 2) load("//path/relative/to/source/root:<file>", ...) loads <file> source tree
  1877. // 3) load("/absolute/path/to/<file> absolute path
  1878. // If the file being generated and the file it wants to load are in the same directory,
  1879. // generate option 1.
  1880. // Otherwise, if output directory is not specified, generate 2)
  1881. // Finally, if output directory has been specified and the file being generated and
  1882. // the file it wants to load from are in the different directories, generate 2) or 3):
  1883. // * if the file being loaded exists in the source tree, generate 2)
  1884. // * otherwise, generate 3)
  1885. // Finally, figure out the loaded module path and name and create a node for it
  1886. loadedModuleDir := filepath.Dir(path)
  1887. base := filepath.Base(path)
  1888. loadedModuleName := strings.TrimSuffix(base, filepath.Ext(base)) + ctx.outputSuffix
  1889. if loadedModuleDir == filepath.Dir(ctx.script.mkFile) {
  1890. return ":" + loadedModuleName
  1891. }
  1892. if ctx.outputDir == "" {
  1893. return fmt.Sprintf("//%s:%s", loadedModuleDir, loadedModuleName)
  1894. }
  1895. if _, err := os.Stat(filepath.Join(loadedModuleDir, loadedModuleName)); err == nil {
  1896. return fmt.Sprintf("//%s:%s", loadedModuleDir, loadedModuleName)
  1897. }
  1898. return filepath.Join(ctx.outputDir, loadedModuleDir, loadedModuleName)
  1899. }
  1900. func (ctx *parseContext) addSoongNamespace(ns string) {
  1901. if _, ok := ctx.soongNamespaces[ns]; ok {
  1902. return
  1903. }
  1904. ctx.soongNamespaces[ns] = make(map[string]bool)
  1905. }
  1906. func (ctx *parseContext) hasSoongNamespace(name string) bool {
  1907. _, ok := ctx.soongNamespaces[name]
  1908. return ok
  1909. }
  1910. func (ctx *parseContext) updateSoongNamespace(replace bool, namespaceName string, varNames []string) {
  1911. ctx.addSoongNamespace(namespaceName)
  1912. vars := ctx.soongNamespaces[namespaceName]
  1913. if replace {
  1914. vars = make(map[string]bool)
  1915. ctx.soongNamespaces[namespaceName] = vars
  1916. }
  1917. for _, v := range varNames {
  1918. vars[v] = true
  1919. }
  1920. }
  1921. func (ctx *parseContext) hasNamespaceVar(namespaceName string, varName string) bool {
  1922. vars, ok := ctx.soongNamespaces[namespaceName]
  1923. if ok {
  1924. _, ok = vars[varName]
  1925. }
  1926. return ok
  1927. }
  1928. func (ctx *parseContext) errorLocation(node mkparser.Node) ErrorLocation {
  1929. return ErrorLocation{ctx.script.mkFile, ctx.script.nodeLocator(node.Pos())}
  1930. }
  1931. func (ss *StarlarkScript) String() string {
  1932. return NewGenerateContext(ss).emit()
  1933. }
  1934. func (ss *StarlarkScript) SubConfigFiles() []string {
  1935. var subs []string
  1936. for _, src := range ss.inherited {
  1937. subs = append(subs, src.originalPath)
  1938. }
  1939. return subs
  1940. }
  1941. func (ss *StarlarkScript) HasErrors() bool {
  1942. return ss.hasErrors
  1943. }
  1944. // Convert reads and parses a makefile. If successful, parsed tree
  1945. // is returned and then can be passed to String() to get the generated
  1946. // Starlark file.
  1947. func Convert(req Request) (*StarlarkScript, error) {
  1948. reader := req.Reader
  1949. if reader == nil {
  1950. mkContents, err := ioutil.ReadFile(req.MkFile)
  1951. if err != nil {
  1952. return nil, err
  1953. }
  1954. reader = bytes.NewBuffer(mkContents)
  1955. }
  1956. parser := mkparser.NewParser(req.MkFile, reader)
  1957. nodes, errs := parser.Parse()
  1958. if len(errs) > 0 {
  1959. for _, e := range errs {
  1960. fmt.Fprintln(os.Stderr, "ERROR:", e)
  1961. }
  1962. return nil, fmt.Errorf("bad makefile %s", req.MkFile)
  1963. }
  1964. starScript := &StarlarkScript{
  1965. moduleName: moduleNameForFile(req.MkFile),
  1966. mkFile: req.MkFile,
  1967. traceCalls: req.TraceCalls,
  1968. sourceFS: req.SourceFS,
  1969. makefileFinder: req.MakefileFinder,
  1970. nodeLocator: func(pos mkparser.Pos) int { return parser.Unpack(pos).Line },
  1971. nodes: make([]starlarkNode, 0),
  1972. }
  1973. ctx := newParseContext(starScript, nodes)
  1974. ctx.outputSuffix = req.OutputSuffix
  1975. ctx.outputDir = req.OutputDir
  1976. ctx.errorLogger = req.ErrorLogger
  1977. if len(req.TracedVariables) > 0 {
  1978. ctx.tracedVariables = make(map[string]bool)
  1979. for _, v := range req.TracedVariables {
  1980. ctx.tracedVariables[v] = true
  1981. }
  1982. }
  1983. for ctx.hasNodes() && ctx.fatalError == nil {
  1984. starScript.nodes = append(starScript.nodes, ctx.handleSimpleStatement(ctx.getNode())...)
  1985. }
  1986. if ctx.fatalError != nil {
  1987. return nil, ctx.fatalError
  1988. }
  1989. return starScript, nil
  1990. }
  1991. func Launcher(mainModuleUri, inputVariablesUri, mainModuleName string) string {
  1992. var buf bytes.Buffer
  1993. fmt.Fprintf(&buf, "load(%q, %q)\n", baseUri, baseName)
  1994. fmt.Fprintf(&buf, "load(%q, input_variables_init = \"init\")\n", inputVariablesUri)
  1995. fmt.Fprintf(&buf, "load(%q, \"init\")\n", mainModuleUri)
  1996. fmt.Fprintf(&buf, "%s(%s(%q, init, input_variables_init))\n", cfnPrintVars, cfnMain, mainModuleName)
  1997. return buf.String()
  1998. }
  1999. func BoardLauncher(mainModuleUri string, inputVariablesUri string) string {
  2000. var buf bytes.Buffer
  2001. fmt.Fprintf(&buf, "load(%q, %q)\n", baseUri, baseName)
  2002. fmt.Fprintf(&buf, "load(%q, \"init\")\n", mainModuleUri)
  2003. fmt.Fprintf(&buf, "load(%q, input_variables_init = \"init\")\n", inputVariablesUri)
  2004. fmt.Fprintf(&buf, "%s(%s(init, input_variables_init))\n", cfnPrintVars, cfnBoardMain)
  2005. return buf.String()
  2006. }
  2007. func MakePath2ModuleName(mkPath string) string {
  2008. return strings.TrimSuffix(mkPath, filepath.Ext(mkPath))
  2009. }