1from shutil import copyfile, rmtree
2from string import Template
3import os.path
4import re
5import sys
6import autogen
7
8class ForwardHeaderGenerator():
9	def __init__( self, copy, path, includepath, srcpath, project, subprojects, prefix,
10				prefixed, cleanIncludeDir = True, additionalHeaders = {} ):
11		self.copy = copy
12		self.path = path
13		self.includepath = includepath
14		self.srcpath = srcpath
15		self.project = project
16		self.subprojects = subprojects
17		self.prefix = prefix
18		self.prefixed = prefixed
19		self.cleanIncludeDir = cleanIncludeDir
20		self.additionalHeaders = additionalHeaders
21		self.__projectFile = ""
22
23	def run( self ):
24		self.createProject()
25
26	def _isValidHeaderFile( self, filename ):
27		if ( filename.endswith( ".h" ) ):
28			if filename.startswith( "moc_" ):
29				return False
30			if filename.startswith( "ui_" ):
31				return False
32			if filename.startswith( "qrc_" ):
33				return False;
34			if filename.endswith( "_p.h" ):
35				return False
36			return True
37		else:
38			return False
39
40	def _suggestedHeaderNames( self, project, header ):
41		regex = re.compile( "(?:class\s+[{0}|{1}][_0-9A-Z]*_EXPORT|MAKEINCLUDES_EXPORT)\s+([a-zA-Z_][A-Za-z0-9_]*)"
42		                     .format( project.upper(), self.project.upper() ) )
43		regex2 = re.compile( "(?:class\s+MAKEINCLUDES_EXPORT)\s+([a-zA-Z_][A-Za-z0-9_]*)" )
44		regex3 = re.compile( "(?:\\\\file)\s+([a-zA-Z_][A-Za-z0-9_]*)" )
45
46		f = open( header, "r" )
47		classNames = set()
48
49		for line in f.readlines():
50			line = line.strip()
51
52			className = None
53			noPrefix = False
54			if ( regex.match( line ) ):
55				className = regex.match( line ).groups()[0]
56				noPrefix = False
57			else:
58				if regex2.match( line ):
59					className = regex2.match( line ).groups()[0]
60					noPrefix = False
61				else:
62					if ( None != regex3.search( line ) ):
63						className = regex3.search( line ).groups()[0]
64						noPrefix = True
65
66			if not className:
67				continue
68
69			if self.prefixed and not noPrefix:
70				className = project + className
71
72			classNames.add( className )
73
74		f.close()
75
76		return classNames
77
78	def _addForwardHeader( self, targetPath, fileName, projectFile ):
79		INCLUDE_STR = "#include \"{0}\""
80		newHeader = open( targetPath, "wb" )
81		newHeader.write( INCLUDE_STR.format( fileName ) + os.linesep )
82		newHeader.close()
83
84		basename = os.path.basename( targetPath )
85		projectFile.write( basename + " \\" + os.linesep )
86
87	def _createForwardHeader( self, header, projectFile, project ):
88		path = os.path.dirname( header )
89		basename = os.path.basename( header )
90		classNames = self._suggestedHeaderNames( project, header )
91
92		if len( classNames ) > 0:
93			for classname in classNames:
94				fHeaderName = os.path.abspath( path + "/" + classname )
95				self._addForwardHeader( fHeaderName, basename, projectFile )
96
97			projectFile.write( basename + " \\" + os.linesep )
98		elif not basename in self.additionalHeaders.values(): # only create "foo" for "foo.h" if additionalHeaders doesn't overrides it
99			sanitizedBasename = basename.replace( ".h", "" )
100
101			#fHeaderName = os.path.abspath( self.includepath + "/" + sanitizedBasename )
102			#self._addForwardHeader( fHeaderName, basename, self.__projectFile )
103
104			fHeaderNameProjectDir = os.path.dirname( os.path.abspath( header ) ) + "/" + sanitizedBasename;
105			self._addForwardHeader( fHeaderNameProjectDir, "{0}".format( basename ), projectFile )
106			projectFile.write( basename + " \\" + os.linesep )
107
108	def createProject( self ):
109		if ( not os.path.exists( self.path ) ):
110			errStr = Template( "Error, the directory $DIR does not exist!" )
111			errStr = errStr.substitute( DIR = self.path )
112			raise BaseException( errStr )
113
114		if self.cleanIncludeDir and os.path.exists( self.includepath ):
115			rmtree( self.includepath )
116		if not os.path.exists( self.includepath ):
117			os.mkdir( self.includepath )
118
119		if autogen.policyVersion() >= 2:
120			includeProjectName = os.path.basename( self.includepath.rstrip( "/" ) )
121		else:
122			includeProjectName = self.project
123		profilename = os.path.abspath( self.includepath ) + "/" + includeProjectName + ".pro"
124		projectFile = open( profilename, "wb" )
125		self.__projectFile = projectFile
126		lines = []
127		lines.append( "TEMPLATE = subdirs" + os.linesep )
128		lines.append( "SUBDIRS = " )
129
130		for subProject in self.subprojects:
131			line = subProject
132			if ( subProject != self.subprojects[ -1 ] ):
133				line += " \\"
134			lines.append( line + os.linesep )
135
136		projectFile.writelines( lines )
137		projectFile.write( os.linesep )
138
139		projectFile.write( "INSTALL_HEADERS.files = " )
140
141		for subProject in self.subprojects:
142			self._createSubproject( subProject )
143
144		for fileName, includePath in self.additionalHeaders.items():
145			targetPath = os.path.join( self.includepath, fileName )
146			self._addForwardHeader( targetPath , includePath, projectFile )
147
148		self._copyHeaders( self.srcpath, self.includepath, projectFile, self.project, self.prefixed )
149		installPath = "{0}/include".format( self.prefix )
150		self._projectFile_finalize( projectFile, installPath )
151		projectFile.close()
152
153	def _createSubproject( self, project ):
154		inclPath = os.path.abspath( self.includepath + "/" + project )
155		srcPath = os.path.abspath( self.srcpath + "/" + project )
156		os.mkdir( inclPath )
157		profilename = os.path.abspath( self.includepath ) + "/" + project + "/" + project + ".pro"
158		projectFile = open( profilename, "wb" )
159		projectFile.write( "TEMPLATE = subdirs" + os.linesep )
160		projectFile.write( "INSTALL_HEADERS.files = " )
161		self._copyHeaders( srcPath, inclPath, projectFile, project, self.prefixed )
162		installPath = "{0}/include/{1}".format( self.prefix, project )
163		self._projectFile_finalize( projectFile, installPath )
164		projectFile.close()
165
166	def _projectFile_finalize( self, projectFile, installPath ):
167		projectFile.write( os.linesep )
168		#projectFile.write( "message( $$INSTALL_HEADERS.path )" + os.linesep )
169		projectFile.write( "INSTALL_HEADERS.path = {0}".format( installPath ) + os.linesep )
170		#projectFile.write( "message( $$INSTALL_HEADERS.path )" + os.linesep )
171		projectFile.write( "INSTALLS += INSTALL_HEADERS" + os.linesep )
172
173	def _copyHeaders( self, srcDir, destDir, projectFile, project, prefixed = False ):
174		rootDir = srcDir == self.srcpath
175		dir = os.listdir( srcDir )
176		headersForCatchAll = []
177		for filename in dir:
178			if ( rootDir ):
179				if ( filename in self.subprojects ):
180					continue
181			file = os.path.abspath( srcDir + "/" + filename )
182			if os.path.isdir( file ):
183				self._copyHeaders( file, destDir, projectFile, project, prefixed )
184			else:
185				if self._isValidHeaderFile( filename ):
186					destfile = os.path.abspath( destDir + "/" + filename )
187					srcfile = os.path.abspath( srcDir + "/" + filename )
188					copyfile( srcfile, destfile )
189					self._createForwardHeader( destfile, projectFile, project )
190					headersForCatchAll.append( filename )
191		# Create "catch all" convenience header including all headers:
192		if len( headersForCatchAll ) > 0:
193			catchAllFileName = os.path.abspath( destDir + "/" + os.path.basename( destDir ) )
194			catchAllContent = ["#include \"%s\"%s" % (header, os.linesep) for header in headersForCatchAll]
195			catchAllFile = open( catchAllFileName, "wb" )
196			catchAllFile.writelines( catchAllContent )
197			catchAllFile.close()
198			projectFile.write( os.path.basename( catchAllFileName ) + " \\" + os.linesep )
199
200