cmQtAutoGeneratorRcc.cxx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmQtAutoGeneratorRcc.h"
  4. #include "cmQtAutoGen.h"
  5. #include "cmAlgorithms.h"
  6. #include "cmCryptoHash.h"
  7. #include "cmFileLockResult.h"
  8. #include "cmMakefile.h"
  9. #include "cmSystemTools.h"
  10. #include "cmUVHandlePtr.h"
  11. #include <functional>
  12. // -- Class methods
  13. cmQtAutoGeneratorRcc::cmQtAutoGeneratorRcc()
  14. {
  15. // Initialize libuv asynchronous iteration request
  16. UVRequest().init(*UVLoop(), &cmQtAutoGeneratorRcc::UVPollStage, this);
  17. }
  18. cmQtAutoGeneratorRcc::~cmQtAutoGeneratorRcc()
  19. {
  20. }
  21. bool cmQtAutoGeneratorRcc::Init(cmMakefile* makefile)
  22. {
  23. // -- Utility lambdas
  24. auto InfoGet = [makefile](std::string const& key) {
  25. return makefile->GetSafeDefinition(key);
  26. };
  27. auto InfoGetList =
  28. [makefile](std::string const& key) -> std::vector<std::string> {
  29. std::vector<std::string> list;
  30. cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
  31. return list;
  32. };
  33. auto InfoGetConfig = [makefile,
  34. this](std::string const& key) -> std::string {
  35. const char* valueConf = nullptr;
  36. {
  37. std::string keyConf = key;
  38. keyConf += '_';
  39. keyConf += InfoConfig();
  40. valueConf = makefile->GetDefinition(keyConf);
  41. }
  42. if (valueConf == nullptr) {
  43. return makefile->GetSafeDefinition(key);
  44. }
  45. return std::string(valueConf);
  46. };
  47. auto InfoGetConfigList =
  48. [&InfoGetConfig](std::string const& key) -> std::vector<std::string> {
  49. std::vector<std::string> list;
  50. cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
  51. return list;
  52. };
  53. // -- Read info file
  54. if (!makefile->ReadListFile(InfoFile())) {
  55. Log().ErrorFile(GeneratorT::RCC, InfoFile(), "File processing failed");
  56. return false;
  57. }
  58. // - Configurations
  59. Log().RaiseVerbosity(InfoGet("ARCC_VERBOSITY"));
  60. MultiConfig_ = makefile->IsOn("ARCC_MULTI_CONFIG");
  61. // - Directories
  62. AutogenBuildDir_ = InfoGet("ARCC_BUILD_DIR");
  63. if (AutogenBuildDir_.empty()) {
  64. Log().ErrorFile(GeneratorT::RCC, InfoFile(), "Build directory empty");
  65. return false;
  66. }
  67. IncludeDir_ = InfoGetConfig("ARCC_INCLUDE_DIR");
  68. if (IncludeDir_.empty()) {
  69. Log().ErrorFile(GeneratorT::RCC, InfoFile(), "Include directory empty");
  70. return false;
  71. }
  72. // - Rcc executable
  73. RccExecutable_ = InfoGet("ARCC_RCC_EXECUTABLE");
  74. RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS");
  75. // - Job
  76. LockFile_ = InfoGet("ARCC_LOCK_FILE");
  77. QrcFile_ = InfoGet("ARCC_SOURCE");
  78. QrcFileName_ = cmSystemTools::GetFilenameName(QrcFile_);
  79. QrcFileDir_ = cmSystemTools::GetFilenamePath(QrcFile_);
  80. RccPathChecksum_ = InfoGet("ARCC_OUTPUT_CHECKSUM");
  81. RccFileName_ = InfoGet("ARCC_OUTPUT_NAME");
  82. Options_ = InfoGetConfigList("ARCC_OPTIONS");
  83. Inputs_ = InfoGetList("ARCC_INPUTS");
  84. // - Settings file
  85. SettingsFile_ = InfoGetConfig("ARCC_SETTINGS_FILE");
  86. // - Validity checks
  87. if (LockFile_.empty()) {
  88. Log().ErrorFile(GeneratorT::RCC, InfoFile(), "Lock file name missing");
  89. return false;
  90. }
  91. if (SettingsFile_.empty()) {
  92. Log().ErrorFile(GeneratorT::RCC, InfoFile(), "Settings file name missing");
  93. return false;
  94. }
  95. if (AutogenBuildDir_.empty()) {
  96. Log().ErrorFile(GeneratorT::RCC, InfoFile(),
  97. "Autogen build directory missing");
  98. return false;
  99. }
  100. if (RccExecutable_.empty()) {
  101. Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc executable missing");
  102. return false;
  103. }
  104. if (QrcFile_.empty()) {
  105. Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc input file missing");
  106. return false;
  107. }
  108. if (RccFileName_.empty()) {
  109. Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc output file missing");
  110. return false;
  111. }
  112. // Init derived information
  113. // ------------------------
  114. RccFilePublic_ = AutogenBuildDir_;
  115. RccFilePublic_ += '/';
  116. RccFilePublic_ += RccPathChecksum_;
  117. RccFilePublic_ += '/';
  118. RccFilePublic_ += RccFileName_;
  119. // Compute rcc output file name
  120. if (IsMultiConfig()) {
  121. RccFileOutput_ = IncludeDir_;
  122. RccFileOutput_ += '/';
  123. RccFileOutput_ += MultiConfigOutput();
  124. } else {
  125. RccFileOutput_ = RccFilePublic_;
  126. }
  127. return true;
  128. }
  129. bool cmQtAutoGeneratorRcc::Process()
  130. {
  131. // Run libuv event loop
  132. UVRequest().send();
  133. if (uv_run(UVLoop(), UV_RUN_DEFAULT) == 0) {
  134. if (Error_) {
  135. return false;
  136. }
  137. } else {
  138. return false;
  139. }
  140. return true;
  141. }
  142. void cmQtAutoGeneratorRcc::UVPollStage(uv_async_t* handle)
  143. {
  144. reinterpret_cast<cmQtAutoGeneratorRcc*>(handle->data)->PollStage();
  145. }
  146. void cmQtAutoGeneratorRcc::PollStage()
  147. {
  148. switch (Stage_) {
  149. // -- Initialize
  150. case StageT::SETTINGS_READ:
  151. if (SettingsFileRead()) {
  152. SetStage(StageT::TEST_QRC_RCC_FILES);
  153. } else {
  154. SetStage(StageT::FINISH);
  155. }
  156. break;
  157. // -- Change detection
  158. case StageT::TEST_QRC_RCC_FILES:
  159. if (TestQrcRccFiles()) {
  160. SetStage(StageT::GENERATE);
  161. } else {
  162. SetStage(StageT::TEST_RESOURCES_READ);
  163. }
  164. break;
  165. case StageT::TEST_RESOURCES_READ:
  166. if (TestResourcesRead()) {
  167. SetStage(StageT::TEST_RESOURCES);
  168. }
  169. break;
  170. case StageT::TEST_RESOURCES:
  171. if (TestResources()) {
  172. SetStage(StageT::GENERATE);
  173. } else {
  174. SetStage(StageT::TEST_INFO_FILE);
  175. }
  176. break;
  177. case StageT::TEST_INFO_FILE:
  178. TestInfoFile();
  179. SetStage(StageT::GENERATE_WRAPPER);
  180. break;
  181. // -- Generation
  182. case StageT::GENERATE:
  183. GenerateParentDir();
  184. SetStage(StageT::GENERATE_RCC);
  185. break;
  186. case StageT::GENERATE_RCC:
  187. if (GenerateRcc()) {
  188. SetStage(StageT::GENERATE_WRAPPER);
  189. }
  190. break;
  191. case StageT::GENERATE_WRAPPER:
  192. GenerateWrapper();
  193. SetStage(StageT::SETTINGS_WRITE);
  194. break;
  195. // -- Finalize
  196. case StageT::SETTINGS_WRITE:
  197. SettingsFileWrite();
  198. SetStage(StageT::FINISH);
  199. break;
  200. case StageT::FINISH:
  201. // Clear all libuv handles
  202. UVRequest().reset();
  203. // Set highest END stage manually
  204. Stage_ = StageT::END;
  205. break;
  206. case StageT::END:
  207. break;
  208. }
  209. }
  210. void cmQtAutoGeneratorRcc::SetStage(StageT stage)
  211. {
  212. if (Error_) {
  213. stage = StageT::FINISH;
  214. }
  215. // Only allow to increase the stage
  216. if (Stage_ < stage) {
  217. Stage_ = stage;
  218. UVRequest().send();
  219. }
  220. }
  221. std::string cmQtAutoGeneratorRcc::MultiConfigOutput() const
  222. {
  223. static std::string const suffix = "_CMAKE_";
  224. std::string res;
  225. res += RccPathChecksum_;
  226. res += '/';
  227. res += AppendFilenameSuffix(RccFileName_, suffix);
  228. return res;
  229. }
  230. bool cmQtAutoGeneratorRcc::SettingsFileRead()
  231. {
  232. // Compose current settings strings
  233. {
  234. cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
  235. std::string const sep(" ~~~ ");
  236. {
  237. std::string str;
  238. str += RccExecutable_;
  239. str += sep;
  240. str += cmJoin(RccListOptions_, ";");
  241. str += sep;
  242. str += QrcFile_;
  243. str += sep;
  244. str += RccPathChecksum_;
  245. str += sep;
  246. str += RccFileName_;
  247. str += sep;
  248. str += cmJoin(Options_, ";");
  249. str += sep;
  250. str += cmJoin(Inputs_, ";");
  251. str += sep;
  252. SettingsString_ = crypt.HashString(str);
  253. }
  254. }
  255. // Make sure the settings file exists
  256. if (!FileSys().FileExists(SettingsFile_, true)) {
  257. // Touch the settings file to make sure it exists
  258. FileSys().Touch(SettingsFile_, true);
  259. }
  260. // Lock the lock file
  261. {
  262. // Make sure the lock file exists
  263. if (!FileSys().FileExists(LockFile_, true)) {
  264. if (!FileSys().Touch(LockFile_, true)) {
  265. Log().ErrorFile(GeneratorT::RCC, LockFile_,
  266. "Lock file creation failed");
  267. Error_ = true;
  268. return false;
  269. }
  270. }
  271. // Lock the lock file
  272. cmFileLockResult lockResult =
  273. LockFileLock_.Lock(LockFile_, static_cast<unsigned long>(-1));
  274. if (!lockResult.IsOk()) {
  275. Log().ErrorFile(GeneratorT::RCC, LockFile_,
  276. "File lock failed: " + lockResult.GetOutputMessage());
  277. Error_ = true;
  278. return false;
  279. }
  280. }
  281. // Read old settings
  282. {
  283. std::string content;
  284. if (FileSys().FileRead(content, SettingsFile_)) {
  285. SettingsChanged_ = (SettingsString_ != SettingsFind(content, "rcc"));
  286. // In case any setting changed clear the old settings file.
  287. // This triggers a full rebuild on the next run if the current
  288. // build is aborted before writing the current settings in the end.
  289. if (SettingsChanged_) {
  290. FileSys().FileWrite(GeneratorT::RCC, SettingsFile_, "");
  291. }
  292. } else {
  293. SettingsChanged_ = true;
  294. }
  295. }
  296. return true;
  297. }
  298. void cmQtAutoGeneratorRcc::SettingsFileWrite()
  299. {
  300. // Only write if any setting changed
  301. if (SettingsChanged_) {
  302. if (Log().Verbose()) {
  303. Log().Info(GeneratorT::RCC,
  304. "Writing settings file " + Quoted(SettingsFile_));
  305. }
  306. // Write settings file
  307. std::string content = "rcc:";
  308. content += SettingsString_;
  309. content += '\n';
  310. if (!FileSys().FileWrite(GeneratorT::RCC, SettingsFile_, content)) {
  311. Log().ErrorFile(GeneratorT::RCC, SettingsFile_,
  312. "Settings file writing failed");
  313. // Remove old settings file to trigger a full rebuild on the next run
  314. FileSys().FileRemove(SettingsFile_);
  315. Error_ = true;
  316. }
  317. }
  318. // Unlock the lock file
  319. LockFileLock_.Release();
  320. }
  321. bool cmQtAutoGeneratorRcc::TestQrcRccFiles()
  322. {
  323. // Do basic checks if rcc generation is required
  324. // Test if the rcc output file exists
  325. if (!FileSys().FileExists(RccFileOutput_)) {
  326. if (Log().Verbose()) {
  327. std::string reason = "Generating ";
  328. reason += Quoted(RccFileOutput_);
  329. reason += " from its source file ";
  330. reason += Quoted(QrcFile_);
  331. reason += " because it doesn't exist";
  332. Log().Info(GeneratorT::RCC, reason);
  333. }
  334. Generate_ = true;
  335. return Generate_;
  336. }
  337. // Test if the settings changed
  338. if (SettingsChanged_) {
  339. if (Log().Verbose()) {
  340. std::string reason = "Generating ";
  341. reason += Quoted(RccFileOutput_);
  342. reason += " from ";
  343. reason += Quoted(QrcFile_);
  344. reason += " because the RCC settings changed";
  345. Log().Info(GeneratorT::RCC, reason);
  346. }
  347. Generate_ = true;
  348. return Generate_;
  349. }
  350. // Test if the rcc output file is older than the .qrc file
  351. {
  352. bool isOlder = false;
  353. {
  354. std::string error;
  355. isOlder = FileSys().FileIsOlderThan(RccFileOutput_, QrcFile_, &error);
  356. if (!error.empty()) {
  357. Log().ErrorFile(GeneratorT::RCC, QrcFile_, error);
  358. Error_ = true;
  359. }
  360. }
  361. if (isOlder) {
  362. if (Log().Verbose()) {
  363. std::string reason = "Generating ";
  364. reason += Quoted(RccFileOutput_);
  365. reason += " because it is older than ";
  366. reason += Quoted(QrcFile_);
  367. Log().Info(GeneratorT::RCC, reason);
  368. }
  369. Generate_ = true;
  370. }
  371. }
  372. return Generate_;
  373. }
  374. bool cmQtAutoGeneratorRcc::TestResourcesRead()
  375. {
  376. if (!Inputs_.empty()) {
  377. // Inputs are known already
  378. return true;
  379. }
  380. if (!RccListOptions_.empty()) {
  381. // Start a rcc list process and parse the output
  382. if (Process_) {
  383. // Process is running already
  384. if (Process_->IsFinished()) {
  385. // Process is finished
  386. if (!ProcessResult_.error()) {
  387. // Process success
  388. std::string parseError;
  389. if (!RccListParseOutput(ProcessResult_.StdOut, ProcessResult_.StdErr,
  390. Inputs_, parseError)) {
  391. Log().ErrorFile(GeneratorT::RCC, QrcFile_, parseError);
  392. Error_ = true;
  393. }
  394. } else {
  395. Log().ErrorFile(GeneratorT::RCC, QrcFile_,
  396. ProcessResult_.ErrorMessage);
  397. Error_ = true;
  398. }
  399. // Clean up
  400. Process_.reset();
  401. ProcessResult_.reset();
  402. } else {
  403. // Process is not finished, yet.
  404. return false;
  405. }
  406. } else {
  407. // Start a new process
  408. // rcc prints relative entry paths when started in the directory of the
  409. // qrc file with a pathless qrc file name argument.
  410. // This is important because on Windows absolute paths returned by rcc
  411. // might contain bad multibyte characters when the qrc file path
  412. // contains non-ASCII pcharacters.
  413. std::vector<std::string> cmd;
  414. cmd.push_back(RccExecutable_);
  415. cmd.insert(cmd.end(), RccListOptions_.begin(), RccListOptions_.end());
  416. cmd.push_back(QrcFileName_);
  417. // We're done here if the process fails to start
  418. return !StartProcess(QrcFileDir_, cmd, false);
  419. }
  420. } else {
  421. // rcc does not support the --list command.
  422. // Read the qrc file content and parse it.
  423. std::string qrcContent;
  424. if (FileSys().FileRead(GeneratorT::RCC, qrcContent, QrcFile_)) {
  425. RccListParseContent(qrcContent, Inputs_);
  426. }
  427. }
  428. if (!Inputs_.empty()) {
  429. // Convert relative paths to absolute paths
  430. RccListConvertFullPath(QrcFileDir_, Inputs_);
  431. }
  432. return true;
  433. }
  434. bool cmQtAutoGeneratorRcc::TestResources()
  435. {
  436. if (Inputs_.empty()) {
  437. return true;
  438. }
  439. {
  440. std::string error;
  441. for (std::string const& resFile : Inputs_) {
  442. // Check if the resource file exists
  443. if (!FileSys().FileExists(resFile)) {
  444. error = "Could not find the resource file\n ";
  445. error += Quoted(resFile);
  446. error += '\n';
  447. Log().ErrorFile(GeneratorT::RCC, QrcFile_, error);
  448. Error_ = true;
  449. break;
  450. }
  451. // Check if the resource file is newer than the build file
  452. if (FileSys().FileIsOlderThan(RccFileOutput_, resFile, &error)) {
  453. if (Log().Verbose()) {
  454. std::string reason = "Generating ";
  455. reason += Quoted(RccFileOutput_);
  456. reason += " from ";
  457. reason += Quoted(QrcFile_);
  458. reason += " because it is older than ";
  459. reason += Quoted(resFile);
  460. Log().Info(GeneratorT::RCC, reason);
  461. }
  462. Generate_ = true;
  463. break;
  464. }
  465. // Print error and break on demand
  466. if (!error.empty()) {
  467. Log().ErrorFile(GeneratorT::RCC, QrcFile_, error);
  468. Error_ = true;
  469. break;
  470. }
  471. }
  472. }
  473. return Generate_;
  474. }
  475. void cmQtAutoGeneratorRcc::TestInfoFile()
  476. {
  477. // Test if the rcc output file is older than the info file
  478. {
  479. bool isOlder = false;
  480. {
  481. std::string error;
  482. isOlder = FileSys().FileIsOlderThan(RccFileOutput_, InfoFile(), &error);
  483. if (!error.empty()) {
  484. Log().ErrorFile(GeneratorT::RCC, QrcFile_, error);
  485. Error_ = true;
  486. }
  487. }
  488. if (isOlder) {
  489. if (Log().Verbose()) {
  490. std::string reason = "Touching ";
  491. reason += Quoted(RccFileOutput_);
  492. reason += " because it is older than ";
  493. reason += Quoted(InfoFile());
  494. Log().Info(GeneratorT::RCC, reason);
  495. }
  496. // Touch build file
  497. FileSys().Touch(RccFileOutput_);
  498. BuildFileChanged_ = true;
  499. }
  500. }
  501. }
  502. void cmQtAutoGeneratorRcc::GenerateParentDir()
  503. {
  504. // Make sure the parent directory exists
  505. if (!FileSys().MakeParentDirectory(GeneratorT::RCC, RccFileOutput_)) {
  506. Error_ = true;
  507. }
  508. }
  509. /**
  510. * @return True when finished
  511. */
  512. bool cmQtAutoGeneratorRcc::GenerateRcc()
  513. {
  514. if (!Generate_) {
  515. // Nothing to do
  516. return true;
  517. }
  518. if (Process_) {
  519. // Process is running already
  520. if (Process_->IsFinished()) {
  521. // Process is finished
  522. if (!ProcessResult_.error()) {
  523. // Rcc process success
  524. // Print rcc output
  525. if (!ProcessResult_.StdOut.empty()) {
  526. Log().Info(GeneratorT::RCC, ProcessResult_.StdOut);
  527. }
  528. BuildFileChanged_ = true;
  529. } else {
  530. // Rcc process failed
  531. {
  532. std::string emsg = "The rcc process failed to compile\n ";
  533. emsg += Quoted(QrcFile_);
  534. emsg += "\ninto\n ";
  535. emsg += Quoted(RccFileOutput_);
  536. if (ProcessResult_.error()) {
  537. emsg += "\n";
  538. emsg += ProcessResult_.ErrorMessage;
  539. }
  540. Log().ErrorCommand(GeneratorT::RCC, emsg, Process_->Setup().Command,
  541. ProcessResult_.StdOut);
  542. }
  543. FileSys().FileRemove(RccFileOutput_);
  544. Error_ = true;
  545. }
  546. // Clean up
  547. Process_.reset();
  548. ProcessResult_.reset();
  549. } else {
  550. // Process is not finished, yet.
  551. return false;
  552. }
  553. } else {
  554. // Start a rcc process
  555. std::vector<std::string> cmd;
  556. cmd.push_back(RccExecutable_);
  557. cmd.insert(cmd.end(), Options_.begin(), Options_.end());
  558. cmd.push_back("-o");
  559. cmd.push_back(RccFileOutput_);
  560. cmd.push_back(QrcFile_);
  561. // We're done here if the process fails to start
  562. return !StartProcess(AutogenBuildDir_, cmd, true);
  563. }
  564. return true;
  565. }
  566. void cmQtAutoGeneratorRcc::GenerateWrapper()
  567. {
  568. // Generate a wrapper source file on demand
  569. if (IsMultiConfig()) {
  570. // Wrapper file content
  571. std::string content;
  572. content += "// This is an autogenerated configuration wrapper file.\n";
  573. content += "// Changes will be overwritten.\n";
  574. content += "#include <";
  575. content += MultiConfigOutput();
  576. content += ">\n";
  577. // Write content to file
  578. if (FileSys().FileDiffers(RccFilePublic_, content)) {
  579. // Write new wrapper file
  580. if (Log().Verbose()) {
  581. Log().Info(GeneratorT::RCC,
  582. "Generating RCC wrapper file " + RccFilePublic_);
  583. }
  584. if (!FileSys().FileWrite(GeneratorT::RCC, RccFilePublic_, content)) {
  585. Log().ErrorFile(GeneratorT::RCC, RccFilePublic_,
  586. "RCC wrapper file writing failed");
  587. Error_ = true;
  588. }
  589. } else if (BuildFileChanged_) {
  590. // Just touch the wrapper file
  591. if (Log().Verbose()) {
  592. Log().Info(GeneratorT::RCC,
  593. "Touching RCC wrapper file " + RccFilePublic_);
  594. }
  595. FileSys().Touch(RccFilePublic_);
  596. }
  597. }
  598. }
  599. bool cmQtAutoGeneratorRcc::StartProcess(
  600. std::string const& workingDirectory, std::vector<std::string> const& command,
  601. bool mergedOutput)
  602. {
  603. // Log command
  604. if (Log().Verbose()) {
  605. std::string msg = "Running command:\n";
  606. msg += QuotedCommand(command);
  607. msg += '\n';
  608. Log().Info(GeneratorT::RCC, msg);
  609. }
  610. // Create process handler
  611. Process_ = cm::make_unique<ReadOnlyProcessT>();
  612. Process_->setup(&ProcessResult_, mergedOutput, command, workingDirectory);
  613. // Start process
  614. if (!Process_->start(UVLoop(),
  615. std::bind(&cm::uv_async_ptr::send, &UVRequest()))) {
  616. Log().ErrorFile(GeneratorT::RCC, QrcFile_, ProcessResult_.ErrorMessage);
  617. Error_ = true;
  618. // Clean up
  619. Process_.reset();
  620. ProcessResult_.reset();
  621. return false;
  622. }
  623. return true;
  624. }