Merge upstream source for version “60+dfsg.1”.
[debian_mullvad-client.git] / setup.py
blob78f8df637ef8130c13d6044db45b59c95e135bff
1 import os
2 import sys
3 import platform
4 import glob
5 import subprocess
6 from setuptools import setup, find_packages
9 CLIENT_VERSION = None
10 execfile('src/mullvad/version.py')
12 base_dir = os.path.dirname(__file__)
13 with open(os.path.join(base_dir, 'README.rst')) as f:
14 long_description = f.read()
16 with open(os.path.join(base_dir, 'CHANGES.rst')) as f:
17 long_description = '\n'.join([long_description, f.read()])
19 common_args = dict(
20 version=CLIENT_VERSION,
21 description='The Mullvad VPN Client',
22 long_description=long_description,
23 url='https://www.mullvad.net/',
24 author='Amagicom AB',
25 author_email='admin@mullvad.net',
26 license='GPL-2+',
28 classifiers=[
29 ('License :: OSI Approved :: '
30 'GNU General Public License v2 or later (GPLv2+)'),
31 'Development Status :: 5 - Production/Stable',
32 'Intended Audience :: End Users/Desktop',
33 'Natural Language :: English',
34 'Natural Language :: French',
35 'Natural Language :: Swedish',
36 'Operating System :: MacOS :: MacOS X',
37 'Operating System :: Microsoft :: Windows',
38 'Programming Language :: Python :: 2 :: Only',
39 'Topic :: Internet',
40 'Topic :: Security',
43 keywords='vpn privacy anonymity security',
45 package_dir={
46 '': 'src',
49 packages=find_packages('src', exclude=['tests']),
51 extras_require={
52 'obfsproxy': ['obfsproxy'],
56 MANIFEST_TEMPLATE = """
57 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
58 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
59 <assemblyIdentity
60 version="5.0.0.0"
61 processorArchitecture="x86"
62 name="%(prog)s"
63 type="win32"
65 <description>%(prog)s</description>
66 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
67 <security>
68 <requestedPrivileges>
69 <requestedExecutionLevel
70 level="requireAdministrator"
71 uiAccess="false">
72 </requestedExecutionLevel>
73 </requestedPrivileges>
74 </security>
75 </trustInfo>
76 <dependency>
77 <dependentAssembly>
78 <assemblyIdentity
79 type="win32"
80 name="Microsoft.VC90.CRT"
81 version="9.0.21022.8"
82 processorArchitecture="x86"
83 publicKeyToken="1fc8b3b9a1e18e3b">
84 </assemblyIdentity>
85 </dependentAssembly>
86 </dependency>
87 <dependency>
88 <dependentAssembly>
89 <assemblyIdentity
90 type="win32"
91 name="Microsoft.Windows.Common-Controls"
92 version="6.0.0.0"
93 processorArchitecture="X86"
94 publicKeyToken="6595b64144ccf1df"
95 language="*"
97 </dependentAssembly>
98 </dependency>
99 </assembly>
102 APPLE_SCRIPT = """
103 tell application "Finder"
104 tell disk "wc"
105 open
107 tell container window
108 set current view to icon view
109 set toolbar visible to false
110 set statusbar visible to false
111 set the bounds to {0, 0, 512, 256}
112 -- set statusbar visible to false
113 end tell
115 set opts to the icon view options of container window
117 tell opts
118 set icon size to 128
119 set arrangement to not arranged
120 end tell
122 -- TODO Uncomment when background image scaling problem is solved
123 -- set background picture of opts to file ".background:mullvad_background.png"
125 tell container window
126 set position of item "Mullvad.app" to {128, 110}
127 set position of item "Applications" to {384, 110}
128 end tell
130 update without registering applications
131 end tell
132 end tell"""
134 # Helper functions =======================================================
137 def call(command, **kwargs):
138 subprocess.call(command.split(), **kwargs)
141 def create_pot(in_file, out_file, po_dir):
142 """Generate a pot file from the given source file and merge with existing
143 po files if any exist in the given directory.
145 call('xgettext -L Python {} -o {}'.format(in_file, out_file))
147 call('mkdir -p {}'.format(po_dir))
148 for po in glob.glob(os.path.join(po_dir, '*')):
149 print 'Merging with existing po:', po
150 call('msgmerge --update --backup=off {} {}'.format(po, out_file))
151 os.remove(out_file)
154 def create_locale(locale_dir, po_dir, domain):
155 """Compile all po files in the given source directory into mo files for the
156 given domain and store thme in the given destination directory.
158 po_files = glob.glob(os.path.join(po_dir, '*'))
160 for po in po_files:
161 lang = os.path.splitext(os.path.basename(po))[0]
163 dest_dir = os.path.join(locale_dir, lang, 'LC_MESSAGES')
164 call('mkdir -p {}'.format(dest_dir))
166 output = os.path.join(dest_dir, domain + '.mo')
167 print 'Creating', output
168 call('msgfmt -o {} {}'.format(output, po))
171 def list_tree(root, include_root=True, dest_root=''):
172 """Generates a list of all files in the given directory tree formatted as
173 an argument to the data_files parameter in setup().
175 If include_root is True, the entire tree will be included in the
176 distribution. dest_root can be used to store the tree under a given path
177 in the distribution.
179 result = []
180 for dirpath, dirs, files in os.walk(root):
181 dest = dirpath
182 if not include_root:
183 dest = os.path.relpath(dirpath, root)
184 if dest == '.':
185 dest = ''
186 if files:
187 files = [os.path.join(dirpath, f) for f in files]
188 result.append((os.path.join(dest_root, dest), files))
190 return result
193 def create_dmg(name):
194 MASTER_DMG = '{}-{}.dmg'.format(name, CLIENT_VERSION)
196 # Create an empty image
197 call('mkdir -p template')
198 call('hdiutil create -fs HFSX -layout SPUD -size 100m {} -srcfolder '
199 'template -format UDRW -volname {}'.format('wc.dmg', name))
201 # Create a mount point and mount the image
202 call('mkdir -p wc')
203 call('hdiutil attach {} -noautoopen -quiet -mountpoint {}'.format(
204 'wc.dmg', 'wc'))
206 # Copy the app to the image
207 call('ditto -rsrc dist/Mullvad.app wc/Mullvad.app')
209 # TODO Uncomment this when image scaling problem is solved
210 # Create a hidden directory on the image which contains a background image
211 # call('mkdir -p wc/.background')
212 # call('ditto -rsrc mac/mullvad_background.png '
213 # 'wc/.background/mullvad_background.png')
215 # Create a shortcut to Applications on the image to make installation
216 # easier
217 call('ln -s /Applications wc/Applications')
219 # Apply a script to the image which defines it's layout
220 with open('tempscript', 'w') as f:
221 f.write(APPLE_SCRIPT)
222 call('osascript tempscript')
224 # Unmount the disk image
225 call('hdiutil detach wc -quiet -force')
227 # Remove old MASTER_DMG if exists
228 call('rm -f {}'.format(MASTER_DMG))
230 call('hdiutil convert {} -quiet -format UDZO -imagekey '
231 'zlib-level=9 -o {}'.format('wc.dmg', MASTER_DMG))
233 # Cleanup
234 call('rm -rf wc wc.dmg tempscript template')
237 # ========================================================================
239 if 'pot' in sys.argv:
240 create_pot('src/mullvad/mui.py', 'msgs.pot', 'po')
242 elif 'locale' in sys.argv:
243 create_locale('src/mullvad/locale', 'po', 'mullvad')
244 create_locale('locale', 'po', 'mullvad')
246 elif 'dmg' in sys.argv and platform.system() == 'Darwin':
247 create_dmg('Mullvad')
249 elif platform.system() == 'Windows': # ==================================
250 import py2exe
251 import jinja2
253 with open('winstaller.nsi.template', 'r') as f:
254 nsi_template = jinja2.Template(f.read())
255 with open('winstaller.nsi', 'w') as f:
256 f.write(nsi_template.render(version=CLIENT_VERSION))
258 windows_data_files = [
259 ('ssl', glob.glob('src/mullvad/ssl/*')),
260 ('', [
261 'src/mullvad/client.conf.windows',
262 'src/mullvad/backupservers.txt',
263 'src/mullvad/harddnsbackup.txt',
264 'src/mullvad/openssl.cnf',
265 'src/mullvad/mullvad.png',
266 'src/mullvad/rdot.png',
267 'src/mullvad/ydot.png',
268 'src/mullvad/gdot.png',
272 windows_data_files.extend(list_tree('locale'))
273 windows_data_files.extend(list_tree('client-binaries/windows', False))
275 setup(
276 name='mullvad',
277 windows=[{
278 'script': 'client-binaries/windows/mullvad.py',
279 'icon_resources': [
280 (1, 'client-binaries/windows/mullvad.ico')
282 'other_resources': [
283 (24, 1, MANIFEST_TEMPLATE % dict(prog='Mullvad Client'))
287 options={
288 'py2exe': {
289 'excludes': ['Tkinter'],
290 'dll_excludes': [
291 'w9xpopen.exe',
292 'MSVCP90.dll',
293 # Pulled in by psutil
294 'IPHLPAPI.DLL',
295 'NSI.dll',
296 'WINNSI.DLL',
297 'WTSAPI32.dll',
298 'API-MS-Win-Core-DelayLoad-L1-1-0.dll',
299 'API-MS-Win-Core-ErrorHandling-L1-1-0.dll',
300 'API-MS-Win-Core-File-L1-1-0.dll',
301 'API-MS-Win-Core-Handle-L1-1-0.dll',
302 'API-MS-Win-Core-Heap-L1-1-0.dll',
303 'API-MS-Win-Core-Interlocked-L1-1-0.dll',
304 'API-MS-Win-Core-IO-L1-1-0.dll',
305 'API-MS-Win-Core-LibraryLoader-L1-1-0.dll',
306 'API-MS-Win-Core-LocalRegistry-L1-1-0.dll',
307 'API-MS-Win-Core-Misc-L1-1-0.dll',
308 'API-MS-Win-Core-ProcessThreads-L1-1-0.dll',
309 'API-MS-Win-Core-Profile-L1-1-0.dll',
310 'API-MS-Win-Core-String-L1-1-0.dll',
311 'API-MS-Win-Core-Synch-L1-1-0.dll',
312 'API-MS-Win-Core-SysInfo-L1-1-0.dll',
313 'API-MS-Win-Core-ThreadPool-L1-1-0.dll',
314 'API-MS-Win-Security-Base-L1-1-0.dll',
319 data_files=windows_data_files,
321 **common_args
324 elif platform.system() == 'Darwin': # ===================================
325 osx_data_files = [
326 ('', glob.glob('client-binaries/mac/include/*')),
328 ('ssl', glob.glob('src/mullvad/ssl/*')),
330 ('', [
331 'src/mullvad/client.conf.mac',
332 'src/mullvad/backupservers.txt',
333 'src/mullvad/harddnsbackup.txt',
334 'src/mullvad/openssl.cnf',
335 'src/mullvad/mullvad.xpm',
336 'src/mullvad/mullvad.png',
337 'src/mullvad/rdot.png',
338 'src/mullvad/ydot.png',
339 'src/mullvad/gdot.png',
343 osx_data_files.extend(list_tree('locale'))
345 setup(
346 name='Mullvad',
347 setup_requires=['py2app'],
349 # Specify the script to be used as entry point for the application
350 app=['client-binaries/mac/mullvad_mac.py'],
352 options={
353 'py2app': {
354 'iconfile': 'client-binaries/mac/mullvad.icns',
356 # Specify packages to include. Specifying the package in the
357 # setup arguments is unfortunately not enough
358 'packages': ['src/mullvad'],
360 'argv_emulation': True, # Enable file-drop
361 'arch': 'i386', # wxPython doesn't like 64bit which is default
365 data_files=osx_data_files,
367 **common_args
370 os.system('chmod a+x dist/mullvad.app/Contents/Resources/openvpn')
371 os.system('chmod a+x dist/mullvad.app/Contents/Resources/obfsproxy')
372 os.system('chmod a+x '
373 'dist/mullvad.app/Contents/Resources/process-network-changes')
374 os.system('chmod a+x '
375 'dist/mullvad.app/Contents/Resources/client.up.osx.sh')
376 os.system('chmod a+x '
377 'dist/mullvad.app/Contents/Resources/client.down.osx.sh')
379 else: # =================================================================
380 # data_files only applies when creating a built distribution such as a
381 # debian package.
382 data_files = [
383 ('share/pixmaps', ['src/mullvad/mullvad.png']),
384 ('share/applications', ['src/mullvad/mullvad.desktop']),
386 data_files.extend(list_tree('icons', dest_root='share'))
388 setup(
389 name='mullvad',
390 include_package_data=True,
391 data_files=data_files,
392 entry_points={
393 'gui_scripts': [
394 'mullvad=mullvad.mui:main',
396 'console_scripts': [
397 'mtunnel=mullvad.tunnelprocess:main',
400 **common_args