cmake/Source/CPack/cmCPackProductBuildGenerator.cxx

254 lines
8.8 KiB
C++
Raw Normal View History

2016-10-30 18:24:19 +01:00
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmCPackProductBuildGenerator.h"
2020-02-01 23:06:01 +01:00
#include <cstddef>
2017-04-14 19:02:05 +02:00
#include <map>
#include <sstream>
2016-10-30 18:24:19 +01:00
#include "cmCPackComponentGroup.h"
#include "cmCPackLog.h"
2018-04-23 21:13:27 +02:00
#include "cmDuration.h"
2016-10-30 18:24:19 +01:00
#include "cmGeneratedFileStream.h"
2020-02-01 23:06:01 +01:00
#include "cmStringAlgorithms.h"
2016-10-30 18:24:19 +01:00
#include "cmSystemTools.h"
cmCPackProductBuildGenerator::cmCPackProductBuildGenerator()
{
this->componentPackageMethod = ONE_PACKAGE;
}
2019-11-11 23:01:05 +01:00
cmCPackProductBuildGenerator::~cmCPackProductBuildGenerator() = default;
2016-10-30 18:24:19 +01:00
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.
2020-02-01 23:06:01 +01:00
std::string basePackageDir =
cmStrCat(packageDirFileName, "/Contents/Packages");
2016-10-30 18:24:19 +01:00
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) {
2020-02-01 23:06:01 +01:00
std::string packageDir = cmStrCat(toplevel, '/', compIt->first);
2016-10-30 18:24:19 +01:00
if (!this->GenerateComponentPackage(basePackageDir,
GetPackageName(compIt->second),
packageDir, &compIt->second)) {
return 0;
}
}
} else {
if (!this->GenerateComponentPackage(basePackageDir,
this->GetOption("CPACK_PACKAGE_NAME"),
2018-01-26 17:06:56 +01:00
toplevel, nullptr)) {
2016-10-30 18:24:19 +01:00
return 0;
}
}
std::string resDir = packageDirFileName + "/Contents";
2017-07-20 19:35:53 +02:00
if (this->IsSet("CPACK_PRODUCTBUILD_RESOURCES_DIR")) {
std::string userResDir =
this->GetOption("CPACK_PRODUCTBUILD_RESOURCES_DIR");
if (!cmSystemTools::CopyADirectory(userResDir, resDir)) {
2018-08-09 18:06:22 +02:00
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Problem copying the resource files" << std::endl);
2017-07-20 19:35:53 +02:00
return 0;
}
}
// Copy or create all of the resource files we need.
2016-10-30 18:24:19 +01:00
if (!this->CopyCreateResourceFile("License", resDir) ||
!this->CopyCreateResourceFile("ReadMe", resDir) ||
!this->CopyCreateResourceFile("Welcome", resDir)) {
2017-07-20 19:35:53 +02:00
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Problem copying the License, ReadMe and Welcome files"
2016-10-30 18:24:19 +01:00
<< std::endl);
return 0;
}
// combine package(s) into a distribution
2020-08-30 11:54:41 +02:00
WriteDistributionFile(packageDirFileName.c_str(), "PRODUCTBUILD");
2016-10-30 18:24:19 +01:00
std::ostringstream pkgCmd;
std::string version = this->GetOption("CPACK_PACKAGE_VERSION");
std::string productbuild = this->GetOption("CPACK_COMMAND_PRODUCTBUILD");
2017-04-14 19:02:05 +02:00
std::string identityName;
if (const char* n = this->GetOption("CPACK_PRODUCTBUILD_IDENTITY_NAME")) {
identityName = n;
}
std::string keychainPath;
if (const char* p = this->GetOption("CPACK_PRODUCTBUILD_KEYCHAIN_PATH")) {
keychainPath = p;
}
2016-10-30 18:24:19 +01:00
pkgCmd << productbuild << " --distribution \"" << packageDirFileName
<< "/Contents/distribution.dist\""
<< " --package-path \"" << packageDirFileName << "/Contents/Packages"
<< "\""
<< " --resources \"" << resDir << "\""
<< " --version \"" << version << "\""
2017-04-14 19:02:05 +02:00
<< (identityName.empty() ? "" : " --sign \"" + identityName + "\"")
<< (keychainPath.empty() ? ""
: " --keychain \"" + keychainPath + "\"")
2016-10-30 18:24:19 +01:00
<< " \"" << 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()) {
2018-08-09 18:06:22 +02:00
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Cannot find pkgbuild executable" << std::endl);
2016-10-30 18:24:19 +01:00
return 0;
}
this->SetOptionIfNotSet("CPACK_COMMAND_PKGBUILD", program.c_str());
program = cmSystemTools::FindProgram("productbuild", no_paths, false);
if (program.empty()) {
2018-08-09 18:06:22 +02:00
cmCPackLogger(cmCPackLog::LOG_ERROR,
"Cannot find productbuild executable" << std::endl);
2016-10-30 18:24:19 +01:00
return 0;
}
this->SetOptionIfNotSet("CPACK_COMMAND_PRODUCTBUILD", program.c_str());
return this->Superclass::InitializeInternal();
}
bool cmCPackProductBuildGenerator::RunProductBuild(const std::string& command)
{
2020-02-01 23:06:01 +01:00
std::string tmpFile = cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"),
"/ProductBuildOutput.log");
2016-10-30 18:24:19 +01:00
cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Execute: " << command << std::endl);
2018-10-28 12:09:07 +01:00
std::string output;
2016-10-30 18:24:19 +01:00
int retVal = 1;
2018-04-23 21:13:27 +02:00
bool res = cmSystemTools::RunSingleCommand(
2019-11-11 23:01:05 +01:00
command, &output, &output, &retVal, nullptr, this->GeneratorVerbose,
cmDuration::zero());
2016-10-30 18:24:19 +01:00
cmCPackLogger(cmCPackLog::LOG_VERBOSE, "Done running command" << std::endl);
if (!res || retVal) {
2019-11-11 23:01:05 +01:00
cmGeneratedFileStream ofs(tmpFile);
2016-10-30 18:24:19 +01:00
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)
{
2020-02-01 23:06:01 +01:00
std::string packageFile = cmStrCat(packageFileDir, '/', packageFileName);
2016-10-30 18:24:19 +01:00
2018-08-09 18:06:22 +02:00
cmCPackLogger(cmCPackLog::LOG_OUTPUT,
"- Building component package: " << packageFile
<< std::endl);
2016-10-30 18:24:19 +01:00
2018-01-26 17:06:56 +01:00
const char* comp_name = component ? component->Name.c_str() : nullptr;
2016-10-30 18:24:19 +01:00
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);
2018-01-26 17:06:56 +01:00
return false;
2016-10-30 18:24:19 +01:00
}
// if preflight, postflight, or postupgrade are set
// then copy them into the script directory and make
// them executable
if (preflight) {
this->CopyInstallScript(scriptDir, preflight, "preinstall");
}
if (postflight) {
this->CopyInstallScript(scriptDir, postflight, "postinstall");
}
// The command that will be used to run ProductBuild
std::ostringstream pkgCmd;
2020-02-01 23:06:01 +01:00
std::string pkgId = cmStrCat("com.", this->GetOption("CPACK_PACKAGE_VENDOR"),
'.', this->GetOption("CPACK_PACKAGE_NAME"));
2016-10-30 18:24:19 +01:00
if (component) {
pkgId += '.';
pkgId += component->Name;
}
std::string version = this->GetOption("CPACK_PACKAGE_VERSION");
std::string pkgbuild = this->GetOption("CPACK_COMMAND_PKGBUILD");
2017-04-14 19:02:05 +02:00
std::string identityName;
if (const char* n = this->GetOption("CPACK_PKGBUILD_IDENTITY_NAME")) {
identityName = n;
}
std::string keychainPath;
if (const char* p = this->GetOption("CPACK_PKGBUILD_KEYCHAIN_PATH")) {
keychainPath = p;
}
2016-10-30 18:24:19 +01:00
pkgCmd << pkgbuild << " --root \"" << packageDir << "\""
<< " --identifier \"" << pkgId << "\""
<< " --scripts \"" << scriptDir << "\""
<< " --version \"" << version << "\""
<< " --install-location \"/\""
2017-04-14 19:02:05 +02:00
<< (identityName.empty() ? "" : " --sign \"" + identityName + "\"")
<< (keychainPath.empty() ? ""
: " --keychain \"" + keychainPath + "\"")
2016-10-30 18:24:19 +01:00
<< " \"" << packageFile << "\"";
2017-07-20 19:35:53 +02:00
if (component && !component->Plist.empty()) {
pkgCmd << " --component-plist \"" << component->Plist << "\"";
}
2016-10-30 18:24:19 +01:00
// 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);
}