summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
Diffstat (limited to 'Source')
-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;
};