bmk_ng.bmx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912
  1. SuperStrict
  2. Import BRL.Reflection
  3. Import BRL.Map
  4. Import BRL.LinkedList
  5. ?threaded
  6. Import BRL.Threads
  7. ?
  8. ?Not win32
  9. Import "waitpid.c"
  10. ?
  11. Import "bmk_config.bmx"
  12. Import "bmk_ng_utils.bmx"
  13. Global commands:TMap = New TMap
  14. Global processor:TBMK = New TBMK
  15. Global globals:TBMKGlobals = New TBMKGlobals
  16. ' load in the base stuff
  17. LoadBMK(AppDir + "/core.bmk")
  18. LoadBMK(AppDir + "/make.bmk")
  19. ' optional
  20. LoadBMK(AppDir + "/config.bmk")
  21. ' add some defaults
  22. If processor.Platform() = "macos"
  23. globals.SetVar("macos_version", String(macos_version))
  24. End If
  25. globals.SetVar("cc_opts", New TOptionVariable)
  26. globals.SetVar("ld_opts", New TOptionVariable)
  27. Function LoadBMK(path:String)
  28. processor.LoadBMK(path)
  29. End Function
  30. ' this is the core bmk processor.
  31. Type TBMK
  32. Method New()
  33. LuaRegisterObject Self,"bmk"
  34. End Method
  35. ' loads a .bmk, stores any functions, and runs any commands.
  36. Method LoadBMK(path:String)
  37. Local str:String
  38. Try
  39. If FileType(path) = 1 Then
  40. str = LoadText( path )
  41. Else
  42. Return
  43. End If
  44. Catch e:Object
  45. Try
  46. If FileType(AppDir + "/" + path) = 1 Then
  47. str = LoadText( AppDir + "/" + path )
  48. Else
  49. Return
  50. End If
  51. Catch e:Object
  52. ' we tried... twice
  53. ' fail silently...
  54. Return
  55. End Try
  56. End Try
  57. Local pos:Int, inDefine:Int, text:String, name:String
  58. While pos < str.length
  59. Local eol:Int = str.Find( "~n",pos )
  60. If eol = -1 Then
  61. eol = str.length
  62. End If
  63. Local line:String = str[pos..eol].Trim()
  64. pos = eol+1
  65. ProcessLine(line, inDefine, text, name)
  66. ' anything else?
  67. Wend
  68. End Method
  69. ' processes a pragma
  70. Method ProcessPragma(line:String)
  71. Local inDefine:Int, text:String, name:String
  72. ProcessLine(line, inDefine, text, name)
  73. End Method
  74. Method ProcessLine(line:String, inDefine:Int Var, text:String Var, name:String Var)
  75. If line.StartsWith("#") Then
  76. Return
  77. End If
  78. Local lline:String = line.ToLower()
  79. If line.StartsWith("@") Then
  80. If lline[1..].StartsWith("define") Then
  81. inDefine = True
  82. name = line[8..].Trim()
  83. Local cmd:TBMKCommand = New TBMKCommand
  84. cmd.name = name
  85. commands.Insert(name.ToLower(), cmd)
  86. Return
  87. End If
  88. If lline[1..].StartsWith("end") Then
  89. If inDefine Then
  90. Local cmd:TBMKCommand = TBMKCommand(commands.ValueForKey(name.ToLower()))
  91. cmd.LoadCommand(text)
  92. text = ""
  93. inDefine = False
  94. End If
  95. Return
  96. End If
  97. End If
  98. If inDefine Then
  99. text:+ line + "~n"
  100. Return
  101. End If
  102. If line.length = 0 Then
  103. Return
  104. End If
  105. ' find command, and run
  106. Local i:Int=1
  107. While i < lline.length And (CharIsAlpha(lline[i]) Or CharIsDigit(lline[i]))
  108. i:+1
  109. Wend
  110. 'If i = lline.length Then
  111. ' Continue
  112. 'End If
  113. Local command:String = lline[..i]
  114. Local cmd:TBMKCommand = TBMKCommand(commands.ValueForKey(command))
  115. ' this is a command!
  116. If cmd Then
  117. cmd.RunCommand(line[i+1..])
  118. Return
  119. End If
  120. ' what's left?
  121. ' setting a variable?
  122. i = line.Find("=")
  123. If i <> -1 Then
  124. ' hmm. maybe a variable...
  125. Local variable:String = line[..i].Trim()
  126. Local value:String = Parse(line[i+1..].Trim())
  127. globals.SetVar(variable, value)
  128. End If
  129. End Method
  130. Method Parse:String(str:String)
  131. Local done:Int
  132. While Not done
  133. Local pos:Int, restart:Int, changed:Int
  134. While pos < str.length And Not restart
  135. Local eol:Int = str.Find( "~n",pos )
  136. If eol = -1 Then
  137. eol = str.length
  138. End If
  139. Local line:String = str[pos..eol].Trim()
  140. pos = eol+1
  141. Local i:Int
  142. While i < line.length
  143. i = line.find("%", i)
  144. If i = -1 Then
  145. i = line.length
  146. Continue
  147. End If
  148. Local start:Int = i
  149. i:+ 1
  150. While i < line.length And (CharIsAlpha(line[i]) Or CharIsDigit(line[i]))
  151. i:+1
  152. Wend
  153. If i > start Then
  154. If line[i..i+1] = "%" Then
  155. i:+ 1
  156. Local toReplace:String = line[start..i]
  157. ' we want to replace this with something, so we
  158. ' will look in the globals list and env for a match.
  159. ' Otherwise, it will swap % with $, and leave it as is.
  160. Local with:String = FindValue(toReplace)
  161. If with Then
  162. str = str.Replace(toReplace, with)
  163. restart = True
  164. End If
  165. End If
  166. End If
  167. Wend
  168. Wend
  169. If Not restart Then
  170. done = True
  171. End If
  172. Wend
  173. Return str
  174. End Method
  175. Method FindValue:String(variable:String)
  176. Local plainVar:String = variable.Replace("%", "")
  177. Local value:String = globals.Get(plainVar)
  178. If value Then
  179. Return value
  180. End If
  181. ' look for environment variable ?
  182. Local env:String = getenv_(plainVar)
  183. If env Then
  184. Return env
  185. End If
  186. ' return the original
  187. Return variable.Replace("%", "$")
  188. End Method
  189. ' quotes a string, if required (does it have spaces in it?)
  190. Method Quote:String(t:String)
  191. Return CQuote(t)
  192. End Method
  193. ' returns the platform as a string
  194. Method Platform:String()
  195. If Not opt_target_platform Then
  196. ' the native target platform
  197. ?macos
  198. Return "macos"
  199. ?linux
  200. Return "linux"
  201. ?win32
  202. Return "win32"
  203. ?
  204. Else
  205. ' the custom target platform
  206. Return opt_target_platform
  207. End If
  208. End Method
  209. Field cputypes:String[] = ["ppc", "x86"]
  210. ?ppc
  211. Field cputype:Int = 0
  212. ?x86
  213. Field cputype:Int = 1
  214. ?
  215. ' returns the cpu type, as a string
  216. Method CPU:String()
  217. Return cputypes[cputype]
  218. End Method
  219. Method ToggleCPU()
  220. If opt_universal Then
  221. cputype = 1 - cputype
  222. End If
  223. End Method
  224. Method Sys:Int(cmd:String)
  225. If Int(globals.Get("verbose")) Or opt_verbose
  226. Print cmd
  227. Else If Int(globals.Get("dumpbuild"))
  228. Local p$=cmd
  229. p = p.Replace( BlitzMaxPath()+"/","./" )
  230. WriteStdout p+"~n"
  231. Local t$="mkdir "
  232. If cmd.StartsWith( t ) And FileType( cmd[t.length..] ) Return False
  233. EndIf
  234. Return system_( cmd )
  235. End Method
  236. Method MultiSys:Int(cmd:String, src:String)
  237. If Int(globals.Get("verbose")) Or opt_verbose
  238. Print cmd
  239. Else If Int(globals.Get("dumpbuild"))
  240. Local p$=cmd
  241. p = p.Replace( BlitzMaxPath()+"/","./" )
  242. WriteStdout p+"~n"
  243. Local t$="mkdir "
  244. If cmd.StartsWith( t ) And FileType( cmd[t.length..] ) Return False
  245. EndIf
  246. ?threaded
  247. processManager.DoSystem(cmd, src)
  248. ?Not threaded
  249. Return system_( cmd )
  250. ?
  251. End Method
  252. Method ThrowNew(e:String)
  253. Throw e
  254. End Method
  255. Method Call(name:String, args:String[])
  256. RunCommand(name, args)
  257. End Method
  258. Method AddArg(option:String, extra:String)
  259. Local args:String[] = [option]
  260. If extra Then
  261. args:+ [extra]
  262. End If
  263. ParseConfigArgs args
  264. End Method
  265. Method Option:String(key:String, defaultValue:String)
  266. Local value:String = globals.Get(key)
  267. If Not value Then
  268. Return defaultValue
  269. Else
  270. Return value
  271. End If
  272. End Method
  273. End Type
  274. ' stores variables, as well as a variable stack which can be pushed and popped.
  275. Type TBMKGlobals
  276. ' current value of variables
  277. Field vars:TMap = New TMap
  278. ' variable stack
  279. Field stack:TMap = New TMap
  280. Method New()
  281. LuaRegisterObject Self,"globals"
  282. End Method
  283. ' sets the variable with value
  284. Method SetVar(variable:String, value:Object)
  285. vars.Insert(variable.ToUpper(), value)
  286. End Method
  287. ' returns the current value for variable
  288. Method Get:String(variable:String)
  289. Local obj:Object = vars.ValueForKey(variable.ToUpper())
  290. If obj Then
  291. Return obj.ToString()
  292. End If
  293. End Method
  294. Method GetRawVar:Object(variable:String)
  295. Local obj:Object = vars.ValueForKey(variable.ToUpper())
  296. If TOptionVariable(obj) Then
  297. ' return a copy of the object - any changes to this won't affect the current value.
  298. Return TOptionVariable(obj).Clone()
  299. End If
  300. Return obj
  301. End Method
  302. ' push the variable onto the stack (save the value)
  303. Method Push(variable:String)
  304. variable = variable.ToUpper()
  305. Local list:TList = TList(stack.ValueForKey(variable))
  306. If Not list Then
  307. list = New TList
  308. stack.Insert(variable, list)
  309. End If
  310. list.AddLast(GetRawVar(variable))
  311. End Method
  312. ' pop the variable from the stack (load the value)
  313. Method Pop(variable:String)
  314. variable = variable.ToUpper()
  315. Local list:TList = TList(stack.ValueForKey(variable))
  316. If list And Not list.IsEmpty() Then
  317. SetVar(variable, list.RemoveLast())
  318. End If
  319. End Method
  320. ' push all the variables
  321. Method PushAll()
  322. For Local v:String = EachIn vars.Keys()
  323. Push(v)
  324. Next
  325. End Method
  326. ' pop all the variables
  327. Method PopAll()
  328. For Local v:String = EachIn vars.Keys()
  329. Pop(v)
  330. Next
  331. End Method
  332. ' adds value to the end of variable
  333. Method Add(variable:String, value:String)
  334. variable = variable.ToUpper()
  335. Local v:Object = vars.ValueForKey(variable)
  336. If Not TOptionVariable(v) Then
  337. SetVar(variable, String(v) + " " + value)
  338. End If
  339. End Method
  340. Method AddOption(variable:String, key:String, value:String)
  341. variable = variable.ToUpper()
  342. Local v:Object = vars.ValueForKey(variable)
  343. If TOptionVariable(v) Then
  344. TOptionVariable(v).AddVar(key, value)
  345. Else
  346. Local opt:TOptionVariable = New TOptionVariable
  347. opt.addVar(key, value)
  348. setVar(variable, opt)
  349. End If
  350. End Method
  351. Method SetOption(variable:String, key:String, value:String)
  352. variable = variable.ToUpper()
  353. Local v:Object = vars.ValueForKey(variable)
  354. If TOptionVariable(v) Then
  355. TOptionVariable(v).SetVar(key, value)
  356. Else
  357. Local opt:TOptionVariable = New TOptionVariable
  358. opt.SetVar(key, value)
  359. setVar(variable, opt)
  360. End If
  361. End Method
  362. ' only appropriate for TOptionVariables
  363. Method RemoveVar(variable:String, name:String)
  364. variable = variable.ToUpper()
  365. Local v:Object = vars.ValueForKey(variable)
  366. If TOptionVariable(v) Then
  367. TOptionVariable(v).RemoveVar(name)
  368. End If
  369. End Method
  370. Method Clear(variable:String)
  371. variable = variable.ToUpper()
  372. Local v:Object = vars.ValueForKey(variable)
  373. If TOptionVariable(v) Then
  374. vars.remove(variable)
  375. End If
  376. End Method
  377. Method Reset()
  378. stack.Clear()
  379. End Method
  380. End Type
  381. Type TOpt
  382. Field name:String
  383. Field value:String
  384. End Type
  385. ' holds a list of options.
  386. ' useful for storing a list of cc_opts, for example.
  387. ' the list can be modified as required, and cloned during push/pop calls.
  388. Type TOptionVariable
  389. Field options:TMap = New TMap
  390. Field orderedOptions:TList = New TList
  391. Method AddVar(name:String, value:String)', insertBefore:Int = False)
  392. Local opt:TOpt = New TOpt
  393. If Not name Then
  394. Global count:Int
  395. count:+1
  396. name = "VAR" + count
  397. opt.name = name
  398. Else
  399. opt.name = name
  400. End If
  401. opt.value = value
  402. options.Insert(name, opt)
  403. orderedOptions.AddLast(opt)
  404. End Method
  405. Method SetVar(name:String, value:String)', insertBefore:Int = False)
  406. Local opt:TOpt = New TOpt
  407. If Not name Then
  408. Global count:Int
  409. count:+1
  410. name = "VAR" + count
  411. opt.name = name
  412. Else
  413. opt.name = name
  414. End If
  415. opt.value = value
  416. ' option already exists?
  417. Local o:TOpt = TOpt(options.ValueForKey(name))
  418. If o Then
  419. orderedOptions.Remove(o)
  420. End If
  421. options.Insert(name, opt)
  422. orderedOptions.AddLast(opt)
  423. End Method
  424. ' finds and removes a matching value
  425. Method RemoveVar(name:String)
  426. Local opt:TOpt = TOpt(options.ValueForKey(name))
  427. options.Remove(opt)
  428. orderedOptions.Remove(opt)
  429. End Method
  430. Method ToString:String()
  431. Local s:String = " "
  432. For Local opt:TOpt = EachIn orderedOptions
  433. s:+ opt.value + " "
  434. Next
  435. Return s
  436. End Method
  437. ' create an exact copy of me
  438. Method Clone:TOptionVariable()
  439. Local me:TOptionVariable = New TOptionVariable
  440. For Local name:String = EachIn options.Keys()
  441. me.options.insert(name, options.ValueForKey(name))
  442. Next
  443. For Local opt:TOpt = EachIn orderedOptions
  444. me.orderedOptions.AddLast(opt)
  445. Next
  446. Return me
  447. End Method
  448. End Type
  449. Function RunCommand:Object(command:String, args:String[])
  450. Local cmd:TBMKCommand = TBMKCommand(commands.ValueForKey(command.ToLower()))
  451. If cmd Then
  452. ' we need to add the "arg0" string to the front of the array
  453. Local all:String
  454. For Local i:Int = 0 Until args.length
  455. Local arg:String = args[i]
  456. all:+ CQuote$(arg) + " "
  457. Next
  458. args = [ all.Trim() ] + args
  459. ' now we can run the command
  460. Return cmd.RunCommandArgs(args)
  461. End If
  462. End Function
  463. ' a bmk function/command
  464. Type TBMKCommand
  465. Field name:String
  466. Field command:String
  467. Field argCount:Int = 0
  468. Field class:TLuaClass
  469. Field instance:TLuaObject
  470. Method LoadCommand(cmd:String)
  471. cmd = WrapVariables(ParseArgs(cmd))
  472. Local code:String = "function bmk_" + name + "(...)~n" + ..
  473. GetArgs() + ..
  474. "nvl = function(a1,a2) if a1 == nil then return a2 else return a1 end end~n" + ..
  475. cmd + ..
  476. "end"
  477. class = New TLuaClass.SetSourceCode( code )
  478. instance = New TLuaObject.Init( class, Null )
  479. End Method
  480. Method RunCommand:Object(args:String)
  481. Return RunCommandArgs([args] + ExtractArgs(args))
  482. End Method
  483. ' This assumes we have arg0 + other args
  484. Method RunCommandArgs:Object(args:Object[])
  485. Return instance.invoke("bmk_" + name, args)
  486. End Method
  487. ' handles quotes and arrays [].
  488. ' [] inside quotes are ignored.
  489. Method ExtractArgs:Object[](args:String)
  490. Local argArray:Object[]
  491. Local arg:String, arr:String[]
  492. Local i:Int, inString:Int, inArray:Int
  493. While i < args.length
  494. Local c:String = args[i..i+1]
  495. i:+ 1
  496. If c = "~q" Then
  497. If inString Then
  498. If Not inArray Then
  499. argArray:+ [ arg ]
  500. Else
  501. arr:+ [ arg ]
  502. End If
  503. arg = ""
  504. inString = False
  505. Continue
  506. Else
  507. arg = ""
  508. inString = True
  509. Continue
  510. End If
  511. End If
  512. If c = " " And Not inString Then
  513. If arg Then
  514. If Not inArray Then
  515. argArray:+ [ arg ]
  516. Else
  517. arr:+ [ arg ]
  518. End If
  519. arg = ""
  520. End If
  521. Continue
  522. End If
  523. If c = "[" And Not inString Then
  524. If Not inArray Then
  525. inArray = True
  526. arr = Null
  527. arg = ""
  528. Continue
  529. End If
  530. End If
  531. If c = "]" And Not inString Then
  532. If inArray Then
  533. If arg Then
  534. arr:+ [ arg ]
  535. End If
  536. inArray = False
  537. argArray:+ [ arr ]
  538. arr = Null
  539. arg = ""
  540. Continue
  541. End If
  542. End If
  543. arg:+ c
  544. Wend
  545. If arg Then
  546. If arr Then
  547. arr:+ [arg]
  548. argArray:+ [arr]
  549. Else
  550. argArray:+ [arg]
  551. End If
  552. Else
  553. If arr Then
  554. argArray:+ [arr]
  555. End If
  556. End If
  557. Return argArray
  558. End Method
  559. Method ParseArgs:String(cmd:String)
  560. ' This needs to process the command text to work out what args are used.
  561. ' so, for example, arg0, arg1 and arg2.
  562. ' That way, we generate the correct functionality when we build the function code.
  563. Local pos:Int
  564. While pos < cmd.length
  565. Local eol:Int = cmd.Find( "~n",pos )
  566. If eol = -1 Then
  567. eol = cmd.length
  568. End If
  569. Local line:String = cmd[pos..eol].Trim()
  570. pos = eol+1
  571. Local i:Int
  572. While i < line.length
  573. i = line.find("arg", i)
  574. If i = -1 Then
  575. i = line.length
  576. Continue
  577. End If
  578. i:+ 3
  579. Local start:Int = i
  580. While i < line.length And CharIsDigit(line[i])
  581. i:+1
  582. Wend
  583. Local num:Int = line[start..i].ToInt()
  584. If num Then
  585. argCount = Max(argCount, num)
  586. End If
  587. Wend
  588. Wend
  589. Return cmd
  590. End Method
  591. Method GetArgs:String()
  592. Local args:String = "local arg0"
  593. Local rep:String = "arg0 = bmk.Parse(arg0)~n"
  594. If argCount > 0 Then
  595. For Local i:Int = 1 To argCount
  596. args:+ ",arg" + i
  597. rep :+ "arg" + i + " = bmk.Parse(arg" + i + ")~n"
  598. Next
  599. End If
  600. args :+ " = unpack(arg)~n"
  601. args :+ rep
  602. Return args
  603. End Method
  604. Method WrapVariables:String(str:String)
  605. Local done:Int
  606. While Not done
  607. Local pos:Int, restart:Int, changed:Int
  608. While pos < str.length And Not restart
  609. Local eol:Int = str.Find( "~n",pos )
  610. If eol = -1 Then
  611. eol = str.length
  612. End If
  613. Local line:String = str[pos..eol].Trim()
  614. pos = eol+1
  615. Local i:Int
  616. While i < line.length
  617. i = line.find("%", i)
  618. If i = -1 Then
  619. i = line.length
  620. Continue
  621. End If
  622. Local start:Int = i
  623. i:+ 1
  624. While i < line.length And (CharIsAlpha(line[i]) Or CharIsDigit(line[i]))
  625. i:+1
  626. Wend
  627. If i > start Then
  628. If line[i..i+1] = "%" Then
  629. i:+ 1
  630. Local toReplace:String = line[start..i]
  631. Local with:String = "globals.Get(~q" + toReplace.Replace("%", "") + "~q)"
  632. str = str.Replace(toReplace, with)
  633. restart = True
  634. End If
  635. End If
  636. Wend
  637. Wend
  638. If Not restart Then
  639. done = True
  640. End If
  641. Wend
  642. Return str
  643. End Method
  644. End Type
  645. ?threaded
  646. Type TProcessManager
  647. Field cpuCount:Int
  648. Field threads:TList = New TList
  649. Method New()
  650. cpuCount = GetCoreCount() + 1
  651. End Method
  652. Method CheckThreads()
  653. While threads.Count() = cpuCount
  654. For Local thread:TThread = EachIn threads
  655. If Not thread.Running() Then
  656. threads.Remove(thread)
  657. End If
  658. Next
  659. Delay 100
  660. Wend
  661. End Method
  662. Method WaitForThreads()
  663. While threads.Count()
  664. For Local thread:TThread = EachIn threads
  665. If Not thread.Running() Then
  666. threads.Remove(thread)
  667. End If
  668. Next
  669. Delay 100
  670. Wend
  671. End Method
  672. Method DoSystem(cmd:String, src:String)
  673. threads.AddLast(CreateThread(TProcessTask._DoTasks, New TProcessTask.Create(cmd, src)))
  674. CheckThreads()
  675. End Method
  676. End Type
  677. ?Not win32
  678. Extern
  679. Function fork:Int()
  680. Function bmx_waitpid:Int(pid:Int)
  681. Function bmx_system(cmd:Byte Ptr)
  682. End Extern
  683. ?
  684. Type TProcessTask
  685. Field command:String
  686. Field source:String
  687. Method Create:TProcessTask(cmd:String, src:String)
  688. command = cmd
  689. source = src
  690. Return Self
  691. End Method
  692. Function _DoTasks:Object(data:Object)
  693. Return TProcessTask(data).DoTasks()
  694. End Function
  695. Method DoTasks:Object()
  696. Local res:Int
  697. ?Not win32
  698. Local pid:Int = fork()
  699. If Not pid Then
  700. bmx_system(command)
  701. Else
  702. res = bmx_waitpid(pid)
  703. End If
  704. ?win32
  705. res = system_(command)
  706. ?
  707. If res Then
  708. Local s:String = "Build Error: failed to compile (" + res + ") " + source
  709. Print s + "~n"
  710. Throw s
  711. End If
  712. End Method
  713. End Type
  714. ?threaded
  715. Global processManager:TProcessManager = New TProcessManager
  716. ?