xref: /aosp_15_r20/external/bazelbuild-rules_python/tools/wheelmaker.py (revision 60517a1edbc8ecf509223e9af94a7adec7d736b8)
1*60517a1eSAndroid Build Coastguard Worker# Copyright 2018 The Bazel Authors. All rights reserved.
2*60517a1eSAndroid Build Coastguard Worker#
3*60517a1eSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*60517a1eSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*60517a1eSAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*60517a1eSAndroid Build Coastguard Worker#
7*60517a1eSAndroid Build Coastguard Worker#    http://www.apache.org/licenses/LICENSE-2.0
8*60517a1eSAndroid Build Coastguard Worker#
9*60517a1eSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*60517a1eSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*60517a1eSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*60517a1eSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*60517a1eSAndroid Build Coastguard Worker# limitations under the License.
14*60517a1eSAndroid Build Coastguard Worker
15*60517a1eSAndroid Build Coastguard Workerfrom __future__ import annotations
16*60517a1eSAndroid Build Coastguard Worker
17*60517a1eSAndroid Build Coastguard Workerimport argparse
18*60517a1eSAndroid Build Coastguard Workerimport base64
19*60517a1eSAndroid Build Coastguard Workerimport hashlib
20*60517a1eSAndroid Build Coastguard Workerimport os
21*60517a1eSAndroid Build Coastguard Workerimport re
22*60517a1eSAndroid Build Coastguard Workerimport stat
23*60517a1eSAndroid Build Coastguard Workerimport sys
24*60517a1eSAndroid Build Coastguard Workerimport zipfile
25*60517a1eSAndroid Build Coastguard Workerfrom pathlib import Path
26*60517a1eSAndroid Build Coastguard Worker
27*60517a1eSAndroid Build Coastguard Worker_ZIP_EPOCH = (1980, 1, 1, 0, 0, 0)
28*60517a1eSAndroid Build Coastguard Worker
29*60517a1eSAndroid Build Coastguard Worker
30*60517a1eSAndroid Build Coastguard Workerdef commonpath(path1, path2):
31*60517a1eSAndroid Build Coastguard Worker    ret = []
32*60517a1eSAndroid Build Coastguard Worker    for a, b in zip(path1.split(os.path.sep), path2.split(os.path.sep)):
33*60517a1eSAndroid Build Coastguard Worker        if a != b:
34*60517a1eSAndroid Build Coastguard Worker            break
35*60517a1eSAndroid Build Coastguard Worker        ret.append(a)
36*60517a1eSAndroid Build Coastguard Worker    return os.path.sep.join(ret)
37*60517a1eSAndroid Build Coastguard Worker
38*60517a1eSAndroid Build Coastguard Worker
39*60517a1eSAndroid Build Coastguard Workerdef escape_filename_segment(segment):
40*60517a1eSAndroid Build Coastguard Worker    """Escapes a filename segment per https://www.python.org/dev/peps/pep-0427/#escaping-and-unicode
41*60517a1eSAndroid Build Coastguard Worker
42*60517a1eSAndroid Build Coastguard Worker    This is a legacy function, kept for backwards compatibility,
43*60517a1eSAndroid Build Coastguard Worker    and may be removed in the future. See `escape_filename_distribution_name`
44*60517a1eSAndroid Build Coastguard Worker    and `normalize_pep440` for the modern alternatives.
45*60517a1eSAndroid Build Coastguard Worker    """
46*60517a1eSAndroid Build Coastguard Worker    return re.sub(r"[^\w\d.]+", "_", segment, re.UNICODE)
47*60517a1eSAndroid Build Coastguard Worker
48*60517a1eSAndroid Build Coastguard Worker
49*60517a1eSAndroid Build Coastguard Workerdef normalize_package_name(name):
50*60517a1eSAndroid Build Coastguard Worker    """Normalize a package name according to the Python Packaging User Guide.
51*60517a1eSAndroid Build Coastguard Worker
52*60517a1eSAndroid Build Coastguard Worker    See https://packaging.python.org/en/latest/specifications/name-normalization/
53*60517a1eSAndroid Build Coastguard Worker    """
54*60517a1eSAndroid Build Coastguard Worker    return re.sub(r"[-_.]+", "-", name).lower()
55*60517a1eSAndroid Build Coastguard Worker
56*60517a1eSAndroid Build Coastguard Worker
57*60517a1eSAndroid Build Coastguard Workerdef escape_filename_distribution_name(name):
58*60517a1eSAndroid Build Coastguard Worker    """Escape the distribution name component of a filename.
59*60517a1eSAndroid Build Coastguard Worker
60*60517a1eSAndroid Build Coastguard Worker    See https://packaging.python.org/en/latest/specifications/binary-distribution-format/#escaping-and-unicode
61*60517a1eSAndroid Build Coastguard Worker    """
62*60517a1eSAndroid Build Coastguard Worker    return normalize_package_name(name).replace("-", "_")
63*60517a1eSAndroid Build Coastguard Worker
64*60517a1eSAndroid Build Coastguard Worker
65*60517a1eSAndroid Build Coastguard Workerdef normalize_pep440(version):
66*60517a1eSAndroid Build Coastguard Worker    """Normalize version according to PEP 440, with fallback for placeholders.
67*60517a1eSAndroid Build Coastguard Worker
68*60517a1eSAndroid Build Coastguard Worker    If there's a placeholder in braces, such as {BUILD_TIMESTAMP},
69*60517a1eSAndroid Build Coastguard Worker    replace it with 0. Such placeholders can be used with stamping, in
70*60517a1eSAndroid Build Coastguard Worker    which case they would have been resolved already by now; if they
71*60517a1eSAndroid Build Coastguard Worker    haven't, we're doing an unstamped build, but we still need to
72*60517a1eSAndroid Build Coastguard Worker    produce a valid version. If such replacements are made, the
73*60517a1eSAndroid Build Coastguard Worker    original version string, sanitized to dot-separated alphanumerics,
74*60517a1eSAndroid Build Coastguard Worker    is appended as a local version segment, so you understand what
75*60517a1eSAndroid Build Coastguard Worker    placeholder was involved.
76*60517a1eSAndroid Build Coastguard Worker
77*60517a1eSAndroid Build Coastguard Worker    If that still doesn't produce a valid version, use version 0 and
78*60517a1eSAndroid Build Coastguard Worker    append the original version string, sanitized to dot-separated
79*60517a1eSAndroid Build Coastguard Worker    alphanumerics, as a local version segment.
80*60517a1eSAndroid Build Coastguard Worker
81*60517a1eSAndroid Build Coastguard Worker    """
82*60517a1eSAndroid Build Coastguard Worker
83*60517a1eSAndroid Build Coastguard Worker    import packaging.version
84*60517a1eSAndroid Build Coastguard Worker
85*60517a1eSAndroid Build Coastguard Worker    try:
86*60517a1eSAndroid Build Coastguard Worker        return str(packaging.version.Version(version))
87*60517a1eSAndroid Build Coastguard Worker    except packaging.version.InvalidVersion:
88*60517a1eSAndroid Build Coastguard Worker        pass
89*60517a1eSAndroid Build Coastguard Worker
90*60517a1eSAndroid Build Coastguard Worker    sanitized = re.sub(r"[^a-z0-9]+", ".", version.lower()).strip(".")
91*60517a1eSAndroid Build Coastguard Worker    substituted = re.sub(r"\{\w+\}", "0", version)
92*60517a1eSAndroid Build Coastguard Worker    delimiter = "." if "+" in substituted else "+"
93*60517a1eSAndroid Build Coastguard Worker    try:
94*60517a1eSAndroid Build Coastguard Worker        return str(packaging.version.Version(f"{substituted}{delimiter}{sanitized}"))
95*60517a1eSAndroid Build Coastguard Worker    except packaging.version.InvalidVersion:
96*60517a1eSAndroid Build Coastguard Worker        return str(packaging.version.Version(f"0+{sanitized}"))
97*60517a1eSAndroid Build Coastguard Worker
98*60517a1eSAndroid Build Coastguard Worker
99*60517a1eSAndroid Build Coastguard Workerclass _WhlFile(zipfile.ZipFile):
100*60517a1eSAndroid Build Coastguard Worker    def __init__(
101*60517a1eSAndroid Build Coastguard Worker        self,
102*60517a1eSAndroid Build Coastguard Worker        filename,
103*60517a1eSAndroid Build Coastguard Worker        *,
104*60517a1eSAndroid Build Coastguard Worker        mode,
105*60517a1eSAndroid Build Coastguard Worker        distribution_prefix: str,
106*60517a1eSAndroid Build Coastguard Worker        strip_path_prefixes=None,
107*60517a1eSAndroid Build Coastguard Worker        compression=zipfile.ZIP_DEFLATED,
108*60517a1eSAndroid Build Coastguard Worker        **kwargs,
109*60517a1eSAndroid Build Coastguard Worker    ):
110*60517a1eSAndroid Build Coastguard Worker        self._distribution_prefix = distribution_prefix
111*60517a1eSAndroid Build Coastguard Worker
112*60517a1eSAndroid Build Coastguard Worker        self._strip_path_prefixes = strip_path_prefixes or []
113*60517a1eSAndroid Build Coastguard Worker        # Entries for the RECORD file as (filename, hash, size) tuples.
114*60517a1eSAndroid Build Coastguard Worker        self._record = []
115*60517a1eSAndroid Build Coastguard Worker
116*60517a1eSAndroid Build Coastguard Worker        super().__init__(filename, mode=mode, compression=compression, **kwargs)
117*60517a1eSAndroid Build Coastguard Worker
118*60517a1eSAndroid Build Coastguard Worker    def distinfo_path(self, basename):
119*60517a1eSAndroid Build Coastguard Worker        return f"{self._distribution_prefix}.dist-info/{basename}"
120*60517a1eSAndroid Build Coastguard Worker
121*60517a1eSAndroid Build Coastguard Worker    def data_path(self, basename):
122*60517a1eSAndroid Build Coastguard Worker        return f"{self._distribution_prefix}.data/{basename}"
123*60517a1eSAndroid Build Coastguard Worker
124*60517a1eSAndroid Build Coastguard Worker    def add_file(self, package_filename, real_filename):
125*60517a1eSAndroid Build Coastguard Worker        """Add given file to the distribution."""
126*60517a1eSAndroid Build Coastguard Worker
127*60517a1eSAndroid Build Coastguard Worker        def arcname_from(name):
128*60517a1eSAndroid Build Coastguard Worker            # Always use unix path separators.
129*60517a1eSAndroid Build Coastguard Worker            normalized_arcname = name.replace(os.path.sep, "/")
130*60517a1eSAndroid Build Coastguard Worker            # Don't manipulate names filenames in the .distinfo or .data directories.
131*60517a1eSAndroid Build Coastguard Worker            if normalized_arcname.startswith(self._distribution_prefix):
132*60517a1eSAndroid Build Coastguard Worker                return normalized_arcname
133*60517a1eSAndroid Build Coastguard Worker            for prefix in self._strip_path_prefixes:
134*60517a1eSAndroid Build Coastguard Worker                if normalized_arcname.startswith(prefix):
135*60517a1eSAndroid Build Coastguard Worker                    return normalized_arcname[len(prefix) :]
136*60517a1eSAndroid Build Coastguard Worker
137*60517a1eSAndroid Build Coastguard Worker            return normalized_arcname
138*60517a1eSAndroid Build Coastguard Worker
139*60517a1eSAndroid Build Coastguard Worker        if os.path.isdir(real_filename):
140*60517a1eSAndroid Build Coastguard Worker            directory_contents = os.listdir(real_filename)
141*60517a1eSAndroid Build Coastguard Worker            for file_ in directory_contents:
142*60517a1eSAndroid Build Coastguard Worker                self.add_file(
143*60517a1eSAndroid Build Coastguard Worker                    "{}/{}".format(package_filename, file_),
144*60517a1eSAndroid Build Coastguard Worker                    "{}/{}".format(real_filename, file_),
145*60517a1eSAndroid Build Coastguard Worker                )
146*60517a1eSAndroid Build Coastguard Worker            return
147*60517a1eSAndroid Build Coastguard Worker
148*60517a1eSAndroid Build Coastguard Worker        arcname = arcname_from(package_filename)
149*60517a1eSAndroid Build Coastguard Worker        zinfo = self._zipinfo(arcname)
150*60517a1eSAndroid Build Coastguard Worker
151*60517a1eSAndroid Build Coastguard Worker        # Write file to the zip archive while computing the hash and length
152*60517a1eSAndroid Build Coastguard Worker        hash = hashlib.sha256()
153*60517a1eSAndroid Build Coastguard Worker        size = 0
154*60517a1eSAndroid Build Coastguard Worker        with open(real_filename, "rb") as fsrc:
155*60517a1eSAndroid Build Coastguard Worker            with self.open(zinfo, "w") as fdst:
156*60517a1eSAndroid Build Coastguard Worker                while True:
157*60517a1eSAndroid Build Coastguard Worker                    block = fsrc.read(2**20)
158*60517a1eSAndroid Build Coastguard Worker                    if not block:
159*60517a1eSAndroid Build Coastguard Worker                        break
160*60517a1eSAndroid Build Coastguard Worker                    fdst.write(block)
161*60517a1eSAndroid Build Coastguard Worker                    hash.update(block)
162*60517a1eSAndroid Build Coastguard Worker                    size += len(block)
163*60517a1eSAndroid Build Coastguard Worker
164*60517a1eSAndroid Build Coastguard Worker        self._add_to_record(arcname, self._serialize_digest(hash), size)
165*60517a1eSAndroid Build Coastguard Worker
166*60517a1eSAndroid Build Coastguard Worker    def add_string(self, filename, contents):
167*60517a1eSAndroid Build Coastguard Worker        """Add given 'contents' as filename to the distribution."""
168*60517a1eSAndroid Build Coastguard Worker        if isinstance(contents, str):
169*60517a1eSAndroid Build Coastguard Worker            contents = contents.encode("utf-8", "surrogateescape")
170*60517a1eSAndroid Build Coastguard Worker        zinfo = self._zipinfo(filename)
171*60517a1eSAndroid Build Coastguard Worker        self.writestr(zinfo, contents)
172*60517a1eSAndroid Build Coastguard Worker        hash = hashlib.sha256()
173*60517a1eSAndroid Build Coastguard Worker        hash.update(contents)
174*60517a1eSAndroid Build Coastguard Worker        self._add_to_record(filename, self._serialize_digest(hash), len(contents))
175*60517a1eSAndroid Build Coastguard Worker
176*60517a1eSAndroid Build Coastguard Worker    def _serialize_digest(self, hash):
177*60517a1eSAndroid Build Coastguard Worker        # https://www.python.org/dev/peps/pep-0376/#record
178*60517a1eSAndroid Build Coastguard Worker        # "base64.urlsafe_b64encode(digest) with trailing = removed"
179*60517a1eSAndroid Build Coastguard Worker        digest = base64.urlsafe_b64encode(hash.digest())
180*60517a1eSAndroid Build Coastguard Worker        digest = b"sha256=" + digest.rstrip(b"=")
181*60517a1eSAndroid Build Coastguard Worker        return digest
182*60517a1eSAndroid Build Coastguard Worker
183*60517a1eSAndroid Build Coastguard Worker    def _add_to_record(self, filename, hash, size):
184*60517a1eSAndroid Build Coastguard Worker        size = str(size).encode("ascii")
185*60517a1eSAndroid Build Coastguard Worker        self._record.append((filename, hash, size))
186*60517a1eSAndroid Build Coastguard Worker
187*60517a1eSAndroid Build Coastguard Worker    def _zipinfo(self, filename):
188*60517a1eSAndroid Build Coastguard Worker        """Construct deterministic ZipInfo entry for a file named filename"""
189*60517a1eSAndroid Build Coastguard Worker        # Strip leading path separators to mirror ZipInfo.from_file behavior
190*60517a1eSAndroid Build Coastguard Worker        separators = os.path.sep
191*60517a1eSAndroid Build Coastguard Worker        if os.path.altsep is not None:
192*60517a1eSAndroid Build Coastguard Worker            separators += os.path.altsep
193*60517a1eSAndroid Build Coastguard Worker        arcname = filename.lstrip(separators)
194*60517a1eSAndroid Build Coastguard Worker
195*60517a1eSAndroid Build Coastguard Worker        zinfo = zipfile.ZipInfo(filename=arcname, date_time=_ZIP_EPOCH)
196*60517a1eSAndroid Build Coastguard Worker        zinfo.create_system = 3  # ZipInfo entry created on a unix-y system
197*60517a1eSAndroid Build Coastguard Worker        # Both pip and installer expect the regular file bit to be set in order for the
198*60517a1eSAndroid Build Coastguard Worker        # executable bit to be preserved after extraction
199*60517a1eSAndroid Build Coastguard Worker        # https://github.com/pypa/pip/blob/23.3.2/src/pip/_internal/utils/unpacking.py#L96-L100
200*60517a1eSAndroid Build Coastguard Worker        # https://github.com/pypa/installer/blob/0.7.0/src/installer/sources.py#L310-L313
201*60517a1eSAndroid Build Coastguard Worker        zinfo.external_attr = (
202*60517a1eSAndroid Build Coastguard Worker            stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO | stat.S_IFREG
203*60517a1eSAndroid Build Coastguard Worker        ) << 16  # permissions: -rwxrwxrwx
204*60517a1eSAndroid Build Coastguard Worker        zinfo.compress_type = self.compression
205*60517a1eSAndroid Build Coastguard Worker        return zinfo
206*60517a1eSAndroid Build Coastguard Worker
207*60517a1eSAndroid Build Coastguard Worker    def add_recordfile(self):
208*60517a1eSAndroid Build Coastguard Worker        """Write RECORD file to the distribution."""
209*60517a1eSAndroid Build Coastguard Worker        record_path = self.distinfo_path("RECORD")
210*60517a1eSAndroid Build Coastguard Worker        entries = self._record + [(record_path, b"", b"")]
211*60517a1eSAndroid Build Coastguard Worker        contents = b""
212*60517a1eSAndroid Build Coastguard Worker        for filename, digest, size in entries:
213*60517a1eSAndroid Build Coastguard Worker            if isinstance(filename, str):
214*60517a1eSAndroid Build Coastguard Worker                filename = filename.lstrip("/").encode("utf-8", "surrogateescape")
215*60517a1eSAndroid Build Coastguard Worker            contents += b"%s,%s,%s\n" % (filename, digest, size)
216*60517a1eSAndroid Build Coastguard Worker
217*60517a1eSAndroid Build Coastguard Worker        self.add_string(record_path, contents)
218*60517a1eSAndroid Build Coastguard Worker        return contents
219*60517a1eSAndroid Build Coastguard Worker
220*60517a1eSAndroid Build Coastguard Worker
221*60517a1eSAndroid Build Coastguard Workerclass WheelMaker(object):
222*60517a1eSAndroid Build Coastguard Worker    def __init__(
223*60517a1eSAndroid Build Coastguard Worker        self,
224*60517a1eSAndroid Build Coastguard Worker        name,
225*60517a1eSAndroid Build Coastguard Worker        version,
226*60517a1eSAndroid Build Coastguard Worker        build_tag,
227*60517a1eSAndroid Build Coastguard Worker        python_tag,
228*60517a1eSAndroid Build Coastguard Worker        abi,
229*60517a1eSAndroid Build Coastguard Worker        platform,
230*60517a1eSAndroid Build Coastguard Worker        outfile=None,
231*60517a1eSAndroid Build Coastguard Worker        strip_path_prefixes=None,
232*60517a1eSAndroid Build Coastguard Worker    ):
233*60517a1eSAndroid Build Coastguard Worker        self._name = name
234*60517a1eSAndroid Build Coastguard Worker        self._version = normalize_pep440(version)
235*60517a1eSAndroid Build Coastguard Worker        self._build_tag = build_tag
236*60517a1eSAndroid Build Coastguard Worker        self._python_tag = python_tag
237*60517a1eSAndroid Build Coastguard Worker        self._abi = abi
238*60517a1eSAndroid Build Coastguard Worker        self._platform = platform
239*60517a1eSAndroid Build Coastguard Worker        self._outfile = outfile
240*60517a1eSAndroid Build Coastguard Worker        self._strip_path_prefixes = strip_path_prefixes
241*60517a1eSAndroid Build Coastguard Worker        self._wheelname_fragment_distribution_name = escape_filename_distribution_name(
242*60517a1eSAndroid Build Coastguard Worker            self._name
243*60517a1eSAndroid Build Coastguard Worker        )
244*60517a1eSAndroid Build Coastguard Worker
245*60517a1eSAndroid Build Coastguard Worker        self._distribution_prefix = (
246*60517a1eSAndroid Build Coastguard Worker            self._wheelname_fragment_distribution_name + "-" + self._version
247*60517a1eSAndroid Build Coastguard Worker        )
248*60517a1eSAndroid Build Coastguard Worker
249*60517a1eSAndroid Build Coastguard Worker        self._whlfile = None
250*60517a1eSAndroid Build Coastguard Worker
251*60517a1eSAndroid Build Coastguard Worker    def __enter__(self):
252*60517a1eSAndroid Build Coastguard Worker        self._whlfile = _WhlFile(
253*60517a1eSAndroid Build Coastguard Worker            self.filename(),
254*60517a1eSAndroid Build Coastguard Worker            mode="w",
255*60517a1eSAndroid Build Coastguard Worker            distribution_prefix=self._distribution_prefix,
256*60517a1eSAndroid Build Coastguard Worker            strip_path_prefixes=self._strip_path_prefixes,
257*60517a1eSAndroid Build Coastguard Worker        )
258*60517a1eSAndroid Build Coastguard Worker        return self
259*60517a1eSAndroid Build Coastguard Worker
260*60517a1eSAndroid Build Coastguard Worker    def __exit__(self, type, value, traceback):
261*60517a1eSAndroid Build Coastguard Worker        self._whlfile.close()
262*60517a1eSAndroid Build Coastguard Worker        self._whlfile = None
263*60517a1eSAndroid Build Coastguard Worker
264*60517a1eSAndroid Build Coastguard Worker    def wheelname(self) -> str:
265*60517a1eSAndroid Build Coastguard Worker        components = [
266*60517a1eSAndroid Build Coastguard Worker            self._wheelname_fragment_distribution_name,
267*60517a1eSAndroid Build Coastguard Worker            self._version,
268*60517a1eSAndroid Build Coastguard Worker        ]
269*60517a1eSAndroid Build Coastguard Worker        if self._build_tag:
270*60517a1eSAndroid Build Coastguard Worker            components.append(self._build_tag)
271*60517a1eSAndroid Build Coastguard Worker        components += [self._python_tag, self._abi, self._platform]
272*60517a1eSAndroid Build Coastguard Worker        return "-".join(components) + ".whl"
273*60517a1eSAndroid Build Coastguard Worker
274*60517a1eSAndroid Build Coastguard Worker    def filename(self) -> str:
275*60517a1eSAndroid Build Coastguard Worker        if self._outfile:
276*60517a1eSAndroid Build Coastguard Worker            return self._outfile
277*60517a1eSAndroid Build Coastguard Worker        return self.wheelname()
278*60517a1eSAndroid Build Coastguard Worker
279*60517a1eSAndroid Build Coastguard Worker    def disttags(self):
280*60517a1eSAndroid Build Coastguard Worker        return ["-".join([self._python_tag, self._abi, self._platform])]
281*60517a1eSAndroid Build Coastguard Worker
282*60517a1eSAndroid Build Coastguard Worker    def distinfo_path(self, basename):
283*60517a1eSAndroid Build Coastguard Worker        return self._whlfile.distinfo_path(basename)
284*60517a1eSAndroid Build Coastguard Worker
285*60517a1eSAndroid Build Coastguard Worker    def data_path(self, basename):
286*60517a1eSAndroid Build Coastguard Worker        return self._whlfile.data_path(basename)
287*60517a1eSAndroid Build Coastguard Worker
288*60517a1eSAndroid Build Coastguard Worker    def add_file(self, package_filename, real_filename):
289*60517a1eSAndroid Build Coastguard Worker        """Add given file to the distribution."""
290*60517a1eSAndroid Build Coastguard Worker        self._whlfile.add_file(package_filename, real_filename)
291*60517a1eSAndroid Build Coastguard Worker
292*60517a1eSAndroid Build Coastguard Worker    def add_wheelfile(self):
293*60517a1eSAndroid Build Coastguard Worker        """Write WHEEL file to the distribution"""
294*60517a1eSAndroid Build Coastguard Worker        # TODO(pstradomski): Support non-purelib wheels.
295*60517a1eSAndroid Build Coastguard Worker        wheel_contents = """\
296*60517a1eSAndroid Build Coastguard WorkerWheel-Version: 1.0
297*60517a1eSAndroid Build Coastguard WorkerGenerator: bazel-wheelmaker 1.0
298*60517a1eSAndroid Build Coastguard WorkerRoot-Is-Purelib: {}
299*60517a1eSAndroid Build Coastguard Worker""".format(
300*60517a1eSAndroid Build Coastguard Worker            "true" if self._platform == "any" else "false"
301*60517a1eSAndroid Build Coastguard Worker        )
302*60517a1eSAndroid Build Coastguard Worker        for tag in self.disttags():
303*60517a1eSAndroid Build Coastguard Worker            wheel_contents += "Tag: %s\n" % tag
304*60517a1eSAndroid Build Coastguard Worker        self._whlfile.add_string(self.distinfo_path("WHEEL"), wheel_contents)
305*60517a1eSAndroid Build Coastguard Worker
306*60517a1eSAndroid Build Coastguard Worker    def add_metadata(self, metadata, name, description):
307*60517a1eSAndroid Build Coastguard Worker        """Write METADATA file to the distribution."""
308*60517a1eSAndroid Build Coastguard Worker        # https://www.python.org/dev/peps/pep-0566/
309*60517a1eSAndroid Build Coastguard Worker        # https://packaging.python.org/specifications/core-metadata/
310*60517a1eSAndroid Build Coastguard Worker        metadata = re.sub("^Name: .*$", "Name: %s" % name, metadata, flags=re.MULTILINE)
311*60517a1eSAndroid Build Coastguard Worker        metadata += "Version: %s\n\n" % self._version
312*60517a1eSAndroid Build Coastguard Worker        # setuptools seems to insert UNKNOWN as description when none is
313*60517a1eSAndroid Build Coastguard Worker        # provided.
314*60517a1eSAndroid Build Coastguard Worker        metadata += description if description else "UNKNOWN"
315*60517a1eSAndroid Build Coastguard Worker        metadata += "\n"
316*60517a1eSAndroid Build Coastguard Worker        self._whlfile.add_string(self.distinfo_path("METADATA"), metadata)
317*60517a1eSAndroid Build Coastguard Worker
318*60517a1eSAndroid Build Coastguard Worker    def add_recordfile(self):
319*60517a1eSAndroid Build Coastguard Worker        """Write RECORD file to the distribution."""
320*60517a1eSAndroid Build Coastguard Worker        self._whlfile.add_recordfile()
321*60517a1eSAndroid Build Coastguard Worker
322*60517a1eSAndroid Build Coastguard Worker
323*60517a1eSAndroid Build Coastguard Workerdef get_files_to_package(input_files):
324*60517a1eSAndroid Build Coastguard Worker    """Find files to be added to the distribution.
325*60517a1eSAndroid Build Coastguard Worker
326*60517a1eSAndroid Build Coastguard Worker    input_files: list of pairs (package_path, real_path)
327*60517a1eSAndroid Build Coastguard Worker    """
328*60517a1eSAndroid Build Coastguard Worker    files = {}
329*60517a1eSAndroid Build Coastguard Worker    for package_path, real_path in input_files:
330*60517a1eSAndroid Build Coastguard Worker        files[package_path] = real_path
331*60517a1eSAndroid Build Coastguard Worker    return files
332*60517a1eSAndroid Build Coastguard Worker
333*60517a1eSAndroid Build Coastguard Worker
334*60517a1eSAndroid Build Coastguard Workerdef resolve_argument_stamp(
335*60517a1eSAndroid Build Coastguard Worker    argument: str, volatile_status_stamp: Path, stable_status_stamp: Path
336*60517a1eSAndroid Build Coastguard Worker) -> str:
337*60517a1eSAndroid Build Coastguard Worker    """Resolve workspace status stamps format strings found in the argument string
338*60517a1eSAndroid Build Coastguard Worker
339*60517a1eSAndroid Build Coastguard Worker    Args:
340*60517a1eSAndroid Build Coastguard Worker        argument (str): The raw argument represenation for the wheel (may include stamp variables)
341*60517a1eSAndroid Build Coastguard Worker        volatile_status_stamp (Path): The path to a volatile workspace status file
342*60517a1eSAndroid Build Coastguard Worker        stable_status_stamp (Path): The path to a stable workspace status file
343*60517a1eSAndroid Build Coastguard Worker
344*60517a1eSAndroid Build Coastguard Worker    Returns:
345*60517a1eSAndroid Build Coastguard Worker        str: A resolved argument string
346*60517a1eSAndroid Build Coastguard Worker    """
347*60517a1eSAndroid Build Coastguard Worker    lines = (
348*60517a1eSAndroid Build Coastguard Worker        volatile_status_stamp.read_text().splitlines()
349*60517a1eSAndroid Build Coastguard Worker        + stable_status_stamp.read_text().splitlines()
350*60517a1eSAndroid Build Coastguard Worker    )
351*60517a1eSAndroid Build Coastguard Worker    for line in lines:
352*60517a1eSAndroid Build Coastguard Worker        if not line:
353*60517a1eSAndroid Build Coastguard Worker            continue
354*60517a1eSAndroid Build Coastguard Worker        key, value = line.split(" ", maxsplit=1)
355*60517a1eSAndroid Build Coastguard Worker        stamp = "{" + key + "}"
356*60517a1eSAndroid Build Coastguard Worker        argument = argument.replace(stamp, value)
357*60517a1eSAndroid Build Coastguard Worker
358*60517a1eSAndroid Build Coastguard Worker    return argument
359*60517a1eSAndroid Build Coastguard Worker
360*60517a1eSAndroid Build Coastguard Worker
361*60517a1eSAndroid Build Coastguard Workerdef parse_args() -> argparse.Namespace:
362*60517a1eSAndroid Build Coastguard Worker    parser = argparse.ArgumentParser(description="Builds a python wheel")
363*60517a1eSAndroid Build Coastguard Worker    metadata_group = parser.add_argument_group("Wheel name, version and platform")
364*60517a1eSAndroid Build Coastguard Worker    metadata_group.add_argument(
365*60517a1eSAndroid Build Coastguard Worker        "--name", required=True, type=str, help="Name of the distribution"
366*60517a1eSAndroid Build Coastguard Worker    )
367*60517a1eSAndroid Build Coastguard Worker    metadata_group.add_argument(
368*60517a1eSAndroid Build Coastguard Worker        "--version", required=True, type=str, help="Version of the distribution"
369*60517a1eSAndroid Build Coastguard Worker    )
370*60517a1eSAndroid Build Coastguard Worker    metadata_group.add_argument(
371*60517a1eSAndroid Build Coastguard Worker        "--build_tag",
372*60517a1eSAndroid Build Coastguard Worker        type=str,
373*60517a1eSAndroid Build Coastguard Worker        default="",
374*60517a1eSAndroid Build Coastguard Worker        help="Optional build tag for the distribution",
375*60517a1eSAndroid Build Coastguard Worker    )
376*60517a1eSAndroid Build Coastguard Worker    metadata_group.add_argument(
377*60517a1eSAndroid Build Coastguard Worker        "--python_tag",
378*60517a1eSAndroid Build Coastguard Worker        type=str,
379*60517a1eSAndroid Build Coastguard Worker        default="py3",
380*60517a1eSAndroid Build Coastguard Worker        help="Python version, e.g. 'py2' or 'py3'",
381*60517a1eSAndroid Build Coastguard Worker    )
382*60517a1eSAndroid Build Coastguard Worker    metadata_group.add_argument("--abi", type=str, default="none")
383*60517a1eSAndroid Build Coastguard Worker    metadata_group.add_argument(
384*60517a1eSAndroid Build Coastguard Worker        "--platform", type=str, default="any", help="Target platform. "
385*60517a1eSAndroid Build Coastguard Worker    )
386*60517a1eSAndroid Build Coastguard Worker
387*60517a1eSAndroid Build Coastguard Worker    output_group = parser.add_argument_group("Output file location")
388*60517a1eSAndroid Build Coastguard Worker    output_group.add_argument(
389*60517a1eSAndroid Build Coastguard Worker        "--out", type=str, default=None, help="Override name of ouptut file"
390*60517a1eSAndroid Build Coastguard Worker    )
391*60517a1eSAndroid Build Coastguard Worker    output_group.add_argument(
392*60517a1eSAndroid Build Coastguard Worker        "--name_file",
393*60517a1eSAndroid Build Coastguard Worker        type=Path,
394*60517a1eSAndroid Build Coastguard Worker        help="A file where the canonical name of the " "wheel will be written",
395*60517a1eSAndroid Build Coastguard Worker    )
396*60517a1eSAndroid Build Coastguard Worker
397*60517a1eSAndroid Build Coastguard Worker    output_group.add_argument(
398*60517a1eSAndroid Build Coastguard Worker        "--strip_path_prefix",
399*60517a1eSAndroid Build Coastguard Worker        type=str,
400*60517a1eSAndroid Build Coastguard Worker        action="append",
401*60517a1eSAndroid Build Coastguard Worker        default=[],
402*60517a1eSAndroid Build Coastguard Worker        help="Path prefix to be stripped from input package files' path. "
403*60517a1eSAndroid Build Coastguard Worker        "Can be supplied multiple times. Evaluated in order.",
404*60517a1eSAndroid Build Coastguard Worker    )
405*60517a1eSAndroid Build Coastguard Worker
406*60517a1eSAndroid Build Coastguard Worker    wheel_group = parser.add_argument_group("Wheel metadata")
407*60517a1eSAndroid Build Coastguard Worker    wheel_group.add_argument(
408*60517a1eSAndroid Build Coastguard Worker        "--metadata_file",
409*60517a1eSAndroid Build Coastguard Worker        type=Path,
410*60517a1eSAndroid Build Coastguard Worker        help="Contents of the METADATA file (before appending contents of "
411*60517a1eSAndroid Build Coastguard Worker        "--description_file)",
412*60517a1eSAndroid Build Coastguard Worker    )
413*60517a1eSAndroid Build Coastguard Worker    wheel_group.add_argument(
414*60517a1eSAndroid Build Coastguard Worker        "--description_file", help="Path to the file with package description"
415*60517a1eSAndroid Build Coastguard Worker    )
416*60517a1eSAndroid Build Coastguard Worker    wheel_group.add_argument(
417*60517a1eSAndroid Build Coastguard Worker        "--description_content_type", help="Content type of the package description"
418*60517a1eSAndroid Build Coastguard Worker    )
419*60517a1eSAndroid Build Coastguard Worker    wheel_group.add_argument(
420*60517a1eSAndroid Build Coastguard Worker        "--entry_points_file",
421*60517a1eSAndroid Build Coastguard Worker        help="Path to a correctly-formatted entry_points.txt file",
422*60517a1eSAndroid Build Coastguard Worker    )
423*60517a1eSAndroid Build Coastguard Worker
424*60517a1eSAndroid Build Coastguard Worker    contents_group = parser.add_argument_group("Wheel contents")
425*60517a1eSAndroid Build Coastguard Worker    contents_group.add_argument(
426*60517a1eSAndroid Build Coastguard Worker        "--input_file",
427*60517a1eSAndroid Build Coastguard Worker        action="append",
428*60517a1eSAndroid Build Coastguard Worker        help="'package_path;real_path' pairs listing "
429*60517a1eSAndroid Build Coastguard Worker        "files to be included in the wheel. "
430*60517a1eSAndroid Build Coastguard Worker        "Can be supplied multiple times.",
431*60517a1eSAndroid Build Coastguard Worker    )
432*60517a1eSAndroid Build Coastguard Worker    contents_group.add_argument(
433*60517a1eSAndroid Build Coastguard Worker        "--input_file_list",
434*60517a1eSAndroid Build Coastguard Worker        action="append",
435*60517a1eSAndroid Build Coastguard Worker        help="A file that has all the input files defined as a list to avoid "
436*60517a1eSAndroid Build Coastguard Worker        "the long command",
437*60517a1eSAndroid Build Coastguard Worker    )
438*60517a1eSAndroid Build Coastguard Worker    contents_group.add_argument(
439*60517a1eSAndroid Build Coastguard Worker        "--extra_distinfo_file",
440*60517a1eSAndroid Build Coastguard Worker        action="append",
441*60517a1eSAndroid Build Coastguard Worker        help="'filename;real_path' pairs listing extra files to include in"
442*60517a1eSAndroid Build Coastguard Worker        "dist-info directory. Can be supplied multiple times.",
443*60517a1eSAndroid Build Coastguard Worker    )
444*60517a1eSAndroid Build Coastguard Worker    contents_group.add_argument(
445*60517a1eSAndroid Build Coastguard Worker        "--data_files",
446*60517a1eSAndroid Build Coastguard Worker        action="append",
447*60517a1eSAndroid Build Coastguard Worker        help="'filename;real_path' pairs listing data files to include in"
448*60517a1eSAndroid Build Coastguard Worker        "data directory. Can be supplied multiple times.",
449*60517a1eSAndroid Build Coastguard Worker    )
450*60517a1eSAndroid Build Coastguard Worker
451*60517a1eSAndroid Build Coastguard Worker    build_group = parser.add_argument_group("Building requirements")
452*60517a1eSAndroid Build Coastguard Worker    build_group.add_argument(
453*60517a1eSAndroid Build Coastguard Worker        "--volatile_status_file",
454*60517a1eSAndroid Build Coastguard Worker        type=Path,
455*60517a1eSAndroid Build Coastguard Worker        help="Pass in the stamp info file for stamping",
456*60517a1eSAndroid Build Coastguard Worker    )
457*60517a1eSAndroid Build Coastguard Worker    build_group.add_argument(
458*60517a1eSAndroid Build Coastguard Worker        "--stable_status_file",
459*60517a1eSAndroid Build Coastguard Worker        type=Path,
460*60517a1eSAndroid Build Coastguard Worker        help="Pass in the stamp info file for stamping",
461*60517a1eSAndroid Build Coastguard Worker    )
462*60517a1eSAndroid Build Coastguard Worker
463*60517a1eSAndroid Build Coastguard Worker    return parser.parse_args(sys.argv[1:])
464*60517a1eSAndroid Build Coastguard Worker
465*60517a1eSAndroid Build Coastguard Worker
466*60517a1eSAndroid Build Coastguard Workerdef _parse_file_pairs(content: List[str]) -> List[List[str]]:
467*60517a1eSAndroid Build Coastguard Worker    """
468*60517a1eSAndroid Build Coastguard Worker    Parse ; delimited lists of files into a 2D list.
469*60517a1eSAndroid Build Coastguard Worker    """
470*60517a1eSAndroid Build Coastguard Worker    return [i.split(";", maxsplit=1) for i in content or []]
471*60517a1eSAndroid Build Coastguard Worker
472*60517a1eSAndroid Build Coastguard Worker
473*60517a1eSAndroid Build Coastguard Workerdef main() -> None:
474*60517a1eSAndroid Build Coastguard Worker    arguments = parse_args()
475*60517a1eSAndroid Build Coastguard Worker
476*60517a1eSAndroid Build Coastguard Worker    input_files = _parse_file_pairs(arguments.input_file)
477*60517a1eSAndroid Build Coastguard Worker    extra_distinfo_file = _parse_file_pairs(arguments.extra_distinfo_file)
478*60517a1eSAndroid Build Coastguard Worker    data_files = _parse_file_pairs(arguments.data_files)
479*60517a1eSAndroid Build Coastguard Worker
480*60517a1eSAndroid Build Coastguard Worker    for input_file in arguments.input_file_list:
481*60517a1eSAndroid Build Coastguard Worker        with open(input_file) as _file:
482*60517a1eSAndroid Build Coastguard Worker            input_file_list = _file.read().splitlines()
483*60517a1eSAndroid Build Coastguard Worker        for _input_file in input_file_list:
484*60517a1eSAndroid Build Coastguard Worker            input_files.append(_input_file.split(";"))
485*60517a1eSAndroid Build Coastguard Worker
486*60517a1eSAndroid Build Coastguard Worker    all_files = get_files_to_package(input_files)
487*60517a1eSAndroid Build Coastguard Worker    # Sort the files for reproducible order in the archive.
488*60517a1eSAndroid Build Coastguard Worker    all_files = sorted(all_files.items())
489*60517a1eSAndroid Build Coastguard Worker
490*60517a1eSAndroid Build Coastguard Worker    strip_prefixes = [p for p in arguments.strip_path_prefix]
491*60517a1eSAndroid Build Coastguard Worker
492*60517a1eSAndroid Build Coastguard Worker    if arguments.volatile_status_file and arguments.stable_status_file:
493*60517a1eSAndroid Build Coastguard Worker        name = resolve_argument_stamp(
494*60517a1eSAndroid Build Coastguard Worker            arguments.name,
495*60517a1eSAndroid Build Coastguard Worker            arguments.volatile_status_file,
496*60517a1eSAndroid Build Coastguard Worker            arguments.stable_status_file,
497*60517a1eSAndroid Build Coastguard Worker        )
498*60517a1eSAndroid Build Coastguard Worker    else:
499*60517a1eSAndroid Build Coastguard Worker        name = arguments.name
500*60517a1eSAndroid Build Coastguard Worker
501*60517a1eSAndroid Build Coastguard Worker    if arguments.volatile_status_file and arguments.stable_status_file:
502*60517a1eSAndroid Build Coastguard Worker        version = resolve_argument_stamp(
503*60517a1eSAndroid Build Coastguard Worker            arguments.version,
504*60517a1eSAndroid Build Coastguard Worker            arguments.volatile_status_file,
505*60517a1eSAndroid Build Coastguard Worker            arguments.stable_status_file,
506*60517a1eSAndroid Build Coastguard Worker        )
507*60517a1eSAndroid Build Coastguard Worker    else:
508*60517a1eSAndroid Build Coastguard Worker        version = arguments.version
509*60517a1eSAndroid Build Coastguard Worker
510*60517a1eSAndroid Build Coastguard Worker    with WheelMaker(
511*60517a1eSAndroid Build Coastguard Worker        name=name,
512*60517a1eSAndroid Build Coastguard Worker        version=version,
513*60517a1eSAndroid Build Coastguard Worker        build_tag=arguments.build_tag,
514*60517a1eSAndroid Build Coastguard Worker        python_tag=arguments.python_tag,
515*60517a1eSAndroid Build Coastguard Worker        abi=arguments.abi,
516*60517a1eSAndroid Build Coastguard Worker        platform=arguments.platform,
517*60517a1eSAndroid Build Coastguard Worker        outfile=arguments.out,
518*60517a1eSAndroid Build Coastguard Worker        strip_path_prefixes=strip_prefixes,
519*60517a1eSAndroid Build Coastguard Worker    ) as maker:
520*60517a1eSAndroid Build Coastguard Worker        for package_filename, real_filename in all_files:
521*60517a1eSAndroid Build Coastguard Worker            maker.add_file(package_filename, real_filename)
522*60517a1eSAndroid Build Coastguard Worker        maker.add_wheelfile()
523*60517a1eSAndroid Build Coastguard Worker
524*60517a1eSAndroid Build Coastguard Worker        description = None
525*60517a1eSAndroid Build Coastguard Worker        if arguments.description_file:
526*60517a1eSAndroid Build Coastguard Worker            with open(
527*60517a1eSAndroid Build Coastguard Worker                arguments.description_file, "rt", encoding="utf-8"
528*60517a1eSAndroid Build Coastguard Worker            ) as description_file:
529*60517a1eSAndroid Build Coastguard Worker                description = description_file.read()
530*60517a1eSAndroid Build Coastguard Worker
531*60517a1eSAndroid Build Coastguard Worker        metadata = arguments.metadata_file.read_text(encoding="utf-8")
532*60517a1eSAndroid Build Coastguard Worker
533*60517a1eSAndroid Build Coastguard Worker        # This is not imported at the top of the file due to the reliance
534*60517a1eSAndroid Build Coastguard Worker        # on this file in the `whl_library` repository rule which does not
535*60517a1eSAndroid Build Coastguard Worker        # provide `packaging` but does import symbols defined here.
536*60517a1eSAndroid Build Coastguard Worker        from packaging.requirements import Requirement
537*60517a1eSAndroid Build Coastguard Worker
538*60517a1eSAndroid Build Coastguard Worker        # Search for any `Requires-Dist` entries that refer to other files and
539*60517a1eSAndroid Build Coastguard Worker        # expand them.
540*60517a1eSAndroid Build Coastguard Worker
541*60517a1eSAndroid Build Coastguard Worker        def get_new_requirement_line(reqs_text, extra):
542*60517a1eSAndroid Build Coastguard Worker            req = Requirement(reqs_text.strip())
543*60517a1eSAndroid Build Coastguard Worker            if req.marker:
544*60517a1eSAndroid Build Coastguard Worker                if extra:
545*60517a1eSAndroid Build Coastguard Worker                    return f"Requires-Dist: {req.name}{req.specifier}; ({req.marker}) and {extra}"
546*60517a1eSAndroid Build Coastguard Worker                else:
547*60517a1eSAndroid Build Coastguard Worker                    return f"Requires-Dist: {req.name}{req.specifier}; {req.marker}"
548*60517a1eSAndroid Build Coastguard Worker            else:
549*60517a1eSAndroid Build Coastguard Worker                return f"Requires-Dist: {req.name}{req.specifier}; {extra}".strip(" ;")
550*60517a1eSAndroid Build Coastguard Worker
551*60517a1eSAndroid Build Coastguard Worker        for meta_line in metadata.splitlines():
552*60517a1eSAndroid Build Coastguard Worker            if not meta_line.startswith("Requires-Dist: "):
553*60517a1eSAndroid Build Coastguard Worker                continue
554*60517a1eSAndroid Build Coastguard Worker
555*60517a1eSAndroid Build Coastguard Worker            if not meta_line[len("Requires-Dist: ") :].startswith("@"):
556*60517a1eSAndroid Build Coastguard Worker                # This is a normal requirement.
557*60517a1eSAndroid Build Coastguard Worker                package, _, extra = meta_line[len("Requires-Dist: ") :].rpartition(";")
558*60517a1eSAndroid Build Coastguard Worker                if not package:
559*60517a1eSAndroid Build Coastguard Worker                    # This is when the package requirement does not have markers.
560*60517a1eSAndroid Build Coastguard Worker                    continue
561*60517a1eSAndroid Build Coastguard Worker                extra = extra.strip()
562*60517a1eSAndroid Build Coastguard Worker                metadata = metadata.replace(
563*60517a1eSAndroid Build Coastguard Worker                    meta_line, get_new_requirement_line(package, extra)
564*60517a1eSAndroid Build Coastguard Worker                )
565*60517a1eSAndroid Build Coastguard Worker                continue
566*60517a1eSAndroid Build Coastguard Worker
567*60517a1eSAndroid Build Coastguard Worker            # This is a requirement that refers to a file.
568*60517a1eSAndroid Build Coastguard Worker            file, _, extra = meta_line[len("Requires-Dist: @") :].partition(";")
569*60517a1eSAndroid Build Coastguard Worker            extra = extra.strip()
570*60517a1eSAndroid Build Coastguard Worker
571*60517a1eSAndroid Build Coastguard Worker            reqs = []
572*60517a1eSAndroid Build Coastguard Worker            for reqs_line in Path(file).read_text(encoding="utf-8").splitlines():
573*60517a1eSAndroid Build Coastguard Worker                reqs_text = reqs_line.strip()
574*60517a1eSAndroid Build Coastguard Worker                if not reqs_text or reqs_text.startswith(("#", "-")):
575*60517a1eSAndroid Build Coastguard Worker                    continue
576*60517a1eSAndroid Build Coastguard Worker
577*60517a1eSAndroid Build Coastguard Worker                # Strip any comments
578*60517a1eSAndroid Build Coastguard Worker                reqs_text, _, _ = reqs_text.partition("#")
579*60517a1eSAndroid Build Coastguard Worker
580*60517a1eSAndroid Build Coastguard Worker                reqs.append(get_new_requirement_line(reqs_text, extra))
581*60517a1eSAndroid Build Coastguard Worker
582*60517a1eSAndroid Build Coastguard Worker            metadata = metadata.replace(meta_line, "\n".join(reqs))
583*60517a1eSAndroid Build Coastguard Worker
584*60517a1eSAndroid Build Coastguard Worker        maker.add_metadata(
585*60517a1eSAndroid Build Coastguard Worker            metadata=metadata,
586*60517a1eSAndroid Build Coastguard Worker            name=name,
587*60517a1eSAndroid Build Coastguard Worker            description=description,
588*60517a1eSAndroid Build Coastguard Worker        )
589*60517a1eSAndroid Build Coastguard Worker
590*60517a1eSAndroid Build Coastguard Worker        if arguments.entry_points_file:
591*60517a1eSAndroid Build Coastguard Worker            maker.add_file(
592*60517a1eSAndroid Build Coastguard Worker                maker.distinfo_path("entry_points.txt"), arguments.entry_points_file
593*60517a1eSAndroid Build Coastguard Worker            )
594*60517a1eSAndroid Build Coastguard Worker
595*60517a1eSAndroid Build Coastguard Worker        # Sort the files for reproducible order in the archive.
596*60517a1eSAndroid Build Coastguard Worker        for filename, real_path in sorted(data_files):
597*60517a1eSAndroid Build Coastguard Worker            maker.add_file(maker.data_path(filename), real_path)
598*60517a1eSAndroid Build Coastguard Worker        for filename, real_path in sorted(extra_distinfo_file):
599*60517a1eSAndroid Build Coastguard Worker            maker.add_file(maker.distinfo_path(filename), real_path)
600*60517a1eSAndroid Build Coastguard Worker
601*60517a1eSAndroid Build Coastguard Worker        maker.add_recordfile()
602*60517a1eSAndroid Build Coastguard Worker
603*60517a1eSAndroid Build Coastguard Worker        # Since stamping may otherwise change the target name of the
604*60517a1eSAndroid Build Coastguard Worker        # wheel, the canonical name (with stamps resolved) is written
605*60517a1eSAndroid Build Coastguard Worker        # to a file so consumers of the wheel can easily determine
606*60517a1eSAndroid Build Coastguard Worker        # the correct name.
607*60517a1eSAndroid Build Coastguard Worker        arguments.name_file.write_text(maker.wheelname())
608*60517a1eSAndroid Build Coastguard Worker
609*60517a1eSAndroid Build Coastguard Worker
610*60517a1eSAndroid Build Coastguard Workerif __name__ == "__main__":
611*60517a1eSAndroid Build Coastguard Worker    main()
612