ENH: Add patch for feature request #6847 - CPack components for NSIS and PackageMaker installers. Thanks to Doug Gregor for all the hard work involved with implementing this patch! Also added new test CPackComponents that is conditionally executed only when NSIS or PackageMaker installer builders are available.

This commit is contained in:
David Cole 2008-06-17 11:39:26 -04:00
parent 64498a1287
commit 1105a86c52
18 changed files with 1723 additions and 235 deletions

View File

@ -32,6 +32,6 @@
<key>IFPkgFormatVersion</key>
<real>0.10000000149011612</real>
<key>CFBundleIdentifier</key>
<string>com.@CPACK_PACKAGE_VENDOR@.@CPACK_PACKAGE_NAME@.@CPACK_PACKAGE_VERSION@</string>
<string>com.@CPACK_PACKAGE_VENDOR@.@CPACK_PACKAGE_NAME@.@CPACK_PACKAGE_VERSION@@CPACK_MODULE_VERSION_SUFFIX@</string>
</dict>
</plist>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<installer-gui-script minSpecVersion="1.0">
<title>@CPACK_PACKAGE_NAME@</title>
<options allow-external-scripts="no" customize="allow" rootVolumeOnly="false"></options>
@CPACK_PACKAGEMAKER_CHOICES@
</installer-gui-script>

View File

@ -36,58 +36,113 @@
;Set compression
SetCompressor @CPACK_NSIS_COMPRESSOR@
!include Sections.nsh
;--- Component support macros: ---
; The code for the add/remove functionality is from:
; http://nsis.sourceforge.net/Add/Remove_Functionality
; It has been modified slightly and extended to provide
; inter-component dependencies.
Var AR_SecFlags
Var AR_RegFlags
@CPACK_NSIS_SECTION_SELECTED_VARS@
;--------------------------------
; determine admin versus local install
; Is install for "AllUsers" or "JustMe"?
; Default to "JustMe" - set to "AllUsers" if admin or on Win9x
; This function is used for the very first "custom page" of the installer.
; This custom page does not show up visibly, but it executes prior to the
; first visible page and sets up $INSTDIR properly...
; Choose different default installation folder based on SV_ALLUSERS...
; "Program Files" for AllUsers, "My Documents" for JustMe...
; Loads the "selected" flag for the section named SecName into the
; variable VarName.
!macro LoadSectionSelectedIntoVar SecName VarName
SectionGetFlags ${${SecName}} $${VarName}
IntOp $${VarName} $${VarName} & ${SF_SELECTED} ;Turn off all other bits
!macroend
Function .onInit
StrCpy $SV_ALLUSERS "JustMe"
StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
; Loads the value of a variable... can we get around this?
!macro LoadVar VarName
IntOp $R0 0 + $${VarName}
!macroend
!macro InitSection SecName
; This macro reads component installed flag from the registry and
;changes checked state of the section on the components page.
;Input: section index constant name specified in Section command.
ClearErrors
UserInfo::GetName
IfErrors noLM
Pop $0
UserInfo::GetAccountType
Pop $1
StrCmp $1 "Admin" 0 +3
SetShellVarContext all
;MessageBox MB_OK 'User "$0" is in the Admin group'
StrCpy $SV_ALLUSERS "AllUsers"
Goto done
StrCmp $1 "Power" 0 +3
SetShellVarContext all
;MessageBox MB_OK 'User "$0" is in the Power Users group'
StrCpy $SV_ALLUSERS "AllUsers"
Goto done
noLM:
StrCpy $SV_ALLUSERS "AllUsers"
;Get installation folder from registry if available
;Reading component status from registry
ReadRegDWORD $AR_RegFlags HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_DIRECTORY@\Components\${SecName}" "Installed"
IfErrors "default_${SecName}"
;Status will stay default if registry value not found
;(component was never installed)
IntOp $AR_RegFlags $AR_RegFlags & ${SF_SELECTED} ;Turn off all other bits
SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading default section flags
IntOp $AR_SecFlags $AR_SecFlags & 0xFFFE ;Turn lowest (enabled) bit off
IntOp $AR_SecFlags $AR_RegFlags | $AR_SecFlags ;Change lowest bit
done:
StrCmp $SV_ALLUSERS "AllUsers" 0 +2
StrCpy $INSTDIR "$PROGRAMFILES\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
;Writing modified flags
SectionSetFlags ${${SecName}} $AR_SecFlags
"default_${SecName}:"
!insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected
!macroend
!macro FinishSection SecName
; This macro reads section flag set by user and removes the section
;if it is not selected.
;Then it writes component installed flag to registry
;Input: section index constant name specified in Section command.
SectionGetFlags ${${SecName}} $AR_SecFlags ;Reading section flags
;Checking lowest bit:
IntOp $AR_SecFlags $AR_SecFlags & ${SF_SELECTED}
IntCmp $AR_SecFlags 1 "leave_${SecName}"
;Section is not selected:
;Calling Section uninstall macro and writing zero installed flag
!insertmacro "Remove_${${SecName}}"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_DIRECTORY@\Components\${SecName}" \
"Installed" 0
Goto "exit_${SecName}"
"leave_${SecName}:"
;Section is selected:
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\@CPACK_PACKAGE_INSTALL_DIRECTORY@\Components\${SecName}" \
"Installed" 1
"exit_${SecName}:"
!macroend
!macro RemoveSection SecName
; This macro is used to call section's Remove_... macro
;from the uninstaller.
;Input: section index constant name specified in Section command.
!insertmacro "Remove_${${SecName}}"
!macroend
StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage
!insertmacro MUI_INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini"
noOptionsPage:
FunctionEnd
; Determine whether the selection of SecName changed
!macro MaybeSelectionChanged SecName
!insertmacro LoadVar ${SecName}_selected
SectionGetFlags ${${SecName}} $R1
IntOp $R1 $R1 & ${SF_SELECTED} ;Turn off all other bits
; See if the status has changed:
IntCmp $R0 $R1 "${SecName}_unchanged"
!insertmacro LoadSectionSelectedIntoVar ${SecName} ${SecName}_selected
IntCmp $R1 ${SF_SELECTED} "${SecName}_was_selected"
!insertmacro "Deselect_required_by_${SecName}"
goto "${SecName}_unchanged"
"${SecName}_was_selected:"
!insertmacro "Select_${SecName}_depends"
"${SecName}_unchanged:"
!macroend
;--- End of Add/Remove macros ---
;--------------------------------
;Interface Settings
!define MUI_HEADERIMAGE
!define MUI_ABORTWARNING
;--------------------------------
; path functions
@ -289,7 +344,6 @@ Function un.RemoveFromPath
Pop $0
FunctionEnd
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Uninstall sutff
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -406,10 +460,19 @@ Function ConditionalAddToRegisty
ConditionalAddToRegisty_EmptyString:
FunctionEnd
;--------------------------------
; Installation types
@CPACK_NSIS_INSTALLATION_TYPES@
;--------------------------------
; Component sections
@CPACK_NSIS_COMPONENT_SECTIONS@
;--------------------------------
; Define some macro setting for the gui
@CPACK_NSIS_INSTALLER_MUI_ICON_CODE@
@CPACK_NSIS_INSTALLER_ICON_CODE@
@CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC@
;--------------------------------
;Pages
@ -425,6 +488,8 @@ FunctionEnd
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
@CPACK_NSIS_PAGE_COMPONENTS@
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_PAGE_FINISH
@ -446,16 +511,14 @@ FunctionEnd
ReserveFile "NSIS.InstallOptions.ini"
!insertmacro MUI_RESERVEFILE_INSTALLOPTIONS
;--------------------------------
;Installer Sections
Section "Installer Section" InstSection
Section "-Core installation"
;Use the entire tree produced by the INSTALL target. Keep the
;list of directories here in sync with the RMDir commands below.
SetOutPath "$INSTDIR"
File /r "${INST_DIR}\*.*"
@CPACK_NSIS_FULL_INSTALL@
;Store installation folder
WriteRegStr SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@" "" $INSTDIR
@ -474,7 +537,8 @@ Section "Installer Section" InstSection
Push "UninstallString"
Push "$INSTDIR\Uninstall.exe"
Call ConditionalAddToRegisty
Push "ModifyPath"
; Optional registration
Push "DisplayIcon"
Push "$INSTDIR\@CPACK_NSIS_INSTALLED_ICON_NAME@"
@ -520,7 +584,7 @@ Section "Installer Section" InstSection
SectionEnd
Section "Add to path"
Section "-Add to path"
Push $INSTDIR\bin
;Read a value from an InstallOptions INI file
!insertmacro MUI_INSTALLOPTIONS_READ $DO_NOT_ADD_TO_PATH "NSIS.InstallOptions.ini" "Field 2" "State"
@ -532,7 +596,6 @@ Section "Add to path"
doNotAddToPath:
SectionEnd
;--------------------------------
; Create custom pages
Function InstallOptionsPage
@ -564,6 +627,26 @@ Function un.onInit
;Get installation folder from registry if available
done:
FunctionEnd
;--- Add/Remove callback functions: ---
!macro SectionList MacroName
;This macro used to perform operation on multiple sections.
;List all of your components in following manner here.
@CPACK_NSIS_COMPONENT_SECTION_LIST@
!macroend
Section -FinishComponents
;Removes unselected components and writes component status to registry
!insertmacro SectionList "FinishSection"
SectionEnd
;--- End of Add/Remove callback functions ---
;--------------------------------
; Component dependencies
Function .onSelChange
!insertmacro SectionList MaybeSelectionChanged
FunctionEnd
;--------------------------------
@ -601,6 +684,9 @@ Section "Uninstall"
; Remove the registry entries.
DeleteRegKey SHCTX "Software\@CPACK_PACKAGE_VENDOR@\@CPACK_PACKAGE_INSTALL_REGISTRY_KEY@"
; Removes all optional components
!insertmacro SectionList "RemoveSection"
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
@ -647,4 +733,50 @@ Section "Uninstall"
doNotRemoveFromPath:
SectionEnd
;--------------------------------
; determine admin versus local install
; Is install for "AllUsers" or "JustMe"?
; Default to "JustMe" - set to "AllUsers" if admin or on Win9x
; This function is used for the very first "custom page" of the installer.
; This custom page does not show up visibly, but it executes prior to the
; first visible page and sets up $INSTDIR properly...
; Choose different default installation folder based on SV_ALLUSERS...
; "Program Files" for AllUsers, "My Documents" for JustMe...
Function .onInit
; Reads components status for registry
!insertmacro SectionList "InitSection"
StrCpy $SV_ALLUSERS "JustMe"
StrCpy $INSTDIR "$DOCUMENTS\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
ClearErrors
UserInfo::GetName
IfErrors noLM
Pop $0
UserInfo::GetAccountType
Pop $1
StrCmp $1 "Admin" 0 +3
SetShellVarContext all
;MessageBox MB_OK 'User "$0" is in the Admin group'
StrCpy $SV_ALLUSERS "AllUsers"
Goto done
StrCmp $1 "Power" 0 +3
SetShellVarContext all
;MessageBox MB_OK 'User "$0" is in the Power Users group'
StrCpy $SV_ALLUSERS "AllUsers"
Goto done
noLM:
StrCpy $SV_ALLUSERS "AllUsers"
;Get installation folder from registry if available
done:
StrCmp $SV_ALLUSERS "AllUsers" 0 +2
StrCpy $INSTDIR "$PROGRAMFILES\@CPACK_PACKAGE_INSTALL_DIRECTORY@"
StrCmp "@CPACK_NSIS_MODIFY_PATH@" "ON" 0 noOptionsPage
!insertmacro MUI_INSTALLOPTIONS_EXTRACT "NSIS.InstallOptions.ini"
noOptionsPage:
FunctionEnd

View File

@ -0,0 +1,117 @@
/*=========================================================================
Program: CMake - Cross-Platform Makefile Generator
Module: $RCSfile$
Language: C++
Date: $Date$
Version: $Revision$
Copyright (c) 2002 Kitware, Inc. All rights reserved.
See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#ifndef cmCPackComponentGroup_h
#define cmCPackComponentGroup_h
#include <map>
#include <string>
#include <vector>
class cmCPackComponentGroup;
/** \class cmCPackInstallationType
* \brief A certain type of installation, which encompasses a
* set of components.
*/
class cmCPackInstallationType
{
public:
/// The name of the installation type (used to reference this
/// installation type).
std::string Name;
/// The name of the installation type as displayed to the user.
std::string DisplayName;
/// The index number of the installation type. This is an arbitrary
/// numbering from 1 to the number of installation types.
unsigned Index;
};
/** \class cmCPackComponent
* \brief A single component to be installed by CPack.
*/
class cmCPackComponent
{
public:
cmCPackComponent() : Group(0) { }
/// The name of the component (used to reference the component).
std::string Name;
/// The name of the component as displayed to the user.
std::string DisplayName;
/// The component group that contains this component (if any).
cmCPackComponentGroup *Group;
/// Whether this component group must always be installed.
bool IsRequired : 1;
/// Whether this component group is hidden. A hidden component group
/// is always installed. However, it may still be shown to the user.
bool IsHidden : 1;
/// Whether this component defaults to "disabled".
bool IsDisabledByDefault : 1;
/// A description of this component.
std::string Description;
/// The installation types that this component is a part of.
std::vector<cmCPackInstallationType *> InstallationTypes;
/// The components that this component depends on.
std::vector<cmCPackComponent *> Dependencies;
/// The components that depend on this component.
std::vector<cmCPackComponent *> ReverseDependencies;
/// The list of installed files that are part of this component.
std::vector<std::string> Files;
/// The list of installed directories that are part of this component.
std::vector<std::string> Directories;
};
/** \class cmCPackComponentGroup
* \brief A component group to be installed by CPack.
*/
class cmCPackComponentGroup
{
public:
/// The name of the group (used to reference the group).
std::string Name;
/// The name of the component as displayed to the user.
std::string DisplayName;
/// The description of this component group.
std::string Description;
/// Whether the name of the component will be shown in bold.
bool IsBold : 1;
/// Whether the section should be expanded by default
bool IsExpandedByDefault : 1;
/// The components within this group.
std::vector<cmCPackComponent*> Components;
};
#endif

View File

@ -23,6 +23,7 @@
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
#include "cmGeneratedFileStream.h"
#include "cmCPackComponentGroup.h"
#include <cmsys/SystemTools.hxx>
#include <cmsys/Glob.hxx>
@ -101,13 +102,6 @@ int cmCPackGenerator::PrepareNames()
std::string destFile = pdir;
destFile += "/" + outName;
std::string outFile = topDirectory + "/" + outName;
bool setDestDir = cmSystemTools::IsOn(this->GetOption("CPACK_SET_DESTDIR"));
std::string installPrefix = tempDirectory;
if (!setDestDir)
{
installPrefix += this->GetPackagingInstallPrefix();
}
this->SetOptionIfNotSet("CPACK_TOPLEVEL_DIRECTORY", topDirectory.c_str());
this->SetOptionIfNotSet("CPACK_TEMPORARY_DIRECTORY", tempDirectory.c_str());
this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_NAME", outName.c_str());
@ -118,7 +112,7 @@ int cmCPackGenerator::PrepareNames()
this->SetOptionIfNotSet("CPACK_NATIVE_INSTALL_DIRECTORY",
cmsys::SystemTools::ConvertToOutputPath(this->GetInstallPath()).c_str());
this->SetOptionIfNotSet("CPACK_TEMPORARY_INSTALL_DIRECTORY",
installPrefix.c_str());
tempDirectory.c_str());
cmCPackLogger(cmCPackLog::LOG_DEBUG,
"Look for: CPACK_PACKAGE_DESCRIPTION_FILE" << std::endl);
@ -172,11 +166,19 @@ int cmCPackGenerator::InstallProject()
{
cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Install projects" << std::endl);
this->CleanTemporaryDirectory();
std::string tempInstallDirectoryWithPostfix
std::string bareTempInstallDirectory
= this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY");
const char* tempInstallDirectory = tempInstallDirectoryWithPostfix.c_str();
std::string tempInstallDirectoryStr = bareTempInstallDirectory;
bool setDestDir = cmSystemTools::IsOn(this->GetOption("CPACK_SET_DESTDIR"));
if (!setDestDir)
{
tempInstallDirectoryStr += this->GetPackagingInstallPrefix();
}
const char* tempInstallDirectory = tempInstallDirectoryStr.c_str();
int res = 1;
if ( !cmsys::SystemTools::MakeDirectory(tempInstallDirectory))
if ( !cmsys::SystemTools::MakeDirectory(bareTempInstallDirectory.c_str()))
{
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Problem creating temporary directory: "
@ -185,7 +187,6 @@ int cmCPackGenerator::InstallProject()
return 0;
}
bool setDestDir = cmSystemTools::IsOn(this->GetOption("CPACK_SET_DESTDIR"));
if ( setDestDir )
{
std::string destDir = "DESTDIR=";
@ -227,7 +228,7 @@ int cmCPackGenerator::InstallProject()
// If the project is a CMAKE project then run pre-install
// and then read the cmake_install script to run it
if ( !this->InstallProjectViaInstallCMakeProjects(
setDestDir, tempInstallDirectory) )
setDestDir, bareTempInstallDirectory.c_str()) )
{
return 0;
}
@ -244,8 +245,6 @@ int cmCPackGenerator::InstallProject()
int cmCPackGenerator::InstallProjectViaInstallCommands(
bool setDestDir, const char* tempInstallDirectory)
{
(void)setDestDir;
(void)tempInstallDirectory;
const char* installCommands = this->GetOption("CPACK_INSTALL_COMMANDS");
if ( installCommands && *installCommands )
{
@ -454,7 +453,7 @@ int cmCPackGenerator::InstallProjectViaInstallScript(
//----------------------------------------------------------------------
int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
bool setDestDir, const char* tempInstallDirectory)
bool setDestDir, const char* baseTempInstallDirectory)
{
const char* cmakeProjects
= this->GetOption("CPACK_INSTALL_CMAKE_PROJECTS");
@ -502,6 +501,51 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
std::string installSubDirectory = it->c_str();
std::string installFile = installDirectory + "/cmake_install.cmake";
std::vector<std::string> componentsVector;
bool componentInstall = false;
if (this->SupportsComponentInstallation())
{
// Determine the installation types for this project (if provided).
std::string installTypesVar = "CPACK_"
+ cmSystemTools::UpperCase(installComponent) + "_INSTALL_TYPES";
const char *installTypes = this->GetOption(installTypesVar.c_str());
if (installTypes && *installTypes)
{
std::vector<std::string> installTypesVector;
cmSystemTools::ExpandListArgument(installTypes, installTypesVector);
std::vector<std::string>::iterator installTypeIt;
for (installTypeIt = installTypesVector.begin();
installTypeIt != installTypesVector.end();
++installTypeIt)
{
this->GetInstallationType(installProjectName.c_str(),
installTypeIt->c_str());
}
}
// Determine the set of components that will be used in this project
std::string componentsVar
= "CPACK_COMPONENTS_" + cmSystemTools::UpperCase(installComponent);
const char *components = this->GetOption(componentsVar.c_str());
if (components && *components)
{
cmSystemTools::ExpandListArgument(components, componentsVector);
std::vector<std::string>::iterator compIt;
for (compIt = componentsVector.begin();
compIt != componentsVector.end();
++compIt)
{
GetComponent(installProjectName.c_str(), compIt->c_str());
}
componentInstall = true;
}
}
if (componentsVector.empty())
{
componentsVector.push_back(installComponent);
}
const char* buildConfig = this->GetOption("CPACK_BUILD_CONFIG");
cmGlobalGenerator* globalGenerator
= this->MakefileMap->GetCMakeInstance()->CreateGlobalGenerator(
@ -555,72 +599,100 @@ int cmCPackGenerator::InstallProjectViaInstallCMakeProjects(
cmCPackLogger(cmCPackLog::LOG_OUTPUT,
"- Install project: " << installProjectName << std::endl);
cmake cm;
cm.AddCMakePaths();
cm.SetProgressCallback(cmCPackGeneratorProgress, this);
cmGlobalGenerator gg;
gg.SetCMakeInstance(&cm);
std::auto_ptr<cmLocalGenerator> lg(gg.CreateLocalGenerator());
lg->SetGlobalGenerator(&gg);
cmMakefile *mf = lg->GetMakefile();
std::string realInstallDirectory = tempInstallDirectory;
if ( !installSubDirectory.empty() && installSubDirectory != "/" )
{
realInstallDirectory += installSubDirectory;
}
if ( setDestDir )
// Run the installation for each component
std::vector<std::string>::iterator componentIt;
for (componentIt = componentsVector.begin();
componentIt != componentsVector.end();
++componentIt)
{
// For DESTDIR based packaging, use the *project* CMAKE_INSTALL_PREFIX
// underneath the tempInstallDirectory. The value of the project's
// CMAKE_INSTALL_PREFIX is sent in here as the value of the
// CPACK_INSTALL_PREFIX variable.
std::string dir;
if (this->GetOption("CPACK_INSTALL_PREFIX"))
std::string tempInstallDirectory = baseTempInstallDirectory;
installComponent = *componentIt;
if (componentInstall)
{
dir += this->GetOption("CPACK_INSTALL_PREFIX");
cmCPackLogger(cmCPackLog::LOG_OUTPUT,
"- Install component: " << installComponent
<< std::endl);
}
cmake cm;
cm.AddCMakePaths();
cm.SetProgressCallback(cmCPackGeneratorProgress, this);
cmGlobalGenerator gg;
gg.SetCMakeInstance(&cm);
std::auto_ptr<cmLocalGenerator> lg(gg.CreateLocalGenerator());
lg->SetGlobalGenerator(&gg);
cmMakefile *mf = lg->GetMakefile();
std::string realInstallDirectory = tempInstallDirectory;
if ( !installSubDirectory.empty() && installSubDirectory != "/" )
{
realInstallDirectory += installSubDirectory;
}
if (componentInstall)
{
tempInstallDirectory += "/";
tempInstallDirectory += installComponent;
}
mf->AddDefinition("CMAKE_INSTALL_PREFIX", dir.c_str());
cmCPackLogger(cmCPackLog::LOG_DEBUG,
"- Using DESTDIR + CPACK_INSTALL_PREFIX... (mf->AddDefinition)"
<< std::endl);
cmCPackLogger(cmCPackLog::LOG_DEBUG,
"- Setting CMAKE_INSTALL_PREFIX to '" << dir << "'" << std::endl);
}
else
{
mf->AddDefinition("CMAKE_INSTALL_PREFIX", tempInstallDirectory);
if (!setDestDir)
{
tempInstallDirectory += this->GetPackagingInstallPrefix();
}
cmCPackLogger(cmCPackLog::LOG_DEBUG,
"- Using non-DESTDIR install... (mf->AddDefinition)" << std::endl);
cmCPackLogger(cmCPackLog::LOG_DEBUG,
"- Setting CMAKE_INSTALL_PREFIX to '" << tempInstallDirectory
<< "'" << std::endl);
}
if ( setDestDir )
{
// For DESTDIR based packaging, use the *project* CMAKE_INSTALL_PREFIX
// underneath the tempInstallDirectory. The value of the project's
// CMAKE_INSTALL_PREFIX is sent in here as the value of the
// CPACK_INSTALL_PREFIX variable.
std::string dir;
if (this->GetOption("CPACK_INSTALL_PREFIX"))
{
dir += this->GetOption("CPACK_INSTALL_PREFIX");
}
mf->AddDefinition("CMAKE_INSTALL_PREFIX", dir.c_str());
if ( buildConfig && *buildConfig )
{
mf->AddDefinition("BUILD_TYPE", buildConfig);
}
std::string installComponentLowerCase
= cmSystemTools::LowerCase(installComponent);
if ( installComponentLowerCase != "all" )
{
mf->AddDefinition("CMAKE_INSTALL_COMPONENT",
installComponent.c_str());
}
cmCPackLogger(cmCPackLog::LOG_DEBUG,
"- Using DESTDIR + CPACK_INSTALL_PREFIX... (mf->AddDefinition)"
<< std::endl);
cmCPackLogger(cmCPackLog::LOG_DEBUG,
"- Setting CMAKE_INSTALL_PREFIX to '" << dir << "'"
<< std::endl);
}
else
{
mf->AddDefinition("CMAKE_INSTALL_PREFIX", tempInstallDirectory.c_str());
// strip on TRUE, ON, 1, one or several file names, but not on
// FALSE, OFF, 0 and an empty string
if (!cmSystemTools::IsOff(this->GetOption("CPACK_STRIP_FILES")))
{
mf->AddDefinition("CMAKE_INSTALL_DO_STRIP", "1");
}
int res = mf->ReadListFile(0, installFile.c_str());
if ( cmSystemTools::GetErrorOccuredFlag() || !res )
{
return 0;
cmCPackLogger(cmCPackLog::LOG_DEBUG,
"- Using non-DESTDIR install... (mf->AddDefinition)" << std::endl);
cmCPackLogger(cmCPackLog::LOG_DEBUG,
"- Setting CMAKE_INSTALL_PREFIX to '" << tempInstallDirectory
<< "'" << std::endl);
}
if ( buildConfig && *buildConfig )
{
mf->AddDefinition("BUILD_TYPE", buildConfig);
}
std::string installComponentLowerCase
= cmSystemTools::LowerCase(installComponent);
if ( installComponentLowerCase != "all" )
{
mf->AddDefinition("CMAKE_INSTALL_COMPONENT",
installComponent.c_str());
}
// strip on TRUE, ON, 1, one or several file names, but not on
// FALSE, OFF, 0 and an empty string
if (!cmSystemTools::IsOff(this->GetOption("CPACK_STRIP_FILES")))
{
mf->AddDefinition("CMAKE_INSTALL_DO_STRIP", "1");
}
int res = mf->ReadListFile(0, installFile.c_str());
if ( cmSystemTools::GetErrorOccuredFlag() || !res )
{
return 0;
}
}
}
}
@ -709,7 +781,6 @@ int cmCPackGenerator::DoPackage()
const char* packageFileName = this->GetOption("CPACK_OUTPUT_FILE_PATH");
const char* tempDirectory = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl);
cmsys::Glob gl;
std::string findExpr = tempDirectory;
@ -736,8 +807,33 @@ int cmCPackGenerator::DoPackage()
{
tempDirectory = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
}
// The files to be installed
std::vector<std::string> files = gl.GetFiles();
// For component installations, determine which files go into which
// components.
if (!this->Components.empty())
{
std::vector<std::string>::const_iterator it;
for ( it = files.begin(); it != files.end(); ++ it )
{
std::string fileN = cmSystemTools::RelativePath(tempDirectory,
it->c_str());
// Determine which component we are in.
std::string componentName = fileN.substr(0, fileN.find('/'));
// Strip off the component part of the path.
fileN = fileN.substr(fileN.find('/')+1, std::string::npos);
// Add this file to the list of files for the component.
this->Components[componentName].Files.push_back(fileN);
}
}
if ( !this->CompressFiles(tempPackageFileName,
tempDirectory, gl.GetFiles()) || cmSystemTools::GetErrorOccuredFlag())
tempDirectory, files) || cmSystemTools::GetErrorOccuredFlag())
{
cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem compressing the directory"
<< std::endl);
@ -1092,3 +1188,158 @@ int cmCPackGenerator::CleanTemporaryDirectory()
}
return 1;
}
//----------------------------------------------------------------------
bool cmCPackGenerator::SupportsComponentInstallation() const
{
return false;
}
//----------------------------------------------------------------------
cmCPackInstallationType*
cmCPackGenerator::GetInstallationType(const char *projectName, const char *name)
{
bool hasInstallationType = this->InstallationTypes.count(name) != 0;
cmCPackInstallationType *installType = &this->InstallationTypes[name];
if (!hasInstallationType)
{
// Define the installation type
std::string macroPrefix = "CPACK_INSTALL_TYPE_"
+ cmsys::SystemTools::UpperCase(name);
installType->Name = name;
const char* displayName
= this->GetOption((macroPrefix + "_DISPLAY_NAME").c_str());
if (displayName && *displayName)
{
installType->DisplayName = displayName;
}
else
{
installType->DisplayName = installType->Name;
}
installType->Index = this->InstallationTypes.size();
}
return installType;
}
//----------------------------------------------------------------------
cmCPackComponent*
cmCPackGenerator::GetComponent(const char *projectName, const char *name)
{
bool hasComponent = this->Components.count(name) != 0;
cmCPackComponent *component = &this->Components[name];
if (!hasComponent)
{
// Define the component
std::string macroPrefix = "CPACK_COMPONENT_"
+ cmsys::SystemTools::UpperCase(name);
component->Name = name;
const char* displayName
= this->GetOption((macroPrefix + "_DISPLAY_NAME").c_str());
if (displayName && *displayName)
{
component->DisplayName = displayName;
}
else
{
component->DisplayName = component->Name;
}
component->IsHidden
= this->IsSet((macroPrefix + "_HIDDEN").c_str());
component->IsRequired
= this->IsSet((macroPrefix + "_REQUIRED").c_str());
component->IsDisabledByDefault
= this->IsSet((macroPrefix + "_DISABLED").c_str());
const char* groupName = this->GetOption((macroPrefix + "_GROUP").c_str());
if (groupName && *groupName)
{
component->Group = GetComponentGroup(projectName, groupName);
component->Group->Components.push_back(component);
}
else
{
component->Group = 0;
}
const char* description
= this->GetOption((macroPrefix + "_DESCRIPTION").c_str());
if (description && *description)
{
component->Description = description;
}
// Determine the installation types.
const char *installTypes
= this->GetOption((macroPrefix + "_INSTALL_TYPES").c_str());
if (installTypes && *installTypes)
{
std::vector<std::string> installTypesVector;
cmSystemTools::ExpandListArgument(installTypes, installTypesVector);
std::vector<std::string>::iterator installTypesIt;
for (installTypesIt = installTypesVector.begin();
installTypesIt != installTypesVector.end();
++installTypesIt)
{
component->InstallationTypes.push_back(
this->GetInstallationType(projectName, installTypesIt->c_str()));
}
}
// Determine the component dependencies.
const char *depends = this->GetOption((macroPrefix + "_DEPENDS").c_str());
if (depends && *depends)
{
std::vector<std::string> dependsVector;
cmSystemTools::ExpandListArgument(depends, dependsVector);
std::vector<std::string>::iterator dependIt;
for (dependIt = dependsVector.begin();
dependIt != dependsVector.end();
++dependIt)
{
cmCPackComponent *child = GetComponent(projectName, dependIt->c_str());
component->Dependencies.push_back(child);
child->ReverseDependencies.push_back(component);
}
}
}
return component;
}
//----------------------------------------------------------------------
cmCPackComponentGroup*
cmCPackGenerator::GetComponentGroup(const char *projectName, const char *name)
{
std::string macroPrefix = "CPACK_COMPONENT_GROUP_"
+ cmsys::SystemTools::UpperCase(name);
bool hasGroup = this->ComponentGroups.count(name) != 0;
cmCPackComponentGroup *group = &this->ComponentGroups[name];
if (!hasGroup)
{
// Define the group
group->Name = name;
const char* displayName
= this->GetOption((macroPrefix + "_DISPLAY_NAME").c_str());
if (displayName && *displayName)
{
group->DisplayName = displayName;
}
else
{
group->DisplayName = group->Name;
}
const char* description
= this->GetOption((macroPrefix + "_DESCRIPTION").c_str());
if (description && *description)
{
group->Description = description;
}
group->IsBold
= this->IsSet((macroPrefix + "_BOLD_TITLE").c_str());
group->IsExpandedByDefault
= this->IsSet((macroPrefix + "_EXPANDED").c_str());
}
return group;
}

View File

@ -19,6 +19,8 @@
#define cmCPackGenerator_h
#include "cmObject.h"
#include <map>
#include <vector>
#define cmCPackTypeMacro(class, superclass) \
cmTypeMacro(class, superclass); \
@ -44,6 +46,9 @@
class cmMakefile;
class cmCPackLog;
class cmCPackInstallationType;
class cmCPackComponent;
class cmCPackComponentGroup;
/** \class cmCPackGenerator
* \brief A superclass of all CPack Generators
@ -120,6 +125,11 @@ protected:
virtual int InstallProjectViaInstallCMakeProjects(
bool setDestDir, const char* tempInstallDirectory);
virtual bool SupportsComponentInstallation() const;
virtual cmCPackInstallationType* GetInstallationType(const char *projectName, const char* name);
virtual cmCPackComponent* GetComponent(const char *projectName, const char* name);
virtual cmCPackComponentGroup* GetComponentGroup(const char *projectName, const char* name);
bool GeneratorVerbose;
std::string Name;
@ -129,6 +139,10 @@ protected:
std::string CMakeSelf;
std::string CMakeRoot;
std::map<std::string, cmCPackInstallationType> InstallationTypes;
std::map<std::string, cmCPackComponent> Components;
std::map<std::string, cmCPackComponentGroup> ComponentGroups;
cmCPackLog* Logger;
private:
cmMakefile* MakefileMap;

View File

@ -23,6 +23,7 @@
#include "cmMakefile.h"
#include "cmGeneratedFileStream.h"
#include "cmCPackLog.h"
#include "cmCPackComponentGroup.h"
#include <cmsys/SystemTools.hxx>
#include <cmsys/Glob.hxx>
@ -79,7 +80,12 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
for ( it = files.begin(); it != files.end(); ++ it )
{
std::string fileN = cmSystemTools::RelativePath(toplevel,
it->c_str());
it->c_str());
if (!this->Components.empty())
{
// Strip off the component part of the path.
fileN = fileN.substr(fileN.find('/')+1, std::string::npos);
}
cmSystemTools::ReplaceString(fileN, "/", "\\");
str << " Delete \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
}
@ -92,14 +98,32 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
cmOStringStream dstr;
for ( sit = dirs.begin(); sit != dirs.end(); ++ sit )
{
std::string fileN = cmSystemTools::RelativePath(toplevel,
sit->c_str());
std::string componentName;
std::string fileN = cmSystemTools::RelativePath(toplevel, sit->c_str());
if ( fileN.empty() )
{
continue;
}
if (!Components.empty())
{
// If this is a component installation, strip off the component
// part of the path.
std::string::size_type slash = fileN.find('/');
if (slash != std::string::npos)
{
// If this is a component installation, determine which component it is.
componentName = fileN.substr(0, slash);
// Strip off the component part of the path.
fileN = fileN.substr(slash+1, std::string::npos);
}
}
cmSystemTools::ReplaceString(fileN, "/", "\\");
dstr << " RMDir \"$INSTDIR\\" << fileN.c_str() << "\"" << std::endl;
if (!componentName.empty())
{
this->Components[componentName].Directories.push_back(fileN);
}
}
cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Dirs: "
<< dstr.str().c_str() << std::endl);
@ -128,6 +152,116 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_ICON_CODE",
installerIconCode.c_str());
}
// Setup all of the component sections
if (this->Components.empty())
{
this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", "");
this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC", "");
this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS", "");
this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL",
"File /r \"${INST_DIR}\\*.*\"");
this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS", "");
this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST", "");
this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS", "");
}
else
{
std::string componentCode;
std::string sectionList;
std::string selectedVarsList;
std::string componentDescriptions;
std::string groupDescriptions;
std::string installTypesCode;
// Create installation types. The order is significant, so we first fill
// in a vector based on the indices, and print them in that order.
std::vector<cmCPackInstallationType *>
installTypes(this->InstallationTypes.size());
std::map<std::string, cmCPackInstallationType>::iterator installTypeIt;
for (installTypeIt = this->InstallationTypes.begin();
installTypeIt != this->InstallationTypes.end();
++installTypeIt)
{
installTypes[installTypeIt->second.Index-1] = &installTypeIt->second;
}
std::vector<cmCPackInstallationType *>::iterator installTypeIt2;
for (installTypeIt2 = installTypes.begin();
installTypeIt2 != installTypes.end();
++installTypeIt2)
{
installTypesCode += "InstType \"";
installTypesCode += (*installTypeIt2)->DisplayName;
installTypesCode += + "\"\n";
}
// Create installation groups first
std::map<std::string, cmCPackComponentGroup>::iterator groupIt;
for (groupIt = this->ComponentGroups.begin();
groupIt != this->ComponentGroups.end();
++groupIt)
{
componentCode += this->CreateComponentGroupDescription(&groupIt->second);
// Add the group description, if any.
if (!groupIt->second.Description.empty())
{
groupDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${"
+ groupIt->first + "} \""
+ this->TranslateNewlines(groupIt->second.Description) + "\"\n";
}
}
// Create the remaining components, which aren't associated with groups.
std::map<std::string, cmCPackComponent>::iterator compIt;
for (compIt = this->Components.begin();
compIt != this->Components.end();
++compIt)
{
if (!compIt->second.Group)
{
componentCode += this->CreateComponentDescription(&compIt->second);
}
// Add this component to the various section lists.
sectionList += " !insertmacro \"${MacroName}\" \"";
sectionList += compIt->first;
sectionList += "\"\n";
selectedVarsList += "Var " + compIt->first + "_selected\n";
// Add the component description, if any.
if (!compIt->second.Description.empty())
{
componentDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${"
+ compIt->first + "} \""
+ this->TranslateNewlines(compIt->second.Description) + "\"\n";
}
}
if (componentDescriptions.empty() && groupDescriptions.empty())
{
// Turn off the "Description" box
this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC",
"!define MUI_COMPONENTSPAGE_NODESC");
}
else
{
componentDescriptions =
"!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN\n"
+ componentDescriptions
+ groupDescriptions
+ "!insertmacro MUI_FUNCTION_DESCRIPTION_END\n";
this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC",
componentDescriptions.c_str());
}
this->SetOptionIfNotSet("CPACK_NSIS_INSTALLATION_TYPES", installTypesCode.c_str());
this->SetOptionIfNotSet("CPACK_NSIS_PAGE_COMPONENTS", "!insertmacro MUI_PAGE_COMPONENTS");
this->SetOptionIfNotSet("CPACK_NSIS_FULL_INSTALL", "");
this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTIONS", componentCode.c_str());
this->SetOptionIfNotSet("CPACK_NSIS_COMPONENT_SECTION_LIST", sectionList.c_str());
this->SetOptionIfNotSet("CPACK_NSIS_SECTION_SELECTED_VARS", selectedVarsList.c_str());
}
this->ConfigureFile(nsisInInstallOptions.c_str(),
nsisInstallOptions.c_str());
this->ConfigureFile(nsisInFileName.c_str(), nsisFileName.c_str());
@ -252,9 +386,8 @@ int cmCPackNSISGenerator::InitializeInternal()
}
else
{
cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
<< "not set" << std::endl);
cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: "
<< "not set" << std::endl);
}
if ( cpackPackageExecutables )
{
@ -274,8 +407,8 @@ int cmCPackNSISGenerator::InitializeInternal()
}
std::vector<std::string>::iterator it;
for ( it = cpackPackageExecutablesVector.begin();
it != cpackPackageExecutablesVector.end();
++it )
it != cpackPackageExecutablesVector.end();
++it )
{
std::string execName = *it;
++ it;
@ -415,3 +548,183 @@ bool cmCPackNSISGenerator::GetListOfSubdirectories(const char* topdir,
dirs.push_back(topdir);
return true;
}
//----------------------------------------------------------------------
bool cmCPackNSISGenerator::SupportsComponentInstallation() const
{
return true;
}
//----------------------------------------------------------------------
std::string
cmCPackNSISGenerator::
CreateComponentDescription(cmCPackComponent *component) const
{
// Basic description of the component
std::string componentCode = "Section ";
if (component->IsDisabledByDefault)
{
componentCode += "/o ";
}
componentCode += "\"";
if (component->IsHidden)
{
componentCode += "-";
}
componentCode += component->DisplayName + "\" " + component->Name + "\n";
if (component->IsRequired)
{
componentCode += " SectionIn RO\n";
}
else if (!component->InstallationTypes.empty())
{
std::ostringstream out;
std::vector<cmCPackInstallationType *>::iterator installTypeIter;
for (installTypeIter = component->InstallationTypes.begin();
installTypeIter != component->InstallationTypes.end();
++installTypeIter)
{
out << " " << (*installTypeIter)->Index;
}
componentCode += " SectionIn" + out.str() + "\n";
}
componentCode += " SetOutPath \"$INSTDIR\"\n";
componentCode += " File /r \"${INST_DIR}\\" + component->Name + "\\*.*\"\n";
componentCode += "SectionEnd\n";
// Macro used to remove the component
componentCode += "!macro Remove_${" + component->Name + "}\n";
std::vector<std::string>::iterator pathIt;
for (pathIt = component->Files.begin();
pathIt != component->Files.end();
++pathIt)
{
componentCode += " Delete \"$INSTDIR\\" + *pathIt + "\"\n";
}
for (pathIt = component->Directories.begin();
pathIt != component->Directories.end();
++pathIt)
{
componentCode += " RMDir \"$INSTDIR\\" + *pathIt + "\"\n";
}
componentCode += "!macroend\n";
// Macro used to select each of the components that this component
// depends on.
std::set<cmCPackComponent *> visited;
componentCode += "!macro Select_" + component->Name + "_depends\n";
componentCode += CreateSelectionDependenciesDescription(component, visited);
componentCode += "!macroend\n";
// Macro used to deselect each of the components that depend on this
// component.
visited.clear();
componentCode += "!macro Deselect_required_by_" + component->Name + "\n";
componentCode += CreateDeselectionDependenciesDescription(component, visited);
componentCode += "!macroend\n";
return componentCode;
}
//----------------------------------------------------------------------
std::string cmCPackNSISGenerator::CreateSelectionDependenciesDescription
(cmCPackComponent *component,
std::set<cmCPackComponent *>& visited) const
{
// Don't visit a component twice
if (visited.count(component))
{
return std::string();
}
visited.insert(component);
std::ostringstream out;
std::vector<cmCPackComponent *>::iterator dependIt;
for (dependIt = component->Dependencies.begin();
dependIt != component->Dependencies.end();
++dependIt)
{
// Write NSIS code to select this dependency
out << " SectionGetFlags ${" << (*dependIt)->Name << "} $0\n";
out << " IntOp $0 $0 | ${SF_SELECTED}\n";
out << " SectionSetFlags ${" << (*dependIt)->Name << "} $0\n";
out << " IntOp $" << (*dependIt)->Name << "_selected 0 + ${SF_SELECTED}\n";
// Recurse
out << CreateSelectionDependenciesDescription(*dependIt, visited).c_str();
}
return out.str();
}
//----------------------------------------------------------------------
std::string cmCPackNSISGenerator::CreateDeselectionDependenciesDescription
(cmCPackComponent *component,
std::set<cmCPackComponent *>& visited) const
{
// Don't visit a component twice
if (visited.count(component))
{
return std::string();
}
visited.insert(component);
std::ostringstream out;
std::vector<cmCPackComponent *>::iterator dependIt;
for (dependIt = component->ReverseDependencies.begin();
dependIt != component->ReverseDependencies.end();
++dependIt)
{
// Write NSIS code to deselect this dependency
out << " SectionGetFlags ${" << (*dependIt)->Name << "} $0\n";
out << " IntOp $1 ${SF_SELECTED} ~\n";
out << " IntOp $0 $0 & $1\n";
out << " SectionSetFlags ${" << (*dependIt)->Name << "} $0\n";
out << " IntOp $" << (*dependIt)->Name << "_selected 0 + 0\n";
// Recurse
out << CreateDeselectionDependenciesDescription(*dependIt, visited).c_str();
}
return out.str();
}
//----------------------------------------------------------------------
std::string
cmCPackNSISGenerator::
CreateComponentGroupDescription(cmCPackComponentGroup *group) const
{
if (group->Components.empty())
{
// Silently skip empty groups. NSIS doesn't support them.
return std::string();
}
std::string code = "SectionGroup ";
if (group->IsExpandedByDefault)
{
code += "/e ";
}
if (group->IsBold)
{
code += "\"!" + group->DisplayName + "\" " + group->Name + "\n";
}
else
{
code += "\"" + group->DisplayName + "\" " + group->Name + "\n";
}
std::vector<cmCPackComponent*>::iterator comp;
for (comp = group->Components.begin();
comp != group->Components.end();
++comp)
{
code += this->CreateComponentDescription(*comp);
}
code += "SectionGroupEnd\n";
return code;
}
std::string cmCPackNSISGenerator::TranslateNewlines(std::string str)
{
cmSystemTools::ReplaceString(str, "\n", "$\\r$\\n");
return str;
}

View File

@ -20,6 +20,7 @@
#include "cmCPackGenerator.h"
#include <set>
/** \class cmCPackNSISGenerator
* \brief A generator for NSIS files
@ -48,6 +49,32 @@ protected:
bool GetListOfSubdirectories(const char* dir,
std::vector<std::string>& dirs);
virtual bool SupportsComponentInstallation() const;
/// Produce a string that contains the NSIS code to describe a
/// particular component.
std::string CreateComponentDescription(cmCPackComponent *component) const;
/// Produce NSIS code that selects all of the components that this component
/// depends on, recursively.
std::string CreateSelectionDependenciesDescription
(cmCPackComponent *component,
std::set<cmCPackComponent *>& visited) const;
/// Produce NSIS code that de-selects all of the components that are dependent
/// on this component, recursively.
std::string CreateDeselectionDependenciesDescription
(cmCPackComponent *component,
std::set<cmCPackComponent *>& visited) const;
/// Produce a string that contains the NSIS code to describe a
/// particular component group, including its components.
std::string CreateComponentGroupDescription(cmCPackComponentGroup *group) const;
/// Translations any newlines found in the string into \r\n, so that the
/// resulting string can be used within NSIS.
static std::string TranslateNewlines(std::string str);
};
#endif

View File

@ -22,6 +22,7 @@
#include "cmSystemTools.h"
#include "cmMakefile.h"
#include "cmGeneratedFileStream.h"
#include "cmCPackComponentGroup.h"
#include "cmCPackLog.h"
#include <cmsys/SystemTools.hxx>
@ -38,6 +39,13 @@ cmCPackPackageMakerGenerator::~cmCPackPackageMakerGenerator()
{
}
//----------------------------------------------------------------------
bool cmCPackPackageMakerGenerator::SupportsComponentInstallation() const
{
return true;
}
//----------------------------------------------------------------------
int cmCPackPackageMakerGenerator::CopyInstallScript(const char* resdir,
const char* script,
const char* name)
@ -96,23 +104,59 @@ int cmCPackPackageMakerGenerator::CompressFiles(const char* outFileName,
// them executable
if(preflight)
{
this->CopyInstallScript(resDir.c_str(),
preflight,
"preflight");
this->CopyInstallScript(resDir.c_str(),
preflight,
"preflight");
}
if(postflight)
{
this->CopyInstallScript(resDir.c_str(),
postflight,
"postflight");
this->CopyInstallScript(resDir.c_str(),
postflight,
"postflight");
}
if(postupgrade)
{
this->CopyInstallScript(resDir.c_str(),
postupgrade,
"postupgrade");
this->CopyInstallScript(resDir.c_str(),
postupgrade,
"postupgrade");
}
if (!this->Components.empty())
{
// Create the directory where component packages will be installed.
std::string basePackageDir = toplevel;
basePackageDir += "/packages";
if (!cmsys::SystemTools::MakeDirectory(basePackageDir.c_str()))
{
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Problem creating component packages directory: "
<< basePackageDir.c_str() << std::endl);
return 0;
}
// Create packages for each component
std::map<std::string, cmCPackComponent>::iterator compIt;
for (compIt = this->Components.begin(); compIt != this->Components.end();
++compIt)
{
std::string packageFile = basePackageDir;
packageFile += '/';
packageFile += GetPackageName(compIt->second);
std::string packageDir = toplevel;
packageDir += '/';
packageDir += compIt->first;
if (!this->GenerateComponentPackage(packageFile.c_str(),
packageDir.c_str(),
compIt->second))
{
return 0;
}
}
}
this->SetOption("CPACK_MODULE_VERSION_SUFFIX", "");
// Copy or create all of the resource files we need.
if ( !this->CopyCreateResourceFile("License")
|| !this->CopyCreateResourceFile("ReadMe")
|| !this->CopyCreateResourceFile("Welcome")
@ -126,68 +170,58 @@ int cmCPackPackageMakerGenerator::CompressFiles(const char* outFileName,
std::string packageDirFileName
= this->GetOption("CPACK_TEMPORARY_DIRECTORY");
packageDirFileName += ".pkg";
if (this->Components.empty())
{
packageDirFileName += ".pkg";
}
else
{
packageDirFileName += ".mpkg";
if (this->PackageMakerVersion == 3.0)
{
cmCPackLogger(cmCPackLog::LOG_ERROR,
"PackageMaker 3.0 cannot build component-based installations."
<< std::endl << "Please use PackageMaker 2.5 instead." << std::endl);
}
}
std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
tmpFile += "/PackageMakerOutput.log";
cmOStringStream pkgCmd;
pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
<< "\" -build -p \"" << packageDirFileName << "\" -f \""
<< this->GetOption("CPACK_TEMPORARY_DIRECTORY")
<< "\" -r \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
<< "/Resources\" -i \""
<< this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Info.plist\" -d \""
<< this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Description.plist\"";
<< "\" -build -p \"" << packageDirFileName << "\"";
if (this->Components.empty())
{
pkgCmd << " -f \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY");
}
else
{
pkgCmd << " -mi \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
<< "/packages/";
}
pkgCmd << "\" -r \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
<< "/Resources\" -i \""
<< this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Info.plist\" -d \""
<< this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/Description.plist\"";
if ( this->PackageMakerVersion > 2.0 )
{
pkgCmd << " -v";
}
cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << pkgCmd.str().c_str()
<< std::endl);
std::string output;
int retVal = 1;
//bool res = cmSystemTools::RunSingleCommand(pkgCmd.str().c_str(), &output,
//&retVal, 0, this->GeneratorVerbose, 0);
bool res = true;
retVal = system(pkgCmd.str().c_str());
cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Done running package maker"
<< std::endl);
if ( !res || retVal )
{
cmGeneratedFileStream ofs(tmpFile.c_str());
ofs << "# Run command: " << pkgCmd.str().c_str() << std::endl
<< "# Output:" << std::endl
<< output.c_str() << std::endl;
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Problem running PackageMaker command: " << pkgCmd.str().c_str()
<< std::endl << "Please check " << tmpFile.c_str() << " for errors"
<< std::endl);
if (!RunPackageMaker(pkgCmd.str().c_str(), packageDirFileName.c_str()))
return 0;
}
// sometimes the pkgCmd finishes but the directory is not yet
// created, so try 10 times to see if it shows up
int tries = 10;
while(tries > 0 &&
!cmSystemTools::FileExists(packageDirFileName.c_str()))
if (!this->Components.empty())
{
cmSystemTools::Delay(500);
tries--;
WriteDistributionFile(packageDirFileName.c_str());
}
if(!cmSystemTools::FileExists(packageDirFileName.c_str()))
{
cmCPackLogger(
cmCPackLog::LOG_ERROR,
"Problem running PackageMaker command: " << pkgCmd.str().c_str()
<< std::endl << "Package not created: " << packageDirFileName.c_str()
<< std::endl);
}
tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
tmpFile += "/hdiutilOutput.log";
cmOStringStream dmgCmd;
dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE")
<< "\" create -ov -format UDZO -srcfolder \"" << packageDirFileName
<< "\" \"" << outFileName << "\"";
res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output,
std::string output;
int retVal = 1;
bool res = cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output,
&retVal, 0, this->GeneratorVerbose, 0);
if ( !res || retVal )
{
@ -339,8 +373,14 @@ bool cmCPackPackageMakerGenerator::CopyCreateResourceFile(const char* name)
return true;
}
bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name)
bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name,
const char* outName)
{
if (!outName)
{
outName = name;
}
std::string inFName = "CPack.";
inFName += name;
inFName += ".in";
@ -354,10 +394,374 @@ bool cmCPackPackageMakerGenerator::CopyResourcePlistFile(const char* name)
std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
destFileName += "/";
destFileName += name;
destFileName += outName;
cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: "
<< inFileName.c_str() << " to " << destFileName.c_str() << std::endl);
this->ConfigureFile(inFileName.c_str(), destFileName.c_str());
return true;
}
//----------------------------------------------------------------------
bool cmCPackPackageMakerGenerator::RunPackageMaker(const char *command,
const char *packageFile)
{
std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
tmpFile += "/PackageMakerOutput.log";
cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << command << std::endl);
std::string output;
int retVal = 1;
bool res = cmSystemTools::RunSingleCommand(command, &output, &retVal, 0,
this->GeneratorVerbose, 0);
cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Done running package maker"
<< std::endl);
if ( !res || retVal )
{
cmGeneratedFileStream ofs(tmpFile.c_str());
ofs << "# Run command: " << command << std::endl
<< "# Output:" << std::endl
<< output.c_str() << std::endl;
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Problem running PackageMaker command: " << command
<< std::endl << "Please check " << tmpFile.c_str() << " for errors"
<< std::endl);
return false;
}
// sometimes the command finishes but the directory is not yet
// created, so try 10 times to see if it shows up
int tries = 10;
while(tries > 0 &&
!cmSystemTools::FileExists(packageFile))
{
cmSystemTools::Delay(500);
tries--;
}
if(!cmSystemTools::FileExists(packageFile))
{
cmCPackLogger(
cmCPackLog::LOG_ERROR,
"Problem running PackageMaker command: " << command
<< std::endl << "Package not created: " << packageFile
<< std::endl);
return false;
}
return true;
}
//----------------------------------------------------------------------
std::string
cmCPackPackageMakerGenerator::GetPackageName(const cmCPackComponent& component)
{
std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
packagesDir += ".dummy";
cmOStringStream out;
out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir)
<< "-" << component.Name << ".pkg";
return out.str();
}
//----------------------------------------------------------------------
bool
cmCPackPackageMakerGenerator::
GenerateComponentPackage(const char *packageFile,
const char *packageDir,
const cmCPackComponent& component)
{
cmCPackLogger(cmCPackLog::LOG_OUTPUT,
"- Building component package: " << packageFile << std::endl);
// Create the description file for this component.
std::string descriptionFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
descriptionFile += '/' + component.Name + "-Description.plist";
std::ofstream out(descriptionFile.c_str());
out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl
<< "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\""
<< "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" << std::endl
<< "<plist version=\"1.4\">" << std::endl
<< "<dict>" << std::endl
<< " <key>IFPkgDescriptionTitle</key>" << std::endl
<< " <string>" << component.DisplayName << "</string>" << std::endl
<< " <key>IFPkgDescriptionVersion</key>" << std::endl
<< " <string>" << this->GetOption("CPACK_PACKAGE_VERSION")
<< "</string>" << std::endl
<< " <key>IFPkgDescriptionDescription</key>" << std::endl
<< " <string>" + this->EscapeForXML(component.Description)
<< "</string>" << std::endl
<< "</dict>" << std::endl
<< "</plist>" << std::endl;
out.close();
// Create the Info.plist file for this component
std::string moduleVersionSuffix = ".";
moduleVersionSuffix += component.Name;
this->SetOption("CPACK_MODULE_VERSION_SUFFIX", moduleVersionSuffix.c_str());
std::string infoFileName = component.Name;
infoFileName += "-Info.plist";
if (!this->CopyResourcePlistFile("Info.plist", infoFileName.c_str()))
{
return false;
}
// Run PackageMaker
cmOStringStream pkgCmd;
pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM")
<< "\" -build -p \"" << packageFile << "\""
<< " -f \"" << packageDir << "\""
<< "-i \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY")
<< "/" << infoFileName << "\""
<< "-d \"" << descriptionFile << "\"";
return RunPackageMaker(pkgCmd.str().c_str(), packageFile);
}
//----------------------------------------------------------------------
void
cmCPackPackageMakerGenerator::
WriteDistributionFile(const char* metapackageFile)
{
std::string distributionTemplate
= this->FindTemplate("CPack.distribution.dist.in");
if ( distributionTemplate.empty() )
{
cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find input file: "
<< distributionTemplate << std::endl);
return;
}
std::string distributionFile = metapackageFile;
distributionFile += "/Contents/distribution.dist";
// Create the choice outline, which provides a tree-based view of
// the components in their groups.
cmOStringStream choiceOut;
choiceOut << "<choices-outline>" << std::endl;
// Emit the outline for the groups
std::map<std::string, cmCPackComponentGroup>::iterator groupIt;
for (groupIt = this->ComponentGroups.begin();
groupIt != this->ComponentGroups.end();
++groupIt)
{
CreateChoiceOutline(groupIt->second, choiceOut);
}
// Emit the outline for the non-grouped components
std::map<std::string, cmCPackComponent>::iterator compIt;
for (compIt = this->Components.begin(); compIt != this->Components.end();
++compIt)
{
if (!compIt->second.Group)
{
choiceOut << "<line choice=\"" << compIt->first << "Choice\"></line>"
<< std::endl;
}
}
choiceOut << "</choices-outline>" << std::endl;
// Create the actual choices
for (groupIt = this->ComponentGroups.begin();
groupIt != this->ComponentGroups.end();
++groupIt)
{
CreateChoice(groupIt->second, choiceOut);
}
for (compIt = this->Components.begin(); compIt != this->Components.end();
++compIt)
{
CreateChoice(compIt->second, choiceOut);
}
this->SetOption("CPACK_PACKAGEMAKER_CHOICES", choiceOut.str().c_str());
// Create the distribution.dist file in the metapackage to turn it
// into a distribution package.
this->ConfigureFile(distributionTemplate.c_str(),
distributionFile.c_str());
}
//----------------------------------------------------------------------
void
cmCPackPackageMakerGenerator::
CreateChoiceOutline(const cmCPackComponentGroup& group, cmOStringStream& out)
{
out << "<line choice=\"" << group.Name << "Choice\">" << std::endl;
std::vector<cmCPackComponent*>::const_iterator compIt;
for (compIt = group.Components.begin(); compIt != group.Components.end();
++compIt)
{
out << " <line choice=\"" << (*compIt)->Name << "Choice\"></line>"
<< std::endl;
}
out << "</line>" << std::endl;
}
//----------------------------------------------------------------------
void
cmCPackPackageMakerGenerator::CreateChoice(const cmCPackComponentGroup& group,
cmOStringStream& out)
{
out << "<choice id=\"" << group.Name << "Choice\" "
<< "title=\"" << group.DisplayName << "\" "
<< "start_selected=\"true\" "
<< "start_enabled=\"true\" "
<< "start_visible=\"true\" ";
if (!group.Description.empty())
{
out << "description=\"" << EscapeForXML(group.Description)
<< "\"";
}
out << "></choice>" << std::endl;
}
//----------------------------------------------------------------------
void
cmCPackPackageMakerGenerator::CreateChoice(const cmCPackComponent& component,
cmOStringStream& out)
{
std::string packageId = "com.";
packageId += this->GetOption("CPACK_PACKAGE_VENDOR");
packageId += '.';
packageId += this->GetOption("CPACK_PACKAGE_NAME");
packageId += '.';
packageId += this->GetOption("CPACK_PACKAGE_VERSION");
packageId += '.';
packageId += component.Name;
out << "<choice id=\"" << component.Name << "Choice\" "
<< "title=\"" << component.DisplayName << "\" "
<< "start_selected=\""
<< (component.IsDisabledByDefault && !component.IsRequired? "false" : "true")
<< "\" "
<< "start_enabled=\""
<< (component.IsRequired? "false" : "true")
<< "\" "
<< "start_visible=\"" << (component.IsHidden? "false" : "true") << "\" ";
if (!component.Description.empty())
{
out << "description=\"" << EscapeForXML(component.Description)
<< "\" ";
}
if (!component.Dependencies.empty() || !component.ReverseDependencies.empty())
{
// The "selected" expression is evaluated each time any choice is
// selected, for all choices *except* the one that the user
// selected. A component is marked selected if it has been
// selected (my.choice.selected in Javascript) and all of the
// components it depends on have been selected (transitively) or
// if any of the components that depend on it have been selected
// (transitively). Assume that we have components A, B, C, D, and
// E, where each component depends on the previous component (B
// depends on A, C depends on B, D depends on C, and E depends on
// D). The expression we build for the component C will be
// my.choice.selected && B && A || D || E
// This way, selecting C will automatically select everything it depends
// on (B and A), while selecting something that depends on C--either D
// or E--will automatically cause C to get selected.
out << "selected=\"my.choice.selected";
AddDependencyAttributes(component, out);
AddReverseDependencyAttributes(component, out);
out << "\"";
}
out << ">" << std::endl;
out << " <pkg-ref id=\"" << packageId << "\"></pkg-ref>" << std::endl;
out << "</choice>" << std::endl;
// Create a description of the package associated with this
// component.
std::string relativePackageLocation = "Contents/Packages/";
relativePackageLocation += GetPackageName(component);
// Determine the installed size of the package. To do so, we dig
// into the Info.plist file from the generated package to retrieve
// this size.
int installedSize = 0;
std::string infoPlistFile = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
infoPlistFile += ".mpkg/";
infoPlistFile += relativePackageLocation;
infoPlistFile += "/Contents/Info.plist";
bool foundFlagInstalledSize = false;
std::string line;
std::ifstream ifs(infoPlistFile.c_str());
while ( cmSystemTools::GetLineFromStream(ifs, line) )
{
if (foundFlagInstalledSize)
{
std::string::size_type pos = line.find("<integer>");
if (pos == std::string::npos)
{
cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot parse package size in "
<< infoPlistFile << std::endl
<< "String is \"" << line << "\"" << std::endl);
}
else
{
line.erase(0, pos + 9);
pos = line.find("</integer>");
if (pos == std::string::npos)
{
cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot parse package size in "
<< infoPlistFile << std::endl);
}
else
{
line.erase(pos, std::string::npos);
installedSize = atoi(line.c_str());
}
}
foundFlagInstalledSize = false;
}
else
{
foundFlagInstalledSize
= line.find("IFPkgFlagInstalledSize") != std::string::npos;
}
}
out << "<pkg-ref id=\"" << packageId << "\" "
<< "version=\"" << this->GetOption("CPACK_PACKAGE_VERSION") << "\" "
<< "installKBytes=\"" << installedSize << "\" "
<< "auth=\"Admin\" onConclusion=\"None\">"
<< "file:./" << relativePackageLocation << "</pkg-ref>" << std::endl;
}
//----------------------------------------------------------------------
void
cmCPackPackageMakerGenerator::
AddDependencyAttributes(const cmCPackComponent& component, cmOStringStream& out)
{
std::vector<cmCPackComponent *>::const_iterator dependIt;
for (dependIt = component.Dependencies.begin();
dependIt != component.Dependencies.end();
++dependIt)
{
out << " &amp;&amp; choices['" << (*dependIt)->Name << "Choice'].selected";
AddDependencyAttributes(**dependIt, out);
}
}
//----------------------------------------------------------------------
void
cmCPackPackageMakerGenerator::
AddReverseDependencyAttributes(const cmCPackComponent& component,
cmOStringStream& out)
{
std::vector<cmCPackComponent *>::const_iterator dependIt;
for (dependIt = component.ReverseDependencies.begin();
dependIt != component.ReverseDependencies.end();
++dependIt)
{
out << " || choices['" << (*dependIt)->Name << "Choice'].selected";
AddReverseDependencyAttributes(**dependIt, out);
}
}
//----------------------------------------------------------------------
std::string cmCPackPackageMakerGenerator::EscapeForXML(std::string str)
{
cmSystemTools::ReplaceString(str, "&", "&amp;");
cmSystemTools::ReplaceString(str, "<", "&lt;");
cmSystemTools::ReplaceString(str, ">", "&gt;");
cmSystemTools::ReplaceString(str, "\"", "&quot;");
return str;
}

View File

@ -21,6 +21,8 @@
#include "cmCPackGenerator.h"
class cmCPackComponent;
/** \class cmCPackPackageMakerGenerator
* \brief A generator for PackageMaker files
*
@ -38,6 +40,8 @@ public:
cmCPackPackageMakerGenerator();
virtual ~cmCPackPackageMakerGenerator();
virtual bool SupportsComponentInstallation() const;
protected:
int CopyInstallScript(const char* resdir,
const char* script,
@ -49,8 +53,63 @@ protected:
virtual const char* GetOutputPostfix() { return "darwin"; }
bool CopyCreateResourceFile(const char* name);
bool CopyResourcePlistFile(const char* name);
bool CopyResourcePlistFile(const char* name, const char* outName = 0);
// Run PackageMaker with the given command line, which will (if
// successful) produce the given package file. Returns true if
// PackageMaker succeeds, false otherwise.
bool RunPackageMaker(const char *command, const char *packageFile);
// Retrieve the name of package file that will be generated for this
// component. The name is just the file name with extension, and
// does not include the subdirectory.
std::string GetPackageName(const cmCPackComponent& component);
// Generate a package in the file packageFile for the given
// component. All of the files within this component are stored in
// the directory packageDir. Returns true if successful, false
// otherwise.
bool GenerateComponentPackage(const char *packageFile,
const char *packageDir,
const cmCPackComponent& component);
// Writes a distribution.dist file, which turns a metapackage into a
// full-fledged distribution. This file is used to describe
// inter-component dependencies. metapackageFile is the name of the
// metapackage for the distribution. Only valid for a
// component-based install.
void WriteDistributionFile(const char* metapackageFile);
// Subroutine of WriteDistributionFile that writes out the
// dependency attributes for inter-component dependencies.
void AddDependencyAttributes(const cmCPackComponent& component,
cmOStringStream& out);
// Subroutine of WriteDistributionFile that writes out the
// reverse dependency attributes for inter-component dependencies.
void AddReverseDependencyAttributes(const cmCPackComponent& component,
cmOStringStream& out);
// Generates XML that encodes the hierarchy of component groups and
// their components in a form that can be used by distribution
// metapackages.
void CreateChoiceOutline(const cmCPackComponentGroup& group,
cmOStringStream& out);
/// Create the "choice" XML element to describe a component group
/// for the installer GUI.
void CreateChoice(const cmCPackComponentGroup& group,
cmOStringStream& out);
/// Create the "choice" XML element to describe a component for the
/// installer GUI.
void CreateChoice(const cmCPackComponent& component,
cmOStringStream& out);
// Escape the given string to make it usable as an XML attribute
// value.
std::string EscapeForXML(std::string str);
double PackageMakerVersion;
};

View File

@ -19,7 +19,7 @@ IF(BUILD_TESTING)
OPTION(CMAKE_RUN_LONG_TESTS
"Should the long tests be run (such as Bootstrap)." ON)
MARK_AS_ADVANCED(CMAKE_RUN_LONG_TESTS)
IF (CMAKE_RUN_LONG_TESTS)
OPTION(CTEST_TEST_CTEST
"Should the tests that run a full sub ctest process be run?"
@ -27,6 +27,34 @@ IF(BUILD_TESTING)
MARK_AS_ADVANCED(CTEST_TEST_CTEST)
ENDIF (CMAKE_RUN_LONG_TESTS)
# Should CPack tests be run? By default, yes, but...
#
# Disable packaging test on Apple 10.3 and below. PackageMaker starts
# DiskManagementTool as root and disowns it
# (http://lists.apple.com/archives/installer-dev/2005/Jul/msg00005.html).
# It is left holding open pipe handles and preventing ProcessUNIX from
# detecting end-of-data even after its immediate child exits. Then
# the test hangs until it times out and is killed. This is a
# well-known bug in kwsys process execution that I would love to get
# time to fix.
#
OPTION(CTEST_TEST_CPACK
"Should the tests that use '--build-target package' be run?"
ON)
MARK_AS_ADVANCED(CTEST_TEST_CPACK)
IF(APPLE AND CTEST_TEST_CPACK)
EXECUTE_PROCESS(
COMMAND sw_vers -productVersion
OUTPUT_VARIABLE OSX_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
IF(OSX_VERSION MATCHES "^10\\.[0123]" OR OSX_VERSION MATCHES "ProductVersion:\t10\\.[0123]")
MESSAGE(STATUS "Forcing CTEST_TEST_CPACK=OFF on OSX < 10.4")
MESSAGE(STATUS "OSX_VERSION='${OSX_VERSION}'")
SET(CTEST_TEST_CPACK OFF)
ENDIF(OSX_VERSION MATCHES "^10\\.[0123]" OR OSX_VERSION MATCHES "ProductVersion:\t10\\.[0123]")
ENDIF(APPLE AND CTEST_TEST_CPACK)
# Use 1500 or CTEST_TEST_TIMEOUT for long test timeout value,
# whichever is greater.
SET(CMAKE_LONG_TEST_TIMEOUT 1500)
@ -259,6 +287,21 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=CVS -P ${CMake_SOURCE_DIR}/Utilities/Rel
"-DSTAGE2:BOOL=1"
--test-command ${SimpleInstallInstallDir}/MyTest/bin/SimpleInstExeS2)
IF(CTEST_TEST_CPACK)
ADD_TEST(CPackComponents ${CMAKE_CTEST_COMMAND}
--build-and-test
"${CMake_SOURCE_DIR}/Tests/CPackComponents"
"${CMake_BINARY_DIR}/Tests/CPackComponents"
--build-generator ${CMAKE_TEST_GENERATOR}
--build-project CPackComponents
--build-makeprogram ${CMAKE_TEST_MAKEPROGRAM}
--build-two-config
--build-target package
--test-command ${CMAKE_CMAKE_COMMAND}
"-DCPackComponents_BINARY_DIR:PATH=${CMake_BINARY_DIR}/Tests/CPackComponents"
-P "${CMake_SOURCE_DIR}/Tests/CPackComponents/VerifyResult.cmake")
ENDIF(CTEST_TEST_CPACK)
ADD_TEST(X11 ${CMAKE_CTEST_COMMAND}
--build-and-test
"${CMake_SOURCE_DIR}/Tests/X11"

View File

@ -0,0 +1,92 @@
# CPack Example: User-selectable Installation Components
#
# In this example, we have a simple library (mylib) with an example
# application (mylibapp). We create a binary installer that allows
# users to select which pieces will be installed: the example
# application, the library binaries, and/or the header file.
cmake_minimum_required(VERSION 2.6)
project(CPackComponents)
# Create the mylib library
add_library(mylib mylib.cpp)
# Create the mylibapp application
add_executable(mylibapp mylibapp.cpp)
target_link_libraries(mylibapp mylib)
# Create installation targets. Note that we put each kind of file
# into a different component via COMPONENT. These components will
# be used to create the installation components.
install(TARGETS mylib
ARCHIVE
DESTINATION lib
COMPONENT libraries)
install(TARGETS mylibapp
RUNTIME
DESTINATION bin
COMPONENT applications)
install(FILES mylib.h
DESTINATION include
COMPONENT headers)
# CPack boilerplate for this project
set(CPACK_PACKAGE_NAME "MyLib")
set(CPACK_PACKAGE_VENDOR "CMake.org")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MyLib - CPack Component Installation Example")
set(CPACK_PACKAGE_VERSION "1.0.0")
set(CPACK_PACKAGE_VERSION_MAJOR "1")
set(CPACK_PACKAGE_VERSION_MINOR "0")
set(CPACK_PACKAGE_VERSION_PATCH "0")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "CPack Component Example")
# Tell CPack all of the components to install. The "ALL"
# refers to the fact that this is the set of components that
# will be included when CPack is instructed to put everything
# into the binary installer (the default behavior).
set(CPACK_COMPONENTS_ALL applications libraries headers)
# Set the displayed names for each of the components to install.
# These will be displayed in the list of components inside the installer.
set(CPACK_COMPONENT_APPLICATIONS_DISPLAY_NAME "MyLib Application")
set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries")
set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "C++ Headers")
# Provide descriptions for each of the components to install.
# When the user hovers the mouse over the name of a component,
# the description will be shown in the "Description" box in the
# installer. If no descriptions are provided, the "Description"
# box will be removed.
set(CPACK_COMPONENT_APPLICATIONS_DESCRIPTION
"An extremely useful application that makes use of MyLib")
set(CPACK_COMPONENT_LIBRARIES_DESCRIPTION
"Static libraries used to build programs with MyLib")
set(CPACK_COMPONENT_HEADERS_DESCRIPTION
"C/C++ header files for use with MyLib")
# Put the components into two different groups: "Runtime" and "Development"
set(CPACK_COMPONENT_APPLICATIONS_GROUP "Runtime")
set(CPACK_COMPONENT_LIBRARIES_GROUP "Development")
set(CPACK_COMPONENT_HEADERS_GROUP "Development")
# Expand the "Development" group by default, since we have so few components.
# Also, provide this group with a description.
set(CPACK_COMPONENT_GROUP_DEVELOPMENT_EXPANDED ON)
set(CPACK_COMPONENT_GROUP_DEVELOPMENT_DESCRIPTION
"All of the tools you'll ever need to develop software")
# It doesn't make sense to install the headers without the libraries
# (because you could never use the headers!), so make the headers component
# depend on the libraries component.
set(CPACK_COMPONENT_HEADERS_DEPENDS libraries)
# Create two installation types with pre-selected components.
# The "Developer" installation has just the library and headers,
# while the "Full" installation has everything.
set(CPACK_ALL_INSTALL_TYPES Full Developer)
set(CPACK_INSTALL_TYPE_FULL_DISPLAY_NAME "Everything")
set(CPACK_COMPONENT_LIBRARIES_INSTALL_TYPES Developer Full)
set(CPACK_COMPONENT_HEADERS_INSTALL_TYPES Developer Full)
set(CPACK_COMPONENT_APPLICATIONS_INSTALL_TYPES Full)
# Include CPack to introduce the appropriate targets
include(CPack)

View File

@ -0,0 +1,48 @@
message(STATUS "=============================================================================")
message(STATUS "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)")
message(STATUS "")
if(NOT CPackComponents_BINARY_DIR)
message(FATAL_ERROR "CPackComponents_BINARY_DIR not set")
endif(NOT CPackComponents_BINARY_DIR)
set(expected_file_mask "")
if(WIN32)
# Only expect the *.exe installer if it looks like NSIS is
# installed on this machine:
#
find_program(NSIS_MAKENSIS_EXECUTABLE NAMES makensis
PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS]
DOC "makensis.exe location"
)
if(NSIS_MAKENSIS_EXECUTABLE)
set(expected_file_mask "${CPackComponents_BINARY_DIR}/*.exe")
endif(NSIS_MAKENSIS_EXECUTABLE)
endif(WIN32)
if(APPLE)
# Always expect the *.dmg installer - PackageMaker should always
# be installed on a development Mac:
#
set(expected_file_mask "${CPackComponents_BINARY_DIR}/*.dmg")
endif(APPLE)
if(expected_file_mask)
set(expected_count 1)
file(GLOB expected_file "${expected_file_mask}")
message(STATUS "expected_count='${expected_count}'")
message(STATUS "expected_file='${expected_file}'")
message(STATUS "expected_file_mask='${expected_file_mask}'")
if(NOT expected_file)
message(FATAL_ERROR "error: expected_file does not exist: CPackComponents test fails.")
endif(NOT expected_file)
list(LENGTH expected_file actual_count)
message(STATUS "actual_count='${actual_count}'")
if(NOT actual_count EQUAL expected_count)
message(FATAL_ERROR "error: expected_count does not match actual_count: CPackComponents test fails.")
endif(NOT actual_count EQUAL expected_count)
endif(expected_file_mask)

View File

@ -0,0 +1,7 @@
#include "mylib.h"
#include "stdio.h"
void mylib_function()
{
printf("This is mylib");
}

View File

@ -0,0 +1 @@
void mylib_function();

View File

@ -0,0 +1,6 @@
#include "mylib.h"
int main()
{
mylib_function();
}

View File

@ -355,27 +355,11 @@ SET(CMAKE_INSTALL_DEBUG_LIBRARIES 1)
INCLUDE(InstallRequiredSystemLibraries)
INCLUDE(CPack)
# Disable packaging test on Apple 10.3 and below. PackageMaker starts
# DiskManagementTool as root and disowns it
# (http://lists.apple.com/archives/installer-dev/2005/Jul/msg00005.html).
# It is left holding open pipe handles and preventing ProcessUNIX from
# detecting end-of-data even after its immediate child exits. Then
# the test hangs until it times out and is killed. This is a
# well-known bug in kwsys process execution that I would love to get
# time to fix.
SET(PACKAGE_TARGET --build-target package)
IF(APPLE AND NOT CTEST_TEST_CPACK)
EXECUTE_PROCESS(
COMMAND sw_vers -productVersion
OUTPUT_VARIABLE OSX_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
IF("${OSX_VERSION}" MATCHES "^10\\.[0123]" OR "${OSX_VERSION}" MATCHES "ProductVersion:\t10\\.[0123]")
MESSAGE(STATUS "Disabling package test on OSX < 10.4")
MESSAGE(STATUS "OSX_VERSION='${OSX_VERSION}'")
SET(PACKAGE_TARGET)
ENDIF("${OSX_VERSION}" MATCHES "^10\\.[0123]" OR "${OSX_VERSION}" MATCHES "ProductVersion:\t10\\.[0123]")
ENDIF(APPLE AND NOT CTEST_TEST_CPACK)
IF(CTEST_TEST_CPACK)
SET(PACKAGE_TARGET --build-target package)
ELSE(CTEST_TEST_CPACK)
SET(PACKAGE_TARGET)
ENDIF(CTEST_TEST_CPACK)
ADD_CUSTOM_COMMAND(
TARGET ${install_target}

View File

@ -355,27 +355,11 @@ SET(CMAKE_INSTALL_DEBUG_LIBRARIES 1)
INCLUDE(InstallRequiredSystemLibraries)
INCLUDE(CPack)
# Disable packaging test on Apple 10.3 and below. PackageMaker starts
# DiskManagementTool as root and disowns it
# (http://lists.apple.com/archives/installer-dev/2005/Jul/msg00005.html).
# It is left holding open pipe handles and preventing ProcessUNIX from
# detecting end-of-data even after its immediate child exits. Then
# the test hangs until it times out and is killed. This is a
# well-known bug in kwsys process execution that I would love to get
# time to fix.
SET(PACKAGE_TARGET --build-target package)
IF(APPLE AND NOT CTEST_TEST_CPACK)
EXECUTE_PROCESS(
COMMAND sw_vers -productVersion
OUTPUT_VARIABLE OSX_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
IF("${OSX_VERSION}" MATCHES "^10\\.[0123]" OR "${OSX_VERSION}" MATCHES "ProductVersion:\t10\\.[0123]")
MESSAGE(STATUS "Disabling package test on OSX < 10.4")
MESSAGE(STATUS "OSX_VERSION='${OSX_VERSION}'")
SET(PACKAGE_TARGET)
ENDIF("${OSX_VERSION}" MATCHES "^10\\.[0123]" OR "${OSX_VERSION}" MATCHES "ProductVersion:\t10\\.[0123]")
ENDIF(APPLE AND NOT CTEST_TEST_CPACK)
IF(CTEST_TEST_CPACK)
SET(PACKAGE_TARGET --build-target package)
ELSE(CTEST_TEST_CPACK)
SET(PACKAGE_TARGET)
ENDIF(CTEST_TEST_CPACK)
ADD_CUSTOM_COMMAND(
TARGET ${install_target}