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);
299
300 for (const auto &[dirStr, jobs] : byDir)
301 {
304
305
306 const size_t numThreads =
static_cast<size_t>(
Config_getInt(DOT_NUM_THREADS));
307 const size_t batchSize =
static_cast<size_t>(
Config_getInt(DOT_BATCH_SIZE));
309 const size_t maxArgLen = 32000-exeLen;
310
311
312 std::vector<size_t> indices(jobs.size());
313 std::iota(indices.begin(), indices.end(), 0);
314 std::shuffle(indices.begin(), indices.end(), rng);
315
316
317 struct CommandArgument
318 {
319 CommandArgument(const DString &args) : arguments(args) {}
320 DString arguments;
321 size_t numDotFiles = 0;
322 const DotJob *firstJob = nullptr;
323 };
324
325 std::vector<CommandArgument> partialCommands;
326 std::vector<CommandArgument> finalCommands;
327
328 bool hasImageMap = std::any_of(jobs.begin(),jobs.end(),[](const auto &j) { return j->generateImageMap; });
329
330
331 DString baseArgs = DString("-T") + format;
332 if (hasImageMap)
333 {
334 baseArgs += " -Tcmapx";
335 }
336 baseArgs += " -O";
337
338
339 for (size_t i=0; i<numThreads; i++)
340 {
341 partialCommands.emplace_back(baseArgs);
342 }
343
344
345 size_t index=0;
346 for (size_t i : indices)
347 {
348 const auto &job = jobs[i];
349 DString fileArg = DString(" ") + job->relDotName;
350 auto &cmd = partialCommands[index];
351 if (cmd.numDotFiles<batchSize && cmd.arguments.length()+fileArg.
length()<maxArgLen)
352 {
353 cmd.arguments+=fileArg;
354 cmd.numDotFiles++;
355 }
356 else
357 {
358 finalCommands.push_back(cmd);
359 cmd.arguments=baseArgs+fileArg;
360 cmd.numDotFiles=1;
361 }
362 if (cmd.firstJob==nullptr) cmd.firstJob=job;
363 index = (index+1)%numThreads;
364 }
365
366
367 finalCommands.insert(finalCommands.end(),partialCommands.begin(),partialCommands.end());
368
369
371 {
372 for (const auto &cmd : finalCommands)
373 {
374 if (cmd.numDotFiles>0)
375 {
376 if (cmd.numDotFiles>1)
377 {
378 msg(
"Running dot for graphs {}-{}/{}\n",prev+1,prev+cmd.numDotFiles,dotJobs.size());
379 }
380 else
381 {
382 msg(
"Running dot for graph {}/{}\n",prev+1,dotJobs.size());
383 }
384 prev+=cmd.numDotFiles;
385 int exitCode;
387 {
389 "Problems running dot: exit code={}, command='{}', dir='{}', arguments='{}'",
390 exitCode,
m_dotExe, dirStr, cmd.arguments);
391 ok = false;
392 }
393 }
394 }
395 }
396 else
397 {
398 ThreadPool workers(numThreads);
399 std::vector< std::future<size_t> > results;
400 for (auto & cmd: finalCommands)
401 {
402 if (cmd.numDotFiles>0)
403 {
404 auto locDirStr = dirStr;
405 auto process = [this,cmd,locDirStr]() -> size_t
406 {
407 int exitCode;
409 {
411 "Problems running dot: exit code={}, command='{}', dir='{}', arguments='{}'",
412 exitCode,
m_dotExe, locDirStr, cmd.arguments);
413 }
414 return cmd.numDotFiles;
415 };
416 results.emplace_back(workers.queue(process));
417 }
418 }
419 for (auto &f : results)
420 {
421 size_t numDotFiles = f.get();
422 if (numDotFiles>1)
423 {
424 msg(
"Finished running dot for graphs {}-{}/{}\n",prev+1,prev+numDotFiles,dotJobs.size());
425 }
426 else
427 {
428 msg(
"Finished running dot for graph {}/{}\n",prev+1,dotJobs.size());
429 }
430 prev+=numDotFiles;
431 }
432 }
433
434
435
436
437 for (const auto *job : jobs)
438 {
440 DString dotOutput = job->absPath + job->relDotName + "." + format;
441 DString output = base + "." + format;
442 Dir d;
444 {
445 err(
"Failed to rename {} to {}!\n", dotOutput, output);
446 ok = false;
447 continue;
448 }
449 if (job->generateImageMap)
450 {
451 DString dotMapOutput = job->absPath + job->relDotName + ".cmapx";
452 DString mapOutput = base + ".map";
454 {
455 err(
"Failed to rename {} to {}!\n", dotMapOutput, mapOutput);
456 ok = false;
457 continue;
458 }
459 }
460
461 if (format.startsWith("pdf"))
462 {
463 int width=0, height=0;
465 {
466 ok = false;
467 continue;
468 }
470 {
472 {
473 ok = false;
474 continue;
475 }
476
477 DString rerunArgs = DString("-T") + format + " -O \"" + job->relDotName + "\"";
478 int exitCode;
480 {
482 "Problems running dot: exit code={}, command='{}', dir='{}', arguments='{}'",
483 exitCode,
m_dotExe, dirStr, rerunArgs);
484 ok = false;
485 }
486 else
487 {
488 Dir d2;
490 {
491 err(
"Failed to rename {} to {}!\n", dotOutput, output);
492 ok = false;
493 }
494 }
495 }
496 }
497 else if (format.startsWith("png"))
498 {
500 }
501 }
503 }
504 }
505
506
507 std::set<std::string> processed;
508 for (const auto &job : dotJobs)
509 {
510 if (!processed.insert((job.absPath + job.relDotName).str()).second) continue;
511
512 if (!job.md5Hash.empty())
513 {
516 if (f)
517 {
518 fwrite(job.md5Hash.data(), 1, 32, f);
520 }
521 }
522
524 {
526 }
527 }
528
529 return ok;
530}
const std::string & str() 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)