1# Script for building the _ssl and _hashlib modules for Windows. 2# Uses Perl to setup the OpenSSL environment correctly 3# and build OpenSSL, then invokes a simple nmake session 4# for the actual _ssl.pyd and _hashlib.pyd DLLs. 5 6# THEORETICALLY, you can: 7# * Unpack the latest SSL release one level above your main Python source 8# directory. It is likely you will already find the zlib library and 9# any other external packages there. 10# * Install ActivePerl and ensure it is somewhere on your path. 11# * Run this script from the PC/VS8.0 directory. 12# 13# it should configure and build SSL, then build the _ssl and _hashlib 14# Python extensions without intervention. 15 16# Modified by Christian Heimes 17# Now this script supports pre-generated makefiles and assembly files. 18# Developers don't need an installation of Perl anymore to build Python. A svn 19# checkout from our svn repository is enough. 20# 21# In Order to create the files in the case of an update you still need Perl. 22# Run build_ssl in this order: 23# python.exe build_ssl.py Release x64 24# python.exe build_ssl.py Release Win32 25 26import os, sys, re, shutil 27 28# Find all "foo.exe" files on the PATH. 29def find_all_on_path(filename, extras = None): 30 entries = os.environ["PATH"].split(os.pathsep) 31 ret = [] 32 for p in entries: 33 fname = os.path.abspath(os.path.join(p, filename)) 34 if os.path.isfile(fname) and fname not in ret: 35 ret.append(fname) 36 if extras: 37 for p in extras: 38 fname = os.path.abspath(os.path.join(p, filename)) 39 if os.path.isfile(fname) and fname not in ret: 40 ret.append(fname) 41 return ret 42 43# Find a suitable Perl installation for OpenSSL. 44# cygwin perl does *not* work. ActivePerl does. 45# Being a Perl dummy, the simplest way I can check is if the "Win32" package 46# is available. 47def find_working_perl(perls): 48 for perl in perls: 49 fh = os.popen('"%s" -e "use Win32;"' % perl) 50 fh.read() 51 rc = fh.close() 52 if rc: 53 continue 54 return perl 55 print("Can not find a suitable PERL:") 56 if perls: 57 print(" the following perl interpreters were found:") 58 for p in perls: 59 print(" ", p) 60 print(" None of these versions appear suitable for building OpenSSL") 61 else: 62 print(" NO perl interpreters were found on this machine at all!") 63 print(" Please install ActivePerl and ensure it appears on your path") 64 return None 65 66# Locate the best SSL directory given a few roots to look into. 67def find_best_ssl_dir(sources): 68 candidates = [] 69 for s in sources: 70 try: 71 # note: do not abspath s; the build will fail if any 72 # higher up directory name has spaces in it. 73 fnames = os.listdir(s) 74 except os.error: 75 fnames = [] 76 for fname in fnames: 77 fqn = os.path.join(s, fname) 78 if os.path.isdir(fqn) and fname.startswith("openssl-"): 79 candidates.append(fqn) 80 # Now we have all the candidates, locate the best. 81 best_parts = [] 82 best_name = None 83 for c in candidates: 84 parts = re.split("[.-]", os.path.basename(c))[1:] 85 # eg - openssl-0.9.7-beta1 - ignore all "beta" or any other qualifiers 86 if len(parts) >= 4: 87 continue 88 if parts > best_parts: 89 best_parts = parts 90 best_name = c 91 if best_name is not None: 92 print("Found an SSL directory at '%s'" % (best_name,)) 93 else: 94 print("Could not find an SSL directory in '%s'" % (sources,)) 95 sys.stdout.flush() 96 return best_name 97 98def create_makefile64(makefile, m32): 99 """Create and fix makefile for 64bit 100 101 Replace 32 with 64bit directories 102 """ 103 if not os.path.isfile(m32): 104 return 105 with open(m32) as fin: 106 with open(makefile, 'w') as fout: 107 for line in fin: 108 line = line.replace("=tmp32", "=tmp64") 109 line = line.replace("=out32", "=out64") 110 line = line.replace("=inc32", "=inc64") 111 # force 64 bit machine 112 line = line.replace("MKLIB=lib", "MKLIB=lib /MACHINE:X64") 113 line = line.replace("LFLAGS=", "LFLAGS=/MACHINE:X64 ") 114 # don't link against the lib on 64bit systems 115 line = line.replace("bufferoverflowu.lib", "") 116 fout.write(line) 117 os.unlink(m32) 118 119def fix_makefile(makefile): 120 """Fix some stuff in all makefiles 121 """ 122 if not os.path.isfile(makefile): 123 return 124 with open(makefile) as fin: 125 lines = fin.readlines() 126 with open(makefile, 'w') as fout: 127 for line in lines: 128 if line.startswith("PERL="): 129 continue 130 if line.startswith("CP="): 131 line = "CP=copy\n" 132 if line.startswith("MKDIR="): 133 line = "MKDIR=mkdir\n" 134 if line.startswith("CFLAG="): 135 line = line.strip() 136 for algo in ("RC5", "MDC2", "IDEA"): 137 noalgo = " -DOPENSSL_NO_%s" % algo 138 if noalgo not in line: 139 line = line + noalgo 140 line = line + '\n' 141 fout.write(line) 142 143def run_configure(configure, do_script): 144 print("perl Configure "+configure) 145 os.system("perl Configure "+configure) 146 print(do_script) 147 os.system(do_script) 148 149def main(): 150 build_all = "-a" in sys.argv 151 if sys.argv[1] == "Release": 152 debug = False 153 elif sys.argv[1] == "Debug": 154 debug = True 155 else: 156 raise ValueError(str(sys.argv)) 157 158 if sys.argv[2] == "Win32": 159 arch = "x86" 160 configure = "VC-WIN32" 161 do_script = "ms\\do_nasm" 162 makefile="ms\\nt.mak" 163 m32 = makefile 164 elif sys.argv[2] == "x64": 165 arch="amd64" 166 configure = "VC-WIN64A" 167 do_script = "ms\\do_win64a" 168 makefile = "ms\\nt64.mak" 169 m32 = makefile.replace('64', '') 170 #os.environ["VSEXTCOMP_USECL"] = "MS_OPTERON" 171 else: 172 raise ValueError(str(sys.argv)) 173 174 make_flags = "" 175 if build_all: 176 make_flags = "-a" 177 # perl should be on the path, but we also look in "\perl" and "c:\\perl" 178 # as "well known" locations 179 perls = find_all_on_path("perl.exe", ["\\perl\\bin", "C:\\perl\\bin"]) 180 perl = find_working_perl(perls) 181 if perl: 182 print("Found a working perl at '%s'" % (perl,)) 183 else: 184 print("No Perl installation was found. Existing Makefiles are used.") 185 sys.stdout.flush() 186 # Look for SSL 3 levels up from PC/VS8.0 - ie, same place zlib etc all live. 187 ssl_dir = find_best_ssl_dir(("..\\..\\..",)) 188 if ssl_dir is None: 189 sys.exit(1) 190 191 old_cd = os.getcwd() 192 try: 193 os.chdir(ssl_dir) 194 # rebuild makefile when we do the role over from 32 to 64 build 195 if arch == "amd64" and os.path.isfile(m32) and not os.path.isfile(makefile): 196 os.unlink(m32) 197 198 # If the ssl makefiles do not exist, we invoke Perl to generate them. 199 # Due to a bug in this script, the makefile sometimes ended up empty 200 # Force a regeneration if it is. 201 if not os.path.isfile(makefile) or os.path.getsize(makefile)==0: 202 if perl is None: 203 print("Perl is required to build the makefiles!") 204 sys.exit(1) 205 206 print("Creating the makefiles...") 207 sys.stdout.flush() 208 # Put our working Perl at the front of our path 209 os.environ["PATH"] = os.path.dirname(perl) + \ 210 os.pathsep + \ 211 os.environ["PATH"] 212 run_configure(configure, do_script) 213 if debug: 214 print("OpenSSL debug builds aren't supported.") 215 #if arch=="x86" and debug: 216 # # the do_masm script in openssl doesn't generate a debug 217 # # build makefile so we generate it here: 218 # os.system("perl util\mk1mf.pl debug "+configure+" >"+makefile) 219 220 if arch == "amd64": 221 create_makefile64(makefile, m32) 222 fix_makefile(makefile) 223 shutil.copy(r"crypto\buildinf.h", r"crypto\buildinf_%s.h" % arch) 224 shutil.copy(r"crypto\opensslconf.h", r"crypto\opensslconf_%s.h" % arch) 225 226 # Now run make. 227 if arch == "amd64": 228 rc = os.system(r"ml64 -c -Foms\uptable.obj ms\uptable.asm") 229 if rc: 230 print("ml64 assembler has failed.") 231 sys.exit(rc) 232 233 shutil.copy(r"crypto\buildinf_%s.h" % arch, r"crypto\buildinf.h") 234 shutil.copy(r"crypto\opensslconf_%s.h" % arch, r"crypto\opensslconf.h") 235 236 #makeCommand = "nmake /nologo PERL=\"%s\" -f \"%s\"" %(perl, makefile) 237 makeCommand = "nmake /nologo -f \"%s\"" % makefile 238 print("Executing ssl makefiles:", makeCommand) 239 sys.stdout.flush() 240 rc = os.system(makeCommand) 241 if rc: 242 print("Executing "+makefile+" failed") 243 print(rc) 244 sys.exit(rc) 245 finally: 246 os.chdir(old_cd) 247 sys.exit(rc) 248 249if __name__=='__main__': 250 main() 251