summaryrefslogtreecommitdiffstats
path: root/Source/CPack/cmCPackNSISGenerator.cxx
diff options
context:
space:
mode:
authorDavid Cole <david.cole@kitware.com>2008-07-08 15:52:25 (GMT)
committerDavid Cole <david.cole@kitware.com>2008-07-08 15:52:25 (GMT)
commit83ec8c35933439305895b8569b2125a7e091fc57 (patch)
treeee94a4f9cdd476a27a4ad466269c214dc1254d6c /Source/CPack/cmCPackNSISGenerator.cxx
parent75154920793c285cd00a4ff225654d693158b679 (diff)
downloadCMake-83ec8c35933439305895b8569b2125a7e091fc57.zip
CMake-83ec8c35933439305895b8569b2125a7e091fc57.tar.gz
CMake-83ec8c35933439305895b8569b2125a7e091fc57.tar.bz2
ENH: Further refinement of the CPack components functionality from Doug Gregor.
Details: ========== - New cpack_add_component, cpack_add_component_group, and cpack_add_install_type "commands" defined as macros in the CPack module. - Documentation for all of the variables and commands in the CPack module. - Added get_cmake_property(... COMPONENTS) to CMake to ask for the names of all components. Used in the CPack module to automatically build component-based installers. (Set CPACK_MONOLITHIC_INSTALL to turn off component-based installation). - A group can declare its PARENT_GROUP, to build an arbitrary hierarchy of groups. - New CPack command cpack_configure_downloads, which creates an installer that downloads only the selected components on-the-fly. Those components marked DOWNLOADED will be separate packages downloaded on-the-fly (or, all packages can be marked as such with the ALL option to cpack_configure_downloads). Individual components are compressed with ZIP at installer-creation time and downloaded/uncompressed by the installer as needed. This feature is only available on Windows with NSIS at the moment. - NSIS installers can install themselves and enable the "Change" button in Add/Remove programs, allowing users to go back and install or remove components. This can be disabled through cpack_configure_downloads, because it's only really useful is most of the application's functionality is in downloaded components. - Bug fix: automatically install everything whose COMPONENT was not specified (it's a hidden, required group) - Bug fix: fixed removal of components when re-running the NSIS installer and unchecking components - Bug fix: NSIS installers now only install/remove the minimal number of files when re-run to update the installation (or by clicking "Change" in Add/Remove programs)
Diffstat (limited to 'Source/CPack/cmCPackNSISGenerator.cxx')
-rw-r--r--Source/CPack/cmCPackNSISGenerator.cxx241
1 files changed, 222 insertions, 19 deletions
diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx
index cd4ffb8..7be5cb2 100644
--- a/Source/CPack/cmCPackNSISGenerator.cxx
+++ b/Source/CPack/cmCPackNSISGenerator.cxx
@@ -173,6 +173,9 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
std::string componentDescriptions;
std::string groupDescriptions;
std::string installTypesCode;
+ std::string defines;
+ cmOStringStream macrosOut;
+ bool anyDownloadedComponents = false;
// Create installation types. The order is significant, so we first fill
// in a vector based on the indices, and print them in that order.
@@ -201,7 +204,11 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
groupIt != this->ComponentGroups.end();
++groupIt)
{
- componentCode += this->CreateComponentGroupDescription(&groupIt->second);
+ if (groupIt->second.ParentGroup == 0)
+ {
+ componentCode +=
+ this->CreateComponentGroupDescription(&groupIt->second, macrosOut);
+ }
// Add the group description, if any.
if (!groupIt->second.Description.empty())
@@ -218,9 +225,19 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
compIt != this->Components.end();
++compIt)
{
+ if (compIt->second.Files.empty())
+ {
+ // NSIS cannot cope with components that have no files.
+ continue;
+ }
+
+ anyDownloadedComponents =
+ anyDownloadedComponents || compIt->second.IsDownloaded;
+
if (!compIt->second.Group)
{
- componentCode += this->CreateComponentDescription(&compIt->second);
+ componentCode
+ += this->CreateComponentDescription(&compIt->second, macrosOut);
}
// Add this component to the various section lists.
@@ -228,6 +245,7 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
sectionList += compIt->first;
sectionList += "\"\n";
selectedVarsList += "Var " + compIt->first + "_selected\n";
+ selectedVarsList += "Var " + compIt->first + "_was_installed\n";
// Add the component description, if any.
if (!compIt->second.Description.empty())
@@ -238,6 +256,8 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
}
}
+ componentCode += macrosOut.str();
+
if (componentDescriptions.empty() && groupDescriptions.empty())
{
// Turn off the "Description" box
@@ -254,12 +274,23 @@ int cmCPackNSISGenerator::CompressFiles(const char* outFileName,
this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_COMPONENTS_DESC",
componentDescriptions.c_str());
}
+
+ if (anyDownloadedComponents)
+ {
+ defines += "!define CPACK_USES_DOWNLOAD\n";
+ if (cmSystemTools::IsOn(this->GetOption("CPACK_ADD_REMOVE")))
+ {
+ defines += "!define CPACK_NSIS_ADD_REMOVE\n";
+ }
+ }
+
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->SetOption("CPACK_NSIS_DEFINES", defines.c_str());
}
this->ConfigureFile(nsisInInstallOptions.c_str(),
@@ -559,7 +590,7 @@ bool cmCPackNSISGenerator::SupportsComponentInstallation() const
//----------------------------------------------------------------------
std::string
cmCPackNSISGenerator::
-CreateComponentDescription(cmCPackComponent *component) const
+CreateComponentDescription(cmCPackComponent *component, cmOStringStream& macrosOut)
{
// Basic description of the component
std::string componentCode = "Section ";
@@ -590,46 +621,204 @@ CreateComponentDescription(cmCPackComponent *component) const
componentCode += " SectionIn" + out.str() + "\n";
}
componentCode += " SetOutPath \"$INSTDIR\"\n";
- componentCode += " File /r \"${INST_DIR}\\" + component->Name + "\\*.*\"\n";
+
+ componentCode += " IntCmp $" + component->Name
+ + "_was_installed ${SF_SELECTED} noinstall_" + component->Name + "\n";
+
+ // Create the actual installation commands
+ if (component->IsDownloaded)
+ {
+ if (component->ArchiveFile.empty())
+ {
+ // Compute the name of the archive.
+ std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+ packagesDir += ".dummy";
+ cmOStringStream out;
+ out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir)
+ << "-" << component->Name << ".zip";
+ component->ArchiveFile = out.str();
+ }
+
+ // Create the directory for the upload area
+ const char* userUploadDirectory = this->GetOption("CPACK_UPLOAD_DIRECTORY");
+ std::string uploadDirectory;
+ if (userUploadDirectory && *userUploadDirectory)
+ {
+ uploadDirectory = userUploadDirectory;
+ }
+ else
+ {
+ uploadDirectory= this->GetOption("CPACK_PACKAGE_DIRECTORY");
+ uploadDirectory += "/CPackUploads";
+ }
+ if(!cmSystemTools::FileExists(uploadDirectory.c_str()))
+ {
+ if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str()))
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Unable to create NSIS upload directory " << uploadDirectory
+ << std::endl);
+ return "";
+ }
+ }
+
+ // Remove the old archive, if one exists
+ std::string archiveFile = uploadDirectory + '/' + component->ArchiveFile;
+ cmCPackLogger(cmCPackLog::LOG_OUTPUT,
+ "- Building downloaded component archive: "
+ << archiveFile << std::endl);
+ if (cmSystemTools::FileExists(archiveFile.c_str(), true))
+ {
+ if (!cmSystemTools::RemoveFile(archiveFile.c_str()))
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Unable to remove archive file " << archiveFile
+ << std::endl);
+ return "";
+ }
+ }
+
+ // Find a ZIP program
+ if (!this->IsSet("ZIP_EXECUTABLE"))
+ {
+ this->ReadListFile("CPackZIP.cmake");
+
+ if (!this->IsSet("ZIP_EXECUTABLE"))
+ {
+ cmCPackLogger(cmCPackLog::LOG_ERROR,
+ "Unable to find ZIP program"
+ << std::endl);
+ return "";
+ }
+ }
+
+ // The directory where this component's files reside
+ std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+ dirName += '/';
+ dirName += component->Name;
+ dirName += '/';
+
+ // Build the list of files to go into this archive, and determine the
+ // size of the installed component.
+ std::string zipListFileName = this->GetOption("CPACK_TEMPORARY_DIRECTORY");
+ zipListFileName += "/winZip.filelist";
+ bool needQuotesInFile
+ = cmSystemTools::IsOn(this->GetOption("CPACK_ZIP_NEED_QUOTES"));
+ unsigned long totalSize = 0;
+ { // the scope is needed for cmGeneratedFileStream
+ cmGeneratedFileStream out(zipListFileName.c_str());
+ std::vector<std::string>::iterator fileIt;
+ for (fileIt = component->Files.begin();
+ fileIt != component->Files.end();
+ ++fileIt)
+ {
+ if ( needQuotesInFile )
+ {
+ out << "\"";
+ }
+ out << *fileIt;
+ if ( needQuotesInFile )
+ {
+ out << "\"";
+ }
+ out << std::endl;
+
+ totalSize += cmSystemTools::FileLength((dirName + *fileIt).c_str());
+ }
+ }
+
+ // Build the archive in the upload area
+ std::string cmd = this->GetOption("CPACK_ZIP_COMMAND");
+ cmsys::SystemTools::ReplaceString(cmd, "<ARCHIVE>", archiveFile.c_str());
+ cmsys::SystemTools::ReplaceString(cmd, "<FILELIST>",
+ zipListFileName.c_str());
+ std::string output;
+ int retVal = -1;
+ int res = cmSystemTools::RunSingleCommand(cmd.c_str(), &output, &retVal,
+ dirName.c_str(), false, 0);
+ if ( !res || retVal )
+ {
+ std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY");
+ tmpFile += "/CompressZip.log";
+ cmGeneratedFileStream ofs(tmpFile.c_str());
+ ofs << "# Run command: " << cmd.c_str() << std::endl
+ << "# Output:" << std::endl
+ << output.c_str() << std::endl;
+ cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running zip command: "
+ << cmd.c_str() << std::endl
+ << "Please check " << tmpFile.c_str() << " for errors" << std::endl);
+ return "";
+ }
+
+ // Create the NSIS code to download this file on-the-fly.
+ unsigned totalSizeInKbytes = (totalSize + 512) / 1024;
+ if (totalSizeInKbytes == 0)
+ {
+ totalSizeInKbytes = 1;
+ }
+ cmOStringStream out;
+ out << " AddSize " << totalSizeInKbytes << "\n"
+ << " Push \"" << component->ArchiveFile << "\"\n"
+ << " Call DownloadFile\n"
+ << " ZipDLL::extractall \"$INSTDIR\\"
+ << component->ArchiveFile << "\" \"$INSTDIR\"\n"
+ << " Pop $2 ; error message\n"
+ " StrCmp $2 \"success\" +2 0\n"
+ " MessageBox MB_OK \"Failed to unzip $2\"\n"
+ " Delete $INSTDIR\\$0\n";
+ componentCode += out.str();
+ }
+ else
+ {
+ componentCode += " File /r \"${INST_DIR}\\" + component->Name + "\\*.*\"\n";
+ }
+ componentCode += " noinstall_" + component->Name + ":\n";
componentCode += "SectionEnd\n";
// Macro used to remove the component
- componentCode += "!macro Remove_${" + component->Name + "}\n";
+ macrosOut << "!macro Remove_${" << component->Name << "}\n";
+ macrosOut << " IntCmp $" << component->Name << "_was_installed 0 noremove_"
+ << component->Name << "\n";
std::vector<std::string>::iterator pathIt;
for (pathIt = component->Files.begin();
pathIt != component->Files.end();
++pathIt)
{
- componentCode += " Delete \"$INSTDIR\\" + *pathIt + "\"\n";
+ macrosOut << " Delete \"$INSTDIR\\"
+ << cmSystemTools::ConvertToWindowsOutputPath(pathIt->c_str())
+ << "\"\n";
}
for (pathIt = component->Directories.begin();
pathIt != component->Directories.end();
++pathIt)
{
- componentCode += " RMDir \"$INSTDIR\\" + *pathIt + "\"\n";
+ macrosOut << " RMDir \"$INSTDIR\\"
+ << cmSystemTools::ConvertToWindowsOutputPath(pathIt->c_str())
+ << "\"\n";
}
- componentCode += "!macroend\n";
+ macrosOut << " noremove_" << component->Name << ":\n";
+ macrosOut << "!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";
+ macrosOut << "!macro Select_" << component->Name << "_depends\n";
+ macrosOut << CreateSelectionDependenciesDescription(component, visited);
+ macrosOut << "!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";
+ macrosOut << "!macro Deselect_required_by_" << component->Name << "\n";
+ macrosOut << CreateDeselectionDependenciesDescription(component, visited);
+ macrosOut << "!macroend\n";
return componentCode;
}
//----------------------------------------------------------------------
std::string cmCPackNSISGenerator::CreateSelectionDependenciesDescription
(cmCPackComponent *component,
- std::set<cmCPackComponent *>& visited) const
+ std::set<cmCPackComponent *>& visited)
{
// Don't visit a component twice
if (visited.count(component))
@@ -660,7 +849,7 @@ std::string cmCPackNSISGenerator::CreateSelectionDependenciesDescription
//----------------------------------------------------------------------
std::string cmCPackNSISGenerator::CreateDeselectionDependenciesDescription
(cmCPackComponent *component,
- std::set<cmCPackComponent *>& visited) const
+ std::set<cmCPackComponent *>& visited)
{
// Don't visit a component twice
if (visited.count(component))
@@ -692,9 +881,10 @@ std::string cmCPackNSISGenerator::CreateDeselectionDependenciesDescription
//----------------------------------------------------------------------
std::string
cmCPackNSISGenerator::
-CreateComponentGroupDescription(cmCPackComponentGroup *group) const
+CreateComponentGroupDescription(cmCPackComponentGroup *group,
+ cmOStringStream& macrosOut)
{
- if (group->Components.empty())
+ if (group->Components.empty() && group->Subgroups.empty())
{
// Silently skip empty groups. NSIS doesn't support them.
return std::string();
@@ -713,12 +903,25 @@ CreateComponentGroupDescription(cmCPackComponentGroup *group) const
{
code += "\"" + group->DisplayName + "\" " + group->Name + "\n";
}
+
+ std::vector<cmCPackComponentGroup*>::iterator groupIt;
+ for (groupIt = group->Subgroups.begin(); groupIt != group->Subgroups.end();
+ ++groupIt)
+ {
+ code += this->CreateComponentGroupDescription(*groupIt, macrosOut);
+ }
+
std::vector<cmCPackComponent*>::iterator comp;
for (comp = group->Components.begin();
comp != group->Components.end();
++comp)
{
- code += this->CreateComponentDescription(*comp);
+ if ((*comp)->Files.empty())
+ {
+ continue;
+ }
+
+ code += this->CreateComponentDescription(*comp, macrosOut);
}
code += "SectionGroupEnd\n";
return code;