CodeLite: Optionally use targets to create (sub)project files

The basic codelite generator creates .project files based on the
`project()` stanza.  Add a `CMAKE_CODELITE_USE_TARGETS` option to use
the targets instead.
This commit is contained in:
Minze Zwerver 2016-09-23 08:42:20 +02:00 committed by Brad King
parent d0a27ae998
commit cbe4887927
6 changed files with 284 additions and 99 deletions

View File

@ -5,7 +5,11 @@ Generates CodeLite project files.
Project files for CodeLite will be created in the top directory and Project files for CodeLite will be created in the top directory and
in every subdirectory which features a CMakeLists.txt file containing in every subdirectory which features a CMakeLists.txt file containing
a PROJECT() call. The appropriate make program can build the a :command:`project` call.
The :variable:`CMAKE_CODELITE_USE_TARGETS` variable may be set to ``ON``
to change the default behaviour from projects to targets as the basis
for project files.
The appropriate make program can build the
project through the default make target. A "make install" target is project through the default make target. A "make install" target is
also provided. also provided.

View File

@ -112,6 +112,7 @@ Variables that Change Behavior
/variable/CMAKE_AUTOMOC_RELAXED_MODE /variable/CMAKE_AUTOMOC_RELAXED_MODE
/variable/CMAKE_BACKWARDS_COMPATIBILITY /variable/CMAKE_BACKWARDS_COMPATIBILITY
/variable/CMAKE_BUILD_TYPE /variable/CMAKE_BUILD_TYPE
/variable/CMAKE_CODELITE_USE_TARGETS
/variable/CMAKE_COLOR_MAKEFILE /variable/CMAKE_COLOR_MAKEFILE
/variable/CMAKE_CONFIGURATION_TYPES /variable/CMAKE_CONFIGURATION_TYPES
/variable/CMAKE_DEBUG_TARGET_PROPERTIES /variable/CMAKE_DEBUG_TARGET_PROPERTIES

View File

@ -0,0 +1,6 @@
codelite-organize-by-target
---------------------------
* The :generator:`CodeLite` generator gained a new
:variable:`CMAKE_CODELITE_USE_TARGETS` option
to change project creation from projects to targets.

View File

@ -0,0 +1,7 @@
CMAKE_CODELITE_USE_TARGETS
--------------------------
Change the way the CodeLite generator creates projectfiles.
If this variable is set to ``ON`` the generator creates projectfiles
based on targets rather than projects.

View File

@ -70,6 +70,8 @@ void cmExtraCodeLiteGenerator::Generate()
// loop projects and locate the root project. // loop projects and locate the root project.
// and extract the information for creating the worspace // and extract the information for creating the worspace
// root makefile
const cmMakefile* rmf = CM_NULLPTR;
for (std::map<std::string, std::vector<cmLocalGenerator*> >::const_iterator for (std::map<std::string, std::vector<cmLocalGenerator*> >::const_iterator
it = projectMap.begin(); it = projectMap.begin();
it != projectMap.end(); ++it) { it != projectMap.end(); ++it) {
@ -84,6 +86,7 @@ void cmExtraCodeLiteGenerator::Generate()
workspaceFileName = workspaceOutputDir + "/"; workspaceFileName = workspaceOutputDir + "/";
workspaceFileName += workspaceProjectName + ".workspace"; workspaceFileName += workspaceProjectName + ".workspace";
this->WorkspacePath = it->second[0]->GetCurrentBinaryDirectory(); this->WorkspacePath = it->second[0]->GetCurrentBinaryDirectory();
rmf = it->second[0]->GetMakefile();
; ;
break; break;
} }
@ -96,13 +99,90 @@ void cmExtraCodeLiteGenerator::Generate()
xml.StartElement("CodeLite_Workspace"); xml.StartElement("CodeLite_Workspace");
xml.Attribute("Name", workspaceProjectName); xml.Attribute("Name", workspaceProjectName);
bool const targetsAreProjects =
rmf && rmf->IsOn("CMAKE_CODELITE_USE_TARGETS");
std::vector<std::string> ProjectNames;
if (targetsAreProjects) {
ProjectNames = CreateProjectsByTarget(&xml);
} else {
ProjectNames = CreateProjectsByProjectMaps(&xml);
}
xml.StartElement("BuildMatrix");
xml.StartElement("WorkspaceConfiguration");
xml.Attribute("Name", this->ConfigName);
xml.Attribute("Selected", "yes");
for (std::vector<std::string>::iterator it(ProjectNames.begin());
it != ProjectNames.end(); it++) {
xml.StartElement("Project");
xml.Attribute("Name", *it);
xml.Attribute("ConfigName", this->ConfigName);
xml.EndElement();
}
xml.EndElement(); // WorkspaceConfiguration
xml.EndElement(); // BuildMatrix
xml.EndElement(); // CodeLite_Workspace
}
// Create projects where targets are the projects
std::vector<std::string> cmExtraCodeLiteGenerator::CreateProjectsByTarget(
cmXMLWriter* xml)
{
std::vector<std::string> retval;
// for each target in the workspace create a codelite project
const std::vector<cmLocalGenerator*>& lgs =
this->GlobalGenerator->GetLocalGenerators();
for (std::vector<cmLocalGenerator*>::const_iterator lg(lgs.begin());
lg != lgs.end(); lg++) {
for (std::vector<cmGeneratorTarget*>::const_iterator lt =
(*lg)->GetGeneratorTargets().begin();
lt != (*lg)->GetGeneratorTargets().end(); lt++) {
cmState::TargetType type = (*lt)->GetType();
std::string outputDir = (*lg)->GetCurrentBinaryDirectory();
std::string filename = outputDir + "/" + (*lt)->GetName() + ".project";
retval.push_back((*lt)->GetName());
// Make the project file relative to the workspace
std::string relafilename = cmSystemTools::RelativePath(
this->WorkspacePath.c_str(), filename.c_str());
std::string visualname = (*lt)->GetName();
switch (type) {
case cmState::SHARED_LIBRARY:
case cmState::STATIC_LIBRARY:
case cmState::MODULE_LIBRARY:
visualname = "lib" + visualname;
case cmState::EXECUTABLE:
xml->StartElement("Project");
xml->Attribute("Name", visualname);
xml->Attribute("Path", relafilename);
xml->Attribute("Active", "No");
xml->EndElement();
CreateNewProjectFile(*lt, filename);
break;
default:
break;
}
}
}
return retval;
}
// The "older way of doing it.
std::vector<std::string> cmExtraCodeLiteGenerator::CreateProjectsByProjectMaps(
cmXMLWriter* xml)
{
std::vector<std::string> retval;
// for each sub project in the workspace create a codelite project // for each sub project in the workspace create a codelite project
for (std::map<std::string, std::vector<cmLocalGenerator*> >::const_iterator for (std::map<std::string, std::vector<cmLocalGenerator*> >::const_iterator
it = projectMap.begin(); it = this->GlobalGenerator->GetProjectMap().begin();
it != projectMap.end(); ++it) { it != this->GlobalGenerator->GetProjectMap().end(); it++) {
// retrive project information
std::string outputDir = it->second[0]->GetCurrentBinaryDirectory(); std::string outputDir = it->second[0]->GetCurrentBinaryDirectory();
std::string projectName = it->second[0]->GetProjectName(); std::string projectName = it->second[0]->GetProjectName();
retval.push_back(projectName);
std::string filename = outputDir + "/" + projectName + ".project"; std::string filename = outputDir + "/" + projectName + ".project";
// Make the project file relative to the workspace // Make the project file relative to the workspace
@ -111,33 +191,13 @@ void cmExtraCodeLiteGenerator::Generate()
// create a project file // create a project file
this->CreateProjectFile(it->second); this->CreateProjectFile(it->second);
xml.StartElement("Project"); xml->StartElement("Project");
xml.Attribute("Name", projectName); xml->Attribute("Name", projectName);
xml.Attribute("Path", filename); xml->Attribute("Path", filename);
xml.Attribute("Active", "No"); xml->Attribute("Active", "No");
xml.EndElement(); xml->EndElement();
} }
return retval;
xml.StartElement("BuildMatrix");
xml.StartElement("WorkspaceConfiguration");
xml.Attribute("Name", this->ConfigName);
xml.Attribute("Selected", "yes");
for (std::map<std::string, std::vector<cmLocalGenerator*> >::const_iterator
it = projectMap.begin();
it != projectMap.end(); ++it) {
// retrive project information
std::string projectName = it->second[0]->GetProjectName();
xml.StartElement("Project");
xml.Attribute("Name", projectName);
xml.Attribute("ConfigName", this->ConfigName);
xml.EndElement();
}
xml.EndElement(); // WorkspaceConfiguration
xml.EndElement(); // BuildMatrix
xml.EndElement(); // CodeLite_Workspace
} }
/* create the project file */ /* create the project file */
@ -152,6 +212,70 @@ void cmExtraCodeLiteGenerator::CreateProjectFile(
this->CreateNewProjectFile(lgs, filename); this->CreateNewProjectFile(lgs, filename);
} }
std::string cmExtraCodeLiteGenerator::CollectSourceFiles(
const cmMakefile* makefile, const cmGeneratorTarget* gt,
std::map<std::string, cmSourceFile*>& cFiles,
std::set<std::string>& otherFiles)
{
const std::vector<std::string>& srcExts =
this->GlobalGenerator->GetCMakeInstance()->GetSourceExtensions();
std::string projectType;
switch (gt->GetType()) {
case cmState::EXECUTABLE: {
projectType = "Executable";
} break;
case cmState::STATIC_LIBRARY: {
projectType = "Static Library";
} break;
case cmState::SHARED_LIBRARY: {
projectType = "Dynamic Library";
} break;
case cmState::MODULE_LIBRARY: {
projectType = "Dynamic Library";
} break;
default: // intended fallthrough
break;
}
switch (gt->GetType()) {
case cmState::EXECUTABLE:
case cmState::STATIC_LIBRARY:
case cmState::SHARED_LIBRARY:
case cmState::MODULE_LIBRARY: {
std::vector<cmSourceFile*> sources;
gt->GetSourceFiles(sources,
makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
for (std::vector<cmSourceFile*>::const_iterator si = sources.begin();
si != sources.end(); si++) {
// check whether it is a C/C++ implementation file
bool isCFile = false;
std::string lang = (*si)->GetLanguage();
if (lang == "C" || lang == "CXX") {
std::string srcext = (*si)->GetExtension();
for (std::vector<std::string>::const_iterator ext = srcExts.begin();
ext != srcExts.end(); ++ext) {
if (srcext == *ext) {
isCFile = true;
break;
}
}
}
// then put it accordingly into one of the two containers
if (isCFile) {
cFiles[(*si)->GetFullPath()] = *si;
} else {
otherFiles.insert((*si)->GetFullPath());
}
}
}
default: // intended fallthrough
break;
}
return projectType;
}
void cmExtraCodeLiteGenerator::CreateNewProjectFile( void cmExtraCodeLiteGenerator::CreateNewProjectFile(
const std::vector<cmLocalGenerator*>& lgs, const std::string& filename) const std::vector<cmLocalGenerator*>& lgs, const std::string& filename)
{ {
@ -168,82 +292,42 @@ void cmExtraCodeLiteGenerator::CreateNewProjectFile(
xml.Attribute("Name", lgs[0]->GetProjectName()); xml.Attribute("Name", lgs[0]->GetProjectName());
xml.Attribute("InternalType", ""); xml.Attribute("InternalType", "");
std::string projectType;
// Collect all used source files in the project // Collect all used source files in the project
// Sort them into two containers, one for C/C++ implementation files // Sort them into two containers, one for C/C++ implementation files
// which may have an acompanying header, one for all other files // which may have an acompanying header, one for all other files
std::string projectType;
std::vector<std::string> srcExts =
this->GlobalGenerator->GetCMakeInstance()->GetSourceExtensions();
std::vector<std::string> headerExts =
this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions();
std::map<std::string, cmSourceFile*> cFiles; std::map<std::string, cmSourceFile*> cFiles;
std::set<std::string> otherFiles; std::set<std::string> otherFiles;
for (std::vector<cmLocalGenerator*>::const_iterator lg = lgs.begin(); for (std::vector<cmLocalGenerator*>::const_iterator lg = lgs.begin();
lg != lgs.end(); lg++) { lg != lgs.end(); lg++) {
cmMakefile* makefile = (*lg)->GetMakefile(); cmMakefile* makefile = (*lg)->GetMakefile();
std::vector<cmGeneratorTarget*> targets = (*lg)->GetGeneratorTargets(); std::vector<cmGeneratorTarget*> targets = (*lg)->GetGeneratorTargets();
for (std::vector<cmGeneratorTarget*>::iterator ti = targets.begin(); for (std::vector<cmGeneratorTarget*>::iterator ti = targets.begin();
ti != targets.end(); ti++) { ti != targets.end(); ti++) {
projectType = CollectSourceFiles(makefile, *ti, cFiles, otherFiles);
switch ((*ti)->GetType()) {
case cmState::EXECUTABLE: {
projectType = "Executable";
} break;
case cmState::STATIC_LIBRARY: {
projectType = "Static Library";
} break;
case cmState::SHARED_LIBRARY: {
projectType = "Dynamic Library";
} break;
case cmState::MODULE_LIBRARY: {
projectType = "Dynamic Library";
} break;
default: // intended fallthrough
break;
}
switch ((*ti)->GetType()) {
case cmState::EXECUTABLE:
case cmState::STATIC_LIBRARY:
case cmState::SHARED_LIBRARY:
case cmState::MODULE_LIBRARY: {
std::vector<cmSourceFile*> sources;
cmGeneratorTarget* gt = *ti;
gt->GetSourceFiles(sources,
makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
for (std::vector<cmSourceFile*>::const_iterator si = sources.begin();
si != sources.end(); si++) {
// check whether it is a C/C++ implementation file
bool isCFile = false;
std::string lang = (*si)->GetLanguage();
if (lang == "C" || lang == "CXX") {
std::string srcext = (*si)->GetExtension();
for (std::vector<std::string>::const_iterator ext =
srcExts.begin();
ext != srcExts.end(); ++ext) {
if (srcext == *ext) {
isCFile = true;
break;
}
}
}
// then put it accordingly into one of the two containers
if (isCFile) {
cFiles[(*si)->GetFullPath()] = *si;
} else {
otherFiles.insert((*si)->GetFullPath());
}
}
}
default: // intended fallthrough
break;
}
} }
} }
// Get the project path ( we need it later to convert files to
// their relative path)
std::string projectPath = cmSystemTools::GetFilenamePath(filename);
CreateProjectSourceEntries(cFiles, otherFiles, &xml, projectPath, mf,
projectType);
xml.EndElement(); // CodeLite_Project
}
void cmExtraCodeLiteGenerator::FindMatchingHeaderfiles(
std::map<std::string, cmSourceFile*>& cFiles,
std::set<std::string>& otherFiles)
{
const std::vector<std::string>& headerExts =
this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions();
// The following loop tries to add header files matching to implementation // The following loop tries to add header files matching to implementation
// files to the project. It does that by iterating over all source files, // files to the project. It does that by iterating over all source files,
// replacing the file name extension with ".h" and checks whether such a // replacing the file name extension with ".h" and checks whether such a
@ -275,11 +359,17 @@ void cmExtraCodeLiteGenerator::CreateNewProjectFile(
} }
} }
} }
}
// Get the project path ( we need it later to convert files to void cmExtraCodeLiteGenerator::CreateProjectSourceEntries(
// their relative path) std::map<std::string, cmSourceFile*>& cFiles,
std::string projectPath = cmSystemTools::GetFilenamePath(filename); std::set<std::string>& otherFiles, cmXMLWriter* _xml,
const std::string& projectPath, const cmMakefile* mf,
const std::string& projectType)
{
cmXMLWriter& xml(*_xml);
FindMatchingHeaderfiles(cFiles, otherFiles);
// Create 2 virtual folders: src and include // Create 2 virtual folders: src and include
// and place all the implementation files into the src // and place all the implementation files into the src
// folder, the rest goes to the include folder // folder, the rest goes to the include folder
@ -292,8 +382,10 @@ void cmExtraCodeLiteGenerator::CreateNewProjectFile(
cFiles.begin(); cFiles.begin();
sit != cFiles.end(); ++sit) { sit != cFiles.end(); ++sit) {
xml.StartElement("File"); xml.StartElement("File");
xml.Attribute("Name", cmSystemTools::RelativePath(projectPath.c_str(), std::string fpath(sit->first);
sit->first.c_str())); std::string frelapath =
cmSystemTools::RelativePath(projectPath.c_str(), sit->first.c_str());
xml.Attribute("Name", frelapath);
xml.EndElement(); xml.EndElement();
} }
xml.EndElement(); // VirtualDirectory xml.EndElement(); // VirtualDirectory
@ -350,11 +442,18 @@ void cmExtraCodeLiteGenerator::CreateNewProjectFile(
xml.EndElement(); // ResourceCompiler xml.EndElement(); // ResourceCompiler
xml.StartElement("General"); xml.StartElement("General");
xml.Attribute("OutputFile", "$(IntermediateDirectory)/$(ProjectName)"); std::string outputPath = mf->GetSafeDefinition("EXECUTABLE_OUTPUT_PATH");
if (!outputPath.empty())
xml.Attribute("OutputFile", outputPath + "/$(ProjectName)");
else
xml.Attribute("OutputFile", "$(IntermediateDirectory)/$(ProjectName)");
xml.Attribute("IntermediateDirectory", "./"); xml.Attribute("IntermediateDirectory", "./");
xml.Attribute("Command", "./$(ProjectName)"); xml.Attribute("Command", "./$(ProjectName)");
xml.Attribute("CommandArguments", ""); xml.Attribute("CommandArguments", "");
xml.Attribute("WorkingDirectory", "$(IntermediateDirectory)"); if (!outputPath.empty())
xml.Attribute("WorkingDirectory", outputPath);
else
xml.Attribute("WorkingDirectory", "$(IntermediateDirectory)");
xml.Attribute("PauseExecWhenProcTerminates", "yes"); xml.Attribute("PauseExecWhenProcTerminates", "yes");
xml.EndElement(); // General xml.EndElement(); // General
@ -408,6 +507,53 @@ void cmExtraCodeLiteGenerator::CreateNewProjectFile(
xml.EndElement(); // GlobalSettings xml.EndElement(); // GlobalSettings
xml.EndElement(); // Settings xml.EndElement(); // Settings
}
void cmExtraCodeLiteGenerator::CreateNewProjectFile(
const cmGeneratorTarget* gt, const std::string& filename)
{
const cmMakefile* mf = gt->Makefile;
cmGeneratedFileStream fout(filename.c_str());
if (!fout) {
return;
}
cmXMLWriter xml(fout);
////////////////////////////////////
xml.StartDocument("utf-8");
xml.StartElement("CodeLite_Project");
std::string visualname = gt->GetName();
switch (gt->GetType()) {
case cmState::STATIC_LIBRARY:
case cmState::SHARED_LIBRARY:
case cmState::MODULE_LIBRARY:
visualname = "lib" + visualname;
default: // intended fallthrough
break;
}
xml.Attribute("Name", visualname);
xml.Attribute("InternalType", "");
// Collect all used source files in the project
// Sort them into two containers, one for C/C++ implementation files
// which may have an acompanying header, one for all other files
std::string projectType;
std::vector<std::string> headerExts =
this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions();
std::map<std::string, cmSourceFile*> cFiles;
std::set<std::string> otherFiles;
projectType = CollectSourceFiles(mf, gt, cFiles, otherFiles);
// Get the project path ( we need it later to convert files to
// their relative path)
std::string projectPath = cmSystemTools::GetFilenamePath(filename);
CreateProjectSourceEntries(cFiles, otherFiles, &xml, projectPath, mf,
projectType);
xml.EndElement(); // CodeLite_Project xml.EndElement(); // CodeLite_Project
} }

View File

@ -18,11 +18,16 @@
#include "cmExternalMakefileProjectGenerator.h" #include "cmExternalMakefileProjectGenerator.h"
#include <map>
#include <set>
#include <string> #include <string>
#include <vector> #include <vector>
class cmLocalGenerator; class cmLocalGenerator;
class cmMakefile; class cmMakefile;
class cmGeneratorTarget;
class cmXMLWriter;
class cmSourceFile;
class cmExtraCodeLiteGenerator : public cmExternalMakefileProjectGenerator class cmExtraCodeLiteGenerator : public cmExternalMakefileProjectGenerator
{ {
@ -38,6 +43,20 @@ protected:
std::string GetCleanCommand(const cmMakefile* mf) const; std::string GetCleanCommand(const cmMakefile* mf) const;
std::string GetRebuildCommand(const cmMakefile* mf) const; std::string GetRebuildCommand(const cmMakefile* mf) const;
std::string GetSingleFileBuildCommand(const cmMakefile* mf) const; std::string GetSingleFileBuildCommand(const cmMakefile* mf) const;
std::vector<std::string> CreateProjectsByTarget(cmXMLWriter* xml);
std::vector<std::string> CreateProjectsByProjectMaps(cmXMLWriter* xml);
std::string CollectSourceFiles(const cmMakefile* makefile,
const cmGeneratorTarget* gt,
std::map<std::string, cmSourceFile*>& cFiles,
std::set<std::string>& otherFiles);
void FindMatchingHeaderfiles(std::map<std::string, cmSourceFile*>& cFiles,
std::set<std::string>& otherFiles);
void CreateProjectSourceEntries(std::map<std::string, cmSourceFile*>& cFiles,
std::set<std::string>& otherFiles,
cmXMLWriter* xml,
const std::string& projectPath,
const cmMakefile* mf,
const std::string& projectType);
public: public:
cmExtraCodeLiteGenerator(); cmExtraCodeLiteGenerator();
@ -49,6 +68,8 @@ public:
void CreateNewProjectFile(const std::vector<cmLocalGenerator*>& lgs, void CreateNewProjectFile(const std::vector<cmLocalGenerator*>& lgs,
const std::string& filename); const std::string& filename);
void CreateNewProjectFile(const cmGeneratorTarget* lg,
const std::string& filename);
}; };
#endif #endif