Ninja: Add install_prep targets for minimal builds

Add `install_prep/<component>` convenience targets to the Ninja
generators to build dependencies of install components, and
`install_prep/all` to build the dependencies of all install components.

This is similar in flavor to commit ba6b6fbc24 (Ninja: Add Build
Dependencies for Tests, 2026-04-07), but here for the install command.

Co-authored-by: Tyler Yankee <tyler.yankee@kitware.com>
Issue: #27643
This commit is contained in:
Taylor SasserandTyler Yankee authored and Tyler Yankee committed 2026-10-12 12:23:46 -04:00
1 parent 5ea2838ac6
commit 0ea2d28b54
109 files changed
+2067 -4

No files matched your search

+9
View File
@@ -47,6 +47,10 @@ are executed in order during installation.
and the order that install rules added in different subdirectories will run is
not guaranteed.
.. versionadded:: 4.5
Some generators provide ``install_prep/`` build targets that build only the
prerequisites of an installation. See :ref:`Install Preparation Targets`.
Common Options
""""""""""""""
@@ -122,6 +126,11 @@ signatures that specify them. The common options are:
:module:`CPackComponent` module. Global target ``list_install_components``
lists all available components.
.. versionadded:: 4.5
Some generators provide an ``install_prep/<component>`` build target that
builds the prerequisites of installing a component. See
:ref:`Install Preparation Targets`.
``EXCLUDE_FROM_ALL``
.. versionadded:: 3.6
+21
View File
@@ -31,6 +31,27 @@ Builtin Targets
Created only if the :prop_gbl:`INSTALL_PARALLEL` global property is ``ON``.
Runs the install step for each subdirectory independently and in parallel.
``install_prep/<component>``
.. versionadded:: 4.5
Created if the project has install rules, unless the
:variable:`CMAKE_SKIP_INSTALL_PREP_TARGETS` or
:variable:`CMAKE_SKIP_INSTALL_RULES` variable is enabled. Builds all known
build dependencies for the named install component. Components whose
names cannot be represented safely are excluded. See
:ref:`Install Preparation Targets` and :ref:`Target Names`.
``install_prep/all``
.. versionadded:: 4.5
Created if the project has install rules, unless the
:variable:`CMAKE_SKIP_INSTALL_PREP_TARGETS` or
:variable:`CMAKE_SKIP_INSTALL_RULES` variable is enabled. Builds all known
build dependencies for an installation that does not select a component.
See :ref:`Install Preparation Targets`.
``test_prep/<test-name>``
.. versionadded:: 4.4
+114 -4
View File
@@ -68,10 +68,14 @@ project-wide convenience targets.
:manual:`cmake-generators(7)` manual for the target names documented by
each generator.
Some convenience targets derive their names from other names in the project.
The ``test_prep/<name>`` targets use test names, which have their own
representability rules beyond those above. See also :command:`add_test` and
:variable:`CMAKE_TEST_BUILD_DEPENDS`.
Some convenience targets derive their names from other names in the project:
* The ``test_prep/<name>`` targets use test names, which have their own
representability rules beyond those above. See also :command:`add_test`
and :variable:`CMAKE_TEST_BUILD_DEPENDS`.
* The ``install_prep/<component>`` targets use install component names.
A component name may contain the characters allowed in target names above,
plus ``:`` and ``/``. See also :ref:`Install Preparation Targets`.
.. _`Executables`:
@@ -1773,3 +1777,109 @@ target properties with a limited set of names:
* .. versionadded:: 3.11
Properties named with a leading underscore (``_``)
or lowercase ASCII character.
Preparation Targets
===================
The :ref:`Ninja Generators` provide convenience targets that build only the
prerequisites of a later step, such as running tests or installing.
.. _`Install Preparation Targets`:
Install Preparation Targets
---------------------------
.. versionadded:: 4.5
If the project has install rules, the generators which support preparation
targets provide convenience build targets that build the known prerequisites
of an installation before running the installation, unless
:variable:`CMAKE_SKIP_INSTALL_PREP_TARGETS` or
:variable:`CMAKE_SKIP_INSTALL_RULES` is enabled:
``install_prep/all``
Prepare for an installation that does not select a component, such as
:option:`cmake --install \<build-dir\> <cmake --install>` without
:cmake-install-option:`--component`. This includes rules of every component,
but omits :command:`install` rules using the ``EXCLUDE_FROM_ALL`` option.
``install_prep/<component>``
Prepare to install ``<component>``. Rules for that component are included
even when they use the ``EXCLUDE_FROM_ALL`` option.
Building ``install_prep/all`` does not necessarily prepare the installation
of a particular component, because installing a component also runs that
component's ``EXCLUDE_FROM_ALL`` rules. Build the matching
``install_prep/<component>`` target before installing a specific component.
For example:
.. code-block:: console
cmake --build <build-dir> --config Release --target install_prep/Runtime
cmake --install <build-dir> --config Release --component Runtime
Only install rules active for the selected configuration contribute build
dependencies. For multi-config generators, the same configuration should
be used for the preparation and installation steps.
A component preparation target is generated only when its name can be
represented safely by the native build tool, as described under
:ref:`Target Names`. CMake warns about a component that has build
dependencies but whose name cannot be represented.
.. note::
A component named ``all`` does not get its own target because its name would
conflict with ``install_prep/all``. Its rules contribute to
``install_prep/all`` only when they do not use the ``EXCLUDE_FROM_ALL``
option.
CMake infers dependencies associated with installed target artifacts,
imported runtime artifacts, runtime dependency sets, C++ module BMIs, and
C++ module export metadata. Preparing BMIs or module export metadata builds
the targets that produce them.
Installing files with the ``FILES`` or ``PROGRAMS`` signatures of the
:command:`install` command, or a target's file sets or its
:prop_tgt:`PUBLIC_HEADER`, :prop_tgt:`PRIVATE_HEADER`, or :prop_tgt:`RESOURCE`
files, implies build dependencies only for files that the build produces,
i.e., target output artifacts, and outputs and byproducts of custom commands
and custom targets. How such a file is prepared depends on how it is produced:
* A target's output artifact, such as one named by :genex:`TARGET_FILE`, is
prepared by building that target.
* A file that is the output of a custom command attached to exactly one
target is prepared by running that custom command. The target which
installs the file (or the file set to which the file belongs), if any, is
not built.
* Any other generated file, such as a byproduct or the output of a custom
command attached to more than one target, is prepared by depending on the
file itself, which runs whatever produces it.
For example:
.. code-block:: cmake
# Preparation runs only the custom command, not the library build.
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/generated.h"
COMMAND ...)
add_library(A a.c "${CMAKE_CURRENT_BINARY_DIR}/generated.h")
set_property(TARGET A PROPERTY PUBLIC_HEADER
"${CMAKE_CURRENT_BINARY_DIR}/generated.h")
# Preparation runs gen, the producer of the byproduct.
add_custom_target(gen COMMAND ...
BYPRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/byproduct.h")
add_library(B b.c)
add_dependencies(B gen)
set_property(TARGET B PROPERTY PUBLIC_HEADER
"${CMAKE_CURRENT_BINARY_DIR}/byproduct.h")
Rules created by the ``DIRECTORY``, ``SCRIPT``, and ``CODE`` signatures of
the :command:`install` command, or by the deprecated
:command:`install_targets` command, do not imply build dependencies.
Install rules below a directory added with :prop_dir:`EXCLUDE_FROM_ALL` are
ignored, matching installation from the parent directory.
+1
View File
@@ -299,6 +299,7 @@ Variables that Change Behavior
/variable/CMAKE_PROJECT_TOP_LEVEL_INCLUDES
/variable/CMAKE_REQUIRE_FIND_PACKAGE_PackageName
/variable/CMAKE_SKIP_INSTALL_ALL_DEPENDENCY
/variable/CMAKE_SKIP_INSTALL_PREP_TARGETS
/variable/CMAKE_SKIP_TEST_ALL_DEPENDENCY
/variable/CMAKE_STAGING_PREFIX
/variable/CMAKE_SUBLIME_TEXT_2_ENV_SETTINGS
@@ -0,0 +1,17 @@
CMAKE_SKIP_INSTALL_PREP_TARGETS
-------------------------------
.. versionadded:: 4.5
Do not generate ``install_prep/<component>`` and ``install_prep/all`` build
targets.
When this variable is set to a true value, CMake does not generate the
preparation targets that build the known prerequisites of a subsequent
component-specific or full installation, even when using a generator
that supports them. See :ref:`Install Preparation Targets`. The
preparation targets are also not generated if the project has no install
rules or :variable:`CMAKE_SKIP_INSTALL_RULES` is enabled.
Set this variable in the top-level directory. It is off by default, in
which case the preparation targets are generated.
+2
View File
@@ -539,6 +539,8 @@ Entry const kEntries[] = {
"Do not include RPATHs in the build tree"_s },
{ "CMAKE_SKIP_INSTALL_ALL_DEPENDENCY"_s,
"If true, install target does not depend on all target; installation proceeds independently of build status."_s },
{ "CMAKE_SKIP_INSTALL_PREP_TARGETS"_s,
"Do not generate install_prep/<component> and install_prep/all build targets."_s },
{ "CMAKE_SKIP_INSTALL_RPATH"_s,
"Do not include RPATHs in the install tree"_s },
{ "CMAKE_SKIP_LINTING"_s,
+243
View File
@@ -51,6 +51,7 @@
#include "cmInstallGenerator.h"
#include "cmInstallRuntimeDependencySet.h"
#include "cmInstallSbomGenerator.h"
#include "cmLinkItem.h"
#include "cmLinkLineComputer.h"
#include "cmList.h"
#include "cmListFileCache.h"
@@ -2583,6 +2584,8 @@ void cmGlobalGenerator::ClearGeneratorMembers()
this->GeneratedByBuildIndex.clear();
this->GeneratedByBuildIndexComputed = false;
this->InstallPrepDependencies.clear();
this->Makefiles.clear();
this->LocalGenerators.clear();
@@ -3380,6 +3383,246 @@ std::string cmGlobalGenerator::GetTestBuildDependencyPath(
return target->GetFullPath(config);
}
std::vector<cmInstallGenerator::BuildDependency> const&
cmGlobalGenerator::GetInstallPrepDependencies(
cmInstallGenerator const* installer, std::string const& config)
{
auto const inserted = this->InstallPrepDependencies.emplace(
std::make_pair(installer, config),
std::vector<cmInstallGenerator::BuildDependency>());
if (inserted.second) {
installer->GetInstallBuildDependencies(config, inserted.first->second);
}
return inserted.first->second;
}
cmGlobalGenerator::InstallPrepTargets cmGlobalGenerator::GetInstallPrepTargets(
std::string const& config)
{
InstallPrepTargets prepTargets;
if (!this->AreInstallPrepTargetsEnabled()) {
return prepTargets;
}
auto isRepresentableComponent = [this](std::string const& component) {
return component != "all" &&
this->IsInstallPrepComponentRepresentable(component);
};
for (std::string const& component : this->InstallComponents) {
if (isRepresentableComponent(component)) {
prepTargets.Components[component];
}
}
auto addDependency = [](InstallPrepTargets::TargetMap& targets,
cmInstallGenerator::BuildDependency dependency) {
if (dependency.Type ==
cmInstallGenerator::BuildDependencyType::GeneratedFile) {
targets[dependency.Target].GeneratedFiles.insert(dependency.File);
return;
}
std::set<cmGeneratorTarget const*> visited;
std::function<void(cmGeneratorTarget const*,
cmInstallGenerator::BuildDependencyType)>
addTarget;
addTarget = [&addTarget, &targets,
&visited](cmGeneratorTarget const* target,
cmInstallGenerator::BuildDependencyType type) {
if (!target || !visited.insert(target).second) {
return;
}
if (!target->IsInBuildSystem() || target->IsImported()) {
for (cmLinkItem const& utility : target->GetUtilityItems()) {
addTarget(utility.Target,
cmInstallGenerator::BuildDependencyType::Normal);
}
return;
}
InstallPrepTargets::TargetRequirements& requirements = targets[target];
if (type == cmInstallGenerator::BuildDependencyType::InstallArtifact) {
requirements.InstallArtifact = true;
} else {
requirements.Normal = true;
}
};
addTarget(dependency.Target, dependency.Type);
};
for (auto const& localGen : this->LocalGenerators) {
if (this->IsExcluded(this->LocalGenerators.front().get(),
localGen.get())) {
continue;
}
for (auto const& installer :
localGen->GetMakefile()->GetInstallGenerators()) {
if (!installer->InstallsForConfig(config)) {
continue;
}
std::vector<cmInstallGenerator::BuildDependency> const& dependencies =
this->GetInstallPrepDependencies(installer.get(), config);
std::vector<std::string> components;
installer->GetInstallBuildDependencyComponents(components);
for (cmInstallGenerator::BuildDependency const& dependency :
dependencies) {
if (!dependency.Target) {
// A generated file that no target prepares.
if (dependency.Type ==
cmInstallGenerator::BuildDependencyType::GeneratedFile) {
if (installer->GetAllComponentsFlag()) {
for (auto const& component : prepTargets.Components) {
prepTargets.ComponentFiles[component.first].insert(
dependency.File);
}
} else {
for (std::string const& component : components) {
if (isRepresentableComponent(component)) {
prepTargets.ComponentFiles[component].insert(
dependency.File);
}
}
}
if (!installer->GetExcludeFromAll()) {
prepTargets.AllFiles.insert(dependency.File);
}
}
continue;
}
if (installer->GetAllComponentsFlag()) {
for (auto& component : prepTargets.Components) {
addDependency(component.second, dependency);
}
} else {
for (std::string const& component : components) {
if (isRepresentableComponent(component)) {
addDependency(prepTargets.Components[component], dependency);
}
}
}
if (!installer->GetExcludeFromAll()) {
addDependency(prepTargets.All, dependency);
}
}
}
}
return prepTargets;
}
bool cmGlobalGenerator::IsInstallPrepComponentRepresentable(
std::string const& component) const
{
static cmsys::RegularExpression const invalidComponentRegex(
"[^a-zA-Z0-9_.:+/-]|(^|/)\\.?\\.?($|/)");
cmsys::RegularExpressionMatch match;
return !component.empty() &&
!invalidComponentRegex.find(component.c_str(), match);
}
void cmGlobalGenerator::CheckInstallPrepTargets()
{
if (!this->AreInstallPrepTargetsEnabled()) {
return;
}
// Diagnose install rules with build dependencies that no preparation target
// builds.
std::vector<std::string> const configs =
this->Makefiles.front()->GetGeneratorConfigs(
cmMakefile::IncludeEmptyConfig);
std::set<std::string> unrepresentableComponents;
std::set<std::string> unpreparedFiles;
for (auto const& localGen : this->LocalGenerators) {
if (this->IsExcluded(this->LocalGenerators.front().get(),
localGen.get())) {
continue;
}
for (auto const& installer :
localGen->GetMakefile()->GetInstallGenerators()) {
std::vector<cmInstallGenerator::BuildDependency> dependencies;
for (std::string const& config : configs) {
if (installer->InstallsForConfig(config)) {
std::vector<cmInstallGenerator::BuildDependency> const&
configDependencies =
this->GetInstallPrepDependencies(installer.get(), config);
dependencies.insert(dependencies.end(), configDependencies.begin(),
configDependencies.end());
}
}
// A generated file that no target prepares.
if (!this->SupportsInstallPrepFileDependencies()) {
for (cmInstallGenerator::BuildDependency const& dependency :
dependencies) {
if (!dependency.Target &&
dependency.Type ==
cmInstallGenerator::BuildDependencyType::GeneratedFile &&
unpreparedFiles.insert(dependency.File).second) {
localGen->IssueDiagnostic(
cmDiagnostics::CMD_AUTHOR,
cmStrCat("An install rule installs or depends on file\n ",
dependency.File,
"\nwhich is generated but is not the unique output of "
"a build target, so \"install_prep/\" targets cannot "
"build it with this generator. Produce the file with "
"a custom command attached to a single target to "
"prepare it."),
installer->GetDiagnosticContext());
}
}
}
if (installer->GetAllComponentsFlag() || dependencies.empty()) {
continue;
}
std::vector<std::string> components;
installer->GetInstallBuildDependencyComponents(components);
for (std::string const& component : components) {
if (component == "all") {
// A component named "all" has no preparation target of its own,
// and install_prep/all omits EXCLUDE_FROM_ALL rules.
if (installer->GetExcludeFromAll()) {
localGen->IssueDiagnostic(
cmDiagnostics::CMD_AUTHOR,
"An install rule in the component named \"all\" uses "
"EXCLUDE_FROM_ALL and has build dependencies, but no "
"install_prep/ target builds them: install_prep/all omits "
"EXCLUDE_FROM_ALL rules, and the \"all\" component has no "
"preparation target of its own. Rename the component or "
"remove EXCLUDE_FROM_ALL.",
installer->GetDiagnosticContext());
}
} else if (!this->IsInstallPrepComponentRepresentable(component) &&
unrepresentableComponents.insert(component).second) {
localGen->IssueDiagnostic(
cmDiagnostics::CMD_AUTHOR,
cmStrCat("The install component \"", component,
"\" has build dependencies, but its name cannot be used "
"in a build target name with this generator. No "
"\"install_prep/\" target will be generated for it. "
"Rename the component to build its dependencies with an "
"\"install_prep/\" target."),
installer->GetDiagnosticContext());
}
}
}
}
}
bool cmGlobalGenerator::AreInstallPrepTargetsEnabled() const
{
return !this->Makefiles.empty() && this->InstallTargetEnabled &&
!this->Makefiles.front()->IsOn("CMAKE_SKIP_INSTALL_PREP_TARGETS") &&
!this->Makefiles.front()->IsOn("CMAKE_SKIP_INSTALL_RULES");
}
// If the file has no extension it's either a raw executable or might
// be a direct reference to a binary within a framework (bad practice!).
// This is where we change the path to point to the framework directory.
+49
View File
@@ -26,6 +26,7 @@
#include "cmDuration.h"
#include "cmExportFileGenerator.h"
#include "cmExportSet.h"
#include "cmInstallGenerator.h"
#include "cmLocalGenerator.h"
#include "cmSbomBuilder.h"
#include "cmStateSnapshot.h"
@@ -137,6 +138,44 @@ public:
std::string const& config,
cmTestGenerator::BuildDependencies const& deps) const;
struct InstallPrepTargets
{
struct TargetRequirements
{
bool Normal = false;
bool InstallArtifact = false;
std::set<std::string> GeneratedFiles;
};
using TargetMap = std::map<cmGeneratorTarget const*, TargetRequirements>;
TargetMap All;
std::map<std::string, TargetMap> Components;
// Generated files that no target prepares, such as byproducts installed
// by install(FILES). Only generators that support depending on them
// prepare these files.
std::set<std::string> AllFiles;
std::map<std::string, std::set<std::string>> ComponentFiles;
};
InstallPrepTargets GetInstallPrepTargets(std::string const& config);
// Whether install preparation targets can depend on a generated file that
// is not the unique output of a target.
virtual bool SupportsInstallPrepFileDependencies() const { return false; }
// Whether the name of an install component can be used in the name of its
// install preparation target.
virtual bool IsInstallPrepComponentRepresentable(
std::string const& component) const;
// Diagnose install rules that no install preparation target prepares.
// Generators that write install preparation targets call this once.
void CheckInstallPrepTargets();
bool AreInstallPrepTargetsEnabled() const;
/** Check whether the given name matches the current generator. */
virtual bool MatchesGeneratorName(std::string const& name) const
{
@@ -1049,6 +1088,16 @@ private:
std::unordered_set<std::string> GeneratedByBuildIndex;
bool GeneratedByBuildIndexComputed = false;
// The build dependencies of each install rule for each configuration,
// computed once and shared by CheckInstallPrepTargets and
// GetInstallPrepTargets.
std::map<std::pair<cmInstallGenerator const*, std::string>,
std::vector<cmInstallGenerator::BuildDependency>>
InstallPrepDependencies;
std::vector<cmInstallGenerator::BuildDependency> const&
GetInstallPrepDependencies(cmInstallGenerator const* installer,
std::string const& config);
friend class cmake;
void CreateGeneratorTargets(
TargetTypes targetTypes, cmMakefile* mf, cmLocalGenerator* lg,
+56
View File
@@ -647,6 +647,7 @@ void cmGlobalNinjaGenerator::Generate()
this->cmGlobalGenerator::Generate();
this->WriteAssumedSourceDependencies();
this->WriteInstallPrepTargets();
this->WriteTestPrepTargets();
this->WriteTargetAliases(*this->GetCommonFileStream());
this->WriteFolderTargets(*this->GetCommonFileStream());
@@ -1298,6 +1299,61 @@ void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies()
}
}
void cmGlobalNinjaGenerator::WriteInstallPrepTargets()
{
if (!this->AreInstallPrepTargetsEnabled()) {
return;
}
this->CheckInstallPrepTargets();
auto writeConfig = [this](std::string const& config, std::ostream& os) {
auto writePrepTarget =
[this, &config,
&os](std::string const& name,
cmGlobalGenerator::InstallPrepTargets::TargetMap const& targets,
std::set<std::string> const& files) {
cmNinjaBuild build("phony");
build.Outputs.push_back(this->ConvertToNinjaPath(name));
for (std::string const& file : files) {
build.ExplicitDeps.push_back(this->ConvertToNinjaPath(file));
}
for (auto const& target : targets) {
if (target.second.Normal || target.second.InstallArtifact) {
this->AppendTargetOutputs(target.first, build.ExplicitDeps, config,
DependOnTargetArtifact);
}
for (std::string const& file : target.second.GeneratedFiles) {
build.ExplicitDeps.push_back(this->ConvertToNinjaPath(file));
}
}
std::sort(build.ExplicitDeps.begin(), build.ExplicitDeps.end());
build.ExplicitDeps.erase(
std::unique(build.ExplicitDeps.begin(), build.ExplicitDeps.end()),
build.ExplicitDeps.end());
this->WriteBuild(os, build);
};
InstallPrepTargets const prepTargets = this->GetInstallPrepTargets(config);
std::set<std::string> const noFiles;
for (auto const& component : prepTargets.Components) {
auto const it = prepTargets.ComponentFiles.find(component.first);
std::set<std::string> const& files =
it == prepTargets.ComponentFiles.end() ? noFiles : it->second;
writePrepTarget(cmStrCat("install_prep/", component.first),
component.second, files);
}
writePrepTarget("install_prep/all", prepTargets.All, prepTargets.AllFiles);
};
if (this->IsMultiConfig()) {
for (std::string const& config : this->GetConfigNames()) {
writeConfig(config, *this->GetConfigFileStream(config));
}
} else {
writeConfig(this->GetConfigNames().front(), *this->GetCommonFileStream());
}
}
void cmGlobalNinjaGenerator::WriteTestPrepTargets()
{
auto writeConfig = [this](std::string const& config, std::ostream& os) {
+3
View File
@@ -103,6 +103,8 @@ public:
bool IsIPOSupported() const override { return true; }
bool SupportsInstallPrepFileDependencies() const override { return true; }
/**
* Write a build statement @a build to @a os.
* @warning no escaping of any kind is done here.
@@ -532,6 +534,7 @@ private:
void WriteDisclaimer(std::ostream& os) const;
void WriteAssumedSourceDependencies();
void WriteInstallPrepTargets();
void WriteTestPrepTargets();
void WriteTargetAliases(std::ostream& os);
+3
View File
@@ -866,6 +866,7 @@ bool CreateInstallGeneratorsForTarget(
privateHeaderGenerator = CreateInstallFilesGenerator(
helper.Makefile, absFiles, privateHeaderArgs, false,
helper.GetIncludeDestination(&privateHeaderArgs), configurations);
privateHeaderGenerator->SetOwningTarget(target.GetName());
} else {
helper.Makefile->IssueDiagnostic(
cmDiagnostics::CMD_AUTHOR,
@@ -889,6 +890,7 @@ bool CreateInstallGeneratorsForTarget(
publicHeaderGenerator = CreateInstallFilesGenerator(
helper.Makefile, absFiles, publicHeaderArgs, false,
helper.GetIncludeDestination(&publicHeaderArgs), configurations);
publicHeaderGenerator->SetOwningTarget(target.GetName());
} else {
helper.Makefile->IssueDiagnostic(
cmDiagnostics::CMD_AUTHOR,
@@ -912,6 +914,7 @@ bool CreateInstallGeneratorsForTarget(
resourceGenerator = CreateInstallFilesGenerator(
helper.Makefile, absFiles, resourceArgs, false,
resourceArgs.GetDestination(), configurations);
resourceGenerator->SetOwningTarget(target.GetName());
} else if (!target.IsAppBundleOnApple()) {
helper.Makefile->IssueDiagnostic(
cmDiagnostics::CMD_AUTHOR,
@@ -43,6 +43,15 @@ bool cmInstallCxxModuleBmiGenerator::Compute(cmLocalGenerator* lg)
return true;
}
void cmInstallCxxModuleBmiGenerator::GetInstallBuildDependencies(
std::string const& config, std::vector<BuildDependency>& dependencies) const
{
if (this->Target && this->Target->HaveInterfaceCxx20ModuleSources() &&
!this->GetDestination(config).empty()) {
dependencies.push_back({ this->Target, BuildDependencyType::Normal });
}
}
std::string cmInstallCxxModuleBmiGenerator::GetScriptLocation(
std::string const& config) const
{
+4
View File
@@ -29,6 +29,10 @@ public:
bool Compute(cmLocalGenerator* lg) override;
void GetInstallBuildDependencies(
std::string const& config,
std::vector<BuildDependency>& dependencies) const override;
std::string const& GetFilePermissions() const
{
return this->FilePermissions;
+19
View File
@@ -10,11 +10,13 @@
#include "cmDiagnosticContext.h"
#include "cmExportInstallFileGenerator.h"
#include "cmExportSet.h"
#include "cmGeneratorTarget.h"
#include "cmInstallType.h"
#include "cmLocalGenerator.h"
#include "cmScriptGenerator.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTargetExport.h"
cmInstallExportGenerator::cmInstallExportGenerator(
cmExportSet* exportSet, std::string destination, std::string filePermissions,
@@ -42,6 +44,23 @@ bool cmInstallExportGenerator::Compute(cmLocalGenerator* lg)
return this->ExportSet->Compute(lg);
}
void cmInstallExportGenerator::GetInstallBuildDependencies(
std::string const&, std::vector<BuildDependency>& dependencies) const
{
if (this->CxxModulesDirectory.empty()) {
return;
}
// Module export metadata is generated during the target's build.
for (auto const& targetExport : this->ExportSet->GetTargetExports()) {
auto const* target = targetExport->Target;
if (!targetExport->NamelinkOnly && target &&
target->HaveCxx20ModuleSources()) {
dependencies.push_back({ target, BuildDependencyType::Normal });
}
}
}
std::string cmInstallExportGenerator::TempDirCalculate() const
{
// Choose a temporary directory in which to generate the import
+4
View File
@@ -42,6 +42,10 @@ public:
bool Compute(cmLocalGenerator* lg) override;
void GetInstallBuildDependencies(
std::string const& config,
std::vector<BuildDependency>& dependencies) const override;
cmLocalGenerator* GetLocalGenerator() const { return this->LocalGenerator; }
std::string const& GetNamespace() const { return this->Namespace; }
+15
View File
@@ -93,6 +93,21 @@ bool cmInstallFileSetGenerator::Compute(cmLocalGenerator* lg)
return true;
}
void cmInstallFileSetGenerator::GetInstallBuildDependencies(
std::string const& config, std::vector<BuildDependency>& dependencies) const
{
if (!this->FileSet) {
return;
}
std::vector<std::string> files;
for (auto const& dirEntry : this->CalculateFilesPerDir(config)) {
files.insert(files.end(), dirEntry.second.begin(), dirEntry.second.end());
}
AddGeneratedFileDependencies(this->LocalGenerator->GetGlobalGenerator(),
config, this->Target, files, dependencies);
}
std::string cmInstallFileSetGenerator::GetDestination() const
{
this->CheckAbsoluteDestination(this->Destination, this->LocalGenerator);
+4
View File
@@ -29,6 +29,10 @@ public:
bool Compute(cmLocalGenerator* lg) override;
void GetInstallBuildDependencies(
std::string const& config,
std::vector<BuildDependency>& dependencies) const override;
struct DestinationContext
{
std::string UnescapedDestination;
+12
View File
@@ -8,6 +8,7 @@
#include "cmGeneratorExpression.h"
#include "cmInstallType.h"
#include "cmList.h"
#include "cmLocalGenerator.h"
cmInstallFilesGenerator::cmInstallFilesGenerator(
std::vector<std::string> const& files, std::string const& dest,
@@ -48,9 +49,20 @@ cmInstallFilesGenerator::~cmInstallFilesGenerator() = default;
bool cmInstallFilesGenerator::Compute(cmLocalGenerator* lg)
{
this->LocalGenerator = lg;
if (!this->OwningTargetName.empty()) {
this->OwningTarget = lg->FindGeneratorTargetToUse(this->OwningTargetName);
}
return true;
}
void cmInstallFilesGenerator::GetInstallBuildDependencies(
std::string const& config, std::vector<BuildDependency>& dependencies) const
{
AddGeneratedFileDependencies(this->LocalGenerator->GetGlobalGenerator(),
config, this->OwningTarget,
this->GetFiles(config), dependencies);
}
std::string cmInstallFilesGenerator::GetDestination(
std::string const& config) const
{
+13
View File
@@ -6,11 +6,13 @@
#include <iosfwd>
#include <string>
#include <utility>
#include <vector>
#include "cmInstallGenerator.h"
class cmDiagnosticContext;
class cmGeneratorTarget;
class cmLocalGenerator;
/** \class cmInstallFilesGenerator
@@ -30,11 +32,20 @@ public:
bool Compute(cmLocalGenerator* lg) override;
void GetInstallBuildDependencies(
std::string const& config,
std::vector<BuildDependency>& dependencies) const override;
std::string GetDestination(std::string const& config) const;
std::string GetRename(std::string const& config) const;
std::vector<std::string> GetFiles(std::string const& config) const;
bool GetOptional() const { return this->Optional; }
void SetOwningTarget(std::string targetName)
{
this->OwningTargetName = std::move(targetName);
}
protected:
void GenerateScriptActions(std::ostream& os, Indent indent) override;
void GenerateScriptForConfig(std::ostream& os, std::string const& config,
@@ -47,6 +58,8 @@ protected:
std::vector<std::string> const Files;
std::string const FilePermissions;
std::string const Rename;
std::string OwningTargetName;
cmGeneratorTarget const* OwningTarget = nullptr;
bool const Programs;
bool const Optional;
};
+21
View File
@@ -8,6 +8,8 @@
#include <cm/string_view>
#include "cmDiagnostics.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmStringAlgorithms.h"
@@ -282,6 +284,25 @@ std::string cmInstallGenerator::GetDestDirPath(std::string const& file)
return toDestDirPath;
}
void cmInstallGenerator::AddGeneratedFileDependencies(
cmGlobalGenerator* gg, std::string const& config,
cmGeneratorTarget const* target, std::vector<std::string> const& files,
std::vector<BuildDependency>& dependencies)
{
for (std::string const& file : files) {
if (cmGeneratorTarget const* owner = gg->FindOutputOwningTarget(file)) {
dependencies.push_back(
{ owner, BuildDependencyType::GeneratedFile, file });
} else if (cmGeneratorTarget const* linker =
gg->FindArtifactOwningTarget(file, config)) {
dependencies.push_back({ linker, BuildDependencyType::Normal });
} else if (gg->IsGeneratedByBuild(file)) {
dependencies.push_back(
{ target, BuildDependencyType::GeneratedFile, file });
}
}
}
void cmInstallGenerator::AddTweak(std::ostream& os, Indent indent,
std::string const& config,
std::string const& file,
+48
View File
@@ -7,6 +7,7 @@
#include <functional>
#include <iosfwd>
#include <string>
#include <utility>
#include <vector>
#include "cmDiagnosticContext.h"
@@ -16,6 +17,8 @@
class cmLocalGenerator;
class cmMakefile;
class cmListFileBacktrace;
class cmGeneratorTarget;
class cmGlobalGenerator;
/** \class cmInstallGenerator
* \brief Support class for generating install scripts.
@@ -24,6 +27,28 @@ class cmListFileBacktrace;
class cmInstallGenerator : public cmScriptGenerator
{
public:
enum class BuildDependencyType
{
Normal,
InstallArtifact,
GeneratedFile,
};
struct BuildDependency
{
BuildDependency(cmGeneratorTarget const* target, BuildDependencyType type,
std::string file = std::string())
: Target(target)
, Type(type)
, File(std::move(file))
{
}
cmGeneratorTarget const* Target;
BuildDependencyType Type;
std::string File; // only for GeneratedFile dependencies
};
enum MessageLevel
{
MessageDefault,
@@ -75,6 +100,19 @@ public:
virtual bool Compute(cmLocalGenerator*) { return true; }
virtual void GetInstallBuildDependencies(std::string const&,
std::vector<BuildDependency>&) const
{
}
virtual void GetInstallBuildDependencyComponents(
std::vector<std::string>& components) const
{
if (!this->Component.empty()) {
components.push_back(this->Component);
}
}
std::string const& GetComponent() const { return this->Component; }
bool GetExcludeFromAll() const { return this->ExcludeFromAll; }
@@ -110,6 +148,16 @@ protected:
std::vector<std::string> const& files,
TweakMethod const& tweak);
// Add dependencies for the files that the build produces among the given
// files. A file linked by a target depends on building that target. Any
// other generated file is attributed to the target owning the custom
// command that produces it or, if no single target owns it, such as for a
// byproduct, to the installing target, if any.
static void AddGeneratedFileDependencies(
cmGlobalGenerator* gg, std::string const& config,
cmGeneratorTarget const* target, std::vector<std::string> const& files,
std::vector<BuildDependency>& dependencies);
// Information shared by most generator types.
std::string Destination;
std::string const Component;
@@ -24,6 +24,8 @@
#include "cmScriptGenerator.h"
#include "cmStringAlgorithms.h"
class cmGeneratorTarget;
namespace {
template <typename T, typename F>
void WriteMultiArgument(std::ostream& os, cm::string_view keyword,
@@ -113,6 +115,29 @@ bool cmInstallGetRuntimeDependenciesGenerator::Compute(cmLocalGenerator* lg)
return true;
}
void cmInstallGetRuntimeDependenciesGenerator::GetInstallBuildDependencies(
std::string const&, std::vector<BuildDependency>& dependencies) const
{
std::vector<cmGeneratorTarget const*> targets;
this->RuntimeDependencySet->GetInstallBuildDependencies(targets);
for (cmGeneratorTarget const* target : targets) {
dependencies.push_back({ target, BuildDependencyType::Normal });
}
}
void cmInstallGetRuntimeDependenciesGenerator::
GetInstallBuildDependencyComponents(
std::vector<std::string>& components) const
{
if (!this->LibraryComponent.empty()) {
components.push_back(this->LibraryComponent);
}
if (!this->FrameworkComponent.empty() &&
this->FrameworkComponent != this->LibraryComponent) {
components.push_back(this->FrameworkComponent);
}
}
void cmInstallGetRuntimeDependenciesGenerator::GenerateScript(std::ostream& os)
{
// Track indentation.
@@ -32,6 +32,13 @@ public:
bool Compute(cmLocalGenerator* lg) override;
void GetInstallBuildDependencies(
std::string const& config,
std::vector<BuildDependency>& dependencies) const override;
void GetInstallBuildDependencyComponents(
std::vector<std::string>& components) const override;
protected:
void GenerateScript(std::ostream& os) override;
@@ -57,6 +57,12 @@ bool cmInstallImportedRuntimeArtifactsGenerator::Compute(cmLocalGenerator* lg)
return true;
}
void cmInstallImportedRuntimeArtifactsGenerator::GetInstallBuildDependencies(
std::string const&, std::vector<BuildDependency>& dependencies) const
{
dependencies.push_back({ this->Target, BuildDependencyType::Normal });
}
std::string cmInstallImportedRuntimeArtifactsGenerator::GetDestination(
std::string const& config) const
{
@@ -24,6 +24,10 @@ public:
bool Compute(cmLocalGenerator* lg) override;
void GetInstallBuildDependencies(
std::string const& config,
std::vector<BuildDependency>& dependencies) const override;
cmGeneratorTarget* GetTarget() const { return this->Target; }
bool GetOptional() const { return this->Optional; }
+28
View File
@@ -51,6 +51,28 @@ std::string cmInstallRuntimeDependencySet::TargetItem::GetItemPath(
return this->Target->GetTarget()->GetFullPath(config);
}
cmGeneratorTarget const*
cmInstallRuntimeDependencySet::TargetItem::GetGeneratorTarget() const
{
return this->Target->GetTarget();
}
void cmInstallRuntimeDependencySet::GetInstallBuildDependencies(
std::vector<cmGeneratorTarget const*>& dependencies) const
{
auto appendTargets =
[&dependencies](std::vector<std::unique_ptr<Item>> const& items) {
for (auto const& item : items) {
if (cmGeneratorTarget const* target = item->GetGeneratorTarget()) {
dependencies.push_back(target);
}
}
};
appendTargets(this->Executables);
appendTargets(this->Libraries);
appendTargets(this->Modules);
}
namespace {
std::set<cmGeneratorTarget const*> const& GetTargetDependsClosure(
std::map<cmGeneratorTarget const*, std::set<cmGeneratorTarget const*>>&
@@ -93,3 +115,9 @@ std::string cmInstallRuntimeDependencySet::ImportedTargetItem::GetItemPath(
{
return this->Target->GetTarget()->GetFullPath(config);
}
cmGeneratorTarget const*
cmInstallRuntimeDependencySet::ImportedTargetItem::GetGeneratorTarget() const
{
return this->Target->GetTarget();
}
+12
View File
@@ -41,6 +41,11 @@ public:
virtual std::string GetItemPath(std::string const& config) const = 0;
virtual cmGeneratorTarget const* GetGeneratorTarget() const
{
return nullptr;
}
virtual void AddPostExcludeFiles(
std::string const& /*config*/, std::set<std::string>& /*files*/,
cmInstallRuntimeDependencySet* /*set*/) const
@@ -58,6 +63,8 @@ public:
std::string GetItemPath(std::string const& config) const override;
cmGeneratorTarget const* GetGeneratorTarget() const override;
void AddPostExcludeFiles(
std::string const& config, std::set<std::string>& files,
cmInstallRuntimeDependencySet* set) const override;
@@ -76,6 +83,8 @@ public:
std::string GetItemPath(std::string const& config) const override;
cmGeneratorTarget const* GetGeneratorTarget() const override;
private:
cmInstallImportedRuntimeArtifactsGenerator* Target;
};
@@ -145,6 +154,9 @@ public:
Item* GetBundleExecutable() const { return this->BundleExecutable; }
void GetInstallBuildDependencies(
std::vector<cmGeneratorTarget const*>& dependencies) const;
bool Empty() const
{
return this->Executables.empty() && this->Libraries.empty() &&
+9
View File
@@ -558,6 +558,15 @@ bool cmInstallTargetGenerator::Compute(cmLocalGenerator* lg)
return true;
}
void cmInstallTargetGenerator::GetInstallBuildDependencies(
std::string const& config, std::vector<BuildDependency>& dependencies) const
{
if (!this->GetFiles(config).From.empty()) {
dependencies.push_back(
{ this->Target, BuildDependencyType::InstallArtifact });
}
}
void cmInstallTargetGenerator::PreReplacementTweaks(std::ostream& os,
Indent indent,
std::string const& config,
+4
View File
@@ -62,6 +62,10 @@ public:
bool Compute(cmLocalGenerator* lg) override;
void GetInstallBuildDependencies(
std::string const& config,
std::vector<BuildDependency>& dependencies) const override;
cmGeneratorTarget* GetTarget() const { return this->Target; }
bool IsImportLibrary() const { return this->ImportLibrary; }
+3
View File
@@ -292,6 +292,9 @@ if(CMAKE_GENERATOR MATCHES "Ninja")
-DRunCMake_GENERATOR_IS_MULTI_CONFIG=${_isMultiConfig})
endif()
add_RunCMake_test(InstallParallel)
if(CMAKE_GENERATOR MATCHES "Ninja")
add_RunCMake_test(InstallPrep)
endif()
add_RunCMake_test(CTest)
add_RunCMake_test(CTestLaunch)
@@ -176,6 +176,19 @@ function (run_cxx_module_test_rebuild directory)
run_cxx_module_test("${directory}" ${ARGN})
endfunction ()
function (run_cxx_module_install_prep_test kind)
set(test "install-prep-${kind}")
set(prefix "${RunCMake_BINARY_DIR}/${test}-install")
file(REMOVE_RECURSE "${prefix}")
set(RunCMake_CXXModules_INSTALL 0)
run_cxx_module_test_target("${test}" "install_prep/${kind}"
"${test}" "-DCMAKE_INSTALL_PREFIX=${prefix}")
set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${test}-build")
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command("${test}-install" "${CMAKE_COMMAND}" --install . --config Debug)
endfunction ()
if (RunCMake_GENERATOR MATCHES "Ninja")
if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
set(ninja_cmp0154_target "CMakeFiles/ninja_cmp0154.dir/Debug/unrelated.cxx${CMAKE_CXX_OUTPUT_EXTENSION}")
@@ -380,6 +393,17 @@ endif ()
# All of the following tests perform installation.
set(RunCMake_CXXModules_INSTALL 1)
if (RunCMake_GENERATOR MATCHES "Ninja" AND
NOT CMake_TEST_MODULE_VARIANT MATCHES "^Build")
if ("install_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION)
run_cxx_module_install_prep_test(BMI)
endif ()
if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION)
run_cxx_module_install_prep_test(Metadata)
endif ()
include("${CMAKE_CURRENT_LIST_DIR}/install-prep.cmake")
endif ()
# Tests which install BMIs
if ("install_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION)
run_cxx_module_test(install-bmi)
@@ -0,0 +1,4 @@
set_property(TARGET "export_interfaces"
PROPERTY IMPORTED_CXX_MODULES_DEBUG
"importable=${_IMPORT_PREFIX}/lib/cxx/miu/importable.cxx"
)
@@ -0,0 +1,4 @@
set_property(TARGET "export_interfaces"
PROPERTY IMPORTED_CXX_MODULES_RELEASE
"importable=${_IMPORT_PREFIX}/lib/cxx/miu/importable.cxx"
)
@@ -0,0 +1,12 @@
set(bmi)
foreach (ext IN ITEMS gcm ifc pcm)
set(path "${prefix}/lib/cxx/bmi/importable.${ext}")
if (EXISTS "${path}" AND NOT IS_DIRECTORY "${path}")
set(bmi "${path}")
break ()
endif ()
endforeach ()
if (NOT bmi)
list(APPEND RunCMake_TEST_FAILED "The prepared BMI was not installed.")
endif ()
@@ -0,0 +1,40 @@
cmake_minimum_required(VERSION 4.4)
project(cxx_modules_install_prep_bmi CXX)
include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
add_library(install_bmi STATIC)
target_sources(install_bmi
PUBLIC
FILE_SET CXX_MODULES
BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}"
FILES importable.cxx)
target_compile_features(install_bmi PUBLIC cxx_std_20)
set(install_targets install_bmi)
set(archive_destination "lib")
if (prep_test)
set_property(TARGET install_bmi PROPERTY EXCLUDE_FROM_ALL 1)
add_library(no_modules STATIC ../install-prep-never-build.cxx)
add_library(unrelated STATIC ../install-prep-never-build.cxx)
# Private module file sets have no installable BMIs.
add_library(private_module STATIC EXCLUDE_FROM_ALL)
target_sources(private_module PRIVATE FILE_SET CXX_MODULES FILES private.cxx)
target_compile_features(private_module PRIVATE cxx_std_20)
list(APPEND install_targets no_modules private_module)
set(archive_destination "artifacts")
set(archive_options COMPONENT Artifacts EXCLUDE_FROM_ALL)
set(source_options FILE_SET CXX_MODULES DESTINATION "sources"
COMPONENT Sources EXCLUDE_FROM_ALL)
endif ()
set(bmi_destination "lib/cxx/bmi")
if (prep_empty_destination)
set(bmi_destination "$<0:lib/cxx/bmi>")
endif ()
install(TARGETS ${install_targets}
ARCHIVE DESTINATION "${archive_destination}" ${archive_options}
${source_options}
CXX_MODULES_BMI DESTINATION "${bmi_destination}" COMPONENT BMI ${prep_rule_options})
@@ -0,0 +1,6 @@
export module importable;
export int from_import()
{
return 0;
}
@@ -0,0 +1,4 @@
export module private_module;
static_assert(false,
"install_prep must not build an uninstalled private module");
@@ -0,0 +1,14 @@
set(metadata "${prefix}/lib/cmake/export_interfaces/cxx-modules/target-export__interfaces-Debug.cmake")
if (NOT EXISTS "${metadata}")
list(APPEND RunCMake_TEST_FAILED "The prepared module export metadata was not installed.")
return ()
endif ()
execute_process(
COMMAND "${CMAKE_COMMAND}" -E compare_files --ignore-eol
"${CMAKE_CURRENT_LIST_DIR}/expect/install-prep-export-interfaces-Debug.cmake"
"${metadata}"
RESULT_VARIABLE result)
if (NOT result EQUAL 0)
list(APPEND RunCMake_TEST_FAILED "Installed module export metadata does not match the expected file.")
endif ()
@@ -0,0 +1,36 @@
cmake_minimum_required(VERSION 4.4)
project(cxx_modules_install_prep_metadata CXX)
include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
add_library(export_interfaces STATIC)
target_sources(export_interfaces PUBLIC FILE_SET CXX_MODULES FILES importable.cxx)
target_compile_features(export_interfaces PUBLIC cxx_std_20)
set(install_targets export_interfaces)
set(archive_destination "lib")
if (prep_test)
set_property(TARGET export_interfaces PROPERTY EXCLUDE_FROM_ALL 1)
# Exporting a target without modules must not build it for module metadata.
add_library(no_modules STATIC ../install-prep-never-build.cxx)
add_library(unrelated STATIC ../install-prep-never-build.cxx)
list(APPEND install_targets no_modules)
set(archive_destination "artifacts")
set(archive_options COMPONENT Artifacts EXCLUDE_FROM_ALL)
set(source_options COMPONENT Sources EXCLUDE_FROM_ALL)
endif ()
install(TARGETS ${install_targets}
EXPORT CXXModules
ARCHIVE DESTINATION "${archive_destination}" ${archive_options}
FILE_SET CXX_MODULES DESTINATION "lib/cxx/miu" ${source_options})
set(module_options CXX_MODULES_DIRECTORY "cxx-modules")
if (prep_no_module_directory)
set(module_options)
endif ()
install(EXPORT CXXModules
DESTINATION "lib/cmake/export_interfaces"
COMPONENT Metadata
${module_options}
${prep_rule_options})
@@ -0,0 +1,6 @@
export module importable;
export int from_import()
{
return 0;
}
@@ -0,0 +1,51 @@
if (EXISTS "${prefix}")
list(APPEND RunCMake_TEST_FAILED "Preparation ran installation.")
return ()
endif ()
if (kind STREQUAL "GeneratedSource" AND
NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/generated.cxx")
list(APPEND RunCMake_TEST_FAILED "Preparation did not generate the module source.")
return ()
endif ()
if (expect_build)
set(bmi_dir "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/${module_target}.dir")
if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
string(APPEND bmi_dir "/${config}")
endif ()
set(bmi)
foreach (ext IN ITEMS gcm ifc pcm)
set(path "${bmi_dir}/importable.${ext}")
if (EXISTS "${path}" AND NOT IS_DIRECTORY "${path}")
set(bmi "${path}")
break ()
endif ()
endforeach ()
if (NOT bmi)
list(APPEND RunCMake_TEST_FAILED "Preparation did not build the module BMI.")
return ()
endif ()
if (kind STREQUAL "BMI")
set(script "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/${module_target}.dir/install-cxx-module-bmi-${config}.cmake")
if (NOT EXISTS "${script}")
list(APPEND RunCMake_TEST_FAILED "Preparation did not generate the BMI install script.")
endif ()
else ()
file(GLOB metadata
"${RunCMake_TEST_BINARY_DIR}/CMakeFiles/Export/*/cxx-modules/target-export__interfaces-${config}.cmake")
if (NOT metadata)
list(APPEND RunCMake_TEST_FAILED "Preparation did not generate module export metadata.")
endif ()
endif ()
else ()
file(GLOB_RECURSE bmis LIST_DIRECTORIES FALSE
"${RunCMake_TEST_BINARY_DIR}/*.gcm"
"${RunCMake_TEST_BINARY_DIR}/*.ifc"
"${RunCMake_TEST_BINARY_DIR}/*.pcm")
if (bmis)
list(APPEND RunCMake_TEST_FAILED "Preparation built unneeded BMIs: ${bmis}")
endif ()
endif ()
string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
@@ -0,0 +1,50 @@
if (EXISTS "${prefix}/artifacts")
list(APPEND RunCMake_TEST_FAILED "Installation included an excluded archive.")
endif ()
if (kind STREQUAL "GeneratedSource")
execute_process(
COMMAND "${CMAKE_COMMAND}" -E compare_files --ignore-eol
"${RunCMake_TEST_SOURCE_DIR}/importable.cxx"
"${prefix}/sources/generated.cxx"
RESULT_VARIABLE result)
if (NOT result EQUAL 0)
list(APPEND RunCMake_TEST_FAILED "The installed generated module source does not match the input.")
endif ()
string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
return ()
endif ()
if (kind STREQUAL "BMI")
set(bmi)
foreach (ext IN ITEMS gcm ifc pcm)
set(path "${prefix}/lib/cxx/bmi/importable.${ext}")
if (EXISTS "${path}" AND NOT IS_DIRECTORY "${path}")
set(bmi "${path}")
break ()
endif ()
endforeach ()
if (NOT bmi)
list(APPEND RunCMake_TEST_FAILED "The prepared BMI was not installed.")
endif ()
else ()
set(metadata "${prefix}/lib/cmake/export_interfaces/cxx-modules/target-export__interfaces-${config}.cmake")
if (NOT EXISTS "${metadata}")
list(APPEND RunCMake_TEST_FAILED "The prepared module export metadata was not installed.")
return ()
endif ()
execute_process(
COMMAND "${CMAKE_COMMAND}" -E compare_files --ignore-eol
"${CMAKE_CURRENT_LIST_DIR}/expect/install-prep-export-interfaces-${config}.cmake"
"${metadata}"
RESULT_VARIABLE result)
if (NOT result EQUAL 0)
list(APPEND RunCMake_TEST_FAILED "Installed module export metadata does not match the expected file.")
endif ()
endif ()
if (EXISTS "${prefix}/${source_destination}")
list(APPEND RunCMake_TEST_FAILED "Installation included an excluded source file set.")
endif ()
string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
@@ -0,0 +1 @@
#error "install_prep must not build this target"
@@ -0,0 +1,90 @@
function (run_cxx_module_install_prep kind case component config expect_build)
cmake_parse_arguments(PARSE_ARGV 5 prep
"EXCLUDE_FROM_ALL;EMPTY_DESTINATION;NO_MODULE_DIRECTORY" "CONFIGURATIONS" "")
set(test "install-prep-${kind}-${case}")
if (kind STREQUAL "GeneratedSource")
set(directory install-prep)
set(source_destination sources)
elseif (kind STREQUAL "BMI")
set(directory install-prep-BMI)
set(module_target install_bmi)
set(source_destination sources)
else ()
set(directory install-prep-Metadata)
set(module_target export_interfaces)
set(source_destination lib/cxx/miu)
endif ()
set(RunCMake_TEST_SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/${directory}")
set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/${test}-build")
set(prefix "${RunCMake_BINARY_DIR}/${test}-install")
file(REMOVE_RECURSE "${prefix}")
set(rule_options)
if (prep_EXCLUDE_FROM_ALL)
list(APPEND rule_options EXCLUDE_FROM_ALL)
endif ()
if (prep_CONFIGURATIONS)
list(APPEND rule_options CONFIGURATIONS "${prep_CONFIGURATIONS}")
endif ()
string(REPLACE ";" "\\;" rule_options "${rule_options}")
set(RunCMake_TEST_OPTIONS
"-DCMAKE_INSTALL_PREFIX=${prefix}"
"-Dprep_test=ON"
"-Dprep_rule_options=${rule_options}"
"-Dprep_empty_destination=${prep_EMPTY_DESTINATION}"
"-Dprep_no_module_directory=${prep_NO_MODULE_DIRECTORY}"
"-DCMake_TEST_MODULE_COMPILATION_RULES=${CMake_TEST_MODULE_COMPILATION_RULES}"
)
if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
list(APPEND RunCMake_TEST_OPTIONS "-DCMAKE_CONFIGURATION_TYPES=${config}")
else ()
list(APPEND RunCMake_TEST_OPTIONS "-DCMAKE_BUILD_TYPE=${config}")
endif ()
run_cmake("${test}")
set(RunCMake_TEST_NO_CLEAN 1)
set(RunCMake-check-file "install-prep-build-check.cmake")
run_cmake_command("${test}-build" "${CMAKE_COMMAND}"
--build . --config "${config}" --target "install_prep/${component}")
unset(RunCMake-check-file)
# Component-specific BMI installation has separate limitations. Check the
# prepared BMI above; exercise installation with the full-install case.
if ((expect_build AND (kind STREQUAL "Metadata" OR component STREQUAL "all"))
OR kind STREQUAL "GeneratedSource")
set(install_args)
if (NOT component STREQUAL "all")
list(APPEND install_args --component "${component}")
endif ()
set(RunCMake-check-file "install-prep-install-check.cmake")
run_cmake_command("${test}-install" "${CMAKE_COMMAND}"
--install . --config "${config}" ${install_args})
endif ()
endfunction ()
# Preparing a generated module source must not compile it to a BMI.
if ("named" IN_LIST CMake_TEST_MODULE_COMPILATION)
run_cxx_module_install_prep(GeneratedSource sources Sources Debug 0)
endif ()
if ("install_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION)
run_cxx_module_install_prep(BMI all all Debug 1)
run_cxx_module_install_prep(BMI component BMI Debug 1)
run_cxx_module_install_prep(BMI excluded-all all Debug 0 EXCLUDE_FROM_ALL)
run_cxx_module_install_prep(BMI excluded-component BMI Debug 1 EXCLUDE_FROM_ALL)
run_cxx_module_install_prep(BMI config-inactive BMI Debug 0 CONFIGURATIONS Release)
run_cxx_module_install_prep(BMI config-active BMI Release 1 CONFIGURATIONS Release)
run_cxx_module_install_prep(BMI sources Sources Debug 0)
run_cxx_module_install_prep(BMI empty-destination BMI Debug 0 EMPTY_DESTINATION)
endif ()
if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION)
run_cxx_module_install_prep(Metadata all all Debug 1)
run_cxx_module_install_prep(Metadata component Metadata Debug 1)
run_cxx_module_install_prep(Metadata excluded-all all Debug 0 EXCLUDE_FROM_ALL)
run_cxx_module_install_prep(Metadata excluded-component Metadata Debug 1 EXCLUDE_FROM_ALL)
run_cxx_module_install_prep(Metadata config-inactive Metadata Debug 0 CONFIGURATIONS Release)
run_cxx_module_install_prep(Metadata config-active Metadata Release 1 CONFIGURATIONS Release)
run_cxx_module_install_prep(Metadata sources Sources Debug 0)
run_cxx_module_install_prep(Metadata no-module-directory Metadata Debug 0 NO_MODULE_DIRECTORY)
endif ()
@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 4.4)
project(cxx_modules_install_prep_sources CXX)
include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/generated.cxx"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/importable.cxx"
COMMAND "${CMAKE_COMMAND}"
-E copy
"${CMAKE_CURRENT_SOURCE_DIR}/importable.cxx"
"${CMAKE_CURRENT_BINARY_DIR}/generated.cxx")
add_library(generated_module STATIC EXCLUDE_FROM_ALL)
target_sources(generated_module
PUBLIC
FILE_SET CXX_MODULES
BASE_DIRS
"${CMAKE_CURRENT_BINARY_DIR}"
FILES
"${CMAKE_CURRENT_BINARY_DIR}/generated.cxx")
target_compile_features(generated_module PUBLIC cxx_std_20)
install(TARGETS generated_module
ARCHIVE DESTINATION "artifacts" COMPONENT Artifacts EXCLUDE_FROM_ALL
FILE_SET CXX_MODULES DESTINATION "sources" COMPONENT Sources)
@@ -0,0 +1,6 @@
export module importable;
export int from_import()
{
return 0;
}
@@ -0,0 +1,19 @@
set(_check_files
[[bin]]
[[bin/imported-tool(\.exe)?]]
[[bin/runtime(\.exe)?]]
[[lib]]
[[lib/(lib)?default_component\.(a|lib|l)]]
[[lib/(lib)?development\.(a|lib|l)]]
[[share]]
[[share/install-prep]]
[[share/install-prep/lib\.c]])
check_installed("^${_check_files}$")
foreach(program IN ITEMS runtime imported-tool)
execute_process(COMMAND "${CMAKE_INSTALL_PREFIX}/bin/${program}${CMAKE_HOST_EXECUTABLE_SUFFIX}"
RESULT_VARIABLE result)
if(NOT result EQUAL 0)
set(RunCMake_TEST_FAILED "Installed ${program} failed: ${result}")
endif()
endforeach()
+65
View File
@@ -0,0 +1,65 @@
enable_language(C)
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME DefaultComponent)
add_library(runtime_helper STATIC lib.c)
add_executable(runtime EXCLUDE_FROM_ALL runtime.c)
target_link_libraries(runtime PRIVATE runtime_helper)
install(TARGETS runtime
RUNTIME DESTINATION bin
COMPONENT Runtime
)
add_library(default_component STATIC lib.c)
install(TARGETS default_component
ARCHIVE DESTINATION lib
)
add_library(hidden STATIC neverbuild.c)
install(TARGETS hidden
ARCHIVE DESTINATION lib
COMPONENT Hidden
EXCLUDE_FROM_ALL
)
add_executable(imported_producer EXCLUDE_FROM_ALL runtime_main.c)
set_target_properties(imported_producer PROPERTIES
OUTPUT_NAME imported-tool
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}"
)
set(imported_location "${CMAKE_BINARY_DIR}/imported-tool${CMAKE_EXECUTABLE_SUFFIX}")
add_executable(imported_tool IMPORTED)
set_target_properties(imported_tool PROPERTIES
IMPORTED_LOCATION "${imported_location}"
)
add_dependencies(imported_tool imported_producer)
install(IMPORTED_RUNTIME_ARTIFACTS imported_tool
RUNTIME DESTINATION bin
COMPONENT Imported
)
install(FILES lib.c
DESTINATION share/install-prep
COMPONENT Documentation
)
install(FILES lib.c
DESTINATION share/install-prep
COMPONENT "Unsafe/../Runtime"
EXCLUDE_FROM_ALL
)
string(ASCII 9 component_tab)
install(FILES lib.c
DESTINATION share/install-prep
COMPONENT "Unsafe${component_tab}Component"
EXCLUDE_FROM_ALL
)
add_library(unit_tests STATIC neverbuild.c)
add_subdirectory(subdir)
add_subdirectory(excluded EXCLUDE_FROM_ALL)
@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 4.4)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
@@ -0,0 +1 @@
check_prepared(all_component_included)
@@ -0,0 +1 @@
[^0]
@@ -0,0 +1 @@
[^0]
@@ -0,0 +1,36 @@
^CMake Warning \(author\) at ComponentNames\.cmake:[0-9]+ \(install\):
The install component "Unsafe/\.\./Name" has build dependencies, but its name
cannot be used in a build target name with this generator\. No
"install_prep/" target will be generated for it\. Rename the component to
build its dependencies with an "install_prep/" target\.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
This warning is for project developers\. Use -Wno-author to suppress it\.
+
CMake Warning \(author\) at ComponentNames\.cmake:[0-9]+ \(install\):
The install component "Empty//Segment" has build dependencies, but its name
cannot be used in a build target name with this generator\. No
"install_prep/" target will be generated for it\. Rename the component to
build its dependencies with an "install_prep/" target\.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
This warning is for project developers\. Use -Wno-author to suppress it\.
+
CMake Warning \(author\) at ComponentNames\.cmake:[0-9]+ \(install\):
The install component "\./Dot" has build dependencies, but its name cannot
be used in a build target name with this generator\. No "install_prep/"
target will be generated for it\. Rename the component to build its
dependencies with an "install_prep/" target\.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
This warning is for project developers\. Use -Wno-author to suppress it\.
+
CMake Warning \(author\) at ComponentNames\.cmake:[0-9]+ \(install\):
An install rule in the component named "all" uses EXCLUDE_FROM_ALL and has
build dependencies, but no install_prep/ target builds them:
install_prep/all omits EXCLUDE_FROM_ALL rules, and the "all" component has
no preparation target of its own\. Rename the component or remove
EXCLUDE_FROM_ALL\.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
This warning is for project developers\. Use -Wno-author to suppress it\.$
@@ -0,0 +1,37 @@
enable_language(C)
add_library(colon_dependency STATIC EXCLUDE_FROM_ALL lib.c)
install(TARGETS colon_dependency
ARCHIVE DESTINATION lib
COMPONENT "Colon:Name"
)
# Components whose names are not representable get no preparation target,
# which is diagnosed when they have build dependencies.
add_library(unsafe_dependency STATIC EXCLUDE_FROM_ALL lib.c)
foreach(component IN ITEMS "Unsafe/../Name" "Empty//Segment" "./Dot")
install(TARGETS unsafe_dependency
ARCHIVE DESTINATION lib
COMPONENT "${component}"
EXCLUDE_FROM_ALL
)
endforeach()
# Rules in a component named "all" contribute to install_prep/all only when
# they are part of a full installation.
add_library(all_component_included STATIC EXCLUDE_FROM_ALL lib.c)
install(TARGETS all_component_included
ARCHIVE DESTINATION lib
COMPONENT all
)
# An excluded rule in a component named "all" is prepared by nothing, which
# is diagnosed.
add_library(all_component_excluded STATIC EXCLUDE_FROM_ALL neverbuild.c)
install(TARGETS all_component_excluded
ARCHIVE DESTINATION lib
COMPONENT all
EXCLUDE_FROM_ALL
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1 @@
check_installed([[^lib;lib/(lib)?debug_only\.(a|lib|l)$]])
@@ -0,0 +1,17 @@
enable_language(C)
add_library(debug_only STATIC EXCLUDE_FROM_ALL lib.c)
install(TARGETS debug_only
ARCHIVE DESTINATION lib
COMPONENT Configured
CONFIGURATIONS Debug
)
add_library(release_only STATIC EXCLUDE_FROM_ALL neverbuild.c)
install(TARGETS release_only
ARCHIVE DESTINATION lib
COMPONENT Configured
CONFIGURATIONS Release
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1,2 @@
check_prepared(debug_only)
check_not_prepared(release_only)
@@ -0,0 +1 @@
check_prepared(release_only)
@@ -0,0 +1,17 @@
enable_language(C)
add_library(debug_only STATIC EXCLUDE_FROM_ALL lib.c)
install(TARGETS debug_only
ARCHIVE DESTINATION lib
COMPONENT Configured
CONFIGURATIONS Debug
)
add_library(release_only STATIC EXCLUDE_FROM_ALL lib.c)
install(TARGETS release_only
ARCHIVE DESTINATION lib
COMPONENT Configured
CONFIGURATIONS Release
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1 @@
check_installed([[^lib;lib/(lib)?release_only\.(a|lib|l)$]])
@@ -0,0 +1,17 @@
enable_language(C)
add_library(debug_only STATIC EXCLUDE_FROM_ALL neverbuild.c)
install(TARGETS debug_only
ARCHIVE DESTINATION lib
COMPONENT Configured
CONFIGURATIONS Debug
)
add_library(release_only STATIC EXCLUDE_FROM_ALL lib.c)
install(TARGETS release_only
ARCHIVE DESTINATION lib
COMPONENT Configured
CONFIGURATIONS Release
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1 @@
check_installed([[^lib;lib/(lib)?default_component\.(a|lib|l)$]])
+16
View File
@@ -0,0 +1,16 @@
enable_language(C)
set(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME DefaultComponent)
add_library(default_component STATIC lib.c)
install(TARGETS default_component
ARCHIVE DESTINATION lib
)
add_library(other_component STATIC neverbuild.c)
install(TARGETS other_component
ARCHIVE DESTINATION lib
COMPONENT Other
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1 @@
check_installed([[^lib;lib/(lib)?development\.(a|lib|l)$]])
@@ -0,0 +1,4 @@
enable_language(C)
add_subdirectory(subdir)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1 @@
check_installed([[^share;share/install-prep;share/install-prep/lib\.c$]])
@@ -0,0 +1,8 @@
enable_language(C)
install(FILES lib.c
DESTINATION share/install-prep
COMPONENT Documentation
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1 @@
check_installed([[^$]])
@@ -0,0 +1,4 @@
enable_language(C)
add_subdirectory(excluded EXCLUDE_FROM_ALL)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1,33 @@
enable_language(C)
# A generated header that is not the output of a custom command attached to
# a single target is prepared by depending on the file itself, which runs its
# producer. Generators that cannot do so instead build the installing target,
# which builds the producer through its dependencies, so its sources must
# compile there.
if(CMAKE_GENERATOR MATCHES "Ninja")
set(generated_target_source neverbuild.c)
else()
set(generated_target_source lib.c)
endif()
set(generated_header "${CMAKE_CURRENT_BINARY_DIR}/generated.h")
add_custom_target(produce_header
COMMAND ${CMAKE_COMMAND} -E touch "${generated_header}"
BYPRODUCTS "${generated_header}"
)
add_library(generated_headers STATIC EXCLUDE_FROM_ALL
${generated_target_source})
add_dependencies(generated_headers produce_header)
set_property(TARGET generated_headers
PROPERTY PUBLIC_HEADER "${generated_header}")
install(TARGETS generated_headers
ARCHIVE DESTINATION lib
COMPONENT Archive
EXCLUDE_FROM_ALL
PUBLIC_HEADER DESTINATION include
COMPONENT Headers
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1,22 @@
enable_language(C)
# A generated header produced by a custom command attached to another target
# is prepared by running that command, without building the installing target.
set(generated_header "${CMAKE_CURRENT_BINARY_DIR}/generated.h")
add_custom_command(OUTPUT "${generated_header}"
COMMAND ${CMAKE_COMMAND} -E touch "${generated_header}"
)
add_custom_target(produce_header DEPENDS "${generated_header}")
add_library(generated_headers STATIC EXCLUDE_FROM_ALL neverbuild.c)
set_property(TARGET generated_headers
PROPERTY PUBLIC_HEADER "${generated_header}")
install(TARGETS generated_headers
ARCHIVE DESTINATION lib
COMPONENT Archive
EXCLUDE_FROM_ALL
PUBLIC_HEADER DESTINATION include
COMPONENT Headers
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1,15 @@
enable_language(C)
# Generators that can depend on individual files prepare a generated file by
# running only the custom command that produces it.
set(generated_target_source "${CMAKE_CURRENT_LIST_DIR}/neverbuild.c")
# Add a custom command that generates the named file in the current binary
# directory and store its full path in the given variable.
function(add_generated_file var name)
set(file "${CMAKE_CURRENT_BINARY_DIR}/${name}")
add_custom_command(OUTPUT "${file}"
COMMAND ${CMAKE_COMMAND} -E touch "${file}"
)
set(${var} "${file}" PARENT_SCOPE)
endfunction()
@@ -0,0 +1,33 @@
include(GeneratedCommon.cmake)
add_generated_file(generated_header generated.h)
add_library(generated_headers STATIC EXCLUDE_FROM_ALL
"${generated_target_source}")
target_sources(generated_headers PUBLIC
FILE_SET HEADERS
BASE_DIRS "${CMAKE_CURRENT_BINARY_DIR}"
FILES "${generated_header}"
)
install(TARGETS generated_headers
ARCHIVE DESTINATION lib
COMPONENT Archive
EXCLUDE_FROM_ALL
FILE_SET HEADERS DESTINATION include
COMPONENT Headers
)
# A file set without generated files does not need its target built.
add_library(plain_headers STATIC EXCLUDE_FROM_ALL neverbuild.c)
target_sources(plain_headers PUBLIC
FILE_SET HEADERS
FILES plain.h
)
install(TARGETS plain_headers
ARCHIVE DESTINATION lib
COMPONENT Archive
EXCLUDE_FROM_ALL
FILE_SET HEADERS DESTINATION include
COMPONENT Headers
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1,19 @@
include(GeneratedCommon.cmake)
# install(FILES) and install(PROGRAMS) imply build dependencies only for files
# that the build generates. A file produced by a custom command attached to
# a single target is prepared by running only that command.
add_generated_file(generated_file generated.txt)
add_generated_file(generated_program generated.sh)
add_library(generated_files STATIC EXCLUDE_FROM_ALL
"${generated_target_source}" "${generated_file}" "${generated_program}")
install(FILES "${generated_file}" lib.c
DESTINATION share/install-prep
COMPONENT Files
)
install(PROGRAMS "${generated_program}"
DESTINATION bin
COMPONENT Files
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1,16 @@
enable_language(C)
# A generated file installed by install(FILES) that is not the unique output
# of a target is prepared by depending on the file itself. Generators that
# cannot do so diagnose it.
set(byproduct "${CMAKE_CURRENT_BINARY_DIR}/byproduct.txt")
add_custom_target(produce_byproduct
COMMAND ${CMAKE_COMMAND} -E touch "${byproduct}"
BYPRODUCTS "${byproduct}"
)
install(FILES "${byproduct}"
DESTINATION share/install-prep
COMPONENT Byproduct
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1,18 @@
include(GeneratedCommon.cmake)
# The target and its generated headers are defined in a subdirectory but
# installed from here, so the generated files must be found in the target's
# directory rather than the directory of the install rule.
add_subdirectory(generated)
install(TARGETS subdirectory_headers
ARCHIVE DESTINATION lib
COMPONENT Archive
EXCLUDE_FROM_ALL
FILE_SET HEADERS DESTINATION include
COMPONENT Headers
PUBLIC_HEADER DESTINATION include/public
COMPONENT Headers
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1,32 @@
include(GeneratedCommon.cmake)
# Each property uses its own target so that preparing one file cannot
# produce another as a side effect.
foreach(property IN ITEMS PUBLIC_HEADER PRIVATE_HEADER RESOURCE)
string(TOLOWER "${property}" name)
add_generated_file(generated_file "generated_${name}.h")
add_library(generated_${name} STATIC EXCLUDE_FROM_ALL
"${generated_target_source}" "${generated_file}")
set_property(TARGET generated_${name}
PROPERTY ${property} "${generated_file}")
install(TARGETS generated_${name}
ARCHIVE DESTINATION lib
COMPONENT Archive
EXCLUDE_FROM_ALL
${property} DESTINATION include/${name}
COMPONENT Headers
)
endforeach()
# Public headers that are not generated do not need their target built.
add_library(plain_headers STATIC EXCLUDE_FROM_ALL neverbuild.c plain.h)
set_property(TARGET plain_headers PROPERTY PUBLIC_HEADER plain.h)
install(TARGETS plain_headers
ARCHIVE DESTINATION lib
COMPONENT Archive
EXCLUDE_FROM_ALL
PUBLIC_HEADER DESTINATION include
COMPONENT Headers
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1 @@
check_installed([[^lib;lib/(lib)?hidden\.(a|lib|l)$]])
+10
View File
@@ -0,0 +1,10 @@
enable_language(C)
add_library(hidden STATIC lib.c)
install(TARGETS hidden
ARCHIVE DESTINATION lib
COMPONENT Hidden
EXCLUDE_FROM_ALL
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1,7 @@
check_installed([[^bin;bin/imported-tool(\.exe)?$]])
execute_process(COMMAND "${CMAKE_INSTALL_PREFIX}/bin/imported-tool${CMAKE_HOST_EXECUTABLE_SUFFIX}"
RESULT_VARIABLE result)
if(NOT result EQUAL 0)
set(RunCMake_TEST_FAILED "Installed imported-tool failed: ${result}")
endif()
+22
View File
@@ -0,0 +1,22 @@
enable_language(C)
add_executable(imported_producer EXCLUDE_FROM_ALL runtime_main.c)
set_target_properties(imported_producer PROPERTIES
OUTPUT_NAME imported-tool
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}"
)
set(imported_location "${CMAKE_BINARY_DIR}/imported-tool${CMAKE_EXECUTABLE_SUFFIX}")
add_executable(imported_tool IMPORTED)
set_target_properties(imported_tool PROPERTIES
IMPORTED_LOCATION "${imported_location}"
)
add_dependencies(imported_tool imported_producer)
install(IMPORTED_RUNTIME_ARTIFACTS imported_tool
RUNTIME DESTINATION bin
COMPONENT Imported
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1 @@
[^0]
@@ -0,0 +1,8 @@
set(CMAKE_SKIP_INSTALL_PREP_TARGETS ON)
enable_language(C)
add_library(install_prep_gate_on STATIC lib.c)
install(TARGETS install_prep_gate_on
ARCHIVE DESTINATION lib
)
@@ -0,0 +1,17 @@
check_installed([[^lib;lib/libnamelink\.(so|dylib)$]])
if(APPLE)
set(namelink "${CMAKE_INSTALL_PREFIX}/lib/libnamelink.dylib")
set(expected libnamelink.1.dylib)
else()
set(namelink "${CMAKE_INSTALL_PREFIX}/lib/libnamelink.so")
set(expected libnamelink.so.1)
endif()
if(NOT IS_SYMLINK "${namelink}")
set(RunCMake_TEST_FAILED "The installed namelink is not a symlink.")
return()
endif()
file(READ_SYMLINK "${namelink}" actual)
if(NOT actual STREQUAL expected)
set(RunCMake_TEST_FAILED "Namelink points to ${actual}, expected ${expected}.")
endif()
+12
View File
@@ -0,0 +1,12 @@
enable_language(C)
add_library(namelink SHARED EXCLUDE_FROM_ALL lib.c)
set_target_properties(namelink PROPERTIES
VERSION 1.0
SOVERSION 1
)
install(TARGETS namelink
LIBRARY DESTINATION lib
COMPONENT Runtime
NAMELINK_COMPONENT Namelink
)
@@ -0,0 +1 @@
check_installed([[^lib;lib/(lib)?nested\.(a|lib|l)$]])
+9
View File
@@ -0,0 +1,9 @@
enable_language(C)
add_library(nested STATIC lib.c)
install(TARGETS nested
ARCHIVE DESTINATION lib
COMPONENT Nested/Runtime_1.2+dbg
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1 @@
[^0]
Whitespace-only changes.
@@ -0,0 +1,183 @@
include(RunCMake)
# Function called in *-check.cmake scripts to check installed files.
function(check_installed expect)
file(GLOB_RECURSE actual
LIST_DIRECTORIES TRUE
RELATIVE ${CMAKE_INSTALL_PREFIX}
${CMAKE_INSTALL_PREFIX}/*
)
if(actual)
list(SORT actual)
endif()
if(NOT "${actual}" MATCHES "${expect}")
set(RunCMake_TEST_FAILED "Installed files:
${actual}
do not match what we expected:
${expect}
in directory:
${CMAKE_INSTALL_PREFIX}\n" PARENT_SCOPE)
endif()
endfunction()
# Functions called in *-check.cmake scripts to check whether preparing for
# installation built the given static library targets.
function(check_prepared)
foreach(target IN LISTS ARGN)
if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/lib${target}.a" AND
NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/${target}.lib" AND
NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/${target}.l")
set(RunCMake_TEST_FAILED "Preparation did not build ${target}." PARENT_SCOPE)
return()
endif()
endforeach()
endfunction()
function(check_not_prepared)
foreach(target IN LISTS ARGN)
file(GLOB_RECURSE artifacts LIST_DIRECTORIES FALSE
"${RunCMake_TEST_BINARY_DIR}/lib${target}.a"
"${RunCMake_TEST_BINARY_DIR}/${target}.lib"
)
if(artifacts)
set(RunCMake_TEST_FAILED "Preparation built ${target}: ${artifacts}" PARENT_SCOPE)
return()
endif()
endforeach()
endfunction()
# Configure a test project. Callers set RunCMake_TEST_NO_CLEAN afterward to
# run further commands in the same build tree.
macro(configure_install_prep test config)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
set(CMAKE_INSTALL_PREFIX ${RunCMake_BINARY_DIR}/${test}-install)
file(REMOVE_RECURSE "${CMAKE_INSTALL_PREFIX}")
set(RunCMake_TEST_CONFIG "")
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
set(RunCMake_TEST_CONFIG "${config}")
endif()
set(RunCMake_TEST_OPTIONS
-DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX})
if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=${config})
endif()
run_cmake(${test})
set(RunCMake_TEST_NO_CLEAN TRUE)
endmacro()
# Build a preparation target that is expected not to exist.
function(run_missing_install_prep test prep_target)
set(RunCMake_TEST_OUTPUT_MERGE TRUE)
run_cmake_command(${test}-build ${CMAKE_COMMAND}
--build . --config Debug --target ${prep_target})
endfunction()
# Build a preparation target and then install the matching component.
function(run_install_prep test prep_target install_component)
configure_install_prep(${test} Debug)
run_cmake_command(${test}-prep ${CMAKE_COMMAND}
--build . --config Debug --target ${prep_target})
if(EXISTS "${CMAKE_INSTALL_PREFIX}")
message(SEND_ERROR "${test}: preparation ran installation.")
endif()
set(install_args --install . --config Debug)
if(NOT install_component STREQUAL "ALL")
list(APPEND install_args --component ${install_component})
endif()
set(RunCMake-check-file ${test}-install-check.cmake)
run_cmake_command(${test}-install ${CMAKE_COMMAND} ${install_args})
endfunction()
block()
configure_install_prep(InstallPrepGate-ON Debug)
run_missing_install_prep(InstallPrepGate-ON install_prep/all)
endblock()
block()
configure_install_prep(NoInstallRules Debug)
run_missing_install_prep(NoInstallRules install_prep/all)
endblock()
run_install_prep(All install_prep/all ALL)
run_install_prep(Runtime install_prep/Runtime Runtime)
run_install_prep(Development install_prep/Development Development)
run_install_prep(Nested
install_prep/Nested/Runtime_1.2+dbg Nested/Runtime_1.2+dbg)
run_install_prep(Default install_prep/DefaultComponent DefaultComponent)
run_install_prep(Hidden install_prep/Hidden Hidden)
run_install_prep(Imported install_prep/Imported Imported)
run_install_prep(Documentation install_prep/Documentation Documentation)
run_install_prep(ExcludedSubdirectory
install_prep/ExcludedSubdirectory ExcludedSubdirectory)
run_install_prep(GeneratedFileSetHeaders install_prep/Headers Headers)
run_install_prep(GeneratedTargetProperties install_prep/Headers Headers)
run_install_prep(GeneratedByOtherTarget install_prep/Headers Headers)
run_install_prep(GeneratedByDependency install_prep/Headers Headers)
run_install_prep(GeneratedSubdirectory install_prep/Headers Headers)
run_install_prep(GeneratedFiles install_prep/Files Files)
run_install_prep(TargetFileArtifact install_prep/Artifacts Artifacts)
if(RunCMake_GENERATOR MATCHES "Ninja")
run_install_prep(GeneratedFilesByproduct install_prep/Byproduct Byproduct)
else()
# The byproduct cannot be prepared, which is diagnosed.
block()
configure_install_prep(GeneratedFilesByproduct Debug)
endblock()
endif()
block()
configure_install_prep(ComponentNames Debug)
run_cmake_command(ComponentNames-all-build ${CMAKE_COMMAND}
--build . --config Debug --target install_prep/all)
run_cmake_command(ComponentNames-colon-build ${CMAKE_COMMAND}
--build . --config Debug --target install_prep/Colon:Name)
run_missing_install_prep(ComponentNames-dotdot install_prep/Unsafe/../Name)
run_missing_install_prep(ComponentNames-empty-segment
install_prep/Empty//Segment)
run_missing_install_prep(ComponentNames-dot install_prep/./Dot)
endblock()
function(run_configured_install_prep test config)
configure_install_prep(${test} ${config})
run_cmake_command(${test}-prep ${CMAKE_COMMAND}
--build . --config ${config} --target install_prep/Configured)
if(EXISTS "${CMAKE_INSTALL_PREFIX}")
message(SEND_ERROR "${test}: preparation ran installation.")
endif()
set(RunCMake-check-file ${test}-install-check.cmake)
run_cmake_command(${test}-install ${CMAKE_COMMAND}
--install . --config ${config} --component Configured)
endfunction()
run_configured_install_prep(ConfigurationsDebug Debug)
run_configured_install_prep(ConfigurationsRelease Release)
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
block()
configure_install_prep(ConfigurationsMulti Debug)
foreach(config IN ITEMS Debug Release)
set(RunCMake_TEST_CONFIG "${config}")
run_cmake_command(ConfigurationsMulti-prep-${config} ${CMAKE_COMMAND}
--build . --config ${config} --target install_prep/Configured)
run_cmake_command(ConfigurationsMulti-install-${config} ${CMAKE_COMMAND}
--install . --config ${config} --component Configured)
endforeach()
endblock()
endif()
if(CMAKE_HOST_SYSTEM_NAME MATCHES "^(Windows|Linux|Darwin|FreeBSD)$")
run_install_prep(RuntimeDependencySet
install_prep/RuntimeDependencySet RuntimeDependencySet)
run_install_prep(RuntimeDependencies
install_prep/RuntimeDependencies RuntimeDependencies)
endif()
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
run_install_prep(RuntimeDependencyFramework
install_prep/FrameworkDependencies FrameworkDependencies)
endif()
if(CMAKE_HOST_SYSTEM_NAME MATCHES "^(Linux|Darwin)$")
run_install_prep(Namelink install_prep/Namelink Namelink)
endif()
@@ -0,0 +1,7 @@
check_installed([[^bin;bin/runtime(\.exe)?$]])
execute_process(COMMAND "${CMAKE_INSTALL_PREFIX}/bin/runtime${CMAKE_HOST_EXECUTABLE_SUFFIX}"
RESULT_VARIABLE result)
if(NOT result EQUAL 0)
set(RunCMake_TEST_FAILED "Installed runtime failed: ${result}")
endif()
+17
View File
@@ -0,0 +1,17 @@
enable_language(C)
add_library(runtime_helper STATIC lib.c)
add_executable(runtime EXCLUDE_FROM_ALL runtime.c)
target_link_libraries(runtime PRIVATE runtime_helper)
install(TARGETS runtime
RUNTIME DESTINATION bin
COMPONENT Runtime
)
add_library(other_component STATIC neverbuild.c)
install(TARGETS other_component
ARCHIVE DESTINATION lib
COMPONENT Other
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1,14 @@
check_installed([[^$]])
if(WIN32)
if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/runtime_dependencies_root.dll" AND
NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/libruntime_dependencies_root.dll")
set(RunCMake_TEST_FAILED "Preparation did not build the module to scan.")
endif()
else()
execute_process(COMMAND "${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/runtime_dependencies_root${CMAKE_HOST_EXECUTABLE_SUFFIX}"
RESULT_VARIABLE result)
if(NOT result EQUAL 0)
set(RunCMake_TEST_FAILED "Prepared runtime dependency root failed: ${result}")
endif()
endif()
@@ -0,0 +1,24 @@
enable_language(C)
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
add_library(runtime_dependencies_root MODULE EXCLUDE_FROM_ALL lib.c)
set(runtime_component RuntimeDependencies)
set(library_component RuntimeDependenciesSeed)
else()
add_executable(runtime_dependencies_root EXCLUDE_FROM_ALL runtime_main.c)
set(runtime_component RuntimeDependenciesSeed)
set(library_component RuntimeDependencies)
endif()
install(TARGETS runtime_dependencies_root
RUNTIME_DEPENDENCIES
PRE_EXCLUDE_REGEXES ".*"
RUNTIME DESTINATION bin
COMPONENT ${runtime_component}
LIBRARY DESTINATION lib
COMPONENT ${library_component}
FRAMEWORK DESTINATION Frameworks
COMPONENT RuntimeDependencies
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1,7 @@
check_installed([[^$]])
execute_process(COMMAND "${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/runtime_dependency_framework_root"
RESULT_VARIABLE result)
if(NOT result EQUAL 0)
set(RunCMake_TEST_FAILED "Prepared framework dependency root failed: ${result}")
endif()
@@ -0,0 +1,20 @@
enable_language(C)
add_executable(runtime_dependency_framework_root EXCLUDE_FROM_ALL
runtime_main.c)
install(TARGETS runtime_dependency_framework_root
RUNTIME_DEPENDENCY_SET runtime_dependency_framework_set
RUNTIME DESTINATION bin
COMPONENT RuntimeDependencySeed
)
install(RUNTIME_DEPENDENCY_SET runtime_dependency_framework_set
PRE_EXCLUDE_REGEXES ".*"
RUNTIME DESTINATION bin
COMPONENT RuntimeDependencies
LIBRARY DESTINATION lib
COMPONENT RuntimeDependencies
FRAMEWORK DESTINATION Frameworks
COMPONENT FrameworkDependencies
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1,20 @@
if(WIN32)
check_installed([[^bin;bin/(lib)?runtime_dependency_support\.dll$]])
else()
check_installed([[^lib;lib/libruntime_dependency_support\.(so|dylib)$]])
endif()
foreach(target IN ITEMS runtime_dependency_shared runtime_dependency_module)
if(NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/lib${target}.so" AND
NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/lib${target}.dylib" AND
NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/lib${target}.dll" AND
NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/${RunCMake_TEST_CONFIG}/${target}.dll")
set(RunCMake_TEST_FAILED "Preparation did not build ${target} for scanning.")
return()
endif()
endforeach()
execute_process(COMMAND "${RunCMake_TEST_BINARY_DIR}/runtime-dependency-imported${CMAKE_HOST_EXECUTABLE_SUFFIX}"
RESULT_VARIABLE result)
if(NOT result EQUAL 0)
set(RunCMake_TEST_FAILED "Prepared imported runtime dependency root failed: ${result}")
endif()
@@ -0,0 +1,49 @@
enable_language(C)
add_library(runtime_dependency_shared SHARED EXCLUDE_FROM_ALL lib.c)
add_library(runtime_dependency_module MODULE EXCLUDE_FROM_ALL lib.c)
add_library(runtime_dependency_support SHARED EXCLUDE_FROM_ALL lib.c)
set_target_properties(runtime_dependency_support PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}")
add_executable(runtime_dependency_producer EXCLUDE_FROM_ALL runtime.c)
target_link_libraries(runtime_dependency_producer PRIVATE runtime_dependency_support)
set_target_properties(runtime_dependency_producer PROPERTIES
OUTPUT_NAME runtime-dependency-imported
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}"
)
set(imported_location
"${CMAKE_BINARY_DIR}/runtime-dependency-imported${CMAKE_EXECUTABLE_SUFFIX}")
add_executable(runtime_dependency_imported IMPORTED)
set_target_properties(runtime_dependency_imported PROPERTIES
IMPORTED_LOCATION "${imported_location}"
)
add_dependencies(runtime_dependency_imported runtime_dependency_producer)
install(TARGETS runtime_dependency_shared runtime_dependency_module
RUNTIME_DEPENDENCY_SET runtime_dependency_set
RUNTIME DESTINATION bin
COMPONENT RuntimeDependencySeeds
LIBRARY DESTINATION lib
COMPONENT RuntimeDependencySeeds
)
install(IMPORTED_RUNTIME_ARTIFACTS runtime_dependency_imported
RUNTIME_DEPENDENCY_SET runtime_dependency_set
RUNTIME DESTINATION bin
COMPONENT RuntimeDependencySeeds
)
install(RUNTIME_DEPENDENCY_SET runtime_dependency_set
PRE_INCLUDE_REGEXES "runtime_dependency_support"
PRE_EXCLUDE_REGEXES ".*"
RUNTIME DESTINATION bin
COMPONENT RuntimeDependencySet
LIBRARY DESTINATION lib
COMPONENT RuntimeDependencySet
FRAMEWORK DESTINATION Frameworks
COMPONENT RuntimeDependencySet
)
add_library(unit_tests STATIC neverbuild.c)
@@ -0,0 +1 @@
check_installed([[^lib;lib/(lib)?file_artifact\.(a|lib|l)$]])
Loaded 100 of 109 files, more files were not shown because too many files have changed in this diff. Show more