eclcc.cpp 80 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326
  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 "build-config.h"
  21. #include "workunit.hpp"
  22. #ifndef _WIN32
  23. #include <pwd.h>
  24. #endif
  25. #include "hqlecl.hpp"
  26. #include "hqlir.hpp"
  27. #include "hqlerrors.hpp"
  28. #include "hqlwuerr.hpp"
  29. #include "hqlfold.hpp"
  30. #include "hqlplugins.hpp"
  31. #include "hqlmanifest.hpp"
  32. #include "hqlcollect.hpp"
  33. #include "hqlrepository.hpp"
  34. #include "hqlerror.hpp"
  35. #include "hqlcerrors.hpp"
  36. #include "hqlgram.hpp"
  37. #include "hqltrans.ipp"
  38. #include "hqlutil.hpp"
  39. #include "hqlstmt.hpp"
  40. #include "build-config.h"
  41. #include "rmtfile.hpp"
  42. #ifdef _USE_CPPUNIT
  43. #include <cppunit/extensions/TestFactoryRegistry.h>
  44. #include <cppunit/ui/text/TestRunner.h>
  45. #endif
  46. //#define TEST_LEGACY_DEPENDENCY_CODE
  47. #define INIFILE "eclcc.ini"
  48. #define SYSTEMCONFDIR CONFIG_DIR
  49. #define DEFAULTINIFILE "eclcc.ini"
  50. #define SYSTEMCONFFILE ENV_CONF_FILE
  51. #define DEFAULT_OUTPUTNAME "a.out"
  52. //=========================================================================================
  53. //The following flag could be used not free items to speed up closedown
  54. static bool optDebugMemLeak = false;
  55. #if defined(_WIN32) && defined(_DEBUG)
  56. static HANDLE leakHandle;
  57. static void appendLeaks(size32_t len, const void * data)
  58. {
  59. SetFilePointer(leakHandle, 0, 0, FILE_END);
  60. DWORD written;
  61. WriteFile(leakHandle, data, len, &written, 0);
  62. }
  63. void initLeakCheck(const char * title)
  64. {
  65. StringBuffer leakFilename("eclccleaks.log");
  66. leakHandle = CreateFile(leakFilename.str(), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, 0);
  67. if (title)
  68. appendLeaks(strlen(title), title);
  69. _CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_FILE|_CRTDBG_MODE_DEBUG );
  70. _CrtSetReportFile( _CRT_WARN, leakHandle );
  71. _CrtSetReportMode( _CRT_ERROR, _CRTDBG_MODE_FILE|_CRTDBG_MODE_DEBUG );
  72. _CrtSetReportFile( _CRT_ERROR, leakHandle );
  73. _CrtSetReportMode( _CRT_ASSERT, _CRTDBG_MODE_FILE|_CRTDBG_MODE_DEBUG );
  74. _CrtSetReportFile( _CRT_ASSERT, leakHandle );
  75. //
  76. // set the states we want to monitor
  77. //
  78. int LeakTmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
  79. LeakTmpFlag &= ~_CRTDBG_CHECK_CRT_DF;
  80. LeakTmpFlag |= _CRTDBG_LEAK_CHECK_DF;
  81. _CrtSetDbgFlag(LeakTmpFlag);
  82. }
  83. /**
  84. * Error handler for ctrl-break: Don't care about memory leaks.
  85. */
  86. void __cdecl IntHandler(int)
  87. {
  88. enableMemLeakChecking(false);
  89. exit(2);
  90. }
  91. #include <signal.h> // for signal()
  92. MODULE_INIT(INIT_PRIORITY_STANDARD)
  93. {
  94. signal(SIGINT, IntHandler);
  95. return true;
  96. }
  97. #else
  98. void initLeakCheck(const char *)
  99. {
  100. }
  101. #endif // _WIN32 && _DEBUG
  102. static bool extractOption(StringBuffer & option, IProperties * globals, const char * envName, const char * propertyName, const char * defaultPrefix, const char * defaultSuffix)
  103. {
  104. if (option.length()) // check if already specified via a command line option
  105. return true;
  106. if (globals->getProp(propertyName, option))
  107. return true;
  108. const char * env = getenv(envName);
  109. if (env)
  110. {
  111. option.append(env);
  112. return true;
  113. }
  114. option.append(defaultPrefix).append(defaultSuffix);
  115. return false;
  116. }
  117. static bool extractOption(StringAttr & option, IProperties * globals, const char * envName, const char * propertyName, const char * defaultPrefix, const char * defaultSuffix)
  118. {
  119. if (option)
  120. return true;
  121. StringBuffer temp;
  122. bool ret = extractOption(temp, globals, envName, propertyName, defaultPrefix, defaultSuffix);
  123. option.set(temp.str());
  124. return ret;
  125. }
  126. static bool getPackageFolder(StringBuffer & path)
  127. {
  128. StringBuffer folder;
  129. splitDirTail(queryCurrentProcessPath(), folder);
  130. removeTrailingPathSepChar(folder);
  131. if (folder.length())
  132. {
  133. StringBuffer foldersFolder;
  134. splitDirTail(folder.str(), foldersFolder);
  135. if (foldersFolder.length())
  136. {
  137. path = foldersFolder;
  138. return true;
  139. }
  140. }
  141. return false;
  142. }
  143. static bool getHomeFolder(StringBuffer & homepath)
  144. {
  145. if (!getHomeDir(homepath))
  146. return false;
  147. addPathSepChar(homepath);
  148. #ifndef WIN32
  149. homepath.append('.');
  150. #endif
  151. homepath.append(DIR_NAME);
  152. return true;
  153. }
  154. struct EclCompileInstance
  155. {
  156. public:
  157. EclCompileInstance(IFile * _inputFile, IErrorReceiver & _errorProcessor, FILE * _errout, const char * _outputFilename, bool _legacyImport, bool _legacyWhen) :
  158. inputFile(_inputFile), errorProcessor(&_errorProcessor), errout(_errout), outputFilename(_outputFilename)
  159. {
  160. legacyImport = _legacyImport;
  161. legacyWhen = _legacyWhen;
  162. ignoreUnknownImport = false;
  163. fromArchive = false;
  164. stats.parseTime = 0;
  165. stats.generateTime = 0;
  166. stats.xmlSize = 0;
  167. stats.cppSize = 0;
  168. }
  169. void logStats();
  170. void checkEclVersionCompatible();
  171. bool reportErrorSummary();
  172. inline IErrorReceiver & queryErrorProcessor() { return *errorProcessor; }
  173. public:
  174. Linked<IFile> inputFile;
  175. Linked<IPropertyTree> archive;
  176. Linked<IWorkUnit> wu;
  177. Owned<IEclRepository> dataServer; // A member which can be cleared after parsing the query
  178. OwnedHqlExpr query; // parsed query - cleared when generating to free memory
  179. StringAttr eclVersion;
  180. const char * outputFilename;
  181. FILE * errout;
  182. Owned<IPropertyTree> srcArchive;
  183. Owned<IPropertyTree> generatedMeta;
  184. bool legacyImport;
  185. bool legacyWhen;
  186. bool fromArchive;
  187. bool ignoreUnknownImport;
  188. struct {
  189. unsigned parseTime;
  190. unsigned generateTime;
  191. offset_t xmlSize;
  192. offset_t cppSize;
  193. } stats;
  194. protected:
  195. Linked<IErrorReceiver> errorProcessor;
  196. };
  197. class EclCC : public CInterfaceOf<ICodegenContextCallback>
  198. {
  199. public:
  200. EclCC(int _argc, const char **_argv)
  201. : programName(_argv[0])
  202. {
  203. argc = _argc;
  204. argv = _argv;
  205. logVerbose = false;
  206. logTimings = false;
  207. optArchive = false;
  208. optCheckEclVersion = true;
  209. optEvaluateResult = false;
  210. optGenerateMeta = false;
  211. optGenerateDepend = false;
  212. optIncludeMeta = false;
  213. optLegacyImport = false;
  214. optLegacyWhen = false;
  215. optShared = false;
  216. optWorkUnit = false;
  217. optNoCompile = false;
  218. optNoLogFile = false;
  219. optNoStdInc = false;
  220. optNoBundles = false;
  221. optOnlyCompile = false;
  222. optBatchMode = false;
  223. optSaveQueryText = false;
  224. optGenerateHeader = false;
  225. optShowPaths = false;
  226. optNoSourcePath = false;
  227. optTargetClusterType = HThorCluster;
  228. optTargetCompiler = DEFAULT_COMPILER;
  229. optThreads = 0;
  230. optLogDetail = 0;
  231. batchPart = 0;
  232. batchSplit = 1;
  233. batchLog = NULL;
  234. cclogFilename.append("cc.").append((unsigned)GetCurrentProcessId()).append(".log");
  235. defaultAllowed = true;
  236. }
  237. bool parseCommandLineOptions(int argc, const char* argv[]);
  238. void loadOptions();
  239. void loadManifestOptions();
  240. bool processFiles();
  241. void processBatchedFile(IFile & file, bool multiThreaded);
  242. virtual void noteCluster(const char *clusterName);
  243. virtual bool allowAccess(const char * category);
  244. protected:
  245. void addFilenameDependency(StringBuffer & target, EclCompileInstance & instance, const char * filename);
  246. void applyApplicationOptions(IWorkUnit * wu);
  247. void applyDebugOptions(IWorkUnit * wu);
  248. bool checkWithinRepository(StringBuffer & attributePath, const char * sourcePathname);
  249. IFileIO * createArchiveOutputFile(EclCompileInstance & instance);
  250. ICppCompiler *createCompiler(const char * coreName, const char * sourceDir = NULL, const char * targetDir = NULL);
  251. void evaluateResult(EclCompileInstance & instance);
  252. bool generatePrecompiledHeader();
  253. void generateOutput(EclCompileInstance & instance);
  254. void instantECL(EclCompileInstance & instance, IWorkUnit *wu, const char * queryFullName, IErrorReceiver & errorProcessor, const char * outputFile);
  255. bool isWithinPath(const char * sourcePathname, const char * searchPath);
  256. void getComplexity(IWorkUnit *wu, IHqlExpression * query, IErrorReceiver & errorProcessor);
  257. void outputXmlToOutputFile(EclCompileInstance & instance, IPropertyTree * xml);
  258. void processSingleQuery(EclCompileInstance & instance,
  259. IFileContents * queryContents,
  260. const char * queryAttributePath);
  261. void processXmlFile(EclCompileInstance & instance, const char *archiveXML);
  262. void processFile(EclCompileInstance & info);
  263. void processReference(EclCompileInstance & instance, const char * queryAttributePath);
  264. void processBatchFiles();
  265. void reportCompileErrors(IErrorReceiver & errorProcessor, const char * processName);
  266. void setDebugOption(const char * name, bool value);
  267. void usage();
  268. inline const char * queryTemplateDir() { return templatePath.length() ? templatePath.str() : NULL; }
  269. protected:
  270. Owned<IEclRepository> pluginsRepository;
  271. Owned<IEclRepository> libraryRepository;
  272. Owned<IEclRepository> bundlesRepository;
  273. Owned<IEclRepository> includeRepository;
  274. const char * programName;
  275. StringBuffer cppIncludePath;
  276. StringBuffer pluginsPath;
  277. StringBuffer hooksPath;
  278. StringBuffer templatePath;
  279. StringBuffer eclLibraryPath;
  280. StringBuffer eclBundlePath;
  281. StringBuffer stdIncludeLibraryPath;
  282. StringBuffer includeLibraryPath;
  283. StringBuffer compilerPath;
  284. StringBuffer libraryPath;
  285. StringBuffer cclogFilename;
  286. StringAttr optLogfile;
  287. StringAttr optIniFilename;
  288. StringAttr optManifestFilename;
  289. StringAttr optOutputDirectory;
  290. StringAttr optOutputFilename;
  291. StringAttr optQueryRepositoryReference;
  292. FILE * batchLog;
  293. IFileArray inputFiles;
  294. StringArray inputFileNames;
  295. StringArray applicationOptions;
  296. StringArray debugOptions;
  297. StringArray warningMappings;
  298. StringArray compileOptions;
  299. StringArray linkOptions;
  300. StringArray libraryPaths;
  301. StringArray allowedPermissions;
  302. StringArray deniedPermissions;
  303. bool defaultAllowed;
  304. ClusterType optTargetClusterType;
  305. CompilerType optTargetCompiler;
  306. unsigned optThreads;
  307. unsigned batchPart;
  308. unsigned batchSplit;
  309. unsigned optLogDetail;
  310. bool logVerbose;
  311. bool logTimings;
  312. bool optArchive;
  313. bool optCheckEclVersion;
  314. bool optEvaluateResult;
  315. bool optGenerateMeta;
  316. bool optGenerateDepend;
  317. bool optIncludeMeta;
  318. bool optWorkUnit;
  319. bool optNoCompile;
  320. bool optNoLogFile;
  321. bool optNoStdInc;
  322. bool optNoBundles;
  323. bool optBatchMode;
  324. bool optShared;
  325. bool optOnlyCompile;
  326. bool optSaveQueryText;
  327. bool optLegacyImport;
  328. bool optLegacyWhen;
  329. bool optGenerateHeader;
  330. bool optShowPaths;
  331. bool optNoSourcePath;
  332. int argc;
  333. const char **argv;
  334. };
  335. //=========================================================================================
  336. static int doSelfTest(int argc, const char *argv[])
  337. {
  338. #ifdef _USE_CPPUNIT
  339. queryStderrLogMsgHandler()->setMessageFields(MSGFIELD_time | MSGFIELD_prefix);
  340. CppUnit::TextUi::TestRunner runner;
  341. if (argc==2)
  342. {
  343. CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
  344. runner.addTest( registry.makeTest() );
  345. }
  346. else
  347. {
  348. // MORE - maybe add a 'list' function here?
  349. for (int name = 2; name < argc; name++)
  350. {
  351. if (stricmp(argv[name], "-q")==0)
  352. {
  353. removeLog();
  354. }
  355. else
  356. {
  357. CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry(argv[name]);
  358. runner.addTest( registry.makeTest() );
  359. }
  360. }
  361. }
  362. bool wasSucessful = runner.run( "", false );
  363. releaseAtoms();
  364. return wasSucessful;
  365. #else
  366. return true;
  367. #endif
  368. }
  369. static int doMain(int argc, const char *argv[])
  370. {
  371. if (argc>=2 && stricmp(argv[1], "-selftest")==0)
  372. return doSelfTest(argc, argv);
  373. EclCC processor(argc, argv);
  374. if (!processor.parseCommandLineOptions(argc, argv))
  375. return 1;
  376. try
  377. {
  378. if (!processor.processFiles())
  379. return 2;
  380. }
  381. catch (IException *E)
  382. {
  383. StringBuffer m("Error: ");
  384. E->errorMessage(m);
  385. fputs(m.newline().str(), stderr);
  386. E->Release();
  387. return 2;
  388. }
  389. #ifndef _DEBUG
  390. catch (...)
  391. {
  392. ERRLOG("Unexpected exception\n");
  393. return 4;
  394. }
  395. #endif
  396. return 0;
  397. }
  398. int main(int argc, const char *argv[])
  399. {
  400. EnableSEHtoExceptionMapping();
  401. setTerminateOnSEH(true);
  402. InitModuleObjects();
  403. queryStderrLogMsgHandler()->setMessageFields(0);
  404. // Turn logging down (we turn it back up if -v option seen)
  405. Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(MSGAUD_user, MSGCLS_error);
  406. queryLogMsgManager()->changeMonitorFilter(queryStderrLogMsgHandler(), filter);
  407. unsigned exitCode = doMain(argc, argv);
  408. releaseAtoms();
  409. removeFileHooks();
  410. return exitCode;
  411. }
  412. //=========================================================================================
  413. bool setTargetPlatformOption(const char *platform, ClusterType &optTargetClusterType)
  414. {
  415. if (!platform || !*platform)
  416. return false;
  417. ClusterType clusterType = getClusterType(platform);
  418. if (clusterType == NoCluster)
  419. {
  420. ERRLOG("Unknown ecl target platform %s\n", platform);
  421. return false;
  422. }
  423. optTargetClusterType = clusterType;
  424. return true;
  425. }
  426. void EclCC::loadManifestOptions()
  427. {
  428. if (!optManifestFilename)
  429. return;
  430. Owned<IPropertyTree> mf = createPTreeFromXMLFile(optManifestFilename);
  431. IPropertyTree *ecl = mf->queryPropTree("ecl");
  432. if (ecl)
  433. {
  434. if (ecl->hasProp("@filename"))
  435. {
  436. StringBuffer dir, abspath;
  437. splitDirTail(optManifestFilename, dir);
  438. makeAbsolutePath(ecl->queryProp("@filename"), dir.str(), abspath);
  439. processArgvFilename(inputFiles, abspath.str());
  440. }
  441. if (!optLegacyImport && !optLegacyWhen)
  442. {
  443. bool optLegacy = ecl->getPropBool("@legacy");
  444. optLegacyImport = ecl->getPropBool("@legacyImport", optLegacy);
  445. optLegacyWhen = ecl->getPropBool("@legacyWhen", optLegacy);
  446. }
  447. if (!optQueryRepositoryReference && ecl->hasProp("@main"))
  448. optQueryRepositoryReference.set(ecl->queryProp("@main"));
  449. if (ecl->hasProp("@targetPlatform"))
  450. setTargetPlatformOption(ecl->queryProp("@targetPlatform"), optTargetClusterType);
  451. else if (ecl->hasProp("@targetClusterType")) //deprecated name
  452. setTargetPlatformOption(ecl->queryProp("@targetClusterType"), optTargetClusterType);
  453. Owned<IPropertyTreeIterator> paths = ecl->getElements("IncludePath");
  454. ForEach(*paths)
  455. {
  456. IPropertyTree &item = paths->query();
  457. if (item.hasProp("@path"))
  458. includeLibraryPath.append(ENVSEPCHAR).append(item.queryProp("@path"));
  459. }
  460. paths.setown(ecl->getElements("LibraryPath"));
  461. ForEach(*paths)
  462. {
  463. IPropertyTree &item = paths->query();
  464. if (item.hasProp("@path"))
  465. libraryPaths.append(item.queryProp("@path"));
  466. }
  467. }
  468. }
  469. void EclCC::loadOptions()
  470. {
  471. Owned<IProperties> globals;
  472. if (!optIniFilename)
  473. {
  474. if (checkFileExists(INIFILE))
  475. optIniFilename.set(INIFILE);
  476. else
  477. {
  478. StringBuffer fn(SYSTEMCONFDIR);
  479. fn.append(PATHSEPSTR).append(DEFAULTINIFILE);
  480. if (checkFileExists(fn))
  481. optIniFilename.set(fn);
  482. }
  483. }
  484. if (logVerbose && optIniFilename.length())
  485. fprintf(stdout, "Found ini file '%s'\n", optIniFilename.get());
  486. globals.setown(createProperties(optIniFilename, true));
  487. if (globals->hasProp("targetGcc"))
  488. optTargetCompiler = globals->getPropBool("targetGcc") ? GccCppCompiler : Vs6CppCompiler;
  489. StringBuffer syspath, homepath;
  490. if (getPackageFolder(syspath) && getHomeFolder(homepath))
  491. {
  492. #if _WIN32
  493. extractOption(compilerPath, globals, "CL_PATH", "compilerPath", syspath, "componentfiles\\cl");
  494. #else
  495. extractOption(compilerPath, globals, "CL_PATH", "compilerPath", "/usr", NULL);
  496. #endif
  497. if (!extractOption(libraryPath, globals, "ECLCC_LIBRARY_PATH", "libraryPath", syspath, "lib"))
  498. libraryPath.append(ENVSEPCHAR).append(syspath).append("plugins");
  499. extractOption(cppIncludePath, globals, "ECLCC_INCLUDE_PATH", "includePath", syspath, "componentfiles" PATHSEPSTR "cl" PATHSEPSTR "include");
  500. extractOption(pluginsPath, globals, "ECLCC_PLUGIN_PATH", "plugins", syspath, "plugins");
  501. extractOption(hooksPath, globals, "HPCC_FILEHOOKS_PATH", "filehooks", syspath, "filehooks");
  502. extractOption(templatePath, globals, "ECLCC_TPL_PATH", "templatePath", syspath, "componentfiles");
  503. extractOption(eclLibraryPath, globals, "ECLCC_ECLLIBRARY_PATH", "eclLibrariesPath", syspath, "share" PATHSEPSTR "ecllibrary" PATHSEPSTR);
  504. extractOption(eclBundlePath, globals, "ECLCC_ECLBUNDLE_PATH", "eclBundlesPath", homepath, PATHSEPSTR "bundles" PATHSEPSTR);
  505. }
  506. extractOption(stdIncludeLibraryPath, globals, "ECLCC_ECLINCLUDE_PATH", "eclIncludePath", ".", NULL);
  507. if (!optLogfile.length() && !optBatchMode && !optNoLogFile)
  508. extractOption(optLogfile, globals, "ECLCC_LOGFILE", "logfile", "eclcc.log", NULL);
  509. if ((logVerbose || optLogfile) && !optNoLogFile)
  510. {
  511. if (optLogfile.length())
  512. {
  513. StringBuffer lf;
  514. openLogFile(lf, optLogfile, optLogDetail, false);
  515. if (logVerbose)
  516. fprintf(stdout, "Logging to '%s'\n",lf.str());
  517. }
  518. }
  519. if (hooksPath.length())
  520. installFileHooks(hooksPath.str());
  521. if (!optNoCompile)
  522. setCompilerPath(compilerPath.str(), cppIncludePath.str(), libraryPath.str(), NULL, optTargetCompiler, logVerbose);
  523. }
  524. //=========================================================================================
  525. void EclCC::applyDebugOptions(IWorkUnit * wu)
  526. {
  527. ForEachItemIn(i, debugOptions)
  528. {
  529. const char * option = debugOptions.item(i);
  530. const char * eq = strchr(option, '=');
  531. if (eq)
  532. {
  533. StringAttr name;
  534. name.set(option, eq-option);
  535. wu->setDebugValue(name, eq+1, true);
  536. }
  537. else
  538. {
  539. size_t len = strlen(option);
  540. if (len)
  541. {
  542. char last = option[len-1];
  543. if (last == '-' || last == '+')
  544. {
  545. StringAttr name;
  546. name.set(option, len-1);
  547. wu->setDebugValueInt(name, last == '+' ? 1 : 0, true);
  548. }
  549. else
  550. wu->setDebugValue(option, "1", true);
  551. }
  552. }
  553. }
  554. }
  555. void EclCC::applyApplicationOptions(IWorkUnit * wu)
  556. {
  557. ForEachItemIn(i, applicationOptions)
  558. {
  559. const char * option = applicationOptions.item(i);
  560. const char * eq = strchr(option, '=');
  561. if (eq)
  562. {
  563. StringAttr name;
  564. name.set(option, eq-option);
  565. wu->setApplicationValue("eclcc", name, eq+1, true);
  566. }
  567. else
  568. {
  569. wu->setApplicationValueInt("eclcc", option, 1, true);
  570. }
  571. }
  572. }
  573. //=========================================================================================
  574. ICppCompiler * EclCC::createCompiler(const char * coreName, const char * sourceDir, const char * targetDir)
  575. {
  576. Owned<ICppCompiler> compiler = ::createCompiler(coreName, sourceDir, targetDir, optTargetCompiler, logVerbose);
  577. compiler->setOnlyCompile(optOnlyCompile);
  578. compiler->setCCLogPath(cclogFilename);
  579. ForEachItemIn(iComp, compileOptions)
  580. compiler->addCompileOption(compileOptions.item(iComp));
  581. ForEachItemIn(iLink, linkOptions)
  582. compiler->addLinkOption(linkOptions.item(iLink));
  583. ForEachItemIn(iLib, libraryPaths)
  584. compiler->addLibraryPath(libraryPaths.item(iLib));
  585. return compiler.getClear();
  586. }
  587. void EclCC::reportCompileErrors(IErrorReceiver & errorProcessor, const char * processName)
  588. {
  589. StringBuffer failText;
  590. StringBuffer absCCLogName;
  591. if (optLogfile.get())
  592. createUNCFilename(optLogfile.get(), absCCLogName, false);
  593. else
  594. absCCLogName = "log file";
  595. failText.appendf("Compile/Link failed for %s (see '%s' for details)",processName,absCCLogName.str());
  596. errorProcessor.reportError(ERR_INTERNALEXCEPTION, failText.toCharArray(), processName, 0, 0, 0);
  597. try
  598. {
  599. StringBuffer s;
  600. Owned<IFile> log = createIFile(cclogFilename);
  601. Owned<IFileIO> io = log->open(IFOread);
  602. if (io)
  603. {
  604. offset_t len = io->size();
  605. if (len)
  606. {
  607. io->read(0, (size32_t)len, s.reserve((size32_t)len));
  608. #ifdef _WIN32
  609. const char * noCompiler = "is not recognized as an internal";
  610. #else
  611. const char * noCompiler = "could not locate compiler";
  612. #endif
  613. if (strstr(s.str(), noCompiler))
  614. {
  615. ERRLOG("Fatal Error: Unable to locate C++ compiler/linker");
  616. }
  617. ERRLOG("\n---------- compiler output --------------\n%s\n--------- end compiler output -----------", s.str());
  618. }
  619. }
  620. }
  621. catch (IException * e)
  622. {
  623. e->Release();
  624. }
  625. }
  626. //=========================================================================================
  627. void EclCC::instantECL(EclCompileInstance & instance, IWorkUnit *wu, const char * queryFullName, IErrorReceiver & errorProcessor, const char * outputFile)
  628. {
  629. StringBuffer processName(outputFile);
  630. if (instance.query && containsAnyActions(instance.query))
  631. {
  632. try
  633. {
  634. const char * templateDir = queryTemplateDir();
  635. bool optSaveTemps = wu->getDebugValueBool("saveEclTempFiles", false);
  636. bool optSaveCpp = optSaveTemps || optNoCompile || wu->getDebugValueBool("saveCppTempFiles", false);
  637. //New scope - testing things are linked correctly
  638. {
  639. Owned<IHqlExprDllGenerator> generator = createDllGenerator(&errorProcessor, processName.toCharArray(), NULL, wu, templateDir, optTargetClusterType, this, false, false);
  640. setWorkunitHash(wu, instance.query);
  641. if (!optShared)
  642. wu->setDebugValueInt("standAloneExe", 1, true);
  643. EclGenerateTarget target = optWorkUnit ? EclGenerateNone : (optNoCompile ? EclGenerateCpp : optShared ? EclGenerateDll : EclGenerateExe);
  644. if (optManifestFilename)
  645. generator->addManifest(optManifestFilename);
  646. if (instance.srcArchive)
  647. {
  648. generator->addManifestFromArchive(instance.srcArchive);
  649. instance.srcArchive.clear();
  650. }
  651. generator->setSaveGeneratedFiles(optSaveCpp);
  652. bool generateOk = generator->processQuery(instance.query, target); // NB: May clear instance.query
  653. instance.stats.cppSize = generator->getGeneratedSize();
  654. if (generateOk && !optNoCompile)
  655. {
  656. Owned<ICppCompiler> compiler = createCompiler(processName.toCharArray());
  657. compiler->setSaveTemps(optSaveTemps);
  658. bool compileOk = true;
  659. if (optShared)
  660. {
  661. compileOk = generator->generateDll(compiler);
  662. }
  663. else
  664. {
  665. if (optTargetClusterType==RoxieCluster)
  666. generator->addLibrary("ccd");
  667. else
  668. generator->addLibrary("hthor");
  669. compileOk = generator->generateExe(compiler);
  670. }
  671. if (!compileOk)
  672. reportCompileErrors(errorProcessor, processName);
  673. }
  674. else
  675. wu->setState(generateOk ? WUStateCompleted : WUStateFailed);
  676. }
  677. if (logVerbose)
  678. {
  679. switch (wu->getState())
  680. {
  681. case WUStateCompiled:
  682. fprintf(stdout, "Output file '%s' created\n",outputFile);
  683. break;
  684. case WUStateFailed:
  685. ERRLOG("Failed to create output file '%s'\n",outputFile);
  686. break;
  687. case WUStateUploadingFiles:
  688. fprintf(stdout, "Output file '%s' created, local file upload required\n",outputFile);
  689. break;
  690. case WUStateCompleted:
  691. fprintf(stdout, "No DLL/SO required\n");
  692. break;
  693. default:
  694. ERRLOG("Unexpected Workunit state %d\n", (int) wu->getState());
  695. break;
  696. }
  697. }
  698. }
  699. catch (IException * e)
  700. {
  701. if (e->errorCode() != HQLERR_ErrorAlreadyReported)
  702. {
  703. StringBuffer exceptionText;
  704. e->errorMessage(exceptionText);
  705. errorProcessor.reportError(ERR_INTERNALEXCEPTION, exceptionText.toCharArray(), queryFullName, 1, 0, 0);
  706. }
  707. e->Release();
  708. }
  709. try
  710. {
  711. Owned<IFile> log = createIFile(cclogFilename);
  712. log->remove();
  713. }
  714. catch (IException * e)
  715. {
  716. e->Release();
  717. }
  718. }
  719. }
  720. //=========================================================================================
  721. void EclCC::getComplexity(IWorkUnit *wu, IHqlExpression * query, IErrorReceiver & errs)
  722. {
  723. double complexity = getECLcomplexity(query, &errs, wu, optTargetClusterType);
  724. LOG(MCstats, unknownJob, "Complexity = %g", complexity);
  725. }
  726. //=========================================================================================
  727. static bool convertPathToModule(StringBuffer & out, const char * filename)
  728. {
  729. const char * dot = strrchr(filename, '.');
  730. if (dot)
  731. {
  732. if (!strieq(dot, ".ecl") && !strieq(dot, ".hql") && !strieq(dot, ".eclmod") && !strieq(dot, ".eclattr"))
  733. return false;
  734. }
  735. else
  736. return false;
  737. const unsigned copyLen = dot-filename;
  738. if (copyLen == 0)
  739. return false;
  740. out.ensureCapacity(copyLen);
  741. for (unsigned i= 0; i < copyLen; i++)
  742. {
  743. char next = filename[i];
  744. if (isPathSepChar(next))
  745. next = '.';
  746. out.append(next);
  747. }
  748. return true;
  749. }
  750. static bool findFilenameInSearchPath(StringBuffer & attributePath, const char * searchPath, const char * expandedSourceName)
  751. {
  752. const char * cur = searchPath;
  753. unsigned lenSource = strlen(expandedSourceName);
  754. loop
  755. {
  756. const char * sep = strchr(cur, ENVSEPCHAR);
  757. StringBuffer curExpanded;
  758. if (!sep)
  759. {
  760. if (*cur)
  761. makeAbsolutePath(cur, curExpanded);
  762. }
  763. else if (sep != cur)
  764. {
  765. StringAttr temp(cur, sep-cur);
  766. makeAbsolutePath(temp, curExpanded);
  767. }
  768. if (curExpanded.length() && (curExpanded.length() < lenSource))
  769. {
  770. #ifdef _WIN32
  771. //windows paths are case insensitive
  772. bool same = memicmp(curExpanded.str(), expandedSourceName, curExpanded.length()) == 0;
  773. #else
  774. bool same = memcmp(curExpanded.str(), expandedSourceName, curExpanded.length()) == 0;
  775. #endif
  776. if (same)
  777. {
  778. const char * tail = expandedSourceName+curExpanded.length();
  779. if (isPathSepChar(*tail))
  780. tail++;
  781. if (convertPathToModule(attributePath, tail))
  782. return true;
  783. }
  784. }
  785. if (!sep)
  786. return false;
  787. cur = sep+1;
  788. }
  789. }
  790. bool EclCC::isWithinPath(const char * sourcePathname, const char * searchPath)
  791. {
  792. if (!sourcePathname)
  793. return false;
  794. StringBuffer expandedSourceName;
  795. makeAbsolutePath(sourcePathname, expandedSourceName);
  796. StringBuffer attributePath;
  797. return findFilenameInSearchPath(attributePath, searchPath, expandedSourceName);
  798. }
  799. bool EclCC::checkWithinRepository(StringBuffer & attributePath, const char * sourcePathname)
  800. {
  801. if (!sourcePathname)
  802. return false;
  803. StringBuffer searchPath;
  804. searchPath.append(eclLibraryPath).append(ENVSEPCHAR);
  805. if (!optNoBundles)
  806. searchPath.append(eclBundlePath).append(ENVSEPCHAR);
  807. if (!optNoStdInc)
  808. searchPath.append(stdIncludeLibraryPath).append(ENVSEPCHAR);
  809. searchPath.append(includeLibraryPath);
  810. StringBuffer expandedSourceName;
  811. makeAbsolutePath(sourcePathname, expandedSourceName);
  812. return findFilenameInSearchPath(attributePath, searchPath, expandedSourceName);
  813. }
  814. void EclCC::evaluateResult(EclCompileInstance & instance)
  815. {
  816. IHqlExpression *query = instance.query;
  817. if (query->getOperator()==no_output)
  818. query = query->queryChild(0);
  819. if (query->getOperator()==no_datasetfromdictionary)
  820. query = query->queryChild(0);
  821. if (query->getOperator()==no_selectfields)
  822. query = query->queryChild(0);
  823. if (query->getOperator()==no_createdictionary)
  824. query = query->queryChild(0);
  825. OwnedHqlExpr folded = foldHqlExpression(instance.queryErrorProcessor(), query, NULL, HFOthrowerror|HFOloseannotations|HFOforcefold|HFOfoldfilterproject|HFOconstantdatasets);
  826. StringBuffer out;
  827. IValue *result = folded->queryValue();
  828. if (result)
  829. result->generateECL(out);
  830. else if (folded->getOperator()==no_list)
  831. {
  832. out.append('[');
  833. ForEachChild(idx, folded)
  834. {
  835. IHqlExpression *child = folded->queryChild(idx);
  836. if (idx)
  837. out.append(", ");
  838. result = child->queryValue();
  839. if (result)
  840. result->generateECL(out);
  841. else
  842. throw MakeStringException(1, "Expression cannot be evaluated");
  843. }
  844. out.append(']');
  845. }
  846. else if (folded->getOperator()==no_inlinetable)
  847. {
  848. IHqlExpression *transformList = folded->queryChild(0);
  849. if (transformList && transformList->getOperator()==no_transformlist)
  850. {
  851. IHqlExpression *transform = transformList->queryChild(0);
  852. assertex(transform && transform->getOperator()==no_transform);
  853. out.append('[');
  854. ForEachChild(idx, transform)
  855. {
  856. IHqlExpression *child = transform->queryChild(idx);
  857. assertex(child->getOperator()==no_assign);
  858. if (idx)
  859. out.append(", ");
  860. result = child->queryChild(1)->queryValue();
  861. if (result)
  862. result->generateECL(out);
  863. else
  864. throw MakeStringException(1, "Expression cannot be evaluated");
  865. }
  866. out.append(']');
  867. }
  868. else
  869. throw MakeStringException(1, "Expression cannot be evaluated");
  870. }
  871. else
  872. {
  873. #ifdef _DEBUG
  874. EclIR::dump_ir(folded);
  875. #endif
  876. throw MakeStringException(1, "Expression cannot be evaluated");
  877. }
  878. printf("%s\n", out.str());
  879. }
  880. void EclCC::processSingleQuery(EclCompileInstance & instance,
  881. IFileContents * queryContents,
  882. const char * queryAttributePath)
  883. {
  884. #ifdef TEST_LEGACY_DEPENDENCY_CODE
  885. setLegacyEclSemantics(instance.legacyImportMode, instance.legacyWhenMode);
  886. Owned<IPropertyTree> dependencies = gatherAttributeDependencies(instance.dataServer, "");
  887. if (dependencies)
  888. saveXML("depends.xml", dependencies);
  889. #endif
  890. Owned<IErrorReceiver> wuErrs = new WorkUnitErrorReceiver(instance.wu, "eclcc");
  891. Owned<IErrorReceiver> compoundErrs = createCompoundErrorReceiver(&instance.queryErrorProcessor(), wuErrs);
  892. Owned<ErrorSeverityMapper> severityMapper = new ErrorSeverityMapper(*compoundErrs);
  893. //Apply command line mappings...
  894. ForEachItemIn(i, warningMappings)
  895. {
  896. if (!severityMapper->addCommandLineMapping(warningMappings.item(i)))
  897. return;
  898. //Preserve command line mappings in the generated archive
  899. if (instance.archive)
  900. instance.archive->addPropTree("OnWarning", createPTree())->setProp("@value",warningMappings.item(i));
  901. }
  902. //Apply preserved onwarning mappings from any source archive
  903. if (instance.srcArchive)
  904. {
  905. Owned<IPropertyTreeIterator> iter = instance.srcArchive->getElements("OnWarning");
  906. ForEach(*iter)
  907. {
  908. const char * name = iter->query().queryProp("@name");
  909. const char * option = iter->query().queryProp("@value");
  910. if (name)
  911. {
  912. if (!severityMapper->addMapping(name, option))
  913. return;
  914. }
  915. else
  916. {
  917. if (!severityMapper->addCommandLineMapping(option))
  918. return;
  919. }
  920. }
  921. }
  922. IErrorReceiver & errorProcessor = *severityMapper;
  923. //All dlls/exes are essentially cloneable because you may be running multiple instances at once
  924. //The only exception would be a dll created for a one-time query. (Currently handled by eclserver.)
  925. instance.wu->setCloneable(true);
  926. applyDebugOptions(instance.wu);
  927. applyApplicationOptions(instance.wu);
  928. if (optTargetCompiler != DEFAULT_COMPILER)
  929. instance.wu->setDebugValue("targetCompiler", compilerTypeText[optTargetCompiler], true);
  930. bool withinRepository = (queryAttributePath && *queryAttributePath);
  931. bool syntaxChecking = instance.wu->getDebugValueBool("syntaxCheck", false);
  932. size32_t prevErrs = errorProcessor.errCount();
  933. unsigned startTime = msTick();
  934. const char * sourcePathname = queryContents ? queryContents->querySourcePath()->str() : NULL;
  935. const char * defaultErrorPathname = sourcePathname ? sourcePathname : queryAttributePath;
  936. //The following is only here to provide information about the source file being compiled when reporting leaks
  937. if (instance.inputFile)
  938. setActiveSource(instance.inputFile->queryFilename());
  939. {
  940. //Minimize the scope of the parse context to reduce lifetime of cached items.
  941. HqlParseContext parseCtx(instance.dataServer, instance.archive);
  942. if (optGenerateMeta || optIncludeMeta)
  943. {
  944. HqlParseContext::MetaOptions options;
  945. options.includePublicDefinitions = instance.wu->getDebugValueBool("metaIncludePublic", true);
  946. options.includePrivateDefinitions = instance.wu->getDebugValueBool("metaIncludePrivate", true);
  947. options.onlyGatherRoot = instance.wu->getDebugValueBool("metaIncludeMainOnly", false);
  948. options.includeImports = instance.wu->getDebugValueBool("metaIncludeImports", true);
  949. options.includeExternalUses = instance.wu->getDebugValueBool("metaIncludeExternalUse", true);
  950. options.includeExternalUses = instance.wu->getDebugValueBool("metaIncludeExternalUse", true);
  951. options.includeLocations = instance.wu->getDebugValueBool("metaIncludeLocations", true);
  952. options.includeJavadoc = instance.wu->getDebugValueBool("metaIncludeJavadoc", true);
  953. parseCtx.setGatherMeta(options);
  954. }
  955. setLegacyEclSemantics(instance.legacyImport, instance.legacyWhen);
  956. if (instance.archive)
  957. {
  958. instance.archive->setPropBool("@legacyImport", instance.legacyImport);
  959. instance.archive->setPropBool("@legacyWhen", instance.legacyWhen);
  960. }
  961. parseCtx.ignoreUnknownImport = instance.ignoreUnknownImport;
  962. try
  963. {
  964. HqlLookupContext ctx(parseCtx, &errorProcessor);
  965. if (withinRepository)
  966. {
  967. if (instance.archive)
  968. {
  969. instance.archive->setProp("Query", "");
  970. instance.archive->setProp("Query/@attributePath", queryAttributePath);
  971. }
  972. instance.query.setown(getResolveAttributeFullPath(queryAttributePath, LSFpublic, ctx));
  973. if (!instance.query && !syntaxChecking && (errorProcessor.errCount() == prevErrs))
  974. {
  975. StringBuffer msg;
  976. msg.append("Could not resolve attribute ").append(queryAttributePath);
  977. errorProcessor.reportError(3, msg.str(), defaultErrorPathname, 0, 0, 0);
  978. }
  979. }
  980. else
  981. {
  982. Owned<IHqlScope> scope = createPrivateScope();
  983. if (instance.legacyImport)
  984. importRootModulesToScope(scope, ctx);
  985. instance.query.setown(parseQuery(scope, queryContents, ctx, NULL, NULL, true));
  986. if (instance.archive)
  987. {
  988. StringBuffer queryText;
  989. queryText.append(queryContents->length(), queryContents->getText());
  990. const char * p = queryText;
  991. if (0 == strncmp(p, (const char *)UTF8_BOM,3))
  992. p += 3;
  993. instance.archive->setProp("Query", p );
  994. instance.archive->setProp("Query/@originalFilename", sourcePathname);
  995. }
  996. }
  997. if (syntaxChecking && instance.query && instance.query->getOperator() == no_forwardscope)
  998. {
  999. IHqlScope * scope = instance.query->queryScope();
  1000. //Have the side effect of resolving the symbols and triggering any syntax errors
  1001. IHqlScope * resolved = scope->queryResolvedScope(&ctx);
  1002. if (resolved)
  1003. instance.query.set(resolved->queryExpression());
  1004. }
  1005. gatherParseWarnings(ctx.errs, instance.query, parseCtx.orphanedWarnings);
  1006. if (instance.query && !syntaxChecking && !optGenerateMeta && !optEvaluateResult)
  1007. instance.query.setown(convertAttributeToQuery(instance.query, ctx));
  1008. instance.stats.parseTime = msTick()-startTime;
  1009. if (instance.wu->getDebugValueBool("addTimingToWorkunit", true))
  1010. updateWorkunitTimeStat(instance.wu, "eclcc", "workunit", "parse time", NULL, milliToNano(instance.stats.parseTime), 1, 0);
  1011. if (optIncludeMeta || optGenerateMeta)
  1012. instance.generatedMeta.setown(parseCtx.getMetaTree());
  1013. if (optEvaluateResult && !errorProcessor.errCount() && instance.query)
  1014. evaluateResult(instance);
  1015. }
  1016. catch (IException *e)
  1017. {
  1018. StringBuffer s;
  1019. e->errorMessage(s);
  1020. errorProcessor.reportError(3, s.toCharArray(), defaultErrorPathname, 1, 0, 0);
  1021. e->Release();
  1022. }
  1023. }
  1024. //Free up the repository (and any cached expressions) as soon as the expression has been parsed
  1025. instance.dataServer.clear();
  1026. if (!syntaxChecking && (errorProcessor.errCount() == prevErrs) && (!instance.query || !containsAnyActions(instance.query)))
  1027. {
  1028. errorProcessor.reportError(3, "Query is empty", defaultErrorPathname, 1, 0, 0);
  1029. return;
  1030. }
  1031. if (instance.archive)
  1032. return;
  1033. if (syntaxChecking || optGenerateMeta || optEvaluateResult)
  1034. return;
  1035. StringBuffer targetFilename;
  1036. const char * outputFilename = instance.outputFilename;
  1037. if (!outputFilename)
  1038. {
  1039. addNonEmptyPathSepChar(targetFilename.append(optOutputDirectory));
  1040. targetFilename.append(DEFAULT_OUTPUTNAME);
  1041. }
  1042. else if (strcmp(outputFilename, "-") == 0)
  1043. targetFilename.append("stdout:");
  1044. else
  1045. addNonEmptyPathSepChar(targetFilename.append(optOutputDirectory)).append(outputFilename);
  1046. //Check if it overlaps with the source file and add .eclout if so
  1047. if (instance.inputFile)
  1048. {
  1049. const char * originalFilename = instance.inputFile->queryFilename();
  1050. if (streq(targetFilename, originalFilename))
  1051. targetFilename.append(".eclout");
  1052. }
  1053. if (errorProcessor.errCount() == prevErrs)
  1054. {
  1055. const char * queryFullName = NULL;
  1056. instantECL(instance, instance.wu, queryFullName, errorProcessor, targetFilename);
  1057. }
  1058. else
  1059. {
  1060. if (stdIoHandle(targetFilename) == -1)
  1061. {
  1062. // MORE - what about intermediate files?
  1063. #ifdef _WIN32
  1064. StringBuffer goer;
  1065. remove(goer.append(targetFilename).append(".exe"));
  1066. remove(goer.clear().append(targetFilename).append(".exe.manifest"));
  1067. #else
  1068. remove(targetFilename);
  1069. #endif
  1070. }
  1071. }
  1072. unsigned totalTime = msTick() - startTime;
  1073. instance.stats.generateTime = totalTime - instance.stats.parseTime;
  1074. if (instance.wu->getDebugValueBool("addTimingToWorkunit", true))
  1075. updateWorkunitTimeStat(instance.wu, "eclcc", "workunit", "totalTime", NULL, milliToNano(totalTime), 1, 0);
  1076. }
  1077. void EclCC::processXmlFile(EclCompileInstance & instance, const char *archiveXML)
  1078. {
  1079. instance.srcArchive.setown(createPTreeFromXMLString(archiveXML, ipt_caseInsensitive));
  1080. IPropertyTree * archiveTree = instance.srcArchive;
  1081. Owned<IPropertyTreeIterator> iter = archiveTree->getElements("Option");
  1082. ForEach(*iter)
  1083. {
  1084. IPropertyTree &item = iter->query();
  1085. instance.wu->setDebugValue(item.queryProp("@name"), item.queryProp("@value"), true);
  1086. }
  1087. const char * queryText = archiveTree->queryProp("Query");
  1088. const char * queryAttributePath = archiveTree->queryProp("Query/@attributePath");
  1089. //Takes precedence over an entry in the archive - so you can submit parts of an archive.
  1090. if (optQueryRepositoryReference)
  1091. queryAttributePath = optQueryRepositoryReference;
  1092. //The legacy mode (if specified) in the archive takes precedence - it needs to match to compile.
  1093. instance.legacyImport = archiveTree->getPropBool("@legacyMode", instance.legacyImport);
  1094. instance.legacyWhen = archiveTree->getPropBool("@legacyMode", instance.legacyWhen);
  1095. instance.legacyImport = archiveTree->getPropBool("@legacyImport", instance.legacyImport);
  1096. instance.legacyWhen = archiveTree->getPropBool("@legacyWhen", instance.legacyWhen);
  1097. //Some old archives contained imports, but no definitions of the module. This option is to allow them to compile.
  1098. //It shouldn't be needed for new archives in non-legacy mode. (But neither should it cause any harm.)
  1099. instance.ignoreUnknownImport = archiveTree->getPropBool("@ignoreUnknownImport", true);
  1100. instance.eclVersion.set(archiveTree->queryProp("@eclVersion"));
  1101. if (optCheckEclVersion)
  1102. instance.checkEclVersionCompatible();
  1103. Owned<IEclSourceCollection> archiveCollection;
  1104. if (archiveTree->getPropBool("@testRemoteInterface", false))
  1105. {
  1106. //This code is purely here for regression testing some of the classes used in the enterprise version.
  1107. Owned<IXmlEclRepository> xmlRepository = createArchiveXmlEclRepository(archiveTree);
  1108. archiveCollection.setown(createRemoteXmlEclCollection(NULL, *xmlRepository, NULL, false));
  1109. archiveCollection->checkCacheValid();
  1110. }
  1111. else
  1112. archiveCollection.setown(createArchiveEclCollection(archiveTree));
  1113. EclRepositoryArray repositories;
  1114. repositories.append(*LINK(pluginsRepository));
  1115. if (archiveTree->getPropBool("@useLocalSystemLibraries", false)) // Primarily for testing.
  1116. repositories.append(*LINK(libraryRepository));
  1117. Owned<IFileContents> contents;
  1118. StringBuffer fullPath; // Here so it doesn't get freed when leaving the else block
  1119. if (queryText || queryAttributePath)
  1120. {
  1121. const char * sourceFilename = archiveTree->queryProp("Query/@originalFilename");
  1122. Owned<ISourcePath> sourcePath = createSourcePath(sourceFilename);
  1123. contents.setown(createFileContentsFromText(queryText, sourcePath));
  1124. if (queryAttributePath && queryText && *queryText)
  1125. {
  1126. Owned<IEclSourceCollection> inputFileCollection = createSingleDefinitionEclCollection(queryAttributePath, contents);
  1127. repositories.append(*createRepository(inputFileCollection));
  1128. }
  1129. }
  1130. else
  1131. {
  1132. //This is really only useful for regression testing
  1133. const char * queryText = archiveTree->queryProp("SyntaxCheck");
  1134. const char * syntaxCheckModule = archiveTree->queryProp("SyntaxCheck/@module");
  1135. const char * syntaxCheckAttribute = archiveTree->queryProp("SyntaxCheck/@attribute");
  1136. if (!queryText || !syntaxCheckModule || !syntaxCheckAttribute)
  1137. throw MakeStringException(1, "No query found in xml");
  1138. instance.wu->setDebugValueInt("syntaxCheck", true, true);
  1139. fullPath.append(syntaxCheckModule).append('.').append(syntaxCheckAttribute);
  1140. queryAttributePath = fullPath.str();
  1141. //Create a repository with just that attribute, and place it before the archive in the resolution order.
  1142. Owned<IFileContents> contents = createFileContentsFromText(queryText, NULL);
  1143. repositories.append(*createSingleDefinitionEclRepository(syntaxCheckModule, syntaxCheckAttribute, contents));
  1144. }
  1145. repositories.append(*createRepository(archiveCollection));
  1146. instance.dataServer.setown(createCompoundRepository(repositories));
  1147. //Ensure classes are not linked by anything else
  1148. archiveCollection.clear();
  1149. repositories.kill();
  1150. processSingleQuery(instance, contents, queryAttributePath);
  1151. }
  1152. //=========================================================================================
  1153. void EclCC::processFile(EclCompileInstance & instance)
  1154. {
  1155. const char * curFilename = instance.inputFile->queryFilename();
  1156. assertex(curFilename);
  1157. Owned<ISourcePath> sourcePath = optNoSourcePath ? NULL : createSourcePath(curFilename);
  1158. Owned<IFileContents> queryText = createFileContentsFromFile(curFilename, sourcePath);
  1159. const char * queryTxt = queryText->getText();
  1160. if (optArchive || optGenerateDepend)
  1161. instance.archive.setown(createAttributeArchive());
  1162. instance.wu.setown(createLocalWorkUnit());
  1163. if (optSaveQueryText)
  1164. {
  1165. Owned<IWUQuery> q = instance.wu->updateQuery();
  1166. q->setQueryText(queryTxt);
  1167. }
  1168. //On a system with userECL not allowed, all compilations must be from checked-in code that has been
  1169. //deployed to the eclcc machine via other means (typically via a version-control system)
  1170. if (!allowAccess("userECL") && (!optQueryRepositoryReference || queryText->length()))
  1171. {
  1172. instance.queryErrorProcessor().reportError(HQLERR_UserCodeNotAllowed, HQLERR_UserCodeNotAllowed_Text, NULL, 1, 0, 0);
  1173. }
  1174. else if (isArchiveQuery(queryTxt))
  1175. {
  1176. instance.fromArchive = true;
  1177. processXmlFile(instance, queryTxt);
  1178. }
  1179. else
  1180. {
  1181. StringBuffer attributePath;
  1182. bool withinRepository = false;
  1183. bool inputFromStdIn = streq(curFilename, "stdin:");
  1184. //Specifying --main indicates that the query text (if present) replaces that definition
  1185. if (optQueryRepositoryReference)
  1186. {
  1187. withinRepository = true;
  1188. attributePath.clear().append(optQueryRepositoryReference);
  1189. }
  1190. else
  1191. {
  1192. withinRepository = !inputFromStdIn && !optNoSourcePath && checkWithinRepository(attributePath, curFilename);
  1193. }
  1194. StringBuffer expandedSourceName;
  1195. if (!inputFromStdIn && !optNoSourcePath)
  1196. makeAbsolutePath(curFilename, expandedSourceName);
  1197. else
  1198. expandedSourceName.append(curFilename);
  1199. EclRepositoryArray repositories;
  1200. repositories.append(*LINK(pluginsRepository));
  1201. repositories.append(*LINK(libraryRepository));
  1202. if (bundlesRepository)
  1203. repositories.append(*LINK(bundlesRepository));
  1204. //Ensure that this source file is used as the definition (in case there are potential clashes)
  1205. //Note, this will not override standard library files.
  1206. if (withinRepository)
  1207. {
  1208. //-main only overrides the definition if the query is non-empty. Otherwise use the existing text.
  1209. if (!optQueryRepositoryReference || queryText->length())
  1210. {
  1211. Owned<IEclSourceCollection> inputFileCollection = createSingleDefinitionEclCollection(attributePath, queryText);
  1212. repositories.append(*createRepository(inputFileCollection));
  1213. }
  1214. }
  1215. else
  1216. {
  1217. //Ensure that $ is valid for any file submitted - even if it isn't in the include direcotories
  1218. //Disable this for the moment when running the regression suite.
  1219. if (!optBatchMode && !withinRepository && !inputFromStdIn && !optNoSourcePath && !optLegacyImport)
  1220. {
  1221. //Associate the contents of the directory with an internal module called _local_directory_
  1222. //(If it was root it might override existing root symbols). $ is the only public way to get at the symbol
  1223. const char * moduleName = "_local_directory_";
  1224. IIdAtom * moduleNameId = createIdAtom(moduleName);
  1225. StringBuffer thisDirectory;
  1226. StringBuffer thisTail;
  1227. splitFilename(expandedSourceName, &thisDirectory, &thisDirectory, &thisTail, NULL);
  1228. attributePath.append(moduleName).append(".").append(thisTail);
  1229. Owned<IEclSourceCollection> inputFileCollection = createSingleDefinitionEclCollection(attributePath, queryText);
  1230. repositories.append(*createRepository(inputFileCollection));
  1231. Owned<IEclSourceCollection> directory = createFileSystemEclCollection(&instance.queryErrorProcessor(), thisDirectory, 0, 0);
  1232. Owned<IEclRepository> directoryRepository = createRepository(directory, moduleName);
  1233. Owned<IEclRepository> nested = createNestedRepository(moduleNameId, directoryRepository);
  1234. repositories.append(*LINK(nested));
  1235. }
  1236. }
  1237. repositories.append(*LINK(includeRepository));
  1238. instance.dataServer.setown(createCompoundRepository(repositories));
  1239. repositories.kill();
  1240. processSingleQuery(instance, queryText, attributePath.str());
  1241. }
  1242. if (instance.reportErrorSummary() && !instance.archive && !(optGenerateMeta && instance.generatedMeta))
  1243. return;
  1244. generateOutput(instance);
  1245. }
  1246. IFileIO * EclCC::createArchiveOutputFile(EclCompileInstance & instance)
  1247. {
  1248. StringBuffer archiveName;
  1249. if (instance.outputFilename && !streq(instance.outputFilename, "-"))
  1250. addNonEmptyPathSepChar(archiveName.append(optOutputDirectory)).append(instance.outputFilename);
  1251. else
  1252. archiveName.append("stdout:");
  1253. //Work around windows problem writing 64K to stdout if not redirected/piped
  1254. OwnedIFile ifile = createIFile(archiveName);
  1255. return ifile->open(IFOcreate);
  1256. }
  1257. void EclCC::outputXmlToOutputFile(EclCompileInstance & instance, IPropertyTree * xml)
  1258. {
  1259. OwnedIFileIO ifileio = createArchiveOutputFile(instance);
  1260. if (ifileio)
  1261. {
  1262. //Work around windows problem writing 64K to stdout if not redirected/piped
  1263. Owned<IIOStream> stream = createIOStream(ifileio.get());
  1264. Owned<IIOStream> buffered = createBufferedIOStream(stream,0x8000);
  1265. saveXML(*buffered, xml);
  1266. }
  1267. }
  1268. void EclCC::addFilenameDependency(StringBuffer & target, EclCompileInstance & instance, const char * filename)
  1269. {
  1270. if (!filename)
  1271. return;
  1272. //Ignore plugins and standard library components
  1273. if (isWithinPath(filename, pluginsPath) || isWithinPath(filename, eclLibraryPath))
  1274. return;
  1275. //Don't include the input file in the dependencies.
  1276. if (instance.inputFile)
  1277. {
  1278. const char * sourceFilename = instance.inputFile->queryFilename();
  1279. if (sourceFilename && streq(sourceFilename, filename))
  1280. return;
  1281. }
  1282. target.append(filename).newline();
  1283. }
  1284. void EclCC::generateOutput(EclCompileInstance & instance)
  1285. {
  1286. const char * outputFilename = instance.outputFilename;
  1287. if (instance.archive)
  1288. {
  1289. if (optGenerateDepend)
  1290. {
  1291. //Walk the archive, and output all filenames that aren't
  1292. //a)in a plugin b) in std.lib c) the original source file.
  1293. StringBuffer filenames;
  1294. Owned<IPropertyTreeIterator> modIter = instance.archive->getElements("Module");
  1295. ForEach(*modIter)
  1296. {
  1297. IPropertyTree * module = &modIter->query();
  1298. if (module->hasProp("@plugin"))
  1299. continue;
  1300. addFilenameDependency(filenames, instance, module->queryProp("@sourcePath"));
  1301. Owned<IPropertyTreeIterator> defIter = module->getElements("Attribute");
  1302. ForEach(*defIter)
  1303. {
  1304. IPropertyTree * definition = &defIter->query();
  1305. addFilenameDependency(filenames, instance, definition->queryProp("@sourcePath"));
  1306. }
  1307. }
  1308. OwnedIFileIO ifileio = createArchiveOutputFile(instance);
  1309. if (ifileio)
  1310. ifileio->write(0, filenames.length(), filenames.str());
  1311. }
  1312. else
  1313. {
  1314. // Output option settings
  1315. Owned<IStringIterator> debugValues = &instance.wu->getDebugValues();
  1316. ForEach (*debugValues)
  1317. {
  1318. SCMStringBuffer debugStr, valueStr;
  1319. debugValues->str(debugStr);
  1320. instance.wu->getDebugValue(debugStr.str(), valueStr);
  1321. Owned<IPropertyTree> option = createPTree("Option");
  1322. option->setProp("@name", debugStr.str());
  1323. option->setProp("@value", valueStr.str());
  1324. instance.archive->addPropTree("Option", option.getClear());
  1325. }
  1326. if (optManifestFilename)
  1327. addManifestResourcesToArchive(instance.archive, optManifestFilename);
  1328. outputXmlToOutputFile(instance, instance.archive);
  1329. }
  1330. }
  1331. if (optGenerateMeta && instance.generatedMeta)
  1332. outputXmlToOutputFile(instance, instance.generatedMeta);
  1333. if (optWorkUnit && instance.wu)
  1334. {
  1335. StringBuffer xmlFilename;
  1336. addNonEmptyPathSepChar(xmlFilename.append(optOutputDirectory));
  1337. if (outputFilename)
  1338. xmlFilename.append(outputFilename);
  1339. else
  1340. xmlFilename.append(DEFAULT_OUTPUTNAME);
  1341. xmlFilename.append(".xml");
  1342. exportWorkUnitToXMLFile(instance.wu, xmlFilename, 0, true, false);
  1343. }
  1344. }
  1345. void EclCC::processReference(EclCompileInstance & instance, const char * queryAttributePath)
  1346. {
  1347. const char * outputFilename = instance.outputFilename;
  1348. instance.wu.setown(createLocalWorkUnit());
  1349. if (optArchive || optGenerateDepend)
  1350. instance.archive.setown(createAttributeArchive());
  1351. EclRepositoryArray repositories;
  1352. repositories.append(*LINK(pluginsRepository));
  1353. repositories.append(*LINK(libraryRepository));
  1354. if (bundlesRepository)
  1355. repositories.append(*LINK(bundlesRepository));
  1356. repositories.append(*LINK(includeRepository));
  1357. instance.dataServer.setown(createCompoundRepository(repositories));
  1358. processSingleQuery(instance, NULL, queryAttributePath);
  1359. if (instance.reportErrorSummary())
  1360. return;
  1361. generateOutput(instance);
  1362. }
  1363. bool EclCC::generatePrecompiledHeader()
  1364. {
  1365. if (inputFiles.ordinality() != 0)
  1366. {
  1367. ERRLOG("No input files should be specified when generating precompiled header");
  1368. return false;
  1369. }
  1370. StringArray paths;
  1371. paths.appendList(cppIncludePath, ENVSEPSTR);
  1372. const char *foundPath = NULL;
  1373. ForEachItemIn(idx, paths)
  1374. {
  1375. StringBuffer fullpath;
  1376. fullpath.append(paths.item(idx));
  1377. addPathSepChar(fullpath).append("eclinclude4.hpp");
  1378. if (checkFileExists(fullpath))
  1379. {
  1380. foundPath = paths.item(idx);
  1381. break;
  1382. }
  1383. }
  1384. if (!foundPath)
  1385. {
  1386. ERRLOG("Cannot find eclinclude4.hpp");
  1387. return false;
  1388. }
  1389. Owned<ICppCompiler> compiler = createCompiler("eclinclude4.hpp", foundPath, NULL);
  1390. compiler->setDebug(true); // a precompiled header with debug can be used for no-debug, but not vice versa
  1391. compiler->setPrecompileHeader(true);
  1392. if (compiler->compile())
  1393. {
  1394. try
  1395. {
  1396. Owned<IFile> log = createIFile(cclogFilename);
  1397. log->remove();
  1398. }
  1399. catch (IException * e)
  1400. {
  1401. e->Release();
  1402. }
  1403. return true;
  1404. }
  1405. else
  1406. {
  1407. ERRLOG("Compilation failed - see %s for details", cclogFilename.str());
  1408. return false;
  1409. }
  1410. }
  1411. bool EclCC::processFiles()
  1412. {
  1413. loadOptions();
  1414. ForEachItemIn(idx, inputFileNames)
  1415. {
  1416. processArgvFilename(inputFiles, inputFileNames.item(idx));
  1417. }
  1418. if (optShowPaths)
  1419. {
  1420. printf("CL_PATH=%s\n", compilerPath.str());
  1421. printf("ECLCC_ECLBUNDLE_PATH=%s\n", eclBundlePath.str());
  1422. printf("ECLCC_ECLINCLUDE_PATH=%s\n", stdIncludeLibraryPath.str());
  1423. printf("ECLCC_ECLLIBRARY_PATH=%s\n", eclLibraryPath.str());
  1424. printf("ECLCC_INCLUDE_PATH=%s\n", cppIncludePath.str());
  1425. printf("ECLCC_LIBRARY_PATH=%s\n", libraryPath.str());
  1426. printf("ECLCC_PLUGIN_PATH=%s\n", pluginsPath.str());
  1427. printf("ECLCC_TPL_PATH=%s\n", templatePath.str());
  1428. printf("HPCC_FILEHOOKS_PATH=%s\n", hooksPath.str());
  1429. return true;
  1430. }
  1431. if (optGenerateHeader)
  1432. {
  1433. return generatePrecompiledHeader();
  1434. }
  1435. else if (inputFiles.ordinality() == 0)
  1436. {
  1437. if (optBatchMode || !optQueryRepositoryReference)
  1438. {
  1439. ERRLOG("No input files could be opened");
  1440. return false;
  1441. }
  1442. }
  1443. StringBuffer searchPath;
  1444. if (!optNoStdInc)
  1445. searchPath.append(stdIncludeLibraryPath).append(ENVSEPCHAR);
  1446. searchPath.append(includeLibraryPath);
  1447. Owned<IErrorReceiver> errs = createFileErrorReceiver(stderr);
  1448. pluginsRepository.setown(createNewSourceFileEclRepository(errs, pluginsPath.str(), ESFallowplugins, logVerbose ? PLUGIN_DLL_MODULE : 0));
  1449. if (!optNoBundles)
  1450. bundlesRepository.setown(createNewSourceFileEclRepository(errs, eclBundlePath.str(), 0, 0));
  1451. libraryRepository.setown(createNewSourceFileEclRepository(errs, eclLibraryPath.str(), 0, 0));
  1452. includeRepository.setown(createNewSourceFileEclRepository(errs, searchPath.str(), 0, 0));
  1453. //Ensure symbols for plugins are initialised - see comment before CHqlMergedScope...
  1454. // lookupAllRootDefinitions(pluginsRepository);
  1455. bool ok = true;
  1456. if (optBatchMode)
  1457. {
  1458. processBatchFiles();
  1459. }
  1460. else if (inputFiles.ordinality() == 0)
  1461. {
  1462. assertex(optQueryRepositoryReference);
  1463. EclCompileInstance info(NULL, *errs, stderr, optOutputFilename, optLegacyImport, optLegacyWhen);
  1464. processReference(info, optQueryRepositoryReference);
  1465. ok = (errs->errCount() == 0);
  1466. info.logStats();
  1467. }
  1468. else
  1469. {
  1470. EclCompileInstance info(&inputFiles.item(0), *errs, stderr, optOutputFilename, optLegacyImport, optLegacyWhen);
  1471. processFile(info);
  1472. ok = (errs->errCount() == 0);
  1473. info.logStats();
  1474. }
  1475. if (logTimings)
  1476. {
  1477. StringBuffer s;
  1478. fprintf(stderr, "%s", defaultTimer->getTimings(s).str());
  1479. }
  1480. return ok;
  1481. }
  1482. void EclCC::setDebugOption(const char * name, bool value)
  1483. {
  1484. StringBuffer temp;
  1485. temp.append(name).append("=").append(value ? "1" : "0");
  1486. debugOptions.append(temp);
  1487. }
  1488. void EclCompileInstance::checkEclVersionCompatible()
  1489. {
  1490. //Strange function that might modify errorProcessor...
  1491. ::checkEclVersionCompatible(errorProcessor, eclVersion);
  1492. }
  1493. void EclCompileInstance::logStats()
  1494. {
  1495. if (wu && wu->getDebugValueBool("logCompileStats", false))
  1496. {
  1497. memsize_t peakVm, peakResident;
  1498. getPeakMemUsage(peakVm, peakResident);
  1499. //Stats: added as a prefix so it is easy to grep, and a comma so can be read as a csv list.
  1500. DBGLOG("Stats:,parse,%u,generate,%u,peakmem,%u,xml,%"I64F"u,cpp,%"I64F"u",
  1501. stats.parseTime, stats.generateTime, (unsigned)(peakResident / 0x100000),
  1502. (unsigned __int64)stats.xmlSize, (unsigned __int64)stats.cppSize);
  1503. }
  1504. }
  1505. bool EclCompileInstance::reportErrorSummary()
  1506. {
  1507. if (errorProcessor->errCount() || errorProcessor->warnCount())
  1508. {
  1509. fprintf(errout, "%d error%s, %d warning%s\n", errorProcessor->errCount(), errorProcessor->errCount()<=1 ? "" : "s",
  1510. errorProcessor->warnCount(), errorProcessor->warnCount()<=1?"":"s");
  1511. }
  1512. return errorProcessor->errCount() != 0;
  1513. }
  1514. //=========================================================================================
  1515. void EclCC::noteCluster(const char *clusterName)
  1516. {
  1517. }
  1518. bool EclCC::allowAccess(const char * category)
  1519. {
  1520. ForEachItemIn(idx1, deniedPermissions)
  1521. {
  1522. if (stricmp(deniedPermissions.item(idx1), category)==0)
  1523. return false;
  1524. }
  1525. ForEachItemIn(idx2, allowedPermissions)
  1526. {
  1527. if (stricmp(allowedPermissions.item(idx2), category)==0)
  1528. return true;
  1529. }
  1530. return defaultAllowed;
  1531. }
  1532. //=========================================================================================
  1533. bool EclCC::parseCommandLineOptions(int argc, const char* argv[])
  1534. {
  1535. if (argc < 2)
  1536. {
  1537. usage();
  1538. return false;
  1539. }
  1540. ArgvIterator iter(argc, argv);
  1541. StringAttr tempArg;
  1542. bool tempBool;
  1543. bool showHelp = false;
  1544. for (; !iter.done(); iter.next())
  1545. {
  1546. const char * arg = iter.query();
  1547. if (iter.matchFlag(tempArg, "-a"))
  1548. {
  1549. applicationOptions.append(tempArg);
  1550. }
  1551. else if (iter.matchOption(tempArg, "--allow"))
  1552. {
  1553. allowedPermissions.append(tempArg);
  1554. }
  1555. else if (iter.matchFlag(optBatchMode, "-b"))
  1556. {
  1557. }
  1558. else if (iter.matchOption(tempArg, "-brk"))
  1559. {
  1560. #if defined(_WIN32) && defined(_DEBUG)
  1561. unsigned id = atoi(tempArg);
  1562. if (id == 0)
  1563. DebugBreak();
  1564. else
  1565. _CrtSetBreakAlloc(id);
  1566. #endif
  1567. }
  1568. else if (iter.matchFlag(optOnlyCompile, "-c"))
  1569. {
  1570. }
  1571. else if (iter.matchFlag(optCheckEclVersion, "-checkVersion"))
  1572. {
  1573. }
  1574. else if (iter.matchOption(tempArg, "--deny"))
  1575. {
  1576. if (stricmp(tempArg, "all")==0)
  1577. defaultAllowed = false;
  1578. else
  1579. deniedPermissions.append(tempArg);
  1580. }
  1581. else if (iter.matchFlag(optArchive, "-E"))
  1582. {
  1583. }
  1584. else if (iter.matchFlag(tempArg, "-f"))
  1585. {
  1586. debugOptions.append(tempArg);
  1587. }
  1588. else if (iter.matchFlag(tempBool, "-g"))
  1589. {
  1590. if (tempBool)
  1591. {
  1592. debugOptions.append("debugQuery");
  1593. debugOptions.append("saveCppTempFiles");
  1594. }
  1595. else
  1596. debugOptions.append("debugQuery=0");
  1597. }
  1598. else if (strcmp(arg, "-internal")==0)
  1599. {
  1600. outputSizeStmts();
  1601. testHqlInternals();
  1602. }
  1603. else if (iter.matchFlag(tempBool, "-save-cpps"))
  1604. {
  1605. setDebugOption("saveCppTempFiles", tempBool);
  1606. }
  1607. else if (iter.matchFlag(tempBool, "-save-temps"))
  1608. {
  1609. setDebugOption("saveEclTempFiles", tempBool);
  1610. }
  1611. else if (iter.matchFlag(showHelp, "-help") || iter.matchFlag(showHelp, "--help"))
  1612. {
  1613. }
  1614. else if (iter.matchPathFlag(includeLibraryPath, "-I"))
  1615. {
  1616. }
  1617. else if (iter.matchFlag(tempArg, "-L"))
  1618. {
  1619. libraryPaths.append(tempArg);
  1620. }
  1621. else if (iter.matchFlag(tempBool, "-legacy"))
  1622. {
  1623. optLegacyImport = tempBool;
  1624. optLegacyWhen = tempBool;
  1625. }
  1626. else if (iter.matchFlag(optLegacyImport, "-legacyimport"))
  1627. {
  1628. }
  1629. else if (iter.matchFlag(optLegacyWhen, "-legacywhen"))
  1630. {
  1631. }
  1632. else if (iter.matchOption(optLogfile, "--logfile"))
  1633. {
  1634. }
  1635. else if (iter.matchFlag(optNoLogFile, "--nologfile"))
  1636. {
  1637. }
  1638. else if (iter.matchFlag(optNoStdInc, "--nostdinc"))
  1639. {
  1640. }
  1641. else if (iter.matchFlag(optNoBundles, "--nobundles"))
  1642. {
  1643. }
  1644. else if (iter.matchOption(optLogDetail, "--logdetail"))
  1645. {
  1646. }
  1647. else if (iter.matchOption(optQueryRepositoryReference, "-main"))
  1648. {
  1649. }
  1650. else if (iter.matchFlag(optDebugMemLeak, "-m"))
  1651. {
  1652. }
  1653. else if (iter.matchFlag(optIncludeMeta, "-meta"))
  1654. {
  1655. }
  1656. else if (iter.matchFlag(optGenerateMeta, "-M"))
  1657. {
  1658. }
  1659. else if (iter.matchFlag(optGenerateDepend, "-Md"))
  1660. {
  1661. }
  1662. else if (iter.matchFlag(optEvaluateResult, "-Me"))
  1663. {
  1664. }
  1665. else if (iter.matchFlag(optNoSourcePath, "--nosourcepath"))
  1666. {
  1667. }
  1668. else if (iter.matchFlag(optOutputFilename, "-o"))
  1669. {
  1670. }
  1671. else if (iter.matchFlag(optOutputDirectory, "-P"))
  1672. {
  1673. }
  1674. else if (iter.matchFlag(optGenerateHeader, "-pch"))
  1675. {
  1676. }
  1677. else if (iter.matchFlag(optSaveQueryText, "-q"))
  1678. {
  1679. }
  1680. else if (iter.matchFlag(optNoCompile, "-S"))
  1681. {
  1682. }
  1683. else if (iter.matchFlag(optShared, "-shared"))
  1684. {
  1685. }
  1686. else if (iter.matchFlag(tempBool, "-syntax"))
  1687. {
  1688. setDebugOption("syntaxCheck", tempBool);
  1689. }
  1690. else if (iter.matchOption(optIniFilename, "-specs"))
  1691. {
  1692. if (!checkFileExists(optIniFilename))
  1693. {
  1694. ERRLOG("Error: INI file '%s' does not exist",optIniFilename.get());
  1695. return false;
  1696. }
  1697. }
  1698. else if (iter.matchFlag(optShowPaths, "-showpaths"))
  1699. {
  1700. }
  1701. else if (iter.matchOption(optManifestFilename, "-manifest"))
  1702. {
  1703. if (!isManifestFileValid(optManifestFilename))
  1704. return false;
  1705. }
  1706. else if (iter.matchOption(tempArg, "-split"))
  1707. {
  1708. batchPart = atoi(tempArg)-1;
  1709. const char * split = strchr(tempArg, ':');
  1710. if (!split)
  1711. {
  1712. ERRLOG("Error: syntax is -split=part:splits\n");
  1713. return false;
  1714. }
  1715. batchSplit = atoi(split+1);
  1716. if (batchSplit == 0)
  1717. batchSplit = 1;
  1718. if (batchPart >= batchSplit)
  1719. batchPart = 0;
  1720. }
  1721. else if (iter.matchFlag(logTimings, "--timings"))
  1722. {
  1723. }
  1724. else if (iter.matchOption(tempArg, "-platform") || /*deprecated*/ iter.matchOption(tempArg, "-target"))
  1725. {
  1726. if (!setTargetPlatformOption(tempArg.get(), optTargetClusterType))
  1727. return false;
  1728. }
  1729. else if (iter.matchFlag(logVerbose, "-v") || iter.matchFlag(logVerbose, "--verbose"))
  1730. {
  1731. Owned<ILogMsgFilter> filter = getDefaultLogMsgFilter();
  1732. queryLogMsgManager()->changeMonitorFilter(queryStderrLogMsgHandler(), filter);
  1733. }
  1734. else if (strcmp(arg, "--version")==0)
  1735. {
  1736. fprintf(stdout,"%s %s\n", LANGUAGE_VERSION, BUILD_TAG);
  1737. return false;
  1738. }
  1739. else if (startsWith(arg, "-Wc,"))
  1740. {
  1741. expandCommaList(compileOptions, arg+4);
  1742. }
  1743. else if (startsWith(arg, "-Wl,"))
  1744. {
  1745. //Pass these straight through to the linker - with -Wl, prefix removed
  1746. linkOptions.append(arg+4);
  1747. }
  1748. else if (startsWith(arg, "-Wp,") || startsWith(arg, "-Wa,"))
  1749. {
  1750. //Pass these straight through to the gcc compiler
  1751. compileOptions.append(arg);
  1752. }
  1753. else if (iter.matchFlag(optWorkUnit, "-wu"))
  1754. {
  1755. }
  1756. else if (iter.matchFlag(tempArg, "-w"))
  1757. {
  1758. //Any other option beginning -wxxx are treated as warning mappings
  1759. warningMappings.append(tempArg);
  1760. }
  1761. else if (strcmp(arg, "-")==0)
  1762. {
  1763. inputFileNames.append("stdin:");
  1764. }
  1765. else if (arg[0] == '-')
  1766. {
  1767. ERRLOG("Error: unrecognised option %s",arg);
  1768. usage();
  1769. return false;
  1770. }
  1771. else
  1772. inputFileNames.append(arg);
  1773. }
  1774. if (showHelp)
  1775. {
  1776. usage();
  1777. return false;
  1778. }
  1779. // Option post processing follows:
  1780. if (optArchive || optWorkUnit || optGenerateMeta || optGenerateDepend || optShowPaths)
  1781. optNoCompile = true;
  1782. loadManifestOptions();
  1783. if (inputFileNames.ordinality() == 0)
  1784. {
  1785. if (optGenerateHeader || optShowPaths || (!optBatchMode && optQueryRepositoryReference))
  1786. return true;
  1787. ERRLOG("No input filenames supplied");
  1788. return false;
  1789. }
  1790. if (optDebugMemLeak)
  1791. {
  1792. StringBuffer title;
  1793. title.append(inputFileNames.item(0)).newline();
  1794. initLeakCheck(title);
  1795. }
  1796. return true;
  1797. }
  1798. //=========================================================================================
  1799. // Exclamation in the first column indicates it is only part of the verbose output
  1800. const char * const helpText[] = {
  1801. "",
  1802. "Usage:",
  1803. " eclcc <options> queryfile.ecl",
  1804. "",
  1805. "General options:",
  1806. " -I <path> Add path to locations to search for ecl imports",
  1807. " -L <path> Add path to locations to search for system libraries",
  1808. " -o <file> Specify name of output file (default a.out if linking to",
  1809. " executable, or stdout)",
  1810. " -manifest Specify path to manifest file listing resources to add",
  1811. " -foption[=value] Set an ecl option (#option)",
  1812. " -main <ref> Compile definition <ref> from the source collection",
  1813. " -syntax Perform a syntax check of the ECL",
  1814. " -platform=hthor Generate code for hthor executable (default)",
  1815. " -platform=roxie Generate code for roxie cluster",
  1816. " -platform=thor Generate code for thor cluster",
  1817. "",
  1818. "Output control options",
  1819. " -E Output preprocessed ECL in xml archive form",
  1820. "! -M Output meta information for the ecl files",
  1821. "! -Md Output dependency information",
  1822. "! -Me eclcc should evaluate supplied ecl code rather than generating a workunit",
  1823. " -q Save ECL query text as part of workunit",
  1824. " -wu Only generate workunit information as xml file",
  1825. "",
  1826. "c++ options",
  1827. " -S Generate c++ output, but don't compile",
  1828. "! -c compile only (don't link)",
  1829. " -g Enable debug symbols in generated code",
  1830. " -Wc,xx Pass option xx to the c++ compiler",
  1831. "! -Wl,xx Pass option xx to the linker",
  1832. "! -Wa,xx Passed straight through to c++ compiler",
  1833. "! -Wp,xx Passed straight through to c++ compiler",
  1834. "! -save-cpps Do not delete generated c++ files (implied if -g)",
  1835. "! -save-temps Do not delete intermediate files",
  1836. " -shared Generate workunit shared object instead of a stand-alone exe",
  1837. "",
  1838. "Other options:",
  1839. "! -aoption[=value] Set an application option",
  1840. "! --allow=str Allow use of named feature",
  1841. "! -b Batch mode. Each source file is processed in turn. Output",
  1842. "! name depends on the input filename",
  1843. "! -checkVersion Enable/disable ecl version checking from archives",
  1844. #ifdef _WIN32
  1845. "! -brk <n> Trigger a break point in eclcc after nth allocation",
  1846. #endif
  1847. "! --deny=all Disallow use of all named features not specifically allowed using --allow",
  1848. "! --deny=str Disallow use of named feature",
  1849. " -help, --help Display this message",
  1850. " -help -v Display verbose help message",
  1851. "! -internal Run internal tests",
  1852. "! -legacy Use legacy import and when semantics (deprecated)",
  1853. "! -legacyimport Use legacy import semantics (deprecated)",
  1854. "! -legacywhen Use legacy when/side-effects semantics (deprecated)",
  1855. " --logfile <file> Write log to specified file",
  1856. "! --logdetail=n Set the level of detail in the log file",
  1857. "! --nologfile Do not write any logfile",
  1858. #ifdef _WIN32
  1859. "! -m Enable leak checking",
  1860. #endif
  1861. " --nosourcepath Compile as if the source came from stdin",
  1862. #ifndef _WIN32
  1863. "! -pch Generate precompiled header for eclinclude4.hpp",
  1864. #endif
  1865. "! -P <path> Specify the path of the output files (only with -b option)",
  1866. "! -showpaths Print information about the searchpaths eclcc is using",
  1867. " -specs file Read eclcc configuration from specified file",
  1868. "! -split m:n Process a subset m of n input files (only with -b option)",
  1869. " -v --verbose Output additional tracing information while compiling",
  1870. " -wxxxx=level Set the severity for a particular warning code or category",
  1871. "! -wall sets default severity for all warnings",
  1872. "! level=ignore|log|warning|error|fail",
  1873. " --version Output version information",
  1874. "! --timings Output additional timing information",
  1875. "!",
  1876. "!#options",
  1877. "! -factivitiesPerCpp Number of activities in each c++ file",
  1878. "! (requires -fspanMultipleCpp)",
  1879. "! -fapplyInstantEclTransformations Limit non file outputs with a CHOOSEN",
  1880. "! -fapplyInstantEclTransformationsLimit Number of records to limit to",
  1881. "! -fcheckAsserts Check ASSERT() statements",
  1882. "! -fmaxCompileThreads Number of compiler instances to compile the c++",
  1883. "! -fnoteRecordSizeInGraph Add estimates of record sizes to the graph",
  1884. "! -fpickBestEngine Allow simple thor queries to be passed to thor",
  1885. "! -fshowActivitySizeInGraph Show estimates of generated c++ size in the graph",
  1886. "! -fshowMetaInGraph Add distribution/sort orders to the graph",
  1887. "! -fshowRecordCountInGraph Show estimates of record counts in the graph",
  1888. "! -fspanMultipleCpp Generate a work unit in multiple c++ files",
  1889. "",
  1890. };
  1891. void EclCC::usage()
  1892. {
  1893. for (unsigned line=0; line < _elements_in(helpText); line++)
  1894. {
  1895. const char * text = helpText[line];
  1896. if (*text == '!')
  1897. {
  1898. if (logVerbose)
  1899. {
  1900. //Allow conditional headers
  1901. if (text[1] == ' ')
  1902. fprintf(stdout, " %s\n", text+1);
  1903. else
  1904. fprintf(stdout, "%s\n", text+1);
  1905. }
  1906. }
  1907. else
  1908. fprintf(stdout, "%s\n", text);
  1909. }
  1910. }
  1911. //=========================================================================================
  1912. // The following methods are concerned with running eclcc in batch mode (primarily to aid regression testing)
  1913. void EclCC::processBatchedFile(IFile & file, bool multiThreaded)
  1914. {
  1915. StringBuffer basename, logFilename, xmlFilename, outFilename;
  1916. splitFilename(file.queryFilename(), NULL, NULL, &basename, &basename);
  1917. addNonEmptyPathSepChar(logFilename.append(optOutputDirectory)).append(basename).append(".log");
  1918. addNonEmptyPathSepChar(xmlFilename.append(optOutputDirectory)).append(basename).append(".xml");
  1919. splitFilename(file.queryFilename(), NULL, NULL, &outFilename, &outFilename);
  1920. unsigned startTime = msTick();
  1921. FILE * logFile = fopen(logFilename.str(), "w");
  1922. if (!logFile)
  1923. throw MakeStringException(99, "couldn't create log output %s", logFilename.str());
  1924. Owned<ILogMsgHandler> handler;
  1925. try
  1926. {
  1927. // Print compiler and arguments to help reproduce problems
  1928. for (int i=0; i<argc; i++)
  1929. fprintf(logFile, "%s ", argv[i]);
  1930. fprintf(logFile, "\n");
  1931. fprintf(logFile, "--- %s --- \n", basename.str());
  1932. {
  1933. if (!multiThreaded)
  1934. {
  1935. handler.setown(getHandleLogMsgHandler(logFile, 0, false));
  1936. Owned<ILogMsgFilter> filter = getCategoryLogMsgFilter(MSGAUD_all, MSGCLS_all, DefaultDetail);
  1937. queryLogMsgManager()->addMonitor(handler, filter);
  1938. resetUniqueId();
  1939. resetLexerUniqueNames();
  1940. }
  1941. Owned<IErrorReceiver> localErrs = createFileErrorReceiver(logFile);
  1942. EclCompileInstance info(&file, *localErrs, logFile, outFilename, optLegacyImport, optLegacyWhen);
  1943. processFile(info);
  1944. //Following only produces output if the system has been compiled with TRANSFORM_STATS defined
  1945. dbglogTransformStats(true);
  1946. if (info.wu &&
  1947. (info.wu->getDebugValueBool("generatePartialOutputOnError", false) || info.queryErrorProcessor().errCount() == 0))
  1948. {
  1949. exportWorkUnitToXMLFile(info.wu, xmlFilename, XML_NoBinaryEncode64, true, false);
  1950. Owned<IFile> xml = createIFile(xmlFilename);
  1951. info.stats.xmlSize = xml->size();
  1952. }
  1953. info.logStats();
  1954. }
  1955. }
  1956. catch (IException * e)
  1957. {
  1958. StringBuffer s;
  1959. e->errorMessage(s);
  1960. e->Release();
  1961. fprintf(logFile, "Unexpected exception: %s", s.str());
  1962. }
  1963. if (handler)
  1964. {
  1965. queryLogMsgManager()->removeMonitor(handler);
  1966. handler.clear();
  1967. }
  1968. fflush(logFile);
  1969. fclose(logFile);
  1970. unsigned nowTime = msTick();
  1971. StringBuffer s;
  1972. s.append(basename).append(":");
  1973. s.padTo(50);
  1974. s.appendf("%8d ms\n", nowTime-startTime);
  1975. fprintf(batchLog, "%s", s.str());
  1976. // fflush(batchLog);
  1977. }
  1978. typedef SafeQueueOf<IFile, true> RegressQueue;
  1979. class BatchThread : public Thread
  1980. {
  1981. public:
  1982. BatchThread(EclCC & _compiler, RegressQueue & _queue, Semaphore & _fileReady)
  1983. : compiler(_compiler), queue(_queue), fileReady(_fileReady)
  1984. {
  1985. }
  1986. virtual int run()
  1987. {
  1988. loop
  1989. {
  1990. fileReady.wait();
  1991. IFile * next = queue.dequeue();
  1992. if (!next)
  1993. return 0;
  1994. compiler.processBatchedFile(*next, true);
  1995. next->Release();
  1996. }
  1997. }
  1998. protected:
  1999. EclCC & compiler;
  2000. RegressQueue & queue;
  2001. Semaphore & fileReady;
  2002. };
  2003. int compareFilenames(IInterface * * pleft, IInterface * * pright)
  2004. {
  2005. IFile * left = static_cast<IFile *>(*pleft);
  2006. IFile * right = static_cast<IFile *>(*pright);
  2007. return stricmp(pathTail(left->queryFilename()), pathTail(right->queryFilename()));
  2008. }
  2009. void EclCC::processBatchFiles()
  2010. {
  2011. Thread * * threads = NULL;
  2012. RegressQueue queue;
  2013. Semaphore fileReady;
  2014. unsigned startAllTime = msTick();
  2015. if (optThreads > 0)
  2016. {
  2017. threads = new Thread * [optThreads];
  2018. for (unsigned i = 0; i < optThreads; i++)
  2019. {
  2020. threads[i] = new BatchThread(*this, queue, fileReady);
  2021. threads[i]->start();
  2022. }
  2023. }
  2024. StringBuffer batchLogName;
  2025. addNonEmptyPathSepChar(batchLogName.append(optOutputDirectory)).append("_batch_.");
  2026. batchLogName.append(batchPart+1);
  2027. batchLogName.append(".log");
  2028. batchLog = fopen(batchLogName.str(), "w");
  2029. if (!batchLog)
  2030. throw MakeStringException(99, "couldn't create log output %s", batchLogName.str());
  2031. //Divide the files up based on file size, rather than name
  2032. inputFiles.sort(compareFilenames);
  2033. unsigned __int64 totalSize = 0;
  2034. ForEachItemIn(iSize, inputFiles)
  2035. {
  2036. IFile & cur = inputFiles.item(iSize);
  2037. totalSize += cur.size();
  2038. }
  2039. //Sort the filenames so you have a consistent order between windows and linux
  2040. unsigned __int64 averageFileSize = totalSize / inputFiles.ordinality();
  2041. unsigned splitter = 0;
  2042. unsigned __int64 sizeSoFar = 0;
  2043. ForEachItemIn(i, inputFiles)
  2044. {
  2045. IFile &file = inputFiles.item(i);
  2046. if (splitter == batchPart)
  2047. {
  2048. if (optThreads > 0)
  2049. {
  2050. queue.enqueue(LINK(&file));
  2051. fileReady.signal();
  2052. }
  2053. else
  2054. processBatchedFile(file, false);
  2055. }
  2056. unsigned __int64 thisSize = file.size();
  2057. sizeSoFar += thisSize;
  2058. if (sizeSoFar > averageFileSize)
  2059. {
  2060. sizeSoFar = 0;
  2061. splitter++;
  2062. }
  2063. if (splitter == batchSplit)
  2064. splitter = 0;
  2065. }
  2066. if (optThreads > 0)
  2067. {
  2068. for (unsigned i = 0; i < optThreads; i++)
  2069. fileReady.signal();
  2070. for (unsigned j = 0; j < optThreads; j++)
  2071. threads[j]->join();
  2072. for (unsigned i2 = 0; i2 < optThreads; i2++)
  2073. threads[i2]->Release();
  2074. delete [] threads;
  2075. }
  2076. fprintf(batchLog, "@%5ds total time for part %d\n", (msTick()-startAllTime)/1000, batchPart);
  2077. fclose(batchLog);
  2078. batchLog = NULL;
  2079. }