Versioning

This section covers both techniques for versioning your own plug-ins, and techniques for handling multiple DDImage versions.

Plug-in Versioning

The NDK does not explicitly provide plug-in versioning functionality, so to work around this there are a number of common approaches for allowing multiple versions of plug-ins whilst maintaining compatibility. This section explains some of these approaches. It is not necessary reading for those just getting started with the NDK, however it helps to have an awareness of the issues associated. Note that some of the terminology used here may be unfamiliar, so the section may need revisiting once you’ve covered later topics.

Ensuring Compatibility & Obsoleting

NUKE stores a user’s project in a plain text file known as a NUKE script with a .nk extension. The script contains a serialised version of the directed acyclic graph which represents the node tree, along with user interface settings on these nodes which differ from their default values. It does not store values which are left at their default value to minimise the size of the resultant scripts and keep load times fast. Values are stashed against their corresponding knob name, and the node themselves according to the node’s name.

One common technique is to keep the node name the same for the new version (whilst stashing the version in some kind of user interface element such as the tooltip or a text knob). The node has to ensure that its underlying engine continues to output the same output for the same values, excepting results which are obviously a bug (otherwise when the client updates they’ll find their existing scripts suddenly start rendering differently, without warning them). Optionally, if the results have changed, the underlying algorithm can check the version stored in the script (by putting the version number in a disabled or hidden knob’s value with ALWAYS_SAVE set on its flags - see the Knob Flags section for more information) and use that to decide whether to render the old result or the new. On top of that, if the first version didn’t store it’s version in the script, then the new version can do and can assume that no version found in the script means the previous version. Not very pleasant, but retaining script compatibility comes at a price.

If a user interface element changes name, is retired, or changes behaviour under this scheme, then the new version needs to make judicious use of the Obsolete_knob knob type, which allow a value found in a script to by run through NUKE’s internal expression/scripting language to convert it to a new value. For example, if the control has been simply retired then the obsolete would ignore the value (but would still need to be present otherwise NUKE will report an error about missing knobs and unknown values). Another example - lets say the control has changed name; the obsolete will simply grab the value and set the new knob to be this value. Final example - lets say that the control has changed polarity; in this case the obsolete needs to set the new knob to be the inserse of the saved value.

For an example of a node employing the obsoleting approach, see the Noise.cpp example.

Breaking Compatibility & Appending Version Name

The opposite end of the scale is to explicitly break compatibility with new versions of your plug-ins, so that old scripts will not load using the new versions, and thus not render with different results. To do this, the node version is appended to its name.

A prime example of this are the merge nodes encorporated into NUKE. You may still be labouring under the belief that there is only one of these, however, try doing an ‘update’ in the all plug-ins menu or tab selector, and then looking for merge. You’ll find that, excepting the MergeExpression, MergeGeo and MergeMat nodes, there are two main entries - one called Merge and one called Merge2. In fact, the Merge menu entry on the merge menu actually creates the node listed as Merge2 (which is its class, available by pressing ‘i’ when the node is selected). The all plug-ins Merge entry creates a plug-in of class Merge, which is colloquially known as “merge1.” To further confuse matters, the tab selector lists two entries for Merge, and one for Merge2. The Merge entries are differentiated by the menu they appear under listed in square brackets after their name, ie:

  • Merge [Merge]
  • Merge [M]
  • Merge2

The first is the menu entry seen in the Merge menu, the second is the merge entry seen in the All Plug-ins->M menu, and the third is the Merge2 entry seen in the All Plug-ins->M menu. Selecting the first or the last will create a node of class Merge2, using the second will create a node of class Merge.

This touches on an important point - that a node’s menu entry (as specified by the associated menu.py) does not necessarily have the same name as the underlying node’s class. Equally a node can specify a different node name, for use when building the default name on the DAG, to its class.

If you create the two types of merge node and compare them you’ll see that the parameter panels are significantly different both in layout, controls and merge operations. Merge2 was designed as a non-backwards compatible replacement for Merge. What is important is that both versions are still shipped with NUKE, simply with the old version not put on the menus. This allows old scripts originally created using the Merge node to open using “merge1” and new scripts to open using Merge2.

There are a number of other examples of this inside of NUKE. Truelight, for one, currently ships with Truelight3. Interestingly, Truelight and Truelight2 are not shipped, due to enough time having passed since their superceeding and lack of availability of 64-bit compiles of the underlying libraries. Rather than appending a number you can alternatively change name in some other manner. This is not generally recommended, since it can be difficult to keep track of changes in the long run, but if the differences are large enough it may make little impact on the user. For example the ColorCorrect class supercedes the CCorect class node. A final example of interest is the Tracker node, for which the current version is Tracker3. NUKE also bundles a gizmo of name Tracker. This provides backwards compatibility by providing appropriately named knobs by which very old scripts with a tracker in can be loaded. Since this old version of tracker did not have inbuilt functionality for stabilising or matchmoving the image, all that is required is the param panel to allow expression links to still work correctly.

External bundles may opt to be even more explicit in their compatibility breakage. Ocula appends both the major and minor to the node class in the form <node name>_<major version>_<minor version>.

In some circumstances a user may well be able to update their scripts manually to allow the old version stored to load with the new binary. To do this they must open the script in a text editor and do a find and replace to convert the old class name to the new. By doing this they explicitly have to understand the risks that their script may well render differently.

DDImage Versioning

The DDImage exposed API will change, and it will break your existing binary plug-in compiles. We try to ensure these changes only happen when NUKE itself changes major or minor version, but not when it changes v number (ie 6.1v3 to 6.2v1 will require recompilation of related plug-ins, however 6.2v1 to 6.2v2 should not).

To allow you to cope with these changes we define a number of preprocessor macros in a header called ddImageVersionNumbers.h. Check the header for a full list of these, however the most commonly employed is the kDDImageVersionInteger. This can be tested by preprocessor hash ifs to allow multiple NDK API versions to be built from the same codebase. For example:

#if kDDImageVersionInteger >= 62100
<do NDK API call in NUKE 6.2v1 fashion>
#elif kDDImageVersionInteger < 60100
# error Pre NUKE 6.0 there was no equivalent call. Not supported.
#else
<do NDK API call in pre NUKE 6.2v1, but post NUKE 6.0v1 fashion>
#endif

In this case a new API call we want to use was introduced in Nuke6.0v1, and its function signature amended with the release of Nuke6.2v1. We use the preprocessor to pick the appropriate path to compile or to error out if there’s no equivalent call available.

Note

For legacy reasons there is also a ddImageVersion.h header file. This is obsolete and should be ignored. You may also see references in examples to DD_IMAGE_VERSION_MAJOR and DD_IMAGE_VERSION_MINOR. These symbols were defined in this obsolete file and in recent versions of NUKE have no impact.

Table Of Contents

Previous topic

Building & Installing Plug-ins

Next topic

2D