blob: eb0b4c95189fd20a9ade68cc29fe0b27ac981304 [file] [log] [blame]
Christian Heimese8954f82007-11-22 11:21:16 +00001# 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 PCBuild directory.
12#
13# it should configure and build SSL, then build the _ssl and _hashlib
14# Python extensions without intervention.
15
Christian Heimes23361112007-11-23 07:05:03 +000016# 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
Jeremy Klothebbccea2017-06-20 14:53:39 -060026from __future__ import with_statement, print_function
27import os
28import re
29import sys
30import time
Zachary Ware14269d92016-11-07 13:29:07 -060031import subprocess
Jeremy Klothebbccea2017-06-20 14:53:39 -060032from shutil import copy
33from distutils import log
34from distutils.spawn import find_executable
35from distutils.file_util import copy_file
36from distutils.sysconfig import parse_makefile, expand_makefile_vars
Christian Heimese8954f82007-11-22 11:21:16 +000037
Jeremy Klothebbccea2017-06-20 14:53:39 -060038# The mk1mf.pl output filename template
39# !!! This must match what is used in prepare_ssl.py
40MK1MF_FMT = 'ms\\nt{}.mak'
Christian Heimese8954f82007-11-22 11:21:16 +000041
Jeremy Klothebbccea2017-06-20 14:53:39 -060042# The header files output directory name template
43# !!! This must match what is used in prepare_ssl.py
44INCLUDE_FMT = 'include{}'
45
46# Fetch all the directory definitions from VC properties
47def get_project_properties(propfile):
48 macro_pattern = r'<UserMacro\s+Name="([^"]+?)"\s+Value="([^"]*?)"\s*/>'
49 with open(propfile) as fin:
50 items = re.findall(macro_pattern, fin.read(), re.MULTILINE)
51 props = dict(items)
52 for name, value in items:
Zachary Ware14269d92016-11-07 13:29:07 -060053 try:
Jeremy Klothebbccea2017-06-20 14:53:39 -060054 props[name] = expand_makefile_vars(value, props)
55 except TypeError:
56 # value contains undefined variable reference, drop it
57 del props[name]
58 return props
Martin v. Löwis0fc2b742012-05-18 13:58:30 +020059
Christian Heimese8954f82007-11-22 11:21:16 +000060
Jeremy Klothebbccea2017-06-20 14:53:39 -060061_variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
62def fix_makefile(makefile, platform_makefile, suffix):
Christian Heimes23361112007-11-23 07:05:03 +000063 """Fix some stuff in all makefiles
64 """
Jeremy Klothebbccea2017-06-20 14:53:39 -060065 subs = {
66 'PERL': 'rem', # just in case
67 'CP': 'copy',
68 'MKDIR': 'mkdir',
69 'OUT_D': 'out' + suffix,
70 'TMP_D': 'tmp' + suffix,
71 'INC_D': INCLUDE_FMT.format(suffix),
72 'INCO_D': '$(INC_D)\\openssl',
73 }
74 with open(platform_makefile) as fin, open(makefile, 'w') as fout:
75 for line in fin:
76 m = _variable_rx.match(line)
77 if m:
78 name = m.group(1)
79 if name in subs:
80 line = '%s=%s\n' % (name, subs[name])
Christian Heimes23361112007-11-23 07:05:03 +000081 fout.write(line)
82
Jeremy Klothebbccea2017-06-20 14:53:39 -060083
84_copy_rx = re.compile(r'\t\$\(PERL\) '
85 r'\$\(SRC_D\)\\util\\copy-if-different.pl '
86 r'"([^"]+)"\s+"([^"]+)"')
87def copy_files(makefile, makevars):
88 # Create the destination directories (see 'init' rule in nt.dll)
89 for varname in ('TMP_D', 'LIB_D', 'INC_D', 'INCO_D'):
90 dirname = makevars[varname]
91 if not os.path.isdir(dirname):
92 os.mkdir(dirname)
93 # Process the just local library headers (HEADER) as installed headers
94 # (EXHEADER) are handled by prepare_ssl.py (see 'headers' rule in nt.dll)
95 headers = set(makevars['HEADER'].split())
96 with open(makefile) as fin:
97 for line in fin:
98 m = _copy_rx.match(line)
99 if m:
100 src, dst = m.groups()
101 src = expand_makefile_vars(src, makevars)
102 dst = expand_makefile_vars(dst, makevars)
103 if dst in headers:
104 copy_file(src, dst, preserve_times=False, update=True)
105
106
107# Update buildinf.h for the build platform.
108def fix_buildinf(makevars):
109 platform_cpp_symbol = 'MK1MF_PLATFORM_'
110 platform_cpp_symbol += makevars['PLATFORM'].replace('-', '_')
111 fn = expand_makefile_vars('$(INCL_D)\\buildinf.h', makevars)
112 with open(fn, 'w') as f:
113 # sanity check
114 f.write(('#ifndef {}\n'
115 ' #error "Windows build (PLATFORM={PLATFORM}) only"\n'
116 '#endif\n').format(platform_cpp_symbol, **makevars))
117 buildinf = (
118 '#define CFLAGS "compiler: cl {CFLAG}"\n'
119 '#define PLATFORM "{PLATFORM}"\n'
120 '#define DATE "{}"\n'
121 ).format(time.asctime(time.gmtime()),
122 **makevars)
123 f.write(buildinf)
124 print('Updating buildinf:')
125 print(buildinf)
126 sys.stdout.flush()
127
Christian Heimese8954f82007-11-22 11:21:16 +0000128
129def main():
Jeremy Klothebbccea2017-06-20 14:53:39 -0600130 if sys.argv[1] == "Debug":
131 print("OpenSSL debug builds aren't supported.")
132 elif sys.argv[1] != "Release":
133 raise ValueError('Unrecognized configuration: %s' % sys.argv[1])
Christian Heimese8954f82007-11-22 11:21:16 +0000134
135 if sys.argv[2] == "Win32":
Jeremy Klothebbccea2017-06-20 14:53:39 -0600136 platform = "VC-WIN32"
137 suffix = '32'
Christian Heimese8954f82007-11-22 11:21:16 +0000138 elif sys.argv[2] == "x64":
Jeremy Klothebbccea2017-06-20 14:53:39 -0600139 platform = "VC-WIN64A"
140 suffix = '64'
Christian Heimese8954f82007-11-22 11:21:16 +0000141 else:
Jeremy Klothebbccea2017-06-20 14:53:39 -0600142 raise ValueError('Unrecognized platform: %s' % sys.argv[2])
Christian Heimese8954f82007-11-22 11:21:16 +0000143
Jeremy Klothebbccea2017-06-20 14:53:39 -0600144 # Have the distutils functions display information output
145 log.set_verbosity(1)
146
147 # Use the same properties that are used in the VS projects
148 solution_dir = os.path.dirname(__file__)
149 propfile = os.path.join(solution_dir, 'pyproject.vsprops')
150 props = get_project_properties(propfile)
151
152 # Ensure we have the necessary external depenedencies
153 ssl_dir = os.path.join(solution_dir, props['opensslDir'])
154 if not os.path.isdir(ssl_dir):
155 print("Could not find the OpenSSL sources, try running "
156 "'build.bat -e'")
157 sys.exit(1)
158
159 # Ensure the executables used herein are available.
160 if not find_executable('nmake.exe'):
161 print('Could not find nmake.exe, try running env.bat')
Christian Heimese8954f82007-11-22 11:21:16 +0000162 sys.exit(1)
163
Zachary Ware247b6442014-11-01 17:11:08 -0500164 # add our copy of NASM to PATH. It will be on the same level as openssl
Jeremy Klothebbccea2017-06-20 14:53:39 -0600165 externals_dir = os.path.join(solution_dir, props['externalsDir'])
166 for dir in os.listdir(externals_dir):
Zachary Ware247b6442014-11-01 17:11:08 -0500167 if dir.startswith('nasm'):
Jeremy Klothebbccea2017-06-20 14:53:39 -0600168 nasm_dir = os.path.join(externals_dir, dir)
Zachary Ware247b6442014-11-01 17:11:08 -0500169 nasm_dir = os.path.abspath(nasm_dir)
Zachary Warec26afcc2015-04-09 20:16:05 -0500170 old_path = os.environ['PATH']
171 os.environ['PATH'] = os.pathsep.join([nasm_dir, old_path])
Zachary Ware247b6442014-11-01 17:11:08 -0500172 break
173 else:
Jeremy Klothebbccea2017-06-20 14:53:39 -0600174 if not find_executable('nasm.exe'):
175 print('Could not find nasm.exe, please add to PATH')
176 sys.exit(1)
Zachary Ware247b6442014-11-01 17:11:08 -0500177
Jeremy Klothebbccea2017-06-20 14:53:39 -0600178 # If the ssl makefiles do not exist, we invoke PCbuild/prepare_ssl.py
179 # to generate them.
180 platform_makefile = MK1MF_FMT.format(suffix)
181 if not os.path.isfile(os.path.join(ssl_dir, platform_makefile)):
182 pcbuild_dir = os.path.join(os.path.dirname(externals_dir), 'PCbuild')
183 prepare_ssl = os.path.join(pcbuild_dir, 'prepare_ssl.py')
184 rc = subprocess.call([sys.executable, prepare_ssl, ssl_dir])
185 if rc:
186 print('Executing', prepare_ssl, 'failed (error %d)' % rc)
187 sys.exit(rc)
Zachary Ware247b6442014-11-01 17:11:08 -0500188
Christian Heimese8954f82007-11-22 11:21:16 +0000189 old_cd = os.getcwd()
190 try:
191 os.chdir(ssl_dir)
Christian Heimese8954f82007-11-22 11:21:16 +0000192
Jeremy Klothebbccea2017-06-20 14:53:39 -0600193 # Get the variables defined in the current makefile, if it exists.
194 makefile = MK1MF_FMT.format('')
195 try:
196 makevars = parse_makefile(makefile)
197 except EnvironmentError:
198 makevars = {'PLATFORM': None}
Christian Heimes23361112007-11-23 07:05:03 +0000199
Jeremy Klothebbccea2017-06-20 14:53:39 -0600200 # Rebuild the makefile when building for different a platform than
201 # the last run.
202 if makevars['PLATFORM'] != platform:
203 print("Updating the makefile...")
Christian Heimese8954f82007-11-22 11:21:16 +0000204 sys.stdout.flush()
Jeremy Klothebbccea2017-06-20 14:53:39 -0600205 # Firstly, apply the changes for the platform makefile into
206 # a temporary file to prevent any errors from this script
207 # causing false positives on subsequent runs.
208 new_makefile = makefile + '.new'
209 fix_makefile(new_makefile, platform_makefile, suffix)
210 makevars = parse_makefile(new_makefile)
Christian Heimese8954f82007-11-22 11:21:16 +0000211
Jeremy Klothebbccea2017-06-20 14:53:39 -0600212 # Secondly, perform the make recipes that use Perl
213 copy_files(new_makefile, makevars)
214
215 # Set our build information in buildinf.h.
216 # XXX: This isn't needed for a properly "prepared" SSL, but
217 # it fixes the current checked-in external (as of 2017-05).
218 fix_buildinf(makevars)
219
220 # Finally, move the temporary file to its real destination.
221 if os.path.exists(makefile):
222 os.remove(makefile)
223 os.rename(new_makefile, makefile)
Christian Heimese8954f82007-11-22 11:21:16 +0000224
225 # Now run make.
Jeremy Klothebbccea2017-06-20 14:53:39 -0600226 makeCommand = "nmake /nologo /f \"%s\" lib" % makefile
Christian Heimese8954f82007-11-22 11:21:16 +0000227 print("Executing ssl makefiles:", makeCommand)
228 sys.stdout.flush()
229 rc = os.system(makeCommand)
230 if rc:
Jeremy Klothebbccea2017-06-20 14:53:39 -0600231 print("Executing", makefile, "failed (error %d)" % rc)
Christian Heimese8954f82007-11-22 11:21:16 +0000232 sys.exit(rc)
233 finally:
234 os.chdir(old_cd)
235 sys.exit(rc)
236
237if __name__=='__main__':
238 main()