Mercurial > hgrepos > Python > apps > py-cutils
comparison intree-build-helper/cutils_build.py @ 407:d3429477cd55 build-2.7
MERGE: current trunk
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Tue, 17 Feb 2026 16:19:53 +0100 |
| parents | deabdfed3b96 |
| children |
comparison
equal
deleted
inserted
replaced
| 404:6930917a3752 | 407:d3429477cd55 |
|---|---|
| 1 # -*- coding: utf-8 -*- | |
| 2 # :- | |
| 3 # SPDX-FileCopyrightText: © 2025-2026 Franz Glasner | |
| 4 # SPDX-License-Identifier: BSD-3-Clause | |
| 5 # :- | |
| 6 r"""An intree build backend that is mostly just a wrapper for | |
| 7 :mod:`setuptools.build_meta`. | |
| 8 | |
| 9 Augments :fn:`build_sdist` to automatically postprocess it | |
| 10 (i.e. add symlinks as symlinks). | |
| 11 | |
| 12 """ | |
| 13 | |
| 14 # Firstly, we are just a wrapper for setuptools.build_meta | |
| 15 from setuptools.build_meta import * # noqa:F403,F401 | |
| 16 from setuptools.build_meta import build_sdist as _orig_build_sdist | |
| 17 | |
| 18 import gzip as _gzip | |
| 19 import io as _io | |
| 20 import logging as _logging | |
| 21 import os as _os | |
| 22 import tarfile as _tarfile | |
| 23 | |
| 24 | |
| 25 _log = _logging.getLogger(__name__) | |
| 26 | |
| 27 | |
| 28 def _postprocess_sdist(sdist_directory, sdist_archive, config_settings): | |
| 29 _log.info("post-processing the sdist %r ...", sdist_archive) | |
| 30 # | |
| 31 # PEP 625 requires that sdist archive filenames are of the form | |
| 32 # <normalized_project_name>-<project_version>.tar.gz | |
| 33 # | |
| 34 if sdist_archive.endswith(".tar.gz"): | |
| 35 uncompressed_sdist_archive = sdist_archive[:-3] | |
| 36 # the directory prefix within the archive | |
| 37 archive_path_prefix = uncompressed_sdist_archive[:-4] | |
| 38 normalized_project_name, sep, project_version = \ | |
| 39 archive_path_prefix.rpartition('-') | |
| 40 if not sep: | |
| 41 raise ValueError( | |
| 42 "unexpected archive path prefix: %s" % (archive_path_prefix,)) | |
| 43 else: | |
| 44 raise ValueError("unexpected archive name: %s" % (sdist_archive,)) | |
| 45 | |
| 46 uncompressed_sdist_path = f"{sdist_directory}/{uncompressed_sdist_archive}" | |
| 47 | |
| 48 # Metadata directories in the FS and the archive | |
| 49 egg_directory = f"{normalized_project_name}.egg-info" | |
| 50 if not _os.path.isdir(egg_directory): | |
| 51 raise RuntimeError("directory does not exist: %s" % (egg_directory,)) | |
| 52 sources_txt_path = f"{egg_directory}/SOURCES.txt" | |
| 53 sources_txt_arcname = f"{archive_path_prefix}/{egg_directory}/SOURCES.txt" | |
| 54 | |
| 55 if _os.path.isfile(uncompressed_sdist_path): | |
| 56 _log.warning("warning: overwriting existing %r", | |
| 57 uncompressed_sdist_path) | |
| 58 | |
| 59 # Uncompress | |
| 60 _log.info("uncompressing the created archive %r into %r", | |
| 61 f"{sdist_directory}/{sdist_archive}", | |
| 62 uncompressed_sdist_path) | |
| 63 with _gzip.GzipFile(f"{sdist_directory}/{sdist_archive}", | |
| 64 mode="rb") as ca: | |
| 65 with open(uncompressed_sdist_path, "wb") as uca: | |
| 66 while True: | |
| 67 data = ca.read(64*1024) | |
| 68 if not data: | |
| 69 break | |
| 70 uca.write(data) | |
| 71 | |
| 72 # Get SOURCES.txt from the metadata within the sdist | |
| 73 with _tarfile.TarFile(uncompressed_sdist_path, "r") as tf: | |
| 74 sf = tf.extractfile(sources_txt_arcname) | |
| 75 try: | |
| 76 sources_txt = sf.read() | |
| 77 finally: | |
| 78 sf.close() | |
| 79 | |
| 80 with _tarfile.TarFile(uncompressed_sdist_path, "a") as tf: | |
| 81 arcname = "{}/tests/data".format(archive_path_prefix) | |
| 82 try: | |
| 83 info = tf.getmember(arcname) | |
| 84 except KeyError: | |
| 85 pass | |
| 86 else: | |
| 87 raise RuntimeError("already postprocessed") | |
| 88 pre_names = set(tf.getnames()) | |
| 89 tf.add("tests/data", arcname=arcname, recursive=True) | |
| 90 | |
| 91 # | |
| 92 # Determine the new files and symlinks that are to be added | |
| 93 # to SOURCES.txt. Skip directories. | |
| 94 # | |
| 95 post_names = set(tf.getnames()) | |
| 96 new_names = list(post_names - pre_names) | |
| 97 new_names.sort() | |
| 98 new_sources = [] | |
| 99 | |
| 100 for np in new_names: | |
| 101 nn = np[len(archive_path_prefix)+1:] | |
| 102 info = tf.getmember(np) | |
| 103 if not info.isdir(): | |
| 104 _log.info("adding %s -> %s", nn, np) | |
| 105 new_sources.append(nn) | |
| 106 | |
| 107 # Augment SOURCES.txt and add it to the archive | |
| 108 sources_info = tf.gettarinfo( | |
| 109 sources_txt_path, arcname=sources_txt_arcname) | |
| 110 sf = _io.BytesIO() | |
| 111 sf.write(sources_txt) | |
| 112 if not sources_txt.endswith(b'\n'): | |
| 113 sf.write(b'\n') | |
| 114 sf.write(_b('\n'.join(new_sources))) | |
| 115 sources_info.size = len(sf.getvalue()) | |
| 116 sf.seek(0) | |
| 117 # | |
| 118 # This adds SOURCES.txt a 2nd time -- effectively overwriting | |
| 119 # the "earlier" one. | |
| 120 # | |
| 121 tf.addfile(sources_info, sf) | |
| 122 | |
| 123 # Compress | |
| 124 _log.info("recompressing the augmented archive %r into %r", | |
| 125 uncompressed_sdist_path, | |
| 126 f"{sdist_directory}/{sdist_archive}") | |
| 127 with open(uncompressed_sdist_path, "rb") as uca: | |
| 128 with open(f"{sdist_directory}/{sdist_archive}", "wb") as ca: | |
| 129 with _gzip.GzipFile(filename=uncompressed_sdist_archive, | |
| 130 fileobj=ca, | |
| 131 mode="wb") as gzfile: | |
| 132 while True: | |
| 133 data = uca.read(64*1024) | |
| 134 if not data: | |
| 135 break | |
| 136 gzfile.write(data) | |
| 137 | |
| 138 _log.info("post-processing the sdist done.") | |
| 139 return sdist_archive | |
| 140 | |
| 141 | |
| 142 def _b(buf, encoding="ascii"): | |
| 143 if isinstance(buf, bytes): | |
| 144 return buf | |
| 145 else: | |
| 146 return buf.encode(encoding) | |
| 147 | |
| 148 | |
| 149 def build_sdist(sdist_directory, config_settings=None): | |
| 150 # NOTE: logging is obviously set to level WARN (default?) | |
| 151 _log.debug( | |
| 152 "debug: build_sdist in cutils_build called with params" | |
| 153 " sdist_directory=%r, config_settings=%r", | |
| 154 sdist_directory, config_settings) | |
| 155 # NOTE: orig_build_sdist re-configures logging to level INFO | |
| 156 sdist_archive = _orig_build_sdist( | |
| 157 sdist_directory, config_settings=config_settings) | |
| 158 return _postprocess_sdist(sdist_directory, sdist_archive, config_settings) |
