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.
283{
284 if (dotJobs.empty()) return true;
285
286
287 std::map<std::string, std::map<std::string, std::vector<const DotJob*>>> byFormatAndDir;
288 for (const auto &job : dotJobs)
289 {
290 byFormatAndDir[job.format.str()][job.absPath.str()].push_back(&job);
291 }
292
293 std::mt19937 rng(std::random_device{}());
294 bool ok = true;
295 size_t prev=0;
296 for (const auto &[fmtStr, byDir] : byFormatAndDir)
297 {
298 DString format = DString(fmtStr);
301
302 for (const auto &[dirStr, jobs] : byDir)
303 {
306
307
308 const size_t numThreads =
static_cast<size_t>(
Config_getInt(DOT_NUM_THREADS));
309 const size_t batchSize =
static_cast<size_t>(
Config_getInt(DOT_BATCH_SIZE));
311 const size_t maxArgLen = 32000-exeLen;
312
313
314 std::vector<size_t> indices(jobs.size());
315 std::iota(indices.begin(), indices.end(), 0);
316 std::shuffle(indices.begin(), indices.end(), rng);
317
318
319 struct CommandArgument
320 {
321 CommandArgument(const DString &args) : arguments(args) {}
322 DString arguments;
323 size_t numDotFiles = 0;
324 const DotJob *firstJob = nullptr;
325 };
326
327 std::vector<CommandArgument> partialCommands;
328 std::vector<CommandArgument> finalCommands;
329
330 bool hasImageMap = std::any_of(jobs.begin(),jobs.end(),[](const auto &j) { return j->generateImageMap; });
331
332
333 DString baseArgs = DString("-T") + format;
334 if (hasImageMap)
335 {
336 baseArgs += " -Tcmapx";
337 }
338 baseArgs += " -O";
339
340
341 for (size_t i=0; i<numThreads; i++)
342 {
343 partialCommands.emplace_back(baseArgs);
344 }
345
346
347 size_t index=0;
348 for (size_t i : indices)
349 {
350 const auto &job = jobs[i];
351 DString fileArg = DString(" ") + job->relDotName;
352 auto &cmd = partialCommands[index];
353 if (cmd.numDotFiles<batchSize && cmd.arguments.length()+fileArg.
length()<maxArgLen)
354 {
355 cmd.arguments+=fileArg;
356 cmd.numDotFiles++;
357 }
358 else
359 {
360 finalCommands.push_back(cmd);
361 cmd.arguments=baseArgs+fileArg;
362 cmd.numDotFiles=1;
363 }
364 if (cmd.firstJob==nullptr) cmd.firstJob=job;
365 index = (index+1)%numThreads;
366 }
367
368
369 finalCommands.insert(finalCommands.end(),partialCommands.begin(),partialCommands.end());
370
371
373 {
374 for (const auto &cmd : finalCommands)
375 {
376 if (cmd.numDotFiles>0)
377 {
378 if (cmd.numDotFiles>1)
379 {
380 msg(
"Running dot for graphs {}-{}/{}\n",prev+1,prev+cmd.numDotFiles,dotJobs.size());
381 }
382 else
383 {
384 msg(
"Running dot for graph {}/{}\n",prev+1,dotJobs.size());
385 }
386 prev+=cmd.numDotFiles;
387 int exitCode;
389 {
391 "Problems running dot: exit code={}, command='{}', dir='{}', arguments='{}'",
392 exitCode,
m_dotExe, dirStr, cmd.arguments);
393 ok = false;
394 }
395 }
396 }
397 }
398 else
399 {
400 ThreadPool workers(numThreads);
401 std::vector< std::future<size_t> > results;
402 for (auto & cmd: finalCommands)
403 {
404 if (cmd.numDotFiles>0)
405 {
406 auto locDirStr = dirStr;
407 auto process = [this,cmd,locDirStr]() -> size_t
408 {
409 int exitCode;
411 {
413 "Problems running dot: exit code={}, command='{}', dir='{}', arguments='{}'",
414 exitCode,
m_dotExe, locDirStr, cmd.arguments);
415 }
416 return cmd.numDotFiles;
417 };
418 results.emplace_back(workers.queue(process));
419 }
420 }
421 for (auto &f : results)
422 {
423 size_t numDotFiles = f.get();
424 if (numDotFiles>1)
425 {
426 msg(
"Finished running dot for graphs {}-{}/{}\n",prev+1,prev+numDotFiles,dotJobs.size());
427 }
428 else
429 {
430 msg(
"Finished running dot for graph {}/{}\n",prev+1,dotJobs.size());
431 }
432 prev+=numDotFiles;
433 }
434 }
435
436
437
438
439 for (const auto *job : jobs)
440 {
442 DString dotOutput = job->absPath + job->relDotName + "." + extGen;
443 DString output = base + "." + extension;
444 Dir d;
446 {
447 err(
"Failed to rename {} to {}!\n", dotOutput, output);
448 ok = false;
449 continue;
450 }
451 if (job->generateImageMap)
452 {
453 DString dotMapOutput = job->absPath + job->relDotName + ".cmapx";
454 DString mapOutput = base + ".map";
456 {
457 err(
"Failed to rename {} to {}!\n", dotMapOutput, mapOutput);
458 ok = false;
459 continue;
460 }
461 }
462
464 {
465 int width=0, height=0;
467 {
468 ok = false;
469 continue;
470 }
472 {
474 {
475 ok = false;
476 continue;
477 }
478
479 DString rerunArgs = DString("-T") + format + " -O \"" + job->relDotName + "\"";
480 int exitCode;
482 {
484 "Problems running dot: exit code={}, command='{}', dir='{}', arguments='{}'",
485 exitCode,
m_dotExe, dirStr, rerunArgs);
486 ok = false;
487 }
488 else
489 {
490 Dir d2;
492 {
493 err(
"Failed to rename {} to {}!\n", dotOutput, output);
494 ok = false;
495 }
496 }
497 }
498 }
500 {
502 }
503 }
505 }
506 }
507
508
509 std::set<std::string> processed;
510 for (const auto &job : dotJobs)
511 {
512 if (!processed.insert((job.absPath + job.relDotName).str()).second) continue;
513
514 if (!job.md5Hash.empty())
515 {
518 if (f)
519 {
520 fwrite(job.md5Hash.data(), 1, 32, f);
522 }
523 }
524
526 {
528 }
529 }
530
531 return ok;
532}
const std::string & str() const
bool startsWith(const char *s) const
size_t length() const
Returns the length of the string, not counting the 0-terminator.
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 DString &fileName, int *width, int *height, bool isEps)
#define Config_getInt(name)
#define Config_getBool(name)
static void checkPngResult(const DString &imgName)
#define MAX_LATEX_GRAPH_SIZE
static DString getBaseNameOfOutput(const DString &output)
static bool resetPDFSize(const int width, const int height, const DString &base)
#define err_full(file, line, fmt,...)
int system(const DString &command, const DString &args, bool commandHasConsole=true)
void unlink(const DString &fileName)
FILE * fopen(const DString &fileName, const DString &mode)
DString getDotImageExtension()
DString getDotImageExtensionGenerated(const DString &format)