/
package.py
97 lines (77 loc) · 2.84 KB
/
package.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
import pathlib
import shutil
import zipfile
import distlib.scripts
import distlib.wheel
import invoke
import passa.internals._pip
import plette
import requirementslib
ROOT = pathlib.Path(__file__).resolve().parent.parent
OUTPUT_DIR = ROOT.joinpath('pack')
STUBFILES_DIR = pathlib.Path(__file__).resolve().with_name('pack')
DONT_PACKAGE = {
# Rely on the client for them.
'pip', 'setuptools',
'importlib', # We only support 2.7 so this is not needed.
'modutil', # This breaks <3.7.
'toml', # Why is requirementslib still not dropping it?
'typing', # This breaks 2.7. We'll provide a special stub for it.
}
IGNORE_LIB_PATTERNS = {
'*.pyd', # Binary on Windows.
'*.so', # Binary on POSIX.
}
@invoke.task()
def clean_pack(ctx):
if OUTPUT_DIR.exists():
shutil.rmtree(str(OUTPUT_DIR))
print(f'[clean-pack] Removing {OUTPUT_DIR}')
def _recursive_write_to_zip(zf, path, root=None):
if path == pathlib.Path(zf.filename):
return
if root is None:
if not path.is_dir():
raise ValueError('root is required for non-directory path')
root = path
if not path.is_dir():
zf.write(str(path), str(path.relative_to(root)))
return
for c in path.iterdir():
_recursive_write_to_zip(zf, c, root)
@invoke.task(pre=[clean_pack])
def pack(ctx, remove_lib=True):
"""Build a isolated runnable package.
"""
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
with ROOT.joinpath('Pipfile.lock').open() as f:
lockfile = plette.Lockfile.load(f)
libdir = OUTPUT_DIR.joinpath('lib')
paths = {'purelib': libdir, 'platlib': libdir}
sources = lockfile.meta.sources._data
maker = distlib.scripts.ScriptMaker(None, None)
# Install packages from Pipfile.lock.
for name, package in lockfile.default._data.items():
if name in DONT_PACKAGE:
continue
print(f'[pack] Installing {name}')
package.pop('editable', None) # Don't install things as editable.
package.pop('markers', None) # Always install everything.
r = requirementslib.Requirement.from_pipfile(name, package)
wheel = passa.internals._pip.build_wheel(
r.as_ireq(), sources, r.hashes or None,
)
wheel.install(paths, maker, lib_only=True)
for pattern in IGNORE_LIB_PATTERNS:
for path in libdir.rglob(pattern):
print(f'[pack] Removing {path}')
path.unlink()
# Pack everything into ZIP.
zipname = OUTPUT_DIR.joinpath('passa.zip')
with zipfile.ZipFile(zipname, 'w') as zf:
_recursive_write_to_zip(zf, OUTPUT_DIR)
_recursive_write_to_zip(zf, STUBFILES_DIR)
print(f'[pack] Written archive {zipname}')
if remove_lib and libdir.exists():
print(f'[pack] Removing {libdir}')
shutil.rmtree(str(libdir))