diff --git a/bob/extension/__init__.py b/bob/extension/__init__.py index 3a3fc7dea2f3ba7b2722a3eaf455e3154c7fd622..e20794d04af9a517f20c3075955bc6029eef5f75 100644 --- a/bob/extension/__init__.py +++ b/bob/extension/__init__.py @@ -16,9 +16,17 @@ import distutils.sysconfig from pkg_resources import resource_filename +DEFAULT_PREFIXES = [ + "/opt/local", + "/usr/local", + "/usr", +] +"""The list common places to search for library-related files.""" + + from .pkgconfig import pkgconfig from .boost import boost -from .utils import uniq, uniq_paths, find_executable, find_library +from .utils import uniq, uniq_paths, find_executable, find_library, construct_search_paths from .cmake import CMakeListsGenerator from .rc_config import _loadrc diff --git a/bob/extension/pkgconfig.py b/bob/extension/pkgconfig.py index 4f234859b7f4ac164e473dde903331149db11704..37111c4923deb6e316cddcd1c727a3b0dc9b01a5 100644 --- a/bob/extension/pkgconfig.py +++ b/bob/extension/pkgconfig.py @@ -7,12 +7,35 @@ import os import sys import subprocess import logging -from .utils import uniq, uniq_paths, find_executable +from .utils import uniq, uniq_paths, find_executable, construct_search_paths -def call_pkgconfig(cmd, paths=None): - """Runs a command as a subprocess and raises if that does not work - Returns the exit status, stdout and stderr. +def call_pkgconfig(cmd, paths=None): + """Calls pkg-config with a constructed PKG_CONFIG_PATH environment variable. + + The PKG_CONFIG_PATH variable is constructed using + :any:`construct_search_paths`. + + Parameters + ---------- + cmd : [str] + A list of commands to be given to pkg-config. + paths : [str] + A list of paths to be added to PKG_CONFIG_PATH. + + Returns + ------- + returncode : int + The exit status of pkg-config. + stdout : str + The stdout output of pkg-config. + stderr : str + The stderr output of pkg-config. + + Raises + ------ + OSError + If the `pkg-config' executable is not found. """ # locates the pkg-config program @@ -20,27 +43,8 @@ def call_pkgconfig(cmd, paths=None): if not pkg_config: raise OSError("Cannot find `pkg-config' - did you install it?") - # sets the PKG_CONFIG_PATH taking into consideration: - # 1. BOB_PREFIX_PATH, if set - # 2. the paths set by the user - # 3. the current python executable prefix - - # 1. BOB_PREFIX_PATH - bob_prefix = os.environ.get('BOB_PREFIX_PATH', False) - if bob_prefix: - bob_prefix = bob_prefix.split(os.pathsep) - pkg_path = [os.path.join(k, 'lib', 'pkgconfig') for k in bob_prefix] - else: - pkg_path = [] - - # 2. user path - if paths: pkg_path += paths - - # 3. adds the current python executable prefix - pkg_path.append(os.path.join(os.path.dirname(os.path.dirname(sys.executable)), 'lib', 'pkgconfig')) - - # Make unique to avoid searching twice - pkg_path = uniq_paths(pkg_path) + pkg_path = construct_search_paths( + prefixes=paths, suffix=os.sep + 'lib' + os.sep + 'pkgconfig') env = os.environ.copy() var = os.pathsep.join(pkg_path) @@ -83,6 +87,7 @@ def version(): return stdout.strip() + class pkgconfig: """A class for capturing configuration information from pkg-config diff --git a/bob/extension/utils.py b/bob/extension/utils.py index 86fc3296c0a0b150e1677665b736d9d439e97681..edc583e9cf84bc6989ddc60ce4648c0466ca55d2 100644 --- a/bob/extension/utils.py +++ b/bob/extension/utils.py @@ -11,59 +11,62 @@ import sys import glob import platform import pkg_resources - -DEFAULT_PREFIXES = [ - "/opt/local", - "/usr/local", - "/usr", - ] - -def find_file(name, subpaths=None, prefixes=None): - """Finds a generic file on the file system. Returns all candidates. - - This method will find all occurrences of a given name on the file system and - will return them to the user. - - If the environment variable ``BOB_PREFIX_PATH`` is set, then it is - considered a unix path list that is prepended to the list of prefixes to - search for. The environment variable has the highest priority on the search - order. The order on the variable for each path is respected. - - Parameters: - - name, str - The name of the file to be found, including extension - - subpaths, str list - A list of subpaths to be appended to each prefix for the search. For - example, if you specificy ``['foo', 'bar']`` for this parameter, then - search on ``os.path.join(prefixes[0], 'foo')``, ``os.path.join(prefixes[0], - 'bar')``, and so on. Globs are accepted in this list and resolved using - the function :py:func:`glob.glob`. - - prefixes, str list - A list of prefixes that will be searched prioritarily to the - ``DEFAULT_PREFIXES`` defined in this module. - - Returns a list of filenames that exist on the filesystem, matching your - description. +from . import DEFAULT_PREFIXES + + +def construct_search_paths(prefixes=None, subpaths=None, suffix=None): + """Constructs a list of candidate paths to search for. + + The list of paths is constructed using the following order of priority: + + 1. ``BOB_PREFIX_PATH`` environment variable, if set. ``BOB_PREFIX_PATH`` can + contain several paths divided by :any:`os.pathsep`. + 2. The paths provided with the ``prefixes`` parameter. + 3. The current python executable prefix. + 4. The ``CONDA_PREFIX`` environment variable, if set. + 5. :any:`DEFAULT_PREFIXES`. + + Parameters + ---------- + prefixes : [:obj:`str`], optional + The list of paths to be added to the results. + subpaths : [:obj:`str`], optional + A list of subpaths to be appended to each path at the end. For + example, if you specify ``['foo', 'bar']`` for this parameter, then + ``os.path.join(paths[0], 'foo')``, + ``os.path.join(paths[0], 'bar')``, and so on are added to the returned + paths. Globs are accepted in this list and resolved using the function + :py:func:`glob.glob`. + suffix : :obj:`str`, optional + ``suffix`` will be appended to all paths except ``prefixes``. + + Returns + ------- + paths : [str] + A list of unique and existing paths to be used in your search. """ - search = [] + suffix = suffix or '' # Priority 1: the environment if 'BOB_PREFIX_PATH' in os.environ: - search += os.environ['BOB_PREFIX_PATH'].split(os.pathsep) + paths = os.environ['BOB_PREFIX_PATH'].split(os.pathsep) + search += [p + suffix for p in paths] # Priority 2: user passed paths if prefixes: search += prefixes # Priority 3: the current system executable - search.append(os.path.dirname(os.path.dirname(sys.executable))) + search.append(os.path.dirname(os.path.dirname(sys.executable)) + suffix) + + # Priority 4: the conda prefix + conda_prefix = os.environ.get('CONDA_PREFIX') + if conda_prefix: + search.append(conda_prefix + suffix) - # Priority 4: the default search prefixes - search += DEFAULT_PREFIXES + # Priority 5: the default search prefixes + search += [p + suffix for p in DEFAULT_PREFIXES] # Make unique to avoid searching twice search = uniq_paths(search) @@ -77,48 +80,71 @@ def find_file(name, subpaths=None, prefixes=None): subsearch.append(s) search = subsearch - # Before we do a filesystem check, filter out the unexisting paths + # Before we do a file-system check, filter out the un-existing paths tmp = [] - for k in search: tmp += glob.glob(k) + for k in search: + tmp += glob.glob(k) search = tmp - retval = [] - candidates = [] - for path in search: - candidate = os.path.join(path, name) - candidates.append(candidate) - if os.path.exists(candidate): retval.append(candidate) + return search - return retval -def find_header(name, subpaths=None, prefixes=None): - """Finds a header file on the file system. Returns all candidates. +def find_file(name, subpaths=None, prefixes=None): + """Finds a generic file on the file system. Returns all occurrences. This method will find all occurrences of a given name on the file system and - will return them to the user. + will return them to the user. It uses :any:`construct_search_paths` to + construct the candidate folders that file may exist in. + + Parameters + ---------- + name : str + The name of the file. For example, ``gcc``. + subpaths : [:obj:`str`], optional + See :any:`construct_search_paths` + subpaths : :obj:`str`, optional + See :any:`construct_search_paths` + + Returns + ------- + [str] + A list of filenames that exist on the filesystem, matching your + description. + """ - If the environment variable ``BOB_PREFIX_PATH`` is set, then it is - considered a unix path list that is prepended to the list of prefixes to - search for. The environment variable has the highest priority on the search - order. The order on the variable for each path is respected. + search = construct_search_paths(prefixes=prefixes, subpaths=subpaths) - Parameters: + retval = [] + for path in search: + candidate = os.path.join(path, name) + if os.path.exists(candidate): + retval.append(candidate) - name, str - The name of the file to be found, including extension + return retval - subpaths, str list - A list of subpaths to be appended to each prefix for the search. For - example, if you specificy ``['foo', 'bar']`` for this parameter, then - search on ``os.path.join(prefixes[0], 'foo')``, ``os.path.join(prefixes[0], - 'bar')``, and so on. - prefixes, str list - A list of prefixes that will be searched prioritarily to the - ``DEFAULT_PREFIXES`` defined in this module. +def find_header(name, subpaths=None, prefixes=None): + """Finds a header file on the file system. Returns all candidates. - Returns a list of filenames that exist on the filesystem, matching your - description. + This method will find all occurrences of a given name on the file system and + will return them to the user. It uses :any:`construct_search_paths` to + construct the candidate folders that header may exist in accounting + automatically for typical header folder names. + + Parameters + ---------- + name : str + The name of the header file. + subpaths : [:obj:`str`], optional + See :any:`construct_search_paths` + subpaths : :obj:`str`, optional + See :any:`construct_search_paths` + + Returns + ------- + [str] + A list of filenames that exist on the filesystem, matching your + description. """ headerpaths = [] @@ -146,44 +172,37 @@ def find_header(name, subpaths=None, prefixes=None): return find_file(name, my_subpaths, prefixes) + def find_library(name, version=None, subpaths=None, prefixes=None, only_static=False): """Finds a library file on the file system. Returns all candidates. This method will find all occurrences of a given name on the file system and - will return them to the user. - - If the environment variable ``BOB_PREFIX_PATH`` is set, then it is - considered a unix path list that is prepended to the list of prefixes to - search for. The environment variable has the highest priority on the search - order. The order on the variable for each path is respected. - - Parameters: - - name, str - The name of the module to be found. If you'd like to find libz.so, for - example, specify ``"z"``. For libmath.so, specify ``"math"``. - - version, str - The version of the library we are searching for. If not specified, then - look only for the default names, such as ``libz.so`` and the such. - - subpaths, str list - A list of subpaths to be appended to each prefix for the search. For - example, if you specificy ``['foo', 'bar']`` for this parameter, then - search on ``os.path.join(prefixes[0], 'foo')``, ``os.path.join(prefixes[0], - 'bar')``, and so on. - - prefixes, str list - A list of prefixes that will be searched prioritarily to the - ``DEFAULT_PREFIXES`` defined in this module. - - static (bool) - A boolean, indicating if we should try only to search for static versions - of the libraries. If not set, any would do. - - Returns a list of filenames that exist on the filesystem, matching your - description. + will return them to the user. It uses :any:`construct_search_paths` to + construct the candidate folders that the library may exist in accounting + automatically for typical library folder names. + + Parameters + ---------- + name : str + The name of the module to be found. If you'd like to find libz.so, for + example, specify ``"z"``. For libmath.so, specify ``"math"``. + version : :obj:`str`, optional + The version of the library we are searching for. If not specified, then + look only for the default names, such as ``libz.so`` and the such. + subpaths : [:obj:`str`], optional + See :any:`construct_search_paths` + subpaths : :obj:`str`, optional + See :any:`construct_search_paths` + only_static : :obj:`bool`, optional + A boolean, indicating if we should try only to search for static versions + of the libraries. If not set, any would do. + + Returns + ------- + [str] + A list of filenames that exist on the filesystem, matching your + description. """ libpaths = [] @@ -246,30 +265,24 @@ def find_executable(name, subpaths=None, prefixes=None): """Finds an executable on the file system. Returns all candidates. This method will find all occurrences of a given name on the file system and - will return them to the user. - - If the environment variable ``BOB_PREFIX_PATH`` is set, then it is - considered a unix path list that is prepended to the list of prefixes to - search for. The environment variable has the highest priority on the search - order. The order on the variable for each path is respected. - - Parameters: - - name, str - The name of the executable to be found. - - subpaths, str list - A list of subpaths to be appended to each prefix for the search. For - example, if you specificy ``['foo', 'bar']`` for this parameter, then - search on ``os.path.join(prefixes[0], 'foo')``, ``os.path.join(prefixes[0], - 'bar')``, and so on. - - prefixes, str list - A list of prefixes that will be searched prioritarily to the - ``DEFAULT_PREFIXES`` defined in this module. - - Returns a list of filenames that exist on the filesystem, matching your - description. + will return them to the user. It uses :any:`construct_search_paths` to + construct the candidate folders that the executable may exist in accounting + automatically for typical executable folder names. + + Parameters + ---------- + name : str + The name of the file. For example, ``gcc``. + subpaths : [:obj:`str`], optional + See :any:`construct_search_paths` + subpaths : :obj:`str`, optional + See :any:`construct_search_paths` + + Returns + ------- + [str] + A list of filenames that exist on the filesystem, matching your + description. """ binpaths = [] diff --git a/doc/py_api.rst b/doc/py_api.rst index 11224aa38c726abf2b939d1b4a01a35d92db550a..db164fa7a2f11f501bfb48595b758f015030bcf2 100644 --- a/doc/py_api.rst +++ b/doc/py_api.rst @@ -8,11 +8,74 @@ This section includes information for using the Python API of ``bob.extension``. -Functions ---------- +Summary +------- + +Core Functionality +^^^^^^^^^^^^^^^^^^ + +.. autosummary:: + bob.extension.boost + bob.extension.build_ext + bob.extension.check_packages + bob.extension.CMakeListsGenerator + bob.extension.construct_search_paths + bob.extension.DEFAULT_PREFIXES + bob.extension.Extension + bob.extension.find_executable + bob.extension.find_library + bob.extension.generate_self_macros + bob.extension.get_bob_libraries + bob.extension.get_config + bob.extension.get_full_libname + bob.extension.Library + bob.extension.load_bob_library + bob.extension.normalize_requirements + bob.extension.pkgconfig + bob.extension.rc + bob.extension.reorganize_isystem + bob.extension.uniq + bob.extension.uniq_paths + + +Utilities +^^^^^^^^^ + +.. autosummary:: + bob.extension.utils.egrep + bob.extension.utils.find_file + bob.extension.utils.find_header + bob.extension.utils.find_packages + bob.extension.utils.link_documentation + bob.extension.utils.load_requirements + +Configuration +^^^^^^^^^^^^^ + +.. autosummary:: + bob.extension.rc_config.ENVNAME + bob.extension.rc_config.RCFILENAME + bob.extension.config.load + +Stacked Processors +^^^^^^^^^^^^^^^^^^ + +.. autosummary:: + bob.extension.processors.SequentialProcessor + bob.extension.processors.ParallelProcessor + + +Core Functionality +------------------ .. automodule:: bob.extension +Utilities +--------- + +.. automodule:: bob.extension.utils + :exclude-members: find_executable,find_library,uniq,uniq_paths,construct_search_paths + Configuration -------------