Commit ae4cd8a2 authored by André Anjos's avatar André Anjos 💬
Browse files

Implement file, header and library search, egrep functionality

parent 40d7dab1
......@@ -11,27 +11,21 @@ import sys
import glob
from distutils.version import LooseVersion
from .utils import uniq
from .utils import uniq, egrep, find_header, find_library
def boost_version(boost_dir):
def boost_version(version_hpp):
fname = os.path.join(boost_dir, "version.hpp")
if not os.path.exists(fname):
fname = os.path.join(boost_dir, "boost/version.hpp")
if not os.path.exists(fname):
return None
matches = egrep(version_hpp, r"^#\s*define\s+BOOST_VERSION\s+(\d+)\s*$")
if not len(matches): return None
try:
m = re.search("#\s*define\s+BOOST_VERSION\s+(\d+)", open(fname).read())
if m:
version_int = int(m.group(1))
version_tuple = (version_int // 100000, (version_int // 100) % 1000,
version_int % 100)
return '.'.join([str(k) for k in version_tuple])
except:
pass
return None
# we have a match, produce a string version of the version number
version_int = int(matches[0].group(1))
version_tuple = (
version_int // 100000,
(version_int // 100) % 1000,
version_int % 100,
)
return '.'.join([str(k) for k in version_tuple])
class boost:
"""A class for capturing configuration information from boost
......@@ -69,34 +63,16 @@ class boost:
the standard path locations.
"""
default_roots = [
"/usr",
"/usr/local",
"/opt/local",
]
if 'XBOB_PREFIX_PATH' in os.environ:
roots = os.environ['XBOB_PREFIX_PATH'].split(os.pathsep) + default_roots
else:
roots = default_roots
globs = []
for k in roots:
globs += [
os.path.join(k, 'include', 'boost') + os.sep,
os.path.join(k, 'include', 'boost?*') + os.sep,
]
valid_globs = [glob.glob(k) for k in globs]
candidates = [d for k in valid_globs for d in k] #flatten
candidates = find_header('version.hpp', subpaths=['boost', 'boost?*'])
if not candidates:
raise RuntimeError("could not find any version of boost on the file system (looked at: %s)" % (', '.join(candidates)))
raise RuntimeError("could not find boost's `version.hpp' - have you installed Boost on this machine?")
found = False
if not requirement:
self.include_directory = candidates[0]
self.version = boost_version(self.include_directory)
self.include_directory = os.path.dirname(candidates[0])
self.version = boost_version(candidates[0])
found = True
else:
......@@ -156,7 +132,7 @@ class boost:
%(name)s
resolves to the module name you are searching for
%(version)s
%(ver)s
resolves to the current boost version string (e.g. ``'1.50.0'``)
%(py)s
......@@ -167,7 +143,7 @@ class boost:
* ``'boost_%(name)s-mt'``
* ``'boost_%(name)s'``
* ``'boost_%(name)s-gcc43-%(version)s'``
* ``'boost_%(name)s-gcc43-%(ver)s'``
Returns:
......@@ -178,57 +154,38 @@ class boost:
A list of strings indicating the names of the libraries you can use
"""
if only_static:
extensions = ['.a']
else:
if sys.platform == 'darwin':
extensions = ['.dylib', '.a']
elif sys.platform == 'win32':
extensions = ['.dll', '.a']
else: # linux like
extensions = ['.so', '.a']
# make the include header prefix preferential
prefix = os.path.dirname(os.path.dirname(self.include_directory))
libpaths = [
os.path.join(prefix, 'lib'),
]
if __import__('platform').architecture()[0] == '32bit':
libpaths += [
os.path.join(prefix, 'lib', 'i386-linux-gnu'),
os.path.join(prefix, 'lib32'),
]
else:
libpaths += [
os.path.join(prefix, 'lib', 'x86_64-linux-gnu'),
os.path.join(prefix, 'lib64'),
]
py = 'py%d%d' % sys.version_info[:2]
def paths(module):
"""Yields all possible paths for a module in good order"""
filenames = []
for module in modules:
candidates = []
modnames = [k % dict(name=module, ver=self.version, py=py) for k in
templates]
for extension in extensions:
for template in templates:
for libpath in libpaths:
modname = template % dict(name=module, version=self.version, py=py)
yield modname, os.path.join(libpath, 'lib' + modname + extension)
for modname in modnames:
candidates += find_library(modname, version=self.version,
prefixes=[prefix], only_static=only_static)
items = {}
for module in modules:
for modname, path in paths(module):
if os.path.exists(path):
items[module] = (os.path.dirname(path), modname)
break
if not candidates:
raise RuntimeError("cannot find required boost module `%s' - make sure boost is installed on `%s' and that this module is named %s on the filesystem" % (module, prefix, ' or '.join(modnames)))
# checks all modules were found, reports
for module in modules:
if module not in items:
raise RuntimeError("cannot find required boost module `%s', searched as `%s'" % (module, ', '.join([k[1] for k in paths(module)])))
# take only the first choice (supposed to be the best choice!)
filenames.append(candidates[0])
# libraries
libraries = []
for f in filenames:
name, ext = os.path.splitext(os.path.basename(f))
if ext in ['.so', '.a', '.dylib', '.dll']:
libraries.append(name[3:]) #strip 'lib' from the name
else: #link against the whole thing
libraries.append(':' + f)
libpaths, libraries = zip(*items.values())
# library paths
libpaths = [os.path.dirname(k) for k in filenames]
return uniq(libpaths), uniq(libraries)
......@@ -250,4 +207,3 @@ class boost:
"""
return [('HAVE_BOOST', '1'), ('BOOST_VERSION', '"%s"' % self.version)]
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# Andre Anjos <andre.anjos@idiap.ch>
# Thu 20 Mar 2014 12:43:48 CET
"""Tests for file search utilities
"""
import os
import sys
import nose.tools
from .utils import uniq, egrep, find_file, find_header, find_library
def test_uniq():
a = [1, 2, 3, 7, 3, 2]
nose.tools.eq_(uniq(a), [1, 2, 3, 7])
def test_find_file():
f = find_file('array.h', subpaths=[os.path.join('include', 'blitz')])
assert f
nose.tools.eq_(len(f), 1)
nose.tools.eq_(os.path.basename(f[0]), 'array.h')
def test_find_header():
f1 = find_file('array.h', subpaths=[os.path.join('include', 'blitz')])
assert f1
nose.tools.eq_(len(f1), 1)
nose.tools.eq_(os.path.basename(f1[0]), 'array.h')
f2 = find_header(os.path.join('blitz', 'array.h'))
nose.tools.eq_(len(f2), 1)
nose.tools.eq_(os.path.basename(f2[0]), 'array.h')
assert f2
nose.tools.eq_(f1, f2)
def test_find_library():
f = find_library('blitz')
assert f
assert len(f) >= 1
for k in f:
assert k.find('blitz') >= 0
def test_egrep():
f = find_header('version.hpp', subpaths=['boost', 'boost?*'])
assert f
nose.tools.eq_(len(f), 1)
matches = egrep(f[0], r"^#\s*define\s+BOOST_VERSION\s+(\d+)\s*$")
nose.tools.eq_(len(matches), 1)
def test_find_versioned_library():
f = find_header('version.hpp', subpaths=['boost', 'boost?*'])
assert f
nose.tools.eq_(len(f), 1)
matches = egrep(f[0], r"^#\s*define\s+BOOST_VERSION\s+(\d+)\s*$")
nose.tools.eq_(len(matches), 1)
version_int = int(matches[0].group(1))
version_tuple = (
version_int // 100000,
(version_int // 100) % 1000,
version_int % 100,
)
version = '.'.join([str(k) for k in version_tuple])
lib = find_library('boost_system', version=version)
assert len(lib) >= 1
for k in lib:
assert k.find('boost_system') >= 0
......@@ -5,9 +5,242 @@
'''General utilities for building extensions'''
import os
import re
import sys
import glob
import platform
DEFAULT_PREFIXES = [
"/usr",
"/usr/local",
"/opt/local",
]
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 ``XBOB_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.
"""
search = []
# Priority 1
if 'XBOB_PREFIX_PATH' in os.environ:
search += os.environ['XBOB_PREFIX_PATH'].split(os.pathsep)
# Priority 2
if prefixes:
search += prefixes
# Priority 3
search += DEFAULT_PREFIXES
# Make unique to avoid searching twice
search = uniq(search)
# Exhaustive combination of paths and subpaths
if subpaths:
subsearch = []
for s in search:
for p in subpaths:
subsearch.append(os.path.join(s, p))
subsearch.append(s)
search = subsearch
# Before we do a filesystem check, filter out the unexisting paths
tmp = []
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 retval
def find_header(name, subpaths=None, prefixes=None):
"""Finds a header 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 ``XBOB_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.
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.
"""
# Exhaustive combination of paths and subpaths
if subpaths:
my_subpaths = [os.path.join('include', k) for k in subpaths]
else:
my_subpaths = ['include']
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 ``XBOB_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.
"""
libpaths = ['lib']
if platform.architecture()[0] == '32bit':
libpaths += [
os.path.join('lib', 'i386-linux-gnu'),
os.path.join('lib32'),
]
else:
libpaths += [
os.path.join('lib', 'x86_64-linux-gnu'),
os.path.join('lib64'),
]
# Exhaustive combination of paths and subpaths
if subpaths:
my_subpaths = []
for lp in libpaths:
my_subpaths += [os.path.join(lp, k) for k in subpaths]
else:
my_subpaths = libpaths
# Extensions to consider
if only_static:
extensions = ['.a']
else:
if sys.platform == 'darwin':
extensions = ['.dylib', '.a']
elif sys.platform == 'win32':
extensions = ['.dll', '.a']
else: # linux like
extensions = ['.so', '.a']
# The module names can be set with or without version number
retval = []
if version:
for ext in extensions:
if sys.platform == 'darwin': # version in the middle
libname = 'lib' + name + '.' + version + ext
else: # version at the end
libname = 'lib' + name + ext + '.' + version
retval += find_file(libname, my_subpaths, prefixes)
for ext in extensions:
libname = 'lib' + name + ext
retval += find_file(libname, my_subpaths, prefixes)
return retval
def uniq(seq):
"""Uniqu-fy preserving order"""
seen = set()
seen_add = seen.add
return [x for x in seq if x not in seen and not seen_add(x)]
def egrep(filename, expression):
"""Runs grep for a given expression on each line of the file
Parameters:
filename, str
The name of the file to grep for the expression
expression
A regular expression, that will be initialized using :py:func:`re.compile`.
Returns a list of re matches.
"""
retval = []
with open(filename, 'rt') as f:
rexp = re.compile(expression)
for line in f:
p = rexp.match(line)
if p: retval.append(p)
return retval
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment