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