/*========================================================================= Program: CMake - Cross-Platform Makefile Generator 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 "cmLocalUnixMakefileGenerator2.h" #include "cmDepends.h" #include "cmGeneratedFileStream.h" #include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmSourceFile.h" #include "cmake.h" // Include dependency scanners for supported languages. Only the // C/C++ scanner is needed for bootstrapping CMake. #include "cmDependsC.h" #ifdef CMAKE_BUILD_WITH_CMAKE # include "cmDependsFortran.h" #endif #include // auto_ptr #include #include // Quick-switch for generating old makefiles. #if 1 # define CMLUMG_MAKEFILE_NAME "Makefile" #else # define CMLUMG_WRITE_OLD_MAKEFILE # define CMLUMG_MAKEFILE_NAME "Makefile2" #endif // TODO: Add "help" target. // TODO: Identify remaining relative path violations. // TODO: Need test for separate executable/library output path. //---------------------------------------------------------------------------- cmLocalUnixMakefileGenerator2::cmLocalUnixMakefileGenerator2() { } //---------------------------------------------------------------------------- cmLocalUnixMakefileGenerator2::~cmLocalUnixMakefileGenerator2() { } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2::SetEmptyCommand(const char* cmd) { m_EmptyCommands.clear(); if(cmd) { m_EmptyCommands.push_back(cmd); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2::Generate(bool fromTheTop) { #ifdef CMLUMG_WRITE_OLD_MAKEFILE // Generate old style for now. this->cmLocalUnixMakefileGenerator::Generate(fromTheTop); #else // Make sure we never run a local generate. if(!fromTheTop) { cmSystemTools::Error("Local generate invoked in ", m_Makefile->GetStartOutputDirectory()); return; } // Handle EXECUTABLE_OUTPUT_PATH and LIBRARY_OUTPUT_PATH since // superclass generator is not called. this->ConfigureOutputPaths(); #endif // Generate the rule files for each target. const cmTargets& targets = m_Makefile->GetTargets(); 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)) { this->GenerateTargetRuleFile(t->second); } else if(t->second.GetType() == cmTarget::UTILITY) { this->GenerateUtilityRuleFile(t->second); } } // Generate the rule files for each custom command. const std::vector& sources = m_Makefile->GetSourceFiles(); for(std::vector::const_iterator i = sources.begin(); i != sources.end(); ++i) { if(const cmCustomCommand* cc = (*i)->GetCustomCommand()) { this->GenerateCustomRuleFile(*cc); } } // Generate the main makefile. this->GenerateMakefile(); // Generate the cmake file that keeps the makefile up to date. this->GenerateCMakefile(); // Generate the cmake file with information for this directory. this->GenerateDirectoryInformationFile(); } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2::GenerateMakefile() { // 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 = m_Makefile->GetStartOutputDirectory(); makefileName += "/" CMLUMG_MAKEFILE_NAME; cmGeneratedFileStream makefileStream(makefileName.c_str()); if(!makefileStream) { return; } // Write the do not edit header. this->WriteDisclaimer(makefileStream); // Write standard variables to the makefile. this->WriteMakeVariables(makefileStream); // Write special targets that belong at the top of the file. this->WriteSpecialTargetsTop(makefileStream); // Write rules to build dependencies and targets. this->WriteAllRules(makefileStream); // Write dependency generation rules. this->WritePassRules(makefileStream, "depend"); this->WriteLocalRule(makefileStream, "depend", 0); // Write main build rules. this->WritePassRules(makefileStream, "build"); this->WriteLocalRule(makefileStream, "build", 0); // Write clean rules. this->WritePassRules(makefileStream, "clean"); this->WriteLocalCleanRule(makefileStream); // Write include statements to get rules for this directory. this->WriteRuleFileIncludes(makefileStream); // Write jump-and-build rules that were recorded in the map. this->WriteJumpAndBuildRules(makefileStream); // Write special targets that belong at the bottom of the file. this->WriteSpecialTargetsBottom(makefileStream); } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2::GenerateCMakefile() { std::string makefileName = m_Makefile->GetStartOutputDirectory(); makefileName += "/" CMLUMG_MAKEFILE_NAME; std::string cmakefileName = makefileName; cmakefileName += ".cmake"; // Open the output file. cmGeneratedFileStream cmakefileStream(cmakefileName.c_str()); if(!cmakefileStream) { return; } // Write the do not edit header. this->WriteDisclaimer(cmakefileStream); // Get the list of files contributing to this generation step. // Sort the list and remove duplicates. std::vector lfiles = m_Makefile->GetListFiles(); 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()); // Build the path to the cache file. std::string cache = m_Makefile->GetHomeOutputDirectory(); cache += "/CMakeCache.txt"; // Save the list to the cmake file. cmakefileStream << "# The corresponding makefile\n" << "# \"" << this->ConvertToRelativePath(makefileName.c_str()).c_str() << "\"\n" << "# was generated from the following files:\n" << "SET(CMAKE_MAKEFILE_DEPENDS\n" << " \"" << this->ConvertToRelativePath(cache.c_str()).c_str() << "\"\n"; for(std::vector::const_iterator i = lfiles.begin(); i != lfiles.end(); ++i) { cmakefileStream << " \"" << this->ConvertToRelativePath(i->c_str()).c_str() << "\"\n"; } cmakefileStream << " )\n\n"; // Build the path to the cache check file. std::string check = m_Makefile->GetHomeOutputDirectory(); check += "/cmake.check_cache"; // Set the corresponding makefile in the cmake file. cmakefileStream << "# The corresponding makefile is:\n" << "SET(CMAKE_MAKEFILE_OUTPUTS\n" << " \"" << this->ConvertToRelativePath(makefileName.c_str()).c_str() << "\"\n" << " \"" << this->ConvertToRelativePath(check.c_str()).c_str() << "\"\n" << " \"CMakeDirectoryInformation.cmake\"\n" << " )\n\n"; // Set the set of files to check for dependency integrity. cmakefileStream << "# The set of files whose dependency integrity should be checked:\n"; cmakefileStream << "SET(CMAKE_DEPENDS_LANGUAGES\n"; for(std::map::const_iterator l = m_CheckDependFiles.begin(); l != m_CheckDependFiles.end(); ++l) { cmakefileStream << " \"" << l->first.c_str() << "\"\n"; } cmakefileStream << " )\n"; for(std::map::const_iterator l = m_CheckDependFiles.begin(); l != m_CheckDependFiles.end(); ++l) { cmakefileStream << "SET(CMAKE_DEPENDS_CHECK_" << l->first.c_str() << "\n"; for(std::set::const_iterator i = l->second.begin(); i != l->second.end(); ++i) { cmakefileStream << " \"" << this->ConvertToRelativePath(i->c_str()).c_str() << "\"\n"; } cmakefileStream << " )\n"; } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2::GenerateDirectoryInformationFile() { std::string infoFileName = m_Makefile->GetStartOutputDirectory(); infoFileName += "/CMakeDirectoryInformation.cmake"; // Open the output file. cmGeneratedFileStream infoFileStream(infoFileName.c_str()); if(!infoFileStream) { return; } // Write the do not edit header. this->WriteDisclaimer(infoFileStream); // Store the include search path for this directory. infoFileStream << "# The C and CXX include file search paths:\n"; infoFileStream << "SET(CMAKE_C_INCLUDE_PATH\n"; std::vector includeDirs; this->GetIncludeDirectories(includeDirs); for(std::vector::iterator i = includeDirs.begin(); i != includeDirs.end(); ++i) { infoFileStream << " \"" << this->ConvertToRelativePath(i->c_str()).c_str() << "\"\n"; } infoFileStream << " )\n"; infoFileStream << "SET(CMAKE_CXX_INCLUDE_PATH ${CMAKE_C_INCLUDE_PATH})\n"; // Store the include regular expressions for this directory. infoFileStream << "\n" << "# The C and CXX include file regular expressions for this directory.\n"; infoFileStream << "SET(CMAKE_C_INCLUDE_REGEX_SCAN "; this->WriteCMakeArgument(infoFileStream, m_Makefile->GetIncludeRegularExpression()); infoFileStream << ")\n"; infoFileStream << "SET(CMAKE_C_INCLUDE_REGEX_COMPLAIN "; this->WriteCMakeArgument(infoFileStream, m_Makefile->GetComplainRegularExpression()); infoFileStream << ")\n"; infoFileStream << "SET(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN})\n"; infoFileStream << "SET(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN})\n"; } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::GenerateTargetRuleFile(const cmTarget& target) { // Create a directory for this target. std::string dir = this->GetTargetDirectory(target); cmSystemTools::MakeDirectory(this->ConvertToFullPath(dir).c_str()); // First generate the object rule files. Save a list of all object // files for this target. std::vector objects; std::vector external_objects; std::vector provides_requires; const std::vector& sources = target.GetSourceFiles(); for(std::vector::const_iterator source = sources.begin(); source != sources.end(); ++source) { if(!(*source)->GetPropertyAsBool("HEADER_FILE_ONLY") && !(*source)->GetCustomCommand()) { if(!m_GlobalGenerator->IgnoreFile((*source)->GetSourceExtension().c_str())) { // Generate this object file's rule file. this->GenerateObjectRuleFile(target, *(*source), objects, provides_requires); } else if((*source)->GetPropertyAsBool("EXTERNAL_OBJECT")) { // This is an external object file. Just add it. external_objects.push_back((*source)->GetFullPath()); } } } // Generate the build-time dependencies file for this target. std::string depBase = dir; depBase += "/"; depBase += target.GetName(); // Construct the rule file name. std::string ruleFileName = dir; ruleFileName += "/"; ruleFileName += target.GetName(); ruleFileName += ".make"; // The rule file must be included by the makefile. m_IncludeRuleFiles.push_back(ruleFileName); // Open the rule file. This should be copy-if-different because the // rules may depend on this file itself. std::string ruleFileNameFull = this->ConvertToFullPath(ruleFileName); cmGeneratedFileStream ruleFileStream(ruleFileNameFull.c_str()); ruleFileStream.SetCopyIfDifferent(true); if(!ruleFileStream) { return; } this->WriteDisclaimer(ruleFileStream); ruleFileStream << "# Rule file for target " << target.GetName() << ".\n\n"; // Include the rule file for each object. if(!objects.empty()) { ruleFileStream << "# Include make rules for object files.\n"; for(std::vector::const_iterator obj = objects.begin(); obj != objects.end(); ++obj) { std::string objRuleFileName = *obj; objRuleFileName += ".make"; ruleFileStream << m_IncludeDirective << " " << this->ConvertToOutputForExisting(objRuleFileName.c_str()).c_str() << "\n"; } ruleFileStream << "\n"; } // Write the rule for this target type. switch(target.GetType()) { case cmTarget::STATIC_LIBRARY: this->WriteStaticLibraryRule(ruleFileStream, ruleFileName.c_str(), target, objects, external_objects, provides_requires); break; case cmTarget::SHARED_LIBRARY: this->WriteSharedLibraryRule(ruleFileStream, ruleFileName.c_str(), target, objects, external_objects, provides_requires); break; case cmTarget::MODULE_LIBRARY: this->WriteModuleLibraryRule(ruleFileStream, ruleFileName.c_str(), target, objects, external_objects, provides_requires); break; case cmTarget::EXECUTABLE: this->WriteExecutableRule(ruleFileStream, ruleFileName.c_str(), target, objects, external_objects, provides_requires); break; default: break; } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::GenerateObjectRuleFile(const cmTarget& target, const cmSourceFile& source, std::vector& objects, std::vector& provides_requires) { // Identify the language of the source file. const char* lang = this->GetSourceFileLanguage(source); if(!lang) { // If language is not known, this is an error. cmSystemTools::Error("Source file \"", source.GetFullPath().c_str(), "\" has unknown type."); return; } // Get the full path name of the object file. // TODO: Remove duplicate objects and warn. std::string obj = this->GetObjectFileName(target, source); // Create the directory containing the object file. This may be a // subdirectory under the target's directory. std::string dir = cmSystemTools::GetFilenamePath(obj.c_str()); cmSystemTools::MakeDirectory(this->ConvertToFullPath(dir).c_str()); // Generate the build-time dependencies file for this object file. std::string depMakeFile; std::string depMarkFile; if(!this->GenerateDependsMakeFile(lang, obj.c_str(), depMakeFile, depMarkFile)) { cmSystemTools::Error("No dependency checker available for language \"", lang, "\"."); return; } // Save this in the target's list of object files. objects.push_back(obj); // The object file should be checked for dependency integrity. m_CheckDependFiles[lang].insert(obj); // Open the rule file for writing. This should be copy-if-different // because the rules may depend on this file itself. std::string ruleFileName = obj; ruleFileName += ".make"; std::string ruleFileNameFull = this->ConvertToFullPath(ruleFileName); cmGeneratedFileStream ruleFileStream(ruleFileNameFull.c_str()); ruleFileStream.SetCopyIfDifferent(true); if(!ruleFileStream) { return; } this->WriteDisclaimer(ruleFileStream); ruleFileStream << "# Rule file for object file " << obj.c_str() << ".\n\n"; // Include the dependencies for the target. ruleFileStream << "# Include any dependencies generated for this rule.\n" << m_IncludeDirective << " " << this->ConvertToOutputForExisting(depMakeFile.c_str()).c_str() << "\n\n"; // Create the list of dependencies known at cmake time. These are // shared between the object file and dependency scanning rule. std::vector depends; depends.push_back(source.GetFullPath()); if(const char* objectDeps = source.GetProperty("OBJECT_DEPENDS")) { std::vector deps; cmSystemTools::ExpandListArgument(objectDeps, deps); for(std::vector::iterator i = deps.begin(); i != deps.end(); ++i) { depends.push_back(i->c_str()); } } depends.push_back(ruleFileName); // Write the dependency generation rule. { std::string depEcho = "Scanning "; depEcho += lang; depEcho += " dependencies of "; depEcho += this->ConvertToRelativeOutputPath(obj.c_str()); depEcho += "..."; // Add a command to call CMake to scan dependencies. CMake will // touch the corresponding depends file after scanning dependencies. cmOStringStream depCmd; // TODO: Account for source file properties and directory-level // definitions when scanning for dependencies. depCmd << "$(CMAKE_COMMAND) -E cmake_depends " << lang << " " << this->ConvertToRelativeOutputPath(obj.c_str()) << " " << this->ConvertToRelativeOutputPath(source.GetFullPath().c_str()); std::vector commands; commands.push_back(depCmd.str()); // Write the rule. this->WriteMakeRule(ruleFileStream, 0, depEcho.c_str(), depMarkFile.c_str(), depends, commands); } // Write the build rule. { // Build the set of compiler flags. std::string flags; // Add the export symbol definition for shared library objects. bool shared = ((target.GetType() == cmTarget::SHARED_LIBRARY) || (target.GetType() == cmTarget::MODULE_LIBRARY)); if(shared) { flags += "-D"; if(const char* custom_export_name = target.GetProperty("DEFINE_SYMBOL")) { flags += custom_export_name; } else { std::string in = target.GetName(); in += "_EXPORTS"; flags += cmSystemTools::MakeCindentifier(in.c_str()); } } // Add flags from source file properties. this->AppendFlags(flags, source.GetProperty("COMPILE_FLAGS")); // Add language-specific flags. this->AddLanguageFlags(flags, lang); // Add shared-library flags if needed. this->AddSharedFlags(flags, lang, shared); // Add include directory flags. this->AppendFlags(flags, this->GetIncludeFlags(lang)); // Get the output paths for source and object files. std::string sourceFile = this->ConvertToRelativeOutputPath(source.GetFullPath().c_str()); std::string objectFile = this->ConvertToRelativeOutputPath(obj.c_str()); // Construct the compile rules. std::vector commands; std::string compileRuleVar = "CMAKE_"; compileRuleVar += lang; compileRuleVar += "_COMPILE_OBJECT"; std::string compileRule = m_Makefile->GetRequiredDefinition(compileRuleVar.c_str()); cmSystemTools::ExpandListArgument(compileRule, commands); // Expand placeholders in the commands. for(std::vector::iterator i = commands.begin(); i != commands.end(); ++i) { this->ExpandRuleVariables(*i, lang, 0, // no objects 0, // no target 0, // no link libs sourceFile.c_str(), objectFile.c_str(), flags.c_str()); } // Write the rule. std::string buildEcho = "Building "; buildEcho += lang; buildEcho += " object "; buildEcho += this->ConvertToRelativeOutputPath(obj.c_str()); buildEcho += "..."; this->WriteMakeRule(ruleFileStream, 0, buildEcho.c_str(), obj.c_str(), depends, commands); } // If the language needs provides-requires mode, create the // corresponding targets. if(strcmp(lang, "Fortran") == 0) { std::string objectRequires = obj; std::string objectProvides = obj; objectRequires += ".requires"; objectProvides += ".provides"; { // Add the provides target to build the object file. std::vector no_commands; std::vector p_depends; p_depends.push_back(obj); this->WriteMakeRule(ruleFileStream, 0, 0, objectProvides.c_str(), p_depends, no_commands); } { // Add the requires target to recursively build the provides // target after needed information is up to date. std::vector no_depends; std::vector r_commands; r_commands.push_back(this->GetRecursiveMakeCall(objectProvides.c_str())); this->WriteMakeRule(ruleFileStream, 0, 0, objectRequires.c_str(), no_depends, r_commands); } // Add this to the set of provides-requires objects on the target. provides_requires.push_back(objectRequires); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::GenerateCustomRuleFile(const cmCustomCommand& cc) { // Create a directory for custom rule files. std::string dir = "CMakeCustomRules.dir"; cmSystemTools::MakeDirectory(this->ConvertToFullPath(dir).c_str()); // Construct the name of the rule file. std::string customName = this->GetCustomBaseName(cc); std::string ruleFileName = dir; ruleFileName += "/"; ruleFileName += customName; ruleFileName += ".make"; // If this is a duplicate rule produce an error. if(m_CustomRuleFiles.find(ruleFileName) != m_CustomRuleFiles.end()) { cmSystemTools::Error("An output was found with multiple rules on how to build it for output: ", cc.GetOutput().c_str()); return; } m_CustomRuleFiles.insert(ruleFileName); // This rule should be included by the makefile. m_IncludeRuleFiles.push_back(ruleFileName); // Open the rule file. This should be copy-if-different because the // rules may depend on this file itself. std::string ruleFileNameFull = this->ConvertToFullPath(ruleFileName); cmGeneratedFileStream ruleFileStream(ruleFileNameFull.c_str()); ruleFileStream.SetCopyIfDifferent(true); if(!ruleFileStream) { return; } this->WriteDisclaimer(ruleFileStream); ruleFileStream << "# Custom command rule file for " << customName.c_str() << ".\n\n"; // Collect the commands. std::vector commands; this->AppendCustomCommand(commands, cc); // Collect the dependencies. std::vector depends; this->AppendCustomDepend(depends, cc); // Add a dependency on the rule file itself. depends.push_back(ruleFileName); // Write the rule. const char* comment = 0; if(cc.GetComment().size()) { comment = cc.GetComment().c_str(); } std::string preEcho = "Generating "; preEcho += customName; preEcho += "..."; this->WriteMakeRule(ruleFileStream, comment, preEcho.c_str(), cc.GetOutput().c_str(), depends, commands); // Write the clean rule for this custom command. std::string cleanTarget = customName; cleanTarget += ".clean"; commands.clear(); depends.clear(); std::vector cleanFiles; cleanFiles.push_back(cc.GetOutput().c_str()); this->AppendCleanCommand(commands, cleanFiles); this->WriteMakeRule(ruleFileStream, "Clean the output of this custom command.", 0, cleanTarget.c_str(), depends, commands); // Check whether to attach the clean rule. bool attach = true; if(const char* clean_no_custom = m_Makefile->GetProperty("CLEAN_NO_CUSTOM")) { if(!cmSystemTools::IsOff(clean_no_custom)) { attach = false; } } // Attach the clean rule to the directory-level clean rule. if(attach) { this->WriteLocalRule(ruleFileStream, "clean", cleanTarget.c_str()); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::GenerateUtilityRuleFile(const cmTarget& target) { // Create a directory for utility rule files. std::string dir = "CMakeCustomRules.dir"; cmSystemTools::MakeDirectory(this->ConvertToFullPath(dir).c_str()); // Construct the name of the rule file. std::string ruleFileName = dir; ruleFileName += "/"; ruleFileName += target.GetName(); ruleFileName += ".make"; // This rule should be included by the makefile. m_IncludeRuleFiles.push_back(ruleFileName); // Open the rule file. This should be copy-if-different because the // rules may depend on this file itself. std::string ruleFileNameFull = this->ConvertToFullPath(ruleFileName); cmGeneratedFileStream ruleFileStream(ruleFileNameFull.c_str()); ruleFileStream.SetCopyIfDifferent(true); if(!ruleFileStream) { return; } this->WriteDisclaimer(ruleFileStream); ruleFileStream << "# Utility rule file for " << target.GetName() << ".\n\n"; // TODO: Pre-build and pre-link rules. // Collect the commands and dependencies. std::vector commands; std::vector depends; // Utility targets store their rules in post-build commands. this->AppendCustomDepends(depends, target.GetPostBuildCommands()); this->AppendCustomCommands(commands, target.GetPostBuildCommands()); // Add dependencies on targets that must be built first. this->AppendTargetDepends(depends, target); // Add a dependency on the rule file itself. depends.push_back(ruleFileName); // Write the rule. this->WriteMakeRule(ruleFileStream, 0, 0, target.GetName(), depends, commands); // Add this to the list of build rules in this directory. if(target.IsInAll()) { this->WriteLocalRule(ruleFileStream, "build", target.GetName()); } } //---------------------------------------------------------------------------- bool cmLocalUnixMakefileGenerator2 ::GenerateDependsMakeFile(const std::string& lang, const char* objFile, std::string& depMakeFile, std::string& depMarkFile) { // Construct a checker for the given language. std::auto_ptr checker(this->GetDependsChecker(lang, m_Makefile->GetStartOutputDirectory(), objFile)); if(checker.get()) { // Save the make and mark file names. depMakeFile = checker->GetMakeFileName(); depMarkFile = checker->GetMarkFileName(); // Check the dependencies. checker->Check(); return true; } return false; } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteMakeRule(std::ostream& os, const char* comment, const char* preEcho, const char* target, const std::vector& depends, const std::vector& commands, const char* postEcho) { // Make sure there is a target. if(!target || !*target) { cmSystemTools::Error("No target for WriteMakeRule!"); return; } std::string replace; // Write the comment describing the rule in the makefile. if(comment) { replace = comment; m_Makefile->ExpandVariablesInString(replace); std::string::size_type lpos = 0; std::string::size_type rpos; while((rpos = replace.find('\n', lpos)) != std::string::npos) { os << "# " << replace.substr(lpos, rpos-lpos) << "\n"; lpos = rpos+1; } os << "# " << replace.substr(lpos) << "\n"; } // Construct the left hand side of the rule. replace = target; m_Makefile->ExpandVariablesInString(replace); std::string tgt = this->ConvertToRelativeOutputPath(replace.c_str()); tgt = this->ConvertToMakeTarget(tgt.c_str()); const char* space = ""; if(tgt.size() == 1) { // Add a space before the ":" to avoid drive letter confusion on // Windows. space = " "; } // Write the rule. if(depends.empty()) { // No dependencies. The commands will always run. os << tgt.c_str() << space << ":\n"; } else { // Split dependencies into multiple rule lines. This allows for // very long dependency lists even on older make implementations. for(std::vector::const_iterator dep = depends.begin(); dep != depends.end(); ++dep) { replace = *dep; m_Makefile->ExpandVariablesInString(replace); replace = this->ConvertToRelativeOutputPath(replace.c_str()); replace = this->ConvertToMakeTarget(replace.c_str()); os << tgt.c_str() << space << ": " << replace.c_str() << "\n"; } } // Write the list of commands. bool first = true; for(std::vector::const_iterator i = commands.begin(); i != commands.end(); ++i) { replace = *i; m_Makefile->ExpandVariablesInString(replace); if(first && preEcho) { this->OutputEcho(os, preEcho); } os << "\t" << replace.c_str() << "\n"; first = false; } if(postEcho) { this->OutputEcho(os, postEcho); } os << "\n"; } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2::WriteDivider(std::ostream& os) { os << "#======================================" << "=======================================\n"; } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2::WriteDisclaimer(std::ostream& os) { os << "# CMAKE generated file: DO NOT EDIT!\n" << "# Generated by \"" << m_GlobalGenerator->GetName() << "\"" << " Generator, CMake Version " << cmMakefile::GetMajorVersion() << "." << cmMakefile::GetMinorVersion() << "\n\n"; } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteMakeVariables(std::ostream& makefileStream) { this->WriteDivider(makefileStream); makefileStream << "# Set environment variables for the build.\n" << "\n"; if(m_WindowsShell) { makefileStream << "!IF \"$(OS)\" == \"Windows_NT\"\n" << "NULL=\n" << "!ELSE\n" << "NULL=nul\n" << "!ENDIF\n"; } else { makefileStream << "# The shell in which to execute make rules.\n" << "SHELL = /bin/sh\n" << "\n"; } if(m_Makefile->IsOn("CMAKE_VERBOSE_MAKEFILE")) { makefileStream << "# Produce verbose output by default.\n" << "VERBOSE = 1\n" << "\n"; } std::string cmakecommand = this->ConvertToOutputForExisting( m_Makefile->GetRequiredDefinition("CMAKE_COMMAND")); makefileStream << "# The CMake executable.\n" << "CMAKE_COMMAND = " << this->ConvertToRelativeOutputPath(cmakecommand.c_str()).c_str() << "\n" << "\n"; makefileStream << "# The command to remove a file.\n" << "RM = " << this->ConvertToRelativeOutputPath(cmakecommand.c_str()).c_str() << " -E remove -f\n" << "\n"; if(m_Makefile->GetDefinition("CMAKE_EDIT_COMMAND")) { makefileStream << "# The program to use to edit the cache.\n" << "CMAKE_EDIT_COMMAND = " << (this->ConvertToOutputForExisting( m_Makefile->GetDefinition("CMAKE_EDIT_COMMAND"))) << "\n" << "\n"; } makefileStream << "# The source directory corresponding to this makefile.\n" << "CMAKE_CURRENT_SOURCE = " << this->ConvertToRelativeOutputPath(m_Makefile->GetStartDirectory()) << "\n" << "\n"; makefileStream << "# The build directory corresponding to this makefile.\n" << "CMAKE_CURRENT_BINARY = " << this->ConvertToRelativeOutputPath(m_Makefile->GetStartOutputDirectory()) << "\n" << "\n"; makefileStream << "# The top-level source directory on which CMake was run.\n" << "CMAKE_SOURCE_DIR = " << this->ConvertToRelativeOutputPath(m_Makefile->GetHomeDirectory()) << "\n" << "\n"; makefileStream << "# The top-level build directory on which CMake was run.\n" << "CMAKE_BINARY_DIR = " << this->ConvertToRelativeOutputPath(m_Makefile->GetHomeOutputDirectory()) << "\n" << "\n"; } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteSpecialTargetsTop(std::ostream& makefileStream) { this->WriteDivider(makefileStream); makefileStream << "# Special targets provided by cmake.\n" << "\n"; // 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. this->WriteMakeRule(makefileStream, "Default target executed when no arguments are " "given to make.", 0, "default_target", depends, no_commands); } // Write special "install" target to run cmake_install.cmake script. { std::vector no_depends; std::vector commands; std::string cmd; if(m_Makefile->GetDefinition("CMake_BINARY_DIR")) { // We are building CMake itself. We cannot use the original // executable to install over itself. cmd = m_ExecutableOutputPath; cmd += "cmake"; cmd = this->ConvertToRelativeOutputPath(cmd.c_str()); } else { cmd = "$(CMAKE_COMMAND)"; } cmd += " -P cmake_install.cmake"; commands.push_back(cmd); this->WriteMakeRule(makefileStream, "Special rule to run installation script.", 0, "install", no_depends, commands); } // Write special "rebuild_cache" target to re-run cmake. { std::vector no_depends; std::vector commands; commands.push_back( "$(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)"); this->WriteMakeRule(makefileStream, "Special rule to re-run CMake using make.", "Running CMake to regenerate build system...", "rebuild_cache", no_depends, commands); } // Use CMAKE_EDIT_COMMAND for the edit_cache rule if it is defined. // Otherwise default to the interactive command-line interface. if(m_Makefile->GetDefinition("CMAKE_EDIT_COMMAND")) { std::vector no_depends; std::vector commands; commands.push_back( "$(CMAKE_EDIT_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)"); this->WriteMakeRule(makefileStream, "Special rule to re-run CMake cache editor using make.", "Running CMake cache editor...", "edit_cache", no_depends, commands); } else { std::vector no_depends; std::vector commands; commands.push_back( "$(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) -i"); this->WriteMakeRule(makefileStream, "Special rule to re-run CMake cache editor using make.", "Running interactive CMake command-line interface...", "edit_cache", no_depends, commands); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteSpecialTargetsBottom(std::ostream& makefileStream) { this->WriteDivider(makefileStream); makefileStream << "# Special targets to cleanup operation of make.\n" << "\n"; // Write special "cmake_check_build_system" target to run cmake with // the --check-build-system flag. { // Build command to run CMake to check if anything needs regenerating. std::string cmakefileName = m_Makefile->GetStartOutputDirectory(); cmakefileName += "/" CMLUMG_MAKEFILE_NAME ".cmake"; std::string runRule = "$(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)"; runRule += " --check-build-system "; runRule += this->ConvertToRelativeOutputPath(cmakefileName.c_str()); std::vector no_depends; std::vector commands; commands.push_back(runRule); std::string preEcho = "Checking build system in "; preEcho += m_Makefile->GetStartOutputDirectory(); preEcho += "..."; this->WriteMakeRule(makefileStream, "Special rule to run CMake to check the build system " "integrity.\n" "No rule that depends on this can have " "commands that come from listfiles\n" "because they might be regenerated.", preEcho.c_str(), "cmake_check_build_system", no_depends, commands); } std::vector no_commands; // Write special target to silence make output. This must be after // the default target in case VERBOSE is set (which changes the // name). The setting of CMAKE_VERBOSE_MAKEFILE to ON will cause a // "VERBOSE=1" to be added as a make variable which will change the // name of this special target. This gives a make-time choice to // the user. std::vector no_depends; this->WriteMakeRule(makefileStream, "Suppress display of executed commands.", 0, "$(VERBOSE).SILENT", no_depends, no_commands); // Special target to cleanup operation of make tool. std::vector depends; this->WriteMakeRule(makefileStream, "Disable implicit rules so canoncical targets will work.", 0, ".SUFFIXES", depends, no_commands); depends.push_back(".hpux_make_must_have_suffixes_list"); this->WriteMakeRule(makefileStream, 0, 0, ".SUFFIXES", depends, no_commands); } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteAllRules(std::ostream& makefileStream) { // Write section header. this->WriteDivider(makefileStream); makefileStream << "# Rules to build dependencies and targets.\n" << "\n"; // Write rules to traverse the directory tree building dependencies // and targets. this->WriteDriverRules(makefileStream, "all", "depend.local", "build.local"); } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WritePassRules(std::ostream& makefileStream, const char* pass) { // Write section header. this->WriteDivider(makefileStream); makefileStream << "# Rules for the " << pass << " pass.\n" << "\n"; // Write rules to traverse the directory tree for this pass. std::string passLocal = pass; passLocal += ".local"; this->WriteDriverRules(makefileStream, pass, passLocal.c_str()); } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteDriverRules(std::ostream& makefileStream, const char* pass, const char* local1, const char* local2) { // Write a set of targets that will traverse the directory structure // in order and build given local targets in each directory. // The dependencies and commands generated for this rule must not // depend on listfile contents because the build system check might // regenerate the makefile but it might not get read again by make // before the commands run. std::vector depends; std::vector commands; // Check the build system in this directory. depends.push_back("cmake_check_build_system"); // Recursively handle pre-order directories. std::string preTarget = pass; preTarget += ".pre-order"; commands.push_back(this->GetRecursiveMakeCall(preTarget.c_str())); // Recursively build the local targets in this directory. if(local1) { commands.push_back(this->GetRecursiveMakeCall(local1)); } if(local2) { commands.push_back(this->GetRecursiveMakeCall(local2)); } // Recursively handle post-order directories. std::string postTarget = pass; postTarget += ".post-order"; commands.push_back(this->GetRecursiveMakeCall(postTarget.c_str())); // Write the rule. std::string preEcho = "Entering directory "; preEcho += m_Makefile->GetStartOutputDirectory(); std::string postEcho = "Finished directory "; postEcho += m_Makefile->GetStartOutputDirectory(); this->WriteMakeRule(makefileStream, 0, preEcho.c_str(), pass, depends, commands, postEcho.c_str()); // Write the subdirectory traversal rules. this->WriteSubdirRules(makefileStream, pass); } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteSubdirRules(std::ostream& makefileStream, const char* pass) { // Iterate through subdirectories. Only entries in which the // boolean is true should be included. Keep track of the last // pre-order and last post-order rule created so that ordering can // be enforced. const std::vector >& subdirs = m_Makefile->GetSubDirectories(); std::string lastPre = ""; std::string lastPost = ""; for(std::vector >::const_iterator i = subdirs.begin(); i != subdirs.end(); ++i) { if(i->second) { // Add the subdirectory rule either for pre-order or post-order. if(m_Makefile->IsDirectoryPreOrder(i->first.c_str())) { this->WriteSubdirRule(makefileStream, pass, i->first.c_str(), lastPre); } else { this->WriteSubdirRule(makefileStream, pass, i->first.c_str(), lastPost); } } } // Write the subdir driver rules. Hook them to the last // subdirectory of their corresponding order. this->WriteSubdirDriverRule(makefileStream, pass, "pre", lastPre); this->WriteSubdirDriverRule(makefileStream, pass, "post", lastPost); } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteSubdirRule(std::ostream& makefileStream, const char* pass, const char* subdir, std::string& last) { std::vector depends; std::vector commands; // Construct the name of the subdirectory rule. std::string tgt = this->GetSubdirTargetName(pass, subdir); if(m_WindowsShell) { // On Windows we must perform each step separately and then change // back because the shell keeps the working directory between // commands. std::string cmd = "cd "; cmd += this->ConvertToOutputForExisting(subdir); commands.push_back(cmd); // Build the target for this pass. commands.push_back(this->GetRecursiveMakeCall(pass)); // Change back to the starting directory. Any trailing slash must // be removed to avoid problems with Borland Make. std::string destFull = m_Makefile->GetStartOutputDirectory(); destFull += "/"; destFull += subdir; std::string back = cmSystemTools::RelativePath(destFull.c_str(), m_Makefile->GetStartOutputDirectory()); if(back.size() && back[back.size()-1] == '/') { back = back.substr(0, back.size()-1); } cmd = "cd "; cmd += this->ConvertToOutputForExisting(back.c_str()); commands.push_back(cmd); } else { // On UNIX we must construct a single shell command to change // directory and build because make resets the directory between // each command. std::string cmd = "cd "; cmd += this->ConvertToOutputForExisting(subdir); // Build the target for this pass. cmd += " && "; cmd += this->GetRecursiveMakeCall(pass); // Add the command as a single line. commands.push_back(cmd); } // Depend on the last directory written out to enforce ordering. if(last.size() > 0) { depends.push_back(last); } // Write the rule. this->WriteMakeRule(makefileStream, 0, 0, tgt.c_str(), depends, commands); // This rule is now the last one written. last = tgt; } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteSubdirDriverRule(std::ostream& makefileStream, const char* pass, const char* order, const std::string& last) { // This rule corresponds to a particular pass (all, clean, etc). It // dispatches the build into subdirectories in pre- or post-order. std::vector depends; std::vector commands; if(last.size()) { // Use the dependency to drive subdirectory processing. depends.push_back(last); } else { // There are no subdirectories. Use the empty commands to avoid // make errors on some platforms. commands = m_EmptyCommands; } // Build comment to describe purpose. std::string comment = "Driver target for "; comment += order; comment += "-order subdirectories during the "; comment += pass; comment += " pass."; // Build the make target name. std::string tgt = pass; tgt += "."; tgt += order; tgt += "-order"; // Write the rule. this->WriteMakeRule(makefileStream, comment.c_str(), 0, tgt.c_str(), depends, commands); } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteLocalRule(std::ostream& ruleFileStream, const char* pass, const char* dependency) { std::string localTarget = pass; localTarget += ".local"; if(dependency) { std::string comment = "Include this target in the \""; comment += pass; comment += "\" pass for this directory."; std::vector depends; std::vector no_commands; depends.push_back(dependency); this->WriteMakeRule(ruleFileStream, comment.c_str(), 0, localTarget.c_str(), depends, no_commands); } else { std::vector no_depends; std::vector commands = m_EmptyCommands; this->WriteMakeRule(ruleFileStream, "Local rule is empty by default. " "Targets may add dependencies.", 0, localTarget.c_str(), no_depends, commands); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteConvenienceRules(std::ostream& ruleFileStream, const cmTarget& target, const char* targetOutPath) { // Add a rule to build the target by name. std::string localName = this->GetFullTargetName(target.GetName(), target); localName = this->ConvertToRelativeOutputPath(localName.c_str()); this->WriteConvenienceRule(ruleFileStream, targetOutPath, localName.c_str()); // Add a target with the canonical name (no prefix, suffix or path). if(localName != target.GetName()) { this->WriteConvenienceRule(ruleFileStream, targetOutPath, target.GetName()); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteConvenienceRule(std::ostream& ruleFileStream, const char* realTarget, const char* helpTarget) { // A rule is only needed if the names are different. if(strcmp(realTarget, helpTarget) != 0) { // The helper target depends on the real target. std::vector depends; depends.push_back(realTarget); // There are no commands. std::vector no_commands; // Write the rule. this->WriteMakeRule(ruleFileStream, "Convenience name for target.", 0, helpTarget, depends, no_commands); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteRuleFileIncludes(std::ostream& makefileStream) { // Make sure we have some rules to include. if(m_IncludeRuleFiles.empty()) { return; } // Write section header. this->WriteDivider(makefileStream); makefileStream << "# Include rule files for this directory.\n" << "\n"; // Write the include rules. for(std::vector::const_iterator i = m_IncludeRuleFiles.begin(); i != m_IncludeRuleFiles.end(); ++i) { makefileStream << m_IncludeDirective << " " << this->ConvertToOutputForExisting(i->c_str()).c_str() << "\n"; } makefileStream << "\n"; } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteExecutableRule(std::ostream& ruleFileStream, const char* ruleFileName, const cmTarget& target, const std::vector& objects, const std::vector& external_objects, const std::vector& provides_requires) { // Write the dependency generation rule. this->WriteTargetDependsRule(ruleFileStream, ruleFileName, target, objects); std::vector commands; // Build list of dependencies. std::vector depends; for(std::vector::const_iterator obj = objects.begin(); obj != objects.end(); ++obj) { depends.push_back(*obj); } // Add dependencies on targets that must be built first. this->AppendTargetDepends(depends, target); // Add a dependency on the rule file itself. depends.push_back(ruleFileName); // Construct the full path to the executable that will be generated. std::string targetFullPath = m_ExecutableOutputPath; if(targetFullPath.length() == 0) { targetFullPath = m_Makefile->GetStartOutputDirectory(); targetFullPath += "/"; } #ifdef __APPLE__ if(target.GetPropertyAsBool("MACOSX_BUNDLE")) { // Make bundle directories targetFullPath += target.GetName(); targetFullPath += ".app/Contents/MacOS/"; } #endif targetFullPath += target.GetName(); targetFullPath += cmSystemTools::GetExecutableExtension(); // Convert to the output path to use in constructing commands. std::string targetOutPath = this->ConvertToRelativeOutputPath(targetFullPath.c_str()); // Get the language to use for linking this executable. const char* linkLanguage = target.GetLinkerLanguage(this->GetGlobalGenerator()); // Build a list of compiler flags and linker flags. std::string flags; std::string linkFlags; // Add flags to create an executable. this->AddConfigVariableFlags(linkFlags, "CMAKE_EXE_LINKER_FLAGS"); if(target.GetPropertyAsBool("WIN32_EXECUTABLE")) { this->AppendFlags(linkFlags, m_Makefile->GetDefinition("CMAKE_CREATE_WIN32_EXE")); } else { this->AppendFlags(linkFlags, m_Makefile->GetDefinition("CMAKE_CREATE_CONSOLE_EXE")); } // Add language-specific flags. this->AddLanguageFlags(flags, linkLanguage); // Add flags to deal with shared libraries. Any library being // linked in might be shared, so always use shared flags for an // executable. this->AddSharedFlags(flags, linkLanguage, true); // Add target-specific linker flags. this->AppendFlags(linkFlags, target.GetProperty("LINK_FLAGS")); // TODO: Pre-build and pre-link rules. // Construct the main link rule. std::string linkRuleVar = "CMAKE_"; linkRuleVar += linkLanguage; linkRuleVar += "_LINK_EXECUTABLE"; std::string linkRule = m_Makefile->GetRequiredDefinition(linkRuleVar.c_str()); cmSystemTools::ExpandListArgument(linkRule, commands); // Add the post-build rules. this->AppendCustomCommands(commands, target.GetPostBuildCommands()); // Collect up flags to link in needed libraries. cmOStringStream linklibs; this->OutputLinkLibraries(linklibs, 0, target); // Construct object file lists that may be needed to expand the // rule. std::string variableName; std::string variableNameExternal; this->WriteObjectsVariable(ruleFileStream, target, objects, external_objects, variableName, variableNameExternal); std::string buildObjs = "$("; buildObjs += variableName; buildObjs += ") $("; buildObjs += variableNameExternal; buildObjs += ")"; std::string cleanObjs = "$("; cleanObjs += variableName; cleanObjs += ")"; // Expand placeholders in the commands. for(std::vector::iterator i = commands.begin(); i != commands.end(); ++i) { this->ExpandRuleVariables(*i, linkLanguage, buildObjs.c_str(), targetOutPath.c_str(), linklibs.str().c_str(), 0, 0, flags.c_str(), 0, 0, 0, linkFlags.c_str()); } // Write the build rule. std::string buildEcho = "Linking "; buildEcho += linkLanguage; buildEcho += " executable "; buildEcho += targetOutPath; buildEcho += "..."; this->WriteMakeRule(ruleFileStream, 0, buildEcho.c_str(), targetFullPath.c_str(), depends, commands); // Write convenience targets. this->WriteConvenienceRules(ruleFileStream, target, targetOutPath.c_str()); // Write clean target. std::vector cleanFiles; cleanFiles.push_back(targetOutPath.c_str()); cleanFiles.push_back(cleanObjs); this->WriteTargetCleanRule(ruleFileStream, target, cleanFiles); // Write the driving make target. this->WriteTargetRequiresRule(ruleFileStream, target, provides_requires); } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteStaticLibraryRule(std::ostream& ruleFileStream, const char* ruleFileName, const cmTarget& target, const std::vector& objects, const std::vector& external_objects, const std::vector& provides_requires) { const char* linkLanguage = target.GetLinkerLanguage(this->GetGlobalGenerator()); std::string linkRuleVar = "CMAKE_"; linkRuleVar += linkLanguage; linkRuleVar += "_CREATE_STATIC_LIBRARY"; std::string extraFlags; this->AppendFlags(extraFlags, target.GetProperty("STATIC_LIBRARY_FLAGS")); this->WriteLibraryRule(ruleFileStream, ruleFileName, target, objects, external_objects, linkRuleVar.c_str(), extraFlags.c_str(), provides_requires); } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteSharedLibraryRule(std::ostream& ruleFileStream, const char* ruleFileName, const cmTarget& target, const std::vector& objects, const std::vector& external_objects, const std::vector& provides_requires) { const char* linkLanguage = target.GetLinkerLanguage(this->GetGlobalGenerator()); std::string linkRuleVar = "CMAKE_"; linkRuleVar += linkLanguage; linkRuleVar += "_CREATE_SHARED_LIBRARY"; std::string extraFlags; this->AppendFlags(extraFlags, target.GetProperty("LINK_FLAGS")); this->AddConfigVariableFlags(extraFlags, "CMAKE_SHARED_LINKER_FLAGS"); if(m_Makefile->IsOn("WIN32") && !(m_Makefile->IsOn("CYGWIN") || m_Makefile->IsOn("MINGW"))) { const std::vector& sources = target.GetSourceFiles(); for(std::vector::const_iterator i = sources.begin(); i != sources.end(); ++i) { if((*i)->GetSourceExtension() == "def") { extraFlags += " "; extraFlags += m_Makefile->GetSafeDefinition("CMAKE_LINK_DEF_FILE_FLAG"); extraFlags += this->ConvertToRelativeOutputPath((*i)->GetFullPath().c_str()); } } } this->WriteLibraryRule(ruleFileStream, ruleFileName, target, objects, external_objects, linkRuleVar.c_str(), extraFlags.c_str(), provides_requires); } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteModuleLibraryRule(std::ostream& ruleFileStream, const char* ruleFileName, const cmTarget& target, const std::vector& objects, const std::vector& external_objects, const std::vector& provides_requires) { const char* linkLanguage = target.GetLinkerLanguage(this->GetGlobalGenerator()); std::string linkRuleVar = "CMAKE_"; linkRuleVar += linkLanguage; linkRuleVar += "_CREATE_SHARED_MODULE"; std::string extraFlags; this->AppendFlags(extraFlags, target.GetProperty("LINK_FLAGS")); this->AddConfigVariableFlags(extraFlags, "CMAKE_MODULE_LINKER_FLAGS"); // TODO: .def files should be supported here also. this->WriteLibraryRule(ruleFileStream, ruleFileName, target, objects, external_objects, linkRuleVar.c_str(), extraFlags.c_str(), provides_requires); } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteLibraryRule(std::ostream& ruleFileStream, const char* ruleFileName, const cmTarget& target, const std::vector& objects, const std::vector& external_objects, const char* linkRuleVar, const char* extraFlags, const std::vector& provides_requires) { // Write the dependency generation rule. this->WriteTargetDependsRule(ruleFileStream, ruleFileName, target, objects); // TODO: Merge the methods that call this method to avoid // code duplication. std::vector commands; // Build list of dependencies. std::vector depends; for(std::vector::const_iterator obj = objects.begin(); obj != objects.end(); ++obj) { depends.push_back(*obj); } for(std::vector::const_iterator obj = external_objects.begin(); obj != external_objects.end(); ++obj) { depends.push_back(*obj); } // Add dependencies on targets that must be built first. this->AppendTargetDepends(depends, target); // Add a dependency on the rule file itself. depends.push_back(ruleFileName); // Create set of linking flags. const char* linkLanguage = target.GetLinkerLanguage(this->GetGlobalGenerator()); std::string linkFlags; this->AppendFlags(linkFlags, extraFlags); // Construct the name of the library. std::string targetName; std::string targetNameSO; std::string targetNameReal; std::string targetNameBase; this->GetLibraryNames(target.GetName(), target, targetName, targetNameSO, targetNameReal, targetNameBase); // Construct the full path version of the names. std::string outpath = m_LibraryOutputPath; if(outpath.length() == 0) { outpath = m_Makefile->GetStartOutputDirectory(); outpath += "/"; } std::string targetFullPath = outpath + targetName; std::string targetFullPathSO = outpath + targetNameSO; std::string targetFullPathReal = outpath + targetNameReal; std::string targetFullPathBase = outpath + targetNameBase; // Construct the output path version of the names for use in command // arguments. std::string targetOutPath = this->ConvertToRelativeOutputPath(targetFullPath.c_str()); std::string targetOutPathSO = this->ConvertToRelativeOutputPath(targetFullPathSO.c_str()); std::string targetOutPathReal = this->ConvertToRelativeOutputPath(targetFullPathReal.c_str()); std::string targetOutPathBase = this->ConvertToRelativeOutputPath(targetFullPathBase.c_str()); // Add a command to remove any existing files for this library. std::vector cleanFiles; cleanFiles.push_back(targetFullPathReal); if(targetOutPathSO != targetOutPathReal) { cleanFiles.push_back(targetFullPathSO); } if(targetOutPath != targetOutPathSO && targetOutPath != targetOutPathReal) { cleanFiles.push_back(targetFullPath); } this->AppendCleanCommand(commands, cleanFiles); // TODO: Pre-build and pre-link rules. // Construct the main link rule. std::string linkRule = m_Makefile->GetRequiredDefinition(linkRuleVar); cmSystemTools::ExpandListArgument(linkRule, commands); // Add a rule to create necessary symlinks for the library. if(targetOutPath != targetOutPathReal) { std::string symlink = "$(CMAKE_COMMAND) -E cmake_symlink_library "; symlink += targetOutPathReal; symlink += " "; symlink += targetOutPathSO; symlink += " "; symlink += targetOutPath; commands.push_back(symlink); } // Add the post-build rules. this->AppendCustomCommands(commands, target.GetPostBuildCommands()); // Collect up flags to link in needed libraries. cmOStringStream linklibs; this->OutputLinkLibraries(linklibs, target.GetName(), target); // Construct object file lists that may be needed to expand the // rule. std::string variableName; std::string variableNameExternal; this->WriteObjectsVariable(ruleFileStream, target, objects, external_objects, variableName, variableNameExternal); std::string buildObjs = "$("; buildObjs += variableName; buildObjs += ") $("; buildObjs += variableNameExternal; buildObjs += ")"; std::string cleanObjs = "$("; cleanObjs += variableName; cleanObjs += ")"; // Expand placeholders in the commands. for(std::vector::iterator i = commands.begin(); i != commands.end(); ++i) { // TODO: Fix target output paths to use "cd...;pwd" idiom to pass // a full path to the linker. This should be done by identifying // a relative path and stripping the directory part off to put in // this format. This rule is the only place that this idiom is // needed. this->ExpandRuleVariables(*i, linkLanguage, buildObjs.c_str(), targetOutPathReal.c_str(), linklibs.str().c_str(), 0, 0, 0, buildObjs.c_str(), targetOutPathBase.c_str(), targetNameSO.c_str(), linkFlags.c_str()); } // Write the build rule. std::string buildEcho = "Linking "; buildEcho += linkLanguage; switch(target.GetType()) { case cmTarget::STATIC_LIBRARY: buildEcho += " static library "; break; case cmTarget::SHARED_LIBRARY: buildEcho += " shared library "; break; case cmTarget::MODULE_LIBRARY: buildEcho += " shared module "; break; default: buildEcho += " library "; break; } buildEcho += targetOutPath.c_str(); buildEcho += "..."; this->WriteMakeRule(ruleFileStream, 0, buildEcho.c_str(), targetFullPath.c_str(), depends, commands); // Write convenience targets. this->WriteConvenienceRules(ruleFileStream, target, targetOutPath.c_str()); // Write clean target. cleanFiles.push_back(cleanObjs); this->WriteTargetCleanRule(ruleFileStream, target, cleanFiles); // Write the driving make target. this->WriteTargetRequiresRule(ruleFileStream, target, provides_requires); } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteObjectsVariable(std::ostream& ruleFileStream, const cmTarget& target, const std::vector& objects, const std::vector& external_objects, std::string& variableName, std::string& variableNameExternal) { // Write a make variable assignment that lists all objects for the // target. variableName = this->CreateMakeVariable(target.GetName(), "_OBJECTS"); ruleFileStream << "# Object files for target " << target.GetName() << "\n" << variableName.c_str() << " ="; for(std::vector::const_iterator i = objects.begin(); i != objects.end(); ++i) { ruleFileStream << " \\\n" << "\"" << this->ConvertToRelativeOutputPath(i->c_str()) << "\""; } ruleFileStream << "\n"; // Write a make variable assignment that lists all external objects // for the target. variableNameExternal = this->CreateMakeVariable(target.GetName(), "_EXTERNAL_OBJECTS"); ruleFileStream << "# External object files for target " << target.GetName() << "\n" << variableNameExternal.c_str() << " ="; for(std::vector::const_iterator i = external_objects.begin(); i != external_objects.end(); ++i) { ruleFileStream << " \\\n" << "\"" << this->ConvertToRelativeOutputPath(i->c_str()) << "\""; } ruleFileStream << "\n" << "\n"; } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteTargetDependsRule(std::ostream& ruleFileStream, const char* ruleFileName, const cmTarget& target, const std::vector& objects) { std::vector depends; std::vector no_commands; // Construct the output message for the rule. std::string depEcho = "Building dependencies for "; depEcho += target.GetName(); depEcho += "..."; // Construct the name of the dependency generation target. std::string depTarget = this->GetTargetDirectory(target); depTarget += "/"; depTarget += target.GetName(); depTarget += ".depends"; // This target drives dependency generation for all object files. for(std::vector::const_iterator obj = objects.begin(); obj != objects.end(); ++obj) { depends.push_back((*obj)+".depends"); } // Depend on the rule file itself. depends.push_back(ruleFileName); // Write the rule. this->WriteMakeRule(ruleFileStream, 0, depEcho.c_str(), depTarget.c_str(), depends, no_commands); // Add this to the list of depends rules in this directory. if(target.IsInAll()) { this->WriteLocalRule(ruleFileStream, "depend", depTarget.c_str()); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteTargetCleanRule(std::ostream& ruleFileStream, const cmTarget& target, const std::vector& files) { std::vector no_depends; std::vector commands; // Construct the clean target name. std::string cleanTarget = target.GetName(); cleanTarget += ".clean"; // Construct the clean command. this->AppendCleanCommand(commands, files); // Write the rule. this->WriteMakeRule(ruleFileStream, 0, 0, cleanTarget.c_str(), no_depends, commands); // Add this to the list of clean rules in this directory. if(target.IsInAll()) { this->WriteLocalRule(ruleFileStream, "clean", cleanTarget.c_str()); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteTargetRequiresRule(std::ostream& ruleFileStream, const cmTarget& target, const std::vector& provides_requires) { // Create the driving make target. std::string targetRequires = target.GetName(); targetRequires += ".requires"; std::string comment = "Directory-level driver rulue for this target."; if(provides_requires.empty()) { // No provides-requires mode objects in this target. Anything // that requires the target can build it directly. std::vector no_commands; std::vector depends; depends.push_back(target.GetName()); this->WriteMakeRule(ruleFileStream, comment.c_str(), 0, targetRequires.c_str(), depends, no_commands); } else { // There are provides-requires mode objects in this target. Use // provides-requires mode to build the target itself. std::string targetProvides = target.GetName(); targetProvides += ".provides"; { std::vector no_commands; std::vector depends; depends.push_back(target.GetName()); this->WriteMakeRule(ruleFileStream, 0, 0, targetProvides.c_str(), depends, no_commands); } { // Build list of require-level dependencies. std::vector depends; for(std::vector::const_iterator pr = provides_requires.begin(); pr != provides_requires.end(); ++pr) { depends.push_back(*pr); } // Write the requires rule for this target. std::vector commands; commands.push_back(this->GetRecursiveMakeCall(targetProvides.c_str())); this->WriteMakeRule(ruleFileStream, comment.c_str(), 0, targetRequires.c_str(), depends, commands); } } // Add this to the list of build rules in this directory. if(target.IsInAll()) { this->WriteLocalRule(ruleFileStream, "build", targetRequires.c_str()); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteLocalCleanRule(std::ostream& makefileStream) { // Collect a list of extra files to clean in this directory. std::vector files; // Look for files registered for cleaning in this directory. if(const char* additional_clean_files = m_Makefile->GetProperty("ADDITIONAL_MAKE_CLEAN_FILES")) { cmSystemTools::ExpandListArgument(additional_clean_files, files); } // Write the local clean rule for this directory. if(files.empty()) { // No extra files to clean. Write an empty rule. this->WriteLocalRule(makefileStream, "clean", 0); } else { // Have extra files to clean. Write the action to remove them. std::vector no_depends; std::vector commands; this->AppendCleanCommand(commands, files); this->WriteMakeRule(makefileStream, "Clean extra files in this directory.", 0, "clean.local", no_depends, commands); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteCMakeArgument(std::ostream& os, const char* s) { // Write the given string to the stream with escaping to get it back // into CMake through the lexical scanner. os << "\""; for(const char* c = s; *c; ++c) { if(*c == '\\') { os << "\\\\"; } else if(*c == '"') { os << "\\\""; } else { os << *c; } } os << "\""; } //---------------------------------------------------------------------------- std::string cmLocalUnixMakefileGenerator2 ::GetTargetDirectory(const cmTarget& target) { std::string dir = target.GetName(); dir += ".dir"; return dir; } //---------------------------------------------------------------------------- std::string cmLocalUnixMakefileGenerator2 ::GetSubdirTargetName(const char* pass, const char* subdir) { // Convert the subdirectory name to a valid make target name. std::string s = pass; s += "_"; s += subdir; // Replace "../" with 3 underscores. This allows one .. at the beginning. size_t pos = s.find("../"); if(pos != std::string::npos) { s.replace(pos, 3, "___"); } // Replace "/" directory separators with a single underscore. while((pos = s.find('/')) != std::string::npos) { s.replace(pos, 1, "_"); } return s; } //---------------------------------------------------------------------------- std::string cmLocalUnixMakefileGenerator2 ::GetObjectFileName(const cmTarget& target, const cmSourceFile& source) { // If the full path to the source file includes this directory, // we want to use the relative path for the filename of the // object file. Otherwise, we will use just the filename // portion. std::string objectName; if((cmSystemTools::GetFilenamePath( source.GetFullPath()).find( m_Makefile->GetCurrentDirectory()) == 0) || (cmSystemTools::GetFilenamePath( source.GetFullPath()).find( m_Makefile->GetStartOutputDirectory()) == 0)) { objectName = source.GetSourceName(); } else { objectName = cmSystemTools::GetFilenameName(source.GetSourceName()); } // Append the object file extension. objectName += m_GlobalGenerator->GetLanguageOutputExtensionFromExtension( source.GetSourceExtension().c_str()); // Convert to a safe name. objectName = this->CreateSafeUniqueObjectFileName(objectName.c_str()); // Prepend the target directory. std::string obj = this->GetTargetDirectory(target); obj += "/"; obj += objectName; return obj; } //---------------------------------------------------------------------------- std::string cmLocalUnixMakefileGenerator2 ::GetCustomBaseName(const cmCustomCommand& cc) { // If the full path to the output file includes this build // directory, we want to use the relative path for the filename of // the custom file. Otherwise, we will use just the filename // portion. std::string customName; if(cmSystemTools::FileIsFullPath(cc.GetOutput().c_str()) && (cc.GetOutput().find(m_Makefile->GetStartOutputDirectory()) == 0)) { // Use the relative path but convert it to a valid file name. customName = cmSystemTools::RelativePath(m_Makefile->GetStartOutputDirectory(), cc.GetOutput().c_str()); cmSystemTools::ReplaceString(customName, "/", "_"); } else { customName = cmSystemTools::GetFilenameName(cc.GetOutput().c_str()); } return customName; } //---------------------------------------------------------------------------- const char* cmLocalUnixMakefileGenerator2 ::GetSourceFileLanguage(const cmSourceFile& source) { // Identify the language of the source file. return (m_GlobalGenerator ->GetLanguageFromExtension(source.GetSourceExtension().c_str())); } //---------------------------------------------------------------------------- std::string cmLocalUnixMakefileGenerator2 ::ConvertToFullPath(const std::string& localPath) { std::string dir = m_Makefile->GetStartOutputDirectory(); dir += "/"; dir += localPath; return dir; } //---------------------------------------------------------------------------- std::string cmLocalUnixMakefileGenerator2::ConvertToRelativePath(const char* p) { // The path should never be quoted. assert(p[0] != '\"'); // If the path is already relative or relative paths are disabled // then just return the path. if(m_RelativePathTop.size() == 0 || !cmSystemTools::FileIsFullPath(p)) { return p; } // If the path does not begin with the minimum relative path prefix // then do not convert it. std::string original = p; if(original.size() < m_RelativePathTop.size() || !this->ComparePath(original.substr(0, m_RelativePathTop.size()).c_str(), m_RelativePathTop.c_str())) { return p; } // Identify the longest shared path component between the given path // and the current output directory. std::vector path; this->SplitFullPath(p, path); unsigned int common=0; while(common < path.size() && common < m_CurrentOutputDirectoryComponents.size() && this->ComparePath(path[common].c_str(), m_CurrentOutputDirectoryComponents[common].c_str())) { ++common; } // If the entire path is in common then just return a ".". if(common == path.size() && common == m_CurrentOutputDirectoryComponents.size()) { return "."; } // If the entire path is in common except for a trailing slash then // just return a "./". if(common+1 == path.size() && path[common].size() == 0 && common == m_CurrentOutputDirectoryComponents.size()) { return "./"; } // Construct the relative path. std::string relative; // First add enough ../ to get up to the level of the shared portion // of the path. Leave off the trailing slash. Note that the last // component of m_CurrentOutputDirectoryComponents will never be // empty because m_CurrentOutputDirectory does not have a trailing // slash. for(unsigned int i=common; i < m_CurrentOutputDirectoryComponents.size(); ++i) { relative += ".."; if(i < m_CurrentOutputDirectoryComponents.size()-1) { relative += "/"; } } // Now add the portion of the destination path that is not included // in the shared portion of the path. Add a slash the first time // only if there was already something in the path. If there was a // trailing slash in the input then the last iteration of the loop // will add a slash followed by an empty string which will preserve // the trailing slash in the output. for(unsigned int i=common; i < path.size(); ++i) { if(relative.size() > 0) { relative += "/"; } relative += path[i]; } // Finally return the path. return relative; } //---------------------------------------------------------------------------- std::string cmLocalUnixMakefileGenerator2::ConvertToRelativeOutputPath(const char* p) { // Convert the path to a relative path. std::string relative = this->ConvertToRelativePath(p); // Now convert it to an output path. return cmSystemTools::ConvertToOutputPath(relative.c_str()); } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2::ConfigureOutputPaths() { // Call superclass version first. this->cmLocalUnixMakefileGenerator::ConfigureOutputPaths(); // Setup fully collapsed paths. m_CurrentOutputDirectory = cmSystemTools::CollapseFullPath(m_Makefile->GetCurrentOutputDirectory()); m_HomeOutputDirectory = cmSystemTools::CollapseFullPath(m_Makefile->GetHomeOutputDirectory()); m_HomeDirectory = cmSystemTools::CollapseFullPath(m_Makefile->GetHomeDirectory()); // Identify the longest shared path component between the source // directory and the build directory. std::vector source; std::vector binary; this->SplitFullPath(m_HomeDirectory.c_str(), source); this->SplitFullPath(m_HomeOutputDirectory.c_str(), binary); unsigned int common=0; while(common < source.size() && common < binary.size() && this->ComparePath(source[common].c_str(), binary[common].c_str())) { ++common; } // Require more than just the root portion of the path to be in // common before allowing relative paths. Also disallow relative // paths if the build tree is a network path. The current working // directory on Windows cannot be a network path. Therefore // relative paths cannot work with network paths. if(common > 1 && source[0] != "//") { // Build the minimum prefix required of a path to be converted to // a relative path. m_RelativePathTop = source[0]; for(unsigned int i=1; i < common; ++i) { if(i > 1) { m_RelativePathTop += "/"; } m_RelativePathTop += source[i]; } // Split the current output directory now to save time when // converting paths. this->SplitFullPath(m_CurrentOutputDirectory.c_str(), m_CurrentOutputDirectoryComponents); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::SplitFullPath(const char* p, std::vector& components) { // The path is split into its basic components. This starts with a // root ("/" for UNIX, "c:/" for Windows, "//" for Network) and is // followed by the directory names. If there is a trailing slash // then the last component is the empty string. The components can // be recombined as "c[0]c[1]/c[2]/.../c[n]". assert(cmSystemTools::FileIsFullPath(p)); // Identify the root component. const char* c = p; if(c[0] == '/' && c[1] == '/') { // Network path. components.push_back("//"); c += 2; } else if(c[0] == '/') { // Unix path. components.push_back("/"); c += 1; } else if(c[0] && c[1] == ':' && c[2] == '/') { // Windows path. std::string root = "_:/"; root[0] = c[0]; components.push_back(root); c += 3; } else { // Already a relative path. cmSystemTools::Error("SplitFullPath called with path ", p); return; } // Parse the remaining components. const char* first = c; const char* last = first; for(;*last; ++last) { if(*last == '/') { // End of a component. Save it. components.push_back(std::string(first, last-first)); first = last+1; } } // Save the last component unless there were no components. if(last != c) { components.push_back(std::string(first, last-first)); } } //---------------------------------------------------------------------------- bool cmLocalUnixMakefileGenerator2::ComparePath(const char* c1, const char* c2) { #if defined(_WIN32) || defined(__APPLE__) return cmSystemTools::Strucmp(c1, c2) == 0; #else return strcmp(c1, c2) == 0; #endif } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::AppendTargetDepends(std::vector& depends, const cmTarget& target) { // Do not bother with dependencies for static libraries. if(target.GetType() == cmTarget::STATIC_LIBRARY) { return; } // Keep track of dependencies already listed. std::set emitted; // A target should not depend on itself. emitted.insert(target.GetName()); // Loop over all library dependencies. const cmTarget::LinkLibraries& tlibs = target.GetLinkLibraries(); for(cmTarget::LinkLibraries::const_iterator lib = tlibs.begin(); lib != tlibs.end(); ++lib) { // Don't emit the same library twice for this target. if(emitted.insert(lib->first).second) { // Add this dependency. this->AppendAnyDepend(depends, lib->first.c_str()); } } // Loop over all utility dependencies. const std::set& tutils = target.GetUtilities(); for(std::set::const_iterator util = tutils.begin(); util != tutils.end(); ++util) { // Don't emit the same utility twice for this target. if(emitted.insert(*util).second) { // Add this dependency. this->AppendAnyDepend(depends, util->c_str()); } } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::AppendAnyDepend(std::vector& depends, const char* name) { // There are a few cases for the name of the target: // - CMake target in this directory: depend on it. // - CMake target in another directory: depend and add jump-and-build. // - Full path to a file: depend on it. // - Other format (like -lm): do nothing. // If it is an executable or library target there will be a // definition for it. std::string dirVar = name; dirVar += "_CMAKE_PATH"; const char* dir = m_Makefile->GetDefinition(dirVar.c_str()); if(dir && *dir) { // This is a CMake target somewhere in this project. bool jumpAndBuild = false; // Get the type of the library. If it does not have a type then // it is an executable. std::string typeVar = name; typeVar += "_LIBRARY_TYPE"; const char* libType = m_Makefile->GetDefinition(typeVar.c_str()); // Get the output path for this target type. std::string tgtOutputPath; if(libType) { tgtOutputPath = m_LibraryOutputPath; } else { tgtOutputPath = m_ExecutableOutputPath; } // Get the path to the target. std::string tgtPath; if(this->SamePath(m_Makefile->GetStartOutputDirectory(), dir)) { // The target is in the current directory so this makefile will // know about it already. tgtPath = tgtOutputPath; } else { // The target is in another directory. Get the path to it. if(tgtOutputPath.size()) { tgtPath = tgtOutputPath; } else { tgtPath = dir; tgtPath += "/"; } // We need to add a jump-and-build rule for this library. jumpAndBuild = true; } // Add the name of the targets's file. This depends on the type // of the target. std::string prefix; std::string suffix; if(!libType) { suffix = cmSystemTools::GetExecutableExtension(); } else if(strcmp(libType, "SHARED") == 0) { prefix = m_Makefile->GetSafeDefinition("CMAKE_SHARED_LIBRARY_PREFIX"); suffix = m_Makefile->GetSafeDefinition("CMAKE_SHARED_LIBRARY_SUFFIX"); } else if(strcmp(libType, "MODULE") == 0) { prefix = m_Makefile->GetSafeDefinition("CMAKE_SHARED_MODULE_PREFIX"); suffix = m_Makefile->GetSafeDefinition("CMAKE_SHARED_MODULE_SUFFIX"); } else if(strcmp(libType, "STATIC") == 0) { prefix = m_Makefile->GetSafeDefinition("CMAKE_STATIC_LIBRARY_PREFIX"); suffix = m_Makefile->GetSafeDefinition("CMAKE_STATIC_LIBRARY_SUFFIX"); } tgtPath += prefix; tgtPath += name; tgtPath += suffix; if(jumpAndBuild) { // We need to add a jump-and-build rule for this target. cmLocalUnixMakefileGenerator2::RemoteTarget rt; rt.m_BuildDirectory = dir; rt.m_FilePath = tgtPath; m_JumpAndBuild[name] = rt; } // Add a dependency on the target. depends.push_back(tgtPath.c_str()); } else if(m_Makefile->GetTargets().find(name) != m_Makefile->GetTargets().end()) { // This is a CMake target that is not an executable or library. // It must be in this directory, so just depend on the name // directly. depends.push_back(name); } else if(cmSystemTools::FileIsFullPath(name)) { // This is a path to a file. Just trust that it will be present. depends.push_back(name); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::AppendCustomDepends(std::vector& depends, const std::vector& ccs) { for(std::vector::const_iterator i = ccs.begin(); i != ccs.end(); ++i) { this->AppendCustomDepend(depends, *i); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::AppendCustomDepend(std::vector& depends, const cmCustomCommand& cc) { for(std::vector::const_iterator d = cc.GetDepends().begin(); d != cc.GetDepends().end(); ++d) { // Add this dependency. this->AppendAnyDepend(depends, d->c_str()); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::AppendCustomCommands(std::vector& commands, const std::vector& ccs) { for(std::vector::const_iterator i = ccs.begin(); i != ccs.end(); ++i) { this->AppendCustomCommand(commands, *i); } } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::AppendCustomCommand(std::vector& commands, const cmCustomCommand& cc) { // TODO: Convert outputs/dependencies (arguments?) to relative paths. // Build the command line in a single string. std::string cmd = cc.GetCommand(); cmSystemTools::ReplaceString(cmd, "/./", "/"); cmd = this->ConvertToRelativeOutputPath(cmd.c_str()); if(cc.GetArguments().size() > 0) { cmd += " "; cmd += cc.GetArguments(); } commands.push_back(cmd); } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::AppendCleanCommand(std::vector& commands, const std::vector& files) { if(!files.empty()) { std::string remove = "$(CMAKE_COMMAND) -E remove -f"; for(std::vector::const_iterator f = files.begin(); f != files.end(); ++f) { remove += " "; remove += this->ConvertToRelativeOutputPath(f->c_str()); } commands.push_back(remove); } } //---------------------------------------------------------------------------- std::string cmLocalUnixMakefileGenerator2 ::GetRecursiveMakeCall(const char* tgt) { // Call make on the given file. std::string cmd; cmd += "$(MAKE) -f " CMLUMG_MAKEFILE_NAME " "; // Pass down verbosity level. if(m_MakeSilentFlag.size()) { cmd += m_MakeSilentFlag; 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(m_PassMakeflags) { cmd += "-$(MAKEFLAGS) "; } // Add the target. cmd += tgt; return cmd; } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2 ::WriteJumpAndBuildRules(std::ostream& makefileStream) { // Write the header for this section. if(!m_JumpAndBuild.empty()) { this->WriteDivider(makefileStream); makefileStream << "# Targets to make sure needed libraries exist.\n" << "# These will jump to other directories to build targets.\n" << "\n"; } std::vector depends; std::vector commands; for(std::map::iterator jump = m_JumpAndBuild.begin(); jump != m_JumpAndBuild.end(); ++jump) { const cmLocalUnixMakefileGenerator2::RemoteTarget& rt = jump->second; const char* destination = rt.m_BuildDirectory.c_str(); // Construct the dependency and build target names. std::string dep = jump->first; dep += ".dir/"; dep += jump->first; dep += ".depends"; dep = this->ConvertToRelativeOutputPath(dep.c_str()); std::string tgt = jump->first; tgt += ".requires"; tgt = this->ConvertToRelativeOutputPath(tgt.c_str()); // Build the jump-and-build command list. commands.clear(); if(m_WindowsShell) { // On Windows we must perform each step separately and then jump // back because the shell keeps the working directory between // commands. std::string cmd = "cd "; cmd += this->ConvertToOutputForExisting(destination); commands.push_back(cmd); // Check the build system in destination directory. commands.push_back(this->GetRecursiveMakeCall("cmake_check_build_system")); // Build the targets's dependencies. commands.push_back(this->GetRecursiveMakeCall(dep.c_str())); // Build the target. commands.push_back(this->GetRecursiveMakeCall(tgt.c_str())); // Jump back to the starting directory. cmd = "cd "; cmd += this->ConvertToOutputForExisting(m_Makefile->GetStartOutputDirectory()); commands.push_back(cmd); } else { // On UNIX we must construct a single shell command to jump and // build because make resets the directory between each command. std::string cmd = "cd "; cmd += this->ConvertToOutputForExisting(destination); // Check the build system in destination directory. cmd += " && "; cmd += this->GetRecursiveMakeCall("cmake_check_build_system"); // Build the targets's dependencies. cmd += " && "; cmd += this->GetRecursiveMakeCall(dep.c_str()); // Build the target. cmd += " && "; cmd += this->GetRecursiveMakeCall(tgt.c_str()); // Add the command as a single line. commands.push_back(cmd); } // Write the rule. std::string jumpPreEcho = "Jumping to "; jumpPreEcho += rt.m_BuildDirectory.c_str(); jumpPreEcho += " to build "; jumpPreEcho += jump->first; jumpPreEcho += "..."; std::string jumpPostEcho = "Returning to "; jumpPostEcho += m_Makefile->GetStartOutputDirectory(); jumpPostEcho += "..."; this->WriteMakeRule(makefileStream, 0, jumpPreEcho.c_str(), rt.m_FilePath.c_str(), depends, commands, jumpPostEcho.c_str()); } } //---------------------------------------------------------------------------- cmDepends* cmLocalUnixMakefileGenerator2::GetDependsChecker(const std::string& lang, const char* dir, const char* objFile) { if(lang == "C" || lang == "CXX" || lang == "RC") { return new cmDependsC(dir, objFile); } #ifdef CMAKE_BUILD_WITH_CMAKE else if(lang == "Fortran") { return new cmDependsFortran(dir, objFile); } #endif return 0; } //---------------------------------------------------------------------------- bool cmLocalUnixMakefileGenerator2 ::ScanDependencies(std::vector const& args) { // Format of arguments is: // $(CMAKE_COMMAND), cmake_depends, , , // The caller has ensured that all required arguments exist. // The language for which we are scanning dependencies. std::string const& lang = args[2]; // The file to which to write dependencies. const char* objFile = args[3].c_str(); // The source file at which to start the scan. const char* srcFile = args[4].c_str(); // Read the directory information file. cmake cm; cmGlobalGenerator gg; gg.SetCMakeInstance(&cm); std::auto_ptr lg(gg.CreateLocalGenerator()); lg->SetGlobalGenerator(&gg); cmMakefile* mf = lg->GetMakefile(); bool haveDirectoryInfo = false; if(mf->ReadListFile(0, "CMakeDirectoryInformation.cmake") && !cmSystemTools::GetErrorOccuredFlag()) { haveDirectoryInfo = true; } // Get the set of include directories. std::vector includes; if(haveDirectoryInfo) { std::string includePathVar = "CMAKE_"; includePathVar += lang; includePathVar += "_INCLUDE_PATH"; if(const char* includePath = mf->GetDefinition(includePathVar.c_str())) { cmSystemTools::ExpandListArgument(includePath, includes); } } // Get the include file regular expression. std::string includeRegexScan = "^.*$"; std::string includeRegexComplain = "^$"; if(haveDirectoryInfo) { std::string scanRegexVar = "CMAKE_"; scanRegexVar += lang; scanRegexVar += "_INCLUDE_REGEX_SCAN"; if(const char* scanRegex = mf->GetDefinition(scanRegexVar.c_str())) { includeRegexScan = scanRegex; } std::string complainRegexVar = "CMAKE_"; complainRegexVar += lang; complainRegexVar += "_INCLUDE_REGEX_COMPLAIN"; if(const char* complainRegex = mf->GetDefinition(complainRegexVar.c_str())) { includeRegexComplain = complainRegex; } } // Dispatch the scan for each language. if(lang == "C" || lang == "CXX" || lang == "RC") { // TODO: Handle RC (resource files) dependencies correctly. cmDependsC scanner(".", objFile, srcFile, includes, includeRegexScan.c_str(), includeRegexComplain.c_str()); return scanner.Write(); } #ifdef CMAKE_BUILD_WITH_CMAKE else if(lang == "Fortran") { cmDependsFortran scanner(".", objFile, srcFile, includes); return scanner.Write(); } #endif return false; } //---------------------------------------------------------------------------- void cmLocalUnixMakefileGenerator2::CheckDependencies(cmMakefile* mf) { // Get the list of languages that may have sources to check. const char* langDef = mf->GetDefinition("CMAKE_DEPENDS_LANGUAGES"); if(!langDef) { return; } std::vector languages; cmSystemTools::ExpandListArgument(langDef, languages); // For each language get the set of files to check. for(std::vector::iterator l = languages.begin(); l != languages.end(); ++l) { std::string depCheck = "CMAKE_DEPENDS_CHECK_"; depCheck += *l; if(const char* fileDef = mf->GetDefinition(depCheck.c_str())) { // Check each file. The current working directory is already // correct. std::vector files; cmSystemTools::ExpandListArgument(fileDef, files); for(std::vector::iterator f = files.begin(); f != files.end(); ++f) { // Construct a checker for the given language. std::auto_ptr checker(cmLocalUnixMakefileGenerator2 ::GetDependsChecker(*l, ".", f->c_str())); if(checker.get()) { checker->Check(); } } } } }