CompileManager.coffee 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. ResourceWriter = require "./ResourceWriter"
  2. LatexRunner = require "./LatexRunner"
  3. OutputFileFinder = require "./OutputFileFinder"
  4. OutputCacheManager = require "./OutputCacheManager"
  5. Settings = require("settings-sharelatex")
  6. Path = require "path"
  7. logger = require "logger-sharelatex"
  8. Metrics = require "./Metrics"
  9. child_process = require "child_process"
  10. module.exports = CompileManager =
  11. doCompile: (request, callback = (error, outputFiles) ->) ->
  12. compileDir = Path.join(Settings.path.compilesDir, request.project_id)
  13. timer = new Metrics.Timer("write-to-disk")
  14. logger.log project_id: request.project_id, "starting compile"
  15. ResourceWriter.syncResourcesToDisk request.project_id, request.resources, compileDir, (error) ->
  16. return callback(error) if error?
  17. logger.log project_id: request.project_id, time_taken: Date.now() - timer.start, "written files to disk"
  18. timer.done()
  19. timer = new Metrics.Timer("run-compile")
  20. Metrics.inc("compiles")
  21. LatexRunner.runLatex request.project_id, {
  22. directory: compileDir
  23. mainFile: request.rootResourcePath
  24. compiler: request.compiler
  25. timeout: request.timeout
  26. }, (error) ->
  27. return callback(error) if error?
  28. logger.log project_id: request.project_id, time_taken: Date.now() - timer.start, "done compile"
  29. timer.done()
  30. OutputFileFinder.findOutputFiles request.resources, compileDir, (error, outputFiles) ->
  31. return callback(error) if error?
  32. OutputCacheManager.saveOutputFiles outputFiles, compileDir, (error, newOutputFiles) ->
  33. callback null, newOutputFiles
  34. clearProject: (project_id, _callback = (error) ->) ->
  35. callback = (error) ->
  36. _callback(error)
  37. _callback = () ->
  38. compileDir = Path.join(Settings.path.compilesDir, project_id)
  39. proc = child_process.spawn "rm", ["-r", compileDir]
  40. proc.on "error", callback
  41. stderr = ""
  42. proc.stderr.on "data", (chunk) -> stderr += chunk.toString()
  43. proc.on "close", (code) ->
  44. if code == 0
  45. return callback(null)
  46. else
  47. return callback(new Error("rm -r #{compileDir} failed: #{stderr}"))
  48. syncFromCode: (project_id, file_name, line, column, callback = (error, pdfPositions) ->) ->
  49. # If LaTeX was run in a virtual environment, the file path that synctex expects
  50. # might not match the file path on the host. The .synctex.gz file however, will be accessed
  51. # wherever it is on the host.
  52. base_dir = Settings.path.synctexBaseDir(project_id)
  53. file_path = base_dir + "/" + file_name
  54. synctex_path = Path.join(Settings.path.compilesDir, project_id, "output.pdf")
  55. CompileManager._runSynctex ["code", synctex_path, file_path, line, column], (error, stdout) ->
  56. return callback(error) if error?
  57. logger.log project_id: project_id, file_name: file_name, line: line, column: column, stdout: stdout, "synctex code output"
  58. callback null, CompileManager._parseSynctexFromCodeOutput(stdout)
  59. syncFromPdf: (project_id, page, h, v, callback = (error, filePositions) ->) ->
  60. base_dir = Settings.path.synctexBaseDir(project_id)
  61. synctex_path = Path.join(Settings.path.compilesDir, project_id, "output.pdf")
  62. CompileManager._runSynctex ["pdf", synctex_path, page, h, v], (error, stdout) ->
  63. return callback(error) if error?
  64. logger.log project_id: project_id, page: page, h: h, v:v, stdout: stdout, "synctex pdf output"
  65. callback null, CompileManager._parseSynctexFromPdfOutput(stdout, base_dir)
  66. _runSynctex: (args, callback = (error, stdout) ->) ->
  67. bin_path = Path.resolve(__dirname + "/../../bin/synctex")
  68. seconds = 1000
  69. child_process.execFile bin_path, args, timeout: 10 * seconds, (error, stdout, stderr) ->
  70. return callback(error) if error?
  71. callback(null, stdout)
  72. _parseSynctexFromCodeOutput: (output) ->
  73. results = []
  74. for line in output.split("\n")
  75. [node, page, h, v, width, height] = line.split("\t")
  76. if node == "NODE"
  77. results.push {
  78. page: parseInt(page, 10)
  79. h: parseFloat(h)
  80. v: parseFloat(v)
  81. height: parseFloat(height)
  82. width: parseFloat(width)
  83. }
  84. return results
  85. _parseSynctexFromPdfOutput: (output, base_dir) ->
  86. results = []
  87. for line in output.split("\n")
  88. [node, file_path, line, column] = line.split("\t")
  89. if node == "NODE"
  90. file = file_path.slice(base_dir.length + 1)
  91. results.push {
  92. file: file
  93. line: parseInt(line, 10)
  94. column: parseInt(column, 10)
  95. }
  96. return results