eclcc.cpp 117 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include <stdio.h>
  14. #include "jcomp.hpp"
  15. #include "jfile.hpp"
  16. #include "jlzw.hpp"
  17. #include "jqueue.tpp"
  18. #include "jargv.hpp"
  19. #include "junicode.hpp"
  20. #include "workunit.hpp"
  21. #include "thorplugin.hpp"
  22. #ifndef _WIN32
  23. #include <pwd.h>
  24. #endif
  25. #include "portlist.h"
  26. #include "dadfs.hpp"
  27. #include "dasess.hpp"
  28. #include "daclient.hpp"
  29. #include "mpcomm.hpp"
  30. #include "hqlecl.hpp"
  31. #include "hqlir.hpp"
  32. #include "hqlerrors.hpp"
  33. #include "hqlwuerr.hpp"
  34. #include "hqlfold.hpp"
  35. #include "hqlplugins.hpp"
  36. #include "hqlmanifest.hpp"
  37. #include "hqlcollect.hpp"
  38. #include "hqlrepository.hpp"
  39. #include "hqlerror.hpp"
  40. #include "hqlcerrors.hpp"
  41. #include "hqlgram.hpp"
  42. #include "hqltrans.ipp"
  43. #include "hqlutil.hpp"
  44. #include "hqlstmt.hpp"
  45. #include "hqlcache.hpp"
  46. #include "rmtfile.hpp"
  47. #include "deffield.hpp"
  48. #include "reservedwords.hpp"
  49. #include "eclcc.hpp"
  50. #include "codesigner.hpp"
  51. #ifndef _CONTAINERIZED
  52. #include "environment.hpp"
  53. #endif
  54. #ifdef _USE_CPPUNIT
  55. #include <cppunit/extensions/TestFactoryRegistry.h>
  56. #include <cppunit/ui/text/TestRunner.h>
  57. #endif
  58. #ifdef _USE_ZLIB
  59. #include "zcrypt.hpp"
  60. #endif
  61. #include "ws_dfsclient.hpp"
  62. //#define TEST_LEGACY_DEPENDENCY_CODE
  63. #define INIFILE "eclcc.ini"
  64. #define DEFAULTINIFILE "eclcc.ini"
  65. #define DEFAULT_OUTPUTNAME "a.out"
  66. //=========================================================================================
  67. //The following flag is used to speed up closedown by not freeing items
  68. static bool optReleaseAllMemory = false;
  69. static Owned<IPropertyTree> configuration;
  70. #if defined(_WIN32) && defined(_DEBUG)
  71. static HANDLE leakHandle;
  72. static void appendLeaks(size32_t len, const void * data)
  73. {
  74. SetFilePointer(leakHandle, 0, 0, FILE_END);
  75. DWORD written;
  76. WriteFile(leakHandle, data, len, &written, 0);
  77. }
  78. void initLeakCheck(const char * title)
  79. {
  80. StringBuffer leakFilename("eclccleaks.log");
  81. leakHandle = CreateFile(leakFilename.str(), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, 0);
  82. if (title)
  83. appendLeaks(strlen(title), title);
  84. _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE|_CRTDBG_MODE_DEBUG );
  85. _CrtSetReportFile( _CRT_WARN, leakHandle );
  86. _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE|_CRTDBG_MODE_DEBUG );
  87. _CrtSetReportFile( _CRT_ERROR, leakHandle );
  88. _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE|_CRTDBG_MODE_DEBUG );
  89. _CrtSetReportFile( _CRT_ASSERT, leakHandle );
  90. //
  91. // set the states we want to monitor
  92. //
  93. int LeakTmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
  94. LeakTmpFlag &= ~_CRTDBG_CHECK_CRT_DF;
  95. LeakTmpFlag |= _CRTDBG_LEAK_CHECK_DF;
  96. _CrtSetDbgFlag(LeakTmpFlag);
  97. }
  98. /**
  99. * Error handler for ctrl-break: Don't care about memory leaks.
  100. */
  101. void __cdecl IntHandler(int)
  102. {
  103. enableMemLeakChecking(false);
  104. exit(2);
  105. }
  106. #include <signal.h> // for signal()
  107. MODULE_INIT(INIT_PRIORITY_STANDARD)
  108. {
  109. signal(SIGINT, IntHandler);
  110. return true;
  111. }
  112. #else
  113. void initLeakCheck(const char *)
  114. {
  115. }
  116. #endif // _WIN32 && _DEBUG
  117. static bool extractOption(StringBuffer & option, IProperties * globals, const char * envName, const char * propertyName, const char * defaultPrefix, const char * defaultSuffix)
  118. {
  119. if (option.length()) // check if already specified via a command line option
  120. return true;
  121. if (globals->getProp(propertyName, option))
  122. return true;
  123. const char * env = getenv(envName);
  124. if (env)
  125. {
  126. option.append(env);
  127. return true;
  128. }
  129. option.append(defaultPrefix).append(defaultSuffix);
  130. return false;
  131. }
  132. static bool extractOption(StringAttr & option, IProperties * globals, const char * envName, const char * propertyName, const char * defaultPrefix, const char * defaultSuffix)
  133. {
  134. if (option)
  135. return true;
  136. StringBuffer temp;
  137. bool ret = extractOption(temp, globals, envName, propertyName, defaultPrefix, defaultSuffix);
  138. option.set(temp.str());
  139. return ret;
  140. }
  141. static bool getHomeFolder(StringBuffer & homepath)
  142. {
  143. if (!getHomeDir(homepath))
  144. return false;
  145. addPathSepChar(homepath);
  146. #ifndef WIN32
  147. homepath.append('.');
  148. #endif
  149. homepath.append(hpccBuildInfo.dirName);
  150. return true;
  151. }
  152. class EclCC;
  153. struct EclCompileInstance : public CInterfaceOf<ICodegenContextCallback>
  154. {
  155. public:
  156. EclCompileInstance(EclCC & _eclcc, IFile * _inputFile, IErrorReceiver & _errorProcessor, FILE * _errout, const char * _outputFilename, bool _legacyImport, bool _legacyWhen, bool _ignoreSignatures, bool _optIgnoreUnknownImport, bool _optXml) :
  157. eclcc(_eclcc), inputFile(_inputFile), errorProcessor(&_errorProcessor), errout(_errout), outputFilename(_outputFilename),
  158. legacyImport(_legacyImport), legacyWhen(_legacyWhen), ignoreSignatures(_ignoreSignatures), ignoreUnknownImport(_optIgnoreUnknownImport), optXml(_optXml)
  159. {
  160. stats.parseTime = 0;
  161. stats.generateTime = 0;
  162. stats.xmlSize = 0;
  163. stats.cppSize = 0;
  164. }
  165. void logStats(bool logTimings);
  166. void checkEclVersionCompatible();
  167. bool reportErrorSummary();
  168. inline IErrorReceiver & queryErrorProcessor() { return *errorProcessor; }
  169. // interface ICodegenContextCallback
  170. virtual void noteCluster(const char *clusterName) override;
  171. virtual void pushCluster(const char *clusterName) override;
  172. virtual void popCluster() override;
  173. virtual bool allowAccess(const char * category, bool isSigned) override;
  174. virtual IHqlExpression *lookupDFSlayout(const char *filename, IErrorReceiver &errs, const ECLlocation &location, bool isOpt) const override;
  175. virtual unsigned lookupClusterSize() const override;
  176. virtual void getTargetPlatform(StringBuffer & result) override;
  177. public:
  178. EclCC & eclcc;
  179. Linked<IFile> inputFile;
  180. Linked<IPropertyTree> archive;
  181. Linked<IWorkUnit> wu;
  182. Owned<IEclPackage> dataServer; // A member which can be cleared after parsing the query
  183. OwnedHqlExpr query; // parsed query - cleared when generating to free memory
  184. StringAttr eclVersion;
  185. const char * outputFilename;
  186. FILE * errout;
  187. Owned<IPropertyTree> srcArchive;
  188. Owned<IPropertyTree> generatedMeta;
  189. Owned<IPropertyTree> globalDependTree;
  190. StringAttr metaOutputFilename;
  191. bool legacyImport;
  192. bool legacyWhen;
  193. bool fromArchive = false;
  194. bool ignoreUnknownImport = false;
  195. bool ignoreSignatures = false;
  196. bool optXml = false;
  197. struct {
  198. unsigned parseTime;
  199. unsigned generateTime;
  200. offset_t xmlSize;
  201. offset_t cppSize;
  202. } stats;
  203. protected:
  204. Linked<IErrorReceiver> errorProcessor;
  205. };
  206. class EclCC
  207. {
  208. public:
  209. EclCC(int _argc, const char **_argv)
  210. : programName(_argv[0])
  211. {
  212. argc = _argc;
  213. argv = _argv;
  214. cclogFilename.append("cc.").append((unsigned)GetCurrentProcessId()).append(".log");
  215. defaultAllowed[false] = true; // May want to change that?
  216. defaultAllowed[true] = true;
  217. #ifdef _CONTAINERIZED
  218. setSecurityOptions();
  219. #endif
  220. const char * defaultGitPrefix = getenv("ECLCC_DEFAULT_GITPREFIX");
  221. if (isEmptyString(defaultGitPrefix))
  222. defaultGitPrefix = "https://github.com/";
  223. optDefaultGitPrefix.set(defaultGitPrefix);
  224. }
  225. ~EclCC()
  226. {
  227. if (daliConnected)
  228. {
  229. try
  230. {
  231. ::closedownClientProcess();
  232. }
  233. catch (...)
  234. {
  235. }
  236. }
  237. }
  238. bool printKeywordsToXml();
  239. int parseCommandLineOptions(int argc, const char* argv[]);
  240. void setSecurityOptions();
  241. void loadOptions();
  242. void loadManifestOptions();
  243. bool processFiles();
  244. void processBatchedFile(IFile & file, bool multiThreaded);
  245. // interface ICodegenContextCallback
  246. void pushCluster(const char *clusterName);
  247. void popCluster();
  248. bool allowAccess(const char * category, bool isSigned);
  249. IHqlExpression *lookupDFSlayout(const char *filename, IErrorReceiver &errs, const ECLlocation &location, bool isOpt) const;
  250. unsigned lookupClusterSize() const;
  251. void getTargetPlatform(StringBuffer & result);
  252. protected:
  253. void appendNeverSimplifyList(const char *attribsList);
  254. bool checkDaliConnected() const;
  255. void addFilenameDependency(StringBuffer & target, EclCompileInstance & instance, const char * filename);
  256. void applyApplicationOptions(IWorkUnit * wu);
  257. void applyDebugOptions(IWorkUnit * wu);
  258. bool checkWithinRepository(StringBuffer & attributePath, const char * sourcePathname);
  259. IFileIO * createArchiveOutputFile(EclCompileInstance & instance);
  260. ICppCompiler *createCompiler(const char * coreName, const char * sourceDir, const char * targetDir, const char *compileBatchOut);
  261. void evaluateResult(EclCompileInstance & instance);
  262. bool generatePrecompiledHeader();
  263. void generateOutput(EclCompileInstance & instance);
  264. void instantECL(EclCompileInstance & instance, IWorkUnit *wu, const char * queryFullName, IErrorReceiver & errorProcessor, const char * outputFile);
  265. bool isWithinPath(const char * sourcePathname, const char * searchPath);
  266. void getComplexity(IWorkUnit *wu, IHqlExpression * query, IErrorReceiver & errorProcessor);
  267. void outputXmlToOutputFile(EclCompileInstance & instance, IPropertyTree * xml);
  268. void processSingleQuery(EclCompileInstance & instance,
  269. IFileContents * queryContents,
  270. const char * queryAttributePath,
  271. IEclPackage * mainPackage);
  272. void processXmlFile(EclCompileInstance & instance, const char *archiveXML);
  273. void processFile(EclCompileInstance & info);
  274. void processReference(EclCompileInstance & instance, const char * queryAttributePath, const char * queryAttributePackage);
  275. void processBatchFiles();
  276. void processDefinitions(EclRepositoryManager & target);
  277. void reportCompileErrors(IErrorReceiver & errorProcessor, const char * processName);
  278. void setDebugOption(const char * name, bool value);
  279. void traceError(char const * format, ...) __attribute__((format(printf, 2, 3)));
  280. void usage();
  281. protected:
  282. EclRepositoryManager repositoryManager;
  283. mutable CriticalSection dfsCrit;
  284. mutable MapStringToMyClass<IHqlExpression> fileCache;
  285. mutable MapStringTo<int> fileMissCache; // values are the error code
  286. mutable Owned<IUserDescriptor> udesc; // For file lookups
  287. const char * programName;
  288. StringBuffer cppIncludePath;
  289. StringBuffer pluginsPath;
  290. StringBuffer hooksPath;
  291. StringBuffer eclLibraryPath;
  292. StringBuffer eclBundlePath;
  293. StringBuffer eclRepoPath;
  294. StringBuffer stdIncludeLibraryPath;
  295. StringBuffer includeLibraryPath;
  296. StringBuffer compilerPath;
  297. StringBuffer libraryPath;
  298. StringBuffer querySearchPath;
  299. StringBuffer cclogFilename;
  300. StringAttr optLogfile;
  301. StringAttr optIniFilename;
  302. StringAttr optOutputDirectory;
  303. StringAttr optOutputFilename;
  304. StringAttr optQueryMainAttribute;
  305. StringAttr optQueryMainPackage;
  306. StringAttr optComponentName;
  307. StringAttr optDFS;
  308. StringAttr optCluster;
  309. StringAttr optConfig;
  310. StringAttr optScope;
  311. StringAttr optUser;
  312. StringAttr optPassword;
  313. StringAttr optWUID;
  314. StringAttr optCompileBatchOut;
  315. StringArray clusters;
  316. mutable int prevClusterSize = -1; // i.e. not cached
  317. StringAttr optExpandPath;
  318. FILE * batchLog = nullptr;
  319. StringAttr optManifestFilename;
  320. StringArray resourceManifestFiles;
  321. IFileArray inputFiles;
  322. StringArray inputFileNames;
  323. StringArray applicationOptions;
  324. StringArray debugOptions;
  325. StringArray repoMappings;
  326. StringArray definitions;
  327. StringArray warningMappings;
  328. StringArray compileOptions;
  329. StringArray linkOptions;
  330. StringArray libraryPaths;
  331. StringArray allowedPermissions;
  332. StringArray allowSignedPermissions;
  333. StringArray deniedPermissions;
  334. StringAttr optMetaLocation;
  335. StringBuffer neverSimplifyRegEx;
  336. StringAttr optDefaultGitPrefix;
  337. bool defaultAllowed[2];
  338. ClusterType optTargetClusterType = RoxieCluster;
  339. CompilerType optTargetCompiler = DEFAULT_COMPILER;
  340. unsigned optThreads = 0;
  341. unsigned batchPart = 0;
  342. unsigned batchSplit = 1;
  343. unsigned optLogDetail = 0;
  344. unsigned optMonitorInterval = 60;
  345. unsigned optMaxErrors = 0;
  346. unsigned optDaliTimeout = 30000;
  347. bool optUnsuppressImmediateSyntaxErrors = false;
  348. bool logVerbose = false;
  349. bool logTimings = false;
  350. bool optArchive = false;
  351. bool optCheckEclVersion = true;
  352. bool optCheckDirty = false;
  353. bool optDebugMemLeak = false;
  354. bool optEvaluateResult = false;
  355. bool optGenerateMeta = false;
  356. bool optGenerateDepend = false;
  357. bool optIncludeMeta = false;
  358. bool optKeywords = false;
  359. bool optLeakCheck = false;
  360. bool optWorkUnit = false;
  361. bool optNoCompile = false;
  362. bool optNoLogFile = false;
  363. bool optLogToStdOut = false;
  364. bool optNoStdInc = false;
  365. bool optNoBundles = false;
  366. bool optBatchMode = false;
  367. bool optShared = false;
  368. bool optOnlyCompile = false;
  369. bool optSaveQueryText = false;
  370. bool optSaveQueryArchive = false;
  371. bool optSyntax = false;
  372. bool optLegacyImport = false;
  373. bool optLegacyWhen = false;
  374. bool optIgnoreSignatures = false;
  375. bool optGenerateHeader = false;
  376. bool optShowPaths = false;
  377. bool optNoSourcePath = false;
  378. bool optFastSyntax = false;
  379. bool optCheckIncludePaths = true;
  380. bool optXml = false;
  381. bool optTraceCache = false;
  382. bool optVerifySimplified = false;
  383. bool optRegenerateCache = false;
  384. bool optIgnoreUnknownImport = false;
  385. bool optIgnoreCache = false;
  386. bool optIgnoreSimplified = false;
  387. bool optExtraStats = false;
  388. bool optPruneArchive = true;
  389. bool optFetchRepos = true;
  390. bool optUpdateRepos = true;
  391. mutable bool daliConnected = false;
  392. mutable bool disconnectReported = false;
  393. int argc;
  394. const char **argv;
  395. };
  396. //=========================================================================================
  397. static int doSelfTest(int argc, const char *argv[])
  398. {
  399. #ifdef _USE_CPPUNIT
  400. queryStderrLogMsgHandler()->setMessageFields(MSGFIELD_time | MSGFIELD_prefix);
  401. CppUnit::TextUi::TestRunner runner;
  402. if (argc==2)
  403. {
  404. CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
  405. runner.addTest( registry.makeTest() );
  406. }
  407. else
  408. {
  409. // MORE - maybe add a 'list' function here?
  410. for (int name = 2; name < argc; name++)
  411. {
  412. if (stricmp(argv[name], "-q")==0)
  413. {
  414. removeLog();
  415. }
  416. else
  417. {
  418. CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry(argv[name]);
  419. runner.addTest( registry.makeTest() );
  420. }
  421. }
  422. }
  423. bool wasSucessful = runner.run( "", false );
  424. releaseAtoms();
  425. return wasSucessful;
  426. #else
  427. return true;
  428. #endif
  429. }
  430. static int doMain(int argc, const char *argv[])
  431. {
  432. if (argc>=2 && stricmp(argv[1], "-selftest")==0)
  433. return doSelfTest(argc, argv);
  434. EclCC processor(argc, argv);
  435. try
  436. {
  437. int ret = processor.parseCommandLineOptions(argc, argv);
  438. if (ret != 0)
  439. return ret;
  440. if (processor.printKeywordsToXml())
  441. return 0;
  442. if (!processor.processFiles())
  443. return 2;
  444. }
  445. catch (IException *E)
  446. {
  447. StringBuffer m("Error: ");
  448. E->errorMessage(m);
  449. fputs(m.newline().str(), stderr);
  450. E->Release();
  451. return 2;
  452. }
  453. #ifndef _DEBUG
  454. catch (...)
  455. {
  456. IERRLOG("Unexpected exception\n");
  457. return 4;
  458. }
  459. #endif
  460. return 0;
  461. }
  462. static constexpr const char * defaultYaml = R"!!(
  463. version: "1.0"
  464. eclccserver:
  465. name: eclccserver
  466. logging:
  467. audiences: "USR+ADT"
  468. classes: "ERR"
  469. )!!";
  470. int main(int argc, const char *argv[])
  471. {
  472. EnableSEHtoExceptionMapping();
  473. setTerminateOnSEH(true);
  474. InitModuleObjects();
  475. queryStderrLogMsgHandler()->setMessageFields(0);
  476. unsigned exitCode = 0;
  477. try
  478. {
  479. configuration.setown(loadConfiguration(defaultYaml, argv, "eclccserver", "ECLCCSERVER", nullptr, nullptr, nullptr, false));
  480. #ifndef _CONTAINERIZED
  481. // Turn logging down (we turn it back up if -v option seen)
  482. Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(MSGAUD_user| MSGAUD_operator, MSGCLS_error);
  483. queryLogMsgManager()->changeMonitorFilter(queryStderrLogMsgHandler(), filter);
  484. #else
  485. setupContainerizedLogMsgHandler();
  486. bool useChildProcesses = configuration->getPropBool("@useChildProcesses", false);
  487. if (!useChildProcesses) // If using eclcc in separate container (useChildProcesses==false),
  488. { // it will need to create a directory for gpg and import keys from secrets
  489. queryCodeSigner().initForContainer();
  490. }
  491. #endif
  492. exitCode = doMain(argc, argv);
  493. stopPerformanceMonitor();
  494. }
  495. catch (IException *E)
  496. {
  497. StringBuffer m("Error: ");
  498. E->errorMessage(m);
  499. fputs(m.newline().str(), stderr);
  500. E->Release();
  501. exitCode = 2;
  502. }
  503. if (!optReleaseAllMemory)
  504. {
  505. //In release mode exit without calling all the clean up code.
  506. //It is faster, and it helps avoid potential crashes if there are active objects which depend on objects in file hook dlls.
  507. fflush(NULL);
  508. _exit(exitCode);
  509. }
  510. configuration.clear();
  511. releaseAtoms();
  512. ClearTypeCache(); // Clear this cache before the file hooks are unloaded
  513. removeFileHooks();
  514. return exitCode;
  515. }
  516. //=========================================================================================
  517. bool setTargetPlatformOption(const char *platform, ClusterType &optTargetClusterType)
  518. {
  519. if (!platform || !*platform)
  520. return false;
  521. ClusterType clusterType = getClusterType(platform);
  522. if (clusterType == NoCluster)
  523. {
  524. UERRLOG("Unknown ecl target platform %s\n", platform);
  525. return false;
  526. }
  527. optTargetClusterType = clusterType;
  528. return true;
  529. }
  530. void EclCC::loadManifestOptions()
  531. {
  532. if (!optManifestFilename)
  533. return;
  534. resourceManifestFiles.append(optManifestFilename);
  535. Owned<IPropertyTree> mf = createPTreeFromXMLFile(optManifestFilename);
  536. IPropertyTree *ecl = mf->queryPropTree("ecl");
  537. if (ecl)
  538. {
  539. if (ecl->hasProp("@filename"))
  540. {
  541. StringBuffer dir, abspath;
  542. splitDirTail(optManifestFilename, dir);
  543. makeAbsolutePath(ecl->queryProp("@filename"), dir.str(), abspath);
  544. processArgvFilename(inputFiles, abspath.str());
  545. }
  546. if (!optLegacyImport && !optLegacyWhen)
  547. {
  548. bool optLegacy = ecl->getPropBool("@legacy");
  549. optLegacyImport = ecl->getPropBool("@legacyImport", optLegacy);
  550. optLegacyWhen = ecl->getPropBool("@legacyWhen", optLegacy);
  551. }
  552. if (!optQueryMainAttribute && ecl->hasProp("@main"))
  553. {
  554. optQueryMainAttribute.set(ecl->queryProp("@main"));
  555. optQueryMainPackage.set(ecl->queryProp("@package"));
  556. }
  557. if (ecl->hasProp("@targetPlatform"))
  558. setTargetPlatformOption(ecl->queryProp("@targetPlatform"), optTargetClusterType);
  559. else if (ecl->hasProp("@targetClusterType")) //deprecated name
  560. setTargetPlatformOption(ecl->queryProp("@targetClusterType"), optTargetClusterType);
  561. Owned<IPropertyTreeIterator> paths = ecl->getElements("IncludePath");
  562. ForEach(*paths)
  563. {
  564. IPropertyTree &item = paths->query();
  565. if (item.hasProp("@path"))
  566. includeLibraryPath.append(ENVSEPCHAR).append(item.queryProp("@path"));
  567. }
  568. paths.setown(ecl->getElements("LibraryPath"));
  569. ForEach(*paths)
  570. {
  571. IPropertyTree &item = paths->query();
  572. if (item.hasProp("@path"))
  573. libraryPaths.append(item.queryProp("@path"));
  574. }
  575. }
  576. }
  577. void EclCC::loadOptions()
  578. {
  579. Owned<IProperties> globals;
  580. if (!optIniFilename)
  581. {
  582. if (checkFileExists(INIFILE))
  583. optIniFilename.set(INIFILE);
  584. else
  585. {
  586. StringBuffer fn(hpccBuildInfo.configDir);
  587. fn.append(PATHSEPSTR).append(DEFAULTINIFILE);
  588. if (checkFileExists(fn))
  589. optIniFilename.set(fn);
  590. }
  591. }
  592. if (logVerbose && optIniFilename.length())
  593. fprintf(stdout, "Found ini file '%s'\n", optIniFilename.get());
  594. globals.setown(createProperties(optIniFilename, true));
  595. if (globals->hasProp("targetGcc"))
  596. optTargetCompiler = globals->getPropBool("targetGcc") ? GccCppCompiler : Vs6CppCompiler;
  597. StringBuffer syspath, homepath;
  598. if (getPackageFolder(syspath) && getHomeFolder(homepath))
  599. {
  600. #if _WIN32
  601. extractOption(compilerPath, globals, "CL_PATH", "compilerPath", syspath, "componentfiles\\cl");
  602. #else
  603. extractOption(compilerPath, globals, "CL_PATH", "compilerPath", "/usr", NULL);
  604. #endif
  605. extractOption(cppIncludePath, globals, "ECLCC_INCLUDE_PATH", "includePath", syspath, "componentfiles" PATHSEPSTR "cl" PATHSEPSTR "include");
  606. extractOption(pluginsPath, globals, "ECLCC_PLUGIN_PATH", "plugins", syspath, "plugins");
  607. getAdditionalPluginsPath(pluginsPath, syspath);
  608. if (!extractOption(libraryPath, globals, "ECLCC_LIBRARY_PATH", "libraryPath", syspath, "lib"))
  609. {
  610. libraryPath.append(ENVSEPCHAR).append(syspath).append("plugins");
  611. getAdditionalPluginsPath(libraryPath, syspath);
  612. }
  613. extractOption(hooksPath, globals, "HPCC_FILEHOOKS_PATH", "filehooks", syspath, "filehooks");
  614. extractOption(eclLibraryPath, globals, "ECLCC_ECLLIBRARY_PATH", "eclLibrariesPath", syspath, "share" PATHSEPSTR "ecllibrary" PATHSEPSTR);
  615. extractOption(eclBundlePath, globals, "ECLCC_ECLBUNDLE_PATH", "eclBundlesPath", homepath, PATHSEPSTR "bundles" PATHSEPSTR);
  616. extractOption(eclRepoPath, globals, "ECLCC_ECLREPO_PATH", "eclRepoPath", homepath, PATHSEPSTR "repos" PATHSEPSTR);
  617. }
  618. extractOption(stdIncludeLibraryPath, globals, "ECLCC_ECLINCLUDE_PATH", "eclIncludePath", ".", NULL);
  619. if (optLogToStdOut)
  620. {
  621. Owned<ILogMsgHandler> handler = getHandleLogMsgHandler(stdout);
  622. handler->setMessageFields(MSGFIELD_STANDARD);
  623. Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(MSGAUD_all, MSGCLS_all, optLogDetail ? optLogDetail : DefaultDetail, true);
  624. queryLogMsgManager()->addMonitor(handler, filter);
  625. }
  626. #ifndef _CONTAINERIZED
  627. else
  628. {
  629. if (!optLogfile.length() && !optBatchMode && !optNoLogFile)
  630. extractOption(optLogfile, globals, "ECLCC_LOGFILE", "logfile", "eclcc.log", NULL);
  631. if ((logVerbose || optLogfile) && !optNoLogFile)
  632. {
  633. if (optLogfile.length())
  634. {
  635. StringBuffer lf;
  636. openLogFile(lf, optLogfile, optLogDetail, false);
  637. if (logVerbose)
  638. fprintf(stdout, "Logging to '%s'\n",lf.str());
  639. }
  640. if (optMonitorInterval)
  641. startPerformanceMonitor(optMonitorInterval*1000, PerfMonStandard, nullptr);
  642. }
  643. }
  644. #endif
  645. if (hooksPath.length())
  646. installFileHooks(hooksPath.str());
  647. if (!optNoCompile)
  648. setCompilerPath(compilerPath.str(), cppIncludePath.str(), libraryPath.str(), NULL, optTargetCompiler, logVerbose);
  649. }
  650. //=========================================================================================
  651. void EclCC::applyDebugOptions(IWorkUnit * wu)
  652. {
  653. ForEachItemIn(i, debugOptions)
  654. {
  655. const char * option = debugOptions.item(i);
  656. const char * eq = strchr(option, '=');
  657. if (eq)
  658. {
  659. StringAttr name;
  660. name.set(option, eq-option);
  661. wu->setDebugValue(name, eq+1, true);
  662. }
  663. else
  664. {
  665. size_t len = strlen(option);
  666. if (len)
  667. {
  668. char last = option[len-1];
  669. if (last == '-' || last == '+')
  670. {
  671. StringAttr name;
  672. name.set(option, len-1);
  673. wu->setDebugValueInt(name, last == '+' ? 1 : 0, true);
  674. }
  675. else
  676. wu->setDebugValue(option, "1", true);
  677. }
  678. }
  679. }
  680. }
  681. void EclCC::applyApplicationOptions(IWorkUnit * wu)
  682. {
  683. ForEachItemIn(i, applicationOptions)
  684. {
  685. const char * option = applicationOptions.item(i);
  686. const char * eq = strchr(option, '=');
  687. if (eq)
  688. {
  689. StringAttr name;
  690. name.set(option, eq-option);
  691. wu->setApplicationValue("eclcc", name, eq+1, true);
  692. }
  693. else
  694. {
  695. wu->setApplicationValueInt("eclcc", option, 1, true);
  696. }
  697. }
  698. }
  699. //=========================================================================================
  700. ICppCompiler * EclCC::createCompiler(const char * coreName, const char * sourceDir, const char * targetDir, const char *compileBatchOut)
  701. {
  702. Owned<ICppCompiler> compiler = ::createCompiler(coreName, sourceDir, targetDir, optTargetCompiler, logVerbose, compileBatchOut);
  703. compiler->setOnlyCompile(optOnlyCompile);
  704. compiler->setCCLogPath(cclogFilename);
  705. ForEachItemIn(iComp, compileOptions)
  706. compiler->addCompileOption(compileOptions.item(iComp));
  707. ForEachItemIn(iLink, linkOptions)
  708. compiler->addLinkOption(linkOptions.item(iLink));
  709. ForEachItemIn(iLib, libraryPaths)
  710. compiler->addLibraryPath(libraryPaths.item(iLib));
  711. return compiler.getClear();
  712. }
  713. void EclCC::reportCompileErrors(IErrorReceiver & errorProcessor, const char * processName)
  714. {
  715. StringBuffer failText;
  716. StringBuffer absCCLogName;
  717. #ifndef _CONTAINERIZED
  718. if (optLogfile.get())
  719. createUNCFilename(optLogfile.get(), absCCLogName, false);
  720. else
  721. #endif
  722. absCCLogName = "log file";
  723. failText.appendf("Compile/Link failed for %s (see %s for details)",processName,absCCLogName.str());
  724. errorProcessor.reportError(ERR_INTERNALEXCEPTION, failText.str(), processName, 0, 0, 0);
  725. try
  726. {
  727. StringBuffer s;
  728. Owned<IFile> log = createIFile(cclogFilename);
  729. Owned<IFileIO> io = log->open(IFOread);
  730. if (io)
  731. {
  732. offset_t len = io->size();
  733. if (len)
  734. {
  735. io->read(0, (size32_t)len, s.reserve((size32_t)len));
  736. #ifdef _WIN32
  737. const char * noCompiler = "is not recognized as an internal";
  738. #else
  739. const char * noCompiler = "could not locate compiler";
  740. #endif
  741. if (strstr(s.str(), noCompiler))
  742. {
  743. OERRLOG("Fatal Error: Unable to locate C++ compiler/linker");
  744. }
  745. UERRLOG("\n---------- compiler output --------------\n%s\n--------- end compiler output -----------", s.str());
  746. }
  747. }
  748. }
  749. catch (IException * e)
  750. {
  751. e->Release();
  752. }
  753. }
  754. //=========================================================================================
  755. void gatherResourceManifestFilenames(EclCompileInstance & instance, StringArray &filenames)
  756. {
  757. IPropertyTree *tree = (instance.archive) ? instance.archive.get() : instance.globalDependTree.get();
  758. if (!tree)
  759. return;
  760. Owned<IPropertyTreeIterator> iter = tree->getElements((instance.archive) ? "Module/Attribute" : "Attribute");
  761. ForEach(*iter)
  762. {
  763. StringBuffer filename(iter->query().queryProp("@sourcePath"));
  764. if (filename.length())
  765. {
  766. getFullFileName(filename, true).append(".manifest");
  767. if (filenames.contains(filename))
  768. continue;
  769. Owned<IFile> manifest = createIFile(filename);
  770. if (manifest->exists())
  771. filenames.append(filename);
  772. }
  773. }
  774. }
  775. void EclCC::instantECL(EclCompileInstance & instance, IWorkUnit *wu, const char * queryFullName, IErrorReceiver & errorProcessor, const char * outputFile)
  776. {
  777. StringBuffer processName(outputFile);
  778. if (instance.query && containsAnyActions(instance.query))
  779. {
  780. try
  781. {
  782. bool optSaveTemps = wu->getDebugValueBool("saveEclTempFiles", false);
  783. bool optSaveCpp = optSaveTemps || optNoCompile || !optCompileBatchOut.isEmpty() || wu->getDebugValueBool("saveCppTempFiles", false) || wu->getDebugValueBool("saveCpp", false);
  784. //New scope - testing things are linked correctly
  785. {
  786. Owned<IHqlExprDllGenerator> generator = createDllGenerator(&errorProcessor, processName.str(), NULL, wu, optTargetClusterType, &instance, false, false);
  787. setWorkunitHash(wu, instance.query);
  788. if (!optShared)
  789. wu->setDebugValueInt("standAloneExe", 1, true);
  790. EclGenerateTarget target = optWorkUnit ? EclGenerateNone : (optNoCompile ? EclGenerateCpp : optShared ? EclGenerateDll : EclGenerateExe);
  791. if (instance.srcArchive)
  792. {
  793. generator->addManifestsFromArchive(instance.srcArchive);
  794. instance.srcArchive.clear();
  795. }
  796. else
  797. {
  798. gatherResourceManifestFilenames(instance, resourceManifestFiles);
  799. ForEachItemIn(i, resourceManifestFiles)
  800. generator->addManifest(resourceManifestFiles.item(i));
  801. }
  802. generator->setSaveGeneratedFiles(optSaveCpp);
  803. if (optSaveQueryArchive && instance.wu && instance.archive)
  804. {
  805. StringBuffer buf;
  806. toXML(instance.archive, buf);
  807. if (optWorkUnit)
  808. {
  809. Owned<IWUQuery> q = instance.wu->updateQuery();
  810. q->setQueryText(buf);
  811. }
  812. else
  813. {
  814. generator->addArchiveAsResource(buf);
  815. }
  816. }
  817. bool generateOk = generator->processQuery(instance.query, target); // NB: May clear instance.query
  818. instance.stats.cppSize = generator->getGeneratedSize();
  819. if (generateOk && !optNoCompile)
  820. {
  821. Owned<ICppCompiler> compiler = createCompiler(processName.str(), nullptr, nullptr, optCompileBatchOut);
  822. compiler->setSaveTemps(optSaveTemps);
  823. bool compileOk = true;
  824. if (optShared)
  825. {
  826. compileOk = generator->generateDll(compiler);
  827. }
  828. else
  829. {
  830. if (optTargetClusterType==RoxieCluster)
  831. generator->addLibrary("ccd");
  832. else
  833. generator->addLibrary("hthorlib");
  834. compileOk = generator->generateExe(compiler);
  835. }
  836. if (!compileOk)
  837. reportCompileErrors(errorProcessor, processName);
  838. compiler->finish();
  839. }
  840. else
  841. wu->setState(generateOk ? WUStateCompleted : WUStateFailed);
  842. }
  843. if (logVerbose)
  844. {
  845. switch (wu->getState())
  846. {
  847. case WUStateCompiled:
  848. fprintf(stdout, "Output file '%s' created\n",outputFile);
  849. break;
  850. case WUStateFailed:
  851. traceError("Failed to create output file '%s'\n",outputFile);
  852. break;
  853. case WUStateUploadingFiles:
  854. fprintf(stdout, "Output file '%s' created, local file upload required\n",outputFile);
  855. break;
  856. case WUStateCompleted:
  857. fprintf(stdout, "No DLL/SO required\n");
  858. break;
  859. default:
  860. traceError("Unexpected Workunit state %d\n", (int) wu->getState());
  861. break;
  862. }
  863. }
  864. }
  865. catch (IError * _e)
  866. {
  867. Owned<IError> e = _e;
  868. errorProcessor.report(e);
  869. }
  870. catch (IException * _e)
  871. {
  872. Owned<IException> e = _e;
  873. unsigned errCode = e->errorCode();
  874. if (errCode != HQLERR_ErrorAlreadyReported)
  875. {
  876. StringBuffer exceptionText;
  877. e->errorMessage(exceptionText);
  878. if (errCode == 0)
  879. errCode = ERR_INTERNALEXCEPTION;
  880. errorProcessor.reportError(ERR_INTERNALEXCEPTION, exceptionText.str(), queryFullName, 1, 0, 0);
  881. }
  882. }
  883. try
  884. {
  885. Owned<IFile> log = createIFile(cclogFilename);
  886. log->remove();
  887. }
  888. catch (IException * e)
  889. {
  890. e->Release();
  891. }
  892. }
  893. }
  894. //=========================================================================================
  895. void EclCC::getComplexity(IWorkUnit *wu, IHqlExpression * query, IErrorReceiver & errs)
  896. {
  897. double complexity = getECLcomplexity(query, &errs, wu, optTargetClusterType);
  898. LOG(MCstats, unknownJob, "Complexity = %g", complexity);
  899. }
  900. //=========================================================================================
  901. static bool convertPathToModule(StringBuffer & out, const char * filename)
  902. {
  903. StringBuffer temp;
  904. #ifdef _USE_ZLIB
  905. removeZipExtension(temp, filename);
  906. #else
  907. temp.append(filename);
  908. #endif
  909. const char * dot = strrchr(temp.str(), '.');
  910. if (dot)
  911. {
  912. if (!strieq(dot, ".ecl") && !strieq(dot, ".hql") && !strieq(dot, ".eclmod") && !strieq(dot, ".eclattr"))
  913. return false;
  914. }
  915. else
  916. return false;
  917. const unsigned copyLen = dot-temp.str();
  918. if (copyLen == 0)
  919. return false;
  920. out.ensureCapacity(copyLen);
  921. for (unsigned i= 0; i < copyLen; i++)
  922. {
  923. char next = filename[i];
  924. if (isPathSepChar(next))
  925. next = '.';
  926. out.append(next);
  927. }
  928. return true;
  929. }
  930. static bool findFilenameInSearchPath(StringBuffer & attributePath, const char * searchPath, const char * expandedSourceName)
  931. {
  932. const char * cur = searchPath;
  933. unsigned lenSource = strlen(expandedSourceName);
  934. for (;;)
  935. {
  936. const char * sep = strchr(cur, ENVSEPCHAR);
  937. StringBuffer curExpanded;
  938. if (!sep)
  939. {
  940. if (*cur)
  941. makeAbsolutePath(cur, curExpanded);
  942. }
  943. else if (sep != cur)
  944. {
  945. StringAttr temp(cur, sep-cur);
  946. makeAbsolutePath(temp, curExpanded);
  947. }
  948. if (curExpanded.length() && (curExpanded.length() < lenSource))
  949. {
  950. #ifdef _WIN32
  951. //windows paths are case insensitive
  952. bool same = memicmp(curExpanded.str(), expandedSourceName, curExpanded.length()) == 0;
  953. #else
  954. bool same = memcmp(curExpanded.str(), expandedSourceName, curExpanded.length()) == 0;
  955. #endif
  956. if (same)
  957. {
  958. const char * tail = expandedSourceName+curExpanded.length();
  959. if (isPathSepChar(*tail))
  960. tail++;
  961. if (convertPathToModule(attributePath, tail))
  962. return true;
  963. }
  964. }
  965. if (!sep)
  966. return false;
  967. cur = sep+1;
  968. }
  969. }
  970. bool EclCC::isWithinPath(const char * sourcePathname, const char * searchPath)
  971. {
  972. if (!sourcePathname)
  973. return false;
  974. StringBuffer expandedSourceName;
  975. makeAbsolutePath(sourcePathname, expandedSourceName);
  976. StringBuffer attributePath;
  977. return findFilenameInSearchPath(attributePath, searchPath, expandedSourceName);
  978. }
  979. bool EclCC::checkWithinRepository(StringBuffer & attributePath, const char * sourcePathname)
  980. {
  981. if (!sourcePathname)
  982. return false;
  983. StringBuffer searchPath;
  984. searchPath.append(eclLibraryPath).append(ENVSEPCHAR);
  985. if (!optNoBundles)
  986. searchPath.append(eclBundlePath).append(ENVSEPCHAR);
  987. if (!optNoStdInc)
  988. searchPath.append(stdIncludeLibraryPath).append(ENVSEPCHAR);
  989. searchPath.append(includeLibraryPath);
  990. StringBuffer expandedSourceName;
  991. makeAbsolutePath(sourcePathname, expandedSourceName);
  992. return findFilenameInSearchPath(attributePath, searchPath, expandedSourceName);
  993. }
  994. void EclCC::evaluateResult(EclCompileInstance & instance)
  995. {
  996. IHqlExpression *query = instance.query;
  997. if (query->getOperator()==no_output)
  998. query = query->queryChild(0);
  999. if (query->getOperator()==no_datasetfromdictionary)
  1000. query = query->queryChild(0);
  1001. if (query->getOperator()==no_selectfields)
  1002. query = query->queryChild(0);
  1003. if (query->getOperator()==no_createdictionary)
  1004. query = query->queryChild(0);
  1005. OwnedHqlExpr folded = foldHqlExpression(instance.queryErrorProcessor(), query, HFOthrowerror|HFOloseannotations|HFOforcefold|HFOfoldfilterproject|HFOconstantdatasets);
  1006. StringBuffer out;
  1007. IValue *result = folded->queryValue();
  1008. if (result)
  1009. result->generateECL(out);
  1010. else if (folded->getOperator()==no_list)
  1011. {
  1012. out.append('[');
  1013. ForEachChild(idx, folded)
  1014. {
  1015. IHqlExpression *child = folded->queryChild(idx);
  1016. if (idx)
  1017. out.append(", ");
  1018. result = child->queryValue();
  1019. if (result)
  1020. result->generateECL(out);
  1021. else
  1022. throw MakeStringException(1, "Expression cannot be evaluated");
  1023. }
  1024. out.append(']');
  1025. }
  1026. else if (folded->getOperator()==no_inlinetable)
  1027. {
  1028. IHqlExpression *transformList = folded->queryChild(0);
  1029. if (transformList && transformList->getOperator()==no_transformlist)
  1030. {
  1031. IHqlExpression *transform = transformList->queryChild(0);
  1032. assertex(transform && transform->getOperator()==no_transform);
  1033. out.append('[');
  1034. ForEachChild(idx, transform)
  1035. {
  1036. IHqlExpression *child = transform->queryChild(idx);
  1037. assertex(child->getOperator()==no_assign);
  1038. if (idx)
  1039. out.append(", ");
  1040. result = child->queryChild(1)->queryValue();
  1041. if (result)
  1042. result->generateECL(out);
  1043. else
  1044. throw MakeStringException(1, "Expression cannot be evaluated");
  1045. }
  1046. out.append(']');
  1047. }
  1048. else
  1049. throw MakeStringException(1, "Expression cannot be evaluated");
  1050. }
  1051. else
  1052. {
  1053. #ifdef _DEBUG
  1054. EclIR::dump_ir(folded);
  1055. #endif
  1056. throw MakeStringException(1, "Expression cannot be evaluated");
  1057. }
  1058. printf("%s\n", out.str());
  1059. }
  1060. void EclCC::processSingleQuery(EclCompileInstance & instance,
  1061. IFileContents * queryContents,
  1062. const char * queryAttributePath,
  1063. IEclPackage * mainPackage)
  1064. {
  1065. #ifdef TEST_LEGACY_DEPENDENCY_CODE
  1066. setLegacyEclSemantics(instance.legacyImportMode, instance.legacyWhenMode);
  1067. Owned<IPropertyTree> dependencies = gatherAttributeDependencies(instance.dataServer, "");
  1068. if (dependencies)
  1069. saveXML("depends.xml", dependencies);
  1070. #endif
  1071. Owned<IErrorReceiver> wuErrs = new WorkUnitErrorReceiver(instance.wu, "eclcc", optBatchMode);
  1072. Owned<IErrorReceiver> compoundErrs = createCompoundErrorReceiver(&instance.queryErrorProcessor(), wuErrs);
  1073. Owned<ErrorSeverityMapper> severityMapper = new ErrorSeverityMapper(*compoundErrs);
  1074. //Apply command line mappings...
  1075. ForEachItemIn(i, warningMappings)
  1076. {
  1077. if (!severityMapper->addCommandLineMapping(warningMappings.item(i)))
  1078. return;
  1079. //Preserve command line mappings in the generated archive
  1080. if (instance.archive)
  1081. instance.archive->addPropTree("OnWarning")->setProp("@value",warningMappings.item(i));
  1082. }
  1083. //Apply preserved onwarning mappings from any source archive
  1084. if (instance.srcArchive)
  1085. {
  1086. Owned<IPropertyTreeIterator> iter = instance.srcArchive->getElements("OnWarning");
  1087. ForEach(*iter)
  1088. {
  1089. const char * name = iter->query().queryProp("@name");
  1090. const char * option = iter->query().queryProp("@value");
  1091. if (name)
  1092. {
  1093. if (!severityMapper->addMapping(name, option))
  1094. return;
  1095. }
  1096. else
  1097. {
  1098. if (!severityMapper->addCommandLineMapping(option))
  1099. return;
  1100. }
  1101. }
  1102. }
  1103. IErrorReceiver & errorProcessor = *severityMapper;
  1104. //All dlls/exes are essentially cloneable because you may be running multiple instances at once
  1105. //The only exception would be a dll created for a one-time query. (Currently handled by eclserver.)
  1106. instance.wu->setCloneable(true);
  1107. recordQueueFilePrefixes(instance.wu, configuration);
  1108. applyDebugOptions(instance.wu);
  1109. applyApplicationOptions(instance.wu);
  1110. if (optTargetCompiler != DEFAULT_COMPILER)
  1111. instance.wu->setDebugValue("targetCompiler", compilerTypeText[optTargetCompiler], true);
  1112. bool withinRepository = (queryAttributePath && *queryAttributePath);
  1113. bool syntaxChecking = instance.wu->getDebugValueBool("syntaxCheck", false);
  1114. if (syntaxChecking || instance.archive)
  1115. severityMapper->addMapping("security", "ignore");
  1116. //This option isn't particularly useful, but is here to help test the code to gather disk information
  1117. bool optGatherDiskStats = instance.wu->getDebugValueBool("gatherEclccDiskStats", false);
  1118. size32_t prevErrs = errorProcessor.errCount();
  1119. cycle_t startCycles = get_cycles_now();
  1120. CpuInfo systemStartTime(false, true);
  1121. CpuInfo processStartTime(true, false);
  1122. //Avoid creating the OsDiskStats object if not gathering timings to avoid unnecessary initialisation
  1123. OwnedPtr<OsDiskStats> systemIoStartInfo;
  1124. if (optGatherDiskStats)
  1125. systemIoStartInfo.setown(new OsDiskStats(true));
  1126. if (optCompileBatchOut.isEmpty())
  1127. addTimeStamp(instance.wu, SSTcompilestage, "compile", StWhenStarted);
  1128. const char * sourcePathname = queryContents ? str(queryContents->querySourcePath()) : NULL;
  1129. const char * defaultErrorPathname = sourcePathname ? sourcePathname : queryAttributePath;
  1130. //The following is only here to provide information about the source file being compiled when reporting leaks
  1131. if (instance.inputFile)
  1132. setActiveSource(instance.inputFile->queryFilename());
  1133. Owned<IEclCachedDefinitionCollection> cache;
  1134. hash64_t optionHash = 0;
  1135. if (optMetaLocation)
  1136. {
  1137. //Update the hash to include information about which options affect how symbols are processed. It should only include options that
  1138. //affect how the code is parsed, not how it is generated.
  1139. //Include path
  1140. optionHash = rtlHash64VStr(eclLibraryPath, optionHash);
  1141. if (!optNoBundles)
  1142. optionHash = rtlHash64VStr(eclBundlePath, optionHash);
  1143. if (!optNoStdInc)
  1144. optionHash = rtlHash64VStr(stdIncludeLibraryPath, optionHash);
  1145. optionHash = rtlHash64VStr(includeLibraryPath, optionHash);
  1146. //Any explicit -D definitions
  1147. ForEachItemIn(i, definitions)
  1148. optionHash = rtlHash64VStr(definitions.item(i), optionHash);
  1149. optionHash = rtlHash64Data(sizeof(optLegacyImport), &optLegacyImport, optionHash);
  1150. optionHash = rtlHash64Data(sizeof(optLegacyWhen), &optLegacyWhen, optionHash);
  1151. //And create a cache instances
  1152. cache.setown(createEclFileCachedDefinitionCollection(instance.dataServer, optMetaLocation));
  1153. }
  1154. if (instance.archive)
  1155. {
  1156. instance.archive->setPropBool("@legacyImport", instance.legacyImport);
  1157. instance.archive->setPropBool("@legacyWhen", instance.legacyWhen);
  1158. if (withinRepository)
  1159. {
  1160. IEclPackage * package = mainPackage ? mainPackage : instance.dataServer.get();
  1161. instance.archive->setProp("Query", "");
  1162. instance.archive->setProp("Query/@attributePath", queryAttributePath);
  1163. instance.archive->setProp("Query/@package", package->queryPackageName());
  1164. }
  1165. }
  1166. if (withinRepository && instance.archive && cache)
  1167. {
  1168. Owned<IEclCachedDefinition> main = cache->getDefinition(queryAttributePath);
  1169. if (main->isUpToDate(optionHash))
  1170. {
  1171. if (main->hasKnownDependents())
  1172. {
  1173. DBGLOG("Create archive from cache for %s", queryAttributePath);
  1174. updateArchiveFromCache(instance.archive, cache, queryAttributePath);
  1175. return;
  1176. }
  1177. UWARNLOG("Cannot create archive from cache for %s because it is a macro", queryAttributePath);
  1178. }
  1179. else
  1180. UWARNLOG("Cannot create archive from cache for %s because it is not up to date", queryAttributePath);
  1181. }
  1182. {
  1183. //Minimize the scope of the parse context to reduce lifetime of cached items.
  1184. WuStatisticTarget statsTarget(instance.wu, "eclcc");
  1185. HqlParseContext parseCtx(&instance, instance.archive, statsTarget);
  1186. parseCtx.cache = cache;
  1187. parseCtx.optionHash = optionHash;
  1188. if (optSyntax)
  1189. parseCtx.setSyntaxChecking();
  1190. if (optVerifySimplified)
  1191. parseCtx.setCheckSimpleDef();
  1192. if (optRegenerateCache)
  1193. parseCtx.setRegenerateCache();
  1194. if (optIgnoreCache)
  1195. parseCtx.setIgnoreCache();
  1196. if (optIgnoreSimplified)
  1197. parseCtx.setIgnoreSimplified();
  1198. if (neverSimplifyRegEx)
  1199. parseCtx.setNeverSimplify(neverSimplifyRegEx.str());
  1200. //Allow fastsyntax to be specified in the archive to aid with regression testing
  1201. if (optFastSyntax || (instance.srcArchive && instance.srcArchive->getPropBool("@fastSyntax", false)))
  1202. parseCtx.setFastSyntax();
  1203. parseCtx.timeParser = instance.wu->getDebugValueBool("timeParser", false);
  1204. //Avoid creating location annotations if syntax checking or creating an archive - since they are only really
  1205. //used when transforming the tree and generating code. Can significantly speed up some queries.
  1206. //Option noteLocations can be used to override the default.
  1207. enableLocationAnnotations(instance.wu->getDebugValueBool("noteLocations", !optSyntax && !optArchive));
  1208. unsigned maxErrorsDebugOption = instance.wu->getDebugValueInt("maxErrors", 0);
  1209. if (maxErrorsDebugOption != 0)
  1210. parseCtx.maxErrors = maxErrorsDebugOption;
  1211. if (optMaxErrors > 0)
  1212. parseCtx.maxErrors = optMaxErrors;
  1213. parseCtx.unsuppressImmediateSyntaxErrors = optUnsuppressImmediateSyntaxErrors;
  1214. parseCtx.checkDirty = optCheckDirty;
  1215. if (!instance.archive)
  1216. parseCtx.globalDependTree.setown(createPTree(ipt_fast)); //to locate associated manifests, keep separate from user specified MetaOptions
  1217. if (optGenerateMeta || optIncludeMeta)
  1218. {
  1219. //Currently the meta information is generated as a side-effect of parsing the attributes, so disable
  1220. //using the simplified expressions if meta information is requested. HPCC-20716 will improve this.
  1221. parseCtx.setIgnoreCache();
  1222. HqlParseContext::MetaOptions options;
  1223. options.includePublicDefinitions = instance.wu->getDebugValueBool("metaIncludePublic", true);
  1224. options.includePrivateDefinitions = instance.wu->getDebugValueBool("metaIncludePrivate", true);
  1225. options.onlyGatherRoot = instance.wu->getDebugValueBool("metaIncludeMainOnly", false);
  1226. options.includeImports = instance.wu->getDebugValueBool("metaIncludeImports", true);
  1227. options.includeInternalUses = instance.wu->getDebugValueBool("metaIncludeInternalUse", true);
  1228. options.includeExternalUses = instance.wu->getDebugValueBool("metaIncludeExternalUse", true);
  1229. options.includeLocations = instance.wu->getDebugValueBool("metaIncludeLocations", true);
  1230. options.includeJavadoc = instance.wu->getDebugValueBool("metaIncludeJavadoc", true);
  1231. parseCtx.setGatherMeta(options);
  1232. }
  1233. if (optMetaLocation && !instance.fromArchive)
  1234. parseCtx.setCacheLocation(optMetaLocation);
  1235. setLegacyEclSemantics(instance.legacyImport, instance.legacyWhen);
  1236. parseCtx.ignoreUnknownImport = instance.ignoreUnknownImport;
  1237. parseCtx.ignoreSignatures = instance.ignoreSignatures;
  1238. bool exportDependencies = instance.wu->getDebugValueBool("exportDependencies",false);
  1239. if (exportDependencies || optMetaLocation)
  1240. parseCtx.nestedDependTree.setown(createPTree("Dependencies", ipt_fast));
  1241. addTimeStamp(instance.wu, SSTcompilestage, "compile:parse", StWhenStarted);
  1242. try
  1243. {
  1244. HqlLookupContext ctx(parseCtx, &errorProcessor, instance.dataServer);
  1245. if (withinRepository)
  1246. {
  1247. instance.query.setown(getResolveAttributeFullPath(queryAttributePath, LSFpublic, ctx, mainPackage));
  1248. if (!instance.query && !syntaxChecking && (errorProcessor.errCount() == prevErrs))
  1249. {
  1250. StringBuffer msg;
  1251. msg.append("Could not resolve attribute ").append(queryAttributePath);
  1252. errorProcessor.reportError(3, msg.str(), defaultErrorPathname, 0, 0, 0);
  1253. }
  1254. }
  1255. else
  1256. {
  1257. Owned<IHqlScope> scope = createPrivateScope();
  1258. instance.query.setown(parseQuery(scope, queryContents, ctx, NULL, NULL, true, true));
  1259. if (instance.archive)
  1260. {
  1261. StringBuffer queryText;
  1262. queryText.append(queryContents->length(), queryContents->getText());
  1263. const char * p = queryText;
  1264. if (0 == strncmp(p, (const char *)UTF8_BOM,3))
  1265. p += 3;
  1266. instance.archive->setProp("Query", p );
  1267. instance.archive->setProp("Query/@originalFilename", sourcePathname);
  1268. }
  1269. }
  1270. if (syntaxChecking && instance.query && instance.query->getOperator() == no_forwardscope)
  1271. {
  1272. IHqlScope * scope = instance.query->queryScope();
  1273. //Have the side effect of resolving the symbols and triggering any syntax errors
  1274. IHqlScope * resolved = scope->queryResolvedScope(&ctx);
  1275. if (resolved)
  1276. instance.query.set(resolved->queryExpression());
  1277. }
  1278. gatherParseWarnings(ctx.errs, instance.query, parseCtx.orphanedWarnings);
  1279. if (instance.query && !optGenerateMeta && !optEvaluateResult)
  1280. instance.query.setown(convertAttributeToQuery(instance.query, ctx, syntaxChecking));
  1281. unsigned __int64 parseTimeNs = cycle_to_nanosec(get_cycles_now() - startCycles);
  1282. instance.stats.parseTime = (unsigned)nanoToMilli(parseTimeNs);
  1283. updateWorkunitStat(instance.wu, SSTcompilestage, "compile:parse", StTimeElapsed, NULL, parseTimeNs);
  1284. if (optExtraStats)
  1285. {
  1286. updateWorkunitStat(instance.wu, SSTcompilestage, "compile:cache", StNumAttribsProcessed, NULL, parseCtx.numAttribsProcessed);
  1287. }
  1288. if (exportDependencies)
  1289. {
  1290. StringBuffer dependenciesName;
  1291. if (instance.outputFilename && !streq(instance.outputFilename, "-"))
  1292. addNonEmptyPathSepChar(dependenciesName.append(optOutputDirectory)).append(instance.outputFilename);
  1293. else
  1294. dependenciesName.append(DEFAULT_OUTPUTNAME);
  1295. dependenciesName.append(".dependencies.xml");
  1296. Owned<IWUQuery> query = instance.wu->updateQuery();
  1297. associateLocalFile(query, FileTypeXml, dependenciesName, "Dependencies", 0);
  1298. saveXML(dependenciesName.str(), parseCtx.nestedDependTree);
  1299. }
  1300. if (optGenerateMeta)
  1301. instance.generatedMeta.setown(parseCtx.getClearMetaTree());
  1302. else if (optIncludeMeta && instance.metaOutputFilename)
  1303. {
  1304. Owned<IPropertyTree> meta = parseCtx.getClearMetaTree();
  1305. saveXML(instance.metaOutputFilename, meta, 0, XML_Embed|XML_LineBreak);
  1306. }
  1307. if (parseCtx.globalDependTree)
  1308. instance.globalDependTree.set(parseCtx.globalDependTree);
  1309. if (optEvaluateResult && !errorProcessor.errCount() && instance.query)
  1310. evaluateResult(instance);
  1311. }
  1312. catch (IException *e)
  1313. {
  1314. StringBuffer s;
  1315. e->errorMessage(s);
  1316. errorProcessor.reportError(3, s.str(), defaultErrorPathname, 1, 0, 0);
  1317. e->Release();
  1318. }
  1319. }
  1320. //Free up the repository (and any cached expressions) as soon as the expression has been parsed
  1321. instance.dataServer.clear();
  1322. if (!syntaxChecking && (errorProcessor.errCount() == prevErrs) && (!instance.query || !containsAnyActions(instance.query)))
  1323. {
  1324. errorProcessor.reportError(3, "Query is empty", defaultErrorPathname, 1, 0, 0);
  1325. return;
  1326. }
  1327. if (optArchive || optGenerateDepend)
  1328. return;
  1329. if (syntaxChecking || optGenerateMeta || optEvaluateResult)
  1330. return;
  1331. StringBuffer targetFilename;
  1332. const char * outputFilename = instance.outputFilename;
  1333. if (!outputFilename)
  1334. {
  1335. addNonEmptyPathSepChar(targetFilename.append(optOutputDirectory));
  1336. targetFilename.append(DEFAULT_OUTPUTNAME);
  1337. }
  1338. else if (strcmp(outputFilename, "-") == 0)
  1339. targetFilename.append("stdout:");
  1340. else
  1341. addNonEmptyPathSepChar(targetFilename.append(optOutputDirectory)).append(outputFilename);
  1342. //Check if it overlaps with the source file and add .eclout if so
  1343. if (instance.inputFile)
  1344. {
  1345. const char * originalFilename = instance.inputFile->queryFilename();
  1346. if (streq(targetFilename, originalFilename))
  1347. targetFilename.append(".eclout");
  1348. }
  1349. if (errorProcessor.errCount() == prevErrs)
  1350. {
  1351. const char * queryFullName = NULL;
  1352. instantECL(instance, instance.wu, queryFullName, errorProcessor, targetFilename);
  1353. }
  1354. else
  1355. {
  1356. if (stdIoHandle(targetFilename) == -1)
  1357. {
  1358. // MORE - what about intermediate files?
  1359. #ifdef _WIN32
  1360. StringBuffer goer;
  1361. remove(goer.append(targetFilename).append(".exe"));
  1362. remove(goer.clear().append(targetFilename).append(".exe.manifest"));
  1363. #else
  1364. remove(targetFilename);
  1365. #endif
  1366. }
  1367. }
  1368. unsigned __int64 totalTimeNs = cycle_to_nanosec(get_cycles_now() - startCycles);
  1369. CpuInfo systemFinishTime(false, true);
  1370. CpuInfo processFinishTime(true, false);
  1371. OwnedPtr<OsDiskStats> systemIoFinishInfo;
  1372. if (optGatherDiskStats)
  1373. systemIoFinishInfo.setown(new OsDiskStats(true));
  1374. instance.stats.generateTime = (unsigned)nanoToMilli(totalTimeNs) - instance.stats.parseTime;
  1375. const char *scopeName = optCompileBatchOut.isEmpty() ? "compile" : "compile:generate";
  1376. if (optCompileBatchOut.isEmpty())
  1377. updateWorkunitStat(instance.wu, SSTcompilestage, scopeName, StTimeElapsed, NULL, totalTimeNs);
  1378. const cost_type cost = money2cost_type(calcCost(getMachineCostRate(), nanoToMilli(totalTimeNs)));
  1379. if (cost)
  1380. instance.wu->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), SSTcompilestage, scopeName, StCostCompile, NULL, cost, 1, 0, StatsMergeReplace);
  1381. if (systemFinishTime.getTotal())
  1382. {
  1383. CpuInfo systemElapsed = systemFinishTime - systemStartTime;
  1384. CpuInfo processElapsed = processFinishTime - processStartTime;
  1385. updateWorkunitStat(instance.wu, SSTcompilestage, scopeName, StNumSysContextSwitches, NULL, systemElapsed.getNumContextSwitches());
  1386. updateWorkunitStat(instance.wu, SSTcompilestage, scopeName, StTimeOsUser, NULL, systemElapsed.getUserNs());
  1387. updateWorkunitStat(instance.wu, SSTcompilestage, scopeName, StTimeOsSystem, NULL, systemElapsed.getSystemNs());
  1388. updateWorkunitStat(instance.wu, SSTcompilestage, scopeName, StTimeOsTotal, NULL, systemElapsed.getTotalNs());
  1389. updateWorkunitStat(instance.wu, SSTcompilestage, scopeName, StTimeUser, NULL, processElapsed.getUserNs());
  1390. updateWorkunitStat(instance.wu, SSTcompilestage, scopeName, StTimeSystem, NULL, processElapsed.getSystemNs());
  1391. }
  1392. if (optGatherDiskStats)
  1393. {
  1394. const BlockIoStats summaryIo = systemIoFinishInfo->querySummaryStats() - systemIoStartInfo->querySummaryStats();
  1395. if (summaryIo.rd_sectors)
  1396. updateWorkunitStat(instance.wu, SSTcompilestage, scopeName, StSizeOsDiskRead, NULL, summaryIo.rd_sectors * summaryIo.getSectorSize());
  1397. if (summaryIo.wr_sectors)
  1398. updateWorkunitStat(instance.wu, SSTcompilestage, scopeName, StSizeOsDiskWrite, NULL, summaryIo.wr_sectors * summaryIo.getSectorSize());
  1399. }
  1400. }
  1401. void EclCC::processDefinitions(EclRepositoryManager & target)
  1402. {
  1403. ForEachItemIn(iDef, definitions)
  1404. {
  1405. const char * definition = definitions.item(iDef);
  1406. StringAttr name;
  1407. StringBuffer value;
  1408. const char * eq = strchr(definition, '=');
  1409. if (eq)
  1410. {
  1411. name.set(definition, eq-definition);
  1412. value.append(eq+1);
  1413. }
  1414. else
  1415. {
  1416. name.set(definition);
  1417. value.append("true");
  1418. }
  1419. value.append(";");
  1420. StringAttr module;
  1421. const char * attr;
  1422. const char * dot = strrchr(name, '.');
  1423. if (dot)
  1424. {
  1425. module.set(name, dot-name);
  1426. attr = dot+1;
  1427. }
  1428. else
  1429. {
  1430. module.set("");
  1431. attr = name;
  1432. }
  1433. //Create a repository with just that attribute.
  1434. timestamp_type ts = 1; // Use a non zero timestamp so the value can be cached. Changes are spotted through the optionHash
  1435. Owned<IFileContents> contents = createFileContentsFromText(value, NULL, false, NULL, ts);
  1436. target.addSingleDefinitionEclRepository(module, attr, contents, true);
  1437. }
  1438. }
  1439. void EclCC::processXmlFile(EclCompileInstance & instance, const char *archiveXML)
  1440. {
  1441. instance.srcArchive.setown(createPTreeFromXMLString(archiveXML, ipt_caseInsensitive));
  1442. if (optExpandPath)
  1443. {
  1444. expandArchive(optExpandPath, instance.srcArchive, true);
  1445. const char * queryText = instance.srcArchive->queryProp("Query");
  1446. if (queryText)
  1447. printf("%s", queryText);
  1448. return;
  1449. }
  1450. IPropertyTree * archiveTree = instance.srcArchive;
  1451. Owned<IPropertyTreeIterator> iter = archiveTree->getElements("Option");
  1452. ForEach(*iter)
  1453. {
  1454. IPropertyTree &item = iter->query();
  1455. instance.wu->setDebugValue(item.queryProp("@name"), item.queryProp("@value"), true);
  1456. }
  1457. //Mainly for testing...
  1458. Owned<IPropertyTreeIterator> iterDef = archiveTree->getElements("Definition");
  1459. ForEach(*iterDef)
  1460. {
  1461. IPropertyTree &item = iterDef->query();
  1462. const char * name = item.queryProp("@name");
  1463. const char * value = item.queryProp("@value");
  1464. StringBuffer definition;
  1465. definition.append(name);
  1466. if (value)
  1467. definition.append('=').append(value);
  1468. definitions.append(definition);
  1469. }
  1470. const char * queryText = archiveTree->queryProp("Query");
  1471. const char * queryAttributePath = archiveTree->queryProp("Query/@attributePath");
  1472. const char * queryAttributePackage = archiveTree->queryProp("Query/@package");
  1473. //Takes precedence over an entry in the archive - so you can submit parts of an archive.
  1474. if (optQueryMainAttribute)
  1475. {
  1476. queryAttributePath = optQueryMainAttribute;
  1477. queryAttributePackage = optQueryMainPackage;
  1478. }
  1479. //The legacy mode (if specified) in the archive takes precedence - it needs to match to compile.
  1480. instance.legacyImport = archiveTree->getPropBool("@legacyMode", instance.legacyImport);
  1481. instance.legacyWhen = archiveTree->getPropBool("@legacyMode", instance.legacyWhen);
  1482. instance.legacyImport = archiveTree->getPropBool("@legacyImport", instance.legacyImport);
  1483. instance.legacyWhen = archiveTree->getPropBool("@legacyWhen", instance.legacyWhen);
  1484. //Some old archives contained imports, but no definitions of the module. This option is to allow them to compile.
  1485. //It shouldn't be needed for new archives in non-legacy mode. (But neither should it cause any harm.)
  1486. instance.ignoreUnknownImport = archiveTree->getPropBool("@ignoreUnknownImport", true);
  1487. instance.eclVersion.set(archiveTree->queryProp("@eclVersion"));
  1488. if (optCheckEclVersion)
  1489. instance.checkEclVersionCompatible();
  1490. EclRepositoryManager localRepositoryManager;
  1491. processDefinitions(localRepositoryManager);
  1492. localRepositoryManager.inherit(repositoryManager); // Definitions, plugins, std library etc.
  1493. Owned<IFileContents> contents;
  1494. Owned<IEclPackage> mainPackage;
  1495. StringBuffer fullPath; // Here so it doesn't get freed when leaving the else block
  1496. if (queryText || queryAttributePath)
  1497. {
  1498. const char * sourceFilename = archiveTree->queryProp("Query/@originalFilename");
  1499. Owned<ISourcePath> sourcePath = createSourcePath(sourceFilename);
  1500. contents.setown(createFileContentsFromText(queryText, sourcePath, false, NULL, 0));
  1501. if (queryAttributePath && queryText && *queryText)
  1502. {
  1503. Owned<IEclSourceCollection> inputFileCollection = createSingleDefinitionEclCollection(queryAttributePath, contents);
  1504. localRepositoryManager.addRepository(inputFileCollection, nullptr, true);
  1505. }
  1506. }
  1507. else
  1508. {
  1509. //This is really only useful for regression testing
  1510. const char * queryText = archiveTree->queryProp("SyntaxCheck");
  1511. const char * syntaxCheckModule = archiveTree->queryProp("SyntaxCheck/@module");
  1512. const char * syntaxCheckAttribute = archiveTree->queryProp("SyntaxCheck/@attribute");
  1513. if (!queryText || !syntaxCheckModule || !syntaxCheckAttribute)
  1514. throw MakeStringException(1, "No query found in xml");
  1515. instance.wu->setDebugValueInt("syntaxCheck", true, true);
  1516. fullPath.append(syntaxCheckModule).append('.').append(syntaxCheckAttribute);
  1517. queryAttributePath = fullPath.str();
  1518. //Create a repository with just that attribute, and place it before the archive in the resolution order.
  1519. Owned<IFileContents> contents = createFileContentsFromText(queryText, NULL, false, NULL, 0);
  1520. localRepositoryManager.addSingleDefinitionEclRepository(syntaxCheckModule, syntaxCheckAttribute, contents, true);
  1521. }
  1522. localRepositoryManager.processArchive(archiveTree);
  1523. instance.dataServer.setown(localRepositoryManager.createPackage(nullptr));
  1524. if (queryAttributePackage)
  1525. mainPackage.set(localRepositoryManager.queryDependentRepository(nullptr, queryAttributePackage));
  1526. //Ensure classes are not linked by anything else
  1527. localRepositoryManager.kill(); // help ensure non-shared repositories are freed as soon as possible
  1528. processSingleQuery(instance, contents, queryAttributePath, mainPackage);
  1529. }
  1530. //=========================================================================================
  1531. void EclCC::processFile(EclCompileInstance & instance)
  1532. {
  1533. clearTransformStats();
  1534. Linked<IFile> inputFile = instance.inputFile;
  1535. const char * curFilename = inputFile->queryFilename();
  1536. assertex(curFilename);
  1537. bool inputFromStdIn = streq(curFilename, "stdin:");
  1538. //Ensure the filename is fully expanded, but do not recreate the IFile if it was already absolute
  1539. StringBuffer expandedSourceName;
  1540. if (!inputFromStdIn && !optNoSourcePath && !isAbsolutePath(curFilename))
  1541. {
  1542. makeAbsolutePath(curFilename, expandedSourceName);
  1543. inputFile.setown(createIFile(expandedSourceName));
  1544. }
  1545. else
  1546. expandedSourceName.append(curFilename);
  1547. Owned<ISourcePath> sourcePath = (optNoSourcePath||inputFromStdIn) ? NULL : createSourcePath(expandedSourceName);
  1548. Owned<IFileContents> queryText = createFileContents(inputFile, sourcePath, false, NULL);
  1549. const char * queryTxt = queryText->getText();
  1550. if (optArchive || optGenerateDepend || optSaveQueryArchive)
  1551. instance.archive.setown(createAttributeArchive());
  1552. instance.wu.setown(createLocalWorkUnit());
  1553. //Record the version of the compiler in the workunit, but not when regression testing (to avoid spurious differences)
  1554. if (!optBatchMode)
  1555. instance.wu->setDebugValue("eclcc_compiler_version", LANGUAGE_VERSION, true);
  1556. if (optSaveQueryText)
  1557. {
  1558. Owned<IWUQuery> q = instance.wu->updateQuery();
  1559. q->setQueryText(queryTxt);
  1560. }
  1561. //On a system with userECL not allowed, all compilations must be from checked-in code that has been
  1562. //deployed to the eclcc machine via other means (typically via a version-control system)
  1563. if (!allowAccess("userECL", false) && (!optQueryMainAttribute || queryText->length()))
  1564. {
  1565. instance.queryErrorProcessor().reportError(HQLERR_UserCodeNotAllowed, HQLERR_UserCodeNotAllowed_Text, NULL, 1, 0, 0);
  1566. }
  1567. else if (isArchiveQuery(queryTxt))
  1568. {
  1569. instance.fromArchive = true;
  1570. processXmlFile(instance, queryTxt);
  1571. }
  1572. else
  1573. {
  1574. StringBuffer attributePath;
  1575. const char * attributePackage = nullptr;
  1576. bool withinRepository = false;
  1577. //Specifying --main indicates that the query text (if present) replaces that definition
  1578. if (optQueryMainAttribute)
  1579. {
  1580. withinRepository = true;
  1581. attributePath.clear().append(optQueryMainAttribute);
  1582. attributePackage = optQueryMainPackage;
  1583. }
  1584. else
  1585. {
  1586. withinRepository = !inputFromStdIn && !optNoSourcePath && checkWithinRepository(attributePath, curFilename);
  1587. }
  1588. EclRepositoryManager localRepositoryManager;
  1589. processDefinitions(localRepositoryManager);
  1590. localRepositoryManager.inherit(repositoryManager); // don't include -I
  1591. if (!optNoBundles)
  1592. localRepositoryManager.addQuerySourceFileEclRepository(&instance.queryErrorProcessor(), eclBundlePath.str(), ESFoptional|ESFnodependencies, 0);
  1593. //Ensure that this source file is used as the definition (in case there are potential clashes)
  1594. //Note, this will not override standard library files.
  1595. if (withinRepository)
  1596. {
  1597. //-main only overrides the definition if the query is non-empty. Otherwise use the existing text.
  1598. if (!optQueryMainAttribute || queryText->length())
  1599. {
  1600. Owned<IEclSourceCollection> inputFileCollection = createSingleDefinitionEclCollection(attributePath, queryText);
  1601. localRepositoryManager.addRepository(inputFileCollection, nullptr, true);
  1602. }
  1603. }
  1604. else
  1605. {
  1606. //Ensure that $ is valid for any file submitted - even if it isn't in the include direcotories
  1607. //Disable this for the moment when running the regression suite.
  1608. if (!optBatchMode && !withinRepository && !inputFromStdIn && !optNoSourcePath && !optLegacyImport)
  1609. {
  1610. //Associate the contents of the directory with an internal module called _local_directory_
  1611. //(If it was root it might override existing root symbols). $ is the only public way to get at the symbol
  1612. const char * moduleName = INTERNAL_LOCAL_MODULE_NAME;
  1613. IIdAtom * moduleNameId = createIdAtom(moduleName);
  1614. StringBuffer thisDirectory;
  1615. StringBuffer thisTail;
  1616. splitFilename(expandedSourceName, &thisDirectory, &thisDirectory, &thisTail, NULL);
  1617. attributePath.append(moduleName).append(".").append(thisTail);
  1618. Owned<IEclSourceCollection> inputFileCollection = createSingleDefinitionEclCollection(attributePath, queryText);
  1619. localRepositoryManager.addRepository(inputFileCollection, nullptr, true);
  1620. Owned<IEclSourceCollection> directory = createFileSystemEclCollection(&instance.queryErrorProcessor(), thisDirectory, ESFnone, 0);
  1621. localRepositoryManager.addNestedRepository(moduleNameId, directory, true);
  1622. }
  1623. }
  1624. if (attributePackage)
  1625. {
  1626. //If attribute package is specified, resolve that package as the source for the query, and pass null to processSingleQuery
  1627. instance.dataServer.set(localRepositoryManager.queryDependentRepository(nullptr, attributePackage));
  1628. }
  1629. else
  1630. {
  1631. localRepositoryManager.addQuerySourceFileEclRepository(&instance.queryErrorProcessor(), querySearchPath.str(), ESFdependencies, 0);
  1632. instance.dataServer.setown(localRepositoryManager.createPackage(nullptr));
  1633. }
  1634. processSingleQuery(instance, queryText, attributePath.str(), nullptr);
  1635. }
  1636. if (!instance.reportErrorSummary() || instance.archive || (optGenerateMeta && instance.generatedMeta))
  1637. generateOutput(instance);
  1638. //Transform stats are gathered in static global variables. Revisit if the code generator is multi threaded.
  1639. if (instance.wu->getDebugValueBool("timeTransforms", false))
  1640. {
  1641. WuStatisticTarget statsTarget(instance.wu, "eclcc");
  1642. gatherTransformStats(statsTarget);
  1643. }
  1644. }
  1645. IFileIO * EclCC::createArchiveOutputFile(EclCompileInstance & instance)
  1646. {
  1647. StringBuffer archiveName;
  1648. if (instance.outputFilename && !streq(instance.outputFilename, "-"))
  1649. addNonEmptyPathSepChar(archiveName.append(optOutputDirectory)).append(instance.outputFilename);
  1650. else
  1651. archiveName.append("stdout:");
  1652. //Work around windows problem writing 64K to stdout if not redirected/piped
  1653. OwnedIFile ifile = createIFile(archiveName);
  1654. return ifile->open(IFOcreate);
  1655. }
  1656. void EclCC::outputXmlToOutputFile(EclCompileInstance & instance, IPropertyTree * xml)
  1657. {
  1658. OwnedIFileIO ifileio = createArchiveOutputFile(instance);
  1659. if (ifileio)
  1660. {
  1661. //Work around windows problem writing 64K to stdout if not redirected/piped
  1662. Owned<IIOStream> stream = createIOStream(ifileio.get());
  1663. Owned<IIOStream> buffered = createBufferedIOStream(stream,0x8000);
  1664. saveXML(*buffered, xml);
  1665. }
  1666. }
  1667. void EclCC::addFilenameDependency(StringBuffer & target, EclCompileInstance & instance, const char * filename)
  1668. {
  1669. if (!filename)
  1670. return;
  1671. //Ignore plugins and standard library components
  1672. if (isWithinPath(filename, pluginsPath) || isWithinPath(filename, eclLibraryPath))
  1673. return;
  1674. //Don't include the input file in the dependencies.
  1675. if (instance.inputFile)
  1676. {
  1677. const char * sourceFilename = instance.inputFile->queryFilename();
  1678. if (sourceFilename && streq(sourceFilename, filename))
  1679. return;
  1680. }
  1681. target.append(filename).newline();
  1682. }
  1683. void EclCC::generateOutput(EclCompileInstance & instance)
  1684. {
  1685. const char * outputFilename = instance.outputFilename;
  1686. if (instance.archive)
  1687. {
  1688. if (optGenerateDepend)
  1689. {
  1690. //Walk the archive, and output all filenames that aren't
  1691. //a)in a plugin b) in std.lib c) the original source file.
  1692. StringBuffer filenames;
  1693. Owned<IPropertyTreeIterator> modIter = instance.archive->getElements("Module");
  1694. ForEach(*modIter)
  1695. {
  1696. IPropertyTree * module = &modIter->query();
  1697. if (module->hasProp("@plugin"))
  1698. continue;
  1699. addFilenameDependency(filenames, instance, module->queryProp("@sourcePath"));
  1700. Owned<IPropertyTreeIterator> defIter = module->getElements("Attribute");
  1701. ForEach(*defIter)
  1702. {
  1703. IPropertyTree * definition = &defIter->query();
  1704. addFilenameDependency(filenames, instance, definition->queryProp("@sourcePath"));
  1705. }
  1706. }
  1707. OwnedIFileIO ifileio = createArchiveOutputFile(instance);
  1708. if (ifileio)
  1709. ifileio->write(0, filenames.length(), filenames.str());
  1710. }
  1711. else
  1712. {
  1713. // Output option settings
  1714. Owned<IStringIterator> debugValues = &instance.wu->getDebugValues();
  1715. ForEach (*debugValues)
  1716. {
  1717. SCMStringBuffer debugStr, valueStr;
  1718. debugValues->str(debugStr);
  1719. instance.wu->getDebugValue(debugStr.str(), valueStr);
  1720. Owned<IPropertyTree> option = createPTree("Option");
  1721. option->setProp("@name", debugStr.str());
  1722. option->setProp("@value", valueStr.str());
  1723. instance.archive->addPropTree("Option", option.getClear());
  1724. }
  1725. gatherResourceManifestFilenames(instance, resourceManifestFiles);
  1726. ForEachItemIn(i, resourceManifestFiles)
  1727. addManifestResourcesToArchive(instance.archive, resourceManifestFiles.item(i));
  1728. if (optArchive)
  1729. {
  1730. if (optCheckDirty)
  1731. {
  1732. Owned<IPipeProcess> pipe = createPipeProcess();
  1733. if (!pipe->run("git", "git describe --always --tags --dirty --long", ".", false, true, false, 0, false))
  1734. {
  1735. UWARNLOG("Failed to run git describe");
  1736. }
  1737. else
  1738. {
  1739. try
  1740. {
  1741. unsigned retcode = pipe->wait();
  1742. StringBuffer buf;
  1743. Owned<ISimpleReadStream> pipeReader = pipe->getOutputStream();
  1744. readSimpleStream(buf, *pipeReader, 128);
  1745. if (retcode)
  1746. UWARNLOG("Failed to run git describe: returned %d (%s)", retcode, buf.str());
  1747. else if (buf.length())
  1748. {
  1749. buf.replaceString("\n","");
  1750. instance.archive->setProp("@git", buf);
  1751. }
  1752. }
  1753. catch (IException *e)
  1754. {
  1755. EXCLOG(e, "Exception running git describe");
  1756. e->Release();
  1757. }
  1758. }
  1759. }
  1760. outputXmlToOutputFile(instance, instance.archive);
  1761. }
  1762. }
  1763. }
  1764. if (optGenerateMeta && instance.generatedMeta)
  1765. outputXmlToOutputFile(instance, instance.generatedMeta);
  1766. if (optWorkUnit && instance.wu)
  1767. {
  1768. StringBuffer xmlFilename;
  1769. addNonEmptyPathSepChar(xmlFilename.append(optOutputDirectory));
  1770. if (outputFilename)
  1771. xmlFilename.append(outputFilename);
  1772. else
  1773. xmlFilename.append(DEFAULT_OUTPUTNAME);
  1774. xmlFilename.append(".xml");
  1775. exportWorkUnitToXMLFile(instance.wu, xmlFilename, 0, true, false, false, false);
  1776. }
  1777. }
  1778. void EclCC::processReference(EclCompileInstance & instance, const char * queryAttributePath, const char * queryAttributePackage)
  1779. {
  1780. const char * outputFilename = instance.outputFilename;
  1781. instance.wu.setown(createLocalWorkUnit());
  1782. if (optArchive || optGenerateDepend || optSaveQueryArchive)
  1783. instance.archive.setown(createAttributeArchive());
  1784. EclRepositoryManager localRepositoryManager;
  1785. processDefinitions(localRepositoryManager);
  1786. localRepositoryManager.inherit(repositoryManager);
  1787. if (!optNoBundles)
  1788. localRepositoryManager.addQuerySourceFileEclRepository(&instance.queryErrorProcessor(), eclBundlePath.str(), ESFoptional|ESFnodependencies, 0);
  1789. if (queryAttributePackage)
  1790. {
  1791. instance.dataServer.set(localRepositoryManager.queryDependentRepository(nullptr, queryAttributePackage));
  1792. }
  1793. else
  1794. {
  1795. const char * searchPath = querySearchPath;
  1796. while (*searchPath == ENVSEPCHAR)
  1797. searchPath++;
  1798. if (looksLikeGitPackage(searchPath))
  1799. {
  1800. instance.dataServer.set(localRepositoryManager.queryDependentRepository(nullptr, searchPath));
  1801. }
  1802. else
  1803. {
  1804. localRepositoryManager.addQuerySourceFileEclRepository(&instance.queryErrorProcessor(), searchPath, ESFdependencies, 0);
  1805. instance.dataServer.setown(localRepositoryManager.createPackage(nullptr));
  1806. }
  1807. }
  1808. processSingleQuery(instance, NULL, queryAttributePath, nullptr);
  1809. if (instance.reportErrorSummary())
  1810. return;
  1811. generateOutput(instance);
  1812. }
  1813. bool EclCC::generatePrecompiledHeader()
  1814. {
  1815. if (inputFiles.ordinality() != 0)
  1816. {
  1817. traceError("No input files should be specified when generating precompiled header");
  1818. return false;
  1819. }
  1820. StringArray paths;
  1821. paths.appendList(cppIncludePath, ENVSEPSTR);
  1822. const char *foundPath = NULL;
  1823. ForEachItemIn(idx, paths)
  1824. {
  1825. StringBuffer fullpath;
  1826. fullpath.append(paths.item(idx));
  1827. addPathSepChar(fullpath).append("eclinclude4.hpp");
  1828. if (checkFileExists(fullpath))
  1829. {
  1830. foundPath = paths.item(idx);
  1831. break;
  1832. }
  1833. }
  1834. if (!foundPath)
  1835. {
  1836. traceError("Cannot find eclinclude4.hpp");
  1837. return false;
  1838. }
  1839. Owned<ICppCompiler> compiler = createCompiler("precompile", foundPath, nullptr, nullptr);
  1840. compiler->setDebug(true); // a precompiled header with debug can be used for no-debug, but not vice versa
  1841. compiler->addSourceFile("eclinclude4.hpp", nullptr);
  1842. compiler->setPrecompileHeader(true);
  1843. if (compiler->compile())
  1844. {
  1845. try
  1846. {
  1847. Owned<IFile> log = createIFile(cclogFilename);
  1848. log->remove();
  1849. }
  1850. catch (IException * e)
  1851. {
  1852. e->Release();
  1853. }
  1854. return true;
  1855. }
  1856. else
  1857. {
  1858. traceError("Compilation failed - see %s for details", cclogFilename.str());
  1859. return false;
  1860. }
  1861. }
  1862. static void checkForOverlappingPaths(const char * path)
  1863. {
  1864. StringArray originalPaths;
  1865. originalPaths.appendList(path, ENVSEPSTR);
  1866. StringArray expandedPaths;
  1867. ForEachItemIn(i1, originalPaths)
  1868. {
  1869. const char * cur = originalPaths.item(i1);
  1870. if (*cur)
  1871. {
  1872. StringBuffer expanded;
  1873. makeAbsolutePath(cur, expanded);
  1874. expandedPaths.append(expanded);
  1875. }
  1876. }
  1877. //Sort alphabetically, smallest strings will come first
  1878. expandedPaths.sortAscii(!filenamesAreCaseSensitive);
  1879. //If one string is a subset of another then the shorter will come immediately before at least one that overlaps
  1880. for (unsigned i=1; i < expandedPaths.ordinality(); i++)
  1881. {
  1882. const char * prev = expandedPaths.item(i-1);
  1883. const char * next = expandedPaths.item(i);
  1884. if (hasPrefix(next, prev, filenamesAreCaseSensitive))
  1885. {
  1886. if (!streq(next, prev))
  1887. fprintf(stderr, "Warning: Include paths -I '%s' and '%s' overlap\n", prev, next);
  1888. }
  1889. }
  1890. }
  1891. bool EclCC::processFiles()
  1892. {
  1893. loadOptions();
  1894. ForEachItemIn(idx, inputFileNames)
  1895. {
  1896. processArgvFilename(inputFiles, inputFileNames.item(idx));
  1897. }
  1898. if (optShowPaths)
  1899. {
  1900. printf("CL_PATH=%s\n", compilerPath.str());
  1901. printf("ECLCC_ECLBUNDLE_PATH=%s\n", eclBundlePath.str());
  1902. printf("ECLCC_ECLREPO_PATH=%s\n", eclRepoPath.str());
  1903. printf("ECLCC_ECLINCLUDE_PATH=%s\n", stdIncludeLibraryPath.str());
  1904. printf("ECLCC_ECLLIBRARY_PATH=%s\n", eclLibraryPath.str());
  1905. printf("ECLCC_INCLUDE_PATH=%s\n", cppIncludePath.str());
  1906. printf("ECLCC_LIBRARY_PATH=%s\n", libraryPath.str());
  1907. printf("ECLCC_PLUGIN_PATH=%s\n", pluginsPath.str());
  1908. printf("HPCC_FILEHOOKS_PATH=%s\n", hooksPath.str());
  1909. return true;
  1910. }
  1911. if (optGenerateHeader)
  1912. {
  1913. return generatePrecompiledHeader();
  1914. }
  1915. else if (inputFiles.ordinality() == 0)
  1916. {
  1917. if (optBatchMode || !optQueryMainAttribute)
  1918. {
  1919. UERRLOG("No input files could be opened");
  1920. return false;
  1921. }
  1922. }
  1923. if (!optNoStdInc && stdIncludeLibraryPath.length())
  1924. querySearchPath.append(stdIncludeLibraryPath).append(ENVSEPCHAR);
  1925. querySearchPath.append(includeLibraryPath);
  1926. if (optCheckIncludePaths)
  1927. checkForOverlappingPaths(querySearchPath);
  1928. bool includePluginsInArchive = !optPruneArchive;
  1929. bool includeLibraryInArchive = !optPruneArchive;
  1930. Owned<IErrorReceiver> errs = optXml ? createXmlFileErrorReceiver(stderr) : createFileErrorReceiver(stderr);
  1931. bool ok = true;
  1932. try
  1933. {
  1934. //Set up the default repository information. This could be simplified to not use a localRepositoryManager later
  1935. //if eclcc did not have a strange mode for running multiple queries as part of the regression suite testing on windows.
  1936. repositoryManager.setOptions(eclRepoPath, optDefaultGitPrefix, optFetchRepos, optUpdateRepos, logVerbose);
  1937. ForEachItemIn(iMapping, repoMappings)
  1938. {
  1939. const char * cur = repoMappings.item(iMapping);
  1940. const char * eq = strchr(cur, '=');
  1941. StringBuffer repo(eq-cur, cur);
  1942. repositoryManager.addMapping(repo, eq+1);
  1943. }
  1944. //Items first in the list have priority -Dxxx=y overrides all
  1945. repositoryManager.addSharedSourceFileEclRepository(errs, pluginsPath.str(), ESFallowplugins|ESFnodependencies, logVerbose ? PLUGIN_DLL_MODULE : 0, includePluginsInArchive);
  1946. repositoryManager.addSharedSourceFileEclRepository(errs, eclLibraryPath.str(), ESFnodependencies, 0, includeLibraryInArchive);
  1947. //Bundles are not included in other repos. Should eventually be replaced by dependent repos
  1948. //Ensure symbols for plugins are initialised - see comment before CHqlMergedScope...
  1949. // lookupAllRootDefinitions(pluginsRepository);
  1950. if (optBatchMode)
  1951. {
  1952. processBatchFiles();
  1953. }
  1954. else if (inputFiles.ordinality() == 0)
  1955. {
  1956. EclCompileInstance info(*this, NULL, *errs, stderr, optOutputFilename, optLegacyImport, optLegacyWhen, optIgnoreSignatures, optIgnoreUnknownImport, optXml);
  1957. processReference(info, optQueryMainAttribute, optQueryMainPackage);
  1958. ok = (errs->errCount() == 0);
  1959. info.logStats(logTimings);
  1960. }
  1961. else
  1962. {
  1963. EclCompileInstance info(*this, &inputFiles.item(0), *errs, stderr, optOutputFilename, optLegacyImport, optLegacyWhen, optIgnoreSignatures, optIgnoreUnknownImport, optXml);
  1964. processFile(info);
  1965. ok = (errs->errCount() == 0);
  1966. info.logStats(logTimings);
  1967. }
  1968. }
  1969. catch (IException * e)
  1970. {
  1971. //Ensure any exceptions are reported as xml if that option is selected
  1972. StringBuffer msg;
  1973. errs->reportError(e->errorCode(), e->errorMessage(msg).str(), nullptr, 0, 0, 0);
  1974. e->Release();
  1975. ok = false;
  1976. }
  1977. return ok;
  1978. }
  1979. void EclCC::setDebugOption(const char * name, bool value)
  1980. {
  1981. StringBuffer temp;
  1982. temp.append(name).append("=").append(value ? "1" : "0");
  1983. debugOptions.append(temp);
  1984. }
  1985. void EclCC::traceError(char const * format, ...)
  1986. {
  1987. va_list args;
  1988. va_start(args, format);
  1989. if (optXml)
  1990. {
  1991. StringBuffer msg;
  1992. msg.valist_appendf(format, args);
  1993. StringBuffer encoded;
  1994. encodeXML(msg.str(), encoded);
  1995. UERRLOG("<exception msg='%s'/>", encoded.str());
  1996. }
  1997. else
  1998. VALOG(MCuserError, unknownJob, format, args);
  1999. va_end(args);
  2000. }
  2001. void EclCompileInstance::checkEclVersionCompatible()
  2002. {
  2003. //Strange function that might modify errorProcessor...
  2004. ::checkEclVersionCompatible(errorProcessor, eclVersion);
  2005. }
  2006. class StatsLogger : public WuScopeVisitorBase
  2007. {
  2008. public:
  2009. virtual void noteStatistic(StatisticKind kind, unsigned __int64 value, IConstWUStatistic & cur) override
  2010. {
  2011. const char * scope = cur.queryScope();
  2012. OwnedPTree tree = createPTree("stat", ipt_fast);
  2013. tree->setProp("@kind", queryStatisticName(cur.getKind()));
  2014. tree->setProp("@scope", scope);
  2015. tree->setPropInt("@scopeType", (unsigned)cur.getScopeType());
  2016. tree->setPropInt64("@value", cur.getValue());
  2017. tree->setPropInt64("@max", cur.getMax());
  2018. tree->setPropInt64("@count", cur.getCount());
  2019. StringBuffer msg;
  2020. toXML(tree, msg, 0, XML_Embed);
  2021. fprintf(stderr, "%s\n", msg.str());
  2022. }
  2023. };
  2024. void EclCompileInstance::logStats(bool logTimings)
  2025. {
  2026. if (wu && wu->getDebugValueBool("logCompileStats", false))
  2027. {
  2028. memsize_t peakVm, peakResident;
  2029. getPeakMemUsage(peakVm, peakResident);
  2030. //Stats: added as a prefix so it is easy to grep, and a comma so can be read as a csv list.
  2031. DBGLOG("Stats:,parse,%u,generate,%u,peakmem,%u,xml,%" I64F "u,cpp,%" I64F "u",
  2032. stats.parseTime, stats.generateTime, (unsigned)(peakResident / 0x100000),
  2033. (unsigned __int64)stats.xmlSize, (unsigned __int64)stats.cppSize);
  2034. //Following only produces output if the system has been compiled with TRANSFORM_STATS defined
  2035. dbglogTransformStats(true);
  2036. }
  2037. if (logTimings)
  2038. {
  2039. const WuScopeFilter filter("props[stat]");
  2040. StatsLogger logger;
  2041. Owned<IConstWUScopeIterator> scopes = &wu->getScopeIterator(filter);
  2042. ForEach(*scopes)
  2043. scopes->playProperties(logger);
  2044. }
  2045. }
  2046. bool EclCompileInstance::reportErrorSummary()
  2047. {
  2048. if (errorProcessor->errCount() || errorProcessor->warnCount())
  2049. {
  2050. if (optXml)
  2051. fprintf(errout, "<summary errors='%u' warnings='%u'/>\n", errorProcessor->errCount(), errorProcessor->warnCount());
  2052. else
  2053. fprintf(errout, "%d error%s, %d warning%s\n", errorProcessor->errCount(), errorProcessor->errCount()<=1 ? "" : "s",
  2054. errorProcessor->warnCount(), errorProcessor->warnCount()<=1?"":"s");
  2055. }
  2056. return errorProcessor->errCount() != 0;
  2057. }
  2058. void EclCompileInstance::noteCluster(const char *clusterName)
  2059. {
  2060. }
  2061. void EclCompileInstance::pushCluster(const char *clusterName)
  2062. {
  2063. eclcc.pushCluster(clusterName);
  2064. }
  2065. void EclCompileInstance::popCluster()
  2066. {
  2067. eclcc.popCluster();
  2068. }
  2069. unsigned EclCompileInstance::lookupClusterSize() const
  2070. {
  2071. return eclcc.lookupClusterSize();
  2072. }
  2073. bool EclCompileInstance::allowAccess(const char * category, bool isSigned)
  2074. {
  2075. return eclcc.allowAccess(category, isSigned);
  2076. }
  2077. IHqlExpression * EclCompileInstance::lookupDFSlayout(const char *filename, IErrorReceiver &errs, const ECLlocation &location, bool isOpt) const
  2078. {
  2079. return eclcc.lookupDFSlayout(filename, errs, location, isOpt);
  2080. }
  2081. void EclCompileInstance::getTargetPlatform(StringBuffer & result)
  2082. {
  2083. SCMStringBuffer targetText;
  2084. wu->getDebugValue("targetClusterType", targetText);
  2085. ClusterType clusterType = getClusterType(targetText.s.str());
  2086. if (clusterType != NoCluster)
  2087. result.append(clusterTypeString(clusterType, true));
  2088. else
  2089. return eclcc.getTargetPlatform(result);
  2090. }
  2091. void EclCC::appendNeverSimplifyList(const char *attribsList)
  2092. {
  2093. const char * p = attribsList;
  2094. while (*p)
  2095. {
  2096. StringBuffer attribRegex;
  2097. if (*p == ',') p++;
  2098. for (; *p && *p != ','; p++)
  2099. {
  2100. if (*p=='/' || *p=='.' || *p=='\\')
  2101. attribRegex.append("\\.");
  2102. else
  2103. attribRegex.append(*p);
  2104. }
  2105. if (attribRegex.length() > 0)
  2106. {
  2107. if (neverSimplifyRegEx.length() > 0)
  2108. neverSimplifyRegEx.append("|");
  2109. // Match attribute and all child scopes
  2110. neverSimplifyRegEx.append(attribRegex.str()).append("(\\..+)?");
  2111. }
  2112. }
  2113. }
  2114. void EclCC::pushCluster(const char *clusterName)
  2115. {
  2116. clusters.append(clusterName);
  2117. prevClusterSize = -1; // i.e. not cached
  2118. }
  2119. void EclCC::popCluster()
  2120. {
  2121. clusters.pop();
  2122. prevClusterSize = -1; // i.e. not cached
  2123. }
  2124. bool EclCC::checkDaliConnected() const
  2125. {
  2126. if (!daliConnected)
  2127. {
  2128. try
  2129. {
  2130. Owned<IGroup> serverGroup = createIGroup(optDFS.str(), DALI_SERVER_PORT);
  2131. if (!initClientProcess(serverGroup, DCR_EclCC, 0, NULL, NULL, optDaliTimeout))
  2132. {
  2133. disconnectReported = true;
  2134. return false;
  2135. }
  2136. if (!optUser.isEmpty())
  2137. {
  2138. udesc.setown(createUserDescriptor());
  2139. udesc->set(optUser, optPassword);
  2140. }
  2141. }
  2142. catch (IException *E)
  2143. {
  2144. E->Release();
  2145. disconnectReported = true;
  2146. return false;
  2147. }
  2148. daliConnected = true;
  2149. }
  2150. return true;
  2151. }
  2152. unsigned EclCC::lookupClusterSize() const
  2153. {
  2154. CriticalBlock b(dfsCrit); // Overkill at present but maybe one day codegen will start threading? If it does the stack is also iffy!
  2155. #ifndef _CONTAINERIZED
  2156. if (!optDFS || disconnectReported || !checkDaliConnected())
  2157. return 0;
  2158. #endif
  2159. if (prevClusterSize != -1)
  2160. return (unsigned) prevClusterSize;
  2161. const char *cluster = clusters ? clusters.tos() : optCluster.str();
  2162. if (isEmptyString(cluster) || strieq(cluster, "<unknown>"))
  2163. prevClusterSize = 0;
  2164. else
  2165. {
  2166. #ifdef _CONTAINERIZED
  2167. VStringBuffer xpath("queues[@name=\"%s\"]", cluster);
  2168. IPropertyTree * queue = configuration->queryPropTree(xpath);
  2169. if (queue)
  2170. prevClusterSize = queue->getPropInt("@width", 1);
  2171. else
  2172. prevClusterSize = 0;
  2173. #else
  2174. Owned<IConstWUClusterInfo> clusterInfo = getTargetClusterInfo(cluster);
  2175. prevClusterSize = clusterInfo ? clusterInfo->getSize() : 0;
  2176. #endif
  2177. }
  2178. DBGLOG("Cluster %s has size %d", cluster, prevClusterSize);
  2179. return prevClusterSize;
  2180. }
  2181. IHqlExpression *EclCC::lookupDFSlayout(const char *filename, IErrorReceiver &errs, const ECLlocation &location, bool isOpt) const
  2182. {
  2183. CriticalBlock b(dfsCrit); // Overkill at present but maybe one day codegen will start threading?
  2184. if (!optDFS || disconnectReported)
  2185. {
  2186. // Dali lookup disabled, yet translation requested. Should we report if OPT set?
  2187. if (!(optArchive || optGenerateDepend || optSyntax || optGenerateMeta || optEvaluateResult || disconnectReported))
  2188. {
  2189. VStringBuffer msg("Error looking up file %s in DFS - DFS not configured", filename);
  2190. errs.reportWarning(CategoryDFS, HQLWRN_DFSlookupFailure, msg.str(), str(location.sourcePath), location.lineno, location.column, location.position);
  2191. disconnectReported = true;
  2192. }
  2193. return nullptr;
  2194. }
  2195. if (!checkDaliConnected())
  2196. {
  2197. VStringBuffer msg("Error looking up file %s in DFS - failed to connect to %s", filename, optDFS.str());
  2198. errs.reportError(HQLWRN_DFSlookupFailure, msg.str(), str(location.sourcePath), location.lineno, location.column, location.position);
  2199. return nullptr;
  2200. }
  2201. // Do any scope manipulation
  2202. StringBuffer lookupName; // do NOT move inside the curly braces below - this needs to stay in scope longer than that
  2203. if (filename[0]=='~')
  2204. filename++;
  2205. else if (!optScope.isEmpty())
  2206. {
  2207. lookupName.appendf("%s::%s", optScope.str(), filename);
  2208. filename = lookupName.str();
  2209. }
  2210. // First lookup the name in our cache...
  2211. Linked<IHqlExpression> ret = fileCache.getValue(filename);
  2212. if (ret)
  2213. return ret.getClear();
  2214. int err = 0;
  2215. OwnedHqlExpr diskRecord;
  2216. // check the nohit cache...
  2217. int *nohit = fileMissCache.getValue(filename);
  2218. if (nohit)
  2219. err = *nohit;
  2220. else
  2221. {
  2222. // Look up the file in Dali
  2223. try
  2224. {
  2225. Owned<IDistributedFile> dfsFile = wsdfs::lookup(filename, udesc, AccessMode::tbdRead, false, false, nullptr, defaultPrivilegedUser, INFINITE);
  2226. if (dfsFile)
  2227. {
  2228. const char *recordECL = dfsFile->queryAttributes().queryProp("ECL");
  2229. if (recordECL)
  2230. {
  2231. MultiErrorReceiver errs;
  2232. diskRecord.setown(parseQuery(recordECL, &errs));
  2233. if (errs.errCount())
  2234. err = HQLWRN_DFSlookupInvalidRecord;
  2235. else
  2236. {
  2237. diskRecord.set(diskRecord->queryBody()); // Remove location info - it's meaningless
  2238. if (dfsFile->queryAttributes().hasProp("_record_layout"))
  2239. {
  2240. MemoryBuffer mb;
  2241. dfsFile->queryAttributes().getPropBin("_record_layout", mb);
  2242. diskRecord.setown(patchEclRecordDefinitionFromRecordLayout(diskRecord, mb));
  2243. }
  2244. }
  2245. }
  2246. else
  2247. err = HQLWRN_DFSlookupNoRecord;
  2248. }
  2249. else
  2250. err = HQLWRN_DFSlookupNoFile;
  2251. }
  2252. catch (IException *E)
  2253. {
  2254. unsigned errCode = E->errorCode();
  2255. if (errCode==DFSERR_LookupAccessDenied)
  2256. err = HQLWRN_DFSdenied;
  2257. else
  2258. throw; // Anything else is an internal error which will be caught elsewhere
  2259. }
  2260. }
  2261. if (err)
  2262. {
  2263. // Report error, and add it to the nohit cache
  2264. const char *reason = nullptr;
  2265. switch (err)
  2266. {
  2267. case HQLWRN_DFSlookupInvalidRecord:
  2268. reason = "invalid layout information found";
  2269. break;
  2270. case HQLWRN_DFSlookupNoRecord:
  2271. reason = "no layout information found";
  2272. break;
  2273. case HQLWRN_DFSdenied:
  2274. reason = "access denied";
  2275. break;
  2276. case HQLWRN_DFSlookupNoFile:
  2277. if (!isOpt)
  2278. reason = "file not found";
  2279. break;
  2280. }
  2281. if (reason)
  2282. {
  2283. VStringBuffer msg("Error looking up file %s in DFS - %s", filename, reason);
  2284. errs.reportWarning(CategoryDFS, err, msg.str(), str(location.sourcePath), location.lineno, location.column, location.position);
  2285. }
  2286. if (!nohit)
  2287. fileMissCache.setValue(filename, err);
  2288. return nullptr;
  2289. }
  2290. assertex(diskRecord);
  2291. // Add it to the cache
  2292. fileCache.setValue(filename, diskRecord);
  2293. return diskRecord.getClear();
  2294. }
  2295. bool EclCC::allowAccess(const char * category, bool isSigned)
  2296. {
  2297. ForEachItemIn(idx1, deniedPermissions)
  2298. {
  2299. if (stricmp(deniedPermissions.item(idx1), category)==0)
  2300. return false;
  2301. }
  2302. ForEachItemIn(idx2, allowSignedPermissions)
  2303. {
  2304. if (stricmp(allowSignedPermissions.item(idx2), category)==0)
  2305. return isSigned;
  2306. }
  2307. ForEachItemIn(idx3, allowedPermissions)
  2308. {
  2309. if (stricmp(allowedPermissions.item(idx3), category)==0)
  2310. return true;
  2311. }
  2312. return defaultAllowed[isSigned];
  2313. }
  2314. void EclCC::getTargetPlatform(StringBuffer & result)
  2315. {
  2316. result.append(clusterTypeString(optTargetClusterType, true));
  2317. }
  2318. //=========================================================================================
  2319. int EclCC::parseCommandLineOptions(int argc, const char* argv[])
  2320. {
  2321. if (argc < 2)
  2322. {
  2323. usage();
  2324. return 1;
  2325. }
  2326. ArgvIterator iter(argc, argv);
  2327. StringAttr tempArg;
  2328. bool tempBool;
  2329. bool showHelp = false;
  2330. for (; !iter.done(); iter.next())
  2331. {
  2332. const char * arg = iter.query();
  2333. if (iter.matchFlag(tempArg, "-a"))
  2334. {
  2335. applicationOptions.append(tempArg);
  2336. }
  2337. else if (iter.matchOption(tempArg, "--allow"))
  2338. {
  2339. allowedPermissions.append(tempArg);
  2340. }
  2341. else if (iter.matchOption(tempArg, "--allowsigned"))
  2342. {
  2343. if (stricmp(tempArg, "all")==0)
  2344. defaultAllowed[true] = true;
  2345. else
  2346. allowSignedPermissions.append(tempArg);
  2347. }
  2348. else if (iter.matchFlag(optBatchMode, "-b"))
  2349. {
  2350. }
  2351. else if (iter.matchOption(tempArg, "-brk"))
  2352. {
  2353. #if defined(_WIN32) && defined(_DEBUG)
  2354. unsigned id = atoi(tempArg);
  2355. if (id == 0)
  2356. DebugBreak();
  2357. else
  2358. _CrtSetBreakAlloc(id);
  2359. #endif
  2360. }
  2361. else if (iter.matchFlag(optOnlyCompile, "-c"))
  2362. {
  2363. }
  2364. else if (iter.matchFlag(optCheckEclVersion, "-checkVersion"))
  2365. {
  2366. }
  2367. else if (iter.matchFlag(optCheckDirty, "-checkDirty"))
  2368. {
  2369. }
  2370. else if (iter.matchOption(optCluster, "-cluster"))
  2371. {
  2372. }
  2373. else if (iter.matchOption(optConfig, "--config"))
  2374. {
  2375. }
  2376. else if (iter.matchOption(tempArg, "--daemon"))
  2377. {
  2378. //Ignore any --daemon option supplied to eclccserver which may be passed onto eclcc
  2379. }
  2380. else if (iter.matchOption(optDefaultGitPrefix, "--defaultgitprefix"))
  2381. {
  2382. }
  2383. else if (iter.matchOption(optDFS, "-dfs") || /*deprecated*/ iter.matchOption(optDFS, "-dali"))
  2384. {
  2385. // Note - we wait until first use before actually connecting to dali
  2386. }
  2387. else if (iter.matchOption(optDaliTimeout, "--dfs-timeout") || /*deprecated*/ iter.matchOption(optDaliTimeout, "--dali-timeout"))
  2388. {
  2389. }
  2390. else if (iter.matchOption(optUser, "-user"))
  2391. {
  2392. }
  2393. else if (iter.matchOption(tempArg, "-password"))
  2394. {
  2395. if (tempArg.isEmpty())
  2396. {
  2397. StringBuffer pw;
  2398. passwordInput("Password: ", pw);
  2399. optPassword.set(pw);
  2400. }
  2401. else
  2402. optPassword.set(tempArg);
  2403. }
  2404. else if (iter.matchOption(tempArg, "-token"))
  2405. {
  2406. // For use by eclccserver - not documented in usage()
  2407. StringBuffer wuid,user;
  2408. extractFromWorkunitDAToken(tempArg, &wuid, &user,nullptr);
  2409. optWUID.set(wuid.str());
  2410. optUser.set(user.str());
  2411. }
  2412. else if (iter.matchOption(optWUID, "-wuid"))
  2413. {
  2414. // For use by eclccserver - not documented in usage()
  2415. }
  2416. else if (iter.matchFlag(optCheckIncludePaths, "--checkIncludePaths"))
  2417. {
  2418. //Only here to provide backward compatibility for the include path checking if it proves to cause issues.
  2419. }
  2420. else if (iter.matchOption(tempArg, "--deny"))
  2421. {
  2422. if (stricmp(tempArg, "all")==0)
  2423. {
  2424. defaultAllowed[false] = false;
  2425. defaultAllowed[true] = false;
  2426. }
  2427. else
  2428. deniedPermissions.append(tempArg);
  2429. }
  2430. else if (iter.matchFlag(tempArg, "-D"))
  2431. {
  2432. definitions.append(tempArg);
  2433. }
  2434. else if (iter.matchFlag(optArchive, "-E"))
  2435. {
  2436. }
  2437. else if (iter.matchOption(optExpandPath, "--expand"))
  2438. {
  2439. }
  2440. else if (iter.matchFlag(tempArg, "-f"))
  2441. {
  2442. debugOptions.append(tempArg);
  2443. }
  2444. else if (iter.matchFlag(optFastSyntax, "--fastsyntax"))
  2445. {
  2446. }
  2447. else if (iter.matchFlag(optFetchRepos, "--fetchrepos"))
  2448. {
  2449. }
  2450. else if (iter.matchFlag(tempBool, "-g") || iter.matchFlag(tempBool, "--debug"))
  2451. {
  2452. if (tempBool)
  2453. {
  2454. debugOptions.append("debugQuery");
  2455. debugOptions.append("saveCppTempFiles");
  2456. }
  2457. else
  2458. debugOptions.append("debugQuery=0");
  2459. }
  2460. else if (strcmp(arg, "-internal")==0)
  2461. {
  2462. outputSizeStmts();
  2463. int error = testHqlInternals() + testReservedWords(); //NOTE: testReservedWords() depends on testHqlInternals() so must be be called after.
  2464. // report test result
  2465. if (error)
  2466. {
  2467. printf("%d error%s found!\n", error, error<=1?"":"s");
  2468. return 300;
  2469. }
  2470. else
  2471. printf("No errors\n");
  2472. }
  2473. else if (iter.matchFlag(optXml, "--xml"))
  2474. {
  2475. }
  2476. else if (iter.matchFlag(tempBool, "-save-cpps"))
  2477. {
  2478. setDebugOption("saveCppTempFiles", tempBool);
  2479. }
  2480. else if (iter.matchFlag(tempBool, "-save-temps"))
  2481. {
  2482. setDebugOption("saveEclTempFiles", tempBool);
  2483. }
  2484. else if (iter.matchOption(tempArg, "-scope"))
  2485. {
  2486. optScope.set(tempArg);
  2487. }
  2488. else if (iter.matchFlag(showHelp, "-help") || iter.matchFlag(showHelp, "--help"))
  2489. {
  2490. }
  2491. else if (iter.matchPathFlag(includeLibraryPath, "-I"))
  2492. {
  2493. }
  2494. else if (iter.matchFlag(optIgnoreUnknownImport, "--ignoreunknownimport"))
  2495. {
  2496. }
  2497. else if (iter.matchOption(tempArg, "--jobid"))
  2498. {
  2499. setDefaultJobId(tempArg, false);
  2500. }
  2501. else if (iter.matchFlag(optKeywords, "--keywords"))
  2502. {
  2503. }
  2504. else if (iter.matchFlag(optLeakCheck, "--leakcheck"))
  2505. {
  2506. }
  2507. else if (iter.matchFlag(tempArg, "-L"))
  2508. {
  2509. libraryPaths.append(tempArg);
  2510. }
  2511. else if (iter.matchFlag(tempBool, "-legacy"))
  2512. {
  2513. optLegacyImport = tempBool;
  2514. optLegacyWhen = tempBool;
  2515. }
  2516. else if (iter.matchFlag(optLegacyImport, "-legacyimport"))
  2517. {
  2518. }
  2519. else if (iter.matchFlag(optLegacyWhen, "-legacywhen"))
  2520. {
  2521. }
  2522. else if (iter.matchOption(optLogfile, "--logfile"))
  2523. {
  2524. }
  2525. else if (iter.matchFlag(optNoLogFile, "--nologfile"))
  2526. {
  2527. }
  2528. else if (iter.matchFlag(optLogToStdOut, "--logtostdout"))
  2529. {
  2530. }
  2531. else if (iter.matchFlag(optIgnoreSignatures, "--nogpg"))
  2532. {
  2533. }
  2534. else if (iter.matchFlag(optNoStdInc, "--nostdinc"))
  2535. {
  2536. }
  2537. else if (iter.matchFlag(optNoBundles, "--nobundles"))
  2538. {
  2539. }
  2540. else if (iter.matchOption(optLogDetail, "--logdetail"))
  2541. {
  2542. }
  2543. else if (iter.matchOption(optMonitorInterval, "--monitorinterval"))
  2544. {
  2545. }
  2546. else if (iter.matchOption(tempArg, "-main") || iter.matchOption(tempArg, "--main"))
  2547. {
  2548. const char * arg = tempArg;
  2549. const char * at = strchr(arg, '@');
  2550. if (at)
  2551. {
  2552. optQueryMainAttribute.set(arg, at - arg);
  2553. optQueryMainPackage.set(at+1);
  2554. }
  2555. else
  2556. {
  2557. optQueryMainAttribute.set(arg);
  2558. optQueryMainPackage.set(nullptr);
  2559. }
  2560. }
  2561. else if (iter.matchFlag(optDebugMemLeak, "-m"))
  2562. {
  2563. }
  2564. else if (iter.matchFlag(optIncludeMeta, "-meta") || iter.matchFlag(optIncludeMeta, "--meta"))
  2565. {
  2566. }
  2567. else if (iter.matchOption(tempArg, "--metacache"))
  2568. {
  2569. if (!tempArg.isEmpty())
  2570. optMetaLocation.set(tempArg);
  2571. else
  2572. optMetaLocation.clear();
  2573. }
  2574. else if (iter.matchOption(tempArg, "--neversimplify"))
  2575. {
  2576. appendNeverSimplifyList(tempArg);
  2577. }
  2578. else if (iter.matchFlag(optGenerateMeta, "-M"))
  2579. {
  2580. }
  2581. else if (iter.matchFlag(optGenerateDepend, "-Md"))
  2582. {
  2583. }
  2584. else if (iter.matchFlag(optEvaluateResult, "-Me"))
  2585. {
  2586. }
  2587. else if (iter.matchFlag(optNoSourcePath, "--nosourcepath"))
  2588. {
  2589. }
  2590. else if (iter.matchFlag(optOutputFilename, "-o"))
  2591. {
  2592. }
  2593. else if (iter.matchFlag(optOutputDirectory, "-P"))
  2594. {
  2595. }
  2596. else if (iter.matchFlag(optGenerateHeader, "-pch"))
  2597. {
  2598. }
  2599. else if (iter.matchFlag(optPruneArchive, "--prunearchive"))
  2600. {
  2601. }
  2602. else if (iter.matchOption(optComponentName, "--component"))
  2603. {
  2604. }
  2605. else if (iter.matchFlag(optSaveQueryText, "-q"))
  2606. {
  2607. }
  2608. else if (iter.matchFlag(optSaveQueryArchive, "-qa"))
  2609. {
  2610. }
  2611. else if (iter.matchFlag(optNoCompile, "-S"))
  2612. {
  2613. }
  2614. else if (iter.matchOption(optCompileBatchOut, "-Sx"))
  2615. {
  2616. }
  2617. else if (iter.matchFlag(optShared, "-shared"))
  2618. {
  2619. }
  2620. else if (iter.matchFlag(tempBool, "-syntax"))
  2621. {
  2622. optSyntax = tempBool;
  2623. setDebugOption("syntaxCheck", tempBool);
  2624. }
  2625. else if (iter.matchOption(optMaxErrors, "--maxErrors"))
  2626. {
  2627. }
  2628. else if (iter.matchFlag(optUnsuppressImmediateSyntaxErrors, "--unsuppressImmediateSyntaxErrors"))
  2629. {
  2630. }
  2631. else if (iter.matchOption(optIniFilename, "-specs"))
  2632. {
  2633. if (!checkFileExists(optIniFilename))
  2634. {
  2635. traceError("Error: INI file '%s' does not exist",optIniFilename.get());
  2636. return 1;
  2637. }
  2638. }
  2639. else if (iter.matchFlag(optShowPaths, "-showpaths"))
  2640. {
  2641. }
  2642. else if (iter.matchOption(optManifestFilename, "-manifest"))
  2643. {
  2644. if (!isManifestFileValid(optManifestFilename))
  2645. return 1;
  2646. }
  2647. else if (iter.matchOption(tempArg, "-split"))
  2648. {
  2649. batchPart = atoi(tempArg)-1;
  2650. const char * split = strchr(tempArg, ':');
  2651. if (!split)
  2652. {
  2653. UERRLOG("Error: syntax is -split=part:splits\n");
  2654. return 1;
  2655. }
  2656. batchSplit = atoi(split+1);
  2657. if (batchSplit == 0)
  2658. batchSplit = 1;
  2659. if (batchPart >= batchSplit)
  2660. batchPart = 0;
  2661. }
  2662. else if (iter.matchFlag(logTimings, "--timings"))
  2663. {
  2664. }
  2665. else if (iter.matchOption(tempArg, "-platform") || /*deprecated*/ iter.matchOption(tempArg, "-target"))
  2666. {
  2667. if (!setTargetPlatformOption(tempArg.get(), optTargetClusterType))
  2668. return 1;
  2669. }
  2670. else if (iter.matchFlag(optTraceCache, "--tracecache"))
  2671. {
  2672. }
  2673. else if (iter.matchFlag(optVerifySimplified, "--internalverifysimplified"))
  2674. {
  2675. }
  2676. else if (iter.matchFlag(optRegenerateCache, "--regeneratecache"))
  2677. {
  2678. }
  2679. else if (iter.matchFlag(tempArg, "-R"))
  2680. {
  2681. if (!strchr(tempArg, '='))
  2682. throw MakeStringException(99, "'-R%s' should have form -Rrepo[#version]=path", tempArg.str());
  2683. repoMappings.append(tempArg);
  2684. }
  2685. else if (iter.matchFlag(optIgnoreCache, "--internalignorecache"))
  2686. {
  2687. }
  2688. else if (iter.matchFlag(optIgnoreSimplified, "--ignoresimplified"))
  2689. {
  2690. }
  2691. else if (iter.matchFlag(optExtraStats, "--internalextrastats"))
  2692. {
  2693. }
  2694. else if (iter.matchFlag(optUpdateRepos, "--updaterepos"))
  2695. {
  2696. }
  2697. else if (iter.matchFlag(logVerbose, "-v") || iter.matchFlag(logVerbose, "--verbose"))
  2698. {
  2699. Owned<ILogMsgFilter> filter = getDefaultLogMsgFilter();
  2700. queryLogMsgManager()->changeMonitorFilter(queryStderrLogMsgHandler(), filter);
  2701. }
  2702. else if (strcmp(arg, "--version")==0)
  2703. {
  2704. fprintf(stdout,"%s %s\n", LANGUAGE_VERSION, hpccBuildInfo.buildTag);
  2705. return 1;
  2706. }
  2707. else if (startsWith(arg, "-Wc,"))
  2708. {
  2709. expandCommaList(compileOptions, arg+4);
  2710. }
  2711. else if (startsWith(arg, "-Wl,"))
  2712. {
  2713. //Pass these straight through to the linker - with -Wl, prefix removed
  2714. linkOptions.append(arg+4);
  2715. }
  2716. else if (startsWith(arg, "-Wp,") || startsWith(arg, "-Wa,"))
  2717. {
  2718. //Pass these straight through to the gcc compiler
  2719. compileOptions.append(arg);
  2720. }
  2721. else if (iter.matchFlag(optWorkUnit, "-wu"))
  2722. {
  2723. }
  2724. else if (iter.matchFlag(tempArg, "-w"))
  2725. {
  2726. //Any other option beginning -wxxx are treated as warning mappings
  2727. warningMappings.append(tempArg);
  2728. }
  2729. else if (strcmp(arg, "-")==0)
  2730. {
  2731. inputFileNames.append("stdin:");
  2732. }
  2733. else if (iter.matchFlag(tempArg, "--logging.postMortem"))
  2734. {
  2735. // Ignore, but may be present
  2736. }
  2737. else if (arg[0] == '-')
  2738. {
  2739. //If --config has been specified, then ignore any unknown options beginning with -- since they will be added to the globals.
  2740. if ((arg[1] == '-') && optConfig)
  2741. continue;
  2742. traceError("Error: unrecognised option %s",arg);
  2743. usage();
  2744. return 1;
  2745. }
  2746. else
  2747. inputFileNames.append(arg);
  2748. }
  2749. if (showHelp)
  2750. {
  2751. usage();
  2752. return 1;
  2753. }
  2754. if (optComponentName.length())
  2755. setStatisticsComponentName(SCTeclcc, optComponentName, false);
  2756. else
  2757. setStatisticsComponentName(SCTeclcc, "eclcc", false);
  2758. // Option post processing follows:
  2759. if (optArchive || optWorkUnit || optGenerateMeta || optGenerateDepend || optShowPaths)
  2760. {
  2761. optNoCompile = true;
  2762. optIgnoreSignatures = true;
  2763. }
  2764. optReleaseAllMemory = optDebugMemLeak || optLeakCheck;
  2765. loadManifestOptions();
  2766. if (optDebugMemLeak)
  2767. {
  2768. StringBuffer title;
  2769. title.append(inputFileNames.item(0)).newline();
  2770. initLeakCheck(title);
  2771. }
  2772. setTraceCache(optTraceCache);
  2773. if (inputFileNames.ordinality() == 0 && !optKeywords)
  2774. {
  2775. if (optGenerateHeader || optShowPaths || (!optBatchMode && optQueryMainAttribute))
  2776. return 0;
  2777. UERRLOG("No input filenames supplied");
  2778. return 1;
  2779. }
  2780. return 0;
  2781. }
  2782. void EclCC::setSecurityOptions()
  2783. {
  2784. IPropertyTree *eclSecurity = configuration->getPropTree("eclSecurity");
  2785. if (eclSecurity)
  2786. {
  2787. // Name of security option in configuration yaml
  2788. const char * configName[] = {"@embedded", "@pipe", "@extern", "@datafile" };
  2789. // Name of security option used internally
  2790. const char * securityOption[] = {"cpp", "pipe", "extern", "datafile" };
  2791. for (int i=0; i < 4; i++)
  2792. {
  2793. const char * optVal = eclSecurity->queryProp(configName[i]);
  2794. if (optVal)
  2795. {
  2796. if (!strcmp(optVal, "allow"))
  2797. allowedPermissions.append(securityOption[i]);
  2798. else if (!strcmp(optVal, "deny"))
  2799. deniedPermissions.append(securityOption[i]);
  2800. else if (!strcmp(optVal, "allowSigned"))
  2801. allowSignedPermissions.append(securityOption[i]);
  2802. }
  2803. }
  2804. }
  2805. }
  2806. //=========================================================================================
  2807. void EclCC::usage()
  2808. {
  2809. for (unsigned line=0; line < _elements_in(helpText); line++)
  2810. {
  2811. const char * text = helpText[line];
  2812. StringBuffer wsPrefix;
  2813. if (*text == '?') //NOTE: '?' indicates eclcmd usage so don't print.
  2814. {
  2815. text = text+1;
  2816. if (*text == ' ' || ( *text == '!' && text[1] == ' '))
  2817. wsPrefix.append(' ');
  2818. }
  2819. if (*text == '!')
  2820. {
  2821. if (logVerbose)
  2822. {
  2823. text = text+1;
  2824. if (*text == ' ')
  2825. wsPrefix.append(' ');
  2826. fprintf(stdout, "%s%s\n", wsPrefix.str(), text);
  2827. }
  2828. }
  2829. else
  2830. fprintf(stdout, "%s%s\n", wsPrefix.str(), text);
  2831. }
  2832. }
  2833. //=========================================================================================
  2834. // The following methods are concerned with running eclcc in batch mode (primarily to aid regression testing)
  2835. void EclCC::processBatchedFile(IFile & file, bool multiThreaded)
  2836. {
  2837. StringBuffer basename, logFilename, xmlFilename, outFilename, metaFilename;
  2838. splitFilename(file.queryFilename(), NULL, NULL, &basename, &basename);
  2839. addNonEmptyPathSepChar(logFilename.append(optOutputDirectory)).append(basename).append(".log");
  2840. addNonEmptyPathSepChar(xmlFilename.append(optOutputDirectory)).append(basename).append(".xml");
  2841. addNonEmptyPathSepChar(metaFilename.append(optOutputDirectory)).append(basename).append(".meta");
  2842. splitFilename(file.queryFilename(), NULL, NULL, &outFilename, &outFilename);
  2843. unsigned startTime = msTick();
  2844. FILE * logFile = fopen(logFilename.str(), "w");
  2845. if (!logFile)
  2846. throw MakeStringException(99, "couldn't create log output %s", logFilename.str());
  2847. Owned<ILogMsgHandler> handler;
  2848. try
  2849. {
  2850. // Print compiler and arguments to help reproduce problems
  2851. for (int i=0; i<argc; i++)
  2852. fprintf(logFile, "%s ", argv[i]);
  2853. fprintf(logFile, "\n");
  2854. fprintf(logFile, "--- %s --- \n", basename.str());
  2855. {
  2856. if (!multiThreaded)
  2857. {
  2858. handler.setown(getHandleLogMsgHandler(logFile, 0, false));
  2859. Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(MSGAUD_all, MSGCLS_all, DefaultDetail);
  2860. queryLogMsgManager()->addMonitor(handler, filter);
  2861. resetUniqueId();
  2862. resetLexerUniqueNames();
  2863. }
  2864. Owned<IErrorReceiver> localErrs = createFileErrorReceiver(logFile);
  2865. EclCompileInstance info(*this, &file, *localErrs, logFile, outFilename, optLegacyImport, optLegacyWhen, optIgnoreSignatures, optIgnoreUnknownImport, optXml);
  2866. info.metaOutputFilename.set(metaFilename);
  2867. processFile(info);
  2868. if (info.wu &&
  2869. (info.wu->getDebugValueBool("generatePartialOutputOnError", false) || info.queryErrorProcessor().errCount() == 0))
  2870. {
  2871. exportWorkUnitToXMLFile(info.wu, xmlFilename, XML_NoBinaryEncode64, true, false, false, true);
  2872. Owned<IFile> xml = createIFile(xmlFilename);
  2873. info.stats.xmlSize = xml->size();
  2874. }
  2875. info.logStats(logTimings);
  2876. }
  2877. }
  2878. catch (IException * e)
  2879. {
  2880. StringBuffer s;
  2881. e->errorMessage(s);
  2882. e->Release();
  2883. fprintf(logFile, "Unexpected exception: %s", s.str());
  2884. }
  2885. if (handler)
  2886. {
  2887. queryLogMsgManager()->removeMonitor(handler);
  2888. handler.clear();
  2889. }
  2890. fflush(logFile);
  2891. fclose(logFile);
  2892. unsigned nowTime = msTick();
  2893. StringBuffer s;
  2894. s.append(basename).append(":");
  2895. s.padTo(50);
  2896. s.appendf("%8d ms\n", nowTime-startTime);
  2897. fprintf(batchLog, "%s", s.str());
  2898. // fflush(batchLog);
  2899. }
  2900. typedef SafeQueueOf<IFile, true> RegressQueue;
  2901. class BatchThread : public Thread
  2902. {
  2903. public:
  2904. BatchThread(EclCC & _compiler, RegressQueue & _queue, Semaphore & _fileReady)
  2905. : compiler(_compiler), queue(_queue), fileReady(_fileReady)
  2906. {
  2907. }
  2908. virtual int run()
  2909. {
  2910. for (;;)
  2911. {
  2912. fileReady.wait();
  2913. IFile * next = queue.dequeue();
  2914. if (!next)
  2915. return 0;
  2916. compiler.processBatchedFile(*next, true);
  2917. next->Release();
  2918. }
  2919. }
  2920. protected:
  2921. EclCC & compiler;
  2922. RegressQueue & queue;
  2923. Semaphore & fileReady;
  2924. };
  2925. int compareFilenames(IInterface * const * pleft, IInterface * const * pright)
  2926. {
  2927. IFile * left = static_cast<IFile *>(*pleft);
  2928. IFile * right = static_cast<IFile *>(*pright);
  2929. return stricmp(pathTail(left->queryFilename()), pathTail(right->queryFilename()));
  2930. }
  2931. void EclCC::processBatchFiles()
  2932. {
  2933. Thread * * threads = NULL;
  2934. RegressQueue queue;
  2935. Semaphore fileReady;
  2936. unsigned startAllTime = msTick();
  2937. if (optThreads > 0)
  2938. {
  2939. threads = new Thread * [optThreads];
  2940. for (unsigned i = 0; i < optThreads; i++)
  2941. {
  2942. threads[i] = new BatchThread(*this, queue, fileReady);
  2943. threads[i]->start();
  2944. }
  2945. }
  2946. StringBuffer batchLogName;
  2947. addNonEmptyPathSepChar(batchLogName.append(optOutputDirectory)).append("_batch_.");
  2948. batchLogName.append(batchPart+1);
  2949. batchLogName.append(".log");
  2950. batchLog = fopen(batchLogName.str(), "w");
  2951. if (!batchLog)
  2952. throw MakeStringException(99, "couldn't create log output %s", batchLogName.str());
  2953. //Divide the files up based on file size, rather than name
  2954. inputFiles.sort(compareFilenames);
  2955. unsigned __int64 totalSize = 0;
  2956. ForEachItemIn(iSize, inputFiles)
  2957. {
  2958. IFile & cur = inputFiles.item(iSize);
  2959. totalSize += cur.size();
  2960. }
  2961. //Sort the filenames so you have a consistent order between windows and linux
  2962. unsigned __int64 averageFileSize = totalSize / inputFiles.ordinality();
  2963. unsigned splitter = 0;
  2964. unsigned __int64 sizeSoFar = 0;
  2965. ForEachItemIn(i, inputFiles)
  2966. {
  2967. IFile &file = inputFiles.item(i);
  2968. if (splitter == batchPart)
  2969. {
  2970. if (optThreads > 0)
  2971. {
  2972. queue.enqueue(LINK(&file));
  2973. fileReady.signal();
  2974. }
  2975. else
  2976. processBatchedFile(file, false);
  2977. }
  2978. unsigned __int64 thisSize = file.size();
  2979. sizeSoFar += thisSize;
  2980. if (sizeSoFar > averageFileSize)
  2981. {
  2982. sizeSoFar = 0;
  2983. splitter++;
  2984. }
  2985. if (splitter == batchSplit)
  2986. splitter = 0;
  2987. }
  2988. if (optThreads > 0)
  2989. {
  2990. for (unsigned i = 0; i < optThreads; i++)
  2991. fileReady.signal();
  2992. for (unsigned j = 0; j < optThreads; j++)
  2993. threads[j]->join();
  2994. for (unsigned i2 = 0; i2 < optThreads; i2++)
  2995. threads[i2]->Release();
  2996. delete [] threads;
  2997. }
  2998. fprintf(batchLog, "@%5ds total time for part %d\n", (msTick()-startAllTime)/1000, batchPart);
  2999. fclose(batchLog);
  3000. batchLog = NULL;
  3001. }
  3002. bool EclCC::printKeywordsToXml()
  3003. {
  3004. if(!optKeywords)
  3005. return false;
  3006. ::printKeywordsToXml();
  3007. return true;
  3008. }