Exemple #1
0
    def run(self):
        print("Welcome to thread " + self.getName())
        cmd = self.caller.make_cmd + [self.caller.file_name]
        self.caller.output("[Compiling " + self.caller.file_name + "]")
        if DEBUG:
            print(cmd.encode('UTF-8'))

        # Handle path; copied from exec.py
        if self.caller.path:
            old_path = os.environ["PATH"]
            # The user decides in the build system  whether he wants to append $PATH
            # or tuck it at the front: "$PATH;C:\\new\\path", "C:\\new\\path;$PATH"
            # Handle differently in Python 2 and 3, to be safe:
            if not _ST3:
                os.environ["PATH"] = os.path.expandvars(
                    self.caller.path).encode(sys.getfilesystemencoding())
            else:
                os.environ["PATH"] = os.path.expandvars(self.caller.path)

        try:
            if platform.system() == "Windows":
                # make sure console does not come up
                startupinfo = subprocess.STARTUPINFO()
                startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
                proc = subprocess.Popen(cmd, startupinfo=startupinfo)
            else:
                proc = subprocess.Popen(cmd)
        except:
            self.caller.output("\n\nCOULD NOT COMPILE!\n\n")
            self.caller.output("Attempted command:")
            self.caller.output(" ".join(cmd))
            self.caller.proc = None
            return

        # restore path if needed
        if self.caller.path:
            os.environ["PATH"] = old_path

        # Handle killing
        # First, save process handle into caller; then communicate (which blocks)
        self.caller.proc = proc
        # out, err = proc.communicate()
        proc.wait(
        )  # TODO: if needed, must use tempfiles instead of stdout/err

        # if DEBUG:
        #   self.caller.output(out)

        # Here the process terminated, but it may have been killed. If so, do not process log file.
        # Since we set self.caller.proc above, if it is None, the process must have been killed.
        # TODO: clean up?
        if not self.caller.proc:
            print(proc.returncode)
            self.caller.output("\n\n[User terminated compilation process]\n")
            self.caller.finish(False)  # We kill, so won't switch to PDF anyway
            return
        # Here we are done cleanly:
        self.caller.proc = None
        print("Finished normally")
        print(proc.returncode)

        # this is a conundrum. We used (ST1) to open in binary mode ('rb') to avoid
        # issues, but maybe we just need to decode?
        # 12-10-27 NO! We actually do need rb, because MikTeX on Windows injects Ctrl-Z's in the
        # log file, and this just causes Python to stop reading the file.

        # OK, this seems solid: first we decode using the self.caller.encoding,
        # then we reencode using the default locale's encoding.
        # Note: we get this using ST2's own getdefaultencoding(), not the locale module
        # We ignore bad chars in both cases.

        # CHANGED 12/10/19: use platform encoding (self.caller.encoding), then
        # keep it that way!

        # CHANGED 12-10-27. OK, here's the deal. We must open in binary mode on Windows
        # because silly MiKTeX inserts ASCII control characters in over/underfull warnings.
        # In particular it inserts EOFs, which stop reading altogether; reading in binary
        # prevents that. However, that's not the whole story: if a FS character is encountered,
        # AND if we invoke splitlines on a STRING, it sadly breaks the line in two. This messes up
        # line numbers in error reports. If, on the other hand, we invoke splitlines on a
        # byte array (? whatever read() returns), this does not happen---we only break at \n, etc.
        # However, we must still decode the resulting lines using the relevant encoding.
        # 121101 -- moved splitting and decoding logic to parseTeXlog, where it belongs.

        # Note to self: need to think whether we don't want to codecs.open this, too...
        data = open(self.caller.tex_base + ".log", 'rb').read()

        errors = []
        warnings = []

        try:
            (errors, warnings) = parseTeXlog.parse_tex_log(data)
            content = ["", ""]
            if errors:
                content.append("There were errors in your LaTeX source")
                content.append("")
                content.extend(errors)
            else:
                content.append("Texification succeeded!")
                content.append("")
            if warnings:
                if errors:
                    content.append("")
                    content.append("There were also warnings.")
                else:
                    content.append(
                        "However, there were warnings in your LaTeX source")
                content.append("")
                content.extend(warnings)
        except Exception as e:
            content = ["", ""]
            content.append("LaTeXtools could not parse the TeX log file")
            content.append("(actually, we never should have gotten here)")
            content.append("")
            content.append("Python exception: " + repr(e))
            content.append("")
            content.append("Please let me know on GitHub. Thanks!")

        self.caller.output(content)
        if errors:
            self.caller.output("\n\n[Finished, with Errors...]\n")
        else:
            self.caller.output("\n\n[Done!]\n")
        self.caller.finish(len(errors) == 0)
Exemple #2
0
	def run ( self ):
		print ("Welcome to thread " + self.getName())
		self.caller.output("[Compiling " + self.caller.file_name + "]")

		# Handle custom env variables
		if self.caller.env:
			old_env = os.environ;
			if not _ST3:
				os.environ.update(dict((k.encode(sys.getfilesystemencoding()), v) for (k, v) in self.caller.env.items()))
			else:
				os.environ.update(self.caller.env.items());

		# Handle path; copied from exec.py
		if self.caller.path:
			# if we had an env, the old path is already backuped in the env
			if not self.caller.env:
				old_path = os.environ["PATH"]
			# The user decides in the build system  whether he wants to append $PATH
			# or tuck it at the front: "$PATH;C:\\new\\path", "C:\\new\\path;$PATH"
			# Handle differently in Python 2 and 3, to be safe:
			if not _ST3:
				os.environ["PATH"] = os.path.expandvars(self.caller.path).encode(sys.getfilesystemencoding())
			else:
				os.environ["PATH"] = os.path.expandvars(self.caller.path)

		# Set up Windows-specific parameters
		if self.caller.plat == "windows":
			# make sure console does not come up
			startupinfo = subprocess.STARTUPINFO()
			startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW

		# Now, iteratively call the builder iterator
		#
		cmd_iterator = self.caller.builder.commands()
		for (cmd, msg) in cmd_iterator:

			# If there is a message, display it
			if msg:
				self.caller.output(msg)

			# If there is nothing to be done, exit loop
			# (Avoids error with empty cmd_iterator)
			if cmd == "":
				break
			print(cmd)
			# Now create a Popen object
			try:
				if self.caller.plat == "windows":
					proc = subprocess.Popen(cmd, startupinfo=startupinfo, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
				elif self.caller.plat == "osx":
					# Temporary (?) fix for Yosemite: pass environment
					proc = subprocess.Popen(
						cmd,
						stderr=subprocess.STDOUT,
						stdout=subprocess.PIPE, 
						env=os.environ,
						preexec_fn=os.setsid
					)
				else: # Must be linux
					proc = subprocess.Popen(
						cmd,
						stderr=subprocess.STDOUT,
						stdout=subprocess.PIPE,
						preexec_fn=os.setsid
					)
			except:
				self.caller.output("\n\nCOULD NOT COMPILE!\n\n")
				self.caller.output("Attempted command:")
				self.caller.output(" ".join(cmd))
				self.caller.output("\nBuild engine: " + self.caller.builder.name)
				self.caller.proc = None
				if self.caller.env:
					os.environ = old_env
				elif self.caller.path:
					os.environ["PATH"] = old_path
				return
			
			# Now actually invoke the command, making sure we allow for killing
			# First, save process handle into caller; then communicate (which blocks)
			with self.caller.proc_lock:
				self.caller.proc = proc
			out, err = proc.communicate()
			self.caller.builder.set_output(out.decode(self.caller.encoding,"ignore"))

			# Here the process terminated, but it may have been killed. If so, stop and don't read log
			# Since we set self.caller.proc above, if it is None, the process must have been killed.
			# TODO: clean up?
			with self.caller.proc_lock:
				if not self.caller.proc:
					print (proc.returncode)
					self.caller.output("\n\n[User terminated compilation process]\n")
					self.caller.finish(False)	# We kill, so won't switch to PDF anyway
					return
			# Here we are done cleanly:
			with self.caller.proc_lock:
				self.caller.proc = None
			print ("Finished normally")
			print (proc.returncode)

			# At this point, out contains the output from the current command;
			# we pass it to the cmd_iterator and get the next command, until completion

		# Clean up
		cmd_iterator.close()

		# restore env or path if needed
		if self.caller.env:
			os.environ = old_env
		elif self.caller.path:
			os.environ["PATH"] = old_path

		# CHANGED 12-10-27. OK, here's the deal. We must open in binary mode on Windows
		# because silly MiKTeX inserts ASCII control characters in over/underfull warnings.
		# In particular it inserts EOFs, which stop reading altogether; reading in binary
		# prevents that. However, that's not the whole story: if a FS character is encountered,
		# AND if we invoke splitlines on a STRING, it sadly breaks the line in two. This messes up
		# line numbers in error reports. If, on the other hand, we invoke splitlines on a
		# byte array (? whatever read() returns), this does not happen---we only break at \n, etc.
		# However, we must still decode the resulting lines using the relevant encoding.
		# 121101 -- moved splitting and decoding logic to parseTeXlog, where it belongs.
		
		# Note to self: need to think whether we don't want to codecs.open this, too...
		# Also, we may want to move part of this logic to the builder...
		data = open(self.caller.tex_base + ".log", 'rb').read()		

		errors = []
		warnings = []

		try:
			(errors, warnings) = parseTeXlog.parse_tex_log(data)
			content = [""]
			if errors:
				content.append("Errors:") 
				content.append("")
				content.extend(errors)
			else:
				content.append("No errors.")
			if warnings:
				if errors:
					content.extend(["", "Warnings:"])
				else:
					content[-1] = content[-1] + " Warnings:" 
				content.append("")
				content.extend(warnings)
			else:
				content.append("")

			hide_panel = {
				"always": True,
				"no_errors": not errors,
				"no_warnings": not errors and not warnings,
				"never": False
			}.get(self.caller.hide_panel_level, False)

			if hide_panel:
				# hide the build panel (ST2 api is not thread save)
				if _ST3:
					self.caller.window.run_command("hide_panel", {"panel": "output.exec"})
				else:
					sublime.set_timeout(lambda: self.caller.window.run_command("hide_panel", {"panel": "output.exec"}), 10)
				message = "build completed"
				if errors:
					message += " with errors"
				if warnings:
					message += " and" if errors else " with"
					message += " warnings"
				if _ST3:
					sublime.status_message(message)
				else:
					sublime.set_timeout(lambda: sublime.status_message(message), 10)
		except Exception as e:
			content=["",""]
			content.append("LaTeXtools could not parse the TeX log file")
			content.append("(actually, we never should have gotten here)")
			content.append("")
			content.append("Python exception: " + repr(e))
			content.append("")
			content.append("Please let me know on GitHub. Thanks!")

		self.caller.output(content)
		self.caller.output("\n\n[Done!]\n")
		self.caller.finish(len(errors) == 0)
	def run ( self ):
		print ("Welcome to thread " + self.getName())
		self.caller.output("[Compiling " + self.caller.file_name + "]")

		# Handle custom env variables
		if self.caller.env:
			old_env = os.environ;
			if not _ST3:
				os.environ.update(dict((k.encode(sys.getfilesystemencoding()), v) for (k, v) in self.caller.env.items()))
			else:
				os.environ.update(self.caller.env.items());

		# Handle path; copied from exec.py
		if self.caller.path:
			# if we had an env, the old path is already backuped in the env
			if not self.caller.env:
				old_path = os.environ["PATH"]
			# The user decides in the build system  whether he wants to append $PATH
			# or tuck it at the front: "$PATH;C:\\new\\path", "C:\\new\\path;$PATH"
			# Handle differently in Python 2 and 3, to be safe:
			if not _ST3:
				os.environ["PATH"] = os.path.expandvars(self.caller.path).encode(sys.getfilesystemencoding())
			else:
				os.environ["PATH"] = os.path.expandvars(self.caller.path)

		# Set up Windows-specific parameters
		if self.caller.plat == "windows":
			# make sure console does not come up
			startupinfo = subprocess.STARTUPINFO()
			startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW

		# Now, iteratively call the builder iterator
		#
		cmd_iterator = self.caller.builder.commands()
		try:
			for (cmd, msg) in cmd_iterator:

				# If there is a message, display it
				if msg:
					self.caller.output(msg)

				# If there is nothing to be done, exit loop
				# (Avoids error with empty cmd_iterator)
				if cmd == "":
					break

				if isinstance(cmd, strbase) or isinstance(cmd, list):
					print(cmd)
					# Now create a Popen object
					try:
						if self.caller.plat == "windows":
							proc = subprocess.Popen(cmd, startupinfo=startupinfo, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
						elif self.caller.plat == "osx":
							# Temporary (?) fix for Yosemite: pass environment
							proc = subprocess.Popen(
								cmd,
								stderr=subprocess.STDOUT,
								stdout=subprocess.PIPE, 
								env=os.environ,
								preexec_fn=os.setsid
							)
						else: # Must be linux
							proc = subprocess.Popen(
								cmd,
								stderr=subprocess.STDOUT,
								stdout=subprocess.PIPE,
								preexec_fn=os.setsid
							)
					except:
						self.caller.output("\n\nCOULD NOT COMPILE!\n\n")
						self.caller.output("Attempted command:")
						self.caller.output(" ".join(cmd))
						self.caller.output("\nBuild engine: " + self.caller.builder.name)
						self.caller.proc = None
						print(traceback.format_exc())
						return
				# Abundance of caution / for possible future extensions:
				elif isinstance(cmd, subprocess.Popen):
					proc = cmd
				else:
					# don't know what the command is
					continue
				
				# Now actually invoke the command, making sure we allow for killing
				# First, save process handle into caller; then communicate (which blocks)
				with self.caller.proc_lock:
					self.caller.proc = proc
				out, err = proc.communicate()
				self.caller.builder.set_output(out.decode(self.caller.encoding,"ignore"))

				
				# Here the process terminated, but it may have been killed. If so, stop and don't read log
				# Since we set self.caller.proc above, if it is None, the process must have been killed.
				# TODO: clean up?
				with self.caller.proc_lock:
					if not self.caller.proc:
						print (proc.returncode)
						self.caller.output("\n\n[User terminated compilation process]\n")
						self.caller.finish(False)	# We kill, so won't switch to PDF anyway
						return
				# Here we are done cleanly:
				with self.caller.proc_lock:
					self.caller.proc = None
				print ("Finished normally")
				print (proc.returncode)
				# At this point, out contains the output from the current command;
				# we pass it to the cmd_iterator and get the next command, until completion
		except:
			self.caller.output("\n\nCOULD NOT COMPILE!\n\n")
			self.caller.output("\nBuild engine: " + self.caller.builder.name)
			self.caller.proc = None
			print(traceback.format_exc())
			return
		finally:
			# restore environment
			if self.caller.env:
				os.environ = old_env
			elif self.caller.path:
				os.environ['PATH'] = old_path

		# Clean up
		cmd_iterator.close()

		try:
			# Here we try to find the log file...
			# 1. Check the aux_directory if there is one
			# 2. Check the output_directory if there is one
			# 3. Assume the log file is in the same folder as the main file
			log_file_base = self.caller.tex_base + ".log"
			if self.caller.aux_directory is None:
				if self.caller.output_directory is None:
					log_file = log_file_base
				else:
					log_file = os.path.join(
						self.caller.output_directory,
						log_file_base
					)
			else:
				log_file = os.path.join(
					self.caller.aux_directory,
					log_file_base
				)

				if not os.path.exists(log_file):
					if (self.caller.output_directory is not None and
						self.caller.output_directory != self.caller.aux_directory
					):
						log_file = os.path.join(
							self.caller.output_directory,
							log_file_base
						)

					if not os.path.exists(log_file):
						log_file = log_file_base

			# CHANGED 12-10-27. OK, here's the deal. We must open in binary mode
			# on Windows because silly MiKTeX inserts ASCII control characters in
			# over/underfull warnings. In particular it inserts EOFs, which
			# stop reading altogether; reading in binary prevents that. However,
			# that's not the whole story: if a FS character is encountered,
			# AND if we invoke splitlines on a STRING, it sadly breaks the line
			# in two. This messes up line numbers in error reports. If, on the
			# other hand, we invoke splitlines on a byte array (? whatever read()
			# returns), this does not happen---we only break at \n, etc.
			# However, we must still decode the resulting lines using the relevant
			# encoding.

			# Note to self: need to think whether we don't want to codecs.open
			# this, too... Also, we may want to move part of this logic to the
			# builder...
			with open(log_file, 'rb') as f:
				data = f.read()
		except IOError:
			self.caller.output([
				"", ""
				"Could not find log file {0}!".format(log_file_base),
			])
			try:
				self.handle_std_outputs(out, err)
			except:
				# if out or err don't yet exist
				self.caller.finish(False)
		else:
			errors = []
			warnings = []
			badboxes = []

			try:
				(errors, warnings, badboxes) = parseTeXlog.parse_tex_log(data)
				content = [""]
				if errors:
					content.append("Errors:") 
					content.append("")
					content.extend(errors)
				else:
					content.append("No errors.")
				if warnings:
					if errors:
						content.extend(["", "Warnings:"])
					else:
						content[-1] = content[-1] + " Warnings:" 
					content.append("")
					content.extend(warnings)
				else:
					if errors:
						content.append("")
					content.append("No warnings.")

				if badboxes and self.caller.display_bad_boxes:
					if warnings or errors:
						content.extend(["", "Bad Boxes:"])
					else:
						content[-1] = content[-1] + " Bad Boxes:"
					content.append("")
					content.extend(badboxes)
				else:
					if self.caller.display_bad_boxes:
						if errors or warnings:
							content.append("")
						content.append("No bad boxes.")

				hide_panel = {
					"always": True,
					"no_errors": not errors,
					"no_warnings": not errors and not warnings,
					"no_badboxes": not errors and not warnings and \
						(not self.caller.display_bad_boxes or not badboxes),
					"never": False
				}.get(self.caller.hide_panel_level, False)

				if hide_panel:
					# hide the build panel (ST2 api is not thread save)
					if _ST3:
						self.caller.window.run_command("hide_panel", {"panel": "output.latextools"})
					else:
						sublime.set_timeout(lambda: self.caller.window.run_command("hide_panel", {"panel": "output.latextools"}), 10)
					message = "build completed"
					if errors:
						message += " with errors"
					if warnings:
						if errors:
							if badboxes and self.caller.display_bad_boxes:
								message += ","
							else:
								message += " and"
						else:
							message += " with"
						message += " warnings"
					if badboxes and self.caller.display_bad_boxes:
						if errors or warnings:
							message += " and"
						else:
							message += " with"
						message += " bad boxes"

					if _ST3:
						sublime.status_message(message)
					else:
						sublime.set_timeout(lambda: sublime.status_message(message), 10)
			except Exception as e:
				# dumpt exception to console
				traceback.print_exc()

				content = ["", ""]
				content.append(
					"LaTeXTools could not parse the TeX log file {0}".format(
						log_file
					)
				)
				content.append("(actually, we never should have gotten here)")
				content.append("")
				content.append("Python exception: {0!r}".format(e))
				content.append("")
				content.append(
					"The full error description can be found on the console."
				)
				content.append("Please let us know on GitHub. Thanks!")

			self.caller.output(content)
			self.caller.output("\n\n[Done!]\n")
			self.caller.finish(len(errors) == 0)
Exemple #4
0
	def run ( self ):
		print "Welcome to thread " + self.getName()
		cmd = self.caller.make_cmd + [self.caller.file_name]
		self.caller.output("[Compiling " + self.caller.file_name + "]")
		if DEBUG:
			print cmd.encode('UTF-8')

		# Handle path; copied from exec.py
		if self.caller.path:
			old_path = os.environ["PATH"]
			# The user decides in the build system  whether he wants to append $PATH
			# or tuck it at the front: "$PATH;C:\\new\\path", "C:\\new\\path;$PATH"
			os.environ["PATH"] = os.path.expandvars(self.caller.path).encode(sys.getfilesystemencoding())

		try:
			if platform.system() == "Windows":
				# make sure console does not come up
				startupinfo = subprocess.STARTUPINFO()
				startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
				proc = subprocess.Popen(cmd, startupinfo=startupinfo)
			else:
				proc = subprocess.Popen(cmd)
		except:
			self.caller.output("\n\nCOULD NOT COMPILE!\n\n")
			self.caller.output("Attempted command:")
			self.caller.output(" ".join(cmd))
			self.caller.proc = None
			return
		
		# restore path if needed
		if self.caller.path:
			os.environ["PATH"] = old_path

		# Handle killing
		# First, save process handle into caller; then communicate (which blocks)
		self.caller.proc = proc
		# out, err = proc.communicate()
		proc.wait() # TODO: if needed, must use tempfiles instead of stdout/err

		# if DEBUG:
		# 	self.caller.output(out)

		# Here the process terminated, but it may have been killed. If so, do not process log file.
		# Since we set self.caller.proc above, if it is None, the process must have been killed.
		# TODO: clean up?
		if not self.caller.proc:
			print proc.returncode
			self.caller.output("\n\n[User terminated compilation process]\n")
			self.caller.finish(False)	# We kill, so won't switch to PDF anyway
			return
		# Here we are done cleanly:
		self.caller.proc = None
		print "Finished normally"
		print proc.returncode

		# this is a conundrum. We used (ST1) to open in binary mode ('rb') to avoid
		# issues, but maybe we just need to decode?
		# 12-10-27 NO! We actually do need rb, because MikTeX on Windows injects Ctrl-Z's in the
		# log file, and this just causes Python to stop reading the file.

		# OK, this seems solid: first we decode using the self.caller.encoding, 
		# then we reencode using the default locale's encoding.
		# Note: we get this using ST2's own getdefaultencoding(), not the locale module
		# We ignore bad chars in both cases.

		# CHANGED 12/10/19: use platform encoding (self.caller.encoding), then
		# keep it that way!

		# CHANGED 12-10-27. OK, here's the deal. We must open in binary mode on Windows
		# because silly MiKTeX inserts ASCII control characters in over/underfull warnings.
		# In particular it inserts EOFs, which stop reading altogether; reading in binary
		# prevents that. However, that's not the whole story: if a FS character is encountered,
		# AND if we invoke splitlines on a STRING, it sadly breaks the line in two. This messes up
		# line numbers in error reports. If, on the other hand, we invoke splitlines on a
		# byte array (? whatever read() returns), this does not happen---we only break at \n, etc.
		# However, we must still decode the resulting lines using the relevant encoding.
		# 121101 -- moved splitting and decoding logic to parseTeXlog, where it belongs.
		
		data = open(self.caller.tex_base + ".log", 'rb').read()		

		errors = []
		warnings = []

		try:
			(errors, warnings) = parseTeXlog.parse_tex_log(data)
			content = ["",""]
			if errors:
				content.append("There were errors in your LaTeX source") 
				content.append("")
				content.extend(errors)
			else:
				content.append("Texification succeeded: no errors!")
				content.append("") 
			if warnings:
				if errors:
					content.append("")
					content.append("There were also warnings.") 
				else:
					content.append("However, there were warnings in your LaTeX source") 
				content.append("")
				content.extend(warnings)
		except Exception as e:
			content=["",""]
			content.append("LaTeXtools could not parse the TeX log file")
			content.append("(actually, we never should have gotten here)")
			content.append("")
			content.append("Python exception: " + repr(e))
			content.append("")
			content.append("Please let me know on GitHub. Thanks!")

		self.caller.output(content)
		self.caller.output("\n\n[Done!]\n")
		self.caller.finish(len(errors) == 0)
Exemple #5
0
	def run ( self ):
		print ("Welcome to thread " + self.getName())
		self.caller.output("[Compiling " + self.caller.file_name + "]")

		env = dict(os.environ)
		if self.caller.path:
			env['PATH'] = self.caller.path

		# Handle custom env variables
		if self.caller.env:
			update_env(env, self.caller.env)

		# Now, iteratively call the builder iterator
		#
		cmd_iterator = self.caller.builder.commands()
		try:
			for (cmd, msg) in cmd_iterator:

				# If there is a message, display it
				if msg:
					self.caller.output(msg)

				# If there is nothing to be done, exit loop
				# (Avoids error with empty cmd_iterator)
				if cmd == "":
					break

				if isinstance(cmd, strbase) or isinstance(cmd, list):
					print(cmd)
					# Now create a Popen object
					try:
						proc = external_command(
							cmd,
							env=env,
							use_texpath=False,
							preexec_fn=os.setsid if self.caller.plat != 'windows' else None,
							cwd=self.caller.tex_dir
						)
					except:
						self.caller.show_output_panel()
						self.caller.output("\n\nCOULD NOT COMPILE!\n\n")
						self.caller.output("Attempted command:")
						self.caller.output(" ".join(cmd))
						self.caller.output("\nBuild engine: " + self.caller.builder.name)
						self.caller.proc = None
						traceback.print_exc()
						return
				# Abundance of caution / for possible future extensions:
				elif isinstance(cmd, subprocess.Popen):
					proc = cmd
				else:
					# don't know what the command is
					continue
				
				# Now actually invoke the command, making sure we allow for killing
				# First, save process handle into caller; then communicate (which blocks)
				with self.caller.proc_lock:
					self.caller.proc = proc
				out, err = proc.communicate()
				self.caller.builder.set_output(out.decode(self.caller.encoding,"ignore"))

				
				# Here the process terminated, but it may have been killed. If so, stop and don't read log
				# Since we set self.caller.proc above, if it is None, the process must have been killed.
				# TODO: clean up?
				with self.caller.proc_lock:
					if not self.caller.proc:
						print (proc.returncode)
						self.caller.output("\n\n[User terminated compilation process]\n")
						self.caller.finish(False)	# We kill, so won't switch to PDF anyway
						return
				# Here we are done cleanly:
				with self.caller.proc_lock:
					self.caller.proc = None
				print ("Finished normally")
				print (proc.returncode)
				# At this point, out contains the output from the current command;
				# we pass it to the cmd_iterator and get the next command, until completion
		except:
			self.caller.show_output_panel()
			self.caller.output("\n\nCOULD NOT COMPILE!\n\n")
			self.caller.output("\nBuild engine: " + self.caller.builder.name)
			self.caller.proc = None
			traceback.print_exc()
			return

		# Clean up
		cmd_iterator.close()

		try:
			# Here we try to find the log file...
			# 1. Check the aux_directory if there is one
			# 2. Check the output_directory if there is one
			# 3. Assume the log file is in the same folder as the main file
			log_file_base = self.caller.tex_base + ".log"
			if self.caller.aux_directory is None:
				if self.caller.output_directory is None:
					log_file = os.path.join(
						self.caller.tex_dir,
						log_file_base
					)
				else:
					log_file = os.path.join(
						self.caller.output_directory,
						log_file_base
					)

					if not os.path.exists(log_file):
						log_file = os.path.join(
							self.caller.tex_dir,
							log_file_base
						)
			else:
				log_file = os.path.join(
					self.caller.aux_directory,
					log_file_base
				)

				if not os.path.exists(log_file):
					if (
						self.caller.output_directory is not None and
						self.caller.output_directory != self.caller.aux_directory
					):
						log_file = os.path.join(
							self.caller.output_directory,
							log_file_base
						)

					if not os.path.exists(log_file):
						log_file = os.path.join(
							self.caller.tex_dir,
							log_file_base
						)

			# CHANGED 12-10-27. OK, here's the deal. We must open in binary mode
			# on Windows because silly MiKTeX inserts ASCII control characters in
			# over/underfull warnings. In particular it inserts EOFs, which
			# stop reading altogether; reading in binary prevents that. However,
			# that's not the whole story: if a FS character is encountered,
			# AND if we invoke splitlines on a STRING, it sadly breaks the line
			# in two. This messes up line numbers in error reports. If, on the
			# other hand, we invoke splitlines on a byte array (? whatever read()
			# returns), this does not happen---we only break at \n, etc.
			# However, we must still decode the resulting lines using the relevant
			# encoding.

			# Note to self: need to think whether we don't want to codecs.open
			# this, too... Also, we may want to move part of this logic to the
			# builder...
			with open(log_file, 'rb') as f:
				data = f.read()
		except IOError:
			traceback.print_exc()

			self.caller.show_output_panel()

			content = ['', 'Could not read log file {0}.log'.format(
				self.caller.tex_base
			), '']
			if out is not None:
				content.extend(['Output from compilation:', '', out.decode('utf-8')])
			if err is not None:
				content.extend(['Errors from compilation:', '', err.decode('utf-8')])
			self.caller.output(content)
			# if we got here, there shouldn't be a PDF at all
			self.caller.finish(False)
		else:
			errors = []
			warnings = []
			badboxes = []

			try:
				(errors, warnings, badboxes) = parseTeXlog.parse_tex_log(
					data, self.caller.tex_dir
				)
				content = [""]
				if errors:
					content.append("Errors:")
					content.append("")
					content.extend(errors)
				else:
					content.append("No errors.")
				if warnings:
					if errors:
						content.extend(["", "Warnings:"])
					else:
						content[-1] = content[-1] + " Warnings:"
					content.append("")
					content.extend(warnings)
				else:
					if errors:
						content.append("")
						content.append("No warnings.")
					else:
						content[-1] = content[-1] + " No warnings."

				if badboxes and self.caller.display_bad_boxes:
					if warnings or errors:
						content.extend(["", "Bad Boxes:"])
					else:
						content[-1] = content[-1] + " Bad Boxes:"
					content.append("")
					content.extend(badboxes)
				else:
					if self.caller.display_bad_boxes:
						if errors or warnings:
							content.append("")
							content.append("No bad boxes.")
						else:
							content[-1] = content[-1] + " No bad boxes."

				show_panel = {
					"always": False,
					"no_errors": bool(errors),
					"no_warnings": bool(errors or warnings),
					"no_badboxes": bool(
						errors or warnings or
						(self.caller.display_bad_boxes and badboxes)),
					"never": True
				}.get(self.caller.hide_panel_level, bool(errors or warnings))

				if show_panel:
					self.caller.progress_indicator.success_message = "Build completed"
					self.caller.show_output_panel(force=True)
				else:
					message = "Build completed"
					if errors:
						message += " with errors"
					if warnings:
						if errors:
							if badboxes and self.caller.display_bad_boxes:
								message += ","
							else:
								message += " and"
						else:
							message += " with"
						message += " warnings"
					if badboxes and self.caller.display_bad_boxes:
						if errors or warnings:
							message += " and"
						else:
							message += " with"
						message += " bad boxes"

					self.caller.progress_indicator.success_message = message
			except Exception as e:
				self.caller.show_output_panel()
				content = ["", ""]
				content.append(
					"LaTeXTools could not parse the TeX log file {0}".format(
						log_file
					)
				)
				content.append("(actually, we never should have gotten here)")
				content.append("")
				content.append("Python exception: {0!r}".format(e))
				content.append("")
				content.append(
					"The full error description can be found on the console."
				)
				content.append("Please let us know on GitHub. Thanks!")

				traceback.print_exc()

			self.caller.output(content)
			self.caller.output("\n\n[Done!]\n")

			if _HAS_PHANTOMS:
				self.caller.errors = locals().get("errors", [])
				self.caller.warnings = locals().get("warnings", [])
				self.caller.badboxes = locals().get("badboxes", [])

			self.caller.finish(len(errors) == 0)
Exemple #6
0
    def run(self):
        print("Welcome to thread " + self.getName())
        self.caller.output("[Compiling " + self.caller.file_name + "]")

        env = dict(os.environ)
        if self.caller.path:
            env['PATH'] = self.caller.path

        # Handle custom env variables
        if self.caller.env:
            update_env(env, self.caller.env)

        # Now, iteratively call the builder iterator
        #
        cmd_iterator = self.caller.builder.commands()
        try:
            for (cmd, msg) in cmd_iterator:

                # If there is a message, display it
                if msg:
                    self.caller.output(msg)

                # If there is nothing to be done, exit loop
                # (Avoids error with empty cmd_iterator)
                if cmd == "":
                    break

                if isinstance(cmd, strbase) or isinstance(cmd, list):
                    # Now create a Popen object
                    try:
                        proc = external_command(
                            cmd,
                            env=env,
                            use_texpath=False,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT,
                            preexec_fn=os.setsid
                            if self.caller.plat != 'windows' else None,
                            cwd=self.caller.tex_dir)
                    except:
                        self.caller.show_output_panel()
                        self.caller.output("\n\nCOULD NOT COMPILE!\n\n")
                        self.caller.output("Attempted command:")
                        self.caller.output(" ".join(cmd))
                        self.caller.output("\nBuild engine: " +
                                           self.caller.builder.name)
                        self.caller.proc = None
                        traceback.print_exc()
                        return
                # Abundance of caution / for possible future extensions:
                elif isinstance(cmd, subprocess.Popen):
                    proc = cmd
                else:
                    # don't know what the command is
                    continue

                # Now actually invoke the command, making sure we allow for killing
                # First, save process handle into caller; then communicate (which blocks)
                with self.caller.proc_lock:
                    self.caller.proc = proc
                out, err = proc.communicate()
                self.caller.builder.set_output(
                    out.decode(self.caller.encoding, "ignore"))

                # Here the process terminated, but it may have been killed. If so, stop and don't read log
                # Since we set self.caller.proc above, if it is None, the process must have been killed.
                # TODO: clean up?
                with self.caller.proc_lock:
                    if not self.caller.proc:
                        print(proc.returncode)
                        self.caller.output(
                            "\n\n[User terminated compilation process]\n")
                        self.caller.finish(
                            False)  # We kill, so won't switch to PDF anyway
                        return
                # Here we are done cleanly:
                with self.caller.proc_lock:
                    self.caller.proc = None
                print("Finished normally")
                print(proc.returncode)
                # At this point, out contains the output from the current command;
                # we pass it to the cmd_iterator and get the next command, until completion
        except:
            self.caller.show_output_panel()
            self.caller.output("\n\nCOULD NOT COMPILE!\n\n")
            self.caller.output("\nBuild engine: " + self.caller.builder.name)
            self.caller.proc = None
            traceback.print_exc()
            return

        # Clean up
        cmd_iterator.close()

        try:
            # Here we try to find the log file...
            # 1. Check the aux_directory if there is one
            # 2. Check the output_directory if there is one
            # 3. Assume the log file is in the same folder as the main file
            log_file_base = self.caller.tex_base + ".log"
            if self.caller.aux_directory is None:
                if self.caller.output_directory is None:
                    log_file = os.path.join(self.caller.tex_dir, log_file_base)
                else:
                    log_file = os.path.join(self.caller.output_directory,
                                            log_file_base)

                    if not os.path.exists(log_file):
                        log_file = os.path.join(self.caller.tex_dir,
                                                log_file_base)
            else:
                log_file = os.path.join(self.caller.aux_directory,
                                        log_file_base)

                if not os.path.exists(log_file):
                    if (self.caller.output_directory is not None
                            and self.caller.output_directory !=
                            self.caller.aux_directory):
                        log_file = os.path.join(self.caller.output_directory,
                                                log_file_base)

                    if not os.path.exists(log_file):
                        log_file = os.path.join(self.caller.tex_dir,
                                                log_file_base)

            # CHANGED 12-10-27. OK, here's the deal. We must open in binary mode
            # on Windows because silly MiKTeX inserts ASCII control characters in
            # over/underfull warnings. In particular it inserts EOFs, which
            # stop reading altogether; reading in binary prevents that. However,
            # that's not the whole story: if a FS character is encountered,
            # AND if we invoke splitlines on a STRING, it sadly breaks the line
            # in two. This messes up line numbers in error reports. If, on the
            # other hand, we invoke splitlines on a byte array (? whatever read()
            # returns), this does not happen---we only break at \n, etc.
            # However, we must still decode the resulting lines using the relevant
            # encoding.

            # Note to self: need to think whether we don't want to codecs.open
            # this, too... Also, we may want to move part of this logic to the
            # builder...
            with open(log_file, 'rb') as f:
                data = f.read()
        except IOError:
            traceback.print_exc()

            self.caller.show_output_panel()

            content = [
                '',
                'Could not read log file {0}.log'.format(self.caller.tex_base),
                ''
            ]
            if out is not None:
                content.extend(
                    ['Output from compilation:', '',
                     out.decode('utf-8')])
            if err is not None:
                content.extend(
                    ['Errors from compilation:', '',
                     err.decode('utf-8')])
            self.caller.output(content)
            # if we got here, there shouldn't be a PDF at all
            self.caller.finish(False)
        else:
            errors = []
            warnings = []
            badboxes = []

            try:
                (errors, warnings,
                 badboxes) = parseTeXlog.parse_tex_log(data,
                                                       self.caller.tex_dir)
                content = [""]
                if errors:
                    content.append("Errors:")
                    content.append("")
                    content.extend(errors)
                else:
                    content.append("No errors.")
                if warnings:
                    if errors:
                        content.extend(["", "Warnings:"])
                    else:
                        content[-1] = content[-1] + " Warnings:"
                    content.append("")
                    content.extend(warnings)
                else:
                    if errors:
                        content.append("")
                        content.append("No warnings.")
                    else:
                        content[-1] = content[-1] + " No warnings."

                if badboxes and self.caller.display_bad_boxes:
                    if warnings or errors:
                        content.extend(["", "Bad Boxes:"])
                    else:
                        content[-1] = content[-1] + " Bad Boxes:"
                    content.append("")
                    content.extend(badboxes)
                else:
                    if self.caller.display_bad_boxes:
                        if errors or warnings:
                            content.append("")
                            content.append("No bad boxes.")
                        else:
                            content[-1] = content[-1] + " No bad boxes."

                content.append("")
                content.append(log_file +
                               ":1: Double-click here to open the full log.")

                show_panel = {
                    "always":
                    False,
                    "no_errors":
                    bool(errors),
                    "no_warnings":
                    bool(errors or warnings),
                    "no_badboxes":
                    bool(errors or warnings
                         or (self.caller.display_bad_boxes and badboxes)),
                    "never":
                    True
                }.get(self.caller.hide_panel_level, bool(errors or warnings))

                if show_panel:
                    self.caller.progress_indicator.success_message = "Build completed"
                    self.caller.show_output_panel(force=True)
                else:
                    message = "Build completed"
                    if errors:
                        message += " with errors"
                    if warnings:
                        if errors:
                            if badboxes and self.caller.display_bad_boxes:
                                message += ","
                            else:
                                message += " and"
                        else:
                            message += " with"
                        message += " warnings"
                    if badboxes and self.caller.display_bad_boxes:
                        if errors or warnings:
                            message += " and"
                        else:
                            message += " with"
                        message += " bad boxes"

                    self.caller.progress_indicator.success_message = message
            except Exception as e:
                self.caller.show_output_panel()
                content = ["", ""]
                content.append(
                    "LaTeXTools could not parse the TeX log file {0}".format(
                        log_file))
                content.append("(actually, we never should have gotten here)")
                content.append("")
                content.append("Python exception: {0!r}".format(e))
                content.append("")
                content.append(
                    "The full error description can be found on the console.")
                content.append("Please let us know on GitHub. Thanks!")

                traceback.print_exc()

            self.caller.output(content)
            self.caller.output("\n\n[Done!]\n")

            if _HAS_PHANTOMS:
                self.caller.errors = locals().get("errors", [])
                self.caller.warnings = locals().get("warnings", [])
                self.caller.badboxes = locals().get("badboxes", [])

            self.caller.finish(len(errors) == 0)
Exemple #7
0
	def run ( self ):
		print ("Welcome to thread " + self.getName())
		self.caller.output("[Compiling " + self.caller.file_name + "]")

		# Handle custom env variables
		if self.caller.env:
			old_env = os.environ;
			if not _ST3:
				os.environ.update(dict((k.encode(sys.getfilesystemencoding()), v) for (k, v) in self.caller.env.items()))
			else:
				os.environ.update(self.caller.env.items());

		# Handle path; copied from exec.py
		if self.caller.path:
			# if we had an env, the old path is already backuped in the env
			if not self.caller.env:
				old_path = os.environ["PATH"]
			# The user decides in the build system  whether he wants to append $PATH
			# or tuck it at the front: "$PATH;C:\\new\\path", "C:\\new\\path;$PATH"
			# Handle differently in Python 2 and 3, to be safe:
			if not _ST3:
				os.environ["PATH"] = os.path.expandvars(self.caller.path).encode(sys.getfilesystemencoding())
			else:
				os.environ["PATH"] = os.path.expandvars(self.caller.path)

		# Set up Windows-specific parameters
		if self.caller.plat == "windows":
			# make sure console does not come up
			startupinfo = subprocess.STARTUPINFO()
			startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW

		# Now, iteratively call the builder iterator
		#
		cmd_iterator = self.caller.builder.commands()
		for (cmd, msg) in cmd_iterator:

			# If there is a message, display it
			if msg:
				self.caller.output(msg)

			# If there is nothing to be done, exit loop
			# (Avoids error with empty cmd_iterator)
			if cmd == "":
				break
			print(cmd)
			# Now create a Popen object
			try:
				if self.caller.plat == "windows":
					proc = subprocess.Popen(cmd, startupinfo=startupinfo, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
				elif self.caller.plat == "osx":
					# Temporary (?) fix for Yosemite: pass environment
					proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, env=os.environ)
				else: # Must be linux
					proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
			except:
				self.caller.output("\n\nCOULD NOT COMPILE!\n\n")
				self.caller.output("Attempted command:")
				self.caller.output(" ".join(cmd))
				self.caller.output("\nBuild engine: " + self.caller.builder.name)
				self.caller.proc = None
				if self.caller.env:
					os.environ = old_env
				elif self.caller.path:
					os.environ["PATH"] = old_path
				return
			
			# Now actually invoke the command, making sure we allow for killing
			# First, save process handle into caller; then communicate (which blocks)
			self.caller.proc = proc
			out, err = proc.communicate()
			self.caller.builder.set_output(out.decode(self.caller.encoding,"ignore"))

			# Here the process terminated, but it may have been killed. If so, stop and don't read log
			# Since we set self.caller.proc above, if it is None, the process must have been killed.
			# TODO: clean up?
			if not self.caller.proc:
				print (proc.returncode)
				self.caller.output("\n\n[User terminated compilation process]\n")
				self.caller.finish(False)	# We kill, so won't switch to PDF anyway
				return
			# Here we are done cleanly:
			self.caller.proc = None
			print ("Finished normally")
			print (proc.returncode)

			# At this point, out contains the output from the current command;
			# we pass it to the cmd_iterator and get the next command, until completion

		# Clean up
		cmd_iterator.close()

		# restore env or path if needed
		if self.caller.env:
			os.environ = old_env
		elif self.caller.path:
			os.environ["PATH"] = old_path

		# CHANGED 12-10-27. OK, here's the deal. We must open in binary mode on Windows
		# because silly MiKTeX inserts ASCII control characters in over/underfull warnings.
		# In particular it inserts EOFs, which stop reading altogether; reading in binary
		# prevents that. However, that's not the whole story: if a FS character is encountered,
		# AND if we invoke splitlines on a STRING, it sadly breaks the line in two. This messes up
		# line numbers in error reports. If, on the other hand, we invoke splitlines on a
		# byte array (? whatever read() returns), this does not happen---we only break at \n, etc.
		# However, we must still decode the resulting lines using the relevant encoding.
		# 121101 -- moved splitting and decoding logic to parseTeXlog, where it belongs.
		
		# Note to self: need to think whether we don't want to codecs.open this, too...
		# Also, we may want to move part of this logic to the builder...
		data = open(self.caller.tex_base + ".log", 'rb').read()		

		errors = []
		warnings = []

		try:
			(errors, warnings) = parseTeXlog.parse_tex_log(data)
			content = [""]
			if errors:
				content.append("Errors:") 
				content.append("")
				content.extend(errors)
			else:
				content.append("No errors.")
			if warnings:
				if errors:
					content.extend(["", "Warnings:"])
				else:
					content[-1] = content[-1] + " Warnings:" 
				content.append("")
				content.extend(warnings)
			else:
				content.append("")
		except Exception as e:
			content=["",""]
			content.append("LaTeXtools could not parse the TeX log file")
			content.append("(actually, we never should have gotten here)")
			content.append("")
			content.append("Python exception: " + repr(e))
			content.append("")
			content.append("Please let me know on GitHub. Thanks!")

		self.caller.output(content)
		self.caller.output("\n\n[Done!]\n")
		self.caller.finish(len(errors) == 0)