January 15, 2016

CPack NSIS Windows installer

When we use CMake for build it is almost straightforward to extend its usage to create installer.
It provides several advantages.

  • CPack generate new project in Visual Studio dependent from all others and building it we create installer on one click
  • installer often depends on .h files (.api) or .lib files (api) or .dll (exec) which are changed in CMake by developers during building project. In all these cases installer assembles all needed component.
  • cross-platform solution
As usual we need do standard steps but also encounter several problems.

Standard steps:

  • install targets with COMPONENT parameter target belongs to
  • if you have msvc project
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
include(InstallRequiredSystemLibraries)
install(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS}
      DESTINATION bin/dll/Msvc
      COMPONENT Driver)
  • provide all needed dlls, i.e.
set(BoostDllDIR ${Boost_LIBRARY_DIR_RELEASE})
file(GLOB BoostDllList "${BoostDllDIR}/*.dll")
install(FILES ${BoostDllList}
       DESTINATION bin/dll/Boost
       COMPONENT Driver)
  • provide msi installers your app depends on, i.e.
install(PROGRAMS DependencyInstall/Bonjour64.msi
          DESTINATION DependencyInstall
 COMPONENT Driver)
  • provide exe installers your app depends on, i.e. (if your project is from msvc, do exactly this)
install(PROGRAMS DependencyInstall/vcredist_x64.exe
          DESTINATION DependencyInstall
          COMPONENT Driver)
  • copy whole directories
set(DATA_FILE_DIR D:/data)
install(DIRECTORY ${DATA_FILE_DIR}/
       DESTINATION data
       COMPONENT Driver)
  • set standard CPack variables
CPACK_PACKAGE_NAME
CPACK_PACKAGE_VENDOR 
CPACK_PACKAGE_DESCRIPTION_SUMMARY 
CPACK_PACKAGE_VERSION_MAJOR 
CPACK_PACKAGE_VERSION_MINOR 
CPACK_PACKAGE_VERSION_PATCH 
CPACK_PACKAGE_VERSION 
CPACK_PACKAGE_INSTALL_DIRECTORY
CPACK_COMPONENTS_ALL
CPACK_COMPONENT_???_DISPLAY_NAME
CPACK_COMPONENT_???_DESCRIPTION
CPACK_COMPONENT_???_DEPENDS
CPACK_COMPONENT_???_GROUP
CPACK_COMPONENT_GROUP_???_DISPLAY_NAME
CPACK_COMPONENT_GROUP_???_DESCRIPTION
CPACK_RESOURCE_FILE_LICENSE
CPACK_PACKAGE_ICON #use \\\\ where / or \ should be
CPACK_NSIS_MUI_ICON
CPACK_NSIS_MUI_UNIICON
CPACK_NSIS_MENU_LINKS

Nonstandard steps:


Not all steps you could wish are possible in CPack, but you can directly inject NSIS code. There are two lists of commands:
CPACK_NSIS_EXTRA_INSTALL_COMMANDS used in installation
CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS used in uninstallation
Main problem is how to escape strings to be parsed right. Follow the examples.
  • execute 3rdParty .exe installers
list(APPEND CPACK_NSIS_EXTRA_INSTALL_COMMANDS " 
     ExecWait '$INSTDIR\\\\DependencyInstall\\\\vcredist_x64.exe /quiet /norestart'
     ")
  • execute 3rdParty .msi installers. There is problem (at least on my machine) to run msi installer if installation dir is protected, like Program Files. Workaround is to run it from unprotected folder like AppData/Local
list(APPEND CPACK_NSIS_EXTRA_INSTALL_COMMANDS "
CopyFiles '$INSTDIR\\\\DependencyInstall\\\\Bonjour64.msi' '$LOCALAPPDATA\\\\MyProgramName\\\\Bonjour64.msi'
")
list(APPEND CPACK_NSIS_EXTRA_INSTALL_COMMANDS "
         ExecWait 'msiexec /quiet /qn /norestart /i $LOCALAPPDATA\\\\MyProgramName\\\\Bonjour64.msi'
         ")
  • set entries in Windows Registry (why? check my another post  about dlls and exe) and after uninstallation remove them (we filled variable ${WindowsRegistryPath} in CMake with proper string)
set(DllInstallationPrefix "$INSTDIR/bin/dll")
set(WindowsRegistryPath "")
set(WindowsRegistryPath "${WindowsRegistryPath}${DllInstallationPrefix}/Msvc;")
set(WindowsRegistryPath "${WindowsRegistryPath}${DllInstallationPrefix}/Boost;")
list(APPEND CPACK_NSIS_EXTRA_INSTALL_COMMANDS "
             WriteRegStr HKLM 'SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\App Paths\\\\MyProgram.exe' '' '$INSTDIR/bin/MyProgramExe.exe'
             WriteRegStr HKLM 'SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\App Paths\\\\MyProgram.exe' 'Path' '${WindowsRegistryPath}'")
list(APPEND CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "
             DeleteRegKey HKLM 'SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\App Paths\\\\MyProgram.exe'")

No comments:

Post a Comment