Merge branch 'refactor/tools-dir'
Move the branding toolkit into tools/, derive names from PLUGIN_NAME, and drive the checker from a data table so the scripts are identical across plugins.
This commit is contained in:
@@ -41,7 +41,7 @@ jobs:
|
||||
# Cheap and needs no toolchain, so it runs first: a rename that only
|
||||
# landed in half the files is worth catching before spending a build on it.
|
||||
- name: Branding consistency
|
||||
run: sh check-branding.sh
|
||||
run: sh tools/check-branding.sh
|
||||
|
||||
# The default build is CLAP-only; the vst3 feature is opt-in, so both
|
||||
# configurations have to be built to know the cfg gates are still correct.
|
||||
|
||||
@@ -24,41 +24,49 @@ Identity is duplicated across `Cargo.toml`, `bundler.toml`, `src/lib.rs`, `NOTIC
|
||||
`Cargo.lock`, the CI workflow and the docs. Nothing makes those agree, so a half-finished
|
||||
rename builds a bundle under one name while CI bundles another.
|
||||
|
||||
`branding.conf` holds the canonical identity once. Two scripts read it:
|
||||
Everything for it lives in `tools/`:
|
||||
|
||||
```bash
|
||||
sh brand-plugin.sh --display "Fuzz Box" # derive, validate, show the plan (dry run)
|
||||
sh brand-plugin.sh --display "Fuzz Box" --apply
|
||||
sh check-branding.sh # exit 0 = consistent, 1 = drift
|
||||
```
|
||||
tools/branding.conf the names, and what derives from what
|
||||
tools/branding-sites.tsv where each name has to appear, and in what form
|
||||
tools/brand-plugin.sh changes names
|
||||
tools/check-branding.sh verifies them
|
||||
```
|
||||
|
||||
**`brand-plugin.sh` changes names.** It derives the package name and Rust types from the
|
||||
display name, validates them against what Cargo actually accepts, rewrites every tracked
|
||||
file, appends the retired spellings to `STALE`, and finishes by running the checker — a run
|
||||
that ends green is a rename that landed. It is a dry run unless you pass `--apply`.
|
||||
```bash
|
||||
sh tools/brand-plugin.sh # interactive; Enter keeps a value
|
||||
sh tools/brand-plugin.sh --display "Fuzz Box" # or non-interactive; dry run
|
||||
sh tools/brand-plugin.sh --display "Fuzz Box" --apply
|
||||
sh tools/check-branding.sh # exit 0 = consistent, 1 = drift
|
||||
```
|
||||
|
||||
**`check-branding.sh` verifies them.** Both directions: every canonical value is asserted
|
||||
present where it belongs, *and* no retired spelling survives anywhere. Absence alone is not
|
||||
enough — renaming to the wrong new spelling would pass a stale-sweep and still break CI.
|
||||
**One name drives the rest.** `PLUGIN_NAME` derives the package name (`Fuzz Box` →
|
||||
`fuzz_box`), the Rust types (`FuzzBox`, `FuzzBoxParams`) and the display name. Each is
|
||||
overridable in `branding.conf`, because the derivation is sometimes wrong and sometimes
|
||||
outright invalid — `206 Prototype` derives to `206_prototype`, which Cargo rejects since a
|
||||
package name cannot start with a digit. The helper refuses those, along with Rust keywords
|
||||
and built-in crate names.
|
||||
|
||||
Three things it will refuse or warn about, all learned the hard way:
|
||||
**The checker does both halves.** Every canonical value is asserted *present* where it
|
||||
belongs, and every retired spelling is swept for. Absence alone is not enough: renaming to
|
||||
the wrong new spelling would pass a stale-sweep and still break CI.
|
||||
|
||||
- **A package name Cargo rejects.** Names cannot start with a digit (`206 Prototype` →
|
||||
`206_prototype`, rejected), be a Rust keyword, or collide with a built-in crate.
|
||||
- **Moving frozen identity.** `CLAP_ID` and `VST3_CLASS_ID` are how hosts recognise saved
|
||||
projects and presets. Change either and every existing session silently loses the plugin —
|
||||
the track comes up empty, with no error. Pick them once before the first release; after
|
||||
that they never move. `--allow-identity-change` is required to touch them, and the helper
|
||||
proposes a valid 16-byte class ID rather than deriving one, because that choice is yours.
|
||||
- **Development history in `CLAP_DESCRIPTION`.** Hosts show it to users who never knew your
|
||||
codename. Any retired name or the repo's own codename appearing there is rejected.
|
||||
**Two things it will not do for you.** `CLAP_ID` and `VST3_CLASS_ID` are how hosts recognise
|
||||
saved projects and presets — change either and every existing session silently loses the
|
||||
plugin, with no error. They need `--allow-identity-change`, and the 16-byte class ID is
|
||||
*proposed* rather than derived, because that choice is yours. And `CLAP_DESCRIPTION` is
|
||||
rejected if it carries a retired name or the repo codename: hosts show it to users who never
|
||||
knew your development history.
|
||||
|
||||
The repo name is deliberately *not* derived. It is fixed when the repo is created and keeps
|
||||
its development codename while the plugin gets a product name; `CODENAME` in `branding.conf`
|
||||
records it so it can be kept out of user-facing text.
|
||||
**The repo name is deliberately not derived.** It is fixed when the repo is created and keeps
|
||||
its development codename while the plugin gets a product name. `CODENAME` records it so it
|
||||
can be kept out of user-facing text.
|
||||
|
||||
Checks for files the template doesn't ship (a `deploy.ps1`, say) report as `skip` and start
|
||||
enforcing once you add them.
|
||||
Adding a site the template doesn't ship is one row in `branding-sites.tsv` with owner
|
||||
`custom` — no shell to edit, and it survives regeneration. Rows the helper owns are rewritten
|
||||
on each rename; custom rows are carried through and value-substituted like any other file.
|
||||
Both files can be hand-edited; the helper is what keeps them agreeing, so run the checker
|
||||
after if you do.
|
||||
|
||||
## Build
|
||||
|
||||
@@ -119,9 +127,7 @@ bundler.toml # Bundle name for `cargo xtask bundle`
|
||||
src/lib.rs # Plugin skeleton — params, DSP, and egui editor, with one example gain param
|
||||
xtask/ # Bundler xtask (cargo xtask bundle)
|
||||
.cargo/config.toml # xtask alias
|
||||
branding.conf # Canonical identity — the one place names are declared
|
||||
check-branding.sh # Asserts every name is present where it belongs and no old one survives
|
||||
brand-plugin.sh # Renames the plugin everywhere, then verifies with the checker
|
||||
tools/ # Branding toolkit — see Renaming
|
||||
.gitattributes # Pins *.sh to LF so Windows checkouts keep a valid shebang
|
||||
.github/workflows/ # CI: branding check, both feature sets build, test, bundle
|
||||
```
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# branding.conf - the plugin's canonical identity, in one place.
|
||||
#
|
||||
# Sourced by both check-branding.sh (which asserts every copy matches) and
|
||||
# brand-plugin.sh (which rewrites this file and every copy when you rename).
|
||||
# Editing a value here and running the checker tells you every place that still
|
||||
# holds the old one; running the helper changes them for you.
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Names that move when the plugin is renamed
|
||||
# ---------------------------------------------------------------------------
|
||||
PKG="nih_plug_template" # Cargo package name == bundler.toml table key
|
||||
DISPLAY="Plugin Name" # bundle name -> "<DISPLAY>.vst3" / "<DISPLAY>.clap"
|
||||
PLUGIN_NAME="Plugin Name" # Plugin::NAME - what the DAW displays
|
||||
VENDOR="Your Name" # Plugin::VENDOR - the org, not the person
|
||||
EMAIL="your@email.com" # Plugin::EMAIL
|
||||
HOMEPAGE="https://github.com/YOUR-USERNAME/YOUR-PLUGIN-NAME" # Cargo homepage -> Plugin::URL
|
||||
DESCRIPTION="A minimal NIH-plug audio plugin" # Cargo package description
|
||||
|
||||
# What the plugin does *now*. Keep development history out of it - no codename,
|
||||
# no "prototype", no "v2 rewrite". Hosts show this to users, who have no idea
|
||||
# what any of that meant. check-branding.sh enforces the mechanical half of this
|
||||
# by rejecting any retired spelling or the repo codename appearing here.
|
||||
CLAP_DESCRIPTION="Your plugin description"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Frozen identity - hosts key saved projects and presets off these two. Changing
|
||||
# either orphans every existing session silently: the track comes up empty, with
|
||||
# no error. Choose them once before the first release and never again.
|
||||
# brand-plugin.sh refuses to touch them without --allow-identity-change.
|
||||
# ---------------------------------------------------------------------------
|
||||
CLAP_ID="com.your-domain.plugin-name" # reverse-DNS, lowercase, no spaces
|
||||
VST3_CLASS_ID="Unique16CharId!!" # MUST be exactly 16 bytes
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Rust type names. Never reach a host, so renaming them is cosmetic - but
|
||||
# leaving "MyPlugin" in a shipped plugin is residue that says nobody read the
|
||||
# file. Leave them alone and the checks simply stay green.
|
||||
# ---------------------------------------------------------------------------
|
||||
STRUCT="MyPlugin" # the Plugin impl target
|
||||
PARAMS_STRUCT="PluginParams" # the Params impl target
|
||||
|
||||
# Where those two are defined. A plugin that outgrows one file points these at
|
||||
# the real locations (e.g. PARAMS_FILE="src/params.rs") - the checker asserts
|
||||
# definitions here, and the STALE sweep catches every use site elsewhere.
|
||||
LIB_FILE="src/lib.rs"
|
||||
PARAMS_FILE="src/lib.rs"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# The development codename: this repository's own name, fixed when the repo was
|
||||
# created and deliberately NOT renamed when the plugin is. It keeps its history
|
||||
# while the plugin gets a product name. Never checked for as branding - it is
|
||||
# only used to keep it out of user-facing descriptions.
|
||||
# ---------------------------------------------------------------------------
|
||||
CODENAME="nih-plug-template"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Spellings that must no longer appear in tracked files. Seeded with this
|
||||
# template's placeholders and with the current values, so renaming is
|
||||
# self-checking in both directions: an entry is skipped while it is still part
|
||||
# of a canonical value above, and arms the moment it stops being one.
|
||||
# brand-plugin.sh appends retired values here automatically.
|
||||
# ---------------------------------------------------------------------------
|
||||
STALE="nih_plug_template
|
||||
MyPlugin
|
||||
PluginParams
|
||||
A minimal NIH-plug audio plugin
|
||||
Your plugin description
|
||||
Plugin Name
|
||||
Your Name
|
||||
your@email.com
|
||||
com.your-domain.plugin-name
|
||||
Unique16CharId!!
|
||||
YOUR-USERNAME
|
||||
YOUR-PLUGIN-NAME
|
||||
your-domain"
|
||||
@@ -22,10 +22,12 @@
|
||||
# --apply actually write. Without it, nothing is modified.
|
||||
|
||||
set -u
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
[ -f branding.conf ] || { printf 'branding.conf not found next to this script\n' >&2; exit 1; }
|
||||
# Resolve the repo root: these live in tools/, every path they touch is
|
||||
# repo-relative, and they must behave the same wherever they are invoked from.
|
||||
cd "$(dirname "$0")/.." || exit 1
|
||||
[ -f tools/branding.conf ] || { printf 'tools/branding.conf not found\n' >&2; exit 1; }
|
||||
# shellcheck disable=SC1091
|
||||
. ./branding.conf
|
||||
. ./tools/branding.conf
|
||||
|
||||
OLD_PKG="$PKG"; OLD_DISPLAY="$DISPLAY"; OLD_PLUGIN_NAME="$PLUGIN_NAME"
|
||||
OLD_VENDOR="$VENDOR"; OLD_EMAIL="$EMAIL"; OLD_HOMEPAGE="$HOMEPAGE"
|
||||
@@ -56,7 +58,28 @@ while [ $# -gt 0 ]; do
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$new_display" ] || { printf 'nothing to do: pass --display "New Name"\n' >&2; exit 2; }
|
||||
# Interactive when nothing was passed and there is a terminal to ask at. Every
|
||||
# prompt offers the current value; Enter keeps it, so a run that answers nothing
|
||||
# is a no-op rather than a surprise. Optional fields additionally accept "-" to
|
||||
# clear them.
|
||||
ask() { # ask VARNAME PROMPT CURRENT
|
||||
printf ' %s [%s]: ' "$2" "$3" > /dev/tty
|
||||
IFS= read -r _reply < /dev/tty || _reply=""
|
||||
[ -n "$_reply" ] || _reply="$3"
|
||||
[ "$_reply" = "-" ] && _reply=""
|
||||
eval "$1=\$_reply"
|
||||
}
|
||||
if [ -z "$new_display" ] && [ -t 0 ] && [ -c /dev/tty ]; then
|
||||
printf 'branding this plugin - Enter keeps the current value, "-" clears an optional one\n\n'
|
||||
ask new_display "display name " "$OLD_DISPLAY"
|
||||
ask new_vendor "vendor " "$OLD_VENDOR"
|
||||
ask new_email "email " "$OLD_EMAIL"
|
||||
ask new_homepage "homepage " "$OLD_HOMEPAGE"
|
||||
ask new_description "cargo description" "$OLD_DESCRIPTION"
|
||||
ask new_clap_description "clap description" "$OLD_CLAP_DESCRIPTION"
|
||||
printf '\n'
|
||||
fi
|
||||
[ -n "$new_display" ] || { printf 'nothing to do: pass --display "New Name" (or run interactively)\n' >&2; exit 2; }
|
||||
|
||||
# --- derivation -------------------------------------------------------------
|
||||
# Package name: lowercase, every run of non-alphanumerics becomes one separator,
|
||||
@@ -184,7 +207,11 @@ trap 'rm -f "$subst_file"' EXIT
|
||||
changed=0
|
||||
git ls-files 2>/dev/null | while IFS= read -r f; do
|
||||
[ -f "$f" ] || continue
|
||||
case "$f" in branding.conf) continue ;; esac
|
||||
# branding.conf alone is excluded: it is rewritten wholesale below, and its
|
||||
# STALE list names every retired spelling by design. The site table IS
|
||||
# substituted, so hand-added custom rows track the rename; owned rows are
|
||||
# regenerated afterwards regardless.
|
||||
case "$f" in tools/branding.conf) continue ;; esac
|
||||
before=$(cat "$f")
|
||||
after="$before"
|
||||
while IFS="$(printf '\t')" read -r old new; do
|
||||
@@ -214,10 +241,63 @@ sed -e "s|^PKG=.*|PKG=\"$new_pkg\"|" \
|
||||
-e "s|^VST3_CLASS_ID=.*|VST3_CLASS_ID=\"$new_vst3\"|" \
|
||||
-e "s|^STRUCT=.*|STRUCT=\"$new_struct\"|" \
|
||||
-e "s|^PARAMS_STRUCT=.*|PARAMS_STRUCT=\"$new_params_struct\"|" \
|
||||
branding.conf > branding.conf.new
|
||||
{ sed '$ s/"$//' branding.conf.new; printf '%s"\n' "$retired"; } > branding.conf
|
||||
rm -f branding.conf.new
|
||||
printf ' rewrote branding.conf (retired spellings appended to STALE)\n'
|
||||
tools/branding.conf > tools/branding.conf.new
|
||||
{ sed '$ s/"$//' tools/branding.conf.new; printf '%s"\n' "$retired"; } > tools/branding.conf
|
||||
rm -f tools/branding.conf.new
|
||||
printf ' rewrote tools/branding.conf (retired spellings appended to STALE)\n'
|
||||
|
||||
# Regenerate the site table. Rows whose owner this script recognises are
|
||||
# rewritten with the new resolved literals; every other row - anything marked
|
||||
# custom, or added by hand for a file this template does not ship - is carried
|
||||
# through untouched. Clobbering those silently is how a tool stops being trusted.
|
||||
sites="tools/branding-sites.tsv"
|
||||
gen="$(mktemp)"; keep="$(mktemp)"
|
||||
trap 'rm -f "$subst_file" "$gen" "$keep"' EXIT
|
||||
T="$(printf '\t')"
|
||||
{
|
||||
printf 'req%spkg.cargo%sCargo.toml%sname = "%s"\n' "$T" "$T" "$T" "$new_pkg"
|
||||
printf 'req%spkg.bundler%sbundler.toml%s[%s]\n' "$T" "$T" "$T" "$new_pkg"
|
||||
printf 'opt%spkg.lock%sCargo.lock%sname = "%s"\n' "$T" "$T" "$T" "$new_pkg"
|
||||
printf 'opt%spkg.ci%s.github/workflows/ci.yaml%scargo xtask bundle %s\n' "$T" "$T" "$T" "$new_pkg"
|
||||
printf 'opt%spkg.readme%sREADME.md%scargo xtask bundle %s\n' "$T" "$T" "$T" "$new_pkg"
|
||||
printf 'req%sdisplay.bundler%sbundler.toml%sname = "%s"\n' "$T" "$T" "$T" "$new_display"
|
||||
printf 'opt%sdisplay.notice%sNOTICE%s%s\n' "$T" "$T" "$T" "$new_display"
|
||||
printf 'req%sname.plugin%s%s%sconst NAME: &'"'"'static str = "%s"\n' "$T" "$T" "$LIB_FILE" "$T" "$new_display"
|
||||
printf 'req%svendor.plugin%s%s%sconst VENDOR: &'"'"'static str = "%s"\n' "$T" "$T" "$LIB_FILE" "$T" "$new_vendor"
|
||||
printf 'req%semail.plugin%s%s%sconst EMAIL: &'"'"'static str = "%s"\n' "$T" "$T" "$LIB_FILE" "$T" "$new_email"
|
||||
printf 'req%sauthors.cargo%sCargo.toml%sauthors = ["%s <%s>"]\n' "$T" "$T" "$T" "$new_vendor" "$new_email"
|
||||
printf 'req%shomepage.cargo%sCargo.toml%shomepage = "%s"\n' "$T" "$T" "$T" "$new_homepage"
|
||||
printf 'req%surl.plugin%s%s%sconst URL: &'"'"'static str = env!("CARGO_PKG_HOMEPAGE")\n' "$T" "$T" "$LIB_FILE" "$T"
|
||||
printf 'req%sdescription.cargo%sCargo.toml%sdescription = "%s"\n' "$T" "$T" "$T" "$new_description"
|
||||
printf 'req%sdescription.clap%s%s%sconst CLAP_DESCRIPTION: Option<&'"'"'static str> = Some("%s")\n' "$T" "$T" "$LIB_FILE" "$T" "$new_clap_description"
|
||||
printf 'req%sclapid.plugin%s%s%sconst CLAP_ID: &'"'"'static str = "%s"\n' "$T" "$T" "$LIB_FILE" "$T" "$new_clap_id"
|
||||
printf 'req%svst3id.plugin%s%s%sconst VST3_CLASS_ID: [u8; 16] = *b"%s"\n' "$T" "$T" "$LIB_FILE" "$T" "$new_vst3"
|
||||
printf 'req%sstruct.def%s%s%sstruct %s {\n' "$T" "$T" "$LIB_FILE" "$T" "$new_struct"
|
||||
printf 'req%sstruct.plugin%s%s%simpl Plugin for %s {\n' "$T" "$T" "$LIB_FILE" "$T" "$new_struct"
|
||||
printf 'req%sstruct.clap%s%s%simpl ClapPlugin for %s {\n' "$T" "$T" "$LIB_FILE" "$T" "$new_struct"
|
||||
printf 'req%sstruct.vst3%s%s%simpl Vst3Plugin for %s {\n' "$T" "$T" "$LIB_FILE" "$T" "$new_struct"
|
||||
printf 'req%sstruct.exportclap%s%s%snih_export_clap!(%s);\n' "$T" "$T" "$LIB_FILE" "$T" "$new_struct"
|
||||
printf 'req%sstruct.exportvst3%s%s%snih_export_vst3!(%s);\n' "$T" "$T" "$LIB_FILE" "$T" "$new_struct"
|
||||
printf 'req%sparams.def%s%s%sstruct %s {\n' "$T" "$T" "$PARAMS_FILE" "$T" "$new_params_struct"
|
||||
printf 'req%sparams.default%s%s%simpl Default for %s {\n' "$T" "$T" "$PARAMS_FILE" "$T" "$new_params_struct"
|
||||
printf 'req%sparams.field%s%s%sparams: Arc<%s>,\n' "$T" "$T" "$LIB_FILE" "$T" "$new_params_struct"
|
||||
printf 'opt%sdeploy.pkg%sdeploy.ps1%s$Package = "%s"\n' "$T" "$T" "$T" "$new_pkg"
|
||||
printf 'opt%sdeploy.vst3%sdeploy.ps1%s$vst3Name = "%s.vst3"\n' "$T" "$T" "$T" "$new_display"
|
||||
printf 'opt%sdeploy.clap%sdeploy.ps1%s$clapName = "%s.clap"\n' "$T" "$T" "$T" "$new_display"
|
||||
} > "$gen"
|
||||
|
||||
owned=$(cut -f2 "$gen")
|
||||
if [ -f "$sites" ]; then
|
||||
grep '^#' "$sites" > "$keep" 2>/dev/null || : > "$keep"
|
||||
while IFS="$T" read -r k o f pat; do
|
||||
case "$k" in ''|\#*) continue ;; esac
|
||||
printf '%s\n' "$owned" | grep -qxF "$o" || printf '%s%s%s%s%s%s%s\n' "$k" "$T" "$o" "$T" "$f" "$T" "$pat" >> "$keep"
|
||||
done < "$sites"
|
||||
else
|
||||
: > "$keep"
|
||||
fi
|
||||
{ cat "$keep"; cat "$gen"; } > "$sites"
|
||||
printf ' rewrote %s (%s owned rows, %s preserved)\n' "$sites" "$(wc -l < "$gen" | tr -d ' ')" "$(grep -cv '^#' "$keep" 2>/dev/null || echo 0)"
|
||||
|
||||
printf '\nverifying with check-branding.sh\n\n'
|
||||
sh check-branding.sh
|
||||
sh tools/check-branding.sh
|
||||
@@ -0,0 +1,45 @@
|
||||
# branding-sites.tsv - every place a canonical name has to appear, and in what form.
|
||||
#
|
||||
# Generated and refreshed by tools/brand-plugin.sh; read by tools/check-branding.sh,
|
||||
# which asserts each pattern is present in its file. Tab-separated, four columns:
|
||||
#
|
||||
# kind req = a missing file is a failure; opt = a missing file is a skip
|
||||
# owner the brand-plugin.sh rule that manages this row, or "custom" for rows
|
||||
# you added by hand. Custom rows are preserved across regeneration;
|
||||
# owned rows are overwritten, so do not hand-edit those.
|
||||
# file repo-relative path
|
||||
# pattern literal text that must appear in that file, already resolved
|
||||
#
|
||||
# Adding a site for something the template does not ship is a one-line edit here
|
||||
# with owner "custom" - no shell to change, and it survives the next rename.
|
||||
#
|
||||
#kind owner file pattern
|
||||
req pkg.cargo Cargo.toml name = "nih_plug_template"
|
||||
req pkg.bundler bundler.toml [nih_plug_template]
|
||||
opt pkg.lock Cargo.lock name = "nih_plug_template"
|
||||
opt pkg.ci .github/workflows/ci.yaml cargo xtask bundle nih_plug_template
|
||||
opt pkg.readme README.md cargo xtask bundle nih_plug_template
|
||||
req display.bundler bundler.toml name = "Plugin Name"
|
||||
opt display.notice NOTICE Plugin Name
|
||||
req name.plugin src/lib.rs const NAME: &'static str = "Plugin Name"
|
||||
req vendor.plugin src/lib.rs const VENDOR: &'static str = "Your Name"
|
||||
req email.plugin src/lib.rs const EMAIL: &'static str = "your@email.com"
|
||||
req authors.cargo Cargo.toml authors = ["Your Name <your@email.com>"]
|
||||
req homepage.cargo Cargo.toml homepage = "https://github.com/YOUR-USERNAME/YOUR-PLUGIN-NAME"
|
||||
req url.plugin src/lib.rs const URL: &'static str = env!("CARGO_PKG_HOMEPAGE")
|
||||
req description.cargo Cargo.toml description = "A minimal NIH-plug audio plugin"
|
||||
req description.clap src/lib.rs const CLAP_DESCRIPTION: Option<&'static str> = Some("Your plugin description")
|
||||
req clapid.plugin src/lib.rs const CLAP_ID: &'static str = "com.your-domain.plugin-name"
|
||||
req vst3id.plugin src/lib.rs const VST3_CLASS_ID: [u8; 16] = *b"Unique16CharId!!"
|
||||
req struct.def src/lib.rs struct MyPlugin {
|
||||
req struct.plugin src/lib.rs impl Plugin for MyPlugin {
|
||||
req struct.clap src/lib.rs impl ClapPlugin for MyPlugin {
|
||||
req struct.vst3 src/lib.rs impl Vst3Plugin for MyPlugin {
|
||||
req struct.exportclap src/lib.rs nih_export_clap!(MyPlugin);
|
||||
req struct.exportvst3 src/lib.rs nih_export_vst3!(MyPlugin);
|
||||
req params.def src/lib.rs struct PluginParams {
|
||||
req params.default src/lib.rs impl Default for PluginParams {
|
||||
req params.field src/lib.rs params: Arc<PluginParams>,
|
||||
opt deploy.pkg deploy.ps1 $Package = "nih_plug_template"
|
||||
opt deploy.vst3 deploy.ps1 $vst3Name = "Plugin Name.vst3"
|
||||
opt deploy.clap deploy.ps1 $clapName = "Plugin Name.clap"
|
||||
|
Can't render this file because it contains an unexpected character in line 7 and column 64.
|
@@ -0,0 +1,90 @@
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# branding.conf - this plugin's canonical identity.
|
||||
#
|
||||
# Managed by tools/brand-plugin.sh, read by tools/check-branding.sh. Hand-editing
|
||||
# works and is sometimes the quickest thing, but the helper is what keeps this
|
||||
# file, the source tree and tools/branding-sites.tsv agreeing with each other.
|
||||
# Edit here and you are on the hook for running the checker afterwards.
|
||||
|
||||
# --- derivations ------------------------------------------------------------
|
||||
# Defined before the values so the defaults below can use them.
|
||||
# Package name: lowercased, every run of non-alphanumerics becomes one
|
||||
# underscore. Cargo accepts hyphens too and maps them to underscores for the
|
||||
# built artifact, which nih_plug_xtask already handles; underscores are used to
|
||||
# match the Rust convention.
|
||||
_derive_pkg() {
|
||||
printf '%s' "$1" | tr '[:upper:]' '[:lower:]' \
|
||||
| sed -e 's/[^a-z0-9]\{1,\}/_/g' -e 's/^_//' -e 's/_$//'
|
||||
}
|
||||
# Rust type name: CamelCase, non-alphanumerics dropped.
|
||||
_derive_struct() {
|
||||
printf '%s' "$1" | sed -e 's/[^A-Za-z0-9]\{1,\}/ /g' \
|
||||
| awk '{for(i=1;i<=NF;i++) printf "%s%s", toupper(substr($i,1,1)), substr($i,2); print ""}'
|
||||
}
|
||||
|
||||
# --- the names ---------------------------------------------------------------
|
||||
PLUGIN_NAME="Plugin Name" # the one name everything else derives from
|
||||
VENDOR="Your Name" # Plugin::VENDOR - the org, not the person
|
||||
EMAIL="your@email.com" # Plugin::EMAIL
|
||||
HOMEPAGE="https://github.com/YOUR-USERNAME/YOUR-PLUGIN-NAME" # -> Plugin::URL
|
||||
DESCRIPTION="A minimal NIH-plug audio plugin" # Cargo description
|
||||
|
||||
# What the plugin does *now*. No codename, no "prototype", no "v2 rewrite" -
|
||||
# hosts show this to users who never knew any of that. check-branding.sh
|
||||
# enforces the mechanical half by rejecting retired names and the repo codename.
|
||||
CLAP_DESCRIPTION="Your plugin description"
|
||||
|
||||
# --- frozen identity ---------------------------------------------------------
|
||||
# Hosts key saved projects and presets off these two. Change either and every
|
||||
# existing session silently loses the plugin: the track comes up empty, with no
|
||||
# error. Choose once before the first release, then never again.
|
||||
# brand-plugin.sh refuses to move them without --allow-identity-change.
|
||||
CLAP_ID="com.your-domain.plugin-name" # reverse-DNS, lowercase, no spaces
|
||||
VST3_CLASS_ID="Unique16CharId!!" # MUST be exactly 16 bytes
|
||||
|
||||
# --- fixed at repo creation --------------------------------------------------
|
||||
# The repository's own name. Deliberately NOT renamed when the plugin is: the
|
||||
# repo keeps its development codename while the plugin gets a product name.
|
||||
# Recorded only so it can be kept out of user-facing text.
|
||||
CODENAME="nih-plug-template"
|
||||
|
||||
# Where the two Rust types are defined. A plugin whose params outgrow lib.rs
|
||||
# points PARAMS_FILE at src/params.rs.
|
||||
LIB_FILE="src/lib.rs"
|
||||
PARAMS_FILE="src/lib.rs"
|
||||
|
||||
# --- derived unless overridden -----------------------------------------------
|
||||
# Set one only when the derivation is wrong or rejected - a name like
|
||||
# "206 Prototype" derives to "206_prototype", which cargo refuses because a
|
||||
# package name cannot start with a digit.
|
||||
#
|
||||
# This template overrides three of them, which a real plugin usually will not:
|
||||
# its package and type names are the template's own rather than derived from a
|
||||
# product name. Delete these three lines after renaming and the derivations
|
||||
# from PLUGIN_NAME take over.
|
||||
PKG="nih_plug_template"
|
||||
STRUCT="MyPlugin"
|
||||
PARAMS_STRUCT="PluginParams"
|
||||
# DISPLAY="Plugin Name" # defaults to PLUGIN_NAME
|
||||
: "${DISPLAY:=$PLUGIN_NAME}"
|
||||
: "${PKG:=$(_derive_pkg "$PLUGIN_NAME")}"
|
||||
: "${STRUCT:=$(_derive_struct "$PLUGIN_NAME")}"
|
||||
: "${PARAMS_STRUCT:=${STRUCT}Params}"
|
||||
|
||||
# --- retired spellings -------------------------------------------------------
|
||||
# Must no longer appear in tracked files. An entry is skipped while it is still
|
||||
# part of a canonical value above and arms the moment it stops being one, so a
|
||||
# rename is self-checking in both directions. brand-plugin.sh appends here.
|
||||
STALE="nih_plug_template
|
||||
MyPlugin
|
||||
PluginParams
|
||||
A minimal NIH-plug audio plugin
|
||||
Your plugin description
|
||||
Plugin Name
|
||||
Your Name
|
||||
your@email.com
|
||||
com.your-domain.plugin-name
|
||||
Unique16CharId!!
|
||||
YOUR-USERNAME
|
||||
YOUR-PLUGIN-NAME
|
||||
your-domain"
|
||||
@@ -12,20 +12,22 @@
|
||||
# with this repo, and it touches every file below. Run this immediately after,
|
||||
# and the STALE machinery will list whatever you missed.
|
||||
#
|
||||
# Usage: sh check-branding.sh (exit 0 = consistent, 1 = drift found)
|
||||
# Usage: sh tools/check-branding.sh (exit 0 = consistent, 1 = drift found)
|
||||
#
|
||||
# The canonical identity lives in branding.conf, shared with brand-plugin.sh.
|
||||
# Edit it there, then run this to find every copy that still holds the old value.
|
||||
|
||||
set -u
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
# Resolve the repo root: these live in tools/, every path they touch is
|
||||
# repo-relative, and they must behave the same wherever they are invoked from.
|
||||
cd "$(dirname "$0")/.." || exit 1
|
||||
|
||||
if [ ! -f branding.conf ]; then
|
||||
printf 'branding.conf not found next to this script\n' >&2
|
||||
if [ ! -f tools/branding.conf ]; then
|
||||
printf 'tools/branding.conf not found\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
# shellcheck disable=SC1091
|
||||
. ./branding.conf
|
||||
. ./tools/branding.conf
|
||||
|
||||
# Every canonical spelling concatenated, for the substring test below.
|
||||
CANONICAL_BLOB="$PKG
|
||||
@@ -65,66 +67,56 @@ opt_check() { # opt_check LABEL FILE LITERAL
|
||||
|
||||
printf 'branding check (canonical: %s / "%s")\n\n' "$PKG" "$DISPLAY"
|
||||
|
||||
printf 'build manifests\n'
|
||||
check "Cargo.toml package name" Cargo.toml "name = \"$PKG\""
|
||||
check "Cargo.toml homepage" Cargo.toml "homepage = \"$HOMEPAGE\""
|
||||
check "Cargo.toml description" Cargo.toml "description = \"$DESCRIPTION\""
|
||||
check "Cargo.toml authors" Cargo.toml "authors = [\"$VENDOR <$EMAIL>\"]"
|
||||
# Cargo regenerates the lockfile on the next build, but only if someone builds
|
||||
# before pushing. A stale root entry here means CI resolves a package that no
|
||||
# longer exists under that name.
|
||||
opt_check "Cargo.lock package" Cargo.lock "name = \"$PKG\""
|
||||
check "bundler.toml table key" bundler.toml "[$PKG]"
|
||||
check "bundler.toml display name" bundler.toml "name = \"$DISPLAY\""
|
||||
# --- consistency: every site in the table holds its resolved literal ----------
|
||||
# The table is data, so adding a site is a row rather than a shell edit, and the
|
||||
# same script runs unmodified across every plugin built from this template.
|
||||
SITES="tools/branding-sites.tsv"
|
||||
if [ ! -f "$SITES" ]; then
|
||||
bad "site table" "$SITES not found - run tools/brand-plugin.sh to generate it"
|
||||
else
|
||||
printf 'sites\n'
|
||||
while IFS="$(printf '\t')" read -r kind owner file pattern; do
|
||||
case "$kind" in ''|\#*) continue ;; esac
|
||||
[ -n "$pattern" ] || continue
|
||||
label="$owner"
|
||||
if [ ! -f "$file" ]; then
|
||||
case "$kind" in
|
||||
opt) note "$label ($file not present)" ;;
|
||||
*) bad "$label" "missing file: $file" ;;
|
||||
esac
|
||||
continue
|
||||
fi
|
||||
if has "$file" "$pattern"; then ok "$label"
|
||||
else bad "$label" "$file does not contain: $pattern"; fi
|
||||
done < "$SITES"
|
||||
fi
|
||||
|
||||
printf '\nsource identity (src/lib.rs)\n'
|
||||
check "Plugin::NAME" "$LIB_FILE" "const NAME: &'static str = \"$PLUGIN_NAME\""
|
||||
check "Plugin::VENDOR" "$LIB_FILE" "const VENDOR: &'static str = \"$VENDOR\""
|
||||
check "Plugin::EMAIL" "$LIB_FILE" "const EMAIL: &'static str = \"$EMAIL\""
|
||||
check "CLAP_ID" "$LIB_FILE" "const CLAP_ID: &'static str = \"$CLAP_ID\""
|
||||
check "CLAP_DESCRIPTION" "$LIB_FILE" "const CLAP_DESCRIPTION: Option<&'static str> = Some(\"$CLAP_DESCRIPTION\")"
|
||||
check "VST3_CLASS_ID" "$LIB_FILE" "const VST3_CLASS_ID: [u8; 16] = *b\"$VST3_CLASS_ID\""
|
||||
# --- validity: rules the table cannot express --------------------------------
|
||||
printf '\nvalidity\n'
|
||||
|
||||
# Plugin::URL is env!("CARGO_PKG_HOMEPAGE"), so the manifest is the only place
|
||||
# it is spelled. Assert the indirection is still in place rather than hardcoded.
|
||||
check "Plugin::URL from manifest" "$LIB_FILE" "const URL: &'static str = env!(\"CARGO_PKG_HOMEPAGE\")"
|
||||
|
||||
# VST3 class IDs are a fixed 16-byte array. A shorter or longer literal is a
|
||||
# compile error, but it is far clearer to say so here than to read rustc on it.
|
||||
# VST3 class IDs are a fixed 16-byte array. A wrong length is a compile error,
|
||||
# but saying so here is clearer than reading rustc on it.
|
||||
n=$(printf '%s' "$VST3_CLASS_ID" | wc -c | tr -d ' ')
|
||||
if [ "$n" -eq 16 ]; then ok "VST3_CLASS_ID is 16 bytes"
|
||||
else bad "VST3_CLASS_ID is 16 bytes" "got $n bytes: \"$VST3_CLASS_ID\""; fi
|
||||
|
||||
# CLAP IDs are reverse-DNS by convention and are compared verbatim by hosts, so
|
||||
# a stray space or capital is a silent identity change between machines.
|
||||
# CLAP IDs are reverse-DNS by convention and compared verbatim by hosts, so a
|
||||
# stray space or capital is a silent identity change between machines.
|
||||
case "$CLAP_ID" in
|
||||
*\ *|*[A-Z]*) bad "CLAP_ID is reverse-DNS safe" "no spaces or capitals allowed: \"$CLAP_ID\"" ;;
|
||||
*.*) ok "CLAP_ID is reverse-DNS safe" ;;
|
||||
*) bad "CLAP_ID is reverse-DNS safe" "expected dotted reverse-DNS, got: \"$CLAP_ID\"" ;;
|
||||
esac
|
||||
|
||||
printf '\nstructural names (src/lib.rs)\n'
|
||||
# Every site the type names appear. A rename that misses one is a compile error
|
||||
# rather than silent drift, but naming the site is faster than reading rustc,
|
||||
# and the Vst3Plugin impl sits behind a cfg so a default build never reports it.
|
||||
check "struct $STRUCT" "$LIB_FILE" "struct $STRUCT {"
|
||||
check "impl Plugin" "$LIB_FILE" "impl Plugin for $STRUCT {"
|
||||
check "impl ClapPlugin" "$LIB_FILE" "impl ClapPlugin for $STRUCT {"
|
||||
check "impl Vst3Plugin" "$LIB_FILE" "impl Vst3Plugin for $STRUCT {"
|
||||
check "nih_export_clap!" "$LIB_FILE" "nih_export_clap!($STRUCT);"
|
||||
check "nih_export_vst3!" "$LIB_FILE" "nih_export_vst3!($STRUCT);"
|
||||
check "struct $PARAMS_STRUCT" "$PARAMS_FILE" "struct $PARAMS_STRUCT {"
|
||||
check "impl Default for params" "$PARAMS_FILE" "impl Default for $PARAMS_STRUCT {"
|
||||
check "params field type" "$LIB_FILE" "params: Arc<$PARAMS_STRUCT>,"
|
||||
# An overridden package name is legitimate - the derivation is sometimes invalid
|
||||
# - but a display rename that forgot the package is not, and looks identical
|
||||
# from here. Reported, never failed.
|
||||
derived_pkg=$(_derive_pkg "$PLUGIN_NAME")
|
||||
[ "$PKG" = "$derived_pkg" ] || info "PKG \"$PKG\" differs from the derivation of PLUGIN_NAME (\"$derived_pkg\") - fine if deliberate"
|
||||
|
||||
printf '\ndocs\n'
|
||||
opt_check "README bundle command" README.md "cargo xtask bundle $PKG"
|
||||
|
||||
# CLAP_DESCRIPTION is shown to users in a host's plugin browser. It should say
|
||||
# what the plugin does now, with no development history in it. "Free of history"
|
||||
# is a judgement a script cannot make, but the failure it produces is mechanical:
|
||||
# a description still carrying a retired name or the repo's development codename.
|
||||
printf '\ndescription hygiene\n'
|
||||
# CLAP_DESCRIPTION is shown to users in a host's plugin browser. "Free of
|
||||
# development history" is a judgement a script cannot make, but the failure it
|
||||
# produces is mechanical: a retired name or the repo codename surviving in it.
|
||||
hygiene_hits=""
|
||||
while IFS= read -r s_entry; do
|
||||
[ -n "$s_entry" ] || continue
|
||||
@@ -137,22 +129,6 @@ HYGIENE_LIST
|
||||
if [ -z "$hygiene_hits" ]; then ok "CLAP_DESCRIPTION carries no retired name or codename"
|
||||
else bad "CLAP_DESCRIPTION carries no retired name or codename" "found:$hygiene_hits"; fi
|
||||
|
||||
printf '\nci\n'
|
||||
# CI bundles by package name, so a rename that misses this file breaks the build
|
||||
# rather than drifting quietly - but it breaks after a full compile, which is a
|
||||
# slow way to learn about a typo. Checking it here costs nothing.
|
||||
opt_check "CI bundle command" .github/workflows/ci.yaml "cargo xtask bundle $PKG"
|
||||
|
||||
printf '\nattribution\n'
|
||||
# NOTICE names the product in its first line; a rename that misses it ships an
|
||||
# artifact crediting a name that no longer exists.
|
||||
opt_check "NOTICE product name" NOTICE "$DISPLAY"
|
||||
|
||||
printf '\ndeploy script\n'
|
||||
opt_check "deploy.ps1 default package" deploy.ps1 "\$Package = \"$PKG\""
|
||||
opt_check "deploy.ps1 VST3 bundle" deploy.ps1 "\$vst3Name = \"$DISPLAY.vst3\""
|
||||
opt_check "deploy.ps1 CLAP bundle" deploy.ps1 "\$clapName = \"$DISPLAY.clap\""
|
||||
|
||||
printf '\nstale spellings\n'
|
||||
# File list for the scan below. Prefer git, so build output and any vendored
|
||||
# tree stay out of it. Fall back to find when this is not a checkout - an
|
||||
@@ -186,7 +162,7 @@ while IFS= read -r s; do
|
||||
# These two name every retired spelling by definition - the STALE list lives in
|
||||
# branding.conf and this script reads it - so neither can ever be a real hit.
|
||||
hits=$(file_list 2>/dev/null | while read -r f; do
|
||||
case "$f" in check-branding.sh|branding.conf) continue ;; esac
|
||||
case "$f" in tools/check-branding.sh|tools/branding.conf) continue ;; esac
|
||||
[ -f "$f" ] && grep -lF "$s" "$f" 2>/dev/null
|
||||
done)
|
||||
if [ -z "$hits" ]; then ok "no occurrences of \"$s\""
|
||||
Reference in New Issue
Block a user