1b3993ea371453bcbbfd7225590f4051d86f5e72
[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", "src/blob.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 BUNDLED = False
87 AMALGAMATION_ROOT = "amalgamation"
88
89 for idx, arg in enumerate(list(sys.argv)):
90     if arg.startswith('--bundled'):
91         sys.argv.pop(idx)
92         BUNDLED = True
93         break
94     if arg.startswith('--amalgamation='):
95         AMALGAMATION_ROOT = arg.split("=",1)[1]
96         break
97
98
99 class DocBuilder(Command):
100     description = "Builds the documentation"
101     user_options = []
102
103     def initialize_options(self):
104         pass
105
106     def finalize_options(self):
107         pass
108
109     def run(self):
110         import shutil
111         try:
112             shutil.rmtree("build/doc")
113         except OSError:
114             pass
115         os.makedirs("build/doc")
116         rc = os.system("sphinx-build doc/sphinx build/doc")
117         if rc != 0:
118             print ("Is sphinx installed? If not, "
119                    "try 'sudo easy_install sphinx'.")
120
121
122 class LibSQLCipherBuilder(build_ext):
123
124     description = ("Build C extension linking against libsqlcipher library.")
125
126     def build_extension(self, ext):
127         ext.extra_compile_args.append("-I/usr/include/sqlcipher/")
128         ext.extra_link_args.append("-lsqlcipher")
129         build_ext.build_extension(self, ext)
130
131
132 class AmalgamationBuildExt(build_ext):
133     amalgamation = True  # We want amalgamation on the default build for now
134     static = False
135
136     def build_extension(self, ext):
137         if self.amalgamation:
138             # build with fulltext search enabled
139             ext.define_macros.append(
140                 ("SQLITE_ENABLE_FTS3", "1"))
141             ext.define_macros.append(
142                 ("SQLITE_ENABLE_FTS5", "1"))
143             ext.define_macros.append(
144                 ("SQLITE_ENABLE_RTREE", "1"))
145
146             # SQLCipher options
147             ext.define_macros.append(
148                 ("SQLITE_ENABLE_LOAD_EXTENSION", "1"))
149             ext.define_macros.append(
150                 ("SQLITE_HAS_CODEC", "1"))
151             ext.define_macros.append(
152                 ("SQLITE_TEMP_STORE", "2"))
153             ext.define_macros.append(
154                 ("HAVE_USLEEP", "1"))
155
156             ext.sources.append(os.path.join(AMALGAMATION_ROOT, "sqlite3.c"))
157             ext.include_dirs.append(AMALGAMATION_ROOT)
158
159             if sys.platform == "win32":
160                 # Try to locate openssl
161                 openssl_conf = os.environ.get('OPENSSL_CONF')
162                 if not openssl_conf:
163                     sys.exit('Fatal error: OpenSSL could not be detected!')
164                 openssl = os.path.dirname(os.path.dirname(openssl_conf))
165
166                 # Configure the compiler
167                 ext.include_dirs.append(os.path.join(openssl, "include"))
168                 ext.define_macros.append(("inline", "__inline"))
169
170                 # Configure the linker
171                 if self.compiler.compiler_type == "msvc":
172                     ext.extra_link_args.append("libeay32.lib")
173                     ext.extra_link_args.append(
174                         "/LIBPATH:" + os.path.join(openssl, "lib")
175                     )
176                 if self.compiler.compiler_type == "mingw32":
177                     ext.extra_link_args.append("-lcrypto")     
178             else:
179                 ext.extra_link_args.append("-lcrypto")
180
181         if self.static:
182             self._build_extension(ext)
183         else:
184             build_ext.build_extension(self, ext)
185
186     def _build_extension(self, ext):
187         sources = ext.sources
188         if sources is None or type(sources) not in (ListType, TupleType):
189             raise DistutilsSetupError, \
190                 ("in 'ext_modules' option (extension '%s'), " +
191                  "'sources' must be present and must be " +
192                  "a list of source filenames") % ext.name
193         sources = list(sources)
194
195         ext_path = self.get_ext_fullpath(ext.name)
196         depends = sources + ext.depends
197         if not (self.force or newer_group(depends, ext_path, 'newer')):
198             log.debug("skipping '%s' extension (up-to-date)", ext.name)
199             return
200         else:
201             log.info("building '%s' extension", ext.name)
202
203         # First, scan the sources for SWIG definition files (.i), run
204         # SWIG on 'em to create .c files, and modify the sources list
205         # accordingly.
206         sources = self.swig_sources(sources, ext)
207
208         # Next, compile the source code to object files.
209
210         # XXX not honouring 'define_macros' or 'undef_macros' -- the
211         # CCompiler API needs to change to accommodate this, and I
212         # want to do one thing at a time!
213
214         # Two possible sources for extra compiler arguments:
215         #   - 'extra_compile_args' in Extension object
216         #   - CFLAGS environment variable (not particularly
217         #     elegant, but people seem to expect it and I
218         #     guess it's useful)
219         # The environment variable should take precedence, and
220         # any sensible compiler will give precedence to later
221         # command line args.  Hence we combine them in order:
222         extra_args = ext.extra_compile_args or []
223
224         macros = ext.define_macros[:]
225         for undef in ext.undef_macros:
226             macros.append((undef,))
227
228         objects = self.compiler.compile(sources,
229                                         output_dir=self.build_temp,
230                                         macros=macros,
231                                         include_dirs=ext.include_dirs,
232                                         debug=self.debug,
233                                         extra_postargs=extra_args,
234                                         depends=ext.depends)
235
236         # XXX -- this is a Vile HACK!
237         #
238         # The setup.py script for Python on Unix needs to be able to
239         # get this list so it can perform all the clean up needed to
240         # avoid keeping object files around when cleaning out a failed
241         # build of an extension module.  Since Distutils does not
242         # track dependencies, we have to get rid of intermediates to
243         # ensure all the intermediates will be properly re-built.
244         #
245         self._built_objects = objects[:]
246
247         # Now link the object files together into a "shared object" --
248         # of course, first we have to figure out all the other things
249         # that go into the mix.
250         if ext.extra_objects:
251             objects.extend(ext.extra_objects)
252         extra_args = ext.extra_link_args or []
253
254         # Detect target language, if not provided
255         language = ext.language or self.compiler.detect_language(sources)
256
257         #self.compiler.link_shared_object(
258             #objects, ext_path,
259             #libraries=self.get_libraries(ext),
260             #library_dirs=ext.library_dirs,
261             #runtime_library_dirs=ext.runtime_library_dirs,
262             #extra_postargs=extra_args,
263             #export_symbols=self.get_export_symbols(ext),
264             #debug=self.debug,
265             #build_temp=self.build_temp,
266             #target_lang=language)
267
268         # XXX may I have a static lib please?
269         # hmm but then I cannot load that extension, or can I?
270         output_dir = os.path.sep.join(ext_path.split(os.path.sep)[:-1])
271
272         self.compiler.create_static_lib(
273             objects,
274             #XXX get library name ... splitting ext_path?
275             "sqlite",
276             output_dir=output_dir,
277             target_lang=language)
278
279     def __setattr__(self, k, v):
280         # Make sure we don't link against the SQLite
281         # library, no matter what setup.cfg says
282         if self.amalgamation and k == "libraries":
283             v = None
284         self.__dict__[k] = v
285
286
287 def get_setup_args():
288
289     PYSQLITE_VERSION = None
290
291     version_re = re.compile('#define PYSQLITE_VERSION "(.*)"')
292     f = open(os.path.join("src", "module.h"))
293     for line in f:
294         match = version_re.match(line)
295         if match:
296             PYSQLITE_VERSION = match.groups()[0]
297             PYSQLITE_MINOR_VERSION = ".".join(PYSQLITE_VERSION.split('.')[:2])
298             break
299     f.close()
300
301     if not PYSQLITE_VERSION:
302         print "Fatal error: PYSQLITE_VERSION could not be detected!"
303         sys.exit(1)
304
305     if DEV_VERSION:
306         PYSQLITE_VERSION += ".dev%s" % DEV_VERSION
307
308     if PATCH_VERSION:
309         PYSQLITE_VERSION += "-%s" % PATCH_VERSION
310
311     # Need to bump minor version, patch handled badly.
312     PYSQLCIPHER_VERSION = "2.6.8"
313
314     setup_args = dict(
315         name="pysqlcipher",
316         #version=PYSQLITE_VERSION,
317         version=PYSQLCIPHER_VERSION,
318         description="DB-API 2.0 interface for SQLCIPHER 3.x",
319         long_description=long_description,
320         author="Kali Kaneko",
321         author_email="kali@leap.se",
322         license="zlib/libpng",
323         # XXX check
324         # It says MIT in the google project
325         platforms="ALL",
326         url="http://github.com/leapcode/pysqlcipher/",
327         # Description of the modules and packages in the distribution
328         package_dir={"pysqlcipher": "lib"},
329         packages=["pysqlcipher", "pysqlcipher.test"] +
330             (["pysqlcipher.test.py25"], [])[sys.version_info < (2, 5)],
331         scripts=[],
332         ext_modules=[
333             Extension(
334                 name="pysqlcipher._sqlite",
335                 sources=sources,
336                 include_dirs=include_dirs,
337                 library_dirs=library_dirs,
338                 runtime_library_dirs=runtime_library_dirs,
339                 libraries=libraries,
340                 extra_objects=extra_objects,
341                 define_macros=define_macros)
342         ],
343         classifiers=[
344             "Development Status :: 4 - Beta",
345             "Intended Audience :: Developers",
346             "License :: OSI Approved :: zlib/libpng License",
347             "Operating System :: MacOS :: MacOS X",
348             "Operating System :: Microsoft :: Windows",
349             "Operating System :: POSIX",
350             "Programming Language :: C",
351             "Programming Language :: Python",
352             "Topic :: Database :: Database Engines/Servers",
353             "Topic :: Software Development :: Libraries :: Python Modules"],
354         cmdclass={"build_docs": DocBuilder}
355     )
356
357
358     if BUNDLED:
359        build_ext = AmalgamationBuildExt 
360     else:
361        build_ext = LibSQLCipherBuilder
362
363     setup_args['cmdclass'].update({'build_ext': build_ext})
364
365     setup_args['cmdclass'].update(
366         {"build_docs": DocBuilder,
367          "cross_bdist_wininst": cross_bdist_wininst.bdist_wininst})
368
369     return setup_args
370
371
372 def main():
373     setuptools.setup(**get_setup_args())
374
375 if __name__ == "__main__":
376     main()