PageRenderTime 55ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/AS3LibTool.hx

http://caffeine-hx.googlecode.com/
Haxe | 569 lines | 429 code | 54 blank | 86 comment | 64 complexity | f0e6abfd48b8055472d1837179ad0ad2 MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0, BSD-3-Clause, LGPL-2.1
  1. /*
  2. * Copyright (c) 2008, The Caffeine-hx project contributors
  3. * Original author : Russell Weir
  4. * Contributors:
  5. * All rights reserved.
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions are met:
  8. *
  9. * - Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * - Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE CAFFEINE-HX PROJECT CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. * ARE DISCLAIMED. IN NO EVENT SHALL THE CAFFEINE-HX PROJECT CONTRIBUTORS
  19. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  20. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  21. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  22. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  23. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  24. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  25. * THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. package tools;
  28. /**
  29. **/
  30. enum AssetType {
  31. UNKNOWN;
  32. SWF;
  33. IMAGE;
  34. CLASS;
  35. }
  36. typedef Descriptor = {
  37. /*
  38. var name:String;
  39. var className:String;
  40. var relpath : String;
  41. */
  42. var path : Array<String>;
  43. var filename : String;
  44. var extension : String;
  45. var type:AssetType;
  46. };
  47. class AS3LibTool {
  48. static var system : String = { neko.Sys.systemName(); }
  49. static var indir : String;
  50. static var outdir : String;
  51. static var haxeOutdir : String;
  52. static var cpath : Array<String>;
  53. static var sources : Array<Descriptor> = new Array();
  54. static var outfilename : String = "as3libtool_assets.swf";
  55. static var mainClassName: String = "as3libtool_res";
  56. static var mainClassContent: String = "package{\n\timport flash.display.Sprite;\n\tpublic class as3libtool_res extends Sprite {\n\t\tpublic function as3libtool_res() { super(); }\n\t}\n}";
  57. static var imgAppend : String = "";
  58. static var mxmlc : String = "mxmlc";
  59. static var compc : String = "compc";
  60. static var haxe : String = "haxe";
  61. static var doClasses : Bool = true;
  62. static var doImages : Bool = true;
  63. static var doSwfs : Bool = true;
  64. static var capitalizeClassNames : Bool = false;
  65. static var ignoreClassPaths : Array<EReg> = new Array();
  66. /*
  67. static var haxe_base: String;
  68. static var haxe_cur : String;
  69. static var env : Hash<String>;
  70. */
  71. static var eClasses = ~/^(.+)\.(as)$/i;
  72. static var eImg = ~/^(.+)\.((png)|(gif)|(jpg)|(jpeg))$/i;
  73. static var eSwf = ~/^(.+)\.(swf)$/i;
  74. public static function usage() {
  75. var pf = log;
  76. pf("as3libtool -i [path] -o [path] -h [path]");
  77. pf("Creates classes for all assets in inputdir into --build dir");
  78. pf("\t-i [inputdir] Input source path.");
  79. pf("\t-o [path] Path for compiling classes into.");
  80. pf("");
  81. pf("Build options");
  82. pf("\t-f [output.swf] Output library file name. Default as3libtool_assets.swf");
  83. pf("\t--haxe-extern [haxeOutdir] Path for outputting haxe extern classes, if needed.");
  84. pf("");
  85. pf("File naming modifiers");
  86. pf("\t--capitalize Capitalizes any input file for class name generation");
  87. pf("\t--img-append [text] Append text to class names for image resources");
  88. pf("");
  89. pf("Exclude targets");
  90. pf("\t--ignore-classpath [class.path.[?*]] ignore sources with filename pattern matching (before appends)");
  91. pf("\t--no-images Do not process images in inputdir");
  92. pf("\t--no-swf Do not add swf files found in input dir");
  93. pf("\t--no-classes Ignore .as files in inputdir");
  94. pf("");
  95. pf("Support program paths (if as3libtool is unable to locate automatically)");
  96. pf("\t--mxmlc [path] mxmlc compiler binary");
  97. pf("\t--haxe [path] haxe compiler binary");
  98. }
  99. public static function main() {
  100. var argv = neko.Sys.args();
  101. var startdir = neko.Sys.getCwd();
  102. if(argv.length < 2) {
  103. usage();
  104. neko.Sys.exit(1);
  105. }
  106. var icp = new Array<String>();
  107. var i = 0;
  108. while(i < argv.length) {
  109. switch(argv[i]) {
  110. case "-i":
  111. indir = neko.FileSystem.fullPath(argv[++i]);
  112. if(!neko.FileSystem.isDirectory(indir))
  113. error("Specify a correct input directory");
  114. case "-o":
  115. outdir = StringTools.trim(argv[++i]);
  116. if(!neko.FileSystem.exists(outdir) || !neko.FileSystem.isDirectory(outdir)) {
  117. try {
  118. neko.FileSystem.createDirectory(outdir);
  119. }
  120. catch(e:Dynamic) {
  121. error("Unable to create output directory.");
  122. }
  123. }
  124. //outdir = StringTools.trim(argv[++i]);
  125. outdir = neko.FileSystem.fullPath(outdir);
  126. case "-f":
  127. outfilename = argv[++i];
  128. case "--haxe-extern":
  129. haxeOutdir = argv[++i];
  130. if(!neko.FileSystem.exists(haxeOutdir) || !neko.FileSystem.isDirectory(haxeOutdir)) {
  131. try {
  132. neko.FileSystem.createDirectory(haxeOutdir);
  133. }
  134. catch(e:Dynamic) {
  135. error("Unable to create output directory.");
  136. }
  137. }
  138. haxeOutdir = neko.FileSystem.fullPath(haxeOutdir);
  139. case "--ignore-classpath":
  140. icp.push(argv[++i]);
  141. case "--img-append":
  142. imgAppend = argv[++i];
  143. log("> appending " + imgAppend + " to images");
  144. case "--capitalize":
  145. capitalizeClassNames = true;
  146. case "--no-images":
  147. doImages = false;
  148. case "--no-swf":
  149. doSwfs = false;
  150. case "--no-classes":
  151. doClasses = false;
  152. case "--mxmlc":
  153. mxmlc = argv[++i];
  154. case "--haxe":
  155. haxe = argv[++i];
  156. case "--help":
  157. usage();
  158. neko.Sys.exit(0);
  159. default:
  160. error("Unknown option " + argv[i]);
  161. }
  162. i++;
  163. }
  164. if(indir == null)
  165. error("No input path specified");
  166. if(outdir == null)
  167. error("No output path specified");
  168. for(cp in icp) {
  169. var parts = cp.split(".");
  170. if(parts.length == 1)
  171. parts.push(".*");
  172. parts[parts.length - 1] = checkCapitalize(parts[parts.length - 1]);
  173. var mcp = parts.join(".");
  174. mcp = StringTools.replace(mcp, ".", "\\.");
  175. mcp = StringTools.replace(mcp, "-", "\\-");
  176. mcp = StringTools.replace(mcp, "?", "[\\.]{1,0}");
  177. mcp = StringTools.replace(mcp, "*", ".*");
  178. mcp = "^" + mcp + "$";
  179. var ereg = new EReg(mcp, "");
  180. ignoreClassPaths.push(new EReg(mcp,""));
  181. }
  182. cpath = new Array();
  183. neko.Sys.setCwd(indir);
  184. sources = processDirectory(".");
  185. neko.Sys.setCwd(startdir);
  186. log("");
  187. if(sources.length == 0) {
  188. error("No asset files to process.");
  189. }
  190. var cmdArgs : Array<String> = [
  191. "-source-path=" + outdir
  192. ];
  193. var includeArgs = new Array<String>();
  194. for(s in sources) {
  195. switch(s.type) {
  196. case UNKNOWN,IMAGE,SWF:
  197. case CLASS:
  198. copyClass(s);
  199. includeArgs.push(classPath(s));
  200. }
  201. }
  202. for(s in sources) {
  203. switch(s.type) {
  204. case UNKNOWN,SWF,CLASS:
  205. case IMAGE:
  206. includeArgs.push(classPath(s,imgAppend));
  207. createImageClass(s);
  208. }
  209. }
  210. if(includeArgs.length > 0) {
  211. cmdArgs.push("-includes");
  212. for(a in includeArgs) {
  213. cmdArgs.push(a);
  214. }
  215. }
  216. cmdArgs.push("-o");
  217. cmdArgs.push(outfilename);
  218. cmdArgs.push(outdir + "/" + mainClassName + ".as");
  219. createMainClass();
  220. log(mxmlc + cmdArgs.join(" "));
  221. if(neko.Sys.command(mxmlc, cmdArgs) != 0) {
  222. error("**** There was an error running mxmlc.\nPlease review the compiler output for possible causes.");
  223. }
  224. if(haxeOutdir != null) {
  225. var cwd = neko.Sys.getCwd();
  226. var orig : String = null;
  227. try orig = neko.FileSystem.fullPath(outfilename) catch(e:Dynamic) {
  228. error("*** Could not locate " + outfilename);
  229. }
  230. /**
  231. This hack is because
  232. 1) Haxe will not take a full path to a file for --gen-hx-classes
  233. 2) Neko has no FileSystem.copy (yes, I could open...stream.. ya ya)
  234. **/
  235. try neko.Sys.setCwd(haxeOutdir) catch(e:Dynamic) {
  236. error("*** Unable to change directory to "+ haxeOutdir);
  237. }
  238. try neko.FileSystem.rename(orig, "./" + outfilename) catch(e:Dynamic) {
  239. neko.Sys.setCwd(cwd);
  240. error("*** Unable to copy asset pack to "+ haxeOutdir + "\n");
  241. }
  242. var cleanup = function() {
  243. neko.FileSystem.rename("./" + outfilename, orig);
  244. neko.Sys.setCwd(cwd);
  245. }
  246. neko.Lib.print("Creating haxe extern classes in " + haxeOutdir + "...");
  247. var proc = new neko.io.Process(haxe, ["--gen-hx-classes", outfilename]);
  248. if(proc.exitCode() != 0) {
  249. log("ERROR.");
  250. cleanup();
  251. error(proc.stderr.readAll().toString());
  252. }
  253. log("complete.");
  254. cleanup();
  255. }
  256. }
  257. /**
  258. Logs a string to stderr and exits program
  259. **/
  260. public static function error( s : String ) {
  261. neko.io.File.stderr().writeString(s + "\n");
  262. neko.Sys.exit(1);
  263. }
  264. public static function getDirs(p : String) {
  265. var a = neko.FileSystem.readDirectory(p);
  266. var b : Array<String> = new Array();
  267. for(d in a) {
  268. if(!neko.FileSystem.isDirectory(d))
  269. continue;
  270. if(d == ".svn")
  271. continue;
  272. b.push(d);
  273. }
  274. return b;
  275. }
  276. public static function getFilesInCwd() : Array<Descriptor> {
  277. var a = neko.FileSystem.readDirectory(".");
  278. var b : Array<Descriptor> = new Array();
  279. for(d in a) {
  280. if(neko.FileSystem.isDirectory(d)) continue;
  281. var type = UNKNOWN;
  282. var filename : String = null;
  283. var ext : String = null;
  284. if(eClasses.match(d)) {
  285. if(!doClasses) continue;
  286. filename = eClasses.matched(1);
  287. ext = eClasses.matched(2);
  288. type = CLASS;
  289. }
  290. else if(eImg.match(d)) {
  291. if(!doImages) continue;
  292. filename = eImg.matched(1);
  293. ext = eImg.matched(2);
  294. type = IMAGE;
  295. }
  296. else if(eSwf.match(d)) {
  297. if(!doSwfs) continue;
  298. filename = eSwf.matched(1);
  299. ext = eSwf.matched(2);
  300. type = SWF;
  301. }
  302. else {
  303. log("!! Found unknown file " + d);
  304. continue;
  305. }
  306. if(!neko.FileSystem.isFile(d)) {
  307. error("file " + neko.Sys.getCwd() + "/"+ d + " is not a regular file!");
  308. }
  309. var d = {
  310. path:cpath.slice(1),
  311. filename: filename,
  312. extension:ext,
  313. type:type
  314. };
  315. if(!ignoreClassPath(classPath(d)))
  316. b.push(d);
  317. }
  318. return b;
  319. }
  320. static function log(s : String) {
  321. neko.io.File.stdout().writeString(s+"\n");
  322. neko.io.File.stdout().flush();
  323. }
  324. /**
  325. Create the output directories for flash and haxe.
  326. **/
  327. static function makeOutputPaths() : Void {
  328. if(cpath.length > 1) {
  329. createOutputPath(outdir, cpath.slice(1));
  330. // --gen-hx-classes takes carfe of this
  331. // if(haxeOutdir != null)
  332. // createOutputPath(haxeOutdir, cpath.slice(1));
  333. }
  334. }
  335. static function processDirectory(p : String) : Array<Descriptor> {
  336. cpath.push(p);
  337. var files = new Array();
  338. var bcp = cpath.slice(1).join(".");
  339. neko.Sys.setCwd(p);
  340. if(ignoreClassPath(bcp + ".")) {
  341. log(">>> Skipping path " + bcp);
  342. } else {
  343. log(">>> Starting " + (if(bcp == "") "(root)" else bcp));
  344. makeOutputPaths();
  345. files = getFilesInCwd();
  346. for(f in files) {
  347. switch(f.type) {
  348. case UNKNOWN:
  349. error("Unset asset type!");
  350. case SWF:
  351. log("+[SWF] " + relativePath(f));
  352. case IMAGE:
  353. log("+[IMG] " + relativePath(f));
  354. case CLASS:
  355. log("+[AS3] " + relativePath(f));
  356. }
  357. }
  358. var dirs = getDirs(".");
  359. for(d in dirs) {
  360. var ef = processDirectory(d);
  361. for(f in ef)
  362. files.push(f);
  363. }
  364. }
  365. cpath.pop();
  366. neko.Sys.setCwd("./../");
  367. return files;
  368. }
  369. static function checkOverWrite(s) {
  370. if(neko.FileSystem.exists(s))
  371. error(s + " already exists in the output directory.");
  372. }
  373. /**
  374. Full relative path to file
  375. ie. /subdir/myimage.png
  376. replExt can replace the extension.
  377. **/
  378. static function relativePath(d:Descriptor, ?replExt:String) {
  379. var ext = d.extension;
  380. if(replExt != null)
  381. ext = replExt;
  382. var pp = "/" + d.filename + "." + ext;
  383. if(d.path.length > 0)
  384. return "/" + d.path.join("/") + pp;
  385. return pp;
  386. }
  387. /**
  388. Full path to original file
  389. **/
  390. static function sourcePath(d:Descriptor) {
  391. return indir + relativePath(d);
  392. }
  393. /**
  394. Full path to output file. If appendToFilename is specified, the resulting
  395. filename will be modified. If it is an image asset, the filename may be capitalized
  396. based on the capitalizeClassNames switch
  397. **/
  398. static function destinationPath(base:String, d : Descriptor, ?appendToFilename:String) : String {
  399. if(appendToFilename == null) appendToFilename = "";
  400. var desc : Descriptor = {
  401. path: d.path,
  402. filename: switch(d.type) {
  403. case IMAGE:
  404. checkCapitalize(d.filename) + appendToFilename;
  405. default:
  406. d.filename + appendToFilename;
  407. },
  408. extension: d.extension,
  409. type: d.type
  410. };
  411. return switch(d.type) {
  412. case UNKNOWN: "";
  413. case SWF, CLASS:
  414. base + relativePath(desc);
  415. case IMAGE:
  416. base + relativePath(desc, "as");
  417. }
  418. }
  419. /**
  420. FQ package name
  421. **/
  422. static function packageName(d) : String {
  423. return d.path.join(".");
  424. }
  425. /**
  426. FQ class path ie mydir.myclass
  427. **/
  428. static function classPath(d:Descriptor, ?appendToClassName:String) {
  429. if(appendToClassName == null)
  430. appendToClassName = "";
  431. var rv = Std.string(packageName(d));
  432. if(d.path.length > 0) rv += ".";
  433. rv += checkCapitalize(d.filename) + appendToClassName;
  434. return rv;
  435. }
  436. /**
  437. Create a path in an output directory. Base is a path with no trailing /
  438. **/
  439. static function createOutputPath(base:String, e:Array<String>) {
  440. var p = base + "/" + e.join("/");
  441. if(neko.FileSystem.exists(p)) {
  442. if(!neko.FileSystem.isDirectory(p)) {
  443. error("Unable to create output directory " + p);
  444. }
  445. return;
  446. }
  447. try {
  448. neko.FileSystem.createDirectory(p);
  449. }
  450. catch(e:Dynamic) {
  451. error("Unable to create output directory " + p);
  452. }
  453. }
  454. static function copyClass(d) {
  455. var spath = sourcePath(d);
  456. var dpath = destinationPath(outdir, d);
  457. neko.io.File.copy(spath,dpath);
  458. }
  459. static function createImageClass(d) {
  460. var spath = sourcePath(d);
  461. var s = "package " + packageName(d) + " {\n";
  462. s += "\timport flash.display.Sprite;\n";
  463. s += "\tpublic class " + checkCapitalize(d.filename) + imgAppend + " extends Sprite {\n";
  464. s += "\t\t[Embed(source=\"" + sourcePath(d) + "\")]\n";
  465. s += "\t\tprivate var c:Class;\n";
  466. s += "\t\tpublic function " + checkCapitalize(d.filename) + imgAppend + "() {\n";
  467. s += "\t\t\tsuper();\n";
  468. s += "\t\t\taddChild(new c());\n";
  469. s += "\t\t}\n";
  470. s += "\t}\n";
  471. s += "}\n";
  472. var fo = neko.io.File.write(destinationPath(outdir, d, imgAppend), false);
  473. fo.writeString(s);
  474. fo.close();
  475. }
  476. /**
  477. This is just a stub class required for compiling with mxmlc
  478. **/
  479. static function createMainClass() {
  480. var fo = neko.io.File.write(
  481. destinationPath(
  482. outdir,
  483. {
  484. path : new Array(),
  485. filename: mainClassName,
  486. extension: "as",
  487. type: CLASS
  488. }
  489. ),
  490. false
  491. );
  492. fo.writeString(mainClassContent);
  493. fo.close();
  494. }
  495. /**
  496. Takes a string class path and checks all --ignore-classpath args
  497. against it, returning true if the class is to be ignored.
  498. **/
  499. static function ignoreClassPath(s:String) : Bool {
  500. for(ereg in ignoreClassPaths) {
  501. if(ereg.match(s))
  502. return true;
  503. }
  504. return false;
  505. }
  506. /**
  507. Capitalizes a string if the
  508. **/
  509. static function checkCapitalize(s : String) : String {
  510. if(!capitalizeClassNames)
  511. return s;
  512. return s.substr(0,1).toUpperCase()+s.substr(1);
  513. }
  514. }