/*========================================================================= 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 "cmMakefileTargetGenerator.h" #include "cmGeneratedFileStream.h" #include "cmGlobalGenerator.h" #include "cmLocalUnixMakefileGenerator3.h" #include "cmMakefile.h" #include "cmSourceFile.h" #include "cmTarget.h" #include "cmMakefileExecutableTargetGenerator.h" #include "cmMakefileLibraryTargetGenerator.h" #include "cmMakefileUtilityTargetGenerator.h" cmMakefileTargetGenerator::cmMakefileTargetGenerator() { this->BuildFileStream = 0; this->InfoFileStream = 0; this->FlagFileStream = 0; } cmMakefileTargetGenerator * cmMakefileTargetGenerator::New(cmLocalUnixMakefileGenerator3 *lg, cmStdString tgtName, cmTarget *tgt) { cmMakefileTargetGenerator *result = 0; switch (tgt->GetType()) { case cmTarget::EXECUTABLE: result = new cmMakefileExecutableTargetGenerator; break; case cmTarget::STATIC_LIBRARY: case cmTarget::SHARED_LIBRARY: case cmTarget::MODULE_LIBRARY: result = new cmMakefileLibraryTargetGenerator; break; case cmTarget::UTILITY: result = new cmMakefileUtilityTargetGenerator; break; default: return result; break; } result->TargetName = tgtName; result->Target = tgt; result->LocalGenerator = lg; result->GlobalGenerator = lg->GetGlobalGenerator(); result->Makefile = lg->GetMakefile(); return result; } //---------------------------------------------------------------------------- void cmMakefileTargetGenerator::CreateRuleFile() { // Create a directory for this target. this->TargetBuildDirectory = this->LocalGenerator->GetTargetDirectory(*this->Target); this->TargetBuildDirectoryFull = this->LocalGenerator->ConvertToFullPath(this->TargetBuildDirectory); cmSystemTools::MakeDirectory(this->TargetBuildDirectoryFull.c_str()); // Construct the rule file name. this->BuildFileName = this->TargetBuildDirectory; this->BuildFileName += "/build.make"; this->BuildFileNameFull = this->TargetBuildDirectoryFull; this->BuildFileNameFull += "/build.make"; // Open the rule file. This should be copy-if-different because the // rules may depend on this file itself. this->BuildFileStream = new cmGeneratedFileStream(this->BuildFileNameFull.c_str()); this->BuildFileStream->SetCopyIfDifferent(true); if(!this->BuildFileStream) { return; } this->LocalGenerator->WriteDisclaimer(*this->BuildFileStream); this->LocalGenerator->WriteSpecialTargetsTop(*this->BuildFileStream); this->LocalGenerator->WriteMakeVariables(*this->BuildFileStream); } //---------------------------------------------------------------------------- void cmMakefileTargetGenerator::WriteCustomCommandsForTarget() { // write the custom commands for this target // Look for files registered for cleaning in this directory. if(const char* additional_clean_files = this->Makefile->GetProperty ("ADDITIONAL_MAKE_CLEAN_FILES")) { cmSystemTools::ExpandListArgument(additional_clean_files, this->CleanFiles); } this->WriteCustomCommands(); } //---------------------------------------------------------------------------- void cmMakefileTargetGenerator::WriteCommonCodeRules() { // Include the dependencies for the target. std::string dependFileNameFull = this->TargetBuildDirectoryFull; dependFileNameFull += "/depend.make"; *this->BuildFileStream << "# Include any dependencies generated for this target.\n" << this->LocalGenerator->m_IncludeDirective << " " << this->Convert(dependFileNameFull.c_str(), cmLocalGenerator::HOME_OUTPUT, cmLocalGenerator::MAKEFILE) << "\n\n"; // make sure the depend file exists if (!cmSystemTools::FileExists(dependFileNameFull.c_str())) { // Write an empty dependency file. cmGeneratedFileStream depFileStream(dependFileNameFull.c_str()); depFileStream << "# Empty dependencies file for " << this->Target->GetName() << ".\n" << "# This may be replaced when dependencies are built." << std::endl; } // Open the flags file. This should be copy-if-different because the // rules may depend on this file itself. this->FlagFileNameFull = this->TargetBuildDirectoryFull; this->FlagFileNameFull += "/flags.make"; this->FlagFileStream = new cmGeneratedFileStream(this->FlagFileNameFull.c_str()); this->FlagFileStream->SetCopyIfDifferent(true); if(!this->FlagFileStream) { return; } this->LocalGenerator->WriteDisclaimer(*this->FlagFileStream); // Include the flags for the target. *this->BuildFileStream << "# Include the compile flags for this target's objects.\n" << this->LocalGenerator->m_IncludeDirective << " " << this->Convert(this->FlagFileNameFull.c_str(), cmLocalGenerator::HOME_OUTPUT, cmLocalGenerator::MAKEFILE) << "\n\n"; // First generate the object rule files. Save a list of all object // files for this target. const std::vector& sources = this->Target->GetSourceFiles(); for(std::vector::const_iterator source = sources.begin(); source != sources.end(); ++source) { if(!(*source)->GetPropertyAsBool("HEADER_FILE_ONLY") && !(*source)->GetCustomCommand()) { if(!this->GlobalGenerator->IgnoreFile ((*source)->GetSourceExtension().c_str())) { // Generate this object file's rule file. this->WriteObjectRuleFiles(*(*source)); } else if((*source)->GetPropertyAsBool("EXTERNAL_OBJECT")) { // This is an external object file. Just add it. this->ExternalObjects.push_back((*source)->GetFullPath()); } else { // We only get here if a source file is not an external object // and has an extension that is listed as an ignored file type // for this language. No message or diagnosis should be // given. } } } // write language flags for target std::map& checkSet = this->LocalGenerator->GetIntegrityCheckSet()[this->Target->GetName()]; for(std::map::const_iterator l = checkSet.begin(); l != checkSet.end(); ++l) { const char *lang = l->first.c_str(); std::string flags; // Add the export symbol definition for shared library objects. bool shared = ((this->Target->GetType() == cmTarget::SHARED_LIBRARY) || (this->Target->GetType() == cmTarget::MODULE_LIBRARY)); if(shared) { flags += "-D"; if(const char* custom_export_name = this->Target->GetProperty("DEFINE_SYMBOL")) { flags += custom_export_name; } else { std::string in = this->Target->GetName(); in += "_EXPORTS"; flags += cmSystemTools::MakeCindentifier(in.c_str()); } } // Add language-specific flags. this->LocalGenerator->AddLanguageFlags(flags, lang); // Add shared-library flags if needed. this->LocalGenerator->AddSharedFlags(flags, lang, shared); // Add include directory flags. this->LocalGenerator-> AppendFlags(flags, this->LocalGenerator->GetIncludeFlags(lang)); // Add include directory flags. this->LocalGenerator-> AppendFlags(flags,this->GetFrameworkFlags().c_str()); *this->FlagFileStream << lang << "_FLAGS = " << flags << "\n\n"; } } //---------------------------------------------------------------------------- void cmMakefileTargetGenerator::WriteObjectRuleFiles(cmSourceFile& source) { // Identify the language of the source file. const char* lang = this->LocalGenerator->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. std::string objNoTargetDir; std::string obj = this->LocalGenerator->GetObjectFileName(*this->Target, source, &objNoTargetDir); // Avoid generating duplicate rules. if(m_ObjectFiles.find(obj) == m_ObjectFiles.end()) { m_ObjectFiles.insert(obj); } else { cmOStringStream err; err << "Warning: Source file \"" << source.GetSourceName().c_str() << "." << source.GetSourceExtension().c_str() << "\" is listed multiple times for target \"" << this->Target->GetName() << "\"."; cmSystemTools::Message(err.str().c_str(), "Warning"); return; } // 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->LocalGenerator->ConvertToFullPath(dir).c_str()); // Save this in the target's list of object files. this->Objects.push_back(obj); std::string relativeObj = this->LocalGenerator->GetHomeRelativeOutputPath(); relativeObj += obj; // we compute some depends when writing the depend.make that we will also // use in the build.make, same with depMakeFile std::vector depends; std::string depMakeFile; // generate the build rule file this->WriteObjectBuildFile(obj, lang, source, depends); // The object file should be checked for dependency integrity. this->LocalGenerator->m_CheckDependFiles[this->Target->GetName()][lang].insert(&source); // add this to the list of objects for this local generator if(cmSystemTools::FileIsFullPath(objNoTargetDir.c_str())) { objNoTargetDir = cmSystemTools::GetFilenameName(objNoTargetDir); } this->LocalGenerator->m_LocalObjectFiles[objNoTargetDir].push_back(this->Target); } //---------------------------------------------------------------------------- void cmMakefileTargetGenerator ::WriteObjectBuildFile(std::string &obj, const char *lang, cmSourceFile& source, std::vector& depends) { this->LocalGenerator->AppendRuleDepend(depends, this->FlagFileNameFull.c_str()); // generate the depend scanning rule this->WriteObjectDependRules(source, depends); std::string relativeObj = this->LocalGenerator->GetHomeRelativeOutputPath(); relativeObj += obj; if(this->Makefile->GetDefinition("CMAKE_WINDOWS_OBJECT_PATH")) { relativeObj = cmSystemTools::ConvertToOutputPath(relativeObj.c_str()); } // Write the build rule. // Build the set of compiler flags. std::string flags; if(this->Target->GetProperty("COMPILE_FLAGS")) { this->LocalGenerator->AppendFlags(flags, this->Target->GetProperty("COMPILE_FLAGS")); } // Add flags from source file properties. if (source.GetProperty("COMPILE_FLAGS")) { this->LocalGenerator->AppendFlags(flags, source.GetProperty("COMPILE_FLAGS")); *this->FlagFileStream << "# Custom flags: " << relativeObj << "_FLAGS = " << source.GetProperty("COMPILE_FLAGS") << "\n" << "\n"; } // Add language-specific flags. std::string langFlags = "$("; langFlags += lang; langFlags += "_FLAGS)"; this->LocalGenerator->AppendFlags(flags, langFlags.c_str()); // Get the output paths for source and object files. std::string sourceFile = source.GetFullPath(); if(this->LocalGenerator->m_UseRelativePaths) { sourceFile = this->Convert(sourceFile.c_str(), cmLocalGenerator::HOME_OUTPUT); } sourceFile = this->Convert(sourceFile.c_str(), cmLocalGenerator::NONE, cmLocalGenerator::SHELL); std::string objectFile = this->Convert(obj.c_str(), cmLocalGenerator::START_OUTPUT, cmLocalGenerator::SHELL); // Construct the build message. std::vector no_commands; std::vector commands; std::string buildEcho = "Building "; buildEcho += lang; buildEcho += " object "; buildEcho += relativeObj; this->LocalGenerator->AppendEcho(commands, buildEcho.c_str()); // Construct the compile rules. std::string compileRuleVar = "CMAKE_"; compileRuleVar += lang; compileRuleVar += "_COMPILE_OBJECT"; std::string compileRule = this->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->LocalGenerator->ExpandRuleVariables(*i, lang, 0, // no objects 0, // no target 0, // no link libs sourceFile.c_str(), relativeObj.c_str(), flags.c_str()); } // Make the target dependency scanning rule include cmake-time-known // dependencies. The others are handled by the check-build-system // path. std::string depMark = this->LocalGenerator->GetRelativeTargetDirectory(*this->Target); depMark += "/depend.make.mark"; this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0, depMark.c_str(), depends, no_commands, false); // Write the rule. this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0, relativeObj.c_str(), depends, commands, false); // If the language needs provides-requires mode, create the // corresponding targets. std::string objectRequires = relativeObj; objectRequires += ".requires"; std::vector p_depends; // always provide an empty requires target this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0, objectRequires.c_str(), p_depends, no_commands, true); // write a build rule to recursively build what this obj provides std::string objectProvides = relativeObj; objectProvides += ".provides"; std::string temp = relativeObj; temp += ".provides.build"; std::vector r_commands; std::string tgtMakefileName = this->LocalGenerator->GetRelativeTargetDirectory(*this->Target); tgtMakefileName += "/build.make"; r_commands.push_back (this->LocalGenerator->GetRecursiveMakeCall(tgtMakefileName.c_str(),temp.c_str())); p_depends.clear(); p_depends.push_back(objectRequires); this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0, objectProvides.c_str(), p_depends, r_commands, true); // write the provides.build rule dependency on the obj file p_depends.clear(); p_depends.push_back(relativeObj); this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0, temp.c_str(), p_depends, no_commands, true); } //---------------------------------------------------------------------------- void cmMakefileTargetGenerator::WriteTargetRequiresRules() { std::vector depends; std::vector no_commands; // Construct the name of the dependency generation target. std::string depTarget = this->LocalGenerator->GetRelativeTargetDirectory(*this->Target); depTarget += "/requires"; // This target drives dependency generation for all object files. std::string relPath = this->LocalGenerator->GetHomeRelativeOutputPath(); std::string objTarget; for(std::vector::const_iterator obj = this->Objects.begin(); obj != this->Objects.end(); ++obj) { objTarget = relPath; objTarget += *obj; objTarget += ".requires"; depends.push_back(objTarget); } // Write the rule. this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0, depTarget.c_str(), depends, no_commands, true); } //---------------------------------------------------------------------------- void cmMakefileTargetGenerator::WriteTargetCleanRules() { std::vector depends; std::vector commands; // Construct the clean target name. std::string cleanTarget = this->LocalGenerator->GetRelativeTargetDirectory(*this->Target); cleanTarget += "/clean"; // Construct the clean command. this->LocalGenerator->AppendCleanCommand(commands, this->CleanFiles); this->LocalGenerator->CreateCDCommand(commands, this->Makefile->GetStartOutputDirectory(), this->Makefile->GetHomeOutputDirectory()); // Write the rule. this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0, cleanTarget.c_str(), depends, commands, true); } //---------------------------------------------------------------------------- void cmMakefileTargetGenerator::WriteTargetDependRules() { // must write the targets depend info file std::string dir = this->LocalGenerator->GetTargetDirectory(*this->Target); this->InfoFileNameFull = dir; this->InfoFileNameFull += "/DependInfo.cmake"; this->InfoFileNameFull = this->LocalGenerator->ConvertToFullPath(this->InfoFileNameFull); this->InfoFileStream = new cmGeneratedFileStream(this->InfoFileNameFull.c_str()); this->InfoFileStream->SetCopyIfDifferent(true); if(!*this->InfoFileStream) { return; } this->LocalGenerator-> WriteDependLanguageInfo(*this->InfoFileStream,*this->Target); // and now write the rule to use it std::vector depends; std::vector commands; // Construct the name of the dependency generation target. std::string depTarget = this->LocalGenerator->GetRelativeTargetDirectory(*this->Target); depTarget += "/depend"; std::string depMark = depTarget; depMark += ".make.mark"; depends.push_back(depMark); this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0, depTarget.c_str(), depends, commands, true); depends.clear(); // Write the dependency generation rule. std::string depEcho = "Scanning dependencies of target "; depEcho += this->Target->GetName(); this->LocalGenerator->AppendEcho(commands, depEcho.c_str()); // 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 " << " \"" << this->GlobalGenerator->GetName() << "\" " << this->LocalGenerator->Convert (this->Makefile->GetHomeOutputDirectory(), cmLocalGenerator::FULL,cmLocalGenerator::SHELL) << " " << this->LocalGenerator->Convert (this->Makefile->GetStartOutputDirectory(), cmLocalGenerator::FULL,cmLocalGenerator::SHELL) << " " << this->Convert(this->InfoFileNameFull.c_str(), cmLocalGenerator::FULL, cmLocalGenerator::SHELL); commands.push_back(depCmd.str()); // Write the rule. this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, 0, depMark.c_str(), depends, commands, false); } //---------------------------------------------------------------------------- void cmMakefileTargetGenerator ::WriteObjectDependRules(cmSourceFile& source, std::vector& depends) { // Create the list of dependencies known at cmake time. These are // shared between the object file and dependency scanning rule. 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()); } } } void cmMakefileTargetGenerator::WriteCustomCommands() { // add custom commands to the clean rules? const char* clean_no_custom = this->Makefile->GetProperty("CLEAN_NO_CUSTOM"); bool clean = cmSystemTools::IsOff(clean_no_custom); // Generate the rule files for each custom command. const std::vector &classes = this->Makefile->GetSourceFiles(); for(std::vector::const_iterator i = classes.begin(); i != classes.end(); i++) { if(cmCustomCommand* cc = (*i)->GetCustomCommand()) { this->GenerateCustomRuleFile(*cc); if (clean) { this->CleanFiles.push_back (this->Convert(cc->GetOutput(), cmLocalGenerator::START_OUTPUT, cmLocalGenerator::SHELL)); } } } } //---------------------------------------------------------------------------- void cmMakefileTargetGenerator ::GenerateCustomRuleFile(const cmCustomCommand& cc) { // Convert the output name to a relative path if possible. std::string output = this->Convert(cc.GetOutput(), cmLocalGenerator::START_OUTPUT); // Collect the commands. std::vector commands; std::string preEcho = "Generating "; preEcho += output; this->LocalGenerator->AppendEcho(commands, preEcho.c_str()); this->LocalGenerator->AppendCustomCommand(commands, cc); // Collect the dependencies. std::vector depends; this->LocalGenerator->AppendCustomDepend(depends, cc); // Write the rule. const char* comment = 0; if(cc.GetComment() && *cc.GetComment()) { comment = cc.GetComment(); } this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, comment, cc.GetOutput(), depends, commands, false); } //---------------------------------------------------------------------------- void cmMakefileTargetGenerator ::WriteObjectsVariable(std::string& variableName, std::string& variableNameExternal) { // Write a make variable assignment that lists all objects for the // target. variableName = this->LocalGenerator->CreateMakeVariable(this->Target->GetName(), "_OBJECTS"); *this->BuildFileStream << "# Object files for target " << this->Target->GetName() << "\n" << variableName.c_str() << " ="; std::string object; const char* objName = this->Makefile->GetDefinition("CMAKE_NO_QUOTED_OBJECTS"); const char* lineContinue = this->Makefile->GetDefinition("CMAKE_MAKE_LINE_CONTINUE"); if(!lineContinue) { lineContinue = "\\"; } for(std::vector::const_iterator i = this->Objects.begin(); i != this->Objects.end(); ++i) { *this->BuildFileStream << " " << lineContinue << "\n"; if(objName) { *this->BuildFileStream << this->Convert(i->c_str(), cmLocalGenerator::START_OUTPUT, cmLocalGenerator::MAKEFILE); } else { *this->BuildFileStream << this->LocalGenerator->ConvertToQuotedOutputPath(i->c_str()); } } *this->BuildFileStream << "\n"; // Write a make variable assignment that lists all external objects // for the target. variableNameExternal = this->LocalGenerator->CreateMakeVariable(this->Target->GetName(),"_EXTERNAL_OBJECTS"); *this->BuildFileStream << "\n" << "# External object files for target " << this->Target->GetName() << "\n" << variableNameExternal.c_str() << " ="; for(std::vector::const_iterator i = this->ExternalObjects.begin(); i != this->ExternalObjects.end(); ++i) { object = this->Convert(i->c_str(),cmLocalGenerator::START_OUTPUT); *this->BuildFileStream << " " << lineContinue << "\n" << this->Makefile->GetSafeDefinition("CMAKE_OBJECT_NAME"); if(objName) { *this->BuildFileStream << this->Convert(i->c_str(), cmLocalGenerator::START_OUTPUT, cmLocalGenerator::MAKEFILE); } else { *this->BuildFileStream << this->LocalGenerator->ConvertToQuotedOutputPath(i->c_str()); } } *this->BuildFileStream << "\n" << "\n"; } //---------------------------------------------------------------------------- std::string cmMakefileTargetGenerator::GetFrameworkFlags() { #ifndef __APPLE__ return std::string(); #else std::set emitted; std::vector includes; this->LocalGenerator->GetIncludeDirectories(includes); std::vector::iterator i; // check all include directories for frameworks as this // will already have added a -F for the framework for(i = includes.begin(); i != includes.end(); ++i) { if(cmSystemTools::IsPathToFramework(i->c_str())) { std::string frameworkDir = *i; frameworkDir += "/../"; frameworkDir = cmSystemTools::CollapseFullPath(frameworkDir.c_str()); emitted.insert(frameworkDir); } } std::string flags; std::vector& frameworks = this->Target->GetFrameworks(); for(i = frameworks.begin(); i != frameworks.end(); ++i) { if(emitted.insert(*i).second) { flags += "-F"; flags += this->LocalGenerator->ConvertToOutputForExisting(i->c_str()); flags += " "; } } return flags; #endif } //---------------------------------------------------------------------------- void cmMakefileTargetGenerator ::AppendTargetDepends(std::vector& depends) { // Static libraries never depend on anything for linking. if(this->Target->GetType() == cmTarget::STATIC_LIBRARY) { return; } // Keep track of dependencies already listed. std::set emitted; // A target should not depend on itself. emitted.insert(this->Target->GetName()); // Loop over all library dependencies. const cmTarget::LinkLibraries& tlibs = this->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) { // Depend only on other CMake targets. if(cmTarget* tgt = this->GlobalGenerator->FindTarget(0, lib->first.c_str())) { if(const char* location = tgt->GetLocation(this->LocalGenerator->m_ConfigurationName.c_str())) { depends.push_back(location); } } } } } //---------------------------------------------------------------------------- void cmMakefileTargetGenerator ::CloseFileStreams() { delete this->BuildFileStream; delete this->InfoFileStream; delete this->FlagFileStream; }