bump version in setup.py
[pysqlcipher.git] / setup.py
1 #-*- coding: ISO-8859-1 -*-
2 # setup.py: the distutils script
3 #
4 # Copyright (C) 2013 Kali Kaneko <kali@futeisha.org> (sqlcipher support)
5 # Copyright (C) 2005-2010 Gerhard Häring <gh@ghaering.de>
6 #
7 # This file is part of pysqlcipher.
8 #
9 # This software is provided 'as-is', without any express or implied
10 # warranty.  In no event will the authors be held liable for any damages
11 # arising from the use of this software.
12 #
13 # Permission is granted to anyone to use this software for any purpose,
14 # including commercial applications, and to alter it and redistribute it
15 # freely, subject to the following restrictions:
16 #
17 # 1. The origin of this software must not be misrepresented; you must not
18 #    claim that you wrote the original software. If you use this software
19 #    in a product, an acknowledgment in the product documentation would be
20 #    appreciated but is not required.
21 # 2. Altered source versions must be plainly marked as such, and must not be
22 #    misrepresented as being the original software.
23 # 3. This notice may not be removed or altered from any source distribution.
24 import os
25 import re
26 import sys
27 import urllib
28 import zipfile
29
30 from types import ListType, TupleType
31
32 from distutils.command.build import build
33 from distutils.command.build_ext import build_ext
34 from distutils.dep_util import newer_group
35 from distutils.errors import DistutilsSetupError
36 from distutils import log
37
38 import setuptools
39 from setuptools import Extension, Command
40
41 import cross_bdist_wininst
42
43 # If you need to change anything, it should be enough to change setup.cfg.
44
45 sqlite = "sqlite"
46
47 PYSQLITE_EXPERIMENTAL = False
48
49 DEV_VERSION = None
50
51 PATCH_VERSION = None
52
53 sources = ["src/module.c", "src/connection.c", "src/cursor.c", "src/cache.c",
54            "src/microprotocols.c", "src/prepare_protocol.c", "src/statement.c",
55            "src/util.c", "src/row.c"]
56
57 if PYSQLITE_EXPERIMENTAL:
58     sources.append("src/backup.c")
59
60
61 if sys.platform == "darwin":
62     # Work around clang raising hard error for unused arguments
63     os.environ['CFLAGS'] = "-Qunused-arguments"
64     print "CFLAGS", os.environ['CFLAGS']
65
66 include_dirs = []
67 library_dirs = []
68 libraries = []
69 runtime_library_dirs = []
70 extra_objects = []
71 define_macros = []
72
73 long_description = \
74 """Python interface to SQLCipher
75
76 pysqlcipher is an interface to the SQLite 3.x embedded relational
77 database engine. It is almost fully compliant with the Python database API
78 version 2.0. At the same time, it also exposes the unique features of
79 SQLCipher."""
80
81 if sys.platform != "win32":
82     define_macros.append(('MODULE_NAME', '"pysqlcipher.dbapi2"'))
83 else:
84     define_macros.append(('MODULE_NAME', '\\"pysqlcipher.dbapi2\\"'))
85
86
87 class DocBuilder(Command):
88     description = "Builds the documentation"
89     user_options = []
90
91     def initialize_options(self):
92         pass
93
94     def finalize_options(self):
95         pass
96
97     def run(self):
98         import shutil
99         try:
100             shutil.rmtree("build/doc")
101         except OSError:
102             pass
103         os.makedirs("build/doc")
104         rc = os.system("sphinx-build doc/sphinx build/doc")
105         if rc != 0:
106             print ("Is sphinx installed? If not, "
107                    "try 'sudo easy_install sphinx'.")
108
109 AMALGAMATION_ROOT = "amalgamation"
110
111
112 def get_amalgamation():
113     """Download the SQLite amalgamation if it isn't there, already."""
114     if os.path.exists(AMALGAMATION_ROOT):
115         return
116     os.mkdir(AMALGAMATION_ROOT)
117     print "Downloading amalgation."
118
119     amalgamation_url = ("https://downloads.leap.se/libs/pysqlcipher"
120                         "amalgamation-sqlcipher-2.1.0.zip")
121
122     # and download it
123     print 'amalgamation url: %s' % (amalgamation_url,)
124     urllib.urlretrieve(amalgamation_url, "tmp.zip")
125
126     zf = zipfile.ZipFile("tmp.zip")
127     files = ["sqlite3.c", "sqlite3.h"]
128     directory = zf.namelist()[0]
129
130     for fn in files:
131         print "Extracting", fn
132         outf = open(AMALGAMATION_ROOT + os.sep + fn, "wb")
133         outf.write(zf.read(directory + fn))
134         outf.close()
135     zf.close()
136     os.unlink("tmp.zip")
137
138
139 class AmalgamationBuilder(build):
140     description = ("Build a statically built pysqlcipher "
141                    "downloading and using a sqlcipher amalgamation.")
142
143     def __init__(self, *args, **kwargs):
144         MyBuildExt.amalgamation = True
145         MyBuildExt.static = True
146         build.__init__(self, *args, **kwargs)
147
148
149 class LibSQLCipherBuilder(build_ext):
150
151     description = ("Build C extension linking against libsqlcipher library.")
152
153     def build_extension(self, ext):
154         ext.extra_compile_args.append("-I/usr/include/sqlcipher/")
155         ext.extra_link_args.append("-lsqlcipher")
156         build_ext.build_extension(self, ext)
157
158
159 class MyBuildExt(build_ext):
160     amalgamation = True  # We want amalgamation on the default build for now
161     static = False
162
163     def build_extension(self, ext):
164         if self.amalgamation:
165             get_amalgamation()
166             # build with fulltext search enabled
167             ext.define_macros.append(
168                 ("SQLITE_ENABLE_FTS3", "1"))
169             ext.define_macros.append(
170                 ("SQLITE_ENABLE_RTREE", "1"))
171
172             # SQLCipher options
173             ext.define_macros.append(
174                 ("SQLITE_ENABLE_LOAD_EXTENSION", "1"))
175             ext.define_macros.append(
176                 ("SQLITE_HAS_CODEC", "1"))
177             ext.define_macros.append(
178                 ("SQLITE_TEMP_STORE", "2"))
179
180             ext.sources.append(os.path.join(AMALGAMATION_ROOT, "sqlite3.c"))
181             ext.include_dirs.append(AMALGAMATION_ROOT)
182
183             if sys.platform == "win32":
184                 # Try to locate openssl
185                 openssl_conf = os.environ.get('OPENSSL_CONF')
186                 if not openssl_conf:
187                     sys.exit('Fatal error: OpenSSL could not be detected!')
188                 openssl = os.path.dirname(os.path.dirname(openssl_conf))
189
190                 # Configure the compiler
191                 ext.include_dirs.append(os.path.join(openssl, "include"))
192                 ext.define_macros.append(("inline", "__inline"))
193
194                 # Configure the linker
195                 ext.extra_link_args.append("libeay32.lib")
196                 ext.extra_link_args.append(
197                     "/LIBPATH:" + os.path.join(openssl, "lib")
198                 )
199             else:
200                 ext.extra_link_args.append("-lcrypto")
201
202         if self.static:
203             self._build_extension(ext)
204         else:
205             build_ext.build_extension(self, ext)
206
207     def _build_extension(self, ext):
208         sources = ext.sources
209         if sources is None or type(sources) not in (ListType, TupleType):
210             raise DistutilsSetupError, \
211                 ("in 'ext_modules' option (extension '%s'), " +
212                  "'sources' must be present and must be " +
213                  "a list of source filenames") % ext.name
214         sources = list(sources)
215
216         ext_path = self.get_ext_fullpath(ext.name)
217         depends = sources + ext.depends
218         if not (self.force or newer_group(depends, ext_path, 'newer')):
219             log.debug("skipping '%s' extension (up-to-date)", ext.name)
220             return
221         else:
222             log.info("building '%s' extension", ext.name)
223
224         # First, scan the sources for SWIG definition files (.i), run
225         # SWIG on 'em to create .c files, and modify the sources list
226         # accordingly.
227         sources = self.swig_sources(sources, ext)
228
229         # Next, compile the source code to object files.
230
231         # XXX not honouring 'define_macros' or 'undef_macros' -- the
232         # CCompiler API needs to change to accommodate this, and I
233         # want to do one thing at a time!
234
235         # Two possible sources for extra compiler arguments:
236         #   - 'extra_compile_args' in Extension object
237         #   - CFLAGS environment variable (not particularly
238         #     elegant, but people seem to expect it and I
239         #     guess it's useful)
240         # The environment variable should take precedence, and
241         # any sensible compiler will give precedence to later
242         # command line args.  Hence we combine them in order:
243         extra_args = ext.extra_compile_args or []
244
245         macros = ext.define_macros[:]
246         for undef in ext.undef_macros:
247             macros.append((undef,))
248
249         objects = self.compiler.compile(sources,
250                                         output_dir=self.build_temp,
251                                         macros=macros,
252                                         include_dirs=ext.include_dirs,
253                                         debug=self.debug,
254                                         extra_postargs=extra_args,
255                                         depends=ext.depends)
256
257         # XXX -- this is a Vile HACK!
258         #
259         # The setup.py script for Python on Unix needs to be able to
260         # get this list so it can perform all the clean up needed to
261         # avoid keeping object files around when cleaning out a failed
262         # build of an extension module.  Since Distutils does not
263         # track dependencies, we have to get rid of intermediates to
264         # ensure all the intermediates will be properly re-built.
265         #
266         self._built_objects = objects[:]
267
268         # Now link the object files together into a "shared object" --
269         # of course, first we have to figure out all the other things
270         # that go into the mix.
271         if ext.extra_objects:
272             objects.extend(ext.extra_objects)
273         extra_args = ext.extra_link_args or []
274
275         # Detect target language, if not provided
276         language = ext.language or self.compiler.detect_language(sources)
277
278         #self.compiler.link_shared_object(
279             #objects, ext_path,
280             #libraries=self.get_libraries(ext),
281             #library_dirs=ext.library_dirs,
282             #runtime_library_dirs=ext.runtime_library_dirs,
283             #extra_postargs=extra_args,
284             #export_symbols=self.get_export_symbols(ext),
285             #debug=self.debug,
286             #build_temp=self.build_temp,
287             #target_lang=language)
288
289         # XXX may I have a static lib please?
290         # hmm but then I cannot load that extension, or can I?
291         output_dir = os.path.sep.join(ext_path.split(os.path.sep)[:-1])
292
293         self.compiler.create_static_lib(
294             objects,
295             #XXX get library name ... splitting ext_path?
296             "sqlite",
297             output_dir=output_dir,
298             target_lang=language)
299
300     def __setattr__(self, k, v):
301         # Make sure we don't link against the SQLite
302         # library, no matter what setup.cfg says
303         if self.amalgamation and k == "libraries":
304             v = None
305         self.__dict__[k] = v
306
307
308 def get_setup_args():
309
310     PYSQLITE_VERSION = None
311
312     version_re = re.compile('#define PYSQLITE_VERSION "(.*)"')
313     f = open(os.path.join("src", "module.h"))
314     for line in f:
315         match = version_re.match(line)
316         if match:
317             PYSQLITE_VERSION = match.groups()[0]
318             PYSQLITE_MINOR_VERSION = ".".join(PYSQLITE_VERSION.split('.')[:2])
319             break
320     f.close()
321
322     if not PYSQLITE_VERSION:
323         print "Fatal error: PYSQLITE_VERSION could not be detected!"
324         sys.exit(1)
325
326     if DEV_VERSION:
327         PYSQLITE_VERSION += ".dev%s" % DEV_VERSION
328
329     if PATCH_VERSION:
330         PYSQLITE_VERSION += "-%s" % PATCH_VERSION
331
332     # Need to bump minor version, patch handled badly.
333     PYSQLCIPHER_VERSION = "2.6.4"
334
335     setup_args = dict(
336         name="pysqlcipher",
337         #version=PYSQLITE_VERSION,
338         version=PYSQLCIPHER_VERSION,
339         description="DB-API 2.0 interface for SQLCIPHER 3.x",
340         long_description=long_description,
341         author="Kali Kaneko",
342         author_email="kali@leap.se",
343         license="zlib/libpng",
344         # XXX check
345         # It says MIT in the google project
346         platforms="ALL",
347         url="http://github.com/leapcode/pysqlcipher/",
348         # Description of the modules and packages in the distribution
349         package_dir={"pysqlcipher": "lib"},
350         packages=["pysqlcipher", "pysqlcipher.test"] +
351             (["pysqlcipher.test.py25"], [])[sys.version_info < (2, 5)],
352         scripts=[],
353         ext_modules=[
354             Extension(
355                 name="pysqlcipher._sqlite",
356                 sources=sources,
357                 include_dirs=include_dirs,
358                 library_dirs=library_dirs,
359                 runtime_library_dirs=runtime_library_dirs,
360                 libraries=libraries,
361                 extra_objects=extra_objects,
362                 define_macros=define_macros)
363         ],
364         classifiers=[
365             "Development Status :: 4 - Beta",
366             "Intended Audience :: Developers",
367             "License :: OSI Approved :: zlib/libpng License",
368             "Operating System :: MacOS :: MacOS X",
369             "Operating System :: Microsoft :: Windows",
370             "Operating System :: POSIX",
371             "Programming Language :: C",
372             "Programming Language :: Python",
373             "Topic :: Database :: Database Engines/Servers",
374             "Topic :: Software Development :: Libraries :: Python Modules"],
375         cmdclass={"build_docs": DocBuilder}
376     )
377
378     setup_args["cmdclass"].update(
379         {"build_docs": DocBuilder,
380          "build_ext": MyBuildExt,
381          "build_static": AmalgamationBuilder,
382          "build_sqlcipher": LibSQLCipherBuilder,
383          "cross_bdist_wininst": cross_bdist_wininst.bdist_wininst})
384     return setup_args
385
386
387 def main():
388     setuptools.setup(**get_setup_args())
389
390 if __name__ == "__main__":
391     main()