diff options
Diffstat (limited to 'Source/CPack')
80 files changed, 15238 insertions, 0 deletions
diff --git a/Source/CPack/IFW/cmCPackIFWGenerator.cxx b/Source/CPack/IFW/cmCPackIFWGenerator.cxx new file mode 100644 index 0000000..4a5eb90 --- /dev/null +++ b/Source/CPack/IFW/cmCPackIFWGenerator.cxx @@ -0,0 +1,582 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackIFWGenerator.h" + +#include <CPack/cmCPackComponentGroup.h> +#include <CPack/cmCPackLog.h> + +#include <cmsys/Directory.hxx> +#include <cmsys/Glob.hxx> +#include <cmsys/RegularExpression.hxx> +#include <cmsys/SystemTools.hxx> + +#include <cmGeneratedFileStream.h> +#include <cmGlobalGenerator.h> +#include <cmMakefile.h> +#include <cmSystemTools.h> +#include <cmTimestamp.h> +#include <cmVersionConfig.h> +#include <cmXMLWriter.h> + +cmCPackIFWGenerator::cmCPackIFWGenerator() +{ +} + +cmCPackIFWGenerator::~cmCPackIFWGenerator() +{ +} + +bool cmCPackIFWGenerator::IsVersionLess(const char* version) +{ + return cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, + FrameworkVersion.data(), version); +} + +bool cmCPackIFWGenerator::IsVersionGreater(const char* version) +{ + return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER, + FrameworkVersion.data(), version); +} + +bool cmCPackIFWGenerator::IsVersionEqual(const char* version) +{ + return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL, + FrameworkVersion.data(), version); +} + +int cmCPackIFWGenerator::PackageFiles() +{ + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "- Configuration" << std::endl); + + // Installer configuragion + Installer.GenerateInstallerFile(); + + // Packages configuration + Installer.GeneratePackageFiles(); + + std::string ifwTLD = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + std::string ifwTmpFile = ifwTLD; + ifwTmpFile += "/IFWOutput.log"; + + // Run repogen + if (!Installer.RemoteRepositories.empty()) { + std::string ifwCmd = RepoGen; + + if (IsVersionLess("2.0.0")) { + ifwCmd += " -c " + this->toplevel + "/config/config.xml"; + } + + ifwCmd += " -p " + this->toplevel + "/packages"; + + if (!PkgsDirsVector.empty()) { + for (std::vector<std::string>::iterator it = PkgsDirsVector.begin(); + it != PkgsDirsVector.end(); ++it) { + ifwCmd += " -p " + *it; + } + } + + if (!OnlineOnly && !DownloadedPackages.empty()) { + ifwCmd += " -i "; + std::set<cmCPackIFWPackage*>::iterator it = DownloadedPackages.begin(); + ifwCmd += (*it)->Name; + ++it; + while (it != DownloadedPackages.end()) { + ifwCmd += "," + (*it)->Name; + ++it; + } + } + ifwCmd += " " + this->toplevel + "/repository"; + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << ifwCmd << std::endl); + std::string output; + int retVal = 1; + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "- Generate repository" + << std::endl); + bool res = cmSystemTools::RunSingleCommand(ifwCmd.c_str(), &output, + &output, &retVal, CM_NULLPTR, + this->GeneratorVerbose, 0); + if (!res || retVal) { + cmGeneratedFileStream ofs(ifwTmpFile.c_str()); + ofs << "# Run command: " << ifwCmd << std::endl + << "# Output:" << std::endl + << output << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running IFW command: " + << ifwCmd << std::endl + << "Please check " << ifwTmpFile << " for errors" + << std::endl); + return 0; + } + + if (!Repository.RepositoryUpdate.empty() && + !Repository.PatchUpdatesXml()) { + cmCPackLogger(cmCPackLog::LOG_WARNING, "Problem patch IFW \"Updates\" " + << "file: " << this->toplevel + "/repository/Updates.xml" + << std::endl); + } + + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "- repository: " + << this->toplevel << "/repository generated" << std::endl); + } + + // Run binary creator + { + std::string ifwCmd = BinCreator; + ifwCmd += " -c " + this->toplevel + "/config/config.xml"; + ifwCmd += " -p " + this->toplevel + "/packages"; + + if (!PkgsDirsVector.empty()) { + for (std::vector<std::string>::iterator it = PkgsDirsVector.begin(); + it != PkgsDirsVector.end(); ++it) { + ifwCmd += " -p " + *it; + } + } + + if (OnlineOnly) { + ifwCmd += " --online-only"; + } else if (!DownloadedPackages.empty() && + !Installer.RemoteRepositories.empty()) { + ifwCmd += " -e "; + std::set<cmCPackIFWPackage*>::iterator it = DownloadedPackages.begin(); + ifwCmd += (*it)->Name; + ++it; + while (it != DownloadedPackages.end()) { + ifwCmd += "," + (*it)->Name; + ++it; + } + } else if (!DependentPackages.empty()) { + ifwCmd += " -i "; + // Binary + std::set<cmCPackIFWPackage*>::iterator bit = BinaryPackages.begin(); + while (bit != BinaryPackages.end()) { + ifwCmd += (*bit)->Name + ","; + ++bit; + } + // Depend + DependenceMap::iterator it = DependentPackages.begin(); + ifwCmd += it->second.Name; + ++it; + while (it != DependentPackages.end()) { + ifwCmd += "," + it->second.Name; + ++it; + } + } + // TODO: set correct name for multipackages + if (!this->packageFileNames.empty()) { + ifwCmd += " " + packageFileNames[0]; + } else { + ifwCmd += " installer"; + } + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << ifwCmd << std::endl); + std::string output; + int retVal = 1; + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "- Generate package" << std::endl); + bool res = cmSystemTools::RunSingleCommand(ifwCmd.c_str(), &output, + &output, &retVal, CM_NULLPTR, + this->GeneratorVerbose, 0); + if (!res || retVal) { + cmGeneratedFileStream ofs(ifwTmpFile.c_str()); + ofs << "# Run command: " << ifwCmd << std::endl + << "# Output:" << std::endl + << output << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running IFW command: " + << ifwCmd << std::endl + << "Please check " << ifwTmpFile << " for errors" + << std::endl); + return 0; + } + } + + return 1; +} + +const char* cmCPackIFWGenerator::GetPackagingInstallPrefix() +{ + const char* defPrefix = cmCPackGenerator::GetPackagingInstallPrefix(); + + std::string tmpPref = defPrefix ? defPrefix : ""; + + if (this->Components.empty()) { + tmpPref += "packages/" + GetRootPackageName() + "/data"; + } + + this->SetOption("CPACK_IFW_PACKAGING_INSTALL_PREFIX", tmpPref.c_str()); + + return this->GetOption("CPACK_IFW_PACKAGING_INSTALL_PREFIX"); +} + +const char* cmCPackIFWGenerator::GetOutputExtension() +{ + return ExecutableSuffix.c_str(); +} + +int cmCPackIFWGenerator::InitializeInternal() +{ + // Search Qt Installer Framework tools + + const std::string BinCreatorOpt = "CPACK_IFW_BINARYCREATOR_EXECUTABLE"; + const std::string RepoGenOpt = "CPACK_IFW_REPOGEN_EXECUTABLE"; + const std::string FrameworkVersionOpt = "CPACK_IFW_FRAMEWORK_VERSION"; + + if (!this->IsSet(BinCreatorOpt) || !this->IsSet(RepoGenOpt) || + !this->IsSet(FrameworkVersionOpt)) { + this->ReadListFile("CPackIFW.cmake"); + } + + // Look 'binarycreator' executable (needs) + + const char* BinCreatorStr = this->GetOption(BinCreatorOpt); + if (!BinCreatorStr || cmSystemTools::IsNOTFOUND(BinCreatorStr)) { + BinCreator = ""; + } else { + BinCreator = BinCreatorStr; + } + + if (BinCreator.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find QtIFW compiler \"binarycreator\": " + "likely it is not installed, or not in your PATH" + << std::endl); + return 0; + } + + // Look 'repogen' executable (optional) + + const char* RepoGenStr = this->GetOption(RepoGenOpt); + if (!RepoGenStr || cmSystemTools::IsNOTFOUND(RepoGenStr)) { + RepoGen = ""; + } else { + RepoGen = RepoGenStr; + } + + // Framework version + if (const char* FrameworkVersionSrt = this->GetOption(FrameworkVersionOpt)) { + FrameworkVersion = FrameworkVersionSrt; + } else { + FrameworkVersion = "1.9.9"; + } + + // Variables that Change Behavior + + // Resolve duplicate names + ResolveDuplicateNames = this->IsOn("CPACK_IFW_RESOLVE_DUPLICATE_NAMES"); + + // Additional packages dirs + PkgsDirsVector.clear(); + if (const char* dirs = this->GetOption("CPACK_IFW_PACKAGES_DIRECTORIES")) { + cmSystemTools::ExpandListArgument(dirs, PkgsDirsVector); + } + + // Installer + Installer.Generator = this; + Installer.ConfigureFromOptions(); + + // Repository + Repository.Generator = this; + Repository.Name = "Unspecified"; + if (const char* site = this->GetOption("CPACK_DOWNLOAD_SITE")) { + Repository.Url = site; + Installer.RemoteRepositories.push_back(&Repository); + } + + // Repositories + if (const char* RepoAllStr = this->GetOption("CPACK_IFW_REPOSITORIES_ALL")) { + std::vector<std::string> RepoAllVector; + cmSystemTools::ExpandListArgument(RepoAllStr, RepoAllVector); + for (std::vector<std::string>::iterator rit = RepoAllVector.begin(); + rit != RepoAllVector.end(); ++rit) { + GetRepository(*rit); + } + } + + if (const char* ifwDownloadAll = this->GetOption("CPACK_IFW_DOWNLOAD_ALL")) { + OnlineOnly = cmSystemTools::IsOn(ifwDownloadAll); + } else if (const char* cpackDownloadAll = + this->GetOption("CPACK_DOWNLOAD_ALL")) { + OnlineOnly = cmSystemTools::IsOn(cpackDownloadAll); + } else { + OnlineOnly = false; + } + + if (!Installer.RemoteRepositories.empty() && RepoGen.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find QtIFW repository generator \"repogen\": " + "likely it is not installed, or not in your PATH" + << std::endl); + return 0; + } + + // Executable suffix + if (const char* optExeSuffix = this->GetOption("CMAKE_EXECUTABLE_SUFFIX")) { + ExecutableSuffix = optExeSuffix; + if (ExecutableSuffix.empty()) { + std::string sysName(this->GetOption("CMAKE_SYSTEM_NAME")); + if (sysName == "Linux") { + ExecutableSuffix = ".run"; + } + } + } else { + ExecutableSuffix = cmCPackGenerator::GetOutputExtension(); + } + + return this->Superclass::InitializeInternal(); +} + +std::string cmCPackIFWGenerator::GetComponentInstallDirNameSuffix( + const std::string& componentName) +{ + const std::string prefix = "packages/"; + const std::string suffix = "/data"; + + if (componentPackageMethod == ONE_PACKAGE) { + return std::string(prefix + GetRootPackageName() + suffix); + } + + return prefix + GetComponentPackageName(&Components[componentName]) + suffix; +} + +cmCPackComponent* cmCPackIFWGenerator::GetComponent( + const std::string& projectName, const std::string& componentName) +{ + ComponentsMap::iterator cit = Components.find(componentName); + if (cit != Components.end()) { + return &(cit->second); + } + + cmCPackComponent* component = + cmCPackGenerator::GetComponent(projectName, componentName); + if (!component) { + return component; + } + + std::string name = GetComponentPackageName(component); + PackagesMap::iterator pit = Packages.find(name); + if (pit != Packages.end()) { + return component; + } + + cmCPackIFWPackage* package = &Packages[name]; + package->Name = name; + package->Generator = this; + if (package->ConfigureFromComponent(component)) { + package->Installer = &Installer; + Installer.Packages.insert( + std::pair<std::string, cmCPackIFWPackage*>(name, package)); + ComponentPackages.insert( + std::pair<cmCPackComponent*, cmCPackIFWPackage*>(component, package)); + if (component->IsDownloaded) { + DownloadedPackages.insert(package); + } else { + BinaryPackages.insert(package); + } + } else { + Packages.erase(name); + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot configure package \"" + << name << "\" for component \"" << component->Name << "\"" + << std::endl); + } + + return component; +} + +cmCPackComponentGroup* cmCPackIFWGenerator::GetComponentGroup( + const std::string& projectName, const std::string& groupName) +{ + cmCPackComponentGroup* group = + cmCPackGenerator::GetComponentGroup(projectName, groupName); + if (!group) { + return group; + } + + std::string name = GetGroupPackageName(group); + PackagesMap::iterator pit = Packages.find(name); + if (pit != Packages.end()) { + return group; + } + + cmCPackIFWPackage* package = &Packages[name]; + package->Name = name; + package->Generator = this; + if (package->ConfigureFromGroup(group)) { + package->Installer = &Installer; + Installer.Packages.insert( + std::pair<std::string, cmCPackIFWPackage*>(name, package)); + GroupPackages.insert( + std::pair<cmCPackComponentGroup*, cmCPackIFWPackage*>(group, package)); + BinaryPackages.insert(package); + } else { + Packages.erase(name); + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot configure package \"" + << name << "\" for component group \"" << group->Name + << "\"" << std::endl); + } + return group; +} + +enum cmCPackGenerator::CPackSetDestdirSupport +cmCPackIFWGenerator::SupportsSetDestdir() const +{ + return cmCPackGenerator::SETDESTDIR_SHOULD_NOT_BE_USED; +} + +bool cmCPackIFWGenerator::SupportsAbsoluteDestination() const +{ + return false; +} + +bool cmCPackIFWGenerator::SupportsComponentInstallation() const +{ + return true; +} + +bool cmCPackIFWGenerator::IsOnePackage() const +{ + return componentPackageMethod == ONE_PACKAGE; +} + +std::string cmCPackIFWGenerator::GetRootPackageName() +{ + // Default value + std::string name = "root"; + if (const char* optIFW_PACKAGE_GROUP = + this->GetOption("CPACK_IFW_PACKAGE_GROUP")) { + // Configure from root group + cmCPackIFWPackage package; + package.Generator = this; + package.ConfigureFromGroup(optIFW_PACKAGE_GROUP); + name = package.Name; + } else if (const char* optIFW_PACKAGE_NAME = + this->GetOption("CPACK_IFW_PACKAGE_NAME")) { + // Configure from root package name + name = optIFW_PACKAGE_NAME; + } else if (const char* optPACKAGE_NAME = + this->GetOption("CPACK_PACKAGE_NAME")) { + // Configure from package name + name = optPACKAGE_NAME; + } + return name; +} + +std::string cmCPackIFWGenerator::GetGroupPackageName( + cmCPackComponentGroup* group) const +{ + std::string name; + if (!group) { + return name; + } + if (cmCPackIFWPackage* package = GetGroupPackage(group)) { + return package->Name; + } + const char* option = + GetOption("CPACK_IFW_COMPONENT_GROUP_" + + cmsys::SystemTools::UpperCase(group->Name) + "_NAME"); + name = option ? option : group->Name; + if (group->ParentGroup) { + cmCPackIFWPackage* package = GetGroupPackage(group->ParentGroup); + bool dot = !ResolveDuplicateNames; + if (dot && name.substr(0, package->Name.size()) == package->Name) { + dot = false; + } + if (dot) { + name = package->Name + "." + name; + } + } + return name; +} + +std::string cmCPackIFWGenerator::GetComponentPackageName( + cmCPackComponent* component) const +{ + std::string name; + if (!component) { + return name; + } + if (cmCPackIFWPackage* package = GetComponentPackage(component)) { + return package->Name; + } + std::string prefix = "CPACK_IFW_COMPONENT_" + + cmsys::SystemTools::UpperCase(component->Name) + "_"; + const char* option = GetOption(prefix + "NAME"); + name = option ? option : component->Name; + if (component->Group) { + cmCPackIFWPackage* package = GetGroupPackage(component->Group); + if ((componentPackageMethod == ONE_PACKAGE_PER_GROUP) || + IsOn(prefix + "COMMON")) { + return package->Name; + } + bool dot = !ResolveDuplicateNames; + if (dot && name.substr(0, package->Name.size()) == package->Name) { + dot = false; + } + if (dot) { + name = package->Name + "." + name; + } + } + return name; +} + +cmCPackIFWPackage* cmCPackIFWGenerator::GetGroupPackage( + cmCPackComponentGroup* group) const +{ + std::map<cmCPackComponentGroup*, cmCPackIFWPackage*>::const_iterator pit = + GroupPackages.find(group); + return pit != GroupPackages.end() ? pit->second : CM_NULLPTR; +} + +cmCPackIFWPackage* cmCPackIFWGenerator::GetComponentPackage( + cmCPackComponent* component) const +{ + std::map<cmCPackComponent*, cmCPackIFWPackage*>::const_iterator pit = + ComponentPackages.find(component); + return pit != ComponentPackages.end() ? pit->second : CM_NULLPTR; +} + +cmCPackIFWRepository* cmCPackIFWGenerator::GetRepository( + const std::string& repositoryName) +{ + RepositoriesMap::iterator rit = Repositories.find(repositoryName); + if (rit != Repositories.end()) { + return &(rit->second); + } + + cmCPackIFWRepository* repository = &Repositories[repositoryName]; + repository->Name = repositoryName; + repository->Generator = this; + if (repository->ConfigureFromOptions()) { + if (repository->Update == cmCPackIFWRepository::None) { + Installer.RemoteRepositories.push_back(repository); + } else { + Repository.RepositoryUpdate.push_back(repository); + } + } else { + Repositories.erase(repositoryName); + repository = CM_NULLPTR; + cmCPackLogger(cmCPackLog::LOG_WARNING, "Invalid repository \"" + << repositoryName << "\"" + << " configuration. Repository will be skipped." + << std::endl); + } + return repository; +} + +void cmCPackIFWGenerator::WriteGeneratedByToStrim(cmXMLWriter& xout) +{ + std::ostringstream comment; + comment << "Generated by CPack " << CMake_VERSION << " IFW generator " + << "for QtIFW "; + if (IsVersionLess("2.0")) { + comment << "less 2.0"; + } else { + comment << FrameworkVersion; + } + comment << " tools at " << cmTimestamp().CurrentTime("", true); + xout.Comment(comment.str().c_str()); +} diff --git a/Source/CPack/IFW/cmCPackIFWGenerator.h b/Source/CPack/IFW/cmCPackIFWGenerator.h new file mode 100644 index 0000000..12f2ca6 --- /dev/null +++ b/Source/CPack/IFW/cmCPackIFWGenerator.h @@ -0,0 +1,167 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackIFWGenerator_h +#define cmCPackIFWGenerator_h + +#include <CPack/cmCPackGenerator.h> + +#include "cmCPackIFWInstaller.h" +#include "cmCPackIFWPackage.h" +#include "cmCPackIFWRepository.h" + +class cmXMLWriter; + +/** \class cmCPackIFWGenerator + * \brief A generator for Qt Installer Framework tools + * + * http://qt-project.org/doc/qtinstallerframework/index.html + */ +class cmCPackIFWGenerator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackIFWGenerator, cmCPackGenerator); + + typedef std::map<std::string, cmCPackIFWPackage> PackagesMap; + typedef std::map<std::string, cmCPackIFWRepository> RepositoriesMap; + typedef std::map<std::string, cmCPackComponent> ComponentsMap; + typedef std::map<std::string, cmCPackComponentGroup> ComponentGoupsMap; + typedef std::map<std::string, cmCPackIFWPackage::DependenceStruct> + DependenceMap; + + /** + * Construct IFW generator + */ + cmCPackIFWGenerator(); + + /** + * Destruct IFW generator + */ + ~cmCPackIFWGenerator() CM_OVERRIDE; + + /** + * Compare \a version with QtIFW framework version + */ + bool IsVersionLess(const char* version); + + /** + * Compare \a version with QtIFW framework version + */ + bool IsVersionGreater(const char* version); + + /** + * Compare \a version with QtIFW framework version + */ + bool IsVersionEqual(const char* version); + +protected: + // cmCPackGenerator reimplementation + + /** + * @brief Initialize generator + * @return 0 on failure + */ + int InitializeInternal() CM_OVERRIDE; + int PackageFiles() CM_OVERRIDE; + const char* GetPackagingInstallPrefix() CM_OVERRIDE; + + /** + * @brief Extension of binary installer + * @return Executable suffix or value from default implementation + */ + const char* GetOutputExtension() CM_OVERRIDE; + + std::string GetComponentInstallDirNameSuffix( + const std::string& componentName) CM_OVERRIDE; + + /** + * @brief Get Component + * @param projectName Project name + * @param componentName Component name + * + * This method calls the base implementation. + * + * @return Pointer to component + */ + cmCPackComponent* GetComponent(const std::string& projectName, + const std::string& componentName) CM_OVERRIDE; + + /** + * @brief Get group of component + * @param projectName Project name + * @param groupName Component group name + * + * This method calls the base implementation. + * + * @return Pointer to component group + */ + cmCPackComponentGroup* GetComponentGroup( + const std::string& projectName, const std::string& groupName) CM_OVERRIDE; + + enum cmCPackGenerator::CPackSetDestdirSupport SupportsSetDestdir() const + CM_OVERRIDE; + bool SupportsAbsoluteDestination() const CM_OVERRIDE; + bool SupportsComponentInstallation() const CM_OVERRIDE; + +protected: + // Methods + + bool IsOnePackage() const; + + std::string GetRootPackageName(); + + std::string GetGroupPackageName(cmCPackComponentGroup* group) const; + std::string GetComponentPackageName(cmCPackComponent* component) const; + + cmCPackIFWPackage* GetGroupPackage(cmCPackComponentGroup* group) const; + cmCPackIFWPackage* GetComponentPackage(cmCPackComponent* component) const; + + cmCPackIFWRepository* GetRepository(const std::string& repositoryName); + + void WriteGeneratedByToStrim(cmXMLWriter& xout); + +protected: + // Data + + friend class cmCPackIFWPackage; + friend class cmCPackIFWInstaller; + friend class cmCPackIFWRepository; + + // Installer + cmCPackIFWInstaller Installer; + // Repository + cmCPackIFWRepository Repository; + // Collection of packages + PackagesMap Packages; + // Collection of repositories + RepositoriesMap Repositories; + // Collection of binary packages + std::set<cmCPackIFWPackage*> BinaryPackages; + // Collection of downloaded packages + std::set<cmCPackIFWPackage*> DownloadedPackages; + // Dependent packages + DependenceMap DependentPackages; + std::map<cmCPackComponent*, cmCPackIFWPackage*> ComponentPackages; + std::map<cmCPackComponentGroup*, cmCPackIFWPackage*> GroupPackages; + +private: + std::string RepoGen; + std::string BinCreator; + std::string FrameworkVersion; + std::string ExecutableSuffix; + + bool OnlineOnly; + bool ResolveDuplicateNames; + std::vector<std::string> PkgsDirsVector; +}; + +#endif diff --git a/Source/CPack/IFW/cmCPackIFWInstaller.cxx b/Source/CPack/IFW/cmCPackIFWInstaller.cxx new file mode 100644 index 0000000..13a3613 --- /dev/null +++ b/Source/CPack/IFW/cmCPackIFWInstaller.cxx @@ -0,0 +1,349 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackIFWInstaller.h" + +#include "cmCPackIFWGenerator.h" + +#include <CPack/cmCPackLog.h> + +#include <cmGeneratedFileStream.h> +#include <cmXMLWriter.h> + +#ifdef cmCPackLogger +#undef cmCPackLogger +#endif +#define cmCPackLogger(logType, msg) \ + do { \ + std::ostringstream cmCPackLog_msg; \ + cmCPackLog_msg << msg; \ + if (Generator) { \ + Generator->Logger->Log(logType, __FILE__, __LINE__, \ + cmCPackLog_msg.str().c_str()); \ + } \ + } while (0) + +cmCPackIFWInstaller::cmCPackIFWInstaller() + : Generator(CM_NULLPTR) +{ +} + +const char* cmCPackIFWInstaller::GetOption(const std::string& op) const +{ + return Generator ? Generator->GetOption(op) : CM_NULLPTR; +} + +bool cmCPackIFWInstaller::IsOn(const std::string& op) const +{ + return Generator ? Generator->IsOn(op) : false; +} + +bool cmCPackIFWInstaller::IsVersionLess(const char* version) +{ + return Generator ? Generator->IsVersionLess(version) : false; +} + +bool cmCPackIFWInstaller::IsVersionGreater(const char* version) +{ + return Generator ? Generator->IsVersionGreater(version) : false; +} + +bool cmCPackIFWInstaller::IsVersionEqual(const char* version) +{ + return Generator ? Generator->IsVersionEqual(version) : false; +} + +void cmCPackIFWInstaller::ConfigureFromOptions() +{ + // Name; + if (const char* optIFW_PACKAGE_NAME = + this->GetOption("CPACK_IFW_PACKAGE_NAME")) { + Name = optIFW_PACKAGE_NAME; + } else if (const char* optPACKAGE_NAME = + this->GetOption("CPACK_PACKAGE_NAME")) { + Name = optPACKAGE_NAME; + } else { + Name = "Your package"; + } + + // Title; + if (const char* optIFW_PACKAGE_TITLE = + GetOption("CPACK_IFW_PACKAGE_TITLE")) { + Title = optIFW_PACKAGE_TITLE; + } else if (const char* optPACKAGE_DESCRIPTION_SUMMARY = + GetOption("CPACK_PACKAGE_DESCRIPTION_SUMMARY")) { + Title = optPACKAGE_DESCRIPTION_SUMMARY; + } else { + Title = "Your package description"; + } + + // Version; + if (const char* option = GetOption("CPACK_PACKAGE_VERSION")) { + Version = option; + } else { + Version = "1.0.0"; + } + + // Publisher + if (const char* optIFW_PACKAGE_PUBLISHER = + GetOption("CPACK_IFW_PACKAGE_PUBLISHER")) { + Publisher = optIFW_PACKAGE_PUBLISHER; + } else if (const char* optPACKAGE_VENDOR = + GetOption("CPACK_PACKAGE_VENDOR")) { + Publisher = optPACKAGE_VENDOR; + } + + // ProductUrl + if (const char* option = GetOption("CPACK_IFW_PRODUCT_URL")) { + ProductUrl = option; + } + + // ApplicationIcon + if (const char* option = GetOption("CPACK_IFW_PACKAGE_ICON")) { + if (cmSystemTools::FileExists(option)) { + InstallerApplicationIcon = option; + } else { + // TODO: implement warning + } + } + + // WindowIcon + if (const char* option = GetOption("CPACK_IFW_PACKAGE_WINDOW_ICON")) { + if (cmSystemTools::FileExists(option)) { + InstallerWindowIcon = option; + } else { + // TODO: implement warning + } + } + + // Logo + if (const char* option = GetOption("CPACK_IFW_PACKAGE_LOGO")) { + if (cmSystemTools::FileExists(option)) { + Logo = option; + } else { + // TODO: implement warning + } + } + + // Start menu + if (const char* optIFW_START_MENU_DIR = + this->GetOption("CPACK_IFW_PACKAGE_START_MENU_DIRECTORY")) { + StartMenuDir = optIFW_START_MENU_DIR; + } else { + StartMenuDir = Name; + } + + // Default target directory for installation + if (const char* optIFW_TARGET_DIRECTORY = + GetOption("CPACK_IFW_TARGET_DIRECTORY")) { + TargetDir = optIFW_TARGET_DIRECTORY; + } else if (const char* optPACKAGE_INSTALL_DIRECTORY = + GetOption("CPACK_PACKAGE_INSTALL_DIRECTORY")) { + TargetDir = "@ApplicationsDir@/"; + TargetDir += optPACKAGE_INSTALL_DIRECTORY; + } else { + TargetDir = "@RootDir@/usr/local"; + } + + // Default target directory for installation with administrator rights + if (const char* option = GetOption("CPACK_IFW_ADMIN_TARGET_DIRECTORY")) { + AdminTargetDir = option; + } + + // Maintenance tool + if (const char* optIFW_MAINTENANCE_TOOL = + this->GetOption("CPACK_IFW_PACKAGE_MAINTENANCE_TOOL_NAME")) { + MaintenanceToolName = optIFW_MAINTENANCE_TOOL; + } + + // Maintenance tool ini file + if (const char* optIFW_MAINTENANCE_TOOL_INI = + this->GetOption("CPACK_IFW_PACKAGE_MAINTENANCE_TOOL_INI_FILE")) { + MaintenanceToolIniFile = optIFW_MAINTENANCE_TOOL_INI; + } + + // Allow non-ASCII characters + if (this->GetOption("CPACK_IFW_PACKAGE_ALLOW_NON_ASCII_CHARACTERS")) { + if (IsOn("CPACK_IFW_PACKAGE_ALLOW_NON_ASCII_CHARACTERS")) { + AllowNonAsciiCharacters = "true"; + } else { + AllowNonAsciiCharacters = "false"; + } + } + + // Space in path + if (this->GetOption("CPACK_IFW_PACKAGE_ALLOW_SPACE_IN_PATH")) { + if (IsOn("CPACK_IFW_PACKAGE_ALLOW_SPACE_IN_PATH")) { + AllowSpaceInPath = "true"; + } else { + AllowSpaceInPath = "false"; + } + } + + // Control script + if (const char* optIFW_CONTROL_SCRIPT = + this->GetOption("CPACK_IFW_PACKAGE_CONTROL_SCRIPT")) { + ControlScript = optIFW_CONTROL_SCRIPT; + } +} + +void cmCPackIFWInstaller::GenerateInstallerFile() +{ + // Lazy directory initialization + if (Directory.empty() && Generator) { + Directory = Generator->toplevel; + } + + // Output stream + cmGeneratedFileStream fout((Directory + "/config/config.xml").data()); + cmXMLWriter xout(fout); + + xout.StartDocument(); + + WriteGeneratedByToStrim(xout); + + xout.StartElement("Installer"); + + xout.Element("Name", Name); + xout.Element("Version", Version); + xout.Element("Title", Title); + + if (!Publisher.empty()) { + xout.Element("Publisher", Publisher); + } + + if (!ProductUrl.empty()) { + xout.Element("ProductUrl", ProductUrl); + } + + // ApplicationIcon + if (!InstallerApplicationIcon.empty()) { + std::string name = + cmSystemTools::GetFilenameName(InstallerApplicationIcon); + std::string path = Directory + "/config/" + name; + name = cmSystemTools::GetFilenameWithoutExtension(name); + cmsys::SystemTools::CopyFileIfDifferent(InstallerApplicationIcon.data(), + path.data()); + xout.Element("InstallerApplicationIcon", name); + } + + // WindowIcon + if (!InstallerWindowIcon.empty()) { + std::string name = cmSystemTools::GetFilenameName(InstallerWindowIcon); + std::string path = Directory + "/config/" + name; + cmsys::SystemTools::CopyFileIfDifferent(InstallerWindowIcon.data(), + path.data()); + xout.Element("InstallerWindowIcon", name); + } + + // Logo + if (!Logo.empty()) { + std::string name = cmSystemTools::GetFilenameName(Logo); + std::string path = Directory + "/config/" + name; + cmsys::SystemTools::CopyFileIfDifferent(Logo.data(), path.data()); + xout.Element("Logo", name); + } + + // Start menu + if (!IsVersionLess("2.0")) { + xout.Element("StartMenuDir", StartMenuDir); + } + + // Target dir + if (!TargetDir.empty()) { + xout.Element("TargetDir", TargetDir); + } + + // Admin target dir + if (!AdminTargetDir.empty()) { + xout.Element("AdminTargetDir", AdminTargetDir); + } + + // Remote repositories + if (!RemoteRepositories.empty()) { + xout.StartElement("RemoteRepositories"); + for (RepositoriesVector::iterator rit = RemoteRepositories.begin(); + rit != RemoteRepositories.end(); ++rit) { + (*rit)->WriteRepositoryConfig(xout); + } + xout.EndElement(); + } + + // Maintenance tool + if (!IsVersionLess("2.0") && !MaintenanceToolName.empty()) { + xout.Element("MaintenanceToolName", MaintenanceToolName); + } + + // Maintenance tool ini file + if (!IsVersionLess("2.0") && !MaintenanceToolIniFile.empty()) { + xout.Element("MaintenanceToolIniFile", MaintenanceToolIniFile); + } + + // Different allows + if (IsVersionLess("2.0")) { + // CPack IFW default policy + xout.Comment("CPack IFW default policy for QtIFW less 2.0"); + xout.Element("AllowNonAsciiCharacters", "true"); + xout.Element("AllowSpaceInPath", "true"); + } else { + if (!AllowNonAsciiCharacters.empty()) { + xout.Element("AllowNonAsciiCharacters", AllowNonAsciiCharacters); + } + if (!AllowSpaceInPath.empty()) { + xout.Element("AllowSpaceInPath", AllowSpaceInPath); + } + } + + // Control script (copy to config dir) + if (!IsVersionLess("2.0") && !ControlScript.empty()) { + std::string name = cmSystemTools::GetFilenameName(ControlScript); + std::string path = Directory + "/config/" + name; + cmsys::SystemTools::CopyFileIfDifferent(ControlScript.data(), path.data()); + xout.Element("ControlScript", name); + } + + xout.EndElement(); + xout.EndDocument(); +} + +void cmCPackIFWInstaller::GeneratePackageFiles() +{ + if (Packages.empty() || Generator->IsOnePackage()) { + // Generate default package + cmCPackIFWPackage package; + package.Generator = Generator; + package.Installer = this; + // Check package group + if (const char* option = GetOption("CPACK_IFW_PACKAGE_GROUP")) { + package.ConfigureFromGroup(option); + package.ForcedInstallation = "true"; + } else { + package.ConfigureFromOptions(); + } + package.GeneratePackageFile(); + return; + } + + // Generate packages meta information + for (PackagesMap::iterator pit = Packages.begin(); pit != Packages.end(); + ++pit) { + cmCPackIFWPackage* package = pit->second; + package->GeneratePackageFile(); + } +} + +void cmCPackIFWInstaller::WriteGeneratedByToStrim(cmXMLWriter& xout) +{ + if (Generator) { + Generator->WriteGeneratedByToStrim(xout); + } +} diff --git a/Source/CPack/IFW/cmCPackIFWInstaller.h b/Source/CPack/IFW/cmCPackIFWInstaller.h new file mode 100644 index 0000000..3170116 --- /dev/null +++ b/Source/CPack/IFW/cmCPackIFWInstaller.h @@ -0,0 +1,118 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackIFWInstaller_h +#define cmCPackIFWInstaller_h + +#include <cmStandardIncludes.h> + +class cmCPackIFWPackage; +class cmCPackIFWGenerator; +class cmCPackIFWRepository; +class cmXMLWriter; + +/** \class cmCPackIFWInstaller + * \brief A binary installer to be created CPack IFW generator + */ +class cmCPackIFWInstaller +{ +public: + // Types + + typedef std::map<std::string, cmCPackIFWPackage*> PackagesMap; + typedef std::vector<cmCPackIFWRepository*> RepositoriesVector; + +public: + // Constructor + + /** + * Construct installer + */ + cmCPackIFWInstaller(); + +public: + // Configuration + + /// Name of the product being installed + std::string Name; + + /// Version number of the product being installed + std::string Version; + + /// Name of the installer as displayed on the title bar + std::string Title; + + /// Publisher of the software (as shown in the Windows Control Panel) + std::string Publisher; + + /// URL to a page that contains product information on your web site + std::string ProductUrl; + + /// Filename for a custom installer icon + std::string InstallerApplicationIcon; + + /// Filename for a custom window icon + std::string InstallerWindowIcon; + + /// Filename for a logo + std::string Logo; + + /// Name of the default program group in the Windows Start menu + std::string StartMenuDir; + + /// Default target directory for installation + std::string TargetDir; + + /// Default target directory for installation with administrator rights + std::string AdminTargetDir; + + /// Filename of the generated maintenance tool + std::string MaintenanceToolName; + + /// Filename for the configuration of the generated maintenance tool + std::string MaintenanceToolIniFile; + + /// Set to true if the installation path can contain non-ASCII characters + std::string AllowNonAsciiCharacters; + + /// Set to false if the installation path cannot contain space characters + std::string AllowSpaceInPath; + + /// Filename for a custom installer control script + std::string ControlScript; + +public: + // Internal implementation + + const char* GetOption(const std::string& op) const; + bool IsOn(const std::string& op) const; + + bool IsVersionLess(const char* version); + bool IsVersionGreater(const char* version); + bool IsVersionEqual(const char* version); + + void ConfigureFromOptions(); + + void GenerateInstallerFile(); + + void GeneratePackageFiles(); + + cmCPackIFWGenerator* Generator; + PackagesMap Packages; + RepositoriesVector RemoteRepositories; + std::string Directory; + +protected: + void WriteGeneratedByToStrim(cmXMLWriter& xout); +}; + +#endif // cmCPackIFWInstaller_h diff --git a/Source/CPack/IFW/cmCPackIFWPackage.cxx b/Source/CPack/IFW/cmCPackIFWPackage.cxx new file mode 100644 index 0000000..5db06e6 --- /dev/null +++ b/Source/CPack/IFW/cmCPackIFWPackage.cxx @@ -0,0 +1,484 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackIFWPackage.h" + +#include "cmCPackIFWGenerator.h" + +#include <CPack/cmCPackLog.h> + +#include <cmGeneratedFileStream.h> +#include <cmTimestamp.h> +#include <cmXMLWriter.h> + +//----------------------------------------------------------------- Logger --- +#ifdef cmCPackLogger +#undef cmCPackLogger +#endif +#define cmCPackLogger(logType, msg) \ + do { \ + std::ostringstream cmCPackLog_msg; \ + cmCPackLog_msg << msg; \ + if (Generator) { \ + Generator->Logger->Log(logType, __FILE__, __LINE__, \ + cmCPackLog_msg.str().c_str()); \ + } \ + } while (0) + +//---------------------------------------------------------- CompareStruct --- +cmCPackIFWPackage::CompareStruct::CompareStruct() + : Type(CompareNone) +{ +} + +//------------------------------------------------------- DependenceStruct --- +cmCPackIFWPackage::DependenceStruct::DependenceStruct() +{ +} + +cmCPackIFWPackage::DependenceStruct::DependenceStruct( + const std::string& dependence) +{ + // Search compare section + size_t pos = std::string::npos; + if ((pos = dependence.find("<=")) != std::string::npos) { + Compare.Type = CompareLessOrEqual; + Compare.Value = dependence.substr(pos + 2); + } else if ((pos = dependence.find(">=")) != std::string::npos) { + Compare.Type = CompareGreaterOrEqual; + Compare.Value = dependence.substr(pos + 2); + } else if ((pos = dependence.find('<')) != std::string::npos) { + Compare.Type = CompareLess; + Compare.Value = dependence.substr(pos + 1); + } else if ((pos = dependence.find('=')) != std::string::npos) { + Compare.Type = CompareEqual; + Compare.Value = dependence.substr(pos + 1); + } else if ((pos = dependence.find('>')) != std::string::npos) { + Compare.Type = CompareGreater; + Compare.Value = dependence.substr(pos + 1); + } + Name = pos == std::string::npos ? dependence : dependence.substr(0, pos); +} + +std::string cmCPackIFWPackage::DependenceStruct::NameWithCompare() const +{ + if (Compare.Type == CompareNone) { + return Name; + } + + std::string result = Name; + + if (Compare.Type == CompareLessOrEqual) { + result += "<="; + } else if (Compare.Type == CompareGreaterOrEqual) { + result += ">="; + } else if (Compare.Type == CompareLess) { + result += "<"; + } else if (Compare.Type == CompareEqual) { + result += "="; + } else if (Compare.Type == CompareGreater) { + result += ">"; + } + + result += Compare.Value; + + return result; +} + +//------------------------------------------------------ cmCPackIFWPackage --- +cmCPackIFWPackage::cmCPackIFWPackage() + : Generator(CM_NULLPTR) + , Installer(CM_NULLPTR) +{ +} + +const char* cmCPackIFWPackage::GetOption(const std::string& op) const +{ + const char* option = Generator ? Generator->GetOption(op) : CM_NULLPTR; + return option && *option ? option : CM_NULLPTR; +} + +bool cmCPackIFWPackage::IsOn(const std::string& op) const +{ + return Generator ? Generator->IsOn(op) : false; +} + +bool cmCPackIFWPackage::IsVersionLess(const char* version) +{ + return Generator ? Generator->IsVersionLess(version) : false; +} + +bool cmCPackIFWPackage::IsVersionGreater(const char* version) +{ + return Generator ? Generator->IsVersionGreater(version) : false; +} + +bool cmCPackIFWPackage::IsVersionEqual(const char* version) +{ + return Generator ? Generator->IsVersionEqual(version) : false; +} + +std::string cmCPackIFWPackage::GetComponentName(cmCPackComponent* component) +{ + if (!component) { + return ""; + } + const char* option = + GetOption("CPACK_IFW_COMPONENT_" + + cmsys::SystemTools::UpperCase(component->Name) + "_NAME"); + return option ? option : component->Name; +} + +void cmCPackIFWPackage::DefaultConfiguration() +{ + DisplayName = ""; + Description = ""; + Version = ""; + ReleaseDate = ""; + Script = ""; + Licenses.clear(); + SortingPriority = ""; + Default = ""; + Essential = ""; + Virtual = ""; + ForcedInstallation = ""; +} + +// Defaul configuration (all in one package) +int cmCPackIFWPackage::ConfigureFromOptions() +{ + // Restore defaul configuration + DefaultConfiguration(); + + // Name + Name = Generator->GetRootPackageName(); + + // Display name + if (const char* option = this->GetOption("CPACK_PACKAGE_NAME")) { + DisplayName = option; + } else { + DisplayName = "Your package"; + } + + // Description + if (const char* option = + this->GetOption("CPACK_PACKAGE_DESCRIPTION_SUMMARY")) { + Description = option; + } else { + Description = "Your package description"; + } + + // Version + if (const char* option = GetOption("CPACK_PACKAGE_VERSION")) { + Version = option; + } else { + Version = "1.0.0"; + } + + ForcedInstallation = "true"; + + return 1; +} + +int cmCPackIFWPackage::ConfigureFromComponent(cmCPackComponent* component) +{ + if (!component) { + return 0; + } + + // Restore defaul configuration + DefaultConfiguration(); + + std::string prefix = "CPACK_IFW_COMPONENT_" + + cmsys::SystemTools::UpperCase(component->Name) + "_"; + + // Display name + DisplayName = component->DisplayName; + + // Description + Description = component->Description; + + // Version + if (const char* optVERSION = GetOption(prefix + "VERSION")) { + Version = optVERSION; + } else if (const char* optPACKAGE_VERSION = + GetOption("CPACK_PACKAGE_VERSION")) { + Version = optPACKAGE_VERSION; + } else { + Version = "1.0.0"; + } + + // Script + if (const char* option = GetOption(prefix + "SCRIPT")) { + Script = option; + } + + // CMake dependencies + if (!component->Dependencies.empty()) { + std::vector<cmCPackComponent*>::iterator dit; + for (dit = component->Dependencies.begin(); + dit != component->Dependencies.end(); ++dit) { + Dependencies.insert(Generator->ComponentPackages[*dit]); + } + } + + // QtIFW dependencies + if (const char* option = this->GetOption(prefix + "DEPENDS")) { + std::vector<std::string> deps; + cmSystemTools::ExpandListArgument(option, deps); + for (std::vector<std::string>::iterator dit = deps.begin(); + dit != deps.end(); ++dit) { + DependenceStruct dep(*dit); + if (!Generator->Packages.count(dep.Name)) { + bool hasDep = Generator->DependentPackages.count(dep.Name) > 0; + DependenceStruct& depRef = Generator->DependentPackages[dep.Name]; + if (!hasDep) { + depRef = dep; + } + AlienDependencies.insert(&depRef); + } + } + } + + // Licenses + if (const char* option = this->GetOption(prefix + "LICENSES")) { + Licenses.clear(); + cmSystemTools::ExpandListArgument(option, Licenses); + if (Licenses.size() % 2 != 0) { + cmCPackLogger( + cmCPackLog::LOG_WARNING, prefix + << "LICENSES" + << " should contain pairs of <display_name> and <file_path>." + << std::endl); + Licenses.clear(); + } + } + + // Priority + if (const char* option = this->GetOption(prefix + "PRIORITY")) { + SortingPriority = option; + } + + // Default + Default = component->IsDisabledByDefault ? "false" : "true"; + + // Essential + if (this->IsOn(prefix + "ESSENTIAL")) { + Essential = "true"; + } + + // Virtual + Virtual = component->IsHidden ? "true" : ""; + + // ForcedInstallation + ForcedInstallation = component->IsRequired ? "true" : "false"; + + return 1; +} + +int cmCPackIFWPackage::ConfigureFromGroup(cmCPackComponentGroup* group) +{ + if (!group) { + return 0; + } + + // Restore defaul configuration + DefaultConfiguration(); + + std::string prefix = "CPACK_IFW_COMPONENT_GROUP_" + + cmsys::SystemTools::UpperCase(group->Name) + "_"; + + DisplayName = group->DisplayName; + Description = group->Description; + + // Version + if (const char* optVERSION = GetOption(prefix + "VERSION")) { + Version = optVERSION; + } else if (const char* optPACKAGE_VERSION = + GetOption("CPACK_PACKAGE_VERSION")) { + Version = optPACKAGE_VERSION; + } else { + Version = "1.0.0"; + } + + // Script + if (const char* option = GetOption(prefix + "SCRIPT")) { + Script = option; + } + + // Licenses + if (const char* option = this->GetOption(prefix + "LICENSES")) { + Licenses.clear(); + cmSystemTools::ExpandListArgument(option, Licenses); + if (Licenses.size() % 2 != 0) { + cmCPackLogger( + cmCPackLog::LOG_WARNING, prefix + << "LICENSES" + << " should contain pairs of <display_name> and <file_path>." + << std::endl); + Licenses.clear(); + } + } + + // Priority + if (const char* option = this->GetOption(prefix + "PRIORITY")) { + SortingPriority = option; + } + + return 1; +} + +int cmCPackIFWPackage::ConfigureFromGroup(const std::string& groupName) +{ + // Group configuration + + cmCPackComponentGroup group; + std::string prefix = + "CPACK_COMPONENT_GROUP_" + cmsys::SystemTools::UpperCase(groupName) + "_"; + + if (const char* option = GetOption(prefix + "DISPLAY_NAME")) { + group.DisplayName = option; + } else { + group.DisplayName = group.Name; + } + + if (const char* option = GetOption(prefix + "DESCRIPTION")) { + group.Description = option; + } + group.IsBold = IsOn(prefix + "BOLD_TITLE"); + group.IsExpandedByDefault = IsOn(prefix + "EXPANDED"); + + // Package configuration + + group.Name = groupName; + + if (Generator) { + Name = Generator->GetGroupPackageName(&group); + } else { + Name = group.Name; + } + + return ConfigureFromGroup(&group); +} + +void cmCPackIFWPackage::GeneratePackageFile() +{ + // Lazy directory initialization + if (Directory.empty()) { + if (Installer) { + Directory = Installer->Directory + "/packages/" + Name; + } else if (Generator) { + Directory = Generator->toplevel + "/packages/" + Name; + } + } + + // Output stream + cmGeneratedFileStream fout((Directory + "/meta/package.xml").data()); + cmXMLWriter xout(fout); + + xout.StartDocument(); + + WriteGeneratedByToStrim(xout); + + xout.StartElement("Package"); + + xout.Element("DisplayName", DisplayName); + xout.Element("Description", Description); + xout.Element("Name", Name); + xout.Element("Version", Version); + + if (!ReleaseDate.empty()) { + xout.Element("ReleaseDate", ReleaseDate); + } else { + xout.Element("ReleaseDate", cmTimestamp().CurrentTime("%Y-%m-%d", true)); + } + + // Script (copy to meta dir) + if (!Script.empty()) { + std::string name = cmSystemTools::GetFilenameName(Script); + std::string path = Directory + "/meta/" + name; + cmsys::SystemTools::CopyFileIfDifferent(Script.data(), path.data()); + xout.Element("Script", name); + } + + // Dependencies + std::set<DependenceStruct> compDepSet; + for (std::set<DependenceStruct*>::iterator ait = AlienDependencies.begin(); + ait != AlienDependencies.end(); ++ait) { + compDepSet.insert(*(*ait)); + } + for (std::set<cmCPackIFWPackage*>::iterator it = Dependencies.begin(); + it != Dependencies.end(); ++it) { + compDepSet.insert(DependenceStruct((*it)->Name)); + } + // Write dependencies + if (!compDepSet.empty()) { + std::ostringstream dependencies; + std::set<DependenceStruct>::iterator it = compDepSet.begin(); + dependencies << it->NameWithCompare(); + ++it; + while (it != compDepSet.end()) { + dependencies << "," << it->NameWithCompare(); + ++it; + } + xout.Element("Dependencies", dependencies.str()); + } + + // Licenses (copy to meta dir) + std::vector<std::string> licenses = Licenses; + for (size_t i = 1; i < licenses.size(); i += 2) { + std::string name = cmSystemTools::GetFilenameName(licenses[i]); + std::string path = Directory + "/meta/" + name; + cmsys::SystemTools::CopyFileIfDifferent(licenses[i].data(), path.data()); + licenses[i] = name; + } + if (!licenses.empty()) { + xout.StartElement("Licenses"); + for (size_t i = 0; i < licenses.size(); i += 2) { + xout.StartElement("License"); + xout.Attribute("name", licenses[i]); + xout.Attribute("file", licenses[i + 1]); + xout.EndElement(); + } + xout.EndElement(); + } + + if (!ForcedInstallation.empty()) { + xout.Element("ForcedInstallation", ForcedInstallation); + } + + if (!Virtual.empty()) { + xout.Element("Virtual", Virtual); + } else if (!Default.empty()) { + xout.Element("Default", Default); + } + + // Essential + if (!Essential.empty()) { + xout.Element("Essential", Essential); + } + + // Priority + if (!SortingPriority.empty()) { + xout.Element("SortingPriority", SortingPriority); + } + + xout.EndElement(); + xout.EndDocument(); +} + +void cmCPackIFWPackage::WriteGeneratedByToStrim(cmXMLWriter& xout) +{ + if (Generator) { + Generator->WriteGeneratedByToStrim(xout); + } +} diff --git a/Source/CPack/IFW/cmCPackIFWPackage.h b/Source/CPack/IFW/cmCPackIFWPackage.h new file mode 100644 index 0000000..55b07ec --- /dev/null +++ b/Source/CPack/IFW/cmCPackIFWPackage.h @@ -0,0 +1,149 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackIFWPackage_h +#define cmCPackIFWPackage_h + +#include <cmStandardIncludes.h> + +class cmCPackComponent; +class cmCPackComponentGroup; +class cmCPackIFWInstaller; +class cmCPackIFWGenerator; +class cmXMLWriter; + +/** \class cmCPackIFWPackage + * \brief A single component to be installed by CPack IFW generator + */ +class cmCPackIFWPackage +{ +public: + // Types + + enum CompareTypes + { + CompareNone = 0x0, + CompareEqual = 0x1, + CompareLess = 0x2, + CompareLessOrEqual = 0x3, + CompareGreater = 0x4, + CompareGreaterOrEqual = 0x5 + }; + + struct CompareStruct + { + CompareStruct(); + + unsigned int Type; + std::string Value; + }; + + struct DependenceStruct + { + DependenceStruct(); + DependenceStruct(const std::string& dependence); + + std::string Name; + CompareStruct Compare; + + std::string NameWithCompare() const; + + bool operator<(const DependenceStruct& other) const + { + return Name < other.Name; + } + }; + +public: + // [Con|De]structor + + /** + * Construct package + */ + cmCPackIFWPackage(); + +public: + // Configuration + + /// Human-readable name of the component + std::string DisplayName; + + /// Human-readable description of the component + std::string Description; + + /// Version number of the component + std::string Version; + + /// Date when this component version was released + std::string ReleaseDate; + + /// Domain-like identification for this component + std::string Name; + + /// File name of a script being loaded + std::string Script; + + /// List of license agreements to be accepted by the installing user + std::vector<std::string> Licenses; + + /// Priority of the component in the tree + std::string SortingPriority; + + /// Set to true to preselect the component in the installer + std::string Default; + + /// Marks the package as essential to force a restart of the MaintenanceTool + std::string Essential; + + /// Set to true to hide the component from the installer + std::string Virtual; + + /// Determines that the package must always be installed + std::string ForcedInstallation; + +public: + // Internal implementation + + const char* GetOption(const std::string& op) const; + bool IsOn(const std::string& op) const; + + bool IsVersionLess(const char* version); + bool IsVersionGreater(const char* version); + bool IsVersionEqual(const char* version); + + std::string GetComponentName(cmCPackComponent* component); + + void DefaultConfiguration(); + + int ConfigureFromOptions(); + int ConfigureFromComponent(cmCPackComponent* component); + int ConfigureFromGroup(cmCPackComponentGroup* group); + int ConfigureFromGroup(const std::string& groupName); + + void GeneratePackageFile(); + + // Pointer to generator + cmCPackIFWGenerator* Generator; + // Pointer to installer + cmCPackIFWInstaller* Installer; + // Collection of dependencies + std::set<cmCPackIFWPackage*> Dependencies; + // Collection of unresolved dependencies + std::set<DependenceStruct*> AlienDependencies; + // Patch to package directory + std::string Directory; + +protected: + void WriteGeneratedByToStrim(cmXMLWriter& xout); +}; + +#endif // cmCPackIFWPackage_h diff --git a/Source/CPack/IFW/cmCPackIFWRepository.cxx b/Source/CPack/IFW/cmCPackIFWRepository.cxx new file mode 100644 index 0000000..fcb1c77 --- /dev/null +++ b/Source/CPack/IFW/cmCPackIFWRepository.cxx @@ -0,0 +1,342 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackIFWRepository.h" + +#include "cmCPackIFWGenerator.h" + +#include <CPack/cmCPackLog.h> + +#include <cmGeneratedFileStream.h> +#include <cmXMLParser.h> +#include <cmXMLWriter.h> + +#ifdef cmCPackLogger +#undef cmCPackLogger +#endif +#define cmCPackLogger(logType, msg) \ + do { \ + std::ostringstream cmCPackLog_msg; \ + cmCPackLog_msg << msg; \ + if (Generator) { \ + Generator->Logger->Log(logType, __FILE__, __LINE__, \ + cmCPackLog_msg.str().c_str()); \ + } \ + } while (0) + +cmCPackIFWRepository::cmCPackIFWRepository() + : Update(None) + , Generator(CM_NULLPTR) +{ +} + +bool cmCPackIFWRepository::IsValid() const +{ + bool valid = true; + + switch (Update) { + case None: + valid = !Url.empty(); + break; + case Add: + valid = !Url.empty(); + break; + case Remove: + valid = !Url.empty(); + break; + case Replace: + valid = !OldUrl.empty() && !NewUrl.empty(); + break; + } + + return valid; +} + +const char* cmCPackIFWRepository::GetOption(const std::string& op) const +{ + return Generator ? Generator->GetOption(op) : CM_NULLPTR; +} + +bool cmCPackIFWRepository::IsOn(const std::string& op) const +{ + return Generator ? Generator->IsOn(op) : false; +} + +bool cmCPackIFWRepository::IsVersionLess(const char* version) +{ + return Generator ? Generator->IsVersionLess(version) : false; +} + +bool cmCPackIFWRepository::IsVersionGreater(const char* version) +{ + return Generator ? Generator->IsVersionGreater(version) : false; +} + +bool cmCPackIFWRepository::IsVersionEqual(const char* version) +{ + return Generator ? Generator->IsVersionEqual(version) : false; +} + +bool cmCPackIFWRepository::ConfigureFromOptions() +{ + // Name; + if (Name.empty()) { + return false; + } + + std::string prefix = + "CPACK_IFW_REPOSITORY_" + cmsys::SystemTools::UpperCase(Name) + "_"; + + // Update + if (IsOn(prefix + "ADD")) { + Update = Add; + } else if (IsOn(prefix + "REMOVE")) { + Update = Remove; + } else if (IsOn(prefix + "REPLACE")) { + Update = Replace; + } else { + Update = None; + } + + // Url + if (const char* url = GetOption(prefix + "URL")) { + Url = url; + } else { + Url = ""; + } + + // Old url + if (const char* oldUrl = GetOption(prefix + "OLD_URL")) { + OldUrl = oldUrl; + } else { + OldUrl = ""; + } + + // New url + if (const char* newUrl = GetOption(prefix + "NEW_URL")) { + NewUrl = newUrl; + } else { + NewUrl = ""; + } + + // Enabled + if (IsOn(prefix + "DISABLED")) { + Enabled = "0"; + } else { + Enabled = ""; + } + + // Username + if (const char* username = GetOption(prefix + "USERNAME")) { + Username = username; + } else { + Username = ""; + } + + // Password + if (const char* password = GetOption(prefix + "PASSWORD")) { + Password = password; + } else { + Password = ""; + } + + // DisplayName + if (const char* displayName = GetOption(prefix + "DISPLAY_NAME")) { + DisplayName = displayName; + } else { + DisplayName = ""; + } + + return IsValid(); +} + +/** \class cmCPackeIFWUpdatesPatcher + * \brief Helper class that parses and patch Updates.xml file (QtIFW) + */ +class cmCPackeIFWUpdatesPatcher : public cmXMLParser +{ +public: + cmCPackeIFWUpdatesPatcher(cmCPackIFWRepository* r, cmXMLWriter& x) + : repository(r) + , xout(x) + , patched(false) + { + } + + cmCPackIFWRepository* repository; + cmXMLWriter& xout; + bool patched; + +protected: + void StartElement(const std::string& name, const char** atts) CM_OVERRIDE + { + xout.StartElement(name); + StartFragment(atts); + } + + void StartFragment(const char** atts) + { + for (size_t i = 0; atts[i]; i += 2) { + const char* key = atts[i]; + const char* value = atts[i + 1]; + xout.Attribute(key, value); + } + } + + void EndElement(const std::string& name) CM_OVERRIDE + { + if (name == "Updates" && !patched) { + repository->WriteRepositoryUpdates(xout); + patched = true; + } + xout.EndElement(); + if (patched) { + return; + } + if (name == "Checksum") { + repository->WriteRepositoryUpdates(xout); + patched = true; + } + } + + void CharacterDataHandler(const char* data, int length) CM_OVERRIDE + { + std::string content(data, data + length); + if (content == "" || content == " " || content == " " || + content == "\n") { + return; + } + xout.Content(content); + } +}; + +bool cmCPackIFWRepository::PatchUpdatesXml() +{ + // Lazy directory initialization + if (Directory.empty() && Generator) { + Directory = Generator->toplevel; + } + + // Filenames + std::string updatesXml = Directory + "/repository/Updates.xml"; + std::string updatesPatchXml = Directory + "/repository/UpdatesPatch.xml"; + + // Output stream + cmGeneratedFileStream fout(updatesPatchXml.data()); + cmXMLWriter xout(fout); + + xout.StartDocument(); + + WriteGeneratedByToStrim(xout); + + // Patch + { + cmCPackeIFWUpdatesPatcher patcher(this, xout); + patcher.ParseFile(updatesXml.data()); + } + + xout.EndDocument(); + + fout.Close(); + + return cmSystemTools::RenameFile(updatesPatchXml.data(), updatesXml.data()); +} + +void cmCPackIFWRepository::WriteRepositoryConfig(cmXMLWriter& xout) +{ + xout.StartElement("Repository"); + + // Url + xout.Element("Url", Url); + // Enabled + if (!Enabled.empty()) { + xout.Element("Enabled", Enabled); + } + // Username + if (!Username.empty()) { + xout.Element("Username", Username); + } + // Password + if (!Password.empty()) { + xout.Element("Password", Password); + } + // DisplayName + if (!DisplayName.empty()) { + xout.Element("DisplayName", DisplayName); + } + + xout.EndElement(); +} + +void cmCPackIFWRepository::WriteRepositoryUpdate(cmXMLWriter& xout) +{ + xout.StartElement("Repository"); + + switch (Update) { + case None: + break; + case Add: + xout.Attribute("action", "add"); + break; + case Remove: + xout.Attribute("action", "remove"); + break; + case Replace: + xout.Attribute("action", "replace"); + break; + } + + // Url + if (Update == Add || Update == Remove) { + xout.Attribute("url", Url); + } else if (Update == Replace) { + xout.Attribute("oldUrl", OldUrl); + xout.Attribute("newUrl", NewUrl); + } + // Enabled + if (!Enabled.empty()) { + xout.Attribute("enabled", Enabled); + } + // Username + if (!Username.empty()) { + xout.Attribute("username", Username); + } + // Password + if (!Password.empty()) { + xout.Attribute("password", Password); + } + // DisplayName + if (!DisplayName.empty()) { + xout.Attribute("displayname", DisplayName); + } + + xout.EndElement(); +} + +void cmCPackIFWRepository::WriteRepositoryUpdates(cmXMLWriter& xout) +{ + if (!RepositoryUpdate.empty()) { + xout.StartElement("RepositoryUpdate"); + for (RepositoriesVector::iterator rit = RepositoryUpdate.begin(); + rit != RepositoryUpdate.end(); ++rit) { + (*rit)->WriteRepositoryUpdate(xout); + } + xout.EndElement(); + } +} + +void cmCPackIFWRepository::WriteGeneratedByToStrim(cmXMLWriter& xout) +{ + if (Generator) { + Generator->WriteGeneratedByToStrim(xout); + } +} diff --git a/Source/CPack/IFW/cmCPackIFWRepository.h b/Source/CPack/IFW/cmCPackIFWRepository.h new file mode 100644 index 0000000..5ffb775 --- /dev/null +++ b/Source/CPack/IFW/cmCPackIFWRepository.h @@ -0,0 +1,105 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackIFWRepository_h +#define cmCPackIFWRepository_h + +#include <cmStandardIncludes.h> + +class cmCPackIFWGenerator; +class cmXMLWriter; + +/** \class cmCPackIFWRepository + * \brief A remote repository to be created CPack IFW generator + */ +class cmCPackIFWRepository +{ +public: + // Types + + enum Action + { + None, + Add, + Remove, + Replace + }; + + typedef std::vector<cmCPackIFWRepository*> RepositoriesVector; + +public: + // Constructor + + /** + * Construct repository + */ + cmCPackIFWRepository(); + +public: + // Configuration + + /// Internal repository name + std::string Name; + + /// Optional update action + Action Update; + + /// Is points to a list of available components + std::string Url; + + /// Is points to a list that will replaced + std::string OldUrl; + + /// Is points to a list that will replace to + std::string NewUrl; + + /// With "0" disabling this repository + std::string Enabled; + + /// Is used as user on a protected repository + std::string Username; + + /// Is password to use on a protected repository + std::string Password; + + /// Is optional string to display instead of the URL + std::string DisplayName; + +public: + // Internal implementation + + bool IsValid() const; + + const char* GetOption(const std::string& op) const; + bool IsOn(const std::string& op) const; + + bool IsVersionLess(const char* version); + bool IsVersionGreater(const char* version); + bool IsVersionEqual(const char* version); + + bool ConfigureFromOptions(); + + bool PatchUpdatesXml(); + + void WriteRepositoryConfig(cmXMLWriter& xout); + void WriteRepositoryUpdate(cmXMLWriter& xout); + void WriteRepositoryUpdates(cmXMLWriter& xout); + + cmCPackIFWGenerator* Generator; + RepositoriesVector RepositoryUpdate; + std::string Directory; + +protected: + void WriteGeneratedByToStrim(cmXMLWriter& xout); +}; + +#endif // cmCPackIFWRepository_h diff --git a/Source/CPack/OSXLauncherScript.scpt b/Source/CPack/OSXLauncherScript.scpt Binary files differnew file mode 100644 index 0000000..342cf8c --- /dev/null +++ b/Source/CPack/OSXLauncherScript.scpt diff --git a/Source/CPack/OSXScriptLauncher.cxx b/Source/CPack/OSXScriptLauncher.cxx new file mode 100644 index 0000000..a233e76 --- /dev/null +++ b/Source/CPack/OSXScriptLauncher.cxx @@ -0,0 +1,130 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include <cmsys/FStream.hxx> +#include <cmsys/Process.h> +#include <cmsys/SystemTools.hxx> + +#include <iostream> + +#include <CoreFoundation/CoreFoundation.h> + +// For the PATH_MAX constant +#include <sys/syslimits.h> + +#define DebugError(x) \ + ofs << x << std::endl; \ + std::cout << x << std::endl + +int main(int argc, char* argv[]) +{ + // if ( cmsys::SystemTools::FileExists( + std::string cwd = cmsys::SystemTools::GetCurrentWorkingDirectory(); + cmsys::ofstream ofs("/tmp/output.txt"); + + CFStringRef fileName; + CFBundleRef appBundle; + CFURLRef scriptFileURL; + UInt8* path; + + // get CF URL for script + if (!(appBundle = CFBundleGetMainBundle())) { + DebugError("Cannot get main bundle"); + return 1; + } + fileName = CFSTR("RuntimeScript"); + if (!(scriptFileURL = + CFBundleCopyResourceURL(appBundle, fileName, NULL, NULL))) { + DebugError("CFBundleCopyResourceURL failed"); + return 1; + } + + // create path string + if (!(path = new UInt8[PATH_MAX])) { + return 1; + } + + // get the file system path of the url as a cstring + // in an encoding suitable for posix apis + if (CFURLGetFileSystemRepresentation(scriptFileURL, true, path, PATH_MAX) == + false) { + DebugError("CFURLGetFileSystemRepresentation failed"); + return 1; + } + + // dispose of the CF variable + CFRelease(scriptFileURL); + + std::string fullScriptPath = reinterpret_cast<char*>(path); + delete[] path; + + if (!cmsys::SystemTools::FileExists(fullScriptPath.c_str())) { + return 1; + } + + std::string scriptDirectory = + cmsys::SystemTools::GetFilenamePath(fullScriptPath); + ofs << fullScriptPath << std::endl; + std::vector<const char*> args; + args.push_back(fullScriptPath.c_str()); + int cc; + for (cc = 1; cc < argc; ++cc) { + args.push_back(argv[cc]); + } + args.push_back(0); + + cmsysProcess* cp = cmsysProcess_New(); + cmsysProcess_SetCommand(cp, &*args.begin()); + cmsysProcess_SetWorkingDirectory(cp, scriptDirectory.c_str()); + cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1); + cmsysProcess_SetTimeout(cp, 0); + cmsysProcess_Execute(cp); + + std::vector<char> tempOutput; + char* data; + int length; + while (cmsysProcess_WaitForData(cp, &data, &length, 0)) { + // Translate NULL characters in the output into valid text. + // Visual Studio 7 puts these characters in the output of its + // build process. + for (int i = 0; i < length; ++i) { + if (data[i] == '\0') { + data[i] = ' '; + } + } + std::cout.write(data, length); + } + + cmsysProcess_WaitForExit(cp, 0); + + bool result = true; + if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) { + if (cmsysProcess_GetExitValue(cp) != 0) { + result = false; + } + } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) { + const char* exception_str = cmsysProcess_GetExceptionString(cp); + std::cerr << exception_str << std::endl; + result = false; + } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) { + const char* error_str = cmsysProcess_GetErrorString(cp); + std::cerr << error_str << std::endl; + result = false; + } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) { + const char* error_str = "Process terminated due to timeout\n"; + std::cerr << error_str << std::endl; + result = false; + } + + cmsysProcess_Delete(cp); + + return 0; +} diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx new file mode 100644 index 0000000..3ecc14d --- /dev/null +++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx @@ -0,0 +1,1134 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2012-2015 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackWIXGenerator.h" + +#include <CPack/cmCPackComponentGroup.h> +#include <CPack/cmCPackLog.h> +#include <cmCryptoHash.h> +#include <cmGeneratedFileStream.h> +#include <cmInstalledFile.h> +#include <cmSystemTools.h> + +#include "cmWIXDirectoriesSourceWriter.h" +#include "cmWIXFeaturesSourceWriter.h" +#include "cmWIXFilesSourceWriter.h" +#include "cmWIXRichTextFormatWriter.h" +#include "cmWIXSourceWriter.h" + +#include <cmsys/Directory.hxx> +#include <cmsys/Encoding.hxx> +#include <cmsys/FStream.hxx> +#include <cmsys/SystemTools.hxx> + +#include <rpc.h> // for GUID generation + +cmCPackWIXGenerator::cmCPackWIXGenerator() + : Patch(0) +{ +} + +cmCPackWIXGenerator::~cmCPackWIXGenerator() +{ + if (this->Patch) { + delete this->Patch; + } +} + +int cmCPackWIXGenerator::InitializeInternal() +{ + componentPackageMethod = ONE_PACKAGE; + this->Patch = new cmWIXPatch(this->Logger); + + return this->Superclass::InitializeInternal(); +} + +bool cmCPackWIXGenerator::RunWiXCommand(std::string const& command) +{ + std::string logFileName = this->CPackTopLevel + "/wix.log"; + + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Running WiX command: " << command + << std::endl); + + std::string output; + + int returnValue = 0; + bool status = cmSystemTools::RunSingleCommand(command.c_str(), &output, + &output, &returnValue, 0, + cmSystemTools::OUTPUT_NONE); + + cmsys::ofstream logFile(logFileName.c_str(), std::ios::app); + logFile << command << std::endl; + logFile << output; + logFile.close(); + + if (!status || returnValue) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running WiX candle. " + "Please check '" + << logFileName << "' for errors." << std::endl); + + return false; + } + + return true; +} + +bool cmCPackWIXGenerator::RunCandleCommand(std::string const& sourceFile, + std::string const& objectFile) +{ + std::string executable; + if (!RequireOption("CPACK_WIX_CANDLE_EXECUTABLE", executable)) { + return false; + } + + std::ostringstream command; + command << QuotePath(executable); + command << " -nologo"; + command << " -arch " << GetArchitecture(); + command << " -out " << QuotePath(objectFile); + + for (extension_set_t::const_iterator i = CandleExtensions.begin(); + i != CandleExtensions.end(); ++i) { + command << " -ext " << QuotePath(*i); + } + + AddCustomFlags("CPACK_WIX_CANDLE_EXTRA_FLAGS", command); + + command << " " << QuotePath(sourceFile); + + return RunWiXCommand(command.str()); +} + +bool cmCPackWIXGenerator::RunLightCommand(std::string const& objectFiles) +{ + std::string executable; + if (!RequireOption("CPACK_WIX_LIGHT_EXECUTABLE", executable)) { + return false; + } + + std::ostringstream command; + command << QuotePath(executable); + command << " -nologo"; + command << " -out " << QuotePath(packageFileNames.at(0)); + + for (extension_set_t::const_iterator i = this->LightExtensions.begin(); + i != this->LightExtensions.end(); ++i) { + command << " -ext " << QuotePath(*i); + } + + const char* const cultures = GetOption("CPACK_WIX_CULTURES"); + if (cultures) { + command << " -cultures:" << cultures; + } + + AddCustomFlags("CPACK_WIX_LIGHT_EXTRA_FLAGS", command); + + command << " " << objectFiles; + + return RunWiXCommand(command.str()); +} + +int cmCPackWIXGenerator::PackageFiles() +{ + if (!PackageFilesImpl() || cmSystemTools::GetErrorOccuredFlag()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Fatal WiX Generator Error" + << std::endl); + return false; + } + + return true; +} + +bool cmCPackWIXGenerator::InitializeWiXConfiguration() +{ + if (!ReadListFile("CPackWIX.cmake")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error while executing CPackWIX.cmake" + << std::endl); + return false; + } + + if (GetOption("CPACK_WIX_PRODUCT_GUID") == 0) { + std::string guid = GenerateGUID(); + SetOption("CPACK_WIX_PRODUCT_GUID", guid.c_str()); + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "CPACK_WIX_PRODUCT_GUID implicitly set to " << guid << " . " + << std::endl); + } + + if (GetOption("CPACK_WIX_UPGRADE_GUID") == 0) { + std::string guid = GenerateGUID(); + SetOption("CPACK_WIX_UPGRADE_GUID", guid.c_str()); + + cmCPackLogger( + cmCPackLog::LOG_WARNING, "CPACK_WIX_UPGRADE_GUID implicitly set to " + << guid << " . " + "Please refer to the documentation on how and why " + "you might want to set this explicitly." + << std::endl); + } + + if (!RequireOption("CPACK_TOPLEVEL_DIRECTORY", this->CPackTopLevel)) { + return false; + } + + if (GetOption("CPACK_WIX_LICENSE_RTF") == 0) { + std::string licenseFilename = this->CPackTopLevel + "/License.rtf"; + SetOption("CPACK_WIX_LICENSE_RTF", licenseFilename.c_str()); + + if (!CreateLicenseFile()) { + return false; + } + } + + if (GetOption("CPACK_PACKAGE_VENDOR") == 0) { + std::string defaultVendor = "Humanity"; + SetOption("CPACK_PACKAGE_VENDOR", defaultVendor.c_str()); + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "CPACK_PACKAGE_VENDOR implicitly set to " + << defaultVendor << " . " << std::endl); + } + + if (GetOption("CPACK_WIX_UI_REF") == 0) { + std::string defaultRef = "WixUI_InstallDir"; + + if (!this->Components.empty()) { + defaultRef = "WixUI_FeatureTree"; + } + + SetOption("CPACK_WIX_UI_REF", defaultRef.c_str()); + } + + const char* packageContact = GetOption("CPACK_PACKAGE_CONTACT"); + if (packageContact != 0 && GetOption("CPACK_WIX_PROPERTY_ARPCONTACT") == 0) { + SetOption("CPACK_WIX_PROPERTY_ARPCONTACT", packageContact); + } + + CollectExtensions("CPACK_WIX_EXTENSIONS", this->CandleExtensions); + CollectExtensions("CPACK_WIX_CANDLE_EXTENSIONS", this->CandleExtensions); + + this->LightExtensions.insert("WixUIExtension"); + CollectExtensions("CPACK_WIX_EXTENSIONS", this->LightExtensions); + CollectExtensions("CPACK_WIX_LIGHT_EXTENSIONS", this->LightExtensions); + + const char* patchFilePath = GetOption("CPACK_WIX_PATCH_FILE"); + if (patchFilePath) { + std::vector<std::string> patchFilePaths; + cmSystemTools::ExpandListArgument(patchFilePath, patchFilePaths); + + for (size_t i = 0; i < patchFilePaths.size(); ++i) { + if (!this->Patch->LoadFragments(patchFilePaths[i])) { + return false; + } + } + } + + return true; +} + +bool cmCPackWIXGenerator::PackageFilesImpl() +{ + if (!InitializeWiXConfiguration()) { + return false; + } + + CreateWiXVariablesIncludeFile(); + CreateWiXPropertiesIncludeFile(); + CreateWiXProductFragmentIncludeFile(); + + if (!CreateWiXSourceFiles()) { + return false; + } + + AppendUserSuppliedExtraSources(); + + std::set<std::string> usedBaseNames; + + std::ostringstream objectFiles; + for (size_t i = 0; i < this->WixSources.size(); ++i) { + std::string const& sourceFilename = this->WixSources[i]; + + std::string baseName = + cmSystemTools::GetFilenameWithoutLastExtension(sourceFilename); + + unsigned int counter = 0; + std::string uniqueBaseName = baseName; + + while (usedBaseNames.find(uniqueBaseName) != usedBaseNames.end()) { + std::ostringstream tmp; + tmp << baseName << ++counter; + uniqueBaseName = tmp.str(); + } + + usedBaseNames.insert(uniqueBaseName); + + std::string objectFilename = + this->CPackTopLevel + "/" + uniqueBaseName + ".wixobj"; + + if (!RunCandleCommand(sourceFilename, objectFilename)) { + return false; + } + + objectFiles << " " << QuotePath(objectFilename); + } + + AppendUserSuppliedExtraObjects(objectFiles); + + return RunLightCommand(objectFiles.str()); +} + +void cmCPackWIXGenerator::AppendUserSuppliedExtraSources() +{ + const char* cpackWixExtraSources = GetOption("CPACK_WIX_EXTRA_SOURCES"); + if (!cpackWixExtraSources) + return; + + cmSystemTools::ExpandListArgument(cpackWixExtraSources, this->WixSources); +} + +void cmCPackWIXGenerator::AppendUserSuppliedExtraObjects(std::ostream& stream) +{ + const char* cpackWixExtraObjects = GetOption("CPACK_WIX_EXTRA_OBJECTS"); + if (!cpackWixExtraObjects) + return; + + std::vector<std::string> expandedExtraObjects; + + cmSystemTools::ExpandListArgument(cpackWixExtraObjects, + expandedExtraObjects); + + for (size_t i = 0; i < expandedExtraObjects.size(); ++i) { + stream << " " << QuotePath(expandedExtraObjects[i]); + } +} + +void cmCPackWIXGenerator::CreateWiXVariablesIncludeFile() +{ + std::string includeFilename = this->CPackTopLevel + "/cpack_variables.wxi"; + + cmWIXSourceWriter includeFile(this->Logger, includeFilename, true); + + CopyDefinition(includeFile, "CPACK_WIX_PRODUCT_GUID"); + CopyDefinition(includeFile, "CPACK_WIX_UPGRADE_GUID"); + CopyDefinition(includeFile, "CPACK_PACKAGE_VENDOR"); + CopyDefinition(includeFile, "CPACK_PACKAGE_NAME"); + CopyDefinition(includeFile, "CPACK_PACKAGE_VERSION"); + CopyDefinition(includeFile, "CPACK_WIX_LICENSE_RTF"); + CopyDefinition(includeFile, "CPACK_WIX_PRODUCT_ICON"); + CopyDefinition(includeFile, "CPACK_WIX_UI_BANNER"); + CopyDefinition(includeFile, "CPACK_WIX_UI_DIALOG"); + SetOptionIfNotSet("CPACK_WIX_PROGRAM_MENU_FOLDER", + GetOption("CPACK_PACKAGE_NAME")); + CopyDefinition(includeFile, "CPACK_WIX_PROGRAM_MENU_FOLDER"); + CopyDefinition(includeFile, "CPACK_WIX_UI_REF"); +} + +void cmCPackWIXGenerator::CreateWiXPropertiesIncludeFile() +{ + std::string includeFilename = this->CPackTopLevel + "/properties.wxi"; + + cmWIXSourceWriter includeFile(this->Logger, includeFilename, true); + + std::string prefix = "CPACK_WIX_PROPERTY_"; + std::vector<std::string> options = GetOptions(); + + for (size_t i = 0; i < options.size(); ++i) { + std::string const& name = options[i]; + + if (name.length() > prefix.length() && + name.substr(0, prefix.length()) == prefix) { + std::string id = name.substr(prefix.length()); + std::string value = GetOption(name.c_str()); + + includeFile.BeginElement("Property"); + includeFile.AddAttribute("Id", id); + includeFile.AddAttribute("Value", value); + includeFile.EndElement("Property"); + } + } + + if (GetOption("CPACK_WIX_PROPERTY_ARPINSTALLLOCATION") == 0) { + includeFile.BeginElement("Property"); + includeFile.AddAttribute("Id", "INSTALL_ROOT"); + includeFile.AddAttribute("Secure", "yes"); + + includeFile.BeginElement("RegistrySearch"); + includeFile.AddAttribute("Id", "FindInstallLocation"); + includeFile.AddAttribute("Root", "HKLM"); + includeFile.AddAttribute( + "Key", "Software\\Microsoft\\Windows\\" + "CurrentVersion\\Uninstall\\[WIX_UPGRADE_DETECTED]"); + includeFile.AddAttribute("Name", "InstallLocation"); + includeFile.AddAttribute("Type", "raw"); + includeFile.EndElement("RegistrySearch"); + includeFile.EndElement("Property"); + + includeFile.BeginElement("SetProperty"); + includeFile.AddAttribute("Id", "ARPINSTALLLOCATION"); + includeFile.AddAttribute("Value", "[INSTALL_ROOT]"); + includeFile.AddAttribute("After", "CostFinalize"); + includeFile.EndElement("SetProperty"); + } +} + +void cmCPackWIXGenerator::CreateWiXProductFragmentIncludeFile() +{ + std::string includeFilename = this->CPackTopLevel + "/product_fragment.wxi"; + + cmWIXSourceWriter includeFile(this->Logger, includeFilename, true); + + this->Patch->ApplyFragment("#PRODUCT", includeFile); +} + +void cmCPackWIXGenerator::CopyDefinition(cmWIXSourceWriter& source, + std::string const& name) +{ + const char* value = GetOption(name.c_str()); + if (value) { + AddDefinition(source, name, value); + } +} + +void cmCPackWIXGenerator::AddDefinition(cmWIXSourceWriter& source, + std::string const& name, + std::string const& value) +{ + std::ostringstream tmp; + tmp << name << "=\"" << value << '"'; + + source.AddProcessingInstruction( + "define", cmWIXSourceWriter::CMakeEncodingToUtf8(tmp.str())); +} + +bool cmCPackWIXGenerator::CreateWiXSourceFiles() +{ + std::string directoryDefinitionsFilename = + this->CPackTopLevel + "/directories.wxs"; + + this->WixSources.push_back(directoryDefinitionsFilename); + + cmWIXDirectoriesSourceWriter directoryDefinitions( + this->Logger, directoryDefinitionsFilename); + directoryDefinitions.BeginElement("Fragment"); + + std::string installRoot; + if (!RequireOption("CPACK_PACKAGE_INSTALL_DIRECTORY", installRoot)) { + return false; + } + + directoryDefinitions.BeginElement("Directory"); + directoryDefinitions.AddAttribute("Id", "TARGETDIR"); + directoryDefinitions.AddAttribute("Name", "SourceDir"); + + size_t installRootSize = + directoryDefinitions.BeginInstallationPrefixDirectory( + GetProgramFilesFolderId(), installRoot); + + std::string fileDefinitionsFilename = this->CPackTopLevel + "/files.wxs"; + + this->WixSources.push_back(fileDefinitionsFilename); + + cmWIXFilesSourceWriter fileDefinitions(this->Logger, + fileDefinitionsFilename); + + fileDefinitions.BeginElement("Fragment"); + + std::string featureDefinitionsFilename = + this->CPackTopLevel + "/features.wxs"; + + this->WixSources.push_back(featureDefinitionsFilename); + + cmWIXFeaturesSourceWriter featureDefinitions(this->Logger, + featureDefinitionsFilename); + + featureDefinitions.BeginElement("Fragment"); + + featureDefinitions.BeginElement("Feature"); + featureDefinitions.AddAttribute("Id", "ProductFeature"); + featureDefinitions.AddAttribute("Display", "expand"); + featureDefinitions.AddAttribute("Absent", "disallow"); + featureDefinitions.AddAttribute("ConfigurableDirectory", "INSTALL_ROOT"); + + std::string cpackPackageName; + if (!RequireOption("CPACK_PACKAGE_NAME", cpackPackageName)) { + return false; + } + + std::string featureTitle = cpackPackageName; + if (const char* title = GetOption("CPACK_WIX_ROOT_FEATURE_TITLE")) { + featureTitle = title; + } + featureDefinitions.AddAttribute("Title", featureTitle); + if (const char* desc = GetOption("CPACK_WIX_ROOT_FEATURE_DESCRIPTION")) { + featureDefinitions.AddAttribute("Description", desc); + } + featureDefinitions.AddAttribute("Level", "1"); + this->Patch->ApplyFragment("#PRODUCTFEATURE", featureDefinitions); + + const char* package = GetOption("CPACK_WIX_CMAKE_PACKAGE_REGISTRY"); + if (package) { + featureDefinitions.CreateCMakePackageRegistryEntry( + package, GetOption("CPACK_WIX_UPGRADE_GUID")); + } + + if (!CreateFeatureHierarchy(featureDefinitions)) { + return false; + } + + featureDefinitions.EndElement("Feature"); + + std::set<cmWIXShortcuts::Type> emittedShortcutTypes; + + cmWIXShortcuts globalShortcuts; + if (Components.empty()) { + AddComponentsToFeature(toplevel, "ProductFeature", directoryDefinitions, + fileDefinitions, featureDefinitions, + globalShortcuts); + + globalShortcuts.AddShortcutTypes(emittedShortcutTypes); + } else { + for (std::map<std::string, cmCPackComponent>::const_iterator i = + this->Components.begin(); + i != this->Components.end(); ++i) { + cmCPackComponent const& component = i->second; + + std::string componentPath = toplevel; + componentPath += "/"; + componentPath += component.Name; + + std::string componentFeatureId = "CM_C_" + component.Name; + + cmWIXShortcuts featureShortcuts; + AddComponentsToFeature(componentPath, componentFeatureId, + directoryDefinitions, fileDefinitions, + featureDefinitions, featureShortcuts); + + featureShortcuts.AddShortcutTypes(emittedShortcutTypes); + + if (!CreateShortcuts(component.Name, componentFeatureId, + featureShortcuts, false, fileDefinitions, + featureDefinitions)) { + return false; + } + } + } + + bool emitUninstallShortcut = + emittedShortcutTypes.find(cmWIXShortcuts::START_MENU) != + emittedShortcutTypes.end(); + + if (!CreateShortcuts(std::string(), "ProductFeature", globalShortcuts, + emitUninstallShortcut, fileDefinitions, + featureDefinitions)) { + return false; + } + + featureDefinitions.EndElement("Fragment"); + fileDefinitions.EndElement("Fragment"); + + directoryDefinitions.EndInstallationPrefixDirectory(installRootSize); + + if (emittedShortcutTypes.find(cmWIXShortcuts::START_MENU) != + emittedShortcutTypes.end()) { + directoryDefinitions.EmitStartMenuFolder( + GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER")); + } + + if (emittedShortcutTypes.find(cmWIXShortcuts::DESKTOP) != + emittedShortcutTypes.end()) { + directoryDefinitions.EmitDesktopFolder(); + } + + if (emittedShortcutTypes.find(cmWIXShortcuts::STARTUP) != + emittedShortcutTypes.end()) { + directoryDefinitions.EmitStartupFolder(); + } + + directoryDefinitions.EndElement("Directory"); + directoryDefinitions.EndElement("Fragment"); + + if (!GenerateMainSourceFileFromTemplate()) { + return false; + } + + return this->Patch->CheckForUnappliedFragments(); +} + +std::string cmCPackWIXGenerator::GetProgramFilesFolderId() const +{ + if (GetArchitecture() == "x86") { + return "ProgramFilesFolder"; + } else { + return "ProgramFiles64Folder"; + } +} + +bool cmCPackWIXGenerator::GenerateMainSourceFileFromTemplate() +{ + std::string wixTemplate = FindTemplate("WIX.template.in"); + if (GetOption("CPACK_WIX_TEMPLATE") != 0) { + wixTemplate = GetOption("CPACK_WIX_TEMPLATE"); + } + + if (wixTemplate.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Could not find CPack WiX template file WIX.template.in" + << std::endl); + return false; + } + + std::string mainSourceFilePath = this->CPackTopLevel + "/main.wxs"; + + if (!ConfigureFile(wixTemplate.c_str(), mainSourceFilePath.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Failed creating '" + << mainSourceFilePath << "'' from template." << std::endl); + + return false; + } + + this->WixSources.push_back(mainSourceFilePath); + + return true; +} + +bool cmCPackWIXGenerator::CreateFeatureHierarchy( + cmWIXFeaturesSourceWriter& featureDefinitions) +{ + for (std::map<std::string, cmCPackComponentGroup>::const_iterator i = + ComponentGroups.begin(); + i != ComponentGroups.end(); ++i) { + cmCPackComponentGroup const& group = i->second; + if (group.ParentGroup == 0) { + featureDefinitions.EmitFeatureForComponentGroup(group); + } + } + + for (std::map<std::string, cmCPackComponent>::const_iterator i = + this->Components.begin(); + i != this->Components.end(); ++i) { + cmCPackComponent const& component = i->second; + + if (!component.Group) { + featureDefinitions.EmitFeatureForComponent(component); + } + } + + return true; +} + +bool cmCPackWIXGenerator::AddComponentsToFeature( + std::string const& rootPath, std::string const& featureId, + cmWIXDirectoriesSourceWriter& directoryDefinitions, + cmWIXFilesSourceWriter& fileDefinitions, + cmWIXFeaturesSourceWriter& featureDefinitions, cmWIXShortcuts& shortcuts) +{ + featureDefinitions.BeginElement("FeatureRef"); + featureDefinitions.AddAttribute("Id", featureId); + + std::vector<std::string> cpackPackageExecutablesList; + const char* cpackPackageExecutables = GetOption("CPACK_PACKAGE_EXECUTABLES"); + if (cpackPackageExecutables) { + cmSystemTools::ExpandListArgument(cpackPackageExecutables, + cpackPackageExecutablesList); + if (cpackPackageExecutablesList.size() % 2 != 0) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and " + "<text label>." + << std::endl); + return false; + } + } + + std::vector<std::string> cpackPackageDesktopLinksList; + const char* cpackPackageDesktopLinks = + GetOption("CPACK_CREATE_DESKTOP_LINKS"); + if (cpackPackageDesktopLinks) { + cmSystemTools::ExpandListArgument(cpackPackageDesktopLinks, + cpackPackageDesktopLinksList); + } + + AddDirectoryAndFileDefinitons(rootPath, "INSTALL_ROOT", directoryDefinitions, + fileDefinitions, featureDefinitions, + cpackPackageExecutablesList, + cpackPackageDesktopLinksList, shortcuts); + + featureDefinitions.EndElement("FeatureRef"); + + return true; +} + +bool cmCPackWIXGenerator::CreateShortcuts( + std::string const& cpackComponentName, std::string const& featureId, + cmWIXShortcuts const& shortcuts, bool emitUninstallShortcut, + cmWIXFilesSourceWriter& fileDefinitions, + cmWIXFeaturesSourceWriter& featureDefinitions) +{ + if (!shortcuts.empty(cmWIXShortcuts::START_MENU)) { + if (!this->CreateShortcutsOfSpecificType( + cmWIXShortcuts::START_MENU, cpackComponentName, featureId, "", + shortcuts, emitUninstallShortcut, fileDefinitions, + featureDefinitions)) { + return false; + } + } + + if (!shortcuts.empty(cmWIXShortcuts::DESKTOP)) { + if (!this->CreateShortcutsOfSpecificType( + cmWIXShortcuts::DESKTOP, cpackComponentName, featureId, "DESKTOP", + shortcuts, false, fileDefinitions, featureDefinitions)) { + return false; + } + } + + if (!shortcuts.empty(cmWIXShortcuts::STARTUP)) { + if (!this->CreateShortcutsOfSpecificType( + cmWIXShortcuts::STARTUP, cpackComponentName, featureId, "STARTUP", + shortcuts, false, fileDefinitions, featureDefinitions)) { + return false; + } + } + + return true; +} + +bool cmCPackWIXGenerator::CreateShortcutsOfSpecificType( + cmWIXShortcuts::Type type, std::string const& cpackComponentName, + std::string const& featureId, std::string const& idPrefix, + cmWIXShortcuts const& shortcuts, bool emitUninstallShortcut, + cmWIXFilesSourceWriter& fileDefinitions, + cmWIXFeaturesSourceWriter& featureDefinitions) +{ + std::string directoryId; + switch (type) { + case cmWIXShortcuts::START_MENU: + directoryId = "PROGRAM_MENU_FOLDER"; + break; + case cmWIXShortcuts::DESKTOP: + directoryId = "DesktopFolder"; + break; + case cmWIXShortcuts::STARTUP: + directoryId = "StartupFolder"; + break; + default: + return false; + } + + featureDefinitions.BeginElement("FeatureRef"); + featureDefinitions.AddAttribute("Id", featureId); + + std::string cpackVendor; + if (!RequireOption("CPACK_PACKAGE_VENDOR", cpackVendor)) { + return false; + } + + std::string cpackPackageName; + if (!RequireOption("CPACK_PACKAGE_NAME", cpackPackageName)) { + return false; + } + + std::string idSuffix; + if (!cpackComponentName.empty()) { + idSuffix += "_"; + idSuffix += cpackComponentName; + } + + std::string componentId = "CM_SHORTCUT"; + if (idPrefix.size()) { + componentId += "_" + idPrefix; + } + + componentId += idSuffix; + + fileDefinitions.BeginElement("DirectoryRef"); + fileDefinitions.AddAttribute("Id", directoryId); + + fileDefinitions.BeginElement("Component"); + fileDefinitions.AddAttribute("Id", componentId); + fileDefinitions.AddAttribute("Guid", "*"); + + this->Patch->ApplyFragment(componentId, fileDefinitions); + + std::string registryKey = + std::string("Software\\") + cpackVendor + "\\" + cpackPackageName; + + shortcuts.EmitShortcuts(type, registryKey, cpackComponentName, + fileDefinitions); + + if (type == cmWIXShortcuts::START_MENU) { + fileDefinitions.EmitRemoveFolder("CM_REMOVE_PROGRAM_MENU_FOLDER" + + idSuffix); + } + + if (emitUninstallShortcut) { + fileDefinitions.EmitUninstallShortcut(cpackPackageName); + } + + fileDefinitions.EndElement("Component"); + fileDefinitions.EndElement("DirectoryRef"); + + featureDefinitions.EmitComponentRef(componentId); + featureDefinitions.EndElement("FeatureRef"); + + return true; +} + +bool cmCPackWIXGenerator::CreateLicenseFile() +{ + std::string licenseSourceFilename; + if (!RequireOption("CPACK_RESOURCE_FILE_LICENSE", licenseSourceFilename)) { + return false; + } + + std::string licenseDestinationFilename; + if (!RequireOption("CPACK_WIX_LICENSE_RTF", licenseDestinationFilename)) { + return false; + } + + std::string extension = GetRightmostExtension(licenseSourceFilename); + + if (extension == ".rtf") { + cmSystemTools::CopyAFile(licenseSourceFilename.c_str(), + licenseDestinationFilename.c_str()); + } else if (extension == ".txt") { + cmWIXRichTextFormatWriter rtfWriter(licenseDestinationFilename); + + cmsys::ifstream licenseSource(licenseSourceFilename.c_str()); + + std::string line; + while (std::getline(licenseSource, line)) { + rtfWriter.AddText(line); + rtfWriter.AddText("\n"); + } + } else { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "unsupported WiX License file extension '" + << extension << "'" << std::endl); + + return false; + } + + return true; +} + +void cmCPackWIXGenerator::AddDirectoryAndFileDefinitons( + std::string const& topdir, std::string const& directoryId, + cmWIXDirectoriesSourceWriter& directoryDefinitions, + cmWIXFilesSourceWriter& fileDefinitions, + cmWIXFeaturesSourceWriter& featureDefinitions, + std::vector<std::string> const& packageExecutables, + std::vector<std::string> const& desktopExecutables, + cmWIXShortcuts& shortcuts) +{ + cmsys::Directory dir; + dir.Load(topdir.c_str()); + + std::string relativeDirectoryPath = + cmSystemTools::RelativePath(toplevel.c_str(), topdir.c_str()); + + if (relativeDirectoryPath.empty()) { + relativeDirectoryPath = "."; + } + + cmInstalledFile const* directoryInstalledFile = this->GetInstalledFile( + this->RelativePathWithoutComponentPrefix(relativeDirectoryPath)); + + bool emptyDirectory = dir.GetNumberOfFiles() == 2; + bool createDirectory = false; + + if (emptyDirectory) { + createDirectory = true; + } + + if (directoryInstalledFile) { + if (directoryInstalledFile->HasProperty("CPACK_WIX_ACL")) { + createDirectory = true; + } + } + + if (createDirectory) { + std::string componentId = fileDefinitions.EmitComponentCreateFolder( + directoryId, GenerateGUID(), directoryInstalledFile); + featureDefinitions.EmitComponentRef(componentId); + } + + if (emptyDirectory) { + return; + } + + for (size_t i = 0; i < dir.GetNumberOfFiles(); ++i) { + std::string fileName = dir.GetFile(static_cast<unsigned long>(i)); + + if (fileName == "." || fileName == "..") { + continue; + } + + std::string fullPath = topdir + "/" + fileName; + + std::string relativePath = + cmSystemTools::RelativePath(toplevel.c_str(), fullPath.c_str()); + + std::string id = PathToId(relativePath); + + if (cmSystemTools::FileIsDirectory(fullPath.c_str())) { + std::string subDirectoryId = std::string("CM_D") + id; + + directoryDefinitions.BeginElement("Directory"); + directoryDefinitions.AddAttribute("Id", subDirectoryId); + directoryDefinitions.AddAttribute("Name", fileName); + + AddDirectoryAndFileDefinitons( + fullPath, subDirectoryId, directoryDefinitions, fileDefinitions, + featureDefinitions, packageExecutables, desktopExecutables, shortcuts); + + this->Patch->ApplyFragment(subDirectoryId, directoryDefinitions); + directoryDefinitions.EndElement("Directory"); + } else { + cmInstalledFile const* installedFile = this->GetInstalledFile( + this->RelativePathWithoutComponentPrefix(relativePath)); + + if (installedFile) { + shortcuts.CreateFromProperties(id, directoryId, *installedFile); + } + + std::string componentId = fileDefinitions.EmitComponentFile( + directoryId, id, fullPath, *(this->Patch), installedFile); + + featureDefinitions.EmitComponentRef(componentId); + + for (size_t j = 0; j < packageExecutables.size(); ++j) { + std::string const& executableName = packageExecutables[j++]; + std::string const& textLabel = packageExecutables[j]; + + if (cmSystemTools::LowerCase(fileName) == + cmSystemTools::LowerCase(executableName) + ".exe") { + cmWIXShortcut shortcut; + shortcut.label = textLabel; + shortcut.workingDirectoryId = directoryId; + shortcuts.insert(cmWIXShortcuts::START_MENU, id, shortcut); + + if (!desktopExecutables.empty() && + std::find(desktopExecutables.begin(), desktopExecutables.end(), + executableName) != desktopExecutables.end()) { + shortcuts.insert(cmWIXShortcuts::DESKTOP, id, shortcut); + } + } + } + } + } +} + +bool cmCPackWIXGenerator::RequireOption(std::string const& name, + std::string& value) const +{ + const char* tmp = GetOption(name.c_str()); + if (tmp) { + value = tmp; + + return true; + } else { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Required variable " + << name << " not set" << std::endl); + + return false; + } +} + +std::string cmCPackWIXGenerator::GetArchitecture() const +{ + std::string void_p_size; + RequireOption("CPACK_WIX_SIZEOF_VOID_P", void_p_size); + + if (void_p_size == "8") { + return "x64"; + } else { + return "x86"; + } +} + +std::string cmCPackWIXGenerator::GenerateGUID() +{ + UUID guid; + UuidCreate(&guid); + + unsigned short* tmp = 0; + UuidToStringW(&guid, &tmp); + + std::string result = + cmsys::Encoding::ToNarrow(reinterpret_cast<wchar_t*>(tmp)); + RpcStringFreeW(&tmp); + + return cmSystemTools::UpperCase(result); +} + +std::string cmCPackWIXGenerator::QuotePath(std::string const& path) +{ + return std::string("\"") + path + '"'; +} + +std::string cmCPackWIXGenerator::GetRightmostExtension( + std::string const& filename) +{ + std::string extension; + + std::string::size_type i = filename.rfind("."); + if (i != std::string::npos) { + extension = filename.substr(i); + } + + return cmSystemTools::LowerCase(extension); +} + +std::string cmCPackWIXGenerator::PathToId(std::string const& path) +{ + id_map_t::const_iterator i = PathToIdMap.find(path); + if (i != PathToIdMap.end()) + return i->second; + + std::string id = CreateNewIdForPath(path); + return id; +} + +std::string cmCPackWIXGenerator::CreateNewIdForPath(std::string const& path) +{ + std::vector<std::string> components; + cmSystemTools::SplitPath(path.c_str(), components, false); + + size_t replacementCount = 0; + + std::string identifier; + std::string currentComponent; + + for (size_t i = 1; i < components.size(); ++i) { + if (i != 1) + identifier += '.'; + + currentComponent = + NormalizeComponentForId(components[i], replacementCount); + + identifier += currentComponent; + } + + std::string idPrefix = "P"; + size_t replacementPercent = replacementCount * 100 / identifier.size(); + if (replacementPercent > 33 || identifier.size() > 60) { + identifier = CreateHashedId(path, currentComponent); + idPrefix = "H"; + } + + std::ostringstream result; + result << idPrefix << "_" << identifier; + + size_t ambiguityCount = ++IdAmbiguityCounter[identifier]; + + if (ambiguityCount > 999) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error while trying to generate a unique Id for '" + << path << "'" << std::endl); + + return std::string(); + } else if (ambiguityCount > 1) { + result << "_" << ambiguityCount; + } + + std::string resultString = result.str(); + + PathToIdMap[path] = resultString; + + return resultString; +} + +std::string cmCPackWIXGenerator::CreateHashedId( + std::string const& path, std::string const& normalizedFilename) +{ + CM_AUTO_PTR<cmCryptoHash> sha1 = cmCryptoHash::New("SHA1"); + std::string hash = sha1->HashString(path.c_str()); + + std::string identifier; + identifier += hash.substr(0, 7) + "_"; + + const size_t maxFileNameLength = 52; + if (normalizedFilename.length() > maxFileNameLength) { + identifier += normalizedFilename.substr(0, maxFileNameLength - 3); + identifier += "..."; + } else { + identifier += normalizedFilename; + } + + return identifier; +} + +std::string cmCPackWIXGenerator::NormalizeComponentForId( + std::string const& component, size_t& replacementCount) +{ + std::string result; + result.resize(component.size()); + + for (size_t i = 0; i < component.size(); ++i) { + char c = component[i]; + if (IsLegalIdCharacter(c)) { + result[i] = c; + } else { + result[i] = '_'; + ++replacementCount; + } + } + + return result; +} + +bool cmCPackWIXGenerator::IsLegalIdCharacter(char c) +{ + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || c == '_' || c == '.'; +} + +void cmCPackWIXGenerator::CollectExtensions(std::string const& variableName, + extension_set_t& extensions) +{ + const char* variableContent = GetOption(variableName.c_str()); + if (!variableContent) + return; + + std::vector<std::string> list; + cmSystemTools::ExpandListArgument(variableContent, list); + extensions.insert(list.begin(), list.end()); +} + +void cmCPackWIXGenerator::AddCustomFlags(std::string const& variableName, + std::ostream& stream) +{ + const char* variableContent = GetOption(variableName.c_str()); + if (!variableContent) + return; + + std::vector<std::string> list; + cmSystemTools::ExpandListArgument(variableContent, list); + + for (std::vector<std::string>::const_iterator i = list.begin(); + i != list.end(); ++i) { + stream << " " << QuotePath(*i); + } +} + +std::string cmCPackWIXGenerator::RelativePathWithoutComponentPrefix( + std::string const& path) +{ + if (this->Components.empty()) { + return path; + } + + std::string::size_type pos = path.find('/'); + + return path.substr(pos + 1); +} diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.h b/Source/CPack/WiX/cmCPackWIXGenerator.h new file mode 100644 index 0000000..9d3a522 --- /dev/null +++ b/Source/CPack/WiX/cmCPackWIXGenerator.h @@ -0,0 +1,167 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2012-2015 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackWIXGenerator_h +#define cmCPackWIXGenerator_h + +#include <CPack/cmCPackGenerator.h> + +#include "cmWIXPatch.h" +#include "cmWIXShortcut.h" + +#include <map> +#include <string> + +class cmWIXSourceWriter; +class cmWIXDirectoriesSourceWriter; +class cmWIXFilesSourceWriter; +class cmWIXFeaturesSourceWriter; + +/** \class cmCPackWIXGenerator + * \brief A generator for WIX files + */ +class cmCPackWIXGenerator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackWIXGenerator, cmCPackGenerator); + + cmCPackWIXGenerator(); + ~cmCPackWIXGenerator(); + +protected: + virtual int InitializeInternal(); + + virtual int PackageFiles(); + + virtual const char* GetOutputExtension() { return ".msi"; } + + virtual enum CPackSetDestdirSupport SupportsSetDestdir() const + { + return SETDESTDIR_UNSUPPORTED; + } + + virtual bool SupportsAbsoluteDestination() const { return false; } + + virtual bool SupportsComponentInstallation() const { return true; } + +private: + typedef std::map<std::string, std::string> id_map_t; + typedef std::map<std::string, size_t> ambiguity_map_t; + typedef std::set<std::string> extension_set_t; + + bool InitializeWiXConfiguration(); + + bool PackageFilesImpl(); + + void CreateWiXVariablesIncludeFile(); + + void CreateWiXPropertiesIncludeFile(); + + void CreateWiXProductFragmentIncludeFile(); + + void CopyDefinition(cmWIXSourceWriter& source, std::string const& name); + + void AddDefinition(cmWIXSourceWriter& source, std::string const& name, + std::string const& value); + + bool CreateWiXSourceFiles(); + + std::string GetProgramFilesFolderId() const; + + bool GenerateMainSourceFileFromTemplate(); + + bool CreateFeatureHierarchy(cmWIXFeaturesSourceWriter& featureDefinitions); + + bool AddComponentsToFeature( + std::string const& rootPath, std::string const& featureId, + cmWIXDirectoriesSourceWriter& directoryDefinitions, + cmWIXFilesSourceWriter& fileDefinitions, + cmWIXFeaturesSourceWriter& featureDefinitions, cmWIXShortcuts& shortcuts); + + bool CreateShortcuts(std::string const& cpackComponentName, + std::string const& featureId, + cmWIXShortcuts const& shortcuts, + bool emitUninstallShortcut, + cmWIXFilesSourceWriter& fileDefinitions, + cmWIXFeaturesSourceWriter& featureDefinitions); + + bool CreateShortcutsOfSpecificType( + cmWIXShortcuts::Type type, std::string const& cpackComponentName, + std::string const& featureId, std::string const& idPrefix, + cmWIXShortcuts const& shortcuts, bool emitUninstallShortcut, + cmWIXFilesSourceWriter& fileDefinitions, + cmWIXFeaturesSourceWriter& featureDefinitions); + + void AppendUserSuppliedExtraSources(); + + void AppendUserSuppliedExtraObjects(std::ostream& stream); + + bool CreateLicenseFile(); + + bool RunWiXCommand(std::string const& command); + + bool RunCandleCommand(std::string const& sourceFile, + std::string const& objectFile); + + bool RunLightCommand(std::string const& objectFiles); + + void AddDirectoryAndFileDefinitons( + std::string const& topdir, std::string const& directoryId, + cmWIXDirectoriesSourceWriter& directoryDefinitions, + cmWIXFilesSourceWriter& fileDefinitions, + cmWIXFeaturesSourceWriter& featureDefinitions, + std::vector<std::string> const& packageExecutables, + std::vector<std::string> const& desktopExecutables, + cmWIXShortcuts& shortcuts); + + bool RequireOption(std::string const& name, std::string& value) const; + + std::string GetArchitecture() const; + + static std::string GenerateGUID(); + + static std::string QuotePath(std::string const& path); + + static std::string GetRightmostExtension(std::string const& filename); + + std::string PathToId(std::string const& path); + + std::string CreateNewIdForPath(std::string const& path); + + static std::string CreateHashedId(std::string const& path, + std::string const& normalizedFilename); + + std::string NormalizeComponentForId(std::string const& component, + size_t& replacementCount); + + static bool IsLegalIdCharacter(char c); + + void CollectExtensions(std::string const& variableName, + extension_set_t& extensions); + + void AddCustomFlags(std::string const& variableName, std::ostream& stream); + + std::string RelativePathWithoutComponentPrefix(std::string const& path); + + std::vector<std::string> WixSources; + id_map_t PathToIdMap; + ambiguity_map_t IdAmbiguityCounter; + + extension_set_t CandleExtensions; + extension_set_t LightExtensions; + + std::string CPackTopLevel; + + cmWIXPatch* Patch; +}; + +#endif diff --git a/Source/CPack/WiX/cmWIXAccessControlList.cxx b/Source/CPack/WiX/cmWIXAccessControlList.cxx new file mode 100644 index 0000000..bbbd92d --- /dev/null +++ b/Source/CPack/WiX/cmWIXAccessControlList.cxx @@ -0,0 +1,136 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2014 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmWIXAccessControlList.h" + +#include <CPack/cmCPackGenerator.h> + +#include <cmSystemTools.h> + +cmWIXAccessControlList::cmWIXAccessControlList( + cmCPackLog* logger, cmInstalledFile const& installedFile, + cmWIXSourceWriter& sourceWriter) + : Logger(logger) + , InstalledFile(installedFile) + , SourceWriter(sourceWriter) +{ +} + +bool cmWIXAccessControlList::Apply() +{ + std::vector<std::string> entries; + this->InstalledFile.GetPropertyAsList("CPACK_WIX_ACL", entries); + + for (size_t i = 0; i < entries.size(); ++i) { + this->CreatePermissionElement(entries[i]); + } + + return true; +} + +void cmWIXAccessControlList::CreatePermissionElement(std::string const& entry) +{ + std::string::size_type pos = entry.find('='); + if (pos == std::string::npos) { + this->ReportError(entry, "Did not find mandatory '='"); + return; + } + + std::string user_and_domain = entry.substr(0, pos); + std::string permission_string = entry.substr(pos + 1); + + pos = user_and_domain.find('@'); + std::string user; + std::string domain; + if (pos != std::string::npos) { + user = user_and_domain.substr(0, pos); + domain = user_and_domain.substr(pos + 1); + } else { + user = user_and_domain; + } + + std::vector<std::string> permissions = + cmSystemTools::tokenize(permission_string, ","); + + this->SourceWriter.BeginElement("Permission"); + this->SourceWriter.AddAttribute("User", user); + if (!domain.empty()) { + this->SourceWriter.AddAttribute("Domain", domain); + } + for (size_t i = 0; i < permissions.size(); ++i) { + this->EmitBooleanAttribute(entry, + cmSystemTools::TrimWhitespace(permissions[i])); + } + this->SourceWriter.EndElement("Permission"); +} + +void cmWIXAccessControlList::ReportError(std::string const& entry, + std::string const& message) +{ + cmCPackLogger(cmCPackLog::LOG_ERROR, "Failed processing ACL entry '" + << entry << "': " << message << std::endl); +} + +bool cmWIXAccessControlList::IsBooleanAttribute(std::string const& name) +{ + static const char* validAttributes[] = { + /* clang-format needs this comment to break after the opening brace */ + "Append", + "ChangePermission", + "CreateChild", + "CreateFile", + "CreateLink", + "CreateSubkeys", + "Delete", + "DeleteChild", + "EnumerateSubkeys", + "Execute", + "FileAllRights", + "GenericAll", + "GenericExecute", + "GenericRead", + "GenericWrite", + "Notify", + "Read", + "ReadAttributes", + "ReadExtendedAttributes", + "ReadPermission", + "SpecificRightsAll", + "Synchronize", + "TakeOwnership", + "Traverse", + "Write", + "WriteAttributes", + "WriteExtendedAttributes", + 0 + }; + + size_t i = 0; + while (validAttributes[i]) { + if (name == validAttributes[i++]) + return true; + } + + return false; +} + +void cmWIXAccessControlList::EmitBooleanAttribute(std::string const& entry, + std::string const& name) +{ + if (!this->IsBooleanAttribute(name)) { + std::ostringstream message; + message << "Unknown boolean attribute '" << name << "'"; + this->ReportError(entry, message.str()); + } + + this->SourceWriter.AddAttribute(name, "yes"); +} diff --git a/Source/CPack/WiX/cmWIXAccessControlList.h b/Source/CPack/WiX/cmWIXAccessControlList.h new file mode 100644 index 0000000..a1ac593 --- /dev/null +++ b/Source/CPack/WiX/cmWIXAccessControlList.h @@ -0,0 +1,44 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2014 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmWIXAccessControlList_h +#define cmWIXAccessControlList_h + +#include "cmWIXSourceWriter.h" + +#include <CPack/cmCPackLog.h> +#include <cmInstalledFile.h> + +class cmWIXAccessControlList +{ +public: + cmWIXAccessControlList(cmCPackLog* logger, + cmInstalledFile const& installedFile, + cmWIXSourceWriter& sourceWriter); + + bool Apply(); + +private: + void CreatePermissionElement(std::string const& entry); + + void ReportError(std::string const& entry, std::string const& message); + + bool IsBooleanAttribute(std::string const& name); + + void EmitBooleanAttribute(std::string const& entry, std::string const& name); + + cmCPackLog* Logger; + cmInstalledFile const& InstalledFile; + cmWIXSourceWriter& SourceWriter; +}; + +#endif diff --git a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx new file mode 100644 index 0000000..de64059 --- /dev/null +++ b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx @@ -0,0 +1,88 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2014 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmWIXDirectoriesSourceWriter.h" + +cmWIXDirectoriesSourceWriter::cmWIXDirectoriesSourceWriter( + cmCPackLog* logger, std::string const& filename) + : cmWIXSourceWriter(logger, filename) +{ +} + +void cmWIXDirectoriesSourceWriter::EmitStartMenuFolder( + std::string const& startMenuFolder) +{ + BeginElement("Directory"); + AddAttribute("Id", "ProgramMenuFolder"); + + BeginElement("Directory"); + AddAttribute("Id", "PROGRAM_MENU_FOLDER"); + AddAttribute("Name", startMenuFolder); + EndElement("Directory"); + + EndElement("Directory"); +} + +void cmWIXDirectoriesSourceWriter::EmitDesktopFolder() +{ + BeginElement("Directory"); + AddAttribute("Id", "DesktopFolder"); + AddAttribute("Name", "Desktop"); + EndElement("Directory"); +} + +void cmWIXDirectoriesSourceWriter::EmitStartupFolder() +{ + BeginElement("Directory"); + AddAttribute("Id", "StartupFolder"); + AddAttribute("Name", "Startup"); + EndElement("Directory"); +} + +size_t cmWIXDirectoriesSourceWriter::BeginInstallationPrefixDirectory( + std::string const& programFilesFolderId, + std::string const& installRootString) +{ + BeginElement("Directory"); + AddAttribute("Id", programFilesFolderId); + + std::vector<std::string> installRoot; + + cmSystemTools::SplitPath(installRootString.c_str(), installRoot); + + if (!installRoot.empty() && installRoot.back().empty()) { + installRoot.pop_back(); + } + + for (size_t i = 1; i < installRoot.size(); ++i) { + BeginElement("Directory"); + + if (i == installRoot.size() - 1) { + AddAttribute("Id", "INSTALL_ROOT"); + } else { + std::ostringstream tmp; + tmp << "INSTALL_PREFIX_" << i; + AddAttribute("Id", tmp.str()); + } + + AddAttribute("Name", installRoot[i]); + } + + return installRoot.size(); +} + +void cmWIXDirectoriesSourceWriter::EndInstallationPrefixDirectory(size_t size) +{ + for (size_t i = 0; i < size; ++i) { + EndElement("Directory"); + } +} diff --git a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h new file mode 100644 index 0000000..023f4b8 --- /dev/null +++ b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.h @@ -0,0 +1,44 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2014 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmWIXDirectoriesSourceWriter_h +#define cmWIXDirectoriesSourceWriter_h + +#include "cmWIXSourceWriter.h" + +#include <CPack/cmCPackGenerator.h> + +#include <string> + +/** \class cmWIXDirectoriesSourceWriter + * \brief Helper class to generate directories.wxs + */ +class cmWIXDirectoriesSourceWriter : public cmWIXSourceWriter +{ +public: + cmWIXDirectoriesSourceWriter(cmCPackLog* logger, + std::string const& filename); + + void EmitStartMenuFolder(std::string const& startMenuFolder); + + void EmitDesktopFolder(); + + void EmitStartupFolder(); + + size_t BeginInstallationPrefixDirectory( + std::string const& programFilesFolderId, + std::string const& installRootString); + + void EndInstallationPrefixDirectory(size_t size); +}; + +#endif diff --git a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx new file mode 100644 index 0000000..1747b62 --- /dev/null +++ b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx @@ -0,0 +1,101 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2014 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmWIXFeaturesSourceWriter.h" + +cmWIXFeaturesSourceWriter::cmWIXFeaturesSourceWriter( + cmCPackLog* logger, std::string const& filename) + : cmWIXSourceWriter(logger, filename) +{ +} + +void cmWIXFeaturesSourceWriter::CreateCMakePackageRegistryEntry( + std::string const& package, std::string const& upgradeGuid) +{ + BeginElement("Component"); + AddAttribute("Id", "CM_PACKAGE_REGISTRY"); + AddAttribute("Directory", "TARGETDIR"); + AddAttribute("Guid", "*"); + + std::string registryKey = + std::string("Software\\Kitware\\CMake\\Packages\\") + package; + + BeginElement("RegistryValue"); + AddAttribute("Root", "HKLM"); + AddAttribute("Key", registryKey); + AddAttribute("Name", upgradeGuid); + AddAttribute("Type", "string"); + AddAttribute("Value", "[INSTALL_ROOT]"); + AddAttribute("KeyPath", "yes"); + EndElement("RegistryValue"); + + EndElement("Component"); +} + +void cmWIXFeaturesSourceWriter::EmitFeatureForComponentGroup( + cmCPackComponentGroup const& group) +{ + BeginElement("Feature"); + AddAttribute("Id", "CM_G_" + group.Name); + + if (group.IsExpandedByDefault) { + AddAttribute("Display", "expand"); + } + + AddAttributeUnlessEmpty("Title", group.DisplayName); + AddAttributeUnlessEmpty("Description", group.Description); + + for (std::vector<cmCPackComponentGroup*>::const_iterator i = + group.Subgroups.begin(); + i != group.Subgroups.end(); ++i) { + EmitFeatureForComponentGroup(**i); + } + + for (std::vector<cmCPackComponent*>::const_iterator i = + group.Components.begin(); + i != group.Components.end(); ++i) { + EmitFeatureForComponent(**i); + } + + EndElement("Feature"); +} + +void cmWIXFeaturesSourceWriter::EmitFeatureForComponent( + cmCPackComponent const& component) +{ + BeginElement("Feature"); + AddAttribute("Id", "CM_C_" + component.Name); + + AddAttributeUnlessEmpty("Title", component.DisplayName); + AddAttributeUnlessEmpty("Description", component.Description); + + if (component.IsRequired) { + AddAttribute("Absent", "disallow"); + } + + if (component.IsHidden) { + AddAttribute("Display", "hidden"); + } + + if (component.IsDisabledByDefault) { + AddAttribute("Level", "2"); + } + + EndElement("Feature"); +} + +void cmWIXFeaturesSourceWriter::EmitComponentRef(std::string const& id) +{ + BeginElement("ComponentRef"); + AddAttribute("Id", id); + EndElement("ComponentRef"); +} diff --git a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h new file mode 100644 index 0000000..ee9c17a --- /dev/null +++ b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.h @@ -0,0 +1,38 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2014 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmWIXFeaturesSourceWriter_h +#define cmWIXFeaturesSourceWriter_h + +#include "cmWIXSourceWriter.h" + +#include <CPack/cmCPackGenerator.h> + +/** \class cmWIXFeaturesSourceWriter + * \brief Helper class to generate features.wxs + */ +class cmWIXFeaturesSourceWriter : public cmWIXSourceWriter +{ +public: + cmWIXFeaturesSourceWriter(cmCPackLog* logger, std::string const& filename); + + void CreateCMakePackageRegistryEntry(std::string const& package, + std::string const& upgradeGuid); + + void EmitFeatureForComponentGroup(const cmCPackComponentGroup& group); + + void EmitFeatureForComponent(const cmCPackComponent& component); + + void EmitComponentRef(std::string const& id); +}; + +#endif diff --git a/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx b/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx new file mode 100644 index 0000000..9a143cc --- /dev/null +++ b/Source/CPack/WiX/cmWIXFilesSourceWriter.cxx @@ -0,0 +1,170 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2014-2015 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmWIXFilesSourceWriter.h" + +#include "cmWIXAccessControlList.h" + +#include <cmInstalledFile.h> + +#include <sys/types.h> +// include sys/stat.h after sys/types.h +#include <sys/stat.h> + +cmWIXFilesSourceWriter::cmWIXFilesSourceWriter(cmCPackLog* logger, + std::string const& filename) + : cmWIXSourceWriter(logger, filename) +{ +} + +void cmWIXFilesSourceWriter::EmitShortcut(std::string const& id, + cmWIXShortcut const& shortcut, + std::string const& shortcutPrefix, + size_t shortcutIndex) +{ + std::ostringstream shortcutId; + shortcutId << shortcutPrefix << id; + + if (shortcutIndex > 0) { + shortcutId << "_" << shortcutIndex; + } + + std::string fileId = std::string("CM_F") + id; + + BeginElement("Shortcut"); + AddAttribute("Id", shortcutId.str()); + AddAttribute("Name", shortcut.label); + std::string target = "[#" + fileId + "]"; + AddAttribute("Target", target); + AddAttribute("WorkingDirectory", shortcut.workingDirectoryId); + EndElement("Shortcut"); +} + +void cmWIXFilesSourceWriter::EmitRemoveFolder(std::string const& id) +{ + BeginElement("RemoveFolder"); + AddAttribute("Id", id); + AddAttribute("On", "uninstall"); + EndElement("RemoveFolder"); +} + +void cmWIXFilesSourceWriter::EmitInstallRegistryValue( + std::string const& registryKey, std::string const& cpackComponentName, + std::string const& suffix) +{ + std::string valueName; + if (!cpackComponentName.empty()) { + valueName = cpackComponentName + "_"; + } + + valueName += "installed"; + valueName += suffix; + + BeginElement("RegistryValue"); + AddAttribute("Root", "HKCU"); + AddAttribute("Key", registryKey); + AddAttribute("Name", valueName); + AddAttribute("Type", "integer"); + AddAttribute("Value", "1"); + AddAttribute("KeyPath", "yes"); + EndElement("RegistryValue"); +} + +void cmWIXFilesSourceWriter::EmitUninstallShortcut( + std::string const& packageName) +{ + BeginElement("Shortcut"); + AddAttribute("Id", "UNINSTALL"); + AddAttribute("Name", "Uninstall " + packageName); + AddAttribute("Description", "Uninstalls " + packageName); + AddAttribute("Target", "[SystemFolder]msiexec.exe"); + AddAttribute("Arguments", "/x [ProductCode]"); + EndElement("Shortcut"); +} + +std::string cmWIXFilesSourceWriter::EmitComponentCreateFolder( + std::string const& directoryId, std::string const& guid, + cmInstalledFile const* installedFile) +{ + std::string componentId = std::string("CM_C_EMPTY_") + directoryId; + + BeginElement("DirectoryRef"); + AddAttribute("Id", directoryId); + + BeginElement("Component"); + AddAttribute("Id", componentId); + AddAttribute("Guid", guid); + + BeginElement("CreateFolder"); + + if (installedFile) { + cmWIXAccessControlList acl(Logger, *installedFile, *this); + acl.Apply(); + } + + EndElement("CreateFolder"); + EndElement("Component"); + EndElement("DirectoryRef"); + + return componentId; +} + +std::string cmWIXFilesSourceWriter::EmitComponentFile( + std::string const& directoryId, std::string const& id, + std::string const& filePath, cmWIXPatch& patch, + cmInstalledFile const* installedFile) +{ + std::string componentId = std::string("CM_C") + id; + std::string fileId = std::string("CM_F") + id; + + BeginElement("DirectoryRef"); + AddAttribute("Id", directoryId); + + BeginElement("Component"); + AddAttribute("Id", componentId); + AddAttribute("Guid", "*"); + + if (installedFile) { + if (installedFile->GetPropertyAsBool("CPACK_NEVER_OVERWRITE")) { + AddAttribute("NeverOverwrite", "yes"); + } + if (installedFile->GetPropertyAsBool("CPACK_PERMANENT")) { + AddAttribute("Permanent", "yes"); + } + } + + BeginElement("File"); + AddAttribute("Id", fileId); + AddAttribute("Source", filePath); + AddAttribute("KeyPath", "yes"); + + mode_t fileMode = 0; + cmSystemTools::GetPermissions(filePath.c_str(), fileMode); + + if (!(fileMode & S_IWRITE)) { + AddAttribute("ReadOnly", "yes"); + } + + if (installedFile) { + cmWIXAccessControlList acl(Logger, *installedFile, *this); + acl.Apply(); + } + + patch.ApplyFragment(fileId, *this); + EndElement("File"); + + patch.ApplyFragment(componentId, *this); + EndElement("Component"); + EndElement("DirectoryRef"); + + return componentId; +} diff --git a/Source/CPack/WiX/cmWIXFilesSourceWriter.h b/Source/CPack/WiX/cmWIXFilesSourceWriter.h new file mode 100644 index 0000000..c577e5b --- /dev/null +++ b/Source/CPack/WiX/cmWIXFilesSourceWriter.h @@ -0,0 +1,52 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2014-2015 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmWIXFilesSourceWriter_h +#define cmWIXFilesSourceWriter_h + +#include "cmWIXSourceWriter.h" + +#include "cmWIXPatch.h" +#include "cmWIXShortcut.h" + +#include <CPack/cmCPackGenerator.h> + +/** \class cmWIXFilesSourceWriter + * \brief Helper class to generate files.wxs + */ +class cmWIXFilesSourceWriter : public cmWIXSourceWriter +{ +public: + cmWIXFilesSourceWriter(cmCPackLog* logger, std::string const& filename); + + void EmitShortcut(std::string const& id, cmWIXShortcut const& shortcut, + std::string const& shortcutPrefix, size_t shortcutIndex); + + void EmitRemoveFolder(std::string const& id); + + void EmitInstallRegistryValue(std::string const& registryKey, + std::string const& cpackComponentName, + std::string const& suffix); + + void EmitUninstallShortcut(std::string const& packageName); + + std::string EmitComponentCreateFolder(std::string const& directoryId, + std::string const& guid, + cmInstalledFile const* installedFile); + + std::string EmitComponentFile(std::string const& directoryId, + std::string const& id, + std::string const& filePath, cmWIXPatch& patch, + cmInstalledFile const* installedFile); +}; + +#endif diff --git a/Source/CPack/WiX/cmWIXPatch.cxx b/Source/CPack/WiX/cmWIXPatch.cxx new file mode 100644 index 0000000..c9d010e --- /dev/null +++ b/Source/CPack/WiX/cmWIXPatch.cxx @@ -0,0 +1,105 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2014 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmWIXPatch.h" + +#include <CPack/cmCPackGenerator.h> + +cmWIXPatch::cmWIXPatch(cmCPackLog* logger) + : Logger(logger) +{ +} + +bool cmWIXPatch::LoadFragments(std::string const& patchFilePath) +{ + cmWIXPatchParser parser(Fragments, Logger); + if (!parser.ParseFile(patchFilePath.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Failed parsing XML patch file: '" + << patchFilePath << "'" << std::endl); + return false; + } + + return true; +} + +void cmWIXPatch::ApplyFragment(std::string const& id, + cmWIXSourceWriter& writer) +{ + cmWIXPatchParser::fragment_map_t::iterator i = Fragments.find(id); + if (i == Fragments.end()) + return; + + const cmWIXPatchElement& fragment = i->second; + + this->ApplyElementChildren(fragment, writer); + + Fragments.erase(i); +} + +void cmWIXPatch::ApplyElementChildren(const cmWIXPatchElement& element, + cmWIXSourceWriter& writer) +{ + for (cmWIXPatchElement::child_list_t::const_iterator j = + element.children.begin(); + j != element.children.end(); ++j) { + cmWIXPatchNode* node = *j; + + switch (node->type()) { + case cmWIXPatchNode::ELEMENT: + ApplyElement(dynamic_cast<const cmWIXPatchElement&>(*node), writer); + break; + case cmWIXPatchNode::TEXT: + writer.AddTextNode(dynamic_cast<const cmWIXPatchText&>(*node).text); + break; + } + } +} + +void cmWIXPatch::ApplyElement(const cmWIXPatchElement& element, + cmWIXSourceWriter& writer) +{ + writer.BeginElement(element.name); + + for (cmWIXPatchElement::attributes_t::const_iterator i = + element.attributes.begin(); + i != element.attributes.end(); ++i) { + writer.AddAttribute(i->first, i->second); + } + + this->ApplyElementChildren(element, writer); + + writer.EndElement(element.name); +} + +bool cmWIXPatch::CheckForUnappliedFragments() +{ + std::string fragmentList; + for (cmWIXPatchParser::fragment_map_t::const_iterator i = Fragments.begin(); + i != Fragments.end(); ++i) { + if (!fragmentList.empty()) { + fragmentList += ", "; + } + + fragmentList += "'"; + fragmentList += i->first; + fragmentList += "'"; + } + + if (!fragmentList.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Some XML patch fragments did not have matching IDs: " + << fragmentList << std::endl); + return false; + } + + return true; +} diff --git a/Source/CPack/WiX/cmWIXPatch.h b/Source/CPack/WiX/cmWIXPatch.h new file mode 100644 index 0000000..57b74cd --- /dev/null +++ b/Source/CPack/WiX/cmWIXPatch.h @@ -0,0 +1,47 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2014 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmWIXPatch_h +#define cmWIXPatch_h + +#include "cmWIXPatchParser.h" +#include "cmWIXSourceWriter.h" + +#include <string> + +/** \class cmWIXPatch + * \brief Class that maintains and applies patch fragments + */ +class cmWIXPatch +{ +public: + cmWIXPatch(cmCPackLog* logger); + + bool LoadFragments(std::string const& patchFilePath); + + void ApplyFragment(std::string const& id, cmWIXSourceWriter& writer); + + bool CheckForUnappliedFragments(); + +private: + void ApplyElementChildren(const cmWIXPatchElement& element, + cmWIXSourceWriter& writer); + + void ApplyElement(const cmWIXPatchElement& element, + cmWIXSourceWriter& writer); + + cmCPackLog* Logger; + + cmWIXPatchParser::fragment_map_t Fragments; +}; + +#endif diff --git a/Source/CPack/WiX/cmWIXPatchParser.cxx b/Source/CPack/WiX/cmWIXPatchParser.cxx new file mode 100644 index 0000000..449a70b --- /dev/null +++ b/Source/CPack/WiX/cmWIXPatchParser.cxx @@ -0,0 +1,156 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2013 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmWIXPatchParser.h" + +#include <CPack/cmCPackGenerator.h> + +#include <cm_expat.h> + +cmWIXPatchNode::Type cmWIXPatchText::type() +{ + return cmWIXPatchNode::TEXT; +} + +cmWIXPatchNode::Type cmWIXPatchElement::type() +{ + return cmWIXPatchNode::ELEMENT; +} + +cmWIXPatchNode::~cmWIXPatchNode() +{ +} + +cmWIXPatchElement::~cmWIXPatchElement() +{ + for (child_list_t::iterator i = children.begin(); i != children.end(); ++i) { + delete *i; + } +} + +cmWIXPatchParser::cmWIXPatchParser(fragment_map_t& fragments, + cmCPackLog* logger) + : Logger(logger) + , State(BEGIN_DOCUMENT) + , Valid(true) + , Fragments(fragments) +{ +} + +void cmWIXPatchParser::StartElement(const std::string& name, const char** atts) +{ + if (State == BEGIN_DOCUMENT) { + if (name == "CPackWiXPatch") { + State = BEGIN_FRAGMENTS; + } else { + ReportValidationError("Expected root element 'CPackWiXPatch'"); + } + } else if (State == BEGIN_FRAGMENTS) { + if (name == "CPackWiXFragment") { + State = INSIDE_FRAGMENT; + StartFragment(atts); + } else { + ReportValidationError("Expected 'CPackWixFragment' element"); + } + } else if (State == INSIDE_FRAGMENT) { + cmWIXPatchElement& parent = *ElementStack.back(); + + cmWIXPatchElement* element = new cmWIXPatchElement; + parent.children.push_back(element); + + element->name = name; + + for (size_t i = 0; atts[i]; i += 2) { + std::string key = atts[i]; + std::string value = atts[i + 1]; + + element->attributes[key] = value; + } + + ElementStack.push_back(element); + } +} + +void cmWIXPatchParser::StartFragment(const char** attributes) +{ + for (size_t i = 0; attributes[i]; i += 2) { + std::string key = attributes[i]; + std::string value = attributes[i + 1]; + + if (key == "Id") { + if (Fragments.find(value) != Fragments.end()) { + std::ostringstream tmp; + tmp << "Invalid reuse of 'CPackWixFragment' 'Id': " << value; + ReportValidationError(tmp.str()); + } + + ElementStack.push_back(&Fragments[value]); + } else { + ReportValidationError( + "The only allowed 'CPackWixFragment' attribute is 'Id'"); + } + } +} + +void cmWIXPatchParser::EndElement(const std::string& name) +{ + if (State == INSIDE_FRAGMENT) { + if (name == "CPackWiXFragment") { + State = BEGIN_FRAGMENTS; + ElementStack.clear(); + } else { + ElementStack.pop_back(); + } + } +} + +void cmWIXPatchParser::CharacterDataHandler(const char* data, int length) +{ + const char* whitespace = "\x20\x09\x0d\x0a"; + + if (State == INSIDE_FRAGMENT) { + cmWIXPatchElement& parent = *ElementStack.back(); + + std::string text(data, length); + + std::string::size_type first = text.find_first_not_of(whitespace); + std::string::size_type last = text.find_last_not_of(whitespace); + + if (first != std::string::npos && last != std::string::npos) { + cmWIXPatchText* text_node = new cmWIXPatchText; + text_node->text = text.substr(first, last - first + 1); + + parent.children.push_back(text_node); + } + } +} + +void cmWIXPatchParser::ReportError(int line, int column, const char* msg) +{ + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error while processing XML patch file at " + << line << ":" << column << ": " << msg << std::endl); + Valid = false; +} + +void cmWIXPatchParser::ReportValidationError(std::string const& message) +{ + ReportError( + XML_GetCurrentLineNumber(static_cast<XML_Parser>(this->Parser)), + XML_GetCurrentColumnNumber(static_cast<XML_Parser>(this->Parser)), + message.c_str()); +} + +bool cmWIXPatchParser::IsValid() const +{ + return Valid; +} diff --git a/Source/CPack/WiX/cmWIXPatchParser.h b/Source/CPack/WiX/cmWIXPatchParser.h new file mode 100644 index 0000000..f9b85bd --- /dev/null +++ b/Source/CPack/WiX/cmWIXPatchParser.h @@ -0,0 +1,100 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2013 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackWIXPatchParser_h +#define cmCPackWIXPatchParser_h + +#include <CPack/cmCPackLog.h> + +#include <cmXMLParser.h> + +#include <list> +#include <map> + +struct cmWIXPatchNode +{ + enum Type + { + TEXT, + ELEMENT + }; + + virtual ~cmWIXPatchNode(); + + virtual Type type() = 0; +}; + +struct cmWIXPatchText : public cmWIXPatchNode +{ + virtual Type type(); + + std::string text; +}; + +struct cmWIXPatchElement : cmWIXPatchNode +{ + virtual Type type(); + + ~cmWIXPatchElement(); + + typedef std::list<cmWIXPatchNode*> child_list_t; + typedef std::map<std::string, std::string> attributes_t; + + std::string name; + child_list_t children; + attributes_t attributes; +}; + +/** \class cmWIXPatchParser + * \brief Helper class that parses XML patch files (CPACK_WIX_PATCH_FILE) + */ +class cmWIXPatchParser : public cmXMLParser +{ +public: + typedef std::map<std::string, cmWIXPatchElement> fragment_map_t; + + cmWIXPatchParser(fragment_map_t& Fragments, cmCPackLog* logger); + +private: + virtual void StartElement(const std::string& name, const char** atts); + + void StartFragment(const char** attributes); + + virtual void EndElement(const std::string& name); + + virtual void CharacterDataHandler(const char* data, int length); + + virtual void ReportError(int line, int column, const char* msg); + + void ReportValidationError(std::string const& message); + + bool IsValid() const; + + cmCPackLog* Logger; + + enum ParserState + { + BEGIN_DOCUMENT, + BEGIN_FRAGMENTS, + INSIDE_FRAGMENT + }; + + ParserState State; + + bool Valid; + + fragment_map_t& Fragments; + + std::list<cmWIXPatchElement*> ElementStack; +}; + +#endif diff --git a/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx b/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx new file mode 100644 index 0000000..f3dbcb9 --- /dev/null +++ b/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx @@ -0,0 +1,196 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2012 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmWIXRichTextFormatWriter.h" + +#include <cmVersion.h> + +cmWIXRichTextFormatWriter::cmWIXRichTextFormatWriter( + std::string const& filename) + : File(filename.c_str(), std::ios::binary) +{ + StartGroup(); + WriteHeader(); + WriteDocumentPrefix(); +} + +cmWIXRichTextFormatWriter::~cmWIXRichTextFormatWriter() +{ + EndGroup(); + + /* I haven't seen this in the RTF spec but + * wordpad terminates its RTF like this */ + File << "\r\n"; + File.put(0); +} + +void cmWIXRichTextFormatWriter::AddText(std::string const& text) +{ + typedef unsigned char rtf_byte_t; + + for (size_t i = 0; i < text.size(); ++i) { + rtf_byte_t c = rtf_byte_t(text[i]); + + switch (c) { + case '\\': + File << "\\\\"; + break; + case '{': + File << "\\{"; + break; + case '}': + File << "\\}"; + break; + case '\n': + File << "\\par\r\n"; + break; + case '\r': + continue; + default: { + if (c <= 0x7F) { + File << c; + } else { + if (c <= 0xC0) { + EmitInvalidCodepoint(c); + } else if (c < 0xE0 && i + 1 < text.size()) { + EmitUnicodeCodepoint((text[i + 1] & 0x3F) | ((c & 0x1F) << 6)); + i += 1; + } else if (c < 0xF0 && i + 2 < text.size()) { + EmitUnicodeCodepoint((text[i + 2] & 0x3F) | + ((text[i + 1] & 0x3F) << 6) | + ((c & 0xF) << 12)); + i += 2; + } else if (c < 0xF8 && i + 3 < text.size()) { + EmitUnicodeCodepoint( + (text[i + 3] & 0x3F) | ((text[i + 2] & 0x3F) << 6) | + ((text[i + 1] & 0x3F) << 12) | ((c & 0x7) << 18)); + i += 3; + } else { + EmitInvalidCodepoint(c); + } + } + } break; + } + } +} + +void cmWIXRichTextFormatWriter::WriteHeader() +{ + ControlWord("rtf1"); + ControlWord("ansi"); + ControlWord("ansicpg1252"); + ControlWord("deff0"); + ControlWord("deflang1031"); + + WriteFontTable(); + WriteColorTable(); + WriteGenerator(); +} + +void cmWIXRichTextFormatWriter::WriteFontTable() +{ + StartGroup(); + ControlWord("fonttbl"); + + StartGroup(); + ControlWord("f0"); + ControlWord("fswiss"); + ControlWord("fcharset0 Arial;"); + EndGroup(); + + EndGroup(); +} + +void cmWIXRichTextFormatWriter::WriteColorTable() +{ + StartGroup(); + ControlWord("colortbl ;"); + ControlWord("red255"); + ControlWord("green0"); + ControlWord("blue0;"); + ControlWord("red0"); + ControlWord("green255"); + ControlWord("blue0;"); + ControlWord("red0"); + ControlWord("green0"); + ControlWord("blue255;"); + EndGroup(); +} + +void cmWIXRichTextFormatWriter::WriteGenerator() +{ + StartGroup(); + NewControlWord("generator"); + File << " CPack WiX Generator (" << cmVersion::GetCMakeVersion() << ");"; + EndGroup(); +} + +void cmWIXRichTextFormatWriter::WriteDocumentPrefix() +{ + ControlWord("viewkind4"); + ControlWord("uc1"); + ControlWord("pard"); + ControlWord("f0"); + ControlWord("fs20"); +} + +void cmWIXRichTextFormatWriter::ControlWord(std::string const& keyword) +{ + File << "\\" << keyword; +} + +void cmWIXRichTextFormatWriter::NewControlWord(std::string const& keyword) +{ + File << "\\*\\" << keyword; +} + +void cmWIXRichTextFormatWriter::StartGroup() +{ + File.put('{'); +} + +void cmWIXRichTextFormatWriter::EndGroup() +{ + File.put('}'); +} + +void cmWIXRichTextFormatWriter::EmitUnicodeCodepoint(int c) +{ + // Do not emit byte order mark (BOM) + if (c == 0xFEFF) { + return; + } else if (c <= 0xFFFF) { + EmitUnicodeSurrogate(c); + } else { + c -= 0x10000; + EmitUnicodeSurrogate(((c >> 10) & 0x3FF) + 0xD800); + EmitUnicodeSurrogate((c & 0x3FF) + 0xDC00); + } +} + +void cmWIXRichTextFormatWriter::EmitUnicodeSurrogate(int c) +{ + ControlWord("u"); + if (c <= 32767) { + File << c; + } else { + File << (c - 65536); + } + File << "?"; +} + +void cmWIXRichTextFormatWriter::EmitInvalidCodepoint(int c) +{ + ControlWord("cf1 "); + File << "[INVALID-BYTE-" << int(c) << "]"; + ControlWord("cf0 "); +} diff --git a/Source/CPack/WiX/cmWIXRichTextFormatWriter.h b/Source/CPack/WiX/cmWIXRichTextFormatWriter.h new file mode 100644 index 0000000..acf1fa6 --- /dev/null +++ b/Source/CPack/WiX/cmWIXRichTextFormatWriter.h @@ -0,0 +1,54 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2012 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmWIXRichTextFormatWriter_h +#define cmWIXRichTextFormatWriter_h + +#include "cmStandardIncludes.h" + +#include <cmsys/FStream.hxx> + +/** \class cmWIXRichtTextFormatWriter + * \brief Helper class to generate Rich Text Format (RTF) documents + * from plain text (e.g. for license and welcome text) + */ +class cmWIXRichTextFormatWriter +{ +public: + cmWIXRichTextFormatWriter(std::string const& filename); + ~cmWIXRichTextFormatWriter(); + + void AddText(std::string const& text); + +private: + void WriteHeader(); + void WriteFontTable(); + void WriteColorTable(); + void WriteGenerator(); + + void WriteDocumentPrefix(); + + void ControlWord(std::string const& keyword); + void NewControlWord(std::string const& keyword); + + void StartGroup(); + void EndGroup(); + + void EmitUnicodeCodepoint(int c); + void EmitUnicodeSurrogate(int c); + + void EmitInvalidCodepoint(int c); + + cmsys::ofstream File; +}; + +#endif diff --git a/Source/CPack/WiX/cmWIXShortcut.cxx b/Source/CPack/WiX/cmWIXShortcut.cxx new file mode 100644 index 0000000..2685a23 --- /dev/null +++ b/Source/CPack/WiX/cmWIXShortcut.cxx @@ -0,0 +1,115 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2015 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmWIXShortcut.h" + +#include "cmWIXFilesSourceWriter.h" + +void cmWIXShortcuts::insert(Type type, std::string const& id, + cmWIXShortcut const& shortcut) +{ + this->Shortcuts[type][id].push_back(shortcut); +} + +bool cmWIXShortcuts::empty(Type type) const +{ + return this->Shortcuts.find(type) == this->Shortcuts.end(); +} + +bool cmWIXShortcuts::EmitShortcuts( + Type type, std::string const& registryKey, + std::string const& cpackComponentName, + cmWIXFilesSourceWriter& fileDefinitions) const +{ + shortcut_type_map_t::const_iterator i = this->Shortcuts.find(type); + + if (i == this->Shortcuts.end()) { + return false; + } + + shortcut_id_map_t const& id_map = i->second; + + std::string shortcutPrefix; + std::string registrySuffix; + + switch (type) { + case START_MENU: + shortcutPrefix = "CM_S"; + break; + case DESKTOP: + shortcutPrefix = "CM_DS"; + registrySuffix = "_desktop"; + break; + case STARTUP: + shortcutPrefix = "CM_SS"; + registrySuffix = "_startup"; + break; + default: + return false; + } + + for (shortcut_id_map_t::const_iterator j = id_map.begin(); j != id_map.end(); + ++j) { + std::string const& id = j->first; + shortcut_list_t const& shortcutList = j->second; + + for (size_t shortcutListIndex = 0; shortcutListIndex < shortcutList.size(); + ++shortcutListIndex) { + cmWIXShortcut const& shortcut = shortcutList[shortcutListIndex]; + fileDefinitions.EmitShortcut(id, shortcut, shortcutPrefix, + shortcutListIndex); + } + } + + fileDefinitions.EmitInstallRegistryValue(registryKey, cpackComponentName, + registrySuffix); + + return true; +} + +void cmWIXShortcuts::AddShortcutTypes(std::set<Type>& types) +{ + for (shortcut_type_map_t::const_iterator i = this->Shortcuts.begin(); + i != this->Shortcuts.end(); ++i) { + types.insert(i->first); + } +} + +void cmWIXShortcuts::CreateFromProperties(std::string const& id, + std::string const& directoryId, + cmInstalledFile const& installedFile) +{ + CreateFromProperty("CPACK_START_MENU_SHORTCUTS", START_MENU, id, directoryId, + installedFile); + + CreateFromProperty("CPACK_DESKTOP_SHORTCUTS", DESKTOP, id, directoryId, + installedFile); + + CreateFromProperty("CPACK_STARTUP_SHORTCUTS", STARTUP, id, directoryId, + installedFile); +} + +void cmWIXShortcuts::CreateFromProperty(std::string const& propertyName, + Type type, std::string const& id, + std::string const& directoryId, + cmInstalledFile const& installedFile) +{ + std::vector<std::string> list; + installedFile.GetPropertyAsList(propertyName, list); + + for (size_t i = 0; i < list.size(); ++i) { + cmWIXShortcut shortcut; + shortcut.label = list[i]; + shortcut.workingDirectoryId = directoryId; + insert(type, id, shortcut); + } +} diff --git a/Source/CPack/WiX/cmWIXShortcut.h b/Source/CPack/WiX/cmWIXShortcut.h new file mode 100644 index 0000000..593ba34 --- /dev/null +++ b/Source/CPack/WiX/cmWIXShortcut.h @@ -0,0 +1,70 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2014-2015 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmWIXShortcut_h +#define cmWIXShortcut_h + +#include <cmInstalledFile.h> + +#include <map> +#include <set> +#include <string> +#include <vector> + +class cmWIXFilesSourceWriter; + +struct cmWIXShortcut +{ + std::string label; + std::string workingDirectoryId; +}; + +class cmWIXShortcuts +{ +public: + enum Type + { + START_MENU, + DESKTOP, + STARTUP + }; + + typedef std::vector<cmWIXShortcut> shortcut_list_t; + typedef std::map<std::string, shortcut_list_t> shortcut_id_map_t; + + void insert(Type type, std::string const& id, cmWIXShortcut const& shortcut); + + bool empty(Type type) const; + + bool EmitShortcuts(Type type, std::string const& registryKey, + std::string const& cpackComponentName, + cmWIXFilesSourceWriter& fileDefinitions) const; + + void AddShortcutTypes(std::set<Type>& types); + + void CreateFromProperties(std::string const& id, + std::string const& directoryId, + cmInstalledFile const& installedFile); + +private: + typedef std::map<Type, shortcut_id_map_t> shortcut_type_map_t; + + void CreateFromProperty(std::string const& propertyName, Type type, + std::string const& id, + std::string const& directoryId, + cmInstalledFile const& installedFile); + + shortcut_type_map_t Shortcuts; + shortcut_id_map_t EmptyIdMap; +}; + +#endif diff --git a/Source/CPack/WiX/cmWIXSourceWriter.cxx b/Source/CPack/WiX/cmWIXSourceWriter.cxx new file mode 100644 index 0000000..2c0384e --- /dev/null +++ b/Source/CPack/WiX/cmWIXSourceWriter.cxx @@ -0,0 +1,216 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2012 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmWIXSourceWriter.h" + +#include <CPack/cmCPackGenerator.h> + +#include <windows.h> + +cmWIXSourceWriter::cmWIXSourceWriter(cmCPackLog* logger, + std::string const& filename, + bool isIncludeFile) + : Logger(logger) + , File(filename.c_str()) + , State(DEFAULT) + , SourceFilename(filename) +{ + WriteXMLDeclaration(); + + if (isIncludeFile) { + BeginElement("Include"); + } else { + BeginElement("Wix"); + } + + AddAttribute("xmlns", "http://schemas.microsoft.com/wix/2006/wi"); +} + +cmWIXSourceWriter::~cmWIXSourceWriter() +{ + if (Elements.size() > 1) { + cmCPackLogger(cmCPackLog::LOG_ERROR, Elements.size() - 1 + << " WiX elements were still open when closing '" + << SourceFilename << "'" << std::endl); + return; + } + + EndElement(Elements.back()); +} + +void cmWIXSourceWriter::BeginElement(std::string const& name) +{ + if (State == BEGIN) { + File << ">"; + } + + File << "\n"; + Indent(Elements.size()); + File << "<" << name; + + Elements.push_back(name); + State = BEGIN; +} + +void cmWIXSourceWriter::EndElement(std::string const& name) +{ + if (Elements.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "can not end WiX element with no open elements in '" + << SourceFilename << "'" << std::endl); + return; + } + + if (Elements.back() != name) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "WiX element <" + << Elements.back() << "> can not be closed by </" << name + << "> in '" << SourceFilename << "'" << std::endl); + return; + } + + if (State == DEFAULT) { + File << "\n"; + Indent(Elements.size() - 1); + File << "</" << Elements.back() << ">"; + } else { + File << "/>"; + } + + Elements.pop_back(); + State = DEFAULT; +} + +void cmWIXSourceWriter::AddTextNode(std::string const& text) +{ + if (State == BEGIN) { + File << ">"; + } + + if (Elements.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "can not add text without open WiX element in '" + << SourceFilename << "'" << std::endl); + return; + } + + File << this->EscapeAttributeValue(text); + State = DEFAULT; +} + +void cmWIXSourceWriter::AddProcessingInstruction(std::string const& target, + std::string const& content) +{ + if (State == BEGIN) { + File << ">"; + } + + File << "\n"; + Indent(Elements.size()); + File << "<?" << target << " " << content << "?>"; + + State = DEFAULT; +} + +void cmWIXSourceWriter::AddAttribute(std::string const& key, + std::string const& value) +{ + std::string utf8 = CMakeEncodingToUtf8(value); + + File << " " << key << "=\"" << EscapeAttributeValue(utf8) << '"'; +} + +void cmWIXSourceWriter::AddAttributeUnlessEmpty(std::string const& key, + std::string const& value) +{ + if (!value.empty()) { + AddAttribute(key, value); + } +} + +std::string cmWIXSourceWriter::CMakeEncodingToUtf8(std::string const& value) +{ +#ifdef CMAKE_ENCODING_UTF8 + return value; +#else + if (value.empty()) { + return std::string(); + } + + int characterCount = MultiByteToWideChar( + CP_ACP, 0, value.c_str(), static_cast<int>(value.size()), 0, 0); + + if (characterCount == 0) { + return std::string(); + } + + std::vector<wchar_t> utf16(characterCount); + + MultiByteToWideChar(CP_ACP, 0, value.c_str(), static_cast<int>(value.size()), + &utf16[0], static_cast<int>(utf16.size())); + + int utf8ByteCount = WideCharToMultiByte( + CP_UTF8, 0, &utf16[0], static_cast<int>(utf16.size()), 0, 0, 0, 0); + + if (utf8ByteCount == 0) { + return std::string(); + } + + std::vector<char> utf8(utf8ByteCount); + + WideCharToMultiByte(CP_UTF8, 0, &utf16[0], static_cast<int>(utf16.size()), + &utf8[0], static_cast<int>(utf8.size()), 0, 0); + + return std::string(&utf8[0], utf8.size()); +#endif +} + +void cmWIXSourceWriter::WriteXMLDeclaration() +{ + File << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl; +} + +void cmWIXSourceWriter::Indent(size_t count) +{ + for (size_t i = 0; i < count; ++i) { + File << " "; + } +} + +std::string cmWIXSourceWriter::EscapeAttributeValue(std::string const& value) +{ + std::string result; + result.reserve(value.size()); + + char c = 0; + for (size_t i = 0; i < value.size(); ++i) { + c = value[i]; + switch (c) { + case '<': + result += "<"; + break; + case '>': + result += ">"; + break; + case '&': + result += "&"; + break; + case '"': + result += """; + break; + default: + result += c; + break; + } + } + + return result; +} diff --git a/Source/CPack/WiX/cmWIXSourceWriter.h b/Source/CPack/WiX/cmWIXSourceWriter.h new file mode 100644 index 0000000..4efc026 --- /dev/null +++ b/Source/CPack/WiX/cmWIXSourceWriter.h @@ -0,0 +1,75 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2012 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmWIXSourceWriter_h +#define cmWIXSourceWriter_h + +#include <CPack/cmCPackLog.h> + +#include <cmsys/FStream.hxx> + +#include <string> +#include <vector> + +/** \class cmWIXSourceWriter + * \brief Helper class to generate XML WiX source files + */ +class cmWIXSourceWriter +{ +public: + cmWIXSourceWriter(cmCPackLog* logger, std::string const& filename, + bool isIncludeFile = false); + + ~cmWIXSourceWriter(); + + void BeginElement(std::string const& name); + + void EndElement(std::string const& name); + + void AddTextNode(std::string const& text); + + void AddProcessingInstruction(std::string const& target, + std::string const& content); + + void AddAttribute(std::string const& key, std::string const& value); + + void AddAttributeUnlessEmpty(std::string const& key, + std::string const& value); + + static std::string CMakeEncodingToUtf8(std::string const& value); + +protected: + cmCPackLog* Logger; + +private: + enum State + { + DEFAULT, + BEGIN + }; + + void WriteXMLDeclaration(); + + void Indent(size_t count); + + static std::string EscapeAttributeValue(std::string const& value); + + cmsys::ofstream File; + + State State; + + std::vector<std::string> Elements; + + std::string SourceFilename; +}; + +#endif diff --git a/Source/CPack/bills-comments.txt b/Source/CPack/bills-comments.txt new file mode 100644 index 0000000..c3b4ee8 --- /dev/null +++ b/Source/CPack/bills-comments.txt @@ -0,0 +1,68 @@ +cpack.cxx + +cmCPackGenerators -- creates cmCPackGenericGenerator's via NewGenerator + - a cmCPackGenericGenerator factory + + +cmCPackGenericGenerator::Initialize + this->InitializeInternal + CPACK_INCLUDE_TOPLEVEL_DIRECTORY = 0 turns off + + +// binary package run +cmCPackGenericGenerator::ProcessGenerator // DoPackage + cmCPackGenericGenerator::PrepareNames -- sets a bunch of CPACK_vars + cmCPackGenericGenerator::InstallProject + run preinstall (make preinstall/fast) + call ReadListFile(cmake_install.cmake) + glob recurse in install directory to get list of files + this->CompressFiles with the list of files + + +// source package run +cmCPackGenericGenerator::ProcessGenerator // DoPackage + cmCPackGenericGenerator::PrepareNames -- sets a bunch of CPACK_vars + cmCPackGenericGenerator::InstallProject --> + if set CPACK_INSTALLED_DIRECTORIES + glob the files in that directory + copy those files to the tmp install directory _CPack something + glob recurse in install directory to get list of files + this->CompressFiles with the list of files + + +cmCPackGenericGenerator::InstallProject is used for both source and binary +packages. It is controled based on values set in CPACK_ variables. + + +InstallProject + 1. CPACK_INSTALL_COMMANDS - a list of commands used to install the package + + 2. CPACK_INSTALLED_DIRECTORIES - copy this directory to CPACK_TEMPORARY_DIRECTORY + + 3. CPACK_INSTALL_CMAKE_PROJECTS - a cmake install script + - run make preinstall + - run cmake_install.cmake + - set CMAKE_INSTALL_PREFIX to the temp directory + - CPACK_BUILD_CONFIG check this and set the BUILD_TYPE to it + - ReadListFile on the install script cmake_install.cmake + - run strip on the executables and libraries if CPACK_STRIP_FILES is TRUE + +Recommendations: + +rename cmCPackGenerators to cmCPackGeneratorFactory + +rename cmCPackGenericGenerator --> cmCPackGenerator + +rename cmCPackGenericGenerator::ProcessGenerator -> cmCPackGenerator::DoPackage + + +break up cmCPackGenerator::InstallProject so it calls the following: + +// run user provided install commands + cmCPackGenerator::RunInstallCommands(); +// copy entire directories that need no processing like source trees + cmCPackGenerator::CopyPreInstalledDirectories(); +// run the cmake install scripts if provided + cmCPackGenerator::RunCMakeInstallScripts() + +- diff --git a/Source/CPack/cmCPack7zGenerator.cxx b/Source/CPack/cmCPack7zGenerator.cxx new file mode 100644 index 0000000..b01c216 --- /dev/null +++ b/Source/CPack/cmCPack7zGenerator.cxx @@ -0,0 +1,22 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPack7zGenerator.h" + +cmCPack7zGenerator::cmCPack7zGenerator() + : cmCPackArchiveGenerator(cmArchiveWrite::CompressNone, "7zip") +{ +} + +cmCPack7zGenerator::~cmCPack7zGenerator() +{ +} diff --git a/Source/CPack/cmCPack7zGenerator.h b/Source/CPack/cmCPack7zGenerator.h new file mode 100644 index 0000000..ddbcc34 --- /dev/null +++ b/Source/CPack/cmCPack7zGenerator.h @@ -0,0 +1,36 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPack7zGenerator_h +#define cmCPack7zGenerator_h + +#include "cmCPackArchiveGenerator.h" + +/** \class cmCPack7zGenerator + * \brief A generator for 7z files + */ +class cmCPack7zGenerator : public cmCPackArchiveGenerator +{ +public: + cmCPackTypeMacro(cmCPack7zGenerator, cmCPackArchiveGenerator); + + /** + * Construct generator + */ + cmCPack7zGenerator(); + ~cmCPack7zGenerator() CM_OVERRIDE; + +protected: + const char* GetOutputExtension() CM_OVERRIDE { return ".7z"; } +}; + +#endif diff --git a/Source/CPack/cmCPackArchiveGenerator.cxx b/Source/CPack/cmCPackArchiveGenerator.cxx new file mode 100644 index 0000000..b1f6864 --- /dev/null +++ b/Source/CPack/cmCPackArchiveGenerator.cxx @@ -0,0 +1,273 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackArchiveGenerator.h" + +#include "cmCPackLog.h" +#include "cmGeneratedFileStream.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" +#include "cmake.h" +#include <errno.h> + +#include <cm_libarchive.h> +#include <cmsys/Directory.hxx> +#include <cmsys/SystemTools.hxx> + +cmCPackArchiveGenerator::cmCPackArchiveGenerator(cmArchiveWrite::Compress t, + std::string const& format) +{ + this->Compress = t; + this->ArchiveFormat = format; +} + +cmCPackArchiveGenerator::~cmCPackArchiveGenerator() +{ +} + +int cmCPackArchiveGenerator::InitializeInternal() +{ + this->SetOptionIfNotSet("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "1"); + return this->Superclass::InitializeInternal(); +} +int cmCPackArchiveGenerator::addOneComponentToArchive( + cmArchiveWrite& archive, cmCPackComponent* component) +{ + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + " - packaging component: " << component->Name << std::endl); + // Add the files of this component to the archive + std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); + localToplevel += "/" + component->Name; + std::string dir = cmSystemTools::GetCurrentWorkingDirectory(); + // Change to local toplevel + cmSystemTools::ChangeDirectory(localToplevel); + std::string filePrefix; + if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) { + filePrefix = this->GetOption("CPACK_PACKAGE_FILE_NAME"); + filePrefix += "/"; + } + const char* installPrefix = + this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX"); + if (installPrefix && installPrefix[0] == '/' && installPrefix[1] != 0) { + // add to file prefix and remove the leading '/' + filePrefix += installPrefix + 1; + filePrefix += "/"; + } + std::vector<std::string>::const_iterator fileIt; + for (fileIt = component->Files.begin(); fileIt != component->Files.end(); + ++fileIt) { + std::string rp = filePrefix + *fileIt; + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Adding file: " << rp << std::endl); + archive.Add(rp, 0, CM_NULLPTR, false); + if (!archive) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "ERROR while packaging files: " + << archive.GetError() << std::endl); + return 0; + } + } + // Go back to previous dir + cmSystemTools::ChangeDirectory(dir); + return 1; +} + +/* + * The macro will open/create a file 'filename' + * an declare and open the associated + * cmArchiveWrite 'archive' object. + */ +#define DECLARE_AND_OPEN_ARCHIVE(filename, archive) \ + cmGeneratedFileStream gf; \ + gf.Open(filename.c_str(), false, true); \ + if (!GenerateHeader(&gf)) { \ + cmCPackLogger(cmCPackLog::LOG_ERROR, \ + "Problem to generate Header for archive < " \ + << filename << ">." << std::endl); \ + return 0; \ + } \ + cmArchiveWrite archive(gf, this->Compress, this->ArchiveFormat); \ + if (!archive) { \ + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem to create archive < " \ + << filename << ">. ERROR =" << archive.GetError() \ + << std::endl); \ + return 0; \ + } + +int cmCPackArchiveGenerator::PackageComponents(bool ignoreGroup) +{ + packageFileNames.clear(); + // The default behavior is to have one package by component group + // unless CPACK_COMPONENTS_IGNORE_GROUP is specified. + if (!ignoreGroup) { + std::map<std::string, cmCPackComponentGroup>::iterator compGIt; + for (compGIt = this->ComponentGroups.begin(); + compGIt != this->ComponentGroups.end(); ++compGIt) { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Packaging component group: " + << compGIt->first << std::endl); + // Begin the archive for this group + std::string packageFileName = std::string(toplevel); + packageFileName += "/" + + GetComponentPackageFileName(this->GetOption("CPACK_PACKAGE_FILE_NAME"), + compGIt->first, true) + + this->GetOutputExtension(); + // open a block in order to automatically close archive + // at the end of the block + { + DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive); + // now iterate over the component of this group + std::vector<cmCPackComponent*>::iterator compIt; + for (compIt = (compGIt->second).Components.begin(); + compIt != (compGIt->second).Components.end(); ++compIt) { + // Add the files of this component to the archive + addOneComponentToArchive(archive, *compIt); + } + } + // add the generated package to package file names list + packageFileNames.push_back(packageFileName); + } + // Handle Orphan components (components not belonging to any groups) + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt = this->Components.begin(); compIt != this->Components.end(); + ++compIt) { + // Does the component belong to a group? + if (compIt->second.Group == CM_NULLPTR) { + cmCPackLogger( + cmCPackLog::LOG_VERBOSE, "Component <" + << compIt->second.Name + << "> does not belong to any group, package it separately." + << std::endl); + std::string localToplevel( + this->GetOption("CPACK_TEMPORARY_DIRECTORY")); + std::string packageFileName = std::string(toplevel); + + localToplevel += "/" + compIt->first; + packageFileName += "/" + GetComponentPackageFileName( + this->GetOption("CPACK_PACKAGE_FILE_NAME"), + compIt->first, false) + + this->GetOutputExtension(); + { + DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive); + // Add the files of this component to the archive + addOneComponentToArchive(archive, &(compIt->second)); + } + // add the generated package to package file names list + packageFileNames.push_back(packageFileName); + } + } + } + // CPACK_COMPONENTS_IGNORE_GROUPS is set + // We build 1 package per component + else { + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt = this->Components.begin(); compIt != this->Components.end(); + ++compIt) { + std::string localToplevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); + std::string packageFileName = std::string(toplevel); + + localToplevel += "/" + compIt->first; + packageFileName += "/" + + GetComponentPackageFileName(this->GetOption("CPACK_PACKAGE_FILE_NAME"), + compIt->first, false) + + this->GetOutputExtension(); + { + DECLARE_AND_OPEN_ARCHIVE(packageFileName, archive); + // Add the files of this component to the archive + addOneComponentToArchive(archive, &(compIt->second)); + } + // add the generated package to package file names list + packageFileNames.push_back(packageFileName); + } + } + return 1; +} + +int cmCPackArchiveGenerator::PackageComponentsAllInOne() +{ + // reset the package file names + packageFileNames.clear(); + packageFileNames.push_back(std::string(toplevel)); + packageFileNames[0] += "/" + + std::string(this->GetOption("CPACK_PACKAGE_FILE_NAME")) + + this->GetOutputExtension(); + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Packaging all groups in one package..." + "(CPACK_COMPONENTS_ALL_GROUPS_IN_ONE_PACKAGE is set)" + << std::endl); + DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive); + + // The ALL COMPONENTS in ONE package case + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt = this->Components.begin(); compIt != this->Components.end(); + ++compIt) { + // Add the files of this component to the archive + addOneComponentToArchive(archive, &(compIt->second)); + } + + // archive goes out of scope so it will finalized and closed. + return 1; +} + +int cmCPackArchiveGenerator::PackageFiles() +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Toplevel: " << toplevel << std::endl); + + if (WantsComponentInstallation()) { + // CASE 1 : COMPONENT ALL-IN-ONE package + // If ALL COMPONENTS in ONE package has been requested + // then the package file is unique and should be open here. + if (componentPackageMethod == ONE_PACKAGE) { + return PackageComponentsAllInOne(); + } + // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one) + // There will be 1 package for each component group + // however one may require to ignore component group and + // in this case you'll get 1 package for each component. + else { + return PackageComponents(componentPackageMethod == + ONE_PACKAGE_PER_COMPONENT); + } + } + + // CASE 3 : NON COMPONENT package. + DECLARE_AND_OPEN_ARCHIVE(packageFileNames[0], archive); + std::vector<std::string>::const_iterator fileIt; + std::string dir = cmSystemTools::GetCurrentWorkingDirectory(); + cmSystemTools::ChangeDirectory(toplevel); + for (fileIt = files.begin(); fileIt != files.end(); ++fileIt) { + // Get the relative path to the file + std::string rp = + cmSystemTools::RelativePath(toplevel.c_str(), fileIt->c_str()); + archive.Add(rp, 0, CM_NULLPTR, false); + if (!archive) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem while adding file< " + << *fileIt << "> to archive <" << packageFileNames[0] + << "> .ERROR =" << archive.GetError() << std::endl); + return 0; + } + } + cmSystemTools::ChangeDirectory(dir); + // The destructor of cmArchiveWrite will close and finish the write + return 1; +} + +int cmCPackArchiveGenerator::GenerateHeader(std::ostream*) +{ + return 1; +} + +bool cmCPackArchiveGenerator::SupportsComponentInstallation() const +{ + // The Component installation support should only + // be activated if explicitly requested by the user + // (for backward compatibility reason) + return IsOn("CPACK_ARCHIVE_COMPONENT_INSTALL"); +} diff --git a/Source/CPack/cmCPackArchiveGenerator.h b/Source/CPack/cmCPackArchiveGenerator.h new file mode 100644 index 0000000..a018ebd --- /dev/null +++ b/Source/CPack/cmCPackArchiveGenerator.h @@ -0,0 +1,75 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackArchiveGenerator_h +#define cmCPackArchiveGenerator_h + +#include "cmCPackGenerator.h" + +#include "cmArchiveWrite.h" + +/** \class cmCPackArchiveGenerator + * \brief A generator base for libarchive generation. + * The generator itself uses the libarchive wrapper + * \ref cmArchiveWrite. + * + */ +class cmCPackArchiveGenerator : public cmCPackGenerator +{ +public: + cmTypeMacro(cmCPackArchiveGenerator, cmCPackGenerator); + + /** + * Construct generator + */ + cmCPackArchiveGenerator(cmArchiveWrite::Compress, std::string const& format); + ~cmCPackArchiveGenerator() CM_OVERRIDE; + // Used to add a header to the archive + virtual int GenerateHeader(std::ostream* os); + // component support + bool SupportsComponentInstallation() const CM_OVERRIDE; + +protected: + int InitializeInternal() CM_OVERRIDE; + /** + * Add the files belonging to the specified component + * to the provided (already opened) archive. + * @param[in,out] archive the archive object + * @param[in] component the component whose file will be added to archive + */ + int addOneComponentToArchive(cmArchiveWrite& archive, + cmCPackComponent* component); + + /** + * The main package file method. + * If component install was required this + * method will call either PackageComponents or + * PackageComponentsAllInOne. + */ + int PackageFiles() CM_OVERRIDE; + /** + * The method used to package files when component + * install is used. This will create one + * archive for each component group. + */ + int PackageComponents(bool ignoreGroup); + /** + * Special case of component install where all + * components will be put in a single installer. + */ + int PackageComponentsAllInOne(); + const char* GetOutputExtension() CM_OVERRIDE = 0; + cmArchiveWrite::Compress Compress; + std::string ArchiveFormat; +}; + +#endif diff --git a/Source/CPack/cmCPackBundleGenerator.cxx b/Source/CPack/cmCPackBundleGenerator.cxx new file mode 100644 index 0000000..9276e3a --- /dev/null +++ b/Source/CPack/cmCPackBundleGenerator.cxx @@ -0,0 +1,292 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackBundleGenerator.h" + +#include "cmCPackLog.h" +#include "cmSystemTools.h" + +#include <cmsys/RegularExpression.hxx> + +cmCPackBundleGenerator::cmCPackBundleGenerator() +{ +} + +cmCPackBundleGenerator::~cmCPackBundleGenerator() +{ +} + +int cmCPackBundleGenerator::InitializeInternal() +{ + const char* name = this->GetOption("CPACK_BUNDLE_NAME"); + if (0 == name) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_BUNDLE_NAME must be set to use the Bundle generator." + << std::endl); + + return 0; + } + + if (this->GetOption("CPACK_BUNDLE_APPLE_CERT_APP")) { + const std::string codesign_path = cmSystemTools::FindProgram( + "codesign", std::vector<std::string>(), false); + + if (codesign_path.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot locate codesign command" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_COMMAND_CODESIGN", codesign_path.c_str()); + } + + return this->Superclass::InitializeInternal(); +} + +const char* cmCPackBundleGenerator::GetPackagingInstallPrefix() +{ + this->InstallPrefix = "/"; + this->InstallPrefix += this->GetOption("CPACK_BUNDLE_NAME"); + this->InstallPrefix += ".app/Contents/Resources"; + + return this->InstallPrefix.c_str(); +} + +int cmCPackBundleGenerator::ConstructBundle() +{ + + // Get required arguments ... + const std::string cpack_bundle_name = this->GetOption("CPACK_BUNDLE_NAME") + ? this->GetOption("CPACK_BUNDLE_NAME") + : ""; + if (cpack_bundle_name.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "CPACK_BUNDLE_NAME must be set." + << std::endl); + + return 0; + } + + const std::string cpack_bundle_plist = this->GetOption("CPACK_BUNDLE_PLIST") + ? this->GetOption("CPACK_BUNDLE_PLIST") + : ""; + if (cpack_bundle_plist.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "CPACK_BUNDLE_PLIST must be set." + << std::endl); + + return 0; + } + + const std::string cpack_bundle_icon = this->GetOption("CPACK_BUNDLE_ICON") + ? this->GetOption("CPACK_BUNDLE_ICON") + : ""; + if (cpack_bundle_icon.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "CPACK_BUNDLE_ICON must be set." + << std::endl); + + return 0; + } + + // Get optional arguments ... + const std::string cpack_bundle_startup_command = + this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND") + ? this->GetOption("CPACK_BUNDLE_STARTUP_COMMAND") + : ""; + + // The staging directory contains everything that will end-up inside the + // final disk image ... + std::ostringstream staging; + staging << toplevel; + + std::ostringstream contents; + contents << staging.str() << "/" << cpack_bundle_name << ".app/" + << "Contents"; + + std::ostringstream application; + application << contents.str() << "/" + << "MacOS"; + + std::ostringstream resources; + resources << contents.str() << "/" + << "Resources"; + + // Install a required, user-provided bundle metadata file ... + std::ostringstream plist_source; + plist_source << cpack_bundle_plist; + + std::ostringstream plist_target; + plist_target << contents.str() << "/" + << "Info.plist"; + + if (!this->CopyFile(plist_source, plist_target)) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "Error copying plist. Check the value of CPACK_BUNDLE_PLIST." + << std::endl); + + return 0; + } + + // Install a user-provided bundle icon ... + std::ostringstream icon_source; + icon_source << cpack_bundle_icon; + + std::ostringstream icon_target; + icon_target << resources.str() << "/" << cpack_bundle_name << ".icns"; + + if (!this->CopyFile(icon_source, icon_target)) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "Error copying bundle icon. Check the value of CPACK_BUNDLE_ICON." + << std::endl); + + return 0; + } + + // Optionally a user-provided startup command (could be an + // executable or a script) ... + if (!cpack_bundle_startup_command.empty()) { + std::ostringstream command_source; + command_source << cpack_bundle_startup_command; + + std::ostringstream command_target; + command_target << application.str() << "/" << cpack_bundle_name; + + if (!this->CopyFile(command_source, command_target)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error copying startup command. " + " Check the value of CPACK_BUNDLE_STARTUP_COMMAND." + << std::endl); + + return 0; + } + + cmSystemTools::SetPermissions(command_target.str().c_str(), 0777); + } + + return 1; +} + +int cmCPackBundleGenerator::PackageFiles() +{ + if (!this->ConstructBundle()) { + return 0; + } + + if (!this->SignBundle(toplevel)) { + return 0; + } + + return this->CreateDMG(toplevel, packageFileNames[0]); +} + +bool cmCPackBundleGenerator::SupportsComponentInstallation() const +{ + return false; +} + +int cmCPackBundleGenerator::SignBundle(const std::string& src_dir) +{ + const std::string cpack_apple_cert_app = + this->GetOption("CPACK_BUNDLE_APPLE_CERT_APP") + ? this->GetOption("CPACK_BUNDLE_APPLE_CERT_APP") + : ""; + + // codesign the application. + if (!cpack_apple_cert_app.empty()) { + std::string output; + std::string bundle_path; + bundle_path = src_dir + "/"; + bundle_path += this->GetOption("CPACK_BUNDLE_NAME"); + bundle_path += ".app"; + + // A list of additional files to sign, ie. frameworks and plugins. + const std::string sign_parameter = + this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_PARAMETER") + ? this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_PARAMETER") + : "--deep -f"; + + const std::string sign_files = + this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_FILES") + ? this->GetOption("CPACK_BUNDLE_APPLE_CODESIGN_FILES") + : ""; + + std::vector<std::string> relFiles; + cmSystemTools::ExpandListArgument(sign_files, relFiles); + + // sign the files supplied by the user, ie. frameworks. + for (std::vector<std::string>::iterator it = relFiles.begin(); + it != relFiles.end(); ++it) { + std::ostringstream temp_sign_file_cmd; + temp_sign_file_cmd << this->GetOption("CPACK_COMMAND_CODESIGN"); + temp_sign_file_cmd << " " << sign_parameter << " -s \"" + << cpack_apple_cert_app; + temp_sign_file_cmd << "\" -i "; + temp_sign_file_cmd << this->GetOption("CPACK_APPLE_BUNDLE_ID"); + temp_sign_file_cmd << " \""; + temp_sign_file_cmd << bundle_path; + temp_sign_file_cmd << *it << "\""; + + if (!this->RunCommand(temp_sign_file_cmd, &output)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error signing file:" << bundle_path << *it << std::endl + << output << std::endl); + + return 0; + } + } + + // sign main binary + std::ostringstream temp_sign_binary_cmd; + temp_sign_binary_cmd << this->GetOption("CPACK_COMMAND_CODESIGN"); + temp_sign_binary_cmd << " " << sign_parameter << " -s \"" + << cpack_apple_cert_app; + temp_sign_binary_cmd << "\" \"" << bundle_path << "\""; + + if (!this->RunCommand(temp_sign_binary_cmd, &output)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error signing the application binary." << std::endl + << output + << std::endl); + + return 0; + } + + // sign app bundle + std::ostringstream temp_codesign_cmd; + temp_codesign_cmd << this->GetOption("CPACK_COMMAND_CODESIGN"); + temp_codesign_cmd << " " << sign_parameter << " -s \"" + << cpack_apple_cert_app << "\""; + if (this->GetOption("CPACK_BUNDLE_APPLE_ENTITLEMENTS")) { + temp_codesign_cmd << " --entitlements "; + temp_codesign_cmd << this->GetOption("CPACK_BUNDLE_APPLE_ENTITLEMENTS"); + } + temp_codesign_cmd << " \"" << bundle_path << "\""; + + if (!this->RunCommand(temp_codesign_cmd, &output)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error signing the application package." << std::endl + << output + << std::endl); + + return 0; + } + + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "- Application has been codesigned" + << std::endl); + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + (this->GetOption("CPACK_BUNDLE_APPLE_ENTITLEMENTS") + ? "with entitlement sandboxing" + : "without entitlement sandboxing") + << std::endl); + } + + return 1; +} diff --git a/Source/CPack/cmCPackBundleGenerator.h b/Source/CPack/cmCPackBundleGenerator.h new file mode 100644 index 0000000..c6fa408 --- /dev/null +++ b/Source/CPack/cmCPackBundleGenerator.h @@ -0,0 +1,42 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackBundleGenerator_h +#define cmCPackBundleGenerator_h + +#include "cmCPackDragNDropGenerator.h" + +/** \class cmCPackBundleGenerator + * \brief A generator for OSX bundles + * + * Based on Gimp.app + */ +class cmCPackBundleGenerator : public cmCPackDragNDropGenerator +{ +public: + cmCPackTypeMacro(cmCPackBundleGenerator, cmCPackDragNDropGenerator); + + cmCPackBundleGenerator(); + virtual ~cmCPackBundleGenerator(); + +protected: + int InitializeInternal() CM_OVERRIDE; + const char* GetPackagingInstallPrefix() CM_OVERRIDE; + int ConstructBundle(); + int SignBundle(const std::string& src_dir); + int PackageFiles() CM_OVERRIDE; + bool SupportsComponentInstallation() const CM_OVERRIDE; + + std::string InstallPrefix; +}; + +#endif diff --git a/Source/CPack/cmCPackComponentGroup.cxx b/Source/CPack/cmCPackComponentGroup.cxx new file mode 100644 index 0000000..d262ac9 --- /dev/null +++ b/Source/CPack/cmCPackComponentGroup.cxx @@ -0,0 +1,43 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackComponentGroup.h" + +#include "cmSystemTools.h" + +#include <string> +#include <vector> + +unsigned long cmCPackComponent::GetInstalledSize( + const std::string& installDir) const +{ + if (this->TotalSize != 0) { + return this->TotalSize; + } + + std::vector<std::string>::const_iterator fileIt; + for (fileIt = this->Files.begin(); fileIt != this->Files.end(); ++fileIt) { + std::string path = installDir; + path += '/'; + path += *fileIt; + this->TotalSize += cmSystemTools::FileLength(path); + } + + return this->TotalSize; +} + +unsigned long cmCPackComponent::GetInstalledSizeInKbytes( + const std::string& installDir) const +{ + unsigned long result = (GetInstalledSize(installDir) + 512) / 1024; + return result ? result : 1; +} diff --git a/Source/CPack/cmCPackComponentGroup.h b/Source/CPack/cmCPackComponentGroup.h new file mode 100644 index 0000000..01a9e76 --- /dev/null +++ b/Source/CPack/cmCPackComponentGroup.h @@ -0,0 +1,149 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackComponentGroup_h +#define cmCPackComponentGroup_h + +#include "cmStandardIncludes.h" + +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(CM_NULLPTR) + , IsRequired(true) + , IsHidden(false) + , IsDisabledByDefault(false) + , IsDownloaded(false) + , TotalSize(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; + + /// Whether this component should be downloaded on-the-fly. If false, + /// the component will be a part of the installation package. + bool IsDownloaded : 1; + + /// A description of this component. + std::string Description; + + /// The installation types that this component is a part of. + std::vector<cmCPackInstallationType*> InstallationTypes; + + /// If IsDownloaded is true, the name of the archive file that + /// contains the files that are part of this component. + std::string ArchiveFile; + + /// 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; + + /// Get the total installed size of all of the files in this + /// component, in bytes. installDir is the directory into which the + /// component was installed. + unsigned long GetInstalledSize(const std::string& installDir) const; + + /// Identical to GetInstalledSize, but returns the result in + /// kilobytes. + unsigned long GetInstalledSizeInKbytes(const std::string& installDir) const; + +private: + mutable unsigned long TotalSize; +}; + +/** \class cmCPackComponentGroup + * \brief A component group to be installed by CPack. + */ +class cmCPackComponentGroup +{ +public: + cmCPackComponentGroup() + : ParentGroup(CM_NULLPTR) + { + } + + /// 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; + + /// The parent group of this component group (if any). + cmCPackComponentGroup* ParentGroup; + + /// The subgroups of this group. + std::vector<cmCPackComponentGroup*> Subgroups; +}; + +#endif diff --git a/Source/CPack/cmCPackConfigure.h.in b/Source/CPack/cmCPackConfigure.h.in new file mode 100644 index 0000000..3d7702e --- /dev/null +++ b/Source/CPack/cmCPackConfigure.h.in @@ -0,0 +1,11 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ diff --git a/Source/CPack/cmCPackCygwinBinaryGenerator.cxx b/Source/CPack/cmCPackCygwinBinaryGenerator.cxx new file mode 100644 index 0000000..83af89e --- /dev/null +++ b/Source/CPack/cmCPackCygwinBinaryGenerator.cxx @@ -0,0 +1,84 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackCygwinBinaryGenerator.h" + +#include "cmCPackLog.h" +#include "cmGeneratedFileStream.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" +#include "cmake.h" + +#include <cmsys/SystemTools.hxx> + +cmCPackCygwinBinaryGenerator::cmCPackCygwinBinaryGenerator() +{ +} + +cmCPackCygwinBinaryGenerator::~cmCPackCygwinBinaryGenerator() +{ +} + +int cmCPackCygwinBinaryGenerator::InitializeInternal() +{ + this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr"); + this->SetOptionIfNotSet("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "0"); + return this->Superclass::InitializeInternal(); +} + +int cmCPackCygwinBinaryGenerator::PackageFiles() +{ + std::string packageName = this->GetOption("CPACK_PACKAGE_NAME"); + packageName += "-"; + packageName += this->GetOption("CPACK_PACKAGE_VERSION"); + packageName = cmsys::SystemTools::LowerCase(packageName); + std::string manifest = "/usr/share/doc/"; + manifest += packageName; + manifest += "/MANIFEST"; + std::string manifestFile = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + // Create a MANIFEST file that contains all of the files in + // the tar file + std::string tempdir = manifestFile; + manifestFile += manifest; + // create an extra scope to force the stream + // to create the file before the super class is called + { + cmGeneratedFileStream ofs(manifestFile.c_str()); + for (std::vector<std::string>::const_iterator i = files.begin(); + i != files.end(); ++i) { + // remove the temp dir and replace with /usr + ofs << (*i).substr(tempdir.size()) << "\n"; + } + ofs << manifest << "\n"; + } + // add the manifest file to the list of all files + files.push_back(manifestFile); + + // create the bzip2 tar file + return this->Superclass::PackageFiles(); +} + +const char* cmCPackCygwinBinaryGenerator::GetOutputExtension() +{ + this->OutputExtension = "-"; + const char* patchNumber = this->GetOption("CPACK_CYGWIN_PATCH_NUMBER"); + if (!patchNumber) { + patchNumber = "1"; + cmCPackLogger(cmCPackLog::LOG_WARNING, + "CPACK_CYGWIN_PATCH_NUMBER not specified using 1" + << std::endl); + } + this->OutputExtension += patchNumber; + this->OutputExtension += ".tar.bz2"; + return this->OutputExtension.c_str(); +} diff --git a/Source/CPack/cmCPackCygwinBinaryGenerator.h b/Source/CPack/cmCPackCygwinBinaryGenerator.h new file mode 100644 index 0000000..8de4bae --- /dev/null +++ b/Source/CPack/cmCPackCygwinBinaryGenerator.h @@ -0,0 +1,39 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackCygwinBinaryGenerator_h +#define cmCPackCygwinBinaryGenerator_h + +#include "cmCPackTarBZip2Generator.h" + +/** \class cmCPackCygwinBinaryGenerator + * \brief A generator for TarBZip2 files + */ +class cmCPackCygwinBinaryGenerator : public cmCPackTarBZip2Generator +{ +public: + cmCPackTypeMacro(cmCPackCygwinBinaryGenerator, cmCPackTarBZip2Generator); + + /** + * Construct generator + */ + cmCPackCygwinBinaryGenerator(); + virtual ~cmCPackCygwinBinaryGenerator(); + +protected: + virtual int InitializeInternal(); + int PackageFiles(); + virtual const char* GetOutputExtension(); + std::string OutputExtension; +}; + +#endif diff --git a/Source/CPack/cmCPackCygwinSourceGenerator.cxx b/Source/CPack/cmCPackCygwinSourceGenerator.cxx new file mode 100644 index 0000000..5fcfaf4 --- /dev/null +++ b/Source/CPack/cmCPackCygwinSourceGenerator.cxx @@ -0,0 +1,167 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackCygwinSourceGenerator.h" + +#include "cmCPackLog.h" +#include "cmGeneratedFileStream.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" +#include "cmake.h" + +#include <cmsys/SystemTools.hxx> + +// Includes needed for implementation of RenameFile. This is not in +// system tools because it is not implemented robustly enough to move +// files across directories. +#ifdef _WIN32 +#include <sys/stat.h> +#include <windows.h> +#endif + +cmCPackCygwinSourceGenerator::cmCPackCygwinSourceGenerator() +{ +} + +cmCPackCygwinSourceGenerator::~cmCPackCygwinSourceGenerator() +{ +} + +int cmCPackCygwinSourceGenerator::InitializeInternal() +{ + this->SetOptionIfNotSet("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "0"); + return this->Superclass::InitializeInternal(); +} + +int cmCPackCygwinSourceGenerator::PackageFiles() +{ + // Create a tar file of the sources + std::string packageDirFileName = + this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + packageDirFileName += ".tar.bz2"; + packageFileNames[0] = packageDirFileName; + std::string output; + // skip one parent up to the cmCPackTarBZip2Generator + // to create tar.bz2 file with the list of source + // files + this->Compress = cmArchiveWrite::CompressBZip2; + if (!this->cmCPackTarBZip2Generator::PackageFiles()) { + return 0; + } + // Now create a tar file that contains the above .tar.bz2 file + // and the CPACK_CYGWIN_PATCH_FILE and CPACK_TOPLEVEL_DIRECTORY + // files + std::string compressOutFile = packageDirFileName; + // at this point compressOutFile is the full path to + // _CPack_Package/.../package-2.5.0.tar.bz2 + // we want to create a tar _CPack_Package/.../package-2.5.0-1-src.tar.bz2 + // with these + // _CPack_Package/.../package-2.5.0-1.patch + // _CPack_Package/.../package-2.5.0-1.sh + // _CPack_Package/.../package-2.5.0.tar.bz2 + // the -1 is CPACK_CYGWIN_PATCH_NUMBER + + // first copy the patch file and the .sh file + // to the toplevel cpack temp dir + + // copy the patch file into place + if (!this->GetOption("CPACK_CYGWIN_PATCH_FILE")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "No patch file specified for cygwin sources."); + return 0; + } + if (!cmSystemTools::CopyFileAlways( + this->GetOption("CPACK_CYGWIN_PATCH_FILE"), + this->GetOption("CPACK_TOPLEVEL_DIRECTORY"))) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "problem copying: [" + << this->GetOption("CPACK_CYGWIN_PATCH_FILE") << "]\nto\n[" + << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "]\n"); + return 0; + } + if (!this->GetOption("CPACK_CYGWIN_BUILD_SCRIPT")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "No build script specified for cygwin sources."); + return 0; + } + // copy the build script into place + if (!cmSystemTools::CopyFileAlways( + this->GetOption("CPACK_CYGWIN_BUILD_SCRIPT"), + this->GetOption("CPACK_TOPLEVEL_DIRECTORY"))) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "problem copying: " + << this->GetOption("CPACK_CYGWIN_BUILD_SCRIPT") << "\nto\n" + << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "]\n"); + return 0; + } + std::string outerTarFile = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + outerTarFile += "-"; + const char* patch = this->GetOption("CPACK_CYGWIN_PATCH_NUMBER"); + if (!patch) { + cmCPackLogger(cmCPackLog::LOG_WARNING, "CPACK_CYGWIN_PATCH_NUMBER" + << " not specified, defaulting to 1\n"); + patch = "1"; + } + outerTarFile += patch; + outerTarFile += "-src.tar.bz2"; + std::string tmpDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + std::string buildScript = tmpDir; + buildScript += "/"; + buildScript += cmSystemTools::GetFilenameName( + this->GetOption("CPACK_CYGWIN_BUILD_SCRIPT")); + std::string patchFile = tmpDir; + patchFile += "/"; + patchFile += + cmSystemTools::GetFilenameName(this->GetOption("CPACK_CYGWIN_PATCH_FILE")); + + std::string file = cmSystemTools::GetFilenameName(compressOutFile); + std::string sourceTar = cmSystemTools::GetFilenamePath(compressOutFile); + sourceTar += "/"; + sourceTar += file; + /* reset list of file to be packaged */ + files.clear(); + // a source release in cygwin should have the build script used + // to build the package, the patch file that is different from the + // regular upstream version of the sources, and a bziped tar file + // of the original sources + files.push_back(buildScript); + files.push_back(patchFile); + files.push_back(sourceTar); + /* update the name of the produced package */ + packageFileNames[0] = outerTarFile; + /* update the toplevel dir */ + toplevel = tmpDir; + if (!this->cmCPackTarBZip2Generator::PackageFiles()) { + return 0; + } + return 1; +} + +const char* cmCPackCygwinSourceGenerator::GetPackagingInstallPrefix() +{ + this->InstallPrefix = "/"; + this->InstallPrefix += this->GetOption("CPACK_PACKAGE_FILE_NAME"); + return this->InstallPrefix.c_str(); +} + +const char* cmCPackCygwinSourceGenerator::GetOutputExtension() +{ + this->OutputExtension = "-"; + const char* patch = this->GetOption("CPACK_CYGWIN_PATCH_NUMBER"); + if (!patch) { + cmCPackLogger(cmCPackLog::LOG_WARNING, "CPACK_CYGWIN_PATCH_NUMBER" + << " not specified, defaulting to 1\n"); + patch = "1"; + } + this->OutputExtension += patch; + this->OutputExtension += "-src.tar.bz2"; + return this->OutputExtension.c_str(); +} diff --git a/Source/CPack/cmCPackCygwinSourceGenerator.h b/Source/CPack/cmCPackCygwinSourceGenerator.h new file mode 100644 index 0000000..4aba8b9 --- /dev/null +++ b/Source/CPack/cmCPackCygwinSourceGenerator.h @@ -0,0 +1,41 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackCygwinSourceGenerator_h +#define cmCPackCygwinSourceGenerator_h + +#include "cmCPackTarBZip2Generator.h" + +/** \class cmCPackCygwinSourceGenerator + * \brief A generator for cygwin source files + */ +class cmCPackCygwinSourceGenerator : public cmCPackTarBZip2Generator +{ +public: + cmCPackTypeMacro(cmCPackCygwinSourceGenerator, cmCPackTarBZip2Generator); + + /** + * Construct generator + */ + cmCPackCygwinSourceGenerator(); + virtual ~cmCPackCygwinSourceGenerator(); + +protected: + const char* GetPackagingInstallPrefix(); + virtual int InitializeInternal(); + int PackageFiles(); + virtual const char* GetOutputExtension(); + std::string InstallPrefix; + std::string OutputExtension; +}; + +#endif diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx new file mode 100644 index 0000000..3edc430 --- /dev/null +++ b/Source/CPack/cmCPackDebGenerator.cxx @@ -0,0 +1,952 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackDebGenerator.h" + +#include "cmArchiveWrite.h" +#include "cmCPackLog.h" +#include "cmGeneratedFileStream.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" + +#include <cmsys/Glob.hxx> +#include <cmsys/SystemTools.hxx> + +#include <limits.h> // USHRT_MAX +#include <sys/stat.h> + +// NOTE: +// A debian package .deb is simply an 'ar' archive. The only subtle difference +// is that debian uses the BSD ar style archive whereas most Linux distro have +// a GNU ar. +// See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=161593 for more info +// Therefore we provide our own implementation of a BSD-ar: +static int ar_append(const char* archive, + const std::vector<std::string>& files); + +cmCPackDebGenerator::cmCPackDebGenerator() +{ +} + +cmCPackDebGenerator::~cmCPackDebGenerator() +{ +} + +int cmCPackDebGenerator::InitializeInternal() +{ + this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr"); + if (cmSystemTools::IsOff(this->GetOption("CPACK_SET_DESTDIR"))) { + this->SetOption("CPACK_SET_DESTDIR", "I_ON"); + } + return this->Superclass::InitializeInternal(); +} + +int cmCPackDebGenerator::PackageOnePack(std::string const& initialTopLevel, + std::string const& packageName) +{ + int retval = 1; + // Begin the archive for this pack + std::string localToplevel(initialTopLevel); + std::string packageFileName(cmSystemTools::GetParentDirectory(toplevel)); + std::string outputFileName( + std::string(this->GetOption("CPACK_PACKAGE_FILE_NAME")) + "-" + + packageName + this->GetOutputExtension()); + + localToplevel += "/" + packageName; + /* replace the TEMP DIRECTORY with the component one */ + this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel.c_str()); + packageFileName += "/" + outputFileName; + /* replace proposed CPACK_OUTPUT_FILE_NAME */ + this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName.c_str()); + /* replace the TEMPORARY package file name */ + this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", + packageFileName.c_str()); + // Tell CPackDeb.cmake the name of the component GROUP. + this->SetOption("CPACK_DEB_PACKAGE_COMPONENT", packageName.c_str()); + // Tell CPackDeb.cmake the path where the component is. + std::string component_path = "/"; + component_path += packageName; + this->SetOption("CPACK_DEB_PACKAGE_COMPONENT_PART_PATH", + component_path.c_str()); + if (!this->ReadListFile("CPackDeb.cmake")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error while execution CPackDeb.cmake" + << std::endl); + retval = 0; + return retval; + } + + cmsys::Glob gl; + std::string findExpr(this->GetOption("GEN_WDIR")); + findExpr += "/*"; + gl.RecurseOn(); + gl.SetRecurseListDirs(true); + if (!gl.FindFiles(findExpr)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find any files in the installed directory" + << std::endl); + return 0; + } + packageFiles = gl.GetFiles(); + + int res = createDeb(); + if (res != 1) { + retval = 0; + } + // add the generated package to package file names list + packageFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + packageFileName += "/"; + packageFileName += this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"); + packageFileNames.push_back(packageFileName); + return retval; +} + +int cmCPackDebGenerator::PackageComponents(bool ignoreGroup) +{ + int retval = 1; + /* Reset package file name list it will be populated during the + * component packaging run*/ + packageFileNames.clear(); + std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); + + // The default behavior is to have one package by component group + // unless CPACK_COMPONENTS_IGNORE_GROUP is specified. + if (!ignoreGroup) { + std::map<std::string, cmCPackComponentGroup>::iterator compGIt; + for (compGIt = this->ComponentGroups.begin(); + compGIt != this->ComponentGroups.end(); ++compGIt) { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Packaging component group: " + << compGIt->first << std::endl); + // Begin the archive for this group + retval &= PackageOnePack(initialTopLevel, compGIt->first); + } + // Handle Orphan components (components not belonging to any groups) + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt = this->Components.begin(); compIt != this->Components.end(); + ++compIt) { + // Does the component belong to a group? + if (compIt->second.Group == CM_NULLPTR) { + cmCPackLogger( + cmCPackLog::LOG_VERBOSE, "Component <" + << compIt->second.Name + << "> does not belong to any group, package it separately." + << std::endl); + // Begin the archive for this orphan component + retval &= PackageOnePack(initialTopLevel, compIt->first); + } + } + } + // CPACK_COMPONENTS_IGNORE_GROUPS is set + // We build 1 package per component + else { + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt = this->Components.begin(); compIt != this->Components.end(); + ++compIt) { + retval &= PackageOnePack(initialTopLevel, compIt->first); + } + } + return retval; +} + +//---------------------------------------------------------------------- +int cmCPackDebGenerator::PackageComponentsAllInOne( + const std::string& compInstDirName) +{ + int retval = 1; + /* Reset package file name list it will be populated during the + * component packaging run*/ + packageFileNames.clear(); + std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Packaging all groups in one package..." + "(CPACK_COMPONENTS_ALL_[GROUPS_]IN_ONE_PACKAGE is set)" + << std::endl); + + // The ALL GROUPS in ONE package case + std::string localToplevel(initialTopLevel); + std::string packageFileName(cmSystemTools::GetParentDirectory(toplevel)); + std::string outputFileName( + std::string(this->GetOption("CPACK_PACKAGE_FILE_NAME")) + + this->GetOutputExtension()); + // all GROUP in one vs all COMPONENT in one + localToplevel += "/" + compInstDirName; + + /* replace the TEMP DIRECTORY with the component one */ + this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel.c_str()); + packageFileName += "/" + outputFileName; + /* replace proposed CPACK_OUTPUT_FILE_NAME */ + this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName.c_str()); + /* replace the TEMPORARY package file name */ + this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", + packageFileName.c_str()); + + if (!compInstDirName.empty()) { + // Tell CPackDeb.cmake the path where the component is. + std::string component_path = "/"; + component_path += compInstDirName; + this->SetOption("CPACK_DEB_PACKAGE_COMPONENT_PART_PATH", + component_path.c_str()); + } + if (!this->ReadListFile("CPackDeb.cmake")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error while execution CPackDeb.cmake" + << std::endl); + retval = 0; + return retval; + } + + cmsys::Glob gl; + std::string findExpr(this->GetOption("GEN_WDIR")); + findExpr += "/*"; + gl.RecurseOn(); + gl.SetRecurseListDirs(true); + if (!gl.FindFiles(findExpr)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find any files in the installed directory" + << std::endl); + return 0; + } + packageFiles = gl.GetFiles(); + + int res = createDeb(); + if (res != 1) { + retval = 0; + } + // add the generated package to package file names list + packageFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + packageFileName += "/"; + packageFileName += this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"); + packageFileNames.push_back(packageFileName); + return retval; +} + +int cmCPackDebGenerator::PackageFiles() +{ + /* Are we in the component packaging case */ + if (WantsComponentInstallation()) { + // CASE 1 : COMPONENT ALL-IN-ONE package + // If ALL GROUPS or ALL COMPONENTS in ONE package has been requested + // then the package file is unique and should be open here. + if (componentPackageMethod == ONE_PACKAGE) { + return PackageComponentsAllInOne("ALL_COMPONENTS_IN_ONE"); + } + // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one) + // There will be 1 package for each component group + // however one may require to ignore component group and + // in this case you'll get 1 package for each component. + else { + return PackageComponents(componentPackageMethod == + ONE_PACKAGE_PER_COMPONENT); + } + } + // CASE 3 : NON COMPONENT package. + else { + return PackageComponentsAllInOne(""); + } +} + +int cmCPackDebGenerator::createDeb() +{ + // debian-binary file + const std::string strGenWDIR(this->GetOption("GEN_WDIR")); + const std::string dbfilename = strGenWDIR + "/debian-binary"; + { // the scope is needed for cmGeneratedFileStream + cmGeneratedFileStream out(dbfilename.c_str()); + out << "2.0"; + out << std::endl; // required for valid debian package + } + + // control file + std::string ctlfilename = strGenWDIR + "/control"; + + // debian policy enforce lower case for package name + // mandatory entries: + std::string debian_pkg_name = cmsys::SystemTools::LowerCase( + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_NAME")); + const char* debian_pkg_version = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_VERSION"); + const char* debian_pkg_section = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SECTION"); + const char* debian_pkg_priority = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PRIORITY"); + const char* debian_pkg_arch = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ARCHITECTURE"); + const char* maintainer = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_MAINTAINER"); + const char* desc = this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DESCRIPTION"); + + // optional entries + const char* debian_pkg_dep = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_DEPENDS"); + const char* debian_pkg_rec = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_RECOMMENDS"); + const char* debian_pkg_sug = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SUGGESTS"); + const char* debian_pkg_url = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_HOMEPAGE"); + const char* debian_pkg_predep = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PREDEPENDS"); + const char* debian_pkg_enhances = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_ENHANCES"); + const char* debian_pkg_breaks = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_BREAKS"); + const char* debian_pkg_conflicts = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONFLICTS"); + const char* debian_pkg_provides = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_PROVIDES"); + const char* debian_pkg_replaces = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_REPLACES"); + const char* debian_pkg_source = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SOURCE"); + + { // the scope is needed for cmGeneratedFileStream + cmGeneratedFileStream out(ctlfilename.c_str()); + out << "Package: " << debian_pkg_name << "\n"; + out << "Version: " << debian_pkg_version << "\n"; + out << "Section: " << debian_pkg_section << "\n"; + out << "Priority: " << debian_pkg_priority << "\n"; + out << "Architecture: " << debian_pkg_arch << "\n"; + if (debian_pkg_source && *debian_pkg_source) { + out << "Source: " << debian_pkg_source << "\n"; + } + if (debian_pkg_dep && *debian_pkg_dep) { + out << "Depends: " << debian_pkg_dep << "\n"; + } + if (debian_pkg_rec && *debian_pkg_rec) { + out << "Recommends: " << debian_pkg_rec << "\n"; + } + if (debian_pkg_sug && *debian_pkg_sug) { + out << "Suggests: " << debian_pkg_sug << "\n"; + } + if (debian_pkg_url && *debian_pkg_url) { + out << "Homepage: " << debian_pkg_url << "\n"; + } + if (debian_pkg_predep && *debian_pkg_predep) { + out << "Pre-Depends: " << debian_pkg_predep << "\n"; + } + if (debian_pkg_enhances && *debian_pkg_enhances) { + out << "Enhances: " << debian_pkg_enhances << "\n"; + } + if (debian_pkg_breaks && *debian_pkg_breaks) { + out << "Breaks: " << debian_pkg_breaks << "\n"; + } + if (debian_pkg_conflicts && *debian_pkg_conflicts) { + out << "Conflicts: " << debian_pkg_conflicts << "\n"; + } + if (debian_pkg_provides && *debian_pkg_provides) { + out << "Provides: " << debian_pkg_provides << "\n"; + } + if (debian_pkg_replaces && *debian_pkg_replaces) { + out << "Replaces: " << debian_pkg_replaces << "\n"; + } + unsigned long totalSize = 0; + { + std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + dirName += '/'; + for (std::vector<std::string>::const_iterator fileIt = + packageFiles.begin(); + fileIt != packageFiles.end(); ++fileIt) { + totalSize += cmSystemTools::FileLength(*fileIt); + } + } + out << "Installed-Size: " << (totalSize + 1023) / 1024 << "\n"; + out << "Maintainer: " << maintainer << "\n"; + out << "Description: " << desc << "\n"; + out << std::endl; + } + + const std::string shlibsfilename = strGenWDIR + "/shlibs"; + + const char* debian_pkg_shlibs = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_SHLIBS"); + const bool gen_shibs = this->IsOn("CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS") && + debian_pkg_shlibs && *debian_pkg_shlibs; + if (gen_shibs) { + cmGeneratedFileStream out(shlibsfilename.c_str()); + out << debian_pkg_shlibs; + out << std::endl; + } + + const std::string postinst = strGenWDIR + "/postinst"; + const std::string postrm = strGenWDIR + "/postrm"; + if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTINST")) { + cmGeneratedFileStream out(postinst.c_str()); + out << "#!/bin/sh\n\n" + "set -e\n\n" + "if [ \"$1\" = \"configure\" ]; then\n" + "\tldconfig\n" + "fi\n"; + } + if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTRM")) { + cmGeneratedFileStream out(postrm.c_str()); + out << "#!/bin/sh\n\n" + "set -e\n\n" + "if [ \"$1\" = \"remove\" ]; then\n" + "\tldconfig\n" + "fi\n"; + } + + cmArchiveWrite::Compress tar_compression_type = cmArchiveWrite::CompressGZip; + const char* debian_compression_type = + this->GetOption("GEN_CPACK_DEBIAN_COMPRESSION_TYPE"); + if (!debian_compression_type) { + debian_compression_type = "gzip"; + } + + std::string compression_suffix; + if (!strcmp(debian_compression_type, "lzma")) { + compression_suffix = ".lzma"; + tar_compression_type = cmArchiveWrite::CompressLZMA; + } else if (!strcmp(debian_compression_type, "xz")) { + compression_suffix = ".xz"; + tar_compression_type = cmArchiveWrite::CompressXZ; + } else if (!strcmp(debian_compression_type, "bzip2")) { + compression_suffix = ".bz2"; + tar_compression_type = cmArchiveWrite::CompressBZip2; + } else if (!strcmp(debian_compression_type, "gzip")) { + compression_suffix = ".gz"; + tar_compression_type = cmArchiveWrite::CompressGZip; + } else if (!strcmp(debian_compression_type, "none")) { + compression_suffix = ""; + tar_compression_type = cmArchiveWrite::CompressNone; + } else { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error unrecognized compression type: " + << debian_compression_type << std::endl); + } + + std::string filename_data_tar = + strGenWDIR + "/data.tar" + compression_suffix; + + // atomic file generation for data.tar + { + cmGeneratedFileStream fileStream_data_tar; + fileStream_data_tar.Open(filename_data_tar.c_str(), false, true); + if (!fileStream_data_tar) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error opening the file \"" + << filename_data_tar << "\" for writing" << std::endl); + return 0; + } + cmArchiveWrite data_tar(fileStream_data_tar, tar_compression_type, "paxr"); + + // uid/gid should be the one of the root user, and this root user has + // always uid/gid equal to 0. + data_tar.SetUIDAndGID(0u, 0u); + data_tar.SetUNAMEAndGNAME("root", "root"); + + // now add all directories which have to be compressed + // collect all top level install dirs for that + // e.g. /opt/bin/foo, /usr/bin/bar and /usr/bin/baz would + // give /usr and /opt + size_t topLevelLength = strGenWDIR.length(); + cmCPackLogger(cmCPackLog::LOG_DEBUG, "WDIR: \"" + << strGenWDIR << "\", length = " << topLevelLength + << std::endl); + std::set<std::string> orderedFiles; + + // we have to reconstruct the parent folders as well + + for (std::vector<std::string>::const_iterator fileIt = + packageFiles.begin(); + fileIt != packageFiles.end(); ++fileIt) { + std::string currentPath = *fileIt; + while (currentPath != strGenWDIR) { + // the last one IS strGenWDIR, but we do not want this one: + // XXX/application/usr/bin/myprogram with GEN_WDIR=XXX/application + // should not add XXX/application + orderedFiles.insert(currentPath); + currentPath = cmSystemTools::CollapseCombinedPath(currentPath, ".."); + } + } + + for (std::set<std::string>::const_iterator fileIt = orderedFiles.begin(); + fileIt != orderedFiles.end(); ++fileIt) { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "FILEIT: \"" << *fileIt << "\"" + << std::endl); + std::string::size_type slashPos = fileIt->find('/', topLevelLength + 1); + std::string relativeDir = + fileIt->substr(topLevelLength, slashPos - topLevelLength); + cmCPackLogger(cmCPackLog::LOG_DEBUG, "RELATIVEDIR: \"" + << relativeDir << "\"" << std::endl); + + // do not recurse because the loop will do it + if (!data_tar.Add(*fileIt, topLevelLength, ".", false)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem adding file to tar:" + << std::endl + << "#top level directory: " << strGenWDIR << std::endl + << "#file: " << *fileIt << std::endl + << "#error:" << data_tar.GetError() << std::endl); + return 0; + } + } + } // scope for file generation + + std::string md5filename = strGenWDIR + "/md5sums"; + { + // the scope is needed for cmGeneratedFileStream + cmGeneratedFileStream out(md5filename.c_str()); + + std::string topLevelWithTrailingSlash = + this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + topLevelWithTrailingSlash += '/'; + for (std::vector<std::string>::const_iterator fileIt = + packageFiles.begin(); + fileIt != packageFiles.end(); ++fileIt) { + // hash only regular files + if (cmSystemTools::FileIsDirectory(*fileIt) || + cmSystemTools::FileIsSymlink(*fileIt)) { + continue; + } + + char md5sum[33]; + if (!cmSystemTools::ComputeFileMD5(*fileIt, md5sum)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem computing the md5 of " + << *fileIt << std::endl); + } + + md5sum[32] = 0; + + std::string output(md5sum); + output += " " + *fileIt + "\n"; + // debian md5sums entries are like this: + // 014f3604694729f3bf19263bac599765 usr/bin/ccmake + // thus strip the full path (with the trailing slash) + cmSystemTools::ReplaceString(output, topLevelWithTrailingSlash.c_str(), + ""); + out << output; + } + // each line contains a eol. + // Do not end the md5sum file with yet another (invalid) + } + + std::string filename_control_tar = strGenWDIR + "/control.tar.gz"; + // atomic file generation for control.tar + { + cmGeneratedFileStream fileStream_control_tar; + fileStream_control_tar.Open(filename_control_tar.c_str(), false, true); + if (!fileStream_control_tar) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error opening the file \"" + << filename_control_tar << "\" for writing" + << std::endl); + return 0; + } + cmArchiveWrite control_tar(fileStream_control_tar, + cmArchiveWrite::CompressGZip, "paxr"); + + // sets permissions and uid/gid for the files + control_tar.SetUIDAndGID(0u, 0u); + control_tar.SetUNAMEAndGNAME("root", "root"); + + /* permissions are set according to + https://www.debian.org/doc/debian-policy/ch-files.html#s-permissions-owners + and + https://lintian.debian.org/tags/control-file-has-bad-permissions.html + */ + const mode_t permission644 = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + const mode_t permissionExecute = S_IXUSR | S_IXGRP | S_IXOTH; + const mode_t permission755 = permission644 | permissionExecute; + + // for md5sum and control (that we have generated here), we use 644 + // (RW-R--R--) + // so that deb lintian doesn't warn about it + control_tar.SetPermissions(permission644); + + // adds control and md5sums + if (!control_tar.Add(md5filename, strGenWDIR.length(), ".") || + !control_tar.Add(strGenWDIR + "/control", strGenWDIR.length(), ".")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error adding file to tar:" + << std::endl + << "#top level directory: " << strGenWDIR << std::endl + << "#file: \"control\" or \"md5sums\"" << std::endl + << "#error:" << control_tar.GetError() << std::endl); + return 0; + } + + // adds generated shlibs file + if (gen_shibs) { + if (!control_tar.Add(shlibsfilename, strGenWDIR.length(), ".")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error adding file to tar:" + << std::endl + << "#top level directory: " << strGenWDIR << std::endl + << "#file: \"shlibs\"" << std::endl + << "#error:" << control_tar.GetError() << std::endl); + return 0; + } + } + + // adds LDCONFIG related files + if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTINST")) { + control_tar.SetPermissions(permission755); + if (!control_tar.Add(postinst, strGenWDIR.length(), ".")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error adding file to tar:" + << std::endl + << "#top level directory: " << strGenWDIR << std::endl + << "#file: \"postinst\"" << std::endl + << "#error:" << control_tar.GetError() << std::endl); + return 0; + } + control_tar.SetPermissions(permission644); + } + + if (this->IsOn("GEN_CPACK_DEBIAN_GENERATE_POSTRM")) { + control_tar.SetPermissions(permission755); + if (!control_tar.Add(postrm, strGenWDIR.length(), ".")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error adding file to tar:" + << std::endl + << "#top level directory: " << strGenWDIR << std::endl + << "#file: \"postinst\"" << std::endl + << "#error:" << control_tar.GetError() << std::endl); + return 0; + } + control_tar.SetPermissions(permission644); + } + + // for the other files, we use + // -either the original permission on the files + // -either a permission strictly defined by the Debian policies + const char* controlExtra = + this->GetOption("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA"); + if (controlExtra) { + // permissions are now controlled by the original file permissions + + const bool permissionStrictPolicy = + this->IsSet("GEN_CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION"); + + static const char* strictFiles[] = { "config", "postinst", "postrm", + "preinst", "prerm" }; + std::set<std::string> setStrictFiles( + strictFiles, + strictFiles + sizeof(strictFiles) / sizeof(strictFiles[0])); + + // default + control_tar.ClearPermissions(); + + std::vector<std::string> controlExtraList; + cmSystemTools::ExpandListArgument(controlExtra, controlExtraList); + for (std::vector<std::string>::iterator i = controlExtraList.begin(); + i != controlExtraList.end(); ++i) { + std::string filenamename = cmsys::SystemTools::GetFilenameName(*i); + std::string localcopy = strGenWDIR + "/" + filenamename; + + if (permissionStrictPolicy) { + control_tar.SetPermissions(setStrictFiles.count(filenamename) + ? permission755 + : permission644); + } + + // if we can copy the file, it means it does exist, let's add it: + if (cmsys::SystemTools::CopyFileIfDifferent(*i, localcopy)) { + control_tar.Add(localcopy, strGenWDIR.length(), "."); + } + } + } + } + + // ar -r your-package-name.deb debian-binary control.tar.* data.tar.* + // since debian packages require BSD ar (most Linux distros and even + // FreeBSD and NetBSD ship GNU ar) we use a copy of OpenBSD ar here. + std::vector<std::string> arFiles; + std::string topLevelString = strGenWDIR + "/"; + arFiles.push_back(topLevelString + "debian-binary"); + arFiles.push_back(topLevelString + "control.tar.gz"); + arFiles.push_back(topLevelString + "data.tar" + compression_suffix); + std::string outputFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + outputFileName += "/"; + outputFileName += this->GetOption("GEN_CPACK_OUTPUT_FILE_NAME"); + int res = ar_append(outputFileName.c_str(), arFiles); + if (res != 0) { + std::string tmpFile = + this->GetOption("GEN_CPACK_TEMPORARY_PACKAGE_FILE_NAME"); + tmpFile += "/Deb.log"; + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Problem creating archive using: " << res << std::endl; + return 0; + } + return 1; +} + +bool cmCPackDebGenerator::SupportsComponentInstallation() const +{ + return IsOn("CPACK_DEB_COMPONENT_INSTALL"); +} + +std::string cmCPackDebGenerator::GetComponentInstallDirNameSuffix( + const std::string& componentName) +{ + if (componentPackageMethod == ONE_PACKAGE_PER_COMPONENT) { + return componentName; + } + + if (componentPackageMethod == ONE_PACKAGE) { + return std::string("ALL_COMPONENTS_IN_ONE"); + } + // We have to find the name of the COMPONENT GROUP + // the current COMPONENT belongs to. + std::string groupVar = + "CPACK_COMPONENT_" + cmSystemTools::UpperCase(componentName) + "_GROUP"; + if (CM_NULLPTR != GetOption(groupVar)) { + return std::string(GetOption(groupVar)); + } else { + return componentName; + } +} + +// The following code is taken from OpenBSD ar: +// http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ar/ +// It has been slightly modified: +// -return error codes instead exit() in functions +// -use the stdio file I/O functions instead the file descriptor based ones +// -merged into one cxx file +// -no additional options supported +// The coding style hasn't been modified. + +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Hugh Smith at The University of Guelph. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +// include sys/stat.h after sys/types.h +#include <sys/stat.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define ARMAG "!<arch>\n" /* ar "magic number" */ +#define SARMAG 8 /* strlen(ARMAG); */ + +#define AR_EFMT1 "#1/" /* extended format #1 */ +#define ARFMAG "`\n" + +/* Header format strings. */ +#define HDR1 "%s%-13d%-12ld%-6u%-6u%-8o%-10lld%2s" +#define HDR2 "%-16.16s%-12ld%-6u%-6u%-8o%-10lld%2s" + +struct ar_hdr +{ + char ar_name[16]; /* name */ + char ar_date[12]; /* modification time */ + char ar_uid[6]; /* user id */ + char ar_gid[6]; /* group id */ + char ar_mode[8]; /* octal file permissions */ + char ar_size[10]; /* size in bytes */ + char ar_fmag[2]; /* consistency check */ +}; + +/* Set up file copy. */ +#define SETCF(from, fromname, to, toname, pad) \ + { \ + cf.rFile = from; \ + cf.rname = fromname; \ + cf.wFile = to; \ + cf.wname = toname; \ + cf.flags = pad; \ + } + +/* File copy structure. */ +typedef struct +{ + FILE* rFile; /* read file descriptor */ + const char* rname; /* read name */ + FILE* wFile; /* write file descriptor */ + const char* wname; /* write name */ +#define NOPAD 0x00 /* don't pad */ +#define WPAD 0x02 /* pad on writes */ + unsigned int flags; /* pad flags */ +} CF; + +/* misc.c */ + +static const char* ar_rname(const char* path) +{ + const char* ind = strrchr(path, '/'); + return (ind) ? ind + 1 : path; +} + +/* archive.c */ + +typedef struct ar_hdr HDR; +static char ar_hb[sizeof(HDR) + 1]; /* real header */ + +static size_t ar_already_written; + +/* copy_ar -- + * Copy size bytes from one file to another - taking care to handle the + * extra byte (for odd size files) when reading archives and writing an + * extra byte if necessary when adding files to archive. The length of + * the object is the long name plus the object itself; the variable + * already_written gets set if a long name was written. + * + * The padding is really unnecessary, and is almost certainly a remnant + * of early archive formats where the header included binary data which + * a PDP-11 required to start on an even byte boundary. (Or, perhaps, + * because 16-bit word addressed copies were faster?) Anyhow, it should + * have been ripped out long ago. + */ +static int copy_ar(CF* cfp, off_t size) +{ + static char pad = '\n'; + off_t sz = size; + size_t nr, nw; + char buf[8 * 1024]; + + if (sz == 0) { + return 0; + } + + FILE* from = cfp->rFile; + FILE* to = cfp->wFile; + while (sz && + (nr = fread(buf, 1, sz < static_cast<off_t>(sizeof(buf)) + ? static_cast<size_t>(sz) + : sizeof(buf), + from)) > 0) { + sz -= nr; + for (size_t off = 0; off < nr; nr -= off, off += nw) { + if ((nw = fwrite(buf + off, 1, nr, to)) < nr) { + return -1; + } + } + } + if (sz) { + return -2; + } + + if (cfp->flags & WPAD && (size + ar_already_written) & 1 && + fwrite(&pad, 1, 1, to) != 1) { + return -4; + } + + return 0; +} + +/* put_arobj -- Write an archive member to a file. */ +static int put_arobj(CF* cfp, struct stat* sb) +{ + int result = 0; + struct ar_hdr* hdr; + + /* If passed an sb structure, reading a file from disk. Get stat(2) + * information, build a name and construct a header. (Files are named + * by their last component in the archive.) */ + const char* name = ar_rname(cfp->rname); + (void)stat(cfp->rname, sb); + + /* If not truncating names and the name is too long or contains + * a space, use extended format 1. */ + size_t lname = strlen(name); + uid_t uid = sb->st_uid; + gid_t gid = sb->st_gid; + if (uid > USHRT_MAX) { + uid = USHRT_MAX; + } + if (gid > USHRT_MAX) { + gid = USHRT_MAX; + } + if (lname > sizeof(hdr->ar_name) || strchr(name, ' ')) { + (void)sprintf(ar_hb, HDR1, AR_EFMT1, (int)lname, (long int)sb->st_mtime, + (unsigned)uid, (unsigned)gid, (unsigned)sb->st_mode, + (long long)sb->st_size + lname, ARFMAG); + } else { + lname = 0; + (void)sprintf(ar_hb, HDR2, name, (long int)sb->st_mtime, (unsigned)uid, + (unsigned)gid, (unsigned)sb->st_mode, (long long)sb->st_size, + ARFMAG); + } + off_t size = sb->st_size; + + if (fwrite(ar_hb, 1, sizeof(HDR), cfp->wFile) != sizeof(HDR)) { + return -1; + } + + if (lname) { + if (fwrite(name, 1, lname, cfp->wFile) != lname) { + return -2; + } + ar_already_written = lname; + } + result = copy_ar(cfp, size); + ar_already_written = 0; + return result; +} + +/* append.c */ + +/* append -- + * Append files to the archive - modifies original archive or creates + * a new archive if named archive does not exist. + */ +static int ar_append(const char* archive, + const std::vector<std::string>& files) +{ + int eval = 0; + FILE* aFile = cmSystemTools::Fopen(archive, "wb+"); + if (aFile != CM_NULLPTR) { + fwrite(ARMAG, SARMAG, 1, aFile); + if (fseek(aFile, 0, SEEK_END) != -1) { + CF cf; + struct stat sb; + /* Read from disk, write to an archive; pad on write. */ + SETCF(CM_NULLPTR, CM_NULLPTR, aFile, archive, WPAD); + for (std::vector<std::string>::const_iterator fileIt = files.begin(); + fileIt != files.end(); ++fileIt) { + const char* filename = fileIt->c_str(); + FILE* file = cmSystemTools::Fopen(filename, "rb"); + if (file == CM_NULLPTR) { + eval = -1; + continue; + } + cf.rFile = file; + cf.rname = filename; + int result = put_arobj(&cf, &sb); + (void)fclose(file); + if (result != 0) { + eval = -2; + break; + } + } + } else { + eval = -3; + } + fclose(aFile); + } else { + eval = -4; + } + return eval; +} diff --git a/Source/CPack/cmCPackDebGenerator.h b/Source/CPack/cmCPackDebGenerator.h new file mode 100644 index 0000000..bcdc509 --- /dev/null +++ b/Source/CPack/cmCPackDebGenerator.h @@ -0,0 +1,76 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackDebGenerator_h +#define cmCPackDebGenerator_h + +#include "cmCPackGenerator.h" + +/** \class cmCPackDebGenerator + * \brief A generator for Debian packages + * + */ +class cmCPackDebGenerator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackDebGenerator, cmCPackGenerator); + + /** + * Construct generator + */ + cmCPackDebGenerator(); + ~cmCPackDebGenerator() CM_OVERRIDE; + + static bool CanGenerate() + { +#ifdef __APPLE__ + // on MacOS enable CPackDeb iff dpkg is found + std::vector<std::string> locations; + locations.push_back("/sw/bin"); // Fink + locations.push_back("/opt/local/bin"); // MacPorts + return cmSystemTools::FindProgram("dpkg", locations) != "" ? true : false; +#else + // legacy behavior on other systems + return true; +#endif + } + +protected: + int InitializeInternal() CM_OVERRIDE; + /** + * This method factors out the work done in component packaging case. + */ + int PackageOnePack(std::string const& initialToplevel, + std::string const& packageName); + /** + * The method used to package files when component + * install is used. This will create one + * archive for each component group. + */ + int PackageComponents(bool ignoreGroup); + /** + * Special case of component install where all + * components will be put in a single installer. + */ + int PackageComponentsAllInOne(const std::string& compInstDirName); + int PackageFiles() CM_OVERRIDE; + const char* GetOutputExtension() CM_OVERRIDE { return ".deb"; } + bool SupportsComponentInstallation() const CM_OVERRIDE; + std::string GetComponentInstallDirNameSuffix( + const std::string& componentName) CM_OVERRIDE; + +private: + int createDeb(); + std::vector<std::string> packageFiles; +}; + +#endif diff --git a/Source/CPack/cmCPackDragNDropGenerator.cxx b/Source/CPack/cmCPackDragNDropGenerator.cxx new file mode 100644 index 0000000..640e437 --- /dev/null +++ b/Source/CPack/cmCPackDragNDropGenerator.cxx @@ -0,0 +1,898 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackDragNDropGenerator.h" + +#include "cmCPackLog.h" +#include "cmGeneratedFileStream.h" +#include "cmSystemTools.h" + +#include <cmsys/FStream.hxx> +#include <cmsys/RegularExpression.hxx> + +#include <iomanip> + +#include <CoreFoundation/CoreFoundation.h> + +#ifdef HAVE_CoreServices +// For the old LocaleStringToLangAndRegionCodes() function, to convert +// to the old Script Manager RegionCode values needed for the 'LPic' data +// structure used for generating multi-lingual SLAs. +#include <CoreServices/CoreServices.h> +#endif + +static const char* SLAHeader = + "data 'LPic' (5000) {\n" + " $\"0002 0011 0003 0001 0000 0000 0002 0000\"\n" + " $\"0008 0003 0000 0001 0004 0000 0004 0005\"\n" + " $\"0000 000E 0006 0001 0005 0007 0000 0007\"\n" + " $\"0008 0000 0047 0009 0000 0034 000A 0001\"\n" + " $\"0035 000B 0001 0020 000C 0000 0011 000D\"\n" + " $\"0000 005B 0004 0000 0033 000F 0001 000C\"\n" + " $\"0010 0000 000B 000E 0000\"\n" + "};\n" + "\n"; + +static const char* SLASTREnglish = + "resource 'STR#' (5002, \"English\") {\n" + " {\n" + " \"English\",\n" + " \"Agree\",\n" + " \"Disagree\",\n" + " \"Print\",\n" + " \"Save...\",\n" + " \"You agree to the License Agreement terms when you click \"\n" + " \"the \\\"Agree\\\" button.\",\n" + " \"Software License Agreement\",\n" + " \"This text cannot be saved. This disk may be full or locked, " + "or the \"\n" + " \"file may be locked.\",\n" + " \"Unable to print. Make sure you have selected a printer.\"\n" + " }\n" + "};\n" + "\n"; + +cmCPackDragNDropGenerator::cmCPackDragNDropGenerator() + : singleLicense(false) +{ + // default to one package file for components + this->componentPackageMethod = ONE_PACKAGE; +} + +cmCPackDragNDropGenerator::~cmCPackDragNDropGenerator() +{ +} + +int cmCPackDragNDropGenerator::InitializeInternal() +{ + // Starting with Xcode 4.3, look in "/Applications/Xcode.app" first: + // + std::vector<std::string> paths; + paths.push_back("/Applications/Xcode.app/Contents/Developer/Tools"); + paths.push_back("/Developer/Tools"); + + const std::string hdiutil_path = + cmSystemTools::FindProgram("hdiutil", std::vector<std::string>(), false); + if (hdiutil_path.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot locate hdiutil command" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_COMMAND_HDIUTIL", hdiutil_path.c_str()); + + const std::string setfile_path = + cmSystemTools::FindProgram("SetFile", paths, false); + if (setfile_path.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot locate SetFile command" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_COMMAND_SETFILE", setfile_path.c_str()); + + const std::string rez_path = cmSystemTools::FindProgram("Rez", paths, false); + if (rez_path.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot locate Rez command" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_COMMAND_REZ", rez_path.c_str()); + + if (this->IsSet("CPACK_DMG_SLA_DIR")) { + slaDirectory = this->GetOption("CPACK_DMG_SLA_DIR"); + if (!slaDirectory.empty() && this->IsSet("CPACK_RESOURCE_FILE_LICENSE")) { + std::string license_file = + this->GetOption("CPACK_RESOURCE_FILE_LICENSE"); + if (!license_file.empty() && + (license_file.find("CPack.GenericLicense.txt") == + std::string::npos)) { + cmCPackLogger( + cmCPackLog::LOG_OUTPUT, + "Both CPACK_DMG_SLA_DIR and CPACK_RESOURCE_FILE_LICENSE specified, " + "using CPACK_RESOURCE_FILE_LICENSE as a license for all languages." + << std::endl); + singleLicense = true; + } + } + if (!this->IsSet("CPACK_DMG_SLA_LANGUAGES")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_DMG_SLA_DIR set but no languages defined " + "(set CPACK_DMG_SLA_LANGUAGES)" + << std::endl); + return 0; + } + if (!cmSystemTools::FileExists(slaDirectory, false)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "CPACK_DMG_SLA_DIR does not exist" + << std::endl); + return 0; + } + + std::vector<std::string> languages; + cmSystemTools::ExpandListArgument( + this->GetOption("CPACK_DMG_SLA_LANGUAGES"), languages); + if (languages.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_DMG_SLA_LANGUAGES set but empty" << std::endl); + return 0; + } + for (size_t i = 0; i < languages.size(); ++i) { + std::string license = slaDirectory + "/" + languages[i] + ".license.txt"; + if (!singleLicense && !cmSystemTools::FileExists(license)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Missing license file " + << languages[i] << ".license.txt" << std::endl); + return 0; + } + std::string menu = slaDirectory + "/" + languages[i] + ".menu.txt"; + if (!cmSystemTools::FileExists(menu)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Missing menu file " + << languages[i] << ".menu.txt" << std::endl); + return 0; + } + } + } + + return this->Superclass::InitializeInternal(); +} + +const char* cmCPackDragNDropGenerator::GetOutputExtension() +{ + return ".dmg"; +} + +int cmCPackDragNDropGenerator::PackageFiles() +{ + // gather which directories to make dmg files for + // multiple directories occur if packaging components or groups separately + + // monolith + if (this->Components.empty()) { + return this->CreateDMG(toplevel, packageFileNames[0]); + } + + // component install + std::vector<std::string> package_files; + + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt = this->Components.begin(); compIt != this->Components.end(); + ++compIt) { + std::string name = GetComponentInstallDirNameSuffix(compIt->first); + package_files.push_back(name); + } + std::sort(package_files.begin(), package_files.end()); + package_files.erase(std::unique(package_files.begin(), package_files.end()), + package_files.end()); + + // loop to create dmg files + packageFileNames.clear(); + for (size_t i = 0; i < package_files.size(); i++) { + std::string full_package_name = std::string(toplevel) + std::string("/"); + if (package_files[i] == "ALL_IN_ONE") { + full_package_name += this->GetOption("CPACK_PACKAGE_FILE_NAME"); + } else { + full_package_name += package_files[i]; + } + full_package_name += std::string(GetOutputExtension()); + packageFileNames.push_back(full_package_name); + + std::string src_dir = toplevel; + src_dir += "/"; + src_dir += package_files[i]; + + if (0 == this->CreateDMG(src_dir, full_package_name)) { + return 0; + } + } + return 1; +} + +bool cmCPackDragNDropGenerator::CopyFile(std::ostringstream& source, + std::ostringstream& target) +{ + if (!cmSystemTools::CopyFileIfDifferent(source.str().c_str(), + target.str().c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error copying " + << source.str() << " to " << target.str() << std::endl); + + return false; + } + + return true; +} + +bool cmCPackDragNDropGenerator::CreateEmptyFile(std::ostringstream& target, + size_t size) +{ + cmsys::ofstream fout(target.str().c_str(), std::ios::out | std::ios::binary); + if (!fout) { + return false; + } else { + // Seek to desired size - 1 byte + fout.seekp(size - 1, std::ios::beg); + char byte = 0; + // Write one byte to ensure file grows + fout.write(&byte, 1); + } + + return true; +} + +bool cmCPackDragNDropGenerator::RunCommand(std::ostringstream& command, + std::string* output) +{ + int exit_code = 1; + + bool result = + cmSystemTools::RunSingleCommand(command.str().c_str(), output, output, + &exit_code, 0, this->GeneratorVerbose, 0); + + if (!result || exit_code) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error executing: " << command.str() + << std::endl); + + return false; + } + + return true; +} + +int cmCPackDragNDropGenerator::CreateDMG(const std::string& src_dir, + const std::string& output_file) +{ + // Get optional arguments ... + const std::string cpack_package_icon = this->GetOption("CPACK_PACKAGE_ICON") + ? this->GetOption("CPACK_PACKAGE_ICON") + : ""; + + const std::string cpack_dmg_volume_name = + this->GetOption("CPACK_DMG_VOLUME_NAME") + ? this->GetOption("CPACK_DMG_VOLUME_NAME") + : this->GetOption("CPACK_PACKAGE_FILE_NAME"); + + const std::string cpack_dmg_format = this->GetOption("CPACK_DMG_FORMAT") + ? this->GetOption("CPACK_DMG_FORMAT") + : "UDZO"; + + // Get optional arguments ... + std::string cpack_license_file = + this->GetOption("CPACK_RESOURCE_FILE_LICENSE") + ? this->GetOption("CPACK_RESOURCE_FILE_LICENSE") + : ""; + + const std::string cpack_dmg_background_image = + this->GetOption("CPACK_DMG_BACKGROUND_IMAGE") + ? this->GetOption("CPACK_DMG_BACKGROUND_IMAGE") + : ""; + + const std::string cpack_dmg_ds_store = this->GetOption("CPACK_DMG_DS_STORE") + ? this->GetOption("CPACK_DMG_DS_STORE") + : ""; + + const std::string cpack_dmg_languages = + this->GetOption("CPACK_DMG_SLA_LANGUAGES") + ? this->GetOption("CPACK_DMG_SLA_LANGUAGES") + : ""; + + const std::string cpack_dmg_ds_store_setup_script = + this->GetOption("CPACK_DMG_DS_STORE_SETUP_SCRIPT") + ? this->GetOption("CPACK_DMG_DS_STORE_SETUP_SCRIPT") + : ""; + + const bool cpack_dmg_disable_applications_symlink = + this->IsOn("CPACK_DMG_DISABLE_APPLICATIONS_SYMLINK"); + + // only put license on dmg if is user provided + if (!cpack_license_file.empty() && + cpack_license_file.find("CPack.GenericLicense.txt") != + std::string::npos) { + cpack_license_file = ""; + } + + // use sla_dir if both sla_dir and license_file are set + if (!cpack_license_file.empty() && !slaDirectory.empty() && !singleLicense) { + cpack_license_file = ""; + } + + // The staging directory contains everything that will end-up inside the + // final disk image ... + std::ostringstream staging; + staging << src_dir; + + // Add a symlink to /Applications so users can drag-and-drop the bundle + // into it unless this behaviour was disabled + if (!cpack_dmg_disable_applications_symlink) { + std::ostringstream application_link; + application_link << staging.str() << "/Applications"; + cmSystemTools::CreateSymlink("/Applications", + application_link.str().c_str()); + } + + // Optionally add a custom volume icon ... + if (!cpack_package_icon.empty()) { + std::ostringstream package_icon_source; + package_icon_source << cpack_package_icon; + + std::ostringstream package_icon_destination; + package_icon_destination << staging.str() << "/.VolumeIcon.icns"; + + if (!this->CopyFile(package_icon_source, package_icon_destination)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error copying disk volume icon. " + "Check the value of CPACK_PACKAGE_ICON." + << std::endl); + + return 0; + } + } + + // Optionally add a custom .DS_Store file + // (e.g. for setting background/layout) ... + if (!cpack_dmg_ds_store.empty()) { + std::ostringstream package_settings_source; + package_settings_source << cpack_dmg_ds_store; + + std::ostringstream package_settings_destination; + package_settings_destination << staging.str() << "/.DS_Store"; + + if (!this->CopyFile(package_settings_source, + package_settings_destination)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error copying disk volume settings file. " + "Check the value of CPACK_DMG_DS_STORE." + << std::endl); + + return 0; + } + } + + // Optionally add a custom background image ... + // Make sure the background file type is the same as the custom image + // and that the file is hidden so it doesn't show up. + if (!cpack_dmg_background_image.empty()) { + const std::string extension = + cmSystemTools::GetFilenameLastExtension(cpack_dmg_background_image); + std::ostringstream package_background_source; + package_background_source << cpack_dmg_background_image; + + std::ostringstream package_background_destination; + package_background_destination << staging.str() + << "/.background/background" << extension; + + if (!this->CopyFile(package_background_source, + package_background_destination)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error copying disk volume background image. " + "Check the value of CPACK_DMG_BACKGROUND_IMAGE." + << std::endl); + + return 0; + } + } + + bool remount_image = + !cpack_package_icon.empty() || !cpack_dmg_ds_store_setup_script.empty(); + + // Create 1 MB dummy padding file in staging area when we need to remount + // image, so we have enough space for storing changes ... + if (remount_image) { + std::ostringstream dummy_padding; + dummy_padding << staging.str() << "/.dummy-padding-file"; + if (!this->CreateEmptyFile(dummy_padding, 1048576)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error creating dummy padding file." + << std::endl); + + return 0; + } + } + + // Create a temporary read-write disk image ... + std::string temp_image = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + temp_image += "/temp.dmg"; + + std::ostringstream temp_image_command; + temp_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + temp_image_command << " create"; + temp_image_command << " -ov"; + temp_image_command << " -srcfolder \"" << staging.str() << "\""; + temp_image_command << " -volname \"" << cpack_dmg_volume_name << "\""; + temp_image_command << " -format UDRW"; + temp_image_command << " \"" << temp_image << "\""; + + if (!this->RunCommand(temp_image_command)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error generating temporary disk image." << std::endl); + + return 0; + } + + if (remount_image) { + // Store that we have a failure so that we always unmount the image + // before we exit. + bool had_error = false; + + std::ostringstream attach_command; + attach_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + attach_command << " attach"; + attach_command << " \"" << temp_image << "\""; + + std::string attach_output; + if (!this->RunCommand(attach_command, &attach_output)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error attaching temporary disk image." << std::endl); + + return 0; + } + + cmsys::RegularExpression mountpoint_regex(".*(/Volumes/[^\n]+)\n.*"); + mountpoint_regex.find(attach_output.c_str()); + std::string const temp_mount = mountpoint_regex.match(1); + + // Remove dummy padding file so we have enough space on RW image ... + std::ostringstream dummy_padding; + dummy_padding << temp_mount << "/.dummy-padding-file"; + if (!cmSystemTools::RemoveFile(dummy_padding.str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error removing dummy padding file." + << std::endl); + + had_error = true; + } + + // Optionally set the custom icon flag for the image ... + if (!had_error && !cpack_package_icon.empty()) { + std::ostringstream setfile_command; + setfile_command << this->GetOption("CPACK_COMMAND_SETFILE"); + setfile_command << " -a C"; + setfile_command << " \"" << temp_mount << "\""; + + if (!this->RunCommand(setfile_command)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error assigning custom icon to temporary disk image." + << std::endl); + + had_error = true; + } + } + + // Optionally we can execute a custom apple script to generate + // the .DS_Store for the volume folder ... + if (!had_error && !cpack_dmg_ds_store_setup_script.empty()) { + std::ostringstream setup_script_command; + setup_script_command << "osascript" + << " \"" << cpack_dmg_ds_store_setup_script << "\"" + << " \"" << cpack_dmg_volume_name << "\""; + std::string error; + if (!this->RunCommand(setup_script_command, &error)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error executing custom script on disk image." + << std::endl + << error << std::endl); + + had_error = true; + } + } + + std::ostringstream detach_command; + detach_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + detach_command << " detach"; + detach_command << " \"" << temp_mount << "\""; + + if (!this->RunCommand(detach_command)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error detaching temporary disk image." << std::endl); + + return 0; + } + + if (had_error) { + return 0; + } + } + + if (!cpack_license_file.empty() || !slaDirectory.empty()) { + // Use old hardcoded style if sla_dir is not set + bool oldStyle = slaDirectory.empty(); + std::string sla_r = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + sla_r += "/sla.r"; + + std::vector<std::string> languages; + if (!oldStyle) { + cmSystemTools::ExpandListArgument(cpack_dmg_languages, languages); + } + + cmGeneratedFileStream ofs(sla_r.c_str()); + ofs << "#include <CoreServices/CoreServices.r>\n\n"; + if (oldStyle) { + ofs << SLAHeader; + ofs << "\n"; + } else { + /* + * LPic Layout + * (https://github.com/pypt/dmg-add-license/blob/master/main.c) + * as far as I can tell (no official documentation seems to exist): + * struct LPic { + * uint16_t default_language; // points to a resid, defaulting to 0, + * // which is the first set language + * uint16_t length; + * struct { + * uint16_t language_code; + * uint16_t resid; + * uint16_t encoding; // Encoding from TextCommon.h, + * // forcing MacRoman (0) for now. Might need to + * // allow overwrite per license by user later + * } item[1]; + * } + */ + + // Create vector first for readability, then iterate to write to ofs + std::vector<uint16_t> header_data; + header_data.push_back(0); + header_data.push_back(languages.size()); + for (size_t i = 0; i < languages.size(); ++i) { + CFStringRef language_cfstring = CFStringCreateWithCString( + NULL, languages[i].c_str(), kCFStringEncodingUTF8); + CFStringRef iso_language = + CFLocaleCreateCanonicalLanguageIdentifierFromString( + NULL, language_cfstring); + if (!iso_language) { + cmCPackLogger(cmCPackLog::LOG_ERROR, languages[i] + << " is not a recognized language" << std::endl); + } + char* iso_language_cstr = (char*)malloc(65); + CFStringGetCString(iso_language, iso_language_cstr, 64, + kCFStringEncodingMacRoman); + LangCode lang = 0; + RegionCode region = 0; +#ifdef HAVE_CoreServices + OSStatus err = + LocaleStringToLangAndRegionCodes(iso_language_cstr, &lang, ®ion); + if (err != noErr) +#endif + { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "No language/region code available for " + << iso_language_cstr << std::endl); + free(iso_language_cstr); + return 0; + } +#ifdef HAVE_CoreServices + free(iso_language_cstr); + header_data.push_back(region); + header_data.push_back(i); + header_data.push_back(0); +#endif + } + ofs << "data 'LPic' (5000) {\n"; + ofs << std::hex << std::uppercase << std::setfill('0'); + + for (size_t i = 0; i < header_data.size(); ++i) { + if (i % 8 == 0) { + ofs << " $\""; + } + + ofs << std::setw(4) << header_data[i]; + + if (i % 8 == 7 || i == header_data.size() - 1) { + ofs << "\"\n"; + } else { + ofs << " "; + } + } + ofs << "};\n\n"; + // Reset ofs options + ofs << std::dec << std::nouppercase << std::setfill(' '); + } + + bool have_write_license_error = false; + std::string error; + + if (oldStyle) { + if (!this->WriteLicense(ofs, 0, "", cpack_license_file, &error)) { + have_write_license_error = true; + } + } else { + for (size_t i = 0; i < languages.size() && !have_write_license_error; + ++i) { + if (singleLicense) { + if (!this->WriteLicense(ofs, i + 5000, languages[i], + cpack_license_file, &error)) { + have_write_license_error = true; + } + } else { + if (!this->WriteLicense(ofs, i + 5000, languages[i], "", &error)) { + have_write_license_error = true; + } + } + } + } + + ofs.Close(); + + if (have_write_license_error) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error writing license file to SLA." + << std::endl + << error << std::endl); + return 0; + } + + // convert to UDCO + std::string temp_udco = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + temp_udco += "/temp-udco.dmg"; + + std::ostringstream udco_image_command; + udco_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + udco_image_command << " convert \"" << temp_image << "\""; + udco_image_command << " -format UDCO"; + udco_image_command << " -ov -o \"" << temp_udco << "\""; + + if (!this->RunCommand(udco_image_command, &error)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error converting to UDCO dmg for adding SLA." + << std::endl + << error << std::endl); + return 0; + } + + // unflatten dmg + std::ostringstream unflatten_command; + unflatten_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + unflatten_command << " unflatten "; + unflatten_command << "\"" << temp_udco << "\""; + + if (!this->RunCommand(unflatten_command, &error)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error unflattening dmg for adding SLA." << std::endl + << error + << std::endl); + return 0; + } + + // Rez the SLA + std::ostringstream embed_sla_command; + embed_sla_command << this->GetOption("CPACK_COMMAND_REZ"); + const char* sysroot = this->GetOption("CPACK_OSX_SYSROOT"); + if (sysroot && sysroot[0] != '\0') { + embed_sla_command << " -isysroot \"" << sysroot << "\""; + } + embed_sla_command << " \"" << sla_r << "\""; + embed_sla_command << " -a -o "; + embed_sla_command << "\"" << temp_udco << "\""; + + if (!this->RunCommand(embed_sla_command, &error)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error adding SLA." << std::endl + << error + << std::endl); + return 0; + } + + // flatten dmg + std::ostringstream flatten_command; + flatten_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + flatten_command << " flatten "; + flatten_command << "\"" << temp_udco << "\""; + + if (!this->RunCommand(flatten_command, &error)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Error flattening dmg for adding SLA." << std::endl + << error + << std::endl); + return 0; + } + + temp_image = temp_udco; + } + + // Create the final compressed read-only disk image ... + std::ostringstream final_image_command; + final_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL"); + final_image_command << " convert \"" << temp_image << "\""; + final_image_command << " -format "; + final_image_command << cpack_dmg_format; + final_image_command << " -imagekey"; + final_image_command << " zlib-level=9"; + final_image_command << " -o \"" << output_file << "\""; + + if (!this->RunCommand(final_image_command)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error compressing disk image." + << std::endl); + + return 0; + } + + return 1; +} + +bool cmCPackDragNDropGenerator::SupportsComponentInstallation() const +{ + return true; +} + +std::string cmCPackDragNDropGenerator::GetComponentInstallDirNameSuffix( + const std::string& componentName) +{ + // we want to group components together that go in the same dmg package + std::string package_file_name = this->GetOption("CPACK_PACKAGE_FILE_NAME"); + + // we have 3 mutually exclusive modes to work in + // 1. all components in one package + // 2. each group goes in its own package with left over + // components in their own package + // 3. ignore groups - if grouping is defined, it is ignored + // and each component goes in its own package + + if (this->componentPackageMethod == ONE_PACKAGE) { + return "ALL_IN_ONE"; + } + + if (this->componentPackageMethod == ONE_PACKAGE_PER_GROUP) { + // We have to find the name of the COMPONENT GROUP + // the current COMPONENT belongs to. + std::string groupVar = + "CPACK_COMPONENT_" + cmSystemTools::UpperCase(componentName) + "_GROUP"; + const char* _groupName = GetOption(groupVar.c_str()); + if (_groupName) { + std::string groupName = _groupName; + + groupName = + GetComponentPackageFileName(package_file_name, groupName, true); + return groupName; + } + } + + return GetComponentPackageFileName(package_file_name, componentName, false); +} + +bool cmCPackDragNDropGenerator::WriteLicense( + cmGeneratedFileStream& outputStream, int licenseNumber, + std::string licenseLanguage, std::string licenseFile, std::string* error) +{ + if (!licenseFile.empty() && !singleLicense) { + licenseNumber = 5002; + licenseLanguage = "English"; + } + + // License header + outputStream << "data 'TEXT' (" << licenseNumber << ", \"" << licenseLanguage + << "\") {\n"; + // License body + std::string actual_license = !licenseFile.empty() + ? licenseFile + : (slaDirectory + "/" + licenseLanguage + ".license.txt"); + cmsys::ifstream license_ifs; + license_ifs.open(actual_license.c_str()); + if (license_ifs.is_open()) { + while (license_ifs.good()) { + std::string line; + std::getline(license_ifs, line); + if (!line.empty()) { + EscapeQuotesAndBackslashes(line); + std::vector<std::string> lines; + if (!this->BreakLongLine(line, lines, error)) { + return false; + } + for (size_t i = 0; i < lines.size(); ++i) { + outputStream << " \"" << lines[i] << "\"\n"; + } + } + outputStream << " \"\\n\"\n"; + } + license_ifs.close(); + } + + // End of License + outputStream << "};\n\n"; + if (!licenseFile.empty() && !singleLicense) { + outputStream << SLASTREnglish; + } else { + // Menu header + outputStream << "resource 'STR#' (" << licenseNumber << ", \"" + << licenseLanguage << "\") {\n"; + outputStream << " {\n"; + + // Menu body + cmsys::ifstream menu_ifs; + menu_ifs.open( + (slaDirectory + "/" + licenseLanguage + ".menu.txt").c_str()); + if (menu_ifs.is_open()) { + size_t lines_written = 0; + while (menu_ifs.good()) { + // Lines written from original file, not from broken up lines + std::string line; + std::getline(menu_ifs, line); + if (!line.empty()) { + EscapeQuotesAndBackslashes(line); + std::vector<std::string> lines; + if (!this->BreakLongLine(line, lines, error)) { + return false; + } + for (size_t i = 0; i < lines.size(); ++i) { + std::string comma; + // We need a comma after every complete string, + // but not on the very last line + if (lines_written != 8 && i == lines.size() - 1) { + comma = ","; + } else { + comma = ""; + } + outputStream << " \"" << lines[i] << "\"" << comma << "\n"; + } + ++lines_written; + } + } + menu_ifs.close(); + } + + // End of menu + outputStream << " }\n"; + outputStream << "};\n"; + outputStream << "\n"; + } + + return true; +} + +bool cmCPackDragNDropGenerator::BreakLongLine(const std::string& line, + std::vector<std::string>& lines, + std::string* error) +{ + const size_t max_line_length = 512; + for (size_t i = 0; i < line.size(); i += max_line_length) { + size_t line_length = max_line_length; + if (i + line_length > line.size()) { + line_length = line.size() - i; + } else + while (line_length > 0 && line[i + line_length - 1] != ' ') { + line_length = line_length - 1; + } + + if (line_length == 0) { + *error = "Please make sure there are no words " + "(or character sequences not broken up by spaces or newlines) " + "in your license file which are more than 512 characters long."; + return false; + } + lines.push_back(line.substr(i, line_length)); + } + return true; +} + +void cmCPackDragNDropGenerator::EscapeQuotesAndBackslashes(std::string& line) +{ + std::string::size_type backslash_pos = line.find('\\'); + while (backslash_pos != std::string::npos) { + line.replace(backslash_pos, 1, "\\\\"); + backslash_pos = line.find('\\', backslash_pos + 2); + } + + std::string::size_type quote_pos = line.find('\"'); + while (quote_pos != std::string::npos) { + line.replace(quote_pos, 1, "\\\""); + quote_pos = line.find('\"', quote_pos + 2); + } +} diff --git a/Source/CPack/cmCPackDragNDropGenerator.h b/Source/CPack/cmCPackDragNDropGenerator.h new file mode 100644 index 0000000..a5f89f6 --- /dev/null +++ b/Source/CPack/cmCPackDragNDropGenerator.h @@ -0,0 +1,60 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackDragNDropGenerator_h +#define cmCPackDragNDropGenerator_h + +#include "cmCPackGenerator.h" + +class cmGeneratedFileStream; + +/** \class cmCPackDragNDropGenerator + * \brief A generator for OSX drag-n-drop installs + */ +class cmCPackDragNDropGenerator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackDragNDropGenerator, cmCPackGenerator); + + cmCPackDragNDropGenerator(); + virtual ~cmCPackDragNDropGenerator(); + +protected: + int InitializeInternal() CM_OVERRIDE; + const char* GetOutputExtension() CM_OVERRIDE; + int PackageFiles() CM_OVERRIDE; + bool SupportsComponentInstallation() const CM_OVERRIDE; + + bool CopyFile(std::ostringstream& source, std::ostringstream& target); + bool CreateEmptyFile(std::ostringstream& target, size_t size); + bool RunCommand(std::ostringstream& command, std::string* output = 0); + + std::string GetComponentInstallDirNameSuffix( + const std::string& componentName) CM_OVERRIDE; + + int CreateDMG(const std::string& src_dir, const std::string& output_file); + + std::string InstallPrefix; + +private: + std::string slaDirectory; + bool singleLicense; + + bool WriteLicense(cmGeneratedFileStream& outputStream, int licenseNumber, + std::string licenseLanguage, std::string licenseFile, + std::string* error); + bool BreakLongLine(const std::string& line, std::vector<std::string>& lines, + std::string* error); + void EscapeQuotesAndBackslashes(std::string& line); +}; + +#endif diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx new file mode 100644 index 0000000..58a2243 --- /dev/null +++ b/Source/CPack/cmCPackGenerator.cxx @@ -0,0 +1,1433 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackGenerator.h" + +#include "cmCPackComponentGroup.h" +#include "cmCPackLog.h" +#include "cmGeneratedFileStream.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmXMLSafe.h" +#include "cmake.h" + +#include <algorithm> +#include <cmsys/FStream.hxx> +#include <cmsys/Glob.hxx> +#include <cmsys/SystemTools.hxx> +#include <list> + +#if defined(__HAIKU__) +#include <FindDirectory.h> +#include <StorageDefs.h> +#endif + +cmCPackGenerator::cmCPackGenerator() +{ + this->GeneratorVerbose = cmSystemTools::OUTPUT_NONE; + this->MakefileMap = CM_NULLPTR; + this->Logger = CM_NULLPTR; + this->componentPackageMethod = ONE_PACKAGE_PER_GROUP; +} + +cmCPackGenerator::~cmCPackGenerator() +{ + this->MakefileMap = CM_NULLPTR; +} + +void cmCPackGeneratorProgress(const char* msg, float prog, void* ptr) +{ + cmCPackGenerator* self = static_cast<cmCPackGenerator*>(ptr); + self->DisplayVerboseOutput(msg, prog); +} + +void cmCPackGenerator::DisplayVerboseOutput(const char* msg, float progress) +{ + (void)progress; + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "" << msg << std::endl); +} + +int cmCPackGenerator::PrepareNames() +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Create temp directory." << std::endl); + + // checks CPACK_SET_DESTDIR support + if (IsOn("CPACK_SET_DESTDIR")) { + if (SETDESTDIR_UNSUPPORTED == SupportsSetDestdir()) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, "CPACK_SET_DESTDIR is set to ON but the '" + << Name << "' generator does NOT support it." << std::endl); + return 0; + } else if (SETDESTDIR_SHOULD_NOT_BE_USED == SupportsSetDestdir()) { + cmCPackLogger(cmCPackLog::LOG_WARNING, + "CPACK_SET_DESTDIR is set to ON but it is " + << "usually a bad idea to do that with '" << Name + << "' generator. Use at your own risk." << std::endl); + } + } + + std::string tempDirectory = this->GetOption("CPACK_PACKAGE_DIRECTORY"); + tempDirectory += "/_CPack_Packages/"; + const char* toplevelTag = this->GetOption("CPACK_TOPLEVEL_TAG"); + if (toplevelTag) { + tempDirectory += toplevelTag; + tempDirectory += "/"; + } + tempDirectory += this->GetOption("CPACK_GENERATOR"); + std::string topDirectory = tempDirectory; + const char* pfname = this->GetOption("CPACK_PACKAGE_FILE_NAME"); + if (!pfname) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_PACKAGE_FILE_NAME not specified" << std::endl); + return 0; + } + std::string outName = pfname; + tempDirectory += "/" + outName; + if (!this->GetOutputExtension()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "No output extension specified" + << std::endl); + return 0; + } + outName += this->GetOutputExtension(); + const char* pdir = this->GetOption("CPACK_PACKAGE_DIRECTORY"); + if (!pdir) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_PACKAGE_DIRECTORY not specified" << std::endl); + return 0; + } + + std::string destFile = pdir; + this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PREFIX", destFile.c_str()); + destFile += "/" + outName; + std::string outFile = topDirectory + "/" + outName; + 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()); + this->SetOptionIfNotSet("CPACK_OUTPUT_FILE_PATH", destFile.c_str()); + this->SetOptionIfNotSet("CPACK_TEMPORARY_PACKAGE_FILE_NAME", + outFile.c_str()); + this->SetOptionIfNotSet("CPACK_INSTALL_DIRECTORY", this->GetInstallPath()); + this->SetOptionIfNotSet( + "CPACK_NATIVE_INSTALL_DIRECTORY", + cmsys::SystemTools::ConvertToOutputPath(this->GetInstallPath()).c_str()); + this->SetOptionIfNotSet("CPACK_TEMPORARY_INSTALL_DIRECTORY", + tempDirectory.c_str()); + + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "Look for: CPACK_PACKAGE_DESCRIPTION_FILE" << std::endl); + const char* descFileName = this->GetOption("CPACK_PACKAGE_DESCRIPTION_FILE"); + if (descFileName) { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Look for: " << descFileName + << std::endl); + if (!cmSystemTools::FileExists(descFileName)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find description file name: [" + << descFileName << "]" << std::endl); + return 0; + } + cmsys::ifstream ifs(descFileName); + if (!ifs) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot open description file name: " << descFileName + << std::endl); + return 0; + } + std::ostringstream ostr; + std::string line; + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Read description file: " << descFileName << std::endl); + while (ifs && cmSystemTools::GetLineFromStream(ifs, line)) { + ostr << cmXMLSafe(line) << std::endl; + } + this->SetOptionIfNotSet("CPACK_PACKAGE_DESCRIPTION", ostr.str().c_str()); + } + if (!this->GetOption("CPACK_PACKAGE_DESCRIPTION")) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "Project description not specified. Please specify " + "CPACK_PACKAGE_DESCRIPTION or CPACK_PACKAGE_DESCRIPTION_FILE." + << std::endl); + return 0; + } + + this->SetOptionIfNotSet("CPACK_REMOVE_TOPLEVEL_DIRECTORY", "1"); + + return 1; +} + +int cmCPackGenerator::InstallProject() +{ + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Install projects" << std::endl); + this->CleanTemporaryDirectory(); + + std::string bareTempInstallDirectory = + this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY"); + std::string tempInstallDirectoryStr = bareTempInstallDirectory; + bool setDestDir = cmSystemTools::IsOn(this->GetOption("CPACK_SET_DESTDIR")) | + cmSystemTools::IsInternallyOn(this->GetOption("CPACK_SET_DESTDIR")); + if (!setDestDir) { + tempInstallDirectoryStr += this->GetPackagingInstallPrefix(); + } + + const char* tempInstallDirectory = tempInstallDirectoryStr.c_str(); + int res = 1; + if (!cmsys::SystemTools::MakeDirectory(bareTempInstallDirectory.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating temporary directory: " + << (tempInstallDirectory ? tempInstallDirectory : "(NULL)") + << std::endl); + return 0; + } + + if (setDestDir) { + std::string destDir = "DESTDIR="; + destDir += tempInstallDirectory; + cmSystemTools::PutEnv(destDir); + } else { + // Make sure there is no destdir + cmSystemTools::PutEnv("DESTDIR="); + } + + // If the CPackConfig file sets CPACK_INSTALL_COMMANDS then run them + // as listed + if (!this->InstallProjectViaInstallCommands(setDestDir, + tempInstallDirectory)) { + return 0; + } + + // If the CPackConfig file sets CPACK_INSTALL_SCRIPT then run them + // as listed + if (!this->InstallProjectViaInstallScript(setDestDir, + tempInstallDirectory)) { + return 0; + } + + // If the CPackConfig file sets CPACK_INSTALLED_DIRECTORIES + // then glob it and copy it to CPACK_TEMPORARY_DIRECTORY + // This is used in Source packaging + if (!this->InstallProjectViaInstalledDirectories(setDestDir, + tempInstallDirectory)) { + return 0; + } + + // 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, + bareTempInstallDirectory)) { + return 0; + } + + if (setDestDir) { + cmSystemTools::PutEnv("DESTDIR="); + } + + return res; +} + +int cmCPackGenerator::InstallProjectViaInstallCommands( + bool setDestDir, const std::string& tempInstallDirectory) +{ + (void)setDestDir; + const char* installCommands = this->GetOption("CPACK_INSTALL_COMMANDS"); + if (installCommands && *installCommands) { + std::string tempInstallDirectoryEnv = "CMAKE_INSTALL_PREFIX="; + tempInstallDirectoryEnv += tempInstallDirectory; + cmSystemTools::PutEnv(tempInstallDirectoryEnv); + std::vector<std::string> installCommandsVector; + cmSystemTools::ExpandListArgument(installCommands, installCommandsVector); + std::vector<std::string>::iterator it; + for (it = installCommandsVector.begin(); it != installCommandsVector.end(); + ++it) { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << *it << std::endl); + std::string output; + int retVal = 1; + bool resB = + cmSystemTools::RunSingleCommand(it->c_str(), &output, &output, &retVal, + CM_NULLPTR, this->GeneratorVerbose, 0); + if (!resB || retVal) { + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/InstallOutput.log"; + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << *it << std::endl + << "# Output:" << std::endl + << output << std::endl; + cmCPackLogger( + cmCPackLog::LOG_ERROR, "Problem running install command: " + << *it << std::endl + << "Please check " << tmpFile << " for errors" << std::endl); + return 0; + } + } + } + return 1; +} + +int cmCPackGenerator::InstallProjectViaInstalledDirectories( + bool setDestDir, const std::string& tempInstallDirectory) +{ + (void)setDestDir; + (void)tempInstallDirectory; + std::vector<cmsys::RegularExpression> ignoreFilesRegex; + const char* cpackIgnoreFiles = this->GetOption("CPACK_IGNORE_FILES"); + if (cpackIgnoreFiles) { + std::vector<std::string> ignoreFilesRegexString; + cmSystemTools::ExpandListArgument(cpackIgnoreFiles, + ignoreFilesRegexString); + std::vector<std::string>::iterator it; + for (it = ignoreFilesRegexString.begin(); + it != ignoreFilesRegexString.end(); ++it) { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Create ignore files regex for: " << *it << std::endl); + ignoreFilesRegex.push_back(it->c_str()); + } + } + const char* installDirectories = + this->GetOption("CPACK_INSTALLED_DIRECTORIES"); + if (installDirectories && *installDirectories) { + std::vector<std::string> installDirectoriesVector; + cmSystemTools::ExpandListArgument(installDirectories, + installDirectoriesVector); + if (installDirectoriesVector.size() % 2 != 0) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "CPACK_INSTALLED_DIRECTORIES should contain pairs of <directory> and " + "<subdirectory>. The <subdirectory> can be '.' to be installed in " + "the toplevel directory of installation." + << std::endl); + return 0; + } + std::vector<std::string>::iterator it; + const std::string& tempDir = tempInstallDirectory; + for (it = installDirectoriesVector.begin(); + it != installDirectoriesVector.end(); ++it) { + std::list<std::pair<std::string, std::string> > symlinkedFiles; + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl); + cmsys::Glob gl; + std::string top = *it; + it++; + std::string subdir = *it; + std::string findExpr = top; + findExpr += "/*"; + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- Install directory: " << top << std::endl); + gl.RecurseOn(); + gl.SetRecurseListDirs(true); + if (!gl.FindFiles(findExpr)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find any files in the installed directory" + << std::endl); + return 0; + } + files = gl.GetFiles(); + std::vector<std::string>::iterator gfit; + std::vector<cmsys::RegularExpression>::iterator regIt; + for (gfit = files.begin(); gfit != files.end(); ++gfit) { + bool skip = false; + std::string inFile = *gfit; + if (cmSystemTools::FileIsDirectory(*gfit)) { + inFile += '/'; + } + for (regIt = ignoreFilesRegex.begin(); regIt != ignoreFilesRegex.end(); + ++regIt) { + if (regIt->find(inFile.c_str())) { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Ignore file: " << inFile << std::endl); + skip = true; + } + } + if (skip) { + continue; + } + std::string filePath = tempDir; + filePath += "/" + subdir + "/" + + cmSystemTools::RelativePath(top.c_str(), gfit->c_str()); + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Copy file: " + << inFile << " -> " << filePath << std::endl); + /* If the file is a symlink we will have to re-create it */ + if (cmSystemTools::FileIsSymlink(inFile)) { + std::string targetFile; + std::string inFileRelative = + cmSystemTools::RelativePath(top.c_str(), inFile.c_str()); + cmSystemTools::ReadSymlink(inFile, targetFile); + symlinkedFiles.push_back( + std::pair<std::string, std::string>(targetFile, inFileRelative)); + } + /* If it is not a symlink then do a plain copy */ + else if (!(cmSystemTools::CopyFileIfDifferent(inFile.c_str(), + filePath.c_str()) && + cmSystemTools::CopyFileTime(inFile.c_str(), + filePath.c_str()))) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying file: " + << inFile << " -> " << filePath << std::endl); + return 0; + } + } + /* rebuild symlinks in the installed tree */ + if (!symlinkedFiles.empty()) { + std::list<std::pair<std::string, std::string> >::iterator symlinkedIt; + std::string curDir = cmSystemTools::GetCurrentWorkingDirectory(); + std::string goToDir = tempDir; + goToDir += "/" + subdir; + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Change dir to: " << goToDir + << std::endl); + cmSystemTools::ChangeDirectory(goToDir); + for (symlinkedIt = symlinkedFiles.begin(); + symlinkedIt != symlinkedFiles.end(); ++symlinkedIt) { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Will create a symlink: " + << symlinkedIt->second << "--> " + << symlinkedIt->first << std::endl); + // make sure directory exists for symlink + std::string destDir = + cmSystemTools::GetFilenamePath(symlinkedIt->second); + if (!destDir.empty() && !cmSystemTools::MakeDirectory(destDir)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot create dir: " + << destDir << "\nTrying to create symlink: " + << symlinkedIt->second << "--> " + << symlinkedIt->first << std::endl); + } + if (!cmSystemTools::CreateSymlink(symlinkedIt->first, + symlinkedIt->second)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot create symlink: " + << symlinkedIt->second << "--> " + << symlinkedIt->first << std::endl); + return 0; + } + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Going back to: " << curDir + << std::endl); + cmSystemTools::ChangeDirectory(curDir); + } + } + } + return 1; +} + +int cmCPackGenerator::InstallProjectViaInstallScript( + bool setDestDir, const std::string& tempInstallDirectory) +{ + const char* cmakeScripts = this->GetOption("CPACK_INSTALL_SCRIPT"); + if (cmakeScripts && *cmakeScripts) { + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "- Install scripts: " << cmakeScripts + << std::endl); + std::vector<std::string> cmakeScriptsVector; + cmSystemTools::ExpandListArgument(cmakeScripts, cmakeScriptsVector); + std::vector<std::string>::iterator it; + for (it = cmakeScriptsVector.begin(); it != cmakeScriptsVector.end(); + ++it) { + std::string installScript = *it; + + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- Install script: " << installScript << 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"); + } + this->SetOption("CMAKE_INSTALL_PREFIX", dir.c_str()); + cmCPackLogger( + cmCPackLog::LOG_DEBUG, + "- Using DESTDIR + CPACK_INSTALL_PREFIX... (this->SetOption)" + << std::endl); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "- Setting CMAKE_INSTALL_PREFIX to '" << dir << "'" + << std::endl); + } else { + this->SetOption("CMAKE_INSTALL_PREFIX", tempInstallDirectory.c_str()); + + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "- Using non-DESTDIR install... (this->SetOption)" + << std::endl); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "- Setting CMAKE_INSTALL_PREFIX to '" + << tempInstallDirectory << "'" << std::endl); + } + + this->SetOptionIfNotSet("CMAKE_CURRENT_BINARY_DIR", + tempInstallDirectory.c_str()); + this->SetOptionIfNotSet("CMAKE_CURRENT_SOURCE_DIR", + tempInstallDirectory.c_str()); + int res = this->MakefileMap->ReadListFile(installScript.c_str()); + if (cmSystemTools::GetErrorOccuredFlag() || !res) { + return 0; + } + } + } + return 1; +} + +int cmCPackGenerator::InstallProjectViaInstallCMakeProjects( + bool setDestDir, const std::string& baseTempInstallDirectory) +{ + const char* cmakeProjects = this->GetOption("CPACK_INSTALL_CMAKE_PROJECTS"); + const char* cmakeGenerator = this->GetOption("CPACK_CMAKE_GENERATOR"); + std::string absoluteDestFiles; + if (cmakeProjects && *cmakeProjects) { + if (!cmakeGenerator) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPACK_INSTALL_CMAKE_PROJECTS is specified, but " + "CPACK_CMAKE_GENERATOR is not. CPACK_CMAKE_GENERATOR " + "is required to install the project." + << std::endl); + return 0; + } + std::vector<std::string> cmakeProjectsVector; + cmSystemTools::ExpandListArgument(cmakeProjects, cmakeProjectsVector); + std::vector<std::string>::iterator it; + for (it = cmakeProjectsVector.begin(); it != cmakeProjectsVector.end(); + ++it) { + if (it + 1 == cmakeProjectsVector.end() || + it + 2 == cmakeProjectsVector.end() || + it + 3 == cmakeProjectsVector.end()) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "Not enough items on list: CPACK_INSTALL_CMAKE_PROJECTS. " + "CPACK_INSTALL_CMAKE_PROJECTS should hold quadruplet of install " + "directory, install project name, install component, and install " + "subdirectory." + << std::endl); + return 0; + } + std::string installDirectory = *it; + ++it; + std::string installProjectName = *it; + ++it; + std::string installComponent = *it; + ++it; + std::string installSubDirectory = *it; + std::string installFile = installDirectory + "/cmake_install.cmake"; + + std::vector<std::string> componentsVector; + + bool componentInstall = false; + /* + * We do a component install iff + * - the CPack generator support component + * - the user did not request Monolithic install + * (this works at CPack time too) + */ + if (this->SupportsComponentInstallation() & + !(this->IsOn("CPACK_MONOLITHIC_INSTALL"))) { + // Determine the installation types for this project (if provided). + std::string installTypesVar = "CPACK_" + + cmSystemTools::UpperCase(installComponent) + "_INSTALL_TYPES"; + const char* installTypes = this->GetOption(installTypesVar); + 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, *installTypeIt); + } + } + + // 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); + if (components && *components) { + cmSystemTools::ExpandListArgument(components, componentsVector); + std::vector<std::string>::iterator compIt; + for (compIt = componentsVector.begin(); + compIt != componentsVector.end(); ++compIt) { + GetComponent(installProjectName, *compIt); + } + componentInstall = true; + } + } + if (componentsVector.empty()) { + componentsVector.push_back(installComponent); + } + + const char* buildConfigCstr = this->GetOption("CPACK_BUILD_CONFIG"); + std::string buildConfig = buildConfigCstr ? buildConfigCstr : ""; + cmGlobalGenerator* globalGenerator = + this->MakefileMap->GetCMakeInstance()->CreateGlobalGenerator( + cmakeGenerator); + if (!globalGenerator) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Specified package generator not found. " + "CPACK_CMAKE_GENERATOR value is invalid." + << std::endl); + return 0; + } + // set the global flag for unix style paths on cmSystemTools as + // soon as the generator is set. This allows gmake to be used + // on windows. + cmSystemTools::SetForceUnixPaths(globalGenerator->GetForceUnixPaths()); + + // Does this generator require pre-install? + if (const char* preinstall = + globalGenerator->GetPreinstallTargetName()) { + std::string buildCommand = globalGenerator->GenerateCMakeBuildCommand( + preinstall, buildConfig, "", false); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "- Install command: " << buildCommand << std::endl); + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "- Run preinstall target for: " + << installProjectName << std::endl); + std::string output; + int retVal = 1; + bool resB = cmSystemTools::RunSingleCommand( + buildCommand.c_str(), &output, &output, &retVal, + installDirectory.c_str(), this->GeneratorVerbose, 0); + if (!resB || retVal) { + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/PreinstallOutput.log"; + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << buildCommand << std::endl + << "# Directory: " << installDirectory << std::endl + << "# Output:" << std::endl + << output << std::endl; + cmCPackLogger( + cmCPackLog::LOG_ERROR, "Problem running install command: " + << buildCommand << std::endl + << "Please check " << tmpFile << " for errors" << std::endl); + return 0; + } + } + delete globalGenerator; + + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- Install project: " << installProjectName << std::endl); + + // Run the installation for each component + std::vector<std::string>::iterator componentIt; + for (componentIt = componentsVector.begin(); + componentIt != componentsVector.end(); ++componentIt) { + std::string tempInstallDirectory = baseTempInstallDirectory; + installComponent = *componentIt; + if (componentInstall) { + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "- Install component: " + << installComponent << std::endl); + } + + cmake cm; + cm.SetHomeDirectory(""); + cm.SetHomeOutputDirectory(""); + cm.GetCurrentSnapshot().SetDefaultDefinitions(); + cm.AddCMakePaths(); + cm.SetProgressCallback(cmCPackGeneratorProgress, this); + cmGlobalGenerator gg(&cm); + CM_AUTO_PTR<cmMakefile> mf( + new cmMakefile(&gg, cm.GetCurrentSnapshot())); + if (!installSubDirectory.empty() && installSubDirectory != "/" && + installSubDirectory != ".") { + tempInstallDirectory += installSubDirectory; + } + if (componentInstall) { + tempInstallDirectory += "/"; + // Some CPack generators would rather chose + // the local installation directory suffix. + // Some (e.g. RPM) use + // one install directory for each component **GROUP** + // instead of the default + // one install directory for each component. + tempInstallDirectory += + GetComponentInstallDirNameSuffix(installComponent); + if (this->IsOn("CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY")) { + tempInstallDirectory += "/"; + tempInstallDirectory += this->GetOption("CPACK_PACKAGE_FILE_NAME"); + } + } + + if (!setDestDir) { + tempInstallDirectory += this->GetPackagingInstallPrefix(); + } + + 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. + // + // If DESTDIR has been 'internally set ON' this means that + // the underlying CPack specific generator did ask for that + // In this case we may override CPACK_INSTALL_PREFIX with + // CPACK_PACKAGING_INSTALL_PREFIX + // I know this is tricky and awkward but it's the price for + // CPACK_SET_DESTDIR backward compatibility. + if (cmSystemTools::IsInternallyOn( + this->GetOption("CPACK_SET_DESTDIR"))) { + this->SetOption("CPACK_INSTALL_PREFIX", + this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX")); + } + 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); + + // Make sure that DESTDIR + CPACK_INSTALL_PREFIX directory + // exists: + // + if (cmSystemTools::StringStartsWith(dir.c_str(), "/")) { + dir = tempInstallDirectory + dir; + } else { + dir = tempInstallDirectory + "/" + dir; + } + /* + * We must re-set DESTDIR for each component + * We must not add the CPACK_INSTALL_PREFIX part because + * it will be added using the override of CMAKE_INSTALL_PREFIX + * The main reason for this awkward trick is that + * are using DESTDIR for 2 different reasons: + * - Because it was asked by the CPack Generator or the user + * using CPACK_SET_DESTDIR + * - Because it was already used for component install + * in order to put things in subdirs... + */ + cmSystemTools::PutEnv(std::string("DESTDIR=") + + tempInstallDirectory); + cmCPackLogger(cmCPackLog::LOG_DEBUG, "- Creating directory: '" + << dir << "'" << std::endl); + + if (!cmsys::SystemTools::MakeDirectory(dir.c_str())) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "Problem creating temporary directory: " << dir << std::endl); + return 0; + } + } else { + mf->AddDefinition("CMAKE_INSTALL_PREFIX", + tempInstallDirectory.c_str()); + + if (!cmsys::SystemTools::MakeDirectory( + tempInstallDirectory.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating temporary directory: " + << tempInstallDirectory << std::endl); + return 0; + } + + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "- Using non-DESTDIR install... (mf->AddDefinition)" + << std::endl); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "- Setting CMAKE_INSTALL_PREFIX to '" + << tempInstallDirectory << "'" << std::endl); + } + + if (!buildConfig.empty()) { + mf->AddDefinition("BUILD_TYPE", buildConfig.c_str()); + } + 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"); + } + // Remember the list of files before installation + // of the current component (if we are in component install) + const char* InstallPrefix = tempInstallDirectory.c_str(); + std::vector<std::string> filesBefore; + std::string findExpr(InstallPrefix); + if (componentInstall) { + cmsys::Glob glB; + findExpr += "/*"; + glB.RecurseOn(); + glB.SetRecurseListDirs(true); + glB.FindFiles(findExpr); + filesBefore = glB.GetFiles(); + std::sort(filesBefore.begin(), filesBefore.end()); + } + + // If CPack was asked to warn on ABSOLUTE INSTALL DESTINATION + // then forward request to cmake_install.cmake script + if (this->IsOn("CPACK_WARN_ON_ABSOLUTE_INSTALL_DESTINATION")) { + mf->AddDefinition("CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION", "1"); + } + // If current CPack generator does support + // ABSOLUTE INSTALL DESTINATION or CPack has been asked for + // then ask cmake_install.cmake script to error out + // as soon as it occurs (before installing file) + if (!SupportsAbsoluteDestination() || + this->IsOn("CPACK_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION")) { + mf->AddDefinition("CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION", + "1"); + } + // do installation + int res = mf->ReadListFile(installFile.c_str()); + // forward definition of CMAKE_ABSOLUTE_DESTINATION_FILES + // to CPack (may be used by generators like CPack RPM or DEB) + // in order to transparently handle ABSOLUTE PATH + if (mf->GetDefinition("CMAKE_ABSOLUTE_DESTINATION_FILES")) { + mf->AddDefinition( + "CPACK_ABSOLUTE_DESTINATION_FILES", + mf->GetDefinition("CMAKE_ABSOLUTE_DESTINATION_FILES")); + } + + // Now rebuild the list of files after installation + // of the current component (if we are in component install) + if (componentInstall) { + cmsys::Glob glA; + glA.RecurseOn(); + glA.SetRecurseListDirs(true); + glA.FindFiles(findExpr); + std::vector<std::string> filesAfter = glA.GetFiles(); + std::sort(filesAfter.begin(), filesAfter.end()); + std::vector<std::string>::iterator diff; + std::vector<std::string> result(filesAfter.size()); + diff = std::set_difference(filesAfter.begin(), filesAfter.end(), + filesBefore.begin(), filesBefore.end(), + result.begin()); + + std::vector<std::string>::iterator fit; + std::string localFileName; + // Populate the File field of each component + for (fit = result.begin(); fit != diff; ++fit) { + localFileName = + cmSystemTools::RelativePath(InstallPrefix, fit->c_str()); + localFileName = localFileName.substr( + localFileName.find_first_not_of('/'), std::string::npos); + Components[installComponent].Files.push_back(localFileName); + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Adding file <" + << localFileName << "> to component <" + << installComponent << ">" << std::endl); + } + } + + if (CM_NULLPTR != + mf->GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES")) { + if (!absoluteDestFiles.empty()) { + absoluteDestFiles += ";"; + } + absoluteDestFiles += + mf->GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES"); + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "Got some ABSOLUTE DESTINATION FILES: " + << absoluteDestFiles << std::endl); + // define component specific var + if (componentInstall) { + std::string absoluteDestFileComponent = + std::string("CPACK_ABSOLUTE_DESTINATION_FILES") + "_" + + GetComponentInstallDirNameSuffix(installComponent); + if (CM_NULLPTR != this->GetOption(absoluteDestFileComponent)) { + std::string absoluteDestFilesListComponent = + this->GetOption(absoluteDestFileComponent); + absoluteDestFilesListComponent += ";"; + absoluteDestFilesListComponent += + mf->GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES"); + this->SetOption(absoluteDestFileComponent, + absoluteDestFilesListComponent.c_str()); + } else { + this->SetOption( + absoluteDestFileComponent, + mf->GetDefinition("CPACK_ABSOLUTE_DESTINATION_FILES")); + } + } + } + if (cmSystemTools::GetErrorOccuredFlag() || !res) { + return 0; + } + } + } + } + this->SetOption("CPACK_ABSOLUTE_DESTINATION_FILES", + absoluteDestFiles.c_str()); + return 1; +} + +bool cmCPackGenerator::ReadListFile(const char* moduleName) +{ + bool retval; + std::string fullPath = this->MakefileMap->GetModulesFile(moduleName); + retval = this->MakefileMap->ReadListFile(fullPath.c_str()); + // include FATAL_ERROR and ERROR in the return status + retval = retval && (!cmSystemTools::GetErrorOccuredFlag()); + return retval; +} + +void cmCPackGenerator::SetOptionIfNotSet(const std::string& op, + const char* value) +{ + const char* def = this->MakefileMap->GetDefinition(op); + if (def && *def) { + return; + } + this->SetOption(op, value); +} + +void cmCPackGenerator::SetOption(const std::string& op, const char* value) +{ + if (!value) { + this->MakefileMap->RemoveDefinition(op); + return; + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, this->GetNameOfClass() + << "::SetOption(" << op << ", " << value << ")" + << std::endl); + this->MakefileMap->AddDefinition(op, value); +} + +int cmCPackGenerator::DoPackage() +{ + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Create package using " << this->Name + << std::endl); + + // Prepare CPack internal name and check + // values for many CPACK_xxx vars + if (!this->PrepareNames()) { + return 0; + } + + // Digest Component grouping specification + if (!this->PrepareGroupingKind()) { + return 0; + } + + if (cmSystemTools::IsOn( + this->GetOption("CPACK_REMOVE_TOPLEVEL_DIRECTORY"))) { + const char* toplevelDirectory = + this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + if (cmSystemTools::FileExists(toplevelDirectory)) { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Remove toplevel directory: " + << toplevelDirectory << std::endl); + if (!cmSystemTools::RepeatedRemoveDirectory(toplevelDirectory)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem removing toplevel directory: " + << toplevelDirectory << std::endl); + return 0; + } + } + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, "About to install project " + << std::endl); + + if (!this->InstallProject()) { + return 0; + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Done install project " << std::endl); + + const char* tempPackageFileName = + this->GetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME"); + const char* tempDirectory = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Find files" << std::endl); + cmsys::Glob gl; + std::string findExpr = tempDirectory; + findExpr += "/*"; + gl.RecurseOn(); + gl.SetRecurseListDirs(true); + gl.SetRecurseThroughSymlinks(false); + if (!gl.FindFiles(findExpr)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find any files in the packaging tree" << std::endl); + return 0; + } + + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "Create package" << std::endl); + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Package files to: " + << (tempPackageFileName ? tempPackageFileName : "(NULL)") + << std::endl); + if (cmSystemTools::FileExists(tempPackageFileName)) { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Remove old package file" + << std::endl); + cmSystemTools::RemoveFile(tempPackageFileName); + } + if (cmSystemTools::IsOn( + this->GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY"))) { + tempDirectory = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + } + + // The files to be installed + files = gl.GetFiles(); + + packageFileNames.clear(); + /* Put at least one file name into the list of + * wanted packageFileNames. The specific generator + * may update this during PackageFiles. + * (either putting several names or updating the provided one) + */ + packageFileNames.push_back(tempPackageFileName ? tempPackageFileName : ""); + toplevel = tempDirectory; + if (!this->PackageFiles() || cmSystemTools::GetErrorOccuredFlag()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem compressing the directory" + << std::endl); + return 0; + } + + /* + * Copy the generated packages to final destination + * - there may be several of them + * - the initially provided name may have changed + * (because the specific generator did 'normalize' it) + */ + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Copying final package(s) [" + << packageFileNames.size() << "]:" << std::endl); + std::vector<std::string>::iterator it; + /* now copy package one by one */ + for (it = packageFileNames.begin(); it != packageFileNames.end(); ++it) { + std::string tmpPF(this->GetOption("CPACK_OUTPUT_FILE_PREFIX")); + tempPackageFileName = it->c_str(); + tmpPF += "/" + cmSystemTools::GetFilenameName(*it); + const char* packageFileName = tmpPF.c_str(); + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Copy final package(s): " + << (tempPackageFileName ? tempPackageFileName : "(NULL)") + << " to " << (packageFileName ? packageFileName : "(NULL)") + << std::endl); + if (!cmSystemTools::CopyFileIfDifferent(tempPackageFileName, + packageFileName)) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, "Problem copying the package: " + << (tempPackageFileName ? tempPackageFileName : "(NULL)") << " to " + << (packageFileName ? packageFileName : "(NULL)") << std::endl); + return 0; + } + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "- package: " + << packageFileName << " generated." << std::endl); + } + + return 1; +} + +int cmCPackGenerator::Initialize(const std::string& name, cmMakefile* mf) +{ + this->MakefileMap = mf; + this->Name = name; + // set the running generator name + this->SetOption("CPACK_GENERATOR", this->Name.c_str()); + // Load the project specific config file + const char* config = this->GetOption("CPACK_PROJECT_CONFIG_FILE"); + if (config) { + mf->ReadListFile(config); + } + int result = this->InitializeInternal(); + if (cmSystemTools::GetErrorOccuredFlag()) { + return 0; + } + + // If a generator subclass did not already set this option in its + // InitializeInternal implementation, and the project did not already set + // it, the default value should be: + this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/"); + + return result; +} + +int cmCPackGenerator::InitializeInternal() +{ + return 1; +} + +bool cmCPackGenerator::IsSet(const std::string& name) const +{ + return this->MakefileMap->IsSet(name); +} + +bool cmCPackGenerator::IsOn(const std::string& name) const +{ + return cmSystemTools::IsOn(GetOption(name)); +} + +const char* cmCPackGenerator::GetOption(const std::string& op) const +{ + const char* ret = this->MakefileMap->GetDefinition(op); + if (!ret) { + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "Warning, GetOption return NULL for: " << op << std::endl); + } + return ret; +} + +std::vector<std::string> cmCPackGenerator::GetOptions() const +{ + return this->MakefileMap->GetDefinitions(); +} + +int cmCPackGenerator::PackageFiles() +{ + return 0; +} + +const char* cmCPackGenerator::GetInstallPath() +{ + if (!this->InstallPath.empty()) { + return this->InstallPath.c_str(); + } +#if defined(_WIN32) && !defined(__CYGWIN__) + std::string prgfiles; + std::string sysDrive; + if (cmsys::SystemTools::GetEnv("ProgramFiles", prgfiles)) { + this->InstallPath = prgfiles; + } else if (cmsys::SystemTools::GetEnv("SystemDrive", sysDrive)) { + this->InstallPath = sysDrive; + this->InstallPath += "/Program Files"; + } else { + this->InstallPath = "c:/Program Files"; + } + this->InstallPath += "/"; + this->InstallPath += this->GetOption("CPACK_PACKAGE_NAME"); + this->InstallPath += "-"; + this->InstallPath += this->GetOption("CPACK_PACKAGE_VERSION"); +#elif defined(__HAIKU__) + char dir[B_PATH_NAME_LENGTH]; + if (find_directory(B_SYSTEM_DIRECTORY, -1, false, dir, sizeof(dir)) == + B_OK) { + this->InstallPath = dir; + } else { + this->InstallPath = "/boot/system"; + } +#else + this->InstallPath = "/usr/local/"; +#endif + return this->InstallPath.c_str(); +} + +const char* cmCPackGenerator::GetPackagingInstallPrefix() +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, "GetPackagingInstallPrefix: '" + << this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX") << "'" + << std::endl); + + return this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX"); +} + +std::string cmCPackGenerator::FindTemplate(const char* name) +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Look for template: " + << (name ? name : "(NULL)") << std::endl); + std::string ffile = this->MakefileMap->GetModulesFile(name); + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Found template: " << ffile + << std::endl); + return ffile; +} + +bool cmCPackGenerator::ConfigureString(const std::string& inString, + std::string& outString) +{ + this->MakefileMap->ConfigureString(inString, outString, true, false); + return true; +} + +bool cmCPackGenerator::ConfigureFile(const char* inName, const char* outName, + bool copyOnly /* = false */) +{ + return this->MakefileMap->ConfigureFile(inName, outName, copyOnly, true, + false) == 1; +} + +int cmCPackGenerator::CleanTemporaryDirectory() +{ + std::string tempInstallDirectoryWithPostfix = + this->GetOption("CPACK_TEMPORARY_INSTALL_DIRECTORY"); + const char* tempInstallDirectory = tempInstallDirectoryWithPostfix.c_str(); + if (cmsys::SystemTools::FileExists(tempInstallDirectory)) { + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "- Clean temporary : " << tempInstallDirectory << std::endl); + if (!cmSystemTools::RepeatedRemoveDirectory(tempInstallDirectory)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem removing temporary directory: " + << tempInstallDirectory << std::endl); + return 0; + } + } + return 1; +} + +cmInstalledFile const* cmCPackGenerator::GetInstalledFile( + std::string const& name) const +{ + cmake const* cm = this->MakefileMap->GetCMakeInstance(); + return cm->GetInstalledFile(name); +} + +int cmCPackGenerator::PrepareGroupingKind() +{ + // find a component package method specified by the user + ComponentPackageMethod method = UNKNOWN_COMPONENT_PACKAGE_METHOD; + + if (this->GetOption("CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE")) { + method = ONE_PACKAGE; + } + + if (this->GetOption("CPACK_COMPONENTS_IGNORE_GROUPS")) { + method = ONE_PACKAGE_PER_COMPONENT; + } + + if (this->GetOption("CPACK_COMPONENTS_ONE_PACKAGE_PER_GROUP")) { + method = ONE_PACKAGE_PER_GROUP; + } + + std::string groupingType; + + // Second way to specify grouping + if (CM_NULLPTR != this->GetOption("CPACK_COMPONENTS_GROUPING")) { + groupingType = this->GetOption("CPACK_COMPONENTS_GROUPING"); + } + + if (!groupingType.empty()) { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "[" + << this->Name << "]" + << " requested component grouping = " << groupingType + << std::endl); + if (groupingType == "ALL_COMPONENTS_IN_ONE") { + method = ONE_PACKAGE; + } else if (groupingType == "IGNORE") { + method = ONE_PACKAGE_PER_COMPONENT; + } else if (groupingType == "ONE_PER_GROUP") { + method = ONE_PACKAGE_PER_GROUP; + } else { + cmCPackLogger( + cmCPackLog::LOG_WARNING, "[" + << this->Name << "]" + << " requested component grouping type <" << groupingType + << "> UNKNOWN not in (ALL_COMPONENTS_IN_ONE,IGNORE,ONE_PER_GROUP)" + << std::endl); + } + } + + // Some components were defined but NO group + // fallback to default if not group based + if (method == ONE_PACKAGE_PER_GROUP && this->ComponentGroups.empty() && + !this->Components.empty()) { + if (componentPackageMethod == ONE_PACKAGE) { + method = ONE_PACKAGE; + } else { + method = ONE_PACKAGE_PER_COMPONENT; + } + cmCPackLogger( + cmCPackLog::LOG_WARNING, "[" + << this->Name << "]" + << " One package per component group requested, " + << "but NO component groups exist: Ignoring component group." + << std::endl); + } + + // if user specified packaging method, override the default packaging method + if (method != UNKNOWN_COMPONENT_PACKAGE_METHOD) { + componentPackageMethod = method; + } + + const char* method_names[] = { "ALL_COMPONENTS_IN_ONE", "IGNORE_GROUPS", + "ONE_PER_GROUP" }; + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "[" + << this->Name << "]" + << " requested component grouping = " + << method_names[componentPackageMethod] << std::endl); + + return 1; +} + +std::string cmCPackGenerator::GetComponentInstallDirNameSuffix( + const std::string& componentName) +{ + return componentName; +} +std::string cmCPackGenerator::GetComponentPackageFileName( + const std::string& initialPackageFileName, + const std::string& groupOrComponentName, bool isGroupName) +{ + + /* + * the default behavior is to use the + * component [group] name as a suffix + */ + std::string suffix = "-" + groupOrComponentName; + /* check if we should use DISPLAY name */ + std::string dispNameVar = "CPACK_" + Name + "_USE_DISPLAY_NAME_IN_FILENAME"; + if (IsOn(dispNameVar)) { + /* the component Group case */ + if (isGroupName) { + std::string groupDispVar = "CPACK_COMPONENT_GROUP_" + + cmSystemTools::UpperCase(groupOrComponentName) + "_DISPLAY_NAME"; + const char* groupDispName = GetOption(groupDispVar); + if (groupDispName) { + suffix = "-" + std::string(groupDispName); + } + } + /* the [single] component case */ + else { + std::string dispVar = "CPACK_COMPONENT_" + + cmSystemTools::UpperCase(groupOrComponentName) + "_DISPLAY_NAME"; + const char* dispName = GetOption(dispVar); + if (dispName) { + suffix = "-" + std::string(dispName); + } + } + } + return initialPackageFileName + suffix; +} + +enum cmCPackGenerator::CPackSetDestdirSupport +cmCPackGenerator::SupportsSetDestdir() const +{ + return cmCPackGenerator::SETDESTDIR_SUPPORTED; +} + +bool cmCPackGenerator::SupportsAbsoluteDestination() const +{ + return true; +} + +bool cmCPackGenerator::SupportsComponentInstallation() const +{ + return false; +} + +bool cmCPackGenerator::WantsComponentInstallation() const +{ + return (!IsOn("CPACK_MONOLITHIC_INSTALL") && SupportsComponentInstallation() + // check that we have at least one group or component + && (!this->ComponentGroups.empty() || !this->Components.empty())); +} + +cmCPackInstallationType* cmCPackGenerator::GetInstallationType( + const std::string& projectName, const std::string& name) +{ + (void)projectName; + 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"); + if (displayName && *displayName) { + installType->DisplayName = displayName; + } else { + installType->DisplayName = installType->Name; + } + + installType->Index = static_cast<unsigned>(this->InstallationTypes.size()); + } + return installType; +} + +cmCPackComponent* cmCPackGenerator::GetComponent( + const std::string& projectName, const std::string& 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"); + if (displayName && *displayName) { + component->DisplayName = displayName; + } else { + component->DisplayName = component->Name; + } + component->IsHidden = this->IsOn(macroPrefix + "_HIDDEN"); + component->IsRequired = this->IsOn(macroPrefix + "_REQUIRED"); + component->IsDisabledByDefault = this->IsOn(macroPrefix + "_DISABLED"); + component->IsDownloaded = this->IsOn(macroPrefix + "_DOWNLOADED") || + cmSystemTools::IsOn(this->GetOption("CPACK_DOWNLOAD_ALL")); + + const char* archiveFile = this->GetOption(macroPrefix + "_ARCHIVE_FILE"); + if (archiveFile && *archiveFile) { + component->ArchiveFile = archiveFile; + } + + const char* groupName = this->GetOption(macroPrefix + "_GROUP"); + if (groupName && *groupName) { + component->Group = GetComponentGroup(projectName, groupName); + component->Group->Components.push_back(component); + } else { + component->Group = CM_NULLPTR; + } + + const char* description = this->GetOption(macroPrefix + "_DESCRIPTION"); + if (description && *description) { + component->Description = description; + } + + // Determine the installation types. + const char* installTypes = this->GetOption(macroPrefix + "_INSTALL_TYPES"); + 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)); + } + } + + // Determine the component dependencies. + const char* depends = this->GetOption(macroPrefix + "_DEPENDS"); + 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); + component->Dependencies.push_back(child); + child->ReverseDependencies.push_back(component); + } + } + } + return component; +} + +cmCPackComponentGroup* cmCPackGenerator::GetComponentGroup( + const std::string& projectName, const std::string& name) +{ + (void)projectName; + 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"); + if (displayName && *displayName) { + group->DisplayName = displayName; + } else { + group->DisplayName = group->Name; + } + + const char* description = this->GetOption(macroPrefix + "_DESCRIPTION"); + if (description && *description) { + group->Description = description; + } + group->IsBold = this->IsOn(macroPrefix + "_BOLD_TITLE"); + group->IsExpandedByDefault = this->IsOn(macroPrefix + "_EXPANDED"); + const char* parentGroupName = + this->GetOption(macroPrefix + "_PARENT_GROUP"); + if (parentGroupName && *parentGroupName) { + group->ParentGroup = GetComponentGroup(projectName, parentGroupName); + group->ParentGroup->Subgroups.push_back(group); + } else { + group->ParentGroup = CM_NULLPTR; + } + } + return group; +} diff --git a/Source/CPack/cmCPackGenerator.h b/Source/CPack/cmCPackGenerator.h new file mode 100644 index 0000000..23e4bb7 --- /dev/null +++ b/Source/CPack/cmCPackGenerator.h @@ -0,0 +1,331 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackGenerator_h +#define cmCPackGenerator_h + +#include "cmObject.h" + +#include "cmSystemTools.h" +#include <map> +#include <vector> + +#include "cmCPackComponentGroup.h" // cmCPackComponent and friends +// Forward declarations are insufficient since we use them in +// std::map data members below... + +#define cmCPackTypeMacro(klass, superclass) \ + cmTypeMacro(klass, superclass); \ + static cmCPackGenerator* CreateGenerator() { return new klass; } \ + class cmCPackTypeMacro_UseTrailingSemicolon + +#define cmCPackLogger(logType, msg) \ + do { \ + std::ostringstream cmCPackLog_msg; \ + cmCPackLog_msg << msg; \ + this->Logger->Log(logType, __FILE__, __LINE__, \ + cmCPackLog_msg.str().c_str()); \ + } while (0) + +#ifdef cerr +#undef cerr +#endif +#define cerr no_cerr_use_cmCPack_Log + +#ifdef cout +#undef cout +#endif +#define cout no_cout_use_cmCPack_Log + +class cmMakefile; +class cmCPackLog; +class cmInstalledFile; + +/** \class cmCPackGenerator + * \brief A superclass of all CPack Generators + * + */ +class cmCPackGenerator : public cmObject +{ +public: + cmTypeMacro(cmCPackGenerator, cmObject); + /** + * If verbose then more information is printed out + */ + void SetVerbose(bool val) + { + this->GeneratorVerbose = + val ? cmSystemTools::OUTPUT_MERGE : cmSystemTools::OUTPUT_NONE; + } + + /** + * Returns true if the generator may work on this system. + * Rational: + * Some CPack generator may run on some host and may not on others + * (with the same system) because some tools are missing. If the tool + * is missing then CPack won't activate (in the CPackGeneratorFactory) + * this particular generator. + */ + static bool CanGenerate() { return true; } + + /** + * Do the actual whole package processing. + * Subclass may redefine it but its usually enough + * to redefine @ref PackageFiles, because in fact + * this method do call: + * - PrepareName + * - clean-up temp dirs + * - InstallProject (with the appropriate method) + * - prepare list of files and/or components to be package + * - PackageFiles + * - Copy produced packages at the expected place + * @return 0 if error. + */ + virtual int DoPackage(); + + /** + * Initialize generator + */ + int Initialize(const std::string& name, cmMakefile* mf); + + /** + * Construct generator + */ + cmCPackGenerator(); + ~cmCPackGenerator() CM_OVERRIDE; + + //! Set and get the options + void SetOption(const std::string& op, const char* value); + void SetOptionIfNotSet(const std::string& op, const char* value); + const char* GetOption(const std::string& op) const; + std::vector<std::string> GetOptions() const; + bool IsSet(const std::string& name) const; + bool IsOn(const std::string& name) const; + + //! Set the logger + void SetLogger(cmCPackLog* log) { this->Logger = log; } + + //! Display verbose information via logger + void DisplayVerboseOutput(const char* msg, float progress); + + bool ReadListFile(const char* moduleName); + +protected: + /** + * Prepare common used names by inspecting + * several CPACK_xxx var values. + */ + int PrepareNames(); + + /** + * Install the project using appropriate method. + */ + int InstallProject(); + + int CleanTemporaryDirectory(); + + cmInstalledFile const* GetInstalledFile(std::string const& name) const; + + virtual const char* GetOutputExtension() { return ".cpack"; } + virtual const char* GetOutputPostfix() { return CM_NULLPTR; } + + /** + * Prepare requested grouping kind from CPACK_xxx vars + * CPACK_COMPONENTS_ALL_IN_ONE_PACKAGE + * CPACK_COMPONENTS_IGNORE_GROUPS + * or + * CPACK_COMPONENTS_ONE_PACKAGE_PER_GROUP + * @return 1 on success 0 on failure. + */ + virtual int PrepareGroupingKind(); + + /** + * Some CPack generators may prefer to have + * CPack install all components belonging to the same + * [component] group to be install in the same directory. + * The default behavior is to install each component in + * a separate directory. + * @param[in] componentName the name of the component to be installed + * @return the name suffix the generator wants for the specified component + * default is "componentName" + */ + virtual std::string GetComponentInstallDirNameSuffix( + const std::string& componentName); + + /** + * CPack specific generator may mangle CPACK_PACKAGE_FILE_NAME + * with CPACK_COMPONENT_xxxx_<NAME>_DISPLAY_NAME if + * CPACK_<GEN>_USE_DISPLAY_NAME_IN_FILENAME is ON. + * @param[in] initialPackageFileName the initial package name to be mangled + * @param[in] groupOrComponentName the name of the group/component + * @param[in] isGroupName true if previous name refers to a group, + * false otherwise + */ + virtual std::string GetComponentPackageFileName( + const std::string& initialPackageFileName, + const std::string& groupOrComponentName, bool isGroupName); + + /** + * Package the list of files and/or components which + * has been prepared by the beginning of DoPackage. + * @pre the @ref toplevel has been filled-in + * @pre the list of file @ref files has been populated + * @pre packageFileNames contains at least 1 entry + * @post packageFileNames may have been updated and contains + * the list of packages generated by the specific generator. + */ + virtual int PackageFiles(); + virtual const char* GetInstallPath(); + virtual const char* GetPackagingInstallPrefix(); + + virtual std::string FindTemplate(const char* name); + virtual bool ConfigureFile(const char* inName, const char* outName, + bool copyOnly = false); + virtual bool ConfigureString(const std::string& input, std::string& output); + virtual int InitializeInternal(); + + //! Run install commands if specified + virtual int InstallProjectViaInstallCommands( + bool setDestDir, const std::string& tempInstallDirectory); + virtual int InstallProjectViaInstallScript( + bool setDestDir, const std::string& tempInstallDirectory); + virtual int InstallProjectViaInstalledDirectories( + bool setDestDir, const std::string& tempInstallDirectory); + virtual int InstallProjectViaInstallCMakeProjects( + bool setDestDir, const std::string& tempInstallDirectory); + + /** + * The various level of support of + * CPACK_SET_DESTDIR used by the generator. + */ + enum CPackSetDestdirSupport + { + /* the generator works with or without it */ + SETDESTDIR_SUPPORTED, + /* the generator works best if automatically handled */ + SETDESTDIR_INTERNALLY_SUPPORTED, + /* no official support, use at your own risk */ + SETDESTDIR_SHOULD_NOT_BE_USED, + /* officially NOT supported */ + SETDESTDIR_UNSUPPORTED + }; + + /** + * Does the CPack generator support CPACK_SET_DESTDIR? + * The default legacy value is 'SETDESTDIR_SUPPORTED' generator + * have to override it in order change this. + * @return CPackSetDestdirSupport + */ + virtual enum CPackSetDestdirSupport SupportsSetDestdir() const; + + /** + * Does the CPack generator support absolute path + * in INSTALL DESTINATION? + * The default legacy value is 'true' generator + * have to override it in order change this. + * @return true if supported false otherwise + */ + virtual bool SupportsAbsoluteDestination() const; + + /** + * Does the CPack generator support component installation?. + * Some Generators requires the user to set + * CPACK_<GENNAME>_COMPONENT_INSTALL in order to make this + * method return true. + * @return true if supported, false otherwise + */ + virtual bool SupportsComponentInstallation() const; + /** + * Does the currently running generator want a component installation. + * The generator may support component installation but he may + * be requiring monolithic install using CPACK_MONOLITHIC_INSTALL. + * @return true if component installation is supported and wanted. + */ + virtual bool WantsComponentInstallation() const; + virtual cmCPackInstallationType* GetInstallationType( + const std::string& projectName, const std::string& name); + virtual cmCPackComponent* GetComponent(const std::string& projectName, + const std::string& name); + virtual cmCPackComponentGroup* GetComponentGroup( + const std::string& projectName, const std::string& name); + + cmSystemTools::OutputOption GeneratorVerbose; + std::string Name; + + std::string InstallPath; + + /** + * The list of package file names. + * At beginning of DoPackage the (generic) generator will populate + * the list of desired package file names then it will + * call the redefined method PackageFiles which is may + * either use this set of names (usually on entry there should be + * only a single name) or update the vector with the list + * of created package file names. + */ + std::vector<std::string> packageFileNames; + + /** + * The directory where all the files to be packaged reside. + * If the installer support components there will be one + * sub-directory for each component. In those directories + * one will find the file belonging to the specified component. + */ + std::string toplevel; + + /** + * The complete list of files to be packaged. + * This list will be populated by DoPackage before + * PackageFiles is called. + */ + std::vector<std::string> files; + + std::map<std::string, cmCPackInstallationType> InstallationTypes; + /** + * The set of components. + * If component installation is supported then this map + * contains the component specified in CPACK_COMPONENTS_ALL + */ + std::map<std::string, cmCPackComponent> Components; + std::map<std::string, cmCPackComponentGroup> ComponentGroups; + + /** + * If components are enabled, this enum represents the different + * ways of mapping components to package files. + */ + enum ComponentPackageMethod + { + /* one package for all components */ + ONE_PACKAGE, + /* one package for each component */ + ONE_PACKAGE_PER_COMPONENT, + /* one package for each group, + * with left over components in their own package */ + ONE_PACKAGE_PER_GROUP, + UNKNOWN_COMPONENT_PACKAGE_METHOD + }; + + /** + * The component package method + * The default is ONE_PACKAGE_PER_GROUP, + * and generators may override the default + * before PrepareGroupingKind() is called. + */ + ComponentPackageMethod componentPackageMethod; + + cmCPackLog* Logger; + +private: + cmMakefile* MakefileMap; +}; + +#endif diff --git a/Source/CPack/cmCPackGeneratorFactory.cxx b/Source/CPack/cmCPackGeneratorFactory.cxx new file mode 100644 index 0000000..0f0268f --- /dev/null +++ b/Source/CPack/cmCPackGeneratorFactory.cxx @@ -0,0 +1,183 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackGeneratorFactory.h" + +#include "IFW/cmCPackIFWGenerator.h" +#include "cmCPack7zGenerator.h" +#include "cmCPackGenerator.h" +#include "cmCPackNSISGenerator.h" +#include "cmCPackSTGZGenerator.h" +#include "cmCPackTGZGenerator.h" +#include "cmCPackTXZGenerator.h" +#include "cmCPackTarBZip2Generator.h" +#include "cmCPackTarCompressGenerator.h" +#include "cmCPackZIPGenerator.h" + +#ifdef __APPLE__ +#include "cmCPackBundleGenerator.h" +#include "cmCPackDragNDropGenerator.h" +#include "cmCPackOSXX11Generator.h" +#include "cmCPackPackageMakerGenerator.h" +#include "cmCPackProductBuildGenerator.h" +#endif + +#ifdef __CYGWIN__ +#include "cmCPackCygwinBinaryGenerator.h" +#include "cmCPackCygwinSourceGenerator.h" +#endif + +#if !defined(_WIN32) && !defined(__QNXNTO__) && !defined(__BEOS__) && \ + !defined(__HAIKU__) +#include "cmCPackDebGenerator.h" +#include "cmCPackRPMGenerator.h" +#endif + +#ifdef _WIN32 +#include "WiX/cmCPackWIXGenerator.h" +#endif + +#include "cmAlgorithms.h" +#include "cmCPackLog.h" + +cmCPackGeneratorFactory::cmCPackGeneratorFactory() +{ + if (cmCPackTGZGenerator::CanGenerate()) { + this->RegisterGenerator("TGZ", "Tar GZip compression", + cmCPackTGZGenerator::CreateGenerator); + } + if (cmCPackTXZGenerator::CanGenerate()) { + this->RegisterGenerator("TXZ", "Tar XZ compression", + cmCPackTXZGenerator::CreateGenerator); + } + if (cmCPackSTGZGenerator::CanGenerate()) { + this->RegisterGenerator("STGZ", "Self extracting Tar GZip compression", + cmCPackSTGZGenerator::CreateGenerator); + } + if (cmCPackNSISGenerator::CanGenerate()) { + this->RegisterGenerator("NSIS", "Null Soft Installer", + cmCPackNSISGenerator::CreateGenerator); + this->RegisterGenerator("NSIS64", "Null Soft Installer (64-bit)", + cmCPackNSISGenerator::CreateGenerator64); + } + if (cmCPackIFWGenerator::CanGenerate()) { + this->RegisterGenerator("IFW", "Qt Installer Framework", + cmCPackIFWGenerator::CreateGenerator); + } +#ifdef __CYGWIN__ + if (cmCPackCygwinBinaryGenerator::CanGenerate()) { + this->RegisterGenerator("CygwinBinary", "Cygwin Binary Installer", + cmCPackCygwinBinaryGenerator::CreateGenerator); + } + if (cmCPackCygwinSourceGenerator::CanGenerate()) { + this->RegisterGenerator("CygwinSource", "Cygwin Source Installer", + cmCPackCygwinSourceGenerator::CreateGenerator); + } +#endif + + if (cmCPackZIPGenerator::CanGenerate()) { + this->RegisterGenerator("ZIP", "ZIP file format", + cmCPackZIPGenerator::CreateGenerator); + } + if (cmCPack7zGenerator::CanGenerate()) { + this->RegisterGenerator("7Z", "7-Zip file format", + cmCPack7zGenerator::CreateGenerator); + } +#ifdef _WIN32 + if (cmCPackWIXGenerator::CanGenerate()) { + this->RegisterGenerator("WIX", "MSI file format via WiX tools", + cmCPackWIXGenerator::CreateGenerator); + } +#endif + if (cmCPackTarBZip2Generator::CanGenerate()) { + this->RegisterGenerator("TBZ2", "Tar BZip2 compression", + cmCPackTarBZip2Generator::CreateGenerator); + } + if (cmCPackTarCompressGenerator::CanGenerate()) { + this->RegisterGenerator("TZ", "Tar Compress compression", + cmCPackTarCompressGenerator::CreateGenerator); + } +#ifdef __APPLE__ + if (cmCPackDragNDropGenerator::CanGenerate()) { + this->RegisterGenerator("DragNDrop", "Mac OSX Drag And Drop", + cmCPackDragNDropGenerator::CreateGenerator); + } + if (cmCPackBundleGenerator::CanGenerate()) { + this->RegisterGenerator("Bundle", "Mac OSX bundle", + cmCPackBundleGenerator::CreateGenerator); + } + if (cmCPackPackageMakerGenerator::CanGenerate()) { + this->RegisterGenerator("PackageMaker", "Mac OSX Package Maker installer", + cmCPackPackageMakerGenerator::CreateGenerator); + } + if (cmCPackOSXX11Generator::CanGenerate()) { + this->RegisterGenerator("OSXX11", "Mac OSX X11 bundle", + cmCPackOSXX11Generator::CreateGenerator); + } + if (cmCPackProductBuildGenerator::CanGenerate()) { + this->RegisterGenerator("productbuild", "Mac OSX pkg", + cmCPackProductBuildGenerator::CreateGenerator); + } +#endif +#if !defined(_WIN32) && !defined(__QNXNTO__) && !defined(__BEOS__) && \ + !defined(__HAIKU__) + if (cmCPackDebGenerator::CanGenerate()) { + this->RegisterGenerator("DEB", "Debian packages", + cmCPackDebGenerator::CreateGenerator); + } + if (cmCPackRPMGenerator::CanGenerate()) { + this->RegisterGenerator("RPM", "RPM packages", + cmCPackRPMGenerator::CreateGenerator); + } +#endif +} + +cmCPackGeneratorFactory::~cmCPackGeneratorFactory() +{ + cmDeleteAll(this->Generators); +} + +cmCPackGenerator* cmCPackGeneratorFactory::NewGenerator( + const std::string& name) +{ + cmCPackGenerator* gen = this->NewGeneratorInternal(name); + if (!gen) { + return CM_NULLPTR; + } + this->Generators.push_back(gen); + gen->SetLogger(this->Logger); + return gen; +} + +cmCPackGenerator* cmCPackGeneratorFactory::NewGeneratorInternal( + const std::string& name) +{ + cmCPackGeneratorFactory::t_GeneratorCreatorsMap::iterator it = + this->GeneratorCreators.find(name); + if (it == this->GeneratorCreators.end()) { + return CM_NULLPTR; + } + return (it->second)(); +} + +void cmCPackGeneratorFactory::RegisterGenerator( + const std::string& name, const char* generatorDescription, + CreateGeneratorCall* createGenerator) +{ + if (!createGenerator) { + cmCPack_Log(this->Logger, cmCPackLog::LOG_ERROR, + "Cannot register generator" << std::endl); + return; + } + this->GeneratorCreators[name] = createGenerator; + this->GeneratorDescriptions[name] = generatorDescription; +} diff --git a/Source/CPack/cmCPackGeneratorFactory.h b/Source/CPack/cmCPackGeneratorFactory.h new file mode 100644 index 0000000..f0ed57a --- /dev/null +++ b/Source/CPack/cmCPackGeneratorFactory.h @@ -0,0 +1,61 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackGeneratorFactory_h +#define cmCPackGeneratorFactory_h + +#include "cmObject.h" + +class cmCPackLog; +class cmCPackGenerator; + +/** \class cmCPackGeneratorFactory + * \brief A container for CPack generators + * + */ +class cmCPackGeneratorFactory : public cmObject +{ +public: + cmTypeMacro(cmCPackGeneratorFactory, cmObject); + + cmCPackGeneratorFactory(); + ~cmCPackGeneratorFactory() CM_OVERRIDE; + + //! Get the generator + cmCPackGenerator* NewGenerator(const std::string& name); + void DeleteGenerator(cmCPackGenerator* gen); + + typedef cmCPackGenerator* CreateGeneratorCall(); + + void RegisterGenerator(const std::string& name, + const char* generatorDescription, + CreateGeneratorCall* createGenerator); + + void SetLogger(cmCPackLog* logger) { this->Logger = logger; } + + typedef std::map<std::string, std::string> DescriptionsMap; + const DescriptionsMap& GetGeneratorsList() const + { + return this->GeneratorDescriptions; + } + +private: + cmCPackGenerator* NewGeneratorInternal(const std::string& name); + std::vector<cmCPackGenerator*> Generators; + + typedef std::map<std::string, CreateGeneratorCall*> t_GeneratorCreatorsMap; + t_GeneratorCreatorsMap GeneratorCreators; + DescriptionsMap GeneratorDescriptions; + cmCPackLog* Logger; +}; + +#endif diff --git a/Source/CPack/cmCPackLog.cxx b/Source/CPack/cmCPackLog.cxx new file mode 100644 index 0000000..339323e --- /dev/null +++ b/Source/CPack/cmCPackLog.cxx @@ -0,0 +1,190 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackLog.h" + +#include "cmGeneratedFileStream.h" +#include "cmSystemTools.h" + +cmCPackLog::cmCPackLog() +{ + this->Verbose = false; + this->Debug = false; + this->Quiet = false; + this->NewLine = true; + + this->LastTag = cmCPackLog::NOTAG; +#undef cerr +#undef cout + this->DefaultOutput = &std::cout; + this->DefaultError = &std::cerr; + + this->LogOutput = CM_NULLPTR; + this->LogOutputCleanup = false; +} + +cmCPackLog::~cmCPackLog() +{ + this->SetLogOutputStream(CM_NULLPTR); +} + +void cmCPackLog::SetLogOutputStream(std::ostream* os) +{ + if (this->LogOutputCleanup && this->LogOutput) { + delete this->LogOutput; + } + this->LogOutputCleanup = false; + this->LogOutput = os; +} + +bool cmCPackLog::SetLogOutputFile(const char* fname) +{ + cmGeneratedFileStream* cg = CM_NULLPTR; + if (fname) { + cg = new cmGeneratedFileStream(fname); + } + if (cg && !*cg) { + delete cg; + cg = CM_NULLPTR; + } + this->SetLogOutputStream(cg); + if (!cg) { + return false; + } + this->LogOutputCleanup = true; + return true; +} + +void cmCPackLog::Log(int tag, const char* file, int line, const char* msg, + size_t length) +{ + // By default no logging + bool display = false; + + // Display file and line number if debug + bool useFileAndLine = this->Debug; + + bool output = false; + bool debug = false; + bool warning = false; + bool error = false; + bool verbose = false; + + // When writing in file, add list of tags whenever tag changes. + std::string tagString; + bool needTagString = false; + if (this->LogOutput && this->LastTag != tag) { + needTagString = true; + } + + if (tag & LOG_OUTPUT) { + output = true; + display = true; + if (needTagString) { + if (!tagString.empty()) { + tagString += ","; + } + tagString = "VERBOSE"; + } + } + if (tag & LOG_WARNING) { + warning = true; + display = true; + if (needTagString) { + if (!tagString.empty()) { + tagString += ","; + } + tagString = "WARNING"; + } + } + if (tag & LOG_ERROR) { + error = true; + display = true; + if (needTagString) { + if (!tagString.empty()) { + tagString += ","; + } + tagString = "ERROR"; + } + } + if (tag & LOG_DEBUG && this->Debug) { + debug = true; + display = true; + if (needTagString) { + if (!tagString.empty()) { + tagString += ","; + } + tagString = "DEBUG"; + } + useFileAndLine = true; + } + if (tag & LOG_VERBOSE && this->Verbose) { + verbose = true; + display = true; + if (needTagString) { + if (!tagString.empty()) { + tagString += ","; + } + tagString = "VERBOSE"; + } + } + if (this->Quiet) { + display = false; + } + if (this->LogOutput) { + if (needTagString) { + *this->LogOutput << "[" << file << ":" << line << " " << tagString + << "] "; + } + this->LogOutput->write(msg, length); + } + this->LastTag = tag; + if (!display) { + return; + } + if (this->NewLine) { + if (error && !this->ErrorPrefix.empty()) { + *this->DefaultError << this->ErrorPrefix; + } else if (warning && !this->WarningPrefix.empty()) { + *this->DefaultError << this->WarningPrefix; + } else if (output && !this->OutputPrefix.empty()) { + *this->DefaultOutput << this->OutputPrefix; + } else if (verbose && !this->VerbosePrefix.empty()) { + *this->DefaultOutput << this->VerbosePrefix; + } else if (debug && !this->DebugPrefix.empty()) { + *this->DefaultOutput << this->DebugPrefix; + } else if (!this->Prefix.empty()) { + *this->DefaultOutput << this->Prefix; + } + if (useFileAndLine) { + if (error || warning) { + *this->DefaultError << file << ":" << line << " "; + } else { + *this->DefaultOutput << file << ":" << line << " "; + } + } + } + if (error || warning) { + this->DefaultError->write(msg, length); + this->DefaultError->flush(); + } else { + this->DefaultOutput->write(msg, length); + this->DefaultOutput->flush(); + } + if (msg[length - 1] == '\n' || length > 2) { + this->NewLine = true; + } + + if (error) { + cmSystemTools::SetErrorOccured(); + } +} diff --git a/Source/CPack/cmCPackLog.h b/Source/CPack/cmCPackLog.h new file mode 100644 index 0000000..77f0f0b --- /dev/null +++ b/Source/CPack/cmCPackLog.h @@ -0,0 +1,158 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackLog_h +#define cmCPackLog_h + +#include "cmObject.h" + +#define cmCPack_Log(ctSelf, logType, msg) \ + do { \ + std::ostringstream cmCPackLog_msg; \ + cmCPackLog_msg << msg; \ + (ctSelf)->Log(logType, __FILE__, __LINE__, cmCPackLog_msg.str().c_str()); \ + } while (0) + +#ifdef cerr +#undef cerr +#endif +#define cerr no_cerr_use_cmCPack_Log + +#ifdef cout +#undef cout +#endif +#define cout no_cout_use_cmCPack_Log + +/** \class cmCPackLog + * \brief A container for CPack generators + * + */ +class cmCPackLog : public cmObject +{ +public: + cmTypeMacro(cmCPackLog, cmObject); + + cmCPackLog(); + ~cmCPackLog() CM_OVERRIDE; + + enum __log_tags + { + NOTAG = 0, + LOG_OUTPUT = 0x1, + LOG_VERBOSE = 0x2, + LOG_DEBUG = 0x4, + LOG_WARNING = 0x8, + LOG_ERROR = 0x10 + }; + + //! Various signatures for logging. + void Log(const char* file, int line, const char* msg) + { + this->Log(LOG_OUTPUT, file, line, msg); + } + void Log(const char* file, int line, const char* msg, size_t length) + { + this->Log(LOG_OUTPUT, file, line, msg, length); + } + void Log(int tag, const char* file, int line, const char* msg) + { + this->Log(tag, file, line, msg, strlen(msg)); + } + void Log(int tag, const char* file, int line, const char* msg, + size_t length); + + //! Set Verbose + void VerboseOn() { this->SetVerbose(true); } + void VerboseOff() { this->SetVerbose(true); } + void SetVerbose(bool verb) { this->Verbose = verb; } + bool GetVerbose() { return this->Verbose; } + + //! Set Debug + void DebugOn() { this->SetDebug(true); } + void DebugOff() { this->SetDebug(true); } + void SetDebug(bool verb) { this->Debug = verb; } + bool GetDebug() { return this->Debug; } + + //! Set Quiet + void QuietOn() { this->SetQuiet(true); } + void QuietOff() { this->SetQuiet(true); } + void SetQuiet(bool verb) { this->Quiet = verb; } + bool GetQuiet() { return this->Quiet; } + + //! Set the output stream + void SetOutputStream(std::ostream* os) { this->DefaultOutput = os; } + + //! Set the error stream + void SetErrorStream(std::ostream* os) { this->DefaultError = os; } + + //! Set the log output stream + void SetLogOutputStream(std::ostream* os); + + //! Set the log output file. The cmCPackLog will try to create file. If it + // cannot, it will report an error. + bool SetLogOutputFile(const char* fname); + + //! Set the various prefixes for the logging. SetPrefix sets the generic + // prefix that overwrittes missing ones. + void SetPrefix(std::string pfx) { this->Prefix = pfx; } + void SetOutputPrefix(std::string pfx) { this->OutputPrefix = pfx; } + void SetVerbosePrefix(std::string pfx) { this->VerbosePrefix = pfx; } + void SetDebugPrefix(std::string pfx) { this->DebugPrefix = pfx; } + void SetWarningPrefix(std::string pfx) { this->WarningPrefix = pfx; } + void SetErrorPrefix(std::string pfx) { this->ErrorPrefix = pfx; } + +private: + bool Verbose; + bool Debug; + bool Quiet; + + bool NewLine; + + int LastTag; + + std::string Prefix; + std::string OutputPrefix; + std::string VerbosePrefix; + std::string DebugPrefix; + std::string WarningPrefix; + std::string ErrorPrefix; + + std::ostream* DefaultOutput; + std::ostream* DefaultError; + + std::string LogOutputFileName; + std::ostream* LogOutput; + // Do we need to cleanup log output stream + bool LogOutputCleanup; +}; + +class cmCPackLogWrite +{ +public: + cmCPackLogWrite(const char* data, size_t length) + : Data(data) + , Length(length) + { + } + + const char* Data; + size_t Length; +}; + +inline std::ostream& operator<<(std::ostream& os, const cmCPackLogWrite& c) +{ + os.write(c.Data, c.Length); + os.flush(); + return os; +} + +#endif diff --git a/Source/CPack/cmCPackNSISGenerator.cxx b/Source/CPack/cmCPackNSISGenerator.cxx new file mode 100644 index 0000000..d8ff907 --- /dev/null +++ b/Source/CPack/cmCPackNSISGenerator.cxx @@ -0,0 +1,918 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackNSISGenerator.h" + +#include "cmCPackComponentGroup.h" +#include "cmCPackLog.h" +#include "cmGeneratedFileStream.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" + +#include <cmsys/Directory.hxx> +#include <cmsys/Glob.hxx> +#include <cmsys/RegularExpression.hxx> +#include <cmsys/SystemTools.hxx> + +/* NSIS uses different command line syntax on Windows and others */ +#ifdef _WIN32 +#define NSIS_OPT "/" +#else +#define NSIS_OPT "-" +#endif + +cmCPackNSISGenerator::cmCPackNSISGenerator(bool nsis64) +{ + Nsis64 = nsis64; +} + +cmCPackNSISGenerator::~cmCPackNSISGenerator() +{ +} + +int cmCPackNSISGenerator::PackageFiles() +{ + // TODO: Fix nsis to force out file name + + std::string nsisInFileName = this->FindTemplate("NSIS.template.in"); + if (nsisInFileName.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPack error: Could not find NSIS installer template file." + << std::endl); + return false; + } + std::string nsisInInstallOptions = + this->FindTemplate("NSIS.InstallOptions.ini.in"); + if (nsisInInstallOptions.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPack error: Could not find NSIS installer options file." + << std::endl); + return false; + } + + std::string nsisFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + std::string tmpFile = nsisFileName; + tmpFile += "/NSISOutput.log"; + std::string nsisInstallOptions = nsisFileName + "/NSIS.InstallOptions.ini"; + nsisFileName += "/project.nsi"; + std::ostringstream str; + std::vector<std::string>::const_iterator it; + for (it = files.begin(); it != files.end(); ++it) { + std::string fileN = + cmSystemTools::RelativePath(toplevel.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); + } + std::replace(fileN.begin(), fileN.end(), '/', '\\'); + str << " Delete \"$INSTDIR\\" << fileN << "\"" << std::endl; + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Files: " << str.str() + << std::endl); + this->SetOptionIfNotSet("CPACK_NSIS_DELETE_FILES", str.str().c_str()); + std::vector<std::string> dirs; + this->GetListOfSubdirectories(toplevel.c_str(), dirs); + std::vector<std::string>::const_iterator sit; + std::ostringstream dstr; + for (sit = dirs.begin(); sit != dirs.end(); ++sit) { + std::string componentName; + std::string fileN = + cmSystemTools::RelativePath(toplevel.c_str(), 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); + } + } + std::replace(fileN.begin(), fileN.end(), '/', '\\'); + dstr << " RMDir \"$INSTDIR\\" << fileN << "\"" << std::endl; + if (!componentName.empty()) { + this->Components[componentName].Directories.push_back(fileN); + } + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Uninstall Dirs: " << dstr.str() + << std::endl); + this->SetOptionIfNotSet("CPACK_NSIS_DELETE_DIRECTORIES", dstr.str().c_str()); + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " + << nsisInFileName << " to " << nsisFileName << std::endl); + if (this->IsSet("CPACK_NSIS_MUI_ICON") || + this->IsSet("CPACK_NSIS_MUI_UNIICON")) { + std::string installerIconCode; + if (this->IsSet("CPACK_NSIS_MUI_ICON")) { + installerIconCode += "!define MUI_ICON \""; + installerIconCode += this->GetOption("CPACK_NSIS_MUI_ICON"); + installerIconCode += "\"\n"; + } + if (this->IsSet("CPACK_NSIS_MUI_UNIICON")) { + installerIconCode += "!define MUI_UNICON \""; + installerIconCode += this->GetOption("CPACK_NSIS_MUI_UNIICON"); + installerIconCode += "\"\n"; + } + this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_ICON_CODE", + installerIconCode.c_str()); + } + if (this->IsSet("CPACK_PACKAGE_ICON")) { + std::string installerIconCode = "!define MUI_HEADERIMAGE_BITMAP \""; + installerIconCode += this->GetOption("CPACK_PACKAGE_ICON"); + installerIconCode += "\"\n"; + this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_ICON_CODE", + installerIconCode.c_str()); + } + + if (this->IsSet("CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP")) { + std::string installerBitmapCode = + "!define MUI_WELCOMEFINISHPAGE_BITMAP \""; + installerBitmapCode += + this->GetOption("CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP"); + installerBitmapCode += "\"\n"; + this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_WELCOMEFINISH_CODE", + installerBitmapCode.c_str()); + } + + if (this->IsSet("CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP")) { + std::string installerBitmapCode = + "!define MUI_UNWELCOMEFINISHPAGE_BITMAP \""; + installerBitmapCode += + this->GetOption("CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP"); + installerBitmapCode += "\"\n"; + this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_UNWELCOMEFINISH_CODE", + installerBitmapCode.c_str()); + } + + if (this->IsSet("CPACK_NSIS_MUI_FINISHPAGE_RUN")) { + std::string installerRunCode = "!define MUI_FINISHPAGE_RUN \"$INSTDIR\\"; + installerRunCode += this->GetOption("CPACK_NSIS_EXECUTABLES_DIRECTORY"); + installerRunCode += "\\"; + installerRunCode += this->GetOption("CPACK_NSIS_MUI_FINISHPAGE_RUN"); + installerRunCode += "\"\n"; + this->SetOptionIfNotSet("CPACK_NSIS_INSTALLER_MUI_FINISHPAGE_RUN_CODE", + installerRunCode.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; + std::string defines; + std::ostringstream 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. + 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) { + if (groupIt->second.ParentGroup == CM_NULLPTR) { + componentCode += + this->CreateComponentGroupDescription(&groupIt->second, macrosOut); + } + + // 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.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, macrosOut); + } + + // Add this component to the various section lists. + sectionList += " !insertmacro \"${MacroName}\" \""; + 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()) { + componentDescriptions += " !insertmacro MUI_DESCRIPTION_TEXT ${" + + compIt->first + "} \"" + + this->TranslateNewlines(compIt->second.Description) + "\"\n"; + } + } + + componentCode += macrosOut.str(); + + 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()); + } + + 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(), + nsisInstallOptions.c_str()); + this->ConfigureFile(nsisInFileName.c_str(), nsisFileName.c_str()); + std::string nsisCmd = "\""; + nsisCmd += this->GetOption("CPACK_INSTALLER_PROGRAM"); + nsisCmd += "\" \"" + nsisFileName + "\""; + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << nsisCmd << std::endl); + std::string output; + int retVal = 1; + bool res = + cmSystemTools::RunSingleCommand(nsisCmd.c_str(), &output, &output, &retVal, + CM_NULLPTR, this->GeneratorVerbose, 0); + if (!res || retVal) { + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << nsisCmd << std::endl + << "# Output:" << std::endl + << output << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running NSIS command: " + << nsisCmd << std::endl + << "Please check " << tmpFile << " for errors" + << std::endl); + return 0; + } + return 1; +} + +int cmCPackNSISGenerator::InitializeInternal() +{ + if (cmSystemTools::IsOn( + this->GetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY"))) { + cmCPackLogger( + cmCPackLog::LOG_WARNING, + "NSIS Generator cannot work with CPACK_INCLUDE_TOPLEVEL_DIRECTORY set. " + "This option will be reset to 0 (for this generator only)." + << std::endl); + this->SetOption("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", CM_NULLPTR); + } + + cmCPackLogger(cmCPackLog::LOG_DEBUG, "cmCPackNSISGenerator::Initialize()" + << std::endl); + std::vector<std::string> path; + std::string nsisPath; + bool gotRegValue = false; + +#ifdef _WIN32 + if (Nsis64) { + if (!gotRegValue && cmsys::SystemTools::ReadRegistryValue( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS\\Unicode", + nsisPath, cmsys::SystemTools::KeyWOW64_64)) { + gotRegValue = true; + } + if (!gotRegValue && cmsys::SystemTools::ReadRegistryValue( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath, + cmsys::SystemTools::KeyWOW64_64)) { + gotRegValue = true; + } + } + if (!gotRegValue && cmsys::SystemTools::ReadRegistryValue( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS\\Unicode", + nsisPath, cmsys::SystemTools::KeyWOW64_32)) { + gotRegValue = true; + } + if (!gotRegValue && + cmsys::SystemTools::ReadRegistryValue( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS\\Unicode", nsisPath)) { + gotRegValue = true; + } + if (!gotRegValue && cmsys::SystemTools::ReadRegistryValue( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath, + cmsys::SystemTools::KeyWOW64_32)) { + gotRegValue = true; + } + if (!gotRegValue && cmsys::SystemTools::ReadRegistryValue( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", nsisPath)) { + gotRegValue = true; + } + + if (gotRegValue) { + path.push_back(nsisPath); + } +#endif + + nsisPath = cmSystemTools::FindProgram("makensis", path, false); + + if (nsisPath.empty()) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "Cannot find NSIS compiler makensis: likely it is not installed, " + "or not in your PATH" + << std::endl); + + if (!gotRegValue) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "Could not read NSIS registry value. This is usually caused by " + "NSIS not being installed. Please install NSIS from " + "http://nsis.sourceforge.net" + << std::endl); + } + + return 0; + } + + std::string nsisCmd = "\"" + nsisPath + "\" " NSIS_OPT "VERSION"; + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Test NSIS version: " << nsisCmd + << std::endl); + std::string output; + int retVal = 1; + bool resS = + cmSystemTools::RunSingleCommand(nsisCmd.c_str(), &output, &output, &retVal, + CM_NULLPTR, this->GeneratorVerbose, 0); + cmsys::RegularExpression versionRex("v([0-9]+.[0-9]+)"); + cmsys::RegularExpression versionRexCVS("v(.*)\\.cvs"); + if (!resS || retVal || + (!versionRex.find(output) && !versionRexCVS.find(output))) { + const char* topDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + std::string tmpFile = topDir ? topDir : "."; + tmpFile += "/NSISOutput.log"; + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << nsisCmd << std::endl + << "# Output:" << std::endl + << output << std::endl; + cmCPackLogger( + cmCPackLog::LOG_ERROR, "Problem checking NSIS version with command: " + << nsisCmd << std::endl + << "Please check " << tmpFile << " for errors" << std::endl); + return 0; + } + if (versionRex.find(output)) { + double nsisVersion = atof(versionRex.match(1).c_str()); + double minNSISVersion = 2.09; + cmCPackLogger(cmCPackLog::LOG_DEBUG, "NSIS Version: " << nsisVersion + << std::endl); + if (nsisVersion < minNSISVersion) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "CPack requires NSIS Version 2.09 or greater. " + "NSIS found on the system was: " + << nsisVersion << std::endl); + return 0; + } + } + if (versionRexCVS.find(output)) { + // No version check for NSIS cvs build + cmCPackLogger(cmCPackLog::LOG_DEBUG, "NSIS Version: CVS " + << versionRexCVS.match(1) << std::endl); + } + this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", nsisPath.c_str()); + this->SetOptionIfNotSet("CPACK_NSIS_EXECUTABLES_DIRECTORY", "bin"); + const char* cpackPackageExecutables = + this->GetOption("CPACK_PACKAGE_EXECUTABLES"); + const char* cpackPackageDeskTopLinks = + this->GetOption("CPACK_CREATE_DESKTOP_LINKS"); + const char* cpackNsisExecutablesDirectory = + this->GetOption("CPACK_NSIS_EXECUTABLES_DIRECTORY"); + std::vector<std::string> cpackPackageDesktopLinksVector; + if (cpackPackageDeskTopLinks) { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: " + << cpackPackageDeskTopLinks << std::endl); + + cmSystemTools::ExpandListArgument(cpackPackageDeskTopLinks, + cpackPackageDesktopLinksVector); + for (std::vector<std::string>::iterator i = + cpackPackageDesktopLinksVector.begin(); + i != cpackPackageDesktopLinksVector.end(); ++i) { + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "CPACK_CREATE_DESKTOP_LINKS: " << *i << std::endl); + } + } else { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "CPACK_CREATE_DESKTOP_LINKS: " + << "not set" << std::endl); + } + + std::ostringstream str; + std::ostringstream deleteStr; + + if (cpackPackageExecutables) { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "The cpackPackageExecutables: " + << cpackPackageExecutables << "." << std::endl); + std::vector<std::string> cpackPackageExecutablesVector; + cmSystemTools::ExpandListArgument(cpackPackageExecutables, + cpackPackageExecutablesVector); + if (cpackPackageExecutablesVector.size() % 2 != 0) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and " + "<icon name>." + << std::endl); + return 0; + } + std::vector<std::string>::iterator it; + for (it = cpackPackageExecutablesVector.begin(); + it != cpackPackageExecutablesVector.end(); ++it) { + std::string execName = *it; + ++it; + std::string linkName = *it; + str << " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\" << linkName + << ".lnk\" \"$INSTDIR\\" << cpackNsisExecutablesDirectory << "\\" + << execName << ".exe\"" << std::endl; + deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName + << ".lnk\"" << std::endl; + // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on + // if so add a desktop link + if (!cpackPackageDesktopLinksVector.empty() && + std::find(cpackPackageDesktopLinksVector.begin(), + cpackPackageDesktopLinksVector.end(), + execName) != cpackPackageDesktopLinksVector.end()) { + str << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n"; + str << " CreateShortCut \"$DESKTOP\\" << linkName + << ".lnk\" \"$INSTDIR\\" << cpackNsisExecutablesDirectory << "\\" + << execName << ".exe\"" << std::endl; + deleteStr << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n"; + deleteStr << " Delete \"$DESKTOP\\" << linkName << ".lnk\"" + << std::endl; + } + } + } + + this->CreateMenuLinks(str, deleteStr); + this->SetOptionIfNotSet("CPACK_NSIS_CREATE_ICONS", str.str().c_str()); + this->SetOptionIfNotSet("CPACK_NSIS_DELETE_ICONS", deleteStr.str().c_str()); + + this->SetOptionIfNotSet("CPACK_NSIS_COMPRESSOR", "lzma"); + + return this->Superclass::InitializeInternal(); +} + +void cmCPackNSISGenerator::CreateMenuLinks(std::ostream& str, + std::ostream& deleteStr) +{ + const char* cpackMenuLinks = this->GetOption("CPACK_NSIS_MENU_LINKS"); + if (!cpackMenuLinks) { + return; + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, + "The cpackMenuLinks: " << cpackMenuLinks << "." << std::endl); + std::vector<std::string> cpackMenuLinksVector; + cmSystemTools::ExpandListArgument(cpackMenuLinks, cpackMenuLinksVector); + if (cpackMenuLinksVector.size() % 2 != 0) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "CPACK_NSIS_MENU_LINKS should contain pairs of <shortcut target> and " + "<shortcut label>." + << std::endl); + return; + } + + static cmsys::RegularExpression urlRegex( + "^(mailto:|(ftps?|https?|news)://).*$"); + + std::vector<std::string>::iterator it; + for (it = cpackMenuLinksVector.begin(); it != cpackMenuLinksVector.end(); + ++it) { + std::string sourceName = *it; + const bool url = urlRegex.find(sourceName); + + // Convert / to \ in filenames, but not in urls: + // + if (!url) { + std::replace(sourceName.begin(), sourceName.end(), '/', '\\'); + } + + ++it; + std::string linkName = *it; + if (!url) { + str << " CreateShortCut \"$SMPROGRAMS\\$STARTMENU_FOLDER\\" << linkName + << ".lnk\" \"$INSTDIR\\" << sourceName << "\"" << std::endl; + deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName + << ".lnk\"" << std::endl; + } else { + str << " WriteINIStr \"$SMPROGRAMS\\$STARTMENU_FOLDER\\" << linkName + << ".url\" \"InternetShortcut\" \"URL\" \"" << sourceName << "\"" + << std::endl; + deleteStr << " Delete \"$SMPROGRAMS\\$MUI_TEMP\\" << linkName + << ".url\"" << std::endl; + } + // see if CPACK_CREATE_DESKTOP_LINK_ExeName is on + // if so add a desktop link + std::string desktop = "CPACK_CREATE_DESKTOP_LINK_"; + desktop += linkName; + if (this->IsSet(desktop)) { + str << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n"; + str << " CreateShortCut \"$DESKTOP\\" << linkName + << ".lnk\" \"$INSTDIR\\" << sourceName << "\"" << std::endl; + deleteStr << " StrCmp \"$INSTALL_DESKTOP\" \"1\" 0 +2\n"; + deleteStr << " Delete \"$DESKTOP\\" << linkName << ".lnk\"" + << std::endl; + } + } +} + +bool cmCPackNSISGenerator::GetListOfSubdirectories( + const char* topdir, std::vector<std::string>& dirs) +{ + cmsys::Directory dir; + dir.Load(topdir); + size_t fileNum; + for (fileNum = 0; fileNum < dir.GetNumberOfFiles(); ++fileNum) { + if (strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), ".") && + strcmp(dir.GetFile(static_cast<unsigned long>(fileNum)), "..")) { + std::string fullPath = topdir; + fullPath += "/"; + fullPath += dir.GetFile(static_cast<unsigned long>(fileNum)); + if (cmsys::SystemTools::FileIsDirectory(fullPath) && + !cmsys::SystemTools::FileIsSymlink(fullPath)) { + if (!this->GetListOfSubdirectories(fullPath.c_str(), dirs)) { + return false; + } + } + } + } + dirs.push_back(topdir); + return true; +} + +enum cmCPackGenerator::CPackSetDestdirSupport +cmCPackNSISGenerator::SupportsSetDestdir() const +{ + return cmCPackGenerator::SETDESTDIR_SHOULD_NOT_BE_USED; +} + +bool cmCPackNSISGenerator::SupportsAbsoluteDestination() const +{ + return false; +} + +bool cmCPackNSISGenerator::SupportsComponentInstallation() const +{ + return true; +} + +std::string cmCPackNSISGenerator::CreateComponentDescription( + cmCPackComponent* component, std::ostream& macrosOut) +{ + // 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"; + + // 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"; + std::ostringstream 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)) { + 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); + } + } + + // 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, &output, + &retVal, dirName.c_str(), + cmSystemTools::OUTPUT_NONE, 0); + if (!res || retVal) { + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/CompressZip.log"; + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << cmd << std::endl + << "# Output:" << std::endl + << output << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running zip command: " + << cmd << std::endl + << "Please check " << tmpFile << " for errors" + << std::endl); + return ""; + } + + // Create the NSIS code to download this file on-the-fly. + unsigned long totalSizeInKbytes = (totalSize + 512) / 1024; + if (totalSizeInKbytes == 0) { + totalSizeInKbytes = 1; + } + std::ostringstream out; + /* clang-format off */ + 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"; + /* clang-format on */ + componentCode += out.str(); + } else { + componentCode += + " File /r \"${INST_DIR}\\" + component->Name + "\\*.*\"\n"; + } + componentCode += "SectionEnd\n"; + + // Macro used to remove the component + macrosOut << "!macro Remove_${" << component->Name << "}\n"; + macrosOut << " IntCmp $" << component->Name << "_was_installed 0 noremove_" + << component->Name << "\n"; + std::vector<std::string>::iterator pathIt; + std::string path; + for (pathIt = component->Files.begin(); pathIt != component->Files.end(); + ++pathIt) { + path = *pathIt; + std::replace(path.begin(), path.end(), '/', '\\'); + macrosOut << " Delete \"$INSTDIR\\" << path << "\"\n"; + } + for (pathIt = component->Directories.begin(); + pathIt != component->Directories.end(); ++pathIt) { + path = *pathIt; + std::replace(path.begin(), path.end(), '/', '\\'); + macrosOut << " RMDir \"$INSTDIR\\" << path << "\"\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; + 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(); + 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) +{ + // 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) +{ + // 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, std::ostream& macrosOut) +{ + if (group->Components.empty() && group->Subgroups.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<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) { + if ((*comp)->Files.empty()) { + continue; + } + + code += this->CreateComponentDescription(*comp, macrosOut); + } + 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 new file mode 100644 index 0000000..4923cf0 --- /dev/null +++ b/Source/CPack/cmCPackNSISGenerator.h @@ -0,0 +1,85 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackNSISGenerator_h +#define cmCPackNSISGenerator_h + +#include "cmCPackGenerator.h" + +#include <set> + +/** \class cmCPackNSISGenerator + * \brief A generator for NSIS files + * + * http://people.freebsd.org/~kientzle/libarchive/ + */ +class cmCPackNSISGenerator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackNSISGenerator, cmCPackGenerator); + + static cmCPackGenerator* CreateGenerator64() + { + return new cmCPackNSISGenerator(true); + } + + /** + * Construct generator + */ + cmCPackNSISGenerator(bool nsis64 = false); + ~cmCPackNSISGenerator() CM_OVERRIDE; + +protected: + int InitializeInternal() CM_OVERRIDE; + void CreateMenuLinks(std::ostream& str, std::ostream& deleteStr); + int PackageFiles() CM_OVERRIDE; + const char* GetOutputExtension() CM_OVERRIDE { return ".exe"; } + const char* GetOutputPostfix() CM_OVERRIDE { return "win32"; } + + bool GetListOfSubdirectories(const char* dir, + std::vector<std::string>& dirs); + + enum cmCPackGenerator::CPackSetDestdirSupport SupportsSetDestdir() const + CM_OVERRIDE; + bool SupportsAbsoluteDestination() const CM_OVERRIDE; + bool SupportsComponentInstallation() const CM_OVERRIDE; + + /// Produce a string that contains the NSIS code to describe a + /// particular component. Any added macros will be emitted via + /// macrosOut. + std::string CreateComponentDescription(cmCPackComponent* component, + std::ostream& macrosOut); + + /// Produce NSIS code that selects all of the components that this component + /// depends on, recursively. + std::string CreateSelectionDependenciesDescription( + cmCPackComponent* component, std::set<cmCPackComponent*>& visited); + + /// 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); + + /// Produce a string that contains the NSIS code to describe a + /// particular component group, including its components. Any + /// added macros will be emitted via macrosOut. + std::string CreateComponentGroupDescription(cmCPackComponentGroup* group, + std::ostream& macrosOut); + + /// 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); + + bool Nsis64; +}; + +#endif diff --git a/Source/CPack/cmCPackOSXX11Generator.cxx b/Source/CPack/cmCPackOSXX11Generator.cxx new file mode 100644 index 0000000..c0d2553 --- /dev/null +++ b/Source/CPack/cmCPackOSXX11Generator.cxx @@ -0,0 +1,291 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackOSXX11Generator.h" + +#include "cmCPackLog.h" +#include "cmGeneratedFileStream.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" +#include "cmake.h" + +#include <cmsys/Glob.hxx> +#include <cmsys/SystemTools.hxx> +#include <sys/stat.h> + +cmCPackOSXX11Generator::cmCPackOSXX11Generator() +{ +} + +cmCPackOSXX11Generator::~cmCPackOSXX11Generator() +{ +} + +int cmCPackOSXX11Generator::PackageFiles() +{ + // TODO: Use toplevel ? + // It is used! Is this an obsolete comment? + + const char* cpackPackageExecutables = + this->GetOption("CPACK_PACKAGE_EXECUTABLES"); + if (cpackPackageExecutables) { + cmCPackLogger(cmCPackLog::LOG_DEBUG, "The cpackPackageExecutables: " + << cpackPackageExecutables << "." << std::endl); + std::ostringstream str; + std::ostringstream deleteStr; + std::vector<std::string> cpackPackageExecutablesVector; + cmSystemTools::ExpandListArgument(cpackPackageExecutables, + cpackPackageExecutablesVector); + if (cpackPackageExecutablesVector.size() % 2 != 0) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "CPACK_PACKAGE_EXECUTABLES should contain pairs of <executable> and " + "<icon name>." + << std::endl); + return 0; + } + std::vector<std::string>::iterator it; + for (it = cpackPackageExecutablesVector.begin(); + it != cpackPackageExecutablesVector.end(); ++it) { + std::string cpackExecutableName = *it; + ++it; + this->SetOptionIfNotSet("CPACK_EXECUTABLE_NAME", + cpackExecutableName.c_str()); + } + } + + // Disk image directories + std::string diskImageDirectory = toplevel; + std::string diskImageBackgroundImageDir = + diskImageDirectory + "/.background"; + + // App bundle directories + std::string packageDirFileName = toplevel; + packageDirFileName += "/"; + packageDirFileName += this->GetOption("CPACK_PACKAGE_FILE_NAME"); + packageDirFileName += ".app"; + std::string contentsDirectory = packageDirFileName + "/Contents"; + std::string resourcesDirectory = contentsDirectory + "/Resources"; + std::string appDirectory = contentsDirectory + "/MacOS"; + std::string scriptDirectory = resourcesDirectory + "/Scripts"; + std::string resourceFileName = this->GetOption("CPACK_PACKAGE_FILE_NAME"); + resourceFileName += ".rsrc"; + + const char* dir = resourcesDirectory.c_str(); + const char* appdir = appDirectory.c_str(); + const char* scrDir = scriptDirectory.c_str(); + const char* contDir = contentsDirectory.c_str(); + const char* rsrcFile = resourceFileName.c_str(); + const char* iconFile = this->GetOption("CPACK_PACKAGE_ICON"); + if (iconFile) { + std::string iconFileName = cmsys::SystemTools::GetFilenameName(iconFile); + if (!cmSystemTools::FileExists(iconFile)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find icon file: " + << iconFile + << ". Please check CPACK_PACKAGE_ICON setting." + << std::endl); + return 0; + } + std::string destFileName = resourcesDirectory + "/" + iconFileName; + this->ConfigureFile(iconFile, destFileName.c_str(), true); + this->SetOptionIfNotSet("CPACK_APPLE_GUI_ICON", iconFileName.c_str()); + } + + std::string applicationsLinkName = diskImageDirectory + "/Applications"; + cmSystemTools::CreateSymlink("/Applications", applicationsLinkName.c_str()); + + if (!this->CopyResourcePlistFile("VolumeIcon.icns", + diskImageDirectory.c_str(), + ".VolumeIcon.icns", true) || + !this->CopyResourcePlistFile("DS_Store", diskImageDirectory.c_str(), + ".DS_Store", true) || + !this->CopyResourcePlistFile("background.png", + diskImageBackgroundImageDir.c_str(), + "background.png", true) || + !this->CopyResourcePlistFile("RuntimeScript", dir) || + !this->CopyResourcePlistFile("OSXX11.Info.plist", contDir, + "Info.plist") || + !this->CopyResourcePlistFile("OSXX11.main.scpt", scrDir, "main.scpt", + true) || + !this->CopyResourcePlistFile("OSXScriptLauncher.rsrc", dir, rsrcFile, + true) || + !this->CopyResourcePlistFile("OSXScriptLauncher", appdir, + this->GetOption("CPACK_PACKAGE_FILE_NAME"), + true)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying the resource files" + << std::endl); + return 0; + } + + // Two of the files need to have execute permission, so ensure they do: + std::string runTimeScript = dir; + runTimeScript += "/"; + runTimeScript += "RuntimeScript"; + + std::string appScriptName = appdir; + appScriptName += "/"; + appScriptName += this->GetOption("CPACK_PACKAGE_FILE_NAME"); + + mode_t mode; + if (cmsys::SystemTools::GetPermissions(runTimeScript.c_str(), mode)) { + mode |= (S_IXUSR | S_IXGRP | S_IXOTH); + cmsys::SystemTools::SetPermissions(runTimeScript.c_str(), mode); + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "Setting: " << runTimeScript << " to permission: " << mode + << std::endl); + } + + if (cmsys::SystemTools::GetPermissions(appScriptName.c_str(), mode)) { + mode |= (S_IXUSR | S_IXGRP | S_IXOTH); + cmsys::SystemTools::SetPermissions(appScriptName.c_str(), mode); + cmCPackLogger(cmCPackLog::LOG_OUTPUT, + "Setting: " << appScriptName << " to permission: " << mode + << std::endl); + } + + std::string output; + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/hdiutilOutput.log"; + std::ostringstream dmgCmd; + dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE") + << "\" create -ov -format UDZO -srcfolder \"" << diskImageDirectory + << "\" \"" << packageFileNames[0] << "\""; + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Compress disk image using command: " + << dmgCmd.str() << std::endl); + // since we get random dashboard failures with this one + // try running it more than once + int retVal = 1; + int numTries = 10; + bool res = false; + while (numTries > 0) { + res = + cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output, &output, + &retVal, 0, this->GeneratorVerbose, 0); + if (res && !retVal) { + numTries = -1; + break; + } + cmSystemTools::Delay(500); + numTries--; + } + if (!res || retVal) { + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << dmgCmd.str() << std::endl + << "# Output:" << std::endl + << output << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running hdiutil command: " + << dmgCmd.str() << std::endl + << "Please check " << tmpFile << " for errors" + << std::endl); + return 0; + } + + return 1; +} + +int cmCPackOSXX11Generator::InitializeInternal() +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, "cmCPackOSXX11Generator::Initialize()" + << std::endl); + std::vector<std::string> path; + std::string pkgPath = cmSystemTools::FindProgram("hdiutil", path, false); + if (pkgPath.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find hdiutil compiler" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE", + pkgPath.c_str()); + + return this->Superclass::InitializeInternal(); +} + +/* +bool cmCPackOSXX11Generator::CopyCreateResourceFile(const std::string& name) +{ + std::string uname = cmSystemTools::UpperCase(name); + std::string cpackVar = "CPACK_RESOURCE_FILE_" + uname; + const char* inFileName = this->GetOption(cpackVar.c_str()); + if ( !inFileName ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "CPack option: " << cpackVar.c_str() + << " not specified. It should point to " + << (name ? name : "(NULL)") + << ".rtf, " << name + << ".html, or " << name << ".txt file" << std::endl); + return false; + } + if ( !cmSystemTools::FileExists(inFileName) ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find " + << (name ? name : "(NULL)") + << " resource file: " << inFileName << std::endl); + return false; + } + std::string ext = cmSystemTools::GetFilenameLastExtension(inFileName); + if ( ext != ".rtfd" && ext != ".rtf" && ext != ".html" && ext != ".txt" ) + { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Bad file extension specified: " + << ext << ". Currently only .rtfd, .rtf, .html, and .txt files allowed." + << std::endl); + return false; + } + + std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + destFileName += "/Resources/"; + destFileName += name + ext; + + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " + << (inFileName ? inFileName : "(NULL)") + << " to " << destFileName << std::endl); + this->ConfigureFile(inFileName, destFileName.c_str()); + return true; +} +*/ + +bool cmCPackOSXX11Generator::CopyResourcePlistFile( + const std::string& name, const std::string& dir, + const char* outputFileName /* = 0 */, bool copyOnly /* = false */) +{ + std::string inFName = "CPack."; + inFName += name; + inFName += ".in"; + std::string inFileName = this->FindTemplate(inFName.c_str()); + if (inFileName.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find input file: " << inFName << std::endl); + return false; + } + + if (!outputFileName) { + outputFileName = name.c_str(); + } + + std::string destFileName = dir; + destFileName += "/"; + destFileName += outputFileName; + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " + << inFileName << " to " << destFileName << std::endl); + this->ConfigureFile(inFileName.c_str(), destFileName.c_str(), copyOnly); + return true; +} + +const char* cmCPackOSXX11Generator::GetPackagingInstallPrefix() +{ + this->InstallPrefix = "/"; + this->InstallPrefix += this->GetOption("CPACK_PACKAGE_FILE_NAME"); + this->InstallPrefix += ".app/Contents/Resources"; + return this->InstallPrefix.c_str(); +} diff --git a/Source/CPack/cmCPackOSXX11Generator.h b/Source/CPack/cmCPackOSXX11Generator.h new file mode 100644 index 0000000..d1d6789 --- /dev/null +++ b/Source/CPack/cmCPackOSXX11Generator.h @@ -0,0 +1,48 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackOSXX11Generator_h +#define cmCPackOSXX11Generator_h + +#include "cmCPackGenerator.h" + +/** \class cmCPackOSXX11Generator + * \brief A generator for OSX X11 modules + * + * Based on Gimp.app + */ +class cmCPackOSXX11Generator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackOSXX11Generator, cmCPackGenerator); + + /** + * Construct generator + */ + cmCPackOSXX11Generator(); + virtual ~cmCPackOSXX11Generator(); + +protected: + virtual int InitializeInternal() CM_OVERRIDE; + int PackageFiles() CM_OVERRIDE; + const char* GetPackagingInstallPrefix() CM_OVERRIDE; + const char* GetOutputExtension() CM_OVERRIDE { return ".dmg"; } + + // bool CopyCreateResourceFile(const std::string& name, + // const std::string& dir); + bool CopyResourcePlistFile(const std::string& name, const std::string& dir, + const char* outputFileName = 0, + bool copyOnly = false); + std::string InstallPrefix; +}; + +#endif diff --git a/Source/CPack/cmCPackPKGGenerator.cxx b/Source/CPack/cmCPackPKGGenerator.cxx new file mode 100644 index 0000000..19b587a --- /dev/null +++ b/Source/CPack/cmCPackPKGGenerator.cxx @@ -0,0 +1,367 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmCPackPKGGenerator.h" + +#include "cmCPackComponentGroup.h" +#include "cmCPackLog.h" +#include "cmGeneratedFileStream.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" +#include "cmXMLWriter.h" +#include "cmake.h" + +#include <cmsys/Glob.hxx> +#include <cmsys/SystemTools.hxx> + +cmCPackPKGGenerator::cmCPackPKGGenerator() +{ + this->componentPackageMethod = ONE_PACKAGE; +} + +cmCPackPKGGenerator::~cmCPackPKGGenerator() +{ +} + +bool cmCPackPKGGenerator::SupportsComponentInstallation() const +{ + return true; +} + +int cmCPackPKGGenerator::InitializeInternal() +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, "cmCPackPKGGenerator::Initialize()" + << std::endl); + + return this->Superclass::InitializeInternal(); +} + +std::string cmCPackPKGGenerator::GetPackageName( + const cmCPackComponent& component) +{ + if (component.ArchiveFile.empty()) { + std::string packagesDir = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + packagesDir += ".dummy"; + std::ostringstream out; + out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir) << "-" + << component.Name << ".pkg"; + return out.str(); + } else { + return component.ArchiveFile + ".pkg"; + } +} + +void cmCPackPKGGenerator::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. + std::ostringstream choiceOut; + cmXMLWriter xout(choiceOut, 1); + xout.StartElement("choices-outline"); + + // Emit the outline for the groups + std::map<std::string, cmCPackComponentGroup>::iterator groupIt; + for (groupIt = this->ComponentGroups.begin(); + groupIt != this->ComponentGroups.end(); ++groupIt) { + if (groupIt->second.ParentGroup == 0) { + CreateChoiceOutline(groupIt->second, xout); + } + } + + // 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) { + xout.StartElement("line"); + xout.Attribute("choice", compIt->first + "Choice"); + xout.Content(""); // Avoid self-closing tag. + xout.EndElement(); + } + } + if (!this->PostFlightComponent.Name.empty()) { + xout.StartElement("line"); + xout.Attribute("choice", PostFlightComponent.Name + "Choice"); + xout.Content(""); // Avoid self-closing tag. + xout.EndElement(); + } + xout.EndElement(); // choices-outline> + + // Create the actual choices + for (groupIt = this->ComponentGroups.begin(); + groupIt != this->ComponentGroups.end(); ++groupIt) { + CreateChoice(groupIt->second, xout); + } + for (compIt = this->Components.begin(); compIt != this->Components.end(); + ++compIt) { + CreateChoice(compIt->second, xout); + } + + if (!this->PostFlightComponent.Name.empty()) { + CreateChoice(PostFlightComponent, xout); + } + + 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 cmCPackPKGGenerator::CreateChoiceOutline( + const cmCPackComponentGroup& group, cmXMLWriter& xout) +{ + xout.StartElement("line"); + xout.Attribute("choice", group.Name + "Choice"); + std::vector<cmCPackComponentGroup*>::const_iterator groupIt; + for (groupIt = group.Subgroups.begin(); groupIt != group.Subgroups.end(); + ++groupIt) { + CreateChoiceOutline(**groupIt, xout); + } + + std::vector<cmCPackComponent*>::const_iterator compIt; + for (compIt = group.Components.begin(); compIt != group.Components.end(); + ++compIt) { + xout.StartElement("line"); + xout.Attribute("choice", (*compIt)->Name + "Choice"); + xout.Content(""); // Avoid self-closing tag. + xout.EndElement(); + } + xout.EndElement(); +} + +void cmCPackPKGGenerator::CreateChoice(const cmCPackComponentGroup& group, + cmXMLWriter& xout) +{ + xout.StartElement("choice"); + xout.Attribute("id", group.Name + "Choice"); + xout.Attribute("title", group.DisplayName); + xout.Attribute("start_selected", "true"); + xout.Attribute("start_enabled", "true"); + xout.Attribute("start_visible", "true"); + if (!group.Description.empty()) { + xout.Attribute("description", group.Description); + } + xout.EndElement(); +} + +void cmCPackPKGGenerator::CreateChoice(const cmCPackComponent& component, + cmXMLWriter& xout) +{ + std::string packageId = "com."; + packageId += this->GetOption("CPACK_PACKAGE_VENDOR"); + packageId += '.'; + packageId += this->GetOption("CPACK_PACKAGE_NAME"); + packageId += '.'; + packageId += component.Name; + + xout.StartElement("choice"); + xout.Attribute("id", component.Name + "Choice"); + xout.Attribute("title", component.DisplayName); + xout.Attribute( + "start_selected", + component.IsDisabledByDefault && !component.IsRequired ? "false" : "true"); + xout.Attribute("start_enabled", component.IsRequired ? "false" : "true"); + xout.Attribute("start_visible", component.IsHidden ? "false" : "true"); + if (!component.Description.empty()) { + xout.Attribute("description", 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. + std::ostringstream selected("my.choice.selected"); + std::set<const cmCPackComponent*> visited; + AddDependencyAttributes(component, visited, selected); + visited.clear(); + AddReverseDependencyAttributes(component, visited, selected); + xout.Attribute("selected", selected.str()); + } + xout.StartElement("pkg-ref"); + xout.Attribute("id", packageId); + xout.EndElement(); // pkg-ref + xout.EndElement(); // choice + + // Create a description of the package associated with this + // component. + std::string relativePackageLocation = "Contents/Packages/"; + relativePackageLocation += this->GetPackageName(component); + + // Determine the installed size of the package. + std::string dirName = this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + dirName += '/'; + dirName += component.Name; + dirName += this->GetOption("CPACK_PACKAGING_INSTALL_PREFIX"); + unsigned long installedSize = + component.GetInstalledSizeInKbytes(dirName.c_str()); + + xout.StartElement("pkg-ref"); + xout.Attribute("id", packageId); + xout.Attribute("version", this->GetOption("CPACK_PACKAGE_VERSION")); + xout.Attribute("installKBytes", installedSize); + xout.Attribute("auth", "Admin"); + xout.Attribute("onConclusion", "None"); + if (component.IsDownloaded) { + xout.Content(this->GetOption("CPACK_DOWNLOAD_SITE")); + xout.Content(this->GetPackageName(component)); + } else { + xout.Content("file:./"); + xout.Content(relativePackageLocation); + } + xout.EndElement(); // pkg-ref +} + +void cmCPackPKGGenerator::AddDependencyAttributes( + const cmCPackComponent& component, + std::set<const cmCPackComponent*>& visited, std::ostringstream& out) +{ + if (visited.find(&component) != visited.end()) { + return; + } + visited.insert(&component); + + std::vector<cmCPackComponent*>::const_iterator dependIt; + for (dependIt = component.Dependencies.begin(); + dependIt != component.Dependencies.end(); ++dependIt) { + out << " && choices['" << (*dependIt)->Name << "Choice'].selected"; + AddDependencyAttributes(**dependIt, visited, out); + } +} + +void cmCPackPKGGenerator::AddReverseDependencyAttributes( + const cmCPackComponent& component, + std::set<const cmCPackComponent*>& visited, std::ostringstream& out) +{ + if (visited.find(&component) != visited.end()) { + return; + } + visited.insert(&component); + + std::vector<cmCPackComponent*>::const_iterator dependIt; + for (dependIt = component.ReverseDependencies.begin(); + dependIt != component.ReverseDependencies.end(); ++dependIt) { + out << " || choices['" << (*dependIt)->Name << "Choice'].selected"; + AddReverseDependencyAttributes(**dependIt, visited, out); + } +} + +bool cmCPackPKGGenerator::CopyCreateResourceFile(const std::string& name, + const std::string& dirName) +{ + std::string uname = cmSystemTools::UpperCase(name); + std::string cpackVar = "CPACK_RESOURCE_FILE_" + uname; + const char* inFileName = this->GetOption(cpackVar.c_str()); + if (!inFileName) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "CPack option: " + << cpackVar.c_str() + << " not specified. It should point to " + << (!name.empty() ? name : "<empty>") << ".rtf, " << name + << ".html, or " << name << ".txt file" << std::endl); + return false; + } + if (!cmSystemTools::FileExists(inFileName)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find " + << (!name.empty() ? name : "<empty>") + << " resource file: " << inFileName << std::endl); + return false; + } + std::string ext = cmSystemTools::GetFilenameLastExtension(inFileName); + if (ext != ".rtfd" && ext != ".rtf" && ext != ".html" && ext != ".txt") { + cmCPackLogger( + cmCPackLog::LOG_ERROR, "Bad file extension specified: " + << ext + << ". Currently only .rtfd, .rtf, .html, and .txt files allowed." + << std::endl); + return false; + } + + std::string destFileName = dirName; + destFileName += '/'; + destFileName += name + ext; + + // Set this so that distribution.dist gets the right name (without + // the path). + this->SetOption(("CPACK_RESOURCE_FILE_" + uname + "_NOPATH").c_str(), + (name + ext).c_str()); + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Configure file: " << (inFileName ? inFileName : "(NULL)") + << " to " << destFileName << std::endl); + this->ConfigureFile(inFileName, destFileName.c_str()); + return true; +} + +bool cmCPackPKGGenerator::CopyResourcePlistFile(const std::string& name, + const char* outName) +{ + if (!outName) { + outName = name.c_str(); + } + + std::string inFName = "CPack."; + inFName += name; + inFName += ".in"; + std::string inFileName = this->FindTemplate(inFName.c_str()); + if (inFileName.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find input file: " << inFName << std::endl); + return false; + } + + std::string destFileName = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + destFileName += "/"; + destFileName += outName; + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Configure file: " + << inFileName << " to " << destFileName << std::endl); + this->ConfigureFile(inFileName.c_str(), destFileName.c_str()); + return true; +} + +int cmCPackPKGGenerator::CopyInstallScript(const std::string& resdir, + const std::string& script, + const std::string& name) +{ + std::string dst = resdir; + dst += "/"; + dst += name; + cmSystemTools::CopyFileAlways(script.c_str(), dst.c_str()); + cmSystemTools::SetPermissions(dst.c_str(), 0777); + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "copy script : " << script << "\ninto " << dst << std::endl); + + return 1; +} diff --git a/Source/CPack/cmCPackPKGGenerator.h b/Source/CPack/cmCPackPKGGenerator.h new file mode 100644 index 0000000..d2135cc --- /dev/null +++ b/Source/CPack/cmCPackPKGGenerator.h @@ -0,0 +1,96 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackPKGGenerator_h +#define cmCPackPKGGenerator_h + +#include "cmCPackGenerator.h" + +class cmCPackComponent; +class cmXMLWriter; + +/** \class cmCPackPKGGenerator + * \brief A generator for pkg files + * + */ +class cmCPackPKGGenerator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackPKGGenerator, cmCPackGenerator); + + /** + * Construct generator + */ + cmCPackPKGGenerator(); + virtual ~cmCPackPKGGenerator(); + + bool SupportsComponentInstallation() const CM_OVERRIDE; + +protected: + int InitializeInternal() CM_OVERRIDE; + const char* GetOutputPostfix() CM_OVERRIDE { return "darwin"; } + + // Copies or creates the resource file with the given name to the + // package or package staging directory dirName. The variable + // CPACK_RESOURCE_FILE_${NAME} (where ${NAME} is the uppercased + // version of name) specifies the input file to use for this file, + // which will be configured via ConfigureFile. + bool CopyCreateResourceFile(const std::string& name, + const std::string& dirName); + bool CopyResourcePlistFile(const std::string& name, const char* outName = 0); + + int CopyInstallScript(const std::string& resdir, const std::string& script, + const std::string& name); + + // 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); + + // 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, + std::set<const cmCPackComponent*>& visited, + std::ostringstream& out); + + // Subroutine of WriteDistributionFile that writes out the + // reverse dependency attributes for inter-component dependencies. + void AddReverseDependencyAttributes( + const cmCPackComponent& component, + std::set<const cmCPackComponent*>& visited, std::ostringstream& 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, + cmXMLWriter& xout); + + /// Create the "choice" XML element to describe a component group + /// for the installer GUI. + void CreateChoice(const cmCPackComponentGroup& group, cmXMLWriter& xout); + + /// Create the "choice" XML element to describe a component for the + /// installer GUI. + void CreateChoice(const cmCPackComponent& component, cmXMLWriter& xout); + + // The PostFlight component when creating a metapackage + cmCPackComponent PostFlightComponent; +}; + +#endif diff --git a/Source/CPack/cmCPackPackageMakerGenerator.cxx b/Source/CPack/cmCPackPackageMakerGenerator.cxx new file mode 100644 index 0000000..ce329ca --- /dev/null +++ b/Source/CPack/cmCPackPackageMakerGenerator.cxx @@ -0,0 +1,584 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmCPackPackageMakerGenerator.h" + +#include "cmCPackComponentGroup.h" +#include "cmCPackLog.h" +#include "cmGeneratedFileStream.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" +#include "cmXMLWriter.h" +#include "cmake.h" + +#include <cmsys/FStream.hxx> +#include <cmsys/Glob.hxx> +#include <cmsys/SystemTools.hxx> + +#include <assert.h> + +static inline unsigned int getVersion(unsigned int major, unsigned int minor) +{ + assert(major < 256 && minor < 256); + return ((major & 0xFF) << 16 | minor); +} + +cmCPackPackageMakerGenerator::cmCPackPackageMakerGenerator() +{ + this->PackageMakerVersion = 0.0; + this->PackageCompatibilityVersion = getVersion(10, 4); +} + +cmCPackPackageMakerGenerator::~cmCPackPackageMakerGenerator() +{ +} + +bool cmCPackPackageMakerGenerator::SupportsComponentInstallation() const +{ + return this->PackageCompatibilityVersion >= getVersion(10, 4); +} + +int cmCPackPackageMakerGenerator::PackageFiles() +{ + // TODO: Use toplevel + // It is used! Is this an obsolete comment? + + std::string resDir; // Where this package's resources will go. + std::string packageDirFileName = + this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + if (this->Components.empty()) { + packageDirFileName += ".pkg"; + resDir = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + resDir += "/Resources"; + } else { + packageDirFileName += ".mpkg"; + if (!cmsys::SystemTools::MakeDirectory(packageDirFileName.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "unable to create package directory " << packageDirFileName + << std::endl); + return 0; + } + + resDir = packageDirFileName; + resDir += "/Contents"; + if (!cmsys::SystemTools::MakeDirectory(resDir.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "unable to create package subdirectory " << resDir + << std::endl); + return 0; + } + + resDir += "/Resources"; + if (!cmsys::SystemTools::MakeDirectory(resDir.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "unable to create package subdirectory " << resDir + << std::endl); + return 0; + } + + resDir += "/en.lproj"; + } + + const char* preflight = this->GetOption("CPACK_PREFLIGHT_SCRIPT"); + const char* postflight = this->GetOption("CPACK_POSTFLIGHT_SCRIPT"); + const char* postupgrade = this->GetOption("CPACK_POSTUPGRADE_SCRIPT"); + + if (this->Components.empty()) { + // Create directory structure + std::string preflightDirName = resDir + "/PreFlight"; + std::string postflightDirName = resDir + "/PostFlight"; + // if preflight or postflight scripts not there create directories + // of the same name, I think this makes it work + if (!preflight) { + if (!cmsys::SystemTools::MakeDirectory(preflightDirName.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating installer directory: " + << preflightDirName << std::endl); + return 0; + } + } + if (!postflight) { + if (!cmsys::SystemTools::MakeDirectory(postflightDirName.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating installer directory: " + << postflightDirName << std::endl); + return 0; + } + } + // if preflight, postflight, or postupgrade are set + // then copy them into the resource directory and make + // them executable + if (preflight) { + this->CopyInstallScript(resDir.c_str(), preflight, "preflight"); + } + if (postflight) { + this->CopyInstallScript(resDir.c_str(), postflight, "postflight"); + } + if (postupgrade) { + this->CopyInstallScript(resDir.c_str(), postupgrade, "postupgrade"); + } + } else if (postflight) { + // create a postflight component to house the script + this->PostFlightComponent.Name = "PostFlight"; + this->PostFlightComponent.DisplayName = "PostFlight"; + this->PostFlightComponent.Description = "PostFlight"; + this->PostFlightComponent.IsHidden = true; + + // empty directory for pkg contents + std::string packageDir = toplevel + "/" + PostFlightComponent.Name; + if (!cmsys::SystemTools::MakeDirectory(packageDir.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating component packages directory: " + << packageDir << std::endl); + return 0; + } + + // create package + std::string packageFileDir = packageDirFileName + "/Contents/Packages/"; + if (!cmsys::SystemTools::MakeDirectory(packageFileDir.c_str())) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "Problem creating component PostFlight Packages directory: " + << packageFileDir << std::endl); + return 0; + } + std::string packageFile = + packageFileDir + this->GetPackageName(PostFlightComponent); + if (!this->GenerateComponentPackage( + packageFile.c_str(), packageDir.c_str(), PostFlightComponent)) { + return 0; + } + + // copy postflight script into resource directory of .pkg + std::string resourceDir = packageFile + "/Contents/Resources"; + this->CopyInstallScript(resourceDir.c_str(), postflight, "postflight"); + } + + if (!this->Components.empty()) { + // Create the directory where component packages will be built. + std::string basePackageDir = packageDirFileName; + basePackageDir += "/Contents/Packages"; + if (!cmsys::SystemTools::MakeDirectory(basePackageDir.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating component packages directory: " + << basePackageDir << std::endl); + return 0; + } + + // Create the directory where downloaded component packages will + // be placed. + 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"; + } + + // Create packages for each component + bool warnedAboutDownloadCompatibility = false; + + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt = this->Components.begin(); compIt != this->Components.end(); + ++compIt) { + std::string packageFile; + if (compIt->second.IsDownloaded) { + if (this->PackageCompatibilityVersion >= getVersion(10, 5) && + this->PackageMakerVersion >= 3.0) { + // Build this package within the upload directory. + packageFile = uploadDirectory; + + if (!cmSystemTools::FileExists(uploadDirectory.c_str())) { + if (!cmSystemTools::MakeDirectory(uploadDirectory.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Unable to create package upload directory " + << uploadDirectory << std::endl); + return 0; + } + } + } else if (!warnedAboutDownloadCompatibility) { + if (this->PackageCompatibilityVersion < getVersion(10, 5)) { + cmCPackLogger( + cmCPackLog::LOG_WARNING, + "CPack warning: please set CPACK_OSX_PACKAGE_VERSION to 10.5 " + "or greater enable downloaded packages. CPack will build a " + "non-downloaded package." + << std::endl); + } + + if (this->PackageMakerVersion < 3) { + cmCPackLogger(cmCPackLog::LOG_WARNING, + "CPack warning: unable to build downloaded " + "packages with PackageMaker versions prior " + "to 3.0. CPack will build a non-downloaded package." + << std::endl); + } + + warnedAboutDownloadCompatibility = true; + } + } + + if (packageFile.empty()) { + // Build this package within the overall distribution + // metapackage. + packageFile = basePackageDir; + + // We're not downloading this component, even if the user + // requested it. + compIt->second.IsDownloaded = false; + } + + 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", resDir.c_str()) || + !this->CopyCreateResourceFile("ReadMe", resDir.c_str()) || + !this->CopyCreateResourceFile("Welcome", resDir.c_str()) || + !this->CopyResourcePlistFile("Info.plist") || + !this->CopyResourcePlistFile("Description.plist")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying the resource files" + << std::endl); + return 0; + } + + if (this->Components.empty()) { + // Use PackageMaker to build the package. + std::ostringstream pkgCmd; + pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM") + << "\" -build -p \"" << packageDirFileName << "\""; + if (this->Components.empty()) { + pkgCmd << " -f \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + } else { + pkgCmd << " -mi \"" << this->GetOption("CPACK_TEMPORARY_DIRECTORY") + << "/packages/"; + } + pkgCmd << "\" -r \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") + << "/Resources\" -i \"" + << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") + << "/Info.plist\" -d \"" + << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") + << "/Description.plist\""; + if (this->PackageMakerVersion > 2.0) { + pkgCmd << " -v"; + } + if (!RunPackageMaker(pkgCmd.str().c_str(), packageDirFileName.c_str())) + return 0; + } else { + // We have built the package in place. Generate the + // distribution.dist file to describe it for the installer. + WriteDistributionFile(packageDirFileName.c_str()); + } + + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/hdiutilOutput.log"; + std::ostringstream dmgCmd; + dmgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM_DISK_IMAGE") + << "\" create -ov -format UDZO -srcfolder \"" << packageDirFileName + << "\" \"" << packageFileNames[0] << "\""; + std::string output; + int retVal = 1; + int numTries = 10; + bool res = false; + while (numTries > 0) { + res = + cmSystemTools::RunSingleCommand(dmgCmd.str().c_str(), &output, &output, + &retVal, 0, this->GeneratorVerbose, 0); + if (res && !retVal) { + numTries = -1; + break; + } + cmSystemTools::Delay(500); + numTries--; + } + if (!res || retVal) { + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << dmgCmd.str() << std::endl + << "# Output:" << std::endl + << output << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem running hdiutil command: " + << dmgCmd.str() << std::endl + << "Please check " << tmpFile << " for errors" + << std::endl); + return 0; + } + + return 1; +} + +int cmCPackPackageMakerGenerator::InitializeInternal() +{ + this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr"); + + // Starting with Xcode 4.3, PackageMaker is a separate app, and you + // can put it anywhere you want. So... use a variable for its location. + // People who put it in unexpected places can use the variable to tell + // us where it is. + // + // Use the following locations, in "most recent installation" order, + // to search for the PackageMaker app. Assume people who copy it into + // the new Xcode 4.3 app in "/Applications" will copy it into the nested + // Applications folder inside the Xcode bundle itself. Or directly in + // the "/Applications" directory. + // + // If found, save result in the CPACK_INSTALLER_PROGRAM variable. + + std::vector<std::string> paths; + paths.push_back("/Applications/Xcode.app/Contents/Applications" + "/PackageMaker.app/Contents/MacOS"); + paths.push_back("/Applications/Utilities" + "/PackageMaker.app/Contents/MacOS"); + paths.push_back("/Applications" + "/PackageMaker.app/Contents/MacOS"); + paths.push_back("/Developer/Applications/Utilities" + "/PackageMaker.app/Contents/MacOS"); + paths.push_back("/Developer/Applications" + "/PackageMaker.app/Contents/MacOS"); + + std::string pkgPath; + const char* inst_program = this->GetOption("CPACK_INSTALLER_PROGRAM"); + if (inst_program && *inst_program) { + pkgPath = inst_program; + } else { + pkgPath = cmSystemTools::FindProgram("PackageMaker", paths, false); + if (pkgPath.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find PackageMaker compiler" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM", pkgPath.c_str()); + } + + // Get path to the real PackageMaker, not a symlink: + pkgPath = cmSystemTools::GetRealPath(pkgPath.c_str()); + // Up from there to find the version.plist file in the "Contents" dir: + std::string contents_dir; + contents_dir = cmSystemTools::GetFilenamePath(pkgPath); + contents_dir = cmSystemTools::GetFilenamePath(contents_dir); + + std::string versionFile = contents_dir + "/version.plist"; + + if (!cmSystemTools::FileExists(versionFile.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find PackageMaker compiler version file: " + << versionFile << std::endl); + return 0; + } + + cmsys::ifstream ifs(versionFile.c_str()); + if (!ifs) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot open PackageMaker compiler version file" + << std::endl); + return 0; + } + + // Check the PackageMaker version + cmsys::RegularExpression rexKey("<key>CFBundleShortVersionString</key>"); + cmsys::RegularExpression rexVersion("<string>([0-9]+.[0-9.]+)</string>"); + std::string line; + bool foundKey = false; + while (cmSystemTools::GetLineFromStream(ifs, line)) { + if (rexKey.find(line)) { + foundKey = true; + break; + } + } + if (!foundKey) { + cmCPackLogger( + cmCPackLog::LOG_ERROR, + "Cannot find CFBundleShortVersionString in the PackageMaker compiler " + "version file" + << std::endl); + return 0; + } + if (!cmSystemTools::GetLineFromStream(ifs, line) || !rexVersion.find(line)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem reading the PackageMaker compiler version file: " + << versionFile << std::endl); + return 0; + } + this->PackageMakerVersion = atof(rexVersion.match(1).c_str()); + if (this->PackageMakerVersion < 1.0) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Require PackageMaker 1.0 or higher" + << std::endl); + return 0; + } + cmCPackLogger(cmCPackLog::LOG_DEBUG, "PackageMaker version is: " + << this->PackageMakerVersion << std::endl); + + // Determine the package compatibility version. If it wasn't + // specified by the user, we define it based on which features the + // user requested. + const char* packageCompat = this->GetOption("CPACK_OSX_PACKAGE_VERSION"); + if (packageCompat && *packageCompat) { + unsigned int majorVersion = 10; + unsigned int minorVersion = 5; + int res = sscanf(packageCompat, "%u.%u", &majorVersion, &minorVersion); + if (res == 2) { + this->PackageCompatibilityVersion = + getVersion(majorVersion, minorVersion); + } + } else if (this->GetOption("CPACK_DOWNLOAD_SITE")) { + this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.5"); + this->PackageCompatibilityVersion = getVersion(10, 5); + } else if (this->GetOption("CPACK_COMPONENTS_ALL")) { + this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.4"); + this->PackageCompatibilityVersion = getVersion(10, 4); + } else { + this->SetOption("CPACK_OSX_PACKAGE_VERSION", "10.3"); + this->PackageCompatibilityVersion = getVersion(10, 3); + } + + std::vector<std::string> no_paths; + pkgPath = cmSystemTools::FindProgram("hdiutil", no_paths, false); + if (pkgPath.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find hdiutil compiler" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_INSTALLER_PROGRAM_DISK_IMAGE", + pkgPath.c_str()); + + return this->Superclass::InitializeInternal(); +} + +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, &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 << std::endl; + cmCPackLogger( + cmCPackLog::LOG_ERROR, "Problem running PackageMaker command: " + << command << std::endl + << "Please check " << tmpFile << " 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; +} + +bool cmCPackPackageMakerGenerator::GenerateComponentPackage( + const char* packageFile, const char* packageDir, + const cmCPackComponent& component) +{ + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "- Building component package: " + << packageFile << std::endl); + + // The command that will be used to run PackageMaker + std::ostringstream pkgCmd; + + if (this->PackageCompatibilityVersion < getVersion(10, 5) || + this->PackageMakerVersion < 3.0) { + // Create Description.plist and Info.plist files for normal Mac OS + // X packages, which work on Mac OS X 10.3 and newer. + std::string descriptionFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + descriptionFile += '/' + component.Name + "-Description.plist"; + cmsys::ofstream out(descriptionFile.c_str()); + cmXMLWriter xout(out); + xout.StartDocument(); + xout.Doctype("plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\"" + "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\""); + xout.StartElement("plist"); + xout.Attribute("version", "1.4"); + xout.StartElement("dict"); + xout.Element("key", "IFPkgDescriptionTitle"); + xout.Element("string", component.DisplayName); + xout.Element("key", "IFPkgDescriptionVersion"); + xout.Element("string", this->GetOption("CPACK_PACKAGE_VERSION")); + xout.Element("key", "IFPkgDescriptionDescription"); + xout.Element("string", component.Description); + xout.EndElement(); // dict + xout.EndElement(); // plist + xout.EndDocument(); + 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; + } + + pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM") + << "\" -build -p \"" << packageFile << "\"" + << " -f \"" << packageDir << "\"" + << " -i \"" << this->GetOption("CPACK_TOPLEVEL_DIRECTORY") << "/" + << infoFileName << "\"" + << " -d \"" << descriptionFile << "\""; + } else { + // Create a "flat" package on Mac OS X 10.5 and newer. Flat + // packages are stored in a single file, rather than a directory + // like normal packages, and can be downloaded by the installer + // on-the-fly in Mac OS X 10.5 or newer. Thus, we need to create + // flat packages when the packages will be downloaded on the fly. + std::string pkgId = "com."; + pkgId += this->GetOption("CPACK_PACKAGE_VENDOR"); + pkgId += '.'; + pkgId += this->GetOption("CPACK_PACKAGE_NAME"); + pkgId += '.'; + pkgId += component.Name; + + pkgCmd << "\"" << this->GetOption("CPACK_INSTALLER_PROGRAM") + << "\" --root \"" << packageDir << "\"" + << " --id " << pkgId << " --target " + << this->GetOption("CPACK_OSX_PACKAGE_VERSION") << " --out \"" + << packageFile << "\""; + } + + // Run PackageMaker + return RunPackageMaker(pkgCmd.str().c_str(), packageFile); +} diff --git a/Source/CPack/cmCPackPackageMakerGenerator.h b/Source/CPack/cmCPackPackageMakerGenerator.h new file mode 100644 index 0000000..d1314a4 --- /dev/null +++ b/Source/CPack/cmCPackPackageMakerGenerator.h @@ -0,0 +1,60 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackPackageMakerGenerator_h +#define cmCPackPackageMakerGenerator_h + +#include "cmCPackPKGGenerator.h" + +class cmCPackComponent; + +/** \class cmCPackPackageMakerGenerator + * \brief A generator for PackageMaker files + * + * http://developer.apple.com/documentation/Darwin + * /Reference/ManPages/man1/packagemaker.1.html + */ +class cmCPackPackageMakerGenerator : public cmCPackPKGGenerator +{ +public: + cmCPackTypeMacro(cmCPackPackageMakerGenerator, cmCPackPKGGenerator); + + /** + * Construct generator + */ + cmCPackPackageMakerGenerator(); + virtual ~cmCPackPackageMakerGenerator(); + bool SupportsComponentInstallation() const CM_OVERRIDE; + +protected: + int InitializeInternal() CM_OVERRIDE; + int PackageFiles() CM_OVERRIDE; + const char* GetOutputExtension() CM_OVERRIDE { return ".dmg"; } + + // 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); + + // 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); + + double PackageMakerVersion; + unsigned int PackageCompatibilityVersion; +}; + +#endif diff --git a/Source/CPack/cmCPackProductBuildGenerator.cxx b/Source/CPack/cmCPackProductBuildGenerator.cxx new file mode 100644 index 0000000..c2a13d0 --- /dev/null +++ b/Source/CPack/cmCPackProductBuildGenerator.cxx @@ -0,0 +1,231 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmCPackProductBuildGenerator.h" + +#include "cmCPackComponentGroup.h" +#include "cmCPackLog.h" +#include "cmGeneratedFileStream.h" +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" +#include "cmake.h" + +#include <cmsys/Glob.hxx> +#include <cmsys/SystemTools.hxx> + +cmCPackProductBuildGenerator::cmCPackProductBuildGenerator() +{ + this->componentPackageMethod = ONE_PACKAGE; +} + +cmCPackProductBuildGenerator::~cmCPackProductBuildGenerator() +{ +} + +int cmCPackProductBuildGenerator::PackageFiles() +{ + // TODO: Use toplevel + // It is used! Is this an obsolete comment? + + std::string packageDirFileName = + this->GetOption("CPACK_TEMPORARY_DIRECTORY"); + + // Create the directory where component packages will be built. + std::string basePackageDir = packageDirFileName; + basePackageDir += "/Contents/Packages"; + if (!cmsys::SystemTools::MakeDirectory(basePackageDir.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating component packages directory: " + << basePackageDir << std::endl); + return 0; + } + + if (!this->Components.empty()) { + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt = this->Components.begin(); compIt != this->Components.end(); + ++compIt) { + std::string packageDir = toplevel; + packageDir += '/'; + packageDir += compIt->first; + if (!this->GenerateComponentPackage(basePackageDir, + GetPackageName(compIt->second), + packageDir, &compIt->second)) { + return 0; + } + } + } else { + if (!this->GenerateComponentPackage(basePackageDir, + this->GetOption("CPACK_PACKAGE_NAME"), + toplevel, NULL)) { + return 0; + } + } + + // Copy or create all of the resource files we need. + std::string resDir = packageDirFileName + "/Contents"; + if (!this->CopyCreateResourceFile("License", resDir.c_str()) || + !this->CopyCreateResourceFile("ReadMe", resDir.c_str()) || + !this->CopyCreateResourceFile("Welcome", resDir.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Problem copying the resource files" + << std::endl); + return 0; + } + + // combine package(s) into a distribution + WriteDistributionFile(packageDirFileName.c_str()); + std::ostringstream pkgCmd; + + std::string version = this->GetOption("CPACK_PACKAGE_VERSION"); + std::string productbuild = this->GetOption("CPACK_COMMAND_PRODUCTBUILD"); + + pkgCmd << productbuild << " --distribution \"" << packageDirFileName + << "/Contents/distribution.dist\"" + << " --package-path \"" << packageDirFileName << "/Contents/Packages" + << "\"" + << " --resources \"" << resDir << "\"" + << " --version \"" << version << "\"" + << " \"" << packageFileNames[0] << "\""; + + // Run ProductBuild + return RunProductBuild(pkgCmd.str()); +} + +int cmCPackProductBuildGenerator::InitializeInternal() +{ + this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/Applications"); + + std::vector<std::string> no_paths; + std::string program = + cmSystemTools::FindProgram("pkgbuild", no_paths, false); + if (program.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find pkgbuild executable" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_COMMAND_PKGBUILD", program.c_str()); + + program = cmSystemTools::FindProgram("productbuild", no_paths, false); + if (program.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Cannot find productbuild executable" + << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_COMMAND_PRODUCTBUILD", program.c_str()); + + return this->Superclass::InitializeInternal(); +} + +bool cmCPackProductBuildGenerator::RunProductBuild(const std::string& command) +{ + std::string tmpFile = this->GetOption("CPACK_TOPLEVEL_DIRECTORY"); + tmpFile += "/ProductBuildOutput.log"; + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << command << std::endl); + std::string output, error_output; + int retVal = 1; + bool res = + cmSystemTools::RunSingleCommand(command.c_str(), &output, &error_output, + &retVal, 0, this->GeneratorVerbose, 0); + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Done running command" << std::endl); + if (!res || retVal) { + cmGeneratedFileStream ofs(tmpFile.c_str()); + ofs << "# Run command: " << command << std::endl + << "# Output:" << std::endl + << output << std::endl; + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem running command: " << command << std::endl + << "Please check " << tmpFile + << " for errors" << std::endl); + return false; + } + return true; +} + +bool cmCPackProductBuildGenerator::GenerateComponentPackage( + const std::string& packageFileDir, const std::string& packageFileName, + const std::string& packageDir, const cmCPackComponent* component) +{ + std::string packageFile = packageFileDir; + packageFile += '/'; + packageFile += packageFileName; + + cmCPackLogger(cmCPackLog::LOG_OUTPUT, "- Building component package: " + << packageFile << std::endl); + + const char* comp_name = component ? component->Name.c_str() : NULL; + + const char* preflight = this->GetComponentScript("PREFLIGHT", comp_name); + const char* postflight = this->GetComponentScript("POSTFLIGHT", comp_name); + + std::string resDir = packageFileDir; + if (component) { + resDir += "/"; + resDir += component->Name; + } + std::string scriptDir = resDir + "/scripts"; + + if (!cmsys::SystemTools::MakeDirectory(scriptDir.c_str())) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Problem creating installer directory: " << scriptDir + << std::endl); + return 0; + } + + // if preflight, postflight, or postupgrade are set + // then copy them into the script directory and make + // them executable + if (preflight) { + this->CopyInstallScript(scriptDir.c_str(), preflight, "preinstall"); + } + if (postflight) { + this->CopyInstallScript(scriptDir.c_str(), postflight, "postinstall"); + } + + // The command that will be used to run ProductBuild + std::ostringstream pkgCmd; + + std::string pkgId = "com."; + pkgId += this->GetOption("CPACK_PACKAGE_VENDOR"); + pkgId += '.'; + pkgId += this->GetOption("CPACK_PACKAGE_NAME"); + if (component) { + pkgId += '.'; + pkgId += component->Name; + } + + std::string version = this->GetOption("CPACK_PACKAGE_VERSION"); + std::string pkgbuild = this->GetOption("CPACK_COMMAND_PKGBUILD"); + + pkgCmd << pkgbuild << " --root \"" << packageDir << "\"" + << " --identifier \"" << pkgId << "\"" + << " --scripts \"" << scriptDir << "\"" + << " --version \"" << version << "\"" + << " --install-location \"/\"" + << " \"" << packageFile << "\""; + + // Run ProductBuild + return RunProductBuild(pkgCmd.str()); +} + +const char* cmCPackProductBuildGenerator::GetComponentScript( + const char* script, const char* component_name) +{ + std::string scriptname = std::string("CPACK_") + script + "_"; + if (component_name) { + scriptname += cmSystemTools::UpperCase(component_name); + scriptname += "_"; + } + scriptname += "SCRIPT"; + + return this->GetOption(scriptname); +} diff --git a/Source/CPack/cmCPackProductBuildGenerator.h b/Source/CPack/cmCPackProductBuildGenerator.h new file mode 100644 index 0000000..fd2c090 --- /dev/null +++ b/Source/CPack/cmCPackProductBuildGenerator.h @@ -0,0 +1,58 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackProductBuildGenerator_h +#define cmCPackProductBuildGenerator_h + +#include "cmCPackPKGGenerator.h" + +class cmCPackComponent; + +/** \class cmCPackProductBuildGenerator + * \brief A generator for ProductBuild files + * + */ +class cmCPackProductBuildGenerator : public cmCPackPKGGenerator +{ +public: + cmCPackTypeMacro(cmCPackProductBuildGenerator, cmCPackPKGGenerator); + + /** + * Construct generator + */ + cmCPackProductBuildGenerator(); + virtual ~cmCPackProductBuildGenerator(); + +protected: + int InitializeInternal() CM_OVERRIDE; + int PackageFiles() CM_OVERRIDE; + const char* GetOutputExtension() CM_OVERRIDE { return ".pkg"; } + + // Run ProductBuild with the given command line, which will (if + // successful) produce the given package file. Returns true if + // ProductBuild succeeds, false otherwise. + bool RunProductBuild(const std::string& command); + + // 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 std::string& packageFileDir, + const std::string& packageFileName, + const std::string& packageDir, + const cmCPackComponent* component); + + const char* GetComponentScript(const char* script, + const char* script_component); +}; + +#endif diff --git a/Source/CPack/cmCPackRPMGenerator.cxx b/Source/CPack/cmCPackRPMGenerator.cxx new file mode 100644 index 0000000..2568d17 --- /dev/null +++ b/Source/CPack/cmCPackRPMGenerator.cxx @@ -0,0 +1,253 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmCPackRPMGenerator.h" + +#include "cmCPackLog.h" +#include "cmSystemTools.h" + +cmCPackRPMGenerator::cmCPackRPMGenerator() +{ +} + +cmCPackRPMGenerator::~cmCPackRPMGenerator() +{ +} + +int cmCPackRPMGenerator::InitializeInternal() +{ + this->SetOptionIfNotSet("CPACK_PACKAGING_INSTALL_PREFIX", "/usr"); + if (cmSystemTools::IsOff(this->GetOption("CPACK_SET_DESTDIR"))) { + this->SetOption("CPACK_SET_DESTDIR", "I_ON"); + } + /* Replace space in CPACK_PACKAGE_NAME in order to avoid + * rpmbuild scream on unwanted space in filename issue + * Moreover RPM file do not usually embed space in filename + */ + if (this->GetOption("CPACK_PACKAGE_NAME")) { + std::string packageName = this->GetOption("CPACK_PACKAGE_NAME"); + std::replace(packageName.begin(), packageName.end(), ' ', '-'); + this->SetOption("CPACK_PACKAGE_NAME", packageName.c_str()); + } + /* same for CPACK_PACKAGE_FILE_NAME */ + if (this->GetOption("CPACK_PACKAGE_FILE_NAME")) { + std::string packageName = this->GetOption("CPACK_PACKAGE_FILE_NAME"); + std::replace(packageName.begin(), packageName.end(), ' ', '-'); + this->SetOption("CPACK_PACKAGE_FILE_NAME", packageName.c_str()); + } + return this->Superclass::InitializeInternal(); +} + +void cmCPackRPMGenerator::AddGeneratedPackageNames() +{ + // add the generated packages to package file names list + std::string fileNames(this->GetOption("GEN_CPACK_OUTPUT_FILES")); + const char sep = ';'; + std::string::size_type pos1 = 0; + std::string::size_type pos2 = fileNames.find(sep, pos1 + 1); + while (pos2 != std::string::npos) { + packageFileNames.push_back(fileNames.substr(pos1, pos2 - pos1)); + pos1 = pos2 + 1; + pos2 = fileNames.find(sep, pos1 + 1); + } + packageFileNames.push_back(fileNames.substr(pos1, pos2 - pos1)); +} + +int cmCPackRPMGenerator::PackageOnePack(std::string const& initialToplevel, + std::string const& packageName) +{ + int retval = 1; + // Begin the archive for this pack + std::string localToplevel(initialToplevel); + std::string packageFileName(cmSystemTools::GetParentDirectory(toplevel)); + std::string outputFileName( + GetComponentPackageFileName(this->GetOption("CPACK_PACKAGE_FILE_NAME"), + packageName, true) + + this->GetOutputExtension()); + + localToplevel += "/" + packageName; + /* replace the TEMP DIRECTORY with the component one */ + this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel.c_str()); + packageFileName += "/" + outputFileName; + /* replace proposed CPACK_OUTPUT_FILE_NAME */ + this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName.c_str()); + /* replace the TEMPORARY package file name */ + this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", + packageFileName.c_str()); + // Tell CPackRPM.cmake the name of the component NAME. + this->SetOption("CPACK_RPM_PACKAGE_COMPONENT", packageName.c_str()); + // Tell CPackRPM.cmake the path where the component is. + std::string component_path = "/"; + component_path += packageName; + this->SetOption("CPACK_RPM_PACKAGE_COMPONENT_PART_PATH", + component_path.c_str()); + if (!this->ReadListFile("CPackRPM.cmake")) { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error while execution CPackRPM.cmake" + << std::endl); + retval = 0; + } + + return retval; +} + +int cmCPackRPMGenerator::PackageComponents(bool ignoreGroup) +{ + int retval = 1; + /* Reset package file name list it will be populated during the + * component packaging run*/ + packageFileNames.clear(); + std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); + + // The default behavior is to have one package by component group + // unless CPACK_COMPONENTS_IGNORE_GROUP is specified. + if (!ignoreGroup) { + std::map<std::string, cmCPackComponentGroup>::iterator compGIt; + for (compGIt = this->ComponentGroups.begin(); + compGIt != this->ComponentGroups.end(); ++compGIt) { + cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Packaging component group: " + << compGIt->first << std::endl); + retval &= PackageOnePack(initialTopLevel, compGIt->first); + } + // Handle Orphan components (components not belonging to any groups) + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt = this->Components.begin(); compIt != this->Components.end(); + ++compIt) { + // Does the component belong to a group? + if (compIt->second.Group == CM_NULLPTR) { + cmCPackLogger( + cmCPackLog::LOG_VERBOSE, "Component <" + << compIt->second.Name + << "> does not belong to any group, package it separately." + << std::endl); + retval &= PackageOnePack(initialTopLevel, compIt->first); + } + } + } + // CPACK_COMPONENTS_IGNORE_GROUPS is set + // We build 1 package per component + else { + std::map<std::string, cmCPackComponent>::iterator compIt; + for (compIt = this->Components.begin(); compIt != this->Components.end(); + ++compIt) { + retval &= PackageOnePack(initialTopLevel, compIt->first); + } + } + + if (retval) { + AddGeneratedPackageNames(); + } + + return retval; +} + +int cmCPackRPMGenerator::PackageComponentsAllInOne( + const std::string& compInstDirName) +{ + int retval = 1; + /* Reset package file name list it will be populated during the + * component packaging run*/ + packageFileNames.clear(); + std::string initialTopLevel(this->GetOption("CPACK_TEMPORARY_DIRECTORY")); + + cmCPackLogger(cmCPackLog::LOG_VERBOSE, + "Packaging all groups in one package..." + "(CPACK_COMPONENTS_ALL_[GROUPS_]IN_ONE_PACKAGE is set)" + << std::endl); + + // The ALL GROUPS in ONE package case + std::string localToplevel(initialTopLevel); + std::string packageFileName(cmSystemTools::GetParentDirectory(toplevel)); + std::string outputFileName( + std::string(this->GetOption("CPACK_PACKAGE_FILE_NAME")) + + this->GetOutputExtension()); + // all GROUP in one vs all COMPONENT in one + localToplevel += "/" + compInstDirName; + + /* replace the TEMP DIRECTORY with the component one */ + this->SetOption("CPACK_TEMPORARY_DIRECTORY", localToplevel.c_str()); + packageFileName += "/" + outputFileName; + /* replace proposed CPACK_OUTPUT_FILE_NAME */ + this->SetOption("CPACK_OUTPUT_FILE_NAME", outputFileName.c_str()); + /* replace the TEMPORARY package file name */ + this->SetOption("CPACK_TEMPORARY_PACKAGE_FILE_NAME", + packageFileName.c_str()); + + if (!compInstDirName.empty()) { + // Tell CPackRPM.cmake the path where the component is. + std::string component_path = "/"; + component_path += compInstDirName; + this->SetOption("CPACK_RPM_PACKAGE_COMPONENT_PART_PATH", + component_path.c_str()); + } + + if (this->ReadListFile("CPackRPM.cmake")) { + AddGeneratedPackageNames(); + } else { + cmCPackLogger(cmCPackLog::LOG_ERROR, "Error while execution CPackRPM.cmake" + << std::endl); + retval = 0; + } + + return retval; +} + +int cmCPackRPMGenerator::PackageFiles() +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Toplevel: " << toplevel << std::endl); + + /* Are we in the component packaging case */ + if (WantsComponentInstallation()) { + // CASE 1 : COMPONENT ALL-IN-ONE package + // If ALL COMPONENTS in ONE package has been requested + // then the package file is unique and should be open here. + if (componentPackageMethod == ONE_PACKAGE) { + return PackageComponentsAllInOne("ALL_COMPONENTS_IN_ONE"); + } + // CASE 2 : COMPONENT CLASSICAL package(s) (i.e. not all-in-one) + // There will be 1 package for each component group + // however one may require to ignore component group and + // in this case you'll get 1 package for each component. + else { + return PackageComponents(componentPackageMethod == + ONE_PACKAGE_PER_COMPONENT); + } + } + // CASE 3 : NON COMPONENT package. + else { + return PackageComponentsAllInOne(""); + } +} + +bool cmCPackRPMGenerator::SupportsComponentInstallation() const +{ + return IsOn("CPACK_RPM_COMPONENT_INSTALL"); +} + +std::string cmCPackRPMGenerator::GetComponentInstallDirNameSuffix( + const std::string& componentName) +{ + if (componentPackageMethod == ONE_PACKAGE_PER_COMPONENT) { + return componentName; + } + + if (componentPackageMethod == ONE_PACKAGE) { + return std::string("ALL_COMPONENTS_IN_ONE"); + } + // We have to find the name of the COMPONENT GROUP + // the current COMPONENT belongs to. + std::string groupVar = + "CPACK_COMPONENT_" + cmSystemTools::UpperCase(componentName) + "_GROUP"; + if (CM_NULLPTR != GetOption(groupVar)) { + return std::string(GetOption(groupVar)); + } else { + return componentName; + } +} diff --git a/Source/CPack/cmCPackRPMGenerator.h b/Source/CPack/cmCPackRPMGenerator.h new file mode 100644 index 0000000..4baef08 --- /dev/null +++ b/Source/CPack/cmCPackRPMGenerator.h @@ -0,0 +1,78 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackRPMGenerator_h +#define cmCPackRPMGenerator_h + +#include "cmCPackGenerator.h" + +/** \class cmCPackRPMGenerator + * \brief A generator for RPM packages + * The idea of the CPack RPM generator is to use + * as minimal C++ code as possible. + * Ideally the C++ part of the CPack RPM generator + * will only 'execute' (aka ->ReadListFile) several + * CMake macros files. + */ +class cmCPackRPMGenerator : public cmCPackGenerator +{ +public: + cmCPackTypeMacro(cmCPackRPMGenerator, cmCPackGenerator); + + /** + * Construct generator + */ + cmCPackRPMGenerator(); + ~cmCPackRPMGenerator() CM_OVERRIDE; + + static bool CanGenerate() + { +#ifdef __APPLE__ + // on MacOS enable CPackRPM iff rpmbuild is found + std::vector<std::string> locations; + locations.push_back("/sw/bin"); // Fink + locations.push_back("/opt/local/bin"); // MacPorts + return cmSystemTools::FindProgram("rpmbuild") != "" ? true : false; +#else + // legacy behavior on other systems + return true; +#endif + } + +protected: + int InitializeInternal() CM_OVERRIDE; + int PackageFiles() CM_OVERRIDE; + /** + * This method factors out the work done in component packaging case. + */ + int PackageOnePack(std::string const& initialToplevel, + std::string const& packageName); + /** + * The method used to package files when component + * install is used. This will create one + * archive for each component group. + */ + int PackageComponents(bool ignoreGroup); + /** + * Special case of component install where all + * components will be put in a single installer. + */ + int PackageComponentsAllInOne(const std::string& compInstDirName); + const char* GetOutputExtension() CM_OVERRIDE { return ".rpm"; } + bool SupportsComponentInstallation() const CM_OVERRIDE; + std::string GetComponentInstallDirNameSuffix( + const std::string& componentName) CM_OVERRIDE; + + void AddGeneratedPackageNames(); +}; + +#endif diff --git a/Source/CPack/cmCPackSTGZGenerator.cxx b/Source/CPack/cmCPackSTGZGenerator.cxx new file mode 100644 index 0000000..4d07a7e --- /dev/null +++ b/Source/CPack/cmCPackSTGZGenerator.cxx @@ -0,0 +1,125 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackSTGZGenerator.h" + +#include "cmCPackLog.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" +#include "cmake.h" + +#include <cmsys/FStream.hxx> + +#include <sys/types.h> +// include sys/stat.h after sys/types.h +#include <sys/stat.h> + +cmCPackSTGZGenerator::cmCPackSTGZGenerator() +{ +} + +cmCPackSTGZGenerator::~cmCPackSTGZGenerator() +{ +} + +int cmCPackSTGZGenerator::InitializeInternal() +{ + this->SetOptionIfNotSet("CPACK_INCLUDE_TOPLEVEL_DIRECTORY", "0"); + + std::string inFile = this->FindTemplate("CPack.STGZ_Header.sh.in"); + if (inFile.empty()) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot find template file: " << inFile << std::endl); + return 0; + } + this->SetOptionIfNotSet("CPACK_STGZ_HEADER_FILE", inFile.c_str()); + this->SetOptionIfNotSet("CPACK_AT_SIGN", "@"); + + return this->Superclass::InitializeInternal(); +} + +int cmCPackSTGZGenerator::PackageFiles() +{ + bool retval = true; + if (!this->Superclass::PackageFiles()) { + return 0; + } + + /* TGZ generator (our Superclass) may + * have generated several packages (component packaging) + * so we must iterate over generated packages. + */ + for (std::vector<std::string>::iterator it = packageFileNames.begin(); + it != packageFileNames.end(); ++it) { + retval &= cmSystemTools::SetPermissions((*it).c_str(), +#if defined(_MSC_VER) || defined(__MINGW32__) + S_IREAD | S_IWRITE | S_IEXEC +#else + S_IRUSR | S_IWUSR | S_IXUSR | + S_IRGRP | S_IWGRP | S_IXGRP | + S_IROTH | S_IWOTH | S_IXOTH +#endif + ); + } + return retval; +} + +int cmCPackSTGZGenerator::GenerateHeader(std::ostream* os) +{ + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Writing header" << std::endl); + std::ostringstream str; + int counter = 0; + + std::string inLicFile = this->GetOption("CPACK_RESOURCE_FILE_LICENSE"); + std::string line; + cmsys::ifstream ilfs(inLicFile.c_str()); + std::string licenseText; + while (cmSystemTools::GetLineFromStream(ilfs, line)) { + licenseText += line + "\n"; + } + this->SetOptionIfNotSet("CPACK_RESOURCE_FILE_LICENSE_CONTENT", + licenseText.c_str()); + + const char headerLengthTag[] = "###CPACK_HEADER_LENGTH###"; + + // Create the header + std::string inFile = this->GetOption("CPACK_STGZ_HEADER_FILE"); + cmsys::ifstream ifs(inFile.c_str()); + std::string packageHeaderText; + while (cmSystemTools::GetLineFromStream(ifs, line)) { + packageHeaderText += line + "\n"; + } + + // Configure in the values + std::string res; + this->ConfigureString(packageHeaderText, res); + + // Count the lines + const char* ptr = res.c_str(); + while (*ptr) { + if (*ptr == '\n') { + counter++; + } + ++ptr; + } + counter++; + cmCPackLogger(cmCPackLog::LOG_DEBUG, "Number of lines: " << counter + << std::endl); + char buffer[1024]; + sprintf(buffer, "%d", counter); + cmSystemTools::ReplaceString(res, headerLengthTag, buffer); + + // Write in file + *os << res; + return this->Superclass::GenerateHeader(os); +} diff --git a/Source/CPack/cmCPackSTGZGenerator.h b/Source/CPack/cmCPackSTGZGenerator.h new file mode 100644 index 0000000..94cc8aa --- /dev/null +++ b/Source/CPack/cmCPackSTGZGenerator.h @@ -0,0 +1,40 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackSTGZGenerator_h +#define cmCPackSTGZGenerator_h + +#include "cmCPackTGZGenerator.h" + +/** \class cmCPackSTGZGenerator + * \brief A generator for Self extractable TGZ files + * + */ +class cmCPackSTGZGenerator : public cmCPackTGZGenerator +{ +public: + cmCPackTypeMacro(cmCPackSTGZGenerator, cmCPackTGZGenerator); + + /** + * Construct generator + */ + cmCPackSTGZGenerator(); + ~cmCPackSTGZGenerator() CM_OVERRIDE; + +protected: + int PackageFiles() CM_OVERRIDE; + int InitializeInternal() CM_OVERRIDE; + int GenerateHeader(std::ostream* os) CM_OVERRIDE; + const char* GetOutputExtension() CM_OVERRIDE { return ".sh"; } +}; + +#endif diff --git a/Source/CPack/cmCPackTGZGenerator.cxx b/Source/CPack/cmCPackTGZGenerator.cxx new file mode 100644 index 0000000..7c5c245 --- /dev/null +++ b/Source/CPack/cmCPackTGZGenerator.cxx @@ -0,0 +1,22 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackTGZGenerator.h" + +cmCPackTGZGenerator::cmCPackTGZGenerator() + : cmCPackArchiveGenerator(cmArchiveWrite::CompressGZip, "paxr") +{ +} + +cmCPackTGZGenerator::~cmCPackTGZGenerator() +{ +} diff --git a/Source/CPack/cmCPackTGZGenerator.h b/Source/CPack/cmCPackTGZGenerator.h new file mode 100644 index 0000000..cb7620c --- /dev/null +++ b/Source/CPack/cmCPackTGZGenerator.h @@ -0,0 +1,36 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackTGZGenerator_h +#define cmCPackTGZGenerator_h + +#include "cmCPackArchiveGenerator.h" + +/** \class cmCPackTGZGenerator + * \brief A generator for TGZ files + * + */ +class cmCPackTGZGenerator : public cmCPackArchiveGenerator +{ +public: + cmCPackTypeMacro(cmCPackTGZGenerator, cmCPackArchiveGenerator); + /** + * Construct generator + */ + cmCPackTGZGenerator(); + ~cmCPackTGZGenerator() CM_OVERRIDE; + +protected: + const char* GetOutputExtension() CM_OVERRIDE { return ".tar.gz"; } +}; + +#endif diff --git a/Source/CPack/cmCPackTXZGenerator.cxx b/Source/CPack/cmCPackTXZGenerator.cxx new file mode 100644 index 0000000..d17a164 --- /dev/null +++ b/Source/CPack/cmCPackTXZGenerator.cxx @@ -0,0 +1,22 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackTXZGenerator.h" + +cmCPackTXZGenerator::cmCPackTXZGenerator() + : cmCPackArchiveGenerator(cmArchiveWrite::CompressXZ, "paxr") +{ +} + +cmCPackTXZGenerator::~cmCPackTXZGenerator() +{ +} diff --git a/Source/CPack/cmCPackTXZGenerator.h b/Source/CPack/cmCPackTXZGenerator.h new file mode 100644 index 0000000..87c92ef --- /dev/null +++ b/Source/CPack/cmCPackTXZGenerator.h @@ -0,0 +1,36 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackTXZGenerator_h +#define cmCPackTXZGenerator_h + +#include "cmCPackArchiveGenerator.h" + +/** \class cmCPackTXZGenerator + * \brief A generator for TXZ files + * + */ +class cmCPackTXZGenerator : public cmCPackArchiveGenerator +{ +public: + cmCPackTypeMacro(cmCPackTXZGenerator, cmCPackArchiveGenerator); + /** + * Construct generator + */ + cmCPackTXZGenerator(); + ~cmCPackTXZGenerator() CM_OVERRIDE; + +protected: + const char* GetOutputExtension() CM_OVERRIDE { return ".tar.xz"; } +}; + +#endif diff --git a/Source/CPack/cmCPackTarBZip2Generator.cxx b/Source/CPack/cmCPackTarBZip2Generator.cxx new file mode 100644 index 0000000..694d392 --- /dev/null +++ b/Source/CPack/cmCPackTarBZip2Generator.cxx @@ -0,0 +1,22 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackTarBZip2Generator.h" + +cmCPackTarBZip2Generator::cmCPackTarBZip2Generator() + : cmCPackArchiveGenerator(cmArchiveWrite::CompressBZip2, "paxr") +{ +} + +cmCPackTarBZip2Generator::~cmCPackTarBZip2Generator() +{ +} diff --git a/Source/CPack/cmCPackTarBZip2Generator.h b/Source/CPack/cmCPackTarBZip2Generator.h new file mode 100644 index 0000000..6fec882 --- /dev/null +++ b/Source/CPack/cmCPackTarBZip2Generator.h @@ -0,0 +1,35 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackTarBZip2Generator_h +#define cmCPackTarBZip2Generator_h + +#include "cmCPackArchiveGenerator.h" + +/** \class cmCPackTarBZip2Generator + * \brief A generator for TarBZip2 files + */ +class cmCPackTarBZip2Generator : public cmCPackArchiveGenerator +{ +public: + cmCPackTypeMacro(cmCPackTarBZip2Generator, cmCPackArchiveGenerator); + /** + * Construct generator + */ + cmCPackTarBZip2Generator(); + ~cmCPackTarBZip2Generator() CM_OVERRIDE; + +protected: + const char* GetOutputExtension() CM_OVERRIDE { return ".tar.bz2"; } +}; + +#endif diff --git a/Source/CPack/cmCPackTarCompressGenerator.cxx b/Source/CPack/cmCPackTarCompressGenerator.cxx new file mode 100644 index 0000000..aec6893 --- /dev/null +++ b/Source/CPack/cmCPackTarCompressGenerator.cxx @@ -0,0 +1,22 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackTarCompressGenerator.h" + +cmCPackTarCompressGenerator::cmCPackTarCompressGenerator() + : cmCPackArchiveGenerator(cmArchiveWrite::CompressCompress, "paxr") +{ +} + +cmCPackTarCompressGenerator::~cmCPackTarCompressGenerator() +{ +} diff --git a/Source/CPack/cmCPackTarCompressGenerator.h b/Source/CPack/cmCPackTarCompressGenerator.h new file mode 100644 index 0000000..02926a2 --- /dev/null +++ b/Source/CPack/cmCPackTarCompressGenerator.h @@ -0,0 +1,35 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackTarCompressGenerator_h +#define cmCPackTarCompressGenerator_h + +#include "cmCPackTGZGenerator.h" + +/** \class cmCPackTarCompressGenerator + * \brief A generator for TarCompress files + */ +class cmCPackTarCompressGenerator : public cmCPackArchiveGenerator +{ +public: + cmCPackTypeMacro(cmCPackTarCompressGenerator, cmCPackArchiveGenerator); + /** + * Construct generator + */ + cmCPackTarCompressGenerator(); + ~cmCPackTarCompressGenerator() CM_OVERRIDE; + +protected: + const char* GetOutputExtension() CM_OVERRIDE { return ".tar.Z"; } +}; + +#endif diff --git a/Source/CPack/cmCPackZIPGenerator.cxx b/Source/CPack/cmCPackZIPGenerator.cxx new file mode 100644 index 0000000..9b42e6d --- /dev/null +++ b/Source/CPack/cmCPackZIPGenerator.cxx @@ -0,0 +1,22 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#include "cmCPackZIPGenerator.h" + +cmCPackZIPGenerator::cmCPackZIPGenerator() + : cmCPackArchiveGenerator(cmArchiveWrite::CompressNone, "zip") +{ +} + +cmCPackZIPGenerator::~cmCPackZIPGenerator() +{ +} diff --git a/Source/CPack/cmCPackZIPGenerator.h b/Source/CPack/cmCPackZIPGenerator.h new file mode 100644 index 0000000..1130826 --- /dev/null +++ b/Source/CPack/cmCPackZIPGenerator.h @@ -0,0 +1,36 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc. + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ + +#ifndef cmCPackZIPGenerator_h +#define cmCPackZIPGenerator_h + +#include "cmCPackArchiveGenerator.h" + +/** \class cmCPackZIPGenerator + * \brief A generator for ZIP files + */ +class cmCPackZIPGenerator : public cmCPackArchiveGenerator +{ +public: + cmCPackTypeMacro(cmCPackZIPGenerator, cmCPackArchiveGenerator); + + /** + * Construct generator + */ + cmCPackZIPGenerator(); + ~cmCPackZIPGenerator() CM_OVERRIDE; + +protected: + const char* GetOutputExtension() CM_OVERRIDE { return ".zip"; } +}; + +#endif diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx new file mode 100644 index 0000000..771519c --- /dev/null +++ b/Source/CPack/cpack.cxx @@ -0,0 +1,429 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2000-2009 Kitware, Inc., Insight Software Consortium + + Distributed under the OSI-approved BSD License (the "License"); + see accompanying file Copyright.txt for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. +============================================================================*/ +#include "cmSystemTools.h" + +// Need these for documentation support. +#include "cmCPackGenerator.h" +#include "cmCPackGeneratorFactory.h" +#include "cmDocumentation.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmake.h" +#include "cmake.h" + +#include "cmCPackLog.h" + +#include <cmsys/CommandLineArguments.hxx> +#include <cmsys/Encoding.hxx> +#include <cmsys/SystemTools.hxx> + +static const char* cmDocumentationName[][2] = { + { CM_NULLPTR, " cpack - Packaging driver provided by CMake." }, + { CM_NULLPTR, CM_NULLPTR } +}; + +static const char* cmDocumentationUsage[][2] = { + { CM_NULLPTR, " cpack -G <generator> [options]" }, + { CM_NULLPTR, CM_NULLPTR } +}; + +static const char* cmDocumentationOptions[][2] = { + { "-G <generator>", "Use the specified generator to generate package." }, + { "-C <Configuration>", "Specify the project configuration" }, + { "-D <var>=<value>", "Set a CPack variable." }, + { "--config <config file>", "Specify the config file." }, + { "--verbose,-V", "enable verbose output" }, + { "--debug", "enable debug output (for CPack developers)" }, + { "-P <package name>", "override/define CPACK_PACKAGE_NAME" }, + { "-R <package version>", "override/define CPACK_PACKAGE_VERSION" }, + { "-B <package directory>", "override/define CPACK_PACKAGE_DIRECTORY" }, + { "--vendor <vendor name>", "override/define CPACK_PACKAGE_VENDOR" }, + { CM_NULLPTR, CM_NULLPTR } +}; + +int cpackUnknownArgument(const char*, void*) +{ + return 1; +} + +struct cpackDefinitions +{ + typedef std::map<std::string, std::string> MapType; + MapType Map; + cmCPackLog* Log; +}; + +int cpackDefinitionArgument(const char* argument, const char* cValue, + void* call_data) +{ + (void)argument; + cpackDefinitions* def = static_cast<cpackDefinitions*>(call_data); + std::string value = cValue; + size_t pos = value.find_first_of('='); + if (pos == std::string::npos) { + cmCPack_Log(def->Log, cmCPackLog::LOG_ERROR, + "Please specify CPack definitions as: KEY=VALUE" << std::endl); + return 0; + } + std::string key = value.substr(0, pos); + value = value.c_str() + pos + 1; + def->Map[key] = value; + cmCPack_Log(def->Log, cmCPackLog::LOG_DEBUG, "Set CPack variable: " + << key << " to \"" << value << "\"" << std::endl); + return 1; +} + +// this is CPack. +int main(int argc, char const* const* argv) +{ + cmsys::Encoding::CommandLineArguments args = + cmsys::Encoding::CommandLineArguments::Main(argc, argv); + argc = args.argc(); + argv = args.argv(); + + cmSystemTools::FindCMakeResources(argv[0]); + cmCPackLog log; + + log.SetErrorPrefix("CPack Error: "); + log.SetWarningPrefix("CPack Warning: "); + log.SetOutputPrefix("CPack: "); + log.SetVerbosePrefix("CPack Verbose: "); + + cmSystemTools::EnableMSVCDebugHook(); + + if (cmSystemTools::GetCurrentWorkingDirectory().empty()) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Current working directory cannot be established." + << std::endl); + return 1; + } + + std::string generator; + bool help = false; + bool helpVersion = false; + bool verbose = false; + bool debug = false; + std::string helpFull; + std::string helpMAN; + std::string helpHTML; + + std::string cpackProjectName; + std::string cpackProjectDirectory; + std::string cpackBuildConfig; + std::string cpackProjectVersion; + std::string cpackProjectPatch; + std::string cpackProjectVendor; + std::string cpackConfigFile; + + cpackDefinitions definitions; + definitions.Log = &log; + + cpackConfigFile = ""; + + cmsys::CommandLineArguments arg; + arg.Initialize(argc, argv); + typedef cmsys::CommandLineArguments argT; + // Help arguments + arg.AddArgument("--help", argT::NO_ARGUMENT, &help, "CPack help"); + arg.AddArgument("--help-full", argT::SPACE_ARGUMENT, &helpFull, + "CPack help"); + arg.AddArgument("--help-html", argT::SPACE_ARGUMENT, &helpHTML, + "CPack help"); + arg.AddArgument("--help-man", argT::SPACE_ARGUMENT, &helpMAN, "CPack help"); + arg.AddArgument("--version", argT::NO_ARGUMENT, &helpVersion, "CPack help"); + + arg.AddArgument("-V", argT::NO_ARGUMENT, &verbose, "CPack verbose"); + arg.AddArgument("--verbose", argT::NO_ARGUMENT, &verbose, "-V"); + arg.AddArgument("--debug", argT::NO_ARGUMENT, &debug, "-V"); + arg.AddArgument("--config", argT::SPACE_ARGUMENT, &cpackConfigFile, + "CPack configuration file"); + arg.AddArgument("-C", argT::SPACE_ARGUMENT, &cpackBuildConfig, + "CPack build configuration"); + arg.AddArgument("-G", argT::SPACE_ARGUMENT, &generator, "CPack generator"); + arg.AddArgument("-P", argT::SPACE_ARGUMENT, &cpackProjectName, + "CPack project name"); + arg.AddArgument("-R", argT::SPACE_ARGUMENT, &cpackProjectVersion, + "CPack project version"); + arg.AddArgument("-B", argT::SPACE_ARGUMENT, &cpackProjectDirectory, + "CPack project directory"); + arg.AddArgument("--patch", argT::SPACE_ARGUMENT, &cpackProjectPatch, + "CPack project patch"); + arg.AddArgument("--vendor", argT::SPACE_ARGUMENT, &cpackProjectVendor, + "CPack project vendor"); + arg.AddCallback("-D", argT::SPACE_ARGUMENT, cpackDefinitionArgument, + &definitions, "CPack Definitions"); + arg.SetUnknownArgumentCallback(cpackUnknownArgument); + + // Parse command line + int parsed = arg.Parse(); + + // Setup logging + if (verbose) { + log.SetVerbose(verbose); + cmCPack_Log(&log, cmCPackLog::LOG_OUTPUT, "Enable Verbose" << std::endl); + } + if (debug) { + log.SetDebug(debug); + cmCPack_Log(&log, cmCPackLog::LOG_OUTPUT, "Enable Debug" << std::endl); + } + + cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE, + "Read CPack config file: " << cpackConfigFile << std::endl); + + cmake cminst; + cminst.SetHomeDirectory(""); + cminst.SetHomeOutputDirectory(""); + cminst.GetCurrentSnapshot().SetDefaultDefinitions(); + cminst.GetState()->RemoveUnscriptableCommands(); + cmGlobalGenerator cmgg(&cminst); + CM_AUTO_PTR<cmMakefile> globalMF( + new cmMakefile(&cmgg, cminst.GetCurrentSnapshot())); +#if defined(__CYGWIN__) + globalMF->AddDefinition("CMAKE_LEGACY_CYGWIN_WIN32", "0"); +#endif + + bool cpackConfigFileSpecified = true; + if (cpackConfigFile.empty()) { + cpackConfigFile = cmSystemTools::GetCurrentWorkingDirectory(); + cpackConfigFile += "/CPackConfig.cmake"; + cpackConfigFileSpecified = false; + } + + cmCPackGeneratorFactory generators; + generators.SetLogger(&log); + cmCPackGenerator* cpackGenerator = CM_NULLPTR; + + cmDocumentation doc; + doc.addCPackStandardDocSections(); + /* Were we invoked to display doc or to do some work ? + * Unlike cmake launching cpack with zero argument + * should launch cpack using "cpackConfigFile" if it exists + * in the current directory. + */ + help = doc.CheckOptions(argc, argv, "-G") && argc != 1; + + // This part is used for cpack documentation lookup as well. + cminst.AddCMakePaths(); + + if (parsed && !help) { + // find out which system cpack is running on, so it can setup the search + // paths, so FIND_XXX() commands can be used in scripts + std::string systemFile = + globalMF->GetModulesFile("CMakeDetermineSystem.cmake"); + if (!globalMF->ReadListFile(systemFile.c_str())) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Error reading CMakeDetermineSystem.cmake" << std::endl); + return 1; + } + + systemFile = + globalMF->GetModulesFile("CMakeSystemSpecificInformation.cmake"); + if (!globalMF->ReadListFile(systemFile.c_str())) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Error reading CMakeSystemSpecificInformation.cmake" + << std::endl); + return 1; + } + + if (!cpackBuildConfig.empty()) { + globalMF->AddDefinition("CPACK_BUILD_CONFIG", cpackBuildConfig.c_str()); + } + + if (cmSystemTools::FileExists(cpackConfigFile.c_str())) { + cpackConfigFile = cmSystemTools::CollapseFullPath(cpackConfigFile); + cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE, + "Read CPack configuration file: " << cpackConfigFile + << std::endl); + if (!globalMF->ReadListFile(cpackConfigFile.c_str())) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Problem reading CPack config file: \"" + << cpackConfigFile << "\"" << std::endl); + return 1; + } + } else if (cpackConfigFileSpecified) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Cannot find CPack config file: \"" << cpackConfigFile + << "\"" << std::endl); + return 1; + } + + if (!generator.empty()) { + globalMF->AddDefinition("CPACK_GENERATOR", generator.c_str()); + } + if (!cpackProjectName.empty()) { + globalMF->AddDefinition("CPACK_PACKAGE_NAME", cpackProjectName.c_str()); + } + if (!cpackProjectVersion.empty()) { + globalMF->AddDefinition("CPACK_PACKAGE_VERSION", + cpackProjectVersion.c_str()); + } + if (!cpackProjectVendor.empty()) { + globalMF->AddDefinition("CPACK_PACKAGE_VENDOR", + cpackProjectVendor.c_str()); + } + // if this is not empty it has been set on the command line + // go for it. Command line override values set in config file. + if (!cpackProjectDirectory.empty()) { + globalMF->AddDefinition("CPACK_PACKAGE_DIRECTORY", + cpackProjectDirectory.c_str()); + } + // The value has not been set on the command line + else { + // get a default value (current working directory) + cpackProjectDirectory = cmsys::SystemTools::GetCurrentWorkingDirectory(); + // use default value iff no value has been provided by the config file + if (!globalMF->IsSet("CPACK_PACKAGE_DIRECTORY")) { + globalMF->AddDefinition("CPACK_PACKAGE_DIRECTORY", + cpackProjectDirectory.c_str()); + } + } + cpackDefinitions::MapType::iterator cdit; + for (cdit = definitions.Map.begin(); cdit != definitions.Map.end(); + ++cdit) { + globalMF->AddDefinition(cdit->first, cdit->second.c_str()); + } + + const char* cpackModulesPath = + globalMF->GetDefinition("CPACK_MODULE_PATH"); + if (cpackModulesPath) { + globalMF->AddDefinition("CMAKE_MODULE_PATH", cpackModulesPath); + } + const char* genList = globalMF->GetDefinition("CPACK_GENERATOR"); + if (!genList) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, "CPack generator not specified" + << std::endl); + } else { + std::vector<std::string> generatorsVector; + cmSystemTools::ExpandListArgument(genList, generatorsVector); + std::vector<std::string>::iterator it; + for (it = generatorsVector.begin(); it != generatorsVector.end(); ++it) { + const char* gen = it->c_str(); + cmMakefile::ScopePushPop raii(globalMF.get()); + cmMakefile* mf = globalMF.get(); + cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE, + "Specified generator: " << gen << std::endl); + if (parsed && !mf->GetDefinition("CPACK_PACKAGE_NAME")) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "CPack project name not specified" << std::endl); + parsed = 0; + } + if (parsed && + !(mf->GetDefinition("CPACK_PACKAGE_VERSION") || + (mf->GetDefinition("CPACK_PACKAGE_VERSION_MAJOR") && + mf->GetDefinition("CPACK_PACKAGE_VERSION_MINOR") && + mf->GetDefinition("CPACK_PACKAGE_VERSION_PATCH")))) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "CPack project version not specified" + << std::endl + << "Specify CPACK_PACKAGE_VERSION, or " + "CPACK_PACKAGE_VERSION_MAJOR, " + "CPACK_PACKAGE_VERSION_MINOR, and " + "CPACK_PACKAGE_VERSION_PATCH." + << std::endl); + parsed = 0; + } + if (parsed) { + cpackGenerator = generators.NewGenerator(gen); + if (!cpackGenerator) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Cannot initialize CPack generator: " << gen + << std::endl); + parsed = 0; + } + if (parsed && !cpackGenerator->Initialize(gen, mf)) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Cannot initialize the generator " << gen + << std::endl); + parsed = 0; + } + + if (!mf->GetDefinition("CPACK_INSTALL_COMMANDS") && + !mf->GetDefinition("CPACK_INSTALLED_DIRECTORIES") && + !mf->GetDefinition("CPACK_INSTALL_CMAKE_PROJECTS")) { + cmCPack_Log( + &log, cmCPackLog::LOG_ERROR, + "Please specify build tree of the project that uses CMake " + "using CPACK_INSTALL_CMAKE_PROJECTS, specify " + "CPACK_INSTALL_COMMANDS, or specify " + "CPACK_INSTALLED_DIRECTORIES." + << std::endl); + parsed = 0; + } + if (parsed) { + const char* projName = mf->GetDefinition("CPACK_PACKAGE_NAME"); + cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE, "Use generator: " + << cpackGenerator->GetNameOfClass() << std::endl); + cmCPack_Log(&log, cmCPackLog::LOG_VERBOSE, + "For project: " << projName << std::endl); + + const char* projVersion = + mf->GetDefinition("CPACK_PACKAGE_VERSION"); + if (!projVersion) { + const char* projVersionMajor = + mf->GetDefinition("CPACK_PACKAGE_VERSION_MAJOR"); + const char* projVersionMinor = + mf->GetDefinition("CPACK_PACKAGE_VERSION_MINOR"); + const char* projVersionPatch = + mf->GetDefinition("CPACK_PACKAGE_VERSION_PATCH"); + std::ostringstream ostr; + ostr << projVersionMajor << "." << projVersionMinor << "." + << projVersionPatch; + mf->AddDefinition("CPACK_PACKAGE_VERSION", ostr.str().c_str()); + } + + int res = cpackGenerator->DoPackage(); + if (!res) { + cmCPack_Log(&log, cmCPackLog::LOG_ERROR, + "Error when generating package: " << projName + << std::endl); + return 1; + } + } + } + } + } + } + + /* In this case we are building the documentation object + * instance in order to create appropriate structure + * in order to satisfy the appropriate --help-xxx request + */ + if (help) { + // Construct and print requested documentation. + + doc.SetName("cpack"); + doc.SetSection("Name", cmDocumentationName); + doc.SetSection("Usage", cmDocumentationUsage); + doc.PrependSection("Options", cmDocumentationOptions); + + std::vector<cmDocumentationEntry> v; + cmCPackGeneratorFactory::DescriptionsMap::const_iterator generatorIt; + for (generatorIt = generators.GetGeneratorsList().begin(); + generatorIt != generators.GetGeneratorsList().end(); ++generatorIt) { + cmDocumentationEntry e; + e.Name = generatorIt->first; + e.Brief = generatorIt->second; + v.push_back(e); + } + doc.SetSection("Generators", v); + +#undef cout + return doc.PrintRequestedDocumentation(std::cout) ? 0 : 1; +#define cout no_cout_use_cmCPack_Log + } + + if (cmSystemTools::GetErrorOccuredFlag()) { + return 1; + } + + return 0; +} diff --git a/Source/CPack/cygwin.readme b/Source/CPack/cygwin.readme new file mode 100644 index 0000000..c0cd4b9 --- /dev/null +++ b/Source/CPack/cygwin.readme @@ -0,0 +1,69 @@ +http://cygwin.com/setup.html + + +Need to produce two tar files: + +Source- + +- create subdirs +- copy src +- duplicate src +- configure files into duplicate src + CPack.cygwin-readme.in + CPack.cygwin-install.sh.in + CPack.setup.hint.in +- diff duplicate src and orig src +- write diff into toplevel +- create tar file call super class + +cmake-2.2.3-1 + + +1. a source release +cmake-2.2.3-2-src.tar.bz2 + +cmake-2.2.3-2.patch has cmake-2.2.3/CYGWIN-PATCHES/cmake.README cmake-2.2.3/CYGWIN-PATCHES/setup.hint +cmake-2.2.3-2.sh -> script to create cygwin release +cmake-2.2.3.tar.bz2 -> unmodified cmake sources for 2.2.3 + + + + + +2 a binary release +cmake-2.2.3-2.tar.bz2 + +normal binary release with use as the root of the tree: + +Here is the bootstrap command used: + + ${SOURCE_DIR}/bootstrap --prefix=/usr --datadir=/share/cmake-${VER} \ + --docdir=/share/doc/cmake-${VER} --mandir=/share/man + +CMAKE_DOC_DIR /share/doc/${PKG}-${VER} +CMAKE_MAN_DIR /share/man +CMAKE_DATA_DIR /share/${PKG}-${VER} + +Here is the directory stucture: + +usr/bin/cmake.exe +usr/share/doc/cmake-2.2.3/MANIFEST *** +usr/share/doc/Cygwin/cmake-2.2.3-2.README **** +usr/share/cmake-2.2.3/Modules + + + +usr/bin +usr/share/cmake-2.2.3/include +usr/share/cmake-2.2.3/Modules/Platform +usr/share/cmake-2.2.3/Modules +usr/share/cmake-2.2.3/Templates +usr/share/cmake-2.2.3 +usr/share/doc/cmake-2.2.3 +usr/share/doc/Cygwin +usr/share/doc +usr/share/man/man1 +usr/share/man +usr/share +usr + |