summaryrefslogtreecommitdiff
path: root/scripts/builder
Unidiff
Diffstat (limited to 'scripts/builder') (more/less context) (ignore whitespace changes)
-rw-r--r--scripts/builder/backendBuilder.py101
-rw-r--r--scripts/builder/backends/phpBuilder.py (renamed from scripts/builder/phpBuilder.py)7
-rw-r--r--scripts/builder/backends/pythonBuilder.py27
-rw-r--r--scripts/builder/backends/scriptLanguageBuilder.py20
-rw-r--r--scripts/builder/frontendBuilder.py141
-rwxr-xr-xscripts/builder/main.py85
-rw-r--r--scripts/builder/pythonBuilder.py14
-rw-r--r--scripts/builder/repository.py75
8 files changed, 304 insertions, 166 deletions
diff --git a/scripts/builder/backendBuilder.py b/scripts/builder/backendBuilder.py
index 16dbe2f..5ecdda0 100644
--- a/scripts/builder/backendBuilder.py
+++ b/scripts/builder/backendBuilder.py
@@ -1,89 +1,118 @@
1#!/usr/bin/env python 1#!/usr/bin/env python
2# -*- coding: UTF-8 -*- 2# -*- coding: UTF-8 -*-
3 3
4import sys, os, json 4import sys, os, json
5import shutil 5import shutil
6import main
7import hashlib 6import hashlib
8 7
9class BackendBuilder: 8import main
9
10#===================================================================
11
12
13class BackendBuilder(object):
10 14
11 def __init__ (self, projectTargetDir, frontends, versions, settings): 15 def __init__ (self, projectTargetDir, frontends, versions, settings):
12 self.projectTargetDir = projectTargetDir 16 self.projectTargetDir = projectTargetDir
13 self.frontends = frontends 17 self.frontends = frontends
14 self.versions = versions 18 self.versions = versions
15 self.settings = settings 19 self.settings = settings
20
21 # --------------------------------------------------------------------------
16 22
17 def name (self): 23 def name (self):
18 raise NotImplementedError() 24 raise NotImplementedError()
25
19 26
20 def relativePath (self): 27 def relativePath (self):
21 raise NotImplementedError() 28 raise NotImplementedError()
29
22 30
23 def compileCode (self): 31 def compileCode (self):
24 pass 32 raise NotImplementedError()
33
25 34
26 def copyCompiledCodeToTargetDir (self): 35 def createPackage (self):
27 src = self.sourceFolder() 36 raise NotImplementedError()
28 dst = self.targetFolder() 37
29 main.createFolder(os.path.dirname(dst)) 38 # --------------------------------------------------------------------------
30 shutil.copytree(src, dst)
31 39
32 def sourceFolder (self): 40 def sourceFolder (self):
33 return main.projectBaseDir() + '/backend/' + self.relativePath() + '/src' 41 return os.path.join(main.projectBaseDir() , 'backend', self.relativePath(), 'src')
34
35 42
36 def targetFolder (self):
37 return self.projectTargetDir + self.relativePath()
38
39 def createTargetFolder (self):
40 main.createFolder(self.targetFolder())
41
42 43
43 #def copyFrontendResources (self, frontend): 44 def tempFolder (self):
44 # print "copying resources for frontend: " + frontend 45 return os.path.join(self.projectTargetDir, '.tmp', self.relativePath())
45 # print "SETTINGS: " + str(self.settings) 46
46 47
48 def frontEndTempFolder (self):
49 return self.tempFolder()
50
47 51
48 def writeToTargetFolder (self, filename, content): 52 def developmentTargetFolder (self):
49 file = open(self.targetFolder() + '/' + filename, 'w') 53 return os.path.join(self.projectTargetDir, 'development', self.relativePath())
54
55 def targetFolder (self):
56 return os.path.join(self.projectTargetDir, self.relativePath())
57
58 # --------------------------------------------------------------------------
59
60 def writeToFolder (self, folder, filename, content):
61 file = open(os.path.join(folder, filename), 'w')
50 file.write(content.encode('utf-8')) 62 file.write(content.encode('utf-8'))
51 file.close() 63 file.close()
52 64
53 65
54 def configureIndexContent (self, indexContent): 66 def configureIndexContent (self, indexContent, requestPathPrefix = ".."):
55 result = indexContent 67 result = indexContent
56 result = result.replace( '@request.path@', self.settings['request.path'] ) 68 result = result.replace( '@request.path@', requestPathPrefix + '/' + self.settings['request.path'] )
57 result = result.replace( '@should.pay.toll@', self.settings['should.pay.toll'] ) 69 result = result.replace( '@should.pay.toll@', self.settings['should.pay.toll'] )
58 70
59 return result 71 return result
60 72
61 73
62 def logChecksums (self, content, message): 74 def logChecksums (self, content, message):
63 md5Digest = hashlib.md5(content.encode('utf-8')).hexdigest() 75 md5Digest = hashlib.md5(content.encode('utf-8')).hexdigest()
64 shaDigest = hashlib.sha1(content.encode('utf-8')).hexdigest() 76 shaDigest = hashlib.sha1(content.encode('utf-8')).hexdigest()
65 sha256Digest= hashlib.sha256(content.encode('utf-8')).hexdigest() 77 sha256Digest= hashlib.sha256(content.encode('utf-8')).hexdigest()
66 print message + ": " + md5Digest + " (md5)" 78 print message + ": " + md5Digest + " (md5)"
67 print message + ": " + shaDigest + " (sha1)" 79 print message + ": " + shaDigest + " (sha1)"
68 print message + ": " + sha256Digest + " (sha256)" 80 print message + ": " + sha256Digest + " (sha256)"
69 81
70 82
83 def shouldCompileCode (self):
84 return ('debug' in self.versions) or ('install' in self.versions)
85
71 86
72 def run (self): 87 def run (self):
73 print self.name() + " - RUN" 88 print self.name() + " - RUN"
74 89
75 self.compileCode() 90 if self.shouldCompileCode():
76 self.copyCompiledCodeToTargetDir() 91 self.compileCode()
77 92
78 for frontend in self.frontends: 93 for frontend in self.frontends:
79 frontendPath = frontend.module + '/' 94 main.createFolder(os.path.join(self.frontEndTempFolder(), frontend.module))
80 if 'debug' in self.versions: 95
81 frontend.copyResourcesToTargetFolder(self.targetFolder()) 96 if 'debug' in self.versions:
82 #self.writeToTargetFolder(frontendPath + 'index_debug.html', self.configureIndexContent(frontend.assembleDebugVersion())) 97 frontend.copyResourcesToFolder(self.frontEndTempFolder())
83 self.writeToTargetFolder(frontendPath + 'index_debug.html', self.configureIndexContent(frontend.assemble(assemblyMode='DEBUG', versionType='DEBUG'))) 98
99 index = self.configureIndexContent(frontend.assemble(assemblyMode='DEBUG', versionType='DEBUG'))
100 self.writeToFolder(self.frontEndTempFolder(), os.path.join(frontend.module, 'index_debug.html'), index)
101
102 if 'install' in self.versions:
103 index = self.configureIndexContent(frontend.assemble())
104 self.writeToFolder(self.frontEndTempFolder(), os.path.join(frontend.module, 'index.html'), index)
105
106 self.logChecksums(index, "[" + self.name() + " - " + frontend.module + "] index.html checksum")
84 107
85 if 'install' in self.versions: 108 self.createPackage()
86 index = self.configureIndexContent(frontend.assemble()) 109
87 self.writeToTargetFolder(frontendPath + 'index.html', index) 110 if 'development' in self.versions:
88 self.logChecksums(index, "[" + self.name() + " - " + frontend.module + "] index.html checksum") 111 for frontend in self.frontends:
112 main.createFolder(os.path.join(self.developmentTargetFolder(), frontend.module))
113
114 index = self.configureIndexContent(frontend.assemble(assemblyMode='DEVELOPMENT', versionType='DEBUG'), self.settings['development.settings']['url'])
115 self.writeToFolder(self.developmentTargetFolder(), os.path.join(frontend.module, 'index_development.html'), index)
116
89 117
118#===================================================================
diff --git a/scripts/builder/phpBuilder.py b/scripts/builder/backends/phpBuilder.py
index cb4661d..c928aa0 100644
--- a/scripts/builder/phpBuilder.py
+++ b/scripts/builder/backends/phpBuilder.py
@@ -1,14 +1,13 @@
1#!/usr/bin/env python 1#!/usr/bin/env python
2# -*- coding: UTF-8 -*- 2# -*- coding: UTF-8 -*-
3 3
4from backendBuilder import BackendBuilder 4from scriptLanguageBuilder import ScriptLanguageBuilder
5 5
6class PhpBuilder(BackendBuilder): 6class PhpBuilder(ScriptLanguageBuilder):
7 7
8 def name(self): 8 def name(self):
9 return "PHP builder" 9 return "PHP builder"
10
10 11
11 def relativePath(self): 12 def relativePath(self):
12 return 'php' 13 return 'php'
13
14
diff --git a/scripts/builder/backends/pythonBuilder.py b/scripts/builder/backends/pythonBuilder.py
new file mode 100644
index 0000000..1e44209
--- a/dev/null
+++ b/scripts/builder/backends/pythonBuilder.py
@@ -0,0 +1,27 @@
1#!/usr/bin/env python
2# -*- coding: UTF-8 -*-
3
4from scriptLanguageBuilder import ScriptLanguageBuilder
5
6class PythonBuilder(ScriptLanguageBuilder):
7
8 def name(self):
9 return "Python builder"
10
11
12 def relativePath(self):
13 return 'python'
14
15
16 def compileCode (self):
17 src = self.sourceFolder()
18 dst = self.targetFolder()
19
20 shutil.copytree(src, dst)
21
22
23 #def copyCompiledCodeToTargetDir (self):
24 # src = self.sourceFolder()
25 # dst = self.targetFolder()
26#
27 # shutil.copytree(src, dst)
diff --git a/scripts/builder/backends/scriptLanguageBuilder.py b/scripts/builder/backends/scriptLanguageBuilder.py
new file mode 100644
index 0000000..7d5b31c
--- a/dev/null
+++ b/scripts/builder/backends/scriptLanguageBuilder.py
@@ -0,0 +1,20 @@
1#!/usr/bin/env python
2# -*- coding: UTF-8 -*-
3
4import shutil
5from backendBuilder import BackendBuilder
6
7class ScriptLanguageBuilder(BackendBuilder):
8
9 def compileCode (self):
10 src = self.sourceFolder()
11 dst = self.tempFolder()
12
13 shutil.copytree(src, dst)
14
15
16 def createPackage (self):
17 src = self.tempFolder()
18 dst = self.targetFolder()
19
20 shutil.copytree(src, dst)
diff --git a/scripts/builder/frontendBuilder.py b/scripts/builder/frontendBuilder.py
index 55054ee..dae837b 100644
--- a/scripts/builder/frontendBuilder.py
+++ b/scripts/builder/frontendBuilder.py
@@ -1,398 +1,389 @@
1#!/usr/bin/env python 1#!/usr/bin/env python
2# -*- coding: UTF-8 -*- 2# -*- coding: UTF-8 -*-
3 3
4import sys, os, re 4import sys, os, re
5import cssmin 5import cssmin
6import jsmin 6import jsmin
7import codecs 7import codecs
8import shutil 8import shutil
9import StringIO 9import StringIO
10import urllib 10import urllib
11 11
12#from mercurial import ui, hg
13#from mercurial.node import hex
14from dulwich.repo import Repo
15
16import main 12import main
17 13
14class FrontendBuilder(object):
18 15
19 16 def __init__ (self, frontend, settings, repositoryVersion):
20class FrontendBuilder:
21
22 def __init__ (self, frontend, settings):
23 if '.' in frontend: 17 if '.' in frontend:
24 moduleComponents = frontend.split('.') 18 moduleComponents = frontend.split('.')
25 self.module = moduleComponents[0] 19 self.module = moduleComponents[0]
26 self.submodule = moduleComponents[1] 20 self.submodule = moduleComponents[1]
27 else: 21 else:
28 self.module = frontend 22 self.module = frontend
29 self.submodule = frontend 23 self.submodule = frontend
30 24
31 self.settings = settings 25 self.settings = settings
32 self.projectDir = main.projectBaseDir() 26 self.projectDir = main.projectBaseDir()
27 # self.repository = repository.repositoryWithPath(self.projectDir)
28 self.repositoryVersion = repositoryVersion
33 self.processedFiles = {} 29 self.processedFiles = {}
34 30
35 31
36 def mercurialRepositoryVersion (self):
37 repo = hg.repository(ui.ui(), self.projectDir)
38 context = repo['tip']
39 result = str(context)
40
41 return result
42
43
44 def gitRepositoryVersion (self):
45 repo = Repo(self.projectDir)
46 #if repo.is_dirty():
47 #print "WARNING: build run with dirty repository"
48 result = repo.refs['HEAD']
49
50 return result
51
52
53
54 def repositoryVersion (self):
55 cacheKey = 'repositoryVersion'
56 if not self.processedFiles.has_key(cacheKey):
57 #result = self.mercurialRepositoryVersion()
58 result = self.gitRepositoryVersion()
59 self.processedFiles[cacheKey] = result
60 else:
61 result = self.processedFiles[cacheKey]
62
63 return result
64
65
66 #def relativePath (self):
67 #return self.module
68 #
69
70 def log (self, message): 32 def log (self, message):
71 print "frontend [" + self.module + "]: " + message 33 print "frontend [" + self.module + "]: " + message
72 34
73 35
74 def absolutePathForSourceFile (self, folder, basePath, file): 36 def absolutePathForSources (self):
75 return folder + '/frontend/' + self.module + '/' + basePath + '/' + file 37 return os.path.join(self.projectDir, 'frontend', self.module)
38
39
40 def absolutePathForSourceFile (self, basePath, file):
41 return os.path.join(self.absolutePathForSources(), basePath, file)
76 42
77 43
78 def absolutePathForTargetFile (self, folder, basePath, file): 44 def absolutePathForTargetFile (self, folder, basePath, file):
79 return folder + '/' + self.module + '/' + basePath + '/' + file 45 return os.path.join(folder, self.module, basePath, file)
46
80 47
81 def filterFiles (self, files): 48 def filterFiles (self, files):
82 result = [] 49 result = []
83 50
84 for file in files: 51 for file in files:
85 if file.startswith('--'): 52 if file.startswith('--'):
86 pass 53 pass
87 else: 54 else:
88 result.append(file) 55 result.append(file)
89 56
90 return result 57 return result
91 58
92 59
93 def copyResources (self, sourceFolder, destinationFolder, fileType): 60 def copyResources (self, sourceFolder, destinationFolder, fileType):
94 for file in self.filterFiles(self.settings[fileType]): 61 for file in self.filterFiles(self.settings[fileType]):
95 src = self.absolutePathForSourceFile(sourceFolder, fileType, file) 62 src = self.absolutePathForSourceFile(fileType, file)
96 dst = self.absolutePathForTargetFile(destinationFolder, fileType, file) 63 dst = self.absolutePathForTargetFile(destinationFolder, fileType, file)
97 main.createFolder(os.path.dirname(dst)) 64 main.createFolder(os.path.dirname(dst))
98 shutil.copy2(src, dst) 65 shutil.copy2(src, dst)
99 66
100 67
101 def copyResourcesToTargetFolder (self, targetFolder): 68 def copyResourcesToFolder (self, targetFolder):
102 self.copyResources(self.projectDir, targetFolder, 'css') 69 self.copyResources(self.projectDir, targetFolder, 'css')
103 self.copyResources(self.projectDir, targetFolder, 'js') 70 self.copyResources(self.projectDir, targetFolder, 'js')
104 71
105 72
106 def loadFilesContent (self, basePath, files): 73 def loadFilesContent (self, basePath, files):
107 result = "" 74 result = ""
108 75
109 for file in self.filterFiles(files): 76 for file in self.filterFiles(files):
110 try: 77 try:
111 fileHandler = codecs.open(self.absolutePathForSourceFile(self.projectDir, basePath, file), 'r', 'utf-8') 78 fileHandler = codecs.open(self.absolutePathForSourceFile(basePath, file), 'r', 'utf-8')
112 except: 79 except:
113 print "FILE: " + file 80 print "FILE: " + file
114 81
115 result += fileHandler.read() + '\n' 82 result += fileHandler.read() + '\n'
116 fileHandler.close() 83 fileHandler.close()
117 84
118 return result 85 return result
119 86
120 87
121 def template (self): 88 def template (self):
122 processedFile = 'html_template' 89 processedFile = 'html_template'
123 if not self.processedFiles.has_key(processedFile): 90 if not self.processedFiles.has_key(processedFile):
124 self.processedFiles[processedFile] = self.loadFilesContent('html', ['index_template.html']) 91 self.processedFiles[processedFile] = self.loadFilesContent('html', ['index_template.html'])
125 92
126 return self.processedFiles[processedFile] 93 return self.processedFiles[processedFile]
127 94
128 95
129 def cssminCompressor (self, css): 96 def cssminCompressor (self, css):
130 # package found here: 97 # package found here:
131 # - http://stackoverflow.com/questions/222581/python-script-for-minifying-css/2396777#2396777 98 # - http://stackoverflow.com/questions/222581/python-script-for-minifying-css/2396777#2396777
132 # actual downloaded version: http://pypi.python.org/pypi/cssmin/0.1.4 99 # actual downloaded version: http://pypi.python.org/pypi/cssmin/0.1.4
133 return cssmin.cssmin(css) 100 return cssmin.cssmin(css)
134 101
135 102
136 def regexCssCompressor (self, css): 103 def regexCssCompressor (self, css):
137 # http://stackoverflow.com/questions/222581/python-script-for-minifying-css/223689#223689 104 # http://stackoverflow.com/questions/222581/python-script-for-minifying-css/223689#223689
138 105
139 # remove comments - this will break a lot of hacks :-P 106 # remove comments - this will break a lot of hacks :-P
140 css = re.sub( r'\s*/\*\s*\*/', "$$HACK1$$", css ) # preserve IE<6 comment hack 107 css = re.sub( r'\s*/\*\s*\*/', "$$HACK1$$", css ) # preserve IE<6 comment hack
141 css = re.sub( r'/\*[\s\S]*?\*/', "", css ) 108 css = re.sub( r'/\*[\s\S]*?\*/', "", css )
142 css = css.replace( "$$HACK1$$", '/**/' ) # preserve IE<6 comment hack 109 css = css.replace( "$$HACK1$$", '/**/' ) # preserve IE<6 comment hack
143 110
144 # url() doesn't need quotes 111 # url() doesn't need quotes
145 css = re.sub( r'url\((["\'])([^)]*)\1\)', r'url(\2)', css ) 112 css = re.sub( r'url\((["\'])([^)]*)\1\)', r'url(\2)', css )
146 113
147 # spaces may be safely collapsed as generated content will collapse them anyway 114 # spaces may be safely collapsed as generated content will collapse them anyway
148 css = re.sub( r'\s+', ' ', css ) 115 css = re.sub( r'\s+', ' ', css )
149 116
150 # shorten collapsable colors: #aabbcc to #abc 117 # shorten collapsable colors: #aabbcc to #abc
151 css = re.sub( r'#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3(\s|;)', r'#\1\2\3\4', css ) 118 css = re.sub( r'#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3(\s|;)', r'#\1\2\3\4', css )
152 119
153 # fragment values can loose zeros 120 # fragment values can loose zeros
154 css = re.sub( r':\s*0(\.\d+([cm]m|e[mx]|in|p[ctx]))\s*;', r':\1;', css ) 121 css = re.sub( r':\s*0(\.\d+([cm]m|e[mx]|in|p[ctx]))\s*;', r':\1;', css )
155 122
156 for rule in re.findall( r'([^{]+){([^}]*)}', css ): 123 for rule in re.findall( r'([^{]+){([^}]*)}', css ):
157 124
158 # we don't need spaces around operators 125 # we don't need spaces around operators
159 selectors = [re.sub( r'(?<=[\[\(>+=])\s+|\s+(?=[=~^$*|>+\]\)])', r'', selector.strip() ) for selector in rule[0].split( ',' )] 126 selectors = [re.sub( r'(?<=[\[\(>+=])\s+|\s+(?=[=~^$*|>+\]\)])', r'', selector.strip() ) for selector in rule[0].split( ',' )]
160 127
161 # order is important, but we still want to discard repetitions 128 # order is important, but we still want to discard repetitions
162 properties = {} 129 properties = {}
163 porder = [] 130 porder = []
164 for prop in re.findall( '(.*?):(.*?)(;|$)', rule[1] ): 131 for prop in re.findall( '(.*?):(.*?)(;|$)', rule[1] ):
165 key = prop[0].strip().lower() 132 key = prop[0].strip().lower()
166 if key not in porder: porder.append( key ) 133 if key not in porder: porder.append( key )
167 properties[ key ] = prop[1].strip() 134 properties[ key ] = prop[1].strip()
168 135
169 # output rule if it contains any declarations 136 # output rule if it contains any declarations
170 if properties: 137 if properties:
171 print "%s{%s}" % ( ','.join( selectors ), ''.join(['%s:%s;' % (key, properties[key]) for key in porder])[:-1] ) 138 print "%s{%s}" % ( ','.join( selectors ), ''.join(['%s:%s;' % (key, properties[key]) for key in porder])[:-1] )
172 139
173 return css 140 return css
174 141
175 142
176 def compressCSS (self, css): 143 def compressCSS (self, css):
177 self.log("compressing CSS") 144 self.log("compressing CSS")
178 #return self.regexCssCompressor(css) 145 #return self.regexCssCompressor(css)
179 return self.cssminCompressor(css) 146 return self.cssminCompressor(css)
180 147
181 148
182 #========================================================================== 149 #==========================================================================
183 150
184 def compressJS_jsmin (self, js): 151 def compressJS_jsmin (self, js, description):
185 self.log("compressing JS code") 152 self.log("compressing " + description + " code")
186 original = StringIO.StringIO(js) 153 original = StringIO.StringIO(js)
187 output = StringIO.StringIO() 154 output = StringIO.StringIO()
188 155
189 jsMinifier = jsmin.JavascriptMinify() 156 jsMinifier = jsmin.JavascriptMinify()
190 jsMinifier.minify(original, output) 157 jsMinifier.minify(original, output)
191 158
192 result = output.getvalue() 159 result = output.getvalue()
193 160
194 original.close() 161 original.close()
195 output.close() 162 output.close()
196 163
197 return result 164 return result
198 165
199 def compressJS_closureCompiler (self, js): 166 def compressJS_closureCompiler (self, js, description):
200 #Googles Closure compiler 167 #Googles Closure compiler
201 #java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js 168 #java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js
202 169
203 result = js 170 result = js
204 171
205 return result 172 return result
206 173
207 174
208 def compressJS (self, js): 175 def compressJS (self, js, description):
209 return self.compressJS_jsmin(js) 176 return self.compressJS_jsmin(js, description)
210 #return self.compressJS_closureCompiler(js) 177 #return self.compressJS_closureCompiler(js, description)
211 178
212 179
213 #========================================================================== 180 #==========================================================================
214 181
215 def packBookmarklet (self, bookmakeletCode): 182 def packBookmarklet (self, bookmakeletCode, version):
216 replacers = [ 183 replacers = [
217 ('isLoginForm', 'ilf'), 184 ('isLoginForm', 'ilf'),
218 ('findLoginForm', 'flf'), 185 ('findLoginForm', 'flf'),
219 ('findLoginForm', 'flf'), 186 ('findLoginForm', 'flf'),
220 ('formParameters', 'fp' ), 187 ('formParameters', 'fp' ),
221 ('pageParameters', 'pp' ), 188 ('pageParameters', 'pp' ),
222 ('serializeJSON', 'sj' ), 189 ('serializeJSON', 'sj' ),
223 ('reprString', 'rs' ), 190 ('reprString', 'rs' ),
224 ('logFormParameters', 'lfp'), 191 ('logFormParameters', 'lfp'),
225 ('loadClipperzBookmarklet','lcb'), 192 ('loadClipperzBookmarklet','lcb'),
226 ('loginForm', 'lf' ), 193 ('loginForm', 'lf' ),
227 ('parameters', 'p' ), 194 ('parameters', 'p' ),
228 ('inputElementValues', 'iev'), 195 ('inputElementValues', 'iev'),
229 ] 196 ]
230 result = self.compressJS(bookmakeletCode) 197 result = self.compressJS(bookmakeletCode, version + " bookmarklet")
231 198
232 result = re.sub('\n', ' ', result) #Fit all in a single line 199 result = re.sub('\n', ' ', result) #Fit all in a single line
233 # result = re.sub('\s+', ' ', result) #Collapse "redundant" spaces. WARNING: this could have some evil side effects on constant strings used inside to code!! 200 # result = re.sub('\s+', ' ', result) #Collapse "redundant" spaces. WARNING: this could have some evil side effects on constant strings used inside to code!!
234 # result = re.sub('\s?([,\+=\(\)\{\};])\s?', '\\1', result) 201 # result = re.sub('\s?([,\+=\(\)\{\};])\s?', '\\1', result)
235 202
236 for replacer in replacers: 203 for replacer in replacers:
237 result = re.sub(replacer[0], replacer[1], result) 204 result = re.sub(replacer[0], replacer[1], result)
238 205
239 # <!-- escaping required to handle the bookmarklet code within the javascript code --> 206 # <!-- escaping required to handle the bookmarklet code within the javascript code -->
240 result = re.sub('\://', '%3a%2f%2f',result) 207 result = re.sub('\://', '%3a%2f%2f',result)
241 result = re.sub('/', '%2f', result) 208 result = re.sub('/', '%2f', result)
242 # result = re.sub('"', '%22', result) 209 # result = re.sub('"', '%22', result)
243 result = re.sub('"', '\\"', result) 210 result = re.sub('"', '\\"', result)
244 result = re.sub('\"', '%22', result) 211 result = re.sub('\"', '%22', result)
245 result = re.sub('\'', '%22', result) 212 result = re.sub('\'', '%22', result)
246 result = re.sub('\\\\', '%5c', result) 213 result = re.sub('\\\\', '%5c', result)
247 result = result.strip() 214 result = result.strip()
248 result = 'javascript:' + result 215 result = 'javascript:' + result
249 216
250 # replacers = [ 217 # replacers = [
251 # ('aForm', '_1' ), 218 # ('aForm', '_1' ),
252 # ('inputFields', '_2' ), 219 # ('inputFields', '_2' ),
253 # ('passwordFieldsFound','_3' ), 220 # ('passwordFieldsFound','_3' ),
254 # ('aDocument', '_6' ), 221 # ('aDocument', '_6' ),
255 # ('aLevel', '_7' ), 222 # ('aLevel', '_7' ),
256 # # ('result', '_8' ), 223 # # ('result', '_8' ),
257 # ('documentForms', '_9' ), 224 # ('documentForms', '_9' ),
258 # ('iFrames', '_c' ), 225 # ('iFrames', '_c' ),
259 # ('anInputElement', '_d' ), 226 # ('anInputElement', '_d' ),
260 # ('options', '_f' ), 227 # ('options', '_f' ),
261 # ('option', '_12'), 228 # ('option', '_12'),
262 # ('aLoginForm', '_13'), 229 # ('aLoginForm', '_13'),
263 # # ('action', '_17'), 230 # # ('action', '_17'),
264 # ('radioValues', '_18'), 231 # ('radioValues', '_18'),
265 # ('radioValueName', '_19'), 232 # ('radioValueName', '_19'),
266 # ('inputElement', '_1a'), 233 # ('inputElement', '_1a'),
267 # ('elementValues', '_1b'), 234 # ('elementValues', '_1b'),
268 # ('radioValue', '_1c'), 235 # ('radioValue', '_1c'),
269 # ('values', '_1d'), 236 # ('values', '_1d'),
270 # ('objtype', '_21'), 237 # ('objtype', '_21'),
271 # ('useKey', '_27'), 238 # ('useKey', '_27'),
272 # ('bookmarkletDiv', '_28'), 239 # ('bookmarkletDiv', '_28'),
273 # ('someParameters', '_29'), 240 # ('someParameters', '_29'),
274 # ('anException', '_2a'), 241 # ('anException', '_2a'),
275 # ('newDiv', '_2b'), 242 # ('newDiv', '_2b'),
276 # ('base_url', '_2c'), 243 # ('base_url', '_2c'),
277 # ('help_url', '_2d'), 244 # ('help_url', '_2d'),
278 # ('logo_image_url', '_2e'), 245 # ('logo_image_url', '_2e'),
279 # ('background_image_url','_2f'), 246 # ('background_image_url','_2f'),
280 # ('close_image_url', '_30'), 247 # ('close_image_url', '_30'),
281 # #('bookmarklet_textarea','_31'), 248 # #('bookmarklet_textarea','_31'),
282 # ('innerHTML', '_32'), 249 # ('innerHTML', '_32'),
283 # ] 250 # ]
284 # for replacer in replacers: 251 # for replacer in replacers:
285 # result = re.sub('([^\.])' + replacer[0], '\\1' + replacer[1], result) 252 # result = re.sub('([^\.])' + replacer[0], '\\1' + replacer[1], result)
286 253
287 # replacers = [ 254 # replacers = [
288 # ('headNode', '_1' ), 255 # ('headNode', '_1' ),
289 # ('clipperzScriptNode','_2' ), 256 # ('clipperzScriptNode','_2' ),
290 # ] 257 # ]
291 # for replacer in replacers: 258 # for replacer in replacers:
292 # result = re.sub('([^\.])' + replacer[0], '\\1' + replacer[1], result) 259 # result = re.sub('([^\.])' + replacer[0], '\\1' + replacer[1], result)
293 260
294 # result = re.sub(';', ';\n', result) 261 # result = re.sub(';', ';\n', result)
295 262
296 return result 263 return result
297 264
298 265
299 266
300 def bookmarklet (self): 267 def bookmarklet (self):
301 cacheKey = 'bookmarklet' 268 cacheKey = 'bookmarklet'
302 if not self.processedFiles.has_key(cacheKey): 269 if not self.processedFiles.has_key(cacheKey):
303 result = 'bookmarklet="' + self.packBookmarklet(self.loadFilesContent('js', ['Bookmarklet.js'])) + '";bookmarklet_ie="' + self.packBookmarklet(self.loadFilesContent('js', ['Bookmarklet_IE.js'])) + '";' 270 result = 'bookmarklet="' + self.packBookmarklet(self.loadFilesContent('js', ['Bookmarklet.js']), "regular") + '";bookmarklet_ie="' + self.packBookmarklet(self.loadFilesContent('js', ['Bookmarklet_IE.js']), "IE") + '";'
304 self.processedFiles[cacheKey] = result 271 self.processedFiles[cacheKey] = result
305 else: 272 else:
306 result = self.processedFiles[cacheKey] 273 result = self.processedFiles[cacheKey]
307 274
308 return result 275 return result
309 276
310 277
311 def replaceTemplatePlaceholders (self, assemblyMode, pageTitle, copyright, css, code, version, versionType): 278 def replaceTemplatePlaceholders (self, pageTitle, copyright, css, code, jsLoadMode, version, versionType):
312 result = self.template() 279 result = self.template()
313 280
314 result = result.replace('@page.title@', pageTitle, 1) 281 result = result.replace('@page.title@', pageTitle, 1)
315 result = result.replace('@copyright@', copyright, 1) 282 result = result.replace('@copyright@', copyright, 1)
316 result = result.replace('@css@', css, 1) 283 result = result.replace('@css@', css, 1)
317 #result = result.replace('@bookmarklet@', bookmarklet,1) 284 #result = result.replace('@bookmarklet@', bookmarklet,1)
318 result = result.replace('@application.version@', version, 1) 285 result = result.replace('@application.version@', version, 1)
319 result = result.replace('@application.version.type@', versionType,1) 286 result = result.replace('@application.version.type@', versionType,1)
320 result = result.replace('@js_' + assemblyMode + '@', code, 1) 287 result = result.replace('@js_' + jsLoadMode + '@', code, 1)
321 288
322 result = re.sub('@js_[^@]+@', '', result) 289 result = re.sub('@js_[^@]+@', '', result)
323 290
324 return result 291 return result
325 292
326 293
327 def assembleCopyrightHeader (self): 294 def assembleCopyrightHeader (self):
328 processedFile = 'copyright' 295 processedFile = 'copyright'
329 if not self.processedFiles.has_key(processedFile): 296 if not self.processedFiles.has_key(processedFile):
330 #self.log("assembling copyright header") 297 #self.log("assembling copyright header")
331 copyrightValues = self.settings['copyright.values'] 298 copyrightValues = self.settings['copyright.values']
332 license = self.loadFilesContent('../../properties', ['license.txt']) 299 license = self.loadFilesContent('../../properties', ['license.txt'])
333 result = self.loadFilesContent('properties', ['creditsAndCopyrights.txt']) 300 result = self.loadFilesContent('properties', ['creditsAndCopyrights.txt'])
334 301
335 result = re.sub('@clipperz.license@', license, result) 302 result = re.sub('@clipperz.license@', license, result)
336 for key in copyrightValues: 303 for key in copyrightValues:
337 result = re.sub('@'+key+'@', copyrightValues[key], result) 304 result = re.sub('@'+key+'@', copyrightValues[key], result)
338 305
339 self.processedFiles[processedFile] = result 306 self.processedFiles[processedFile] = result
340 307
341 return self.processedFiles[processedFile] 308 return self.processedFiles[processedFile]
342 309
343 310
344 def cssTagsForFiles (self, basePath, files): 311 def cssTagsForFiles (self, basePath, files):
345 #<link rel="stylesheet" type="text/css" href="./css/reset-min.css" /> 312 #<link rel="stylesheet" type="text/css" href="./css/reset-min.css" />
346 return '\n'.join(map(lambda file: '<link rel="stylesheet" type="text/css" href="./' + basePath + '/' + file + '" />', files)) 313 return '\n'.join(map(lambda file: '<link rel="stylesheet" type="text/css" href="' + basePath + '/' + file + '" />', files))
347 314
348 315
349 def cssTagForContent (self, content): 316 def cssTagForContent (self, content):
350 return '<style type="text/css">' + content + '</style>' 317 return '<style type="text/css">' + content + '</style>'
351 318
352 319
353 def scriptTagsForFiles (self, basePath, files): 320 def scriptTagsForFiles (self, basePath, files):
354 #<script type='text/javascript' src='./js/src/bookmarklet.js'></script> 321 #<script type='text/javascript' src='./js/src/bookmarklet.js'></script>
355 return '\n'.join(map(lambda file: '<script type="text/javascript" src="./' + basePath + '/' + file + '"></script>', files)) 322 return '\n'.join(map(lambda file: '<script type="text/javascript" src="' + basePath + '/' + file + '"></script>', files))
356 323
357 324
358 def scriptTagForContent (self, content): 325 def scriptTagForContent (self, content):
359 return '<script>' + content + '</script>' 326 return '<script>' + content + '</script>'
360 327
361 328
362 def assembleVersion (self, assemblyMode, pageTitle, copyright, css, js, version, versionType): 329 def assembleVersion (self, pageTitle, copyright, css, js, jsLoadMode, version, versionType):
363 cacheKey = version + "-" + versionType 330 cacheKey = version + "-" + versionType
364 if not self.processedFiles.has_key(cacheKey): 331 if not self.processedFiles.has_key(cacheKey):
365 result = self.replaceTemplatePlaceholders(assemblyMode, pageTitle, copyright, css, js, version, versionType) 332 result = self.replaceTemplatePlaceholders(pageTitle, copyright, css, js, jsLoadMode, version, versionType)
366 self.processedFiles[cacheKey] = result 333 self.processedFiles[cacheKey] = result
367 else: 334 else:
368 result = self.processedFiles[cacheKey] 335 result = self.processedFiles[cacheKey]
369 336
370 #self.log("# cacheKey:\n" + result) 337 #self.log("# cacheKey:\n" + result)
371 return result 338 return result
372 339
373 340
374 def assemble (self, assemblyMode='INSTALL', versionType='LIVE'): 341 def assemble (self, assemblyMode='INSTALL', versionType='LIVE'):
375 pageTitle = "Clipperz - " + self.module 342
376 if versionType != 'LIVE': 343 if versionType == 'LIVE':
377 pageTitle += " [" + versionType + " - " + assemblyMode +"]" 344 pageTitle = "Clipperz - " + self.module
378 345 else:
346 pageTitle = "Clipperz - " + self.module + " [" + versionType + " - " + assemblyMode +"]"
347
379 if assemblyMode == 'INSTALL': 348 if assemblyMode == 'INSTALL':
380 css= self.cssTagForContent(self.compressCSS(self.loadFilesContent('css', self.settings['css']))) 349 copyright = self.assembleCopyrightHeader()
381 js= self.scriptTagForContent(self.bookmarklet() + '\n' + self.compressJS(self.loadFilesContent('js', self.settings['js']))) 350 css =self.cssTagForContent(self.compressCSS(self.loadFilesContent('css', self.settings['css'])))
351 js =self.scriptTagForContent(
352 self.bookmarklet() +
353 '\n' +
354 self.compressJS(self.loadFilesContent('js', self.settings['js']), "application")
355 )
356 jsLoadMode = 'EMBEDDED'
357
358 elif assemblyMode == 'DEBUG':
359 copyright = self.assembleCopyrightHeader()
360 css =self.cssTagsForFiles('./css', self.filterFiles(self.settings['css']))
361 js =self.scriptTagForContent(self.bookmarklet()) + \
362 '\n' + \
363 self.scriptTagsForFiles('./js', self.filterFiles(self.settings['js']))
364 jsLoadMode = 'LINKED'
365
366 elif assemblyMode == 'DEVELOPMENT':
367 copyright = ""
368 css =self.cssTagsForFiles('file://' + str(os.path.join(self.absolutePathForSources(), 'css')), self.filterFiles(self.settings['css']))
369 js =self.scriptTagForContent(self.bookmarklet()) + \
370 '\n' + \
371 self.scriptTagsForFiles('file://' + str(os.path.join(self.absolutePathForSources(), 'js')), self.filterFiles(self.settings['js']))
372 jsLoadMode = 'LINKED'
373
382 else: 374 else:
383 css= self.cssTagsForFiles('css', self.filterFiles(self.settings['css'])) 375 raise NotImplementedError()
384 js= self.scriptTagForContent(self.bookmarklet()) + '\n' + self.scriptTagsForFiles('js', self.filterFiles(self.settings['js'])) 376
385
386 return self.assembleVersion( 377 return self.assembleVersion(
387 assemblyMode= assemblyMode,
388 pageTitle = pageTitle, 378 pageTitle = pageTitle,
389 copyright = self.assembleCopyrightHeader(), 379 copyright = copyright,
390 css = css, 380 css = css,
391 js = js, 381 js = js,
392 version = self.repositoryVersion(), 382 jsLoadMode = jsLoadMode,
383 version = self.repositoryVersion,
393 versionType = versionType 384 versionType = versionType
394 ) 385 )
395 386
396 387
397 388
398 389
diff --git a/scripts/builder/main.py b/scripts/builder/main.py
index 94f738f..6fce65d 100755
--- a/scripts/builder/main.py
+++ b/scripts/builder/main.py
@@ -1,166 +1,177 @@
1#!/usr/bin/env python 1#!/usr/bin/env python
2# -*- coding: UTF-8 -*- 2# -*- coding: UTF-8 -*-
3 3
4import sys, os, json 4import sys
5import os
6import json
5import shutil 7import shutil
6import pprint 8import pprint
7import frontendBuilder
8import codecs 9import codecs
9import itertools 10import itertools
10
11from collections import deque 11from collections import deque
12from phpBuilder import PhpBuilder 12
13from pythonBuilder import PythonBuilder 13import frontendBuilder
14import repository
14 15
15pp = pprint.PrettyPrinter(indent=4, depth=4) 16pp = pprint.PrettyPrinter(indent=4, depth=4)
16 17
17#-------------------------------------------------------------------- 18#--------------------------------------------------------------------
18 19
19def scriptDir (): 20def scriptDir ():
20 return os.path.dirname(sys.argv[0]) 21 return os.path.dirname(sys.argv[0])
21 22
22def projectBaseDir (): 23def projectBaseDir ():
23 return os.path.abspath(scriptDir() + '/../..') 24 return os.path.abspath(scriptDir() + '/../..')
24 25
25def projectTargetDir(): 26def projectTargetDir():
26 return projectBaseDir() + '/target/' 27 return projectBaseDir() + '/target/'
27 28
28#-------------------------------------------------------------------- 29#--------------------------------------------------------------------
29 30
30def createFolder (path): 31def createFolder (path):
31 if not os.path.exists(path): 32 if not os.path.exists(path):
32 os.makedirs(path) 33 os.makedirs(path)
33 34
34#-------------------------------------------------------------------- 35#--------------------------------------------------------------------
35 36
36def loadSettings (component, module): 37def loadSettings (component, module):
37 print "MODULE: " + module 38 # print "MODULE: " + module
38 39
39 if '.' in module: 40 if '.' in module:
40 moduleComponents = module.split('.') 41 moduleComponents = module.split('.')
41 module = moduleComponents[0] 42 module = moduleComponents[0]
42 submodule = moduleComponents[1] 43 submodule = moduleComponents[1]
43 else: 44 else:
44 submodule = module 45 submodule = module
45 46
46 settings = codecs.open(projectBaseDir() + '/' + component + '/' + module + '/properties/' + submodule + '.properties.json', 'r', 'utf-8') 47 #settings = codecs.open(projectBaseDir() + os.sep + component + os.sep + module + os.sep + 'properties' + os.sep + submodule + '.properties.json', 'r', 'utf-8')
48 settings = codecs.open(os.path.join(projectBaseDir(), component, module, 'properties', submodule + '.properties.json'), 'r', 'utf-8')
47 result = json.load(settings) 49 result = json.load(settings)
48 settings.close 50 settings.close
49 51
50 return result 52 return result
51 53
52#==================================================================== 54#====================================================================
53# 55#
54# def assembleFrontend (frontend, versions): 56# def assembleFrontend (frontend, versions):
55 # result = {} 57 # result = {}
56 # settings = loadSettings('frontend', frontend) 58 # settings = loadSettings('frontend', frontend)
57 # builder = frontendBuilder.FrontendBuilder(frontend, settings, projectBaseDir()) 59 # builder = frontendBuilder.FrontendBuilder(frontend, settings, projectBaseDir())
58 # 60 #
59 # for version in versions: 61 # for version in versions:
60 # if version == 'install': 62 # if version == 'install':
61 # result[version] = builder.assembleInstallVersion() 63 # result[version] = builder.assembleInstallVersion()
62 # elif version == 'debug': 64 # elif version == 'debug':
63 # result[version] = builder.assembleDebugVersion() 65 # result[version] = builder.assembleDebugVersion()
64 # else: 66 # else:
65 # raise Exception('unrecognized version: ' + version) 67 # raise Exception('unrecognized version: ' + version)
66 # 68 #
67 # return result 69 # return result
68# 70#
69#==================================================================== 71#====================================================================
70 72
71def assembleBackend (backend, frontends, versions): 73def assembleBackend (backend, frontends, versions):
72 settings = loadSettings('backend', backend) 74 settings = loadSettings('backend', backend)
73 75
74 if backend == 'php': 76 builderModuleName = backend + 'Builder'
75 backendBuilder = PhpBuilder(projectTargetDir(), frontends, versions, settings) 77 builderClassName = backend.capitalize() + 'Builder'
76 elif backend == 'python': 78
77 backendBuilder = PythonBuilder(projectTargetDir(), frontends, versions, settings) 79 builderModule = __import__(builderModuleName)
78 #elif backend == 'java': 80 builderClass = getattr(builderModule, builderClassName)
79 #buildJavaBackend (frontends, versions, settings) 81
80 else: 82 backendBuilder = builderClass(projectTargetDir(), frontends, versions, settings)
81 raise Exception('unrecognized backend: ' + backend)
82
83 backendBuilder.run() 83 backendBuilder.run()
84 84
85#==================================================================== 85#====================================================================
86 86
87def build (settings): 87def build (settings, repository):
88 frontends = [] 88 frontends = []
89 89
90 if repository.areTherePendingChanges():
91 print "\nWARNING: repository has pending changes\n"
92
90 for frontend in settings['frontends']: 93 for frontend in settings['frontends']:
91 frontends.append(frontendBuilder.FrontendBuilder(frontend, loadSettings('frontend', frontend))) 94 frontends.append(frontendBuilder.FrontendBuilder(frontend, loadSettings('frontend', frontend), repository.version()))
92 95
93 for backend in settings['backends']: 96 for backend in settings['backends']:
94 assembleBackend(backend, frontends, settings['versions']) 97 assembleBackend(backend, frontends, settings['versions'])
95 98
96#-------------------------------------------------------------------- 99#--------------------------------------------------------------------
97 100
98def clean (): 101def clean ():
99 print "cleaning up …" 102 # print "cleaning up …"
100 if os.path.exists(projectTargetDir()): 103 if os.path.exists(projectTargetDir()):
101 shutil.rmtree(projectTargetDir()) 104 shutil.rmtree(projectTargetDir())
102 105
103#-------------------------------------------------------------------- 106#--------------------------------------------------------------------
104 107
105def usage (message): 108def usage (message):
106 if message != None: 109 if message != None:
107 print "ERROR: " + message 110 print "ERROR: " + message
108 111
109 print 112 print
110 print "build.py clean" 113 # print "build clean"
111 print "build.py clean install" 114 # print "build clean install"
112 print "build.py install --ALL" 115 print "build install --ALL"
113 print "build.py install debug --ALL" 116 print "build install debug --ALL"
114 print "build.py clean install debug --ALL" 117 # print "build clean install debug --ALL"
115 print "build.ph install, debug --backends php java --frontends beta gamma" 118 print "build install debug --backends php python --frontends beta gamma"
116 print "build.ph install, debug --backends php java --frontends beta gamma gamma.mobile" 119 print "build install debug development --backends php python --frontends beta gamma gamma.mobile"
117 exit(1) 120 exit(1)
118 121
119#-------------------------------------------------------------------- 122#--------------------------------------------------------------------
120 123
124def allFrontends ():
125 return ['beta', 'gamma', 'mobile']
126
127def allBackends ():
128 return ['php', 'python']
129
130#--------------------------------------------------------------------
131
121def main (): 132def main ():
122 settings = {} 133 settings = {}
123 parameters = list(itertools.islice(sys.argv, 1, None)) 134 parameters = list(itertools.islice(sys.argv, 1, None))
124 135
125 shouldClean = len(filter(lambda x: x == 'clean', parameters)) > 0 136 sys.path.append(os.path.join(scriptDir(), 'backends'))
126 if (shouldClean): 137 currentRepository = repository.repositoryWithPath(projectBaseDir())
127 clean () 138
128 139 clean()
129 parameters = filter(lambda x: x != 'clean', parameters)
130 versions = list(itertools.takewhile(lambda x: not x.startswith('--'), parameters)) 140 versions = list(itertools.takewhile(lambda x: not x.startswith('--'), parameters))
131 settings['versions'] = versions; #['debug', 'install'] 141 settings['versions'] = versions; #['debug', 'install', 'development']
132 parameters = deque(itertools.dropwhile(lambda x: not x.startswith('--'), parameters)) 142 parameters = deque(itertools.dropwhile(lambda x: not x.startswith('--'), parameters))
133 143
134 if len(parameters) > 0: 144 if len(parameters) > 0:
135 parameter = parameters.popleft() 145 parameter = parameters.popleft()
136 if parameter == "--ALL": 146 if parameter == "--ALL":
137 settings['frontends'] = ['beta', 'gamma', 'mobile'] 147 settings['frontends'] = allFrontends()
138 settings['backends'] = ['php', 'python', 'java'] 148 settings['backends'] = allBackends()
139 else: 149 else:
140 while parameter != None: 150 while parameter != None:
141 values = list(itertools.takewhile(lambda x: not x.startswith('--'), parameters)) 151 values = list(itertools.takewhile(lambda x: not x.startswith('--'), parameters))
142 152
143 if parameter == "--backends": 153 if parameter == "--backends":
144 settings['backends'] = values 154 settings['backends'] = values
145 elif parameter == "--frontends": 155 elif parameter == "--frontends":
146 settings['frontends'] = values 156 settings['frontends'] = values
147 157
148 parameters = deque(itertools.dropwhile(lambda x: not x.startswith('--'), parameters)) 158 parameters = deque(itertools.dropwhile(lambda x: not x.startswith('--'), parameters))
149 if parameters: 159 if parameters:
150 parameter = parameters.popleft() 160 parameter = parameters.popleft()
151 else: 161 else:
152 parameter = None 162 parameter = None
153 163
154 if (not settings.has_key('versions')): 164 if (not settings.has_key('versions')):
155 usage("missing 'versions'") 165 usage("missing 'versions'")
156 if (not settings.has_key('frontends')): 166 if (not settings.has_key('frontends')):
157 usage("missing 'frontends'") 167 usage("missing 'frontends'")
158 if (not settings.has_key('backends')): 168 if (not settings.has_key('backends')):
159 usage("missing 'backends'") 169 usage("missing 'backends'")
160 170
161 build (settings) 171 build(settings, currentRepository)
162 172 else:
173 usage("Suggestions on how to call the 'build' script:")
163 174
164 175
165if __name__ == "__main__": 176if __name__ == "__main__":
166 main() 177 main()
diff --git a/scripts/builder/pythonBuilder.py b/scripts/builder/pythonBuilder.py
deleted file mode 100644
index a84598d..0000000
--- a/scripts/builder/pythonBuilder.py
+++ b/dev/null
@@ -1,14 +0,0 @@
1#!/usr/bin/env python
2# -*- coding: UTF-8 -*-
3
4from backendBuilder import BackendBuilder
5
6class PythonBuilder(BackendBuilder):
7
8 def name(self):
9 return "Python builder"
10
11 def relativePath(self):
12 return 'python'
13
14
diff --git a/scripts/builder/repository.py b/scripts/builder/repository.py
new file mode 100644
index 0000000..89db9a5
--- a/dev/null
+++ b/scripts/builder/repository.py
@@ -0,0 +1,75 @@
1#!/usr/bin/env python
2# -*- coding: UTF-8 -*-
3
4
5def repositoryWithPath (path):
6 try:
7 from dulwich.repo import Repo
8
9 repo = Repo(path)
10 result = GitRepository(repo, path)
11 except:
12 from mercurial import ui, hg
13
14 repo = hg.repository(ui.ui(), path)
15 result = HgRepository(repo, path)
16
17 return result
18
19
20#===================================================================
21
22
23class Repository(object):
24
25 def __init__ (self, repository, path):
26 self.repository = repository
27 self.path = path
28
29
30 def revision (self):
31 raise NotImplementedError()
32
33
34 def areTherePendingChanges (self):
35 raise NotImplementedError()
36
37
38 def version (self):
39 result = self.revision()
40 if self.areTherePendingChanges():
41 result = '>>> ' + result + ' <<<'
42
43 # print "VERSION: " + result
44 return result
45
46
47#===================================================================
48
49
50class GitRepository(Repository):
51
52 def revision (self):
53 return repository.refs['HEAD']
54
55
56 def areTherePendingChanges (self):
57 return repository.is_dirty()
58
59
60#===================================================================
61
62
63class HgRepository(Repository):
64 #http://mercurial.selenic.com/wiki/MercurialApi
65
66 def revision (self):
67 return 'hg:' + str(self.repository['tip'])
68
69
70 def areTherePendingChanges (self):
71 # TODO: FIXME: repository.status() does not report 'unknown(?)' files. :(
72 return not all(map(lambda fileList: len(fileList) == 0, self.repository.status()))
73
74
75#===================================================================