pyinstaller_exe   A
last analyzed

Complexity

Total Complexity 35

Size/Duplication

Total Lines 222
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 108
dl 0
loc 222
rs 9.6
c 0
b 0
f 0
wmc 35

16 Functions

Rating   Name   Duplication   Size   Complexity  
A bitsdir() 0 12 3
A is_64bit() 0 6 2
A bit_tail() 0 6 2
A clean_versions() 0 6 1
A write_versions() 0 8 3
A get_ucrt_dlls() 0 8 2
A sz_wrapper() 0 13 3
A copy_json() 0 8 1
A get_sevenzip_write() 0 17 4
A clean_outdir() 0 9 2
A main() 0 17 1
A sz_wrapper_writer() 0 11 2
A call_specs() 0 15 2
A clean_specs() 0 8 2
A generate_specs() 0 12 3
A get_sevenzip() 0 12 2
1
#!/usr/bin/env python3
2
"""Generate .exe files with PyInstaller."""
3
4
from os import devnull, getcwd, listdir, makedirs, remove
5
from os.path import basename, exists, join
6
from platform import architecture
7
from shutil import copy, copytree, rmtree
8
from subprocess import STDOUT, call
9
10
from requests import certs, get
11
12
from bbarchivist.bbconstants import (CAP, COMMITDATE, JSONDIR, LONGVERSION, VERSION)
13
from bbarchivist.utilities import get_seven_zip, prep_seven_zip
14
15
__author__ = "Thurask"
16
__license__ = "WTFPL v2"
17
__copyright__ = "2016-2019 Thurask"
18
19
20
def write_versions():
21
    """
22
    Write temporary version files.
23
    """
24
    with open("version.txt", "w") as afile:
25
        afile.write(VERSION)
26
    with open("longversion.txt", "w") as afile:
27
        afile.write("{0}\n{1}".format(LONGVERSION, COMMITDATE))
28
29
30
def clean_versions():
31
    """
32
    Remove temporary version files.
33
    """
34
    remove("version.txt")
35
    remove("longversion.txt")
36
37
38
def is_64bit():
39
    """
40
    Check if system is 64-bit.
41
    """
42
    is64 = True if architecture()[0] == "64bit" else False
43
    return is64
44
45
46
def bit_tail():
47
    """
48
    String form of 64-bit checking.
49
    """
50
    tail = "x64" if is_64bit() else "x86"
51
    return tail
52
53
54
def bitsdir(indir):
55
    """
56
    Create directories based on indir segregated on bit type.
57
58
    :param indir: Directory to modify.
59
    :type indir: str
60
    """
61
    indirx = "{0}-64".format(indir) if is_64bit() else indir
62
    if exists(indirx):
63
        clean_outdir(indirx)
64
    makedirs(indirx)
65
    return indirx
66
67
68
def get_ucrt_dlls():
69
    """
70
    Get some magic voodoo Windows DLLs.
71
    """
72
    tail = bit_tail()
73
    pfiles = "Program Files (x86)" if tail == "x64" else "Program Files"
74
    folder = join("C:\\", pfiles, "Windows Kits", "10", "Redist", "ucrt", "DLLs", tail)
75
    return folder
76
77
78
def generate_specs():
79
    """
80
    Generate pyinstaller spec files.
81
    """
82
    scripts = ["archivist", "autolookup", "barlinker", "carrierchecker", "certchecker", "devloader", "downloader", "droidlookup", "droidscraper", "escreens", "kernchecker", "lazyloader", "linkgen", "metachecker", "swlookup", "tclscan", "tcldelta", "tclnewprd"]
83
    here = getcwd().replace("\\", "\\\\")
84
    dlldir = get_ucrt_dlls().replace("\\", "\\\\")
85
    tail = bit_tail()
86
    for script in scripts:
87
        template = "# -*- mode: python -*-\n\nblock_cipher = None\n\n\na = Analysis(['bbarchivist\\\\scripts\\\\{0}.py'],\n             pathex=['{1}', '{2}'],\n             binaries=None,\n             datas=None,\n             hiddenimports=[],\n             hookspath=[],\n             runtime_hooks=[],\n             excludes=[],\n             win_no_prefer_redirects=False,\n             win_private_assemblies=False,\n             cipher=block_cipher)\npyz = PYZ(a.pure, a.zipped_data,\n             cipher=block_cipher)\nexe = EXE(pyz,\n          a.scripts,\n          a.binaries,\n          a.zipfiles,\n          a.datas,\n          name='{0}',\n          debug=False,\n          strip=False,\n          upx=False,\n          console=True )\n".format(script, here, dlldir)
88
        with open("{0}.{1}.spec".format(script, tail), "w") as afile:
89
            afile.write(template)
90
91
92
def clean_specs():
93
    """
94
    Remove pyinstaller spec files.
95
    """
96
    tail = bit_tail()
97
    specs = [x for x in listdir() if x.endswith("{0}.spec".format(tail))]
98
    for spec in specs:
99
        remove(spec)
100
101
102
def get_sevenzip():
103
    """
104
    Get 7-Zip.
105
    """
106
    szver = "1900"
107
    szurl = "http://www.7-zip.org/a/7z{0}-extra.7z".format(szver)
108
    psz = prep_seven_zip()
109
    if psz:
110
        get_sevenzip_write(szurl)
111
    else:
112
        print("GO TO {0} AND DO IT MANUALLY".format(szurl))
113
        raise SystemError
114
115
116
def get_sevenzip_write(szurl):
117
    """
118
    Download 7-Zip file.
119
120
    :param szurl: Link to 7z download.
121
    :type szurl: str
122
    """
123
    szexe = get_seven_zip()
124
    szfile = basename(szurl)
125
    with open(szfile, "wb") as afile:
126
        req = get(szurl, stream=True)
127
        for chunk in req.iter_content(chunk_size=1024):
128
            afile.write(chunk)
129
    cmd = "{0} x {1} -o7z".format(szexe, szfile)
130
    with open(devnull, "wb") as dnull:
131
        call(cmd, stdout=dnull, stderr=STDOUT, shell=True)
132
    remove(basename(szurl))
133
134
135
def call_specs(distdir, builddir):
136
    """
137
    Call pyinstaller to make specs.
138
139
    :param distdir: Path to distribute files.
140
    :type distdir: str
141
142
    :param builddir: Path to build files.
143
    :type builddir: str
144
    """
145
    tail = bit_tail()
146
    specs = [x for x in listdir() if x.endswith("{0}.spec".format(tail))]
147
    for spec in specs:  # use UPX 3.93 or up
148
        cmd = "pyinstaller --onefile --workpath {2} --distpath {1} {0}".format(spec, distdir, builddir)
149
        call(cmd, shell=True)
150
151
152
def sz_wrapper(outdir):
153
    """
154
    Copy 7-Zip to outdir.
155
156
    :param outdir: Output directory.
157
    :type outdir: str
158
    """
159
    try:
160
        get_sevenzip()
161
    except SystemError:
162
        pass
163
    else:
164
        sz_wrapper_writer(outdir)
165
166
167
def sz_wrapper_writer(outdir):
168
    """
169
    Copy 7-Zip to outdir, the actual function.
170
171
    :param outdir: Output directory.
172
    :type outdir: str
173
    """
174
    copy(join("7z", "7za.exe"), outdir)
175
    if is_64bit():
176
        copy(join("7z", "x64", "7za.exe"), join(outdir, "7za64.exe"))
177
    rmtree("7z", ignore_errors=True)
178
179
180
def copy_json(outdir):
181
    """
182
    Copy JSON folder to outdir.
183
184
    :param outdir: Output directory.
185
    :type outdir: str
186
    """
187
    copytree(JSONDIR, join(outdir, "json"))
188
189
190
def clean_outdir(outdir):
191
    """
192
    Nuke outdir, if it exists.
193
194
    :param outdir: Output directory.
195
    :type outdir: str
196
    """
197
    if exists(outdir):
198
        rmtree(outdir, ignore_errors=True)
199
200
201
def main():
202
    """
203
    Create .exes with dynamic spec files.
204
    """
205
    outdir = bitsdir("pyinst-dist")
206
    builddir = bitsdir("pyinst-build")
207
    write_versions()
208
    generate_specs()
209
    call_specs(outdir, builddir)
210
    copy("version.txt", outdir)
211
    copy("longversion.txt", outdir)
212
    copy(CAP.location, outdir)
213
    copy_json(outdir)
214
    copy(certs.where(), join(outdir, "cacerts.pem"))
215
    sz_wrapper(outdir)
216
    clean_versions()
217
    clean_specs()
218
219
220
if __name__ == "__main__":
221
    main()
222