Build your first plugin
This tutorial creates a compiled C++ panel with a button, an editable name and persistent panel state. It uses the public SDK and runs in the EpicTuner worker.
1. Prepare your tools
Extract the SDK into a new directory. Install CMake 3.24 or newer, Ninja, and a native C++20 compiler. Use an x64 compiler shell on Windows. MinGW and MSVC produce separate binaries; both use the same C ABI. Python is needed for the generator and package tools, not for running a plugin.
You also need an EpicTuner host build containing epictuner,
epictuner-plugin-host and epictuner-plugin-check. Windows executable names
end in .exe. The host ships separately from the SDK.
2. Generate the project
Run from the extracted SDK root:
python tools/new-plugin.py /absolute/my-plugin --id com.example.analysis --name "My analysis"
The generator writes a standalone CMake project, a development manifest and
plugin.cpp. It refuses to overwrite an existing directory. Choose a reverse
domain ID you control; the library and manifest must agree.
3. Compile
cmake -S /absolute/my-plugin -B /absolute/my-plugin/out -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/absolute/sdk
cmake --build /absolute/my-plugin/out
For MSVC, run in an initialized x64 developer shell and add
-DCMAKE_CXX_COMPILER=cl. For MinGW, select g++ or its absolute path.
Use a fresh output directory when changing compilers.
For MSYS2, use the UCRT64 or MINGW64 shell with its matching native CMake,
Ninja and compiler packages, for example mingw-w64-ucrt-x86_64-cmake,
mingw-w64-ucrt-x86_64-ninja, and mingw-w64-ucrt-x86_64-gcc. which cmake
should point into /ucrt64/bin (or /mingw64/bin), not /usr/bin.
Always specify -G Ninja. MSYS /usr/bin/cmake plus Unix Makefiles and native
mingw32-make mixes /c/ paths with Windows paths and does not form a supported
toolchain. The SDK CMake project is the recommended build path.
Alternatively set EPICTUNER_SDK_ROOT, change into the generated directory,
and run cmake --preset sdk followed by cmake --build --preset sdk.
The result is starter.dll on Windows or starter.so on Linux, beside
plugin.json. The CMake helper also places the manifest beside multi-config
output such as Release/starter.dll.
4. Check the worker
Run the checker from your host installation:
epictuner-plugin-check --development --manifest /absolute/my-plugin/out/plugin.json --ui-panel com.example.analysis.main
Expect READY and UI registration/open/focus/close passed. This verifies
loading and panel registration; the next step verifies actual rendering and input.
A missing native dependency is diagnosed before the panel can register.
5. Open it in EpicTuner
epictuner --plugin-development --plugin-manifest /absolute/my-plugin/out/plugin.json
Open Plugins → Examples → My analysis, then click Say hello. The status changes to “Hello from your compiled plugin!” Edit Name, close the panel and reopen it. The name is opt-in persistent state. The panel supports workspace and floating placement. Plugin diagnostics are available through Tools → Plugins.
Unsigned development manifests require these launch flags every time.
--safe-mode suppresses plugin startup, including development plugins.
Alternatively launch with only --plugin-development, open Tools > Plugins,
and choose Load development manifest…. This loads the plugin for this launch;
unsigned development registration is intentionally not persisted. Repeat the load
after restarting, or use the CLI manifest argument on each launch. The supported
CLI form is two arguments: --plugin-manifest /absolute/path/plugin.json.
How the code works
The complete, buildable source is
the panel template. It subclasses
et::sdk::Plugin, declares its identity/capabilities, and exports the
et_plugin_query entry point through ET_PLUGIN(Starter).
start negotiates Ui, builds a PanelBuilder, and defines it. The host creates
the real controls. Click events run on the worker dispatcher. The handler sends
an atomic Ui::patch using the event's revision and origin, and receives a
completion result. A returned ET_OK means the request was accepted for
processing; check the completion for the final outcome.
The template keeps its callback owner and completion options in the plugin object. The builder owns its temporary strings; service calls copy input data before returning. Never retain pointers into a borrowed event or reply.
Add your workflow
Use read services for telemetry and logs, jobs for background calculation, and calibration proposals to preview edits before an explicit Apply. Try the examples with their synthetic fixtures. Package your result using the publishing guide.