CE Editor C++.cpp 129 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. static Int Compare(C Edit::CodeEditor::BuildFile &a, C Edit::CodeEditor::BuildFile &b)
  5. {
  6. return ComparePath(a.src_proj_path, b.src_proj_path);
  7. }
  8. namespace Edit{
  9. /******************************************************************************/
  10. #define MERGE_HEADERS 0 // this made almost no difference on an SSD Disk
  11. /******************************************************************************/
  12. // TODO: VSRun doesn't include build configuration, debug/release, dx9/dx10+, 32/64bit
  13. void CodeEditor::VS (C Str &command, Bool console, Bool hidden ) {if(console ? !build_process.create(devenv_path, command, hidden) : !Run(devenv_path, command, hidden))Error(S+"Error launching:\n\""+devenv_path+'"');}
  14. //void CodeEditor::VSClean(C Str &project ) {VS(S+"\""+NormalizePath(MakeFullPath(project))+"\" /Clean", true, true);} unused
  15. void CodeEditor::VSBuild(C Str &project, C Str &config , C Str &platform, C Str &out) {VS(VSBuildParams(project, config, platform, out), true, true);}
  16. void CodeEditor::VSRun (C Str &project , C Str &out) {VS(S+"/RunExit \""+NormalizePath(MakeFullPath(project))+'"'+(out.is() ? S+" /Out \""+NormalizePath(MakeFullPath(out))+'"' : S), false, false);}
  17. void CodeEditor::VSOpen (C Str &project, C Str &file ) {VS(S+'"'+NormalizePath(MakeFullPath(project))+'"'+(file.is() ? S+"/command open \""+file+'"' : S), false, false);} // for unknown reasons this opens file and also "open file window"
  18. //void CodeEditor::VSDebug(C Str &exe ) {VS(S+"/DebugExe \""+exe+'"', false, false);} // it doesn't load any project/sources, only pure .exe, unused
  19. /******************************************************************************/
  20. void CodeEditor::validateDevEnv()
  21. {
  22. devenv_version=-1;
  23. devenv_express= false;
  24. devenv_path.clear();
  25. if(vs_path.is())
  26. {
  27. if(!FExistSystem(devenv_path))
  28. {
  29. // don't try .com as 'ConsoleProc.kill' does not work on it
  30. /*devenv_path =Str(vs_path).tailSlash(true)+"Common7\\IDE\\devenv.com"; // first try devenv (pro, ..) com
  31. devenv_com =true;
  32. devenv_express=false;
  33. if(!FExistSystem(devenv_path))*/
  34. {
  35. devenv_path =Str(vs_path).tailSlash(true)+"Common7\\IDE\\devenv.exe"; // try devenv (pro, ..) exe
  36. devenv_com =false;
  37. devenv_express=false;
  38. if(!FExistSystem(devenv_path))
  39. {
  40. /*devenv_path =Str(vs_path).tailSlash(true)+"Common7\\IDE\\VCExpress.com"; // try express com
  41. devenv_com =true;
  42. devenv_express=true;
  43. if(!FExistSystem(devenv_path))*/
  44. {
  45. devenv_path =Str(vs_path).tailSlash(true)+"Common7\\IDE\\VCExpress.exe"; // try express exe
  46. devenv_com =false;
  47. devenv_express=true;
  48. if(!FExistSystem(devenv_path))
  49. {
  50. devenv_path =Str(vs_path).tailSlash(true)+"Common7\\IDE\\WDExpress.exe"; // try express exe 2012 for Windows Desktop
  51. devenv_com =false;
  52. devenv_express=true;
  53. if(!FExistSystem(devenv_path))
  54. {
  55. if(FExistSystem(Str(vs_path).tailSlash(true)+"Common7\\IDE\\VSWinExpress.exe"))Error("This edition of Visual Studio does not support creating Native Applications.\nPlease install Visual Studio for Windows Desktop."); // check for "Visual Studio 2012 for Windows 8"
  56. devenv_path.clear();
  57. }
  58. }
  59. }
  60. }
  61. }
  62. }
  63. if(devenv_path.is())devenv_version=EE::FileVersion(devenv_path);
  64. }
  65. }
  66. /******************************************************************************/
  67. void CodeEditor::setVSPath (C Str &path) { vs_path=path; options. vs_path.set(path, QUIET); validateDevEnv();}
  68. void CodeEditor::setNBPath (C Str &path) {netbeans_path=path; options.netbeans_path.set(path, QUIET); }
  69. void CodeEditor::setASPath (C Str &path) { android_sdk=path; options. android_sdk.set(path, QUIET); }
  70. void CodeEditor::setANPath (C Str &path) { android_ndk=path; options. android_ndk.set(path, QUIET); }
  71. void CodeEditor::setJDKPath (C Str &path) { jdk_path=path; options. jdk_path.set(path, QUIET); }
  72. void CodeEditor::setCertPath(C Str &path) { cert_file=path; options. cert_file.set(path, QUIET); }
  73. void CodeEditor::setCertPass(C Str &pass) { cert_pass=pass; options. cert_pass.set(pass, QUIET); }
  74. /******************************************************************************/
  75. static Bool ValidPackage(C Str &name)
  76. {
  77. if( !name.is())return false; // empty
  78. REPA(name)
  79. {
  80. Char c=name[i];
  81. if(!((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_' || c=='-' || c=='.'))return false;
  82. }
  83. return true;
  84. }
  85. static Bool ValidatePackage()
  86. {
  87. Str name=CE.cei().appPackage();
  88. if(!ValidPackage(name))
  89. {
  90. CE.cei().appInvalidProperty(name.is() ? "Invalid Package Name of the active application.\nThe name parts may contain uppercase or lowercase letters 'A' through 'Z', numbers, hyphens '-' and underscores '_' only." : "Package name of the active application was not specified.");
  91. return false;
  92. }
  93. return true;
  94. }
  95. static Str AndroidPackage(C Str &name) {return Replace(name, '-', '_');} // Android does not support '-' but supports '_'
  96. Bool CodeEditor::verifyVS()
  97. {
  98. if(build_exe_type==EXE_NEW && !ValidatePackage())return false;
  99. if(devenv_version.x<=0)validateDevEnv();
  100. Str message; if(CheckVisualStudio(devenv_version, &message, false))return true; // we can't check the minor version because EXE have it always set to 0 (last tested on VS 2017 regular+preview)
  101. options.activatePaths(); return Error(message);
  102. }
  103. Bool CodeEditor::verifyXcode()
  104. {
  105. if(build_exe_type==EXE_IOS && !ValidatePackage())return false;
  106. return true;
  107. }
  108. Bool CodeEditor::verifyAndroid()
  109. {
  110. Str error;
  111. if(! android_sdk .is())error.line(2)+="The path to Android SDK has not been specified.";else
  112. if(! adbPath().is())error.line(2)+="Can't find \"adb\" tool in Android SDK.";else
  113. if(!zipalignPath().is())error.line(2)+="Can't find \"zipalign\" tool in Android SDK.";
  114. if(! android_ndk .is() )error.line(2)+="The path to Android NDK has not been specified.";else
  115. if(Contains(android_ndk, ' '))error.line(2)+="Android NDK will not work if it's stored in a path with spaces.\nPlease move the Android NDK folder to a path without spaces and try again.";else
  116. if(!ndkBuildPath().is() )error.line(2)+="Can't find \"ndk-build\" tool in Android NDK.";
  117. if(error.is())
  118. {
  119. options.activatePaths();
  120. return Error(error);
  121. }
  122. if(!ValidatePackage())return false;
  123. Str key=cei().appLicenseKey();
  124. if(Contains(key, '"') || Contains(key, '\n') || Contains(key, '\\')){cei().appInvalidProperty("App License Key is invalid."); return false;}
  125. if(cei().appAndroidExpansion() && !key.is()){cei().appInvalidProperty("Auto-downloading Android Expansion Files requires setting Android License Key."); return false;}
  126. return true;
  127. }
  128. Bool CodeEditor::verifyLinuxMake () {return true;}
  129. Bool CodeEditor::verifyLinuxNetBeans()
  130. {
  131. if(!FExistSystem(Str(netbeans_path).tailSlash(true)+"bin/netbeans"))
  132. {
  133. options.activatePaths();
  134. return Error("The path to NetBeans has not been specified or is invalid.\nPlease select the path to a valid installation of NetBeans with C++ support.");
  135. }
  136. return true;
  137. }
  138. /******************************************************************************/
  139. static Str BinPath(Bool allow_relative=true)
  140. {
  141. Str path=GetPath(App.exe())+"/Bin/";
  142. if(CE.options.export_path_mode() && allow_relative)path=GetRelativePath(CE.build_path, path);
  143. return path;
  144. }
  145. static Str AppName(C Str &str, EXE_TYPE exe_type)
  146. {
  147. Str out;
  148. FREPA(str)
  149. {
  150. Char c=str[i];
  151. if(exe_type==EXE_APK)
  152. {
  153. if(c=='&')c='n'; // android will fail when uploading '&' APK to device
  154. }
  155. if(c==':')
  156. {
  157. if(str[i+1]==' ' && CharType(str[i-1])==CHART_CHAR)out+=' '; // check for "main: sub" -> "main - sub"
  158. out+='-';
  159. }else out+=c;
  160. }
  161. return CleanFileName(out);
  162. }
  163. Bool CodeEditor::getBuildPath(Str &build_path, Str &build_project_name, C Str *app_name)
  164. {
  165. build_project_name=AppName(app_name ? *app_name : cei().appName(), build_exe_type); if(!build_project_name.is())return false;
  166. build_path =NormalizePath(MakeFullPath(projects_build_path));
  167. build_path.tailSlash(true)+=build_project_name; FCreateDirs(build_path);
  168. build_path.tailSlash(true); // !! 'build_path' may get deleted in 'clean' !!
  169. return true;
  170. }
  171. /******************************************************************************/
  172. Bool CodeEditor::generateTXT(Memc<Message> &msgs)
  173. {
  174. Bool ok=true;
  175. Memc<Str> files;
  176. FREPA(sources)
  177. {
  178. Source &source=sources[i];
  179. if(source.active)
  180. {
  181. Str src_proj_path=cei().sourceProjPath(source.loc.id);
  182. if( src_proj_path.is())
  183. {
  184. if(files.binaryInclude(src_proj_path, ComparePathCI))
  185. {
  186. Str name=build_path+"Source\\"+src_proj_path+".txt";
  187. FCreateDirs(GetPath(name));
  188. source.saveTxt(name);
  189. }else
  190. {
  191. msgs.New().error(S+"Multiple files with the same name \""+src_proj_path+'"', &source, -1);
  192. ok=false;
  193. }
  194. }
  195. }
  196. }
  197. return ok;
  198. }
  199. /******************************************************************************/
  200. Bool CodeEditor::verifyBuildFiles(Memc<Message> &msgs)
  201. {
  202. Bool ok=true;
  203. build_files.clear();
  204. FREPA(sources)
  205. {
  206. Source &source=sources[i];
  207. if(source.active)
  208. {
  209. Str src_proj_path=cei().sourceProjPath(source.loc.id); if(!src_proj_path.is() && source.loc==Str(AutoSource))src_proj_path=AutoSource;
  210. BuildFile &bf=build_files.New().set(BuildFile::SOURCE, src_proj_path, source.loc);
  211. if(!bf.src_proj_path.is()){ok=false; msgs.New().error(S+"Unknown project path for file \""+source.loc.asText()+"\".");}
  212. // count the number of braces
  213. if(!source.header && !source.cpp)
  214. {
  215. Int level=0;
  216. FREPA(source.tokens)switch((*source.tokens[i])[0])
  217. {
  218. case '{': level++; break;
  219. case '}': if(!level--){ok=false; msgs.New().error("Invalid closing brace", source.tokens[i]); goto found_invalid;} break;
  220. }
  221. if(level){ok=false; msgs.New().error("The number or opening braces '{' does not match the number of closing braces '}'", &source, -1);}
  222. found_invalid:;
  223. }
  224. }
  225. if(source.used() && source.modified())if(!source.overwrite()){ok=false; msgs.New().error(S+"Can't overwrite changes to file \""+source.loc.asText()+"\".");} // overwrite changes in sources
  226. }
  227. REPA(build_files)if(build_files[i].mode==BuildFile::TEXT)if(Source *s=findSource(build_files[i].src_loc))if(s->modified())if(!s->overwrite()){ok=false; msgs.New().error(S+"Can't overwrite changes to file \""+s->loc.asText()+"\".");} // overwrite changes in text files
  228. return ok;
  229. }
  230. /******************************************************************************/
  231. static void CleanNameForLinux(Str &s)
  232. {
  233. if(Equal(s, "all" , true))s="All" ;else // compiling app named "all" will result in endless loop on Linux
  234. if(Equal(s, "build", true))s="Build";else // compiling app named "build" will result in endless loop on Linux
  235. if(Equal(s, "clean", true))s="Clean";else // compiling app named "clean" will result in endless loop on Linux
  236. if(Equal(s, "help" , true))s="Help" ;else // compiling app named "help" will result in endless loop on Linux
  237. if(Equal(s, "test" , true))s="Test" ; // compiling app named "test" will result in endless loop on Linux
  238. }
  239. static Str CleanNameForMakefile(C Str &s)
  240. {
  241. Str o; o.reserve(s.length());
  242. FREPA(s)
  243. {
  244. Char c=s[i];
  245. switch(c) // these characters fail even with escaping
  246. {
  247. case '(':
  248. case ')':
  249. case '\'':
  250. case '=':
  251. case '`':
  252. case ';': c='_'; break;
  253. case '&': c='n'; break;
  254. }
  255. o+=c;
  256. }
  257. CleanNameForLinux(o);
  258. return o;
  259. }
  260. static Str CleanNameForNetBeans(C Str &s)
  261. {
  262. Str o; o.reserve(s.length());
  263. FREPA(s)
  264. {
  265. Char c=s[i];
  266. switch(c) // these characters are not accepted by NetBeans
  267. {
  268. case '!':
  269. case '@':
  270. case '#':
  271. case '%':
  272. case '^':
  273. case '(':
  274. case ')':
  275. case '<':
  276. case '>':
  277. case '[':
  278. case ']':
  279. case '=':
  280. case '+':
  281. case ',':
  282. case ';':
  283. case '"':
  284. case '\'':
  285. case '`':
  286. case '~':
  287. case ' ': c='_'; break;
  288. case '&': c='n'; break;
  289. }
  290. o+=c;
  291. }
  292. CleanNameForLinux(o);
  293. return o;
  294. }
  295. Bool CodeEditor::verifyBuildPath()
  296. {
  297. if(!getBuildPath(build_path, build_project_name))return false;
  298. build_source=build_path+"Source\\"; FCreateDir(build_source);
  299. switch(build_exe_type)
  300. {
  301. case EXE_EXE : build_exe=build_path+build_project_name+".exe"; break;
  302. case EXE_DLL : build_exe=build_path+build_project_name+".dll"; break;
  303. case EXE_LIB : build_exe=build_path+build_project_name+".lib"; break;
  304. case EXE_NEW : build_exe=build_path+build_project_name+".exe"; break;
  305. case EXE_MAC : build_exe=build_path+build_project_name+".app"; break;
  306. case EXE_IOS : build_exe=build_path+build_project_name+".app"; break;
  307. case EXE_APK : build_exe=build_path+"Android/bin/"+build_project_name; break;
  308. case EXE_LINUX: build_exe=build_path+CleanNameForMakefile(build_project_name); break;
  309. case EXE_WEB : build_exe=build_path+"Emscripten/"+(build_debug ? "Debug DX9/" : "Release DX9/")+build_project_name+".html"; break; // warning: this must match codes below if(build_exe_type==EXE_WEB)config+=" DX9";
  310. }
  311. return true;
  312. }
  313. Bool CodeEditor::adjustBuildFiles()
  314. {
  315. // fix duplicate base names, by converting them to "xxxUNIQUE_NAMEi", where 'xxx'=original base name, 'i'=index of a duplicate
  316. {
  317. build_files.sort(Compare); // in order for files to always have the same names, we need to process them in the same order, so sort them
  318. REPA(build_files)build_files[i].ext_not=GetExtNot(build_files[i].src_proj_path);
  319. REPA(build_files)
  320. {
  321. BuildFile &bf=build_files[i]; if(bf.includeInProj())
  322. {
  323. Int same=0;
  324. Str base=GetBase(bf.ext_not);
  325. REPD(j, i)
  326. {
  327. BuildFile &bf2=build_files[j];
  328. if(bf2.includeInProj())
  329. if(GetBase(bf2.ext_not)==base)bf2.ext_not+=S+UNIQUE_NAME+(same++);
  330. }
  331. }
  332. }
  333. }
  334. // set target file names
  335. REPAO(build_files).adjustPaths(build_path);
  336. return true;
  337. }
  338. /******************************************************************************/
  339. static Int CompareByParent(Symbol* C &a, Symbol* C &b)
  340. {
  341. if(Int c=ComparePath(a->parent ? a->parent->full_name : S, b->parent ? b->parent->full_name : S))return c;
  342. if(a->modifiers & b->modifiers & Symbol::MODIF_NAMELESS)return Compare(a->nameless_index, b->nameless_index); // if both are nameless, then use the nameless_index
  343. return CompareCS (*a, *b);
  344. }
  345. struct CodeFile
  346. {
  347. Str dest;
  348. FileText f;
  349. LineMap lm;
  350. void operator++(int)
  351. {
  352. f.endLine();
  353. lm++;
  354. }
  355. void operator+=(C Str &line)
  356. {
  357. f.putLine(line);
  358. lm++;
  359. }
  360. void operator+=(C Memc<CodeLine> &lines)
  361. {
  362. Write(f, lines);
  363. lm.add(SourceLoc(), lines);
  364. }
  365. void add(C SourceLoc &src, C Memc<CodeLine> &lines)
  366. {
  367. Write(f, lines);
  368. lm.add(src, lines);
  369. }
  370. CodeFile(C Str &dest) {T.dest=dest; f.writeMem(UTF_8);} // some compilers don't support UTF-16, also UTF-8 will use less space, and faster to write
  371. ~CodeFile()
  372. {
  373. if(dest.is())
  374. if(OverwriteOnChangeLoud(f, dest))
  375. if(LineMap *lm=CE.build_line_maps.get(dest))Swap(*lm, T.lm);
  376. }
  377. };
  378. /******************************************************************************/
  379. void CodeEditor::generateHeadersH(Memc<Symbol*> &sorted_classes, EXPORT_MODE export_mode, Bool gcc)
  380. {
  381. Memc<CodeLine> lines;
  382. Memc<Symbol* > symbols;
  383. Symbol *Namespace=null;
  384. CodeFile f(build_source+UNIQUE_NAME+UNIQUE_NAME+"headers.h");
  385. f+=SEP_LINE;
  386. if(1 // for unknown reason this is needed also on Linux, so for simplicity, just enable it always
  387. || export_mode==EXPORT_ANDROID){f+="#pragma once"; f++;} // on Android all CPP files are packed into one for faster compilation, so we must make sure that this header can work if included multiple times
  388. // data types
  389. f+="#define bool Bool // boolean value (8-bit)";
  390. f++;
  391. f+="#define char8 Char8 // 8-bit character";
  392. f+="#define char Char // 16-bit character";
  393. f++;
  394. f+="#define sbyte I8 // 8-bit signed integer";
  395. f+="#define byte U8 // 8-bit unsigned integer";
  396. f+="#define short I16 // 16-bit signed integer";
  397. f+="#define ushort U16 // 16-bit unsigned integer";
  398. f+="#define int I32 // 32-bit signed integer";
  399. f+="#define uint U32 // 32-bit unsigned integer";
  400. f+="#define long I64 // 64-bit signed integer";
  401. f+="#define ulong U64 // 64-bit unsigned integer";
  402. f++;
  403. f+="#define flt Flt // 32-bit floating point";
  404. f+="#define dbl Dbl // 64-bit floating point";
  405. f++;
  406. f+="#define ptr Ptr // universal pointer";
  407. f+="#define cptr CPtr // universal pointer to const data";
  408. f++;
  409. f+="#define cchar8 CChar8 // const Char8";
  410. f+="#define cchar CChar // const Char16";
  411. f++;
  412. f+="#define intptr IntPtr // signed integer capable of storing full memory address";
  413. f+="#define uintptr UIntPtr // unsigned integer capable of storing full memory address";
  414. f++;
  415. f+="#define class struct // Esenthel Script \"class\" is a C++ \"struct\"";
  416. // forward headers
  417. f+=SEP_LINE;
  418. // custom macros
  419. f+="// DEFINES";
  420. f+=S+"#define STEAM "+cei().appPublishSteamDll ();
  421. f+=S+"#define OPEN_VR "+cei().appPublishOpenVRDll();
  422. f+=SEP_LINE;
  423. // write class forward declarations
  424. {
  425. Memc<Symbol*> &classes=symbols; classes.clear();
  426. FREPA(Symbols)
  427. {
  428. Symbol &symbol =Symbols.lockedData(i);
  429. Bool processed=false;
  430. if( symbol.type==Symbol::KEYWORD )processed=true; // keywords are already processed
  431. if(!symbol.source || !symbol.source->active)processed=true; // symbols which aren't in current build files (for example EE headers) set as processed
  432. FlagSet(symbol.helper, Symbol::HELPER_PROCESSED|Symbol::HELPER_PROCESSED_FULL, processed);
  433. if(!processed)
  434. if(symbol.type==Symbol::CLASS && symbol.valid && symbol.isGlobal() && !(symbol.modifiers&Symbol::MODIF_NAMELESS)) // process only global named classes
  435. classes.add(&symbol);
  436. }
  437. classes.sort(CompareByParent);
  438. FREPA(classes)
  439. {
  440. Symbol &Class =*classes[i]; Class.helper|=Symbol::HELPER_PROCESSED;
  441. Source &source=*Class.source;
  442. Int start = source.getSymbolStart(Class.token_index);
  443. if(InRange(start, source.tokens))
  444. {
  445. AdjustNameSymbol(lines, Namespace, Class.Namespace());
  446. Int line_start=lines.elms(); source.write(lines, start, Class.token_index); if(lines.elms())lines.last().append(';', TOKEN_OPERATOR);
  447. // expand templates
  448. source.expandTemplate(lines, start , line_start); // only 'start' token needs to be checked
  449. REPA(Class.templates)source.expandTypename(lines, *Class.templates[i], line_start);
  450. }
  451. }
  452. if(lines.elms())
  453. {
  454. AdjustNameSymbol(lines, Namespace, null);
  455. f+=SEP_LINE;
  456. f+="// CLASSES";
  457. f+=SEP_LINE;
  458. f+=lines; lines.clear();
  459. }
  460. }
  461. // TODO: write caches (this is needed so cache typedefs 'CacheElmPtr' will work, more complex solution would be to make a better symbol ordering / dependency checker)
  462. FREPA(Symbols)
  463. {
  464. Symbol &symbol=Symbols.lockedData(i);
  465. if(symbol.isVar() && symbol.valid && symbol.isGlobal() && symbol.source && symbol.source->isFirstVar(symbol) && !(symbol.helper&Symbol::HELPER_PROCESSED) && symbol.value && Equal(symbol.value->full_name, "EE\\Cache", true))
  466. {
  467. symbol.helper|=Symbol::HELPER_PROCESSED;
  468. // adjust namespaces to typedef namespace
  469. AdjustNameSymbol(lines, Namespace, symbol.Namespace());
  470. symbol.source->writeSymbolDecl(lines, symbol, gcc);
  471. symbol.source->removeDefVal (lines, symbol);
  472. }
  473. }
  474. if(lines.elms())
  475. {
  476. AdjustNameSymbol(lines, Namespace, null);
  477. f+=SEP_LINE;
  478. f+="// CACHES";
  479. f+=SEP_LINE;
  480. f+=lines; lines.clear();
  481. }
  482. // enums
  483. {
  484. Memc<Symbol*> namespaces;
  485. Memc<Symbol*> &enums=symbols; enums.clear();
  486. FREPA(Symbols)
  487. {
  488. Symbol &symbol=Symbols.lockedData(i);
  489. if(symbol.type==Symbol::ENUM && symbol.valid && symbol.isGlobal()) // global enums
  490. if(Source *source=symbol.source)if(source->active) // from active sources only
  491. enums.add(&symbol);
  492. }
  493. enums.sort(CompareByParent);
  494. f+=SEP_LINE;
  495. f+="// ENUMS";
  496. f+=SEP_LINE;
  497. FREPA(enums)
  498. {
  499. Symbol &symbol=*enums[i];
  500. Source &source=*symbol.source;
  501. symbol.helper|=Symbol::HELPER_PROCESSED|Symbol::HELPER_PROCESSED_FULL;
  502. Int start=source.getSymbolStart(symbol.token_index),
  503. end =source.getBodyEnd (start);
  504. if(InRange(start, source.tokens))
  505. {
  506. namespaces.clear(); for(Symbol *name=symbol.Parent(); name && name->type==Symbol::NAMESPACE; name=name->Parent())namespaces.New()=name;
  507. // start namespaces
  508. REPA(namespaces)lines.New().append("namespace", TOKEN_KEYWORD).append(' ', TOKEN_NONE).append(*namespaces[i], TOKEN_CODE).append('{', TOKEN_OPERATOR);
  509. if(namespaces.elms())lines.New().append(SEP_LINE, TOKEN_COMMENT);
  510. // write enum
  511. source.write(lines, start, end);
  512. // insert ';' at the end of struct/class/enum declarations
  513. // convert '.' to "->" or "::" when needed
  514. for(Int i=start; i<=end; i++)
  515. {
  516. Token &c=*source.tokens[i]; source.adjustToken(lines, i, gcc);
  517. if(c=='}' && c.parent)if(c.parent->type==Symbol::CLASS || c.parent->type==Symbol::ENUM) // put ';'
  518. if(!InRange(i+1, source.tokens) || (*source.tokens[i+1])!=';') // only if it's not already there (this caused issues when the struct/class/enum was defined by a macro, like UNION)
  519. {
  520. Int col; if(CodeLine *cl=FindLineCol(lines, c.pos(), col))cl->insert(col+1, ';', TOKEN_OPERATOR);
  521. }
  522. }
  523. // clean 'TOKEN_REMOVE'
  524. Clean(lines);
  525. // remove starting spaces
  526. REP(source.tokens[start]->col)
  527. {
  528. REPA (lines)if(lines[i].cols.elms() && ValidType(lines[i].cols[0].type))goto finished_enum_start_spaces;
  529. REPAO(lines).remove(0);
  530. }
  531. finished_enum_start_spaces:;
  532. // close namespaces
  533. lines.New().append(SEP_LINE, TOKEN_COMMENT);
  534. REPA(namespaces)lines.New().append('}', TOKEN_OPERATOR).append(' ', TOKEN_NONE).append(S+"// namespace "+*namespaces[i], TOKEN_COMMENT);
  535. if(namespaces.elms())lines.New().append(SEP_LINE, TOKEN_COMMENT);
  536. // write
  537. f+=lines; lines.clear();
  538. }
  539. }
  540. }
  541. // constants
  542. {
  543. Symbol *Namespace=null;
  544. FREPA(Symbols)
  545. {
  546. Symbol &symbol=Symbols.lockedData(i);
  547. if(Source *source=symbol.source)
  548. if(source->active // only symbols from current build files
  549. && symbol.valid // only valid symbols
  550. && symbol.isGlobal() // only global vars
  551. && symbol.isVar() && source->isFirstVar(symbol) && symbol.constDefineInHeader()) // process only first variables on the list
  552. {
  553. Int start=source->getSymbolStart(symbol.token_index),
  554. end =source->getListEnd (symbol.token_index);
  555. // adjust namespaces to variable namespace
  556. AdjustNameSymbol(lines, Namespace, symbol.Namespace());
  557. // write full variable list until first ';' encountered
  558. Int var_start=lines.elms();
  559. source->write(lines, start, end);
  560. // TODO: make more nicer to look
  561. // adjust token
  562. for(Int i=start; i<=end; i++)source->adjustToken(lines, i, false);
  563. }
  564. }
  565. // adjust namespaces to global namespace
  566. AdjustNameSymbol(lines, Namespace, null);
  567. // clean & parse
  568. Clean(lines);
  569. Parse(lines);
  570. // write
  571. if(lines.elms())
  572. {
  573. f+="// CONSTANTS";
  574. f+=SEP_LINE;
  575. f+=lines; lines.clear();
  576. }
  577. }
  578. // write typedefs
  579. {
  580. Memc<Symbol*> &typedefs=symbols; typedefs.clear();
  581. FREPA(Symbols)
  582. {
  583. Symbol &symbol=Symbols.lockedData(i);
  584. if(!(symbol.helper&Symbol::HELPER_PROCESSED))
  585. if(symbol.type==Symbol::TYPEDEF && symbol.source && symbol.valid && symbol.value && symbol.value->valid && symbol.isGlobal()) // process only global typedefs
  586. typedefs.add(&symbol);
  587. }
  588. for(Memc<Symbol*> to_process; ; )
  589. {
  590. REPA(typedefs)
  591. {
  592. Symbol &Typedef=*typedefs[i];
  593. if(Typedef.value->helper&Symbol::HELPER_PROCESSED){to_process.add(&Typedef); typedefs.remove(i);} // write the typedefs only if their target was already processed (this skips nameless classes which were not processed)
  594. }
  595. if(to_process.elms())
  596. {
  597. to_process.sort(CompareByParent);
  598. FREPA(to_process)
  599. {
  600. Symbol &Typedef=*to_process[i]; Typedef.helper|=Symbol::HELPER_PROCESSED|Symbol::HELPER_PROCESSED_FULL;
  601. Source &source =*Typedef.source;
  602. if( source.isFirstVar(Typedef)) // process only first typedef
  603. {
  604. // adjust namespaces to typedef namespace
  605. AdjustNameSymbol(lines, Namespace, Typedef.Namespace());
  606. source.writeSymbolDecl(lines, Typedef, gcc);
  607. }
  608. }
  609. to_process.clear();
  610. }else break;
  611. }
  612. if(lines.elms())
  613. {
  614. AdjustNameSymbol(lines, Namespace, null);
  615. f+=SEP_LINE;
  616. f+="// TYPEDEFS";
  617. f+=SEP_LINE;
  618. f+=lines; lines.clear();
  619. }
  620. }
  621. f+=SEP_LINE;
  622. // classes
  623. {
  624. f+=SEP_LINE;
  625. f+="// CLASSES";
  626. f+=SEP_LINE;
  627. FREPA(sorted_classes)
  628. if(Source *source=sorted_classes[i]->source)
  629. {
  630. if(source->writeClass(lines, *sorted_classes[i], gcc))
  631. {
  632. #if MERGE_HEADERS
  633. f.add(source->loc, lines);
  634. #else
  635. Str rel=S+UNIQUE_NAME+sorted_classes[i]->fileName()+".h";
  636. CodeFile f2(build_source+rel); f2.add(source->loc, lines); f+=S+"#include \""+UnixPath(rel)+"\""; // Unix compilers don't support '\' paths
  637. #endif
  638. }
  639. lines.clear();
  640. }
  641. }
  642. // cpp headers
  643. f+=SEP_LINE;
  644. f+="// CPP";
  645. f+=SEP_LINE;
  646. FREPA(build_files)
  647. {
  648. BuildFile &bf=build_files[i];
  649. if(bf.mode==BuildFile::SOURCE)
  650. if(Source *source=findSource(bf.src_loc))
  651. {
  652. if(source->writeVarFuncs(lines, gcc))
  653. {
  654. #if MERGE_HEADERS
  655. f.add(source->loc, lines);
  656. #else
  657. Str full=GetExtNot(bf.dest_file_path)+".h";
  658. CodeFile f2(full); f2.add(source->loc, lines); f+=S+"#include \""+UnixPath(SkipStartPath(full, build_source))+"\""; // Unix compilers don't support '\' paths
  659. #endif
  660. }
  661. lines.clear();
  662. }
  663. }
  664. // inline/template classes and functions
  665. f+=SEP_LINE;
  666. f+="// INLINE, TEMPLATES";
  667. f+=SEP_LINE;
  668. FREPA(build_files)
  669. {
  670. BuildFile &bf=build_files[i];
  671. if(bf.mode==BuildFile::SOURCE)
  672. if(Source *source=findSource(bf.src_loc))
  673. {
  674. if(source->writeInline(lines, gcc))
  675. {
  676. #if MERGE_HEADERS
  677. f.add(source->loc, lines);
  678. #else
  679. Str full=GetExtNot(bf.dest_file_path)+".inline.h";
  680. CodeFile f2(full); f2.add(source->loc, lines); f+=S+"#include \""+UnixPath(SkipStartPath(full, build_source))+"\""; // Unix compilers don't support '\' paths
  681. #endif
  682. }
  683. lines.clear();
  684. }
  685. }
  686. f+=SEP_LINE;
  687. }
  688. /******************************************************************************/
  689. static Int CompareBySource(Symbol* C &a, Symbol* C &b)
  690. {
  691. if(Int c=SourceLoc::Compare(a->source->loc, b->source->loc))return c;
  692. return Compare(a->token_index, b->token_index);
  693. }
  694. static void ListHeaders(FileText &ft, Memc<Str> &headers, C Str &condition)
  695. {
  696. if(headers.elms())
  697. {
  698. ft.putLine(S+"#if "+condition);
  699. ft.depth++;
  700. FREPA(headers)ft.putLine(S+"#include \""+headers[i]+'"'); // use "header" instead of <header> because Mac fails to compile user headers with <> but succeeds both user+system with ""
  701. ft.depth--;
  702. ft.putLine("#endif");
  703. }
  704. }
  705. Bool CodeEditor::generateCPPH(Memc<Symbol*> &sorted_classes, EXPORT_MODE export_mode)
  706. {
  707. // setup nameless indexes needed for file names
  708. Memc<Symbol*> nameless;
  709. FREPA(Symbols){Symbol &symbol=Symbols.lockedData(i); if((symbol.modifiers&Symbol::MODIF_NAMELESS) && symbol.source && symbol.source->active && symbol.valid && symbol.isGlobal())nameless.add(&symbol);}
  710. nameless.sort(CompareBySource); // sort by source, so that we can have constant 'nameless_index' for the same symbols (so when sources are reloaded, the same symbols, have the same indexes as before)
  711. REPAO(nameless)->nameless_index=i;
  712. // detect default ctors
  713. FREPA(build_files)
  714. {
  715. BuildFile &bf=build_files[i];
  716. if(bf.mode==BuildFile::SOURCE)
  717. if(Source *source=findSource(bf.src_loc))
  718. source->detectDefaultCtors(); // this probably requires to be processed per-source
  719. }
  720. // count number of source files
  721. Int sources=0; REPA(build_files)if(build_files[i].mode==BuildFile::SOURCE)sources++;
  722. Bool build_headers_in_cpp=(sources<=8),
  723. gcc =(export_mode==EXPORT_ANDROID || export_mode==EXPORT_XCODE || export_mode==EXPORT_LINUX_MAKE || export_mode==EXPORT_LINUX_NETBEANS || config_exe==EXE_WEB);
  724. // make cpp files
  725. FREPA(build_files)
  726. {
  727. BuildFile &bf=build_files[i];
  728. if(bf.mode==BuildFile::SOURCE)
  729. {
  730. /*FileInfo src, dest; if(src.get(bf.)) can't use this because 'build_headers_in_cpp' sometimes generates different CPP files
  731. {
  732. if(dest.get(bf.dest_file_path))
  733. if(!Compare(src.modify_time_utc, dest.modify_time_utc, 1))continue; // if 'src' date is the same as 'dest' date then assume they're the same
  734. }*/
  735. if(Source *source=findSource(bf.src_loc))
  736. source->makeCPP(build_source, bf.dest_file_path, gcc, build_headers_in_cpp);
  737. }
  738. }
  739. // make headers
  740. generateHeadersH(sorted_classes, export_mode, gcc);
  741. // generate precompiled header
  742. {
  743. FileText ft;
  744. // stdafx.cpp
  745. ft.write(build_path+"stdafx.cpp", ANSI); ft.putLine("#include \"stdafx.h\""); ft.del(); DateTime dt; dt.zero(); dt.year=2010; dt.month=1; dt.day=1; FTimeUTC(build_path+"stdafx.cpp", dt);
  746. // stdafx.h
  747. ft.writeMem();
  748. ft.putLine("#pragma once"); // on Apple this header gets included automatically because of project PCH settings, and because of manual "#include "stdafx.h"" needed on Windows, it would get included multiple times
  749. // 3rd party headers (first)
  750. Memc<Str> win_headers=GetFiles(cei().appHeadersWindows()),
  751. mac_headers=GetFiles(cei().appHeadersMac ()),
  752. linux_headers=GetFiles(cei().appHeadersLinux ()),
  753. android_headers=GetFiles(cei().appHeadersAndroid()),
  754. ios_headers=GetFiles(cei().appHeadersiOS ());
  755. /*REPA(headers)
  756. {
  757. Str &header=headers[i]; if(Ends(header, "?optional"))
  758. {
  759. header.removeLast(9); if(!FExistSystem(header))headers.remove(i, true);
  760. }
  761. }*/
  762. Str bin_path=BinPath();
  763. if(win_headers.elms() || mac_headers.elms() || linux_headers.elms() || android_headers.elms() || ios_headers.elms())
  764. {
  765. ft.putLine(S+"#include \""+UnixPath(bin_path+"EsenthelEngine\\_\\System\\begin.h")+'"');
  766. ListHeaders(ft, win_headers, "defined _WIN32");
  767. if(mac_headers.elms() || ios_headers.elms())
  768. {
  769. ft.putLine("#ifdef __APPLE__");
  770. ft.depth++;
  771. ft.putLine("#include <TargetConditionals.h>");
  772. ListHeaders(ft, mac_headers, "!TARGET_OS_IPHONE");
  773. ListHeaders(ft, ios_headers, "TARGET_OS_IPHONE");
  774. ft.depth--;
  775. ft.putLine("#endif");
  776. }
  777. ListHeaders(ft, linux_headers, "defined __linux__ && !defined ANDROID // Android also has '__linux__' defined");
  778. ListHeaders(ft, android_headers, "defined ANDROID");
  779. ft.putLine(S+"#include \""+UnixPath(bin_path+"EsenthelEngine\\_\\System\\end.h")+'"');
  780. }
  781. // EE headers (second)
  782. ft.putLine(S+"#include \""+UnixPath(bin_path+"EsenthelEngine\\EsenthelEngine.h")+'"');
  783. if(!build_headers_in_cpp)ft.putLine(UnixPath(S+"#include \"Source\\"+UNIQUE_NAME+UNIQUE_NAME+"headers.h\""));
  784. OverwriteOnChangeLoud(ft, build_path+"stdafx.h");
  785. }
  786. return true;
  787. }
  788. /******************************************************************************/
  789. struct BuildFileElm
  790. {
  791. struct Name
  792. {
  793. Bool file;
  794. Str name;
  795. Name(Bool file, C Str &name) {T.file=file; T.name=name;}
  796. };
  797. static Int Compare(C Node<BuildFileElm> &elm, C Name &name)
  798. {
  799. if(Int c=::Compare(elm.file() , name.file))return c; // folders first
  800. return CompareCI(elm.base_name, name.name);
  801. }
  802. Str base_name, full_name;
  803. CodeEditor::BuildFile *bf;
  804. U64 id;
  805. Bool file()C {return bf!=null;} // if elm is file or folder
  806. void set(C Str &base, C Str &full) {T.base_name=base; T.full_name=full;}
  807. BuildFileElm() {bf=null; id=0;}
  808. };
  809. static void BuildTree(Node<BuildFileElm> &node, CodeEditor::BuildFile &bf, Str path)
  810. {
  811. if(bf.includeInProj())
  812. {
  813. Str start=GetStart(path); path=GetStartNot(path);
  814. Int index; if(!node.children.binarySearch(BuildFileElm::Name(!path.is(), start), index, BuildFileElm::Compare))node.children.NewAt(index).set(start, Str(node.full_name).tailSlash(true)+start); // keep sorted
  815. Node<BuildFileElm> &elm=node.children[index];
  816. if(path.is())BuildTree(elm, bf, path);else elm.bf=&bf;
  817. }
  818. }
  819. static void StoreTree9(Node<BuildFileElm> &bfe, XmlNode &node)
  820. {
  821. FREPA(bfe.children)
  822. {
  823. Node<BuildFileElm> &elm=bfe.children[i];
  824. if(elm.bf) // file
  825. {
  826. XmlNode &file=node.nodes.New().setName("File");
  827. if(elm.bf->mode==CodeEditor::BuildFile::SOURCE)
  828. {
  829. file.params.New().set("RelativePath", S+".\\"+elm.bf->dest_proj_path);
  830. }else
  831. {
  832. file.params.New().set("RelativePath", elm.bf->dest_proj_path);
  833. }
  834. }else // folder
  835. {
  836. XmlNode &filter=node.nodes.New().setName("Filter");
  837. filter.params.New().set("Name", elm.base_name);
  838. StoreTree9(elm, filter);
  839. }
  840. }
  841. }
  842. static void StoreTree10(Node<BuildFileElm> &bfe, XmlNode &node)
  843. {
  844. FREPA(bfe.children)
  845. {
  846. Node<BuildFileElm> &elm=bfe.children[i];
  847. if(elm.bf) // file
  848. {
  849. XmlNode &file =node.nodes.New().setName((elm.bf->mode==CodeEditor::BuildFile::SOURCE) ? "ClCompile" : (elm.bf->mode==CodeEditor::BuildFile::LIB) ? "Library" : "None"); file.params.New().set("Include", elm.bf->dest_proj_path);
  850. XmlNode &filter=file.nodes.New().setName("Filter"); filter.data.New()=GetPath(elm.full_name);
  851. }else // folder
  852. {
  853. XmlNode &filter=node.nodes.New().setName("Filter");
  854. filter.params.New().set("Include", elm.full_name);
  855. StoreTree10(elm, node);
  856. }
  857. }
  858. }
  859. /******************************************************************************/
  860. static void SetFile(FileText &f, C Str &s, ENCODING encoding=UTF_8)
  861. {
  862. f.writeMem(encoding).putText(s);
  863. }
  864. /******************************************************************************/
  865. static Bool SafeOverwriteLoud(File &f, C Str &dest, C DateTime *modify_time_utc=null)
  866. {
  867. if(!SafeOverwrite(f, dest, modify_time_utc))return ErrorWrite(dest);
  868. return true;
  869. }
  870. static Bool CopyFile(C Str &src, C Str &dest)
  871. {
  872. if(!FCopy(src, dest, FILE_OVERWRITE_DIFFERENT))return ErrorCopy(src, dest);
  873. return true;
  874. }
  875. static Bool CopyFile(C Str &src, C Str &dest, Bool &changed)
  876. {
  877. FileInfoSystem fi(dest); Bool ret=CopyFile(src, dest);
  878. changed=(fi!=FileInfoSystem(dest)); return ret;
  879. }
  880. /******************************************************************************/
  881. static void Add(Memb<PakFileData> &files, Pak &pak, C PakFile &pf)
  882. {
  883. PakFileData &pfd=files.New();
  884. pfd.name =pak.fullName(pf);
  885. pfd.xxHash64_32 =pf.data_xxHash64_32;
  886. pfd.modify_time_utc =pf.modify_time_utc;
  887. pfd.type =pf.type();
  888. pfd.compressed =pf.compression;
  889. pfd.decompressed_size=pf.data_size;
  890. if(pf.data_size)pfd.data.set(pf, pak);
  891. }
  892. static void AddFile(Memb<PakFileData> &files, Pak &pak, C PakFile &pf)
  893. {
  894. if(!FlagTest(pf.flag, PF_STD_DIR))Add(files, pak, pf); // skip folders
  895. }
  896. static void Add (Memb<PakFileData> &files, C PaksFile *pf) {if(pf)Add (files, *pf->pak, *pf->file);}
  897. static void AddFile (Memb<PakFileData> &files, C PaksFile *pf) {if(pf)AddFile(files, *pf->pak, *pf->file);}
  898. static void Add (Memb<PakFileData> &files, Pak &pak, C PakFile *pf) {if(pf)Add (files, pak, *pf );}
  899. static void AddChildren(Memb<PakFileData> &files, C PaksFile *pf) {if(pf)FREP(pf->children_num)AddFile(files, &Paks.file(pf->children_offset+i));}
  900. static void AddChildren(Memb<PakFileData> &files, Pak &pak, C PakFile *pf) {if(pf)FREP(pf->children_num)AddFile(files, pak, pak .file(pf->children_offset+i));}
  901. static Bool CreateEngineEmbedPak(C Str &name, Bool *changed=null)
  902. {
  903. if(changed)*changed=false;
  904. Memb<PakFileData> files;
  905. Bool full=(CE.cei().appEmbedEngineData()>1);
  906. if(!full)
  907. {
  908. // add folders to preserve modification times
  909. Add (files, Paks.find("Shader"));
  910. Add (files, Paks.find("Shader/2"));
  911. Add (files, Paks.find("Shader/3"));
  912. Add (files, Paks.find("Shader/4"));
  913. Add (files, Paks.find("Shader/GL"));
  914. AddFile(files, Paks.find("Shader/2/Early Z"));
  915. AddFile(files, Paks.find("Shader/2/Main"));
  916. AddFile(files, Paks.find("Shader/2/Position"));
  917. AddFile(files, Paks.find("Shader/3/Early Z"));
  918. AddFile(files, Paks.find("Shader/3/Main"));
  919. AddFile(files, Paks.find("Shader/3/Position"));
  920. AddFile(files, Paks.find("Shader/4/Early Z"));
  921. AddFile(files, Paks.find("Shader/4/Main"));
  922. AddFile(files, Paks.find("Shader/4/Position"));
  923. AddFile(files, Paks.find("Shader/GL/Early Z"));
  924. AddFile(files, Paks.find("Shader/GL/Main"));
  925. AddFile(files, Paks.find("Shader/GL/Position"));
  926. }
  927. Bool found=false;
  928. {
  929. SyncLocker locker(Paks._lock);
  930. FREPA(Paks._paks) // iterate all loaded paks starting from those loaded first
  931. {
  932. PakSet::Src &src=Paks._paks[i];
  933. if(GetBase(src.name)=="Engine.pak")
  934. {
  935. if(full)
  936. {
  937. FREPA(src)Add(files, src, src.file(i)); // add all files
  938. }else
  939. {
  940. Add (files, src, src.find("Gui")); // add folder to preserve modification times
  941. AddChildren(files, src, src.find("Gui"));
  942. FREP(src.rootFiles())AddFile(files, src, src.file(i)); // add all root files (gui files)
  943. }
  944. found=true;
  945. break;
  946. }
  947. }
  948. }
  949. if(!found)return Error("Can't find \"Engine.pak\"");
  950. Cipher *cipher=CE.cei().appEmbedCipher();
  951. if(FileInfoSystem(name).modify_time_utc>CE.cei().appEmbedSettingsTime() && PakEqual(files, name, cipher))return true; // check if we have newer settings (for example compression) in that case regenerate the PAK
  952. if(changed)*changed=true;
  953. if(!PakCreate(files, name, 0, cipher, CE.cei().appEmbedCompress(), CE.cei().appEmbedCompressLevel()))return Error("Can't create Embedded Engine Pak");
  954. return true;
  955. }
  956. static Bool CreateAppPak(C Str &name, Bool &exists, Bool *changed=null)
  957. {
  958. Bool ok=false;
  959. exists=false;
  960. if(changed)*changed=false;
  961. Memb<PakFileData> files; CE.cei().appSpecificFiles(files);
  962. if(files.elms())
  963. {
  964. exists=true;
  965. Cipher *cipher=CE.cei().appEmbedCipher();
  966. if(FileInfoSystem(name).modify_time_utc>CE.cei().appEmbedSettingsTime() && PakEqual(files, name, cipher))ok=true; // check if we have newer settings (for example compression) in that case regenerate the PAK
  967. else {if(changed)*changed=true; ok=PakCreate(files, name, 0, cipher, CE.cei().appEmbedCompress(), CE.cei().appEmbedCompressLevel());}
  968. }else
  969. {
  970. ok=true;
  971. Bool c=FDelFile(name); if(changed)*changed=c;
  972. }
  973. if(!ok)Error("Can't create app pak");
  974. return ok;
  975. }
  976. static void Optimize(Image &image)
  977. {
  978. Vec4 min; if(image.stats(&min))if(min.w>=1-2.5f/255)image.copyTry(image, -1, -1, -1, IMAGE_R8G8B8); // if image has no alpha, then remove it, because it will reduce PNG size
  979. if(ImageTI[image.type()].a) // if image has alpha, then zero pixels without alpha to further improve compression
  980. REPD(y, image.h())
  981. REPD(x, image.w())
  982. {
  983. Color c=image.color(x, y);
  984. if(!c.a)image.color(x, y, TRANSPARENT);
  985. }
  986. }
  987. static Bool GetIcon(Image &image, DateTime &modify_time_utc)
  988. {
  989. image.del(); modify_time_utc.zero();
  990. if(C ImagePtr &app_icon=CE.cei().appIcon()){app_icon->copyTry(image, -1, -1, 1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1, FILTER_BEST, true, true); modify_time_utc=FileInfo(app_icon.name()).modify_time_utc;}
  991. if(!image.is()){image.ImportTry("Code/Icon.ico", -1, IMAGE_SOFT, 1); modify_time_utc=FileInfo("Code/Icon.ico").modify_time_utc;}
  992. if( image.is())
  993. {
  994. if(!modify_time_utc.valid())modify_time_utc.getUTC();
  995. Optimize(image);
  996. return true;
  997. }
  998. return false;
  999. }
  1000. static void GetImages(Image &portrait, DateTime &portrait_time, Image &landscape, DateTime &landscape_time)
  1001. {
  1002. portrait_time.zero(); if(C ImagePtr &app_portrait =CE.cei().appImagePortrait ()){app_portrait ->copyTry(portrait , -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1, FILTER_BEST, true, true); portrait_time=FileInfo(app_portrait .name()).modify_time_utc; Optimize( portrait);} if(! portrait_time.valid()) portrait_time.getUTC();
  1003. landscape_time.zero(); if(C ImagePtr &app_landscape=CE.cei().appImageLandscape()){app_landscape->copyTry(landscape, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1, FILTER_BEST, true, true); landscape_time=FileInfo(app_landscape.name()).modify_time_utc; Optimize(landscape);} if(!landscape_time.valid())landscape_time.getUTC();
  1004. }
  1005. static void GetNotificationIcon(Image &image, DateTime &modify_time_utc, C Image &icon, DateTime &icon_time)
  1006. {
  1007. if(C ImagePtr &app_icon=CE.cei().appNotificationIcon())
  1008. {
  1009. app_icon->copyTry(image, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1, FILTER_BEST, true, true); modify_time_utc=FileInfo(app_icon.name()).modify_time_utc; Optimize(image);
  1010. if(!modify_time_utc.valid())modify_time_utc.getUTC();
  1011. }else
  1012. {
  1013. icon.copy(image, -1, -1, -1, IMAGE_L8A8, IMAGE_SOFT, 1, FILTER_BEST, true, true); // convert to grey
  1014. modify_time_utc=icon_time;
  1015. }
  1016. }
  1017. static Bool ImageResize(C Image &src, Image &dest, Int x, Int y, FIT_MODE fit)
  1018. {
  1019. Bool ok=true;
  1020. if(src.is())
  1021. {
  1022. VecI2 size_x=src.size()*x/src.w(), // uniformly scaled size to match target x
  1023. size_y=src.size()*y/src.h(); // uniformly scaled size to match target y
  1024. if((fit==FIT_FILL) ? size_x.y>=y // if after scaling with size_x, height is more than enough
  1025. : size_x.y<=y) // if after scaling with size_x, height fits
  1026. {
  1027. Int image_type=((y>size_x.y && !ImageTI[src.type()].a) ? ImageTypeIncludeAlpha(src.type()) : -1);
  1028. ok=src.copyTry(dest, size_x.x, size_x.y, -1, image_type, IMAGE_SOFT, 1, FILTER_BEST, true, true);
  1029. Int d=size_x.y-y; dest.crop(dest, 0, d/2, dest.w(), y);
  1030. }else
  1031. {
  1032. Int image_type=((x>size_y.x && !ImageTI[src.type()].a) ? ImageTypeIncludeAlpha(src.type()) : -1);
  1033. ok=src.copyTry(dest, size_y.x, size_y.y, -1, image_type, IMAGE_SOFT, 1, FILTER_BEST, true, true);
  1034. Int d=size_y.x-x; dest.crop(dest, d/2, 0, x, dest.h());
  1035. }
  1036. }
  1037. return ok;
  1038. }
  1039. struct ImageConvert
  1040. {
  1041. Bool ok, _square;
  1042. FIT_MODE fit;
  1043. Byte format; // 0=PNG, 1=ICO, 2=ICNS
  1044. Str dest;
  1045. VecI2 size,
  1046. _crop,
  1047. _clamp;
  1048. C Image *src;
  1049. DateTime dt;
  1050. static void Func(ImageConvert &ic, Ptr user, Int thread_index) {ic.process();}
  1051. ImageConvert& set(C Str &dest, C Image &src, C DateTime &dt)
  1052. {
  1053. T.ok=false; T._square=false; T.format=0; T.dest=dest; T.size=T._crop=T._clamp=-1; T.fit=FIT_FULL; T.src=&src; T.dt=dt; return T;
  1054. }
  1055. ImageConvert& ICO ( ) {T.format=1; return T;}
  1056. ImageConvert& ICNS ( ) {T.format=2; return T;}
  1057. ImageConvert& square ( ) {_square=true; return T;}
  1058. ImageConvert& resize (Int w, Int h) {T.size.set(w, h); return T;}
  1059. ImageConvert& resizeFill(Int w, Int h) {T.size.set(w, h); T.fit=FIT_FILL; return T;}
  1060. ImageConvert& crop (Int w, Int h) { _crop.set(w, h); return T;}
  1061. ImageConvert& clamp (Int w, Int h) {_clamp.set(w, h); return T;}
  1062. void process()
  1063. {
  1064. File f; f.writeMem();
  1065. Image temp;
  1066. if(size.x>0 || size.y>0)if(ImageResize(*src, temp, size.x, size.y, fit))src=&temp;else return;
  1067. if(_clamp.x>0 || _clamp.y>0)
  1068. {
  1069. VecI2 size=src->size();
  1070. if(_clamp.x>0 && size.x>_clamp.x)size.set(_clamp.x, Max(1, DivRound(size.y*_clamp.x, size.x)));
  1071. if(_clamp.y>0 && size.y>_clamp.y)size.set(Max(1, DivRound(size.x*_clamp.y, size.y)), _clamp.y);
  1072. if(src->copyTry(temp, size.x, size.y, -1, -1, IMAGE_SOFT, 1, FILTER_BEST, true, true))src=&temp;else return;
  1073. }
  1074. if(_crop.x>0 && _crop.y>0)
  1075. {
  1076. if(!ImageTI[src->type()].a && (_crop.x>src->w() || _crop.y>src->h()))if(src->copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return; // if we're cropping to a bigger size, then make sure that alpha channel is present, so pixels can be set to transparent color
  1077. src->crop(temp, (src->w()-_crop.x)/2, (src->h()-_crop.y)/2, _crop.x, _crop.y); src=&temp;
  1078. }
  1079. if(_square && src->size().allDifferent())
  1080. {
  1081. if(!ImageTI[src->type()].a)if(src->copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return; // if we're cropping to a bigger size, then make sure that alpha channel is present, so pixels can be set to transparent color
  1082. Int size=src->size().max(); src->crop(temp, (src->w()-size)/2, (src->h()-size)/2, size, size); src=&temp;
  1083. }
  1084. switch(format)
  1085. {
  1086. case 0: ok=src->ExportPNG (f, 1); break;
  1087. case 1: ok=src->ExportICO (f ); break;
  1088. case 2: ok=src->ExportICNS(f ); break;
  1089. }
  1090. if(ok){f.pos(0); FCreateDir(GetPath(dest)); ok=SafeOverwrite(f, dest, &dt);}
  1091. }
  1092. };
  1093. Bool CodeEditor::generateVSProj(Int version)
  1094. {
  1095. if(build_exe_type!=EXE_EXE && build_exe_type!=EXE_DLL /*&& build_exe_type!=EXE_LIB*/ && build_exe_type!=EXE_NEW && build_exe_type!=EXE_WEB)return Error("Visual Studio projects support only EXE, DLL, Universal and JS configurations.");
  1096. if(build_exe_type==EXE_WEB && version<10)return Error("WEB configuration requires Visual Studio 2010 or newer.");
  1097. FCreateDirs(build_path+"Assets");
  1098. FileText resource_rc; resource_rc.writeMem(UTF_16); // utf-16 must be used, because VS has problems with utf-8
  1099. FileText src; if(!src.read("Code/Windows/resource.rc"))return ErrorRead("Code/Windows/resource.rc"); for(; !src.end(); )resource_rc.putLine(src.fullLine());
  1100. Bool resource_changed=false;
  1101. Int resource_id=1; // 1 is currently used as the first resource
  1102. // Steam DLL
  1103. if(cei().appPublishSteamDll()) // this must be copied always because the DLL needs to be present in the EXE folder, since we can't specify a custom path for it
  1104. {
  1105. CChar8 *name=(config_32_bit ? "steam_api.dll" : "steam_api64.dll");
  1106. if(!CopyFile(S+"Code/Windows/"+name, build_path+name))return false;
  1107. }
  1108. // OpenVR DLL
  1109. if(cei().appPublishOpenVRDll()) // this must be copied always because the DLL needs to be present in the EXE folder, since we can't specify a custom path for it
  1110. {
  1111. CChar8 *name=(config_32_bit ? "openvr_api.32.dll" : "openvr_api.64.dll");
  1112. if(!CopyFile(S+"Code/Windows/"+name, build_path+"openvr_api.dll"))return false;
  1113. }
  1114. // d3dcompiler_47.dll (Windows 7 and earlier don't have it included, this is needed if app uses shader compilation, or if compiled in debug mode which does not remove unreferenced symbols/functions)
  1115. {
  1116. CChar8 *name=(config_32_bit ? "d3dcompiler_47.32.dll" : "d3dcompiler_47.64.dll");
  1117. if(!CopyFile(S+"Code/Windows/"+name, build_path+"d3dcompiler_47.dll"))return false;
  1118. }
  1119. // icon, generate always because it may be used by WINDOWS_OLD
  1120. resource_rc.putLine(S+(resource_id++)+" ICON \"Assets/Icon.ico\"");
  1121. // embed engine data, generate always because it may be used by WINDOWS_OLD
  1122. if(cei().appEmbedEngineData())
  1123. {
  1124. Bool changed; if(!CreateEngineEmbedPak(build_path+"Assets/EngineEmbed.pak", &changed))return false;
  1125. resource_rc.putLine(S+(resource_id++)+" PAK \"Assets/EngineEmbed.pak\"");
  1126. resource_changed|=changed;
  1127. }
  1128. // app data, generate always because it may be used by WINDOWS_OLD
  1129. {
  1130. Bool exists, changed; if(!CreateAppPak(build_path+"Assets/App.pak", exists, &changed))return false;
  1131. if(exists)resource_rc.putLine(S+(resource_id++)+" PAK \"Assets/App.pak\"");
  1132. resource_changed|=changed;
  1133. }
  1134. // generate images which may be used by WINDOWS_NEW
  1135. Image icon; DateTime icon_time;
  1136. if(GetIcon(icon, icon_time))
  1137. {
  1138. Image portrait ; DateTime portrait_time;
  1139. Image landscape; DateTime landscape_time;
  1140. GetImages(portrait, portrait_time, landscape, landscape_time);
  1141. Memc<ImageConvert> convert;
  1142. DateTime dt;
  1143. CChar8 *rel;
  1144. const Bool splash_from_icon=true;
  1145. Image empty;
  1146. rel="Assets/Icon.ico"; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1)){resource_changed=true; convert.New().set(build_path+rel, icon, icon_time).ICO().clamp(256, 256).square();} // Windows can't handle non-square icons properly (it stretches them)
  1147. // list images starting from the smallest
  1148. rel="Assets/Square44x44Logo.targetsize-48_altform-unplated.png"; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).resize( 48, 48); // this is used for Windows Taskbar , for 1920x1080 screen, taskbar icon is around 32x32, no need to provide bigger size
  1149. rel="Assets/Square44x44Logo.scale-200.png" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).resize( 88, 88); // this is used for Windows Phone App List, for 1280x720 screen, icon is 80x80
  1150. rel="Assets/Square150x150Logo.scale-200.png" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).resize(300, 300); // this is used for Windows Phone Start , for 1280x720 screen, icon is 228x228
  1151. rel="Assets/Logo.png"; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).resize(50, 50);
  1152. rel="Assets/SplashScreen.png";
  1153. if(splash_from_icon)
  1154. {
  1155. if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).resize(128, 128).crop(620, 300);
  1156. }else
  1157. if(landscape.is() || portrait.is())
  1158. {
  1159. dt=(landscape.is() ? landscape_time : portrait_time); if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, dt, 1))convert.New().set(build_path+rel, landscape.is() ? landscape : portrait, dt).resizeFill(620, 300);
  1160. }else // use empty
  1161. {
  1162. dt.zero(); dt.day=1; dt.month=1; dt.year=2000; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, dt, 1)){empty.createSoftTry(620, 300, 1, IMAGE_R8G8B8A8); empty.zero(); convert.New().set(build_path+rel, empty, dt);}
  1163. }
  1164. convert.reverseOrder(); // start working from the biggest ones because they take the most time, yes this is correct
  1165. MultiThreadedCall(convert, ImageConvert::Func);
  1166. FREPA(convert)if(!convert[i].ok)return ErrorWrite(convert[i].dest);
  1167. }
  1168. // resources, generate always because it may be used by WINDOWS_OLD
  1169. if(!OverwriteOnChangeLoud(resource_rc, build_path+"resource.rc"))return false;
  1170. if(resource_changed)FTimeUTC(build_path+"resource.rc", DateTime().getUTC()); // if any resource was changed then we need to adjust "resource.rc" modification time to make sure that VS will rebuild the resources
  1171. // VS project
  1172. Node<BuildFileElm> tree; FREPA(build_files)BuildTree(tree, build_files[i], build_files[i].dest_proj_path);
  1173. Memc<Str> libs=GetFiles(cei().appLibsWindows()),
  1174. dirs=GetFiles(cei().appDirsWindows());
  1175. // web html
  1176. if(build_exe_type==EXE_WEB)
  1177. {
  1178. if(!src.read("Code/Web/Page.html"))return ErrorRead("Code/Web/Page.html");
  1179. Str page; src.getAll(page);
  1180. page=Replace(page, "EE_PAGE_TITLE" , XmlString (build_project_name) , true, true);
  1181. page=Replace(page, "EE_JS_FILE_NAME", GetBaseNoExt(build_exe )+".js", true, true);
  1182. SetFile(src, page, UTF_8_NAKED);
  1183. FCreateDirs(GetPath(build_exe));
  1184. if(!OverwriteOnChangeLoud(src, GetExtNot(build_exe)+".Esenthel.html"))return false; // use custom suffix name because Emscripten uses GetExtNot(build_exe)+".html"
  1185. }
  1186. // manifest
  1187. if(!CopyFile("Code/Windows/Windows Manifest.xml", build_path+"Windows Manifest.xml"))return false;
  1188. // universal manifest
  1189. {
  1190. XmlData xml;
  1191. if(!xml.load(S+"Code/Windows/Package.appxmanifest"))return ErrorRead(S+"Code/Windows/Package.appxmanifest");
  1192. if(XmlNode *package=xml.findNode("Package"))
  1193. {
  1194. if(XmlNode *identity=package->findNode("identity"))
  1195. {
  1196. identity->getParam("Name").setValue(Replace(cei().appPackage(), '_', '-')); // MS doesn't support '_' but supports '-'
  1197. identity->getParam("Version").setValue(S+cei().appBuild()+".0.0.0");
  1198. }
  1199. if(XmlNode *properties=package->findNode("Properties"))
  1200. {
  1201. if(XmlNode *display_name=properties->findNode("DisplayName"))display_name->data.setNum(1)[0]=cei().appName();
  1202. }
  1203. if(XmlNode *applications=package->findNode("Applications"))
  1204. if(XmlNode *application=applications->findNode("Application"))
  1205. if(XmlNode *visual_elements=application->findNode("uap:VisualElements"))
  1206. {
  1207. visual_elements->getParam("DisplayName").setValue(cei().appName());
  1208. visual_elements->getParam("Description").setValue(cei().appName());
  1209. XmlNode &rotations=visual_elements->getNode("uap:InitialRotationPreference");
  1210. UInt flag=cei().appSupportedOrientations();
  1211. rotations.nodes.del();
  1212. if(flag&DIRF_UP )rotations.nodes.New().setName("uap:Rotation").params.New().set("Preference", "portrait");
  1213. if(flag&DIRF_DOWN )rotations.nodes.New().setName("uap:Rotation").params.New().set("Preference", "portraitFlipped");
  1214. if(flag&DIRF_LEFT )rotations.nodes.New().setName("uap:Rotation").params.New().set("Preference", "landscape");
  1215. if(flag&DIRF_RIGHT)rotations.nodes.New().setName("uap:Rotation").params.New().set("Preference", "landscapeFlipped");
  1216. }
  1217. }
  1218. if(!OverwriteOnChangeLoud(xml, build_path+"Package.appxmanifest"))return false;
  1219. }
  1220. // solution
  1221. if(!CopyFile("Code/Windows/Project.sln", build_path+"Project.sln"))return false;
  1222. Str bin_path=BinPath();
  1223. /*if(version==9)
  1224. {
  1225. XmlData xml;
  1226. if(!xml.load("Code/Windows/project.vcproj"))return ErrorRead("Code/Windows/project.vcproj");
  1227. if(XmlNode *proj=xml.findNode("VisualStudioProject"))
  1228. {
  1229. // project name
  1230. proj->params.New().set("Name" , build_project_name);
  1231. proj->params.New().set("RootNamespace", build_project_name);
  1232. // set files
  1233. if(XmlNode *files=proj->findNode("Files"))StoreTree9(tree, *files);
  1234. if(XmlNode *configs=proj->findNode("Configurations"))
  1235. for(Int i=0; XmlNode *config=configs->findNode("Configuration", i); i++)
  1236. {
  1237. // set exe/dll
  1238. if(build_exe_type==EXE_DLL)
  1239. if(XmlParam *type=config->findParam("ConfigurationType"))
  1240. type->value="2"; // '1' is for EXE, '2' is for DLL
  1241. for(Int i=0; XmlNode *tool=config->findNode("Tool", i); i++)
  1242. if(XmlParam *name=tool->findParam("Name"))
  1243. {
  1244. if(name->value=="VCLinkerTool")
  1245. {
  1246. if(build_exe_type==EXE_DLL)
  1247. if(XmlParam *out=tool->findParam("OutputFile"))
  1248. out->value="$(ProjectName).dll"; // or "$(ProjectName).exe"
  1249. // set libs
  1250. if(XmlParam *dependencies=tool->findParam("AdditionalDependencies"))
  1251. {
  1252. Int pos=TextPosI(dependencies->value, "EsenthelEngine");
  1253. if( pos>=0)
  1254. {
  1255. Int len=TextPosI(dependencies->value()+pos, ".lib"); len+=4;
  1256. Str lib; FREP(len)lib+=dependencies->value[pos+i];
  1257. dependencies->value.remove(pos, len+1);
  1258. dependencies->value.insert(0, S+'"'+bin_path+lib+"\" ");
  1259. }
  1260. FREPA(libs)dependencies->value.space()+=S+'"'+libs[i]+'"';
  1261. }
  1262. }else
  1263. if(name->value=="VCCLCompilerTool")
  1264. {
  1265. // set dirs
  1266. if(XmlParam *directories=tool->findParam("AdditionalIncludeDirectories"))
  1267. {
  1268. Str &dest=directories->value;
  1269. FREPA(dirs){if(dest.is() && dest.last()!=';')dest+=';'; dest.space()+=S+'"'+dirs[i]+'"';}
  1270. }
  1271. }
  1272. }
  1273. }
  1274. // save project
  1275. build_project_file=build_path+"Project.vcproj";
  1276. if(!OverwriteOnChangeLoud(xml, build_project_file))return false;
  1277. return true;
  1278. }
  1279. }else*/
  1280. if(version==10 || version==11 || version==12 || version==14 || version==15) // #VisualStudio
  1281. {
  1282. XmlData xml, xml_f;
  1283. if(!xml .load(S+"Code/Windows/Project.vcxproj" ))return ErrorRead(S+"Code/Windows/Project.vcxproj" );
  1284. if(!xml_f.load(S+"Code/Windows/Project.vcxproj.filters"))return ErrorRead(S+"Code/Windows/Project.vcxproj.filters");
  1285. if(XmlNode *proj =xml .findNode("Project"))
  1286. if(XmlNode *proj_f=xml_f.findNode("Project"))
  1287. {
  1288. // first adjust the "Engine.pak" path
  1289. FREPA(proj->nodes)
  1290. {
  1291. XmlNode &node=proj->nodes[i]; if(node.name=="ItemGroup")FREPA(node.nodes)
  1292. {
  1293. XmlNode &sub=node.nodes[i]; if(sub.name=="Media")if(XmlParam *include=sub.findParam("Include"))if(include->value=="Engine.pak")
  1294. {
  1295. include->value=bin_path+"Universal\\Engine.pak"; goto found_engine_pak;
  1296. }
  1297. }
  1298. }
  1299. found_engine_pak:
  1300. FREPA(proj_f->nodes)
  1301. {
  1302. XmlNode &node=proj_f->nodes[i]; if(node.name=="ItemGroup")FREPA(node.nodes)
  1303. {
  1304. XmlNode &sub=node.nodes[i]; if(sub.name=="Media")if(XmlParam *include=sub.findParam("Include"))if(include->value=="Engine.pak")
  1305. {
  1306. include->value=bin_path+"Universal\\Engine.pak"; goto found_engine_pak2;
  1307. }
  1308. }
  1309. }
  1310. found_engine_pak2:
  1311. // project name
  1312. for(Int i=0; XmlNode *prop=proj->findNode("PropertyGroup", i); i++)
  1313. if(XmlParam *label=prop->findParam("Label"))if(label->value=="Globals")
  1314. {
  1315. prop->nodes.New().setName("RootNamespace").data.New()=build_project_name;
  1316. prop->nodes.New().setName("ProjectName" ).data.New()=build_project_name;
  1317. // SDK #VisualStudio
  1318. CChar8 *sdk=null;
  1319. if(version==14)sdk="10.0.10240.0";else // VS 2015
  1320. if(version==15)sdk="10.0.17134.0"; // VS 2017
  1321. prop->getNode("WindowsTargetPlatformVersion" ).data.clear().New()=sdk;
  1322. prop->getNode("WindowsTargetPlatformMinVersion").data.clear().New()=sdk;
  1323. break;
  1324. }
  1325. // set files
  1326. {
  1327. XmlNode &group=proj->nodes.New().setName("ItemGroup");
  1328. FREPA(build_files)
  1329. {
  1330. BuildFile &bf=build_files[i];
  1331. if(bf.includeInProj())
  1332. {
  1333. XmlNode &file=group.nodes.New().setName((bf.mode==BuildFile::SOURCE) ? "ClCompile" : (bf.mode==BuildFile::LIB) ? "Library" : "None");
  1334. file.params.New().set("Include", bf.dest_proj_path);
  1335. }
  1336. }
  1337. }
  1338. // set filters
  1339. {
  1340. XmlNode &group=proj_f->nodes.New().setName("ItemGroup");
  1341. StoreTree10(tree, group);
  1342. }
  1343. // set libs
  1344. for(Int i=0; XmlNode *item=proj->findNode("ItemDefinitionGroup", i); i++)
  1345. if(XmlNode *link =item->findNode("Link"))
  1346. if(XmlNode *dependencies=link->findNode("AdditionalDependencies"))
  1347. if(dependencies->data.elms()>=1)
  1348. {
  1349. Str &dest=dependencies->data[0];
  1350. Int pos=TextPosI(dest, "EsenthelEngine");
  1351. if( pos>=0)
  1352. {
  1353. Int len=TextPosI(dest()+pos, ';');
  1354. Str lib; FREP(len)lib+=dest[pos+i];
  1355. dest.remove(pos, len+1);
  1356. dest.insert(0, S+'"'+bin_path+lib+"\";");
  1357. }
  1358. FREPA(libs){if(dest.is() && dest.last()!=';')dest+=';'; dest.space()+=S+'"'+libs[i]+'"';}
  1359. //if(version>=14)dest=Replace(dest, "libcpmt.lib;", "libcpmt.lib;libucrt.lib;libvcruntime.lib;"); // for VS 2015 (x86 and x64) we need to add libucrt.lib; libvcruntime.lib; libraries, this is now set in the project file by default
  1360. }
  1361. // set dirs
  1362. for(Int i=0; XmlNode *item=proj->findNode("ItemDefinitionGroup", i); i++)
  1363. if(XmlNode *compile =item ->findNode("ClCompile"))
  1364. if(XmlNode *directories=compile->findNode("AdditionalIncludeDirectories"))
  1365. {
  1366. Str dest; FREPA(directories->data)dest.space()+=directories->data[i];
  1367. FREPA(dirs){if(dest.is() && dest.last()!=';')dest+=';'; dest.space()+=S+'"'+dirs[i]+'"';}
  1368. Swap(directories->data.setNum(1)[0], dest);
  1369. }
  1370. // set exe/dll
  1371. if(build_exe_type==EXE_DLL)
  1372. for(Int i=0; XmlNode *prop=proj->findNode("PropertyGroup", i); i++)
  1373. if(XmlNode *type=prop->findNode("ConfigurationType"))
  1374. type->data.setNum(1)[0]="DynamicLibrary"; // or "Application" or "StaticLibrary"
  1375. // Platform toolset #VisualStudio
  1376. CChar8 *platform_toolset=null, *platform_toolset_universal=null;
  1377. if(version==10) platform_toolset="v100";else
  1378. if(version==11) platform_toolset=((Compare(devenv_version, VecI4(11, 0, 51106, 1))>=0) ? "v110_xp" : "v110");else // VS 2012 with XP support version number is "11.0.51106.1"
  1379. if(version==12) platform_toolset="v120_xp";else
  1380. if(version==14){platform_toolset="v140_xp"; platform_toolset_universal="v140";}else
  1381. if(version==15){platform_toolset="v141_xp"; platform_toolset_universal="v141";}
  1382. if(platform_toolset)
  1383. for(Int i=0; XmlNode *prop=proj->findNode("PropertyGroup", i); i++)
  1384. if(C XmlParam *label=prop->findParam("Label"))if(label->value=="Configuration")
  1385. {
  1386. C XmlParam *condition=prop->findParam("Condition");
  1387. Bool keep =(condition && Contains(condition->value, "Emscripten"));
  1388. if(!keep) // keep default value
  1389. {
  1390. Bool universal=(condition && Contains(condition->value, "Universal"));
  1391. XmlNode &PlatformToolset=prop->getNode("PlatformToolset");
  1392. PlatformToolset.data.setNum(1)[0]=(universal ? platform_toolset_universal : platform_toolset);
  1393. }
  1394. }
  1395. // save project
  1396. build_project_file=build_path+"Project.sln";
  1397. if(!OverwriteOnChangeLoud(xml , build_path+"Project.vcxproj" ))return false;
  1398. if(!OverwriteOnChangeLoud(xml_f, build_path+"Project.vcxproj.filters"))return false;
  1399. if(!CopyFile("Code/Windows/Project.vcxproj.user", build_path+"Project.vcxproj.user"))return false; // this is needed so that Windows Phone Device will be listed as the target machine for compilation, without this, currently Visual Studio has a bug which does not display the Phone as the target, this forces displaying it, perhaps it will be fixed in Visual Studio in the future, and then this copy won't be needed
  1400. return true;
  1401. }
  1402. }
  1403. return false;
  1404. }
  1405. /******************************************************************************/
  1406. // Xcode projects operate on 96-bit ID's for elements in hex format, for example: "0B9F063216CD7D29006A0106" (for simplicity here only U64 is used, making upper bytes resulting as zeros)
  1407. static Str XcodeID(U64 id) {return TextHex(id, 24);} // 24 digits = 3 x UInt = 96-bit
  1408. struct XcodeFile
  1409. {
  1410. Str name;
  1411. U64 file, build, copy;
  1412. XcodeFile() {file=build=copy=0;}
  1413. };
  1414. static void SetTreeID(Node<BuildFileElm> &node, U64 &file_id)
  1415. {
  1416. node.id=(node.bf ? node.bf->xcode_file_id : file_id++);
  1417. FREPA(node.children)SetTreeID(node.children[i], file_id);
  1418. }
  1419. static void SetGroups(Node<BuildFileElm> &node, Str &str)
  1420. {
  1421. // Sample:
  1422. // 080E96DDFE201D6D7F000001 /* Source */ = {
  1423. // isa = PBXGroup;
  1424. // children = (
  1425. // 0B9F065B16CD9232006A0106 /* Main.cpp */,
  1426. // );
  1427. // name = Source;
  1428. // sourceTree = "<group>";
  1429. // };
  1430. if(!node.bf) // group
  1431. {
  1432. str+=S+" "+XcodeID(node.id)+" /* "+Replace(node.base_name, '*', '\0')+" */ = {\n";
  1433. str+=S+" isa = PBXGroup;\n";
  1434. str+=S+" children = (\n";
  1435. FREPA(node.children) // keep order
  1436. {
  1437. str+=S+" "+XcodeID(node.children[i].id)+" /* "+Replace(node.children[i].base_name, '*', '\0')+" */,\n";
  1438. }
  1439. str+=S+" );\n";
  1440. str+=S+" name = \""+CString(node.base_name)+"\";\n";
  1441. str+=S+" sourceTree = \"<group>\";\n";
  1442. str+=S+" };\n";
  1443. }
  1444. FREPA(node.children)SetGroups(node.children[i], str);
  1445. }
  1446. static Bool GetXcodeProjTextPos(Str &str, Int &pos, CChar8 *text)
  1447. {
  1448. pos=TextPosI(str, text); if(pos<0)return Error(S+"Text \""+text+"\"\nwas not found in Xcode project template.");
  1449. return true;
  1450. }
  1451. Bool CodeEditor::generateXcodeProj()
  1452. {
  1453. Str bin_path=BinPath();
  1454. XmlData xml;
  1455. Str str, add; Int pos;
  1456. U64 file_id=0xEEC0C0A000000000;
  1457. Node<BuildFileElm> tree;
  1458. Memc<XcodeFile > mac_assets, ios_images, mac_frameworks, ios_frameworks, mac_dylibs;
  1459. FileText src; if(!src.read("Code/Apple/project.pbxproj"))return ErrorRead("Code/Apple/project.pbxproj"); src.getAll(str);
  1460. build_project_file=build_path+"Project.xcodeproj";
  1461. if(!FExistSystem(build_project_file) && !FCreateDirs(build_project_file))return ErrorWrite(build_project_file);
  1462. FCreateDirs(build_path+"Assets");
  1463. if(!CopyFile("Code/Apple/iOS.xib", build_path+"Assets/iOS.xib"))return false;
  1464. Memc<Str> libs_mac=GetFiles(cei().appLibsMac()),
  1465. libs_ios=GetFiles(cei().appLibsiOS()),
  1466. dirs =GetFiles(cei().appDirsNonWindows());
  1467. // embed engine data
  1468. if(cei().appEmbedEngineData())
  1469. {
  1470. mac_assets.New().name="Assets/EngineEmbed.pak";
  1471. if(!CreateEngineEmbedPak(build_path+"Assets/EngineEmbed.pak"))return false;
  1472. }
  1473. // app data
  1474. Bool exists; if(!CreateAppPak(build_path+"Assets/App.pak", exists))return false;
  1475. if( exists)mac_assets.New().name="Assets/App.pak";
  1476. // Steam
  1477. if(cei().appPublishSteamDll ())mac_dylibs.New().name=bin_path+ "libsteam_api.dylib";
  1478. if(cei().appPublishOpenVRDll())mac_dylibs.New().name=bin_path+"libopenvr_api.dylib";
  1479. // Images
  1480. Image icon; DateTime icon_time;
  1481. if(GetIcon(icon, icon_time))
  1482. {
  1483. Image portrait ; DateTime portrait_time;
  1484. Image landscape; DateTime landscape_time;
  1485. GetImages(portrait, portrait_time, landscape, landscape_time);
  1486. Memc<ImageConvert> convert;
  1487. CChar8 *rel="Assets/Icon.icns"; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).ICNS().clamp(256, 256);
  1488. if(build_exe_type==EXE_IOS || build_mode==BUILD_EXPORT) // creating iOS icons/images is slow, so do this only when necessary
  1489. {
  1490. // list images starting from the smallest
  1491. rel="Assets/Icon-76.png" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).resize( 76, 76); ios_images.New().name=rel;
  1492. rel="Assets/[email protected]" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).resize(120, 120); ios_images.New().name=rel;
  1493. rel="Assets/[email protected]" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).resize(152, 152); ios_images.New().name=rel;
  1494. rel="Assets/[email protected]" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).resize(180, 180); ios_images.New().name=rel;
  1495. rel="Assets/[email protected]"; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).resize(167, 167); ios_images.New().name=rel;
  1496. const Bool hi_res=false; DateTime dt;
  1497. dt=(portrait.is() ? portrait_time : landscape.is() ? landscape_time : icon_time); rel="Assets/[email protected]" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, dt, 1))convert.New().set(build_path+rel, portrait.is() ? portrait : landscape.is() ? landscape : icon, dt).resizeFill( 640, 960); ios_images.New().name=rel;
  1498. dt=(portrait.is() ? portrait_time : landscape.is() ? landscape_time : icon_time); rel="Assets/[email protected]" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, dt, 1))convert.New().set(build_path+rel, portrait.is() ? portrait : landscape.is() ? landscape : icon, dt).resizeFill( 640, 1136); ios_images.New().name=rel;
  1499. dt=(portrait.is() ? portrait_time : landscape.is() ? landscape_time : icon_time); rel="Assets/[email protected]" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, dt, 1))convert.New().set(build_path+rel, portrait.is() ? portrait : landscape.is() ? landscape : icon, dt).resizeFill( 750, 1334); ios_images.New().name=rel;
  1500. dt=(portrait.is() ? portrait_time : landscape.is() ? landscape_time : icon_time); rel="Assets/Default-Portrait.png" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, dt, 1))convert.New().set(build_path+rel, portrait.is() ? portrait : landscape.is() ? landscape : icon, dt).resizeFill( 768, 1024); ios_images.New().name=rel;
  1501. if(hi_res){dt=(portrait.is() ? portrait_time : landscape.is() ? landscape_time : icon_time); rel="Assets/[email protected]" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, dt, 1))convert.New().set(build_path+rel, portrait.is() ? portrait : landscape.is() ? landscape : icon, dt).resizeFill(1536, 2048); ios_images.New().name=rel;}
  1502. if(landscape.is()){ dt=( landscape_time ); rel="Assets/Default-Landscape.png" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, dt, 1))convert.New().set(build_path+rel, landscape , dt).resizeFill(1024, 768); ios_images.New().name=rel;
  1503. if(hi_res){dt=( landscape_time ); rel="Assets/[email protected]"; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, dt, 1))convert.New().set(build_path+rel, landscape , dt).resizeFill(2048, 1536); ios_images.New().name=rel;}}
  1504. }
  1505. convert.reverseOrder(); // start working from the biggest ones because they take the most time, yes this is correct
  1506. MultiThreadedCall(convert, ImageConvert::Func);
  1507. FREPA(convert)if(!convert[i].ok)return ErrorWrite(convert[i].dest);
  1508. }
  1509. Str app_package=Replace(cei().appPackage(), '_', '-'); // Apple does not support '_' but supports '-'
  1510. // iOS.plist
  1511. if(!xml.load("Code/Apple/iOS.plist"))return ErrorRead("Code/Apple/iOS.plist");
  1512. if(XmlNode *plist=xml .findNode("plist"))
  1513. if(XmlNode *dict =plist->findNode("dict" ))FREPA(dict->nodes)if(dict->nodes[i].name=="key" && InRange(i+1, dict->nodes))
  1514. {
  1515. XmlNode &node =dict->nodes[i ];
  1516. XmlNode &value=dict->nodes[i+1];
  1517. Str key; FREPA(node.data)key.space()+=node.data[i];
  1518. if(key=="CFBundleDisplayName")
  1519. {
  1520. value.setName("string").nodes.del();
  1521. value.data.setNum(1)[0]=cei().appName();
  1522. }else
  1523. if(key=="CFBundleIdentifier")
  1524. {
  1525. value.setName("string").nodes.del();
  1526. value.data.setNum(1)[0]=app_package;
  1527. }else
  1528. if(key=="CFBundleVersion" || key=="CFBundleShortVersionString")
  1529. {
  1530. value.setName("string").nodes.del();
  1531. value.data.setNum(1)[0]=cei().appBuild();
  1532. }else
  1533. if(key=="UISupportedInterfaceOrientations")
  1534. {
  1535. value.setName("array").nodes.del();
  1536. UInt flag=cei().appSupportedOrientations();
  1537. if(flag&DIRF_UP )value.nodes.New().setName("string").data.add("UIInterfaceOrientationPortrait");
  1538. if(flag&DIRF_DOWN )value.nodes.New().setName("string").data.add("UIInterfaceOrientationPortraitUpsideDown");
  1539. if(flag&DIRF_LEFT )value.nodes.New().setName("string").data.add("UIInterfaceOrientationLandscapeRight");
  1540. if(flag&DIRF_RIGHT)value.nodes.New().setName("string").data.add("UIInterfaceOrientationLandscapeLeft");
  1541. }else
  1542. if(key=="NSLocationAlwaysUsageDescription" || key=="NSLocationWhenInUseUsageDescription")
  1543. {
  1544. value.setName("string").nodes.del();
  1545. value.data.setNum(1)[0]=cei().appLocationUsageReason();
  1546. }else
  1547. if(key=="FacebookAppID")
  1548. {
  1549. value.setName("string").nodes.del();
  1550. value.data.setNum(1)[0]=cei().appFacebookAppID();
  1551. }else
  1552. if(key=="FacebookDisplayName")
  1553. {
  1554. value.setName("string").nodes.del();
  1555. value.data.setNum(1)[0]=cei().appName();
  1556. }else
  1557. if(key=="ChartboostAppID")
  1558. {
  1559. value.setName("string").nodes.del();
  1560. value.data.setNum(1)[0]=cei().appChartboostAppIDiOS();
  1561. }else
  1562. if(key=="ChartboostAppSignature")
  1563. {
  1564. value.setName("string").nodes.del();
  1565. value.data.setNum(1)[0]=cei().appChartboostAppSignatureiOS();
  1566. }else
  1567. if(key=="CFBundleURLTypes")
  1568. {
  1569. if(value.name=="array")
  1570. {
  1571. XmlNode &dict=value.getNode("dict");
  1572. dict.nodes.clear().setNum(2);
  1573. dict.nodes[0].setName("key" ).data.add("CFBundleURLSchemes");
  1574. dict.nodes[1].setName("array").nodes.New().setName("string").data.add(S+"fb"+cei().appFacebookAppID());
  1575. }
  1576. }
  1577. }
  1578. if(!OverwriteOnChangeLoud(xml, build_path+"Assets/iOS.plist"))return false;
  1579. // Mac.plist
  1580. if(!xml.load("Code/Apple/Mac.plist"))return ErrorRead("Code/Apple/Mac.plist");
  1581. if(XmlNode *plist=xml .findNode("plist"))
  1582. if(XmlNode *dict =plist->findNode("dict" ))FREPA(dict->nodes)if(dict->nodes[i].name=="key" && dict->nodes[i].data.elms() && InRange(i+1, dict->nodes))
  1583. {
  1584. C Str &key =dict->nodes[i].data[0];
  1585. XmlNode &value=dict->nodes[i+1];
  1586. if(key=="CFBundleDisplayName")
  1587. {
  1588. value.setName("string").nodes.del();
  1589. value.data.setNum(1)[0]=cei().appName();
  1590. }else
  1591. if(key=="CFBundleIdentifier")
  1592. {
  1593. value.setName("string").nodes.del();
  1594. value.data.setNum(1)[0]=app_package;
  1595. }else
  1596. if(key=="CFBundleVersion")
  1597. {
  1598. value.setName("string").nodes.del();
  1599. value.data.setNum(1)[0]=cei().appBuild();
  1600. }
  1601. }
  1602. if(!OverwriteOnChangeLoud(xml, build_path+"Assets/Mac.plist"))return false;
  1603. str=Replace(str, "path = \"EsenthelEngine.a\"" , UnixPath(S+"path = \""+bin_path+"EsenthelEngine.a\"" ));
  1604. str=Replace(str, "path = \"EsenthelEngine Device.a\"" , UnixPath(S+"path = \""+bin_path+"EsenthelEngine Device.a\"" ));
  1605. str=Replace(str, "path = \"EsenthelEngine Simulator.a\"", UnixPath(S+"path = \""+bin_path+"EsenthelEngine Simulator.a\""));
  1606. str=Replace(str, "path = \"Engine.pak\"" , UnixPath(S+"path = \""+bin_path+"Mobile/Engine.pak\"" ));
  1607. str=Replace(str, "/* ESENTHEL FRAMEWORK DIRS */" , S+'"'+CString(S+'"'+UnixPath(bin_path)+'"')+"\",");
  1608. str=Replace(str, "/* ESENTHEL LIBRARY DIRS */" , S+'"'+CString(S+'"'+UnixPath(bin_path)+'"')+"\",");
  1609. str=Replace(str, "PRODUCT_NAME = Mac;", S+"PRODUCT_NAME = \""+CString(build_project_name)+"\";");
  1610. str=Replace(str, "PRODUCT_NAME = iOS;", S+"PRODUCT_NAME = \""+CString(build_project_name)+"\";");
  1611. str=Replace(str, "path = Mac.app;" , S+"path = \"" +CString(build_project_name)+".app\";");
  1612. str=Replace(str, "path = iOS.app;" , S+"path = \"" +CString(build_project_name)+".app\";");
  1613. {
  1614. Str lib_dirs;
  1615. FREPA(libs_mac){Str path=GetPath(libs_mac[i]); if(path.is())lib_dirs.line()+=S+'"'+CString(S+'"'+path+'"')+"\",";}
  1616. str=Replace(str, "/* ESENTHEL MAC LIBRARY DIRS */", lib_dirs);
  1617. lib_dirs.clear();
  1618. FREPA(libs_ios){Str path=GetPath(libs_ios[i]); if(path.is())lib_dirs.line()+=S+'"'+CString(S+'"'+path+'"')+"\",";}
  1619. str=Replace(str, "/* ESENTHEL IOS LIBRARY DIRS */", lib_dirs);
  1620. }
  1621. // PBXFileReference, Sample:
  1622. // 0B80AFFF1238566900C08944 /* Main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Main.cpp; path = Source/Main.cpp; sourceTree = "<group>"; };
  1623. if(!GetXcodeProjTextPos(str, pos, "/* End PBXFileReference section */"))return false;
  1624. add.clear();
  1625. REPA(build_files)
  1626. {
  1627. BuildFile &bf=build_files[i]; if(bf.mode==BuildFile::SOURCE)
  1628. {
  1629. bf.xcode_file_id=file_id++;
  1630. bf.xcode_mac_id =file_id++;
  1631. bf.xcode_ios_id =file_id++;
  1632. add+=S+" "+XcodeID(bf.xcode_file_id)+" /* "+Replace(bf.dest_proj_path, '*', '\0')+" */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = \""+CString(GetBase(bf.dest_proj_path))+"\"; path = \""+CString(UnixPath(bf.dest_proj_path))+"\"; sourceTree = \"<group>\"; };\n";
  1633. BuildTree(tree, bf, bf.dest_proj_path);
  1634. }
  1635. }
  1636. REPA(mac_dylibs)
  1637. {
  1638. XcodeFile &file=mac_dylibs[i];
  1639. file.file =file_id++;
  1640. file.build=file_id++;
  1641. file.copy =file_id++;
  1642. add+=S+" "+XcodeID(file.file)+" /* "+file.name+" */ = {isa = PBXFileReference; lastKnownFileType = \"compiled.mach-o.dylib\"; name = \""+CString(GetBase(file.name))+"\"; path = \""+CString(UnixPath(file.name))+"\"; sourceTree = \"<absolute>\"; };\n";
  1643. }
  1644. REPA(mac_assets)
  1645. {
  1646. XcodeFile &file=mac_assets[i];
  1647. file.file =file_id++;
  1648. file.build=file_id++;
  1649. add+=S+" "+XcodeID(file.file)+" /* "+file.name+" */ = {isa = PBXFileReference; lastKnownFileType = file; name = \""+CString(GetBase(file.name))+"\"; path = \""+CString(UnixPath(file.name))+"\"; sourceTree = \"<group>\"; };\n";
  1650. }
  1651. REPA(ios_images)
  1652. {
  1653. XcodeFile &file=ios_images[i];
  1654. file.file =file_id++;
  1655. file.build=file_id++;
  1656. add+=S+" "+XcodeID(file.file)+" /* "+file.name+" */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = \""+CString(GetBase(file.name))+"\"; path = \""+CString(UnixPath(file.name))+"\"; sourceTree = \"<group>\"; };\n";
  1657. }
  1658. FREPA(libs_mac)
  1659. {
  1660. XcodeFile &file=mac_frameworks.New();
  1661. file.name =libs_mac[i];
  1662. file.file =file_id++;
  1663. file.build=file_id++;
  1664. add+=S+" "+XcodeID(file.file)+" /* "+file.name+" */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = \""+CString(GetBase(file.name))+"\"; path = \""+CString(UnixPath(file.name))+"\"; sourceTree = \"<absolute>\"; };\n";
  1665. }
  1666. FREPA(libs_ios)
  1667. {
  1668. XcodeFile &file=ios_frameworks.New();
  1669. file.name =libs_ios[i];
  1670. file.file =file_id++;
  1671. file.build=file_id++;
  1672. add+=S+" "+XcodeID(file.file)+" /* "+file.name+" */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = \""+CString(GetBase(file.name))+"\"; path = \""+CString(UnixPath(file.name))+"\"; sourceTree = \"<absolute>\"; };\n";
  1673. }
  1674. str.insert(pos, add);
  1675. // PBXBuildFile, Sample:
  1676. // 0B9F063216CD7D29006A0106 /* Main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0B80AFFF1238566900C08944 /* Main.cpp */; };
  1677. if(!GetXcodeProjTextPos(str, pos, "/* End PBXBuildFile section */"))return false;
  1678. add.clear();
  1679. REPA(build_files)
  1680. {
  1681. BuildFile &bf=build_files[i]; if(bf.mode==BuildFile::SOURCE)
  1682. {
  1683. add+=S+" "+XcodeID(bf.xcode_mac_id)+" /* Mac: "+Replace(bf.dest_proj_path, '*', '\0')+" */ = {isa = PBXBuildFile; fileRef = "+XcodeID(bf.xcode_file_id)+" /* "+Replace(bf.dest_proj_path, '*', '\0')+" */; };\n";
  1684. add+=S+" "+XcodeID(bf.xcode_ios_id)+" /* iOS: "+Replace(bf.dest_proj_path, '*', '\0')+" */ = {isa = PBXBuildFile; fileRef = "+XcodeID(bf.xcode_file_id)+" /* "+Replace(bf.dest_proj_path, '*', '\0')+" */; };\n";
  1685. }
  1686. }
  1687. REPA(mac_assets)
  1688. {
  1689. XcodeFile &file=mac_assets[i];
  1690. add+=S+" "+XcodeID(file.build)+" /* Mac: "+file.name+" */ = {isa = PBXBuildFile; fileRef = "+XcodeID(file.file)+" /* "+file.name+" */; };\n";
  1691. }
  1692. REPA(ios_images)
  1693. {
  1694. XcodeFile &file=ios_images[i];
  1695. add+=S+" "+XcodeID(file.build)+" /* iOS: "+file.name+" */ = {isa = PBXBuildFile; fileRef = "+XcodeID(file.file)+" /* "+file.name+" */; };\n";
  1696. }
  1697. REPA(mac_dylibs)
  1698. {
  1699. XcodeFile &file=mac_dylibs[i];
  1700. add+=S+" "+XcodeID(file.build)+" /* Mac: "+file.name+" */ = {isa = PBXBuildFile; fileRef = "+XcodeID(file.file)+" /* "+file.name+" */; };\n";
  1701. add+=S+" "+XcodeID(file.copy )+" /* Mac: "+file.name+" */ = {isa = PBXBuildFile; fileRef = "+XcodeID(file.file)+" /* "+file.name+" */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n";
  1702. }
  1703. REPA(mac_frameworks)
  1704. {
  1705. XcodeFile &file=mac_frameworks[i];
  1706. add+=S+" "+XcodeID(file.build)+" /* Mac: "+file.name+" */ = {isa = PBXBuildFile; fileRef = "+XcodeID(file.file)+" /* "+file.name+" */; };\n";
  1707. }
  1708. REPA(ios_frameworks)
  1709. {
  1710. XcodeFile &file=ios_frameworks[i];
  1711. add+=S+" "+XcodeID(file.build)+" /* iOS: "+file.name+" */ = {isa = PBXBuildFile; fileRef = "+XcodeID(file.file)+" /* "+file.name+" */; };\n";
  1712. }
  1713. str.insert(pos, add);
  1714. // list frameworks
  1715. if(!GetXcodeProjTextPos(str, pos, "/* ESENTHEL MAC FRAMEWORKS */"))return false;
  1716. add.clear();
  1717. REPA(mac_frameworks)
  1718. {
  1719. XcodeFile &file=mac_frameworks[i];
  1720. // 0B9F062816CD7C2B006A0106 /* Engine.pak in Resources */,
  1721. add+=S+" "+XcodeID(file.build)+" /* "+file.name+" */,\n";
  1722. }
  1723. REPA(mac_dylibs)
  1724. {
  1725. XcodeFile &file=mac_dylibs[i];
  1726. // 0B9F062816CD7C2B006A0106 /* Engine.pak in Resources */,
  1727. add+=S+" "+XcodeID(file.build)+" /* "+file.name+" */,\n";
  1728. }
  1729. str.insert(pos, add);
  1730. if(!GetXcodeProjTextPos(str, pos, "/* ESENTHEL IOS FRAMEWORKS */"))return false;
  1731. add.clear();
  1732. REPA(ios_frameworks)
  1733. {
  1734. XcodeFile &file=ios_frameworks[i];
  1735. // 0B9F062816CD7C2B006A0106 /* Engine.pak in Resources */,
  1736. add+=S+" "+XcodeID(file.build)+" /* "+file.name+" */,\n";
  1737. }
  1738. str.insert(pos, add);
  1739. if(!GetXcodeProjTextPos(str, pos, "/* ESENTHEL ALL FRAMEWORKS */"))return false;
  1740. add.clear();
  1741. REPA(mac_frameworks)
  1742. {
  1743. XcodeFile &file=mac_frameworks[i];
  1744. // 0B9F062816CD7C2B006A0106 /* Engine.pak in Resources */,
  1745. add+=S+" "+XcodeID(file.file)+" /* "+file.name+" */,\n";
  1746. }
  1747. REPA(mac_dylibs)
  1748. {
  1749. XcodeFile &file=mac_dylibs[i];
  1750. // 0B9F062816CD7C2B006A0106 /* Engine.pak in Resources */,
  1751. add+=S+" "+XcodeID(file.file)+" /* "+file.name+" */,\n";
  1752. }
  1753. REPA(ios_frameworks)
  1754. {
  1755. XcodeFile &file=ios_frameworks[i];
  1756. // 0B9F062816CD7C2B006A0106 /* Engine.pak in Resources */,
  1757. add+=S+" "+XcodeID(file.file)+" /* "+file.name+" */,\n";
  1758. }
  1759. str.insert(pos, add);
  1760. // list dylibs
  1761. if(!GetXcodeProjTextPos(str, pos, "/* ESENTHEL MAC DYLIBS */"))return false;
  1762. add.clear();
  1763. REPA(mac_dylibs)
  1764. {
  1765. XcodeFile &file=mac_dylibs[i];
  1766. // 0B9F062816CD7C2B006A0106 /* Engine.pak in Resources */,
  1767. add+=S+" "+XcodeID(file.copy)+" /* "+file.name+" */,\n";
  1768. }
  1769. str.insert(pos, add);
  1770. // create folders ("groups")
  1771. SetTreeID(tree, file_id);
  1772. if(!GetXcodeProjTextPos(str, pos, "/* End PBXGroup section */"))return false;
  1773. add.clear();
  1774. FREPA(tree.children)SetGroups(tree.children[i], add);
  1775. str.insert(pos, add);
  1776. // list root elements
  1777. if(!GetXcodeProjTextPos(str, pos, "/* ESENTHEL ROOT */"))return false;
  1778. add.clear();
  1779. FREPA(tree.children)
  1780. {
  1781. // Sample:
  1782. // 0B80AFFF1238566900C08944 /* Main.cpp */,
  1783. Node<BuildFileElm> &node=tree.children[i];
  1784. add+=S+" "+XcodeID(node.id)+" /* "+Replace(node.base_name, '*', '\0')+" */,\n";
  1785. }
  1786. str.insert(pos, add);
  1787. if(!GetXcodeProjTextPos(str, pos, "/* ESENTHEL H CHILDREN */"))return false;
  1788. // ESENTHEL ASSETS CHILDREN, Sample:
  1789. // 0B9F060916CD7B80006A0106 /* Icon.png */,
  1790. if(!GetXcodeProjTextPos(str, pos, "/* ESENTHEL ASSETS CHILDREN */"))return false;
  1791. add.clear();
  1792. REPA(mac_assets)
  1793. {
  1794. XcodeFile &file=mac_assets[i];
  1795. add+=S+" "+XcodeID(file.file)+" /* "+file.name+" */,\n";
  1796. }
  1797. REPA(ios_images)
  1798. {
  1799. XcodeFile &file=ios_images[i];
  1800. add+=S+" "+XcodeID(file.file)+" /* "+file.name+" */,\n";
  1801. }
  1802. str.insert(pos, add);
  1803. if(!GetXcodeProjTextPos(str, pos, "/* ESENTHEL MAC EMBED */"))return false;
  1804. add.clear();
  1805. REPA(mac_assets)
  1806. {
  1807. XcodeFile &file=mac_assets[i];
  1808. // Sample:
  1809. // 0B9F062816CD7C2B006A0106 /* Engine.pak in Resources */,
  1810. add+=S+" "+XcodeID(file.build)+" /* "+file.name+" */,\n";
  1811. }
  1812. str.insert(pos, add);
  1813. if(!GetXcodeProjTextPos(str, pos, "/* ESENTHEL IOS EMBED */"))return false;
  1814. add.clear();
  1815. REPA(ios_images)
  1816. {
  1817. XcodeFile &file=ios_images[i];
  1818. // Sample:
  1819. // 0B9F062816CD7C2B006A0106 /* Engine.pak in Resources */,
  1820. add+=S+" "+XcodeID(file.build)+" /* "+file.name+" */,\n";
  1821. }
  1822. str.insert(pos, add);
  1823. if(!GetXcodeProjTextPos(str, pos, "/* ESENTHEL MAC SOURCE */"))return false;
  1824. add.clear();
  1825. REPA(build_files)
  1826. {
  1827. BuildFile &bf=build_files[i]; if(bf.mode==BuildFile::SOURCE)
  1828. {
  1829. // Sample:
  1830. // 0B9F063216CD7D29006A0106 /* Main.cpp in Sources */,
  1831. add+=S+" "+XcodeID(bf.xcode_mac_id)+" /* "+Replace(bf.dest_proj_path, '*', '\0')+" */,\n";
  1832. }
  1833. }
  1834. str.insert(pos, add);
  1835. if(!GetXcodeProjTextPos(str, pos, "/* ESENTHEL IOS SOURCE */"))return false;
  1836. add.clear();
  1837. REPA(build_files)
  1838. {
  1839. BuildFile &bf=build_files[i]; if(bf.mode==BuildFile::SOURCE)
  1840. {
  1841. // Sample:
  1842. // 0B9F063216CD7D29006A0106 /* Main.cpp in Sources */,
  1843. add+=S+" "+XcodeID(bf.xcode_ios_id)+" /* "+Replace(bf.dest_proj_path, '*', '\0')+" */,\n";
  1844. }
  1845. }
  1846. str.insert(pos, add);
  1847. // dirs
  1848. Str include_dirs; FREPA(dirs)include_dirs.line()+=S+'"'+CString(S+'"'+dirs[i]+'"')+"\",";
  1849. str=Replace(str, "/* ESENTHEL INCLUDE DIRS */", include_dirs);
  1850. // save 'pbxproj' file
  1851. FileText f; SetFile(f, str, UTF_8_NAKED); if(!OverwriteOnChangeLoud(f, build_project_file+"/project.pbxproj"))return false; // UTF_8_NAKED must be used as Xcode will fail with Byte Order Mark
  1852. // Xcode will not delete any useless existing files in App/Contents/Resources (what's even worse is that it will not replace old resources with new ones), on iOS files are not stored inside "Contents/Resources" but in root of the app folder, so generally just delete everything inside it
  1853. FDelInside(build_exe);
  1854. return true;
  1855. }
  1856. /******************************************************************************/
  1857. static Str UnixEncode(C Str &s)
  1858. {
  1859. Str o; o.reserve(s.length());
  1860. FREPA(s)
  1861. {
  1862. Char c=s[i];
  1863. if(c=='\\')o+='/';else
  1864. if(c==' ' || c=='#' || c=='&' || c=='(' || c==')' || c=='\'' || c=='`' || c=='=' || c==';'){o+='\\'; o+=c;}else
  1865. if(c=='$')o+="$$";else
  1866. o+=c;
  1867. }
  1868. return o;
  1869. }
  1870. /******************************************************************************/
  1871. Bool CodeEditor::generateAndroidProj()
  1872. {
  1873. FCreateDirs(build_path+"Android/jni");
  1874. FCreateDir (build_path+"Android/libs");
  1875. FCreateDir (build_path+"Android/src");
  1876. FCreateDirs(build_path+"Android/src/com/android/vending/billing");
  1877. FCreateDirs(build_path+"Android/res/layout");
  1878. FCreateDirs(build_path+"Android/res/values");
  1879. Str bin_path=BinPath(false),
  1880. android_path=bin_path+"Android/";
  1881. // assets
  1882. {
  1883. Bool want_proj_data=true; // cei().appPublishProjData(); always keep Project data, because even if it's not included, App data will be
  1884. // remove all unwanted
  1885. for(FileFind ff(build_path+"Android/assets"); ff(); )if(!(ff.name=="Engine.pak" || (want_proj_data && ff.name=="Project.pak")))FDel(ff.pathName()); // remove all except "Engine.pak" and "Project.pak"
  1886. // Engine.pak
  1887. FCreateDirs(build_path+"Android/assets");
  1888. if(!CopyFile(bin_path+"Mobile/Engine.pak", build_path+"Android/assets/Engine.pak"))return false;
  1889. }
  1890. // layouts
  1891. if(!CopyFile("Code/Android/LoaderLayout.xml", build_path+"Android/res/layout/loader.xml"))return false;
  1892. // resources
  1893. {
  1894. XmlData xml;
  1895. XmlNode &res=xml.nodes.New().setName("resources");
  1896. XmlNode &fb_app_id=res.nodes.New().setName("string"); fb_app_id.params.New().set("name", "app_id"); fb_app_id.data.add(S+cei().appFacebookAppID());
  1897. if(!OverwriteOnChangeLoud(xml, build_path+"Android/res/values/strings.xml"))return false;
  1898. }
  1899. // Android.mk
  1900. Str load_libraries;
  1901. {
  1902. Str lib_path=GetRelativePath(build_path+"Android/jni", android_path);
  1903. if(FullPath(lib_path) || Contains(lib_path, ' ')) // does not support full paths or paths with spaces - I've tried replacing spaces with "\ ", "$(space)", putting quotes around the full path "C:/Path 2/", and putting path in separate variable CUSTOM:=path and using it $(CUSTOM)/file, NONE of this worked !! so just copy files if needed and use relative path
  1904. {
  1905. lib_path=projects_build_path; lib_path.tailSlash(true); // set target to the same folder for all applications, which means that when using 'CopyFile' we will copy the EE android libs only once for all apps
  1906. Str p;
  1907. //#AndroidArchitecture
  1908. //p=android_path+"EsenthelEngine-armeabi.a" ; CopyFile(p, lib_path+SkipStartPath(p, android_path)); // copy from "Bin" to 'projects_build_path'
  1909. p=android_path+"EsenthelEngine-armeabi-v7a.a"; CopyFile(p, lib_path+SkipStartPath(p, android_path)); // copy from "Bin" to 'projects_build_path'
  1910. p=android_path+"EsenthelEngine-arm64-v8a.a" ; CopyFile(p, lib_path+SkipStartPath(p, android_path)); // copy from "Bin" to 'projects_build_path'
  1911. //p=android_path+"EsenthelEngine-x86.a" ; CopyFile(p, lib_path+SkipStartPath(p, android_path)); // copy from "Bin" to 'projects_build_path'
  1912. lib_path=GetRelativePath(build_path+"Android/jni", lib_path);
  1913. }
  1914. lib_path.tailSlash(false);
  1915. lib_path=UnixPath(lib_path);
  1916. Memc<Str> libs=GetFiles(cei().appLibsAndroid()),
  1917. dirs=GetFiles(cei().PLATFORM(appDirsWindows, appDirsNonWindows)());
  1918. Str ext_libs, ext_static_lib_names, ext_shared_lib_names, include_dirs;
  1919. FREPA(libs)
  1920. {
  1921. // libs can be specified to include "$(TARGET_ARCH_ABI)", for example "/path/$(TARGET_ARCH_ABI)/libXXX.so" or "/path/libXXX-$(TARGET_ARCH_ABI).so"
  1922. if(Contains(libs[i], ' '))return Error(S+"Library path may not contain spaces.\n\""+libs[i]+'"');
  1923. Str name=GetBaseNoExt(libs[i]);
  1924. name=Replace(name, "-$(TARGET_ARCH_ABI)", "");
  1925. name=Replace(name, "$(TARGET_ARCH_ABI)", "");
  1926. ext_libs.line()+=S+"# "+name;
  1927. ext_libs.line()+= "include $(CLEAR_VARS)";
  1928. ext_libs.line()+=S+"LOCAL_MODULE := "+name;
  1929. ext_libs.line()+=S+"LOCAL_SRC_FILES := "+libs[i];
  1930. if(GetExt(libs[i])=="so") // shared lib
  1931. {
  1932. if(!Starts(name, "lib"))return Error(S+"Shared library file names must start with \"lib\".\n\""+libs[i]+'"');
  1933. ext_shared_lib_names.space()+=name;
  1934. ext_libs.line()+="include $(PREBUILT_SHARED_LIBRARY)";
  1935. load_libraries.line()+=S+"System.loadLibrary(\""+SkipStart(name, "lib")+"\");";
  1936. }else // static lib
  1937. {
  1938. ext_static_lib_names.space()+=name;
  1939. ext_libs.line()+="include $(PREBUILT_STATIC_LIBRARY)";
  1940. }
  1941. }
  1942. FREPA(dirs)
  1943. {
  1944. include_dirs.space()+=S+"-I"+UnixEncode(dirs[i]);
  1945. }
  1946. FileText ft; if(!ft.read("Code/Android/Android.mk"))return ErrorRead("Code/Android/Android.mk");
  1947. Str data=ft.getAll();
  1948. data=Replace(data, "ESENTHEL_LIB_PATH" , lib_path , true, true);
  1949. data=Replace(data, "EXTERNAL_LIBS" , ext_libs , true, true);
  1950. data=Replace(data, "EXTERNAL_STATIC_LIB_NAMES", ext_static_lib_names, true, true);
  1951. data=Replace(data, "EXTERNAL_SHARED_LIB_NAMES", ext_shared_lib_names, true, true);
  1952. data=Replace(data, "INCLUDE_DIRS" , include_dirs , true, true);
  1953. SetFile(ft, data, UTF_8_NAKED); // does not support UTF
  1954. if(!OverwriteOnChangeLoud(ft, build_path+"Android/jni/Android.mk"))return false;
  1955. }
  1956. // Application.mk
  1957. {
  1958. FileText ft; ft.writeMem(UTF_8_NAKED); FileText src; if(!src.read("Code/Android/Application.mk"))return ErrorRead("Code/Android/Application.mk"); // does not support UTF
  1959. for(Str s; !src.end(); )
  1960. {
  1961. src.fullLine(s);
  1962. s=Replace(s, "RELEASE_CONDITION", build_debug ? "ifeq (false, true)" : "ifeq (true, true)", true, true);
  1963. s=Replace(s, "ABI", "armeabi-v7a arm64-v8a", true, true); // #AndroidArchitecture arm64-v8a x86
  1964. ft.putLine(s);
  1965. }
  1966. if(!OverwriteOnChangeLoud(ft, build_path+"Android/jni/Application.mk"))return false;
  1967. }
  1968. // Main.cpp
  1969. {
  1970. FileText main; main.writeMem();
  1971. FREPA(build_files)
  1972. {
  1973. BuildFile &bf=build_files[i];
  1974. if(bf.includeInProj() && bf.mode==BuildFile::SOURCE)
  1975. {
  1976. main.putLine(S+"#include \"../../"+UnixPath(bf.dest_proj_path)+'"');
  1977. }
  1978. }
  1979. if(!OverwriteOnChangeLoud(main, build_path+"Android/jni/Main.cpp"))return false;
  1980. }
  1981. // icons
  1982. Image icon, notification_icon; DateTime icon_time, notification_icon_time;
  1983. if(GetIcon(icon, icon_time))
  1984. {
  1985. File f; f.writeMem();
  1986. FCreateDir(build_path+"Android/res"); icon.transparentToNeighbor();
  1987. Memc<ImageConvert> convert;
  1988. CChar8 *rel;
  1989. // list images starting from the smallest
  1990. rel="Android/res/drawable-ldpi/icon.png" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).resize( 36, 36);
  1991. rel="Android/res/drawable-mdpi/icon.png" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).resize( 48, 48);
  1992. rel="Android/res/drawable-hdpi/icon.png" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).resize( 72, 72);
  1993. rel="Android/res/drawable-xhdpi/icon.png" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).resize( 96, 96);
  1994. rel="Android/res/drawable-xxhdpi/icon.png"; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, icon_time, 1))convert.New().set(build_path+rel, icon, icon_time).resize(144, 144);
  1995. // https://developer.android.com/guide/practices/ui_guidelines/icon_design_status_bar.html
  1996. GetNotificationIcon(notification_icon, notification_icon_time, icon, icon_time);
  1997. rel="Android/res/drawable-ldpi/notification.png" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, notification_icon_time, 1))convert.New().set(build_path+rel, notification_icon, notification_icon_time).resize(18, 18);
  1998. rel="Android/res/drawable-mdpi/notification.png" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, notification_icon_time, 1))convert.New().set(build_path+rel, notification_icon, notification_icon_time).resize(24, 24);
  1999. rel="Android/res/drawable-hdpi/notification.png" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, notification_icon_time, 1))convert.New().set(build_path+rel, notification_icon, notification_icon_time).resize(36, 36);
  2000. rel="Android/res/drawable-xhdpi/notification.png" ; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, notification_icon_time, 1))convert.New().set(build_path+rel, notification_icon, notification_icon_time).resize(48, 48);
  2001. rel="Android/res/drawable-xxhdpi/notification.png"; if(Compare(FileInfoSystem(build_path+rel).modify_time_utc, notification_icon_time, 1))convert.New().set(build_path+rel, notification_icon, notification_icon_time).resize(72, 72);
  2002. convert.reverseOrder(); // start working from the biggest ones because they take the most time, yes this is correct
  2003. MultiThreadedCall(convert, ImageConvert::Func);
  2004. FREPA(convert)if(!convert[i].ok)return ErrorWrite(convert[i].dest);
  2005. }
  2006. Str app_package=AndroidPackage(cei().appPackage());
  2007. // AndroidManifest.xml
  2008. XmlData xml;
  2009. if(!xml.load("Code/Android/AndroidManifest.xml"))return ErrorRead("Code/Android/AndroidManifest.xml");
  2010. if(XmlNode *manifest=xml.findNode("manifest"))
  2011. {
  2012. manifest->getParam("package" ).value=app_package;
  2013. manifest->getParam("android:versionCode" ).value=cei().appBuild();
  2014. manifest->getParam("android:versionName" ).value=cei().appBuild();
  2015. manifest->getParam("android:installLocation").value=((cei().appPreferredStorage()==STORAGE_EXTERNAL) ? "preferExternal" : (cei().appPreferredStorage()==STORAGE_AUTO) ? "auto" : "internalOnly");
  2016. XmlNode &application=manifest->getNode("application");
  2017. {
  2018. if(icon.is())application.getParam("android:icon" ).value="@drawable/icon";
  2019. application.getParam("android:label").value=CString(cei().appName()); // android expects this as a C String
  2020. // iterate activities
  2021. REPA(application.nodes)
  2022. {
  2023. XmlNode &node=application.nodes[i]; if(node.name=="activity")if(XmlParam *name=node.findParam("android:name"))
  2024. {
  2025. if(name->value=="EsenthelActivity" || name->value=="LoaderActivity")
  2026. {
  2027. node.getParam("android:label").value=CString(cei().appName()); // android expects this as a C String
  2028. // orientations
  2029. {
  2030. UInt flag =cei().appSupportedOrientations();
  2031. Bool landscape=FlagTest(flag, DIRF_X),
  2032. portrait =FlagTest(flag, DIRF_Y);
  2033. CChar8 *orn=null;
  2034. if( flag==(DIRF_X|DIRF_UP) )orn="sensor" ;else // up/left/right no down
  2035. if((flag&DIRF_X)==DIRF_RIGHT)orn="landscape" ;else // only one horizontal
  2036. if((flag&DIRF_Y)==DIRF_UP )orn="portrait" ;else // only one vertical
  2037. if( landscape && !portrait )orn="sensorLandscape";else
  2038. if(!landscape && portrait )orn="sensorPortrait" ;else
  2039. orn="fullSensor" ;
  2040. node.getParam("android:screenOrientation").value=orn;
  2041. }
  2042. }else
  2043. if(name->value=="com.facebook.LoginActivity")
  2044. {
  2045. node.getParam("android:label").value=CString(cei().appName()); // android expects this as a C String
  2046. }
  2047. }
  2048. }
  2049. }
  2050. }
  2051. if(!OverwriteOnChangeLoud(xml, build_path+"Android/AndroidManifest.xml"))return false;
  2052. // libraries (such as Google Play Services, Facebook, etc) always need to be copied
  2053. // because while building, some files get modified/added which would change the "Editor/Bin" in conflict with auto update functionality, and additionally "local.properties" needs to be manually generated
  2054. android_path="Code/Android/"; // this is inside "Editor.pak"
  2055. Str android_libs_path=Str(projects_build_path).tailSlash(true)+"_Android_\\"; FCreateDir(android_libs_path); // path where to store Android libs "_Build_\_Android_\"
  2056. CChar8 *android_libs[]=
  2057. {
  2058. // Google Play Services
  2059. "play-services-ads-lite",
  2060. "play-services-auth-base",
  2061. "play-services-base",
  2062. "play-services-basement",
  2063. "play-services-drive",
  2064. "play_apk_expansion", // allows downloading APK expansions
  2065. "market_licensing", // needed for "play_apk_expansion"
  2066. "facebook",
  2067. };
  2068. // local.properties
  2069. FileText local; local.writeMem(UTF_8_NAKED); local.putLine(S+"sdk.dir="+UnixPath(android_sdk));
  2070. if(!OverwriteOnChangeLoud(local, build_path+"Android/local.properties"))return false;
  2071. // project.properties
  2072. {
  2073. FileText project; project.writeMem(UTF_8_NAKED);
  2074. project.putLine("target=android-18");
  2075. FREPA(android_libs) // process in order
  2076. {
  2077. Str src_path=android_path+android_libs[i], dest_path=android_libs_path+android_libs[i];
  2078. if(C PaksFile *pf=Paks.find(src_path)){if(!FCopy(*pf->pak, *pf->file, dest_path, FILE_OVERWRITE_DIFFERENT))return ErrorWrite(dest_path);}else return ErrorRead(src_path); // copy from "Editor.pak" to 'android_libs_path'
  2079. project.putLine(S+"android.library.reference."+(i+1)+"="+UnixPath(GetRelativePath(build_path+"Android", dest_path)));
  2080. if(!OverwriteOnChangeLoud(local, dest_path+"/local.properties"))return false;
  2081. }
  2082. if(!OverwriteOnChangeLoud(project, build_path+"Android/project.properties"))return false;
  2083. }
  2084. if(!CopyFile(android_path+"chartboost.jar", build_path+"Android/libs/chartboost.jar"))return false;
  2085. // build.xml
  2086. if(!xml.load("Code/Android/build.xml"))return ErrorRead("Code/Android/build.xml");
  2087. if(XmlNode *project=xml.findNode("project"))
  2088. {
  2089. project->getParam("name").value=build_project_name;
  2090. }
  2091. if(!OverwriteOnChangeLoud(xml, build_path+"Android/build.xml"))return false;
  2092. // IInAppBillingService.aidl
  2093. if(!CopyFile("Code/Android/IInAppBillingService.aidl", build_path+"Android/src/com/android/vending/billing/IInAppBillingService.aidl"))return false;
  2094. // EsenthelActivity.java
  2095. {
  2096. FileText ft; if(!ft.read("Code/Android/EsenthelActivity.java"))return ErrorRead("Code/Android/EsenthelActivity.java");
  2097. Str data=ft.getAll();
  2098. data=Replace(data, "EE_PACKAGE" , app_package , true, true);
  2099. data=Replace(data, "EE_APP_NAME" , CString(cei().appName()) , true, true);
  2100. data=Replace(data, "EE_LICENSE_KEY" , cei().appLicenseKey (), true, true);
  2101. data=Replace(data, "EE_INIT_CHARTBOOST" , TextBool(cei().appChartboostAppIDGooglePlay().is() && cei().appChartboostAppSignatureGooglePlay().is()), true, true);
  2102. data=Replace(data, "EE_CHARTBOOST_APP_ID" , cei().appChartboostAppIDGooglePlay (), true, true);
  2103. data=Replace(data, "EE_CHARTBOOST_APP_SIGNATURE", cei().appChartboostAppSignatureGooglePlay(), true, true);
  2104. data=Replace(data, "EE_LOAD_LIBRARIES" , load_libraries , true, true);
  2105. SetFile(ft, data, UTF_8_NAKED);
  2106. if(!OverwriteOnChangeLoud(ft, build_path+"Android/src/EsenthelActivity.java"))return false;
  2107. }
  2108. // LoaderActivity.java
  2109. {
  2110. FileText ft; if(!ft.read("Code/Android/LoaderActivity.java"))return ErrorRead("Code/Android/LoaderActivity.java");
  2111. Str data=ft.getAll();
  2112. data=Replace(data, "EE_PACKAGE" , app_package , true, true);
  2113. data=Replace(data, "EE_LICENSE_KEY" , cei().appLicenseKey(), true, true);
  2114. data=Replace(data, "EE_DOWNLOAD_EXPANSION", TextBool(cei().appAndroidExpansion() && cei().appLicenseKey().is()), true, true); // require license key provided, because otherwise the Java codes will crash throwing an exception
  2115. SetFile(ft, data, UTF_8_NAKED);
  2116. if(!OverwriteOnChangeLoud(ft, build_path+"Android/src/LoaderActivity.java"))return false;
  2117. }
  2118. if(!CopyFile("Code/Android/Native.java", build_path+"Android/src/Native.java"))return false;
  2119. if(!CopyFile("Code/Android/Base64.java", build_path+"Android/src/Base64.java"))return false;
  2120. return true;
  2121. }
  2122. /******************************************************************************/
  2123. Bool CodeEditor::generateLinuxMakeProj()
  2124. {
  2125. FCreateDirs(build_path+"nbproject");
  2126. FileText f; Str s;
  2127. Str bin_path=BinPath(),
  2128. EE_APP_NAME=UnixEncode(GetBase(build_exe)),
  2129. EE_LIB_PATH=UnixEncode(bin_path+"EsenthelEngine.a"),
  2130. EXTERNAL_LIBS,
  2131. EE_HEADER_PATH,
  2132. EE_OBJ_FILES,
  2133. EE_CPP_FILES_Debug, EE_CPP_FILES_Release;
  2134. Memc<Str> libs=GetFiles(cei().appLibsLinux());
  2135. if(cei().appPublishSteamDll ())libs.add("Bin/libsteam_api.so" ); // this must be relative to the EXE because this path will be embedded in the executable ("Bin/" is needed because without it building will fail)
  2136. if(cei().appPublishOpenVRDll())libs.add("Bin/libopenvr_api.so"); // this must be relative to the EXE because this path will be embedded in the executable ("Bin/" is needed because without it building will fail)
  2137. FREPA(libs)EXTERNAL_LIBS.space()+=UnixEncode(libs[i]);
  2138. Memc<Str> dirs=GetFiles(cei().appDirsNonWindows());
  2139. FREPA(dirs)EE_HEADER_PATH.space()+=S+"-I"+UnixEncode(dirs[i]);
  2140. FREPA(build_files)
  2141. {
  2142. BuildFile &bf=build_files[i];
  2143. if(bf.includeInProj() && bf.mode==BuildFile::SOURCE)
  2144. {
  2145. Str obj_name=S+"${OBJECTDIR}/"+UnixEncode(GetBase(bf.ext_not ))+".o",
  2146. src_name= UnixEncode( bf.dest_proj_path);
  2147. EE_OBJ_FILES+='\t'; EE_OBJ_FILES+=obj_name;
  2148. if(InRange(i+1, build_files))EE_OBJ_FILES+=" \\";
  2149. EE_OBJ_FILES+='\n';
  2150. EE_CPP_FILES_Debug+=obj_name+": "+src_name+'\n'; // ${OBJECTDIR}/_ext/1728301206/Auto.o: ../Source/Auto.cpp
  2151. EE_CPP_FILES_Debug+="\t${MKDIR} -p ${OBJECTDIR}\n"; // ${MKDIR} -p ${OBJECTDIR}
  2152. EE_CPP_FILES_Debug+="\t${RM} \"[email protected]\"\n"; // ${RM} "[email protected]"
  2153. EE_CPP_FILES_Debug+=S+"\t$(COMPILE.cc) -g -DDEBUG=1 -I. "+EE_HEADER_PATH+" -std=c++14 -MMD -MP -MF \"[email protected]\" -o "+obj_name+' '+src_name+'\n'; // $(COMPILE.cc) -g -DDEBUG -MMD -MP -MF "[email protected]" -o ${OBJECTDIR}/_ext/1728301206/Auto.o ../Source/Auto.cpp
  2154. EE_CPP_FILES_Debug+='\n';
  2155. EE_CPP_FILES_Release+=obj_name+": "+src_name+'\n'; // ${OBJECTDIR}/_ext/1728301206/Auto.o: ../Source/Auto.cpp
  2156. EE_CPP_FILES_Release+="\t${MKDIR} -p ${OBJECTDIR}\n"; // ${MKDIR} -p ${OBJECTDIR}
  2157. EE_CPP_FILES_Release+="\t${RM} \"[email protected]\"\n"; // ${RM} "[email protected]"
  2158. EE_CPP_FILES_Release+=S+"\t$(COMPILE.cc) -DDEBUG=0 -O3 -I. "+EE_HEADER_PATH+" -std=c++14 -MMD -MP -MF \"[email protected]\" -o "+obj_name+' '+src_name+'\n'; // $(COMPILE.cc) -O3 -MMD -MP -MF "[email protected]" -o ${OBJECTDIR}/_ext/1728301206/Auto.o ../Source/Auto.cpp
  2159. EE_CPP_FILES_Release+='\n';
  2160. }
  2161. }
  2162. if(!f.read("Code/Linux/Makefile"))return ErrorRead("Code/Linux/Makefile"); f.getAll(s);
  2163. s=Replace(s, "EE_HEADER_PATH", EE_HEADER_PATH, true, true); SetFile(f, s, UTF_8_NAKED); if(!OverwriteOnChangeLoud(f, build_path+"Makefile"))return false;
  2164. if(!f.read("Code/Linux/nbproject/Makefile-impl.mk"))return ErrorRead("Code/Linux/nbproject/Makefile-impl.mk"); f.getAll(s);
  2165. s=Replace(s, "EE_APP_NAME", EE_APP_NAME, true, true); SetFile(f, s, UTF_8_NAKED); if(!OverwriteOnChangeLoud(f, build_path+"nbproject/Makefile-impl.mk"))return false;
  2166. if(!f.read("Code/Linux/nbproject/Makefile-variables.mk"))return ErrorRead("Code/Linux/nbproject/Makefile-variables.mk"); f.getAll(s);
  2167. s=Replace(s, "EE_APP_NAME", EE_APP_NAME, true, true); SetFile(f, s, UTF_8_NAKED); if(!OverwriteOnChangeLoud(f, build_path+"nbproject/Makefile-variables.mk"))return false;
  2168. if(!f.read("Code/Linux/nbproject/Package-Debug.bash"))return ErrorRead("Code/Linux/nbproject/Package-Debug.bash"); f.getAll(s);
  2169. s=Replace(s, "EE_APP_NAME", EE_APP_NAME, true, true); SetFile(f, s, UTF_8_NAKED); if(!OverwriteOnChangeLoud(f, build_path+"nbproject/Package-Debug.bash"))return false;
  2170. if(!f.read("Code/Linux/nbproject/Package-Release.bash"))return ErrorRead("Code/Linux/nbproject/Package-Release.bash"); f.getAll(s);
  2171. s=Replace(s, "EE_APP_NAME", EE_APP_NAME, true, true); SetFile(f, s, UTF_8_NAKED); if(!OverwriteOnChangeLoud(f, build_path+"nbproject/Package-Release.bash"))return false;
  2172. if(!f.read("Code/Linux/nbproject/Makefile-Debug.mk"))return ErrorRead("Code/Linux/nbproject/Makefile-Debug.mk"); f.getAll(s);
  2173. s=Replace(s, "EE_APP_NAME" , EE_APP_NAME , true, true);
  2174. s=Replace(s, "EE_LIB_PATH" , EE_LIB_PATH , true, true);
  2175. s=Replace(s, "EE_HEADER_PATH", EE_HEADER_PATH , true, true);
  2176. s=Replace(s, "EE_OBJ_FILES" , EE_OBJ_FILES , true, true);
  2177. s=Replace(s, "EE_CPP_FILES" , EE_CPP_FILES_Debug, true, true);
  2178. s=Replace(s, "EXTERNAL_LIBS" , EXTERNAL_LIBS , true, true);
  2179. SetFile(f, s, UTF_8_NAKED); if(!OverwriteOnChangeLoud(f, build_path+"nbproject/Makefile-Debug.mk"))return false;
  2180. if(!f.read("Code/Linux/nbproject/Makefile-Release.mk"))return ErrorRead("Code/Linux/nbproject/Makefile-Release.mk"); f.getAll(s);
  2181. s=Replace(s, "EE_APP_NAME" , EE_APP_NAME , true, true);
  2182. s=Replace(s, "EE_LIB_PATH" , EE_LIB_PATH , true, true);
  2183. s=Replace(s, "EE_HEADER_PATH", EE_HEADER_PATH , true, true);
  2184. s=Replace(s, "EE_OBJ_FILES" , EE_OBJ_FILES , true, true);
  2185. s=Replace(s, "EE_CPP_FILES" , EE_CPP_FILES_Release, true, true);
  2186. s=Replace(s, "EXTERNAL_LIBS" , EXTERNAL_LIBS , true, true);
  2187. SetFile(f, s, UTF_8_NAKED); if(!OverwriteOnChangeLoud(f, build_path+"nbproject/Makefile-Release.mk"))return false;
  2188. #if LINUX // do this only on Linux because only on Linux SDK the *.so files are available
  2189. Str bin_dest=build_path+"Bin/";
  2190. #if PHYSX_DLL // PhysX DLL's
  2191. if(cei().appPublishPhysxDll()) // this must be copied always (unlike for Windows where it's copied only for publishing), because on Windows we can specify a custom path for the DLL's, however on Linux it needs to be hardcoded to "./Bin/", so everytime we want to start an app, it needs to have the .so files in the Bin relative to the executable
  2192. {
  2193. FCreateDir(bin_dest);
  2194. CChar8 *dlls[]=
  2195. {
  2196. "libPhysX3_x64.so",
  2197. "libPhysX3Common_x64.so",
  2198. "libPhysX3Cooking_x64.so",
  2199. "libPxFoundation_x64.so",
  2200. };
  2201. FREPA(dlls)if(!CopyFile(bin_path+dlls[i], bin_dest+dlls[i]))return false;
  2202. }
  2203. #endif
  2204. // Steam DLL's
  2205. if(cei().appPublishSteamDll()) // this must be copied always, because libs on Linux need to be hardcoded to "./Bin/", so everytime we want to start an app, it needs to have the .so files in the Bin relative to the executable
  2206. {
  2207. FCreateDir(bin_dest);
  2208. CChar8 *dlls[]=
  2209. {
  2210. "libsteam_api.so",
  2211. };
  2212. FREPA(dlls)if(!CopyFile(bin_path+dlls[i], bin_dest+dlls[i]))return false;
  2213. }
  2214. // OpenVR DLL's
  2215. if(cei().appPublishOpenVRDll()) // this must be copied always because libs on Linux need to be hardcoded to "./Bin/", so everytime we want to start an app, it needs to have the .so files in the Bin relative to the executable
  2216. {
  2217. FCreateDir(bin_dest);
  2218. CChar8 *dlls[]=
  2219. {
  2220. "libopenvr_api.so",
  2221. };
  2222. FREPA(dlls)if(!CopyFile(bin_path+dlls[i], bin_dest+dlls[i]))return false;
  2223. }
  2224. #endif
  2225. // embed resources
  2226. Image icon; DateTime icon_time;
  2227. if(GetIcon(icon, icon_time))
  2228. {
  2229. File f; f.writeMem();
  2230. BuildEmbed &be=build_embed.New().set(CC4('I', 'C', 'O', 'N'), build_path+"Assets/Icon.webp");
  2231. if(Compare(FileInfoSystem(be.path).modify_time_utc, icon_time, 1)){icon.ExportWEBP(f.reset()); if(!OverwriteOnChangeLoud(f, be.path))return false; FTimeUTC(be.path, icon_time);}
  2232. }
  2233. if(cei().appEmbedEngineData())
  2234. {
  2235. BuildEmbed &be=build_embed.New().set(CC4('P', 'A', 'K', 0), build_path+"Assets/EngineEmbed.pak");
  2236. if(!CreateEngineEmbedPak(be.path))return false;
  2237. }
  2238. Str app_pak_path=build_path+"Assets/App.pak";
  2239. Bool exists; if(!CreateAppPak(app_pak_path, exists))return false;
  2240. if( exists)build_embed.New().set(CC4('P', 'A', 'K', 0), app_pak_path);
  2241. // we need to recreate the exe everytime in case we're embedding app resources manually
  2242. FDelFile(build_exe);
  2243. return true;
  2244. }
  2245. static void StoreTree(Node<BuildFileElm> &nodes, Str &EE_APP_ITEMS)
  2246. {
  2247. FREPA(nodes.children)
  2248. {
  2249. Node<BuildFileElm> &node=nodes.children[i];
  2250. if(node.file())
  2251. {
  2252. EE_APP_ITEMS+=S+"<itemPath>"+XmlString(UnixPath(node.full_name))+"</itemPath>\n";
  2253. }else
  2254. {
  2255. EE_APP_ITEMS+=S+"<logicalFolder name=\""+XmlString(node.base_name)+"\" displayName=\""+XmlString(node.base_name)+"\" projectFiles=\"true\">\n";
  2256. StoreTree(node, EE_APP_ITEMS);
  2257. EE_APP_ITEMS+="</logicalFolder>\n";
  2258. }
  2259. }
  2260. }
  2261. Bool CodeEditor::generateLinuxNBProj()
  2262. {
  2263. if(generateLinuxMakeProj())
  2264. {
  2265. FileText f; Str s;
  2266. Str bin_path=BinPath(),
  2267. EE_APP_NAME =XmlString(build_project_name),
  2268. EE_APP_NAME_SAFE=XmlString(CleanNameForNetBeans(GetBase(build_exe))),
  2269. EE_LIB_PATH=XmlString(UnixPath(bin_path+"EsenthelEngine.a")),
  2270. EXTERNAL_LIBS,
  2271. EE_HEADER_PATH,
  2272. EE_APP_ITEMS,
  2273. EE_APP_FILES;
  2274. Node<BuildFileElm> tree; FREPA(build_files)
  2275. {
  2276. BuildFile &bf=build_files[i];
  2277. if(bf.mode==BuildFile::SOURCE)
  2278. {
  2279. BuildTree(tree, bf, bf.dest_proj_path);
  2280. EE_APP_FILES+=S+"<item path=\""+XmlString(UnixPath(bf.dest_proj_path))+"\" ex=\"false\" tool=\"1\" flavor2=\"0\" />\n"; // tool(CPP=1, H=3)
  2281. }
  2282. }
  2283. StoreTree(tree, EE_APP_ITEMS);
  2284. Memc<Str> libs=GetFiles(cei().appLibsLinux());
  2285. if(cei().appPublishSteamDll ())libs.add(bin_path+ "libsteam_api.so");
  2286. if(cei().appPublishOpenVRDll())libs.add(bin_path+"libopenvr_api.so");
  2287. FREPA(libs)EXTERNAL_LIBS+=S+"<linkerLibFileItem>"+XmlString(libs[i])+"</linkerLibFileItem>\n";
  2288. Memc<Str> dirs=GetFiles(cei().appDirsNonWindows());
  2289. FREPA(dirs)EE_HEADER_PATH+=S+"<pElem>"+XmlString(dirs[i])+"</pElem>\n";
  2290. if(!f.read("Code/Linux/nbproject/configurations.xml"))return ErrorRead("Code/Linux/nbproject/configurations.xml"); f.getAll(s);
  2291. s=Replace(s, "EE_APP_NAME" , EE_APP_NAME_SAFE, true, true);
  2292. s=Replace(s, "EE_LIB_PATH" , EE_LIB_PATH , true, true);
  2293. s=Replace(s, "EE_HEADER_PATH", EE_HEADER_PATH , true, true);
  2294. s=Replace(s, "EE_APP_ITEMS" , EE_APP_ITEMS , true, true);
  2295. s=Replace(s, "EE_APP_FILES" , EE_APP_FILES , true, true);
  2296. s=Replace(s, "EXTERNAL_LIBS" , EXTERNAL_LIBS , true, true);
  2297. SetFile(f, s); if(!OverwriteOnChangeLoud(f, build_path+"nbproject/configurations.xml"))return false;
  2298. if(!f.read("Code/Linux/nbproject/project.xml"))return ErrorRead("Code/Linux/nbproject/project.xml"); f.getAll(s);
  2299. s=Replace(s, "EE_APP_NAME", EE_APP_NAME, true, true);
  2300. SetFile(f, s); if(!OverwriteOnChangeLoud(f, build_path+"nbproject/project.xml"))return false;
  2301. return true;
  2302. }
  2303. return false;
  2304. }
  2305. /******************************************************************************/
  2306. static EXPORT_MODE ExeToMode(EXE_TYPE exe)
  2307. {
  2308. switch(exe)
  2309. {
  2310. default: return EXPORT_VS;
  2311. case EXE_APK: return EXPORT_ANDROID;
  2312. case EXE_MAC:
  2313. case EXE_IOS: return EXPORT_XCODE;
  2314. case EXE_LINUX: return EXPORT_LINUX_NETBEANS;
  2315. }
  2316. }
  2317. Bool CodeEditor::Export(EXPORT_MODE mode, BUILD_MODE build_mode)
  2318. {
  2319. stopBuild();
  2320. build_exe_type=config_exe;
  2321. switch(mode) // override EXE type when exporting
  2322. {
  2323. case EXPORT_LINUX_NETBEANS:
  2324. case EXPORT_LINUX_MAKE : build_exe_type=EXE_LINUX; break;
  2325. case EXPORT_ANDROID : build_exe_type=EXE_APK ; break;
  2326. case EXPORT_XCODE : if(build_exe_type!=EXE_MAC && build_exe_type!=EXE_IOS)build_exe_type=EXE_MAC; break;
  2327. // no need to check for VS as it will just fail with a message box about exe/dll/web support only
  2328. }
  2329. if(build_exe_type==EXE_DLL && (build_mode==BUILD_PLAY || build_mode==BUILD_DEBUG))build_mode=BUILD_BUILD;
  2330. T.build_mode =build_mode;
  2331. build_debug =((build_mode==BUILD_PUBLISH) ? false : config_debug); // set before 'verifyBuildPath' and before creating android project
  2332. build_windows_code_sign=((build_mode==BUILD_PUBLISH) && build_exe_type==EXE_EXE && options.authenticode()/*cei().appWindowsCodeSign()*/);
  2333. build_project_id =cei().projectID();
  2334. if(!verifyBuildPath())Error("No application selected or it has invalid name.");else
  2335. {
  2336. if(mode==EXPORT_EXE)mode=ExeToMode(build_exe_type);
  2337. if(mode==EXPORT_VS)
  2338. {
  2339. // #VisualStudio
  2340. if(!verifyVS())return false; // this must be checked to validate currently installed VS
  2341. /*if(devenv_version.x== 9)mode=EXPORT_VS2008;else
  2342. if(devenv_version.x==10)mode=EXPORT_VS2010;else
  2343. if(devenv_version.x==11)mode=EXPORT_VS2012;else
  2344. if(devenv_version.x==12)mode=EXPORT_VS2013;else*/
  2345. if(devenv_version.x==14)mode=EXPORT_VS2015;else
  2346. if(devenv_version.x==15)mode=EXPORT_VS2017;else return false;
  2347. }else
  2348. if(mode==EXPORT_ANDROID ){if(!verifyAndroid ())return false;}else
  2349. if(mode==EXPORT_XCODE ){if(!verifyXcode ())return false;}else
  2350. if(mode==EXPORT_LINUX_MAKE ){if(!verifyLinuxMake())return false;}else
  2351. if(mode==EXPORT_LINUX_NETBEANS){if(!verifyLinuxMake())return false;} // ignore checks for NetBeans as we want to create the project even if there's no NetBeans installed
  2352. Memc<Message> msgs;
  2353. if(mode==EXPORT_TXT){if(generateTXT(msgs))return true;}else
  2354. {
  2355. init();
  2356. Memc<Symbol*> sorted_classes;
  2357. if(verifyBuildFiles(msgs))if(CodeEnvironment::VerifySymbols(msgs, sorted_classes))if(adjustBuildFiles())if(generateCPPH(sorted_classes, mode))
  2358. {
  2359. // #VisualStudio
  2360. if(mode==EXPORT_CPP )return true;
  2361. /*if(mode==EXPORT_VS2008 )return generateVSProj ( 9);
  2362. if(mode==EXPORT_VS2010 )return generateVSProj (10);
  2363. if(mode==EXPORT_VS2012 )return generateVSProj (11);
  2364. if(mode==EXPORT_VS2013 )return generateVSProj (12);*/
  2365. if(mode==EXPORT_VS2015 )return generateVSProj (14);
  2366. if(mode==EXPORT_VS2017 )return generateVSProj (15);
  2367. if(mode==EXPORT_XCODE )return generateXcodeProj ();
  2368. if(mode==EXPORT_ANDROID )return generateAndroidProj ();
  2369. if(mode==EXPORT_LINUX_MAKE )return generateLinuxMakeProj();
  2370. if(mode==EXPORT_LINUX_NETBEANS)return generateLinuxNBProj ();
  2371. }
  2372. }
  2373. if(msgs.elms())
  2374. {
  2375. buildClear ( );
  2376. buildNew (msgs);
  2377. buildUpdate( );
  2378. }
  2379. }
  2380. return false;
  2381. }
  2382. /******************************************************************************/
  2383. void CodeEditor::stopBuild()
  2384. {
  2385. build_phase =build_step =-1;
  2386. build_phases =build_steps= 0;
  2387. build_log .clear();
  2388. build_exe .clear();
  2389. build_embed .clear();
  2390. build_package .clear();
  2391. build_output .clear();
  2392. build_line_maps .clear();
  2393. build_process .del ();
  2394. build_project_id.zero ();
  2395. }
  2396. void CodeEditor::killBuild()
  2397. {
  2398. build_process.stop();
  2399. if(build_process.active())
  2400. {
  2401. if(!build_process.wait(100))build_process.kill();
  2402. buildNew().set("Stopped on user request");
  2403. buildUpdate();
  2404. }
  2405. stopBuild();
  2406. }
  2407. /******************************************************************************/
  2408. void CodeEditor::build(BUILD_MODE mode)
  2409. {
  2410. if((mode==BUILD_PLAY || mode==BUILD_PUBLISH) && (config_exe==EXE_NEW || config_exe==EXE_IOS)){openIDE(); return;} // Play/Publish for WindowsNew and iOS must be done from the IDE
  2411. if(Export(EXPORT_EXE, mode))
  2412. {
  2413. build_list.highlight_line=-1;
  2414. build_list.highlight_time= 0;
  2415. build_refresh=0;
  2416. build_phase =build_step =0;
  2417. build_phases =build_steps=0;
  2418. Int build_threads=Cpu.threads();
  2419. if(build_exe_type==EXE_EXE || build_exe_type==EXE_DLL || build_exe_type==EXE_LIB || build_exe_type==EXE_NEW || build_exe_type==EXE_WEB)
  2420. {
  2421. build_phases=1+build_windows_code_sign;
  2422. build_steps =3+build_windows_code_sign; FREPA(build_files)if(build_files[i].mode==BuildFile::SOURCE)build_steps++; // stdafx.cpp, linking, wait for end, *.cpp
  2423. Str config=(build_debug ? "Debug" : "Release");
  2424. if(build_exe_type==EXE_NEW)config+=" Universal";
  2425. if(build_exe_type==EXE_WEB)config+=" DX9" ;else // always use the same config for WEB because it uses WebGL, warning: this must match codes above: (build_debug ? "Debug DX9/" : "Release DX9/")
  2426. if(build_exe_type==EXE_NEW)config+=" DX11";else // always use the same config for WEB because it uses WebGL, warning: this must match codes above: (build_debug ? "Debug DX9/" : "Release DX9/")
  2427. config+=(config_dx9 ? " DX9" : " DX11");
  2428. Str platform=((build_exe_type==EXE_WEB) ? "4) Web" : config_32_bit ? "2) 32 bit" : "1) 64 bit");
  2429. build_msbuild=false;
  2430. Str msbuild; if(devenv_version.x>=10 && build_exe_type!=EXE_WEB){msbuild=MSBuildPath(vs_path, devenv_version); if(!FExistSystem(msbuild))msbuild.clear();} // detect MSBuild for VS 2010 projects or newer (MSBuild does not support VS 2008 projects and Web)
  2431. if( msbuild.is())
  2432. {
  2433. Str params=MSBuildParams(build_project_file, config, platform);
  2434. if(build_exe_type==EXE_NEW)params.space()+="/p:AppxPackageSigningEnabled=false"; // disable code signing for Windows Universal builds, otherwise build will fail
  2435. build_msbuild=build_process.create(msbuild, params);
  2436. }
  2437. if(!build_msbuild)
  2438. {
  2439. if(!devenv_com){build_log=build_path+"build_log.txt"; FDelFile(build_log);} // if we have "devenv.com" then we get the output from the console (devenv.exe does not generate any output)
  2440. VSBuild(build_project_file, config, platform, build_log);
  2441. }
  2442. }else
  2443. if(build_exe_type==EXE_LINUX)
  2444. {
  2445. build_phases=1;
  2446. build_steps =3; FREPA(build_files)if(build_files[i].mode==BuildFile::SOURCE)build_steps++; // stdafx.cpp, linking, wait for end, *.cpp
  2447. if(!build_process.create("make", LinuxBuildParams(build_path, build_debug ? "Debug" : "Release", build_threads)))Error("Error launching \"make\" system command.\nPlease make sure you have C++ building available.");
  2448. }else
  2449. if(build_exe_type==EXE_MAC || build_exe_type==EXE_IOS)
  2450. {
  2451. build_phases=1;
  2452. build_steps =3; FREPA(build_files)if(build_files[i].mode==BuildFile::SOURCE)build_steps++; // stdafx.cpp, linking, wait for end, *.cpp
  2453. if(!build_process.create("xcodebuild", XcodeBuildParams(build_project_file, build_debug ? "Debug" : "Release", (build_exe_type==EXE_MAC) ? "Mac" : "iOS"))) // sdk "iphonesimulator"
  2454. Error("Error launching \"xcodebuild\" system command.\nPlease make sure you have Xcode installed.");
  2455. }else
  2456. if(build_exe_type==EXE_APK)
  2457. {
  2458. Bool sign=!build_debug; // we want to sign with our own certificate (note that this can be done only in RELEASE mode, because in DEBUG, Android SDK will automatically sign the package with its own Debug certificate, and our own signing will fail)
  2459. if( sign)
  2460. {
  2461. // certificate file, certificate password, JDK
  2462. if(!cert_file.is() || !FExistSystem(cert_file) ){options.activateCert (); Error("Certificate file was not specified or was not found."); return;}
  2463. if( cert_pass.length()<6 ){options.activateCert (); Error("Certificate password must be at least 6 characters long."); return;}
  2464. #if WINDOWS
  2465. if(!jdk_path.is() || !FExistSystem(jdk_path.tailSlash(true)+"bin/jarsigner.exe")){options.activatePaths(); Error("Path to Java Development Kit was not specified or is invalid."); return;}
  2466. #endif
  2467. }
  2468. if(build_mode==BUILD_PLAY || build_mode==BUILD_DEBUG)Run(adbPath(), "start-server", true); // if we're going to launch the app then make sure that the ADB server is running, or else custom launched ADB processess will never exit
  2469. build_steps =0;
  2470. build_phases=1+1+sign+(build_debug ? 0 : 1)+((build_mode==BUILD_PLAY || build_mode==BUILD_DEBUG) ? 2 : 0); // ndk-build (compile) + ant (link) + jarsigner + zipalign (in release) + adb (force_stop) + adb (install)
  2471. //if(1){build_log=build_path+"build_log.txt"; FDelFile(build_log);} this causes some freeze during building?
  2472. build_package=AndroidPackage(cei().appPackage());
  2473. build_process.create(ndkBuildPath(), S+"-j"+build_threads+" -C \""+build_path+"Android\""+(build_log.is() ? S+" > \""+build_log+'"' : S));
  2474. // delete the libs so later if they exist then we can conclude that the build was successfull #AndroidArchitecture
  2475. //FDelFile(build_path+"Android/libs/armeabi/libProject.so");
  2476. FDelFile(build_path+"Android/libs/armeabi-v7a/libProject.so");
  2477. FDelFile(build_path+"Android/libs/arm64-v8a/libProject.so");
  2478. FDelFile(build_path+"Android/libs/x86/libProject.so");
  2479. }
  2480. buildClear ();
  2481. buildUpdate();
  2482. }
  2483. }
  2484. /******************************************************************************/
  2485. void CodeEditor::play()
  2486. {
  2487. build(BUILD_PLAY);
  2488. }
  2489. /******************************************************************************/
  2490. void CodeEditor::debug()
  2491. {
  2492. //build(BUILD_DEBUG);
  2493. switch(config_exe)
  2494. {
  2495. case EXE_EXE:
  2496. case EXE_NEW: if(Export(EXPORT_VS, BUILD_DEBUG))VSRun(build_project_file, S); break;
  2497. case EXE_DLL: build(); break;
  2498. default: play(); break;
  2499. }
  2500. }
  2501. /******************************************************************************/
  2502. void CodeEditor::openIDE()
  2503. {
  2504. #if WINDOWS
  2505. if(Export(EXPORT_VS , BUILD_IDE))VSOpen(build_project_file);
  2506. #elif MAC
  2507. if(Export(EXPORT_XCODE , BUILD_IDE))Run (build_project_file);
  2508. #elif LINUX
  2509. if(verifyLinuxNetBeans())if(Export(EXPORT_LINUX_NETBEANS, BUILD_IDE))Run (Str(netbeans_path).tailSlash(true)+"bin/netbeans", S+"--open \""+build_path+'"');
  2510. #endif
  2511. }
  2512. /******************************************************************************/
  2513. void CodeEditor::runToCursor()
  2514. {
  2515. // TODO: support this
  2516. }
  2517. /******************************************************************************/
  2518. void CodeEditor::clean()
  2519. {
  2520. stopBuild();
  2521. Str build_path, build_project_name; if(getBuildPath(build_path, build_project_name))
  2522. {
  2523. #if MAC
  2524. if(config_exe==EXE_MAC || config_exe==EXE_IOS)
  2525. {
  2526. Str project_file=build_path+"Project.xcodeproj";
  2527. if(FExistSystem(project_file))
  2528. REPD(debug, 2)
  2529. if(build_process.create("xcodebuild", XcodeBuildCleanParams(project_file, debug ? "Debug" : "Release", (config_exe==EXE_MAC) ? "Mac" : "iOS"))) // sdk "iphonesimulator"
  2530. build_process.wait();
  2531. }
  2532. #endif
  2533. FDelDirs(build_path); // delete build path
  2534. }
  2535. }
  2536. void CodeEditor::cleanAll()
  2537. {
  2538. clean(); // clean active project to clean it via IDE command
  2539. FDelDirs(projects_build_path); // delete projects build path
  2540. }
  2541. /******************************************************************************/
  2542. void CodeEditor::rebuild()
  2543. {
  2544. clean();
  2545. rebuildSymbols(false);
  2546. build();
  2547. }
  2548. /******************************************************************************/
  2549. void CodeEditor::configDebug(Bool debug)
  2550. {
  2551. T.config_debug=debug;
  2552. cei().configChangedDebug();
  2553. }
  2554. void CodeEditor::config32Bit(Bool bit32)
  2555. {
  2556. T.config_32_bit=bit32;
  2557. cei().configChanged32Bit();
  2558. }
  2559. void CodeEditor::configDX9(Bool dx9)
  2560. {
  2561. T.config_dx9=dx9;
  2562. cei().configChangedDX9();
  2563. }
  2564. void CodeEditor::configEXE(EXE_TYPE exe)
  2565. {
  2566. T.config_exe=exe;
  2567. cei().configChangedEXE();
  2568. }
  2569. /******************************************************************************/
  2570. }}
  2571. /******************************************************************************/