From 1ea0443548c3ec6657233fd5c1e1aa4f05591c6b Mon Sep 17 00:00:00 2001 From: Oli Larkin Date: Sun, 17 May 2026 11:16:28 +0200 Subject: [PATCH 1/6] Use Markdown for installer and manual docs --- .github/workflows/build-mac.yml | 11 ++ .github/workflows/build-win.yml | 10 ++ TemplateProject/README.md | 4 + TemplateProject/installer/TemplateProject.iss | 12 +- TemplateProject/installer/intro.md | 7 + TemplateProject/installer/intro.rtf | 13 -- TemplateProject/installer/license.md | 37 +++++ TemplateProject/installer/license.rtf | 45 ------ TemplateProject/installer/readme-mac.md | 9 ++ TemplateProject/installer/readme-mac.rtf | 16 --- TemplateProject/installer/readme-win-demo.md | 11 ++ TemplateProject/installer/readme-win-demo.rtf | 20 --- TemplateProject/installer/readme-win.md | 11 ++ TemplateProject/installer/readme-win.rtf | 20 --- .../manual/TemplateProject manual.md | 5 + .../manual/TemplateProject manual.pdf | Bin 7619 -> 0 bytes .../manual/TemplateProject manual.tex | 3 - TemplateProject/scripts/makedist-mac.sh | 2 +- TemplateProject/scripts/makedist-win.bat | 7 +- TemplateProject/scripts/makeinstaller-mac.sh | 8 +- TemplateProject/scripts/makezip-win.py | 2 +- .../scripts/prepare_installer_docs.py | 135 ++++++++++++++++++ .../scripts/update_installer-win.py | 6 +- 23 files changed, 263 insertions(+), 131 deletions(-) create mode 100644 TemplateProject/installer/intro.md delete mode 100644 TemplateProject/installer/intro.rtf create mode 100644 TemplateProject/installer/license.md delete mode 100644 TemplateProject/installer/license.rtf create mode 100644 TemplateProject/installer/readme-mac.md delete mode 100644 TemplateProject/installer/readme-mac.rtf create mode 100644 TemplateProject/installer/readme-win-demo.md delete mode 100644 TemplateProject/installer/readme-win-demo.rtf create mode 100644 TemplateProject/installer/readme-win.md delete mode 100644 TemplateProject/installer/readme-win.rtf create mode 100644 TemplateProject/manual/TemplateProject manual.md delete mode 100644 TemplateProject/manual/TemplateProject manual.pdf delete mode 100644 TemplateProject/manual/TemplateProject manual.tex create mode 100644 TemplateProject/scripts/prepare_installer_docs.py diff --git a/.github/workflows/build-mac.yml b/.github/workflows/build-mac.yml index 6541ec28..fd1d473e 100644 --- a/.github/workflows/build-mac.yml +++ b/.github/workflows/build-mac.yml @@ -91,6 +91,17 @@ jobs: cd iPlug2/Dependencies/IPlug ./download-clap-sdks.sh ${{env.CLAP_VER}} + - name: Validate installer docs + shell: bash + run: | + set -eo pipefail + cd ${{matrix.project}} + python3 scripts/prepare_installer_docs.py mac + test -f build-mac/installer/resources/license.rtf + test -f build-mac/installer/resources/readme-mac.rtf + test -f build-mac/installer/resources/intro.rtf + test -f "manual/${{matrix.project}} manual.md" + - name: Build shell: bash run: | diff --git a/.github/workflows/build-win.yml b/.github/workflows/build-win.yml index 24f015c2..cc6cdfcd 100644 --- a/.github/workflows/build-win.yml +++ b/.github/workflows/build-win.yml @@ -80,6 +80,16 @@ jobs: cd iPlug2/Dependencies/IPlug ./download-clap-sdks.sh ${{env.CLAP_VER}} + - name: Validate installer docs + shell: pwsh + run: | + cd ${{matrix.project}} + python scripts/prepare_installer_docs.py win + if (!(Test-Path "build-win\installer-docs\license.txt")) { throw "Missing generated license.txt" } + if (!(Test-Path "build-win\installer-docs\readme-win.txt")) { throw "Missing generated readme-win.txt" } + if (!(Test-Path "build-win\installer-docs\readme-win-demo.txt")) { throw "Missing generated readme-win-demo.txt" } + if (!(Test-Path "manual\${{matrix.project}} manual.md")) { throw "Missing Markdown manual" } + - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v2 diff --git a/TemplateProject/README.md b/TemplateProject/README.md index 35a7e084..93a2a3d9 100644 --- a/TemplateProject/README.md +++ b/TemplateProject/README.md @@ -1,3 +1,7 @@ # TemplateProject readme iPlug2 template project + +## Distribution docs + +Installer text lives in `installer/*.md` and the user manual lives in `manual/TemplateProject manual.md`. The packaging scripts generate installer-compatible RTF/TXT files from those Markdown sources during release builds. diff --git a/TemplateProject/installer/TemplateProject.iss b/TemplateProject/installer/TemplateProject.iss index d6e2f6b9..5e729741 100644 --- a/TemplateProject/installer/TemplateProject.iss +++ b/TemplateProject/installer/TemplateProject.iss @@ -14,7 +14,7 @@ SolidCompression=yes OutputDir=.\..\build-win\installer ArchitecturesInstallIn64BitMode=x64 OutputBaseFilename=TemplateProject Installer -LicenseFile=license.rtf +LicenseFile=..\build-win\installer-docs\license.txt SetupLogging=yes ShowComponentSizes=no ; WizardImageFile=installer_bg-win.bmp @@ -73,15 +73,15 @@ Source: "..\build-win\TemplateProject.clap"; DestDir: {commoncf64}\CLAP\; Check: ;Source: "..\build-win\TemplateProject.aaxplugin\Desktop.ini"; DestDir: "{cf64}\Avid\Audio\Plug-Ins\TemplateProject.aaxplugin\"; Check: Is64BitInstallMode; Components:aax_64; Flags: overwritereadonly ignoreversion; Attribs: hidden system; ;Source: "..\build-win\TemplateProject.aaxplugin\PlugIn.ico"; DestDir: "{cf64}\Avid\Audio\Plug-Ins\TemplateProject.aaxplugin\"; Check: Is64BitInstallMode; Components:aax_64; Flags: overwritereadonly ignoreversion; Attribs: hidden system; -Source: "..\manual\TemplateProject manual.pdf"; DestDir: "{app}" +Source: "..\manual\TemplateProject manual.md"; DestDir: "{app}" Source: "changelog.txt"; DestDir: "{app}" -Source: "readme-win.rtf"; DestDir: "{app}"; DestName: "readme.rtf"; Flags: isreadme +Source: "..\build-win\installer-docs\readme-win.txt"; DestDir: "{app}"; DestName: "readme.txt"; Flags: isreadme [Icons] Name: "{group}\TemplateProject"; Filename: "{app}\TemplateProject_x64.exe" -Name: "{group}\User guide"; Filename: "{app}\TemplateProject manual.pdf" +Name: "{group}\User guide"; Filename: "{app}\TemplateProject manual.md" Name: "{group}\Changelog"; Filename: "{app}\changelog.txt" -;Name: "{group}\readme"; Filename: "{app}\readme.rtf" +;Name: "{group}\readme"; Filename: "{app}\readme.txt" Name: "{group}\Uninstall TemplateProject"; Filename: "{app}\unins000.exe" [Code] @@ -142,4 +142,4 @@ begin end; [UninstallDelete] -Type: files; Name: "{app}\InstallationLogFile.log" \ No newline at end of file +Type: files; Name: "{app}\InstallationLogFile.log" diff --git a/TemplateProject/installer/intro.md b/TemplateProject/installer/intro.md new file mode 100644 index 00000000..01dd2df1 --- /dev/null +++ b/TemplateProject/installer/intro.md @@ -0,0 +1,7 @@ +# TemplateProject + +Thanks for purchasing TemplateProject. + +contact@acmeinc.com + +http://www.acmeinc.com diff --git a/TemplateProject/installer/intro.rtf b/TemplateProject/installer/intro.rtf deleted file mode 100644 index 1372ced0..00000000 --- a/TemplateProject/installer/intro.rtf +++ /dev/null @@ -1,13 +0,0 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf600 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 LucidaGrande;} -{\colortbl;\red255\green255\blue255;} -{\*\expandedcolortbl;;} -\paperw11900\paperh16840\margl1440\margr1440\vieww14440\viewh8920\viewkind0 -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 - -\f0\fs26 \cf0 Thanks for purchasing TemplateProject\'85 \ -\ -{\field{\*\fldinst{HYPERLINK "mailto::contact@acmeinc.com"}}{\fldrslt contact@acmeinc.com}}\ -\ -{\field{\*\fldinst{HYPERLINK "http://www.acmeinc.com"}}{\fldrslt http://www.acmeinc.com}}\ -} \ No newline at end of file diff --git a/TemplateProject/installer/license.md b/TemplateProject/installer/license.md new file mode 100644 index 00000000..fe9508ee --- /dev/null +++ b/TemplateProject/installer/license.md @@ -0,0 +1,37 @@ +# TemplateProject License + +**THIS IS A PLACEHOLDER LICENCE PROVIDED WITH IPLUG2** + +**IT HAS NO LEGAL BASIS** + +**YOU MAY WISH TO CONSULT A LAWYER BEFORE MAKING A LICENCE** + +## Caveat + +By installing this software you agree to use it at your own risk. The developer cannot be held responsible for any damages caused as a result of its use. + +## Distribution + +You are not permitted to distribute the software without the developer's permission. This includes, but is not limited to, distribution on magazine covers or software review websites. + +## Multiple Installations* + +If you purchased this product as an individual, you are licensed to install and use the software on any computer you need to use it on, providing you remove it afterwards if it is a shared machine. If you purchased it as an institution or company, you are licensed to use it on one machine only, and must purchase additional copies for each machine you wish to use it on. + +## Upgrades* + +If you purchased this product you are entitled to free updates until the next major version number. The developer makes no guarantee that this product will be maintained indefinitely. + +## License transfers* + +If you purchased this product you may transfer your license to another person. As the original owner you are required to contact the developer with the details of the license transfer, so that the new owner can receive the updates and support attached to the license. Upon transferring a license the original owner must remove any copies from their machines and are no longer permitted to use the software. + +TemplateProject is (C) Copyright AcmeInc 2019 + +http://www.acmeinc.com + +VST and VST3 are trademarks of Steinberg Media Technologies GmbH. +Audio Unit is a trademark of Apple, Inc. +AAX is a trademark of Avid, Inc. + +Applies to full version only, not the demo version. diff --git a/TemplateProject/installer/license.rtf b/TemplateProject/installer/license.rtf deleted file mode 100644 index 6a20a306..00000000 --- a/TemplateProject/installer/license.rtf +++ /dev/null @@ -1,45 +0,0 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf600 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 ArialMT;} -{\colortbl;\red255\green255\blue255;} -{\*\expandedcolortbl;;} -\paperw11900\paperh16840\margl1440\margr1440\vieww17060\viewh12300\viewkind0 -\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 - -\f0\b\fs20 \cf0 THIS IS A PLACEHOLDER LICENCE PROVIDED WITH IPLUG2 \ -\ -IT HAS NO LEGAL BASIS\ -\ -YOU MAY WISH TO CONSULT A LAWYER BEFORE MAKING A LICENCE\ -\ -Caveat: -\b0 \ -By installing this software you agree to use it at your own risk. The developer cannot be held responsible for any damages caused as a result of it's use.\ -\ - -\b Distribution: -\b0 \ -You are not permitted to distribute the software without the developer's permission. This includes, but is not limited to the distribution on magazine covers or software review websites.\ -\ - -\b Multiple Installations*: -\b0 If you purchased this product as an individual, you are licensed to install and use the software on any computer you need to use it on, providing you remove it afterwards (if it is a shared machine). If you purchased it as an institution or company, you are licensed to use it on one machine only, and must purchase additional copies for each machine you wish to use it on.\ -\ - -\b Upgrades*: -\b0 If you purchased this product you are entitled to free updates until the next major version number. The developer makes no guarantee is made that this product will be maintained indefinitely.\ -\ - -\b License transfers*: -\b0 If you purchased this product you may transfer your license to another person. As the original owner you are required to contact the developer with the details of the license transfer, so that the new owner can receive the updates and support attached to the license. Upon transferring a license the original owner must remove any copies from their machines and are no longer permitted to use the software.\ -\ - -\b TemplateProject is \'a9 Copyright AcmeInc 2019\ - -\b0 \ -http://www.acmeinc.com\ -\ -VST and VST3 are trademarks of Steinberg Media Technologies GmbH. \ -Audio Unit is a trademark of Apple, Inc. \ -AAX is a trademarks of Avid, Inc.\ -\ -* Applies to full version only, not the demo version.} \ No newline at end of file diff --git a/TemplateProject/installer/readme-mac.md b/TemplateProject/installer/readme-mac.md new file mode 100644 index 00000000..82a1335d --- /dev/null +++ b/TemplateProject/installer/readme-mac.md @@ -0,0 +1,9 @@ +# TemplateProject + +The plugins will be installed in your system plugin folders, which will make them available to all user accounts on your computer. The standalone will be installed in the system Applications folder. + +If you do not want to install all components, click **Customize** on the **Installation Type** page. + +If you experience any problems with TemplateProject, please contact us at: + +support@acmeinc.com diff --git a/TemplateProject/installer/readme-mac.rtf b/TemplateProject/installer/readme-mac.rtf deleted file mode 100644 index ca532ebb..00000000 --- a/TemplateProject/installer/readme-mac.rtf +++ /dev/null @@ -1,16 +0,0 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf600 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 LucidaGrande;\f1\fnil\fcharset0 Monaco;} -{\colortbl;\red255\green255\blue255;} -{\*\expandedcolortbl;;} -\paperw11900\paperh16840\margl1440\margr1440\vieww14320\viewh8340\viewkind0 -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 - -\f0\fs26 \cf0 The plugins will be installed in your system plugin folders which will make them available to all user accounts on your computer. -\f1\fs20 -\f0\fs26 The standalone will be installed in the system Applications folder. \ -\ -If you don't want to install all components, click "Customize" on the "Installation Type" page.\ -\ -If you experience any problems with TemplateProject, please contact us at the following address:\ -\ -{\field{\*\fldinst{HYPERLINK "mailto::support@acmeinc.com"}}{\fldrslt support@acmeinc.com}}} \ No newline at end of file diff --git a/TemplateProject/installer/readme-win-demo.md b/TemplateProject/installer/readme-win-demo.md new file mode 100644 index 00000000..93de5e79 --- /dev/null +++ b/TemplateProject/installer/readme-win-demo.md @@ -0,0 +1,11 @@ +# TemplateProject Demo + +Thanks for installing TemplateProject Demo. + +The Demo is limited ... + +AcmeInc + +If you experience any problems with TemplateProject, please contact us at: + +support@acmeinc.com diff --git a/TemplateProject/installer/readme-win-demo.rtf b/TemplateProject/installer/readme-win-demo.rtf deleted file mode 100644 index f27316c3..00000000 --- a/TemplateProject/installer/readme-win-demo.rtf +++ /dev/null @@ -1,20 +0,0 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf600 -{\fonttbl\f0\fnil\fcharset0 LucidaGrande-Bold;\f1\fnil\fcharset0 LucidaGrande;} -{\colortbl;\red255\green255\blue255;} -{\*\expandedcolortbl;;} -\paperw11900\paperh16840\vieww12000\viewh15840\viewkind0 -\deftab720 -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\partightenfactor0 - -\f0\b\fs26 \cf0 Thanks for installing TemplateProject Demo -\f1\b0 \ -\ -The Demo is limited ...\ -\ -AcmeInc\ -\ -If you experience any problems with TemplateProject, please contact us at the following address:\ -\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 -{\field{\*\fldinst{HYPERLINK "mailto::support@acmeinc.com"}}{\fldrslt \cf0 support@acmeinc.com}}\ -} \ No newline at end of file diff --git a/TemplateProject/installer/readme-win.md b/TemplateProject/installer/readme-win.md new file mode 100644 index 00000000..6ec6023b --- /dev/null +++ b/TemplateProject/installer/readme-win.md @@ -0,0 +1,11 @@ +# TemplateProject + +Thanks for installing TemplateProject. + +BLAH BLAH BLAH + +AcmeInc + +If you experience any problems with TemplateProject, please contact us at: + +acmeinc@AcmeInc.com diff --git a/TemplateProject/installer/readme-win.rtf b/TemplateProject/installer/readme-win.rtf deleted file mode 100644 index f1ccace1..00000000 --- a/TemplateProject/installer/readme-win.rtf +++ /dev/null @@ -1,20 +0,0 @@ -{\rtf1\ansi\ansicpg1252\cocoartf1561\cocoasubrtf600 -{\fonttbl\f0\fnil\fcharset0 LucidaGrande-Bold;\f1\fnil\fcharset0 LucidaGrande;} -{\colortbl;\red255\green255\blue255;} -{\*\expandedcolortbl;;} -\paperw11900\paperh16840\vieww12000\viewh15840\viewkind0 -\deftab720 -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\partightenfactor0 - -\f0\b\fs26 \cf0 Thanks for installing TemplateProject -\f1\b0 \ -\ -BLAH BLAH BLAH\ -\ -AcmeInc\ -\ -If you experience any problems with TemplateProject, please contact us at the following address:\ -\ -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0 -{\field{\*\fldinst{HYPERLINK "mailto::support@acmeinc.com"}}{\fldrslt \cf0 acmeinc@AcmeInc.com}}\ -} \ No newline at end of file diff --git a/TemplateProject/manual/TemplateProject manual.md b/TemplateProject/manual/TemplateProject manual.md new file mode 100644 index 00000000..0b5437d5 --- /dev/null +++ b/TemplateProject/manual/TemplateProject manual.md @@ -0,0 +1,5 @@ +# TemplateProject Manual + +This is a placeholder manual for TemplateProject. + +Replace this file with product documentation before shipping. diff --git a/TemplateProject/manual/TemplateProject manual.pdf b/TemplateProject/manual/TemplateProject manual.pdf deleted file mode 100644 index 799c4ae5dbb0df7bf342879aa7924783644f835c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7619 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 Ogr8ANOv_lC@qYk1)2I&s diff --git a/TemplateProject/manual/TemplateProject manual.tex b/TemplateProject/manual/TemplateProject manual.tex deleted file mode 100644 index 216ad1a1..00000000 --- a/TemplateProject/manual/TemplateProject manual.tex +++ /dev/null @@ -1,3 +0,0 @@ -\documentclass[a4paper,14pt]{report} -\begin{document} -\end{document} \ No newline at end of file diff --git a/TemplateProject/scripts/makedist-mac.sh b/TemplateProject/scripts/makedist-mac.sh index ab443953..ce412254 100755 --- a/TemplateProject/scripts/makedist-mac.sh +++ b/TemplateProject/scripts/makedist-mac.sh @@ -295,7 +295,7 @@ if [ $BUILD_INSTALLER == 1 ]; then else cp installer/changelog.txt build-mac/installer/ cp installer/known-issues.txt build-mac/installer/ - cp "manual/$PLUGIN_NAME manual.pdf" build-mac/installer/ + cp "manual/$PLUGIN_NAME manual.md" build-mac/installer/ hdiutil create build-mac/$ARCHIVE_NAME.dmg -format UDZO -srcfolder build-mac/installer/ -ov -anyowners -volname $PLUGIN_NAME fi diff --git a/TemplateProject/scripts/makedist-win.bat b/TemplateProject/scripts/makedist-win.bat index 98cbce2e..fffbde31 100644 --- a/TemplateProject/scripts/makedist-win.bat +++ b/TemplateProject/scripts/makedist-win.bat @@ -37,6 +37,11 @@ echo ------------------------------------------------------------------ echo Updating version numbers ... call python prepare_resources-win.py %DEMO% +call python prepare_installer_docs.py win +if %ERRORLEVEL% neq 0 ( + echo ERROR: preparing installer docs failed + exit /B 1 +) call python update_installer-win.py %DEMO% cd ..\ @@ -139,4 +144,4 @@ exit /B 1 :SUCCESS echo %ZIP_NAME% -exit /B 0 \ No newline at end of file +exit /B 0 diff --git a/TemplateProject/scripts/makeinstaller-mac.sh b/TemplateProject/scripts/makeinstaller-mac.sh index c941b94e..6c5fc93a 100755 --- a/TemplateProject/scripts/makeinstaller-mac.sh +++ b/TemplateProject/scripts/makeinstaller-mac.sh @@ -44,6 +44,7 @@ OUTPUT_BASE_FILENAME="${PRODUCT_NAME} Installer.pkg" TARGET_DIR="./build-mac/installer" PKG_DIR=${TARGET_DIR}/pkgs +RESOURCES_DIR=${TARGET_DIR}/resources if [[ ! -d ${TARGET_DIR} ]]; then mkdir ${TARGET_DIR} @@ -53,6 +54,8 @@ if [[ ! -d ${PKG_DIR} ]]; then mkdir ${PKG_DIR} fi +python3 ./scripts/prepare_installer_docs.py mac || exit 1 + build_flavor() { TMPDIR=${TARGET_DIR}/tmp @@ -193,7 +196,8 @@ XMLEND # build installation bundle # --resources . -productbuild --distribution ${TARGET_DIR}/distribution.xml --package-path ${PKG_DIR} "${TARGET_DIR}/$OUTPUT_BASE_FILENAME" +productbuild --distribution ${TARGET_DIR}/distribution.xml --resources ${RESOURCES_DIR} --package-path ${PKG_DIR} "${TARGET_DIR}/$OUTPUT_BASE_FILENAME" rm ${TARGET_DIR}/distribution.xml -rm -r $PKG_DIR \ No newline at end of file +rm -r $PKG_DIR +rm -r $RESOURCES_DIR diff --git a/TemplateProject/scripts/makezip-win.py b/TemplateProject/scripts/makezip-win.py index 14943e01..df1445b2 100644 --- a/TemplateProject/scripts/makezip-win.py +++ b/TemplateProject/scripts/makezip-win.py @@ -70,7 +70,7 @@ def main(): projectpath + installer, projectpath + "\\installer\\changelog.txt", projectpath + "\\installer\\known-issues.txt", - projectpath + "\\manual\\TemplateProject manual.pdf" + projectpath + "\\manual\\TemplateProject manual.md" ] for f in files: diff --git a/TemplateProject/scripts/prepare_installer_docs.py b/TemplateProject/scripts/prepare_installer_docs.py new file mode 100644 index 00000000..e1d68316 --- /dev/null +++ b/TemplateProject/scripts/prepare_installer_docs.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 + +import argparse +import os +import re +import shutil + + +PRODUCT_NAME = "TemplateProject" + +SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) +PROJECT_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, os.pardir)) +INSTALLER_DIR = os.path.join(PROJECT_DIR, "installer") + + +def read_text(path): + with open(path, "r", encoding="utf-8") as input_file: + return input_file.read() + + +def write_text(path, text): + os.makedirs(os.path.dirname(path), exist_ok=True) + with open(path, "w", encoding="utf-8", newline="\n") as output_file: + output_file.write(text) + + +def markdown_to_plain_text(markdown): + lines = [] + in_code_block = False + + for line in markdown.splitlines(): + stripped = line.strip() + + if stripped.startswith("```"): + in_code_block = not in_code_block + continue + + if not in_code_block: + line = re.sub(r"^\s{0,3}#{1,6}\s*", "", line) + line = re.sub(r"\[([^\]]+)\]\([^\)]+\)", r"\1", line) + line = re.sub(r"\*\*([^*]+)\*\*", r"\1", line) + line = re.sub(r"__([^_]+)__", r"\1", line) + line = re.sub(r"\*([^*]+)\*", r"\1", line) + line = re.sub(r"_([^_]+)_", r"\1", line) + line = re.sub(r"`([^`]+)`", r"\1", line) + line = re.sub(r"^\s*[-*+]\s+", "- ", line) + + lines.append(line.rstrip()) + + return "\n".join(lines).strip() + "\n" + + +def rtf_escape(text): + escaped = [] + + for char in text: + codepoint = ord(char) + + if char == "\\": + escaped.append(r"\\") + elif char == "{": + escaped.append(r"\{") + elif char == "}": + escaped.append(r"\}") + elif char == "\t": + escaped.append(r"\tab ") + elif codepoint <= 0x7f: + escaped.append(char) + else: + signed_codepoint = codepoint if codepoint < 32768 else codepoint - 65536 + escaped.append(r"\u" + str(signed_codepoint) + "?") + + return "".join(escaped) + + +def plain_text_to_rtf(text): + lines = [ + r"{\rtf1\ansi\deff0", + r"{\fonttbl{\f0 Helvetica;}}", + r"\fs24", + ] + + for line in text.splitlines(): + if line: + lines.append(rtf_escape(line) + r"\par") + else: + lines.append(r"\par") + + lines.append("}") + + return "\n".join(lines) + "\n" + + +def markdown_file_to_plain_text(name): + return markdown_to_plain_text(read_text(os.path.join(INSTALLER_DIR, name + ".md"))) + + +def build_mac_docs(): + target_dir = os.path.join(PROJECT_DIR, "build-mac", "installer", "resources") + + for name in ("license", "readme-mac", "intro"): + source_text = markdown_file_to_plain_text(name) + write_text(os.path.join(target_dir, name + ".rtf"), plain_text_to_rtf(source_text)) + + background = os.path.join(INSTALLER_DIR, PRODUCT_NAME + "-installer-bg.png") + if os.path.exists(background): + os.makedirs(target_dir, exist_ok=True) + shutil.copy2(background, os.path.join(target_dir, os.path.basename(background))) + + print("Prepared macOS installer documents in " + target_dir) + + +def build_win_docs(): + target_dir = os.path.join(PROJECT_DIR, "build-win", "installer-docs") + + for name in ("license", "readme-win", "readme-win-demo"): + write_text(os.path.join(target_dir, name + ".txt"), markdown_file_to_plain_text(name)) + + print("Prepared Windows installer documents in " + target_dir) + + +def main(): + parser = argparse.ArgumentParser(description="Prepare installer-compatible documents from Markdown sources.") + parser.add_argument("platform", choices=("mac", "win", "all"), help="Target installer platform.") + args = parser.parse_args() + + if args.platform in ("mac", "all"): + build_mac_docs() + + if args.platform in ("win", "all"): + build_win_docs() + + +if __name__ == "__main__": + main() diff --git a/TemplateProject/scripts/update_installer-win.py b/TemplateProject/scripts/update_installer-win.py index d81fd881..e1cd2eff 100644 --- a/TemplateProject/scripts/update_installer-win.py +++ b/TemplateProject/scripts/update_installer-win.py @@ -43,11 +43,11 @@ def main(): else: line="OutputBaseFilename=TemplateProject Installer\n" - if 'Source: "readme' in line: + if 'DestName: "readme.txt"' in line: if demo: - line='Source: "readme-win-demo.rtf"; DestDir: "{app}"; DestName: "readme.rtf"; Flags: isreadme\n' + line='Source: "..\\build-win\\installer-docs\\readme-win-demo.txt"; DestDir: "{app}"; DestName: "readme.txt"; Flags: isreadme\n' else: - line='Source: "readme-win.rtf"; DestDir: "{app}"; DestName: "readme.rtf"; Flags: isreadme\n' + line='Source: "..\\build-win\\installer-docs\\readme-win.txt"; DestDir: "{app}"; DestName: "readme.txt"; Flags: isreadme\n' if "WelcomeLabel1" in line: if demo: From 8a02126d49cafc765f860b3c1f78847bf83d7105 Mon Sep 17 00:00:00 2001 From: Oli Larkin Date: Sun, 17 May 2026 13:29:04 +0200 Subject: [PATCH 2/6] Fix release archive name outputs --- .github/workflows/release-native.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-native.yml b/.github/workflows/release-native.yml index f660f2ba..c2b7fe44 100644 --- a/.github/workflows/release-native.yml +++ b/.github/workflows/release-native.yml @@ -68,7 +68,7 @@ jobs: if: matrix.os == 'macOS-latest' run: | ARCHIVE_NAME=`python3 iPlug2/Scripts/get_archive_name.py ${{env.PROJECT_NAME}} mac full` - echo "name=archive_name::$ARCHIVE_NAME" >> $GITHUB_OUTPUT + echo "archive_name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT shell: bash - name: Build macOS @@ -116,7 +116,7 @@ jobs: if: matrix.os == 'windows-latest' run: | ARCHIVE_NAME=`python.exe iPlug2/Scripts/get_archive_name.py ${{env.PROJECT_NAME}} win full` - echo "name=archive_name::$ARCHIVE_NAME" >> $GITHUB_OUTPUT + echo "archive_name=$ARCHIVE_NAME" >> $GITHUB_OUTPUT shell: bash - name: Build Windows From c54676f87772d816359aee96d5232093787f3bd6 Mon Sep 17 00:00:00 2001 From: Oli Larkin Date: Sun, 17 May 2026 13:43:09 +0200 Subject: [PATCH 3/6] Fix Windows installer CLAP source --- TemplateProject/installer/TemplateProject.iss | 2 +- TemplateProject/scripts/makedist-win.bat | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/TemplateProject/installer/TemplateProject.iss b/TemplateProject/installer/TemplateProject.iss index 5e729741..099a6f12 100644 --- a/TemplateProject/installer/TemplateProject.iss +++ b/TemplateProject/installer/TemplateProject.iss @@ -63,7 +63,7 @@ Source: "..\build-win\TemplateProject.vst3\*.*"; Excludes: "\Contents\x86\*,*.pd Source: "..\build-win\TemplateProject.vst3\Desktop.ini"; DestDir: "{cf64}\VST3\TemplateProject.vst3\"; Check: Is64BitInstallMode; Components:vst3_64; Flags: overwritereadonly ignoreversion; Attribs: hidden system; Source: "..\build-win\TemplateProject.vst3\PlugIn.ico"; DestDir: "{cf64}\VST3\TemplateProject.vst3\"; Check: Is64BitInstallMode; Components:vst3_64; Flags: overwritereadonly ignoreversion; Attribs: hidden system; -Source: "..\build-win\TemplateProject.clap"; DestDir: {commoncf64}\CLAP\; Check: Is64BitInstallMode; Components:clap_64; Flags: ignoreversion; +Source: "..\build-win\TemplateProject_x64.clap"; DestDir: {commoncf64}\CLAP\; Check: Is64BitInstallMode; Components:clap_64; Flags: ignoreversion; ;Source: "..\build-win\aax\bin\TemplateProject.aaxplugin\*.*"; Excludes: "\Contents\x64\*,*.pdb,*.exp,*.lib,*.ilk,*.ico,*.ini"; DestDir: "{cf32}\Avid\Audio\Plug-Ins\TemplateProject.aaxplugin\"; Components:aax_32; Flags: ignoreversion recursesubdirs; ;Source: "..\build-win\aax\bin\TemplateProject.aaxplugin\Desktop.ini"; DestDir: "{cf32}\Avid\Audio\Plug-Ins\TemplateProject.aaxplugin\"; Components:aax_32; Flags: overwritereadonly ignoreversion; Attribs: hidden system; diff --git a/TemplateProject/scripts/makedist-win.bat b/TemplateProject/scripts/makedist-win.bat index fffbde31..02c1419d 100644 --- a/TemplateProject/scripts/makedist-win.bat +++ b/TemplateProject/scripts/makedist-win.bat @@ -110,6 +110,10 @@ echo Making Installer ... REM :64-Bit-is "%ProgramFiles(x86)%\Inno Setup 6\iscc" /Q ".\installer\TemplateProject.iss" + if %ERRORLEVEL% neq 0 ( + echo ERROR: installer build failed + exit /B 1 + ) REM goto END-is REM :END-is From 31655e7e7bc174f0d7b37ab2359b77b427eb63d6 Mon Sep 17 00:00:00 2001 From: Oli Larkin Date: Sun, 17 May 2026 13:56:28 +0200 Subject: [PATCH 4/6] Generate PDF manual from Markdown --- .github/workflows/build-mac.yml | 1 + .github/workflows/build-win.yml | 1 + TemplateProject/README.md | 2 +- TemplateProject/installer/TemplateProject.iss | 4 +- TemplateProject/scripts/makedist-mac.sh | 2 +- TemplateProject/scripts/makezip-win.py | 2 +- .../scripts/prepare_installer_docs.py | 174 ++++++++++++++++++ 7 files changed, 181 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-mac.yml b/.github/workflows/build-mac.yml index fd1d473e..3fe613b9 100644 --- a/.github/workflows/build-mac.yml +++ b/.github/workflows/build-mac.yml @@ -100,6 +100,7 @@ jobs: test -f build-mac/installer/resources/license.rtf test -f build-mac/installer/resources/readme-mac.rtf test -f build-mac/installer/resources/intro.rtf + test -f "build-mac/manual/${{matrix.project}} manual.pdf" test -f "manual/${{matrix.project}} manual.md" - name: Build diff --git a/.github/workflows/build-win.yml b/.github/workflows/build-win.yml index cc6cdfcd..ee7f2be1 100644 --- a/.github/workflows/build-win.yml +++ b/.github/workflows/build-win.yml @@ -88,6 +88,7 @@ jobs: if (!(Test-Path "build-win\installer-docs\license.txt")) { throw "Missing generated license.txt" } if (!(Test-Path "build-win\installer-docs\readme-win.txt")) { throw "Missing generated readme-win.txt" } if (!(Test-Path "build-win\installer-docs\readme-win-demo.txt")) { throw "Missing generated readme-win-demo.txt" } + if (!(Test-Path "build-win\manual\${{matrix.project}} manual.pdf")) { throw "Missing generated manual PDF" } if (!(Test-Path "manual\${{matrix.project}} manual.md")) { throw "Missing Markdown manual" } - name: Add msbuild to PATH diff --git a/TemplateProject/README.md b/TemplateProject/README.md index 93a2a3d9..e2230ae8 100644 --- a/TemplateProject/README.md +++ b/TemplateProject/README.md @@ -4,4 +4,4 @@ iPlug2 template project ## Distribution docs -Installer text lives in `installer/*.md` and the user manual lives in `manual/TemplateProject manual.md`. The packaging scripts generate installer-compatible RTF/TXT files from those Markdown sources during release builds. +Installer text lives in `installer/*.md` and the user manual lives in `manual/TemplateProject manual.md`. The packaging scripts generate installer-compatible RTF/TXT files and a PDF manual from those Markdown sources during release builds. diff --git a/TemplateProject/installer/TemplateProject.iss b/TemplateProject/installer/TemplateProject.iss index 099a6f12..91b8aca0 100644 --- a/TemplateProject/installer/TemplateProject.iss +++ b/TemplateProject/installer/TemplateProject.iss @@ -73,13 +73,13 @@ Source: "..\build-win\TemplateProject_x64.clap"; DestDir: {commoncf64}\CLAP\; Ch ;Source: "..\build-win\TemplateProject.aaxplugin\Desktop.ini"; DestDir: "{cf64}\Avid\Audio\Plug-Ins\TemplateProject.aaxplugin\"; Check: Is64BitInstallMode; Components:aax_64; Flags: overwritereadonly ignoreversion; Attribs: hidden system; ;Source: "..\build-win\TemplateProject.aaxplugin\PlugIn.ico"; DestDir: "{cf64}\Avid\Audio\Plug-Ins\TemplateProject.aaxplugin\"; Check: Is64BitInstallMode; Components:aax_64; Flags: overwritereadonly ignoreversion; Attribs: hidden system; -Source: "..\manual\TemplateProject manual.md"; DestDir: "{app}" +Source: "..\build-win\manual\TemplateProject manual.pdf"; DestDir: "{app}" Source: "changelog.txt"; DestDir: "{app}" Source: "..\build-win\installer-docs\readme-win.txt"; DestDir: "{app}"; DestName: "readme.txt"; Flags: isreadme [Icons] Name: "{group}\TemplateProject"; Filename: "{app}\TemplateProject_x64.exe" -Name: "{group}\User guide"; Filename: "{app}\TemplateProject manual.md" +Name: "{group}\User guide"; Filename: "{app}\TemplateProject manual.pdf" Name: "{group}\Changelog"; Filename: "{app}\changelog.txt" ;Name: "{group}\readme"; Filename: "{app}\readme.txt" Name: "{group}\Uninstall TemplateProject"; Filename: "{app}\unins000.exe" diff --git a/TemplateProject/scripts/makedist-mac.sh b/TemplateProject/scripts/makedist-mac.sh index ce412254..6318f3b4 100755 --- a/TemplateProject/scripts/makedist-mac.sh +++ b/TemplateProject/scripts/makedist-mac.sh @@ -295,7 +295,7 @@ if [ $BUILD_INSTALLER == 1 ]; then else cp installer/changelog.txt build-mac/installer/ cp installer/known-issues.txt build-mac/installer/ - cp "manual/$PLUGIN_NAME manual.md" build-mac/installer/ + cp "build-mac/manual/$PLUGIN_NAME manual.pdf" build-mac/installer/ hdiutil create build-mac/$ARCHIVE_NAME.dmg -format UDZO -srcfolder build-mac/installer/ -ov -anyowners -volname $PLUGIN_NAME fi diff --git a/TemplateProject/scripts/makezip-win.py b/TemplateProject/scripts/makezip-win.py index df1445b2..1957359d 100644 --- a/TemplateProject/scripts/makezip-win.py +++ b/TemplateProject/scripts/makezip-win.py @@ -70,7 +70,7 @@ def main(): projectpath + installer, projectpath + "\\installer\\changelog.txt", projectpath + "\\installer\\known-issues.txt", - projectpath + "\\manual\\TemplateProject manual.md" + projectpath + "\\build-win\\manual\\TemplateProject manual.pdf" ] for f in files: diff --git a/TemplateProject/scripts/prepare_installer_docs.py b/TemplateProject/scripts/prepare_installer_docs.py index e1d68316..fae5c8cc 100644 --- a/TemplateProject/scripts/prepare_installer_docs.py +++ b/TemplateProject/scripts/prepare_installer_docs.py @@ -4,6 +4,7 @@ import os import re import shutil +import textwrap PRODUCT_NAME = "TemplateProject" @@ -11,6 +12,7 @@ SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) PROJECT_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, os.pardir)) INSTALLER_DIR = os.path.join(PROJECT_DIR, "installer") +MANUAL_DIR = os.path.join(PROJECT_DIR, "manual") def read_text(path): @@ -50,6 +52,16 @@ def markdown_to_plain_text(markdown): return "\n".join(lines).strip() + "\n" +def markdown_inline_to_text(markdown): + text = re.sub(r"\[([^\]]+)\]\([^\)]+\)", r"\1", markdown) + text = re.sub(r"\*\*([^*]+)\*\*", r"\1", text) + text = re.sub(r"__([^_]+)__", r"\1", text) + text = re.sub(r"\*([^*]+)\*", r"\1", text) + text = re.sub(r"_([^_]+)_", r"\1", text) + text = re.sub(r"`([^`]+)`", r"\1", text) + return text.strip() + + def rtf_escape(text): escaped = [] @@ -95,8 +107,169 @@ def markdown_file_to_plain_text(name): return markdown_to_plain_text(read_text(os.path.join(INSTALLER_DIR, name + ".md"))) +def pdf_text(text): + text = text.encode("cp1252", "replace").decode("cp1252") + return text.replace("\\", "\\\\").replace("(", "\\(").replace(")", "\\)") + + +def write_pdf(path, page_streams): + os.makedirs(os.path.dirname(path), exist_ok=True) + + objects = [ + b"<< /Type /Catalog /Pages 2 0 R >>", + None, + b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>", + b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold >>", + ] + + page_refs = [] + for stream in page_streams: + stream_bytes = stream.encode("cp1252", "replace") + page_object_id = len(objects) + 1 + stream_object_id = len(objects) + 2 + page_refs.append(f"{page_object_id} 0 R") + objects.append( + ( + "<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] " + "/Resources << /Font << /F1 3 0 R /F2 4 0 R >> >> " + f"/Contents {stream_object_id} 0 R >>" + ).encode("ascii") + ) + objects.append( + b"<< /Length " + str(len(stream_bytes)).encode("ascii") + b" >>\nstream\n" + + stream_bytes + b"\nendstream" + ) + + objects[1] = ( + f"<< /Type /Pages /Kids [{' '.join(page_refs)}] /Count {len(page_refs)} >>" + ).encode("ascii") + + output = bytearray(b"%PDF-1.4\n%\xe2\xe3\xcf\xd3\n") + offsets = [0] + + for index, content in enumerate(objects, start=1): + offsets.append(len(output)) + output.extend(f"{index} 0 obj\n".encode("ascii")) + output.extend(content) + output.extend(b"\nendobj\n") + + xref_offset = len(output) + output.extend(f"xref\n0 {len(objects) + 1}\n".encode("ascii")) + output.extend(b"0000000000 65535 f \n") + for offset in offsets[1:]: + output.extend(f"{offset:010d} 00000 n \n".encode("ascii")) + output.extend( + ( + "trailer\n" + f"<< /Size {len(objects) + 1} /Root 1 0 R >>\n" + "startxref\n" + f"{xref_offset}\n" + "%%EOF\n" + ).encode("ascii") + ) + + with open(path, "wb") as output_file: + output_file.write(output) + + +def write_markdown_pdf(markdown_path, pdf_path): + markdown = read_text(markdown_path) + page_width = 595 + page_height = 842 + margin = 54 + page_streams = [] + stream_lines = [] + y = page_height - margin + + def new_page(): + nonlocal stream_lines, y + if stream_lines: + page_streams.append("\n".join(stream_lines)) + stream_lines = [] + y = page_height - margin + + def add_vertical_space(points): + nonlocal y + if y - points < margin: + new_page() + else: + y -= points + + def add_text(text, font="F1", size=11, leading=15, indent=0): + nonlocal y + max_chars = max(10, int((page_width - (2 * margin) - indent) / (size * 0.52))) + wrapped_lines = textwrap.wrap(text, width=max_chars) or [""] + + for line in wrapped_lines: + if y - leading < margin: + new_page() + x = margin + indent + stream_lines.append(f"BT /{font} {size} Tf {x} {y:.2f} Td ({pdf_text(line)}) Tj ET") + y -= leading + + paragraph_lines = [] + in_code_block = False + + def flush_paragraph(): + nonlocal paragraph_lines + if paragraph_lines: + add_text(markdown_inline_to_text(" ".join(paragraph_lines))) + add_vertical_space(6) + paragraph_lines = [] + + for raw_line in markdown.splitlines(): + stripped = raw_line.strip() + + if stripped.startswith("```"): + flush_paragraph() + in_code_block = not in_code_block + continue + + if in_code_block: + add_text(stripped, size=10, leading=13, indent=12) + continue + + if not stripped: + flush_paragraph() + continue + + heading = re.match(r"^(#{1,6})\s+(.+)$", stripped) + bullet = re.match(r"^[-*+]\s+(.+)$", stripped) + numbered = re.match(r"^(\d+\.)\s+(.+)$", stripped) + + if heading: + flush_paragraph() + level = len(heading.group(1)) + size = 22 if level == 1 else 16 if level == 2 else 13 + add_vertical_space(8 if y < page_height - margin else 0) + add_text(markdown_inline_to_text(heading.group(2)), font="F2", size=size, leading=size + 6) + add_vertical_space(6) + elif bullet: + flush_paragraph() + add_text("- " + markdown_inline_to_text(bullet.group(1)), indent=12) + elif numbered: + flush_paragraph() + add_text(numbered.group(1) + " " + markdown_inline_to_text(numbered.group(2)), indent=12) + else: + paragraph_lines.append(stripped) + + flush_paragraph() + if not stream_lines: + add_text(" ") + page_streams.append("\n".join(stream_lines)) + write_pdf(pdf_path, page_streams) + + +def build_manual_pdf(build_dir_name): + source_path = os.path.join(MANUAL_DIR, PRODUCT_NAME + " manual.md") + target_path = os.path.join(PROJECT_DIR, build_dir_name, "manual", PRODUCT_NAME + " manual.pdf") + write_markdown_pdf(source_path, target_path) + print("Prepared manual PDF at " + target_path) + + def build_mac_docs(): target_dir = os.path.join(PROJECT_DIR, "build-mac", "installer", "resources") + build_manual_pdf("build-mac") for name in ("license", "readme-mac", "intro"): source_text = markdown_file_to_plain_text(name) @@ -112,6 +285,7 @@ def build_mac_docs(): def build_win_docs(): target_dir = os.path.join(PROJECT_DIR, "build-win", "installer-docs") + build_manual_pdf("build-win") for name in ("license", "readme-win", "readme-win-demo"): write_text(os.path.join(target_dir, name + ".txt"), markdown_file_to_plain_text(name)) From 09e320bfd9107cda6e67db393641bce79bfc0a56 Mon Sep 17 00:00:00 2001 From: Oli Larkin Date: Sun, 17 May 2026 14:18:50 +0200 Subject: [PATCH 5/6] Use Pandoc for generated docs --- .github/workflows/build-mac.yml | 6 + .github/workflows/build-win.yml | 8 + .github/workflows/release-native.yml | 6 + TemplateProject/README.md | 2 +- TemplateProject/scripts/makeinstaller-mac.sh | 76 ++--- .../scripts/prepare_installer_docs.py | 283 +++--------------- .../scripts/update_installer-win.py | 19 +- 7 files changed, 118 insertions(+), 282 deletions(-) diff --git a/.github/workflows/build-mac.yml b/.github/workflows/build-mac.yml index 3fe613b9..563ada8b 100644 --- a/.github/workflows/build-mac.yml +++ b/.github/workflows/build-mac.yml @@ -91,6 +91,12 @@ jobs: cd iPlug2/Dependencies/IPlug ./download-clap-sdks.sh ${{env.CLAP_VER}} + - name: Install pandoc + uses: pandoc/actions/setup@v1 + + - name: Install typst + uses: typst-community/setup-typst@v5 + - name: Validate installer docs shell: bash run: | diff --git a/.github/workflows/build-win.yml b/.github/workflows/build-win.yml index ee7f2be1..4670b189 100644 --- a/.github/workflows/build-win.yml +++ b/.github/workflows/build-win.yml @@ -80,9 +80,17 @@ jobs: cd iPlug2/Dependencies/IPlug ./download-clap-sdks.sh ${{env.CLAP_VER}} + - name: Install pandoc + uses: pandoc/actions/setup@v1 + + - name: Install typst + uses: typst-community/setup-typst@v5 + - name: Validate installer docs shell: pwsh run: | + $ErrorActionPreference = 'Stop' + $PSNativeCommandUseErrorActionPreference = $true cd ${{matrix.project}} python scripts/prepare_installer_docs.py win if (!(Test-Path "build-win\installer-docs\license.txt")) { throw "Missing generated license.txt" } diff --git a/.github/workflows/release-native.yml b/.github/workflows/release-native.yml index c2b7fe44..b039a57d 100644 --- a/.github/workflows/release-native.yml +++ b/.github/workflows/release-native.yml @@ -51,6 +51,12 @@ jobs: with: submodules: recursive + - name: Install pandoc + uses: pandoc/actions/setup@v1 + + - name: Install typst + uses: typst-community/setup-typst@v5 + - name: Get VST3 SDK run: | cd iPlug2/Dependencies/IPlug diff --git a/TemplateProject/README.md b/TemplateProject/README.md index e2230ae8..56d42966 100644 --- a/TemplateProject/README.md +++ b/TemplateProject/README.md @@ -4,4 +4,4 @@ iPlug2 template project ## Distribution docs -Installer text lives in `installer/*.md` and the user manual lives in `manual/TemplateProject manual.md`. The packaging scripts generate installer-compatible RTF/TXT files and a PDF manual from those Markdown sources during release builds. +Installer text lives in `installer/*.md` and the user manual lives in `manual/TemplateProject manual.md`. The packaging scripts use Pandoc to generate installer-compatible RTF/TXT files and a PDF manual from those Markdown sources during release builds. diff --git a/TemplateProject/scripts/makeinstaller-mac.sh b/TemplateProject/scripts/makeinstaller-mac.sh index 6c5fc93a..86c68e3b 100755 --- a/TemplateProject/scripts/makeinstaller-mac.sh +++ b/TemplateProject/scripts/makeinstaller-mac.sh @@ -43,65 +43,65 @@ RSRCS="~/Music/${PRODUCT_NAME}/Resources" OUTPUT_BASE_FILENAME="${PRODUCT_NAME} Installer.pkg" TARGET_DIR="./build-mac/installer" -PKG_DIR=${TARGET_DIR}/pkgs -RESOURCES_DIR=${TARGET_DIR}/resources +PKG_DIR="${TARGET_DIR}/pkgs" +RESOURCES_DIR="${TARGET_DIR}/resources" -if [[ ! -d ${TARGET_DIR} ]]; then - mkdir ${TARGET_DIR} +if [[ ! -d "${TARGET_DIR}" ]]; then + mkdir "${TARGET_DIR}" fi -if [[ ! -d ${PKG_DIR} ]]; then - mkdir ${PKG_DIR} +if [[ ! -d "${PKG_DIR}" ]]; then + mkdir "${PKG_DIR}" fi python3 ./scripts/prepare_installer_docs.py mac || exit 1 build_flavor() { - TMPDIR=${TARGET_DIR}/tmp - flavor=$1 - flavorprod=$2 - ident=$3 - loc=$4 + TMPDIR="${TARGET_DIR}/tmp" + flavor="$1" + flavorprod="$2" + ident="$3" + loc="$4" echo --- BUILDING ${PRODUCT_NAME}_${flavor}.pkg --- - mkdir -p $TMPDIR - cp -R -L $PRODUCTS/$flavorprod $TMPDIR + mkdir -p "${TMPDIR}" + cp -R -L "${PRODUCTS}/${flavorprod}" "${TMPDIR}" - pkgbuild --root $TMPDIR --identifier $ident --version $VERSION --install-location $loc ${PKG_DIR}/${PRODUCT_NAME}_${flavor}.pkg #|| exit 1 + pkgbuild --root "${TMPDIR}" --identifier "${ident}" --version "${VERSION}" --install-location "${loc}" "${PKG_DIR}/${PRODUCT_NAME}_${flavor}.pkg" #|| exit 1 - rm -r $TMPDIR + rm -r "${TMPDIR}" } # try to build VST2 package -if [[ -d $PRODUCTS/$VST2 ]]; then - build_flavor "VST2" $VST2 "com.AcmeInc.vst2.pkg.${PRODUCT_NAME}" "/Library/Audio/Plug-Ins/VST" +if [[ -d "${PRODUCTS}/${VST2}" ]]; then + build_flavor "VST2" "${VST2}" "com.AcmeInc.vst2.pkg.${PRODUCT_NAME}" "/Library/Audio/Plug-Ins/VST" fi # # try to build VST3 package -if [[ -d $PRODUCTS/$VST3 ]]; then - build_flavor "VST3" $VST3 "com.AcmeInc.vst3.pkg.${PRODUCT_NAME}" "/Library/Audio/Plug-Ins/VST3" +if [[ -d "${PRODUCTS}/${VST3}" ]]; then + build_flavor "VST3" "${VST3}" "com.AcmeInc.vst3.pkg.${PRODUCT_NAME}" "/Library/Audio/Plug-Ins/VST3" fi # # try to build AU package -if [[ -d $PRODUCTS/$AU ]]; then - build_flavor "AU" $AU "com.AcmeInc.au.pkg.${PRODUCT_NAME}" "/Library/Audio/Plug-Ins/Components" +if [[ -d "${PRODUCTS}/${AU}" ]]; then + build_flavor "AU" "${AU}" "com.AcmeInc.au.pkg.${PRODUCT_NAME}" "/Library/Audio/Plug-Ins/Components" fi # # try to build AAX package -if [[ -d $PRODUCTS/$AAX ]]; then - build_flavor "AAX" $AAX "com.AcmeInc.aax.pkg.${PRODUCT_NAME}" ""/Library/Application Support/Avid/Audio/Plug-Ins"" +if [[ -d "${PRODUCTS}/${AAX}" ]]; then + build_flavor "AAX" "${AAX}" "com.AcmeInc.aax.pkg.${PRODUCT_NAME}" "/Library/Application Support/Avid/Audio/Plug-Ins" fi # try to build App package -if [[ -d $PRODUCTS/$APP ]]; then - build_flavor "APP" $APP "com.AcmeInc.app.pkg.${PRODUCT_NAME}" "/Applications" +if [[ -d "${PRODUCTS}/${APP}" ]]; then + build_flavor "APP" "${APP}" "com.AcmeInc.app.pkg.${PRODUCT_NAME}" "/Applications" fi # try to build CLAP package -if [[ -d $PRODUCTS/$CLAP ]]; then - build_flavor "CLAP" $CLAP "com.AcmeInc.clap.pkg.${PRODUCT_NAME}" "/Library/Audio/Plug-Ins/CLAP" +if [[ -d "${PRODUCTS}/${CLAP}" ]]; then + build_flavor "CLAP" "${CLAP}" "com.AcmeInc.clap.pkg.${PRODUCT_NAME}" "/Library/Audio/Plug-Ins/CLAP" fi # write build info to resources folder @@ -120,33 +120,33 @@ fi # create distribution.xml -if [[ -d $PRODUCTS/$VST2 ]]; then +if [[ -d "${PRODUCTS}/${VST2}" ]]; then VST2_PKG_REF="" VST2_CHOICE="" VST2_CHOICE_DEF="${PRODUCT_NAME}_VST2.pkg" fi -if [[ -d $PRODUCTS/$VST3 ]]; then +if [[ -d "${PRODUCTS}/${VST3}" ]]; then VST3_PKG_REF="" VST3_CHOICE="" VST3_CHOICE_DEF="${PRODUCT_NAME}_VST3.pkg" fi -if [[ -d $PRODUCTS/$AU ]]; then +if [[ -d "${PRODUCTS}/${AU}" ]]; then AU_PKG_REF="" AU_CHOICE="" AU_CHOICE_DEF="${PRODUCT_NAME}_AU.pkg" fi -if [[ -d $PRODUCTS/$AAX ]]; then +if [[ -d "${PRODUCTS}/${AAX}" ]]; then AAX_PKG_REF="" AAX_CHOICE="" AAX_CHOICE_DEF="${PRODUCT_NAME}_AAX.pkg" fi -if [[ -d $PRODUCTS/$APP ]]; then +if [[ -d "${PRODUCTS}/${APP}" ]]; then APP_PKG_REF="" APP_CHOICE="" APP_CHOICE_DEF="${PRODUCT_NAME}_APP.pkg" fi -if [[ -d $PRODUCTS/$CLAP ]]; then +if [[ -d "${PRODUCTS}/${CLAP}" ]]; then CLAP_PKG_REF="" CLAP_CHOICE="" CLAP_CHOICE_DEF="${PRODUCT_NAME}_CLAP.pkg" @@ -158,7 +158,7 @@ fi # RES_CHOICE_DEF="${PRODUCT_NAME}_RES.pkg" # fi -cat > ${TARGET_DIR}/distribution.xml << XMLEND +cat > "${TARGET_DIR}/distribution.xml" << XMLEND ${PRODUCT_NAME} ${VERSION} @@ -196,8 +196,8 @@ XMLEND # build installation bundle # --resources . -productbuild --distribution ${TARGET_DIR}/distribution.xml --resources ${RESOURCES_DIR} --package-path ${PKG_DIR} "${TARGET_DIR}/$OUTPUT_BASE_FILENAME" +productbuild --distribution "${TARGET_DIR}/distribution.xml" --resources "${RESOURCES_DIR}" --package-path "${PKG_DIR}" "${TARGET_DIR}/${OUTPUT_BASE_FILENAME}" -rm ${TARGET_DIR}/distribution.xml -rm -r $PKG_DIR -rm -r $RESOURCES_DIR +rm "${TARGET_DIR}/distribution.xml" +rm -r "${PKG_DIR}" +rm -r "${RESOURCES_DIR}" diff --git a/TemplateProject/scripts/prepare_installer_docs.py b/TemplateProject/scripts/prepare_installer_docs.py index fae5c8cc..29a2d084 100644 --- a/TemplateProject/scripts/prepare_installer_docs.py +++ b/TemplateProject/scripts/prepare_installer_docs.py @@ -2,17 +2,15 @@ import argparse import os -import re import shutil -import textwrap +import subprocess -PRODUCT_NAME = "TemplateProject" - SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) PROJECT_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, os.pardir)) INSTALLER_DIR = os.path.join(PROJECT_DIR, "installer") MANUAL_DIR = os.path.join(PROJECT_DIR, "manual") +PRODUCT_NAME = os.path.basename(PROJECT_DIR) def read_text(path): @@ -20,250 +18,54 @@ def read_text(path): return input_file.read() -def write_text(path, text): +def write_text(path, text, newline="\n"): os.makedirs(os.path.dirname(path), exist_ok=True) - with open(path, "w", encoding="utf-8", newline="\n") as output_file: + with open(path, "w", encoding="utf-8", newline=newline) as output_file: output_file.write(text) -def markdown_to_plain_text(markdown): - lines = [] - in_code_block = False - - for line in markdown.splitlines(): - stripped = line.strip() - - if stripped.startswith("```"): - in_code_block = not in_code_block - continue - - if not in_code_block: - line = re.sub(r"^\s{0,3}#{1,6}\s*", "", line) - line = re.sub(r"\[([^\]]+)\]\([^\)]+\)", r"\1", line) - line = re.sub(r"\*\*([^*]+)\*\*", r"\1", line) - line = re.sub(r"__([^_]+)__", r"\1", line) - line = re.sub(r"\*([^*]+)\*", r"\1", line) - line = re.sub(r"_([^_]+)_", r"\1", line) - line = re.sub(r"`([^`]+)`", r"\1", line) - line = re.sub(r"^\s*[-*+]\s+", "- ", line) - - lines.append(line.rstrip()) - - return "\n".join(lines).strip() + "\n" - - -def markdown_inline_to_text(markdown): - text = re.sub(r"\[([^\]]+)\]\([^\)]+\)", r"\1", markdown) - text = re.sub(r"\*\*([^*]+)\*\*", r"\1", text) - text = re.sub(r"__([^_]+)__", r"\1", text) - text = re.sub(r"\*([^*]+)\*", r"\1", text) - text = re.sub(r"_([^_]+)_", r"\1", text) - text = re.sub(r"`([^`]+)`", r"\1", text) - return text.strip() - - -def rtf_escape(text): - escaped = [] - - for char in text: - codepoint = ord(char) - - if char == "\\": - escaped.append(r"\\") - elif char == "{": - escaped.append(r"\{") - elif char == "}": - escaped.append(r"\}") - elif char == "\t": - escaped.append(r"\tab ") - elif codepoint <= 0x7f: - escaped.append(char) - else: - signed_codepoint = codepoint if codepoint < 32768 else codepoint - 65536 - escaped.append(r"\u" + str(signed_codepoint) + "?") - - return "".join(escaped) +def require_tool(name): + if shutil.which(name) is None: + raise RuntimeError(name + " is required to prepare installer documents") -def plain_text_to_rtf(text): - lines = [ - r"{\rtf1\ansi\deff0", - r"{\fonttbl{\f0 Helvetica;}}", - r"\fs24", - ] +def run_pandoc(args): + require_tool("pandoc") + subprocess.run(["pandoc"] + args, check=True) - for line in text.splitlines(): - if line: - lines.append(rtf_escape(line) + r"\par") - else: - lines.append(r"\par") - lines.append("}") +def pandoc_convert(source_path, target_path, output_format, extra_args=None): + os.makedirs(os.path.dirname(target_path), exist_ok=True) + run_pandoc([ + "--from", "markdown", + "--to", output_format, + "--output", target_path, + source_path, + ] + (extra_args or [])) - return "\n".join(lines) + "\n" - -def markdown_file_to_plain_text(name): - return markdown_to_plain_text(read_text(os.path.join(INSTALLER_DIR, name + ".md"))) - - -def pdf_text(text): - text = text.encode("cp1252", "replace").decode("cp1252") - return text.replace("\\", "\\\\").replace("(", "\\(").replace(")", "\\)") - - -def write_pdf(path, page_streams): - os.makedirs(os.path.dirname(path), exist_ok=True) - - objects = [ - b"<< /Type /Catalog /Pages 2 0 R >>", - None, - b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>", - b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold >>", - ] - - page_refs = [] - for stream in page_streams: - stream_bytes = stream.encode("cp1252", "replace") - page_object_id = len(objects) + 1 - stream_object_id = len(objects) + 2 - page_refs.append(f"{page_object_id} 0 R") - objects.append( - ( - "<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] " - "/Resources << /Font << /F1 3 0 R /F2 4 0 R >> >> " - f"/Contents {stream_object_id} 0 R >>" - ).encode("ascii") - ) - objects.append( - b"<< /Length " + str(len(stream_bytes)).encode("ascii") + b" >>\nstream\n" + - stream_bytes + b"\nendstream" - ) - - objects[1] = ( - f"<< /Type /Pages /Kids [{' '.join(page_refs)}] /Count {len(page_refs)} >>" - ).encode("ascii") - - output = bytearray(b"%PDF-1.4\n%\xe2\xe3\xcf\xd3\n") - offsets = [0] - - for index, content in enumerate(objects, start=1): - offsets.append(len(output)) - output.extend(f"{index} 0 obj\n".encode("ascii")) - output.extend(content) - output.extend(b"\nendobj\n") - - xref_offset = len(output) - output.extend(f"xref\n0 {len(objects) + 1}\n".encode("ascii")) - output.extend(b"0000000000 65535 f \n") - for offset in offsets[1:]: - output.extend(f"{offset:010d} 00000 n \n".encode("ascii")) - output.extend( - ( - "trailer\n" - f"<< /Size {len(objects) + 1} /Root 1 0 R >>\n" - "startxref\n" - f"{xref_offset}\n" - "%%EOF\n" - ).encode("ascii") - ) - - with open(path, "wb") as output_file: - output_file.write(output) - - -def write_markdown_pdf(markdown_path, pdf_path): - markdown = read_text(markdown_path) - page_width = 595 - page_height = 842 - margin = 54 - page_streams = [] - stream_lines = [] - y = page_height - margin - - def new_page(): - nonlocal stream_lines, y - if stream_lines: - page_streams.append("\n".join(stream_lines)) - stream_lines = [] - y = page_height - margin - - def add_vertical_space(points): - nonlocal y - if y - points < margin: - new_page() - else: - y -= points - - def add_text(text, font="F1", size=11, leading=15, indent=0): - nonlocal y - max_chars = max(10, int((page_width - (2 * margin) - indent) / (size * 0.52))) - wrapped_lines = textwrap.wrap(text, width=max_chars) or [""] - - for line in wrapped_lines: - if y - leading < margin: - new_page() - x = margin + indent - stream_lines.append(f"BT /{font} {size} Tf {x} {y:.2f} Td ({pdf_text(line)}) Tj ET") - y -= leading - - paragraph_lines = [] - in_code_block = False - - def flush_paragraph(): - nonlocal paragraph_lines - if paragraph_lines: - add_text(markdown_inline_to_text(" ".join(paragraph_lines))) - add_vertical_space(6) - paragraph_lines = [] - - for raw_line in markdown.splitlines(): - stripped = raw_line.strip() - - if stripped.startswith("```"): - flush_paragraph() - in_code_block = not in_code_block - continue - - if in_code_block: - add_text(stripped, size=10, leading=13, indent=12) - continue - - if not stripped: - flush_paragraph() - continue - - heading = re.match(r"^(#{1,6})\s+(.+)$", stripped) - bullet = re.match(r"^[-*+]\s+(.+)$", stripped) - numbered = re.match(r"^(\d+\.)\s+(.+)$", stripped) - - if heading: - flush_paragraph() - level = len(heading.group(1)) - size = 22 if level == 1 else 16 if level == 2 else 13 - add_vertical_space(8 if y < page_height - margin else 0) - add_text(markdown_inline_to_text(heading.group(2)), font="F2", size=size, leading=size + 6) - add_vertical_space(6) - elif bullet: - flush_paragraph() - add_text("- " + markdown_inline_to_text(bullet.group(1)), indent=12) - elif numbered: - flush_paragraph() - add_text(numbered.group(1) + " " + markdown_inline_to_text(numbered.group(2)), indent=12) - else: - paragraph_lines.append(stripped) - - flush_paragraph() - if not stream_lines: - add_text(" ") - page_streams.append("\n".join(stream_lines)) - write_pdf(pdf_path, page_streams) +def pandoc_plain_text(source_path, target_path, newline="\n"): + temp_path = target_path + ".tmp" + pandoc_convert(source_path, temp_path, "plain", ["--wrap=none"]) + try: + text = read_text(temp_path).strip() + "\n" + write_text(target_path, text, newline=newline) + finally: + if os.path.exists(temp_path): + os.remove(temp_path) def build_manual_pdf(build_dir_name): + require_tool("typst") source_path = os.path.join(MANUAL_DIR, PRODUCT_NAME + " manual.md") target_path = os.path.join(PROJECT_DIR, build_dir_name, "manual", PRODUCT_NAME + " manual.pdf") - write_markdown_pdf(source_path, target_path) + os.makedirs(os.path.dirname(target_path), exist_ok=True) + run_pandoc([ + "--from", "markdown", + "--output", target_path, + "--pdf-engine=typst", + source_path, + ]) print("Prepared manual PDF at " + target_path) @@ -272,12 +74,15 @@ def build_mac_docs(): build_manual_pdf("build-mac") for name in ("license", "readme-mac", "intro"): - source_text = markdown_file_to_plain_text(name) - write_text(os.path.join(target_dir, name + ".rtf"), plain_text_to_rtf(source_text)) + pandoc_convert( + os.path.join(INSTALLER_DIR, name + ".md"), + os.path.join(target_dir, name + ".rtf"), + "rtf", + ["--standalone"], + ) background = os.path.join(INSTALLER_DIR, PRODUCT_NAME + "-installer-bg.png") if os.path.exists(background): - os.makedirs(target_dir, exist_ok=True) shutil.copy2(background, os.path.join(target_dir, os.path.basename(background))) print("Prepared macOS installer documents in " + target_dir) @@ -288,7 +93,11 @@ def build_win_docs(): build_manual_pdf("build-win") for name in ("license", "readme-win", "readme-win-demo"): - write_text(os.path.join(target_dir, name + ".txt"), markdown_file_to_plain_text(name)) + pandoc_plain_text( + os.path.join(INSTALLER_DIR, name + ".md"), + os.path.join(target_dir, name + ".txt"), + newline="\r\n", + ) print("Prepared Windows installer documents in " + target_dir) diff --git a/TemplateProject/scripts/update_installer-win.py b/TemplateProject/scripts/update_installer-win.py index e1cd2eff..6bad1d19 100644 --- a/TemplateProject/scripts/update_installer-win.py +++ b/TemplateProject/scripts/update_installer-win.py @@ -2,7 +2,7 @@ # this script will update the version and text in the innosetup installer files, based on config.h and demo 1/0 -import plistlib, os, datetime, fileinput, glob, sys, string +import plistlib, os, datetime, fileinput, glob, sys, string, re scriptpath = os.path.dirname(os.path.realpath(__file__)) projectpath = os.path.abspath(os.path.join(scriptpath, os.pardir)) @@ -12,6 +12,16 @@ from parse_config import parse_config +README_SOURCE_RE = re.compile(r'^\s*Source:\s*"', re.IGNORECASE) +README_DEST_RE = re.compile(r'\bDestName:\s*"readme\.txt"', re.IGNORECASE) + +def is_readme_entry(line): + return README_SOURCE_RE.search(line) and README_DEST_RE.search(line) + +def readme_entry(demo): + readme = "readme-win-demo.txt" if demo else "readme-win.txt" + return f'Source: "..\\build-win\\installer-docs\\{readme}"; DestDir: "{{app}}"; DestName: "readme.txt"; Flags: isreadme\n' + def replacestrs(filename, s, r): files = glob.glob(filename) @@ -43,11 +53,8 @@ def main(): else: line="OutputBaseFilename=TemplateProject Installer\n" - if 'DestName: "readme.txt"' in line: - if demo: - line='Source: "..\\build-win\\installer-docs\\readme-win-demo.txt"; DestDir: "{app}"; DestName: "readme.txt"; Flags: isreadme\n' - else: - line='Source: "..\\build-win\\installer-docs\\readme-win.txt"; DestDir: "{app}"; DestName: "readme.txt"; Flags: isreadme\n' + if is_readme_entry(line): + line=readme_entry(demo) if "WelcomeLabel1" in line: if demo: From 7ad89477836b91092ee5c9ca4eb33a474ebce233 Mon Sep 17 00:00:00 2001 From: Oli Larkin Date: Sun, 17 May 2026 15:34:33 +0200 Subject: [PATCH 6/6] Clean up template installer readmes --- TemplateProject/installer/intro.md | 2 +- TemplateProject/installer/readme-win-demo.md | 4 +--- TemplateProject/installer/readme-win.md | 6 ++---- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/TemplateProject/installer/intro.md b/TemplateProject/installer/intro.md index 01dd2df1..cfe84313 100644 --- a/TemplateProject/installer/intro.md +++ b/TemplateProject/installer/intro.md @@ -2,6 +2,6 @@ Thanks for purchasing TemplateProject. -contact@acmeinc.com +support@acmeinc.com http://www.acmeinc.com diff --git a/TemplateProject/installer/readme-win-demo.md b/TemplateProject/installer/readme-win-demo.md index 93de5e79..707095cc 100644 --- a/TemplateProject/installer/readme-win-demo.md +++ b/TemplateProject/installer/readme-win-demo.md @@ -2,9 +2,7 @@ Thanks for installing TemplateProject Demo. -The Demo is limited ... - -AcmeInc +The demo version is ready to use, but may include feature limits or periodic reminders. If you experience any problems with TemplateProject, please contact us at: diff --git a/TemplateProject/installer/readme-win.md b/TemplateProject/installer/readme-win.md index 6ec6023b..70958421 100644 --- a/TemplateProject/installer/readme-win.md +++ b/TemplateProject/installer/readme-win.md @@ -2,10 +2,8 @@ Thanks for installing TemplateProject. -BLAH BLAH BLAH - -AcmeInc +The plugins and standalone application are ready to use. If you experience any problems with TemplateProject, please contact us at: -acmeinc@AcmeInc.com +support@acmeinc.com