root/trunk/skylive-ng/build.py

Revision 1112, 23.2 kB (checked in by nextime, 3 years ago)

--

  • Property svn:executable set to *
Line 
1 #!/usr/bin/env python
2 #######################################
3 ####### CONFIGURATION OPTIONS #########
4 #######################################
5
6 # main binary Name
7 NAME="skylive-ng"
8
9 # list of main python scripts (use python list!)
10 # WARNING: only the first one will be taken for OSX!!!
11 BASESCRIPTS=["skylive-ng.py", "fitsopener.py", "proxyconf.py"]
12
13 # version
14 VERSION="0.2.0"
15
16 # package description
17 DESCRIPTION="Skylive Client"
18
19 # Produce an optimized python bytecode (0,1,2)
20 OPTIMIZE=0
21
22 # Append all modules to the binary created (bool)
23 APPEND_TO_EXE=False
24
25 # Compression (bool)
26 COMPRESSION=True
27
28 # Compile as service for windows
29 # Use False for no services, a list ['Name', 'Name'] without extension
30 WINSERVICES=False 
31
32 # Use windows console option instead of windows. Valid only if WINSERVICES is
33 # False and WX_GUI_PROGRAM is True
34 WINCONSOLE=False
35
36 # Use False for no daemons, a list ['Name', 'Name'] without extension
37 # This is cause if you need Windows Services, you can't specify
38 # it in basescripts, so, it need to be separated also for Unixes
39 UNIXDAEMONS=False
40
41 # build also MSI installer under windows platforms
42 WIN_BUILD_MSI=False
43
44 # Have this program a pycard/wxpython gui?
45 WX_GUI_PROGRAM=True
46
47 ICON="ICONA.ico"
48
49 # Can be False!
50 OSX_ICON="ICONA.icns"
51
52 # Run before to create the dmg, path relative to root of the tree
53 OSX_PRE_EXECUTE=['scripts/osx_createwrapper.sh']
54
55 # Run before makeself on linux
56 LINUX_PRE_EXECUTE=['scripts/linux_copygstreamer.sh']
57
58 # Files to be added to the root of the package (python list)
59 ADD_FILES = ['LICENSE.txt','README.txt', ICON,
60       'skyliveng.desktop', 'skylive-science.directory',
61       'skylive-ng.exe.manifest', 'VERSION.txt']
62
63
64 AUTHOR="Skylive Staff"
65
66 EMAIL="skylive@skylive.it"
67
68 URL="http://www.skylive.it"
69
70 LICENSE_TYPE="GPLv3"
71
72 # Modules that we need to exclude from our executable
73 EXCLUDES=['Tkinter']
74
75 #######################################
76 # Don't edit below this line          #
77 #######################################
78 #
79 # TODO:
80 #
81 #   Verificare la presenza dei requisiti
82 #   per la creazione dell'installer ed in generale
83 #   migliorare l'error management, magari anche con
84 #   un log file
85 #
86 # TODO:
87 #
88 #   Aggiungere la gestione di installer multilingua
89 #
90 # TODO:
91 #
92 #   Gestire sotto Linux anche eventuale creazione di device
93 #   per i chroot?   
94 #
95 #
96 # XXX TODO:
97 #
98 #   Verificare come si comporta sotto altri sistemi,
99 #   come ad esempio BSD e/o Solaris
100 #
101 # XXX TODO:
102 #
103 #   Gestire anche la creazione di pacchetti python classici?
104 #
105 # TODO:
106 #
107 #   Creazione diretta anche di rpm e deb?
108 #
109 # TODO:
110 #
111 #   gestione .app per OSX
112 #
113 # TODO:
114 #
115 #   gestione 32/64bit e diverse architetture
116 #
117 # TODO:
118 #
119 #   Cython support
120 #
121 ########################################
122 # FUTURE IMPROVEMENTS
123 ########################################
124 #
125 # - Use msilib to generate the MSI installer
126 #
127 # - Avoid the usage of ISS, implement an EXE generator
128 #   based on the bdist_wininst distutils command?
129 #
130 # - under Linux, reimplement makeself in pure python
131 #
132 # - under Linux, reimplement ldd ans strip in pure python
133 #
134 # - reimplement whole things as a python module
135 #
136 # - use a config file to set variables and/or package
137 #   description
138 #
139 ########################################
140
141 ########################################
142 # basic imports                        #
143 ########################################
144
145 import platform
146 import os
147 from distutils.core import Command
148 from distutils.command import clean as distutils_clean
149 import sys, subprocess
150
151
152 ########################################
153 # Setting variables and various things #
154 ########################################
155
156
157 # This way we can run just ./setup.py to build
158 # the installer
159 if len(sys.argv) == 1:
160    sys.argv.append('create')
161
162
163 # Setting usefull paths
164 curdir = os.path.abspath(os.path.dirname(__file__))
165 srcdir = os.path.join(curdir, 'src')
166 distdir = os.path.join(curdir, 'dist')
167 scriptdir = os.path.join(curdir, 'scripts')
168
169 sys.path.append(srcdir)
170
171 # Change current dir to the root of the sources
172 os.chdir(curdir)
173 print 'Working in dir %s' % curdir
174
175
176 # Settings some lists of needed python packages to be included in our
177 # package
178 includes = ['encodings.ascii', 'encodings.utf_8', 'encodings.idna', 'encodings.iso8859_16']
179 includes += ['encodings.string_escape','Crypto.Hash.SHA', 'numpy', 'appcomponents.infohtmlwindow']
180 includes += ['appcomponents.ulist', 'appcomponents.image', 'encodings.latin_1', 'twisted.web.resource']
181 includes += ['appcomponents.container', 'pkg_resources']
182
183 languages = ['it', 'en']
184 for lang in languages:
185    includes.append('skylive.lang.'+lang)
186
187 # Put wx and pycard things in includes
188 if WX_GUI_PROGRAM:
189    pycardcomponents = [
190    'button',
191    'staticbox',
192    'statictext',
193    'textarea',
194    'textfield',
195    'passwordfield',
196    'list',
197    'notebook',
198    'combobox',
199    'spinner',
200    'checkbox',
201    'bitmapcanvas',
202    'slider',
203    'gauge'
204          ]
205
206
207    pycardincludes = []
208    for component in pycardcomponents:
209       pycardincludes.append('Pycard.components.'+component)
210
211    includes += pycardincludes
212
213    wxincludes = []
214    wxcomponents = [
215          '_core'
216          ]
217
218    for component in wxcomponents:
219       wxincludes.append('wx.'+component)
220
221    includes += wxincludes
222
223 # assure that our source dir is included in the search path of included modules
224 sys.path.append(srcdir)
225
226
227
228 ########################################
229 # Utility functions                    #
230 ########################################
231
232 def unix_shellcmd(cmd):
233    """
234       Launch a subprocess in the unix shell
235       and return stdout of the command
236       (blocking!)
237    """
238    ret = subprocess.Popen(cmd, shell=True,
239          stdout=subprocess.PIPE,
240          close_fds=True).communicate()[0]
241    return ret.split("\n")
242
243
244 def win_shellcmd(cmd):
245    """
246       Launch a subprocess in win32
247       (blocking!)
248    """
249    ret = subprocess.Popen(cmd)
250    ret.wait()
251    return
252
253 def cxf_guipackagedata(base):
254    """
255       Collect GUI resource files and gui Images
256       to be copied in the package in the right format
257       for the include_files option in cx_freeze
258
259       Input:
260          str(base) - base (absolute) path of the package
261
262       Output:
263          a list of tuples with (src, target) files
264          where src is the absolute path of the source file,
265          target is the relative path inside the package
266
267    """
268
269    ret = []
270    imgpath = os.path.join(base, 'gui', 'img')
271    guipath = os.path.join(base, 'gui')
272    for root, dirs, files in os.walk(imgpath):
273       if not '.svn' in root:
274          for file in files:
275             src = os.path.join(root, file)
276             target = os.path.join(root.replace(base, '')[1:], file)
277             ret.append((src, target))
278
279    for root, dirs,  files in os.walk(guipath):
280       if not '.svn' in root:
281          for file in files:
282             if file[-8:] == '.rsrc.py':
283                src = os.path.join(root, file)
284                target = os.path.join(root.replace(base, '')[1:], file)
285                ret.append((src, target))
286    return ret
287
288
289 def add_dataFiles(base, compiler='cx_freeze'):
290    ret = []
291    if compiler == 'cx_freeze':
292       for file in ADD_FILES:
293          src = os.path.join(base, file)
294          target = file
295          ret.append((src, target))
296    elif compiler == 'py2exe'  or compiler == 'py2app':
297       srcs = []
298       for file in ADD_FILES:
299          srcs.append(os.path.join(base, file))
300       ret.append(('.', srcs))
301    return ret
302
303 def p2exe_guipackagedata(base):
304    """
305       Collect GUI resource files and gui images
306       to be copied in the package in the right
307       format for the data_files option in py2exe
308    """
309
310    files = cxf_guipackagedata(base)
311    tdict = {}
312    for src, target in files:
313       if os.path.dirname(target) in tdict.keys():
314          tdict[os.path.dirname(target)].append(src)
315       else:
316          tdict[os.path.dirname(target)] = [src]
317    ret = []
318    for target in tdict.keys():
319       ret.append((target, tdict[target]))
320    return ret
321
322 ########################################
323 # distutils commands                   #
324 ########################################
325
326
327
328 class makeDmg(Command):
329
330    """
331       Distutils command that take a .app
332       application and make a DMG compressed
333       image with the app and optionally
334       a README, INSTALL and LICENSE files.
335    """
336
337    user_options = []
338
339    def initialize_options(self):
340       pass
341
342    def finalize_options(self):
343       pass
344
345    def run(self):
346       import shutil
347       print 'Test if we have license, readme or install files'
348       for name in os.listdir(srcdir):
349          if os.path.isfile(os.path.join(srcdir, name)) \
350             and (name[:6].upper() == 'README' or name[:7].upper() == 'LICENSE'
351                or name[:7].upper() == 'INSTALL'):
352             dst = os.path.join(distdir, name)
353             if not os.path.exists(dst):
354                print 'Copying ', name
355                shutil.copy(os.path.join(srcdir, name), os.path.join(distdir, name))
356       dmg = os.path.join(curdir, 'builds', 'osx', NAME+"-"+VERSION+"_OSX_universal.dmg")
357       if os.path.exists(dmg):
358          print 'Removing old dmg image'
359          os.remove(dmg)
360       print 'Creating new dmg image'
361       volname = NAME+"-"+VERSION
362       if len(OSX_PRE_EXECUTE) > 0:
363          for ex in OSX_PRE_EXECUTE:
364             unix_shellcmd(curdir+'/'+ex)
365       cmd = "hdiutil create -srcfolder %s -format UDZO -imagekey zlib-level=9 -volname %s %s" \
366          % (distdir, volname, dmg)
367       print unix_shellcmd(cmd)[0]
368       print 'Installer ready in '+os.path.join(curdir, 'builds', 'osx')+' directory'
369
370 class makeself(Command):
371
372    """
373       Distutils command that take an open package directory
374       created by cx_freeze and create a .run self extracting
375       Linux/Posix executable installer with all
376       libraryes and dependencies inside the package using
377       standard tools like strip, ldd, makeself
378    """
379
380    # TODO: Manage differencies between 32bit and 64bit systems
381
382    user_options = []
383
384    def initialize_options(self):
385       pass
386
387    def finalize_options(self):
388       pass
389
390    def run(self):
391       import shutil
392       libdir=os.path.join(distdir, 'lib')
393       os.mkdir(libdir)
394       if LINUX_PRE_EXECUTE and len(LINUX_PRE_EXECUTE) > 0:
395          print 'execute pre-installer script'
396          for lcommand in LINUX_PRE_EXECUTE:
397             print 'Executing '+lcommand+'...'
398             unix_shellcmd(lcommand+" "+distdir)
399             print 'OK'
400
401       print 'Finding all needed dynamic libraries and stripping binaryes'
402       bins = unix_shellcmd('find '+distdir+' -type f -name "*.so"')
403       bins.append(os.path.join(distdir, NAME))
404       libs = []
405       for bin in bins:
406          if bin:
407             ldd = unix_shellcmd("ldd "+bin)
408             if os.path.basename(bin) != NAME:
409                # If we strip our binary we lose zipped content!
410                unix_shellcmd("strip "+bin)
411             for lib in ldd:
412                if len(lib.split()) > 3:
413                   libs.append(lib.split()[2])
414                elif len(lib.split()) == 3:
415                   libs.append(os.path.join('/lib', lib.split()[0]))
416                elif len(lib.split()) == 2:
417                   libs.append(lib.split()[0])
418
419       libs.sort()
420       libs = list(set(libs))
421       print 'copying needed libraries'
422       for lib in libs:
423          try:
424             shutil.copy(lib, libdir)
425             unix_shellcmd("strip "+os.path.join(libdir, os.path.basename(lib)))
426          except:
427             pass
428       print 'creating installer'
429       shutil.copy(os.path.join(scriptdir, 'linux_install.sh'), distdir)
430       os.chmod(os.path.join(distdir, 'linux_install.sh'), 0755)
431       runname = NAME+"-"+VERSION+"_"+platform.system()+"_i386-x86_64_install.run"
432       cmd = "makeself "+distdir+" "+runname
433       cmd += ' "'+NAME+' for '+platform.system()+' '+VERSION+'" ./linux_install.sh'
434       unix_shellcmd(cmd)
435       shutil.move(os.path.join(curdir, runname), os.path.join(curdir, 'builds', 'linux32'))
436       print 'remove dist directory'
437       shutil.rmtree(distdir)     
438       print 'Installer ready in '+os.path.join(curdir, 'builds', 'linux32')+' directory'
439
440
441 class makeWininstall(Command):
442    """
443       Distutils command that
444       get an open package created by py2exe and
445       make an exe and an optional msi windows
446       installer using InnoSetup
447    """
448
449    # TODO: Manage differencies between 32bit and 64bit systems
450
451    user_options = []
452
453    def initialize_options(self):
454       pass
455
456    def finalize_options(self):
457       pass
458
459    def run(self):
460
461       print 'Creating .exe installer'
462       destpath = os.path.join(curdir, 'builds', 'win32')
463       fname = NAME+"-"+VERSION+"_Win32_install"
464       inopath = os.path.join(curdir, 'scripts', 'ino.iss')
465       win_shellcmd("ISCC /O%s  /F%s %s" % (destpath, fname, inopath))
466       print '.exe installer ready in %s' % destpath
467       if WIN_BUILD_MSI:
468          licensefilertf = False
469          print 'a .msi installer is requested. Try to build it'
470          wixpath = os.getenv('WIX')
471          if not wixpath:
472             print 'WIX environment variable not set. Try to find the WIX path from the PATH environment variable'
473             for path in os.getenv('PATH').split(';'):
474                if os.path.exists(os.path.join(path, 'candle.exe')):
475                      wixpath = path
476                      break
477          if not wixpath:
478             print 'I can\'t find an usable WIX path. please set the WIX environment variable and re-execute '+sys.argv[0]
479          else:
480             print 'Checking for license file...'
481             if os.path.exists(os.path.join(curdir, 'dist', 'license.rtf')):
482                licensefilertf = True
483             else:
484                print 'RTF license file not found. Trying to generate it from the txt one'
485                try:
486                   licensefile = LICENSE_FILE
487                except:
488                   print 'LICENSE_FILE variable not set. Trying to autodetect one'
489                   for file in ['LICENSE', 'LICENSE.txt', 'license.txt']:
490                      if os.path.exists(os.path.join(curdir, 'dist', file)):
491                         licensefile = file
492                         break
493                if not licensefile:
494                   print 'I can\'t find a suitable license file txt. Please supply one!'
495                else:
496                   try:
497                      import PyRTF
498                      tfile = open(os.path.join(curdir, 'dist', licensefile), 'r')
499                      tfilecont = tfile.read()
500                      tfile.close()
501                      doc = PyRTF.Document()
502                      ss = doc.StyleSheet
503                      section = PyRTF.Section()
504                      doc.Sections.append(section)
505                      section.append(tfilecont)
506                      dr = PyRTF.Renderer()
507                      fout = open(os.path.join(curdir, 'dist', 'license.rtf'), 'wb')
508                      dr.Write(doc, fout)
509                      fout.close()
510                      licensefilertf = True
511                      print 'RTF License file successfuly generated'
512                   except:
513                      print 'PyRTF not installed. I can\'t generate the license file!'
514
515             if licensefilertf:
516
517                fname = NAME+"-"+VERSION+"_Win32_install.msi"
518                wxspath = os.path.join(curdir, 'scripts', 'wix.wxs')
519                candlepath = os.path.join(wixpath, 'candle.exe')
520                print 'Compiling wxs with candle.exe'
521                win_shellcmd("%s %s" % (candlepath, wxspath))
522                print 'Linking with light.exe'
523                win_shellcmd("light.exe -out %s %s %s\wixui.wixlib -loc %s\WixUI_en-us.wxl"
524                % ( os.path.join(destpath, fname), os.path.join(curdir, 'wix.wixobj'),
525                   wixpath, wixpath))
526                os.remove(os.path.join(curdir,  'wix.wixobj'))
527                print '.msi installer ready in %s' % destpath
528
529
530
531
532    
533
534 class create(Command):
535
536    """
537       Distutils command
538       detecting the platform and run
539       the right command sequence to produce
540       installers.
541    """
542
543    user_options = []
544
545    def get_sub_commands(self):
546       plat = platform.system()
547       print 'Platform detected: ', plat
548       if plat == 'Linux':
549          return ['build', 'makeself']
550       elif plat == 'Darwin':
551          return ['py2app', 'makeDmg']
552       elif plat == 'Windows' or plat == 'Microsoft':
553          return ['py2exe', 'makeWininstall']
554       return False
555       # XXX Fare anche osx
556
557
558    def initialize_options(self):
559       pass
560
561    def finalize_options(self):
562       pass
563
564    def run(self):
565       print 'Creating right installer for this platform'
566       commands = self.get_sub_commands()
567       if commands:
568          self.run_command('clean')
569          for command in commands:
570             self.run_command(command)
571       else:   
572          print 'i don\'t know what to do, sorry!'
573
574
575
576
577 class clean(distutils_clean.clean):
578
579    """
580       Distutils command that override the default
581       clean command performing more clean actions
582       on the root path of the sources
583    """
584
585    # XXX Deve rimuovere anche eventuali .c e .so/pyd da cython
586
587    paths = [
588          'MANIFEST',
589          'build',
590          'dist',
591          'wix.wixobj'
592          ]
593
594
595    def run(self):
596       distutils_clean.clean.run(self)
597       for path in self.paths:
598          p = os.path.join(curdir, path)
599          if os.path.exists(p):
600             if os.path.isdir(p):
601                for root, dirs,  files in os.walk(p, topdown=False):
602                   for name in files:
603                      os.remove(os.path.join(root,  name))
604                   for name in dirs:
605                      os.rmdir(os.path.join(root, name))
606                os.rmdir(root)
607             else:
608                os.remove(p)
609
610
611 ########################################
612 # Setting different dicts for          #
613 # distutils dependently of the         #
614 # platform                             #
615 ########################################
616
617
618 # Options for all systems
619 cmdclass = {
620    'clean': clean,
621    'create': create
622 }
623
624 # Options for Windows systems (with py2exe)
625 if platform.system() == 'Windows' or platform.system() == 'Microsoft':
626
627    from distutils.core import setup
628    import py2exe
629  
630    if APPEND_TO_EXE:
631       ZIPFILE=None
632
633    includes+=['lxml._elementpath','lxml.ElementInclude']
634
635    setup_options = { 'py2exe': {
636                   'includes':includes,
637                   'excludes': EXCLUDES,
638                   'optimize': OPTIMIZE,
639                   'compressed': COMPRESSION
640                   }
641              }
642
643    extra_options = {
644       'data_files': p2exe_guipackagedata(srcdir)+add_dataFiles(srcdir, 'py2exe')
645    }
646
647    if APPEND_TO_EXE:
648       extra_options['zipfile'] = None
649       #extra_options['bundle_files'] = 1
650       setup_options['py2exe']['bundle_files'] = 1
651
652    service = []
653    console = []
654    # XXX gestire meglio le icone
655    for bscript in BASESCRIPTS:
656       console.append({
657          'script': os.path.join(srcdir, bscript),
658          'icon_resource': [(1, os.path.join(srcdir, ICON))]
659          })
660
661    if WINSERVICES:
662       for bscript in WINSERVICES:
663          service.append({
664             'modules': 'src.'+bscript.replace('.py', ''),
665             'cmdline_style': 'pywin32'
666             })
667            
668
669    if len(service) > 0:
670       extra_options['service'] = service
671    if len(console) > 0:
672       if WX_GUI_PROGRAM:
673          if WINCONSOLE:
674             extra_options['console'] = console
675          else:
676             extra_options['windows'] = console
677       else:
678          extra_options['console'] = console
679
680    # Override build_exe command to get some DLL included
681    origIsSytemDLL = py2exe.build_exe.isSystemDLL
682    def isSystemDLL(path):
683       if os.path.basename(path).lower() in ('gdiplus.dll', 'msvcp71.dll', 'msvcp90.dll', 'gdi32.dll') or \
684             os.path.basename(path).lower()[:5] in ('msvcp', 'msvcr') or \
685             os.path.basename(path).lower().endswith('pyd'):
686          print str(path)+" INCLUDED"
687          return 0
688       return origIsSytemDLL(path)
689    py2exe.build_exe.isSystemDLL = isSystemDLL
690
691    # Add specific command for windows installer
692    cmdclass['makeWininstall'] = makeWininstall
693
694 # Options for Mac OSX systems  (with py2app)
695 elif platform.system() == 'Darwin':
696    from distutils.core import setup
697    import py2app
698
699    #cmdclass['makedmg'] = makedmg
700    
701    executables = []
702    for bscript in BASESCRIPTS:
703       executables.append(os.path.join(srcdir, bscript))
704
705    if UNIXDAEMONS:
706       for bscript in UNIXDAEMONS:
707          executables.append(os.path.join(srcdir, bscript))
708
709    # py2app require the same format of py2exe
710    py2app_guipackagedata = p2exe_guipackagedata
711
712    # Multiple targets not (yet) supported by py2app
713    # get only the first one!
714    extra_options = {
715      'app': [executables[0]],
716      'setup_requires': ["py2app"],
717      'data_files': p2exe_guipackagedata(srcdir)+add_dataFiles(srcdir, 'py2app'),
718    }
719    
720
721    setup_options = {
722       'py2app': {
723          'includes':includes,
724          'excludes': EXCLUDES,
725          'optimize': OPTIMIZE,
726          'plist': {},
727          'compressed': COMPRESSION,
728          'strip': True,
729          'dist_dir': distdir,
730          'site_packages': True,
731          'semi_standalone': False
732       }
733    }
734    
735    if OSX_ICON:
736       setup_options['py2app']['iconfile'] = os.path.join(srcdir, OSX_ICON)
737
738    # Add specific command for creating a DMG Image
739    cmdclass['makeDmg'] = makeDmg
740
741
742 # Options for Linux (and maybe other posix?) systems (with cx_freeze)
743 else:
744
745    from cx_Freeze import setup, Executable
746
747    cmdclass['makeself'] = makeself
748
749    executables = []
750    for bscript in BASESCRIPTS:
751       executables.append(Executable(os.path.join(srcdir, bscript)))
752
753    if UNIXDAEMONS:
754       for bscript in UNIXDAEMONS:
755          executables.append(Executable(os.path.join(srcdir, bscript)))
756
757    extra_options = {
758      'executables': executables,
759    }
760
761    CREATE_SHARED_LIB=True
762    if APPEND_TO_EXE:
763       CREATE_SHARED_LIB=False
764
765    setup_options = {
766       'build':  {
767          'build_exe': distdir
768       },
769       'build_exe': {
770          'optimize': OPTIMIZE,
771          'includes': includes,
772          'excludes': EXCLUDES,
773          'compressed': COMPRESSION,
774          'create_shared_zip': CREATE_SHARED_LIB,
775          'append_script_to_exe': APPEND_TO_EXE,
776          'icon': os.path.join(srcdir, ICON),
777          'include_files': cxf_guipackagedata(srcdir)+add_dataFiles(srcdir),
778       }
779    }
780
781
782 ########################################
783 # Cython setup() declarations          #
784 ########################################
785
786 #if sys.argv[1] == 'create':
787    #
788    # XXX Qui inserire un controllo che verifica l'esistenza
789    # di file .pyx e nel caso usa cython per compilare
790    #
791    #try:
792    #   from distutils.core import setup, Extension
793    #   from Cython.Distutils import build_ext
794    #except:
795    #   print 'Cython compilation required, but Cython not installed!'
796    #   sys.exit(0)
797
798      
799
800
801 ########################################
802 # Distutils setup() declaration        #
803 ########################################
804
805 # TODO: Far configurare anche i classifiers
806 setup(
807       name = NAME,
808       version = VERSION,
809       description = DESCRIPTION,
810       author = AUTHOR,
811       author_email = EMAIL,
812       url = URL,
813       options = setup_options,
814       classifiers = [
815             'Developement Status :: 0.2.0 - Unstable',
816             'Environment :: Desktop GUI',
817             'Intended Audience :: Developers',
818             'Intended Audience :: End Users/Desktop',
819             'License :: '+LICENSE_TYPE,
820             'Operating System :: MacOS :: MacOS X',
821             'Operating System :: Microsoft :: Windows',
822             'Operating System :: POSIX :: GNU/Linux',
823             'Programming Language :: Python',
824             'Topic :: Communications',
825          ],
826       cmdclass = cmdclass,
827       **extra_options
828       )
829
Note: See TracBrowser for help on using the browser.