hqlecl.cpp 18 KB

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