Files
CMake/Source/cmExportCommand.cxx
Arha Gatram 77156c92eb ALIAS: Add support for install and export commands
The install({TARGETS, EXPORT}) versions of the command now accept ALIAS
targets. The behavior is identical to what would happen if the name of
the target that the ALIAS references were put instead. The
export(TARGETS) command also has the same support and behavior for ALIAS
targets now.

Closes: #20979
2026-07-10 17:01:49 -07:00

761 lines
24 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmExportCommand.h"
#include <map>
#include <sstream>
#include <utility>
#include <cm/memory>
#include <cm/optional>
#include <cmext/algorithm>
#include <cmext/string_view>
#include "cmsys/RegularExpression.hxx"
#include "cmArgumentParser.h"
#include "cmArgumentParserTypes.h"
#include "cmBuildSbomGenerator.h"
#include "cmCryptoHash.h"
#include "cmDiagnosticContext.h"
#include "cmDiagnostics.h"
#include "cmExecutionStatus.h"
#include "cmExperimental.h"
#include "cmExportBuildAndroidMKGenerator.h"
#include "cmExportBuildCMakeConfigGenerator.h"
#include "cmExportBuildFileGenerator.h"
#include "cmExportBuildPackageInfoGenerator.h"
#include "cmExportSet.h"
#include "cmGeneratedFileStream.h"
#include "cmGlobalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPackageInfoArguments.h"
#include "cmPolicies.h"
#include "cmRange.h"
#include "cmSbomArguments.h"
#include "cmStringAlgorithms.h"
#include "cmSubcommandTable.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmTargetTypes.h"
#include "cmValue.h"
#if defined(__HAIKU__)
# include <FindDirectory.h>
# include <StorageDefs.h>
#endif
#if defined(_WIN32) && !defined(__CYGWIN__)
# include <windows.h>
#endif
static void StorePackageRegistry(cmMakefile& mf, std::string const& package,
char const* content, char const* hash);
static cm::optional<cmExportSet*> GetExportSet(std::string const& name,
cmGlobalGenerator* generator,
cmExecutionStatus& status)
{
cmExportSetMap& setMap = generator->GetExportSets();
auto const it = setMap.find(name);
if (it == setMap.end()) {
status.SetError(cmStrCat("Export set \""_s, name, "\" not found."_s));
return cm::nullopt;
}
return &it->second;
}
static void AddExportGenerator(
cmMakefile& makefile, cmGlobalGenerator* globalGenerator,
std::unique_ptr<cmExportBuildFileGenerator> exportGenerator,
std::string const& fileName, cmExportSet* exportSet,
std::string const& cxxModulesDirectory)
{
exportGenerator->SetExportFile(fileName.c_str());
exportGenerator->SetCxxModuleDirectory(cxxModulesDirectory);
if (exportSet) {
exportGenerator->SetExportSet(exportSet);
}
std::vector<std::string> configurationTypes =
makefile.GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
for (std::string const& ct : configurationTypes) {
exportGenerator->AddConfiguration(ct);
}
if (exportSet) {
globalGenerator->AddBuildExportExportSet(exportGenerator.get());
}
makefile.AddExportBuildFileGenerator(std::move(exportGenerator));
}
static bool ValidateExportableTarget(std::string const& name,
cmGlobalGenerator* gg,
cmExecutionStatus& status)
{
cmTarget const* target = gg->FindTarget(name);
if (!target) {
status.SetError(cmStrCat("given target \"", name,
"\" which is not built by this project."));
return false;
}
if (target->GetType() == cm::TargetType::UTILITY) {
status.SetError(cmStrCat("given custom target \"", name,
"\" which may not be exported."));
return false;
}
return true;
}
static bool HandleTargetsMode(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
struct Arguments
{
cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> Targets;
ArgumentParser::NonEmpty<std::string> ExportSetName;
ArgumentParser::NonEmpty<std::string> Namespace;
ArgumentParser::NonEmpty<std::string> Filename;
ArgumentParser::NonEmpty<std::string> CxxModulesDirectory;
ArgumentParser::NonEmpty<std::string> AndroidMKFile;
bool Append = false;
bool ExportOld = false;
};
auto parser =
cmArgumentParser<Arguments>{}
.Bind("NAMESPACE"_s, &Arguments::Namespace)
.Bind("FILE"_s, &Arguments::Filename)
.Bind("CXX_MODULES_DIRECTORY"_s, &Arguments::CxxModulesDirectory)
.Bind("TARGETS"_s, &Arguments::Targets)
.Bind("APPEND"_s, &Arguments::Append)
.Bind("ANDROID_MK"_s, &Arguments::AndroidMKFile)
.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, &Arguments::ExportOld);
std::vector<std::string> unknownArgs;
Arguments arguments = parser.Parse(args, &unknownArgs);
if (!unknownArgs.empty()) {
status.SetError(
cmStrCat("Unknown argument: \"", unknownArgs.front(), "\"."));
return false;
}
std::string fname;
bool android = false;
if (!arguments.AndroidMKFile.empty()) {
fname = arguments.AndroidMKFile;
android = true;
} else if (arguments.Filename.empty()) {
fname = arguments.ExportSetName + ".cmake";
} else {
// Make sure the file has a .cmake extension.
if (!cmHasSuffix(arguments.Filename, ".cmake"_s)) {
std::ostringstream e;
e << "FILE option given filename \"" << arguments.Filename
<< "\" which does not have an extension of \".cmake\".\n";
status.SetError(e.str());
return false;
}
fname = arguments.Filename;
}
cmMakefile& mf = status.GetMakefile();
// Get the file to write.
if (cmSystemTools::FileIsFullPath(fname)) {
if (!mf.CanIWriteThisFile(fname)) {
std::ostringstream e;
e << "FILE option given filename \"" << fname
<< "\" which is in the source tree.\n";
status.SetError(e.str());
return false;
}
} else {
// Interpret relative paths with respect to the current build dir.
std::string const& dir = mf.GetCurrentBinaryDirectory();
fname = cmStrCat(dir, '/', fname);
}
std::vector<cmExportBuildFileGenerator::TargetExport> targets;
cmGlobalGenerator* gg = mf.GetGlobalGenerator();
for (std::string const& currentTarget : *arguments.Targets) {
if (!ValidateExportableTarget(currentTarget, gg, status)) {
return false;
}
targets.emplace_back(currentTarget, std::string{});
}
if (arguments.Append) {
if (cmExportBuildFileGenerator* ebfg = gg->GetExportedTargetsFile(fname)) {
ebfg->AppendTargets(targets);
return true;
}
}
// if cmExportBuildFileGenerator is already defined for the file
// and APPEND is not specified, if CMP0103 is OLD ignore previous definition
// else raise an error
if (gg->GetExportedTargetsFile(fname)) {
switch (mf.GetPolicyStatus(cmPolicies::CMP0103)) {
case cmPolicies::WARN:
mf.IssuePolicyWarning(
cmPolicies::CMP0103, {},
cmStrCat("export() command already specified for the file\n ",
arguments.Filename, "\nDid you miss 'APPEND' keyword?"));
CM_FALLTHROUGH;
case cmPolicies::OLD:
break;
default:
status.SetError(cmStrCat("command already specified for the file\n ",
arguments.Filename,
"\nDid you miss 'APPEND' keyword?"));
return false;
}
}
cmDiagnosticContext context = cmExportBuildFileGenerator::CaptureContext(mf);
std::unique_ptr<cmExportBuildFileGenerator> ebfg = nullptr;
if (android) {
auto ebag =
cm::make_unique<cmExportBuildAndroidMKGenerator>(std::move(context));
ebag->SetNamespace(arguments.Namespace);
ebag->SetAppendMode(arguments.Append);
ebfg = std::move(ebag);
} else {
auto ebcg =
cm::make_unique<cmExportBuildCMakeConfigGenerator>(std::move(context));
ebcg->SetNamespace(arguments.Namespace);
ebcg->SetAppendMode(arguments.Append);
ebcg->SetExportOld(arguments.ExportOld);
ebfg = std::move(ebcg);
}
ebfg->SetExportFile(fname.c_str());
ebfg->SetCxxModuleDirectory(arguments.CxxModulesDirectory);
ebfg->SetTargets(targets);
std::vector<std::string> configurationTypes =
mf.GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
for (std::string const& ct : configurationTypes) {
ebfg->AddConfiguration(ct);
}
gg->AddBuildExportSet(ebfg.get());
mf.AddExportBuildFileGenerator(std::move(ebfg));
return true;
}
static bool HandleExportMode(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
struct ExportArguments : public ArgumentParser::ParseResult
{
ArgumentParser::NonEmpty<std::string> ExportSetName;
ArgumentParser::MaybeEmpty<std::string> Namespace;
ArgumentParser::NonEmpty<std::string> Filename;
ArgumentParser::NonEmpty<std::string> CxxModulesDirectory;
bool ExportPackageDependencies = false;
};
auto parser =
cmArgumentParser<ExportArguments>{}
.Bind("EXPORT"_s, &ExportArguments::ExportSetName)
.Bind("NAMESPACE"_s, &ExportArguments::Namespace)
.Bind("FILE"_s, &ExportArguments::Filename)
.Bind("CXX_MODULES_DIRECTORY"_s, &ExportArguments::CxxModulesDirectory);
if (cmExperimental::HasSupportEnabled(
status.GetMakefile(),
cmExperimental::Feature::ExportPackageDependencies)) {
parser.Bind("EXPORT_PACKAGE_DEPENDENCIES"_s,
&ExportArguments::ExportPackageDependencies);
}
std::vector<std::string> unknownArgs;
ExportArguments arguments = parser.Parse(args, &unknownArgs);
cmMakefile& mf = status.GetMakefile();
cmGlobalGenerator* gg = mf.GetGlobalGenerator();
if (!arguments.Check(args[0], &unknownArgs, status)) {
cmPolicies::PolicyStatus const p =
status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0208);
if (!unknownArgs.empty() || p == cmPolicies::NEW) {
return false;
}
if (p == cmPolicies::WARN) {
status.GetMakefile().IssueDiagnostic(
cmDiagnostics::CMD_AUTHOR, cmStrCat("export "_s, status.GetError()));
status.GetMakefile().IssuePolicyWarning(cmPolicies::CMP0208);
}
}
std::string fname;
if (arguments.Filename.empty()) {
fname = arguments.ExportSetName + ".cmake";
} else {
if (!cmHasSuffix(arguments.Filename, ".cmake"_s)) {
std::ostringstream e;
e << "FILE option given filename \"" << arguments.Filename
<< "\" which does not have an extension of \".cmake\".\n";
status.SetError(e.str());
return false;
}
fname = arguments.Filename;
}
if (cmSystemTools::FileIsFullPath(fname)) {
if (!mf.CanIWriteThisFile(fname)) {
std::ostringstream e;
e << "FILE option given filename \"" << fname
<< "\" which is in the source tree.\n";
status.SetError(e.str());
return false;
}
} else {
// Interpret relative paths with respect to the current build dir.
std::string const& dir = mf.GetCurrentBinaryDirectory();
fname = cmStrCat(dir, '/', fname);
}
cm::optional<cmExportSet*> const exportSet =
GetExportSet(arguments.ExportSetName, gg, status);
if (!exportSet) {
return false;
}
// Set up export file generation.
auto ebcg = cm::make_unique<cmExportBuildCMakeConfigGenerator>(
cmExportBuildFileGenerator::CaptureContext(mf));
ebcg->SetNamespace(arguments.Namespace);
ebcg->SetExportPackageDependencies(arguments.ExportPackageDependencies);
AddExportGenerator(mf, gg, std::move(ebcg), fname, *exportSet,
arguments.CxxModulesDirectory);
return true;
}
template <typename ArgumentsType, typename GeneratorType>
static bool HandleSpecialExportMode(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
struct ExportArguments
: public ArgumentsType
, public ArgumentParser::ParseResult
{
ArgumentParser::NonEmpty<std::string> ExportSetName;
ArgumentParser::NonEmpty<std::string> CxxModulesDirectory;
using ArgumentsType::Check;
using ArgumentParser::ParseResult::Check;
};
auto parser =
cmArgumentParser<ExportArguments>{}
.Bind("EXPORT"_s, &ExportArguments::ExportSetName)
.Bind("CXX_MODULES_DIRECTORY"_s, &ExportArguments::CxxModulesDirectory);
ArgumentsType::Bind(parser);
std::vector<std::string> unknownArgs;
ExportArguments arguments = parser.Parse(args, &unknownArgs);
if (!arguments.Check(args[0], &unknownArgs, status)) {
return false;
}
if (arguments.ExportSetName.empty()) {
status.SetError(cmStrCat(args[0], " missing EXPORT."));
return false;
}
if (!arguments.Check(status) || !arguments.SetMetadataFromProject(status)) {
return false;
}
cmMakefile& mf = status.GetMakefile();
cmGlobalGenerator* gg = mf.GetGlobalGenerator();
std::string const& dir =
arguments.GetDefaultDestination(mf.GetCurrentBinaryDirectory());
std::string const fname = cmStrCat(dir, '/', arguments.GetPackageFileName());
if (gg->GetExportedTargetsFile(fname)) {
status.SetError(cmStrCat("command already specified for the file "_s,
cmSystemTools::GetFilenameNameView(fname), '.'));
return false;
}
// Look up the export set
cm::optional<cmExportSet*> const exportSet =
GetExportSet(arguments.ExportSetName, gg, status);
if (!exportSet) {
return false;
}
// Create the export build generator
auto ebpg = cm::make_unique<GeneratorType>(
arguments, cmExportBuildFileGenerator::CaptureContext(mf));
AddExportGenerator(mf, gg, std::move(ebpg), fname, *exportSet,
arguments.CxxModulesDirectory);
return true;
}
static bool HandlePackageInfoMode(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
using arg_t = cmPackageInfoArguments;
using gen_t = cmExportBuildPackageInfoGenerator;
return HandleSpecialExportMode<arg_t, gen_t>(args, status);
}
static bool HandleSbomMode(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
if (!cmExperimental::HasSupportEnabled(
status.GetMakefile(), cmExperimental::Feature::GenerateSbom)) {
status.SetError("does not recognize sub-command SBOM");
return false;
}
struct SbomExportArguments
: public cmSbomArguments
, public ArgumentParser::ParseResult
{
ArgumentParser::NonEmpty<std::vector<std::string>> ExportSetNames;
using cmSbomArguments::Check;
using ArgumentParser::ParseResult::Check;
};
auto parser = cmArgumentParser<SbomExportArguments>{};
cmSbomArguments::Bind(parser);
parser.Bind("EXPORTS"_s, &SbomExportArguments::ExportSetNames);
std::vector<std::string> unknownArgs;
SbomExportArguments arguments = parser.Parse(args, &unknownArgs);
if (!arguments.Check(args[0], &unknownArgs, status)) {
return false;
}
if (arguments.ExportSetNames.empty()) {
status.SetError(cmStrCat(args[0], " missing EXPORTS."));
return false;
}
if (!arguments.Check(status) || !arguments.SetMetadataFromProject(status)) {
return false;
}
cmMakefile& mf = status.GetMakefile();
cmGlobalGenerator* gg = mf.GetGlobalGenerator();
std::string const dir =
arguments.GetDefaultDestination(mf.GetCurrentBinaryDirectory());
std::string const fpath = cmStrCat(dir, '/', arguments.GetPackageName());
if (gg->IsBuildSbomFile(fpath)) {
status.SetError(cmStrCat("SBOM command already specified for the file "_s,
cmSystemTools::GetFilenameNameView(fpath), '.'));
return false;
}
std::vector<cmExportSet*> sets;
sets.reserve(arguments.ExportSetNames.size());
for (std::string const& name : arguments.ExportSetNames) {
cm::optional<cmExportSet*> const exportSet =
GetExportSet(name, gg, status);
if (!exportSet) {
return false;
}
sets.push_back(*exportSet);
}
auto builder = cm::make_unique<cmBuildSbomGenerator>(arguments, sets, fpath);
cmBuildSbomGenerator* rawPtr = builder.get();
mf.AddBuildSbomGenerator(std::move(builder));
gg->AddBuildSbomGenerator(rawPtr);
return true;
}
static bool HandleSetupMode(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
struct SetupArguments
{
ArgumentParser::NonEmpty<std::string> ExportSetName;
ArgumentParser::NonEmpty<std::string> CxxModulesDirectory;
std::vector<std::vector<std::string>> PackageDependencyArgs;
std::vector<std::vector<std::string>> TargetArgs;
};
auto parser = cmArgumentParser<SetupArguments>{};
parser.Bind("SETUP"_s, &SetupArguments::ExportSetName);
if (cmExperimental::HasSupportEnabled(
status.GetMakefile(),
cmExperimental::Feature::ExportPackageDependencies)) {
parser.Bind("PACKAGE_DEPENDENCY"_s,
&SetupArguments::PackageDependencyArgs);
}
parser.Bind("TARGET"_s, &SetupArguments::TargetArgs);
std::vector<std::string> unknownArgs;
SetupArguments arguments = parser.Parse(args, &unknownArgs);
if (!unknownArgs.empty()) {
status.SetError("SETUP given unknown argument: \"" + unknownArgs.front() +
"\".");
return false;
}
cmMakefile& mf = status.GetMakefile();
cmGlobalGenerator* gg = mf.GetGlobalGenerator();
cmExportSetMap& setMap = gg->GetExportSets();
auto& exportSet = setMap[arguments.ExportSetName];
struct PackageDependencyArguments
{
std::string Enabled;
ArgumentParser::MaybeEmpty<std::vector<std::string>> ExtraArgs;
};
auto packageDependencyParser =
cmArgumentParser<PackageDependencyArguments>{}
.Bind("ENABLED"_s, &PackageDependencyArguments::Enabled)
.Bind("EXTRA_ARGS"_s, &PackageDependencyArguments::ExtraArgs);
for (auto const& packageDependencyArgs : arguments.PackageDependencyArgs) {
if (packageDependencyArgs.empty()) {
continue;
}
PackageDependencyArguments const packageDependencyArguments =
packageDependencyParser.Parse(
cmMakeRange(packageDependencyArgs).advance(1), &unknownArgs);
if (!unknownArgs.empty()) {
status.SetError(cmStrCat("PACKAGE_DEPENDENCY given unknown argument: \"",
unknownArgs.front(), "\"."));
return false;
}
auto& packageDependency =
exportSet.GetPackageDependencyForSetup(packageDependencyArgs.front());
if (!packageDependencyArguments.Enabled.empty()) {
if (packageDependencyArguments.Enabled == "AUTO") {
packageDependency.Enabled =
cmExportSet::PackageDependencyExportEnabled::Auto;
} else if (cmIsOff(packageDependencyArguments.Enabled)) {
packageDependency.Enabled =
cmExportSet::PackageDependencyExportEnabled::Off;
} else if (cmIsOn(packageDependencyArguments.Enabled)) {
packageDependency.Enabled =
cmExportSet::PackageDependencyExportEnabled::On;
} else {
status.SetError(
cmStrCat("Invalid enable setting for package dependency: \"",
packageDependencyArguments.Enabled, '"'));
return false;
}
}
cm::append(packageDependency.ExtraArguments,
packageDependencyArguments.ExtraArgs);
}
struct TargetArguments
{
std::string XcFrameworkLocation;
};
auto targetParser = cmArgumentParser<TargetArguments>{}.Bind(
"XCFRAMEWORK_LOCATION"_s, &TargetArguments::XcFrameworkLocation);
for (auto const& targetArgs : arguments.TargetArgs) {
if (targetArgs.empty()) {
continue;
}
TargetArguments const targetArguments =
targetParser.Parse(cmMakeRange(targetArgs).advance(1), &unknownArgs);
if (!unknownArgs.empty()) {
status.SetError(cmStrCat("TARGET given unknown argument: \"",
unknownArgs.front(), "\"."));
return false;
}
exportSet.SetXcFrameworkLocation(targetArgs.front(),
targetArguments.XcFrameworkLocation);
}
return true;
}
static bool HandlePackageMode(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
// Parse PACKAGE mode arguments.
enum Doing
{
DoingNone,
DoingPackage
};
Doing doing = DoingPackage;
std::string package;
for (unsigned int i = 1; i < args.size(); ++i) {
if (doing == DoingPackage) {
package = args[i];
doing = DoingNone;
} else {
std::ostringstream e;
e << "PACKAGE given unknown argument: " << args[i];
status.SetError(e.str());
return false;
}
}
// Verify the package name.
if (package.empty()) {
status.SetError("PACKAGE must be given a package name.");
return false;
}
char const* packageExpr = "^[A-Za-z0-9_.-]+$";
cmsys::RegularExpression packageRegex(packageExpr);
if (!packageRegex.find(package)) {
std::ostringstream e;
e << "PACKAGE given invalid package name \"" << package << "\". "
<< "Package names must match \"" << packageExpr << "\".";
status.SetError(e.str());
return false;
}
cmMakefile& mf = status.GetMakefile();
// CMP0090 decides both the default and what variable changes it.
switch (mf.GetPolicyStatus(cmPolicies::CMP0090)) {
case cmPolicies::WARN:
CM_FALLTHROUGH;
case cmPolicies::OLD:
// Default is to export, but can be disabled.
if (mf.IsOn("CMAKE_EXPORT_NO_PACKAGE_REGISTRY")) {
return true;
}
break;
case cmPolicies::NEW:
// Default is to not export, but can be enabled.
if (!mf.IsOn("CMAKE_EXPORT_PACKAGE_REGISTRY")) {
return true;
}
break;
}
// We store the current build directory in the registry as a value
// named by a hash of its own content. This is deterministic and is
// unique with high probability.
std::string const& outDir = mf.GetCurrentBinaryDirectory();
cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
std::string hash = hasher.HashString(outDir);
StorePackageRegistry(mf, package, outDir.c_str(), hash.c_str());
return true;
}
#if defined(_WIN32) && !defined(__CYGWIN__)
static void ReportRegistryError(cmMakefile& mf, std::string const& msg,
std::string const& key, long err)
{
std::ostringstream e;
e << msg << "\n"
<< " HKEY_CURRENT_USER\\" << key << "\n";
wchar_t winmsg[1024];
if (FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), winmsg, 1024, 0) > 0) {
e << "Windows reported:\n"
<< " " << cmsys::Encoding::ToNarrow(winmsg);
}
mf.IssueMessage(MessageType::WARNING, e.str());
}
static void StorePackageRegistry(cmMakefile& mf, std::string const& package,
char const* content, char const* hash)
{
std::string key = cmStrCat("Software\\Kitware\\CMake\\Packages\\", package);
HKEY hKey;
LONG err =
RegCreateKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(key).c_str(), 0,
0, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, 0, &hKey, 0);
if (err != ERROR_SUCCESS) {
ReportRegistryError(mf, "Cannot create/open registry key", key, err);
return;
}
std::wstring wcontent = cmsys::Encoding::ToWide(content);
err =
RegSetValueExW(hKey, cmsys::Encoding::ToWide(hash).c_str(), 0, REG_SZ,
(BYTE const*)wcontent.c_str(),
static_cast<DWORD>(wcontent.size() + 1) * sizeof(wchar_t));
RegCloseKey(hKey);
if (err != ERROR_SUCCESS) {
std::ostringstream msg;
msg << "Cannot set registry value \"" << hash << "\" under key";
ReportRegistryError(mf, msg.str(), key, err);
return;
}
}
#else
static void StorePackageRegistry(cmMakefile& mf, std::string const& package,
char const* content, char const* hash)
{
# if defined(__HAIKU__)
char dir[B_PATH_NAME_LENGTH];
if (find_directory(B_USER_SETTINGS_DIRECTORY, -1, false, dir, sizeof(dir)) !=
B_OK) {
return;
}
std::string fname = cmStrCat(dir, "/cmake/packages/", package);
# else
std::string fname;
if (!cmSystemTools::GetEnv("HOME", fname)) {
return;
}
cmSystemTools::ConvertToUnixSlashes(fname);
fname += "/.cmake/packages/";
fname += package;
# endif
cmSystemTools::MakeDirectory(fname);
fname += "/";
fname += hash;
if (!cmSystemTools::FileExists(fname)) {
cmGeneratedFileStream entry(fname, true);
if (entry) {
entry << content << "\n";
} else {
mf.IssueMessage(MessageType::WARNING,
cmStrCat("Cannot create package registry file:\n"
" ",
fname, '\n',
cmSystemTools::GetLastSystemError(), '\n'));
}
}
}
#endif
bool cmExportCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
if (args.empty()) {
return true;
}
static cmSubcommandTable const subcommand{
{ "TARGETS"_s, HandleTargetsMode },
{ "EXPORT"_s, HandleExportMode },
{ "SETUP"_s, HandleSetupMode },
{ "PACKAGE"_s, HandlePackageMode },
{ "PACKAGE_INFO"_s, HandlePackageInfoMode },
{ "SBOM"_s, HandleSbomMode },
};
return subcommand(args[0], args, status);
}