Skip to content

Commit cd4d90c

Browse files
author
Ryan Leckey
committed
Restructure and cleanup setup.
1 parent 41df5fa commit cd4d90c

5 files changed

Lines changed: 373 additions & 314 deletions

File tree

setup.py

Lines changed: 83 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -2,160 +2,103 @@
22
import sys
33
import subprocess
44
from os import path
5+
from collections import defaultdict
56
from distutils.core import setup
67
from distutils.extension import Extension
7-
from Cython.Distutils import build_ext
8+
from Cython import Distutils
89

910

10-
#
11-
# HACK
12-
#
11+
PKGCONFIG_TOKEN_MAP = {
12+
'-D': 'define_macros',
13+
'-I': 'include_dirs',
14+
'-L': 'library_dirs',
15+
'-l': 'libraries'
16+
}
1317

14-
def getoutput(command):
18+
def pkgconfig(*packages):
19+
"""
20+
Run the `pkg-config` utility to determine locations of includes,
21+
libraries, etc. for dependencies.
22+
"""
23+
config = defaultdict(set)
24+
25+
# Execute the command in a subprocess and communicate the output.
26+
command = "pkg-config --libs --cflags %s" % ' '.join(packages)
1527
process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
16-
out, err = process.communicate()
17-
return out.decode('utf8')
18-
19-
20-
def get_lxml_include_dirs():
21-
from os import environ
22-
lxml_home = environ.get("LXML_HOME")
23-
if lxml_home is None:
24-
# `LXML_HOME` not specified -- derive from installed `lxml`
25-
import lxml
26-
lxml_home = path.dirname(lxml.__file__)
27-
else:
28-
if not exists(lxml_home):
29-
sys.exit("The directory specified via envvar `LXML_HOME` does not exist")
30-
lxml_home = path.join(lxml_home, "src", "lxml")
31-
# check that it contains what is needed
32-
lxml_include = path.join(lxml_home, "includes")
33-
if not (path.exists(path.join(lxml_home, "etreepublic.pxd")) \
34-
or path.exists(path.join(lxml_include, "etreepublic.pxd"))):
35-
sys.exit("The lxml installation lacks the mandatory `etreepublic.pxd`. You may need to install `lxml` manually or set envvar `LXML_HOME` to an `lxml` installation with `etreepublic.pxd`")
36-
return [lxml_home, lxml_include]
28+
out, _ = process.communicate()
29+
30+
# Clean the output.
31+
out = out.decode('utf8')
32+
out = out.replace('\\\"', "")
33+
34+
# Iterate throught the tokens of the output.
35+
for token in out.split():
36+
key = PKGCONFIG_TOKEN_MAP.get(token[:2])
37+
if key:
38+
config[key].add(token[2:].strip())
39+
40+
# Convert sets to lists.
41+
for name in config:
42+
config[name] = list(config[name])
43+
44+
# Iterate and resolve define macros.
45+
macros = []
46+
for declaration in config['define_macros']:
47+
macros.append(tuple(declaration.split('=')))
48+
49+
config['define_macros'] = macros
50+
51+
# Return discovered configuration.
52+
return config
53+
3754

3855
# we must extend our cflags once `lxml` is installed.
3956
# To this end, we override `Extension`
40-
class Extension(Extension, object):
41-
lxml_extended = False
42-
43-
def get_include_dirs(self):
44-
ids = self.__dict__["include_dirs"]
45-
if self.lxml_extended: return ids
46-
# ensure `lxml` headers come before ours
47-
# this should make sure to use its headers rather than our old copy
48-
# ids.extend(get_lxml_include_dirs())
49-
ids[0:0] = get_lxml_include_dirs()
50-
self.lxml_extended = True
51-
return ids
52-
53-
def set_include_dirs(self, ids): self.__dict__["include_dirs"] = ids
54-
55-
include_dirs = property(get_include_dirs, set_include_dirs)
56-
57-
58-
define_macros = []
59-
include_dirs = ['src']
60-
library_dirs = []
61-
libraries = []
62-
63-
def extract_cflags(cflags):
64-
global define_macros, include_dirs
65-
list = cflags.split(' ')
66-
for flag in list:
67-
if flag == '':
68-
continue
69-
flag = flag.replace("\\\"", "")
70-
if flag[:2] == "-I":
71-
if flag[2:] not in include_dirs:
72-
include_dirs.append(flag[2:])
73-
elif flag[:2] == "-D":
74-
t = tuple(flag[2:].split('='))
75-
if len(t) == 1:
76-
t = (t[0], None)
77-
if t not in define_macros:
78-
define_macros.append(t)
79-
else:
80-
print("Warning : cflag %s skipped" % flag)
81-
82-
def extract_libs(libs):
83-
global library_dirs, libraries
84-
list = libs.split(' ')
85-
for flag in list:
86-
if flag == '':
87-
continue
88-
if flag[:2] == "-l":
89-
if flag[2:] not in libraries:
90-
libraries.append(flag[2:])
91-
elif flag[:2] == "-L":
92-
if flag[2:] not in library_dirs:
93-
library_dirs.append(flag[2:])
94-
else:
95-
print("Warning : linker flag %s skipped" % flag)
96-
97-
98-
libxml2_cflags = getoutput('pkg-config libxml-2.0 --cflags')
99-
if libxml2_cflags[:2] not in ["-I", "-D"]:
100-
libxml2_cflags = getoutput('xml2-config --cflags')
101-
if libxml2_cflags[:2] not in ["-I", "-D"]:
102-
print("Error : cannot get LibXML2 pre-processor and compiler flags")
103-
104-
libxml2_libs = getoutput('pkg-config libxml-2.0 --libs')
105-
if libxml2_libs[:2] not in ["-l", "-L"]:
106-
libxml2_libs = getoutput('xml2-config --libs')
107-
if libxml2_libs[:2] not in ["-l", "-L"]:
108-
print("Error : cannot get LibXML2 linker flags")
109-
110-
xmlsec1_crypto = 'openssl'
111-
cmd = 'pkg-config xmlsec1-%s --cflags' % xmlsec1_crypto
112-
xmlsec1_cflags = getoutput(cmd)
113-
if xmlsec1_cflags[:2] not in ["-I", "-D"]:
114-
cmd = 'xmlsec1-config --cflags --crypto=%s' % xmlsec1_crypto
115-
xmlsec1_cflags = getoutput(cmd)
116-
if xmlsec1_cflags[:2] not in ["-I", "-D"]:
117-
print("Error : cannot get XMLSec1 pre-processor and compiler flags")
118-
119-
cmd = 'pkg-config xmlsec1-%s --libs' % xmlsec1_crypto
120-
xmlsec1_libs = getoutput(cmd)
121-
if xmlsec1_libs[:2] not in ["-l", "-L"]:
122-
cmd = 'xmlsec1-config --libs --crypto=%s' % xmlsec1_crypto
123-
xmlsec1_libs = getoutput(cmd)
124-
if xmlsec1_libs[:2] not in ["-l", "-L"]:
125-
print("Error : cannot get XMLSec1 linker flags")
126-
127-
#print(libxml2_cflags)
128-
#print libxml2_libs
129-
#print xmlsec1_cflags
130-
#print xmlsec1_libs
131-
132-
extract_cflags(libxml2_cflags)
133-
extract_libs(libxml2_libs)
134-
135-
extract_cflags(xmlsec1_cflags)
136-
extract_libs(xmlsec1_libs)
137-
138-
#
139-
# END HACK
140-
#
57+
class Extension(Extension):
58+
59+
lxml_extended = False
60+
61+
@property
62+
def include_dirs(self):
63+
dirs = self.__dict__['include_dirs']
64+
if self.lxml_extended:
65+
return dirs
66+
67+
# Resolve lxml include directories.
68+
import lxml
69+
lxml_base = path.dirname(lxml.__file__)
70+
lxml_include = path.join(lxml_base, 'includes')
71+
72+
dirs.insert(0, lxml_include)
73+
dirs.insert(0, lxml_base)
74+
75+
self.lxml_extended = True
76+
return dirs
77+
78+
@include_dirs.setter
79+
def include_dirs(self, dirs):
80+
self.__dict__['include_dirs'] = dirs
81+
82+
83+
# Declare the crypto implementation.
84+
XMLSEC_CRYPTO = 'openssl'
85+
86+
# Process the `pkg-config` utility and discover include and library
87+
# directories.
88+
config = pkgconfig('libxml-2.0', 'xmlsec1-%s' % XMLSEC_CRYPTO)
89+
config['include_dirs'].insert(0, 'src') # Prepend 'src' as an include dir.
90+
14191

14292
setup(
14393
name='xmlsec',
14494
version='0.1.0',
14595
description='Python bindings for the XML Security Library.',
146-
setup_requires=["lxml >= 3.0",],
147-
install_requires=[
148-
"lxml >= 3.0"
96+
setup_requires=[
97+
'lxml >= 3.0',
14998
],
150-
cmdclass={'build_ext': build_ext},
151-
ext_modules=[
152-
Extension(
153-
'xmlsec', ['xmlsec.pyx'],
154-
define_macros=define_macros,
155-
include_dirs=include_dirs,
156-
library_dirs=library_dirs,
157-
libraries=libraries,
158-
depends=["src/" + f for f in "cxmlsec.pxd cxmlsec.h lxml.etree.h lxml-version.h lxml.etree_api.h".split()]
159-
)
99+
install_requires=[
100+
'lxml >= 3.0',
160101
],
102+
cmdclass = {'build_ext': Distutils.build_ext},
103+
ext_modules=[Extension('xmlsec', ['src/xmlsec.pyx'], **config)]
161104
)
File renamed without changes.

0 commit comments

Comments
 (0)