hqlecl.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. /*##############################################################################
  2. Copyright (C) 2011 HPCC Systems.
  3. All rights reserved. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ############################################################################## */
  14. #include "build-config.h"
  15. #include "jliball.hpp"
  16. #include "jmisc.hpp"
  17. #include "jstream.hpp"
  18. #include "hql.hpp"
  19. #include "hqlexpr.hpp"
  20. #include "hqlecl.hpp"
  21. #include "hqlthql.hpp"
  22. #include "hqlcerrors.hpp"
  23. #include "hqlcatom.hpp"
  24. #include "hqllib.ipp"
  25. #include "hqlutil.hpp"
  26. #include "hqlhtcpp.ipp"
  27. #include "hqlwcpp.hpp"
  28. #include "hqllib.ipp"
  29. #include "hqlttcpp.ipp"
  30. #include "workunit.hpp"
  31. #include "thorplugin.hpp"
  32. #ifdef _DEBUG
  33. #define IS_DEBUG_BUILD true
  34. #else
  35. #define IS_DEBUG_BUILD false
  36. #endif
  37. #define MAIN_MODULE_TEMPLATE "thortpl.cpp"
  38. #define HEADER_TEMPLATE "thortpl.hpp"
  39. #define CHILD_MODULE_TEMPLATE "childtpl.cpp"
  40. #define PROTO_TEMPLATE "prototpl.cpp"
  41. class NullContextCallback : public CInterface, implements ICodegenContextCallback
  42. {
  43. IMPLEMENT_IINTERFACE
  44. virtual void noteCluster(const char *clusterName) {}
  45. virtual void registerFile(const char * filename, const char * description) {}
  46. virtual bool allowAccess(const char * category) { return true; }
  47. };
  48. class HqlDllGenerator : public CInterface, implements IHqlExprDllGenerator, implements IAbortRequestCallback
  49. {
  50. public:
  51. HqlDllGenerator(IErrorReceiver * _errs, const char * _wuname, const char * _targetdir, IWorkUnit * _wu, const char * _template_dir, ClusterType _targetClusterType, ICodegenContextCallback * _ctxCallback, bool _checkForLocalFileUploads) :
  52. errs(_errs), wuname(_wuname), targetDir(_targetdir), wu(_wu), template_dir(_template_dir), targetClusterType(_targetClusterType), ctxCallback(_ctxCallback), checkForLocalFileUploads(_checkForLocalFileUploads)
  53. {
  54. if (!ctxCallback)
  55. ctxCallback.setown(new NullContextCallback);
  56. noOutput = true;
  57. defaultMaxCompileThreads = 1;
  58. generateTarget = EclGenerateNone;
  59. code.setown(createCppInstance(wu, wuname));
  60. deleteGenerated = false;
  61. }
  62. IMPLEMENT_IINTERFACE
  63. virtual void addLibrary(const char * name);
  64. virtual bool processQuery(IHqlExpression * expr, EclGenerateTarget _generateTarget);
  65. virtual bool generateDll(ICppCompiler * compiler);
  66. virtual bool generateExe(ICppCompiler * compiler);
  67. virtual bool generatePackage(const char * packageName);
  68. virtual void setMaxCompileThreads(unsigned value) { defaultMaxCompileThreads = value; }
  69. virtual void addManifest(const char *filename) { code->addManifest(filename); }
  70. virtual void addManifestFromArchive(IPropertyTree *archive) { code->addManifestFromArchive(archive); }
  71. virtual void addWebServiceInfo(IPropertyTree *wsinfo){ code->addWebServiceInfo(wsinfo); }
  72. virtual double getECLcomplexity(IHqlExpression * exprs);
  73. virtual void setSaveGeneratedFiles(bool value) { deleteGenerated = !value; }
  74. protected:
  75. void addCppName(const char * filename);
  76. void addLibrariesToCompiler();
  77. void addWorkUnitAsResource();
  78. void calculateHash(IHqlExpression * expr);
  79. bool doCompile(ICppCompiler * compiler);
  80. void doExpand(HqlCppTranslator & translator);
  81. void expandCode(const char * templateName, const char * ext, IHqlCppInstance * code, bool multiFile, unsigned pass, CompilerType compiler);
  82. void flushResources();
  83. bool generateCode(IHqlExpression * exprs);
  84. void insertStandAloneCode();
  85. void setWuState(bool ok);
  86. inline bool abortRequested();
  87. protected:
  88. Linked<IErrorReceiver> errs;
  89. const char * wuname;
  90. StringAttr targetDir;
  91. Linked<IWorkUnit> wu;
  92. Owned<IHqlCppInstance> code;
  93. const char * template_dir;
  94. ClusterType targetClusterType;
  95. Linked<ICodegenContextCallback> ctxCallback;
  96. unsigned defaultMaxCompileThreads;
  97. StringArray sourceFiles;
  98. StringArray libraries;
  99. bool checkForLocalFileUploads;
  100. bool noOutput;
  101. EclGenerateTarget generateTarget;
  102. bool deleteGenerated;
  103. };
  104. //---------------------------------------------------------------------------
  105. static IHqlExpression * processMetaCommands(HqlCppTranslator & translator, IWorkUnit * wu, IHqlExpression * expr, ICodegenContextCallback *ctxCallback);
  106. void HqlDllGenerator::addCppName(const char * filename)
  107. {
  108. if (wu)
  109. {
  110. Owned<IWUQuery> query = wu->updateQuery();
  111. associateLocalFile(query, FileTypeCpp, filename, pathTail(filename), 0);
  112. ctxCallback->registerFile(filename, "Workunit CPP");
  113. }
  114. }
  115. void HqlDllGenerator::addLibrary(const char * name)
  116. {
  117. libraries.append(name);
  118. }
  119. void HqlDllGenerator::addLibrariesToCompiler()
  120. {
  121. unsigned idx=0;
  122. loop
  123. {
  124. const char * lib = code->queryLibrary(idx);
  125. if (!lib)
  126. break;
  127. PrintLog("Adding library: %s", lib);
  128. addLibrary(lib);
  129. idx++;
  130. }
  131. }
  132. void HqlDllGenerator::expandCode(const char * templateName, const char * ext, IHqlCppInstance * code, bool multiFile, unsigned pass, CompilerType compiler)
  133. {
  134. StringBuffer fullname;
  135. addDirectoryPrefix(fullname, targetDir).append(wuname).append(ext);
  136. Owned<IFile> out = createIFile(fullname.str());
  137. Owned<ITemplateExpander> expander = createTemplateExpander(out, templateName, template_dir);
  138. if (!expander)
  139. throwError2(HQLERR_CouldNotOpenTemplateXatY, templateName, template_dir ? template_dir : ".");
  140. Owned<ISectionWriter> writer = createCppWriter(*code, compiler);
  141. Owned<IProperties> props = createProperties(true);
  142. if (multiFile)
  143. {
  144. props->setProp("multiFile", true);
  145. props->setProp("pass", pass);
  146. }
  147. StringBuffer headerName;
  148. headerName.append(wuname).append(".hpp");
  149. props->setProp("headerName", headerName.str());
  150. props->setProp("outputName", fullname.str());
  151. expander->generate(*writer, pass, props);
  152. if (!deleteGenerated)
  153. addCppName(fullname);
  154. }
  155. //---------------------------------------------------------------------------
  156. bool HqlDllGenerator::processQuery(IHqlExpression * expr, EclGenerateTarget _generateTarget)
  157. {
  158. generateTarget = _generateTarget;
  159. assertex(wu->getHash());
  160. unsigned prevCount = errs->errCount();
  161. bool ok = generateCode(expr);
  162. code->flushHints();
  163. wu->commit();
  164. if (!ok)
  165. return false;
  166. if (errs->errCount() != prevCount)
  167. return false;
  168. switch (generateTarget)
  169. {
  170. case EclGenerateNone:
  171. case EclGenerateCpp:
  172. return true;
  173. }
  174. flushResources();
  175. addLibrariesToCompiler();
  176. // Free up memory before the c++ compile occurs
  177. code.clear();
  178. return true;
  179. }
  180. bool HqlDllGenerator::generateDll(ICppCompiler * compiler)
  181. {
  182. if (noOutput || !compiler)
  183. return true;
  184. bool ok = doCompile(compiler);
  185. setWuState(ok);
  186. return ok;
  187. }
  188. bool HqlDllGenerator::generateExe(ICppCompiler * compiler)
  189. {
  190. if (noOutput || !compiler)
  191. return true;
  192. compiler->setCreateExe(true);
  193. bool ok = doCompile(compiler);
  194. setWuState(ok);
  195. return ok;
  196. }
  197. bool HqlDllGenerator::generatePackage(const char * packageName)
  198. {
  199. return false;
  200. }
  201. bool HqlDllGenerator::generateCode(IHqlExpression * exprs)
  202. {
  203. wu->resetBeforeGeneration();
  204. noOutput = true;
  205. {
  206. // ensure warnings/errors are available before we do the processing...
  207. wu->commit();
  208. MTIME_SECTION (timer, "Generate_code");
  209. unsigned time = msTick();
  210. HqlCppTranslator translator(errs, wuname, code, targetClusterType, ctxCallback);
  211. OwnedHqlExpr query = processMetaCommands(translator, wu, exprs, ctxCallback);
  212. bool ok = false;
  213. try
  214. {
  215. if (!translator.buildCpp(*code, query))
  216. {
  217. wu->setState(WUStateCompleted);
  218. return true;
  219. }
  220. translator.generateStatistics(targetDir);
  221. translator.finalizeResources();
  222. translator.expandFunctions(true);
  223. }
  224. catch (IECLError * e)
  225. {
  226. StringBuffer s;
  227. errs->reportError(e->errorCode(), e->errorMessage(s).str(), e->getFilename(), e->getLine(), e->getColumn(), e->getPosition());
  228. e->Release();
  229. return false;
  230. }
  231. catch (IException * e)
  232. {
  233. if (e->errorCode() != HQLERR_ErrorAlreadyReported)
  234. {
  235. unsigned errcode = e->errorCode() ? e->errorCode() : 1;
  236. StringBuffer s;
  237. errs->reportError(errcode, e->errorMessage(s).str(), NULL, 0, 0, 1);
  238. }
  239. e->Release();
  240. return false;
  241. }
  242. catch (RELEASE_CATCH_ALL)
  243. {
  244. errs->reportError(99, "Unknown error", NULL, 0, 0, 1);
  245. return false;
  246. }
  247. if (generateTarget == EclGenerateExe)
  248. insertStandAloneCode();
  249. wu->commit();
  250. //Commit work unit so can view graphs etc. while compiling the C++
  251. if ((generateTarget == EclGenerateNone) || wu->getDebugValueBool("OnlyCheckQuery", false))
  252. {
  253. wu->setState(WUStateCompleted);
  254. return false;
  255. }
  256. doExpand(translator);
  257. if (wu->getDebugValueBool("addTimingToWorkunit", true))
  258. wu->setTimerInfo("EclServer: generate code", NULL, msTick()-time, 1, 0);
  259. wu->commit();
  260. addWorkUnitAsResource();
  261. }
  262. noOutput = false;
  263. return true;
  264. }
  265. void HqlDllGenerator::addWorkUnitAsResource()
  266. {
  267. SCMStringBuffer wuXML;
  268. exportWorkUnitToXML(wu, wuXML);
  269. code->addCompressResource("WORKUNIT", wuXML.length(), wuXML.str(), NULL, 1000);
  270. }
  271. void HqlDllGenerator::insertStandAloneCode()
  272. {
  273. BuildCtx ctx(static_cast<HqlCppInstance &>(*code), goAtom);
  274. ctx.addQuotedCompound("int main(int argc, const char *argv[])");
  275. ctx.addQuoted("return start_query(argc, argv);\n");
  276. }
  277. void HqlDllGenerator::doExpand(HqlCppTranslator & translator)
  278. {
  279. unsigned startExpandTime = msTick();
  280. unsigned numExtraFiles = translator.getNumExtraCppFiles();
  281. bool isMultiFile = translator.spanMultipleCppFiles() && (numExtraFiles != 0);
  282. expandCode(MAIN_MODULE_TEMPLATE, ".cpp", code, isMultiFile, 0, translator.queryOptions().targetCompiler);
  283. if (isMultiFile)
  284. {
  285. expandCode(HEADER_TEMPLATE, ".hpp", code, true, 0, translator.queryOptions().targetCompiler);
  286. for (unsigned i= 0; i < translator.getNumExtraCppFiles(); i++)
  287. {
  288. StringBuffer fullext;
  289. fullext.append("_").append(i+1).append(".cpp");
  290. expandCode(CHILD_MODULE_TEMPLATE, fullext, code, true, i+1, translator.queryOptions().targetCompiler);
  291. StringBuffer fullname;
  292. fullname.append(wuname).append("_").append(i+1);
  293. sourceFiles.append(fullname);
  294. }
  295. }
  296. unsigned endExpandTime = msTick();
  297. if (wu->getDebugValueBool("addTimingToWorkunit", true))
  298. wu->setTimerInfo("EclServer: write c++", NULL, endExpandTime-startExpandTime, 1, 0);
  299. }
  300. bool HqlDllGenerator::abortRequested()
  301. {
  302. return (wu && wu->aborting());
  303. }
  304. bool HqlDllGenerator::doCompile(ICppCompiler * compiler)
  305. {
  306. ForEachItemIn(i, sourceFiles)
  307. compiler->addSourceFile(sourceFiles.item(i));
  308. unsigned maxThreads = wu->getDebugValueInt("maxCompileThreads", defaultMaxCompileThreads);
  309. compiler->setMaxCompileThreads(maxThreads);
  310. bool debug = wu->getDebugValueBool("debugQuery", false);
  311. bool debugLibrary = debug; // should be wu->getDebugValueBool("debugLibrary", IS_DEBUG_BUILD); change for 3.8
  312. compiler->setDebug(debug);
  313. compiler->setDebugLibrary(debugLibrary);
  314. if (!debug)
  315. {
  316. int optimizeLevel = wu->getDebugValueInt("optimizeLevel", targetClusterType == RoxieCluster ? 3 : -1);
  317. if (optimizeLevel != -1)
  318. compiler->setOptimizeLevel(optimizeLevel);
  319. }
  320. #ifdef __64BIT__
  321. bool target64bit = wu->getDebugValueBool("target64bit", true);
  322. #else
  323. bool target64bit = wu->getDebugValueBool("target64bit", false);
  324. #endif
  325. compiler->setTargetBitLength(target64bit ? 64 : 32);
  326. ForEachItemIn(idx, libraries)
  327. compiler->addLibrary(libraries.item(idx));
  328. StringBuffer options;
  329. StringBufferAdaptor linkOptionAdaptor(options);
  330. wu->getDebugValue("linkOptions", linkOptionAdaptor);
  331. compiler->addLinkOption(options.str());
  332. options.clear();
  333. StringBufferAdaptor optionAdaptor(options);
  334. wu->getDebugValue("compileOptions", optionAdaptor);
  335. compiler->addCompileOption(options.str());
  336. compiler->setAbortChecker(this);
  337. MTIME_SECTION (timer, "Compile_code");
  338. unsigned time = msTick();
  339. PrintLog("Compiling %s", wuname);
  340. bool ok = compiler->compile();
  341. if(ok)
  342. PrintLog("Compiled %s", wuname);
  343. else
  344. PrintLog("Failed to compile %s", wuname);
  345. time = msTick()-time;
  346. if (wu->getDebugValueBool("addTimingToWorkunit", true))
  347. wu->setTimerInfo("EclServer: compile code", NULL, time, 1, 0);
  348. //Keep the files if there was a compile error.
  349. if (ok && deleteGenerated)
  350. {
  351. StringBuffer temp;
  352. remove(temp.clear().append(wuname).append(".cpp").str());
  353. remove(temp.clear().append(wuname).append(".hpp").str());
  354. ForEachItemIn(i, sourceFiles)
  355. {
  356. temp.clear().append(sourceFiles.item(i)).append(".cpp");
  357. remove(temp.str());
  358. }
  359. }
  360. return ok;
  361. }
  362. void HqlDllGenerator::flushResources()
  363. {
  364. StringBuffer resname(wuname);
  365. #ifdef _WIN32
  366. resname.append(".res");
  367. #else
  368. resname.append(".res.o");
  369. #endif
  370. if (code)
  371. code->flushResources(resname.str(), ctxCallback);
  372. }
  373. void HqlDllGenerator::setWuState(bool ok)
  374. {
  375. if(ok)
  376. {
  377. if(checkForLocalFileUploads && wu->requiresLocalFileUpload())
  378. wu->setState(WUStateUploadingFiles);
  379. else
  380. wu->setState(WUStateCompiled);
  381. }
  382. else
  383. wu->setState(WUStateFailed);
  384. }
  385. double HqlDllGenerator::getECLcomplexity(IHqlExpression * exprs)
  386. {
  387. Owned<IHqlCppInstance> code = createCppInstance(wu, NULL);
  388. HqlCppTranslator translator(errs, "temp", code, targetClusterType, NULL);
  389. OwnedHqlExpr query = processMetaCommands(translator, wu, exprs, ctxCallback);
  390. return translator.getComplexity(*code, query);
  391. }
  392. extern HQLCPP_API double getECLcomplexity(IHqlExpression * exprs, IErrorReceiver * errs, IWorkUnit *wu, ClusterType targetClusterType)
  393. {
  394. HqlDllGenerator generator(errs, "unknown", NULL, wu, NULL, targetClusterType, NULL, false);
  395. return generator.getECLcomplexity(exprs);
  396. }
  397. extern HQLCPP_API IHqlExprDllGenerator * createDllGenerator(IErrorReceiver * errs, const char *wuname, const char * targetdir, IWorkUnit *wu, const char * template_dir, ClusterType targetClusterType, ICodegenContextCallback *ctxCallback, bool checkForLocalFileUploads)
  398. {
  399. return new HqlDllGenerator(errs, wuname, targetdir, wu, template_dir, targetClusterType, ctxCallback, checkForLocalFileUploads);
  400. }
  401. /*
  402. extern HQLCPP_API void addLibraryReference(IWorkUnit * wu, IHqlLibraryInfo * library, const char * lid)
  403. {
  404. StringBuffer libraryName;
  405. library->getName(libraryName);
  406. Owned<IWULibrary> entry = wu->updateLibraryByName(libraryName.str());
  407. entry->setMajorVersion(library->queryMajorVersion());
  408. entry->setMinorVersion(library->queryMinorVersion());
  409. entry->setCrc(library->queryInterfaceCrc());
  410. if (lid && *lid)
  411. entry->setLID(lid);
  412. }
  413. */
  414. extern HQLCPP_API ClusterType queryClusterType(IConstWorkUnit * wu, ClusterType prevType)
  415. {
  416. ClusterType targetClusterType = prevType;
  417. if (wu->getDebugValueBool("forceRoxie", false))
  418. targetClusterType = RoxieCluster;
  419. else if (prevType == NoCluster)
  420. targetClusterType = ThorCluster;
  421. SCMStringBuffer targetText;
  422. wu->getDebugValue("targetClusterType", targetText);
  423. targetClusterType = getClusterType(targetText.s.str(), targetClusterType);
  424. if ((targetClusterType != RoxieCluster) && wu->getDebugValueBool("forceFakeThor", false))
  425. targetClusterType = HThorCluster;
  426. return targetClusterType;
  427. }
  428. static IHqlExpression * processMetaCommands(HqlCppTranslator & translator, IWorkUnit * wu, IHqlExpression * expr, ICodegenContextCallback *ctxCallback)
  429. {
  430. NewThorStoredReplacer transformer(translator, wu, ctxCallback);
  431. translator.traceExpression("before process meta commands", expr);
  432. transformer.analyse(expr);
  433. if (!transformer.needToTransform())
  434. return LINK(expr);
  435. return transformer.transform(expr);
  436. }
  437. extern HQLCPP_API unsigned getLibraryCRC(IHqlExpression * library)
  438. {
  439. assertex(library->getOperator() == no_funcdef);
  440. return getExpressionCRC(library->queryChild(0));
  441. }
  442. void setWorkunitHash(IWorkUnit * wu, IHqlExpression * expr)
  443. {
  444. //Assuming builds come from different branches this will change the crc for each one.
  445. unsigned cacheCRC = crc32(BUILD_TAG, strlen(BUILD_TAG), ACTIVITY_INTERFACE_VERSION);
  446. cacheCRC += getExpressionCRC(expr);
  447. #ifdef _WIN32
  448. cacheCRC++; // make sure CRC is different in windows/linux
  449. #endif
  450. #ifdef __64BIT__
  451. cacheCRC += 2; // make sure CRC is different for different host platform (shouldn't really matter if cross-compiling working properly, but fairly harmless)
  452. #endif
  453. IExtendedWUInterface *ewu = queryExtendedWU(wu);
  454. cacheCRC = ewu->calculateHash(cacheCRC);
  455. wu->setHash(cacheCRC);
  456. }