try_compile: Add signature to allow multiple SOURCES

Extend the signature

 try_compile(RESULT_VAR <bindir> <srcfile> ...)

to allow multiple sources as

 try_compile(RESULT_VAR <bindir> SOURCES <srcfile>... ...)

Process the sources to generate a CMakeLists.txt that enables all needed
languages.

Teach the TryCompile test to try cases with two sources of the same
language and of mixed languages.  Teach RunCMake.try_compile to cover
error cases for the signature.
This commit is contained in:
Brad King 2013-06-06 13:54:44 -04:00
parent bb879bcf2d
commit 482f1122ad
17 changed files with 162 additions and 62 deletions

View File

@ -38,10 +38,13 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
char targetNameBuf[64];
bool didOutputVariable = false;
bool didCopyFile = false;
bool useSources = argv[2] == "SOURCES";
std::vector<std::string> sources;
enum Doing { DoingNone, DoingCMakeFlags, DoingCompileDefinitions,
DoingLinkLibraries, DoingOutputVariable, DoingCopyFile };
Doing doing = DoingNone;
DoingLinkLibraries, DoingOutputVariable, DoingCopyFile,
DoingSources };
Doing doing = useSources? DoingSources : DoingNone;
for(size_t i=3; i < argv.size(); ++i)
{
if(argv[i] == "CMAKE_FLAGS")
@ -118,6 +121,10 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
copyFile = argv[i].c_str();
doing = DoingNone;
}
else if(doing == DoingSources)
{
sources.push_back(argv[i]);
}
else if(i == 3)
{
this->SrcFileSignature = false;
@ -149,6 +156,13 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
return -1;
}
if(useSources && sources.empty())
{
this->Makefile->IssueMessage(cmake::FATAL_ERROR,
"SOURCES must be followed by at least one source file");
return -1;
}
// compute the binary dir when TRY_COMPILE is called with a src file
// signature
if (this->SrcFileSignature)
@ -193,6 +207,44 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
std::string ccFile = this->BinaryDirectory + "/CMakeCache.txt";
cmSystemTools::RemoveFile(ccFile.c_str());
// Choose sources.
if(!useSources)
{
sources.push_back(argv[2]);
}
// Detect languages to enable.
cmGlobalGenerator* gg =
this->Makefile->GetCMakeInstance()->GetGlobalGenerator();
std::set<std::string> testLangs;
for(std::vector<std::string>::iterator si = sources.begin();
si != sources.end(); ++si)
{
std::string ext = cmSystemTools::GetFilenameLastExtension(*si);
if(const char* lang = gg->GetLanguageFromExtension(ext.c_str()))
{
testLangs.insert(lang);
}
else
{
cmOStringStream err;
err << "Unknown extension \"" << ext << "\" for file\n"
<< " " << *si << "\n"
<< "try_compile() works only for enabled languages. "
<< "Currently these are:\n ";
std::vector<std::string> langs;
gg->GetEnabledLanguages(langs);
for(std::vector<std::string>::iterator l = langs.begin();
l != langs.end(); ++l)
{
err << " " << *l;
}
err << "\nSee project() command to enable other languages.";
this->Makefile->IssueMessage(cmake::FATAL_ERROR, err.str());
return -1;
}
}
// we need to create a directory and CMakeLists file etc...
// first create the directories
sourceDirectory = this->BinaryDirectory.c_str();
@ -209,10 +261,6 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
return -1;
}
std::string source = argv[2];
std::string ext = cmSystemTools::GetFilenameLastExtension(source);
const char* lang =(this->Makefile->GetCMakeInstance()->GetGlobalGenerator()
->GetLanguageFromExtension(ext.c_str()));
const char* def = this->Makefile->GetDefinition("CMAKE_MODULE_PATH");
fprintf(fout, "cmake_minimum_required(VERSION %u.%u.%u.%u)\n",
cmVersion::GetMajorVersion(), cmVersion::GetMinorVersion(),
@ -222,57 +270,39 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
fprintf(fout, "SET(CMAKE_MODULE_PATH %s)\n", def);
}
const char* rulesOverrideBase = "CMAKE_USER_MAKE_RULES_OVERRIDE";
std::string rulesOverrideLang =
rulesOverrideBase + (lang ? std::string("_") + lang : std::string(""));
if(const char* rulesOverridePath =
this->Makefile->GetDefinition(rulesOverrideLang.c_str()))
std::string projectLangs;
for(std::set<std::string>::iterator li = testLangs.begin();
li != testLangs.end(); ++li)
{
fprintf(fout, "SET(%s \"%s\")\n",
rulesOverrideLang.c_str(), rulesOverridePath);
}
else if(const char* rulesOverridePath2 =
this->Makefile->GetDefinition(rulesOverrideBase))
{
fprintf(fout, "SET(%s \"%s\")\n",
rulesOverrideBase, rulesOverridePath2);
}
if(lang)
{
fprintf(fout, "PROJECT(CMAKE_TRY_COMPILE %s)\n", lang);
}
else
{
fclose(fout);
cmOStringStream err;
err << "Unknown extension \"" << ext << "\" for file\n"
<< " " << source << "\n"
<< "try_compile() works only for enabled languages. "
<< "Currently these are:\n ";
std::vector<std::string> langs;
this->Makefile->GetCMakeInstance()->GetGlobalGenerator()->
GetEnabledLanguages(langs);
for(std::vector<std::string>::iterator l = langs.begin();
l != langs.end(); ++l)
projectLangs += " " + *li;
std::string rulesOverrideBase = "CMAKE_USER_MAKE_RULES_OVERRIDE";
std::string rulesOverrideLang = rulesOverrideBase + "_" + *li;
if(const char* rulesOverridePath =
this->Makefile->GetDefinition(rulesOverrideLang.c_str()))
{
err << " " << *l;
fprintf(fout, "SET(%s \"%s\")\n",
rulesOverrideLang.c_str(), rulesOverridePath);
}
else if(const char* rulesOverridePath2 =
this->Makefile->GetDefinition(rulesOverrideBase.c_str()))
{
fprintf(fout, "SET(%s \"%s\")\n",
rulesOverrideBase.c_str(), rulesOverridePath2);
}
err << "\nSee project() command to enable other languages.";
this->Makefile->IssueMessage(cmake::FATAL_ERROR, err.str());
return -1;
}
std::string langFlags = "CMAKE_";
langFlags += lang;
langFlags += "_FLAGS";
fprintf(fout, "PROJECT(CMAKE_TRY_COMPILE%s)\n", projectLangs.c_str());
fprintf(fout, "SET(CMAKE_VERBOSE_MAKEFILE 1)\n");
fprintf(fout, "SET(CMAKE_%s_FLAGS \"", lang);
const char* flags = this->Makefile->GetDefinition(langFlags.c_str());
if(flags)
for(std::set<std::string>::iterator li = testLangs.begin();
li != testLangs.end(); ++li)
{
fprintf(fout, " %s ", flags);
fprintf(fout, "SET(CMAKE_%s_FLAGS \"", li->c_str());
std::string langFlags = "CMAKE_" + *li + "_FLAGS";
if(const char* flags = this->Makefile->GetDefinition(langFlags.c_str()))
{
fprintf(fout, " %s ", flags);
}
fprintf(fout, " ${COMPILE_DEFINITIONS}\")\n");
}
fprintf(fout, " ${COMPILE_DEFINITIONS}\")\n");
fprintf(fout, "INCLUDE_DIRECTORIES(${INCLUDE_DIRECTORIES})\n");
fprintf(fout, "SET(CMAKE_SUPPRESS_REGENERATION 1)\n");
fprintf(fout, "LINK_DIRECTORIES(${LINK_DIRECTORIES})\n");
@ -357,7 +387,19 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
fprintf(fout, "SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"%s\")\n",
this->BinaryDirectory.c_str());
/* Create the actual executable. */
fprintf(fout, "ADD_EXECUTABLE(%s \"%s\")\n", targetName, source.c_str());
fprintf(fout, "ADD_EXECUTABLE(%s", targetName);
for(std::vector<std::string>::iterator si = sources.begin();
si != sources.end(); ++si)
{
fprintf(fout, " \"%s\"", si->c_str());
// Add dependencies on any non-temporary sources.
if(si->find("CMakeTmp") == si->npos)
{
this->Makefile->AddCMakeDependFile(si->c_str());
}
}
fprintf(fout, ")\n");
if (useOldLinkLibs)
{
fprintf(fout,
@ -371,12 +413,6 @@ int cmCoreTryCompile::TryCompileCode(std::vector<std::string> const& argv)
}
fclose(fout);
projectName = "CMAKE_TRY_COMPILE";
// if the source is not in CMakeTmp
if(source.find("CMakeTmp") == source.npos)
{
this->Makefile->AddCMakeDependFile(source.c_str());
}
}
bool erroroc = cmSystemTools::GetErrorOccuredFlag();

View File

@ -64,16 +64,16 @@ public:
"Specify targetName to build a specific target instead of the 'all' or "
"'ALL_BUILD' target."
"\n"
" try_compile(RESULT_VAR <bindir> <srcfile>\n"
" try_compile(RESULT_VAR <bindir> <srcfile|SOURCES srcfile...>\n"
" [CMAKE_FLAGS flags...]\n"
" [COMPILE_DEFINITIONS flags...]\n"
" [LINK_LIBRARIES libs...]\n"
" [OUTPUT_VARIABLE <var>]\n"
" [COPY_FILE <fileName>])\n"
"Try building a source file into an executable. "
"In this form the user need only supply a source file that defines "
"a 'main'. "
"CMake will create a CMakeLists.txt file to build the source "
"Try building an executable from one or more source files. "
"In this form the user need only supply one or more source files "
"that include a definition for 'main'. "
"CMake will create a CMakeLists.txt file to build the source(s) "
"as an executable. "
"Specify COPY_FILE to get a copy of the linked executable at the "
"given fileName."

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,12 @@
CMake Error at BadSources1.cmake:1 \(try_compile\):
Unknown extension ".c" for file
.*/Tests/RunCMake/try_compile/src.c
try_compile\(\) works only for enabled languages. Currently these are:
NONE
See project\(\) command to enable other languages.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1 @@
try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.c)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,12 @@
CMake Error at BadSources2.cmake:2 \(try_compile\):
Unknown extension ".cxx" for file
.*/Tests/RunCMake/try_compile/src.cxx
try_compile\(\) works only for enabled languages. Currently these are:
C NONE( RC)?
See project\(\) command to enable other languages.
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,5 @@
enable_language(C)
try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR}
SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src.c
${CMAKE_CURRENT_SOURCE_DIR}/src.cxx
)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,4 @@
CMake Error at NoSources.cmake:1 \(try_compile\):
SOURCES must be followed by at least one source file
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1 @@
try_compile(RESULT ${CMAKE_CURRENT_BINARY_DIR} SOURCES)

View File

@ -7,6 +7,9 @@ run_cmake(NoCopyFile)
run_cmake(NoCopyFile2)
run_cmake(NoOutputVariable)
run_cmake(NoOutputVariable2)
run_cmake(NoSources)
run_cmake(BadLinkLibraries)
run_cmake(BadSources1)
run_cmake(BadSources2)
run_cmake(NonSourceCopyFile)
run_cmake(NonSourceCompileDefinitions)

View File

@ -71,6 +71,24 @@ if(SHOULD_FAIL)
message(SEND_ERROR "Should fail passed ${TRY_OUT}")
endif()
# try to compile two files that should compile
try_compile(SHOULD_PASS
${TryCompile_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp
SOURCES ${TryCompile_SOURCE_DIR}/pass2a.c ${TryCompile_SOURCE_DIR}/pass2b.cxx
OUTPUT_VARIABLE TRY_OUT)
if(NOT SHOULD_PASS)
message(SEND_ERROR "should pass failed ${TRY_OUT}")
endif()
# try to compile two files that should not compile
try_compile(SHOULD_FAIL
${TryCompile_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp
SOURCES ${TryCompile_SOURCE_DIR}/fail2a.c ${TryCompile_SOURCE_DIR}/fail2b.c
OUTPUT_VARIABLE TRY_OUT)
if(SHOULD_FAIL)
message(SEND_ERROR "Should fail passed ${TRY_OUT}")
endif()
if(NOT SHOULD_FAIL)
if(SHOULD_PASS)
message("All Tests passed, ignore all previous output.")

View File

@ -0,0 +1 @@
int main(void) { return 0; }

View File

@ -0,0 +1 @@
does_not_compile

View File

@ -0,0 +1,2 @@
extern int pass2b(void);
int main() { return pass2b(); }

View File

@ -0,0 +1 @@
extern "C" int pass2b(void) { return 0; }