17db96d56Sopenharmony_ci#! /usr/bin/env python3
27db96d56Sopenharmony_ci# Script for preparing OpenSSL for building on Windows.
37db96d56Sopenharmony_ci# Uses Perl to create nmake makefiles and otherwise prepare the way
47db96d56Sopenharmony_ci# for building on 32 or 64 bit platforms.
57db96d56Sopenharmony_ci
67db96d56Sopenharmony_ci# Script originally authored by Mark Hammond.
77db96d56Sopenharmony_ci# Major revisions by:
87db96d56Sopenharmony_ci#   Martin v. Löwis
97db96d56Sopenharmony_ci#   Christian Heimes
107db96d56Sopenharmony_ci#   Zachary Ware
117db96d56Sopenharmony_ci
127db96d56Sopenharmony_ci# THEORETICALLY, you can:
137db96d56Sopenharmony_ci# * Unpack the latest OpenSSL release where $(opensslDir) in
147db96d56Sopenharmony_ci#   PCbuild\pyproject.props expects it to be.
157db96d56Sopenharmony_ci# * Install ActivePerl and ensure it is somewhere on your path.
167db96d56Sopenharmony_ci# * Run this script with the OpenSSL source dir as the only argument.
177db96d56Sopenharmony_ci#
187db96d56Sopenharmony_ci# it should configure OpenSSL such that it is ready to be built by
197db96d56Sopenharmony_ci# ssl.vcxproj on 32 or 64 bit platforms.
207db96d56Sopenharmony_ci
217db96d56Sopenharmony_cifrom __future__ import print_function
227db96d56Sopenharmony_ci
237db96d56Sopenharmony_ciimport os
247db96d56Sopenharmony_ciimport re
257db96d56Sopenharmony_ciimport sys
267db96d56Sopenharmony_ciimport subprocess
277db96d56Sopenharmony_cifrom shutil import copy
287db96d56Sopenharmony_ci
297db96d56Sopenharmony_ci# Find all "foo.exe" files on the PATH.
307db96d56Sopenharmony_cidef find_all_on_path(filename, extras=None):
317db96d56Sopenharmony_ci    entries = os.environ["PATH"].split(os.pathsep)
327db96d56Sopenharmony_ci    ret = []
337db96d56Sopenharmony_ci    for p in entries:
347db96d56Sopenharmony_ci        fname = os.path.abspath(os.path.join(p, filename))
357db96d56Sopenharmony_ci        if os.path.isfile(fname) and fname not in ret:
367db96d56Sopenharmony_ci            ret.append(fname)
377db96d56Sopenharmony_ci    if extras:
387db96d56Sopenharmony_ci        for p in extras:
397db96d56Sopenharmony_ci            fname = os.path.abspath(os.path.join(p, filename))
407db96d56Sopenharmony_ci            if os.path.isfile(fname) and fname not in ret:
417db96d56Sopenharmony_ci                ret.append(fname)
427db96d56Sopenharmony_ci    return ret
437db96d56Sopenharmony_ci
447db96d56Sopenharmony_ci
457db96d56Sopenharmony_ci# Find a suitable Perl installation for OpenSSL.
467db96d56Sopenharmony_ci# cygwin perl does *not* work.  ActivePerl does.
477db96d56Sopenharmony_ci# Being a Perl dummy, the simplest way I can check is if the "Win32" package
487db96d56Sopenharmony_ci# is available.
497db96d56Sopenharmony_cidef find_working_perl(perls):
507db96d56Sopenharmony_ci    for perl in perls:
517db96d56Sopenharmony_ci        try:
527db96d56Sopenharmony_ci            subprocess.check_output([perl, "-e", "use Win32;"])
537db96d56Sopenharmony_ci        except subprocess.CalledProcessError:
547db96d56Sopenharmony_ci            continue
557db96d56Sopenharmony_ci        else:
567db96d56Sopenharmony_ci            return perl
577db96d56Sopenharmony_ci
587db96d56Sopenharmony_ci    if perls:
597db96d56Sopenharmony_ci        print("The following perl interpreters were found:")
607db96d56Sopenharmony_ci        for p in perls:
617db96d56Sopenharmony_ci            print(" ", p)
627db96d56Sopenharmony_ci        print(" None of these versions appear suitable for building OpenSSL")
637db96d56Sopenharmony_ci    else:
647db96d56Sopenharmony_ci        print("NO perl interpreters were found on this machine at all!")
657db96d56Sopenharmony_ci    print(" Please install ActivePerl and ensure it appears on your path")
667db96d56Sopenharmony_ci
677db96d56Sopenharmony_ci
687db96d56Sopenharmony_cidef copy_includes(makefile, suffix):
697db96d56Sopenharmony_ci    dir = 'inc'+suffix+'\\openssl'
707db96d56Sopenharmony_ci    try:
717db96d56Sopenharmony_ci        os.makedirs(dir)
727db96d56Sopenharmony_ci    except OSError:
737db96d56Sopenharmony_ci        pass
747db96d56Sopenharmony_ci    copy_if_different = r'$(PERL) $(SRC_D)\util\copy-if-different.pl'
757db96d56Sopenharmony_ci    with open(makefile) as fin:
767db96d56Sopenharmony_ci        for line in fin:
777db96d56Sopenharmony_ci            if copy_if_different in line:
787db96d56Sopenharmony_ci                perl, script, src, dest = line.split()
797db96d56Sopenharmony_ci                if not '$(INCO_D)' in dest:
807db96d56Sopenharmony_ci                    continue
817db96d56Sopenharmony_ci                # We're in the root of the source tree
827db96d56Sopenharmony_ci                src = src.replace('$(SRC_D)', '.').strip('"')
837db96d56Sopenharmony_ci                dest = dest.strip('"').replace('$(INCO_D)', dir)
847db96d56Sopenharmony_ci                print('copying', src, 'to', dest)
857db96d56Sopenharmony_ci                copy(src, dest)
867db96d56Sopenharmony_ci
877db96d56Sopenharmony_ci
887db96d56Sopenharmony_cidef run_configure(configure, do_script):
897db96d56Sopenharmony_ci    print("perl Configure "+configure+" no-idea no-mdc2")
907db96d56Sopenharmony_ci    os.system("perl Configure "+configure+" no-idea no-mdc2")
917db96d56Sopenharmony_ci    print(do_script)
927db96d56Sopenharmony_ci    os.system(do_script)
937db96d56Sopenharmony_ci
947db96d56Sopenharmony_cidef fix_uplink():
957db96d56Sopenharmony_ci    # uplink.c tries to find the OPENSSL_Applink function exported from the current
967db96d56Sopenharmony_ci    # executable. However, we export it from _ssl[_d].pyd instead. So we update the
977db96d56Sopenharmony_ci    # module name here before building.
987db96d56Sopenharmony_ci    with open('ms\\uplink.c', 'r', encoding='utf-8') as f1:
997db96d56Sopenharmony_ci        code = list(f1)
1007db96d56Sopenharmony_ci    os.replace('ms\\uplink.c', 'ms\\uplink.c.orig')
1017db96d56Sopenharmony_ci    already_patched = False
1027db96d56Sopenharmony_ci    with open('ms\\uplink.c', 'w', encoding='utf-8') as f2:
1037db96d56Sopenharmony_ci        for line in code:
1047db96d56Sopenharmony_ci            if not already_patched:
1057db96d56Sopenharmony_ci                if re.search('MODIFIED FOR CPYTHON _ssl MODULE', line):
1067db96d56Sopenharmony_ci                    already_patched = True
1077db96d56Sopenharmony_ci                elif re.match(r'^\s+if\s*\(\(h\s*=\s*GetModuleHandle[AW]?\(NULL\)\)\s*==\s*NULL\)', line):
1087db96d56Sopenharmony_ci                    f2.write("/* MODIFIED FOR CPYTHON _ssl MODULE */\n")
1097db96d56Sopenharmony_ci                    f2.write('if ((h = GetModuleHandleW(L"_ssl.pyd")) == NULL) if ((h = GetModuleHandleW(L"_ssl_d.pyd")) == NULL)\n')
1107db96d56Sopenharmony_ci                    already_patched = True
1117db96d56Sopenharmony_ci            f2.write(line)
1127db96d56Sopenharmony_ci    if not already_patched:
1137db96d56Sopenharmony_ci        print("WARN: failed to patch ms\\uplink.c")
1147db96d56Sopenharmony_ci
1157db96d56Sopenharmony_cidef prep(arch):
1167db96d56Sopenharmony_ci    makefile_template = "ms\\ntdll{}.mak"
1177db96d56Sopenharmony_ci    generated_makefile = makefile_template.format('')
1187db96d56Sopenharmony_ci    if arch == "x86":
1197db96d56Sopenharmony_ci        configure = "VC-WIN32"
1207db96d56Sopenharmony_ci        do_script = "ms\\do_nasm"
1217db96d56Sopenharmony_ci        suffix = "32"
1227db96d56Sopenharmony_ci    elif arch == "amd64":
1237db96d56Sopenharmony_ci        configure = "VC-WIN64A"
1247db96d56Sopenharmony_ci        do_script = "ms\\do_win64a"
1257db96d56Sopenharmony_ci        suffix = "64"
1267db96d56Sopenharmony_ci    else:
1277db96d56Sopenharmony_ci        raise ValueError('Unrecognized platform: %s' % arch)
1287db96d56Sopenharmony_ci
1297db96d56Sopenharmony_ci    print("Creating the makefiles...")
1307db96d56Sopenharmony_ci    sys.stdout.flush()
1317db96d56Sopenharmony_ci    # run configure, copy includes, patch files
1327db96d56Sopenharmony_ci    run_configure(configure, do_script)
1337db96d56Sopenharmony_ci    makefile = makefile_template.format(suffix)
1347db96d56Sopenharmony_ci    try:
1357db96d56Sopenharmony_ci        os.unlink(makefile)
1367db96d56Sopenharmony_ci    except FileNotFoundError:
1377db96d56Sopenharmony_ci        pass
1387db96d56Sopenharmony_ci    os.rename(generated_makefile, makefile)
1397db96d56Sopenharmony_ci    copy_includes(makefile, suffix)
1407db96d56Sopenharmony_ci
1417db96d56Sopenharmony_ci    print('patching ms\\uplink.c...')
1427db96d56Sopenharmony_ci    fix_uplink()
1437db96d56Sopenharmony_ci
1447db96d56Sopenharmony_cidef main():
1457db96d56Sopenharmony_ci    if len(sys.argv) == 1:
1467db96d56Sopenharmony_ci        print("Not enough arguments: directory containing OpenSSL",
1477db96d56Sopenharmony_ci              "sources must be supplied")
1487db96d56Sopenharmony_ci        sys.exit(1)
1497db96d56Sopenharmony_ci
1507db96d56Sopenharmony_ci    if len(sys.argv) == 3 and sys.argv[2] not in ('x86', 'amd64'):
1517db96d56Sopenharmony_ci        print("Second argument must be x86 or amd64")
1527db96d56Sopenharmony_ci        sys.exit(1)
1537db96d56Sopenharmony_ci
1547db96d56Sopenharmony_ci    if len(sys.argv) > 3:
1557db96d56Sopenharmony_ci        print("Too many arguments supplied, all we need is the directory",
1567db96d56Sopenharmony_ci              "containing OpenSSL sources and optionally the architecture")
1577db96d56Sopenharmony_ci        sys.exit(1)
1587db96d56Sopenharmony_ci
1597db96d56Sopenharmony_ci    ssl_dir = sys.argv[1]
1607db96d56Sopenharmony_ci    arch = sys.argv[2] if len(sys.argv) >= 3 else None
1617db96d56Sopenharmony_ci
1627db96d56Sopenharmony_ci    if not os.path.isdir(ssl_dir):
1637db96d56Sopenharmony_ci        print(ssl_dir, "is not an existing directory!")
1647db96d56Sopenharmony_ci        sys.exit(1)
1657db96d56Sopenharmony_ci
1667db96d56Sopenharmony_ci    # perl should be on the path, but we also look in "\perl" and "c:\\perl"
1677db96d56Sopenharmony_ci    # as "well known" locations
1687db96d56Sopenharmony_ci    perls = find_all_on_path("perl.exe", [r"\perl\bin",
1697db96d56Sopenharmony_ci                                          r"C:\perl\bin",
1707db96d56Sopenharmony_ci                                          r"\perl64\bin",
1717db96d56Sopenharmony_ci                                          r"C:\perl64\bin",
1727db96d56Sopenharmony_ci                                         ])
1737db96d56Sopenharmony_ci    perl = find_working_perl(perls)
1747db96d56Sopenharmony_ci    if perl:
1757db96d56Sopenharmony_ci        print("Found a working perl at '%s'" % (perl,))
1767db96d56Sopenharmony_ci    else:
1777db96d56Sopenharmony_ci        sys.exit(1)
1787db96d56Sopenharmony_ci    if not find_all_on_path('nmake.exe'):
1797db96d56Sopenharmony_ci        print('Could not find nmake.exe, try running env.bat')
1807db96d56Sopenharmony_ci        sys.exit(1)
1817db96d56Sopenharmony_ci    if not find_all_on_path('nasm.exe'):
1827db96d56Sopenharmony_ci        print('Could not find nasm.exe, please add to PATH')
1837db96d56Sopenharmony_ci        sys.exit(1)
1847db96d56Sopenharmony_ci    sys.stdout.flush()
1857db96d56Sopenharmony_ci
1867db96d56Sopenharmony_ci    # Put our working Perl at the front of our path
1877db96d56Sopenharmony_ci    os.environ["PATH"] = os.path.dirname(perl) + \
1887db96d56Sopenharmony_ci                                os.pathsep + \
1897db96d56Sopenharmony_ci                                os.environ["PATH"]
1907db96d56Sopenharmony_ci
1917db96d56Sopenharmony_ci    old_cwd = os.getcwd()
1927db96d56Sopenharmony_ci    try:
1937db96d56Sopenharmony_ci        os.chdir(ssl_dir)
1947db96d56Sopenharmony_ci        if arch:
1957db96d56Sopenharmony_ci            prep(arch)
1967db96d56Sopenharmony_ci        else:
1977db96d56Sopenharmony_ci            for arch in ['amd64', 'x86']:
1987db96d56Sopenharmony_ci                prep(arch)
1997db96d56Sopenharmony_ci    finally:
2007db96d56Sopenharmony_ci        os.chdir(old_cwd)
2017db96d56Sopenharmony_ci
2027db96d56Sopenharmony_ciif __name__=='__main__':
2037db96d56Sopenharmony_ci    main()
204