| #!/usr/bin/env python |
| # Licensed to the Apache Software Foundation (ASF) under one |
| # or more contributor license agreements. See the NOTICE file |
| # distributed with this work for additional information |
| # regarding copyright ownership. The ASF licenses this file |
| # to you under the Apache License, Version 2.0 (the |
| # "License"); you may not use this file except in compliance |
| # with the License. You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, |
| # software distributed under the License is distributed on an |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| # KIND, either express or implied. See the License for the |
| # specific language governing permissions and limitations |
| # under the License. |
| |
| import errno, os, re, sys, tempfile |
| |
| from distutils import log |
| from distutils.cmd import Command |
| from distutils.command.build import build as _build |
| from distutils.command.clean import clean as _clean |
| from distutils.core import setup |
| from distutils.dir_util import remove_tree |
| from distutils.errors import DistutilsExecError |
| from distutils.errors import DistutilsOptionError |
| |
| from glob import glob |
| |
| class clean(_clean): |
| """Special distutils command for cleaning the Subversion ctypes bindings.""" |
| description = "clean the Subversion ctypes Python bindings" |
| |
| def initialize_options (self): |
| _clean.initialize_options(self) |
| |
| # initialize_options() |
| |
| def finalize_options (self): |
| _clean.finalize_options(self) |
| |
| # finalize_options() |
| |
| def run(self): |
| functions_py = os.path.join(os.path.dirname(__file__), "csvn", "core", |
| "functions.py") |
| functions_pyc = os.path.join(os.path.dirname(__file__), "csvn", "core", |
| "functions.pyc") |
| svn_all_py = os.path.join(os.path.dirname(__file__), "svn_all.py") |
| svn_all2_py = os.path.join(os.path.dirname(__file__), "svn_all2.py") |
| |
| for f in (functions_py, functions_pyc, svn_all_py, svn_all2_py): |
| if os.path.exists(f): |
| log.info("removing '%s'", os.path.normpath(f)) |
| |
| if not self.dry_run: |
| os.remove(f) |
| else: |
| log.debug("'%s' does not exist -- can't clean it", os.path.normpath(f)) |
| |
| # Run standard clean command |
| _clean.run(self) |
| |
| # run() |
| |
| # class clean |
| |
| class build(_build): |
| """Special distutils command for building the Subversion ctypes bindings.""" |
| |
| description = "build the Subversion ctypes Python bindings" |
| |
| _build.user_options.append(("apr=", None, "full path to where apr is " |
| "installed or the full path to the apr-1-config or " |
| "apr-config file")) |
| _build.user_options.append(("apr-util=", None, "full path to where apr-util " |
| "is installed or the full path to the apu-1-config or " |
| "apu-config file")) |
| _build.user_options.append(("subversion=", None, "full path to where " |
| "Subversion is installed")) |
| _build.user_options.append(("svn-headers=", None, "Full path to the " |
| "Subversion header files, if they are not in a " |
| "standard location")) |
| _build.user_options.append(("ctypesgen=", None, "full path to where ctypesgen " |
| "is installed, to the ctypesgen source tree or " |
| "the full path to ctypesgen.py")) |
| _build.user_options.append(("cppflags=", None, "extra flags to pass to the c " |
| "preprocessor")) |
| _build.user_options.append(("ldflags=", None, "extra flags to pass to the " |
| "ctypesgen linker")) |
| _build.user_options.append(("lib-dirs=", None, "colon-delimited list of paths " |
| "to append to the search path")) |
| _build.user_options.append(("save-preprocessed-headers=", None, "full path to " |
| "where to save the preprocessed headers")) |
| |
| def initialize_options (self): |
| _build.initialize_options(self) |
| self.apr = None |
| self.apr_util = None |
| self.ctypesgen = None |
| self.subversion = None |
| self.svn_headers = None |
| self.cppflags = "" |
| self.ldflags = "" |
| self.lib_dirs = None |
| self.save_preprocessed_headers = None |
| |
| # initialize_options() |
| |
| def finalize_options (self): |
| _build.finalize_options(self) |
| |
| # Distutils doesn't appear to like when you have --dry-run after the build |
| # build command so fail out if this is the case. |
| if self.dry_run != self.distribution.dry_run: |
| raise DistutilsOptionError("The --dry-run flag must be specified " \ |
| "before the 'build' command") |
| |
| # finalize_options() |
| |
| ############################################################################## |
| # Get build configuration |
| ############################################################################## |
| def get_build_config (self): |
| flags = [] |
| ldflags = [] |
| library_path = [] |
| ferr = None |
| apr_include_dir = None |
| |
| fout = self.run_cmd("%s --includes --cppflags" % self.apr_config) |
| if fout: |
| flags = fout.split() |
| apr_prefix = self.run_cmd("%s --prefix" % self.apr_config) |
| apr_prefix = apr_prefix.strip() |
| apr_include_dir = self.run_cmd("%s --includedir" % self.apr_config).strip() |
| apr_version = self.run_cmd("%s --version" % self.apr_config).strip() |
| cpp = self.run_cmd("%s --cpp" % self.apr_config).strip() |
| fout = self.run_cmd("%s --ldflags --link-ld" % self.apr_config) |
| if fout: |
| ldflags = fout.split() |
| else: |
| log.error(ferr) |
| raise DistutilsExecError("Problem running '%s'. Check the output " \ |
| "for details" % self.apr_config) |
| |
| fout = self.run_cmd("%s --includes" % self.apu_config) |
| if fout: |
| flags += fout.split() |
| fout = self.run_cmd("%s --ldflags --link-ld" % self.apu_config) |
| if fout: |
| ldflags += fout.split() |
| else: |
| log.error(ferr) |
| raise DistutilsExecError("Problem running '%s'. Check the output " \ |
| "for details" % self.apr_config) |
| |
| subversion_prefixes = [ |
| self.subversion, |
| "/usr/local", |
| "/usr" |
| ] |
| |
| if self.subversion != "/usr": |
| ldflags.append("-L%s/lib" % self.subversion) |
| if self.svn_include_dir[-18:] == "subversion/include": |
| flags.append("-Isubversion/include") |
| else: |
| flags.append("-I%s" % self.svn_include_dir) |
| |
| # List the libraries in the order they should be loaded |
| libraries = [ |
| "svn_subr-1", |
| "svn_diff-1", |
| "svn_delta-1", |
| "svn_fs-1", |
| "svn_repos-1", |
| "svn_wc-1", |
| "svn_ra-1", |
| "svn_client-1", |
| ] |
| |
| for lib in libraries: |
| ldflags.append("-l%s" % lib) |
| |
| if apr_prefix != '/usr': |
| library_path.append("%s/lib" % apr_prefix) |
| if self.subversion != '/usr' and self.subversion != apr_prefix: |
| library_path.append("%s/lib" % self.subversion) |
| |
| return (apr_prefix, apr_include_dir, cpp + " " + self.cppflags, |
| " ".join(ldflags) + " " + self.ldflags, " ".join(flags), |
| ":".join(library_path)) |
| |
| # get_build_config() |
| |
| ############################################################################## |
| # Build csvn/core/functions.py |
| ############################################################################## |
| def build_functions_py(self): |
| (apr_prefix, apr_include_dir, cpp, ldflags, flags, |
| library_path) = self.get_build_config() |
| cwd = os.getcwd() |
| if self.svn_include_dir[-18:] == "subversion/include": |
| includes = ('subversion/include/svn_*.h ' |
| '%s/ap[ru]_*.h' % apr_include_dir) |
| cmd = ["%s %s --cpp '%s %s' %s " |
| "%s -o subversion/bindings/ctypes-python/svn_all.py " |
| "--no-macro-warnings --strip-build-path=%s" % (sys.executable, |
| self.ctypesgen_py, cpp, |
| flags, ldflags, |
| includes, self.svn_include_dir[:-19])] |
| os.chdir(self.svn_include_dir[:-19]) |
| else: |
| includes = ('%s/svn_*.h ' |
| '%s/ap[ru]_*.h' % (self.svn_include_dir, apr_include_dir)) |
| cmd = ["%s %s --cpp '%s %s' %s " |
| "%s -o svn_all.py --no-macro-warnings" % (sys.executable, |
| self.ctypesgen_py, cpp, |
| flags, ldflags, |
| includes)] |
| if self.lib_dirs: |
| cmd.extend('-R ' + x for x in self.lib_dirs.split(":")) |
| cmd = ' '.join(cmd) |
| |
| if self.save_preprocessed_headers: |
| cmd += " --save-preprocessed-headers=%s" % \ |
| os.path.abspath(self.save_preprocessed_headers) |
| |
| if self.verbose or self.dry_run: |
| status = self.execute(os.system, (cmd,), cmd) |
| else: |
| f = os.popen(cmd, 'r') |
| f.read() # Required to avoid the 'Broken pipe' error. |
| status = f.close() # None is returned for the usual 0 return code |
| |
| os.chdir(cwd) |
| |
| if os.name == "posix" and status and status != 0: |
| if os.WIFEXITED(status): |
| status = os.WEXITSTATUS(status) |
| if status != 0: |
| sys.exit(status) |
| elif os.WIFSIGNALED(status): |
| log.error("ctypesgen.py killed with signal %d" % os.WTERMSIG(status)) |
| sys.exit(2) |
| elif os.WIFSTOPPED(status): |
| log.error("ctypesgen.py stopped with signal %d" % os.WSTOPSIG(status)) |
| sys.exit(2) |
| else: |
| log.error("ctypesgen.py exited with invalid status %d", status) |
| sys.exit(2) |
| |
| if not self.dry_run: |
| r = re.compile(r"(\s+\w+)\.restype = POINTER\(svn_error_t\)") |
| out = open("svn_all2.py", "w") |
| for line in open("svn_all.py"): |
| line = r.sub("\\1.restype = POINTER(svn_error_t)\n\\1.errcheck = _svn_errcheck", line) |
| |
| if not line.startswith("FILE ="): |
| out.write(line) |
| out.close() |
| |
| cmd = "cat csvn/core/functions.py.in svn_all2.py > csvn/core/functions.py" |
| self.execute(os.system, (cmd,), cmd) |
| |
| log.info("Generated csvn/core/functions.py successfully") |
| |
| # build_functions_py() |
| |
| def run_cmd(self, cmd): |
| return os.popen(cmd).read() |
| |
| # run_cmd() |
| |
| def validate_options(self): |
| # Validate apr |
| if not self.apr: |
| self.apr = find_in_path('apr-1-config') |
| |
| if not self.apr: |
| self.apr = find_in_path('apr-config') |
| |
| if self.apr: |
| log.info("Found %s" % self.apr) |
| else: |
| raise DistutilsOptionError("Could not find apr-1-config or " \ |
| "apr-config. Please rerun with the " \ |
| "--apr option.") |
| |
| if os.path.exists(self.apr): |
| if os.path.isdir(self.apr): |
| if os.path.exists(os.path.join(self.apr, "bin", "apr-1-config")): |
| self.apr_config = os.path.join(self.apr, "bin", "apr-1-config") |
| elif os.path.exists(os.path.join(self.apr, "bin", "apr-config")): |
| self.apr_config = os.path.join(self.apr, "bin", "apr-config") |
| else: |
| self.apr_config = None |
| elif os.path.basename(self.apr) in ("apr-1-config", "apr-config"): |
| self.apr_config = self.apr |
| else: |
| self.apr_config = None |
| else: |
| self.apr_config = None |
| |
| if not self.apr_config: |
| raise DistutilsOptionError("The --apr option is not valid. It must " \ |
| "point to a valid apr installation or " \ |
| "to either the apr-1-config file or the " \ |
| "apr-config file") |
| |
| # Validate apr-util |
| if not self.apr_util: |
| self.apr_util = find_in_path('apu-1-config') |
| |
| if not self.apr_util: |
| self.apr_util = find_in_path('apu-config') |
| |
| if self.apr_util: |
| log.info("Found %s" % self.apr_util) |
| else: |
| raise DistutilsOptionError("Could not find apu-1-config or " \ |
| "apu-config. Please rerun with the " \ |
| "--apr-util option.") |
| |
| if os.path.exists(self.apr_util): |
| if os.path.isdir(self.apr_util): |
| if os.path.exists(os.path.join(self.apr_util, "bin", "apu-1-config")): |
| self.apu_config = os.path.join(self.apr_util, "bin", "apu-1-config") |
| elif os.path.exists(os.path.join(self.apr_util, "bin", "apu-config")): |
| self.apu_config = os.path.join(self.apr_util, "bin", "apu-config") |
| else: |
| self.apu_config = None |
| elif os.path.basename(self.apr_util) in ("apu-1-config", "apu-config"): |
| self.apu_config = self.apr_util |
| else: |
| self.apu_config = None |
| else: |
| self.apu_config = None |
| |
| if not self.apu_config: |
| raise DistutilsOptionError("The --apr-util option is not valid. It " \ |
| "must point to a valid apr-util " \ |
| "installation or to either the apu-1-config " \ |
| "file or the apu-config file") |
| |
| # Validate subversion |
| if not self.subversion: |
| self.subversion = find_in_path('svn') |
| |
| if self.subversion: |
| log.info("Found %s" % self.subversion) |
| # Get the installation root instead of path to 'svn' |
| self.subversion = os.path.normpath(os.path.join(self.subversion, "..", |
| "..")) |
| else: |
| raise DistutilsOptionError("Could not find Subversion. Please rerun " \ |
| "with the --subversion option.") |
| |
| # Validate svn-headers, if present |
| if self.svn_headers: |
| if os.path.isdir(self.svn_headers): |
| if os.path.exists(os.path.join(self.svn_headers, "svn_client.h")): |
| self.svn_include_dir = self.svn_headers |
| elif os.path.exists(os.path.join(self.svn_headers, "subversion-1", |
| "svn_client.h")): |
| self.svn_include_dir = os.path.join(self.svn_headers, "subversion-1") |
| else: |
| self.svn_include_dir = None |
| else: |
| self.svn_include_dir = None |
| elif os.path.exists(os.path.join(self.subversion, "include", |
| "subversion-1")): |
| self.svn_include_dir = "%s/include/subversion-1" % self.subversion |
| else: |
| self.svn_include_dir = None |
| |
| if not self.svn_include_dir: |
| msg = "" |
| |
| if self.svn_headers: |
| msg = "The --svn-headers options is not valid. It must point to " \ |
| "either a Subversion include directory or the Subversion " \ |
| "include/subversion-1 directory." |
| else: |
| msg = "The --subversion option is not valid. " \ |
| "Could not locate %s/include/" \ |
| "subversion-1/svn_client.h" % self.subversion |
| |
| raise DistutilsOptionError(msg) |
| |
| # Validate ctypesgen |
| if not self.ctypesgen: |
| self.ctypesgen = find_in_path('ctypesgen.py') |
| |
| if self.ctypesgen: |
| log.info("Found %s" % self.ctypesgen) |
| else: |
| raise DistutilsOptionError("Could not find ctypesgen. Please rerun " \ |
| "with the --ctypesgen option.") |
| |
| if os.path.exists(self.ctypesgen): |
| if os.path.isdir(self.ctypesgen): |
| if os.path.exists(os.path.join(self.ctypesgen, "ctypesgen.py")): |
| self.ctypesgen_py = os.path.join(self.ctypesgen, "ctypesgen.py") |
| elif os.path.exists(os.path.join(self.ctypesgen, "bin", |
| "ctypesgen.py")): |
| self.ctypesgen_py = os.path.join(self.ctypesgen, "bin", |
| "ctypesgen.py") |
| else: |
| self.ctypesgen_py = None |
| elif os.path.basename(self.ctypesgen) == "ctypesgen.py": |
| self.ctypesgen_py = self.ctypesgen |
| else: |
| self.ctypesgen_py = None |
| else: |
| self.ctypesgen_py = None |
| |
| if not self.ctypesgen_py: |
| raise DistutilsOptionError("The --ctypesgen option is not valid. It " \ |
| "must point to a valid ctypesgen " \ |
| "installation, a ctypesgen source tree or " \ |
| "to the ctypesgen.py script") |
| |
| # validate_functions() |
| |
| def run (self): |
| # We only want to build if functions.py is not present. |
| if not os.path.exists(os.path.join(os.path.dirname(__file__), |
| "csvn", "core", |
| "functions.py")): |
| if 'build' not in self.distribution.commands: |
| raise DistutilsOptionError("You must run 'build' explicitly before " \ |
| "you can proceed") |
| |
| # Validate the command line options |
| self.validate_options() |
| |
| # Generate functions.py |
| self.build_functions_py() |
| else: |
| log.info("csvn/core/functions.py was not regenerated (output up-to-date)") |
| |
| # Run the standard build command. |
| _build.run(self) |
| |
| # run() |
| |
| # class build |
| |
| def find_in_path(file): |
| paths = [] |
| if os.environ.has_key('PATH'): |
| paths = os.environ['PATH'].split(os.pathsep) |
| |
| for path in paths: |
| file_path = os.path.join(path, file) |
| |
| if os.path.exists(file_path): |
| # Return the path to the first existing file found in PATH |
| return os.path.abspath(file_path) |
| |
| return None |
| # find_in_path() |
| |
| setup(cmdclass={'build': build, 'clean': clean}, |
| name='svn-ctypes-python-bindings', |
| version='0.1', |
| description='Python bindings for the Subversion version control system.', |
| author='The Subversion Team', |
| author_email='dev@subversion.apache.org', |
| url='http://subversion.apache.org', |
| packages=['csvn', 'csvn.core', 'csvn.ext'], |
| license='Apache License, Version 2.0', |
| ) |
| |
| # TODO: We need to create our own bdist_rpm implementation so that we can pass |
| # our required arguments to the build command being called by bdist_rpm. |