OTWO-1213 Works around lost encoding in Ruby/C binding layer
[ohcount] / python / setup.py
1 #!/usr/bin/env python
2
3 import distutils.ccompiler
4 from distutils.core import setup, Extension
5 from distutils.command.build import build
6 from distutils.command.build_ext import build_ext
7 from distutils.command.install_lib import install_lib
8 import os, sys
9 from glob import glob
10
11 if not hasattr(sys, 'version_info') or sys.version_info < (2,6,0,'final'):
12     raise SystemExit("Ohcount requires Python 2.6 or later.")
13
14 class build_ohcount(build):
15     """Ohcount already have a script named 'build', from the original package,
16     so it conflicts with Python default build path. To solve this, setup.py
17     will use the directory 'build-python' instead. The original distutils
18     execute 'build_py' before 'build_ext', but we need the wrapper ohcount.py
19     created by SWIG to be installed too, so we need to invert this order.
20     """
21
22     sub_commands = [('build_ext',     build.has_ext_modules),  # changed
23                     ('build_py',      build.has_pure_modules), # changed
24                     ('build_clib',    build.has_c_libraries),
25                     ('build_scripts', build.has_scripts),
26                    ]
27
28     def initialize_options(self):
29         build.initialize_options(self)
30         self.build_base = 'build-python'
31
32 def newer_than(srclist, dstlist):
33     for left, right in zip(srclist, dstlist):
34         if not os.path.exists(right):
35             return True
36         left_stat = os.lstat(left)
37         right_stat = os.lstat(right)
38         if left_stat.st_mtime > right_stat.st_mtime:
39             return True
40     return False
41
42 class build_ohcount_ext(build_ext):
43     """This class implements extra steps needed by Ohcount build process."""
44
45     def run(self):
46         parsers = glob('src/parsers/*.rl')
47         parsers_h = [f.replace('.rl', '.h') for f in parsers]
48         if newer_than(parsers, parsers_h):
49             os.system('cd src/parsers/ && bash ./compile')
50         hash_files = glob('src/hash/*.gperf')
51         hash_srcs = []
52         for f in hash_files:
53             if not f.endswith('languages.gperf'):
54                 hash_srcs.append(f.replace('s.gperf', '_hash.h'))
55             else:
56                 hash_srcs.append(f.replace('s.gperf', '_hash.c'))
57         if newer_than(hash_files, hash_srcs):
58             os.system('cd src/hash/ && bash ./generate_headers')
59         return build_ext.run(self)
60
61 # Overwrite default Mingw32 compiler
62 (module_name, class_name, long_description) = \
63         distutils.ccompiler.compiler_class['mingw32']
64 module_name = "distutils." + module_name
65 __import__(module_name)
66 module = sys.modules[module_name]
67 Mingw32CCompiler = vars(module)[class_name]
68
69 class Mingw32CCompiler_ohcount(Mingw32CCompiler):
70     """Ohcount CCompiler version for Mingw32. There is a problem linking
71     against msvcrXX for Python 2.6.4: as both DLLs msvcr and msvcr90 are
72     loaded, it seems to happen some unexpected segmentation faults in
73     several function calls."""
74
75     def __init__(self, *args, **kwargs):
76         Mingw32CCompiler.__init__(self, *args, **kwargs)
77         self.dll_libraries=[] # empty link libraries list
78
79 _new_compiler = distutils.ccompiler.new_compiler
80
81 def ohcount_new_compiler(plat=None,compiler=None,verbose=0,dry_run=0,force=0):
82     if compiler == 'mingw32':
83         inst = Mingw32CCompiler_ohcount(None, dry_run, force)
84     else:
85         inst = _new_compiler(plat,compiler,verbose,dry_run,force)
86     return inst
87
88 distutils.ccompiler.new_compiler = ohcount_new_compiler
89
90 # Ohcount python extension
91 ext_modules=[
92     Extension(
93         name='ohcount._ohcount',
94         sources= [
95             'ruby/ohcount.i',
96             'src/sourcefile.c',
97             'src/detector.c',
98             'src/licenses.c',
99             'src/parser.c',
100             'src/loc.c',
101             'src/log.c',
102             'src/diff.c',
103             'src/parsed_language.c',
104             'src/hash/language_hash.c',
105         ],
106         libraries=['pcre'],
107         swig_opts=['-outdir', './python/'],
108     )
109 ]
110
111 setup(
112     name='ohcount',
113     version = '3.0.0',
114     description = 'Ohcount is the source code line counter that powers Ohloh.',
115     long_description =
116         'Ohcount supports over 70 popular programming languages, and has been '
117         'used to count over 6 billion lines of code by 300,000 developers! '
118         'Ohcount does more more than just count lines of code. It can also '
119         'detect popular open source licenses such as GPL within a large '
120         'directory of source code. It can also detect code that targets a '
121         'particular programming API, such as Win32 or KDE.',
122     author = 'Mitchell Foral',
123     author_email = 'mitchell@caladbolg.net',
124     license = 'GNU GPL',
125     platforms = ['Linux','Mac OSX'],
126     keywords = ['ohcount','ohloh','loc','source','code','line','counter'],
127     url = 'http://www.ohloh.net/p/ohcount',
128     download_url = 'http://sourceforge.net/projects/ohcount/files/',
129     packages = ['ohcount'],
130     package_dir = {'ohcount': 'python'},
131     classifiers = [
132         'Development Status :: 5 - Production/Stable',
133         'License :: OSI Approved :: GNU General Public License (GPL)'
134         'Intended Audience :: Developers',
135         'Natural Language :: English',
136         'Programming Language :: C',
137         'Programming Language :: Python',
138         'Topic :: Software Development :: Libraries :: Python Modules',
139     ],
140     ext_modules=ext_modules,
141     cmdclass={
142         'build': build_ohcount,
143         'build_ext': build_ohcount_ext,
144     },
145 )
146