hqlecl.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  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.finalizeResources();
  221. translator.expandFunctions(true);
  222. }
  223. catch (IECLError * e)
  224. {
  225. StringBuffer s;
  226. errs->reportError(e->errorCode(), e->errorMessage(s).str(), e->getFilename(), e->getLine(), e->getColumn(), e->getPosition());
  227. e->Release();
  228. return false;
  229. }
  230. catch (IException * e)
  231. {
  232. if (e->errorCode() != HQLERR_ErrorAlreadyReported)
  233. {
  234. unsigned errcode = e->errorCode() ? e->errorCode() : 1;
  235. StringBuffer s;
  236. errs->reportError(errcode, e->errorMessage(s).str(), NULL, 0, 0, 1);
  237. }
  238. e->Release();
  239. return false;
  240. }
  241. catch (RELEASE_CATCH_ALL)
  242. {
  243. errs->reportError(99, "Unknown error", NULL, 0, 0, 1);
  244. return false;
  245. }
  246. if (generateTarget == EclGenerateExe)
  247. insertStandAloneCode();
  248. wu->commit();
  249. //Commit work unit so can view graphs etc. while compiling the C++
  250. if ((generateTarget == EclGenerateNone) || wu->getDebugValueBool("OnlyCheckQuery", false))
  251. {
  252. wu->setState(WUStateCompleted);
  253. return false;
  254. }
  255. doExpand(translator);
  256. if (wu->getDebugValueBool("addTimingToWorkunit", true))
  257. wu->setTimerInfo("EclServer: generate code", NULL, msTick()-time, 1, 0);
  258. wu->commit();
  259. addWorkUnitAsResource();
  260. }
  261. noOutput = false;
  262. return true;
  263. }
  264. void HqlDllGenerator::addWorkUnitAsResource()
  265. {
  266. SCMStringBuffer wuXML;
  267. exportWorkUnitToXML(wu, wuXML);
  268. code->addCompressResource("WORKUNIT", wuXML.length(), wuXML.str(), NULL, 1000);
  269. }
  270. void HqlDllGenerator::insertStandAloneCode()
  271. {
  272. BuildCtx ctx(static_cast<HqlCppInstance &>(*code), goAtom);
  273. ctx.addQuotedCompound("int main(int argc, const char *argv[])");
  274. ctx.addQuoted("return start_query(argc, argv);\n");
  275. }
  276. void HqlDllGenerator::doExpand(HqlCppTranslator & translator)
  277. {
  278. unsigned startExpandTime = msTick();
  279. unsigned numExtraFiles = translator.getNumExtraCppFiles();
  280. bool isMultiFile = translator.spanMultipleCppFiles() && (numExtraFiles != 0);
  281. expandCode(MAIN_MODULE_TEMPLATE, ".cpp", code, isMultiFile, 0, translator.queryOptions().targetCompiler);
  282. if (isMultiFile)
  283. {
  284. expandCode(HEADER_TEMPLATE, ".hpp", code, true, 0, translator.queryOptions().targetCompiler);
  285. for (unsigned i= 0; i < translator.getNumExtraCppFiles(); i++)
  286. {
  287. StringBuffer fullext;
  288. fullext.append("_").append(i+1).append(".cpp");
  289. expandCode(CHILD_MODULE_TEMPLATE, fullext, code, true, i+1, translator.queryOptions().targetCompiler);
  290. StringBuffer fullname;
  291. fullname.append(wuname).append("_").append(i+1);
  292. sourceFiles.append(fullname);
  293. }
  294. }
  295. unsigned endExpandTime = msTick();
  296. if (wu->getDebugValueBool("addTimingToWorkunit", true))
  297. wu->setTimerInfo("EclServer: write c++", NULL, endExpandTime-startExpandTime, 1, 0);
  298. }
  299. bool HqlDllGenerator::abortRequested()
  300. {
  301. return (wu && wu->aborting());
  302. }
  303. bool HqlDllGenerator::doCompile(ICppCompiler * compiler)
  304. {
  305. ForEachItemIn(i, sourceFiles)
  306. compiler->addSourceFile(sourceFiles.item(i));
  307. unsigned maxThreads = wu->getDebugValueInt("maxCompileThreads", defaultMaxCompileThreads);
  308. compiler->setMaxCompileThreads(maxThreads);
  309. bool debug = wu->getDebugValueBool("debugQuery", false);
  310. bool debugLibrary = debug; // should be wu->getDebugValueBool("debugLibrary", IS_DEBUG_BUILD); change for 3.8
  311. compiler->setDebug(debug);
  312. compiler->setDebugLibrary(debugLibrary);
  313. if (!debug)
  314. {
  315. int optimizeLevel = wu->getDebugValueInt("optimizeLevel", targetClusterType == RoxieCluster ? 3 : -1);
  316. if (optimizeLevel != -1)
  317. compiler->setOptimizeLevel(optimizeLevel);
  318. }
  319. #ifdef __64BIT__
  320. bool target64bit = wu->getDebugValueBool("target64bit", true);
  321. #else
  322. bool target64bit = wu->getDebugValueBool("target64bit", false);
  323. #endif
  324. compiler->setTargetBitLength(target64bit ? 64 : 32);
  325. ForEachItemIn(idx, libraries)
  326. compiler->addLibrary(libraries.item(idx));
  327. StringBuffer options;
  328. StringBufferAdaptor linkOptionAdaptor(options);
  329. wu->getDebugValue("linkOptions", linkOptionAdaptor);
  330. compiler->addLinkOption(options.str());
  331. options.clear();
  332. StringBufferAdaptor optionAdaptor(options);
  333. wu->getDebugValue("compileOptions", optionAdaptor);
  334. compiler->addCompileOption(options.str());
  335. compiler->setAbortChecker(this);
  336. MTIME_SECTION (timer, "Compile_code");
  337. unsigned time = msTick();
  338. PrintLog("Compiling %s", wuname);
  339. bool ok = compiler->compile();
  340. if(ok)
  341. PrintLog("Compiled %s", wuname);
  342. else
  343. PrintLog("Failed to compile %s", wuname);
  344. time = msTick()-time;
  345. if (wu->getDebugValueBool("addTimingToWorkunit", true))
  346. wu->setTimerInfo("EclServer: compile code", NULL, time, 1, 0);
  347. //Keep the files if there was a compile error.
  348. if (ok && deleteGenerated)
  349. {
  350. StringBuffer temp;
  351. remove(temp.clear().append(wuname).append(".cpp").str());
  352. remove(temp.clear().append(wuname).append(".hpp").str());
  353. ForEachItemIn(i, sourceFiles)
  354. {
  355. temp.clear().append(sourceFiles.item(i)).append(".cpp");
  356. remove(temp.str());
  357. }
  358. }
  359. return ok;
  360. }
  361. void HqlDllGenerator::flushResources()
  362. {
  363. StringBuffer resname(wuname);
  364. #ifdef _WIN32
  365. resname.append(".res");
  366. #else
  367. resname.append(".res.o");
  368. #endif
  369. if (code)
  370. code->flushResources(resname.str(), ctxCallback);
  371. }
  372. void HqlDllGenerator::setWuState(bool ok)
  373. {
  374. if(ok)
  375. {
  376. if(checkForLocalFileUploads && wu->requiresLocalFileUpload())
  377. wu->setState(WUStateUploadingFiles);
  378. else
  379. wu->setState(WUStateCompiled);
  380. }
  381. else
  382. wu->setState(WUStateFailed);
  383. }
  384. double HqlDllGenerator::getECLcomplexity(IHqlExpression * exprs)
  385. {
  386. Owned<IHqlCppInstance> code = createCppInstance(wu, NULL);
  387. HqlCppTranslator translator(errs, "temp", code, targetClusterType, NULL);
  388. OwnedHqlExpr query = processMetaCommands(translator, wu, exprs, ctxCallback);
  389. return translator.getComplexity(*code, query);
  390. }
  391. extern HQLCPP_API double getECLcomplexity(IHqlExpression * exprs, IErrorReceiver * errs, IWorkUnit *wu, ClusterType targetClusterType)
  392. {
  393. HqlDllGenerator generator(errs, "unknown", NULL, wu, NULL, targetClusterType, NULL, false);
  394. return generator.getECLcomplexity(exprs);
  395. }
  396. 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)
  397. {
  398. return new HqlDllGenerator(errs, wuname, targetdir, wu, template_dir, targetClusterType, ctxCallback, checkForLocalFileUploads);
  399. }
  400. /*
  401. extern HQLCPP_API void addLibraryReference(IWorkUnit * wu, IHqlLibraryInfo * library, const char * lid)
  402. {
  403. StringBuffer libraryName;
  404. library->getName(libraryName);
  405. Owned<IWULibrary> entry = wu->updateLibraryByName(libraryName.str());
  406. entry->setMajorVersion(library->queryMajorVersion());
  407. entry->setMinorVersion(library->queryMinorVersion());
  408. entry->setCrc(library->queryInterfaceCrc());
  409. if (lid && *lid)
  410. entry->setLID(lid);
  411. }
  412. */
  413. extern HQLCPP_API ClusterType queryClusterType(IConstWorkUnit * wu, ClusterType prevType)
  414. {
  415. ClusterType targetClusterType = prevType;
  416. if (wu->getDebugValueBool("forceRoxie", false))
  417. targetClusterType = RoxieCluster;
  418. else if (prevType == NoCluster)
  419. targetClusterType = ThorCluster;
  420. SCMStringBuffer targetText;
  421. wu->getDebugValue("targetClusterType", targetText);
  422. targetClusterType = getClusterType(targetText.s.str(), targetClusterType);
  423. if ((targetClusterType != RoxieCluster) && wu->getDebugValueBool("forceFakeThor", false))
  424. targetClusterType = HThorCluster;
  425. return targetClusterType;
  426. }
  427. static IHqlExpression * processMetaCommands(HqlCppTranslator & translator, IWorkUnit * wu, IHqlExpression * expr, ICodegenContextCallback *ctxCallback)
  428. {
  429. NewThorStoredReplacer transformer(translator, wu, ctxCallback);
  430. translator.traceExpression("before process meta commands", expr);
  431. transformer.analyse(expr);
  432. if (!transformer.needToTransform())
  433. return LINK(expr);
  434. return transformer.transform(expr);
  435. }
  436. extern HQLCPP_API unsigned getLibraryCRC(IHqlExpression * library)
  437. {
  438. assertex(library->getOperator() == no_funcdef);
  439. return getExpressionCRC(library->queryChild(0));
  440. }
  441. void setWorkunitHash(IWorkUnit * wu, IHqlExpression * expr)
  442. {
  443. //Assuming builds come from different branches this will change the crc for each one.
  444. unsigned cacheCRC = crc32(BUILD_TAG, strlen(BUILD_TAG), ACTIVITY_INTERFACE_VERSION);
  445. cacheCRC += getExpressionCRC(expr);
  446. #ifdef _WIN32
  447. cacheCRC++; // make sure CRC is different in windows/linux
  448. #endif
  449. #ifdef __64BIT__
  450. cacheCRC += 2; // make sure CRC is different for different host platform (shouldn't really matter if cross-compiling working properly, but fairly harmless)
  451. #endif
  452. IExtendedWUInterface *ewu = queryExtendedWU(wu);
  453. cacheCRC = ewu->calculateHash(cacheCRC);
  454. wu->setHash(cacheCRC);
  455. }