hqlecl.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  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 "build-config.h"
  14. #include "jliball.hpp"
  15. #include "jmisc.hpp"
  16. #include "jstream.hpp"
  17. #include "hql.hpp"
  18. #include "hqlexpr.hpp"
  19. #include "hqlecl.hpp"
  20. #include "hqlthql.hpp"
  21. #include "hqlcerrors.hpp"
  22. #include "hqlcatom.hpp"
  23. #include "hqllib.ipp"
  24. #include "hqlutil.hpp"
  25. #include "hqlhtcpp.ipp"
  26. #include "hqlwcpp.hpp"
  27. #include "hqllib.ipp"
  28. #include "hqlttcpp.ipp"
  29. #include "workunit.hpp"
  30. #include "thorplugin.hpp"
  31. #ifdef _DEBUG
  32. #define IS_DEBUG_BUILD true
  33. #else
  34. #define IS_DEBUG_BUILD false
  35. #endif
  36. #define MAIN_MODULE_TEMPLATE "thortpl.cpp"
  37. #define HEADER_TEMPLATE "thortpl.hpp"
  38. #define CHILD_MODULE_TEMPLATE "childtpl.cpp"
  39. #define PROTO_TEMPLATE "prototpl.cpp"
  40. class NullContextCallback : public CInterface, implements ICodegenContextCallback
  41. {
  42. IMPLEMENT_IINTERFACE
  43. virtual void noteCluster(const char *clusterName) {}
  44. virtual void registerFile(const char * filename, const char * description) {}
  45. virtual bool allowAccess(const char * category) { return true; }
  46. };
  47. class HqlDllGenerator : public CInterface, implements IHqlExprDllGenerator, implements IAbortRequestCallback
  48. {
  49. public:
  50. HqlDllGenerator(IErrorReceiver * _errs, const char * _wuname, const char * _targetdir, IWorkUnit * _wu, const char * _template_dir, ClusterType _targetClusterType, ICodegenContextCallback * _ctxCallback, bool _checkForLocalFileUploads) :
  51. errs(_errs), wuname(_wuname), targetDir(_targetdir), wu(_wu), template_dir(_template_dir), targetClusterType(_targetClusterType), ctxCallback(_ctxCallback), checkForLocalFileUploads(_checkForLocalFileUploads)
  52. {
  53. if (!ctxCallback)
  54. ctxCallback.setown(new NullContextCallback);
  55. noOutput = true;
  56. defaultMaxCompileThreads = 1;
  57. generateTarget = EclGenerateNone;
  58. code.setown(createCppInstance(wu, wuname));
  59. deleteGenerated = false;
  60. }
  61. IMPLEMENT_IINTERFACE
  62. virtual void addLibrary(const char * name);
  63. virtual bool processQuery(OwnedHqlExpr & parsedQuery, EclGenerateTarget _generateTarget);
  64. virtual bool generateDll(ICppCompiler * compiler);
  65. virtual bool generateExe(ICppCompiler * compiler);
  66. virtual bool generatePackage(const char * packageName);
  67. virtual void setMaxCompileThreads(unsigned value) { defaultMaxCompileThreads = value; }
  68. virtual void addManifest(const char *filename) { code->addManifest(filename); }
  69. virtual void addManifestFromArchive(IPropertyTree *archive) { code->addManifestFromArchive(archive); }
  70. virtual void addWebServiceInfo(IPropertyTree *wsinfo){ code->addWebServiceInfo(wsinfo); }
  71. virtual double getECLcomplexity(IHqlExpression * exprs);
  72. virtual void setSaveGeneratedFiles(bool value) { deleteGenerated = !value; }
  73. protected:
  74. void addCppName(const char * filename);
  75. void addLibrariesToCompiler();
  76. void addWorkUnitAsResource();
  77. void calculateHash(IHqlExpression * expr);
  78. bool doCompile(ICppCompiler * compiler);
  79. void doExpand(HqlCppTranslator & translator);
  80. void expandCode(const char * templateName, const char * ext, IHqlCppInstance * code, bool multiFile, unsigned pass, CompilerType compiler);
  81. void flushResources();
  82. bool generateCode(HqlQueryContext & query);
  83. void insertStandAloneCode();
  84. void setWuState(bool ok);
  85. inline bool abortRequested();
  86. protected:
  87. Linked<IErrorReceiver> errs;
  88. const char * wuname;
  89. StringAttr targetDir;
  90. Linked<IWorkUnit> wu;
  91. Owned<IHqlCppInstance> code;
  92. const char * template_dir;
  93. ClusterType targetClusterType;
  94. Linked<ICodegenContextCallback> ctxCallback;
  95. unsigned defaultMaxCompileThreads;
  96. StringArray sourceFiles;
  97. StringArray libraries;
  98. bool checkForLocalFileUploads;
  99. bool noOutput;
  100. EclGenerateTarget generateTarget;
  101. bool deleteGenerated;
  102. };
  103. //---------------------------------------------------------------------------
  104. static void processMetaCommands(HqlCppTranslator & translator, IWorkUnit * wu, HqlQueryContext & query, ICodegenContextCallback *ctxCallback);
  105. void HqlDllGenerator::addCppName(const char * filename)
  106. {
  107. if (wu)
  108. {
  109. Owned<IWUQuery> query = wu->updateQuery();
  110. associateLocalFile(query, FileTypeCpp, filename, pathTail(filename), 0);
  111. ctxCallback->registerFile(filename, "Workunit CPP");
  112. }
  113. }
  114. void HqlDllGenerator::addLibrary(const char * name)
  115. {
  116. libraries.append(name);
  117. }
  118. void HqlDllGenerator::addLibrariesToCompiler()
  119. {
  120. unsigned idx=0;
  121. loop
  122. {
  123. const char * lib = code->queryLibrary(idx);
  124. if (!lib)
  125. break;
  126. PrintLog("Adding library: %s", lib);
  127. addLibrary(lib);
  128. idx++;
  129. }
  130. }
  131. void HqlDllGenerator::expandCode(const char * templateName, const char * ext, IHqlCppInstance * code, bool multiFile, unsigned pass, CompilerType compiler)
  132. {
  133. StringBuffer fullname;
  134. addDirectoryPrefix(fullname, targetDir).append(wuname).append(ext);
  135. Owned<IFile> out = createIFile(fullname.str());
  136. Owned<ITemplateExpander> expander = createTemplateExpander(out, templateName, template_dir);
  137. if (!expander)
  138. throwError2(HQLERR_CouldNotOpenTemplateXatY, templateName, template_dir ? template_dir : ".");
  139. Owned<ISectionWriter> writer = createCppWriter(*code, compiler);
  140. Owned<IProperties> props = createProperties(true);
  141. if (multiFile)
  142. {
  143. props->setProp("multiFile", true);
  144. props->setProp("pass", pass);
  145. }
  146. StringBuffer headerName;
  147. headerName.append(wuname).append(".hpp");
  148. props->setProp("headerName", headerName.str());
  149. props->setProp("outputName", fullname.str());
  150. expander->generate(*writer, pass, props);
  151. if (!deleteGenerated)
  152. addCppName(fullname);
  153. }
  154. //---------------------------------------------------------------------------
  155. bool HqlDllGenerator::processQuery(OwnedHqlExpr & parsedQuery, EclGenerateTarget _generateTarget)
  156. {
  157. generateTarget = _generateTarget;
  158. assertex(wu->getHash());
  159. unsigned prevCount = errs->errCount();
  160. HqlQueryContext query;
  161. query.expr.setown(parsedQuery.getClear());
  162. bool ok = generateCode(query);
  163. code->flushHints();
  164. wu->commit();
  165. if (!ok)
  166. return false;
  167. if (errs->errCount() != prevCount)
  168. return false;
  169. switch (generateTarget)
  170. {
  171. case EclGenerateNone:
  172. case EclGenerateCpp:
  173. return true;
  174. }
  175. flushResources();
  176. addLibrariesToCompiler();
  177. // Free up memory before the c++ compile occurs
  178. code.clear();
  179. return true;
  180. }
  181. bool HqlDllGenerator::generateDll(ICppCompiler * compiler)
  182. {
  183. if (noOutput || !compiler)
  184. return true;
  185. bool ok = doCompile(compiler);
  186. setWuState(ok);
  187. return ok;
  188. }
  189. bool HqlDllGenerator::generateExe(ICppCompiler * compiler)
  190. {
  191. if (noOutput || !compiler)
  192. return true;
  193. compiler->setCreateExe(true);
  194. bool ok = doCompile(compiler);
  195. setWuState(ok);
  196. return ok;
  197. }
  198. bool HqlDllGenerator::generatePackage(const char * packageName)
  199. {
  200. return false;
  201. }
  202. bool HqlDllGenerator::generateCode(HqlQueryContext & query)
  203. {
  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. processMetaCommands(translator, wu, query, 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, false);
  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. HqlQueryContext query;
  390. query.expr.set(exprs);
  391. processMetaCommands(translator, wu, query, ctxCallback);
  392. return translator.getComplexity(*code, query.expr);
  393. }
  394. extern HQLCPP_API double getECLcomplexity(IHqlExpression * exprs, IErrorReceiver * errs, IWorkUnit *wu, ClusterType targetClusterType)
  395. {
  396. HqlDllGenerator generator(errs, "unknown", NULL, wu, NULL, targetClusterType, NULL, false);
  397. return generator.getECLcomplexity(exprs);
  398. }
  399. 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)
  400. {
  401. return new HqlDllGenerator(errs, wuname, targetdir, wu, template_dir, targetClusterType, ctxCallback, checkForLocalFileUploads);
  402. }
  403. /*
  404. extern HQLCPP_API void addLibraryReference(IWorkUnit * wu, IHqlLibraryInfo * library, const char * lid)
  405. {
  406. StringBuffer libraryName;
  407. library->getName(libraryName);
  408. Owned<IWULibrary> entry = wu->updateLibraryByName(libraryName.str());
  409. entry->setMajorVersion(library->queryMajorVersion());
  410. entry->setMinorVersion(library->queryMinorVersion());
  411. entry->setCrc(library->queryInterfaceCrc());
  412. if (lid && *lid)
  413. entry->setLID(lid);
  414. }
  415. */
  416. extern HQLCPP_API ClusterType queryClusterType(IConstWorkUnit * wu, ClusterType prevType)
  417. {
  418. ClusterType targetClusterType = prevType;
  419. if (wu->getDebugValueBool("forceRoxie", false))
  420. targetClusterType = RoxieCluster;
  421. else if (prevType == NoCluster)
  422. targetClusterType = ThorCluster;
  423. SCMStringBuffer targetText;
  424. wu->getDebugValue("targetClusterType", targetText);
  425. targetClusterType = getClusterType(targetText.s.str(), targetClusterType);
  426. if ((targetClusterType != RoxieCluster) && wu->getDebugValueBool("forceFakeThor", false))
  427. targetClusterType = HThorCluster;
  428. return targetClusterType;
  429. }
  430. static void processMetaCommands(HqlCppTranslator & translator, IWorkUnit * wu, HqlQueryContext & query, ICodegenContextCallback *ctxCallback)
  431. {
  432. NewThorStoredReplacer transformer(translator, wu, ctxCallback);
  433. translator.traceExpression("before process meta commands", query.expr);
  434. transformer.analyse(query.expr);
  435. if (transformer.needToTransform())
  436. query.expr.setown(transformer.transform(query.expr));
  437. }
  438. extern HQLCPP_API unsigned getLibraryCRC(IHqlExpression * library)
  439. {
  440. assertex(library->getOperator() == no_funcdef);
  441. return getExpressionCRC(library->queryChild(0));
  442. }
  443. void setWorkunitHash(IWorkUnit * wu, IHqlExpression * expr)
  444. {
  445. //Assuming builds come from different branches this will change the crc for each one.
  446. unsigned cacheCRC = crc32(BUILD_TAG, strlen(BUILD_TAG), ACTIVITY_INTERFACE_VERSION);
  447. cacheCRC += getExpressionCRC(expr);
  448. #ifdef _WIN32
  449. cacheCRC++; // make sure CRC is different in windows/linux
  450. #endif
  451. #ifdef __64BIT__
  452. cacheCRC += 2; // make sure CRC is different for different host platform (shouldn't really matter if cross-compiling working properly, but fairly harmless)
  453. #endif
  454. IExtendedWUInterface *ewu = queryExtendedWU(wu);
  455. cacheCRC = ewu->calculateHash(cacheCRC);
  456. wu->setHash(cacheCRC);
  457. }