Main.hx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. package tools.hxinst;
  2. class Main {
  3. static var SYS = neko.Sys.systemName();
  4. static var NULL = if( SYS == "Windows" ) "NUL" else "/dev/null";
  5. #if xcross
  6. static var wnd : xcross.Winlog;
  7. #end
  8. var baseDir : String;
  9. var binDir : String;
  10. var libDir : String;
  11. var debug : Bool;
  12. function new(dbg) {
  13. debug = dbg;
  14. if( debug ) {
  15. libDir = "/usr/local/lib";
  16. binDir = "/usr/local/bin";
  17. } else {
  18. libDir = "/usr/lib";
  19. binDir = "/usr/bin";
  20. }
  21. baseDir = if( SYS == "Windows" ) neko.Sys.getEnv("ProgramFiles") + "/Motion-Twin" else libDir;
  22. }
  23. function newVersion(v1,v2) {
  24. if( v1 == null )
  25. return true;
  26. return (v1.major * 10000 + v1.minor * 100 + v1.build < v2.major * 10000 + v2.minor * 100 + v2.build);
  27. }
  28. function ask( txt ) {
  29. #if xcross
  30. return xcross.Api.confirm("Question",txt);
  31. #else
  32. var answer = null;
  33. while( true ) {
  34. neko.Lib.print(txt+" [y/n] ");
  35. switch( neko.io.File.stdin().readLine() ) {
  36. case "n": answer = false; break;
  37. case "y": answer = true; break;
  38. }
  39. }
  40. return answer;
  41. #end
  42. }
  43. function error( txt ) {
  44. #if xcross
  45. xcross.Api.error("Error",txt);
  46. #else
  47. neko.io.File.stderr().writeString(txt+"\n");
  48. #end
  49. throw "Installation aborted";
  50. }
  51. function display( txt ) {
  52. #if xcross
  53. wnd.log(txt);
  54. #else
  55. neko.Lib.println(txt);
  56. #end
  57. neko.Sys.sleep(0.03);
  58. }
  59. function version(v : { major : Int, minor : Int, build : Int } ) {
  60. return v.major+"."+v.minor+if( v.build > 0 ) "."+v.build else "";
  61. }
  62. function command( cmd ) {
  63. display("Execute "+cmd);
  64. if( neko.Sys.command(cmd) != 0 )
  65. error("Command '"+cmd+"' failed !");
  66. }
  67. function commandOutput( cmd ) {
  68. var p = try new neko.io.Process(cmd,[]) catch( e : Dynamic ) return "";
  69. return p.stderr.readAll().toString() + p.stdout.readAll().toString();
  70. }
  71. function run() {
  72. try {
  73. install();
  74. display("Installation Completed");
  75. #if xcross
  76. xcross.Api.message("Done","Installation Completed");
  77. #end
  78. } catch( e : Dynamic ) {
  79. display("");
  80. display("");
  81. display("ERROR = "+Std.string(e));
  82. display(haxe.Stack.toString(haxe.Stack.exceptionStack()));
  83. #if xcross
  84. xcross.Api.error("Error","Installation aborted");
  85. #end
  86. }
  87. #if xcross
  88. wnd.enabled = true;
  89. #end
  90. }
  91. function checkRights() {
  92. try {
  93. if( !neko.FileSystem.exists(baseDir) )
  94. neko.FileSystem.createDirectory(baseDir);
  95. var tmp = baseDir + "/.tmp.haxe.inst";
  96. var f = neko.io.File.write(tmp,true);
  97. f.close();
  98. neko.FileSystem.deleteFile(tmp);
  99. return true;
  100. } catch( e : Dynamic ) {
  101. #if xcross
  102. if( xcross.Api.authorize() )
  103. return false;
  104. #end
  105. var msg;
  106. if( SYS == "Windows" )
  107. msg = "You don't have administrative access on this computer,\nplease login on an administrator account.\nOnce haxe is installed, execute '"+baseDir+"\\haxesetup' on your own account.";
  108. else
  109. msg = "You don't have the rights to write in "+baseDir+", please run the installer using 'sudo'";
  110. try error(msg) catch( e : Dynamic ) {};
  111. return false;
  112. }
  113. }
  114. function install() {
  115. // CLEANUP
  116. var dirs = [
  117. "/usr/local/lib/neko",
  118. "/usr/local/lib/haxe",
  119. "/opt/neko",
  120. "/opt/haxe",
  121. ];
  122. for( d in dirs )
  123. if( !debug && neko.FileSystem.exists(d) )
  124. error("A previous haXe/Neko version seems to be installed in '"+d+"', please remove it first");
  125. if( debug )
  126. display("DEBUG MODE ON");
  127. // PROXY
  128. var p = neko.net.ProxyDetect.detect();
  129. if( p == null )
  130. display("No proxy found");
  131. else {
  132. display("Testing proxy "+p.host+":"+p.port);
  133. haxe.Http.PROXY = p;
  134. try {
  135. haxe.Http.request("http://google.com");
  136. } catch( e : Dynamic ) {
  137. display("Could not connect on Google, don't use the proxy");
  138. haxe.Http.PROXY = p;
  139. }
  140. }
  141. // GET haxe Version
  142. display("Getting Local haXe Version");
  143. var content = commandOutput("haxe");
  144. var r = ~/^Haxe Compiler ([0-9]+)\.([0-9]+)/;
  145. var haxeVersion = null;
  146. if( r.match(content) )
  147. haxeVersion = {
  148. major : Std.parseInt(r.matched(1)),
  149. minor : Std.parseInt(r.matched(2)),
  150. build : 0,
  151. };
  152. // GET Neko Version
  153. display("Getting Local Neko Version");
  154. var content = commandOutput("neko");
  155. var r = ~/^NekoVM ([0-9]+)\.([0-9]+)(\.([0-9]+))?/;
  156. var nekoVersion = null;
  157. if( r.match(content) )
  158. nekoVersion = {
  159. major : Std.parseInt(r.matched(1)),
  160. minor : Std.parseInt(r.matched(2)),
  161. build : Std.parseInt(r.matched(4))
  162. };
  163. // GET haXe files list
  164. display("Getting Latest haXe Version");
  165. var haxeFile = null;
  166. var r = ~/^haxe-([0-9]+)\.([0-9]+)(-win|-linux|-osx)(\.zip|\.tar\.gz)$/;
  167. for( f in haxe.Http.request("http://haxe.org/wiki/latest").split("\n") )
  168. if( r.match(f) ) {
  169. var pf = r.matched(3);
  170. switch( SYS ) {
  171. case "Windows": if( pf != "-win" ) continue;
  172. case "Linux": if( pf != "-linux" ) continue;
  173. case "Mac": if( pf != "-osx" ) continue;
  174. default: continue;
  175. }
  176. haxeFile = {
  177. file : f,
  178. version : {
  179. major : Std.parseInt(r.matched(1)),
  180. minor : Std.parseInt(r.matched(2)),
  181. build : 0,
  182. },
  183. };
  184. break;
  185. }
  186. if( haxeFile == null )
  187. error("No haXe File found for your plaform");
  188. // GET Neko files list
  189. display("Getting Latest Neko Version");
  190. var nekoFile = null;
  191. var r = ~/^neko-([0-9]+)\.([0-9]+)(\.([0-9]+))?(-win|-linux|-osx)(\.zip|\.tar\.gz)$/;
  192. for( f in haxe.Http.request("http://nekovm.org/latest.n").split("\n") )
  193. if( r.match(f) ) {
  194. var pf = r.matched(5);
  195. switch( SYS ) {
  196. case "Windows": if( pf != "-win" ) continue;
  197. case "Linux": if( pf != "-linux" ) continue;
  198. case "Mac": if( pf != "-osx" ) continue;
  199. default: continue;
  200. }
  201. nekoFile = {
  202. file : f,
  203. version : {
  204. major : Std.parseInt(r.matched(1)),
  205. minor : Std.parseInt(r.matched(2)),
  206. build : Std.parseInt(r.matched(4)),
  207. }
  208. };
  209. break;
  210. }
  211. if( nekoFile == null )
  212. error("No haXe File found for your plaform");
  213. // ASK QUESTIONS IF OK TO INSTALL
  214. var needHaxe = newVersion(haxeVersion,haxeFile.version);
  215. var needNeko = newVersion(nekoVersion,nekoFile.version);
  216. if( !needHaxe && !needNeko ) {
  217. if( !ask("Both your haXe and Neko versions are up-to-date, do you want to reinstall everything ?") )
  218. throw "Installation Aborted";
  219. needHaxe = true;
  220. needNeko = true;
  221. } else {
  222. var txt = "";
  223. if( needNeko ) {
  224. txt += "Neko "+version(nekoFile.version);
  225. if( needHaxe )
  226. txt += " and ";
  227. }
  228. if( needHaxe )
  229. txt += "haXe "+version(haxeFile.version);
  230. if( !ask("Do you want to install "+txt+" ?") )
  231. error("Installation Aborted");
  232. }
  233. // DOWNLOAD
  234. if( needNeko )
  235. download("http://nekovm.org/_media",nekoFile.file);
  236. if( needHaxe )
  237. download("http://haxe.org/file",haxeFile.file);
  238. // INSTALL
  239. if( needNeko ) {
  240. copy(nekoFile.file,true);
  241. installNeko();
  242. }
  243. if( needHaxe ) {
  244. copy(haxeFile.file,false);
  245. installHaxe();
  246. }
  247. }
  248. static function logProgress( txt ) {
  249. #if xcross
  250. wnd.logProgress(txt);
  251. #else
  252. neko.Lib.print(txt+"\r");
  253. #end
  254. }
  255. function download( url, file ) {
  256. if( neko.FileSystem.exists(file) ) {
  257. display("Using local version of "+file+", skipping download");
  258. return;
  259. }
  260. var str = new haxe.io.BytesOutput();
  261. var progress = new Progress(str);
  262. progress.update = function() {
  263. var p = progress.cur * 100 / progress.max;
  264. p = Std.int(p * 10) / 10;
  265. logProgress("Downloading "+file+" ("+p+"%)");
  266. };
  267. var h = new haxe.Http(url+"/"+file);
  268. var me = this;
  269. h.onError = function(e) {
  270. me.error(Std.string(e));
  271. };
  272. logProgress("Downloading "+file+"...");
  273. h.customRequest(false,progress);
  274. #if xcross
  275. wnd.log("");
  276. #else
  277. neko.Lib.print("\n");
  278. #end
  279. var f = neko.io.File.write(file,true);
  280. f.write(str.getBytes());
  281. f.close();
  282. }
  283. function unzip( file ) {
  284. var ch = neko.io.File.read(file,true);
  285. var entries = if( neko.io.Path.extension(file) == "zip" ) neko.zip.Reader.readZip(ch) else neko.zip.Reader.readTar(ch,true);
  286. ch.close();
  287. return entries;
  288. }
  289. function copy( file, isNeko ) {
  290. var data = unzip(file);
  291. var dir = baseDir + "/" + if( isNeko ) "neko" else "haxe";
  292. if( !neko.FileSystem.exists(dir) )
  293. neko.FileSystem.createDirectory(dir);
  294. if( !isNeko ) {
  295. try {
  296. removeRec(dir+"/std");
  297. } catch( e : Dynamic ) {
  298. }
  299. }
  300. for( f in data ) {
  301. var path = f.fileName.split("/");
  302. path.shift(); // base directory
  303. if( path[path.length-1] == "" ) {
  304. path.pop();
  305. if( path.length == 0 )
  306. continue;
  307. var ddir = dir+"/"+path.join("/");
  308. display("Installing directory "+path.join("/"));
  309. if( !neko.FileSystem.exists(ddir) )
  310. neko.FileSystem.createDirectory(ddir);
  311. continue;
  312. }
  313. var filename = dir + "/" + path.join("/");
  314. var ch = neko.io.File.write(filename,true);
  315. ch.write(neko.zip.Reader.unzip(f));
  316. ch.close();
  317. if( SYS != "Windows" ) {
  318. var exe = neko.io.Path.extension(filename) == "";
  319. neko.Sys.command("chmod "+(if( exe ) 755 else 644)+" "+filename);
  320. }
  321. }
  322. }
  323. function link( dir, file, dest ) {
  324. command("rm -rf "+dest+"/"+file);
  325. command("ln -s "+baseDir+"/"+dir+"/"+file+" "+dest+"/"+file);
  326. }
  327. function installNeko() {
  328. if( SYS == "Windows" )
  329. return;
  330. var so = if( SYS == "Mac" ) ".dylib" else ".so";
  331. link("neko","neko",binDir);
  332. link("neko","nekoc",binDir);
  333. link("neko","nekotools",binDir);
  334. link("neko","libneko"+so,libDir);
  335. }
  336. function installHaxe() {
  337. if( SYS == "Windows" ) {
  338. command('"'+baseDir+'/haxe/haxesetup" -silent');
  339. return;
  340. }
  341. link("haxe","haxe",binDir);
  342. link("haxe","haxelib",binDir);
  343. link("haxe","haxedoc",binDir);
  344. // HAXELIB setup
  345. var haxelib = baseDir + "/haxe/lib";
  346. if( !neko.FileSystem.exists(haxelib) ) {
  347. neko.FileSystem.createDirectory(haxelib);
  348. neko.Sys.command("chmod 777 "+haxelib);
  349. }
  350. }
  351. function removeRec( file ) {
  352. if( !neko.FileSystem.isDirectory(file) ) {
  353. neko.FileSystem.deleteFile(file);
  354. return;
  355. }
  356. for( f in neko.FileSystem.readDirectory(file) )
  357. removeRec(file+"/"+f);
  358. neko.FileSystem.deleteDirectory(file);
  359. }
  360. static function main() {
  361. var debug = neko.Sys.getEnv("INST_DEBUG") != null;
  362. var i = new Main(debug);
  363. if( !i.checkRights() )
  364. return;
  365. #if xcross
  366. wnd = new xcross.Winlog("haXe Installer");
  367. wnd.button = "Exit";
  368. wnd.enabled = false;
  369. wnd.onClick = function() {
  370. neko.vm.Ui.stopLoop();
  371. };
  372. neko.vm.Thread.create(i.run);
  373. neko.vm.Ui.loop();
  374. #else
  375. i.run();
  376. #end
  377. }
  378. }
  379. // --------- TOOLS --------------
  380. class Progress extends haxe.io.Output {
  381. var o : haxe.io.Output;
  382. public var cur : Int;
  383. public var max : Int;
  384. public function new(o) {
  385. this.o = o;
  386. cur = 0;
  387. }
  388. public dynamic function update() {
  389. }
  390. public override function writeByte(c) {
  391. o.writeByte(c);
  392. cur++;
  393. update();
  394. }
  395. public override function writeBytes(s,p,l) {
  396. var r = o.writeBytes(s,p,l);
  397. cur += r;
  398. update();
  399. return r;
  400. }
  401. public override function close() {
  402. super.close();
  403. o.close();
  404. }
  405. public override function prepare(m) {
  406. max = m;
  407. }
  408. }