summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2011-05-24 18:48:14 (GMT)
committerCMake Topic Stage <kwrobot@kitware.com>2011-05-24 18:48:14 (GMT)
commite51bbc14f6c23087e5c1acd48f556de27d0bfdaa (patch)
tree048068a82996ac61495f5554065ea7803e5b5548
parentc5e00bf0b25fb816a80215c4c110d319c6cce079 (diff)
parentcdc2b41cc2161b21192460bb92da40c6d4c6107f (diff)
downloadCMake-e51bbc14f6c23087e5c1acd48f556de27d0bfdaa.zip
CMake-e51bbc14f6c23087e5c1acd48f556de27d0bfdaa.tar.gz
CMake-e51bbc14f6c23087e5c1acd48f556de27d0bfdaa.tar.bz2
Merge topic 'output-compile-lines'
cdc2b41 Fix CompileCommandOutput test build on Windows 7039d1f Fix CompileCommandOutput test for Make tools not supporting spaces 4268e3d run_compile_commands: Cast istream::get() result to char c45c60b run_compile_commands: Avoid extra stl vector conversion 7c5be51 run_compile_commands: Avoid shadow in std::map<>::at workaround 169bb05 Provide std::map<>::at for use in run_compile_commands 4e2185c Make std::map usage more portable in language=>flags/defines maps a7e7a04 Fix run_compile_commands build on Apple GCC 3.3 c9174c0 Fix signed/unsigned comparison in EscapeJSON 8346a28 Only offer the compile command output feature on unix systems 0e6b05f Adds a test for the compile command line output. 5674844 make compile command output optional fe07b05 implement cxx command output 65c0c24 cache flags and defines 3f064ef refactor flags and defines
-rw-r--r--Modules/CMakeGenericSystem.cmake6
-rw-r--r--Source/cmGlobalUnixMakefileGenerator3.cxx41
-rw-r--r--Source/cmGlobalUnixMakefileGenerator3.h6
-rw-r--r--Source/cmMakefileTargetGenerator.cxx133
-rw-r--r--Source/cmMakefileTargetGenerator.h6
-rw-r--r--Source/cmSystemTools.cxx16
-rw-r--r--Source/cmSystemTools.h2
-rw-r--r--Tests/CMakeLib/CMakeLists.txt5
-rw-r--r--Tests/CMakeLib/run_compile_commands.cxx141
-rw-r--r--Tests/CMakeLists.txt12
-rw-r--r--Tests/CompileCommandOutput/CMakeLists.txt16
-rw-r--r--Tests/CompileCommandOutput/compile_command_output.cxx9
-rw-r--r--Tests/CompileCommandOutput/file with spaces.cxx1
-rw-r--r--Tests/CompileCommandOutput/file_with_underscores.cxx3
-rw-r--r--Tests/CompileCommandOutput/file_with_underscores.h1
-rw-r--r--Tests/CompileCommandOutput/relative.cxx3
-rw-r--r--Tests/CompileCommandOutput/relative.h11
17 files changed, 367 insertions, 45 deletions
diff --git a/Modules/CMakeGenericSystem.cmake b/Modules/CMakeGenericSystem.cmake
index b5d3072..6615849 100644
--- a/Modules/CMakeGenericSystem.cmake
+++ b/Modules/CMakeGenericSystem.cmake
@@ -50,6 +50,12 @@ IF(CMAKE_GENERATOR MATCHES "Makefiles")
IF(DEFINED CMAKE_RULE_MESSAGES)
SET_PROPERTY(GLOBAL PROPERTY RULE_MESSAGES ${CMAKE_RULE_MESSAGES})
ENDIF(DEFINED CMAKE_RULE_MESSAGES)
+ IF(CMAKE_GENERATOR MATCHES "Unix Makefiles")
+ SET(CMAKE_EXPORT_COMPILE_COMMANDS OFF CACHE BOOL
+ "Enable/Disable output of compile commands during generation."
+ )
+ MARK_AS_ADVANCED(CMAKE_EXPORT_COMPILE_COMMANDS)
+ ENDIF(CMAKE_GENERATOR MATCHES "Unix Makefiles")
ENDIF(CMAKE_GENERATOR MATCHES "Makefiles")
diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx
index 55ff87e..169d77d 100644
--- a/Source/cmGlobalUnixMakefileGenerator3.cxx
+++ b/Source/cmGlobalUnixMakefileGenerator3.cxx
@@ -30,6 +30,7 @@ cmGlobalUnixMakefileGenerator3::cmGlobalUnixMakefileGenerator3()
#else
this->UseLinkScript = true;
#endif
+ this->CommandDatabase = NULL;
}
void cmGlobalUnixMakefileGenerator3
@@ -138,6 +139,17 @@ void cmGlobalUnixMakefileGenerator3
}
//----------------------------------------------------------------------------
+std::string EscapeJSON(const std::string& s) {
+ std::string result;
+ for (std::string::size_type i = 0; i < s.size(); ++i) {
+ if (s[i] == '"' || s[i] == '\\') {
+ result += '\\';
+ }
+ result += s[i];
+ }
+ return result;
+}
+
void cmGlobalUnixMakefileGenerator3::Generate()
{
// first do superclass method
@@ -179,6 +191,35 @@ void cmGlobalUnixMakefileGenerator3::Generate()
// write the main makefile
this->WriteMainMakefile2();
this->WriteMainCMakefile();
+
+ if (this->CommandDatabase != NULL) {
+ *this->CommandDatabase << std::endl << "]";
+ delete this->CommandDatabase;
+ this->CommandDatabase = NULL;
+ }
+}
+
+void cmGlobalUnixMakefileGenerator3::AddCXXCompileCommand(
+ const std::string &sourceFile, const std::string &workingDirectory,
+ const std::string &compileCommand) {
+ if (this->CommandDatabase == NULL)
+ {
+ std::string commandDatabaseName =
+ std::string(this->GetCMakeInstance()->GetHomeOutputDirectory())
+ + "/compile_commands.json";
+ this->CommandDatabase =
+ new cmGeneratedFileStream(commandDatabaseName.c_str());
+ *this->CommandDatabase << "[" << std::endl;
+ } else {
+ *this->CommandDatabase << "," << std::endl;
+ }
+ *this->CommandDatabase << "{" << std::endl
+ << " \"directory\": \"" << EscapeJSON(workingDirectory) << "\","
+ << std::endl
+ << " \"command\": \"" << EscapeJSON(compileCommand) << "\","
+ << std::endl
+ << " \"file\": \"" << EscapeJSON(sourceFile) << "\""
+ << std::endl << "}";
}
void cmGlobalUnixMakefileGenerator3::WriteMainMakefile2()
diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h
index 11174d7..d21d5b9 100644
--- a/Source/cmGlobalUnixMakefileGenerator3.h
+++ b/Source/cmGlobalUnixMakefileGenerator3.h
@@ -112,6 +112,10 @@ public:
/** Record per-target progress information. */
void RecordTargetProgress(cmMakefileTargetGenerator* tg);
+ void AddCXXCompileCommand(const std::string &sourceFile,
+ const std::string &workingDirectory,
+ const std::string &compileCommand);
+
protected:
void WriteMainMakefile2();
void WriteMainCMakefile();
@@ -176,6 +180,8 @@ protected:
size_t CountProgressMarksInTarget(cmTarget* target,
std::set<cmTarget*>& emitted);
size_t CountProgressMarksInAll(cmLocalUnixMakefileGenerator3* lg);
+
+ cmGeneratedFileStream *CommandDatabase;
};
#endif
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index 69320da..d0df8f0 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -249,51 +249,17 @@ void cmMakefileTargetGenerator::WriteCommonCodeRules()
}
//----------------------------------------------------------------------------
-void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
+std::string cmMakefileTargetGenerator::GetFlags(const std::string &l)
{
- // write language flags for target
- std::set<cmStdString> languages;
- this->Target->GetLanguages(languages);
- // put the compiler in the rules.make file so that if it changes
- // things rebuild
- for(std::set<cmStdString>::const_iterator l = languages.begin();
- l != languages.end(); ++l)
- {
- cmStdString compiler = "CMAKE_";
- compiler += *l;
- compiler += "_COMPILER";
- *this->FlagFileStream << "# compile " << l->c_str() << " with " <<
- this->Makefile->GetSafeDefinition(compiler.c_str()) << "\n";
- }
-
- for(std::set<cmStdString>::const_iterator l = languages.begin();
- l != languages.end(); ++l)
+ ByLanguageMap::iterator i = this->FlagsByLanguage.find(l);
+ if (i == this->FlagsByLanguage.end())
{
- const char *lang = l->c_str();
std::string flags;
- std::string defines;
+ const char *lang = l.c_str();
+
bool shared = ((this->Target->GetType() == cmTarget::SHARED_LIBRARY) ||
(this->Target->GetType() == cmTarget::MODULE_LIBRARY));
- // Add the export symbol definition for shared library objects.
- if(const char* exportMacro = this->Target->GetExportMacro())
- {
- this->LocalGenerator->AppendDefines(defines, exportMacro, lang);
- }
-
- // Add preprocessor definitions for this target and configuration.
- this->LocalGenerator->AppendDefines
- (defines, this->Makefile->GetProperty("COMPILE_DEFINITIONS"), lang);
- this->LocalGenerator->AppendDefines
- (defines, this->Target->GetProperty("COMPILE_DEFINITIONS"), lang);
- std::string defPropName = "COMPILE_DEFINITIONS_";
- defPropName +=
- cmSystemTools::UpperCase(this->LocalGenerator->ConfigurationName);
- this->LocalGenerator->AppendDefines
- (defines, this->Makefile->GetProperty(defPropName.c_str()), lang);
- this->LocalGenerator->AppendDefines
- (defines, this->Target->GetProperty(defPropName.c_str()), lang);
-
// Add language feature flags.
this->AddFeatureFlags(flags, lang);
@@ -301,7 +267,7 @@ void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
lang, this->ConfigName);
// Fortran-specific flags computed for this target.
- if(*l == "Fortran")
+ if(l == "Fortran")
{
this->AddFortranFlags(flags);
}
@@ -320,14 +286,73 @@ void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
this->LocalGenerator->
AppendFlags(flags,this->GetFrameworkFlags().c_str());
- *this->FlagFileStream << lang << "_FLAGS = " << flags << "\n\n";
- *this->FlagFileStream << lang << "_DEFINES = " << defines << "\n\n";
+ ByLanguageMap::value_type entry(l, flags);
+ i = this->FlagsByLanguage.insert(entry).first;
+ }
+ return i->second;
+}
+
+std::string cmMakefileTargetGenerator::GetDefines(const std::string &l)
+{
+ ByLanguageMap::iterator i = this->DefinesByLanguage.find(l);
+ if (i == this->DefinesByLanguage.end())
+ {
+ std::string defines;
+ const char *lang = l.c_str();
+ // Add the export symbol definition for shared library objects.
+ if(const char* exportMacro = this->Target->GetExportMacro())
+ {
+ this->LocalGenerator->AppendDefines(defines, exportMacro, lang);
+ }
+
+ // Add preprocessor definitions for this target and configuration.
+ this->LocalGenerator->AppendDefines
+ (defines, this->Makefile->GetProperty("COMPILE_DEFINITIONS"), lang);
+ this->LocalGenerator->AppendDefines
+ (defines, this->Target->GetProperty("COMPILE_DEFINITIONS"), lang);
+ std::string defPropName = "COMPILE_DEFINITIONS_";
+ defPropName +=
+ cmSystemTools::UpperCase(this->LocalGenerator->ConfigurationName);
+ this->LocalGenerator->AppendDefines
+ (defines, this->Makefile->GetProperty(defPropName.c_str()), lang);
+ this->LocalGenerator->AppendDefines
+ (defines, this->Target->GetProperty(defPropName.c_str()), lang);
+
+ ByLanguageMap::value_type entry(l, defines);
+ i = this->DefinesByLanguage.insert(entry).first;
+ }
+ return i->second;
+}
+
+void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
+{
+ // write language flags for target
+ std::set<cmStdString> languages;
+ this->Target->GetLanguages(languages);
+ // put the compiler in the rules.make file so that if it changes
+ // things rebuild
+ for(std::set<cmStdString>::const_iterator l = languages.begin();
+ l != languages.end(); ++l)
+ {
+ cmStdString compiler = "CMAKE_";
+ compiler += *l;
+ compiler += "_COMPILER";
+ *this->FlagFileStream << "# compile " << l->c_str() << " with " <<
+ this->Makefile->GetSafeDefinition(compiler.c_str()) << "\n";
+ }
+
+ for(std::set<cmStdString>::const_iterator l = languages.begin();
+ l != languages.end(); ++l)
+ {
+ *this->FlagFileStream << *l << "_FLAGS = " << this->GetFlags(*l) << "\n\n";
+ *this->FlagFileStream << *l << "_DEFINES = " << this->GetDefines(*l) <<
+ "\n\n";
}
// Add target-specific flags.
if(this->Target->GetProperty("COMPILE_FLAGS"))
{
- std::string flags;
+ std::string flags;
this->LocalGenerator->AppendFlags
(flags, this->Target->GetProperty("COMPILE_FLAGS"));
*this->FlagFileStream << "# TARGET_FLAGS = " << flags << "\n\n";
@@ -641,6 +666,9 @@ cmMakefileTargetGenerator
vars.Flags = flags.c_str();
vars.Defines = defines.c_str();
+ bool lang_is_c_or_cxx = ((strcmp(lang, "C") == 0) ||
+ (strcmp(lang, "CXX") == 0));
+
// Construct the compile rules.
{
std::string compileRuleVar = "CMAKE_";
@@ -651,6 +679,23 @@ cmMakefileTargetGenerator
std::vector<std::string> compileCommands;
cmSystemTools::ExpandListArgument(compileRule, compileCommands);
+ if (this->Makefile->IsOn("CMAKE_EXPORT_COMPILE_COMMANDS") &&
+ lang_is_c_or_cxx && compileCommands.size() == 1)
+ {
+ std::string compileCommand = compileCommands[0];
+ this->LocalGenerator->ExpandRuleVariables(compileCommand, vars);
+ std::string workingDirectory =
+ this->LocalGenerator->Convert(
+ this->Makefile->GetStartOutputDirectory(), cmLocalGenerator::FULL);
+ compileCommand.replace(compileCommand.find(langFlags),
+ langFlags.size(), this->GetFlags(lang));
+ std::string langDefines = std::string("$(") + lang + "_DEFINES)";
+ compileCommand.replace(compileCommand.find(langDefines),
+ langDefines.size(), this->GetDefines(lang));
+ this->GlobalGenerator->AddCXXCompileCommand(
+ source.GetFullPath(), workingDirectory, compileCommand);
+ }
+
// Expand placeholders in the commands.
for(std::vector<std::string>::iterator i = compileCommands.begin();
i != compileCommands.end(); ++i)
@@ -691,8 +736,6 @@ cmMakefileTargetGenerator
}
}
- bool lang_is_c_or_cxx = ((strcmp(lang, "C") == 0) ||
- (strcmp(lang, "CXX") == 0));
bool do_preprocess_rules = lang_is_c_or_cxx &&
this->LocalGenerator->GetCreatePreprocessedSourceRules();
bool do_assembly_rules = lang_is_c_or_cxx &&
diff --git a/Source/cmMakefileTargetGenerator.h b/Source/cmMakefileTargetGenerator.h
index bd26795..b68f8bf 100644
--- a/Source/cmMakefileTargetGenerator.h
+++ b/Source/cmMakefileTargetGenerator.h
@@ -216,6 +216,12 @@ protected:
std::string MacContentDirectory;
std::set<cmStdString> MacContentFolders;
+ typedef std::map<cmStdString, cmStdString> ByLanguageMap;
+ std::string GetFlags(const std::string &l);
+ ByLanguageMap FlagsByLanguage;
+ std::string GetDefines(const std::string &l);
+ ByLanguageMap DefinesByLanguage;
+
// Target-wide Fortran module output directory.
bool FortranModuleDirectoryComputed;
std::string FortranModuleDirectory;
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index b992054..dc63568 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -445,6 +445,13 @@ public:
args.push_back(*arg);
}
}
+ void Store(std::vector<cmStdString>& args) const
+ {
+ for(char** arg = this->ArgV; arg && *arg; ++arg)
+ {
+ args.push_back(*arg);
+ }
+ }
};
//----------------------------------------------------------------------------
@@ -456,6 +463,15 @@ void cmSystemTools::ParseUnixCommandLine(const char* command,
argv.Store(args);
}
+//----------------------------------------------------------------------------
+void cmSystemTools::ParseUnixCommandLine(const char* command,
+ std::vector<cmStdString>& args)
+{
+ // Invoke the underlying parser.
+ cmSystemToolsArgV argv = cmsysSystem_Parse_CommandForUnix(command, 0);
+ argv.Store(args);
+}
+
std::string cmSystemTools::EscapeWindowsShellArgument(const char* arg,
int shell_flags)
{
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 78b9abf..fc85d4a 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -237,6 +237,8 @@ public:
/** Parse arguments out of a unix command line string. */
static void ParseUnixCommandLine(const char* command,
std::vector<std::string>& args);
+ static void ParseUnixCommandLine(const char* command,
+ std::vector<cmStdString>& args);
/** Compute an escaped version of the given argument for use in a
windows shell. See kwsys/System.h.in for details. */
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index bda2fa5..41bf034 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -30,3 +30,8 @@ endif()
foreach(test ${CMakeLib_TESTS})
add_test(CMakeLib.${test} CMakeLibTests ${test})
endforeach()
+
+if(TEST_CompileCommandOutput)
+ add_executable(runcompilecommands run_compile_commands.cxx)
+ target_link_libraries(runcompilecommands CMakeLib)
+endif()
diff --git a/Tests/CMakeLib/run_compile_commands.cxx b/Tests/CMakeLib/run_compile_commands.cxx
new file mode 100644
index 0000000..3f141c5
--- /dev/null
+++ b/Tests/CMakeLib/run_compile_commands.cxx
@@ -0,0 +1,141 @@
+#include "cmSystemTools.h"
+
+class CompileCommandParser {
+public:
+ class CommandType: public std::map<cmStdString, cmStdString>
+ {
+ public:
+ cmStdString const& at(cmStdString const& k) const
+ {
+ const_iterator i = this->find(k);
+ if(i != this->end()) { return i->second; }
+ static cmStdString emptyString;
+ return emptyString;
+ }
+ };
+ typedef std::vector<CommandType> TranslationUnitsType;
+
+ CompileCommandParser(std::ifstream *input)
+ {
+ this->Input = input;
+ }
+
+ void Parse()
+ {
+ NextNonWhitespace();
+ ParseTranslationUnits();
+ }
+
+ const TranslationUnitsType& GetTranslationUnits()
+ {
+ return this->TranslationUnits;
+ }
+
+private:
+ void ParseTranslationUnits()
+ {
+ this->TranslationUnits = TranslationUnitsType();
+ ExpectOrDie('[', "at start of compile command file");
+ do
+ {
+ ParseTranslationUnit();
+ this->TranslationUnits.push_back(this->Command);
+ } while(Expect(','));
+ ExpectOrDie(']', "at end of array");
+ }
+
+ void ParseTranslationUnit()
+ {
+ this->Command = CommandType();
+ if(!Expect('{')) return;
+ if(Expect('}')) return;
+ do
+ {
+ ParseString();
+ std::string name = this->String;
+ ExpectOrDie(':', "between name and value");
+ ParseString();
+ std::string value = this->String;
+ this->Command[name] = value;
+ } while(Expect(','));
+ ExpectOrDie('}', "at end of object");
+ }
+
+ void ParseString()
+ {
+ this->String.clear();
+ if(!Expect('"')) return;
+ while (!Expect('"'))
+ {
+ Expect('\\');
+ this->String.push_back(C);
+ Next();
+ }
+ }
+
+ bool Expect(char c)
+ {
+ if(this->C == c)
+ {
+ NextNonWhitespace();
+ return true;
+ }
+ return false;
+ }
+
+ void ExpectOrDie(char c, const std::string & message)
+ {
+ if (!Expect(c))
+ ErrorExit(std::string("'") + c + "' expected " + message + ".");
+ }
+
+ void NextNonWhitespace()
+ {
+ do { Next(); } while (IsWhitespace());
+ }
+
+ void Next()
+ {
+ this->C = char(Input->get());
+ if (this->Input->bad()) ErrorExit("Unexpected end of file.");
+ }
+
+ void ErrorExit(const std::string &message) {
+ std::cout << "ERROR: " << message;
+ exit(1);
+ }
+
+ bool IsWhitespace()
+ {
+ return (this->C == ' ' || this->C == '\t' ||
+ this->C == '\n' || this->C == '\r');
+ }
+
+ char C;
+ TranslationUnitsType TranslationUnits;
+ CommandType Command;
+ std::string String;
+ std::ifstream *Input;
+};
+
+int main ()
+{
+ std::ifstream file("compile_commands.json");
+ CompileCommandParser parser(&file);
+ parser.Parse();
+ for(CompileCommandParser::TranslationUnitsType::const_iterator
+ it = parser.GetTranslationUnits().begin(),
+ end = parser.GetTranslationUnits().end(); it != end; ++it)
+ {
+ std::vector<cmStdString> command;
+ cmSystemTools::ParseUnixCommandLine(it->at("command").c_str(), command);
+ if (!cmSystemTools::RunSingleCommand(
+ command, 0, 0, it->at("directory").c_str()))
+ {
+ std::cout << "ERROR: Failed to run command \""
+ << command[0] << "\"" << std::endl;
+ exit(1);
+ }
+ }
+ return 0;
+}
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 157814e..e9aed16 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -11,6 +11,7 @@ MACRO(ADD_TEST_MACRO NAME COMMAND)
--build-generator ${CMAKE_TEST_GENERATOR}
--build-makeprogram ${CMAKE_TEST_MAKEPROGRAM}
--build-project ${proj}
+ ${${NAME}_EXTRA_OPTIONS}
--test-command ${COMMAND} ${ARGN})
LIST(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/${dir}")
ENDMACRO(ADD_TEST_MACRO)
@@ -39,6 +40,10 @@ CONFIGURE_FILE(${CMake_SOURCE_DIR}/Tests/EnforceConfig.cmake.in
# Testing
IF(BUILD_TESTING)
+ IF("${CMAKE_TEST_GENERATOR}" MATCHES "Unix Makefiles")
+ SET(TEST_CompileCommandOutput 1)
+ ENDIF()
+
ADD_SUBDIRECTORY(CMakeLib)
# Collect a list of all test build directories.
@@ -2031,6 +2036,13 @@ ${CMake_BINARY_DIR}/bin/cmake -DVERSION=master -P ${CMake_SOURCE_DIR}/Utilities/
ENDIF()
SET_TESTS_PROPERTIES(Contracts.${project} PROPERTIES TIMEOUT ${timeout})
ENDFOREACH()
+
+ IF(TEST_CompileCommandOutput)
+ SET(CompileCommandOutput_EXTRA_OPTIONS
+ --build-options -DMAKE_SUPPORTS_SPACES=${MAKE_IS_GNU})
+ ADD_TEST_MACRO(CompileCommandOutput
+ "${CMake_BINARY_DIR}/Tests/CMakeLib/runcompilecommands")
+ ENDIF()
ENDIF(BUILD_TESTING)
SUBDIRS(CMakeTests)
diff --git a/Tests/CompileCommandOutput/CMakeLists.txt b/Tests/CompileCommandOutput/CMakeLists.txt
new file mode 100644
index 0000000..bd8e305
--- /dev/null
+++ b/Tests/CompileCommandOutput/CMakeLists.txt
@@ -0,0 +1,16 @@
+# a simple C only test case
+cmake_minimum_required (VERSION 2.6)
+project (CompileCommandOutput CXX)
+
+SET(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+set(CMAKE_DEBUG_POSTFIX "_test_debug_postfix")
+IF(MAKE_SUPPORTS_SPACES)
+ SET(test1_srcs "file with spaces.cxx")
+ELSE()
+ SET(test1_srcs "file_with_underscores.cxx")
+ENDIF()
+ADD_LIBRARY(test1 STATIC ${test1_srcs})
+ADD_LIBRARY(test2 SHARED "../CompileCommandOutput/relative.cxx")
+INCLUDE_DIRECTORIES(${CompileCommandOutput_SOURCE_DIR}/../../Source)
+ADD_EXECUTABLE(CompileCommandOutput compile_command_output.cxx)
+TARGET_LINK_LIBRARIES(CompileCommandOutput test1 test2)
diff --git a/Tests/CompileCommandOutput/compile_command_output.cxx b/Tests/CompileCommandOutput/compile_command_output.cxx
new file mode 100644
index 0000000..145a064
--- /dev/null
+++ b/Tests/CompileCommandOutput/compile_command_output.cxx
@@ -0,0 +1,9 @@
+#include "file_with_underscores.h"
+#include "relative.h"
+
+int main (int argc, char** argv)
+{
+ file_with_underscores();
+ relative();
+ return 0;
+}
diff --git a/Tests/CompileCommandOutput/file with spaces.cxx b/Tests/CompileCommandOutput/file with spaces.cxx
new file mode 100644
index 0000000..554e176
--- /dev/null
+++ b/Tests/CompileCommandOutput/file with spaces.cxx
@@ -0,0 +1 @@
+#include "file_with_underscores.cxx"
diff --git a/Tests/CompileCommandOutput/file_with_underscores.cxx b/Tests/CompileCommandOutput/file_with_underscores.cxx
new file mode 100644
index 0000000..4f42ccf
--- /dev/null
+++ b/Tests/CompileCommandOutput/file_with_underscores.cxx
@@ -0,0 +1,3 @@
+#include "file_with_underscores.h"
+
+void file_with_underscores() {}
diff --git a/Tests/CompileCommandOutput/file_with_underscores.h b/Tests/CompileCommandOutput/file_with_underscores.h
new file mode 100644
index 0000000..0d73e31
--- /dev/null
+++ b/Tests/CompileCommandOutput/file_with_underscores.h
@@ -0,0 +1 @@
+void file_with_underscores();
diff --git a/Tests/CompileCommandOutput/relative.cxx b/Tests/CompileCommandOutput/relative.cxx
new file mode 100644
index 0000000..eae11e2
--- /dev/null
+++ b/Tests/CompileCommandOutput/relative.cxx
@@ -0,0 +1,3 @@
+#include "relative.h"
+
+void relative() {}
diff --git a/Tests/CompileCommandOutput/relative.h b/Tests/CompileCommandOutput/relative.h
new file mode 100644
index 0000000..ddfe551
--- /dev/null
+++ b/Tests/CompileCommandOutput/relative.h
@@ -0,0 +1,11 @@
+#if defined(_WIN32)
+# ifdef test2_EXPORTS
+# define TEST2_EXPORT __declspec(dllexport)
+# else
+# define TEST2_EXPORT __declspec(dllimport)
+# endif
+#else
+# define TEST2_EXPORT
+#endif
+
+TEST2_EXPORT void relative();