| # Script for building the _ssl and _hashlib modules for Windows. |
| # Uses Perl to setup the OpenSSL environment correctly |
| # and build OpenSSL, then invokes a simple nmake session |
| # for the actual _ssl.pyd and _hashlib.pyd DLLs. |
| |
| # THEORETICALLY, you can: |
| # * Unpack the latest SSL release one level above your main Python source |
| # directory. It is likely you will already find the zlib library and |
| # any other external packages there. |
| # * Install ActivePerl and ensure it is somewhere on your path. |
| # * Run this script from the PCBuild directory. |
| # |
| # it should configure and build SSL, then build the _ssl and _hashlib |
| # Python extensions without intervention. |
| |
| # Modified by Christian Heimes |
| # Now this script supports pre-generated makefiles and assembly files. |
| # Developers don't need an installation of Perl anymore to build Python. A svn |
| # checkout from our svn repository is enough. |
| # |
| # In Order to create the files in the case of an update you still need Perl. |
| # Run build_ssl in this order: |
| # python.exe build_ssl.py Release x64 |
| # python.exe build_ssl.py Release Win32 |
| |
| from __future__ import with_statement, print_function |
| import os |
| import re |
| import sys |
| import time |
| import subprocess |
| from shutil import copy |
| from distutils import log |
| from distutils.spawn import find_executable |
| from distutils.file_util import copy_file |
| from distutils.sysconfig import parse_makefile, expand_makefile_vars |
| |
| # The mk1mf.pl output filename template |
| # !!! This must match what is used in prepare_ssl.py |
| MK1MF_FMT = 'ms\\nt{}.mak' |
| |
| # The header files output directory name template |
| # !!! This must match what is used in prepare_ssl.py |
| INCLUDE_FMT = 'include{}' |
| |
| # Fetch all the directory definitions from VC properties |
| def get_project_properties(propfile): |
| macro_pattern = r'<UserMacro\s+Name="([^"]+?)"\s+Value="([^"]*?)"\s*/>' |
| with open(propfile) as fin: |
| items = re.findall(macro_pattern, fin.read(), re.MULTILINE) |
| props = dict(items) |
| for name, value in items: |
| try: |
| props[name] = expand_makefile_vars(value, props) |
| except TypeError: |
| # value contains undefined variable reference, drop it |
| del props[name] |
| return props |
| |
| |
| _variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)") |
| def fix_makefile(makefile, platform_makefile, suffix): |
| """Fix some stuff in all makefiles |
| """ |
| subs = { |
| 'PERL': 'rem', # just in case |
| 'CP': 'copy', |
| 'MKDIR': 'mkdir', |
| 'OUT_D': 'out' + suffix, |
| 'TMP_D': 'tmp' + suffix, |
| 'INC_D': INCLUDE_FMT.format(suffix), |
| 'INCO_D': '$(INC_D)\\openssl', |
| } |
| with open(platform_makefile) as fin, open(makefile, 'w') as fout: |
| for line in fin: |
| m = _variable_rx.match(line) |
| if m: |
| name = m.group(1) |
| if name in subs: |
| line = '%s=%s\n' % (name, subs[name]) |
| fout.write(line) |
| |
| |
| _copy_rx = re.compile(r'\t\$\(PERL\) ' |
| r'\$\(SRC_D\)\\util\\copy-if-different.pl ' |
| r'"([^"]+)"\s+"([^"]+)"') |
| def copy_files(makefile, makevars): |
| # Create the destination directories (see 'init' rule in nt.dll) |
| for varname in ('TMP_D', 'LIB_D', 'INC_D', 'INCO_D'): |
| dirname = makevars[varname] |
| if not os.path.isdir(dirname): |
| os.mkdir(dirname) |
| # Process the just local library headers (HEADER) as installed headers |
| # (EXHEADER) are handled by prepare_ssl.py (see 'headers' rule in nt.dll) |
| headers = set(makevars['HEADER'].split()) |
| with open(makefile) as fin: |
| for line in fin: |
| m = _copy_rx.match(line) |
| if m: |
| src, dst = m.groups() |
| src = expand_makefile_vars(src, makevars) |
| dst = expand_makefile_vars(dst, makevars) |
| if dst in headers: |
| copy_file(src, dst, preserve_times=False, update=True) |
| |
| |
| # Update buildinf.h for the build platform. |
| def fix_buildinf(makevars): |
| platform_cpp_symbol = 'MK1MF_PLATFORM_' |
| platform_cpp_symbol += makevars['PLATFORM'].replace('-', '_') |
| fn = expand_makefile_vars('$(INCL_D)\\buildinf.h', makevars) |
| with open(fn, 'w') as f: |
| # sanity check |
| f.write(('#ifndef {}\n' |
| ' #error "Windows build (PLATFORM={PLATFORM}) only"\n' |
| '#endif\n').format(platform_cpp_symbol, **makevars)) |
| buildinf = ( |
| '#define CFLAGS "compiler: cl {CFLAG}"\n' |
| '#define PLATFORM "{PLATFORM}"\n' |
| '#define DATE "{}"\n' |
| ).format(time.asctime(time.gmtime()), |
| **makevars) |
| f.write(buildinf) |
| print('Updating buildinf:') |
| print(buildinf) |
| sys.stdout.flush() |
| |
| |
| def main(): |
| if sys.argv[1] == "Debug": |
| print("OpenSSL debug builds aren't supported.") |
| elif sys.argv[1] != "Release": |
| raise ValueError('Unrecognized configuration: %s' % sys.argv[1]) |
| |
| if sys.argv[2] == "Win32": |
| platform = "VC-WIN32" |
| suffix = '32' |
| elif sys.argv[2] == "x64": |
| platform = "VC-WIN64A" |
| suffix = '64' |
| else: |
| raise ValueError('Unrecognized platform: %s' % sys.argv[2]) |
| |
| # Have the distutils functions display information output |
| log.set_verbosity(1) |
| |
| # Use the same properties that are used in the VS projects |
| solution_dir = os.path.dirname(__file__) |
| propfile = os.path.join(solution_dir, 'pyproject.vsprops') |
| props = get_project_properties(propfile) |
| |
| # Ensure we have the necessary external depenedencies |
| ssl_dir = os.path.join(solution_dir, props['opensslDir']) |
| if not os.path.isdir(ssl_dir): |
| print("Could not find the OpenSSL sources, try running " |
| "'build.bat -e'") |
| sys.exit(1) |
| |
| # Ensure the executables used herein are available. |
| if not find_executable('nmake.exe'): |
| print('Could not find nmake.exe, try running env.bat') |
| sys.exit(1) |
| |
| # add our copy of NASM to PATH. It will be on the same level as openssl |
| externals_dir = os.path.join(solution_dir, props['externalsDir']) |
| for dir in os.listdir(externals_dir): |
| if dir.startswith('nasm'): |
| nasm_dir = os.path.join(externals_dir, dir) |
| nasm_dir = os.path.abspath(nasm_dir) |
| old_path = os.environ['PATH'] |
| os.environ['PATH'] = os.pathsep.join([nasm_dir, old_path]) |
| break |
| else: |
| if not find_executable('nasm.exe'): |
| print('Could not find nasm.exe, please add to PATH') |
| sys.exit(1) |
| |
| # If the ssl makefiles do not exist, we invoke PCbuild/prepare_ssl.py |
| # to generate them. |
| platform_makefile = MK1MF_FMT.format(suffix) |
| if not os.path.isfile(os.path.join(ssl_dir, platform_makefile)): |
| pcbuild_dir = os.path.join(os.path.dirname(externals_dir), 'PCbuild') |
| prepare_ssl = os.path.join(pcbuild_dir, 'prepare_ssl.py') |
| rc = subprocess.call([sys.executable, prepare_ssl, ssl_dir]) |
| if rc: |
| print('Executing', prepare_ssl, 'failed (error %d)' % rc) |
| sys.exit(rc) |
| |
| old_cd = os.getcwd() |
| try: |
| os.chdir(ssl_dir) |
| |
| # Get the variables defined in the current makefile, if it exists. |
| makefile = MK1MF_FMT.format('') |
| try: |
| makevars = parse_makefile(makefile) |
| except EnvironmentError: |
| makevars = {'PLATFORM': None} |
| |
| # Rebuild the makefile when building for different a platform than |
| # the last run. |
| if makevars['PLATFORM'] != platform: |
| print("Updating the makefile...") |
| sys.stdout.flush() |
| # Firstly, apply the changes for the platform makefile into |
| # a temporary file to prevent any errors from this script |
| # causing false positives on subsequent runs. |
| new_makefile = makefile + '.new' |
| fix_makefile(new_makefile, platform_makefile, suffix) |
| makevars = parse_makefile(new_makefile) |
| |
| # Secondly, perform the make recipes that use Perl |
| copy_files(new_makefile, makevars) |
| |
| # Set our build information in buildinf.h. |
| # XXX: This isn't needed for a properly "prepared" SSL, but |
| # it fixes the current checked-in external (as of 2017-05). |
| fix_buildinf(makevars) |
| |
| # Finally, move the temporary file to its real destination. |
| if os.path.exists(makefile): |
| os.remove(makefile) |
| os.rename(new_makefile, makefile) |
| |
| # Now run make. |
| makeCommand = "nmake /nologo /f \"%s\" lib" % makefile |
| print("Executing ssl makefiles:", makeCommand) |
| sys.stdout.flush() |
| rc = os.system(makeCommand) |
| if rc: |
| print("Executing", makefile, "failed (error %d)" % rc) |
| sys.exit(rc) |
| finally: |
| os.chdir(old_cd) |
| sys.exit(rc) |
| |
| if __name__=='__main__': |
| main() |