/*========================================================================= Program: CMake - Cross-Platform Makefile Generator3 Module: $RCSfile$ Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "cmGlobalUnixMakefileGenerator3.h" #include "cmLocalUnixMakefileGenerator3.h" #include "cmMakefile.h" #include "cmake.h" #include "cmGeneratedFileStream.h" cmGlobalUnixMakefileGenerator3::cmGlobalUnixMakefileGenerator3() { // This type of makefile always requires unix style paths m_ForceUnixPaths = true; m_FindMakeProgramFile = "CMakeUnixFindMake.cmake"; } void cmGlobalUnixMakefileGenerator3 ::EnableLanguage(std::vectorconst& languages, cmMakefile *mf) { mf->AddDefinition("CMAKE_CFG_INTDIR","."); this->cmGlobalGenerator::EnableLanguage(languages, mf); std::string path; for(std::vector::const_iterator l = languages.begin(); l != languages.end(); ++l) { const char* lang = l->c_str(); std::string langComp = "CMAKE_"; langComp += lang; langComp += "_COMPILER"; if(!mf->GetDefinition(langComp.c_str())) { cmSystemTools::Error(langComp.c_str(), " not set, after EnableLanguage"); continue; } const char* cc = mf->GetRequiredDefinition(langComp.c_str()); path = cmSystemTools::FindProgram(cc); if(path.size() == 0) { std::string message = "your "; message += lang; message += " compiler: "; if(cc) { message += cc; } else { message += "(NULL)"; } message += " was not found in your path. " "For CMake to correctly use try compile commands, the compiler must " "be in your path. Please add the compiler to your PATH environment," " and re-run CMake."; cmSystemTools::Error(message.c_str()); } } } ///! Create a local generator appropriate to this Global Generator cmLocalGenerator *cmGlobalUnixMakefileGenerator3::CreateLocalGenerator() { cmLocalGenerator* lg = new cmLocalUnixMakefileGenerator3; lg->SetGlobalGenerator(this); return lg; } //---------------------------------------------------------------------------- void cmGlobalUnixMakefileGenerator3::GetDocumentation(cmDocumentationEntry& entry) const { entry.name = this->GetName(); entry.brief = "Generates standard UNIX makefiles."; entry.full = "A hierarchy of UNIX makefiles is generated into the build tree. Any " "standard UNIX-style make program can build the project through the " "default make target. A \"make install\" target is also provided."; } //---------------------------------------------------------------------------- void cmGlobalUnixMakefileGenerator3::Generate() { // first do superclass method this->cmGlobalGenerator::Generate(); // write the main makefile this->WriteMainMakefile(); this->WriteMainCMakefile(); // now write the support Makefiles this->WriteDependMakefile(); this->WriteBuildMakefile(); this->WriteCleanMakefile(); } void cmGlobalUnixMakefileGenerator3::WriteMainMakefile() { // Open the output file. This should not be copy-if-different // because the check-build-system step compares the makefile time to // see if the build system must be regenerated. std::string makefileName = this->GetCMakeInstance()->GetHomeOutputDirectory(); makefileName += "/Makefile"; cmGeneratedFileStream makefileStream(makefileName.c_str()); if(!makefileStream) { return; } // get a local generator for some useful methods cmLocalUnixMakefileGenerator3 *lg = static_cast(m_LocalGenerators[0]); // Write the do not edit header. lg->WriteDisclaimer(makefileStream); // Write the main entry point target. This must be the VERY first // target so that make with no arguments will run it. // Just depend on the all target to drive the build. std::vector depends; std::vector no_commands; depends.push_back("all"); // Write the rule. lg->WriteMakeRule(makefileStream, "Default target executed when no arguments are " "given to make.", "default_target", depends, no_commands); lg->WriteMakeVariables(makefileStream); lg->WriteSpecialTargetsTop(makefileStream); this->WriteAllRules(lg,makefileStream); // write the target convenience rules unsigned int i; for (i = 0; i < m_LocalGenerators.size(); ++i) { lg = static_cast(m_LocalGenerators[i]); this->WriteConvenienceRules(makefileStream,lg); } lg->WriteSpecialTargetsBottom(makefileStream); } //---------------------------------------------------------------------------- void cmGlobalUnixMakefileGenerator3::WriteMainCMakefile() { // Open the output file. This should not be copy-if-different // because the check-build-system step compares the makefile time to // see if the build system must be regenerated. std::string cmakefileName = this->GetCMakeInstance()->GetHomeOutputDirectory(); cmakefileName += "/Makefile.cmake"; cmGeneratedFileStream cmakefileStream(cmakefileName.c_str()); if(!cmakefileStream) { return; } std::string makefileName = this->GetCMakeInstance()->GetHomeOutputDirectory(); makefileName += "/Makefile"; // get a local generator for some useful methods cmLocalUnixMakefileGenerator3 *lg = static_cast(m_LocalGenerators[0]); // Write the do not edit header. lg->WriteDisclaimer(cmakefileStream); // for each cmMakefile get its list of dependencies unsigned int i; std::vector lfiles; for (i = 0; i < m_LocalGenerators.size(); ++i) { lg = static_cast(m_LocalGenerators[i]); // Get the list of files contributing to this generation step. lfiles.insert(lfiles.end(),lg->GetMakefile()->GetListFiles().begin(), lg->GetMakefile()->GetListFiles().end()); } // Sort the list and remove duplicates. std::sort(lfiles.begin(), lfiles.end(), std::less()); std::vector::iterator new_end = std::unique(lfiles.begin(),lfiles.end()); lfiles.erase(new_end, lfiles.end()); // reset lg to the first makefile lg = static_cast(m_LocalGenerators[0]); // Build the path to the cache file. std::string cache = this->GetCMakeInstance()->GetHomeOutputDirectory(); cache += "/CMakeCache.txt"; // Save the list to the cmake file. cmakefileStream << "# The top level Makefile was generated from the following files:\n" << "SET(CMAKE_MAKEFILE_DEPENDS\n" << " \"" << lg->ConvertToRelativePath(cache.c_str()).c_str() << "\"\n"; for(std::vector::const_iterator i = lfiles.begin(); i != lfiles.end(); ++i) { cmakefileStream << " \"" << lg->ConvertToRelativePath(i->c_str()).c_str() << "\"\n"; } cmakefileStream << " )\n\n"; // Build the path to the cache check file. std::string check = this->GetCMakeInstance()->GetHomeOutputDirectory(); check += "/cmake.check_cache"; // Set the corresponding makefile in the cmake file. cmakefileStream << "# The corresponding makefile is:\n" << "SET(CMAKE_MAKEFILE_OUTPUTS\n" << " \"" << lg->ConvertToRelativePath(makefileName.c_str()).c_str() << "\"\n" << " \"" << lg->ConvertToRelativePath(check.c_str()).c_str() << "\"\n"; // add in all the directory information files std::string tmpStr; for (i = 0; i < m_LocalGenerators.size(); ++i) { lg = static_cast(m_LocalGenerators[i]); tmpStr = lg->GetMakefile()->GetStartOutputDirectory(); tmpStr += "/CMakeDirectoryInformation.cmake"; cmakefileStream << " \"" << this->ConvertToHomeRelativePath(tmpStr.c_str()).c_str() << "\"\n"; } cmakefileStream << " )\n\n"; this->WriteMainCMakefileLanguageRules(cmakefileStream); } void cmGlobalUnixMakefileGenerator3 ::WriteMainCMakefileLanguageRules(cmGeneratedFileStream& cmakefileStream) { cmLocalUnixMakefileGenerator3 *lg; // now write all the language stuff // Set the set of files to check for dependency integrity. // loop over all of the local generators to collect this std::set checkSetLangs; for (unsigned int i = 0; i < m_LocalGenerators.size(); ++i) { lg = static_cast(m_LocalGenerators[i]); std::map& checkSet = lg->GetIntegrityCheckSet(); for(std::map::const_iterator l = checkSet.begin(); l != checkSet.end(); ++l) { checkSetLangs.insert(l->first); } } // list the languages cmakefileStream << "# The set of files whose dependency integrity should be checked:\n"; cmakefileStream << "SET(CMAKE_DEPENDS_LANGUAGES\n"; for(std::set::iterator l = checkSetLangs.begin(); l != checkSetLangs.end(); ++l) { cmakefileStream << " \"" << l->c_str() << "\"\n"; } cmakefileStream << " )\n"; // now list the files for each language for(std::set::iterator l = checkSetLangs.begin(); l != checkSetLangs.end(); ++l) { cmakefileStream << "SET(CMAKE_DEPENDS_CHECK_" << l->c_str() << "\n"; // now for each local gen get the checkset for (unsigned int i = 0; i < m_LocalGenerators.size(); ++i) { lg = static_cast(m_LocalGenerators[i]); // get the check set for this local gen and language cmLocalUnixMakefileGenerator3::IntegrityCheckSet iCheckSet = lg->GetIntegrityCheckSet()[*l]; // for each file for(cmLocalUnixMakefileGenerator3::IntegrityCheckSet::const_iterator csIter = iCheckSet.begin(); csIter != iCheckSet.end(); ++csIter) { cmakefileStream << " \"" << this->ConvertToHomeRelativePath(csIter->c_str()).c_str() << "\"\n"; } } cmakefileStream << " )\n"; } } void cmGlobalUnixMakefileGenerator3::WriteDependMakefile() { unsigned int i; // Open the output file. This should not be copy-if-different // because the check-build-system step compares the makefile time to // see if the build system must be regenerated. std::string makefileName = this->GetCMakeInstance()->GetHomeOutputDirectory(); makefileName += "/depend.make"; cmGeneratedFileStream makefileStream(makefileName.c_str()); if(!makefileStream) { return; } // get a local generator for some useful methods cmLocalUnixMakefileGenerator3 *lg = static_cast(m_LocalGenerators[0]); // Write the do not edit header. lg->WriteDisclaimer(makefileStream); lg->WriteMakeVariables(makefileStream); // add the generic dependency std::vector depends; std::vector no_commands; lg->WriteMakeRule(makefileStream, 0, "depend", depends, no_commands); // include the build rules makefileStream << "# Include make rules for build targets\n"; makefileStream << lg->GetIncludeDirective() << " " << lg->ConvertToOutputForExisting("build.make").c_str() << "\n\n"; // include all the target depends for (i = 0; i < m_LocalGenerators.size(); ++i) { cmLocalUnixMakefileGenerator3 *lg2 = static_cast(m_LocalGenerators[i]); if (!lg2->GetExcludeAll()) { lg2->WriteTargetIncludes(makefileStream,"depend.make","depend"); } } } void cmGlobalUnixMakefileGenerator3::WriteBuildMakefile() { unsigned int i; // Open the output file. This should not be copy-if-different // because the check-build-system step compares the makefile time to // see if the build system must be regenerated. std::string makefileName = this->GetCMakeInstance()->GetHomeOutputDirectory(); makefileName += "/build.make"; cmGeneratedFileStream makefileStream(makefileName.c_str()); if(!makefileStream) { return; } // get a local generator for some useful methods cmLocalUnixMakefileGenerator3 *lg = static_cast(m_LocalGenerators[0]); // Write the do not edit header. lg->WriteDisclaimer(makefileStream); lg->WriteMakeVariables(makefileStream); // add the generic dependency std::vector depends; std::vector no_commands; lg->WriteMakeRule(makefileStream, 0, "build", depends, no_commands); // include all the target depends for (i = 0; i < m_LocalGenerators.size(); ++i) { cmLocalUnixMakefileGenerator3 *lg2 = static_cast(m_LocalGenerators[i]); // are any parents excluded bool exclude = false; cmLocalGenerator *lg3 = lg2; while (lg3) { if (lg3->GetExcludeAll()) { exclude = true; break; } lg3 = lg3->GetParent(); } if (!exclude) { lg2->WriteTargetIncludes(makefileStream,"build.make","build"); } } } void cmGlobalUnixMakefileGenerator3::WriteCleanMakefile() { unsigned int i; // Open the output file. This should not be copy-if-different // because the check-build-system step compares the makefile time to // see if the build system must be regenerated. std::string makefileName = this->GetCMakeInstance()->GetHomeOutputDirectory(); makefileName += "/clean.make"; cmGeneratedFileStream makefileStream(makefileName.c_str()); if(!makefileStream) { return; } // get a local generator for some useful methods cmLocalUnixMakefileGenerator3 *lg = static_cast(m_LocalGenerators[0]); // Write the do not edit header. lg->WriteDisclaimer(makefileStream); lg->WriteMakeVariables(makefileStream); // add the generic dependency std::vector depends; std::vector no_commands; lg->WriteMakeRule(makefileStream, 0, "clean", depends, no_commands); // include all the target depends for (i = 0; i < m_LocalGenerators.size(); ++i) { cmLocalUnixMakefileGenerator3 *lg2 = static_cast(m_LocalGenerators[i]); lg2->WriteTargetIncludes(makefileStream,"clean.make","clean"); // add the directory based rules lg2->WriteLocalCleanRule(makefileStream); } } //---------------------------------------------------------------------------- void cmGlobalUnixMakefileGenerator3 ::WriteAllRules(cmLocalUnixMakefileGenerator3 *lg, std::ostream& makefileStream) { // Write section header. lg->WriteDivider(makefileStream); makefileStream << "# Rules to build dependencies and targets.\n" << "\n"; std::vector depends; std::vector commands; // Check the build system in this directory. depends.push_back("cmake_check_build_system"); commands.push_back(this->GetRecursiveMakeCall("depend.make",0)); commands.push_back(this->GetRecursiveMakeCall("build.make",0)); // Write the rule. lg->WriteMakeRule(makefileStream, "The main all target", "all", depends, commands); // write the clean commands.clear(); commands.push_back(this->GetRecursiveMakeCall("clean.make",0)); lg->WriteMakeRule(makefileStream, "default clean target", "clean", depends, commands); } //---------------------------------------------------------------------------- std::string cmGlobalUnixMakefileGenerator3 ::GetRecursiveMakeCall(const char *Makefile, const char* tgt) { cmLocalUnixMakefileGenerator3 *lg = static_cast(m_LocalGenerators[0]); // Call make on the given file. std::string cmd; cmd += "$(MAKE) -f "; cmd += Makefile; cmd += " "; // Pass down verbosity level. if(lg->GetMakeSilentFlag().size()) { cmd += lg->GetMakeSilentFlag(); cmd += " "; } // Most unix makes will pass the command line flags to make down to // sub-invoked makes via an environment variable. However, some // makes do not support that, so you have to pass the flags // explicitly. if(lg->GetPassMakeflags()) { cmd += "-$(MAKEFLAGS) "; } // Add the target. if (tgt && tgt[0] != '\0') { cmd += tgt; } return cmd; } //---------------------------------------------------------------------------- void cmGlobalUnixMakefileGenerator3 ::WriteConvenienceRules(std::ostream& ruleFileStream, cmLocalUnixMakefileGenerator3 *lg) { std::vector depends; std::vector tgt_depends; std::vector commands; depends.push_back("cmake_check_build_system"); // for each target // Generate the rule files for each target. const cmTargets& targets = lg->GetMakefile()->GetTargets(); std::string localName; std::string makeTargetName; for(cmTargets::const_iterator t = targets.begin(); t != targets.end(); ++t) { if((t->second.GetType() == cmTarget::EXECUTABLE) || (t->second.GetType() == cmTarget::STATIC_LIBRARY) || (t->second.GetType() == cmTarget::SHARED_LIBRARY) || (t->second.GetType() == cmTarget::MODULE_LIBRARY)) { // Add a rule to build the target by name. localName = lg->GetRelativeTargetDirectory(t->second); commands.clear(); makeTargetName = localName; makeTargetName += "/depend"; commands.push_back(this->GetRecursiveMakeCall("depend.make",makeTargetName.c_str())); makeTargetName = localName; makeTargetName += "/build"; commands.push_back(this->GetRecursiveMakeCall("build.make",makeTargetName.c_str())); // Write the rule. lg->WriteMakeRule(ruleFileStream, "Convenience name for target.", localName.c_str(), depends, commands); // Add a target with the canonical name (no prefix, suffix or path). if(localName != t->second.GetName()) { commands.clear(); tgt_depends.clear(); tgt_depends.push_back(localName); lg->WriteMakeRule(ruleFileStream, "Convenience name for target.", t->second.GetName(), tgt_depends, commands); } } } }