Home | History | Annotate | Download | only in VS8.0
      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 
     26 import os, sys, re, shutil
     27 
     28 # Find all "foo.exe" files on the PATH.
     29 def 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.
     47 def 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.
     67 def 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 
     98 def 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 
    119 def 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 
    143 def 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 
    149 def 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 
    249 if __name__=='__main__':
    250     main()
    251