Main.hx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. /*
  2. * Copyright (C)2005-2012 Haxe Foundation
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. * DEALINGS IN THE SOFTWARE.
  21. */
  22. package tools.hxinst;
  23. class Main {
  24. static var SYS = Sys.systemName();
  25. static var NULL = if( SYS == "Windows" ) "NUL" else "/dev/null";
  26. #if xcross
  27. static var wnd : xcross.Winlog;
  28. #end
  29. var baseDir : String;
  30. var binDir : String;
  31. var libDir : String;
  32. var debug : Bool;
  33. function new(dbg) {
  34. debug = dbg;
  35. if( debug ) {
  36. libDir = "/usr/local/lib";
  37. binDir = "/usr/local/bin";
  38. } else {
  39. libDir = "/usr/lib";
  40. binDir = "/usr/bin";
  41. }
  42. baseDir = if( SYS == "Windows" ) {
  43. // use C:/ for Vista, Win7
  44. var baseDir = (Sys.getEnv("ALLUSERSPROFILE") == "C:\\ProgramData") ? "C:" : Sys.getEnv("ProgramFiles");
  45. baseDir + "/HaxeFoundation";
  46. } else
  47. libDir;
  48. }
  49. function newVersion(v1,v2) {
  50. if( v1 == null )
  51. return true;
  52. return (v1.major * 10000 + v1.minor * 100 + v1.build < v2.major * 10000 + v2.minor * 100 + v2.build);
  53. }
  54. function ask( txt ) {
  55. #if xcross
  56. return xcross.Api.confirm("Question",txt);
  57. #else
  58. var answer = null;
  59. while( true ) {
  60. Sys.print(txt+" [y/n] ");
  61. switch( Sys.stdin().readLine() ) {
  62. case "n": answer = false; break;
  63. case "y": answer = true; break;
  64. }
  65. }
  66. return answer;
  67. #end
  68. }
  69. function error( txt ) {
  70. #if xcross
  71. xcross.Api.error("Error",txt);
  72. #else
  73. Sys.stderr().writeString(txt+"\n");
  74. #end
  75. throw "Installation aborted";
  76. }
  77. function display( txt ) {
  78. #if xcross
  79. wnd.log(txt);
  80. #else
  81. Sys.println(txt);
  82. #end
  83. Sys.sleep(0.03);
  84. }
  85. function version(v : { major : Int, minor : Int, build : Int }, twoDigitMinor ) {
  86. var min = twoDigitMinor && v.minor < 10 ? "0"+v.minor : Std.string(v.minor);
  87. return v.major+"."+min+if( v.build > 0 ) "."+v.build else "";
  88. }
  89. function command( cmd ) {
  90. display("Execute "+cmd);
  91. if( Sys.command(cmd) != 0 )
  92. error("Command '"+cmd+"' failed !");
  93. }
  94. function commandOutput( cmd ) {
  95. var p = try new sys.io.Process(cmd,[]) catch( e : Dynamic ) return "";
  96. return p.stderr.readAll().toString() + p.stdout.readAll().toString();
  97. }
  98. function run() {
  99. try {
  100. install();
  101. display("Installation Completed");
  102. #if xcross
  103. xcross.Api.message("Done","Installation Completed");
  104. #end
  105. } catch( e : Dynamic ) {
  106. display("");
  107. display("");
  108. display("ERROR = "+Std.string(e));
  109. display(haxe.CallStack.toString(haxe.CallStack.exceptionStack()));
  110. #if xcross
  111. xcross.Api.error("Error","Installation aborted");
  112. #end
  113. }
  114. #if xcross
  115. wnd.enabled = true;
  116. #end
  117. }
  118. function checkRights() {
  119. try {
  120. if( !sys.FileSystem.exists(baseDir) )
  121. sys.FileSystem.createDirectory(baseDir);
  122. var tmp = baseDir + "/.tmp.haxe.inst";
  123. var f = sys.io.File.write(tmp,true);
  124. f.close();
  125. sys.FileSystem.deleteFile(tmp);
  126. return true;
  127. } catch( e : Dynamic ) {
  128. #if xcross
  129. if( xcross.Api.authorize() )
  130. return false;
  131. #end
  132. var msg;
  133. if( SYS == "Windows" )
  134. 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.";
  135. else
  136. msg = "You don't have the rights to write in "+baseDir+", please run the installer using 'sudo'";
  137. try error(msg) catch( e : Dynamic ) {};
  138. return false;
  139. }
  140. }
  141. var cacheResult : Null<Bool>;
  142. function is64() {
  143. if( cacheResult != null )
  144. return cacheResult;
  145. var p = new sys.io.Process("uname", ["-m"]);
  146. var ret = p.stdout.readAll().toString();
  147. p.exitCode();
  148. cacheResult = ret.indexOf("x86_64") != -1;
  149. return cacheResult;
  150. }
  151. function install() {
  152. // CLEANUP
  153. var dirs = [
  154. "/usr/local/lib/neko",
  155. "/usr/local/lib/haxe",
  156. "/opt/neko",
  157. "/opt/haxe",
  158. "c:/motion-twin",
  159. "c:/program files/motion-twin",
  160. ];
  161. for( d in dirs )
  162. if( !debug && sys.FileSystem.exists(d) )
  163. error("A previous Haxe/Neko version seems to be installed in '"+d+"', please remove it first");
  164. if( debug )
  165. display("DEBUG MODE ON");
  166. // PROXY
  167. var p = neko.net.ProxyDetect.detect();
  168. if( p != null ) {
  169. display("Testing proxy "+p.host+":"+p.port);
  170. haxe.Http.PROXY = p;
  171. try {
  172. haxe.Http.requestUrl("http://google.com");
  173. } catch( e : Dynamic ) {
  174. display("Could not connect on Google, trying with no proxy");
  175. haxe.Http.PROXY = null;
  176. }
  177. }
  178. // GET haxe Version
  179. display("Getting Local Haxe Version");
  180. var content = commandOutput("haxe");
  181. var r = ~/^Haxe Compiler ([0-9]+)\.([0-9]+)(\.([0-9]+))?/;
  182. var haxeVersion = null;
  183. if( r.match(content) ) {
  184. haxeVersion = {
  185. major : Std.parseInt(r.matched(1)),
  186. minor : Std.parseInt(r.matched(2)),
  187. build : Std.parseInt(r.matched(4))
  188. };
  189. if( haxeVersion.build == null ) haxeVersion.build = 0;
  190. }
  191. // GET Neko Version
  192. display("Getting Local Neko Version");
  193. var content = commandOutput("neko");
  194. var r = ~/^NekoVM ([0-9]+)\.([0-9]+)(\.([0-9]+))?/;
  195. var nekoVersion = null;
  196. if( r.match(content) )
  197. nekoVersion = {
  198. major : Std.parseInt(r.matched(1)),
  199. minor : Std.parseInt(r.matched(2)),
  200. build : Std.parseInt(r.matched(4))
  201. };
  202. // GET Haxe files list
  203. display("Getting Latest Haxe Version");
  204. var haxeFile = null;
  205. var r = ~/^haxe-([0-9]+)\.([0-9]+)(-win|-linux|-osx)(\.zip|\.tar\.gz)$/;
  206. for( f in haxe.Http.requestUrl("http://haxe.org/wiki/latest").split("\n") )
  207. if( r.match(f) ) {
  208. var pf = r.matched(3);
  209. switch( SYS ) {
  210. case "Windows": if( pf != "-win" ) continue;
  211. case "Linux": if( pf != "-linux" ) continue;
  212. case "Mac": if( pf != "-osx" ) continue;
  213. default: continue;
  214. }
  215. haxeFile = {
  216. file : f,
  217. version : {
  218. major : Std.parseInt(r.matched(1)),
  219. minor : Std.parseInt(r.matched(2)),
  220. build : 0,
  221. },
  222. };
  223. break;
  224. }
  225. if( haxeFile == null )
  226. error("No Haxe File found for your plaform");
  227. // GET Neko files list
  228. display("Getting Latest Neko Version");
  229. var nekoFile = null;
  230. var r = ~/^neko-([0-9]+)\.([0-9]+)(\.([0-9]+))?(-win|-linux|-osx|-linux64)(\.zip|\.tar\.gz)$/;
  231. for( f in haxe.Http.requestUrl("http://nekovm.org/latest.n").split("\n") )
  232. if( r.match(f) ) {
  233. var pf = r.matched(5);
  234. switch( SYS ) {
  235. case "Windows": if( pf != "-win" ) continue;
  236. case "Linux": if( pf != "-linux"+(is64() ? "64":"") ) continue;
  237. case "Mac": if( pf != "-osx" ) continue;
  238. default: continue;
  239. }
  240. nekoFile = {
  241. file : f,
  242. version : {
  243. major : Std.parseInt(r.matched(1)),
  244. minor : Std.parseInt(r.matched(2)),
  245. build : Std.parseInt(r.matched(4)),
  246. }
  247. };
  248. break;
  249. }
  250. if( nekoFile == null )
  251. error("No Haxe File found for your plaform");
  252. // ASK QUESTIONS IF OK TO INSTALL
  253. var needHaxe = newVersion(haxeVersion,haxeFile.version);
  254. var needNeko = newVersion(nekoVersion,nekoFile.version);
  255. if( !needHaxe && !needNeko ) {
  256. if( !ask("Both your Haxe and Neko versions are up-to-date, do you want to reinstall everything ?") )
  257. throw "Installation Aborted";
  258. needHaxe = true;
  259. needNeko = true;
  260. } else {
  261. var txt = "";
  262. if( needNeko ) {
  263. txt += "Neko "+version(nekoFile.version,false);
  264. if( needHaxe )
  265. txt += " and ";
  266. }
  267. if( needHaxe )
  268. txt += "Haxe "+version(haxeFile.version,true);
  269. if( !ask("Do you want to install "+txt+" ?") )
  270. error("Installation Aborted");
  271. }
  272. // DOWNLOAD
  273. if( needNeko )
  274. download("http://nekovm.org/_media",nekoFile.file);
  275. if( needHaxe )
  276. download("http://haxe.org/file",haxeFile.file);
  277. // INSTALL
  278. if( needNeko ) {
  279. copy(nekoFile.file,true);
  280. installNeko();
  281. }
  282. if( needHaxe ) {
  283. copy(haxeFile.file,false);
  284. installHaxe();
  285. }
  286. }
  287. static function logProgress( txt ) {
  288. #if xcross
  289. wnd.logProgress(txt);
  290. #else
  291. Sys.print(txt+"\r");
  292. #end
  293. }
  294. function download( url, file ) {
  295. if( sys.FileSystem.exists(file) ) {
  296. display("Using local version of "+file+", skipping download");
  297. return;
  298. }
  299. var str = new haxe.io.BytesOutput();
  300. var progress = new Progress(str);
  301. progress.update = function() {
  302. var p = progress.cur * 100 / progress.max;
  303. p = Std.int(p * 10) / 10;
  304. logProgress("Downloading "+file+" ("+p+"%)");
  305. };
  306. var h = new haxe.Http(url+"/"+file);
  307. var me = this;
  308. h.onError = function(e) {
  309. me.error(Std.string(e));
  310. };
  311. logProgress("Downloading "+file+"...");
  312. h.customRequest(false,progress);
  313. #if xcross
  314. wnd.log("");
  315. #else
  316. Sys.print("\n");
  317. #end
  318. var f = sys.io.File.write(file,true);
  319. f.write(str.getBytes());
  320. f.close();
  321. }
  322. function unzip( file ) {
  323. var ch = sys.io.File.read(file,true);
  324. var entries;
  325. if( haxe.io.Path.extension(file) == "zip" )
  326. entries = haxe.zip.Reader.readZip(ch);
  327. else {
  328. entries = new List();
  329. for( f in new format.tgz.Reader(ch).read() )
  330. entries.add({
  331. fileName : f.fileName,
  332. fileTime : f.fileTime,
  333. fileSize : f.fileSize,
  334. data : f.data,
  335. dataSize : f.data.length,
  336. compressed : false,
  337. crc32 : null,
  338. });
  339. }
  340. ch.close();
  341. return entries;
  342. }
  343. function copy( file, isNeko ) {
  344. var data = unzip(file);
  345. var dir = baseDir + "/" + if( isNeko ) "neko" else "haxe";
  346. if( !sys.FileSystem.exists(dir) )
  347. sys.FileSystem.createDirectory(dir);
  348. if( !isNeko ) {
  349. try {
  350. removeRec(dir+"/std");
  351. } catch( e : Dynamic ) {
  352. }
  353. }
  354. for( f in data ) {
  355. var path = f.fileName.split("/");
  356. path.shift(); // base directory
  357. if( path[path.length-1] == "" ) {
  358. path.pop();
  359. if( path.length == 0 )
  360. continue;
  361. var ddir = dir+"/"+path.join("/");
  362. display("Installing directory "+path.join("/"));
  363. if( !sys.FileSystem.exists(ddir) )
  364. sys.FileSystem.createDirectory(ddir);
  365. continue;
  366. }
  367. var filename = dir + "/" + path.join("/");
  368. var ch = sys.io.File.write(filename,true);
  369. ch.write(haxe.zip.Reader.unzip(f));
  370. ch.close();
  371. if( SYS != "Windows" ) {
  372. var exe = haxe.io.Path.extension(filename) == "";
  373. Sys.command("chmod "+(if( exe ) 755 else 644)+" "+filename);
  374. }
  375. }
  376. }
  377. function link( dir, file, dest ) {
  378. command("rm -rf "+dest+"/"+file);
  379. command("ln -s "+baseDir+"/"+dir+"/"+file+" "+dest+"/"+file);
  380. }
  381. function installNeko() {
  382. if( SYS == "Windows" )
  383. return;
  384. var so = if( SYS == "Mac" ) ".dylib" else ".so";
  385. link("neko","neko",binDir);
  386. link("neko","nekoc",binDir);
  387. link("neko","nekotools",binDir);
  388. link("neko","libneko"+so,libDir);
  389. }
  390. function installHaxe() {
  391. if( SYS == "Windows" ) {
  392. command('"'+baseDir+'/haxe/haxesetup" -silent');
  393. return;
  394. }
  395. link("haxe","haxe",binDir);
  396. link("haxe","haxelib",binDir);
  397. link("haxe","haxedoc",binDir);
  398. // HAXELIB setup
  399. var haxelib = baseDir + "/haxe/lib";
  400. if( !sys.FileSystem.exists(haxelib) ) {
  401. sys.FileSystem.createDirectory(haxelib);
  402. Sys.command("chmod 777 "+haxelib);
  403. }
  404. }
  405. function removeRec( file ) {
  406. if( !sys.FileSystem.isDirectory(file) ) {
  407. sys.FileSystem.deleteFile(file);
  408. return;
  409. }
  410. for( f in sys.FileSystem.readDirectory(file) )
  411. removeRec(file+"/"+f);
  412. sys.FileSystem.deleteDirectory(file);
  413. }
  414. static function main() {
  415. var debug = Sys.getEnv("INST_DEBUG") != null;
  416. var i = new Main(debug);
  417. if( !i.checkRights() )
  418. return;
  419. #if xcross
  420. wnd = new xcross.Winlog("Haxe Installer");
  421. wnd.button = "Exit";
  422. wnd.enabled = false;
  423. wnd.onClick = function() {
  424. neko.vm.Ui.stopLoop();
  425. };
  426. neko.vm.Thread.create(i.run);
  427. neko.vm.Ui.loop();
  428. #else
  429. i.run();
  430. #end
  431. }
  432. }
  433. // --------- TOOLS --------------
  434. class Progress extends haxe.io.Output {
  435. var o : haxe.io.Output;
  436. public var cur : Int;
  437. public var max : Int;
  438. public function new(o) {
  439. this.o = o;
  440. cur = 0;
  441. }
  442. public dynamic function update() {
  443. }
  444. public override function writeByte(c) {
  445. o.writeByte(c);
  446. cur++;
  447. update();
  448. }
  449. public override function writeBytes(s,p,l) {
  450. var r = o.writeBytes(s,p,l);
  451. cur += r;
  452. update();
  453. return r;
  454. }
  455. public override function close() {
  456. super.close();
  457. o.close();
  458. }
  459. public override function prepare(m) {
  460. max = m;
  461. }
  462. }