CMake/Source/CTest/cmCTestBuildAndTestHandler.cxx
Brad King 22c62c9e65 BUG: Sweeping changes to cleanup computation of target names. This should
fix many bugs related to target names being computed inconsistently.

- Centralized computation of a target's file name to a method in
  cmTarget.  Now that global knowledge is always available the
  *_CMAKE_PATH cache variables are no longer needed.

- Centralized computation of link library command lines and link
  directory search order.

- Moved computation of link directories needed to link CMake targets
  to be after evaluation of linking dependencies.

This also removed alot of duplicate code in which each version had its
own bugs.

This commit is surrounded by the tags

  CMake-TargetNameCentralization1-pre

and

  CMake-TargetNameCentralization1-post

so make the large set of changes easy to identify.
2006-01-13 18:18:32 -05:00

466 lines
13 KiB
C++

/*=========================================================================
Program: CMake - Cross-Platform Makefile Generator
Module: $RCSfile$
Language: C++
Date: $Date$
Version: $Revision$
Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#include "cmCTestBuildAndTestHandler.h"
#include "cmSystemTools.h"
#include "cmCTest.h"
#include "cmake.h"
#include "cmGlobalGenerator.h"
#include <cmsys/Process.h>
//----------------------------------------------------------------------
cmCTestBuildAndTestHandler::cmCTestBuildAndTestHandler()
{
m_BuildTwoConfig = false;
m_BuildNoClean = false;
m_BuildNoCMake = false;
}
//----------------------------------------------------------------------
void cmCTestBuildAndTestHandler::Initialize()
{
this->Superclass::Initialize();
}
//----------------------------------------------------------------------
const char* cmCTestBuildAndTestHandler::GetOutput()
{
return m_Output.c_str();
}
//----------------------------------------------------------------------
int cmCTestBuildAndTestHandler::ProcessHandler()
{
m_Output = "";
std::string output;
cmSystemTools::ResetErrorOccuredFlag();
cmListFileCache::ClearCache();
int retv = this->RunCMakeAndTest(&m_Output);
cmSystemTools::ResetErrorOccuredFlag();
cmListFileCache::ClearCache();
return retv;
}
//----------------------------------------------------------------------
int cmCTestBuildAndTestHandler::RunCMake(std::string* outstring, cmOStringStream &out,
std::string &cmakeOutString, std::string &cwd,
cmake *cm)
{
unsigned int k;
std::vector<std::string> args;
args.push_back(m_CTest->GetCMakeExecutable());
args.push_back(m_SourceDir);
if(m_BuildGenerator.size())
{
std::string generator = "-G";
generator += m_BuildGenerator;
args.push_back(generator);
}
if ( m_CTest->GetConfigType().size() > 0 )
{
std::string btype = "-DCMAKE_BUILD_TYPE:STRING=" + m_CTest->GetConfigType();
args.push_back(btype);
}
for(k=0; k < m_BuildOptions.size(); ++k)
{
args.push_back(m_BuildOptions[k]);
}
if (cm->Run(args) != 0)
{
out << "Error: cmake execution failed\n";
out << cmakeOutString << "\n";
// return to the original directory
cmSystemTools::ChangeDirectory(cwd.c_str());
if(outstring)
{
*outstring = out.str();
}
else
{
cmCTestLog(m_CTest, ERROR_MESSAGE, out.str() << std::endl);
}
return 1;
}
// do another config?
if(m_BuildTwoConfig)
{
if (cm->Run(args) != 0)
{
out << "Error: cmake execution failed\n";
out << cmakeOutString << "\n";
// return to the original directory
cmSystemTools::ChangeDirectory(cwd.c_str());
if(outstring)
{
*outstring = out.str();
}
else
{
cmCTestLog(m_CTest, ERROR_MESSAGE, out.str() << std::endl);
}
return 1;
}
}
return 0;
}
//----------------------------------------------------------------------
void CMakeMessageCallback(const char* m, const char*, bool&, void* s)
{
std::string* out = (std::string*)s;
*out += m;
*out += "\n";
}
//----------------------------------------------------------------------
void CMakeStdoutCallback(const char* m, int len, void* s)
{
std::string* out = (std::string*)s;
out->append(m, len);
}
//----------------------------------------------------------------------
int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring)
{
unsigned int k;
std::string cmakeOutString;
cmSystemTools::SetErrorCallback(CMakeMessageCallback, &cmakeOutString);
cmSystemTools::SetStdoutCallback(CMakeStdoutCallback, &cmakeOutString);
cmOStringStream out;
// What is this? double timeout = m_CTest->GetTimeOut();
int retVal = 0;
// if the generator and make program are not specified then it is an error
if (!m_BuildGenerator.size() || !m_BuildMakeProgram.size())
{
if(outstring)
{
*outstring =
"--build-and-test requires that both the generator and makeprogram "
"be provided using the --build-generator and --build-makeprogram "
"command line options. ";
}
return 1;
}
// make sure the binary dir is there
std::string cwd = cmSystemTools::GetCurrentWorkingDirectory();
out << "Internal cmake changing into directory: " << m_BinaryDir << "\n";
if (!cmSystemTools::FileIsDirectory(m_BinaryDir.c_str()))
{
cmSystemTools::MakeDirectory(m_BinaryDir.c_str());
}
cmSystemTools::ChangeDirectory(m_BinaryDir.c_str());
// should we cmake?
cmake cm;
cm.SetGlobalGenerator(cm.CreateGlobalGenerator(m_BuildGenerator.c_str()));
if(!m_BuildNoCMake)
{
// do the cmake step
if (this->RunCMake(outstring,out,cmakeOutString,cwd,&cm))
{
return 1;
}
}
// do the build
std::string output;
retVal = cm.GetGlobalGenerator()->Build(
m_SourceDir.c_str(), m_BinaryDir.c_str(),
m_BuildProject.c_str(), m_BuildTarget.c_str(),
&output, m_BuildMakeProgram.c_str(),
m_CTest->GetConfigType().c_str(),!m_BuildNoClean);
out << output;
if(outstring)
{
*outstring = out.str();
}
// if the build failed then return
if (retVal)
{
return 1;
}
// if not test was specified then we are done
if (!m_TestCommand.size())
{
return 0;
}
// now run the compiled test if we can find it
std::vector<std::string> attempted;
std::vector<std::string> failed;
std::string tempPath;
std::string filepath =
cmSystemTools::GetFilenamePath(m_TestCommand);
std::string filename =
cmSystemTools::GetFilenameName(m_TestCommand);
// if full path specified then search that first
if (filepath.size())
{
tempPath = filepath;
tempPath += "/";
tempPath += filename;
attempted.push_back(tempPath);
if(m_CTest->GetConfigType().size())
{
tempPath = filepath;
tempPath += "/";
tempPath += m_CTest->GetConfigType();
tempPath += "/";
tempPath += filename;
attempted.push_back(tempPath);
// If the file is an OSX bundle then the configtyp
// will be at the start of the path
tempPath = m_CTest->GetConfigType();
tempPath += "/";
tempPath += filepath;
tempPath += "/";
tempPath += filename;
attempted.push_back(tempPath);
}
}
// otherwise search local dirs
else
{
attempted.push_back(filename);
if(m_CTest->GetConfigType().size())
{
tempPath = m_CTest->GetConfigType();
tempPath += "/";
tempPath += filename;
attempted.push_back(tempPath);
}
}
// if m_ExecutableDirectory is set try that as well
if (m_ExecutableDirectory.size())
{
tempPath = m_ExecutableDirectory;
tempPath += "/";
tempPath += m_TestCommand;
attempted.push_back(tempPath);
if(m_CTest->GetConfigType().size())
{
tempPath = m_ExecutableDirectory;
tempPath += "/";
tempPath += m_CTest->GetConfigType();
tempPath += "/";
tempPath += filename;
attempted.push_back(tempPath);
}
}
// store the final location in fullPath
std::string fullPath;
// now look in the paths we specified above
for(unsigned int ai=0;
ai < attempted.size() && fullPath.size() == 0; ++ai)
{
// first check without exe extension
if(cmSystemTools::FileExists(attempted[ai].c_str())
&& !cmSystemTools::FileIsDirectory(attempted[ai].c_str()))
{
fullPath = cmSystemTools::CollapseFullPath(attempted[ai].c_str());
}
// then try with the exe extension
else
{
failed.push_back(attempted[ai].c_str());
tempPath = attempted[ai];
tempPath += cmSystemTools::GetExecutableExtension();
if(cmSystemTools::FileExists(tempPath.c_str())
&& !cmSystemTools::FileIsDirectory(tempPath.c_str()))
{
fullPath = cmSystemTools::CollapseFullPath(tempPath.c_str());
}
else
{
failed.push_back(tempPath.c_str());
}
}
}
if(!cmSystemTools::FileExists(fullPath.c_str()))
{
out << "Could not find path to executable, perhaps it was not built: " <<
m_TestCommand << "\n";
out << "tried to find it in these places:\n";
out << fullPath.c_str() << "\n";
for(unsigned int i=0; i < failed.size(); ++i)
{
out << failed[i] << "\n";
}
if(outstring)
{
*outstring = out.str();
}
else
{
cmCTestLog(m_CTest, ERROR_MESSAGE, out.str());
}
// return to the original directory
cmSystemTools::ChangeDirectory(cwd.c_str());
return 1;
}
std::vector<const char*> testCommand;
testCommand.push_back(fullPath.c_str());
for(k=0; k < m_TestCommandArgs.size(); ++k)
{
testCommand.push_back(m_TestCommandArgs[k].c_str());
}
testCommand.push_back(0);
std::string outs;
int retval = 0;
// run the test from the m_BuildRunDir if set
if(m_BuildRunDir.size())
{
out << "Run test in directory: " << m_BuildRunDir << "\n";
cmSystemTools::ChangeDirectory(m_BuildRunDir.c_str());
}
out << "Running test executable: " << fullPath << " ";
for(k=0; k < m_TestCommandArgs.size(); ++k)
{
out << m_TestCommandArgs[k] << " ";
}
out << "\n";
// What is this? m_TimeOut = timeout;
int runTestRes = m_CTest->RunTest(testCommand, &outs, &retval, 0);
if(runTestRes != cmsysProcess_State_Exited || retval != 0)
{
out << "Failed to run test command: " << testCommand[0] << "\n";
retval = 1;
}
out << outs << "\n";
if(outstring)
{
*outstring = out.str();
}
else
{
cmCTestLog(m_CTest, OUTPUT, out.str() << std::endl);
}
return retval;
}
//----------------------------------------------------------------------
int cmCTestBuildAndTestHandler::ProcessCommandLineArguments(
const std::string& currentArg, size_t& idx,
const std::vector<std::string>& allArgs)
{
// --build-and-test options
if(currentArg.find("--build-and-test",0) == 0 && idx < allArgs.size() - 1)
{
if(idx+2 < allArgs.size())
{
idx++;
m_SourceDir = allArgs[idx];
idx++;
m_BinaryDir = allArgs[idx];
// dir must exist before CollapseFullPath is called
cmSystemTools::MakeDirectory(m_BinaryDir.c_str());
m_BinaryDir = cmSystemTools::CollapseFullPath(m_BinaryDir.c_str());
m_SourceDir = cmSystemTools::CollapseFullPath(m_SourceDir.c_str());
}
else
{
cmCTestLog(m_CTest, ERROR_MESSAGE, "--build-and-test must have source and binary dir" << std::endl);
return 0;
}
}
if(currentArg.find("--build-target",0) == 0 && idx < allArgs.size() - 1)
{
idx++;
m_BuildTarget = allArgs[idx];
}
if(currentArg.find("--build-nocmake",0) == 0)
{
m_BuildNoCMake = true;
}
if(currentArg.find("--build-run-dir",0) == 0 && idx < allArgs.size() - 1)
{
idx++;
m_BuildRunDir = allArgs[idx];
}
if(currentArg.find("--build-two-config",0) == 0)
{
m_BuildTwoConfig = true;
}
if(currentArg.find("--build-exe-dir",0) == 0 && idx < allArgs.size() - 1)
{
idx++;
m_ExecutableDirectory = allArgs[idx];
}
if(currentArg.find("--build-generator",0) == 0 && idx < allArgs.size() - 1)
{
idx++;
m_BuildGenerator = allArgs[idx];
}
if(currentArg.find("--build-project",0) == 0 && idx < allArgs.size() - 1)
{
idx++;
m_BuildProject = allArgs[idx];
}
if(currentArg.find("--build-makeprogram",0) == 0 && idx < allArgs.size() - 1)
{
idx++;
m_BuildMakeProgram = allArgs[idx];
}
if(currentArg.find("--build-noclean",0) == 0)
{
m_BuildNoClean = true;
}
if(currentArg.find("--build-options",0) == 0 && idx < allArgs.size() - 1)
{
++idx;
bool done = false;
while(idx < allArgs.size() && !done)
{
m_BuildOptions.push_back(allArgs[idx]);
if(idx+1 < allArgs.size()
&& (allArgs[idx+1] == "--build-target" || allArgs[idx+1] == "--test-command"))
{
done = true;
}
else
{
++idx;
}
}
}
if(currentArg.find("--test-command",0) == 0 && idx < allArgs.size() - 1)
{
++idx;
m_TestCommand = allArgs[idx];
while(idx+1 < allArgs.size())
{
++idx;
m_TestCommandArgs.push_back(allArgs[idx]);
}
}
return 1;
}