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