CMake/CPack does a pretty good job of making it relatively easy to create a basic Windows installer. Sometimes, however, it trips you up when you want to do something seemingly common. One such example is creating Start Menu shortcuts for an executable where you also want to pass it some command line arguments. Surprisingly, CMake/CPack doesn’t give you a simple or generic way to do this. It provides very basic functionality via the CPACK_PACKAGE_EXECUTABLES variable in the CPack module, but that’s just a simple mapping of executable to menu shortcut name with no opportunity to provide command line arguments.

For the NSIS packaging module (the focus of this article), CMake does, however, provide some hook points where you can inject native NSIS code. These are the CPACK_NSIS_CREATE_ICONS_EXTRA and CPACK_NSIS_DELETE_ICONS_EXTRA variables in the CPack module. Being forced to embed native NSIS code isn’t ideal, but NSIS is at least relatively well documented. The unfortunate thing is the inconsistent way CMake constructs its NSIS input file, the result of which is you end up writing code which initially looks like a mistake!

Without further fanfare, here’s the CMake code which creates a shortcut for an executable taking parameters (this is the installer part):

set(CPACK_NSIS_CREATE_ICONS_EXTRA
    "CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\SomeName.lnk' '$INSTDIR\\\\MyProg.exe' '-foo -bar'")

This creates a Start Menu item with the name SomeName which runs the executable MyProg.exe with command line arguments -foo and -bar. $SMPROGRAMS is the good old Program Files area (or its equivalent depending on what Windows platform you are using). The $STARTMENU_FOLDER part will be filled in with the program group the user selects at installation time (typically the name of the package). The $INSTDIR part is the directory into which the user elected to install the package. The relevant NSIS documentation for these things can be found here. The seemingly excessive repeated slashes are needed to escape them through the CMake/CPack/NSIS chain.

Now look at the corresponding CMake code that is needed to have the uninstaller delete that Start Menu shortcut for us:

set(CPACK_NSIS_DELETE_ICONS_EXTRA
    "Delete '$SMPROGRAMS\\\\$START_MENU\\\\SomeName.lnk'")

It seems logical until you look closely and notice that we used $STARTMENU_FOLDER for the installer, but we used $START_MENU for the uninstaller. This inconsistency is a direct result of how CMake constructs the NSIS input file. Internally, CMake configures the installer to save the Start Menu folder selected by the user as a Windows registry item. The uninstaller reads that value back from the registry, but puts it in a differently named variable to the installer. A bit of quick googling will reveal that this is not common knowledge and it is certainly not documented behaviour.

Armed with this little nugget, you can now go ahead and create all kinds of shortcuts, safe in the knowledge that you are being a good citizen and cleaning up after yourself.