2 # Setup file for subvertpy
3 # Copyright (C) 2005-2010 Jelmer Vernooij <jelmer@samba.org>
5 from distutils.core import setup
6 from distutils.extension import Extension
7 from distutils.command.install_lib import install_lib
8 from distutils import log
13 class CommandException(Exception):
14 """Encapsulate exit status of command execution"""
15 def __init__(self, msg, cmd, arg, status, val):
16 self.message = msg % (cmd, val)
17 Exception.__init__(self, self.message)
22 return os.WIFEXITED(self.status) and os.WEXITSTATUS(self.status) == 127
25 def split_shell_results(line):
26 return [s for s in line.split(" ") if s != ""]
29 def run_cmd(cmd, arg):
30 """Run specified command with given arguments, handling status"""
31 f = os.popen("'%s' %s" % (cmd, arg))
32 dir = f.read().rstrip("\n")
36 if os.WIFEXITED(status):
37 code = os.WEXITSTATUS(status)
40 raise CommandException("%s exited with status %d",
41 cmd, arg, status, code)
42 if os.WIFSIGNALED(status):
43 signal = os.WTERMSIG(status)
44 raise CommandException("%s killed by signal %d",
45 cmd, arg, status, signal)
46 raise CommandException("%s terminated abnormally (%d)",
47 cmd, arg, status, status)
50 def config_value(command, arg):
51 cmds = [command] + [os.path.join(p, command) for p in ["/usr/local/apr/bin/", "/opt/local/bin/"]]
54 return run_cmd(cmd, arg)
55 except CommandException:
56 _, e, _ = sys.exc_info()
60 raise Exception("apr-config not found."
61 " Please set APR_CONFIG environment variable")
65 config_cmd = os.getenv("APR_CONFIG")
66 if config_cmd is None:
67 return config_value("apr-1-config", arg)
69 return run_cmd(config_cmd, arg)
73 config_cmd = os.getenv("APU_CONFIG")
74 if config_cmd is None:
75 return config_value("apu-1-config", arg)
77 return run_cmd(config_cmd, arg)
81 """Determine the APR header file location."""
82 includedir = apr_config("--includedir")
83 if not os.path.isdir(includedir):
84 raise Exception("APR development headers not found")
85 extra_link_flags = apr_config("--link-ld --libs")
86 return (includedir, split_shell_results(extra_link_flags))
90 """Determine the APR util header file location."""
91 includedir = apu_config("--includedir")
92 if not os.path.isdir(includedir):
93 raise Exception("APR util development headers not found")
94 extra_link_flags = apu_config("--link-ld --libs")
95 return (includedir, split_shell_results(extra_link_flags))
99 """Determine the Subversion header file location."""
100 if "SVN_HEADER_PATH" in os.environ and "SVN_LIBRARY_PATH" in os.environ:
101 return ([os.getenv("SVN_HEADER_PATH")], [os.getenv("SVN_LIBRARY_PATH")], [], [])
102 svn_prefix = os.getenv("SVN_PREFIX")
103 if svn_prefix is None:
104 basedirs = ["/usr/local", "/usr"]
105 for basedir in basedirs:
106 includedir = os.path.join(basedir, "include/subversion-1")
107 if os.path.isdir(includedir):
110 if svn_prefix is not None:
111 return ([os.path.join(svn_prefix, "include/subversion-1")],
112 [os.path.join(svn_prefix, "lib")], [], [])
113 raise Exception("Subversion development files not found. "
114 "Please set SVN_PREFIX or (SVN_LIBRARY_PATH and SVN_HEADER_PATH) environment variable. ")
116 def is_keychain_provider_available():
118 Checks for the availability of the Keychain simple authentication provider in Subversion by compiling a simple test program.
120 abd = apr_build_data()
121 sbd = svn_build_data()
122 gcc_command_args = ['gcc'] + ['-I' + inc for inc in sbd[0]] + ['-L' + lib for lib in sbd[1]] + ['-I' + abd[0], '-lsvn_subr-1', '-x', 'c', '-']
123 (gcc_in, gcc_out, gcc_err) = os.popen3(gcc_command_args)
125 #include <svn_auth.h>
126 int main(int argc, const char* arv[]) {
127 svn_auth_get_keychain_simple_provider(NULL, NULL);
132 return (gcc_out.close() is None)
135 class VersionQuery(object):
137 def __init__(self, filename):
138 self.filename = filename
139 f = open(filename, "rU")
145 def grep(self, what):
146 m = re.search(r"^#define\s+%s\s+(\d+)\s*$" % (what,), self.text, re.MULTILINE)
148 raise Exception("Definition for %s was not found in file %s." % (what, self.filename))
149 return int(m.group(1))
151 # Windows versions - we use environment variables to locate the directories
152 # and hard-code a list of libraries.
154 def get_apr_version():
155 apr_version_file = os.path.join(os.environ["SVN_DEV"],
156 r"include\apr\apr_version.h")
157 if not os.path.isfile(apr_version_file):
159 "Please check that your SVN_DEV location is correct.\n"
160 "Unable to find required apr\\apr_version.h file.")
161 query = VersionQuery(apr_version_file)
162 return (query.grep("APR_MAJOR_VERSION"),
163 query.grep("APR_MINOR_VERSION"),
164 query.grep("APR_PATCH_VERSION"))
166 def get_svn_version():
167 svn_version_file = os.path.join(os.environ["SVN_DEV"], r"include\svn_version.h")
168 if not os.path.isfile(svn_version_file):
170 "Please check that your SVN_DEV location is correct.\n"
171 "Unable to find required svn_version.h file.")
172 query = VersionQuery(svn_version_file)
173 return (query.grep("SVN_VER_MAJOR"),
174 query.grep("SVN_VER_MINOR"),
175 query.grep("SVN_VER_PATCH"))
177 # just clobber the functions above we can't use
178 # for simplicitly, everything is done in the 'svn' one
179 def apr_build_data():
182 def apu_build_data():
185 def svn_build_data():
186 # environment vars for the directories we need.
187 svn_dev_dir = os.environ.get("SVN_DEV")
188 if not svn_dev_dir or not os.path.isdir(svn_dev_dir):
190 "Please set SVN_DEV to the location of the svn development "
191 "packages.\nThese can be downloaded from:\n"
192 "http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=91")
193 svn_bdb_dir = os.environ.get("SVN_BDB")
194 if not svn_bdb_dir or not os.path.isdir(svn_bdb_dir):
196 "Please set SVN_BDB to the location of the svn BDB packages "
197 "- see README.txt in the SVN_DEV dir")
198 svn_libintl_dir = os.environ.get("SVN_LIBINTL")
199 if not svn_libintl_dir or not os.path.isdir(svn_libintl_dir):
201 "Please set SVN_LIBINTL to the location of the svn libintl "
202 "packages - see README.txt in the SVN_DEV dir")
204 svn_version = get_svn_version()
205 apr_version = get_apr_version()
209 os.path.join(svn_dev_dir, r"include\apr"),
210 os.path.join(svn_dev_dir, r"include\apr-util"),
211 os.path.join(svn_dev_dir, r"include\apr-iconv"),
213 os.path.join(svn_dev_dir, "include"),
216 os.path.join(svn_dev_dir, "lib"),
217 os.path.join(svn_dev_dir, "lib", "apr"),
218 os.path.join(svn_dev_dir, "lib", "apr-iconv"),
219 os.path.join(svn_dev_dir, "lib", "apr-util"),
220 os.path.join(svn_dev_dir, "lib", "neon"),
221 os.path.join(svn_bdb_dir, "lib"),
222 os.path.join(svn_libintl_dir, "lib"),
224 aprlibs = """libapr libapriconv libaprutil""".split()
225 if apr_version[0] == 1:
226 aprlibs = [aprlib + "-1" for aprlib in aprlibs]
227 elif apr_version[0] > 1:
229 "You have apr version %d.%d.%d.\n"
230 "This setup only knows how to build with 0.*.* or 1.*.*." % apr_version)
231 libs = """libneon libsvn_subr-1 libsvn_client-1 libsvn_ra-1
232 libsvn_ra_dav-1 libsvn_ra_local-1 libsvn_ra_svn-1
233 libsvn_repos-1 libsvn_wc-1 libsvn_delta-1 libsvn_diff-1
234 libsvn_fs-1 libsvn_repos-1 libsvn_fs_fs-1 libsvn_fs_base-1
237 advapi32 shell32 ws2_32 zlibstat
239 if svn_version >= (1,5,0):
240 # Since 1.5.0 libsvn_ra_dav-1 was removed
241 libs.remove("libsvn_ra_dav-1")
243 return includes, lib_dirs, [], aprlibs+libs,
245 (apr_includedir, apr_link_flags) = apr_build_data()
246 (apu_includedir, apu_link_flags) = apu_build_data()
247 (svn_includedirs, svn_libdirs, svn_link_flags, extra_libs) = svn_build_data()
249 class SvnExtension(Extension):
251 def __init__(self, name, *args, **kwargs):
252 kwargs["include_dirs"] = ([apr_includedir, apu_includedir] + svn_includedirs +
254 kwargs["library_dirs"] = svn_libdirs
255 # Note that the apr-util link flags are not included here, as
256 # subvertpy only uses some apr util constants but does not use
257 # the library directly.
258 kwargs["extra_link_args"] = apr_link_flags + svn_link_flags
260 # on windows, just ignore and overwrite the libraries!
261 kwargs["libraries"] = extra_libs
262 # APR needs WIN32 defined.
263 kwargs["define_macros"] = [("WIN32", None)]
264 if sys.platform == 'darwin':
265 # on Mac OS X, we need to check for Keychain availability
266 if is_keychain_provider_available():
267 if "define_macros" not in kwargs:
268 kwargs["define_macros"] = []
269 kwargs["define_macros"].extend((
271 ('SVN_KEYCHAIN_PROVIDER_AVAILABLE', '1'))
273 Extension.__init__(self, name, *args, **kwargs)
276 # On Windows, we install the apr binaries too.
277 class install_lib_with_dlls(install_lib):
279 # return a list of of (FQ-in-name, relative-out-name) tuples.
281 apr_bins = [libname + ".dll" for libname in extra_libs
282 if libname.startswith("libapr")]
283 if get_svn_version() >= (1,5,0):
284 # Since 1.5.0 these libraries became shared
285 apr_bins += """libsvn_client-1.dll libsvn_delta-1.dll libsvn_diff-1.dll
286 libsvn_fs-1.dll libsvn_ra-1.dll libsvn_repos-1.dll
287 libsvn_subr-1.dll libsvn_wc-1.dll libsasl.dll""".split()
288 apr_bins += """intl3_svn.dll libdb44.dll libeay32.dll ssleay32.dll""".split()
289 look_dirs = os.environ.get("PATH","").split(os.pathsep)
290 look_dirs.insert(0, os.path.join(os.environ["SVN_DEV"], "bin"))
293 for look in look_dirs:
294 f = os.path.join(look, bin)
295 if os.path.isfile(f):
296 target = os.path.join(self.install_dir, "subvertpy", bin)
297 ret.append((f, target))
300 log.warn("Could not find required DLL %r to include", bin)
301 log.debug("(looked in %s)", look_dirs)
305 install_lib.run(self)
308 # On Windows we package up the apr dlls with the plugin.
309 for s, d in self._get_dlls():
312 def get_outputs(self):
313 ret = install_lib.get_outputs(self)
315 ret.extend([info[1] for info in self._get_dlls()])
319 def source_path(filename):
320 return os.path.join("subvertpy", filename)
323 def subvertpy_modules():
325 SvnExtension("subvertpy.client", [source_path(n) for n in
326 ("client.c", "editor.c", "util.c", "_ra.c", "wc.c")],
327 libraries=["svn_client-1", "svn_subr-1", "svn_ra-1", "svn_wc-1"]),
328 SvnExtension("subvertpy._ra", [source_path(n) for n in
329 ("_ra.c", "util.c", "editor.c")],
330 libraries=["svn_ra-1", "svn_delta-1", "svn_subr-1"]),
331 SvnExtension("subvertpy.repos", [source_path(n) for n in ("repos.c", "util.c")],
332 libraries=["svn_repos-1", "svn_subr-1", "svn_fs-1"]),
333 SvnExtension("subvertpy.wc", [source_path(n) for n in ("wc.c",
334 "util.c", "editor.c")], libraries=["svn_wc-1", "svn_subr-1"])
338 subvertpy_version = (0, 8, 10)
339 subvertpy_version_string = ".".join(map(str, subvertpy_version))
342 if __name__ == "__main__":
343 setup(name='subvertpy',
344 description='Alternative Python bindings for Subversion',
345 keywords='svn subvertpy subversion bindings',
346 version=subvertpy_version_string,
347 url='http://samba.org/~jelmer/subvertpy',
348 download_url="http://samba.org/~jelmer/subvertpy/subvertpy-%s.tar.gz" % (
349 subvertpy_version_string, ),
350 license='LGPLv2.1 or later',
351 author='Jelmer Vernooij',
352 author_email='jelmer@samba.org',
354 Alternative Python bindings for Subversion. The goal is to have complete, portable and "Pythonic" Python bindings.
356 packages=['subvertpy', 'subvertpy.tests'],
357 ext_modules=subvertpy_modules(),
358 scripts=['bin/subvertpy-fast-export'],
359 cmdclass = { 'install_lib': install_lib_with_dlls },