mirror of
				https://github.com/notepad-plus-plus/notepad-plus-plus.git
				synced 2025-10-31 11:34:05 +01:00 
			
		
		
		
	[BUG_FIXED] (Author: Dave Brotherstone) Fix scintilla crash bug while closing a document. git-svn-id: svn://svn.tuxfamily.org/svnroot/notepadplus/repository/trunk@1104 f5eea248-9336-0410-98b8-ebc06183d4e3
		
			
				
	
	
		
			330 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			330 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import distutils.sysconfig
 | |
| import getopt
 | |
| import glob
 | |
| import os
 | |
| import platform
 | |
| import shutil
 | |
| import subprocess
 | |
| import stat
 | |
| import sys
 | |
| 
 | |
| sys.path.append(os.path.join("..", "ScintillaEdit"))
 | |
| import WidgetGen
 | |
| 
 | |
| scintillaDirectory = "../.."
 | |
| scintillaScriptsDirectory = os.path.join(scintillaDirectory, "scripts")
 | |
| sys.path.append(scintillaScriptsDirectory)
 | |
| from FileGenerator import GenerateFile
 | |
| 
 | |
| # Decide up front which platform, treat anything other than Windows or OS X as Linux
 | |
| PLAT_WINDOWS = platform.system() == "Windows"
 | |
| PLAT_DARWIN = platform.system() == "Darwin"
 | |
| PLAT_LINUX = not (PLAT_DARWIN or PLAT_WINDOWS)
 | |
| 
 | |
| def IsFileNewer(name1, name2):
 | |
| 	""" Returns whether file with name1 is newer than file with name2.  Returns 1
 | |
| 	if name2 doesn't exist. """
 | |
| 
 | |
| 	if not os.path.exists(name1):
 | |
| 		return 0
 | |
| 
 | |
| 	if not os.path.exists(name2):
 | |
| 		return 1
 | |
| 
 | |
| 	mod_time1 = os.stat(name1)[stat.ST_MTIME]
 | |
| 	mod_time2 = os.stat(name2)[stat.ST_MTIME]
 | |
| 	return (mod_time1 > mod_time2)
 | |
| 
 | |
| def textFromRun(args):
 | |
| 	(stdoutdata, stderrdata) = subprocess.Popen(args, shell=True, stdout=subprocess.PIPE).communicate()
 | |
| 	return stdoutdata
 | |
| 
 | |
| def runProgram(args, exitOnFailure):
 | |
| 	print(" ".join(args))
 | |
| 	retcode = subprocess.call(" ".join(args), shell=True, stderr=subprocess.STDOUT)
 | |
| 	if retcode:
 | |
| 		print("Failed in " + " ".join(args) + " return code = " + str(retcode))
 | |
| 		if exitOnFailure:
 | |
| 			sys.exit()
 | |
| 
 | |
| def usage():
 | |
| 	print("sepbuild.py [-h|--help][-c|--clean][-u|--underscore-names]")
 | |
| 	print("")
 | |
| 	print("Generate PySide wappers and build them.")
 | |
| 	print("")
 | |
| 	print("options:")
 | |
| 	print("")
 | |
| 	print("-c --clean remove all object and generated files")
 | |
| 	print("-b --pyside-base  Location of the PySide+Qt4 sandbox to use")
 | |
| 	print("-h --help  display this text")
 | |
| 	print("-d --debug=yes|no  force debug build (or non-debug build)")
 | |
| 	print("-u --underscore-names  use method_names consistent with GTK+ standards")
 | |
| 
 | |
| modifyFunctionElement = """		<modify-function signature="%s">%s
 | |
| 		</modify-function>"""
 | |
| 
 | |
| injectCode = """
 | |
| 			<inject-code class="target" position="beginning">%s
 | |
| 			</inject-code>"""
 | |
| 
 | |
| injectCheckN = """
 | |
| 				if (!cppArg%d) {
 | |
| 					PyErr_SetString(PyExc_ValueError, "Null string argument");
 | |
| 					return 0;
 | |
| 				}"""
 | |
| 
 | |
| def methodSignature(name, v, options):
 | |
| 	argTypes = ""
 | |
| 	p1Type = WidgetGen.cppAlias(v["Param1Type"])
 | |
| 	if p1Type:
 | |
| 		argTypes = argTypes + p1Type
 | |
| 	p2Type = WidgetGen.cppAlias(v["Param2Type"])
 | |
| 	if p2Type and v["Param2Type"] != "stringresult":
 | |
| 		if p1Type:
 | |
| 			argTypes = argTypes + ", "
 | |
| 		argTypes = argTypes + p2Type
 | |
| 	methodName = WidgetGen.normalisedName(name, options, v["FeatureType"])
 | |
| 	constDeclarator = " const" if v["FeatureType"] == "get" else ""
 | |
| 	return methodName + "(" + argTypes + ")" + constDeclarator
 | |
| 
 | |
| def printTypeSystemFile(f, options):
 | |
| 	out = []
 | |
| 	for name in f.order:
 | |
| 		v = f.features[name]
 | |
| 		if v["Category"] != "Deprecated":
 | |
| 			feat = v["FeatureType"]
 | |
| 			if feat in ["fun", "get", "set"]:
 | |
| 				checks = ""
 | |
| 				if v["Param1Type"] == "string":
 | |
| 					checks = checks + (injectCheckN % 0)
 | |
| 				if v["Param2Type"] == "string":
 | |
| 					if v["Param1Type"] == "":	# Only arg 2 -> treat as first
 | |
| 						checks = checks + (injectCheckN % 0)
 | |
| 					else:
 | |
| 						checks = checks + (injectCheckN % 1)
 | |
| 				if checks:
 | |
| 					inject = injectCode % checks
 | |
| 					out.append(modifyFunctionElement % (methodSignature(name, v, options), inject))
 | |
| 				#if v["Param1Type"] == "string":
 | |
| 				#	out.append("<string-xml>" + name + "</string-xml>\n")
 | |
| 	return out
 | |
| 
 | |
| def doubleBackSlashes(s):
 | |
| 	# Quote backslashes so qmake does not produce warnings
 | |
| 	return s.replace("\\", "\\\\")
 | |
| 
 | |
| class SepBuilder:
 | |
| 	def __init__(self):
 | |
| 		# Discover configuration parameters
 | |
| 		self.ScintillaEditIncludes = [".", "../ScintillaEdit", "../ScintillaEditBase", "../../include"]
 | |
| 		if PLAT_WINDOWS:
 | |
| 			self.MakeCommand = "nmake"
 | |
| 			self.MakeTarget = "release"
 | |
| 		else:
 | |
| 			self.MakeCommand = "make"
 | |
| 			self.MakeTarget = ""
 | |
| 
 | |
| 		if PLAT_DARWIN:
 | |
| 			self.QMakeOptions = "-spec macx-g++"
 | |
| 		else:
 | |
| 			self.QMakeOptions = ""
 | |
| 
 | |
| 		# Default to debug build if running in a debug build interpreter
 | |
| 		self.DebugBuild = hasattr(sys, 'getobjects')
 | |
| 
 | |
| 		# Python
 | |
| 		self.PyVersion = "%d.%d" % sys.version_info[:2]
 | |
| 		self.PyVersionSuffix = distutils.sysconfig.get_config_var("VERSION")
 | |
| 		self.PyIncludes = distutils.sysconfig.get_python_inc()
 | |
| 		self.PyPrefix = distutils.sysconfig.get_config_var("prefix")
 | |
| 		self.PyLibDir = distutils.sysconfig.get_config_var(
 | |
| 			("LIBDEST" if sys.platform == 'win32' else "LIBDIR"))
 | |
| 
 | |
| 		# Scintilla
 | |
| 		with open("../../version.txt") as f:
 | |
| 			version = f.read()
 | |
| 			self.ScintillaVersion = version[0] + '.' + version[1] + '.' + version[2]
 | |
| 
 | |
| 		# Find out what qmake is called
 | |
| 		self.QMakeCommand = "qmake"
 | |
| 		if not PLAT_WINDOWS:
 | |
| 			# On Unix qmake may not be present but qmake-qt4 may be so check
 | |
| 			pathToQMake = textFromRun("which qmake-qt4 || which qmake").rstrip()
 | |
| 			self.QMakeCommand = os.path.basename(pathToQMake)
 | |
| 
 | |
| 		# Qt default location from qmake
 | |
| 		self._SetQtIncludeBase(textFromRun(self.QMakeCommand + " -query QT_INSTALL_HEADERS").rstrip())
 | |
| 
 | |
| 		# PySide default location
 | |
| 		# No standard for installing PySide development headers and libs on Windows so
 | |
| 		# choose /usr to be like Linux
 | |
| 		self._setPySideBase('\\usr' if PLAT_WINDOWS else '/usr')
 | |
| 
 | |
| 		self.ProInclude = "sepbuild.pri"
 | |
| 
 | |
| 		self.qtStyleInterface = True
 | |
| 
 | |
| 	def _setPySideBase(self, base):
 | |
| 		
 | |
| 		self.PySideBase = base
 | |
| 		if PLAT_LINUX:
 | |
| 			self.PySideTypeSystem = textFromRun("pkg-config --variable=typesystemdir pyside").rstrip()
 | |
| 			self.PySideIncludeBase = textFromRun("pkg-config --variable=includedir pyside").rstrip()
 | |
| 			self.ShibokenIncludeBase = textFromRun("pkg-config --variable=includedir shiboken").rstrip()
 | |
| 		else:
 | |
| 			self.PySideTypeSystem = os.path.join(self.PySideBase, "share", "PySide", "typesystems")
 | |
| 			self.ShibokenIncludeBase = os.path.join(self.PySideBase, "include", "shiboken")
 | |
| 			self.PySideIncludeBase = os.path.join(self.PySideBase, "include", "PySide")
 | |
| 
 | |
| 		self.PySideIncludes = [
 | |
| 			self.ShibokenIncludeBase,
 | |
| 			self.PySideIncludeBase,
 | |
| 			os.path.join(self.PySideIncludeBase, "QtCore"),
 | |
| 			os.path.join(self.PySideIncludeBase, "QtGui")]
 | |
| 
 | |
| 		self.PySideLibDir = os.path.join(self.PySideBase, "lib")
 | |
| 		self.AllIncludes = os.pathsep.join(self.QtIncludes + self.ScintillaEditIncludes + self.PySideIncludes)
 | |
| 
 | |
| 		self.ShibokenGenerator = "shiboken"
 | |
| 		# Is this still needed? It doesn't work with latest shiboken sources
 | |
| 		#if PLAT_DARWIN:
 | |
| 		#	# On OS X, can not automatically find Shiboken dylib so provide a full path
 | |
| 		#	self.ShibokenGenerator = os.path.join(self.PySideLibDir, "generatorrunner", "shiboken")
 | |
| 
 | |
| 	def generateAPI(self, args):
 | |
| 		os.chdir(os.path.join("..", "ScintillaEdit"))
 | |
| 		if not self.qtStyleInterface:
 | |
| 			args.insert(0, '--underscore-names')
 | |
| 		WidgetGen.main(args)
 | |
| 		f = WidgetGen.readInterface(False)
 | |
| 		os.chdir(os.path.join("..", "ScintillaEditPy"))
 | |
| 		options = {"qtStyle": self.qtStyleInterface}
 | |
| 		GenerateFile("typesystem_ScintillaEdit.xml.template", "typesystem_ScintillaEdit.xml", 
 | |
| 			"<!-- ", True, printTypeSystemFile(f, options))
 | |
| 
 | |
| 	def runGenerator(self):
 | |
| 		generatorrunner = "shiboken"
 | |
| 		for name in ('shiboken', 'generatorrunner'):
 | |
| 			if PLAT_WINDOWS:
 | |
| 				name += '.exe'
 | |
| 			name = os.path.join(self.PySideBase, "bin", name)
 | |
| 			if os.path.exists(name):
 | |
| 				generatorrunner = name
 | |
| 				break
 | |
| 				
 | |
| 		args = [
 | |
| 			generatorrunner,
 | |
| 			"--generator-set=" + self.ShibokenGenerator,
 | |
| 			"global.h ",
 | |
| 			"--avoid-protected-hack",
 | |
| 			"--enable-pyside-extensions",
 | |
| 			"--include-paths=" + self.AllIncludes,
 | |
| 			"--typesystem-paths=" + self.PySideTypeSystem,
 | |
| 			"--output-directory=.",
 | |
| 			"typesystem_ScintillaEdit.xml"]
 | |
| 		print(" ".join(args))
 | |
| 		retcode = subprocess.call(" ".join(args), shell=True, stderr=subprocess.STDOUT)
 | |
| 		if retcode:
 | |
| 			print("Failed in generatorrunner", retcode)
 | |
| 			sys.exit()
 | |
| 
 | |
| 	def writeVariables(self):
 | |
| 		# Write variables needed into file to be included from project so it does not have to discover much
 | |
| 		with open(self.ProInclude, "w") as f:
 | |
| 			f.write("SCINTILLA_VERSION=" + self.ScintillaVersion + "\n")
 | |
| 			f.write("PY_VERSION=" + self.PyVersion + "\n")
 | |
| 			f.write("PY_VERSION_SUFFIX=" + self.PyVersionSuffix + "\n")
 | |
| 			f.write("PY_PREFIX=" + doubleBackSlashes(self.PyPrefix) + "\n")
 | |
| 			f.write("PY_INCLUDES=" + doubleBackSlashes(self.PyIncludes) + "\n")
 | |
| 			f.write("PY_LIBDIR=" + doubleBackSlashes(self.PyLibDir) + "\n")
 | |
| 			f.write("PYSIDE_INCLUDES=" + doubleBackSlashes(self.PySideIncludeBase) + "\n")
 | |
| 			f.write("PYSIDE_LIB=" + doubleBackSlashes(self.PySideLibDir) + "\n")
 | |
| 			f.write("SHIBOKEN_INCLUDES=" + doubleBackSlashes(self.ShibokenIncludeBase) + "\n")
 | |
| 			if self.DebugBuild:
 | |
| 				f.write("CONFIG += debug\n")
 | |
| 			else:
 | |
| 				f.write("CONFIG += release\n")
 | |
| 
 | |
| 	def make(self):
 | |
| 		runProgram([self.QMakeCommand, self.QMakeOptions], exitOnFailure=True)
 | |
| 		runProgram([self.MakeCommand, self.MakeTarget], exitOnFailure=True)
 | |
| 
 | |
| 	def cleanEverything(self):
 | |
| 		self.generateAPI(["--clean"])
 | |
| 		runProgram([self.MakeCommand, "distclean"], exitOnFailure=False)
 | |
| 		filesToRemove = [self.ProInclude, "typesystem_ScintillaEdit.xml", 
 | |
| 			"../../bin/ScintillaEditPy.so", "../../bin/ScintillaConstants.py"]
 | |
| 		for file in filesToRemove:
 | |
| 			try:
 | |
| 				os.remove(file)
 | |
| 			except OSError:
 | |
| 				pass
 | |
| 		for logFile in glob.glob("*.log"):
 | |
| 			try:
 | |
| 				os.remove(logFile)
 | |
| 			except OSError:
 | |
| 				pass
 | |
| 		shutil.rmtree("debug", ignore_errors=True)
 | |
| 		shutil.rmtree("release", ignore_errors=True)
 | |
| 		shutil.rmtree("ScintillaEditPy", ignore_errors=True)
 | |
| 
 | |
| 	def buildEverything(self):
 | |
| 		cleanGenerated = False
 | |
| 		opts, args = getopt.getopt(sys.argv[1:], "hcdub",
 | |
| 					["help", "clean", "debug=", 
 | |
| 					"underscore-names", "pyside-base="])
 | |
| 		for opt, arg in opts:
 | |
| 			if opt in ("-h", "--help"):
 | |
| 				usage()
 | |
| 				sys.exit()
 | |
| 			elif opt in ("-c", "--clean"):
 | |
| 				cleanGenerated = True
 | |
| 			elif opt in ("-d", "--debug"):
 | |
| 				self.DebugBuild = (arg == '' or arg.lower() == 'yes')
 | |
| 				if self.DebugBuild and sys.platform == 'win32':
 | |
| 					self.MakeTarget = 'debug'
 | |
| 			elif opt in ("-b", '--pyside-base'):
 | |
| 				self._SetQtIncludeBase(os.path.join(os.path.normpath(arg), 'include'))
 | |
| 				self._setPySideBase(os.path.normpath(arg))
 | |
| 			elif opt in ("-u", "--underscore-names"):
 | |
| 				self.qtStyleInterface = False
 | |
| 
 | |
| 		if cleanGenerated:
 | |
| 			self.cleanEverything()
 | |
| 		else:
 | |
| 			self.writeVariables()
 | |
| 			self.generateAPI([""])
 | |
| 			self.runGenerator()
 | |
| 			self.make()
 | |
| 			self.copyScintillaConstants()
 | |
| 			
 | |
| 	def copyScintillaConstants(self):
 | |
| 		
 | |
| 		orig = 'ScintillaConstants.py'
 | |
| 		dest = '../../bin/' + orig
 | |
| 		if IsFileNewer(dest, orig):
 | |
| 			return
 | |
| 		
 | |
| 		f = open(orig, 'r')
 | |
| 		contents = f.read()
 | |
| 		f.close()
 | |
| 		
 | |
| 		f = open(dest, 'w')
 | |
| 		f.write(contents)
 | |
| 		f.close()
 | |
| 
 | |
| 	def _SetQtIncludeBase(self, base):
 | |
| 
 | |
| 		self.QtIncludeBase = base
 | |
| 		self.QtIncludes = [self.QtIncludeBase] + [os.path.join(self.QtIncludeBase, sub) for sub in ["QtCore", "QtGui"]]
 | |
| 		# Set path so correct qmake is found
 | |
| 		path = os.environ.get('PATH', '').split(os.pathsep)
 | |
| 		qt_bin_dir = os.path.join(os.path.dirname(base), 'bin')
 | |
| 		if qt_bin_dir not in path:
 | |
| 			path.insert(0, qt_bin_dir)
 | |
| 			os.environ['PATH'] = os.pathsep.join(path)
 | |
| 
 | |
| if __name__ == "__main__":
 | |
| 	sepBuild = SepBuilder()
 | |
| 	sepBuild.buildEverything()
 |