PageRenderTime 44ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/ide/src/com/moonshineproject/plugin/actionscript/mxmlc/MXMLCPlugin.as

http://moonshineproject.googlecode.com/
ActionScript | 488 lines | 400 code | 78 blank | 10 comment | 68 complexity | 2561f661ed663a35b17ee0668756a503 MD5 | raw file
Possible License(s): LGPL-3.0, MIT, Apache-2.0, GPL-3.0
  1. package com.moonshineproject.plugin.actionscript.mxmlc
  2. {
  3. import com.moonshineproject.IDEModel;
  4. import com.moonshineproject.plugin.IMenuPlugin;
  5. import com.moonshineproject.plugin.IPlugin;
  6. import com.moonshineproject.plugin.PluginBase;
  7. import com.moonshineproject.plugin.actionscript.as3project.vo.AS3ProjectVO;
  8. import com.moonshineproject.plugin.actionscript.swflauncher.event.SWFLaunchEvent;
  9. import com.moonshineproject.plugin.console.MarkupTextLineModel;
  10. import com.moonshineproject.plugin.core.compiler.CompilerEventBase;
  11. import com.moonshineproject.plugin.menu.vo.MenuItem;
  12. import com.moonshineproject.plugin.project.event.RefreshTreeEvent;
  13. import com.moonshineproject.plugin.project.vo.ProjectVO;
  14. import com.moonshineproject.plugin.settings.ISettingsProvider;
  15. import com.moonshineproject.plugin.settings.vo.BooleanSetting;
  16. import com.moonshineproject.plugin.settings.vo.ISetting;
  17. import com.moonshineproject.plugin.settings.vo.PathSetting;
  18. import com.moonshineproject.text.TextLineModel;
  19. import com.moonshineproject.utils.FileUtil;
  20. import com.moonshineproject.utils.HtmlFormatter;
  21. import com.moonshineproject.vo.Settings;
  22. import flash.desktop.NativeProcess;
  23. import flash.desktop.NativeProcessStartupInfo;
  24. import flash.events.Event;
  25. import flash.events.NativeProcessExitEvent;
  26. import flash.events.ProgressEvent;
  27. import flash.filesystem.File;
  28. import flash.ui.Keyboard;
  29. import flash.utils.Dictionary;
  30. import flash.utils.IDataInput;
  31. import flash.utils.IDataOutput;
  32. public class MXMLCPlugin extends PluginBase implements IPlugin, ISettingsProvider, IMenuPlugin
  33. {
  34. override public function get name():String { return "MXMLC Compiler Plugin"; }
  35. override public function get author():String { return "Miha Lunar & Moonshine Project Team"; }
  36. override public function get description():String { return "Compiles AS3 projects with MXMLC."; }
  37. public var defaultFlexSDK:String
  38. public var incrementalCompile:Boolean = true;
  39. protected var runAfterBuild:Boolean;
  40. private var fcshPath:String = "bin/fcsh";
  41. public function get flexSDK():File
  42. {
  43. return currentSDK;
  44. }
  45. private var fcsh:NativeProcess;
  46. private var exiting:Boolean = false;
  47. private var shellInfo:NativeProcessStartupInfo;
  48. private var lastTarget:File;
  49. private var targets:Dictionary;
  50. private var currentSDK:File = flexSDK;
  51. /** Project currently under compilation */
  52. private var currentProject:ProjectVO;
  53. private var queue:Vector.<String> = new Vector.<String>();
  54. private var errors:String = "";
  55. private var cmdLine:CommandLine;
  56. private var _instance:MXMLCPlugin;
  57. public function MXMLCPlugin()
  58. {
  59. if (Settings.os == "win") fcshPath += ".exe";
  60. }
  61. override public function activate():void
  62. {
  63. super.activate();
  64. dispatcher.addEventListener(CompilerEventBase.BUILD_AND_RUN, buildAndRun);
  65. dispatcher.addEventListener(CompilerEventBase.BUILD_AND_DEBUG, buildAndRun);
  66. dispatcher.addEventListener(CompilerEventBase.BUILD, build);
  67. dispatcher.addEventListener(CompilerEventBase.BUILD_RELEASE, buildRelease);
  68. registerCommand('build', buildCommand);
  69. registerCommand('run', runCommand);
  70. registerCommand('release', releaseCommand);
  71. // Get shell
  72. shellInfo = new NativeProcessStartupInfo();
  73. cmdLine = new CommandLine();
  74. reset();
  75. }
  76. override public function deactivate():void
  77. {
  78. super.deactivate();
  79. reset();
  80. shellInfo = null;
  81. cmdLine = null;
  82. }
  83. public function getSettingsList():Vector.<ISetting>
  84. {
  85. return Vector.<ISetting>([
  86. new PathSetting(this,'defaultFlexSDK', 'Default Flex SDK', true),
  87. new BooleanSetting(this,'incrementalCompile', 'Incremental Compilation')
  88. ])
  89. }
  90. public function getMenu():MenuItem
  91. {
  92. // Since plugin will be activated if needed we can return null to block menu
  93. if( !activated ) return null;
  94. return new MenuItem(
  95. "MXMLC",
  96. [
  97. new MenuItem("Build", null, CompilerEventBase.BUILD,
  98. 'b', [Keyboard.COMMAND],
  99. 'b', [Keyboard.CONTROL]),
  100. new MenuItem("Build & Run", null, CompilerEventBase.BUILD_AND_RUN,
  101. "\n", [Keyboard.COMMAND],
  102. "f8", []),
  103. new MenuItem("Build & Debug", null, CompilerEventBase.BUILD_AND_DEBUG,
  104. "\n", [Keyboard.COMMAND, Keyboard.SHIFT],
  105. "f5", []),
  106. new MenuItem("Build Release", null, CompilerEventBase.BUILD_RELEASE)
  107. ]
  108. );
  109. }
  110. private function buildCommand(args:Array):void
  111. {
  112. build(null, false);
  113. }
  114. private function runCommand(args:Array):void
  115. {
  116. build(null, true);
  117. }
  118. private function releaseCommand(args:Array):void
  119. {
  120. build(null, false, true);
  121. }
  122. private function reset():void
  123. {
  124. fcsh = null;
  125. targets = new Dictionary();
  126. }
  127. private function buildAndRun(e:Event):void
  128. {
  129. build(e, true);
  130. }
  131. private function buildRelease(e:Event):void
  132. {
  133. build(e, false, true);
  134. }
  135. private function build(e:Event, runAfterBuild:Boolean=false, release:Boolean=false):void
  136. {
  137. this.runAfterBuild = runAfterBuild;
  138. var pvo:ProjectVO = IDEModel.getInstance().activeProject;
  139. // Don't compile if there is no project. Don't warn since other compilers might take the job.
  140. if (!pvo) return
  141. if (!(pvo is AS3ProjectVO)) return;
  142. if (!fcsh || pvo.folder.nativePath != shellInfo.workingDirectory.nativePath
  143. || usingInvalidSDK(pvo as AS3ProjectVO))
  144. {
  145. currentSDK = getCurrentSDK(pvo as AS3ProjectVO);
  146. if (!currentSDK)
  147. {
  148. error("No Flex SDK set. Check settings.");
  149. return;
  150. }
  151. var fsch:File = currentSDK.resolvePath(fcshPath);
  152. if (!fsch.exists)
  153. {
  154. error("Couldn't find FSCH binary. Check settings.");
  155. return;
  156. }
  157. shellInfo.executable = fsch;
  158. shellInfo.workingDirectory = pvo.folder;
  159. initShell();
  160. }
  161. debug("SDK path: %s", currentSDK.nativePath);
  162. compile(pvo as AS3ProjectVO, release);
  163. }
  164. /**
  165. * @return True if the current SDK matches the project SDK, false otherwise
  166. */
  167. private function usingInvalidSDK(pvo:AS3ProjectVO):Boolean
  168. {
  169. var customSDK:File = pvo.buildOptions.customSDK;
  170. if ((customSDK && (currentSDK.nativePath != customSDK.nativePath))
  171. || (!customSDK && currentSDK.nativePath != flexSDK.nativePath))
  172. {
  173. return true;
  174. }
  175. return false;
  176. }
  177. private function getCurrentSDK(pvo:AS3ProjectVO):File
  178. {
  179. var customSDK:File = pvo.buildOptions.customSDK;
  180. return customSDK ? customSDK : (defaultFlexSDK ? new File(defaultFlexSDK) : null);
  181. }
  182. private function compile(pvo:AS3ProjectVO, release:Boolean=false):void
  183. {
  184. clearOutput();
  185. dispatcher.dispatchEvent(new MXMLCPluginEvent(CompilerEventBase.PREBUILD, currentSDK));
  186. print("Compiling "+pvo.projectName);
  187. currentProject = pvo;
  188. pvo.updateConfig();
  189. if (pvo.targets.length == 0)
  190. {
  191. error("No targets found for compilation.");
  192. return;
  193. }
  194. var file:File = pvo.targets[0];
  195. if (targets[file] == undefined)
  196. {
  197. lastTarget = file;
  198. // Turn on optimize flag for release builds
  199. var optFlag:Boolean = pvo.buildOptions.optimize;
  200. if (release) pvo.buildOptions.optimize = true;
  201. var buildArgs:String = pvo.buildOptions.getArguments();
  202. pvo.buildOptions.optimize = optFlag;
  203. var dbg:String;
  204. if (release) dbg = " -debug=false";
  205. else dbg = " -debug=true";
  206. if (buildArgs.indexOf(" -debug=") > -1) dbg = "";
  207. var outputFile:File;
  208. if (release && pvo.swfOutput.path)
  209. outputFile = pvo.folder.resolvePath("bin-release/"+pvo.swfOutput.path.name);
  210. else if (pvo.swfOutput.path)
  211. outputFile = pvo.swfOutput.path
  212. var output:String
  213. if (outputFile)
  214. {
  215. output = " -o " + pvo.folder.getRelativePath(outputFile);
  216. if (outputFile.exists == false)
  217. FileUtil.createFile(outputFile);
  218. }
  219. var mxmlcStr:String =
  220. "mxmlc"
  221. +" -load-config+="+pvo.folder.getRelativePath(pvo.config.file)
  222. +buildArgs
  223. +dbg
  224. +output;
  225. debug("mxmlc command: %s", mxmlcStr);
  226. send(mxmlcStr);
  227. }
  228. else
  229. {
  230. var target:int = targets[file];
  231. send("compile "+target);
  232. }
  233. }
  234. private function send(msg:String):void
  235. {
  236. debug("Sending to mxmlx: %s", msg);
  237. if (!fcsh) {
  238. queue.push(msg);
  239. } else {
  240. var input:IDataOutput = fcsh.standardInput;
  241. input.writeUTFBytes(msg+"\n");
  242. }
  243. }
  244. private function flush():void
  245. {
  246. if (queue.length == 0) return;
  247. if (fcsh) {
  248. for (var i:int = 0; i < queue.length; i++) {
  249. send(queue[i]);
  250. }
  251. queue.length = 0;
  252. }
  253. }
  254. private function initShell():void
  255. {
  256. if (fcsh) {
  257. fcsh.exit();
  258. exiting = true;
  259. reset();
  260. } else {
  261. startShell();
  262. }
  263. }
  264. private function startShell():void
  265. {
  266. fcsh = new NativeProcess();
  267. fcsh.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, shellData);
  268. fcsh.addEventListener(ProgressEvent.STANDARD_ERROR_DATA, shellError);
  269. fcsh.addEventListener(NativeProcessExitEvent.EXIT, shellExit);
  270. fcsh.start(shellInfo);
  271. flush();
  272. }
  273. private function shellData(e:ProgressEvent):void
  274. {
  275. var output:IDataInput = fcsh.standardOutput;
  276. var data:String = output.readUTFBytes(output.bytesAvailable);
  277. var match:Array;
  278. match = data.match(/fcsh: Target \d not found/);
  279. if (match)
  280. {
  281. error("Target not found. Try again.");
  282. targets = new Dictionary();
  283. }
  284. match = data.match(/fcsh: Assigned (\d) as the compile target id/);
  285. if (match && lastTarget) {
  286. var target:int = int(match[1]);
  287. targets[lastTarget] = target;
  288. debug("FSCH target: %s", target);
  289. lastTarget = null;
  290. }
  291. match = data.match(/(.*) \(\d+? bytes\)/);
  292. if (match)
  293. { // Successful compile
  294. var swfPath:String = match[1];
  295. print("Done");
  296. dispatcher.dispatchEvent(
  297. new MXMLCPluginEvent(CompilerEventBase.POSTBUILD, currentSDK)
  298. );
  299. dispatcher.dispatchEvent(
  300. new RefreshTreeEvent( currentProject.folder.resolvePath(swfPath) )
  301. );
  302. if (runAfterBuild)
  303. {
  304. testMovie(swfPath);
  305. }
  306. }
  307. if (data == "(fcsh) ")
  308. {
  309. if (errors != "")
  310. {
  311. compilerError(errors);
  312. errors = "";
  313. }
  314. }
  315. if (data.charAt(data.length-1) == "\n") data = data.substr(0, data.length-1);
  316. debug("%s", data);
  317. }
  318. private function testMovie(swfFilePath:String):void
  319. {
  320. var pvo:AS3ProjectVO = currentProject as AS3ProjectVO;
  321. var swfFile:File = currentProject.folder.resolvePath(swfFilePath);
  322. if (pvo.testMovie == AS3ProjectVO.TEST_MOVIE_CUSTOM)
  323. {
  324. var customSplit:Vector.<String> = Vector.<String>(pvo.testMovieCommand.split(";"));
  325. var customFile:String = customSplit[0];
  326. var customArgs:String = customSplit.slice(1).join(" ").replace("$(ProjectName)", pvo.projectName).replace("$(CompilerPath)", currentSDK.nativePath);
  327. cmdLine.write(customFile+" "+customArgs, pvo.folder);
  328. }
  329. else if (pvo.testMovie == AS3ProjectVO.TEST_MOVIE_AIR)
  330. {
  331. // Let SWFLauncher deal with playin' the swf
  332. dispatcher.dispatchEvent(
  333. new SWFLaunchEvent(SWFLaunchEvent.EVENT_LAUNCH_SWF, swfFile, pvo, currentSDK)
  334. );
  335. }
  336. else
  337. {
  338. // Let SWFLauncher deal with playin' the swf
  339. dispatcher.dispatchEvent(
  340. new SWFLaunchEvent(SWFLaunchEvent.EVENT_LAUNCH_SWF, swfFile, pvo)
  341. );
  342. }
  343. currentProject = null;
  344. }
  345. private function shellError(e:ProgressEvent):void
  346. {
  347. var output:IDataInput = fcsh.standardError;
  348. var data:String = output.readUTFBytes(output.bytesAvailable);
  349. var syntaxMatch:Array;
  350. var generalMatch:Array;
  351. var initMatch:Array;
  352. syntaxMatch = data.match(/(.*?)\((\d*)\): col: (\d*) Error: (.*).*/);
  353. if (syntaxMatch) {
  354. var pathStr:String = syntaxMatch[1];
  355. var lineNum:int = syntaxMatch[2];
  356. var colNum:int = syntaxMatch[3];
  357. var errorStr:String = syntaxMatch[4];
  358. pathStr = pathStr.substr(pathStr.lastIndexOf("/")+1);
  359. errors += HtmlFormatter.sprintf("%s<weak>:</weak>%s \t %s\n",
  360. pathStr, lineNum, errorStr);
  361. }
  362. generalMatch = data.match(/(.*?): Error: (.*).*/);
  363. if (!syntaxMatch && generalMatch)
  364. {
  365. pathStr = generalMatch[1];
  366. errorStr = generalMatch[2];
  367. pathStr = pathStr.substr(pathStr.lastIndexOf("/")+1);
  368. errors += HtmlFormatter.sprintf("%s: %s", pathStr, errorStr);
  369. }
  370. debug("%s", data);
  371. }
  372. private function shellExit(e:NativeProcessExitEvent):void
  373. {
  374. debug("FSCH exit code: %s", e.exitCode);
  375. reset();
  376. if (exiting) {
  377. exiting = false;
  378. startShell();
  379. }
  380. }
  381. protected function compilerWarning(...msg):void
  382. {
  383. var text:String = msg.join(" ");
  384. var textLines:Array = text.split("\n");
  385. var lines:Vector.<TextLineModel> = Vector.<TextLineModel>([]);
  386. for (var i:int = 0; i < textLines.length; i++)
  387. {
  388. if (textLines[i] == "") continue;
  389. text = "<warning> ? </warning>" + textLines[i];
  390. var lineModel:TextLineModel = new MarkupTextLineModel(text);
  391. lines.push(lineModel);
  392. }
  393. outputMsg(lines);
  394. }
  395. protected function compilerError(...msg):void
  396. {
  397. var text:String = msg.join(" ");
  398. var textLines:Array = text.split("\n");
  399. var lines:Vector.<TextLineModel> = Vector.<TextLineModel>([]);
  400. for (var i:int = 0; i < textLines.length; i++)
  401. {
  402. if (textLines[i] == "") continue;
  403. text = "<error> ? </error>" + textLines[i];
  404. var lineModel:TextLineModel = new MarkupTextLineModel(text);
  405. lines.push(lineModel);
  406. }
  407. outputMsg(lines);
  408. }
  409. }
  410. }