diff --git a/Help/generator/Qbs.rst b/Help/generator/Qbs.rst new file mode 100644 index 000000000..e569b7730 --- /dev/null +++ b/Help/generator/Qbs.rst @@ -0,0 +1,25 @@ +Qbs +--- + +Generates Qbs project files. + +Project files for Qbs will be created in the top directory and +in every subdirectory which features a CMakeLists.txt file containing +a PROJECT() call. Additionally a hierarchy of makefiles is generated +into the build tree. The appropriate make program can build the +project through the default make target. A "make install" target is +also provided. + +This "extra" generator may be specified as: + +``Qbs - MinGW Makefiles`` + Generate with :generator:`MinGW Makefiles`. + +``Qbs - NMake Makefiles`` + Generate with :generator:`NMake Makefiles`. + +``Qbs - Ninja`` + Generate with :generator:`Ninja`. + +``Qbs - Unix Makefiles`` + Generate with :generator:`Unix Makefiles`. diff --git a/Help/manual/cmake-generators.7.rst b/Help/manual/cmake-generators.7.rst index bda7eefe3..804229b22 100644 --- a/Help/manual/cmake-generators.7.rst +++ b/Help/manual/cmake-generators.7.rst @@ -85,3 +85,4 @@ The following extra generators are known to CMake. /generator/KDevelop3 /generator/Kate /generator/Sublime Text 2 + /generator/Qbs diff --git a/Help/release/dev/add-extra-qbs-generator.rst b/Help/release/dev/add-extra-qbs-generator.rst new file mode 100644 index 000000000..edb441f34 --- /dev/null +++ b/Help/release/dev/add-extra-qbs-generator.rst @@ -0,0 +1,6 @@ +add-extra-qbs-geneator +---------------------- + +* It is now possible to generate :generator:`Qbs` project files + for use with QtCreator IDE, matching make tool must be used + to build the project through the generated makefiles. diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 482bd39d6..04f6a8136 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -229,6 +229,8 @@ set(SRCS cmExtraKateGenerator.h cmExtraSublimeTextGenerator.cxx cmExtraSublimeTextGenerator.h + cmExtraQbsGenerator.cxx + cmExtraQbsGenerator.h cmFileLock.cxx cmFileLock.h cmFileLockPool.cxx diff --git a/Source/cmExtraQbsGenerator.cxx b/Source/cmExtraQbsGenerator.cxx new file mode 100644 index 000000000..5a1f9ef57 --- /dev/null +++ b/Source/cmExtraQbsGenerator.cxx @@ -0,0 +1,260 @@ +#include "cmExtraQbsGenerator.h" + +#include "cmGlobalGenerator.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmGeneratedFileStream.h" +#include "cmSourceFile.h" + +cmExtraQbsGenerator::cmExtraQbsGenerator() +{ +#if defined(_WIN32) + this->SupportedGlobalGenerators.push_back("MinGW Makefiles"); + this->SupportedGlobalGenerators.push_back("NMake Makefiles"); +#endif + this->SupportedGlobalGenerators.push_back("Ninja"); + this->SupportedGlobalGenerators.push_back("Unix Makefiles"); +} + +cmExtraQbsGenerator::~cmExtraQbsGenerator() {} + +void cmExtraQbsGenerator::GetDocumentation(cmDocumentationEntry &entry, + const std::string &) const +{ + entry.Name = this->GetName(); + entry.Brief = "Generates Qbs project files."; +} + +void cmExtraQbsGenerator::Generate() +{ + for (std::map >::const_iterator + it = this->GlobalGenerator->GetProjectMap().begin(); + it != this->GlobalGenerator->GetProjectMap().end(); ++it) + { + // create a project file + this->CreateProjectFile(it->first, it->second); + } +} + +void cmExtraQbsGenerator::CreateProjectFile( + const std::string &name, + const std::vector &lgs) +{ + const cmMakefile *mf = lgs[0]->GetMakefile(); + std::string outputDir = mf->GetStartOutputDirectory(); + + const std::string filename = outputDir + "/" + name + ".qbs"; + + this->CreateNewProjectFile(name, lgs, filename); +} + +void cmExtraQbsGenerator::CreateNewProjectFile( + const std::string &projectName, const std::vector &lgs, + const std::string &filename) +{ + cmGeneratedFileStream fout(filename.c_str()); + if (!fout) + { + return; + } + + fout << "import qbs\n" + << "import qbs.File\n\n" + << "Project {\n" + << "\tname:\"" << projectName << "\"\n"; + std::vector::const_iterator itr = lgs.begin(); + for (; itr != lgs.end(); ++itr) + { + cmLocalGenerator *lg = (*itr); + this->AppendSubProject(fout, lg); + } + fout << "}\n"; +} + +void cmExtraQbsGenerator::AppendSubProject(cmGeneratedFileStream &fout, + cmLocalGenerator *lg) +{ + const cmMakefile *mk = lg->GetMakefile(); + if (!mk || mk->GetTargets().size() == 0) + { + return; + } + + const std::string &relativePath = cmSystemTools::RelativePath( + mk->GetHomeDirectory(), mk->GetCurrentDirectory()); + fout << "\tProject {\n" + << "\t\tname:\"" << relativePath << "\"\n"; + this->AppendProduct(fout, lg); + fout << "\t}\n"; +} + +void cmExtraQbsGenerator::AppendProduct(cmGeneratedFileStream &fout, + cmLocalGenerator *lg) +{ + const cmMakefile *mk = lg->GetMakefile(); + const cmTargets &ts = mk->GetTargets(); + std::string cfg = mk->GetSafeDefinition("CMAKE_BUILD_TYPE"); + cmTargets::const_iterator itr = ts.begin(); + for (; itr != ts.end(); ++itr) + { + const cmTarget &t = itr->second; + this->AppendTarget(fout, lg, t, cfg); + } +} + +void cmExtraQbsGenerator::AppendTarget(cmGeneratedFileStream &fout, + cmLocalGenerator *lg, const cmTarget &t, + const std::string &cfg) +{ + std::string type; + bool isBuildable = true; + switch (t.GetType()) + { + case cmTarget::EXECUTABLE: + type = "application"; + break; + case cmTarget::SHARED_LIBRARY: + type = "dynamiclibrary"; + break; + case cmTarget::STATIC_LIBRARY: + type = "staticlibrary"; + break; + default: + isBuildable = false; + break; + } + + if (type.empty()) + { + fout << "\t\tProject {\n"; + } + else + { + fout << "\t\tProduct {\n"; + fout << "\t\t\tdestinationDirectory: \"" << t.GetDirectory(cfg) << "\"\n"; + } + fout << "\t\t\tname:\"" << t.GetName() << "\"\n"; + + if (!type.empty()) + { + fout << "\t\t\ttype: \"" << type << "\"\n"; + fout << "\t\t\ttargetName: \"" << t.GetName() << "\"\n"; + } + + if (isBuildable) + { + fout << "\t\t\tDepends { name: \"cpp\" }\n"; + cmGeneratorTarget *gt = this->GlobalGenerator->GetGeneratorTarget(&t); + this->AppendSources(fout, gt, t, cfg); + + std::set langs, incPaths, defs; + t.GetLanguages(langs, cfg); + for (std::set::const_iterator lang = langs.begin(); + lang != langs.end(); + ++ lang) + { + const std::vector &paths = + gt->GetIncludeDirectories(cfg, *lang); + std::copy(paths.begin(), paths.end(), + std::inserter(incPaths, incPaths.end())); + + lg->AddCompileDefinitions(defs, &t, cfg, *lang); + } + this->AppendIncludePaths(fout, incPaths); + this->AppendCompileDefinitions(fout, defs); + } + + fout << "\t\t}\n"; +} + +void cmExtraQbsGenerator::AppendSources(cmGeneratedFileStream &fout, + cmGeneratorTarget *gt, + const cmTarget &t, + const std::string &cfg) +{ + std::vector sources; + gt->GetSourceFiles(sources, cfg); + if (sources.empty()) + { + return; + } + + std::vector genSources; + std::vector::const_iterator itr = sources.begin(); + fout << "\t\t\tfiles: [\n" + << "\t\t\t\t\"" << t.GetMakefile()->GetCurrentListFile() << "\",\n"; + for (; itr != sources.end(); ++itr) + { + if (!(*itr)->GetPropertyAsBool("GENERATED")) + { + fout << "\t\t\t\t\"" << (*itr)->GetFullPath() << "\",\n"; + } + else + { + genSources.push_back(*itr); + } + } + fout << "\t\t\t]\n"; + + if (!genSources.empty()) + { + fout << "\t\t\tGroup {\n" + << "\t\t\t\tname:\"Generated\"\n" + << "\t\t\t\tfiles: [\n"; + itr = genSources.begin(); + std::string groupCondition; + bool initialCondition = true; + for (; itr != genSources.end(); ++itr) + { + const std::string &path = (*itr)->GetFullPath(); + fout << "\t\t\t\t\t\"" << path << "\",\n"; + if (initialCondition) + { + initialCondition = false; + } + else + { + groupCondition += "\t\t\t\t\t && "; + } + groupCondition += "File.exists(\"" + path + "\")\n"; + } + fout << "\t\t\t\t]\n" + << "\t\t\t\tcondition: " << groupCondition << "\t\t\t}\n"; + } +} + +void cmExtraQbsGenerator::AppendIncludePaths( + cmGeneratedFileStream &fout, + const std::set &paths) +{ + if (paths.empty()) + { + return; + } + + std::set::const_iterator itr = paths.begin(); + fout << "\t\t\tcpp.includePaths: [\n"; + for (; itr != paths.end(); ++ itr) + { + fout << "\t\t\t\t\"" << (*itr) << "\",\n"; + } + fout << "\t\t\t]\n"; +} + +void cmExtraQbsGenerator::AppendCompileDefinitions( + cmGeneratedFileStream &fout, + const std::set &defs) +{ + if (defs.empty()) + { + return; + } + + std::set::const_iterator itr = defs.begin(); + fout << "\t\t\tcpp.defines: [\n"; + for (; itr != defs.end(); ++ itr) + { + fout << "\t\t\t\t'" << (*itr) << "',\n"; + } + fout << "\t\t\t]\n"; +} diff --git a/Source/cmExtraQbsGenerator.h b/Source/cmExtraQbsGenerator.h new file mode 100644 index 000000000..531ccc925 --- /dev/null +++ b/Source/cmExtraQbsGenerator.h @@ -0,0 +1,48 @@ +#ifndef CMEXTRAQBSGENERATOR_H +#define CMEXTRAQBSGENERATOR_H + +#include "cmExternalMakefileProjectGenerator.h" + +class cmGeneratorTarget; + +class cmExtraQbsGenerator : public cmExternalMakefileProjectGenerator +{ +public: + cmExtraQbsGenerator(); + ~cmExtraQbsGenerator(); + + virtual std::string GetName() const + { return cmExtraQbsGenerator::GetActualName(); } + static std::string GetActualName() { return "Qbs"; } + static cmExternalMakefileProjectGenerator *New() + { return new cmExtraQbsGenerator; } + + /** Get the documentation entry for this generator. */ + virtual void GetDocumentation(cmDocumentationEntry &entry, + const std::string &fullName) const; + + virtual void Generate(); + +private: + void CreateProjectFile(const std::string &name, + const std::vector &lgs); + void CreateNewProjectFile(const std::string &projectName, + const std::vector &lgs, + const std::string &filename); + void AppendSubProject(cmGeneratedFileStream &fout, cmLocalGenerator *lg); + void AppendProduct(cmGeneratedFileStream &fout, cmLocalGenerator *lg); + void AppendTarget(cmGeneratedFileStream &fout, + cmLocalGenerator *lg, + const cmTarget &t, + const std::string &cfg); + void AppendSources(cmGeneratedFileStream &fout, + cmGeneratorTarget *gt, + const cmTarget &t, + const std::string &cfg); + void AppendIncludePaths(cmGeneratedFileStream &fout, + const std::set &paths); + void AppendCompileDefinitions(cmGeneratedFileStream &fout, + const std::set &defs); +}; + +#endif // CMEXTRAQBSGENERATOR_H diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 11196e422..51df7f2f5 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -86,6 +86,8 @@ # include "cmGlobalKdevelopGenerator.h" #endif +#include "cmExtraQbsGenerator.h" + #ifdef CMAKE_USE_ECLIPSE # include "cmExtraEclipseCDT4Generator.h" #endif @@ -1029,6 +1031,8 @@ void cmake::AddDefaultExtraGenerators() &cmExtraSublimeTextGenerator::New); this->AddExtraGenerator(cmExtraKateGenerator::GetActualName(), &cmExtraKateGenerator::New); + this->AddExtraGenerator(cmExtraQbsGenerator::GetActualName(), + &cmExtraQbsGenerator::New); #ifdef CMAKE_USE_ECLIPSE this->AddExtraGenerator(cmExtraEclipseCDT4Generator::GetActualName(), diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 40bea5117..5fa7044ce 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -560,7 +560,21 @@ if(BUILD_TESTING) --test-command Simple) list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Simple_KDevelop3Generator") endif () - + # check for the Qbs generator + if ("${cmakeOutput}" MATCHES Qbs) + add_test(Simple_QbsGenerator ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMake_SOURCE_DIR}/Tests/Simple" + "${CMake_BINARY_DIR}/Tests/Simple_QbsGenerator" + --build-two-config + --build-generator "Qbs - Unix Makefiles" + --build-generator-platform "${CMAKE_GENERATOR_PLATFORM}" + --build-generator-toolset "${CMAKE_GENERATOR_TOOLSET}" + --build-project Simple + --build-options ${build_options} + --test-command Simple) + list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/Simple_QbsGenerator") + endif () endif() # test for correct sub-project generation