From cbe488792746377396a9f38aeb2999750ade518a Mon Sep 17 00:00:00 2001 From: Minze Zwerver Date: Fri, 23 Sep 2016 08:42:20 +0200 Subject: [PATCH] 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. --- Help/generator/CodeLite.rst | 6 +- Help/manual/cmake-variables.7.rst | 1 + .../dev/codelite-organize-by-target.rst | 6 + Help/variable/CMAKE_CODELITE_USE_TARGETS.rst | 7 + Source/cmExtraCodeLiteGenerator.cxx | 342 +++++++++++++----- Source/cmExtraCodeLiteGenerator.h | 21 ++ 6 files changed, 284 insertions(+), 99 deletions(-) create mode 100644 Help/release/dev/codelite-organize-by-target.rst create mode 100644 Help/variable/CMAKE_CODELITE_USE_TARGETS.rst diff --git a/Help/generator/CodeLite.rst b/Help/generator/CodeLite.rst index dbc46d72b..3e60aa6d6 100644 --- a/Help/generator/CodeLite.rst +++ b/Help/generator/CodeLite.rst @@ -5,7 +5,11 @@ Generates CodeLite project files. Project files for CodeLite will be created in the top directory and 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 also provided. diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 9e0efe931..2d2a0b627 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -112,6 +112,7 @@ Variables that Change Behavior /variable/CMAKE_AUTOMOC_RELAXED_MODE /variable/CMAKE_BACKWARDS_COMPATIBILITY /variable/CMAKE_BUILD_TYPE + /variable/CMAKE_CODELITE_USE_TARGETS /variable/CMAKE_COLOR_MAKEFILE /variable/CMAKE_CONFIGURATION_TYPES /variable/CMAKE_DEBUG_TARGET_PROPERTIES diff --git a/Help/release/dev/codelite-organize-by-target.rst b/Help/release/dev/codelite-organize-by-target.rst new file mode 100644 index 000000000..097922835 --- /dev/null +++ b/Help/release/dev/codelite-organize-by-target.rst @@ -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. diff --git a/Help/variable/CMAKE_CODELITE_USE_TARGETS.rst b/Help/variable/CMAKE_CODELITE_USE_TARGETS.rst new file mode 100644 index 000000000..4aede0361 --- /dev/null +++ b/Help/variable/CMAKE_CODELITE_USE_TARGETS.rst @@ -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. diff --git a/Source/cmExtraCodeLiteGenerator.cxx b/Source/cmExtraCodeLiteGenerator.cxx index a039f49e3..aaaa07ad1 100644 --- a/Source/cmExtraCodeLiteGenerator.cxx +++ b/Source/cmExtraCodeLiteGenerator.cxx @@ -70,6 +70,8 @@ void cmExtraCodeLiteGenerator::Generate() // loop projects and locate the root project. // and extract the information for creating the worspace + // root makefile + const cmMakefile* rmf = CM_NULLPTR; for (std::map >::const_iterator it = projectMap.begin(); it != projectMap.end(); ++it) { @@ -84,6 +86,7 @@ void cmExtraCodeLiteGenerator::Generate() workspaceFileName = workspaceOutputDir + "/"; workspaceFileName += workspaceProjectName + ".workspace"; this->WorkspacePath = it->second[0]->GetCurrentBinaryDirectory(); + rmf = it->second[0]->GetMakefile(); ; break; } @@ -96,13 +99,90 @@ void cmExtraCodeLiteGenerator::Generate() xml.StartElement("CodeLite_Workspace"); xml.Attribute("Name", workspaceProjectName); + bool const targetsAreProjects = + rmf && rmf->IsOn("CMAKE_CODELITE_USE_TARGETS"); + + std::vector 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::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 cmExtraCodeLiteGenerator::CreateProjectsByTarget( + cmXMLWriter* xml) +{ + std::vector retval; + // for each target in the workspace create a codelite project + const std::vector& lgs = + this->GlobalGenerator->GetLocalGenerators(); + for (std::vector::const_iterator lg(lgs.begin()); + lg != lgs.end(); lg++) { + for (std::vector::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 cmExtraCodeLiteGenerator::CreateProjectsByProjectMaps( + cmXMLWriter* xml) +{ + std::vector retval; // for each sub project in the workspace create a codelite project for (std::map >::const_iterator - it = projectMap.begin(); - it != projectMap.end(); ++it) { - // retrive project information + it = this->GlobalGenerator->GetProjectMap().begin(); + it != this->GlobalGenerator->GetProjectMap().end(); it++) { + std::string outputDir = it->second[0]->GetCurrentBinaryDirectory(); std::string projectName = it->second[0]->GetProjectName(); + retval.push_back(projectName); std::string filename = outputDir + "/" + projectName + ".project"; // Make the project file relative to the workspace @@ -111,33 +191,13 @@ void cmExtraCodeLiteGenerator::Generate() // create a project file this->CreateProjectFile(it->second); - xml.StartElement("Project"); - xml.Attribute("Name", projectName); - xml.Attribute("Path", filename); - xml.Attribute("Active", "No"); - xml.EndElement(); + xml->StartElement("Project"); + xml->Attribute("Name", projectName); + xml->Attribute("Path", filename); + xml->Attribute("Active", "No"); + xml->EndElement(); } - - xml.StartElement("BuildMatrix"); - xml.StartElement("WorkspaceConfiguration"); - xml.Attribute("Name", this->ConfigName); - xml.Attribute("Selected", "yes"); - - for (std::map >::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 + return retval; } /* create the project file */ @@ -152,6 +212,70 @@ void cmExtraCodeLiteGenerator::CreateProjectFile( this->CreateNewProjectFile(lgs, filename); } +std::string cmExtraCodeLiteGenerator::CollectSourceFiles( + const cmMakefile* makefile, const cmGeneratorTarget* gt, + std::map& cFiles, + std::set& otherFiles) +{ + const std::vector& 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 sources; + gt->GetSourceFiles(sources, + makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); + for (std::vector::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::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( const std::vector& lgs, const std::string& filename) { @@ -168,82 +292,42 @@ void cmExtraCodeLiteGenerator::CreateNewProjectFile( xml.Attribute("Name", lgs[0]->GetProjectName()); xml.Attribute("InternalType", ""); + std::string projectType; + // 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 srcExts = - this->GlobalGenerator->GetCMakeInstance()->GetSourceExtensions(); - std::vector headerExts = - this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions(); - std::map cFiles; std::set otherFiles; + for (std::vector::const_iterator lg = lgs.begin(); lg != lgs.end(); lg++) { cmMakefile* makefile = (*lg)->GetMakefile(); std::vector targets = (*lg)->GetGeneratorTargets(); for (std::vector::iterator ti = targets.begin(); ti != targets.end(); ti++) { - - 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 sources; - cmGeneratorTarget* gt = *ti; - gt->GetSourceFiles(sources, - makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); - for (std::vector::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::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; - } + projectType = CollectSourceFiles(makefile, *ti, 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 +} + +void cmExtraCodeLiteGenerator::FindMatchingHeaderfiles( + std::map& cFiles, + std::set& otherFiles) +{ + + const std::vector& headerExts = + this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions(); + // The following loop tries to add header files matching to implementation // 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 @@ -275,11 +359,17 @@ void cmExtraCodeLiteGenerator::CreateNewProjectFile( } } } +} - // Get the project path ( we need it later to convert files to - // their relative path) - std::string projectPath = cmSystemTools::GetFilenamePath(filename); +void cmExtraCodeLiteGenerator::CreateProjectSourceEntries( + std::map& cFiles, + std::set& 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 // and place all the implementation files into the src // folder, the rest goes to the include folder @@ -292,8 +382,10 @@ void cmExtraCodeLiteGenerator::CreateNewProjectFile( cFiles.begin(); sit != cFiles.end(); ++sit) { xml.StartElement("File"); - xml.Attribute("Name", cmSystemTools::RelativePath(projectPath.c_str(), - sit->first.c_str())); + std::string fpath(sit->first); + std::string frelapath = + cmSystemTools::RelativePath(projectPath.c_str(), sit->first.c_str()); + xml.Attribute("Name", frelapath); xml.EndElement(); } xml.EndElement(); // VirtualDirectory @@ -350,11 +442,18 @@ void cmExtraCodeLiteGenerator::CreateNewProjectFile( xml.EndElement(); // ResourceCompiler 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("Command", "./$(ProjectName)"); xml.Attribute("CommandArguments", ""); - xml.Attribute("WorkingDirectory", "$(IntermediateDirectory)"); + if (!outputPath.empty()) + xml.Attribute("WorkingDirectory", outputPath); + else + xml.Attribute("WorkingDirectory", "$(IntermediateDirectory)"); xml.Attribute("PauseExecWhenProcTerminates", "yes"); xml.EndElement(); // General @@ -408,6 +507,53 @@ void cmExtraCodeLiteGenerator::CreateNewProjectFile( xml.EndElement(); // GlobalSettings 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 headerExts = + this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions(); + + std::map cFiles; + std::set 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 } diff --git a/Source/cmExtraCodeLiteGenerator.h b/Source/cmExtraCodeLiteGenerator.h index f5765d85d..9c571a5f1 100644 --- a/Source/cmExtraCodeLiteGenerator.h +++ b/Source/cmExtraCodeLiteGenerator.h @@ -18,11 +18,16 @@ #include "cmExternalMakefileProjectGenerator.h" +#include +#include #include #include class cmLocalGenerator; class cmMakefile; +class cmGeneratorTarget; +class cmXMLWriter; +class cmSourceFile; class cmExtraCodeLiteGenerator : public cmExternalMakefileProjectGenerator { @@ -38,6 +43,20 @@ protected: std::string GetCleanCommand(const cmMakefile* mf) const; std::string GetRebuildCommand(const cmMakefile* mf) const; std::string GetSingleFileBuildCommand(const cmMakefile* mf) const; + std::vector CreateProjectsByTarget(cmXMLWriter* xml); + std::vector CreateProjectsByProjectMaps(cmXMLWriter* xml); + std::string CollectSourceFiles(const cmMakefile* makefile, + const cmGeneratorTarget* gt, + std::map& cFiles, + std::set& otherFiles); + void FindMatchingHeaderfiles(std::map& cFiles, + std::set& otherFiles); + void CreateProjectSourceEntries(std::map& cFiles, + std::set& otherFiles, + cmXMLWriter* xml, + const std::string& projectPath, + const cmMakefile* mf, + const std::string& projectType); public: cmExtraCodeLiteGenerator(); @@ -49,6 +68,8 @@ public: void CreateNewProjectFile(const std::vector& lgs, const std::string& filename); + void CreateNewProjectFile(const cmGeneratorTarget* lg, + const std::string& filename); }; #endif