PyXMake Developer Guide: src/PyXMake/Plugin/__build.py Source File

| PyXMake Developer Guide 1.0 PyXMake

Loading...
Searching...
No Matches
__build.py
1# -*- coding: utf-8 -*-
2# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3# % Build wrapper module - Classes and functions %
4# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5"""
6Build configuration and management assistance wrapper.
7
8@note:
9Created on 03.02.2024
10
11@version: 1.0
12----------------------------------------------------------------------------------------------
13@requires:
14 -
15
16@change:
17 -
18
19@author: garb_ma [DLR-SY,STM Braunschweig]
20----------------------------------------------------------------------------------------------
21"""
22
23import os, sys, re
24import subprocess
25import signal
26from distutils import log, util
27
28 def build(*args, **kwargs):
29 """
30 Shim interface for python wheel setup
31 """
32 # All local imports
33 from setuptools.command.build_py import build_py as _build_py #@UnresolvedImport
34 from setuptools.command.egg_info import egg_info as _egg_info
35 from wheel.bdist_wheel import bdist_wheel as _bdist_wheel
36
37 class build_py(_build_py):
38 """
39 Compatibility shim for build_py
40 """
41 def initialize_options(self):
42 """
43 Shim interface for all options
44 """
45 # Execute original class method
46 _build_py.initialize_options(self)
47
48 # Update class attributes with user settings
49 try:
50 wc = self.get_finalized_command("bdist_wheel", 0)
51 self.exclude_source_files = wc and wc.exclude_source_files
52 self.bdist_wheel = True
53 except:
54 self.bdist_wheel = False
55 self.exclude_source_files = False
56
57 def finalize_options(self):
58 """
59 Shim interface to finalize all inputs
60 """
61 # User requests to remove all source files (and create a meta package)
62 if self.exclude_source_files:
63 self.force = 1
64 self.compile = 1
65 # Execute original class method
66 _build_py.finalize_options(self)
67
68 def byte_compile(self, files):
69 """
70 Shim interface for class attribute byte_compile
71 """
72 # User requests to remove all source files (and create a meta package)
73 if self.exclude_source_files:
74 for file in files:
75 if os.path.isfile(file):
76 try:
77 os.unlink(file)
78 log.info('removing source file %s', file)
79 except: pass
80 # Execute original class method. Only if required and source files are included
81 else: _build_py.byte_compile(self, files)
82
83 def run(self):
84 """
85 Shim interface to execute the process
86 """
87 if not self.bdist_wheel and not self.exclude_source_files:
88 command = [sys.executable,"setup.py","bdist_wheel"]
89 if kwargs.get("platform_tag",False): command.append("--platform-tag")
90 if kwargs.get("exclude_source_files",False): command.append("--exclude-source-files")
91 # Execute a new subprocess
92 subprocess.check_call(command)
93 return
94 # Execute original class method.
95 _build_py.run(self)
96
97 class bdist_wheel(_bdist_wheel):
98 """
99 Compatibility shim for bdist_wheel
100 """
101 # Update default user settings
102 _bdist_wheel.user_options.append(('exclude-source-files', None, "Remove all .py files from the generated wheel"))
103 _bdist_wheel.user_options.append(('platform-tag', None, "Enforce the creation of a platform-tag"))
104
105 def initialize_options(self):
106 """
107 Shim interface for all options
108 """
109 # Execute original class method
110 _bdist_wheel.initialize_options(self)
111 # Update default settings
112 self.python_tag = None
113 self.exclude_source_files = False
114 self.platform_tag = False
115
116 def finalize_options(self):
117 """
118 Shim interface to finalize all inputs
119 """
120 # Execute original class method
121 _bdist_wheel.finalize_options(self)
122 # Read python tag from user input
123 self.python_tag = kwargs.get("python_tag", "py2.py3")
124 # If no python tag is given - create a pure wheel
125 if self.python_tag in [None,"py2.py3"]: self.root_is_pure = True
126
127 def run(self):
128 """
129 Shim interface to execute the process
130 """
131 # Execute original class method
132 _bdist_wheel.run(self)
133
134 # Update the platform tag
135 if self.platform_tag:
136 # Use the current platform to update the wheel
137 platform_tag = re.sub(r'[^0-9a-zA-Z]+','_', util.get_platform())
138 build_wheel = [os.path.join(os.getcwd(),"dist",x) for x in os.listdir(os.path.join(os.getcwd(),"dist")) if x.endswith(".whl")][-1]
139 subprocess.check_call([sys.executable,"-m","wheel","tags","--platform-tag", platform_tag, build_wheel])
140 # Remove the the oudated wheel
141 os.remove(os.path.join(os.getcwd(),"dist",build_wheel))
142
143 # We created a wheel using user-defined settings. Kill the main process
144 if os.getenv("pyx_poetry_main_pid",""):
145 try:
146 os.remove(os.path.join(os.getcwd(),"setup.py"))
147 os.kill(int(os.getenv("pyx_poetry_main_pid")), getattr(signal,"CTRL_C_EVENT",signal.SIGTERM))
148 except: pass
149
150 class egg_info(_egg_info):
151 """
152 Compatibility shim for egg_info
153 """
154 def run(self):
155 """
156 Shim interface to execute the process
157 """
158 # Execute original class method
159 _egg_info.run(self)
160
161 # Get rid of top_level.txt if it was put in there
162 nl = os.path.join(self.egg_info, "top_level.txt")
163 if os.path.exists(nl): self.delete_file(nl)
164
165 # Parse settings from poetry when a setup file is generated
166 if args: settings = args[-1]
167
168 # Update these settings
169 settings.update({"setup_requires":['wheel>=0.30;python_version>="2.7"', 'wheel==0.29;python_version<"2.7"'],
170 "cmdclass":{"bdist_wheel": bdist_wheel, "build_py": build_py, "egg_info": egg_info}})
171
172 try:
173 from poetry.console.application import Application #@UnresolvedImport
174 configuration = Application().poetry.local_config
175 except (ImportError, RuntimeError) as _: configuration = {}
176
177 # Detect reStructured Text or Markdown based on the the file extension
178 description = configuration.get("readme",[])
179 # Only attempt to execute this statement if a readme file is given
180 if ((isinstance(description,str) and description.endswith(".md")) or
181 any(x.endswith(".md") for x in description)):
182 settings.update({"long_description_content_type":"text/markdown"})
183 # Set a default value
184 else: settings.update({"long_description_content_type":"text/x-rst"})
185
186 # Loop over all poetry exclusive keys to reconstruct all metadata information
187 include_keys = ["classifiers","keywords","urls","repository","documentation"]
188 settings.update({k: v for k, v in configuration.items() if k in include_keys})
189 # Merge all urls
190 urls = settings.pop("urls",{});
191 urls.update({k.title(): v for k, v in settings.items() if k in ["repository","documentation"]})
192 _ = [settings.pop(k,None) for k in ["repository","documentation"]]
193 # Update all settings
194 settings.update({"project_urls":urls})
195
196 # Remove all unsupported options and update settings with kwargs
197 exclude_keys = ["python_tag","platform_tag","exclude_source_files"]
198 settings.update({k: kwargs[k] for k in set(list(kwargs.keys())) - set(exclude_keys)})
199
200 try:
201 # Verify that the maintainer tag is correctly set
202 if settings.get("maintainer","") and settings.get("maintainer","") in ["None"]:
203 settings.pop("maintainer"); settings.pop("maintainer_email")
204 except: pass
205
206 # When a meta package is created - remove all modules from the package
207 try: settings.pop("py_modules")
208 except: pass
209
210 # Save the parent PID. This value is used to kill the parent process runing poetry when the wheel is build from within this script.
211 if not os.getenv("pyx_poetry_main_pid",""): os.environ["pyx_poetry_main_pid"] = str(os.getppid())
212
213 # Return nothing.
214 pass
215
216if __name__ == "__main__":
217 pass
PyXMake.Plugin.__build.build
build(*args, **kwargs)
Definition __build.py:28