Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
bob
bob.extension
Commits
c6b08b1a
Commit
c6b08b1a
authored
Nov 18, 2013
by
André Anjos
💬
Browse files
Merge pypkg here
parent
9d2fd918
Changes
8
Hide whitespace changes
Inline
Side-by-side
.gitignore
View file @
c6b08b1a
*~
*.swp
*.pyc
bin
eggs
parts
.installed.cfg
.mr.developer.cfg
*.egg-info
dist/
src
develop-eggs
sphinx
dist
.nfs*
.gdb_history
build
README.rst
View file @
c6b08b1a
...
...
@@ -71,3 +71,42 @@ your ``buildout.cfg``. This includes, possibly, dependent projects. Currently,
``
zc
.
buildout
``
ignores
the
``
setup_requires
``
entry
on
your
``
setup
.
py
``
file
.
The
recipe
above
creates
a
new
interpreter
that
hooks
that
package
in
and
builds
the
project
considering
variables
like
``
prefixes
``
into
consideration
.
Python
API
to
pkg
-
config
------------------------
This
package
alson
contains
a
set
of
Pythonic
bindings
to
the
popular
pkg
-
config
configuration
utility
.
It
allows
distutils
-
based
setup
files
to
query
for
libraries
installed
on
the
current
system
through
that
command
line
utility
.
library
.
Using
at
your
``
setup
.
py
``
==========================
To
use
this
package
at
your
``
setup
.
py
``
file
,
you
will
need
to
let
distutils
know
it
needs
it
before
importing
it
.
You
can
achieve
this
with
the
following
trick
::
from
setuptools
import
dist
dist
.
Distribution
(
dict
(
setup_requires
=
'xbob.extension'
))
from
xbob
.
extension
.
pkgconfig
import
pkgconfig
..
note
::
In
this
case
,
distutils
should
automatically
download
and
install
this
package
on
the
environment
it
is
required
to
setup
other
package
.
After
inclusion
,
you
can
just
instantiate
an
object
of
type
``
pkgconfig
``::
>>>
zlib
=
pkgconfig
(
'zlib'
)
>>>
zlib
.
version
#
doctest
:
SKIP
1.2.8
>>>
zlib
.
include_directories
()
#
doctest
:
SKIP
[
'/usr/include'
]
>>>
zlib
.
library_dirs
#
doctest
:
SKIP
[
'/usr/lib'
]
>>>
zlib
>
'1.2.6'
True
>>>
zlib
>
'1.2.10'
False
bootstrap.py
0 → 100644
View file @
c6b08b1a
##############################################################################
#
# Copyright (c) 2006 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Bootstrap a buildout-based project
Simply run this script in a directory containing a buildout.cfg.
The script accepts buildout command-line options, so you can
use the -c option to specify an alternate configuration file.
"""
import
os
import
shutil
import
sys
import
tempfile
from
optparse
import
OptionParser
tmpeggs
=
tempfile
.
mkdtemp
()
usage
=
'''
\
[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
Bootstraps a buildout-based project.
Simply run this script in a directory containing a buildout.cfg, using the
Python that you want bin/buildout to use.
Note that by using --find-links to point to local resources, you can keep
this script from going over the network.
'''
parser
=
OptionParser
(
usage
=
usage
)
parser
.
add_option
(
"-v"
,
"--version"
,
help
=
"use a specific zc.buildout version"
)
parser
.
add_option
(
"-t"
,
"--accept-buildout-test-releases"
,
dest
=
'accept_buildout_test_releases'
,
action
=
"store_true"
,
default
=
False
,
help
=
(
"Normally, if you do not specify a --version, the "
"bootstrap script and buildout gets the newest "
"*final* versions of zc.buildout and its recipes and "
"extensions for you. If you use this flag, "
"bootstrap and buildout will get the newest releases "
"even if they are alphas or betas."
))
parser
.
add_option
(
"-c"
,
"--config-file"
,
help
=
(
"Specify the path to the buildout configuration "
"file to be used."
))
parser
.
add_option
(
"-f"
,
"--find-links"
,
help
=
(
"Specify a URL to search for buildout releases"
))
options
,
args
=
parser
.
parse_args
()
######################################################################
# load/install setuptools
to_reload
=
False
try
:
import
pkg_resources
import
setuptools
except
ImportError
:
ez
=
{}
try
:
from
urllib.request
import
urlopen
except
ImportError
:
from
urllib2
import
urlopen
# XXX use a more permanent ez_setup.py URL when available.
exec
(
urlopen
(
'https://bitbucket.org/pypa/setuptools/raw/0.7.2/ez_setup.py'
).
read
(),
ez
)
setup_args
=
dict
(
to_dir
=
tmpeggs
,
download_delay
=
0
)
ez
[
'use_setuptools'
](
**
setup_args
)
if
to_reload
:
reload
(
pkg_resources
)
import
pkg_resources
# This does not (always?) update the default working set. We will
# do it.
for
path
in
sys
.
path
:
if
path
not
in
pkg_resources
.
working_set
.
entries
:
pkg_resources
.
working_set
.
add_entry
(
path
)
######################################################################
# Try to best guess the version of buildout given setuptools
if
options
.
version
is
None
:
try
:
from
distutils.version
import
LooseVersion
package
=
pkg_resources
.
require
(
'setuptools'
)[
0
]
v
=
LooseVersion
(
package
.
version
)
if
v
<
LooseVersion
(
'0.7'
):
options
.
version
=
'2.1.1'
except
:
pass
######################################################################
# Install buildout
ws
=
pkg_resources
.
working_set
cmd
=
[
sys
.
executable
,
'-c'
,
'from setuptools.command.easy_install import main; main()'
,
'-mZqNxd'
,
tmpeggs
]
find_links
=
os
.
environ
.
get
(
'bootstrap-testing-find-links'
,
options
.
find_links
or
(
'http://downloads.buildout.org/'
if
options
.
accept_buildout_test_releases
else
None
)
)
if
find_links
:
cmd
.
extend
([
'-f'
,
find_links
])
setuptools_path
=
ws
.
find
(
pkg_resources
.
Requirement
.
parse
(
'setuptools'
)).
location
requirement
=
'zc.buildout'
version
=
options
.
version
if
version
is
None
and
not
options
.
accept_buildout_test_releases
:
# Figure out the most recent final version of zc.buildout.
import
setuptools.package_index
_final_parts
=
'*final-'
,
'*final'
def
_final_version
(
parsed_version
):
for
part
in
parsed_version
:
if
(
part
[:
1
]
==
'*'
)
and
(
part
not
in
_final_parts
):
return
False
return
True
index
=
setuptools
.
package_index
.
PackageIndex
(
search_path
=
[
setuptools_path
])
if
find_links
:
index
.
add_find_links
((
find_links
,))
req
=
pkg_resources
.
Requirement
.
parse
(
requirement
)
if
index
.
obtain
(
req
)
is
not
None
:
best
=
[]
bestv
=
None
for
dist
in
index
[
req
.
project_name
]:
distv
=
dist
.
parsed_version
if
_final_version
(
distv
):
if
bestv
is
None
or
distv
>
bestv
:
best
=
[
dist
]
bestv
=
distv
elif
distv
==
bestv
:
best
.
append
(
dist
)
if
best
:
best
.
sort
()
version
=
best
[
-
1
].
version
if
version
:
requirement
=
'=='
.
join
((
requirement
,
version
))
cmd
.
append
(
requirement
)
import
subprocess
if
subprocess
.
call
(
cmd
,
env
=
dict
(
os
.
environ
,
PYTHONPATH
=
setuptools_path
))
!=
0
:
raise
Exception
(
"Failed to execute command:
\n
%s"
,
repr
(
cmd
)[
1
:
-
1
])
######################################################################
# Import and run buildout
ws
.
add_entry
(
tmpeggs
)
ws
.
require
(
requirement
)
import
zc.buildout.buildout
if
not
[
a
for
a
in
args
if
'='
not
in
a
]:
args
.
append
(
'bootstrap'
)
# if -c was provided, we push it back into args for buildout' main function
if
options
.
config_file
is
not
None
:
args
[
0
:
0
]
=
[
'-c'
,
options
.
config_file
]
zc
.
buildout
.
buildout
.
main
(
args
)
shutil
.
rmtree
(
tmpeggs
)
buildout.cfg
0 → 100644
View file @
c6b08b1a
; vim: set fileencoding=utf-8 :
; Andre Anjos <andre.anjos@idiap.ch>
; Mon 16 Apr 08:29:18 2012 CEST
[buildout]
parts = scripts
develop = .
eggs = xbob.extension
ipdb
[scripts]
recipe = xbob.buildout:scripts
setup.py
View file @
c6b08b1a
...
...
@@ -28,7 +28,6 @@ setup(
install_requires
=
[
'setuptools'
,
'pypkg'
,
],
classifiers
=
[
...
...
xbob/extension/__init__.py
View file @
c6b08b1a
...
...
@@ -3,13 +3,15 @@
# Andre Anjos <andre.anjos@idiap.ch>
# Mon 28 Jan 2013 16:40:27 CET
"""A custom build class for
Bob/Python
extensions
"""A custom build class for
Pkg-config based
extensions
"""
import
platform
from
pypk
g
import
pkgconfig
from
.pkgconfi
g
import
pkgconfig
from
distutils.extension
import
Extension
as
DistutilsExtension
__version__
=
__import__
(
'pkg_resources'
).
require
(
'xbob.extension'
)[
0
].
version
def
uniq
(
seq
):
"""Uniqu-fy preserving order"""
...
...
@@ -37,19 +39,20 @@ def check_packages(packages):
from
re
import
split
used
=
set
()
retval
=
[]
for
requirement
in
uniq
(
packages
):
splitreq
=
split
(
r
'\s*(?P<cmp>[<>=]+)\s*'
,
requirement
)
if
len
(
splitreq
)
==
1
:
# just package name
if
len
(
splitreq
)
not
in
(
1
,
3
):
p
=
pkgconfig
(
splitreq
[
0
]
)
raise
RuntimeError
(
"cannot parse requirement `%s'"
,
requirement
)
elif
len
(
splitreq
)
==
3
:
# package + version number
p
=
pkgconfig
(
splitreq
[
0
])
p
=
pkgconfig
(
splitreq
[
0
])
if
len
(
splitreq
)
==
3
:
# package + version number
if
splitreq
[
1
]
==
'>'
:
assert
p
>
splitreq
[
2
],
"%s version is not > `%s'"
%
(
p
,
splitreq
[
2
])
...
...
@@ -64,17 +67,17 @@ def check_packages(packages):
else
:
raise
RuntimeError
(
"cannot parse requirement `%s'"
,
requirement
)
else
:
raise
RuntimeError
(
"cannot parse requirement `%s'"
,
requirement
)
retval
.
append
(
p
)
if
p
.
name
in
used
:
raise
RuntimeError
(
"package `%s' had already been requested - cannot (currently) handle recurring requirements"
)
used
.
add
(
p
.
name
)
return
retval
class
Extension
(
DistutilsExtension
):
"""Extension building with
Bob/Python binding
s.
"""Extension building with
pkg-config package
s.
See the documentation for :py:class:`distutils.extension.Extension` for more
details on input parameters.
...
...
@@ -83,8 +86,8 @@ class Extension(DistutilsExtension):
def
__init__
(
self
,
*
args
,
**
kwargs
):
"""Initialize the extension with parameters.
Bob/Pyth
on adds a single parameter to the standard arguments of
the
constructor:
Pkg-config extensi
on
s
adds a single parameter to the standard arguments of
the
constructor:
pkgconfig : [list]
...
...
xbob/extension/pkgconfig.py
0 → 100644
View file @
c6b08b1a
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# Andre Anjos <andre.anjos@idiap.ch>
# Wed 16 Oct 10:08:42 2013 CEST
import
os
import
subprocess
import
logging
def
uniq
(
seq
,
idfun
=
None
):
"""Very fast, order preserving uniq function"""
# order preserving
if
idfun
is
None
:
def
idfun
(
x
):
return
x
seen
=
{}
result
=
[]
for
item
in
seq
:
marker
=
idfun
(
item
)
# in old Python versions:
# if seen.has_key(marker)
# but in new ones:
if
marker
in
seen
:
continue
seen
[
marker
]
=
1
result
.
append
(
item
)
return
result
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.
"""
# if the user has passed their own paths, add it to the environment
env
=
os
.
environ
if
paths
is
not
None
:
env
=
os
.
environ
.
copy
()
var
=
os
.
pathsep
.
join
(
paths
)
old
=
env
.
get
(
'PKG_CONFIG_PATH'
,
False
)
env
[
'PKG_CONFIG_PATH'
]
=
os
.
pathsep
.
join
([
var
,
old
])
if
old
else
var
# calls the lua creation script using the parameters
cmd
=
[
'pkg-config'
]
+
[
str
(
k
)
for
k
in
cmd
]
subproc
=
subprocess
.
Popen
(
cmd
,
env
=
env
,
stderr
=
subprocess
.
PIPE
,
stdout
=
subprocess
.
PIPE
)
logging
.
debug
(
"Running `%s'"
%
(
" "
.
join
(
cmd
),))
stdout
,
stderr
=
subproc
.
communicate
()
# always print the stdout
logger
=
logging
.
getLogger
(
'pkgconfig'
)
for
k
in
stdout
.
split
(
'
\n
'
):
if
k
:
logger
.
debug
(
k
)
# handles py3k string conversion, if necessary
if
isinstance
(
stdout
,
bytes
)
and
not
isinstance
(
stdout
,
str
):
stdout
=
stdout
.
decode
(
'utf8'
)
if
isinstance
(
stderr
,
bytes
)
and
not
isinstance
(
stderr
,
str
):
stderr
=
stderr
.
decode
(
'utf8'
)
return
subproc
.
returncode
,
stdout
,
stderr
class
pkgconfig
:
"""A class for capturing configuration information from pkg-config
Example usage:
.. doctest::
:options: +NORMALIZE_WHITESPACE +ELLIPSIS
>>> glibc = pkgconfig('glibc')
>>> glibc.include_directories() # doctest: SKIP
['/usr/include']
>>> glibc.library_directories() # doctest: SKIP
['/usr/lib']
If the package does not exist, a RuntimeError is raised. All calls to any
methods of a ``pkgconfig`` object are translated into a subprocess call that
queries for that specific information. If ``pkg-config`` fails, a
RuntimeError is raised.
"""
def
__init__
(
self
,
name
,
paths
=
None
):
"""Constructor
Parameters:
name
The name of the package of interest, as you would pass on the command
line
extra_paths
Search paths to be added to the environment's PKG_CONFIG_PATH to search
for packages.
Equivalent command line version:
.. code-block:: sh
$ PKG_CONFIG_PATH=<paths> pkg-config <name>
"""
status
,
stdout
,
stderr
=
call_pkgconfig
([
'--modversion'
,
name
],
paths
)
if
status
!=
0
:
raise
RuntimeError
(
"pkg-config package `%s' was not found"
%
name
)
self
.
name
=
name
self
.
version
=
stdout
.
strip
()
self
.
paths
=
paths
def
__xcall__
(
self
,
cmd
):
"""Calls call_pkgconfig() with self.package and self.paths"""
return
call_pkgconfig
(
cmd
+
[
self
.
name
],
self
.
paths
)
def
__cmp__
(
self
,
other
):
"""Compares this package with a version number
We create a new ``distutils.version.LooseVersion`` object out of your input
argument and then, compare it to our own version, returning the result.
Returns an integer smaller than zero if this package's version number is
smaller than the provided value. Returns zero in case of a match and
greater than zero in the other case.
"""
from
distutils.version
import
LooseVersion
return
cmp
(
self
.
version
,
LooseVersion
(
other
))
def
include_directories
(
self
):
"""Returns a pre-processed list containing include directories.
Equivalent command line version:
.. code-block:: sh
$ PKG_CONFIG_PATH=<paths> pkg-config --cflags-only-I <name>
"""
status
,
stdout
,
stderr
=
self
.
__xcall__
([
'--cflags-only-I'
])
if
status
!=
0
:
raise
RuntimeError
(
"error querying --cflags-only-I for package `%s': %s"
%
(
self
.
package
,
stderr
))
retval
=
[]
for
token
in
stdout
.
split
():
retval
.
append
(
token
[
2
:])
return
uniq
(
retval
)
def
cflags_other
(
self
):
"""Returns a pre-processed dictionary containing compilation options.
Equivalent command line version:
.. code-block:: sh
$ PKG_CONFIG_PATH=<paths> pkg-config --cflags-only-other <name>
The returned dictionary contains two entries ``extra_compile_args`` and
``define_macros``. The ``define_macros`` entries are ready for deployment
in the ``setup()`` function of your package.
"""
status
,
stdout
,
stderr
=
self
.
__xcall__
([
'--cflags-only-other'
])
if
status
!=
0
:
raise
RuntimeError
(
"error querying --cflags-only-other for package `%s': %s"
%
(
self
.
package
,
stderr
))
flag_map
=
{
'-D'
:
'define_macros'
,
}
kw
=
{}
for
token
in
stdout
.
split
():
if
token
[:
2
]
in
flag_map
:
kw
.
setdefault
(
flag_map
.
get
(
token
[:
2
]),
[]).
append
(
token
[
2
:])
else
:
# throw others to extra_link_args
kw
.
setdefault
(
'extra_compile_args'
,
[]).
append
(
token
)
# make it uniq
for
k
,
v
in
kw
.
items
():
kw
[
k
]
=
uniq
(
v
)
# for macros, separate them so they can be plugged on C/C++ extensions
if
'define_macros'
in
kw
:
for
k
,
string
in
enumerate
(
kw
[
'define_macros'
]):
if
string
.
find
(
'='
)
!=
-
1
:
kw
[
'define_macros'
][
k
]
=
string
.
split
(
'='
,
2
)
else
:
kw
[
'define_macros'
][
k
]
=
(
string
,
None
)
return
kw
def
libraries
(
self
):
"""Returns a pre-processed list containing libraries to link against
Equivalent command line version:
.. code-block:: sh
$ PKG_CONFIG_PATH=<paths> pkg-config --libs-only-l <name>
"""
status
,
stdout
,
stderr
=
self
.
__xcall__
([
'--libs-only-l'
])
if
status
!=
0
:
raise
RuntimeError
(
"error querying --libs-only-l for package `%s': %s"
%
(
self
.
package
,
stderr
))
retval
=
[]
for
token
in
stdout
.
split
():
retval
.
append
(
token
[
2
:])
return
uniq
(
retval
)
def
library_directories
(
self
):
"""Returns a pre-processed list containing library directories.
Equivalent command line version:
.. code-block:: sh
$ PKG_CONFIG_PATH=<paths> pkg-config --libs-only-L <name>
"""
status
,
stdout
,
stderr
=
self
.
__xcall__
([
'--libs-only-L'
])
if
status
!=
0
:
raise
RuntimeError
(
"error querying --libs-only-L for package `%s': %s"
%
(
self
.
package
,
stderr
))
retval
=
[]
for
token
in
stdout
.
split
():
retval
.
append
(
token
[
2
:])
return
uniq
(
retval
)
def
extra_link_args
(
self
):
"""Returns a pre-processed list containing extra link arguments.
Equivalent command line version:
.. code-block:: sh
$ PKG_CONFIG_PATH=<paths> pkg-config --libs-only-other <name>
"""
status
,
stdout
,
stderr
=
self
.
__xcall__
([
'--libs-only-other'
])
if
status
!=
0
:
raise
RuntimeError
(
"error querying --libs-only-other for package `%s': %s"
%
(
self
.
package
,
stderr
))
return
stdout
.
strip
().
split
()
def
variable_names
(
self
):
"""Returns a list with all variable names know to this package
Equivalent command line version:
.. code-block:: sh
$ PKG_CONFIG_PATH=<paths> pkg-config --print-variables <name>
"""
status
,
stdout
,
stderr
=
self
.
__xcall__
([
'--print-variables'
])
if
status
!=
0
:
raise
RuntimeError
(
"error querying --print-variables for package `%s': %s"
%
(
self
.
package
,
stderr
))
return
stdout
.
strip
().
split
()