git-cinnabar

Форк
0
/
package.py 
184 строки · 5.4 Кб
1
# This Source Code Form is subject to the terms of the Mozilla Public
2
# License, v. 2.0. If a copy of the MPL was not distributed with this
3
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4

5
import concurrent.futures
6
import os
7
import shutil
8
import struct
9
import subprocess
10
import sys
11
import tarfile
12
import zipfile
13
from io import BytesIO
14
from pathlib import Path
15
from urllib.request import urlopen
16

17

18
def main():
19
    ret = 0
20
    args = sys.argv[1:]
21
    if args and args[0] == "--download":
22
        download_py = Path(__file__).parent.parent / "download.py"
23
        args = (
24
            subprocess.check_output(
25
                [sys.executable, download_py, "--url", "--list"] + args[1:]
26
            )
27
            .decode()
28
            .splitlines()
29
        )
30
    else:
31
        for arg in args:
32
            if arg.startswith("-"):
33
                print(f"{arg} is not supported.")
34
                return 1
35
    if len(args) > 1:
36
        executor = concurrent.futures.ProcessPoolExecutor()
37
        map_ = executor.map
38
    else:
39
        map_ = map
40
    for path, pkg in zip(args, map_(package_from, args)):
41
        if pkg:
42
            print(f"Created {pkg} from {path}", file=sys.stderr)
43
        else:
44
            print(f"Can't determine platform type for {path}", file=sys.stderr)
45
            ret = 1
46
    return ret
47

48

49
def package_from(path):
50
    if path.startswith(("http:", "https:")):
51
        fh = urlopen(path)
52
        size = fh.length
53
    else:
54
        fh = open(path, "rb")
55
        size = os.stat(path).st_size
56
    with fh:
57
        fh = RewindOnce(fh)
58
        system, machine = detect_platform(fh)
59
        if not system or not machine:
60
            return
61

62
        fh.rewind()
63
        if size is None:
64
            fh = BytesIO(fh.read())
65
            size = len(fh.getbuffer())
66
        return package(fh, size, system, machine)
67

68

69
def package(fh, size, system, machine):
70
    stem = f"git-cinnabar.{system.lower()}.{machine.lower()}"
71
    if system == "Windows":
72
        pkg = f"{stem}.zip"
73
        zip = zipfile.ZipFile(
74
            pkg, mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=9
75
        )
76
        # Manually do zip.mkdir("git-cinnabar") until we can depend on python 3.11
77
        zinfo = zipfile.ZipInfo("git-cinnabar/")
78
        zinfo.external_attr = (0o40777 << 16) | 0x10
79
        zip.writestr(zinfo, "")
80
        fh = RewindOnce(fh)
81
        with zip.open("git-cinnabar/git-cinnabar.exe", mode="w") as zipped:
82
            shutil.copyfileobj(fh, zipped)
83
        fh.rewind()
84
        with zip.open("git-cinnabar/git-remote-hg.exe", mode="w") as zipped:
85
            shutil.copyfileobj(fh, zipped)
86
    else:
87
        pkg = f"{stem}.tar.xz"
88
        tar = tarfile.open(pkg, mode="w:xz", preset=9)
89
        info = tarinfo("git-cinnabar/")
90
        info.mode = 0o755
91
        info.type = tarfile.DIRTYPE
92
        tar.addfile(info)
93

94
        info = tarinfo("git-cinnabar/git-cinnabar")
95
        info.mode = 0o700
96
        info.size = size
97
        info.type = tarfile.REGTYPE
98
        tar.addfile(info, fh)
99

100
        info = tarinfo("git-cinnabar/git-remote-hg")
101
        info.mode = 0o777
102
        info.type = tarfile.SYMTYPE
103
        info.linkname = "git-cinnabar"
104
        tar.addfile(info)
105
    return pkg
106

107

108
def tarinfo(name):
109
    info = tarfile.TarInfo(name)
110
    info.uid = 1000
111
    info.gid = 1000
112
    info.uname = "cinnabar"
113
    info.gname = "cinnabar"
114
    return info
115

116

117
class RewindOnce:
118
    def __init__(self, fh):
119
        self.buf = b""
120
        self.off = 0
121
        self.rewound = False
122
        self.fh = fh
123

124
    def read(self, length=None):
125
        if self.rewound:
126
            if length is None:
127
                return self.buf[self.off :] + self.fh.read()
128
            ret = self.buf[self.off :][:length]
129
            self.off += len(ret)
130
            missing = length - len(ret)
131
            if not missing:
132
                return ret
133
            return ret + self.fh.read(missing)
134

135
        ret = self.fh.read(length)
136
        self.buf += ret
137
        return ret
138

139
    def rewind(self):
140
        assert not self.rewound
141
        self.rewound = True
142

143

144
def detect_platform(executable):
145
    system, machine = None, None
146
    head = executable.read(4)
147
    if head[:2] == b"MZ":
148
        # Seek to 0x3c
149
        executable.read(0x3C - 4)
150
        (pe_offset,) = struct.unpack("<L", executable.read(4))
151
        # Seek to pe_offset
152
        executable.read(pe_offset - 0x40)
153
        pe_signature = executable.read(4)
154
        if pe_signature == b"PE\0\0":
155
            system = "Windows"
156
            (machine_type,) = struct.unpack("<H", executable.read(2))
157
            if machine_type == 0x8664:
158
                machine = "x86_64"
159
    elif head == b"\xcf\xfa\xed\xfe":
160
        system = "macOS"
161
        (machine_type,) = struct.unpack("<L", executable.read(4))
162
        if machine_type == 0x1000007:
163
            machine = "x86_64"
164
        elif machine_type == 0x100000C:
165
            machine = "arm64"
166
    elif head == b"\x7fELF":
167
        (ident,) = struct.unpack(">L", executable.read(4))
168
        # 64-bits little-endian Linux (in theory, System-V)
169
        if ident == 0x02010100:
170
            system = "Linux"
171
            # Seek to 0x12
172
            executable.read(10)
173
            (machine_type,) = struct.unpack("<H", executable.read(2))
174
            if machine_type == 0x3E:
175
                machine = "x86_64"
176
            elif machine_type == 0xB7:
177
                machine = "arm64"
178
    if system and machine:
179
        return system, machine
180
    return None, None
181

182

183
if __name__ == "__main__":
184
    sys.exit(main())
185

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.