2 # script to generate SuperCollider WIX (windows installer xml) source file from template
3 # Copyright (c) 2008 Dan Stowell. All rights reserved.
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License as
7 # published by the Free Software Foundation; either version 2 of the
8 # License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
22 # (1) You must have installed the "WiX toolset" and added its "bin" folder to your PATH
23 # (2) You must have run the Visual Studio compilation process to create Psycollider stuff in the "build" folder
24 # (3) This script file, and the wix template, must be in the "windows" folder in SuperCollider3 svn tree (sibling to the "build" folder)
25 # (4) I think you also need to put FFTW and libsndfile DLLs into the "build" folder
27 import os
, glob
, uuid
, re
, sys
, shutil
, zipfile
29 ########################################
30 # Check for SwingOSC, because we definitely want it included :)
31 for detectpath
in ('../common/build/SwingOSC.jar', '../common/build/SCClassLibrary/SwingOSC', '../common/build/Help/SwingOSC'):
32 if not os
.path
.exists(detectpath
):
33 print("ERROR:\n Path %s not detected.\n It's required for bundling SwingOSC into the distro." % detectpath
)
36 ########################################
37 # Run the "py2exe" procedure to build an exe file
39 os
.system('cd ../common/Psycollider/Psycollider && python setup.py py2exe')
40 for detectpath
in ('../common/Psycollider/Psycollider/dist/Psycollider.exe', '../common/Psycollider/Psycollider/dist/w9xpopen.exe'):
41 if not os
.path
.exists(detectpath
):
42 print("ERROR:\n Path %s not detected.\n Generating executable (using py2exe) probably failed." % detectpath
)
44 # Also copy PySCLang.pyd out of its "site-packages" location
45 shutil
.copy(os
.getenv('PYTHONPATH', sys
.exec_prefix
) + '/Lib/site-packages/PySCLang.pyd', '../common/Psycollider/Psycollider/dist/')
47 shutil
.copy(os
.getenv('PYTHONPATH', sys
.exec_prefix
) + '/Lib/site-packages/wx-2.8-msw-unicode/wx/gdiplus.dll', '../common/Psycollider/Psycollider/dist/')
50 ########################################
51 # Now we start to build up the XML content for WiX
52 xmlstr1
= "" # directory tree
53 xmlstr2
= "" # "feature" contents
55 regex1
= re
.compile('[^a-zA-Z0-9.]')
58 id = regex1
.sub('_', path
.replace('../common/build/', ''))
59 return 's'+id[max(len(id)-64, 0):] # Prepending 's' a bit of a hack to ensure never begins with '3', '_' etc
61 return str(uuid
.uuid3(uuid
.NAMESPACE_DNS
, 'supercollider.sourceforge.net/' + path
))
63 # This recursively scans a directory and builds up the requisite XML for installing the relevant files.
64 def scanDirForWix(path
, fileexts
, nestlev
):
65 global xmlstr1
, xmlstr2
66 dircontents
= os
.listdir(path
)
67 for item
in dircontents
:
68 fullerpath
= path
+ '/' + item
69 if os
.path
.isdir(fullerpath
) and item
[0] != '.' and item
!='osx' and item
!='linux': # the '.' is to exclude .svn
71 xmlstr1
= xmlstr1
+ ' '*nestlev
+ '<Directory Id="%s" Name="%s">\n' % (pathToId(fullerpath
), item
)
73 scanDirForWix(fullerpath
, fileexts
, nestlev
+1)
74 xmlstr1
= xmlstr1
+ ' '*nestlev
+ '</Directory>\n'
75 elif os
.path
.isfile(fullerpath
) and not os
.path
.islink(fullerpath
):
76 for fileext
in fileexts
:
77 if item
.lower().endswith(fileexts
): #and item matches a certain range of file extensions:
78 # print fullerpath + " --- FILE"
79 compId
= pathToId(fullerpath
)
80 xmlstr1
= xmlstr1
+ ' '*nestlev
+ '<Component Id="%s" Guid="%s">\n' % (compId
, pathToGuid(fullerpath
))
81 xmlstr1
= xmlstr1
+ ' '*nestlev
+ ' <File Id="%s" Name="%s" Source="%s" DiskId="1"/>\n' % \
82 (compId
+ '.file', item
, fullerpath
)
83 xmlstr1
= xmlstr1
+ ' '*nestlev
+ '</Component>\n'
84 xmlstr2
= xmlstr2
+ ' <ComponentRef Id="%s" />\n' % compId
87 # print 'Ignored %s\n' % fullerpath
89 # Now we do all the different scans we want
91 xmlstr1
= xmlstr1
+ '<DirectoryRef Id="SCpluginsFolder">\n'
92 xmlstr2
= xmlstr2
+ '<Feature Id="CorePluginsFeature" Title="Server plugins" Description="Core set of SC3 plugins" Level="1">\n'
93 scanDirForWix('../common/build/plugins', ('.scx'), 0)
94 xmlstr2
= xmlstr2
+ '</Feature>\n'
95 xmlstr1
= xmlstr1
+ '</DirectoryRef>\n\n'
97 xmlstr1
= xmlstr1
+ '<DirectoryRef Id="SCHelpFolder">\n'
98 xmlstr2
= xmlstr2
+ '<Feature Id="HelpFilesFeature" Title="Help files" Description="SC3 help documentation" Level="1">\n'
99 scanDirForWix('../common/build/Help', ('.html', '.htm', '.rtf', '.rtfd', '.jpg', '.png', '.gif', '.scd'), 0)
100 xmlstr2
= xmlstr2
+ '</Feature>\n'
101 xmlstr1
= xmlstr1
+ '</DirectoryRef>\n\n'
103 includeExtras
= False # whether or not to expect build/sc3-plugins and to bundle it into the extensions folder
105 xmlstr1
= xmlstr1
+ '<DirectoryRef Id="SCextensionsFolder">\n'
107 xmlstr1
= xmlstr1
+ ' <Component Id="SCextensions" Guid="35AF303A-C836-11DD-84E5-084C56D89593">\n'
108 xmlstr1
= xmlstr1
+ ' </Component>\n'
109 xmlstr1
= xmlstr1
+ '<Directory Id="sc3plugins" Name="sc3-plugins">\n'
110 xmlstr2
= xmlstr2
+ '<Feature Id="Sc3PluginsFeature" Title="Community sc3-plugins pack" Description="Third-party plugins pack sc3-plugins" Level="1">\n'
111 scanDirForWix('../common/build/sc3-plugins', ('.html', '.htm', '.rtf', '.rtfd', '.jpg', '.png', '.gif', '.scd', '.scx', '.sc'), 0)
112 xmlstr2
= xmlstr2
+ '</Feature>\n'
113 xmlstr1
= xmlstr1
+ '</Directory>\n\n'
115 xmlstr1
= xmlstr1
+ ' <Component Id="SCextensions" Guid="35AF303A-C836-11DD-84E5-084C56D89593">\n'
116 xmlstr1
= xmlstr1
+ ' <CreateFolder/>\n' # This is how to create an empty folder in wix
117 xmlstr1
= xmlstr1
+ ' </Component>\n'
118 xmlstr1
= xmlstr1
+ '</DirectoryRef>\n\n'
120 xmlstr1
= xmlstr1
+ '<DirectoryRef Id="SCsoundsFolder">\n'
121 xmlstr2
= xmlstr2
+ '<Feature Id="SoundFilesFeature" Title="Sound files" Description="Some audio files" Level="1">\n'
122 scanDirForWix("../common/build/sounds", (".aiff", ".wav", ".aif"), 0)
123 xmlstr2
= xmlstr2
+ '</Feature>\n'
124 xmlstr1
= xmlstr1
+ '</DirectoryRef>\n\n'
126 xmlstr1
= xmlstr1
+ '<DirectoryRef Id="SCClassLibrary">\n'
127 xmlstr2
= xmlstr2
+ '<Feature Id="SCClassLibraryFeature" Title="SC3 class files" Description="The classes which define the SuperCollider language" Level="1">\n'
128 scanDirForWix("../common/build/SCClassLibrary", (".sc"), 0)
129 xmlstr2
= xmlstr2
+ '</Feature>\n'
130 xmlstr1
= xmlstr1
+ '</DirectoryRef>\n\n'
133 # WORKAROUND FOR M$ BUG:
134 # Windows installer is supposed to be able to handle massive numbers of files, but actually it fucks up if a <Feature> contains more than around 1000 files.
135 # See http://www.add-in-express.com/creating-addins-blog/2007/11/12/windows-installer-error-2908/
136 # Because of this, we need to artificially split the helpfiles feature in two.
137 xmlstr2b
= xmlstr2
.split('<ComponentRef Id="Help_Style_Guide', 1)
138 if not len(xmlstr2b
) == 2:
139 print "Warning, unable to break up the XML string as expected."
141 xmlstr2
= xmlstr2b
[0] + '</Feature>\n<Feature Id="HelpFilesFeaturePT2" Title="Help files, part 2" Description="SC3 help documentation" Level="1">\n<ComponentRef Id="Help_Style_Guide' + xmlstr2b
[1]
143 # OK, now we have the XML fragments, we want to substitute them into the XML template file
145 templatef
= open('sc3-win-installer-template.wxs', 'r')
146 for line
in templatef
:
147 template
= template
+ line
150 template
= template
.split('<!-- SUBST:SPLITHERE -->');
152 f
= open('supercollider-installer.wxs', 'w')
160 print "\ndistrowin.py: done generating WiX file\n"
162 print "Calling WiX compile/link steps...\n"
163 os
.system('candle supercollider-installer.wxs')
164 os
.system('light -ext WixUIExtension -cultures:en-us supercollider-installer.wixobj')
166 print "\ndistrowin.py: done building MSI\n"
167 print "\ndistrowin.py: now to bundle a zip file\n"
169 z
= zipfile
.ZipFile('supercollider-installer.zip', 'w', zipfile
.ZIP_DEFLATED
)
170 z
.write('supercollider-installer.msi')
171 z
.write('INSTALL.txt')
172 z
.write('../COPYING', 'COPYING.txt')
173 z
.write('copyright_info_forwinbundle.txt', 'copyright info.txt')
177 print "\ndistrowin.py: done\n"