CMake/Source/cmTestGenerator.cxx
Stephen Kelly 91011bd217 cmGeneratorExpression: Port users to two-stage processing
Removing the Process() API and removing the parameters from the
constructor will allow cmGeneratorExpressions to be cached and evaluated
with multiple configs for example, such as when evaluating target
properties.  This requires the creation of a new compiled representation
of cmGeneratorExpression.  The cmListFileBacktrace remains in the
constructor so that we can record where a particular generator
expression appeared in the CMakeLists file.
2012-09-18 17:03:08 -04:00

185 lines
6.1 KiB
C++

/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmTestGenerator.h"
#include "cmGeneratorExpression.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmTest.h"
//----------------------------------------------------------------------------
cmTestGenerator
::cmTestGenerator(cmTest* test,
std::vector<std::string> const& configurations):
cmScriptGenerator("CTEST_CONFIGURATION_TYPE", configurations),
Test(test)
{
this->ActionsPerConfig = !test->GetOldStyle();
this->TestGenerated = false;
}
//----------------------------------------------------------------------------
cmTestGenerator
::~cmTestGenerator()
{
}
//----------------------------------------------------------------------------
void cmTestGenerator::GenerateScriptConfigs(std::ostream& os,
Indent const& indent)
{
// First create the tests.
this->cmScriptGenerator::GenerateScriptConfigs(os, indent);
// Now generate the test properties.
if(this->TestGenerated)
{
cmTest* test = this->Test;
cmMakefile* mf = test->GetMakefile();
cmLocalGenerator* lg = mf->GetLocalGenerator();
std::ostream& fout = os;
cmPropertyMap::const_iterator pit;
cmPropertyMap* mpit = &test->GetProperties();
if ( mpit->size() )
{
fout << "SET_TESTS_PROPERTIES(" << test->GetName() << " PROPERTIES ";
for ( pit = mpit->begin(); pit != mpit->end(); ++ pit )
{
fout << " " << pit->first
<< " " << lg->EscapeForCMake(pit->second.GetValue());
}
fout << ")" << std::endl;
}
}
}
//----------------------------------------------------------------------------
void cmTestGenerator::GenerateScriptActions(std::ostream& os,
Indent const& indent)
{
if(this->ActionsPerConfig)
{
// This is the per-config generation in a single-configuration
// build generator case. The superclass will call our per-config
// method.
this->cmScriptGenerator::GenerateScriptActions(os, indent);
}
else
{
// This is an old-style test, so there is only one config.
//assert(this->Test->GetOldStyle());
this->GenerateOldStyle(os, indent);
}
}
//----------------------------------------------------------------------------
void cmTestGenerator::GenerateScriptForConfig(std::ostream& os,
const char* config,
Indent const& indent)
{
this->TestGenerated = true;
// Set up generator expression evaluation context.
cmGeneratorExpression ge(this->Test->GetBacktrace());
// Start the test command.
os << indent << "ADD_TEST(" << this->Test->GetName() << " ";
// Get the test command line to be executed.
std::vector<std::string> const& command = this->Test->GetCommand();
// Check whether the command executable is a target whose name is to
// be translated.
std::string exe = command[0];
cmMakefile* mf = this->Test->GetMakefile();
cmTarget* target = mf->FindTargetToUse(exe.c_str());
if(target && target->GetType() == cmTarget::EXECUTABLE)
{
// Use the target file on disk.
exe = target->GetFullPath(config);
}
else
{
// Use the command name given.
exe = ge.Parse(exe.c_str()).Evaluate(mf, config);
cmSystemTools::ConvertToUnixSlashes(exe);
}
// Generate the command line with full escapes.
cmLocalGenerator* lg = mf->GetLocalGenerator();
os << lg->EscapeForCMake(exe.c_str());
for(std::vector<std::string>::const_iterator ci = command.begin()+1;
ci != command.end(); ++ci)
{
os << " " << lg->EscapeForCMake(ge.Parse(*ci).Evaluate(mf, config));
}
// Finish the test command.
os << ")\n";
}
//----------------------------------------------------------------------------
void cmTestGenerator::GenerateScriptNoConfig(std::ostream& os,
Indent const& indent)
{
os << indent << "ADD_TEST(" << this->Test->GetName() << " NOT_AVAILABLE)\n";
}
//----------------------------------------------------------------------------
bool cmTestGenerator::NeedsScriptNoConfig() const
{
return (this->TestGenerated && // test generated for at least one config
this->ActionsPerConfig && // test is config-aware
this->Configurations.empty() && // test runs in all configs
!this->ConfigurationTypes->empty()); // config-dependent command
}
//----------------------------------------------------------------------------
void cmTestGenerator::GenerateOldStyle(std::ostream& fout,
Indent const& indent)
{
this->TestGenerated = true;
// Get the test command line to be executed.
std::vector<std::string> const& command = this->Test->GetCommand();
std::string exe = command[0];
cmSystemTools::ConvertToUnixSlashes(exe);
fout << indent;
fout << "ADD_TEST(";
fout << this->Test->GetName() << " \"" << exe << "\"";
for(std::vector<std::string>::const_iterator argit = command.begin()+1;
argit != command.end(); ++argit)
{
// Just double-quote all arguments so they are re-parsed
// correctly by the test system.
fout << " \"";
for(std::string::const_iterator c = argit->begin();
c != argit->end(); ++c)
{
// Escape quotes within arguments. We should escape
// backslashes too but we cannot because it makes the result
// inconsistent with previous behavior of this command.
if((*c == '"'))
{
fout << '\\';
}
fout << *c;
}
fout << "\"";
}
fout << ")" << std::endl;
}