forked from nmathewson/mixminion
-
Notifications
You must be signed in to change notification settings - Fork 1
/
setup.py
557 lines (485 loc) · 19.9 KB
/
setup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
#!/usr/bin/python
# Copyright 2002-2004 Nick Mathewson. See LICENSE for licensing information.
# $Id: setup.py,v 1.103 2007/09/15 19:06:37 nickm Exp $
import sys
#
# Current Mixminion version
#
VERSION = '0.0.8alpha3'
# System: 0==alpha, 50==beta, 98=pre, 99==release candidate, 100==release
VERSION_INFO = (0,0,8,0,3)
# Check the version. We need to make sure version_info exists before we
# compare to it: it was only added as of Python version 1.6.
#
# (Because of syntax issues, this file won't even parse for any python older
# than 1.3. I'm okay with that.)
if getattr(sys, 'platform', None) == 'win32':
if not hasattr(sys, 'version_info') or sys.version_info < (2,3,0):
print "Sorry, but I require Python 2.3 or higher on Windows."
sys.exit(1)
if not hasattr(sys, 'version_info') or sys.version_info < (2, 0, 0):
print "Sorry, but I require Python 2.0 or higher."
sys.exit(1)
if sys.version_info[:3] == (2,1,0):
print "Python 2.1.0 has known bugs that keep Mixminion from working."
print "Maybe you should upgrade to 2.1.3 or some more recent version."
sys.exit(1)
if sys.version_info[:3] == (2,1,1):
print "Python 2.1.1 has known bugs that keep Mixminion from working."
print "Maybe you should upgrade to 2.1.3 or some more recent version."
sys.exit(1)
try:
import zlib
except ImportError:
print "Zlib support seems to be missing; install Python with zlib support."
sys.exit(1)
try:
import socket
del socket
except ImportError:
print "Your Python installation is somehow missing socket support."
if sys.platform.startswith("sunos") or sys.platform.startswith("solaris"):
print "This is a known issue when building some versions of Python"
print "on Solaris."
sys.exit(1)
import os, re, shutil, string, struct
os.umask(022)
# Function to pull openssl version number out of an opensslv.h file. This
# isn't a real C preprocessor, but it seems to work well enough.
_define_version_line = re.compile(
r'\s*#\s*define\s+OPENSSL_VERSION_NUMBER\s+(\S+)$')
def getOpenSSLVersion(filename):
if not os.path.exists(filename):
print "Uh oh; can't open %s"%filename
return None
f = open(filename, 'r')
version = None
for l in f.readlines():
m = _define_version_line.match(l)
if m:
version = m.group(1)
break
f.close()
if not version:
print "Uh oh; can't find a version in %s"%filename
return None
version = version.lower()
try:
return string.atol(version, 0)
except ValueError:
print "Can't parse version from %s"%filename
return None
USE_OPENSSL = 1
# Lowest allowable OpenSSL version; this corresponds to OpenSSL 0.9.7b3
MIN_OPENSSL_VERSION = 0x00907003L
OPENSSL_CFLAGS = []
OPENSSL_LDFLAGS = []
MACROS=[]
MODULES=[]
BAD_OPENSSL_IN_CONTRIB = """
=========================================================
Bizarrely, ./contrib/openssl contains an obsolete version
of OpenSSL. Try removing ./contrib/openssl, then running
make download-openssl; make build-openssl again.
========================================================="""
NO_OPENSSL_FOUND = """
======================================================================
I need OpenSSL 0.9.7 or greater, and I couldn't find it anywhere that
I looked. If you installed it somewhere unusual, try setting the
variable OPENSSL_PREFIX as in:
make OPENSSL_PREFIX=/opt/openssl-0.9.7
If you have a nonstandard OpenSSL 0.9.7 installation, you may need to
give compiler flags directly, as in:
make OPENSSL_CFLAGS='-I ~/openssl-include' \\
OPENSSL_LDFLAGS='-L ~/openssl-libs -lssl097 -lcrypto097'
If your C compiler knows where to find OpenSSL 0.9.7, and I should
just trust it, use the SKIP_OPENSSL_SEARCH option, as in:
make SKIP_OPENSSL_SEARCH="y"
Finally, if you don't have OpenSSL 0.9.7 and you don't want to install
it, you can grab and build a local copy for Mixminion only by running:
make download-openssl
make build-openssl
(then)
make
(Or, if you have the OpenSSL source somewhere else, use OPENSSL_SRC
as in:
make build-openssl OPENSSL_SRC=~/src/openssl-0.9.7
make OPENSSL_SRC=~/src/openssl-0.9.7
)
======================================================================"""
#XXXX Use pkg-config when possible, if it exists.
if USE_OPENSSL and sys.platform == 'win32':
# If we're on windows, insist on finding the libraries in ./contrib/openssl
INCLUDE_DIRS = []
STATIC_LIBS = []
LIBRARY_DIRS = []
incd = ".\\contrib\\OpenSSL\\include"
libd = ".\\contrib\\OpenSSL\\lib\\VC"
if os.path.exists(incd): INCLUDE_DIRS.append(incd)
if os.path.exists(incd.lower()): INCLUDE_DIRS.append(incd.lower())
if os.path.exists(libd): LIBRARY_DIRS.append(libd)
if os.path.exists(libd.lower()): LIBRARY_DIRS.append(libd.lower())
if not (INCLUDE_DIRS and LIBRARY_DIRS):
print ("Can't find openssl: make sure that a compiled openssl "
"distribution is stored \nat .\\contrib\\OpenSSL")
sys.exit(1)
LIBRARIES = [ "ssleay32", "libeay32", "advapi32" ]
elif USE_OPENSSL:
# For now, we assume that openssl-0.9.7 isn't generally deployed, so we
# need to look carefully.
# If the user has specified an OpenSSL installation, we trust the user.
# Anything else is loopy.
if os.environ.get("OPENSSL_CFLAGS") or os.environ.get("OPENSSL_LDFLAGS"):
OPENSSL_CFLAGS = os.environ.get("OPENSSL_CFLAGS", "").split()
OPENSSL_LDFLAGS = os.environ.get("OPENSSL_LDFLAGS", "").split()
print "Using OpenSSL as specified in OPENSSL_CFLAGS/OPENSSL_LDFLAGS."
INCLUDE_DIRS = []
STATIC_LIBS = []
LIBRARY_DIRS = []
LIBRARIES = []
# Otherwise, if the user has run 'make build-openssl', we have a good
# copy of OpenSSL sitting in ./contrib/openssl that they want us to use.
elif os.environ.get("SKIP_OPENSSL_SEARCH"):
print "Assuming that the C compiler knows where to find OpenSSL."
INCLUDE_DIRS = []
STATIC_LIBS = []
LIBRARY_DIRS = []
LIBRARIES = [ 'ssl', 'crypto' ]
elif ((os.path.exists("./contrib/openssl") or
os.environ.get("OPENSSL_SRC"))
and not os.environ.get("OPENSSL_PREFIX")):
openssl_src = os.environ.get("OPENSSL_SRC", "./contrib/openssl")
openssl_src = os.path.expanduser(openssl_src)
if not os.path.exists(openssl_src):
print "$OPENSSL_SRC does not exist."
sys.exit(1)
print "Using OpenSSL from", openssl_src
openssl_inc = os.path.join(openssl_src, "include")
INCLUDE_DIRS = [openssl_inc]
STATIC_LIBS=[ os.path.join(openssl_src, "libssl.a"),
os.path.join(openssl_src, "libcrypto.a") ]
LIBRARY_DIRS=[]
LIBRARIES=[]
v = getOpenSSLVersion(os.path.join(openssl_src,
"include", "openssl", "opensslv.h"))
if not v or v < MIN_OPENSSL_VERSION:
print BAD_OPENSSL_IN_CONTRIB
sys.exit(1)
# Otherwise, look in a bunch of standard places for a possible OpenSSL
# installation. This logic is adapted from check_ssl.m4 from ac-archive;
# the list of locations is extended with locations from Python's setup.py.
else:
print "Searching for platform OpenSSL."
found = 0
PREFIXES = ("/usr/local/ssl", "/usr/contrib/ssl", "/usr/lib/ssl",
"/usr/ssl", "/usr/pkg", "/usr/local", "/usr", "/")
if os.environ.get("OPENSSL_PREFIX"):
PREFIXES = (os.environ["OPENSSL_PREFIX"],)
for prefix in PREFIXES:
if found:
break
print "Looking in %s ..."%prefix
incdir = os.path.join(prefix, "include")
for trunc in 0,1:
if trunc:
opensslv_h = os.path.join(incdir, "opensslv.h")
else:
opensslv_h = os.path.join(incdir, "openssl", "opensslv.h")
if os.path.exists(opensslv_h):
v = getOpenSSLVersion(opensslv_h)
if v and v >= MIN_OPENSSL_VERSION:
INCLUDE_DIRS = [incdir]
LIBRARY_DIRS = [os.path.join(prefix,"lib")]
if trunc:
MACROS.append(('TRUNCATED_OPENSSL_INCLUDES', None))
print "Using version of OpenSSL in %s"%prefix
found = 1
break
print "Skipping old version of OpenSSL in %s"%prefix
if not found:
print NO_OPENSSL_FOUND
sys.exit(1)
STATIC_LIBS=[]
LIBRARIES=['ssl','crypto']
#======================================================================
# Check the version of Mixminion as it's set in the source, and update
# __init__.py as needed.
f = open("lib/mixminion/__init__.py", 'r')
initFile = f.read()
f.close()
initCorrected = re.compile(r'^__version__\s*=.*$', re.M).sub(
'__version__ = \"%s\"'%VERSION, initFile)
initCorrected = re.compile(r'^version_info\s*=.*$', re.M).sub(
'version_info = %r'%(VERSION_INFO,), initCorrected)
if initCorrected != initFile:
f = open("lib/mixminion/__init__.py", 'w')
f.write(initCorrected)
f.close()
#======================================================================
# Install unittest if python doesn't provide it. (This is a 2.0 issue)
try:
import unittest
except:
if not os.path.exists("lib/mixminion/_unittest.py"):
shutil.copy("contrib/unittest.py", "lib/mixminion/_unittest.py")
# Install textwrap if Python doesn't provide it. (This goes for all python<2.3)
try:
import textwrap
except:
if not os.path.exists("lib/mixminion/_textwrap.py"):
shutil.copy("contrib/textwrap.py", "lib/mixminion/_textwrap.py")
# If we have a version of Python older than 2.2, we can't do bounded-space
# decompression without magic. That magic is written by Zooko.
if sys.version_info[:3] < (2,2,0):
if not os.path.exists("lib/mixminion/_zlibutil.py"):
shutil.copy("contrib/zlibutil.py", "lib/mixminion/_zlibutil.py")
#======================================================================
# Detect endian-ness
#XXXX This breaks cross-compilation, but might be good enough for now.
num = struct.pack("@I", 0x01020304)
big_endian = (num== "\x01\x02\x03\x04")
little_endian = (num=="\x04\x03\x02\x01")
other_endian = not (big_endian or little_endian)
if big_endian:
print "Host is big-endian"
MACROS.append( ("MM_B_ENDIAN", 1) )
elif little_endian:
print "Host is little-endian"
MACROS.append( ("MM_L_ENDIAN", 1) )
elif other_endian:
print "\nWild! Your machine seems to be middle-endian, and yet you've"
print "somehow made it run Python. Despite your perversity, I admire"
print "your nerve, and will try to soldier on.\n"
MACROS.append( ("MM_O_ENDIAN", 1) )
#======================================================================
# Apple's OS X 10.2 has really weird options for its Python distutils.
# The logic to fix this comes from Twisted.
BROKEN_CONFIG = '2.2 (#1, 07/14/02, 23:25:09) \n[GCC Apple cpp-precomp 6.14]'
if sys.platform == 'darwin' and sys.version == BROKEN_CONFIG:
# change this to 1 if you have some need to compile
# with -flat_namespace as opposed to -bundle_loader
FLAT_NAMESPACE = 0
BROKEN_ARCH = '-arch i386'
BROKEN_NAMESPACE = '-flat_namespace -undefined_suppress'
import distutils.sysconfig
distutils.sysconfig.get_config_vars()
x = distutils.sysconfig._config_vars['LDSHARED']
y = x.replace(BROKEN_ARCH, '')
if not FLAT_NAMESPACE:
e = os.path.realpath(sys.executable)
y = y.replace(BROKEN_NAMESPACE, '-bundle_loader ' + e)
if y != x:
print "Fixing some of Apple's compiler flag mistakes..."
distutils.sysconfig._config_vars['LDSHARED'] = y
#======================================================================
# Create a startup script if we're installing.
# This isn't as fully general as distutils allows. Unfortunately, distutils
# doesn't make it easy for us to create a script that knows where distutils
# has been told to install.
if os.environ.get('PREFIX'):
prefix = os.path.expanduser(os.environ["PREFIX"])
pathextra = os.path.join(prefix, "lib",
"python"+(sys.version)[:3],
"site-packages")
else:
pathextra = ""
if not os.path.exists("build"):
os.mkdir("build")
if sys.platform == 'win32':
SCRIPT_SUFFIX = ".py"
else:
SCRIPT_SUFFIX = ""
SCRIPTS = []
for name in "mixminion", "mixminiond":
SCRIPT_PATH = os.path.join("build", name+SCRIPT_SUFFIX)
f = open(SCRIPT_PATH, 'wt')
# Distutils will take care of the executable path, and actually gets angry
# if we try to be smart on our own. *sigh*.
f.write("#!python\n")
f.write("import sys\n")
if pathextra and 'py2exe' not in sys.argv:
f.write("sys.path[0:0] = [%r]\n"%pathextra)
f.write("""\
try:
import mixminion.Main
except:
print 'ERROR importing mixminion package.'
raise
""")
if 'py2exe' in sys.argv:
f.write("""\
if 1==0:
# These import statements need to be here so that py2exe knows to
# include all of the mixminion libraries. Main.py imports libraries
# conditionally with __import__ --- but that confuses py2exe.
import mixminion.Common
import mixminion.test
import mixminion.benchmark
import mixminion.ClientMain
import mixminion.server.ServerMain
import mixminion.directory.DirMain
# The 'anydbm' module uses __import__ to conditionally load bsddb.
# We need to be sure that it gets included, or else we'll get stuck
# using the dumbdbm module.
import bsddb, dbhash
""")
if name == 'mixminiond':
f.write("\nmixminion.Main.main(sys.argv, 1)\n")
else:
if sys.platform == 'win32':
f.write("# On win32, we default to shell mode.\n")
f.write("if len(sys.argv) == 1: sys.argv.append('shell')\n")
f.write("\nmixminion.Main.main(sys.argv)\n")
f.close()
SCRIPTS.append(SCRIPT_PATH)
#======================================================================
# Define a helper to let us run commands from the compiled code.
def _haveCmd(cmdname):
for entry in os.environ.get("PATH", "").split(os.pathsep):
if os.path.exists(os.path.join(entry, cmdname)):
return 1
return 0
def requirePythonDev(e=None):
if os.path.exists("/etc/debian_version"):
v = sys.version[:3]
print "Debian may expect you to install python%s-dev"%v
elif os.path.exists("/etc/redhat-release"):
print "Redhat may expect you to install python2-devel"
else:
print "You may be missing some 'python development' package for your"
print "distribution."
if e:
print "(Error was: %s)"%e
sys.exit(1)
try:
from distutils.core import Command
from distutils.errors import DistutilsPlatformError
from distutils.sysconfig import get_makefile_filename
except ImportError, e:
print "\nUh oh. You have Python installed, but I didn't find the distutils"
print "module, which is supposed to come with the standard library.\n"
requirePythonDev()
if 'py2exe' in sys.argv:
import py2exe
try:
# This catches failures to install python2-dev on some recent Redhats.
mf = get_makefile_filename()
print mf
except IOError:
print "\nUh oh. You have Python installed, but distutils can't find the"
print "Makefile it needs to build additional Python components.\n"
requirePythonDev()
class runMMCommand(Command):
# Based on setup.py from Zooko's pyutil package, which is in turn based on
# http://mail.python.org/pipermail/distutils-sig/2002-January/002714.html
description = "Run a subcommand from mixminion.Main"
user_options = [
('subcommand=', None, 'Subcommand to run')]
def initialize_options(self):
self.subcommand = "unittests"
def finalize_options(self):
build = self.get_finalized_command('build')
self.build_purelib = build.build_purelib
self.build_platlib = build.build_platlib
def run(self):
self.run_command('build')
old_path = sys.path
sys.path[0:0] = [ self.build_purelib, self.build_platlib ]
try:
minion = __import__("mixminion.Main", globals(), "", [])
minion.Main.main(["mixminion.Main", self.subcommand])
finally:
sys.path = old_path
#======================================================================
# Now, tell setup.py how to cope.
import distutils.core
from distutils.core import setup, Extension
from distutils import sysconfig
if os.environ.get("PREFIX") and 'install' in sys.argv:
# Try to suppress the warning about sys.path by appending to the end of
# the path temporarily.
sys.path.append(os.path.join(os.environ.get("PREFIX"),
"lib",
"python%s"%sys.version[:3],
"site-packages"))
if 'install' in sys.argv:
if os.environ.get("PREFIX"):
sp = os.path.join(os.environ.get("PREFIX"),
"lib",
"python%s"%sys.version[:3],
"site-packages")
else:
sp = os.path.join(sys.prefix,
"lib",
"python%s"%sys.version[:3],
"site-packages")
fn = os.path.join(sp, "mixminion", "server", "Queue.py")
if os.path.exists(fn):
print "Removing obsolete Queue.py"
try:
os.unlink(fn)
except OSError, e:
print "Couldn't unlink obsolete Queue.py: %s"%e
# This is needed for a clean build on redhat 9.
if os.path.exists("/usr/kerberos/include"):
INCLUDE_DIRS.append("/usr/kerberos/include")
INCLUDE_DIRS.append("src")
EXTRA_CFLAGS = []
if sys.platform != 'win32':
EXTRA_CFLAGS += [ '-Wno-strict-prototypes' ]
extmodule = Extension(
"mixminion._minionlib",
["src/crypt.c", "src/aes_ctr.c", "src/main.c", "src/tls.c", "src/fec.c" ],
include_dirs=INCLUDE_DIRS,
extra_objects=STATIC_LIBS,
extra_compile_args=EXTRA_CFLAGS + OPENSSL_CFLAGS,
extra_link_args=OPENSSL_LDFLAGS,
library_dirs=LIBRARY_DIRS,
libraries=LIBRARIES,
define_macros=MACROS)
if 'py2exe' in sys.argv:
# Py2EXE wants numberic versions for Windows
VERSION = "." .join(map(str,VERSION_INFO))
# XXXX This is only necessary because of an apparent py2exe 0.5.0 bug;
# XXXX I have submitted a bug report [911596] to sourceforge.
sys.path.append("./build/lib.win32-2.3")
EXTRA = {
'console' : SCRIPTS,
'zipfile' : r'lib\shared.zip',
'options' : {'py2exe':
{ 'compressed':1,
'excludes': ['mixminion._textwrap','mixminion._unittest',
'mixminion._zlibutil','coverage'] }
},
'data_files' : [("",["README","TODO","LICENSE","HISTORY",
"etc/mixminiond.conf"])],
}
elif sys.platform != 'win32':
EXTRA = {
'data_files' : [("share/man/man1", ["doc/mixminion.1"]),
("share/man/man5", ["doc/mixminionrc.5",
"doc/mixminiond.conf.5"]),
("share/man/man8", ["doc/mixminiond.8"])]
}
else:
EXTRA = {}
setup(name='Mixminion',
version=VERSION,
license="MIT",
description=
"Mixminion: Python implementation of the Type III Mix protocol (ALPHA)",
author="Nick Mathewson",
author_email="nickm@freehaven.net",
url="http://www.mixminion.net/",
package_dir={ '' : 'lib' },
packages=['mixminion', 'mixminion.server', 'mixminion.directory'],
scripts=SCRIPTS,
ext_modules=[extmodule],
cmdclass={'run': runMMCommand},
**EXTRA
)
try:
for s in SCRIPTS:
os.unlink(s)
except:
pass