Add infrastructure for generators to select a build tool instance

Add cache entry `CMAKE_GENERATOR_INSTANCE` to hold the instance location
persistently across re-runs of CMake in a given build tree.

For now we reject the option by default if explicitly set.  It will be
implemented on a per-generator basis.  Pass the setting into try_compile
project generation.  Add a RunCMake.GeneratorInstance test to cover
basic use cases for the option.  Verify that `CMAKE_GENERATOR_INSTANCE`
is empty by default, and that it is rejected when the generator does not
support a user setting.

Issue: #17268
This commit is contained in:
Brad King
2017-10-03 14:53:34 -04:00
parent 358ceee5d8
commit 314613d1af
22 changed files with 155 additions and 0 deletions

View File

@@ -42,6 +42,7 @@ Variables that Provide Information
/variable/CMAKE_FIND_PACKAGE_SORT_DIRECTION
/variable/CMAKE_FIND_PACKAGE_SORT_ORDER
/variable/CMAKE_GENERATOR
/variable/CMAKE_GENERATOR_INSTANCE
/variable/CMAKE_GENERATOR_PLATFORM
/variable/CMAKE_GENERATOR_TOOLSET
/variable/CMAKE_HOME_DIRECTORY

View File

@@ -0,0 +1,7 @@
generator-instance
------------------
* A :variable:`CMAKE_GENERATOR_INSTANCE` variable was introduced
to hold the selected instance of the generator's corresponding
native tools if multiple are available. Currently no generators
actually use this, but the infrastructure is in place.

View File

@@ -0,0 +1,22 @@
CMAKE_GENERATOR_INSTANCE
------------------------
Generator-specific instance specification provided by user.
Some CMake generators support selection of an instance of the native build
system when multiple instances are available. If the user specifies an
instance (e.g. by setting this cache entry), or after a default instance is
chosen when a build tree is first configured, the value will be available in
this variable.
The value of this variable should never be modified by project code.
A toolchain file specified by the :variable:`CMAKE_TOOLCHAIN_FILE`
variable may initialize ``CMAKE_GENERATOR_INSTANCE`` as a cache entry.
Once a given build tree has been initialized with a particular value
for this variable, changing the value has undefined behavior.
Instance specification is supported only on specific generators:
* None
See native build system documentation for allowed instance values.

View File

@@ -111,6 +111,26 @@ cmGlobalGenerator::~cmGlobalGenerator()
delete this->ExtraGenerator;
}
bool cmGlobalGenerator::SetGeneratorInstance(std::string const& i,
cmMakefile* mf)
{
if (i.empty()) {
return true;
}
std::ostringstream e;
/* clang-format off */
e <<
"Generator\n"
" " << this->GetName() << "\n"
"does not support instance specification, but instance\n"
" " << i << "\n"
"was specified.";
/* clang-format on */
mf->IssueMessage(cmake::FATAL_ERROR, e.str());
return false;
}
bool cmGlobalGenerator::SetGeneratorPlatform(std::string const& p,
cmMakefile* mf)
{
@@ -491,6 +511,13 @@ void cmGlobalGenerator::EnableLanguage(
}
if (readCMakeSystem) {
// Tell the generator about the instance, if any.
std::string instance = mf->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE");
if (!this->SetGeneratorInstance(instance, mf)) {
cmSystemTools::SetFatalErrorOccured();
return;
}
// Find the native build tool for this generator.
if (!this->FindMakeProgram(mf)) {
return;

View File

@@ -70,6 +70,9 @@ public:
/** Tell the generator about the target system. */
virtual bool SetSystemName(std::string const&, cmMakefile*) { return true; }
/** Set the generator-specific instance. Returns true if supported. */
virtual bool SetGeneratorInstance(std::string const& i, cmMakefile* mf);
/** Set the generator-specific platform name. Returns true if platform
is supported and false otherwise. */
virtual bool SetGeneratorPlatform(std::string const& p, cmMakefile* mf);

View File

@@ -3186,6 +3186,7 @@ int cmMakefile::TryCompile(const std::string& srcdir,
// do a configure
cm.SetHomeDirectory(srcdir);
cm.SetHomeOutputDirectory(bindir);
cm.SetGeneratorInstance(this->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE"));
cm.SetGeneratorPlatform(this->GetSafeDefinition("CMAKE_GENERATOR_PLATFORM"));
cm.SetGeneratorToolset(this->GetSafeDefinition("CMAKE_GENERATOR_TOOLSET"));
cm.LoadCache();

View File

@@ -1326,6 +1326,25 @@ int cmake::ActualConfigure()
cmStateEnums::INTERNAL);
}
if (const char* instance =
this->State->GetInitializedCacheValue("CMAKE_GENERATOR_INSTANCE")) {
if (!this->GeneratorInstance.empty() &&
this->GeneratorInstance != instance) {
std::string message = "Error: generator instance: ";
message += this->GeneratorInstance;
message += "\nDoes not match the instance used previously: ";
message += instance;
message += "\nEither remove the CMakeCache.txt file and CMakeFiles "
"directory or choose a different binary directory.";
cmSystemTools::Error(message.c_str());
return -2;
}
} else {
this->AddCacheEntry(
"CMAKE_GENERATOR_INSTANCE", this->GeneratorInstance.c_str(),
"Generator instance identifier.", cmStateEnums::INTERNAL);
}
if (const char* platformName =
this->State->GetInitializedCacheValue("CMAKE_GENERATOR_PLATFORM")) {
if (!this->GeneratorPlatform.empty() &&
@@ -2360,6 +2379,14 @@ int cmake::Build(const std::string& dir, const std::string& target,
<< "\"\n";
return 1;
}
const char* cachedGeneratorInstance =
this->State->GetCacheEntryValue("CMAKE_GENERATOR_INSTANCE");
if (cachedGeneratorInstance) {
cmMakefile mf(gen.get(), this->GetCurrentSnapshot());
if (!gen->SetGeneratorInstance(cachedGeneratorInstance, &mf)) {
return 1;
}
}
std::string output;
std::string projName;
const char* cachedProjectName =

View File

@@ -203,6 +203,12 @@ public:
///! Get the names of the current registered generators
void GetRegisteredGenerators(std::vector<GeneratorInfo>& generators) const;
///! Set the name of the selected generator-specific instance.
void SetGeneratorInstance(std::string const& instance)
{
this->GeneratorInstance = instance;
}
///! Set the name of the selected generator-specific platform.
void SetGeneratorPlatform(std::string const& ts)
{
@@ -431,6 +437,7 @@ protected:
cmGlobalGenerator* GlobalGenerator;
std::map<std::string, DiagLevel> DiagLevels;
std::string GeneratorInstance;
std::string GeneratorPlatform;
std::string GeneratorToolset;

View File

@@ -15,6 +15,7 @@ macro(add_RunCMake_test test)
add_test(NAME RunCMake.${test} COMMAND ${CMAKE_CMAKE_COMMAND}
-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_SOURCE_DIR}
-DRunCMake_GENERATOR=${CMAKE_GENERATOR}
-DRunCMake_GENERATOR_INSTANCE=${CMAKE_GENERATOR_INSTANCE}
-DRunCMake_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
-DRunCMake_GENERATOR_TOOLSET=${CMAKE_GENERATOR_TOOLSET}
-DRunCMake_MAKE_PROGRAM=${CMake_TEST_EXPLICIT_MAKE_PROGRAM}
@@ -47,6 +48,7 @@ function(add_RunCMake_test_group test types)
-DTEST_TYPE=${type}
-DCMAKE_MODULE_PATH=${CMAKE_CURRENT_SOURCE_DIR}
-DRunCMake_GENERATOR=${CMAKE_GENERATOR}
-DRunCMake_GENERATOR_INSTANCE=${CMAKE_GENERATOR_INSTANCE}
-DRunCMake_GENERATOR_PLATFORM=${CMAKE_GENERATOR_PLATFORM}
-DRunCMake_GENERATOR_TOOLSET=${CMAKE_GENERATOR_TOOLSET}
-DRunCMake_MAKE_PROGRAM=${CMake_TEST_EXPLICIT_MAKE_PROGRAM}
@@ -146,6 +148,7 @@ if(NOT CMAKE_C_COMPILER_ID MATCHES "Watcom")
add_RunCMake_test(GenerateExportHeader)
endif()
add_RunCMake_test(GeneratorExpression)
add_RunCMake_test(GeneratorInstance)
add_RunCMake_test(GeneratorPlatform)
add_RunCMake_test(GeneratorToolset)
add_RunCMake_test(GetPrerequisites)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,10 @@
CMake Error at CMakeLists.txt:[0-9]+ \(project\):
Generator
.*
does not support instance specification, but instance
Bad Instance
was specified.$

View File

@@ -0,0 +1 @@
set(CMAKE_GENERATOR_INSTANCE "Bad Instance")

View File

@@ -0,0 +1 @@
message(FATAL_ERROR "This should not be reached!")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,10 @@
CMake Error at CMakeLists.txt:[0-9]+ \(project\):
Generator
.*
does not support instance specification, but instance
Bad Instance
was specified.$

View File

@@ -0,0 +1 @@
message(FATAL_ERROR "This should not be reached!")

View File

@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.9)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
CMake Error at NoInstance.cmake:2 \(message\):
CMAKE_GENERATOR_INSTANCE is empty as expected.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@@ -0,0 +1,7 @@
if("x${CMAKE_GENERATOR_INSTANCE}" STREQUAL "x")
message(FATAL_ERROR "CMAKE_GENERATOR_INSTANCE is empty as expected.")
else()
message(FATAL_ERROR
"CMAKE_GENERATOR_INSTANCE is \"${CMAKE_GENERATOR_INSTANCE}\" "
"but should be empty!")
endif()

View File

@@ -0,0 +1,11 @@
include(RunCMake)
set(RunCMake_GENERATOR_INSTANCE "")
run_cmake(NoInstance)
set(RunCMake_GENERATOR_INSTANCE "Bad Instance")
run_cmake(BadInstance)
set(RunCMake_TEST_OPTIONS -DCMAKE_TOOLCHAIN_FILE=${RunCMake_SOURCE_DIR}/BadInstance-toolchain.cmake)
run_cmake(BadInstanceToolchain)
unset(RunCMake_TEST_OPTIONS)

View File

@@ -79,11 +79,17 @@ function(run_cmake test)
${maybe_timeout}
)
else()
if(RunCMake_GENERATOR_INSTANCE)
set(_D_CMAKE_GENERATOR_INSTANCE "-DCMAKE_GENERATOR_INSTANCE=${RunCMake_GENERATOR_INSTANCE}")
else()
set(_D_CMAKE_GENERATOR_INSTANCE "")
endif()
execute_process(
COMMAND ${CMAKE_COMMAND} "${RunCMake_TEST_SOURCE_DIR}"
-G "${RunCMake_GENERATOR}"
-A "${RunCMake_GENERATOR_PLATFORM}"
-T "${RunCMake_GENERATOR_TOOLSET}"
${_D_CMAKE_GENERATOR_INSTANCE}
-DRunCMake_TEST=${test}
--no-warn-unused-cli
${RunCMake_TEST_OPTIONS}