-
Notifications
You must be signed in to change notification settings - Fork 36
/
build_tasks.py
397 lines (325 loc) · 13.1 KB
/
build_tasks.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
import shutil
import tarfile
from abc import ABC, abstractmethod
from enum import Enum
from pathlib import Path
from tempfile import TemporaryFile
from platform import architecture
from sys import platform
from typing import Optional, Any
try:
from invoke import task, Context
except ImportError:
# This will happen when doing pip install nassl in an environment that does not have invoke pre-installed
# We still want this script to be usable directly by pip
def task(*args, **kwargs): # type: ignore
# Return a function - anything will do
return repr
Context = Any
# Where we store the src packages of OpenSSL and Zlib
_DEPS_PATH = Path(__file__).parent.absolute() / "deps"
class SupportedPlatformEnum(Enum):
"""Platforms supported by nassl.
"""
OSX_64 = 1
LINUX_64 = 2
LINUX_32 = 3
WINDOWS_32 = 4
WINDOWS_64 = 5
OPENBSD_64 = 6
CURRENT_PLATFORM = None
if architecture()[0] == "64bit":
if platform == "darwin":
CURRENT_PLATFORM = SupportedPlatformEnum.OSX_64
elif platform in ["linux", "linux2"]:
CURRENT_PLATFORM = SupportedPlatformEnum.LINUX_64
elif platform == "win32":
CURRENT_PLATFORM = SupportedPlatformEnum.WINDOWS_64
elif platform == "openbsd5":
CURRENT_PLATFORM = SupportedPlatformEnum.OPENBSD_64
elif architecture()[0] == "32bit":
if platform in ["linux", "linux2"]:
CURRENT_PLATFORM = SupportedPlatformEnum.LINUX_32
elif platform == "win32":
CURRENT_PLATFORM = SupportedPlatformEnum.WINDOWS_32
class BuildConfig(ABC):
"""Base class we use to configure and build Zlib and OpenSSL.
"""
def __init__(self, platform: SupportedPlatformEnum) -> None:
self.platform = platform
@property
@abstractmethod
def src_path(self) -> Path:
"""Where the src package is extracted to before trying to build it.
"""
pass
def clean(self) -> None:
shutil.rmtree(self.src_path, ignore_errors=True)
@property
@abstractmethod
def src_tar_gz_url(self) -> str:
"""Where to download src package from.
"""
pass
def fetch_source(self) -> None:
"""Download the tar archive that contains the source code for the library.
"""
import requests # Do not import at the top that this file can be imported by setup.py
with TemporaryFile() as temp_file:
# Download the source archive
request = requests.get(self.src_tar_gz_url)
temp_file.write(request.content)
# Rewind the file
temp_file.seek(0)
# Extract the content of the archive
tar_file = tarfile.open(fileobj=temp_file)
tar_file.extractall(path=_DEPS_PATH)
@abstractmethod
def build(self, ctx: Context) -> None:
pass
@property
@abstractmethod
def include_path(self) -> Path:
pass
class OpenSslBuildConfig(BuildConfig, ABC):
@property
@abstractmethod
def _openssl_git_tag(self) -> str:
pass
@property
def src_tar_gz_url(self) -> str:
return f"https://github.com/openssl/openssl/archive/{self._openssl_git_tag}.tar.gz"
@property
def src_path(self) -> Path:
return _DEPS_PATH / f"openssl-{self._openssl_git_tag}"
@property
@abstractmethod
def libcrypto_path(self) -> Path:
pass
@property
@abstractmethod
def libssl_path(self) -> Path:
pass
@property
@abstractmethod
def exe_path(self) -> Path:
pass
def _get_build_target(self, should_build_for_debug: bool) -> str:
if self.platform == SupportedPlatformEnum.WINDOWS_32:
openssl_target = "VC-WIN32"
elif self.platform == SupportedPlatformEnum.WINDOWS_64:
openssl_target = "VC-WIN64A"
elif self.platform == SupportedPlatformEnum.OSX_64:
openssl_target = "darwin64-x86_64-cc"
elif self.platform == SupportedPlatformEnum.LINUX_64:
openssl_target = "linux-x86_64"
elif self.platform == SupportedPlatformEnum.LINUX_32:
openssl_target = "linux-elf"
else:
raise ValueError("Unknown platform")
if should_build_for_debug:
if self.platform not in [SupportedPlatformEnum.WINDOWS_32, SupportedPlatformEnum.WINDOWS_64]:
raise ValueError("Debug builds only supported for Windows")
else:
openssl_target = f"debug-{openssl_target}"
return openssl_target
def build(
self,
ctx: Context,
zlib_lib_path: Optional[Path] = None,
zlib_include_path: Optional[Path] = None,
should_build_for_debug: bool = False,
) -> None:
if not zlib_lib_path or not zlib_include_path:
raise ValueError("Missing argument")
# Build OpenSSL
openssl_target = self._get_build_target(should_build_for_debug)
with ctx.cd(str(self.src_path)):
self._run_configure_command(ctx, openssl_target, zlib_lib_path, zlib_include_path)
self._run_build_steps(ctx)
# To be defined in subclasses
_OPENSSL_CONF_CMD: str = None
def _run_configure_command(
self, ctx: Context, openssl_target: str, zlib_lib_path: Path, zlib_include_path: Path
) -> None:
if self.platform in [SupportedPlatformEnum.WINDOWS_32, SupportedPlatformEnum.WINDOWS_64]:
extra_args = "-no-asm -DZLIB_WINAPI" # *hate* zlib
# On Windows OpenSSL wants the full path to the lib file
final_zlib_path = zlib_lib_path
else:
extra_args = " -fPIC"
# On Unix OpenSSL wants the path to the folder where the lib is
final_zlib_path = zlib_lib_path.parent
ctx.run(
self._OPENSSL_CONF_CMD.format(
target=openssl_target,
zlib_lib_path=final_zlib_path,
zlib_include_path=zlib_include_path,
extra_args=extra_args,
)
)
def _run_build_steps(self, ctx: Context) -> None:
if self.platform in [SupportedPlatformEnum.WINDOWS_32, SupportedPlatformEnum.WINDOWS_64]:
if self.platform == SupportedPlatformEnum.WINDOWS_32:
ctx.run("ms\\do_ms")
else:
ctx.run("ms\\do_win64a.bat")
ctx.run("nmake -f ms\\nt.mak clean", warn=True) # Does not work if tmp32 does not exist (fresh build)
ctx.run("nmake -f ms\\nt.mak")
else:
ctx.run("make clean", warn=True)
ctx.run("make") # Only build the libs as it is faster - not available on Windows
class LegacyOpenSslBuildConfig(OpenSslBuildConfig):
@property
def _openssl_git_tag(self) -> str:
return "OpenSSL_1_0_2e"
_OPENSSL_CONF_CMD = (
"perl Configure {target} zlib no-zlib-dynamic no-shared enable-rc5 enable-md2 enable-gost "
"enable-cast enable-idea enable-ripemd enable-mdc2 --with-zlib-include={zlib_include_path} "
"--with-zlib-lib={zlib_lib_path} {extra_args}"
)
@property
def include_path(self) -> Path:
if self.platform in [SupportedPlatformEnum.WINDOWS_32, SupportedPlatformEnum.WINDOWS_64]:
return self.src_path / "inc32"
else:
return self.src_path / "include"
@property
def libcrypto_path(self) -> Path:
if self.platform in [SupportedPlatformEnum.WINDOWS_32, SupportedPlatformEnum.WINDOWS_64]:
return self.src_path / "out32" / "libeay32.lib"
else:
return self.src_path / "libcrypto.a"
@property
def libssl_path(self) -> Path:
if self.platform in [SupportedPlatformEnum.WINDOWS_32, SupportedPlatformEnum.WINDOWS_64]:
return self.src_path / "out32" / "ssleay32.lib"
else:
return self.src_path / "libssl.a"
@property
def exe_path(self) -> Path:
if self.platform in [SupportedPlatformEnum.WINDOWS_32, SupportedPlatformEnum.WINDOWS_64]:
return self.src_path / "out32" / "openssl.exe"
else:
return self.src_path / "apps" / "openssl"
class ModernOpenSslBuildConfig(OpenSslBuildConfig):
@property
def _openssl_git_tag(self) -> str:
return "OpenSSL_1_1_1h"
_OPENSSL_CONF_CMD = (
"perl Configure {target} zlib no-zlib-dynamic no-shared enable-rc5 enable-md2 enable-gost "
"enable-cast enable-idea enable-ripemd enable-mdc2 --with-zlib-include={zlib_include_path} "
"--with-zlib-lib={zlib_lib_path} enable-weak-ssl-ciphers enable-tls1_3 {extra_args} no-async"
)
def _run_build_steps(self, ctx: Context) -> None:
if self.platform in [SupportedPlatformEnum.WINDOWS_32, SupportedPlatformEnum.WINDOWS_64]:
ctx.run("nmake clean", warn=True)
ctx.run("nmake")
else:
return super()._run_build_steps(ctx)
@property
def libcrypto_path(self) -> Path:
if self.platform in [SupportedPlatformEnum.WINDOWS_32, SupportedPlatformEnum.WINDOWS_64]:
return self.src_path / "libcrypto.lib"
else:
return self.src_path / "libcrypto.a"
@property
def libssl_path(self) -> Path:
if self.platform in [SupportedPlatformEnum.WINDOWS_32, SupportedPlatformEnum.WINDOWS_64]:
return self.src_path / "libssl.lib"
else:
return self.src_path / "libssl.a"
@property
def include_path(self) -> Path:
return self.src_path / "include"
@property
def exe_path(self) -> Path:
if self.platform in [SupportedPlatformEnum.WINDOWS_32, SupportedPlatformEnum.WINDOWS_64]:
return self.src_path / "apps" / "openssl.exe"
else:
return self.src_path / "apps" / "openssl"
class ZlibBuildConfig(BuildConfig):
@property
def src_tar_gz_url(self) -> str:
return "http://zlib.net/zlib-1.2.11.tar.gz"
@property
def src_path(self) -> Path:
return _DEPS_PATH / "zlib-1.2.11"
def build(self, ctx: Context) -> None:
if self.platform in [SupportedPlatformEnum.WINDOWS_32, SupportedPlatformEnum.WINDOWS_64]:
if self.platform == SupportedPlatformEnum.WINDOWS_32:
arch = "x86"
build_script = "bld_ml32.bat"
build_platform = "Win32"
else:
arch = "x64"
build_script = "bld_ml64.bat"
build_platform = "x64"
masm_path = self.src_path / "contrib" / f"masm{arch}"
with ctx.cd(str(masm_path)):
ctx.run(build_script)
ctx.run(f"msbuild ..\\vstudio\\vc14\\zlibvc.sln /P:Configuration=Release /P:Platform={build_platform}")
else:
# Linux/macOS build
with ctx.cd(str(self.src_path)):
ctx.run('CFLAGS="-fPIC" ./configure -static')
ctx.run("make clean")
ctx.run("make")
@property
def libz_path(self) -> Path:
if self.platform in [SupportedPlatformEnum.WINDOWS_32, SupportedPlatformEnum.WINDOWS_64]:
arch = "x86" if self.platform == SupportedPlatformEnum.WINDOWS_32 else "x64"
zlib_lib_path = self.src_path / "contrib" / "vstudio" / "vc14" / arch / "ZlibStatRelease" / "zlibstat.lib"
else:
zlib_lib_path = self.src_path / "libz.a"
return zlib_lib_path
@property
def include_path(self) -> Path:
return self.src_path
@task
def build_zlib(ctx, do_not_clean=False):
print("ZLIB: Starting...")
zlib_cfg = ZlibBuildConfig(CURRENT_PLATFORM)
if not do_not_clean:
zlib_cfg.clean()
zlib_cfg.fetch_source()
zlib_cfg.build(ctx)
print("ZLIB: All done")
@task
def build_legacy_openssl(ctx, do_not_clean=False):
print("OPENSSL LEGACY: Starting...")
ssl_legacy_cfg = LegacyOpenSslBuildConfig(CURRENT_PLATFORM)
if not do_not_clean:
ssl_legacy_cfg.clean()
ssl_legacy_cfg.fetch_source()
zlib_cfg = ZlibBuildConfig(CURRENT_PLATFORM)
ssl_legacy_cfg.build(ctx, zlib_lib_path=zlib_cfg.libz_path, zlib_include_path=zlib_cfg.include_path)
print("OPENSSL LEGACY: All done")
@task
def build_modern_openssl(ctx, do_not_clean=False):
print("OPENSSL MODERN: Starting...")
ssl_modern_cfg = ModernOpenSslBuildConfig(CURRENT_PLATFORM)
if not do_not_clean:
ssl_modern_cfg.clean()
ssl_modern_cfg.fetch_source()
zlib_cfg = ZlibBuildConfig(CURRENT_PLATFORM)
ssl_modern_cfg.build(ctx, zlib_lib_path=zlib_cfg.libz_path, zlib_include_path=zlib_cfg.include_path)
print("OPENSSL MODERN: All done")
@task
def build_nassl(ctx):
extra_args = ""
if CURRENT_PLATFORM == SupportedPlatformEnum.WINDOWS_32:
extra_args = "--plat-name=win32"
elif CURRENT_PLATFORM == SupportedPlatformEnum.WINDOWS_64:
extra_args = "--plat-name=win-amd64"
# Reset the ./build folder if there was a previous version of nassl
build_path = Path(__file__).parent.absolute() / "build"
if build_path.exists():
shutil.rmtree(build_path)
ctx.run(f"python setup.py build_ext -i {extra_args}")
@task
def build_all(ctx, do_not_clean=False):
build_zlib(ctx, do_not_clean)
build_legacy_openssl(ctx, do_not_clean)
build_modern_openssl(ctx, do_not_clean)
build_nassl(ctx)