OSDN Git Service

Buildscript: check success of build- and some other commands.
[winmerge-jp/winmerge-jp.git] / Tools / Scripts / create_release.py
1 #
2 # The MIT License
3 # Copyright (c) 2007-2009 Kimmo Varis
4 # Copyright (c) 2008 Matthias Mayer
5 #
6 # Permission is hereby granted, free of charge, to any person obtaining
7 # a copy of this software and associated documentation files
8 # (the "Software"), to deal in the Software without restriction, including
9 # without limitation the rights to use, copy, modify, merge, publish,
10 # distribute, sublicense, and/or sell copies of the Software, and to
11 # permit persons to whom the Software is furnished to do so, subject to
12 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23 # $Id$
24
25 # This is a script for creating a WinMerge release.
26 # Tasks it does:
27 # - cleans previous build files from folders
28 # - sets version number for resources
29 # - updates POT and PO files
30 # - builds libraries (expat, scew, pcre)
31 # - builds WinMerge.exe and WinMergeU.exe
32 # - builds 32-bit ShellExtension targets
33 # - builds user manual
34 # - builds the InnoSetup installer
35 # - creates per-version distribution folder
36 # - exports SVN sources to distribution folder
37 # - creates binary distribution folder
38
39 #Tasks not done (TODO?):
40 # - building 64-bit ShellExtension
41 # - creating packages from source and binary folders
42 # - running virus check
43 # - creating SHA-1 hashes for distributed files
44 # - create installer to correct folder
45 # - make installer compile less verbose
46 # - make building exes and dlls also less verbose
47 # - builds libraries twice: as independent project and from executable project (by prelink.bat)
48 # - check Python version
49 # - check JRE is installed
50 # - check InnoSetup is installed
51
52 # Tools needed:
53 # - Visual Studio 2003 or later
54 # - Python 2.5 or 2.6 :)
55 # - Subversion command line binaries from http://subversion.tigris.org/
56 # - InnoSetup 5
57 # - Manual build tools (in Developer tools at downloads)
58 # - Java runtime environment (JRE) (Manual build tools need it)
59 # There are lots of other dependencies, they are documented in /Docs/Developers documents. Especially be sure to read:
60 # - Compiling.html for compiling executables and dlls
61 # - readme-manual.html for building the manual
62 # - readme-InnoSetup.html for creating the installer
63
64 # Please note that this script is only tested in kimmov's environment. It simply may not work in other environments and
65 # configurations. If you find problems, please report them so we can improve the script.
66
67 # CONFIGURATION:
68 # Set these variables to match your environment and folders you want to use
69
70 # Subversion binary - set this to absolute path to svn.exe
71 #svn_binary = r'C:\Program Files\Subversion\bin\svn.exe'
72 # Visual Studio path
73 #vs_path = r'C:\Program Files\Microsoft Visual Studio .NET 2003'
74 # InnoSetup installation path
75 #innosetup_path = 'C:\\Program Files\\Inno Setup 5'
76 # Relative path where to create a release folder
77 dist_root_folder = 'distrib'
78 # Source location
79 # Give URL to SVN repository to export source from SVN or 'workspace' to export
80 # from workspace
81 #source_location = 'https://winmerge.svn.sourceforge.net/svnroot/winmerge/trunk'
82 #source_location ='workspace'
83
84 # END CONFIGURATION - you don't need to edit anything below...
85
86 from subprocess import *
87 import os
88 import os.path
89 import sys
90 import getopt
91 import shutil
92 import SetVersions
93 import ConfigParser
94
95 class settings:
96     def __init__(self):
97         self.rootpath = ''
98         self.svn_binary = r'C:\Program Files\Subversion\bin\svn.exe'
99         self.vs_path = ''
100         self.vs_path7 = r'C:\Program Files\Microsoft Visual Studio .NET 2003'
101         self.vs_path8 = r'C:\Program Files\Microsoft Visual Studio 8.0'
102         self.vs_path9 = r'C:\Program Files\Microsoft Visual Studio 9.0'
103         self.innosetup_path = r'C:\Program Files\Inno Setup 5'
104         self.winmerge_iss = 'WinMerge.iss' #filename only
105         self.winmerge_iss_path = 'WinMerge.iss' #including path
106         self.source = 'workspace'
107         self.version = ''
108         self.vs_version = 2003
109
110     def create_ini(self, filename):
111         config = ConfigParser.RawConfigParser()
112         sect = 'RUNTIME'
113         if os.path.exists('Tools.ini'):
114             config.readfp(open(filename))
115         if not config.has_section(sect):
116             config.add_section(sect)
117         if not config.has_option(sect, 'type'):
118             config.set(sect, 'type', 'VSXXXX')
119         if not config.has_option(sect, 'VSStudio'):
120             config.set(sect, 'VSStudio', self.vs_version)
121         if not config.has_option(sect, 'Source'):
122             config.set(sect, 'Source', self.source)
123         if not config.has_option(sect, 'svn_binary'):
124             config.set(sect, 'svn_binary', self.svn_binary)
125         if not config.has_option(sect, 'vs_path7'):
126             config.set(sect, 'vs_path7', self.vs_path7)
127         if not config.has_option(sect, 'vs_path8'):
128             config.set(sect, 'vs_path8', self.vs_path8)
129         if not config.has_option(sect, 'vs_path9'):
130             config.set(sect, 'vs_path9', self.vs_path9)
131         if not config.has_option(sect, 'innosetup_path'):
132             config.set(sect, 'innosetup_path', self.innosetup_path)
133
134         # Writing our configuration file to 'Tools.ini'
135         with open(filename, 'w') as configfile:
136             config.write(configfile)
137
138     def read_ini(self, filename):
139         config = ConfigParser.RawConfigParser()
140         if not os.path.exists(filename):
141             # If the config file didn't exist, we create a new file and ask
142             # user to edit the config and re-run the script. This is because
143             # our defaults probably don't match user's environment.
144             self.create_ini(filename)
145             print 'New configuration file created: ' + filename
146             print 'Please edit the file to match your configuration and re-run the script.'
147             sys.exit()
148
149         config.readfp(open(filename))
150         self.svn_binary = config.get('RUNTIME', 'svn_binary')
151         self.vs_path7 = config.get('RUNTIME', 'vs_path7')
152         self.vs_path8 = config.get('RUNTIME', 'vs_path8')
153         self.vs_path9 = config.get('RUNTIME', 'vs_path9')
154         self.innosetup_path = config.get('RUNTIME', 'innosetup_path')
155         self.source = config.get('RUNTIME', 'Source') 
156         self.vs_version = config.getint('RUNTIME', 'VSStudio')
157
158         if self.vs_version ==2003:
159             self.vs_path =self.vs_path7
160         elif self.vs_version ==2005:
161             self.vs_path =self.vs_path8
162         elif self.vs_version ==2008:
163             self.vs_path =self.vs_path9
164
165 # global settings class instance
166 prog = settings()
167
168 def get_vs_ide_bin():
169     """Gets a full path to the Visual Studio IDE executable to run."""
170
171     # These are identical for VS2003.Net, VS2005 and VS2008
172     rel_path = 'Common7/IDE'
173     vs_bin = 'devenv.com'
174
175     vs_ide_path = os.path.join(prog.vs_path, rel_path)
176     vs_cmd_path = os.path.join(vs_ide_path, vs_bin)
177     return vs_cmd_path
178
179 def cleanup_build():
180     """Deletes all build files around folder structure"""
181
182     print 'Delete old build files...'
183     winmerge_temp = 'BuildTmp'
184     if os.path.exists(winmerge_temp):
185         print 'Remove folder %s' % winmerge_temp
186         shutil.rmtree(winmerge_temp, True)
187     else:
188         print 'Skipping folder %s' % winmerge_temp
189     
190     try:
191         print 'Remove ANSI files'
192         if os.path.exists('build/mergerelease/WinMerge.exe'):
193             os.remove('build/mergerelease/WinMerge.exe')
194         if os.path.exists('build/mergerelease/ShellExtension.dll'):
195             os.remove('build/mergerelease/ShellExtension.dll')
196         if os.path.exists('build/mergerelease/MergeLang.dll'):
197             os.remove('build/mergerelease/MergeLang.dll')
198
199         print 'Remove Unicode files'
200         if os.path.exists('build/mergeunicoderelease/WinMergeU.exe'):
201             os.remove('build/mergeunicoderelease/WinMergeU.exe')
202         if os.path.exists('build/mergeunicoderelease/ShellExtensionU.dll'):
203             os.remove('build/mergeunicoderelease/ShellExtensionU.dll')
204         if os.path.exists('build/mergeunicoderelease/MergeLang.dll'):
205             os.remove('build/mergeunicoderelease/MergeLang.dll')
206
207         print 'Remove expat files'
208         if os.path.exists('build/expat'):
209             shutil.rmtree('build/expat', True)
210         if os.path.exists('build/mergerelease/libexpat.dll'):
211             os.remove('build/mergerelease/libexpat.dll')
212         if os.path.exists('build/mergeunicoderelease/libexpat.dll'):
213             os.remove('build/mergeunicoderelease/libexpat.dll')
214
215         print 'Remove pcre files'
216         if os.path.exists('build/pcre'):
217             shutil.rmtree('build/pcre', True)
218         if os.path.exists('build/mergerelease/pcre.dll'):
219             os.remove('build/mergerelease/pcre.dll')
220         if os.path.exists('build/mergeunicoderelease/pcre.dll'):
221             os.remove('build/mergeunicoderelease/pcre.dll')
222
223         if os.path.exists('build/scew'):
224             shutil.rmtree('build/scew', True)
225
226         if os.path.exists('build/Manual'):
227             shutil.rmtree('build/Manual',True)
228
229     except EnvironmentError, einst:
230         print 'Error deleting files: '
231         print einst
232         return False;
233     except:
234         print 'Error deleting files: '
235         print sys.exc_info()[0]
236         return False
237     return True
238
239 def get_product_version(file):
240     """Get the product version number from config file."""
241
242     version = SetVersions.get_product_version(file)
243     return version
244
245 def set_resource_version(file):
246     """Sets the version number to the resource."""
247
248     print 'Update version number to resource(s)...'
249     SetVersions.process_versions(file)
250
251 def setup_translations():
252     """Updates translation files by running scripts in Src/Languages."""
253
254     # Scripts must be run from the directory where they reside
255     curdir = os.getcwd()
256     os.chdir('Translations/WinMerge')
257     retval = call(['cscript', '/nologo', 'CreateMasterPotFile.vbs'])
258     if retval == 0:
259         retval = call(['cscript', '/nologo', 'UpdatePoFilesFromPotFile.vbs'])
260     os.chdir(curdir)
261
262     if retval == 0:
263         return True
264     else:
265         print 'ERROR: Updating translations failed!'
266         return False
267
268 def get_and_create_dist_folder(folder):
269     """Formats a folder name for version-specific distribution folder
270     and creates the folder."""
271
272     abs_folder = os.path.realpath(dist_root_folder)
273     dist_folder = os.path.join(abs_folder, folder)
274     if os.path.exists(dist_folder):
275         print 'Folder: ' + dist_folder + ' already exists!'
276         print 'If you want to re-create this version, remove folder first!'
277         return ''
278     else:
279         print 'Create distribution folder: ' + dist_folder
280         os.mkdir(dist_folder)
281         return dist_folder
282
283 def get_src_dist_folder(dist_folder, folder):
284     """Format a source distribution folder path."""
285
286     dist_src = os.path.join(dist_folder, folder + '-src')
287     return dist_src
288
289 def svn_export(dist_src_folder):
290     """Exports sources to distribution folder."""
291
292     print 'Exporting sources to ' + dist_src_folder
293     print 'Exporting from: ' + prog.source
294     retval = 0
295     if prog.source == 'workspace':
296         retval = call([prog.svn_binary, 'export', '--non-interactive', '.', dist_src_folder])
297     else:
298         retval = call([prog.svn_binary, 'export', '--non-interactive', source_location, dist_src_folder])
299     if retval == 0:
300         return True
301     else:
302         print 'Error exporting sources! SVN return value: ' + retval
303         return False
304
305 def cleanup_dlls_from_plugins(dist_src_folder):
306     """Remove compiled plugin dll files from source distribution folders."""
307
308     dll_folder = os.path.join(dist_src_folder, 'Plugins/dlls')
309     files = os.listdir(dll_folder)
310
311     print 'Removing dll files from plugin folder...'
312     for cur_file in files:
313         fullpath = os.path.join(dll_folder, cur_file)
314         if os.path.isfile(fullpath):
315             file_name, file_ext = os.path.splitext(cur_file)
316             if (file_ext == '.dll'):
317                 os.remove(fullpath)
318
319 def build_libraries():
320     """Builds library targets: expat, scew and pcre."""
321
322     vs_cmd = get_vs_ide_bin()
323     cur_path = os.getcwd()
324
325     print 'Build expat library...'
326     solution_path = os.path.join(cur_path, 'Externals/expat/lib/expat.vcproj')
327     #print solution_path
328     call([vs_cmd, solution_path, '/rebuild', 'Release'], shell=True)
329
330     print 'Build scew library...'
331     solution_path = os.path.join(cur_path, 'Externals/scew/win32/scew.vcproj')
332     #print solution_path
333     call([vs_cmd, solution_path, '/rebuild', 'Release'], shell=True)
334
335     print 'Build pcre library...'
336     solution_path = os.path.join(cur_path, 'Externals/pcre/Win32/pcre.vcproj')
337     #print solution_path
338     call([vs_cmd, solution_path, '/rebuild', 'MinSizeRel'], shell=True)
339
340 def build_targets():
341     """Builds all WinMerge targets."""
342
343     build_libraries()
344
345     vs_cmd = get_vs_ide_bin()
346
347     ret = build_winmerge(vs_cmd)
348     if ret:
349         ret = build_shellext(vs_cmd)
350     return ret
351
352 def build_winmerge(vs_cmd):
353     """Builds WinMerge executable targets."""
354
355     cur_path = os.getcwd()
356     solution_path = os.path.join(cur_path, 'Src\\Merge.vcproj')
357     #print sol_path
358
359     # devenv Src\Merge.dsp /rebuild Release
360     print 'Build WinMerge executables...'
361     ret = call([vs_cmd, solution_path, '/rebuild', 'Release'], shell = True)
362     if ret == 0:
363         ret = call([vs_cmd, solution_path, '/rebuild', 'UnicodeRelease'], shell = True)
364     else:
365         print 'ERROR: Failed to build ANSI release target of WinMerge!'
366         return False
367
368     if ret == 0:
369         return True
370     else:
371         print 'ERROR: Failed to build Unicode release target of WinMerge!'
372         return False
373
374 def build_shellext(vs_cmd):
375     """Builds 32-bit ShellExtension."""
376
377     cur_path = os.getcwd()
378     solution_path = os.path.join(cur_path, 'ShellExtension\\ShellExtension.vcproj')
379
380     # devenv Src\Merge.dsp /rebuild Release
381     print 'Build ShellExtension dlls...'
382     ret = call([vs_cmd, solution_path, '/rebuild', 'Release MinDependency'])
383     if ret == 0:
384         ret = call([vs_cmd, solution_path, '/rebuild', 'Unicode Release MinDependency'])
385     else:
386         print 'ERROR: Failed to build ANSI target of ShellExtension!'
387         return False
388
389     if ret == 0:
390         return True
391     else:
392         print 'ERROR: Failed to build Unicode target of ShellExtension!'
393         return False
394
395 def build_manual():
396     """Builds manual's HTML Help (CHM) version for user install and
397     HTML version for the Web. HTML version is created with ads."""
398
399     curdir = os.getcwd()
400     os.chdir('Docs/Users/Manual/build')
401     print 'Build HTML Help (CHM) manual...' 
402     call(['build_htmlhelp.bat'])
403     
404     # HTML manual not build in trunk.
405     #print 'Build HTML manual for Web with ads...'
406     #call(['build_html.bat', 'withads'])
407     print 'Manual build finished.'
408     os.chdir(curdir)
409
410 def build_innosetup_installer(target_folder):
411     """Builds the InnoSetup installer for the WinMerge."""
412
413     innosetup_exe = os.path.join(prog.innosetup_path, 'iscc.exe')
414     cur_path = os.getcwd()
415
416     prog.winmerge_iss_path = os.path.join(cur_path, 'Installer\\InnoSetup\\' + prog.winmerge_iss)
417
418     #output_switch = '/O"' + target_folder + '"'
419
420     print 'Build Innosetup installer...'
421     # Should be able to give folder for created file and Q switch to make build quiet
422     #call([innosetup_exe, '/Q', output_switch, winmerge_iss])
423     call([innosetup_exe, prog.winmerge_iss_path])
424
425 def get_and_create_bin_folder(dist_folder, folder):
426     """Formats and creates binary distribution folder."""
427
428     bin_folder = os.path.join(dist_folder, folder + '-exe')
429     print 'Create binary distribution folder: ' + bin_folder
430     os.mkdir(bin_folder)
431     return bin_folder
432
433 def create_bin_folders(bin_folder, dist_src_folder):
434     """Creates binary distribution folders."""
435
436     cur_path = os.getcwd()
437     os.chdir(bin_folder)
438     print 'Create binary distribution folder structure...'
439     lang_folder = os.path.join(bin_folder, 'Languages')
440     os.mkdir(lang_folder)
441     doc_folder = os.path.join(bin_folder, 'Docs')
442     os.mkdir(doc_folder)
443     filters_folder = os.path.join(bin_folder, 'Filters')
444     plugins_folder = os.path.join(bin_folder, 'MergePlugins')
445     os.chdir(cur_path)
446
447     print 'Copying files to binary distribution folder...'
448     shutil.copy('build/mergerelease/WinMerge.exe', bin_folder)
449     shutil.copy('build/mergeunicoderelease/WinMergeU.exe', bin_folder)
450
451     shutil.copy('build/mergerelease/ShellExtension.dll', bin_folder)
452     shutil.copy('build/mergeunicoderelease/ShellExtensionU.dll', bin_folder)
453     shutil.copy('build/mergeunicoderelease/MergeLang.dll', bin_folder)
454     shutil.copy('build/shellextensionx64/ShellExtensionX64.dll', bin_folder)
455     shutil.copy('ShellExtension/Register.bat', bin_folder)
456     shutil.copy('ShellExtension/UnRegister.bat', bin_folder)
457
458     shutil.copy('build/pcre/pcre.dll', bin_folder)
459     shutil.copy('build/expat/libexpat.dll', bin_folder)
460
461     copy_po_files(lang_folder)
462     filter_orig = os.path.join(dist_src_folder, 'Filters')
463     shutil.copytree(filter_orig, filters_folder, False)
464
465     # Copy compiled plugins dir and rename it
466     plugin_dir = os.path.join(bin_folder, 'dlls')
467     plugin_orig = os.path.join(dist_src_folder, 'Plugins/dlls')
468     shutil.copytree(plugin_orig, plugin_dir, False)
469     os.rename(plugin_dir, plugins_folder)
470
471     shutil.copy('build/Manual/htmlhelp/WinMerge.chm', doc_folder)
472
473     shutil.copy('Docs/Users/ReleaseNotes.html', doc_folder)
474     shutil.copy('Docs/Users/ReadMe.txt', bin_folder)
475     shutil.copy('Docs/Users/ChangeLog.txt', doc_folder)
476     shutil.copy('Docs/Users/Contributors.txt', bin_folder)
477     shutil.copy('Docs/Users/Files.txt', bin_folder)
478
479 def copy_po_files(dest_folder):
480     """Copies all PO files to destination folder."""
481
482     lang_folder = 'Translations/WinMerge'
483     files = os.listdir(lang_folder)
484
485     print 'Copying PO files to binary folder...'
486     for cur_file in files:
487         fullpath = os.path.join(lang_folder, cur_file)
488         if os.path.isfile(fullpath):
489             file_name, file_ext = os.path.splitext(cur_file)
490             if (file_ext == '.po'):
491                 shutil.copy(fullpath, dest_folder)
492
493 def find_winmerge_root():
494     """Find WinMerge tree root folder from where to run rest of the script.
495
496     This function checks if we are in WinMerge root folder. If we are in some
497     other folder then we must try to find the WinMerge root folder. Because all
498     other code assumes we are in WinMerge root folder. If the root folder is
499     found current folder is changed into it."""
500
501     # If we find Src and Filters -subfolders we are in root 
502     if os.path.exists('Src') and os.path.exists('Filters'):
503         return True
504     
505     # Check if we are in /Tools/Scripts
506     if os.path.exists('../../Src') and os.path.exists('../../Filters'):
507         os.chdir('../../')
508         return True
509     
510     return False
511
512 def check_tools():
513     """Check that needed external tools can be found."""
514
515     global prog
516     if not os.path.exists(prog.svn_binary):
517         print 'Subversion binary could not be found from:'
518         print prog.svn_binary
519         print 'Please check script configuration and/or make sure Subversion is installed.'
520         return False
521
522     vs_cmd = get_vs_ide_bin()
523     if not os.path.exists(vs_cmd):
524         print 'Cannot find Visual Studio IDE binary from:'
525         print vs_cmd
526         print 'Please check script configuration.'
527         return False
528
529     pathhhc = os.path.join(prog.rootpath, 'Docs/Users/Manual/build/hhc/hhc.exe')
530     folderdtd = os.path.join(prog.rootpath, 'Docs/Users/Manual/build/dtd')
531     foldersaxon = os.path.join(prog.rootpath, 'Docs/Users/Manual/build/saxon')
532     folderxerc = os.path.join(prog.rootpath, 'Docs/Users/Manual/build/xerces')
533     folderxsl = os.path.join(prog.rootpath, 'Docs/Users/Manual/build/xsl')
534
535     if not os.path.exists(pathhhc) or not os.path.exists(folderdtd) or \
536             not os.path.exists(foldersaxon) or not os.path.exists(folderxerc) or \
537             not os.path.exists(folderxsl):
538         print 'Cannot find manual build tools'
539         print 'Please download and install manual build tools from:'
540         print 'https://sourceforge.net/project/showfiles.php?group_id=13216'
541         print 'See also Docs/Developers/readme-manual.html'
542         return False
543     return True
544
545 def check_x64shellext():
546     """Checks that 64-bit ShellExtension is compiled prior to running this
547     script.
548
549     This is due to the fact we can't compile 64-bit ShellExtension without some
550     environment tweaks, so it won't work (currently) from this script. And the
551     ShellExtension must be compiled separately.
552     """
553     if not os.path.exists('build/shellextensionx64/ShellExtensionX64.dll'):
554         print 'ERROR: cannot create a release:'
555         print 'You must compile 64-bit ShellExtension (ShellExtensionX64.dll)'
556         print 'before running this script!'
557         return False
558     else:
559         return True
560
561 def usage():
562     """Print script usage information."""
563
564     print 'WinMerge release script.'
565     print 'Usage: create_release [-h] [-f file] [-v n] [-c] [-l]'
566     print '  where:'
567     print '    -h, --help print this help'
568     print '    -v n, --version=n set release version'
569     print '    -c, --cleanup clean up build files (temp files, libraries, executables)'
570     print '    -l, --libraries build libraries (expat, scew, pcre) only'
571     print '    -f file, --file=filename set the version number ini file'
572     print '  For example: create_release -f versions.ini'
573     print '  If no version number (-v) or INI file (-f) given, 0.0.0.0 will be'
574     print '    used as version number.'
575
576 def main(argv):
577     global prog
578     ver_file = ''
579     if len(argv) > 0:
580         opts, args = getopt.getopt(argv, "hclv:f:", [ "help", "cleanup", "libraries",
581                                                     "version=", "file="])
582         
583         for opt, arg in opts:
584             if opt in ("-h", "--help"):
585                 usage()
586                 sys.exit()
587             if opt in ("-v", "--version"):
588                 prog.version = arg
589                 print "Start building WinMerge release version " + prog.version
590             if opt in ("-c", "--cleanup"):
591                 if cleanup_build() == True:
592                     print 'Cleanup done.'
593                 sys.exit()
594             if opt in ("-l", "--libraries"):
595                 build_libraries()
596                 sys.exit()
597             if opt in ("-f", "--file"):
598                 ver_file = arg
599         
600     if ver_file == '' and prog.version == '':
601         print 'WARNING: No version number or INI file given, using default'
602         print '    version number of 0.0.0.0 where applicable in this script.'
603         prog.version = '0.0.0.0'
604
605     # Check we are running from correct folder (and go to root if found)
606     if find_winmerge_root() == False:
607         print 'ERROR: Cannot find WinMerge root folder!'
608         print 'The script must be run from WinMerge tree\'s root folder'
609         print '(which has Src- and Filter -folders as subfolders) or from'
610         print 'Tools/Scripts -folder (where this script is located).'
611         sys.exit()
612
613     # Now read settings from Tools.ini
614     prog.read_ini('Tools.ini')
615     print 'Compiler: ' + prog.vs_path
616     print 'Path:' + os.getcwd()
617
618     # Remember the rootfolder
619     prog.rootpath = os.getcwd()
620
621     # Check all required tools are found (script configuration)
622     if check_tools() == False:
623         sys.exit()
624
625     # Check 64-bit ShellExtension is compiled
626     if check_x64shellext() == False:
627         sys.exit()
628
629     # Create the distribution folder if it doesn't exist
630     try:
631         if not os.path.exists(dist_root_folder):
632             os.mkdir(dist_root_folder)
633     except EnvironmentError, einst:
634         print 'Error creating distribution folder: ' + dist_root_folder
635         print einst
636         sys.exit()
637
638     # Remove old build's files
639     if cleanup_build() == False:
640         sys.exit()
641
642     if len(ver_file) > 0:
643         version_read = get_product_version(ver_file)
644         if len(version_read) > 0:
645             prog.version = version_read
646         set_resource_version(ver_file)
647
648     version_folder = 'WinMerge-' + prog.version
649     dist_folder = get_and_create_dist_folder(version_folder)
650     if dist_folder == '':
651         sys.exit(1)
652     dist_src_folder = get_src_dist_folder(dist_folder, version_folder)
653     if svn_export(dist_src_folder) == False:
654         sys.exit(1)
655
656     if setup_translations() == False:
657         sys.exit(1)
658
659     if build_targets() == False:
660         sys.exit(1)
661     build_manual()
662     build_innosetup_installer(dist_folder)
663
664     dist_bin_folder = get_and_create_bin_folder(dist_folder, version_folder)
665     create_bin_folders(dist_bin_folder, dist_src_folder)
666
667     # Do the cleanup after creating binary distrib folders, as some files
668     # and folders are copied from source folders to binary folders.
669     cleanup_dlls_from_plugins(dist_src_folder)
670
671     print 'WinMerge release script ready!'
672
673
674 ### MAIN ###
675 if __name__ == "__main__":
676     main(sys.argv[1:])