Rendering Engine 0.2.17
Modular Graphics Rendering Engine | v0.2.17
Loading...
Searching...
No Matches
package_sdk.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# This file is part of the Rendering Engine project.
3# Author: Alexander Obzherin <alexanderobzherin@gmail.com>
4# Copyright (c) 2026 Alexander Obzherin
5# Distributed under the terms of the zlib License. See LICENSE.md for details.
6##
7"""
8@file package_sdk.py
9@brief Build-time helper for assembling the Rendering Engine SDK.
10
11This script is intended to be invoked indirectly via the platform build
12scripts:
13
14 - Windows: build_engine.bat --build-sdk
15 - Unix/FreeBSD: ./build_engine.sh --build-sdk
16
17Responsibilities:
18 - Read engine version from Intermediate/Generated/version.h.
19 - Copy the installed RenderingEngine library, headers, and CMake config
20 into a distributable SDK layout.
21 - Copy vendored headers (glm, nlohmann_json, Vulkan headers if used).
22 - Copy project templates and project management scripts.
23 - Copy ContentExamples without modifying their build configuration.
24 - Preserve automatic source/SDK environment detection.
25 - Emit documentation files and a human-readable Manifest.txt.
26 - Create a compressed archive suitable for CI/CD artifacts.
27"""
28
29import os
30import re
31import shutil
32import datetime
33import logging
34import sys
35
36# --------------------------------------------------------------------
37# Configuration
38# --------------------------------------------------------------------
39
40REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))
41BUILD_ROOT = os.path.join(REPO_ROOT, "Build")
42INSTALL_ROOT = os.path.join(BUILD_ROOT, "Installed")
43PACKAGE_ROOT = os.path.join(BUILD_ROOT, "Packages")
44SDK_TEMP_ROOT = os.path.join(BUILD_ROOT, "SDK")
45LOGS_ROOT = os.path.join(BUILD_ROOT, "Logs")
46
47VERSION_HEADER = os.path.join(
48 BUILD_ROOT, "Intermediate", "Generated", "version.h"
49)
50
51DOC_FILES = [
52 "README.md",
53 "Doc/app_config_guide.md",
54 "Doc/application_development_guide.md",
55 "Doc/ci_cd_guide.md",
56 "Doc/debugging_guide.md",
57 "Doc/developer_guide.md",
58 "Doc/docker_container.md",
59 "Doc/getting_started.md",
60 "Doc/performance_profiling_guide.md",
61 "Doc/prepare_environment.md",
62 "Doc/project_creation_guide.md",
63 "Doc/project_packaging_guide.md",
64 "Doc/sdk_packaging_guide.md",
65]
66
67# --------------------------------------------------------------------
68# Helpers
69# --------------------------------------------------------------------
70
71
73 """Parse version.h and extract MAJOR.MINOR.PATCH as a string."""
74 if not os.path.isfile(VERSION_HEADER):
75 raise RuntimeError(f"version.h not found at: {VERSION_HEADER}")
76
77 with open(VERSION_HEADER, "r", encoding="utf-8") as f:
78 text = f.read()
79
80 major = re.search(r"#define\s+RENDERING_ENGINE_VERSION_MAJOR\s+(\d+)", text)
81 minor = re.search(r"#define\s+RENDERING_ENGINE_VERSION_MINOR\s+(\d+)", text)
82 patch = re.search(r"#define\s+RENDERING_ENGINE_VERSION_PATCH\s+(\d+)", text)
83
84 if not (major and minor and patch):
85 raise RuntimeError(
86 "version.h missing required version macros "
87 "(RENDERING_ENGINE_VERSION_MAJOR/MINOR/PATCH)."
88 )
89
90 return f"{major.group(1)}.{minor.group(1)}.{patch.group(1)}"
91
92
94 """Return a human-readable platform string."""
95 p = sys.platform
96 if p.startswith("win"):
97 return "Windows"
98 elif p.startswith("linux"):
99 return "Linux"
100 elif p.startswith("freebsd"):
101 return "FreeBSD"
102 return "Unknown"
103
104
105def copy_tree(src, dst, ignore=None):
106 if not os.path.exists(src):
107 logging.warning(f"Source path does not exist, skipping: {src}")
108 return
109 if os.path.exists(dst):
110 shutil.rmtree(dst)
111 shutil.copytree(src, dst, ignore=ignore)
112 logging.info(f"Copied tree: {src} -> {dst}")
113
114
115def ensure_dir(path):
116 os.makedirs(path, exist_ok=True)
117 return path
118
119
120def write_manifest(path, version, platform):
121 with open(path, "w", encoding="utf-8") as f:
122 f.write("Rendering Engine SDK Manifest\n")
123 f.write("=====================================\n\n")
124 f.write(f"Version : {version}\n")
125 f.write(f"Platform: {platform}\n")
126 f.write(f"Date : {datetime.datetime.now()}\n\n")
127 f.write("Layout:\n")
128 f.write("- RenderingEngine/RenderingLibrary : core engine library + headers + cmake files\n")
129 f.write("- RenderingEngine/MaterialCompiler : material compiler tool + local engine DLL\n")
130 f.write("- RenderingEngine/Scripts : project generation scripts + templates\n")
131 f.write("- ContentExamples/ : sample projects\n")
132 f.write("- UserApplications/ : place for user-created apps\n")
133 f.write("- Doc/ : documentation\n")
134 logging.info(f"Manifest written: {path}")
135
136
137# --------------------------------------------------------------------
138# Main Packaging Logic
139# --------------------------------------------------------------------
140
141
142def main():
143 # Init logging
144 ensure_dir(LOGS_ROOT)
145 logging.basicConfig(
146 filename=os.path.join(LOGS_ROOT, "packaging.log"),
147 level=logging.INFO,
148 format="%(asctime)s %(levelname)s: %(message)s",
149 )
150 logging.info("=== SDK Packaging Started ===")
151
152 try:
153 version = read_version()
154 platform = detect_platform()
155
156 sdk_name = f"RenderingEngine-v{version}-SDK-{platform}"
157 sdk_root = os.path.join(SDK_TEMP_ROOT, sdk_name)
158
159 # Clean previous SDK for this version/platform
160 if os.path.exists(sdk_root):
161 shutil.rmtree(sdk_root)
162 ensure_dir(sdk_root)
163
164 logging.info(f"Version : {version}")
165 logging.info(f"Platform: {platform}")
166 logging.info(f"SDK root: {sdk_root}")
167
168 # Paths inside SDK
169 sdk_rendering_engine_root = ensure_dir(os.path.join(sdk_root, "RenderingEngine"))
170 sdk_render_lib_root = ensure_dir(os.path.join(sdk_rendering_engine_root, "RenderingLibrary"))
171 sdk_material_compiler_root = ensure_dir(os.path.join(sdk_rendering_engine_root, "MaterialCompiler"))
172 sdk_scripts_root = ensure_dir(os.path.join(sdk_rendering_engine_root, "Scripts"))
173
174 # --------------------------------------------------------------
175 # 1. RenderingLibrary: headers + libs + cmake files
176 # --------------------------------------------------------------
177 installed_engine_root = os.path.join(INSTALL_ROOT, "RenderingEngine")
178
179 # RenderingLibrary
180 installed_render_lib = os.path.join(installed_engine_root, "RenderingLibrary")
181
182 for required_dir in ("Include", "Library"):
183 required_path = os.path.join(installed_render_lib, required_dir)
184
185 if not os.path.isdir(required_path):
186 raise FileNotFoundError(
187 f"Required SDK component not found: {required_path}"
188 )
189
190 copy_tree(
191 os.path.join(installed_render_lib, "Include"),
192 os.path.join(sdk_render_lib_root, "Include"),
193 )
194
195 copy_tree(
196 os.path.join(installed_render_lib, "Library"),
197 os.path.join(sdk_render_lib_root, "Library"),
198 )
199
200 # --------------------------------------------------------------
201 # 2. MaterialCompiler (tool + its local DLL)
202 # --------------------------------------------------------------
203 installed_mc_root = os.path.join(installed_engine_root, "MaterialCompiler")
204 copy_tree(installed_mc_root, sdk_material_compiler_root)
205
206 # --------------------------------------------------------------
207 # 3. External vendored headers (glm, json, vulkan,...)
208 # --------------------------------------------------------------
209 installed_external_root = os.path.join(installed_engine_root, "External")
210 copy_tree(installed_external_root, os.path.join(sdk_rendering_engine_root, "External"))
211
212 # --------------------------------------------------------------
213 # 4. Project management scripts and templates
214 # --------------------------------------------------------------
215
216 copy_tree(
217 os.path.join(REPO_ROOT, "RenderingEngine", "Scripts", "Templates"),
218 os.path.join(sdk_scripts_root, "Templates"),
219 )
220
221 project_scripts = [
222 "create_project.py",
223 "register_project.py",
224 ]
225
226 for script_name in project_scripts:
227 script_src = os.path.join(
228 REPO_ROOT,
229 "RenderingEngine",
230 "Scripts",
231 script_name,
232 )
233
234 if os.path.isfile(script_src):
235 shutil.copy2(
236 script_src,
237 os.path.join(sdk_scripts_root, script_name),
238 )
239 logging.info(f"Copied script: {script_name}")
240 else:
241 raise FileNotFoundError(
242 f"Required SDK script not found: {script_src}"
243 )
244
245
246 # --------------------------------------------------------------
247 # 5. Documentation
248 # --------------------------------------------------------------
249 doc_dst = ensure_dir(os.path.join(sdk_root, "Doc"))
250 for doc in DOC_FILES:
251 src = os.path.join(REPO_ROOT, doc)
252 if os.path.isfile(src):
253 shutil.copy2(src, doc_dst)
254 logging.info(f"Copied documentation: {src}")
255 else:
256 logging.warning(f"Documentation file missing, skipped: {src}")
257
258 # --------------------------------------------------------------
259 # 6. Content Examples (copied directly from root-level folder)
260 # --------------------------------------------------------------
261 examples_src_root = os.path.join(REPO_ROOT, "ContentExamples")
262 examples_dst_root = ensure_dir(os.path.join(sdk_root, "ContentExamples"))
263
264 def ignore_example(dirpath, names):
265 ignore = []
266 for n in names:
267 if n in ("Build", "Intermediate", "Local", ".vs", ".vscode", "x64"):
268 ignore.append(n)
269 elif n.endswith(".user") or n.endswith(".vcxproj") or n.endswith(".vcxproj.filters"):
270 ignore.append(n)
271 return ignore
272
273 if os.path.isdir(examples_src_root):
274 copy_tree(
275 examples_src_root,
276 examples_dst_root,
277 ignore=ignore_example,
278 )
279 logging.info("Copied ContentExamples folder.")
280 else:
281 logging.warning("No ContentExamples folder found.")
282
283 # --------------------------------------------------------------
284 # 7. UserApplications (empty placeholder)
285 # --------------------------------------------------------------
286 ensure_dir(os.path.join(sdk_root, "UserApplications"))
287
288 # --------------------------------------------------------------
289 # 7. Manifest
290 # --------------------------------------------------------------
291 write_manifest(os.path.join(sdk_root, "Manifest.txt"), version, platform)
292
293 # --------------------------------------------------------------
294 # 8. Create Archive
295 # --------------------------------------------------------------
296 ensure_dir(PACKAGE_ROOT)
297 archive_path = os.path.join(PACKAGE_ROOT, sdk_name)
298
299 # Clean old archive if exists
300 for ext in (".tar.gz", ".zip", ".tgz"):
301 old = archive_path + ext
302 if os.path.exists(old):
303 os.remove(old)
304
305 shutil.make_archive(
306 archive_path,
307 "gztar",
308 root_dir=SDK_TEMP_ROOT,
309 base_dir=sdk_name,
310 )
311 logging.info(f"Archive created: {archive_path}.tar.gz")
312 logging.info("=== SDK Packaging Complete ===")
313
314 except Exception as e:
315 logging.error(f"SDK packaging failed: {e}", exc_info=True)
316 print(f"SDK packaging failed: {e}")
317 sys.exit(1)
318
319
320if __name__ == "__main__":
321 main()
copy_tree(src, dst, ignore=None)
ensure_dir(path)
write_manifest(path, version, platform)