comparison setup.py @ 4068:e233d7a66343

Refactor setup.py.
author Stefan Seefeld <stefan@seefeld.name>
date Mon, 23 Feb 2009 14:30:32 +0000
parents bca7c59ac400
children a6fdaaa3a8bd
comparison
equal deleted inserted replaced
4067:7309ac3b6e24 4068:e233d7a66343
14 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 14 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
15 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" 15 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
16 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, 16 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
17 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 17 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
18 # 18 #
19 # $Id: setup.py,v 1.105 2008-09-01 01:58:32 richard Exp $
20 19
21 20
22 from roundup.dist.command.build_doc import build_doc 21 from roundup.dist.command.build_doc import build_doc
23 from distutils.core import setup, Extension 22 from roundup.dist.command.build_scripts import build_scripts
24 from distutils.util import get_platform 23 from roundup.dist.command.build_py import build_py
25 from distutils.file_util import write_file 24 from roundup.dist.command.build import build, list_message_files
26 from distutils.command.bdist_rpm import bdist_rpm 25 from roundup.dist.command.bdist_rpm import bdist_rpm
27 from distutils.command.build import build 26 from distutils.core import setup
28 from distutils.command.build_scripts import build_scripts
29 from distutils.command.build_py import build_py
30 27
31 import sys, os, string 28 import sys, os
32 from glob import glob 29 from glob import glob
33 30
34 # patch distutils if it can't cope with the "classifiers" keyword 31 # patch distutils if it can't cope with the "classifiers" keyword
35 from distutils.dist import DistributionMetadata 32 from distutils.dist import DistributionMetadata
36 if not hasattr(DistributionMetadata, 'classifiers'): 33 if not hasattr(DistributionMetadata, 'classifiers'):
37 DistributionMetadata.classifiers = None 34 DistributionMetadata.classifiers = None
38 DistributionMetadata.download_url = None 35 DistributionMetadata.download_url = None
39
40 from roundup import msgfmt
41 36
42 def include(d, e): 37 def include(d, e):
43 """Generate a pair of (directory, file-list) for installation. 38 """Generate a pair of (directory, file-list) for installation.
44 39
45 'd' -- A directory 40 'd' -- A directory
46 41
47 'e' -- A glob pattern""" 42 'e' -- A glob pattern"""
48 43
49 return (d, [f for f in glob('%s/%s'%(d, e)) if os.path.isfile(f)]) 44 return (d, [f for f in glob('%s/%s'%(d, e)) if os.path.isfile(f)])
50 45
51 #############################################################################
52 ### Build script files
53 #############################################################################
54
55 class build_scripts_create(build_scripts):
56 """ Overload the build_scripts command and create the scripts
57 from scratch, depending on the target platform.
58
59 You have to define the name of your package in an inherited
60 class (due to the delayed instantiation of command classes
61 in distutils, this cannot be passed to __init__).
62
63 The scripts are created in an uniform scheme: they start the
64 run() function in the module
65
66 <packagename>.scripts.<mangled_scriptname>
67
68 The mangling of script names replaces '-' and '/' characters
69 with '-' and '.', so that they are valid module paths.
70
71 If the target platform is win32, create .bat files instead of
72 *nix shell scripts. Target platform is set to "win32" if main
73 command is 'bdist_wininst' or if the command is 'bdist' and
74 it has the list of formats (from command line or config file)
75 and the first item on that list is wininst. Otherwise
76 target platform is set to current (build) platform.
77 """
78 package_name = None
79
80 def initialize_options(self):
81 build_scripts.initialize_options(self)
82 self.script_preamble = None
83 self.target_platform = None
84 self.python_executable = None
85
86 def finalize_options(self):
87 build_scripts.finalize_options(self)
88 cmdopt=self.distribution.command_options
89
90 # find the target platform
91 if self.target_platform:
92 # TODO? allow explicit setting from command line
93 target = self.target_platform
94 if cmdopt.has_key("bdist_wininst"):
95 target = "win32"
96 elif cmdopt.get("bdist", {}).has_key("formats"):
97 formats = cmdopt["bdist"]["formats"][1].split(",")
98 if formats[0] == "wininst":
99 target = "win32"
100 else:
101 target = sys.platform
102 if len(formats) > 1:
103 self.warn(
104 "Scripts are built for %s only (requested formats: %s)"
105 % (target, ",".join(formats)))
106 else:
107 # default to current platform
108 target = sys.platform
109 self.target_platfom = target
110
111 # for native builds, use current python executable path;
112 # for cross-platform builds, use default executable name
113 if self.python_executable:
114 # TODO? allow command-line option
115 pass
116 if target == sys.platform:
117 self.python_executable = os.path.normpath(sys.executable)
118 else:
119 self.python_executable = "python"
120
121 # for windows builds, add ".bat" extension
122 if target == "win32":
123 # *nix-like scripts may be useful also on win32 (cygwin)
124 # to build both script versions, use:
125 #self.scripts = list(self.scripts) + [script + ".bat"
126 # for script in self.scripts]
127 self.scripts = [script + ".bat" for script in self.scripts]
128
129 # tweak python path for installations outside main python library
130 if cmdopt.get("install", {}).has_key("prefix"):
131 prefix = os.path.expanduser(cmdopt['install']['prefix'][1])
132 version = '%d.%d'%sys.version_info[:2]
133 self.script_preamble = '''
134 import sys
135 sys.path.insert(1, "%s/lib/python%s/site-packages")
136 '''%(prefix, version)
137 else:
138 self.script_preamble = ''
139
140 def copy_scripts(self):
141 """ Create each script listed in 'self.scripts'
142 """
143 if not self.package_name:
144 raise Exception("You have to inherit build_scripts_create and"
145 " provide a package name")
146
147 to_module = string.maketrans('-/', '_.')
148
149 self.mkpath(self.build_dir)
150 for script in self.scripts:
151 outfile = os.path.join(self.build_dir, os.path.basename(script))
152
153 #if not self.force and not newer(script, outfile):
154 # self.announce("not copying %s (up-to-date)" % script)
155 # continue
156
157 if self.dry_run:
158 self.announce("would create %s" % outfile)
159 continue
160
161 module = os.path.splitext(os.path.basename(script))[0]
162 module = string.translate(module, to_module)
163 script_vars = {
164 'python': self.python_executable,
165 'package': self.package_name,
166 'module': module,
167 'prefix': self.script_preamble,
168 }
169
170 self.announce("creating %s" % outfile)
171 file = open(outfile, 'w')
172
173 try:
174 # could just check self.target_platform,
175 # but looking at the script extension
176 # makes it possible to build both *nix-like
177 # and windows-like scripts on win32.
178 # may be useful for cygwin.
179 if os.path.splitext(outfile)[1] == ".bat":
180 file.write('@echo off\n'
181 'if NOT "%%_4ver%%" == "" "%(python)s" -c "from %(package)s.scripts.%(module)s import run; run()" %%$\n'
182 'if "%%_4ver%%" == "" "%(python)s" -c "from %(package)s.scripts.%(module)s import run; run()" %%*\n'
183 % script_vars)
184 else:
185 file.write('#! %(python)s\n%(prefix)s'
186 'from %(package)s.scripts.%(module)s import run\n'
187 'run()\n'
188 % script_vars)
189 finally:
190 file.close()
191 os.chmod(outfile, 0755)
192
193
194 class build_scripts_roundup(build_scripts_create):
195 package_name = 'roundup'
196
197
198 def scriptname(path): 46 def scriptname(path):
199 """ Helper for building a list of script names from a list of 47 """ Helper for building a list of script names from a list of
200 module files. 48 module files.
201 """ 49 """
202 script = os.path.splitext(os.path.basename(path))[0] 50 script = os.path.splitext(os.path.basename(path))[0]
203 script = string.replace(script, '_', '-') 51 script = script.replace('_', '-')
204 return script 52 return script
205
206 ### Build Roundup
207
208 def list_message_files(suffix=".po"):
209 """Return list of all found message files and their intallation paths"""
210 _files = glob("locale/*" + suffix)
211 _list = []
212 for _file in _files:
213 # basename (without extension) is a locale name
214 _locale = os.path.splitext(os.path.basename(_file))[0]
215 _list.append((_file, os.path.join(
216 "share", "locale", _locale, "LC_MESSAGES", "roundup.mo")))
217 return _list
218
219 def check_manifest():
220 """Check that the files listed in the MANIFEST are present when the
221 source is unpacked.
222 """
223 try:
224 f = open('MANIFEST')
225 except:
226 print '\n*** SOURCE WARNING: The MANIFEST file is missing!'
227 return
228 try:
229 manifest = [l.strip() for l in f.readlines()]
230 finally:
231 f.close()
232 err = [line for line in manifest if not os.path.exists(line)]
233 err.sort()
234 # ignore auto-generated files
235 if err == ['roundup-admin', 'roundup-demo', 'roundup-gettext',
236 'roundup-mailgw', 'roundup-server']:
237 err = []
238 if err:
239 n = len(manifest)
240 print '\n*** SOURCE WARNING: There are files missing (%d/%d found)!'%(
241 n-len(err), n)
242 print 'Missing:', '\nMissing: '.join(err)
243
244
245 class build_py_roundup(build_py):
246
247 def find_modules(self):
248 # Files listed in py_modules are in the toplevel directory
249 # of the source distribution.
250 modules = []
251 for module in self.py_modules:
252 path = string.split(module, '.')
253 package = string.join(path[0:-1], '.')
254 module_base = path[-1]
255 module_file = module_base + '.py'
256 if self.check_module(module, module_file):
257 modules.append((package, module_base, module_file))
258 return modules
259
260
261 class build_roundup(build):
262
263 def build_message_files(self):
264 """For each locale/*.po, build .mo file in target locale directory"""
265 for (_src, _dst) in list_message_files():
266 _build_dst = os.path.join("build", _dst)
267 self.mkpath(os.path.dirname(_build_dst))
268 self.announce("Compiling %s -> %s" % (_src, _build_dst))
269 msgfmt.make(_src, _build_dst)
270
271 def run(self):
272 check_manifest()
273 self.build_message_files()
274 build.run(self)
275
276 class bdist_rpm_roundup(bdist_rpm):
277
278 def finalize_options(self):
279 bdist_rpm.finalize_options(self)
280 if self.install_script:
281 # install script is overridden. skip default
282 return
283 # install script option must be file name.
284 # create the file in rpm build directory.
285 install_script = os.path.join(self.rpm_base, "install.sh")
286 self.mkpath(self.rpm_base)
287 self.execute(write_file, (install_script, [
288 ("%s setup.py install --root=$RPM_BUILD_ROOT "
289 "--record=ROUNDUP_FILES") % self.python,
290 # allow any additional extension for man pages
291 # (rpm may compress them to .gz or .bz2)
292 # man page here is any file
293 # with single-character extension
294 # in man directory
295 "sed -e 's,\(/man/.*\..\)$,\\1*,' "
296 "<ROUNDUP_FILES >INSTALLED_FILES",
297 ]), "writing '%s'" % install_script)
298 self.install_script = install_script
299
300 #############################################################################
301 ### Main setup stuff
302 #############################################################################
303 53
304 def main(): 54 def main():
305 # build list of scripts from their implementation modules 55 # build list of scripts from their implementation modules
306 roundup_scripts = map(scriptname, glob('roundup/scripts/[!_]*.py')) 56 roundup_scripts = map(scriptname, glob('roundup/scripts/[!_]*.py'))
307 57
354 # add docs 104 # add docs
355 installdatafiles.append(include(os.path.join('share', 'doc', 'roundup', 'html'), '*')) 105 installdatafiles.append(include(os.path.join('share', 'doc', 'roundup', 'html'), '*'))
356 106
357 # perform the setup action 107 # perform the setup action
358 from roundup import __version__ 108 from roundup import __version__
359 setup_args = { 109
360 'name': "roundup", 110 setup(name='roundup',
361 'version': __version__, 111 version=__version__,
362 'description': "A simple-to-use and -install issue-tracking system" 112 author="Richard Jones",
363 " with command-line, web and e-mail interfaces. Highly" 113 author_email="richard@users.sourceforge.net",
364 " customisable.", 114 description='Issue-tracking System.',
365 'long_description': 115 long_description="""Roundup is a simple-to-use and -install issue-tracking system
366 '''In this release 116 with command-line, web and e-mail interfaces. Highly customisable.""",
367 =============== 117 url='http://www.roundup-tracker.org',
118 download_url='http://pypi.python.org/pypi/roundup',
119 classifiers=['Development Status :: 5 - Production/Stable',
120 'Environment :: Console',
121 'Environment :: Web Environment',
122 'Intended Audience :: End Users/Desktop',
123 'Intended Audience :: Developers',
124 'Intended Audience :: System Administrators',
125 'License :: OSI Approved :: Python Software Foundation License',
126 'Operating System :: MacOS :: MacOS X',
127 'Operating System :: Microsoft :: Windows',
128 'Operating System :: POSIX',
129 'Programming Language :: Python',
130 'Topic :: Communications :: Email',
131 'Topic :: Office/Business',
132 'Topic :: Software Development :: Bug Tracking',
133 ],
368 134
369 1.4.6 is a bugfix release: 135 # Override certain command classes with our own ones
370 136 cmdclass= {'build_doc': build_doc,
371 - Fix bug introduced in 1.4.5 in RDBMS full-text indexing 137 'build_scripts': build_scripts,
372 - Make URL matching code less matchy 138 'build_py': build_py,
373 139 'build': build,
374 If you're upgrading from an older version of Roundup you *must* follow 140 'bdist_rpm': bdist_rpm,
375 the "Software Upgrade" guidelines given in the maintenance documentation. 141 },
376 142 packages=packagelist,
377 Roundup requires python 2.3 or later for correct operation. 143 py_modules=py_modules,
378 144 scripts=roundup_scripts,
379 To give Roundup a try, just download (see below), unpack and run:: 145 data_files=installdatafiles)
380
381 roundup-demo
382
383 Documentation is available at the website:
384 http://roundup.sourceforge.net/
385 Mailing lists - the place to ask questions:
386 http://sourceforge.net/mail/?group_id=31577
387
388 About Roundup
389 =============
390
391 Roundup is a simple-to-use and -install issue-tracking system with
392 command-line, web and e-mail interfaces. It is based on the winning design
393 from Ka-Ping Yee in the Software Carpentry "Track" design competition.
394
395 Note: Ping is not responsible for this project. The contact for this
396 project is richard@users.sourceforge.net.
397
398 Roundup manages a number of issues (with flexible properties such as
399 "description", "priority", and so on) and provides the ability to:
400
401 (a) submit new issues,
402 (b) find and edit existing issues, and
403 (c) discuss issues with other participants.
404
405 The system will facilitate communication among the participants by managing
406 discussions and notifying interested parties when issues are edited. One of
407 the major design goals for Roundup that it be simple to get going. Roundup
408 is therefore usable "out of the box" with any python 2.3+ installation. It
409 doesn't even need to be "installed" to be operational, though a
410 disutils-based install script is provided.
411
412 It comes with two issue tracker templates (a classic bug/feature tracker and
413 a minimal skeleton) and five database back-ends (anydbm, sqlite, metakit,
414 mysql and postgresql).
415 ''',
416 'author': "Richard Jones",
417 'author_email': "richard@users.sourceforge.net",
418 'url': 'http://roundup.sourceforge.net/',
419 'packages': packagelist,
420 'classifiers': [
421 'Development Status :: 5 - Production/Stable',
422 'Environment :: Console',
423 'Environment :: Web Environment',
424 'Intended Audience :: End Users/Desktop',
425 'Intended Audience :: Developers',
426 'Intended Audience :: System Administrators',
427 'License :: OSI Approved :: Python Software Foundation License',
428 'Operating System :: MacOS :: MacOS X',
429 'Operating System :: Microsoft :: Windows',
430 'Operating System :: POSIX',
431 'Programming Language :: Python',
432 'Topic :: Communications :: Email',
433 'Topic :: Office/Business',
434 'Topic :: Software Development :: Bug Tracking',
435 ],
436
437 # Override certain command classes with our own ones
438 'cmdclass': {
439 'build_doc': build_doc,
440 'build_scripts': build_scripts_roundup,
441 'build_py': build_py_roundup,
442 'build': build_roundup,
443 'bdist_rpm': bdist_rpm_roundup,
444 },
445 'scripts': roundup_scripts,
446
447 'data_files': installdatafiles
448 }
449 if sys.version_info[:2] > (2, 2):
450 setup_args['py_modules'] = py_modules
451
452 setup(**setup_args)
453 146
454 if __name__ == '__main__': 147 if __name__ == '__main__':
455 main() 148 main()
456 149
457 # vim: set filetype=python sts=4 sw=4 et si : 150 # vim: set filetype=python sts=4 sw=4 et si :

Roundup Issue Tracker: http://roundup-tracker.org/