Skip to content

Commit 558744d

Browse files
jcelerierclaude
andcommitted
cmake: make CMake-native addon packaging the default (via avnd_addon_finalize)
Every avendish addon already calls avnd_addon_finalize(); in a standalone (non-score) build it now assembles the per-backend distributable packages automatically through avnd_addon_package(), so addons ship the full layout — externals + the generated help/example patches + LICENSE/README — with no per-addon packaging code and no opt-in. - avnd_addon_finalize: standalone branch calls avnd_addon_package for all backends (empty ones skip themselves). An addon that needs to bundle a runtime dependency still calls avnd_addon_package(... SUPPORT ...) itself before finalize; a global guard makes the auto-call stand down so we never package twice. - New avnd_create_pd_addon_package (flat externals + <c_name>-help.pd) and avnd_create_python_addon_package (py<c_name>.so + a generated __init__.py re-export shim + examples/<c_name>_example.py), wired into avnd_addon_package's dispatch. max/touchdesigner/godot already had packagers that copy their help/example properties; they are now simply auto-invoked. - avnd_make_pd / avnd_make_python register their externals in the AVND_ALL_<BK>_EXTERNALS globals the orchestrator enumerates, like the other backends already did. Verified against score-addon-puara (unmodified): a PD-only build now emits build/package/score_addon_puara/ with all 26 externals + 26 matching -help.pd patches; a Python-only build emits the 26 modules + __init__.py (imports and re-exports all 26) + 26 examples. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 794a381 commit 558744d

4 files changed

Lines changed: 175 additions & 0 deletions

File tree

cmake/AvendishAddon.cmake

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,5 +133,19 @@ macro(avnd_addon_finalize)
133133
BASE_TARGET ${AA_NAME}
134134
PLUGIN_UUID ${AA_UUID}
135135
PLUGIN_VERSION ${AA_VERSION})
136+
else()
137+
# Standalone: assemble the distributable packages from CMake by default, so
138+
# every addon ships the per-backend layout (externals + generated help /
139+
# example patches + metadata) with no per-addon packaging code. An addon that
140+
# must bundle a runtime dependency calls avnd_addon_package(... SUPPORT ...)
141+
# itself before finalize; that sets the guard below so we don't package twice.
142+
# Each back-end whose externals weren't built is skipped inside the call.
143+
get_property(_avnd_pkg_done GLOBAL PROPERTY AVND_ADDON_PACKAGE_INVOKED)
144+
if(NOT _avnd_pkg_done AND COMMAND avnd_addon_package)
145+
avnd_addon_package(
146+
NAME "${AA_NAME}"
147+
SOURCE_PATH "${CMAKE_CURRENT_SOURCE_DIR}"
148+
BACKENDS max pd python touchdesigner godot)
149+
endif()
136150
endif()
137151
endmacro()

cmake/avendish.packaging.cmake

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,141 @@ function(avnd_create_max_addon_package)
162162
endforeach()
163163
endfunction()
164164

165+
# --- Pure Data (addon) ------------------------------------------------------
166+
#
167+
# avnd_create_pd_addon_package(
168+
# NAME <addon-name> SOURCE_PATH <dir> PACKAGE_ROOT <dir>
169+
# EXTERNALS <target> ... # the *_pd external targets
170+
# [SUPPORT <file> ...])
171+
#
172+
# Pd resolves an external and its <name>-help.pd from the same flat folder, so
173+
# the package is a single directory: <root>/<name>/<external><suffix> +
174+
# <name>-help.pd + metadata. Matches the layout the ossia/actions pd packager and
175+
# the Deken composite step expect (per-OS folders union file-by-file).
176+
function(avnd_create_pd_addon_package)
177+
cmake_parse_arguments(AVND "" "NAME;SOURCE_PATH;PACKAGE_ROOT" "EXTERNALS;SUPPORT" ${ARGN})
178+
if(NOT AVND_EXTERNALS)
179+
return()
180+
endif()
181+
182+
set(_pkg "${AVND_PACKAGE_ROOT}/${AVND_NAME}")
183+
set(_pkg_target "${AVND_NAME}_pd_package")
184+
add_custom_target(${_pkg_target} ALL DEPENDS ${AVND_EXTERNALS})
185+
186+
if(AVND_SOURCE_PATH)
187+
avnd_packaging_copy_metadata("${_pkg}" "${AVND_SOURCE_PATH}" "${AVND_NAME}")
188+
endif()
189+
190+
foreach(_external ${AVND_EXTERNALS})
191+
add_custom_command(TARGET ${_external} POST_BUILD
192+
COMMAND ${CMAKE_COMMAND} -E make_directory "${_pkg}"
193+
COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:${_external}>" "${_pkg}/"
194+
COMMENT "Packaging Pd external: ${_external}"
195+
VERBATIM)
196+
197+
get_target_property(_pd_help ${_external} AVND_PD_HELP)
198+
if(_pd_help)
199+
# Interactive help patch (avnd_generate_help). Best-effort: a missing help
200+
# patch (some toolchains) must not fail packaging.
201+
if(TARGET "${_external}_help")
202+
add_dependencies("${_external}" "${_external}_help")
203+
endif()
204+
get_property(_pkgdir GLOBAL PROPERTY AVND_PACKAGING_DIR)
205+
add_custom_command(TARGET ${_external} POST_BUILD
206+
COMMAND ${CMAKE_COMMAND}
207+
"-DSRC=${CMAKE_BINARY_DIR}/${_pd_help}" "-DDST=${_pkg}"
208+
-P "${_pkgdir}/avendish.packaging.copy_optional.cmake"
209+
VERBATIM)
210+
endif()
211+
endforeach()
212+
213+
# SUPPORT libs sit next to the externals (Pd's loader searches the external's
214+
# own folder for siblings).
215+
foreach(_support ${AVND_SUPPORT})
216+
add_custom_command(TARGET ${_pkg_target} POST_BUILD
217+
COMMAND ${CMAKE_COMMAND} -E make_directory "${_pkg}"
218+
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_support}" "${_pkg}/"
219+
VERBATIM)
220+
endforeach()
221+
endfunction()
222+
223+
# --- Python (addon) ---------------------------------------------------------
224+
#
225+
# avnd_create_python_addon_package(
226+
# NAME <addon-name> SOURCE_PATH <dir> PACKAGE_ROOT <dir>
227+
# EXTERNALS <target> ... # the *_python module targets (py<c_name>.so/.pyd)
228+
# [SUPPORT <file> ...])
229+
#
230+
# Layout: <root>/<name>/py<c_name>.<so|pyd> + <root>/<name>/__init__.py (re-exports
231+
# every compiled port) + <root>/<name>/examples/<c_name>_example.py + metadata.
232+
# Mirrors the ossia/actions python packager (which synthesizes the same __init__).
233+
function(avnd_create_python_addon_package)
234+
cmake_parse_arguments(AVND "" "NAME;SOURCE_PATH;PACKAGE_ROOT" "EXTERNALS;SUPPORT" ${ARGN})
235+
if(NOT AVND_EXTERNALS)
236+
return()
237+
endif()
238+
239+
set(_pkg "${AVND_PACKAGE_ROOT}/${AVND_NAME}")
240+
set(_pkg_target "${AVND_NAME}_python_package")
241+
add_custom_target(${_pkg_target} ALL DEPENDS ${AVND_EXTERNALS})
242+
243+
if(AVND_SOURCE_PATH)
244+
avnd_packaging_copy_metadata("${_pkg}" "${AVND_SOURCE_PATH}" "${AVND_NAME}")
245+
endif()
246+
247+
# Loader shim: re-export every py<port> module as an attribute of the package,
248+
# tolerating a single failed import. Addon-independent, written once.
249+
file(WRITE "${_pkg}/__init__.py"
250+
"# Auto-generated by avendish (avnd_create_python_addon_package).
251+
# Re-exports every compiled avendish port (py<port>) as a package attribute.
252+
import importlib as _importlib
253+
import glob as _glob
254+
import os as _os
255+
256+
_here = _os.path.dirname(__file__)
257+
for _f in _glob.glob(_os.path.join(_here, 'py*.*')):
258+
_ext = _os.path.splitext(_f)[1].lower()
259+
if _ext not in ('.so', '.pyd'):
260+
continue
261+
_name = _os.path.splitext(_os.path.basename(_f))[0]
262+
try:
263+
globals()[_name] = _importlib.import_module('.' + _name, package=__name__)
264+
except Exception as _exc:
265+
import sys as _sys
266+
print('avendish: failed to import {}: {}'.format(_name, _exc), file=_sys.stderr)
267+
268+
del _importlib, _glob, _os
269+
")
270+
271+
foreach(_external ${AVND_EXTERNALS})
272+
add_custom_command(TARGET ${_external} POST_BUILD
273+
COMMAND ${CMAKE_COMMAND} -E make_directory "${_pkg}"
274+
COMMAND ${CMAKE_COMMAND} -E copy "$<TARGET_FILE:${_external}>" "${_pkg}/"
275+
COMMENT "Packaging Python module: ${_external}"
276+
VERBATIM)
277+
278+
get_target_property(_py_example ${_external} AVND_PYTHON_EXAMPLE)
279+
if(_py_example)
280+
if(TARGET "${_external}_help")
281+
add_dependencies("${_external}" "${_external}_help")
282+
endif()
283+
get_property(_pkgdir GLOBAL PROPERTY AVND_PACKAGING_DIR)
284+
add_custom_command(TARGET ${_external} POST_BUILD
285+
COMMAND ${CMAKE_COMMAND}
286+
"-DSRC=${CMAKE_BINARY_DIR}/${_py_example}" "-DDST=${_pkg}/examples"
287+
-P "${_pkgdir}/avendish.packaging.copy_optional.cmake"
288+
VERBATIM)
289+
endif()
290+
endforeach()
291+
292+
foreach(_support ${AVND_SUPPORT})
293+
add_custom_command(TARGET ${_pkg_target} POST_BUILD
294+
COMMAND ${CMAKE_COMMAND} -E make_directory "${_pkg}"
295+
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_support}" "${_pkg}/"
296+
VERBATIM)
297+
endforeach()
298+
endfunction()
299+
165300
# --- TouchDesigner ----------------------------------------------------------
166301
#
167302
# avnd_create_touchdesigner_package(
@@ -356,6 +491,12 @@ function(avnd_addon_package)
356491
set(AVND_PACKAGE_ROOT "${CMAKE_BINARY_DIR}/package")
357492
endif()
358493

494+
# Mark that an addon has assembled its packages explicitly, so the default
495+
# auto-packaging in avnd_addon_finalize() stands down (an explicit call is how
496+
# addons pass SUPPORT libs / a custom PACKAGE_ROOT; auto-packaging again would
497+
# duplicate POST_BUILD copies and miss the SUPPORT).
498+
set_property(GLOBAL PROPERTY AVND_ADDON_PACKAGE_INVOKED 1)
499+
359500
foreach(_backend ${AVND_BACKENDS})
360501
string(TOLOWER "${_backend}" _backend)
361502
if(_backend STREQUAL "td")
@@ -375,6 +516,20 @@ function(avnd_addon_package)
375516
PACKAGE_ROOT "${AVND_PACKAGE_ROOT}"
376517
EXTERNALS ${_externals}
377518
SUPPORT ${AVND_SUPPORT})
519+
elseif(_backend STREQUAL "pd")
520+
avnd_create_pd_addon_package(
521+
NAME "${AVND_NAME}"
522+
SOURCE_PATH "${AVND_SOURCE_PATH}"
523+
PACKAGE_ROOT "${AVND_PACKAGE_ROOT}"
524+
EXTERNALS ${_externals}
525+
SUPPORT ${AVND_SUPPORT})
526+
elseif(_backend STREQUAL "python")
527+
avnd_create_python_addon_package(
528+
NAME "${AVND_NAME}"
529+
SOURCE_PATH "${AVND_SOURCE_PATH}"
530+
PACKAGE_ROOT "${AVND_PACKAGE_ROOT}"
531+
EXTERNALS ${_externals}
532+
SUPPORT ${AVND_SUPPORT})
378533
elseif(_backend STREQUAL "touchdesigner")
379534
avnd_create_touchdesigner_package(
380535
NAME "${AVND_NAME}"

cmake/avendish.pd.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ function(avnd_make_pd)
130130

131131
avnd_common_setup("${AVND_TARGET}" "${AVND_FX_TARGET}")
132132

133+
# Register for avnd_addon_package() (the addon-wide packaging orchestrator).
134+
set_property(GLOBAL APPEND PROPERTY AVND_ALL_PD_EXTERNALS ${AVND_FX_TARGET})
135+
133136
# Ship a help patch: <c_name>-help.pd next to the external (Pd finds it on the
134137
# path). Use the explicit HELP_PATCH override if given, else auto-generate.
135138
avnd_generate_help(

cmake/avendish.python.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ function(avnd_make_python)
7878

7979
avnd_common_setup("${AVND_TARGET}" "${AVND_FX_TARGET}")
8080

81+
# Register for avnd_addon_package() (the addon-wide packaging orchestrator).
82+
set_property(GLOBAL APPEND PROPERTY AVND_ALL_PYTHON_EXTERNALS ${AVND_FX_TARGET})
83+
8184
# Usage example next to the module.
8285
avnd_generate_help(
8386
FX_TARGET "${AVND_FX_TARGET}"

0 commit comments

Comments
 (0)