summaryrefslogtreecommitdiffstats
path: root/Source/CPack
diff options
context:
space:
mode:
authorDavid Cole <david.cole@kitware.com>2008-06-17 15:39:26 (GMT)
committerDavid Cole <david.cole@kitware.com>2008-06-17 15:39:26 (GMT)
commit1105a86c520d34a59b482b86b6f288c11ff45b81 (patch)
tree6814d23047d44fe8c2c3d18e40b98143aca186c4 /Source/CPack
parent64498a1287e13e9f7ad3488c1050318030b18f8d (diff)
downloadCMake-1105a86c520d34a59b482b86b6f288c11ff45b81.zip
CMake-1105a86c520d34a59b482b86b6f288c11ff45b81.tar.gz
CMake-1105a86c520d34a59b482b86b6f288c11ff45b81.tar.bz2
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.
Diffstat (limited to 'Source/CPack')
-rw-r--r--Source/CPack/cmCPackComponentGroup.h117
-rw-r--r--Source/CPack/cmCPackGenerator.cxx403
-rw-r--r--Source/CPack/cmCPackGenerator.h14
-rw-r--r--Source/CPack/cmCPackNSISGenerator.cxx329
-rw-r--r--Source/CPack/cmCPackNSISGenerator.h27
-rw-r--r--Source/CPack/cmCPackPackageMakerGenerator.cxx518
-rw-r--r--Source/CPack/cmCPackPackageMakerGenerator.h61
7 files changed, 1327 insertions, 142 deletions
diff --git a/Source/CPack/cmCPackComponentGroup.h b/Source/CPack/cmCPackComponentGroup.h
new file mode 100644
index 0000000..2e1703c
--- /dev/null
+++ b/Source/CPack/cmCPackComponentGroup.h
@@ -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
diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx
index 8502df0..396bd9e 100644
--- a/Source/CPack/cmCPackGenerator.cxx
+++ b/Source/CPack/cmCPackGenerator.cxx
@@ -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());
+
+ 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());
- 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 non-DESTDIR install... (mf->AddDefinition)" << std::endl);
+ cmCPackLogger(cmCPackLog::LOG_DEBUG,
+ "- Setting CMAKE_INSTALL_PREFIX to '" << tempInstallDirectory
+ << "'" << std::endl);
+ }
- // 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;
+ 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;
+}
diff --git a/Source/CPack/cmCPackGenerator.h b/Source/CPack/cmCPackGenerator.h
index da819d6..ad61d65 100644
--- a/Source/CPack/cmCPackGenerator.h
+++ b/Source/CPack/cmCPackGenerator.h
@@ -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;
diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx
index eb1cb1f..6b43e1e 100644
--- a/Source/CPack/cmCPackNSISGenerator.cxx
+++ b/Source/CPack/cmCPackNSISGenerator.cxx
@@ -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;
+}
diff --git a/Source/CPack/cmCPackNSISGenerator.h b/Source/CPack/cmCPackNSISGenerator.h
index 0ae4ef7..70068e41 100644
--- a/Source/CPack/cmCPackNSISGenerator.h
+++ b/Source/CPack/cmCPackNSISGenerator.h
@@ -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
diff --git a/Source/CPack/cmCPackPackageMakerGenerator.cxx b/Source/CPack/cmCPackPackageMakerGenerator.cxx
index a5884a9..e4a003d 100644
--- a/Source/CPack/cmCPackPackageMakerGenerator.cxx
+++ b/Source/CPack/cmCPackPackageMakerGenerator.cxx
@@ -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\"";
- if ( this->PackageMakerVersion > 2.0 )
+ << "\" -build -p \"" << packageDirFileName << "\"";
+ if (this->Components.empty())
{
- pkgCmd << " -v";
+ pkgCmd << " -f \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY");
}
- 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 )
+ else
{
- 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);
- return 0;
+ pkgCmd << " -mi \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY")
+ << "/packages/";
}
- // 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()))
+ 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 )
{
- cmSystemTools::Delay(500);
- tries--;
+ pkgCmd << " -v";
}
- if(!cmSystemTools::FileExists(packageDirFileName.c_str()))
+ if (!RunPackageMaker(pkgCmd.str().c_str(), packageDirFileName.c_str()))
+ return 0;
+
+ if (!this->Components.empty())
{
- cmCPackLogger(
- cmCPackLog::LOG_ERROR,
- "Problem running PackageMaker command: " << pkgCmd.str().c_str()
- << std::endl << "Package not created: " << packageDirFileName.c_str()
- << std::endl);
+ WriteDistributionFile(packageDirFileName.c_str());
}
- 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;
+}
diff --git a/Source/CPack/cmCPackPackageMakerGenerator.h b/Source/CPack/cmCPackPackageMakerGenerator.h
index 6c16c75..2be8a2c 100644
--- a/Source/CPack/cmCPackPackageMakerGenerator.h
+++ b/Source/CPack/cmCPackPackageMakerGenerator.h
@@ -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;
};