Commit e51bbc14 authored by Brad King's avatar Brad King Committed by Kitware Robot

Merge topic 'output-compile-lines'

cdc2b41c Fix CompileCommandOutput test build on Windows
7039d1fd Fix CompileCommandOutput test for Make tools not supporting spaces
4268e3d7 run_compile_commands: Cast istream::get() result to char
c45c60b2 run_compile_commands: Avoid extra stl vector conversion
7c5be511 run_compile_commands: Avoid shadow in std::map<>::at workaround
169bb05f Provide std::map<>::at for use in run_compile_commands
4e2185cb Make std::map usage more portable in language=>flags/defines maps
a7e7a04a Fix run_compile_commands build on Apple GCC 3.3
c9174c0e Fix signed/unsigned comparison in EscapeJSON
8346a28a Only offer the compile command output feature on unix systems
0e6b05fc Adds a test for the compile command line output.
5674844d make compile command output optional
fe07b055 implement cxx command output
65c0c24a cache flags and defines
3f064efe refactor flags and defines
parents c5e00bf0 cdc2b41c
......@@ -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")
......
......@@ -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()
......
......@@ -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
......@@ -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 &&
......
......@@ -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;
......
......@@ -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)
{
......
......@@ -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. */
......
......@@ -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()
#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;
}
......@@ -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)
# 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)
#include "file_with_underscores.h"
#include "relative.h"
int main (int argc, char** argv)
{
file_with_underscores();
relative();
return 0;
}
#include "file_with_underscores.cxx"
#include "file_with_underscores.h"
void file_with_underscores() {}
#include "relative.h"
void relative() {}
#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();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment