Runs dot for all given jobs.
For each unique format, a single dot invocation is made with -O and all input files for that format.
279{
280 if (dotJobs.empty())
return TRUE;
281
282
283 std::map<std::string, std::map<std::string, std::vector<const DotJob*>>> byFormatAndDir;
284 for (const auto &job : dotJobs)
285 {
286 byFormatAndDir[job.format.str()][job.absPath.str()].push_back(&job);
287 }
288
289 std::mt19937 rng(std::random_device{}());
290 bool ok = true;
291 size_t prev=0;
292 for (const auto &[fmtStr, byDir] : byFormatAndDir)
293 {
294 QCString format = QCString(fmtStr);
295
296 for (const auto &[dirStr, jobs] : byDir)
297 {
300
301
302 const size_t numThreads =
static_cast<size_t>(
Config_getInt(DOT_NUM_THREADS));
303 const size_t batchSize =
static_cast<size_t>(
Config_getInt(DOT_BATCH_SIZE));
304 const size_t exeLen =
m_dotExe.length() + 1;
305 const size_t maxArgLen = 32000-exeLen;
306
307
308 std::vector<size_t> indices(jobs.size());
309 std::iota(indices.begin(), indices.end(), 0);
310 std::shuffle(indices.begin(), indices.end(), rng);
311
312
313 struct CommandArgument
314 {
315 CommandArgument(const QCString &args) : arguments(args) {}
316 QCString arguments;
317 size_t numDotFiles = 0;
318 const DotJob *firstJob = nullptr;
319 };
320
321 std::vector<CommandArgument> partialCommands;
322 std::vector<CommandArgument> finalCommands;
323
324 bool hasImageMap = std::any_of(jobs.begin(),jobs.end(),[](const auto &j) { return j->generateImageMap; });
325
326
327 QCString baseArgs = QCString("-T") + format;
328 if (hasImageMap)
329 {
330 baseArgs += " -Tcmapx";
331 }
332 baseArgs += " -O";
333
334
335 for (size_t i=0; i<numThreads; i++)
336 {
337 partialCommands.emplace_back(baseArgs);
338 }
339
340
341 size_t index=0;
342 for (size_t i : indices)
343 {
344 const auto &job = jobs[i];
345 QCString fileArg = QCString(" ") + job->relDotName;
346 auto &cmd = partialCommands[index];
347 if (cmd.numDotFiles<batchSize && cmd.arguments.length()+fileArg.
length()<maxArgLen)
348 {
349 cmd.arguments+=fileArg;
350 cmd.numDotFiles++;
351 }
352 else
353 {
354 finalCommands.push_back(cmd);
355 cmd.arguments=baseArgs+fileArg;
356 cmd.numDotFiles=1;
357 }
358 if (cmd.firstJob==nullptr) cmd.firstJob=job;
359 index = (index+1)%numThreads;
360 }
361
362
363 finalCommands.insert(finalCommands.end(),partialCommands.begin(),partialCommands.end());
364
365
367 {
368 for (const auto &cmd : finalCommands)
369 {
370 if (cmd.numDotFiles>0)
371 {
372 if (cmd.numDotFiles>1)
373 {
374 msg(
"Running dot for graphs {}-{}/{}\n",prev+1,prev+cmd.numDotFiles,dotJobs.size());
375 }
376 else
377 {
378 msg(
"Running dot for graph {}/{}\n",prev+1,dotJobs.size());
379 }
380 prev+=cmd.numDotFiles;
381 int exitCode;
383 {
385 "Problems running dot: exit code={}, command='{}', dir='{}', arguments='{}'",
386 exitCode,
m_dotExe, dirStr, cmd.arguments);
387 ok = false;
388 }
389 }
390 }
391 }
392 else
393 {
394 ThreadPool workers(numThreads);
395 std::vector< std::future<size_t> > results;
396 for (auto & cmd: finalCommands)
397 {
398 if (cmd.numDotFiles>0)
399 {
400 auto process = [this,cmd,dirStr]() -> size_t
401 {
402 int exitCode;
404 {
406 "Problems running dot: exit code={}, command='{}', dir='{}', arguments='{}'",
407 exitCode,
m_dotExe, dirStr, cmd.arguments);
408 }
409 return cmd.numDotFiles;
410 };
411 results.emplace_back(workers.queue(process));
412 }
413 }
414 for (auto &f : results)
415 {
416 size_t numDotFiles = f.get();
417 if (numDotFiles>1)
418 {
419 msg(
"Finished running dot for graphs {}-{}/{}\n",prev+1,prev+numDotFiles,dotJobs.size());
420 }
421 else
422 {
423 msg(
"Finished running dot for graph {}/{}\n",prev+1,dotJobs.size());
424 }
425 prev+=numDotFiles;
426 }
427 }
428
429
430
431
432 for (const auto *job : jobs)
433 {
435 QCString dotOutput = job->absPath + job->relDotName + "." + format;
436 QCString output = base + "." + format;
437 Dir d;
439 {
440 err(
"Failed to rename {} to {}!\n", dotOutput, output);
441 ok = false;
442 continue;
443 }
444 if (job->generateImageMap)
445 {
446 QCString dotMapOutput = job->absPath + job->relDotName + ".cmapx";
447 QCString mapOutput = base + ".map";
449 {
450 err(
"Failed to rename {} to {}!\n", dotMapOutput, mapOutput);
451 ok = false;
452 continue;
453 }
454 }
455
456 if (format.startsWith("pdf"))
457 {
458 int width=0, height=0;
460 {
461 ok = false;
462 continue;
463 }
465 {
467 {
468 ok = false;
469 continue;
470 }
471
472 QCString rerunArgs = QCString("-T") + format + " -O \"" + job->relDotName + "\"";
473 int exitCode;
475 {
477 "Problems running dot: exit code={}, command='{}', dir='{}', arguments='{}'",
478 exitCode,
m_dotExe, dirStr, rerunArgs);
479 ok = false;
480 }
481 else
482 {
483 Dir d2;
485 {
486 err(
"Failed to rename {} to {}!\n", dotOutput, output);
487 ok = false;
488 }
489 }
490 }
491 }
492 else if (format.startsWith("png"))
493 {
495 }
496 }
498 }
499 }
500
501
502 std::set<std::string> processed;
503 for (const auto &job : dotJobs)
504 {
505 if (!processed.insert((job.absPath + job.relDotName).str()).second) continue;
506
507 if (!job.md5Hash.isEmpty())
508 {
511 if (f)
512 {
513 fwrite(job.md5Hash.data(), 1, 32, f);
515 }
516 }
517
519 {
521 }
522 }
523
524 return ok;
525}
static std::string currentDirPath()
bool rename(const std::string &orgName, const std::string &newName, bool acceptsAbsPath=true) const
static bool setCurrent(const std::string &path)
static bool readBoundingBox(const QCString &fileName, int *width, int *height, bool isEps)
size_t length() const
Returns the length of the string, not counting the 0-terminator.
const std::string & str() const
#define Config_getInt(name)
#define Config_getBool(name)
static QCString getBaseNameOfOutput(const QCString &output)
static void checkPngResult(const QCString &imgName)
#define MAX_LATEX_GRAPH_SIZE
static bool resetPDFSize(const int width, const int height, const QCString &base)
#define err_full(file, line, fmt,...)
void unlink(const QCString &fileName)
FILE * fopen(const QCString &fileName, const QCString &mode)
int system(const QCString &command, const QCString &args, bool commandHasConsole=true)