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