eclccserver.cpp 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352
  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 "jlib.hpp"
  14. #include "jmisc.hpp"
  15. #include "jisem.hpp"
  16. #include "jfile.hpp"
  17. #include "jencrypt.hpp"
  18. #include "jregexp.hpp"
  19. #include "jcomp.hpp"
  20. #include "mpbase.hpp"
  21. #include "daclient.hpp"
  22. #include "dasess.hpp"
  23. #include "danqs.hpp"
  24. #include "workunit.hpp"
  25. #include "wujobq.hpp"
  26. #include "dllserver.hpp"
  27. #include "thorplugin.hpp"
  28. #include "daqueue.hpp"
  29. #ifndef _CONTAINERIZED
  30. #include "dalienv.hpp"
  31. #endif
  32. #include <unordered_map>
  33. #include <string>
  34. #include "codesigner.hpp"
  35. static const char * * globalArgv = nullptr;
  36. //------------------------------------------------------------------------------------------------------------------
  37. // We use a separate thread for reading eclcc's stderr output. This prevents the thread that is
  38. // writing to its stdin from being blocked because eclcc is trying to write to stderr...
  39. //------------------------------------------------------------------------------------------------------------------
  40. interface IErrorReporter
  41. {
  42. virtual void reportError(IException *e) = 0;
  43. virtual void reportError(const char *errStr, unsigned retcode) = 0;
  44. };
  45. class ErrorReader : public Thread
  46. {
  47. public:
  48. ErrorReader(IPipeProcess *_pipe, IErrorReporter *_errorReporter)
  49. : Thread("EclccCompileThread::ErrorReader"), pipe(_pipe), errorReporter(_errorReporter), errors(0)
  50. {
  51. }
  52. virtual int run()
  53. {
  54. MemoryAttr buf;
  55. const size32_t incSize = 512;
  56. size32_t bufferSize = 0;
  57. char * buffer = NULL;
  58. size_t remaining = 0;
  59. bool eof = false;
  60. while (!eof)
  61. {
  62. if (remaining == bufferSize)
  63. {
  64. bufferSize += incSize;
  65. buffer = (char *) buf.reallocate(bufferSize);
  66. }
  67. size32_t read = pipe->readError(bufferSize-remaining, buffer+remaining);
  68. if ((read == 0) || (read == (size32_t)-1))
  69. eof = true;
  70. else
  71. remaining += read;
  72. char *finger = buffer;
  73. while (remaining)
  74. {
  75. char *eolpos = (char *) memchr(finger, '\n', remaining);
  76. if (eolpos)
  77. {
  78. *eolpos = '\0';
  79. if (eolpos > finger && eolpos[-1]=='\r')
  80. eolpos[-1] = '\0';
  81. if (errorReporter)
  82. errorReporter->reportError(finger, 0);
  83. else
  84. DBGLOG("%s", finger);
  85. errors++;
  86. remaining -= (eolpos-finger) + 1;
  87. finger = eolpos + 1;
  88. }
  89. else if (eof)
  90. {
  91. StringBuffer e(remaining, finger);
  92. if (errorReporter)
  93. errorReporter->reportError(e, 0);
  94. else
  95. DBGLOG("%s", e.str());
  96. errors++;
  97. break;
  98. }
  99. else
  100. break;
  101. }
  102. if (!eof && (finger != buffer))
  103. memmove(buffer, finger, remaining);
  104. }
  105. return 0;
  106. }
  107. unsigned errCount() const
  108. {
  109. return errors;
  110. }
  111. private:
  112. IPipeProcess *pipe;
  113. IErrorReporter *errorReporter;
  114. unsigned errors;
  115. };
  116. //------------------------------------------------------------------------------------------------------------------
  117. // Check for aborts of the workunit as it is compiling
  118. //------------------------------------------------------------------------------------------------------------------
  119. static bool useChildProcesses = false; // Use k8s jobs for compile tasks
  120. static unsigned childProcessTimeLimit = 0; // If using k8s jobs to compile, try a child process first but abort if it takes longer than this time (seconds)
  121. class AbortWaiter : public Thread
  122. {
  123. public:
  124. AbortWaiter(IConstWorkUnit *_wu, unsigned _timeLimit)
  125. : Thread("EclccCompileThread::AbortWaiter"), wu(_wu), timeLimit(_timeLimit)
  126. {
  127. }
  128. virtual int run()
  129. {
  130. wu->subscribe(SubscribeOptionAbort);
  131. unsigned start = msTick();
  132. timedOut = false;
  133. try
  134. {
  135. for (;;)
  136. {
  137. if (sem.wait(1000))
  138. break;
  139. timedOut = (timeLimit && (msTick() - start >= timeLimit*1000));
  140. if (timedOut || wu->aborting())
  141. {
  142. CriticalBlock b(crit);
  143. DBGLOG("Aborting compilation");
  144. ForEachItemIn(idx, pipes)
  145. {
  146. IPipeProcess *pipe = pipes.item(idx);
  147. pipe->abort();
  148. }
  149. break;
  150. }
  151. }
  152. }
  153. catch (IException *E)
  154. {
  155. ::Release(E);
  156. }
  157. return 0;
  158. }
  159. bool stop()
  160. {
  161. sem.interrupt(NULL);
  162. join();
  163. return timedOut;
  164. }
  165. void addPipe(IPipeProcess *pipe)
  166. {
  167. CriticalBlock b(crit);
  168. pipes.append(LINK(pipe));
  169. if (timedOut)
  170. pipe->abort();
  171. }
  172. void removePipe(IPipeProcess *pipe)
  173. {
  174. CriticalBlock b(crit);
  175. pipes.zap(pipe);
  176. }
  177. private:
  178. CriticalSection crit;
  179. unsigned timeLimit = 0;
  180. bool timedOut = false;
  181. IPointerArrayOf<IPipeProcess> pipes;
  182. IConstWorkUnit *wu;
  183. InterruptableSemaphore sem;
  184. };
  185. class AbortPipeWaiter
  186. {
  187. AbortWaiter &waiter;
  188. IPipeProcess *pipe;
  189. public:
  190. AbortPipeWaiter(AbortWaiter &_waiter, IPipeProcess *_pipe) : waiter(_waiter), pipe(_pipe)
  191. {
  192. waiter.addPipe(pipe);
  193. }
  194. ~AbortPipeWaiter()
  195. {
  196. waiter.removePipe(pipe);
  197. }
  198. };
  199. //------------------------------------------------------------------------------------------------------------------
  200. // Class EclccCompileThread does the work of compiling workunits (using eclcc), and optionally then enqueueing them for execution by agentexec.
  201. // A threadpool is used to allow multiple compiles to be submitted at once. Threads are reused when compilation completes.
  202. //------------------------------------------------------------------------------------------------------------------
  203. class EclccCompileThread : implements IPooledThread, implements IErrorReporter, public CInterface
  204. {
  205. StringAttr wuid;
  206. Owned<IWorkUnit> workunit;
  207. StringBuffer idxStr;
  208. StringArray filesSeen;
  209. unsigned defaultMaxCompileThreads = 1;
  210. bool saveTemps = false;
  211. virtual void reportError(IException *e)
  212. {
  213. StringBuffer s;
  214. reportError(e->errorMessage(s).str(), 2);
  215. }
  216. virtual void reportError(const char *errStr, unsigned retcode)
  217. {
  218. RegExpr errParse, timings, summaryParse;
  219. timings.init("^<stat");
  220. errParse.init("^<exception");
  221. summaryParse.init("^<summary");
  222. try
  223. {
  224. if (timings.find(errStr))
  225. {
  226. OwnedPTree timing = createPTreeFromXMLString(errStr, ipt_fast);
  227. assertex (timing);
  228. unsigned __int64 nval = timing->getPropInt64("@value");
  229. unsigned __int64 nmax = timing->getPropInt64("@max");
  230. unsigned __int64 cnt = timing->getPropInt64("@count");
  231. const char * scope = timing->queryProp("@scope");
  232. StatisticScopeType scopeType = (StatisticScopeType)timing->getPropInt("@scopeType");
  233. StatisticKind kind = queryStatisticKind(timing->queryProp("@kind"), StKindNone);
  234. workunit->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), scopeType, scope, kind, NULL, nval, cnt, nmax, StatsMergeReplace);
  235. }
  236. else if (errParse.find(errStr))
  237. {
  238. OwnedPTree exception = createPTreeFromXMLString(errStr, ipt_fast);
  239. assertex(exception);
  240. Owned<IError> error = createError(exception);
  241. addWorkunitException(workunit, error, false);
  242. const char *filename = exception->queryProp("@filename");
  243. StringArray filesSeen;
  244. if (filename && filesSeen.appendUniq(filename) && endsWithIgnoreCase(filename, ".cpp") && checkFileExists(filename))
  245. {
  246. Owned<IWUQuery> query = workunit->updateQuery();
  247. associateLocalFile(query, FileTypeCpp, filename, pathTail(filename), 0, 0, 0);
  248. }
  249. }
  250. else if (!summaryParse.find(errStr))
  251. IERRLOG("%s", errStr);
  252. }
  253. catch (IException *E)
  254. {
  255. EXCLOG(E, "Error parsing compiler output");
  256. }
  257. }
  258. void processOption(const char *option, const char *value, StringBuffer &eclccCmd, StringBuffer &eclccProgName, IPipeProcess &pipe, bool isLocal)
  259. {
  260. if (memicmp(option, "eclcc-", 6) == 0 || *option=='-')
  261. {
  262. //Allow eclcc-xx-<n> so that multiple values can be passed through for the same named debug symbol
  263. const char * start = option + (*option=='-' ? 1 : 6);
  264. const char * finger = (*start=='-') ? start+1 : start; //support leading double dash
  265. const char * dash = strrchr(finger, '-'); // position of trailing dash, if present
  266. StringAttr optName;
  267. if (dash && (dash != start) && isdigit(dash[1]))
  268. optName.set(start, dash-start);
  269. else
  270. optName.set(start);
  271. if (!optName)
  272. return;
  273. if (stricmp(optName, "hook") == 0)
  274. {
  275. if (isLocal)
  276. throw MakeStringException(0, "eclcc-hook option can not be set per-workunit"); // for security reasons
  277. eclccProgName.set(value);
  278. }
  279. else if (stricmp(optName, "compileOption") == 0)
  280. eclccCmd.appendf(" -Wc,%s", value);
  281. else if (stricmp(optName, "linkOption") == 0)
  282. eclccCmd.appendf(" -Wl,%s", value);
  283. else if (stricmp(optName, "includeLibraryPath") == 0)
  284. eclccCmd.appendf(" -I%s", value);
  285. else if (stricmp(optName, "libraryPath") == 0)
  286. eclccCmd.appendf(" -L%s", value);
  287. else if (strnicmp(optName, "-allow", 6)==0)
  288. {
  289. if (isLocal)
  290. throw MakeStringException(0, "eclcc-allow option can not be set per-workunit"); // for security reasons
  291. eclccCmd.appendf(" -%s=%s", optName.get(), value);
  292. }
  293. else if (*optName == 'd')
  294. {
  295. //Short term work around for the problem that all debug names get lower-cased
  296. eclccCmd.appendf(" -D%s=%s", optName.get()+1, value);
  297. }
  298. else
  299. eclccCmd.appendf(" -%s=%s", optName.get(), value);
  300. }
  301. else if (strchr(option, '-'))
  302. {
  303. StringBuffer envVar;
  304. if (isLocal)
  305. envVar.append("WU_");
  306. envVar.append(option);
  307. envVar.toUpperCase();
  308. envVar.replace('-','_');
  309. pipe.setenv(envVar, value);
  310. }
  311. else
  312. {
  313. if (strieq(option, "maxCompileThreads"))
  314. defaultMaxCompileThreads = atoi(value);
  315. else if (strieq(option, "saveEclTempFiles"))
  316. saveTemps = strToBool(value);
  317. eclccCmd.appendf(" -f%s=%s", option, value);
  318. }
  319. }
  320. unsigned doRunCompileCommand(AbortWaiter &abortWaiter, StringBuffer &output, const char *cmd)
  321. {
  322. try
  323. {
  324. Owned<IPipeProcess> pipe = createPipeProcess();
  325. AbortPipeWaiter aborter(abortWaiter, pipe);
  326. int ret = START_FAILURE;
  327. if (pipe->run(nullptr, cmd, ".", false, true, true, 1024*1024))
  328. {
  329. char buf[1024];
  330. while (true)
  331. {
  332. size32_t read = pipe->read(sizeof(buf), buf);
  333. if (!read)
  334. break;
  335. output.append(read, buf);
  336. }
  337. ret = pipe->wait();
  338. while (true)
  339. {
  340. size32_t read = pipe->readError(sizeof(buf), buf);
  341. if (!read)
  342. break;
  343. output.append(read, buf);
  344. }
  345. }
  346. return ret;
  347. }
  348. catch (IException *E)
  349. {
  350. E->Release();
  351. output.clear();
  352. return START_FAILURE;
  353. }
  354. }
  355. unsigned executeLine(AbortWaiter &abortWaiter, const char *line, StringBuffer &output, bool alreadyFailed)
  356. {
  357. if (isEmptyString(line))
  358. return 0;
  359. if (line[0] == '#')
  360. {
  361. return 0;
  362. }
  363. else if (startsWith(line, "rmdir "))
  364. {
  365. DBGLOG("Removing directory %s", line+6);
  366. Owned<IFile> tempDir = createIFile(line+6);
  367. if (tempDir)
  368. recursiveRemoveDirectory(tempDir);
  369. return 0;
  370. }
  371. else if (startsWith(line , "rm "))
  372. {
  373. DBGLOG("Removing file %s", line+3);
  374. remove(line + 3);
  375. return 0;
  376. }
  377. else if (!alreadyFailed)
  378. {
  379. DBGLOG("Executing %s", line);
  380. unsigned retcode = doRunCompileCommand(abortWaiter, output, line);
  381. if (retcode)
  382. DBGLOG("Error: retcode=%u executing %s", retcode, line);
  383. return retcode;
  384. }
  385. return 0;
  386. }
  387. unsigned doCompileCpp(AbortWaiter &abortWaiter, const char *wuid, unsigned maxThreads)
  388. {
  389. RelaxedAtomic<unsigned> numFailed = { 0 };
  390. if (!maxThreads)
  391. maxThreads = 1;
  392. VStringBuffer ccfileName("%s.cc", wuid);
  393. VStringBuffer cclogfileName("%s.cc.log", wuid);
  394. char dir[_MAX_PATH];
  395. if (!GetCurrentDirectory(sizeof(dir), dir))
  396. strcpy(dir, "unknown");
  397. DBGLOG("Compiling generated file list from %s, current directory %s", ccfileName.str(), dir);
  398. StringBuffer ccfileContents;
  399. ccfileContents.loadFile(ccfileName, false);
  400. StringArray lines;
  401. lines.appendList(ccfileContents, "\n", false);
  402. // We expect #compile, then a bunch of compiles that we may do in parallel, followed by #link and/or #cleanup, after which we go back to sequential again
  403. if (!lines.length())
  404. {
  405. DBGLOG("Invalid compiler batch file %s - 0 lines found", ccfileName.str());
  406. return 1;
  407. }
  408. unsigned lineIdx = 0;
  409. StringBuffer output;
  410. if (streq(lines.item(lineIdx), "#compile"))
  411. {
  412. lineIdx++;
  413. unsigned firstCompile = lineIdx;
  414. while (lines.isItem(lineIdx) && lines.item(lineIdx)[0] != '#')
  415. lineIdx++;
  416. CriticalSection crit;
  417. DBGLOG("Compiling %u files, %u at once", lineIdx-firstCompile, maxThreads);
  418. asyncFor(lineIdx-firstCompile, maxThreads, [this, firstCompile, &lines, &numFailed, &crit, &output, &abortWaiter](unsigned i)
  419. {
  420. try
  421. {
  422. StringBuffer loutput;
  423. if (executeLine(abortWaiter, lines.item(i+firstCompile), loutput, (numFailed != 0)))
  424. numFailed++;
  425. CriticalBlock b(crit);
  426. output.append(loutput);
  427. }
  428. catch (...)
  429. {
  430. numFailed++;
  431. throw;
  432. }
  433. });
  434. }
  435. while (lines.isItem(lineIdx))
  436. {
  437. const char *line = lines.item(lineIdx);
  438. unsigned retcode = executeLine(abortWaiter, line, output, (numFailed != 0));
  439. if (retcode)
  440. numFailed++;
  441. lineIdx++;
  442. }
  443. if (!saveTemps)
  444. removeFileTraceIfFail(ccfileName);
  445. Owned <IFile> dstfile = createIFile(cclogfileName);
  446. dstfile->remove();
  447. if (output.length())
  448. {
  449. Owned<IFileIO> dstIO = dstfile->open(IFOwrite);
  450. dstIO->write(0, output.length(), output.str());
  451. IArrayOf<IError> errors;
  452. extractErrorsFromCppLog(errors, output.str(), numFailed != 0);
  453. ForEachItemIn(i, errors)
  454. addWorkunitException(workunit, &errors.item(i), false);
  455. VStringBuffer failText("Compile/Link failed for %s (see %s for details)",wuid,cclogfileName.str());
  456. Owned<IWUException> msg = workunit->createException();
  457. msg->setExceptionSource("eclccserver");
  458. msg->setExceptionCode(3000);
  459. msg->setExceptionMessage(failText);
  460. msg->setSeverity(SeverityError);
  461. }
  462. return numFailed;
  463. }
  464. #ifdef _CONTAINERIZED
  465. void removeGeneratedFiles(const char *wuid)
  466. {
  467. // Remove the files we generated into /tmp - any we want to retain will have been moved to dllserver dir when registered with workunit
  468. VStringBuffer temp("*%s.*", wuid);
  469. Owned<IDirectoryIterator> tempfiles = createDirectoryIterator(".", temp.str());
  470. ForEach(*tempfiles)
  471. {
  472. removeFileTraceIfFail(tempfiles->getName(temp.clear()).str());
  473. }
  474. }
  475. #endif
  476. bool compile(const char *wuid, const char *target, const char *targetCluster, bool &timedOut)
  477. {
  478. timedOut = false;
  479. Owned<IConstWUQuery> query = workunit->getQuery();
  480. if (!query)
  481. {
  482. reportError("Workunit does not contain a query", 2);
  483. return false;
  484. }
  485. addTimeStamp(workunit, SSTglobal, NULL, StWhenCompiled);
  486. SCMStringBuffer mainDefinition;
  487. SCMStringBuffer eclQuery;
  488. query->getQueryText(eclQuery);
  489. query->getQueryMainDefinition(mainDefinition);
  490. bool syntaxCheck = (workunit->getAction()==WUActionCheck);
  491. if (syntaxCheck && (mainDefinition.length() == 0))
  492. {
  493. SCMStringBuffer syntaxCheckAttr;
  494. workunit->getApplicationValue("SyntaxCheck", "AttributeName", syntaxCheckAttr);
  495. syntaxCheckAttr.s.trim();
  496. if (syntaxCheckAttr.length())
  497. {
  498. workunit->getApplicationValue("SyntaxCheck", "ModuleName", mainDefinition);
  499. mainDefinition.s.trim();
  500. if (mainDefinition.length())
  501. mainDefinition.s.append('.');
  502. mainDefinition.s.append(syntaxCheckAttr.str());
  503. }
  504. }
  505. StringBuffer eclccProgName;
  506. splitDirTail(queryCurrentProcessPath(), eclccProgName);
  507. eclccProgName.append("eclcc");
  508. StringBuffer eclccCmd(" --xml -shared");
  509. //Clone all the options that were passed to eclccserver (but not the filename) and also pass them to eclcc
  510. for (const char * * pArg = globalArgv+1; *pArg; pArg++)
  511. eclccCmd.append(' ').append(*pArg);
  512. if (eclQuery.length())
  513. eclccCmd.append(" -");
  514. if (mainDefinition.length())
  515. eclccCmd.append(" -main \"").append(mainDefinition).append("\"");
  516. eclccCmd.append(" --timings");
  517. eclccCmd.append(" --nostdinc");
  518. eclccCmd.append(" --metacache=");
  519. #ifdef _CONTAINERIZED
  520. /* stderr is reserved for actual errors, and is consumed by this (parent) process
  521. * stdout is unused and will be captured by container logging mechanism */
  522. eclccCmd.append(" --logtostdout");
  523. #else
  524. VStringBuffer logfile("%s.eclcc.log", workunit->queryWuid());
  525. eclccCmd.appendf(" --logfile=%s", logfile.str());
  526. #endif
  527. if (syntaxCheck)
  528. eclccCmd.appendf(" -syntax");
  529. Owned<IPropertyTree> config = getComponentConfig();
  530. if (config->getPropBool("@enableEclccDali", true))
  531. {
  532. const char *daliServers = config->queryProp("@daliServers");
  533. if (!daliServers)
  534. daliServers = ".";
  535. eclccCmd.appendf(" -dfs=%s", daliServers);
  536. const char *wuScope = workunit->queryWuScope();
  537. if (!isEmptyString(wuScope))
  538. eclccCmd.appendf(" -scope=%s", wuScope);
  539. eclccCmd.appendf(" -cluster=%s", targetCluster);
  540. SCMStringBuffer token;
  541. workunit->getWorkunitDistributedAccessToken(token);
  542. if (token.length())
  543. eclccCmd.appendf(" -wuid=%s -token=%s", workunit->queryWuid(), token.str());
  544. }
  545. Owned<IPipeProcess> pipe = createPipeProcess();
  546. pipe->setenv("ECLCCSERVER_THREAD_INDEX", idxStr.str());
  547. Owned<IPropertyTreeIterator> options = config->getElements(isContainerized() ? "./options" : "./Option");
  548. ForEach(*options)
  549. {
  550. IPropertyTree &option = options->query();
  551. const char *name = option.queryProp("@name");
  552. const char *value = option.queryProp("@value");
  553. const char *cluster = option.queryProp("@cluster"); // if cluster is set it's specific to a particular target
  554. if (name && (cluster==NULL || cluster[0]==0 || strcmp(cluster, targetCluster)==0))
  555. processOption(name, value, eclccCmd, eclccProgName, *pipe, false);
  556. }
  557. eclccCmd.appendf(" -o%s", wuid);
  558. eclccCmd.appendf(" -platform=%s", target);
  559. eclccCmd.appendf(" --component=%s", queryStatisticsComponentName());
  560. eclccCmd.appendf(" --fetchrepos=1 --updaterepos=1"); // Default these options on in eclccserver (can be overridden in debug options)
  561. Owned<IStringIterator> debugValues = &workunit->getDebugValues();
  562. ForEach (*debugValues)
  563. {
  564. SCMStringBuffer debugStr, valueStr;
  565. debugValues->str(debugStr);
  566. workunit->getDebugValue(debugStr.str(), valueStr);
  567. processOption(debugStr.str(), valueStr.str(), eclccCmd, eclccProgName, *pipe, true);
  568. }
  569. bool compileCppSeparately = config->getPropBool("@compileCppSeparately", true);
  570. if (compileCppSeparately)
  571. {
  572. workunit->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), SSTcompilestage, "compile", StWhenStarted, NULL, getTimeStampNowValue(), 1, 0, StatsMergeAppend);
  573. eclccCmd.appendf(" -Sx %s.cc", wuid);
  574. }
  575. if (workunit->getResultLimit())
  576. {
  577. eclccCmd.appendf(" -fapplyInstantEclTransformations=1 -fapplyInstantEclTransformationsLimit=%u", workunit->getResultLimit());
  578. }
  579. try
  580. {
  581. Owned<ErrorReader> errorReader = new ErrorReader(pipe, this);
  582. AbortWaiter abortWaiter(workunit, childProcessTimeLimit);
  583. AbortPipeWaiter aborter(abortWaiter, pipe);
  584. eclccCmd.insert(0, eclccProgName);
  585. cycle_t startCompile = get_cycles_now();
  586. if (!pipe->run(eclccProgName, eclccCmd, ".", true, false, true, 0, true))
  587. throw makeStringExceptionV(999, "Failed to run eclcc command %s", eclccCmd.str());
  588. errorReader->start();
  589. abortWaiter.start();
  590. try
  591. {
  592. pipe->write(eclQuery.s.length(), eclQuery.s.str());
  593. pipe->closeInput();
  594. }
  595. catch (IException *e)
  596. {
  597. reportError(e);
  598. e->Release();
  599. }
  600. unsigned retcode = pipe->wait();
  601. errorReader->join();
  602. if (retcode == 0 && compileCppSeparately)
  603. {
  604. cycle_t startCompileCpp = get_cycles_now();
  605. workunit->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), SSTcompilestage, "compile:compile c++", StWhenStarted, NULL, getTimeStampNowValue(), 1, 0, StatsMergeAppend);
  606. retcode = doCompileCpp(abortWaiter, wuid, workunit->getDebugValueInt("maxCompileThreads", defaultMaxCompileThreads));
  607. unsigned __int64 elapsed_compilecpp = cycle_to_nanosec(get_cycles_now() - startCompileCpp);
  608. workunit->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), SSTcompilestage, "compile:compile c++", StTimeElapsed, NULL, elapsed_compilecpp, 1, 0, StatsMergeReplace);
  609. }
  610. if (compileCppSeparately)
  611. {
  612. unsigned __int64 elapsed_compile = cycle_to_nanosec(get_cycles_now() - startCompile);
  613. workunit->setStatistic(queryStatisticsComponentType(), queryStatisticsComponentName(), SSTcompilestage, "compile", StTimeElapsed, NULL, elapsed_compile, 1, 0, StatsMergeReplace);
  614. }
  615. timedOut = abortWaiter.stop();
  616. if (!timedOut)
  617. {
  618. if (retcode == 0 && !timedOut)
  619. {
  620. StringBuffer realdllname, dllurl;
  621. realdllname.append(SharedObjectPrefix).append(wuid).append(SharedObjectExtension);
  622. StringBuffer realdllfilename;
  623. realdllfilename.append(SharedObjectPrefix).append(wuid).append(SharedObjectExtension);
  624. StringBuffer wuXML;
  625. if (!getWorkunitXMLFromFile(realdllfilename, wuXML))
  626. throw makeStringException(999, "Failed to extract workunit from query dll");
  627. Owned<ILocalWorkUnit> embeddedWU = createLocalWorkUnit(wuXML);
  628. queryExtendedWU(workunit)->copyWorkUnit(embeddedWU, false, true);
  629. workunit->setIsClone(false);
  630. const char *jobname = embeddedWU->queryJobName();
  631. if (jobname && *jobname) //let ECL win naming job during initial compile
  632. workunit->setJobName(jobname);
  633. if (!workunit->getDebugValueBool("obfuscateOutput", false))
  634. {
  635. Owned<IWUQuery> query = workunit->updateQuery();
  636. if (getArchiveXMLFromFile(realdllfilename, wuXML.clear())) // MORE - if what was submitted was an archive, this is probably pointless?
  637. query->setQueryText(wuXML.str());
  638. else
  639. query->setQueryText(eclQuery.s.str());
  640. }
  641. createUNCFilename(realdllfilename.str(), dllurl);
  642. unsigned crc = crc_file(realdllfilename.str());
  643. Owned<IWUQuery> query = workunit->updateQuery();
  644. #ifndef _CONTAINERIZED
  645. associateLocalFile(query, FileTypeLog, logfile, "Compiler log", 0);
  646. #endif
  647. associateLocalFile(query, FileTypeDll, realdllfilename, "Workunit DLL", crc);
  648. #ifdef _CONTAINERIZED
  649. removeGeneratedFiles(wuid);
  650. #endif
  651. queryDllServer().registerDll(realdllname.str(), "Workunit DLL", dllurl.str());
  652. }
  653. else
  654. {
  655. #ifndef _CONTAINERIZED
  656. Owned<IWUQuery> query = workunit->updateQuery();
  657. associateLocalFile(query, FileTypeLog, logfile, "Compiler log", 0);
  658. #endif
  659. }
  660. if (compileCppSeparately)
  661. {
  662. //Files need to be added after the workunit has been cloned - otherwise they are overwritten
  663. Owned<IWUQuery> query = workunit->updateQuery();
  664. VStringBuffer ccfileName("%s.cc", wuid);
  665. VStringBuffer cclogfileName("%s.cc.log", wuid);
  666. if (checkFileExists(cclogfileName))
  667. associateLocalFile(query, FileTypeLog, cclogfileName, "CPP log", 0);
  668. if (saveTemps && checkFileExists(ccfileName))
  669. associateLocalFile(query, FileTypeLog, ccfileName, "compile actions log", 0);
  670. }
  671. }
  672. workunit->commit();
  673. return (retcode == 0);
  674. }
  675. catch (IException * e)
  676. {
  677. reportError(e);
  678. e->Release();
  679. }
  680. workunit->commit();
  681. return false;
  682. }
  683. void failCompilation(const char *error)
  684. {
  685. reportError(error, 2);
  686. workunit->setState(WUStateFailed);
  687. workunit->commit();
  688. workunit.clear();
  689. }
  690. public:
  691. IMPLEMENT_IINTERFACE;
  692. EclccCompileThread(unsigned _idx)
  693. {
  694. idxStr.append(_idx);
  695. }
  696. virtual void init(void *param) override
  697. {
  698. wuid.set((const char *) param);
  699. }
  700. #ifdef _CONTAINERIZED
  701. void compileViaK8sJob()
  702. {
  703. Owned<IException> error;
  704. try
  705. {
  706. runK8sJob("compile", wuid, wuid);
  707. }
  708. catch (IException *E)
  709. {
  710. error.setown(E);
  711. }
  712. if (error)
  713. {
  714. Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
  715. workunit.setown(factory->updateWorkUnit(wuid.get()));
  716. if (workunit)
  717. {
  718. if (workunit->aborting())
  719. workunit->setState(WUStateAborted);
  720. else if (workunit->getState()!=WUStateAborted)
  721. {
  722. StringBuffer msg;
  723. error->errorMessage(msg);
  724. addExceptionToWorkunit(workunit, SeverityError, "eclccserver", error->errorCode(), msg.str(), NULL, 0, 0, 0);
  725. workunit->setState(WUStateFailed);
  726. }
  727. }
  728. workunit->commit();
  729. workunit.clear();
  730. }
  731. }
  732. #endif
  733. virtual void threadmain() override
  734. {
  735. DBGLOG("Compile request processing for workunit %s", wuid.get());
  736. Owned<IPropertyTree> config = getComponentConfig();
  737. #ifdef _CONTAINERIZED
  738. if (!useChildProcesses && !childProcessTimeLimit && !config->hasProp("@workunit"))
  739. {
  740. compileViaK8sJob();
  741. return;
  742. }
  743. #endif
  744. Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
  745. workunit.setown(factory->updateWorkUnit(wuid.get()));
  746. if (!workunit)
  747. {
  748. DBGLOG("Workunit %s no longer exists", wuid.get());
  749. return;
  750. }
  751. if (workunit->aborting() || workunit->getState()==WUStateAborted)
  752. {
  753. workunit->setState(WUStateAborted);
  754. DBGLOG("Workunit %s aborted", wuid.get());
  755. workunit->commit();
  756. workunit.clear();
  757. return;
  758. }
  759. CSDSServerStatus serverstatus("ECLCCserverThread");
  760. serverstatus.queryProperties()->setProp("@cluster", config->queryProp("@name"));
  761. serverstatus.queryProperties()->setProp("@thread", idxStr.str());
  762. serverstatus.queryProperties()->setProp("WorkUnit", wuid.get());
  763. serverstatus.commitProperties();
  764. workunit->setAgentSession(myProcessSession());
  765. StringAttr clusterName(workunit->queryClusterName());
  766. #ifdef _CONTAINERIZED
  767. VStringBuffer xpath("queues[@name='%s']", clusterName.str());
  768. Owned<IPropertyTree> queueInfo = config->getBranch(xpath);
  769. assertex(queueInfo);
  770. const char *platformName = queueInfo->queryProp("@type");
  771. #else
  772. Owned<IConstWUClusterInfo> clusterInfo = getTargetClusterInfo(clusterName.str());
  773. if (!clusterInfo)
  774. {
  775. VStringBuffer errStr("Cluster %s not recognized", clusterName.str());
  776. failCompilation(errStr);
  777. return;
  778. }
  779. ClusterType platform = clusterInfo->getPlatform();
  780. const char *platformName = clusterTypeString(platform, true);
  781. clusterInfo.clear();
  782. #endif
  783. workunit->setState(WUStateCompiling);
  784. workunit->commit();
  785. bool ok = false;
  786. try
  787. {
  788. bool timedOut = false;
  789. ok = compile(wuid, platformName, clusterName.str(), timedOut);
  790. #ifdef _CONTAINERIZED
  791. if (timedOut)
  792. {
  793. workunit.clear();
  794. DBGLOG("Workunit %s local compilation timed out, launching k8s job", wuid.get());
  795. compileViaK8sJob();
  796. return;
  797. }
  798. #endif
  799. }
  800. catch (IException * e)
  801. {
  802. StringBuffer msg;
  803. e->errorMessage(msg);
  804. addExceptionToWorkunit(workunit, SeverityError, "eclccserver", e->errorCode(), msg.str(), NULL, 0, 0, 0);
  805. e->Release();
  806. }
  807. if (ok)
  808. {
  809. workunit->setState(WUStateCompiled);
  810. // Workunit can change the cluster name via #workunit, so reload it
  811. // Note that we need a StringAttr here not a char * as the lifetime of the value returned from queryClusterName is
  812. // only until the workunit object is released.
  813. StringAttr newClusterName = workunit->queryClusterName();
  814. if (strcmp(newClusterName, clusterName.str()) != 0)
  815. {
  816. #ifdef _CONTAINERIZED
  817. // MORE?
  818. #else
  819. clusterInfo.setown(getTargetClusterInfo(newClusterName));
  820. if (!clusterInfo)
  821. {
  822. VStringBuffer errStr("Cluster %s by #workunit not recognized", newClusterName.str());
  823. failCompilation(errStr);
  824. return;
  825. }
  826. if (platform != clusterInfo->getPlatform())
  827. {
  828. VStringBuffer errStr("Cluster %s specified by #workunit is wrong type for this queue", newClusterName.str());
  829. failCompilation(errStr);
  830. return;
  831. }
  832. clusterInfo.clear();
  833. #endif
  834. }
  835. if (workunit->getAction()==WUActionRun || workunit->getAction()==WUActionUnknown) // Assume they meant run....
  836. {
  837. if (isLibrary(workunit))
  838. {
  839. workunit->setState(WUStateCompleted);
  840. }
  841. else
  842. {
  843. workunit->schedule();
  844. SCMStringBuffer dllBuff;
  845. Owned<IConstWUQuery> wuQuery = workunit->getQuery();
  846. wuQuery->getQueryDllName(dllBuff);
  847. wuQuery.clear();
  848. if (dllBuff.length() > 0)
  849. {
  850. workunit.clear();
  851. if (!runWorkUnit(wuid, newClusterName))
  852. {
  853. Owned<IWorkUnitFactory> factory = getWorkUnitFactory();
  854. workunit.setown(factory->updateWorkUnit(wuid));
  855. reportError("Failed to execute workunit", 2);
  856. if (workunit->getState() != WUStateAborted)
  857. workunit->setState(WUStateFailed);
  858. }
  859. }
  860. else
  861. {
  862. reportError("Failed to execute workunit (unknown DLL name)", 2);
  863. workunit->setState(WUStateFailed);
  864. }
  865. }
  866. }
  867. }
  868. else if (workunit->getState() != WUStateAborted)
  869. workunit->setState(WUStateFailed);
  870. if (workunit)
  871. workunit->commit();
  872. workunit.clear();
  873. }
  874. virtual bool stop() override
  875. {
  876. return false; // should I try to abort?
  877. }
  878. virtual bool canReuse() const override
  879. {
  880. return true;
  881. }
  882. };
  883. #ifndef _WIN32
  884. static void generatePrecompiledHeader()
  885. {
  886. try
  887. {
  888. Owned<IPipeProcess> pipe = createPipeProcess();
  889. Owned<ErrorReader> errorReader = new ErrorReader(pipe, NULL);
  890. StringBuffer cmd;
  891. splitDirTail(queryCurrentProcessPath(), cmd);
  892. cmd.append("eclcc -pch");
  893. if (pipe->run("eclcc", cmd, ".", false, false, true, 0))
  894. {
  895. errorReader->start();
  896. unsigned retcode = pipe->wait();
  897. errorReader->join();
  898. if (retcode != 0 || errorReader->errCount() != 0)
  899. throw MakeStringException(0, "eclcc -pch failed");
  900. DBGLOG("Created precompiled header");
  901. }
  902. }
  903. catch (IException * e)
  904. {
  905. EXCLOG(e, "Creating precompiled header");
  906. e->Release();
  907. }
  908. }
  909. static void removePrecompiledHeader()
  910. {
  911. removeFileTraceIfFail("eclinclude4.hpp.gch");
  912. }
  913. #endif
  914. //------------------------------------------------------------------------------------------------------------------
  915. // Class EclccServer manages a pool of compile threads
  916. //------------------------------------------------------------------------------------------------------------------
  917. static StringBuffer &getQueues(StringBuffer &queueNames)
  918. {
  919. Owned<IPropertyTree> config = getComponentConfig();
  920. #ifdef _CONTAINERIZED
  921. bool filtered = false;
  922. std::unordered_map<std::string, bool> listenQueues;
  923. Owned<IPTreeIterator> listening = config->getElements("listen");
  924. ForEach (*listening)
  925. {
  926. const char *lq = listening->query().queryProp(".");
  927. if (lq)
  928. {
  929. listenQueues[lq] = true;
  930. filtered = true;
  931. }
  932. }
  933. Owned<IPTreeIterator> queues = config->getElements("queues");
  934. ForEach(*queues)
  935. {
  936. IPTree &queue = queues->query();
  937. const char *qname = queue.queryProp("@name");
  938. if (!filtered || listenQueues.count(qname))
  939. {
  940. if (queueNames.length())
  941. queueNames.append(",");
  942. getClusterEclCCServerQueueName(queueNames, qname);
  943. }
  944. }
  945. #else
  946. const char * processName = config->queryProp("@name");
  947. SCMStringBuffer scmQueueNames;
  948. getEclCCServerQueueNames(scmQueueNames, processName);
  949. queueNames.append(scmQueueNames.str());
  950. #endif
  951. return queueNames;
  952. }
  953. class EclccServer : public CInterface, implements IThreadFactory, implements IAbortHandler
  954. {
  955. StringAttr queueNames;
  956. unsigned poolSize;
  957. Owned<IThreadPool> pool;
  958. unsigned threadsActive;
  959. CriticalSection threadActiveCrit;
  960. std::atomic<bool> running;
  961. CSDSServerStatus serverstatus;
  962. Owned<IJobQueue> queue;
  963. CriticalSection queueUpdateCS;
  964. StringAttr updatedQueueNames;
  965. CConfigUpdateHook reloadConfigHook;
  966. void configUpdate()
  967. {
  968. StringBuffer newQueueNames;
  969. getQueues(newQueueNames);
  970. if (!newQueueNames.length())
  971. ERRLOG("No queues found to listen on");
  972. Linked<IJobQueue> currentQueue;
  973. {
  974. CriticalBlock b(queueUpdateCS);
  975. if (strsame(queueNames, newQueueNames))
  976. return;
  977. updatedQueueNames.set(newQueueNames);
  978. currentQueue.set(queue);
  979. PROGLOG("Updating queue due to queue names change from '%s' to '%s'", queueNames.str(), newQueueNames.str());
  980. }
  981. if (currentQueue)
  982. currentQueue->cancelAcceptConversation();
  983. }
  984. public:
  985. IMPLEMENT_IINTERFACE;
  986. EclccServer(const char *_queueName, unsigned _poolSize)
  987. : updatedQueueNames(_queueName), poolSize(_poolSize), serverstatus("ECLCCserver")
  988. {
  989. threadsActive = 0;
  990. running = false;
  991. pool.setown(createThreadPool("eclccServerPool", this, NULL, poolSize, INFINITE));
  992. serverstatus.queryProperties()->setProp("@cluster", getComponentConfigSP()->queryProp("@name"));
  993. serverstatus.commitProperties();
  994. reloadConfigHook.installOnce(std::bind(&EclccServer::configUpdate, this), false);
  995. }
  996. ~EclccServer()
  997. {
  998. reloadConfigHook.clear();
  999. pool->joinAll(false, INFINITE);
  1000. }
  1001. void run()
  1002. {
  1003. running = true;
  1004. LocalIAbortHandler abortHandler(*this);
  1005. while (running)
  1006. {
  1007. try
  1008. {
  1009. bool newQueues = false;
  1010. {
  1011. CriticalBlock b(queueUpdateCS);
  1012. if (updatedQueueNames)
  1013. {
  1014. queueNames.set(updatedQueueNames);
  1015. updatedQueueNames.clear();
  1016. queue.clear();
  1017. queue.setown(createJobQueue(queueNames.get()));
  1018. newQueues = true;
  1019. }
  1020. // onAbort could have triggered before or during the above switch, if so, we do no want to connect/block on new queue
  1021. if (!running)
  1022. break;
  1023. }
  1024. if (newQueues)
  1025. {
  1026. queue->connect(false);
  1027. serverstatus.queryProperties()->setProp("@queue", queueNames.get());
  1028. serverstatus.commitProperties();
  1029. DBGLOG("eclccServer (%d threads) waiting for requests on queue(s) %s", poolSize, queueNames.get());
  1030. }
  1031. if (!pool->waitAvailable(10000))
  1032. {
  1033. if (getComponentConfigSP()->getPropInt("@traceLevel", 0) > 2)
  1034. DBGLOG("Blocked for 10 seconds waiting for an available compiler thread");
  1035. continue;
  1036. }
  1037. Owned<IJobQueueItem> item = queue->dequeue();
  1038. if (item.get())
  1039. {
  1040. try
  1041. {
  1042. pool->start((void *) item->queryWUID());
  1043. }
  1044. catch(IException *e)
  1045. {
  1046. StringBuffer m;
  1047. EXCLOG(e, "eclccServer::run exception");
  1048. e->Release();
  1049. }
  1050. catch(...)
  1051. {
  1052. IERRLOG("Unexpected exception in eclccServer::run caught");
  1053. }
  1054. }
  1055. }
  1056. catch (IException *E)
  1057. {
  1058. EXCLOG(E);
  1059. releaseAtoms();
  1060. ExitModuleObjects();
  1061. _exit(2);
  1062. }
  1063. catch (...)
  1064. {
  1065. IERRLOG("Unknown exception caught in eclccServer::run - restarting");
  1066. releaseAtoms();
  1067. ExitModuleObjects();
  1068. _exit(2);
  1069. }
  1070. }
  1071. DBGLOG("eclccServer closing");
  1072. }
  1073. virtual IPooledThread *createNew()
  1074. {
  1075. CriticalBlock b(threadActiveCrit);
  1076. return new EclccCompileThread(threadsActive++);
  1077. }
  1078. virtual bool onAbort()
  1079. {
  1080. running = false;
  1081. Linked<IJobQueue> currentQueue;
  1082. {
  1083. CriticalBlock b(queueUpdateCS);
  1084. if (queue)
  1085. currentQueue.set(queue);
  1086. }
  1087. if (currentQueue)
  1088. currentQueue->cancelAcceptConversation();
  1089. return false;
  1090. }
  1091. };
  1092. void openLogFile()
  1093. {
  1094. #ifndef _CONTAINERIZED
  1095. StringBuffer logname;
  1096. getConfigurationDirectory(nullptr, "log","eclccserver", getComponentConfigSP()->queryProp("@name"),logname);
  1097. Owned<IComponentLogFileCreator> lf = createComponentLogFileCreator(logname.str(), "eclccserver");
  1098. lf->beginLogging();
  1099. #else
  1100. setupContainerizedLogMsgHandler();
  1101. #endif
  1102. }
  1103. //=========================================================================================
  1104. //////////////////////////////////////////////////////////////////////////////////////////////
  1105. extern "C" void caughtSIGPIPE(int sig)
  1106. {
  1107. DBGLOG("Caught sigpipe %d", sig);
  1108. }
  1109. extern "C" void caughtSIGHUP(int sig)
  1110. {
  1111. DBGLOG("Caught sighup %d", sig);
  1112. }
  1113. extern "C" void caughtSIGALRM(int sig)
  1114. {
  1115. DBGLOG("Caught sigalrm %d", sig);
  1116. }
  1117. void initSignals()
  1118. {
  1119. #ifndef _WIN32
  1120. signal(SIGPIPE, caughtSIGPIPE);
  1121. signal(SIGHUP, caughtSIGHUP);
  1122. signal(SIGALRM, caughtSIGALRM);
  1123. #endif
  1124. }
  1125. static constexpr const char * defaultYaml = R"!!(
  1126. version: "1.0"
  1127. eclccserver:
  1128. daliServers: dali
  1129. enableEclccDali: true
  1130. enableSysLog: true
  1131. generatePrecompiledHeader: true
  1132. useChildProcesses: false
  1133. name: myeclccserver
  1134. traceLevel: 1
  1135. )!!";
  1136. static IPropertyTree * translateOptions(IPropertyTree * legacy)
  1137. {
  1138. Owned<IPropertyTreeIterator> options = legacy->getElements("./Option");
  1139. unsigned id = 1000; // start with a large number so it is unlikely to clash with any specified by the user
  1140. ForEach(*options)
  1141. {
  1142. IPropertyTree &option = options->query();
  1143. const char *name = option.queryProp("@name");
  1144. //Ensure that any repeated options of the form -x=a -x=y are retained, rather than being deduped, by appending a unique number.
  1145. //Used for --alowsigned=...
  1146. if (name && ((memicmp(name, "eclcc-", 6) == 0) || *name=='-'))
  1147. {
  1148. const char * start = name + (*name=='-' ? 1 : 6);
  1149. const char * dash = strrchr(start, '-'); // position of trailing dash, if present
  1150. //Do not add a unique number on the end if it already has a unique number on the end
  1151. if (!dash || !isdigit(dash[1]))
  1152. {
  1153. VStringBuffer newname("%s-%d", name, id++);
  1154. option.setProp("@name", newname.str());
  1155. }
  1156. }
  1157. }
  1158. return LINK(legacy);
  1159. }
  1160. int main(int argc, const char *argv[])
  1161. {
  1162. if (!checkCreateDaemon(argc, argv))
  1163. return EXIT_FAILURE;
  1164. InitModuleObjects();
  1165. initSignals();
  1166. NoQuickEditSection x;
  1167. Owned<IFile> sentinelFile = createSentinelTarget();
  1168. // We remove any existing sentinel until we have validated that we can successfully start (i.e. all options are valid...)
  1169. removeSentinelFile(sentinelFile);
  1170. Owned<IPropertyTree> globals;
  1171. try
  1172. {
  1173. globalArgv = argv;
  1174. globals.setown(loadConfiguration(defaultYaml, argv, "eclccserver", "ECLCCSERVER", "eclccserver.xml", translateOptions));
  1175. }
  1176. catch (IException * e)
  1177. {
  1178. UERRLOG(e);
  1179. e->Release();
  1180. return 1;
  1181. }
  1182. catch(...)
  1183. {
  1184. OERRLOG("Failed to load configuration");
  1185. return 1;
  1186. }
  1187. const char * processName = globals->queryProp("@name");
  1188. setStatisticsComponentName(SCTeclcc, processName, true);
  1189. if (globals->getPropBool("@enableSysLog",true))
  1190. UseSysLogForOperatorMessages();
  1191. #ifndef _CONTAINERIZED
  1192. #ifndef _WIN32
  1193. if (globals->getPropBool("@generatePrecompiledHeader", true))
  1194. generatePrecompiledHeader();
  1195. else
  1196. removePrecompiledHeader();
  1197. #endif
  1198. #endif
  1199. const char *daliServers = globals->queryProp("@daliServers");
  1200. if (!daliServers)
  1201. {
  1202. UWARNLOG("No Dali server list specified - assuming local");
  1203. daliServers = ".";
  1204. }
  1205. Owned<IGroup> serverGroup = createIGroupRetry(daliServers, DALI_SERVER_PORT);
  1206. try
  1207. {
  1208. initClientProcess(serverGroup, DCR_EclCCServer);
  1209. openLogFile();
  1210. const char *wuid = globals->queryProp("@workunit");
  1211. if (wuid)
  1212. {
  1213. // One shot mode
  1214. EclccCompileThread compiler(0);
  1215. compiler.init(const_cast<char *>(wuid));
  1216. compiler.threadmain();
  1217. }
  1218. else
  1219. {
  1220. #ifndef _CONTAINERIZED
  1221. unsigned optMonitorInterval = globals->getPropInt("@monitorInterval", 60);
  1222. if (optMonitorInterval)
  1223. startPerformanceMonitor(optMonitorInterval*1000, PerfMonStandard, nullptr);
  1224. #endif
  1225. StringBuffer queueNames;
  1226. getQueues(queueNames);
  1227. if (!queueNames.length())
  1228. throw MakeStringException(0, "No queues found to listen on");
  1229. #ifdef _CONTAINERIZED
  1230. queryCodeSigner().initForContainer();
  1231. useChildProcesses = globals->getPropInt("@useChildProcesses", false);
  1232. unsigned maxThreads = globals->getPropInt("@maxActive", 4);
  1233. childProcessTimeLimit = useChildProcesses ? 0 : globals->getPropInt("@childProcessTimeLimit", 10);
  1234. #else
  1235. // The option has been renamed to avoid confusion with the similarly-named eclcc option, but
  1236. // still accept the old name if the new one is not present.
  1237. unsigned maxThreads = globals->getPropInt("@maxEclccProcesses", globals->getPropInt("@maxCompileThreads", 4));
  1238. #endif
  1239. EclccServer server(queueNames.str(), maxThreads);
  1240. // if we got here, eclserver is successfully started and all options are good, so create the "sentinel file" for re-runs from the script
  1241. // put in its own "scope" to force the flush
  1242. writeSentinelFile(sentinelFile);
  1243. server.run();
  1244. }
  1245. }
  1246. catch (IException * e)
  1247. {
  1248. EXCLOG(e, "Terminating unexpectedly");
  1249. e->Release();
  1250. }
  1251. catch(...)
  1252. {
  1253. IERRLOG("Terminating unexpectedly");
  1254. }
  1255. #ifndef _CONTAINERIZED
  1256. stopPerformanceMonitor();
  1257. #endif
  1258. UseSysLogForOperatorMessages(false);
  1259. ::closedownClientProcess(); // dali client closedown
  1260. releaseAtoms();
  1261. ExitModuleObjects();
  1262. return 0;
  1263. }