diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 63ecda36c..960822494 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -121,6 +121,12 @@ SET(SRCS cmExprLexer.cxx cmExprParser.cxx cmExprParserHelper.cxx + cmExportBuildFileGenerator.h + cmExportBuildFileGenerator.cxx + cmExportFileGenerator.h + cmExportFileGenerator.cxx + cmExportInstallFileGenerator.h + cmExportInstallFileGenerator.cxx cmExtraEclipseCDT4Generator.cxx cmExtraEclipseCDT4Generator.h cmFileTimeComparison.cxx diff --git a/Source/cmAddDependenciesCommand.cxx b/Source/cmAddDependenciesCommand.cxx index eccf7d688..4f1e05bc5 100644 --- a/Source/cmAddDependenciesCommand.cxx +++ b/Source/cmAddDependenciesCommand.cxx @@ -32,7 +32,7 @@ bool cmAddDependenciesCommand cmTarget* target = this->GetMakefile()->GetLocalGenerator()-> - GetGlobalGenerator()->FindTarget(0, target_name.c_str(), false); + GetGlobalGenerator()->FindTarget(0, target_name.c_str()); if(target) { std::vector::const_iterator s = args.begin(); diff --git a/Source/cmAddExecutableCommand.cxx b/Source/cmAddExecutableCommand.cxx index 9914e171d..cea12bc34 100644 --- a/Source/cmAddExecutableCommand.cxx +++ b/Source/cmAddExecutableCommand.cxx @@ -51,7 +51,7 @@ bool cmAddExecutableCommand ++s; excludeFromAll = true; } - else if(*s == "IMPORT") + else if(*s == "IMPORTED") { ++s; importTarget = true; @@ -61,12 +61,60 @@ bool cmAddExecutableCommand break; } } - - if (importTarget) + + // Special modifiers are not allowed with IMPORTED signature. + if(importTarget && (use_win32 || use_macbundle || excludeFromAll)) { - this->Makefile->AddNewTarget(cmTarget::EXECUTABLE, exename.c_str(), true); + if(use_win32) + { + this->SetError("may not be given WIN32 for an IMPORTED target."); + } + else if(use_macbundle) + { + this->SetError( + "may not be given MACOSX_BUNDLE for an IMPORTED target."); + } + else // if(excludeFromAll) + { + this->SetError( + "may not be given EXCLUDE_FROM_ALL for an IMPORTED target."); + } + return false; + } + + // Check for an existing target with this name. + cmTarget* existing = this->Makefile->FindTargetToUse(exename.c_str()); + if(importTarget) + { + // Make sure the target does not already exist. + if(existing) + { + cmOStringStream e; + e << "cannot create imported target \"" << exename + << "\" because another target with the same name already exists."; + this->SetError(e.str().c_str()); + return false; + } + + // Create the imported target. + this->Makefile->AddImportedTarget(exename.c_str(), cmTarget::EXECUTABLE); return true; } + else + { + // Make sure the target does not conflict with an imported target. + // This should really enforce global name uniqueness for targets + // built within the project too, but that may break compatiblity + // with projects in which it was accidentally working. + if(existing && existing->IsImported()) + { + cmOStringStream e; + e << "cannot create target \"" << exename + << "\" because an imported target with the same name already exists."; + this->SetError(e.str().c_str()); + return false; + } + } if (s == args.end()) { diff --git a/Source/cmAddExecutableCommand.h b/Source/cmAddExecutableCommand.h index daedc82ab..4ac11cc50 100644 --- a/Source/cmAddExecutableCommand.h +++ b/Source/cmAddExecutableCommand.h @@ -90,6 +90,24 @@ public: "If EXCLUDE_FROM_ALL is given the target will not be built by default. " "It will be built only if the user explicitly builds the target or " "another target that requires the target depends on it." + "\n" + "The add_executable command can also create IMPORTED executable " + "targets using this signature:\n" + " add_executable( IMPORTED)\n" + "An IMPORTED executable target references an executable file located " + "outside the project. " + "No rules are generated to build it. " + "The target name has scope in the directory in which it is created " + "and below. " + "It may be referenced like any target built within the project. " + "IMPORTED executables are useful for convenient reference from " + "commands like add_custom_command. " + "Details about the imported executable are specified by setting " + "properties whose names begin in \"IMPORTED_\". " + "The most important such property is IMPORTED_LOCATION " + "(and its per-configuration version IMPORTED_LOCATION_) " + "which specifies the location of the main executable file on disk. " + "See documentation of the IMPORTED_* properties for more information." ; } diff --git a/Source/cmAddLibraryCommand.cxx b/Source/cmAddLibraryCommand.cxx index 4cc7e7ae3..9406a3584 100644 --- a/Source/cmAddLibraryCommand.cxx +++ b/Source/cmAddLibraryCommand.cxx @@ -46,6 +46,7 @@ bool cmAddLibraryCommand // If the second argument is "SHARED" or "STATIC", then it controls // the type of library. Otherwise, it is treated as a source or // source list name. There may be two keyword arguments, check for them + bool haveSpecifiedType = false; while ( s != args.end() ) { std::string libType = *s; @@ -53,23 +54,26 @@ bool cmAddLibraryCommand { ++s; type = cmTarget::STATIC_LIBRARY; + haveSpecifiedType = true; } else if(libType == "SHARED") { ++s; type = cmTarget::SHARED_LIBRARY; + haveSpecifiedType = true; } else if(libType == "MODULE") { ++s; type = cmTarget::MODULE_LIBRARY; + haveSpecifiedType = true; } else if(*s == "EXCLUDE_FROM_ALL") { ++s; excludeFromAll = true; } - else if(*s == "IMPORT") + else if(*s == "IMPORTED") { ++s; importTarget = true; @@ -98,11 +102,46 @@ bool cmAddLibraryCommand type = cmTarget::STATIC_LIBRARY; } - if (importTarget) + // The IMPORTED signature requires a type to be specified explicitly. + if(importTarget && !haveSpecifiedType) { - this->Makefile->AddNewTarget(type, libName.c_str(), true); + this->SetError("called with IMPORTED argument but no library type."); + return false; + } + + // Check for an existing target with this name. + cmTarget* existing = this->Makefile->FindTargetToUse(libName.c_str()); + if(importTarget) + { + // Make sure the target does not already exist. + if(existing) + { + cmOStringStream e; + e << "cannot create imported target \"" << libName + << "\" because another target with the same name already exists."; + this->SetError(e.str().c_str()); + return false; + } + + // Create the imported target. + this->Makefile->AddImportedTarget(libName.c_str(), type); return true; } + else + { + // Make sure the target does not conflict with an imported target. + // This should really enforce global name uniqueness for targets + // built within the project too, but that may break compatiblity + // with projects in which it was accidentally working. + if(existing && existing->IsImported()) + { + cmOStringStream e; + e << "cannot create target \"" << libName + << "\" because an imported target with the same name already exists."; + this->SetError(e.str().c_str()); + return false; + } + } if (s == args.end()) { diff --git a/Source/cmAddLibraryCommand.h b/Source/cmAddLibraryCommand.h index 6b0f7a494..9b95b86ff 100644 --- a/Source/cmAddLibraryCommand.h +++ b/Source/cmAddLibraryCommand.h @@ -73,7 +73,26 @@ public: "to STATIC.\n" "If EXCLUDE_FROM_ALL is given the target will not be built by default. " "It will be built only if the user explicitly builds the target or " - "another target that requires the target depends on it."; + "another target that requires the target depends on it." + "\n" + "The add_library command can also create IMPORTED library " + "targets using this signature:\n" + " add_library( IMPORTED)\n" + "An IMPORTED library target references a library file located " + "outside the project. " + "No rules are generated to build it. " + "The target name has scope in the directory in which it is created " + "and below. " + "It may be referenced like any target built within the project. " + "IMPORTED libraries are useful for convenient reference from " + "commands like target_link_libraries. " + "Details about the imported library are specified by setting " + "properties whose names begin in \"IMPORTED_\". " + "The most important such property is IMPORTED_LOCATION " + "(and its per-configuration version IMPORTED_LOCATION_) " + "which specifies the location of the main library file on disk. " + "See documentation of the IMPORTED_* properties for more information." + ; } cmTypeMacro(cmAddLibraryCommand, cmCommand); diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx index 605643109..b4cb92eb6 100644 --- a/Source/cmComputeLinkDepends.cxx +++ b/Source/cmComputeLinkDepends.cxx @@ -158,7 +158,7 @@ std::vector const& cmComputeLinkDepends::Compute() { // Follow the link dependencies of the target to be linked. - this->AddLinkEntries(-1, this->Target->GetOriginalLinkLibraries()); + this->AddTargetLinkEntries(-1, this->Target->GetOriginalLinkLibraries()); // Complete the breadth-first search of dependencies. while(!this->BFSQueue.empty()) @@ -222,8 +222,7 @@ int cmComputeLinkDepends::AddLinkEntry(std::string const& item) int index = lei->second; LinkEntry& entry = this->EntryList[index]; entry.Item = item; - entry.Target = - this->GlobalGenerator->FindTarget(0, entry.Item.c_str(), false); + entry.Target = this->Makefile->FindTargetToUse(entry.Item.c_str()); // If the item has dependencies queue it to follow them. if(entry.Target) @@ -264,8 +263,15 @@ void cmComputeLinkDepends::FollowLinkEntry(BFSEntry const& qe) if(entry.Target) { // Follow the target dependencies. - this->AddLinkEntries(depender_index, - entry.Target->GetOriginalLinkLibraries()); + if(entry.Target->IsImported()) + { + this->AddImportedLinkEntries(depender_index, entry.Target); + } + else + { + this->AddTargetLinkEntries(depender_index, + entry.Target->GetOriginalLinkLibraries()); + } } else { @@ -274,6 +280,18 @@ void cmComputeLinkDepends::FollowLinkEntry(BFSEntry const& qe) } } +//---------------------------------------------------------------------------- +void cmComputeLinkDepends::AddImportedLinkEntries(int depender_index, + cmTarget* target) +{ + if(std::vector const* libs = + target->GetImportedLinkLibraries(this->Config)) + { + this->AddLinkEntries(depender_index, *libs); + } +} + +//---------------------------------------------------------------------------- void cmComputeLinkDepends::AddVarLinkEntries(int depender_index, const char* value) { @@ -283,39 +301,49 @@ void cmComputeLinkDepends::AddVarLinkEntries(int depender_index, std::vector deplist; cmSystemTools::ExpandListArgument(value, deplist); - // Construct the vector of type/value pairs from the variable. - LinkLibraryVectorType libs; - cmTarget::LinkLibraryType linkType = cmTarget::GENERAL; + // Compute which library configuration to link. + cmTarget::LinkLibraryType linkType = cmTarget::OPTIMIZED; + if(this->Config && cmSystemTools::UpperCase(this->Config) == "DEBUG") + { + linkType = cmTarget::DEBUG; + } + + // Look for entries meant for this configuration. + std::vector actual_libs; + cmTarget::LinkLibraryType llt = cmTarget::GENERAL; for(std::vector::const_iterator di = deplist.begin(); di != deplist.end(); ++di) { if(*di == "debug") { - linkType = cmTarget::DEBUG; + llt = cmTarget::DEBUG; } else if(*di == "optimized") { - linkType = cmTarget::OPTIMIZED; + llt = cmTarget::OPTIMIZED; } else if(*di == "general") { - linkType = cmTarget::GENERAL; + llt = cmTarget::GENERAL; } else if(!di->empty()) { - cmTarget::LibraryID lib(*di, linkType); - libs.push_back(lib); + if(llt == cmTarget::GENERAL || llt == linkType) + { + actual_libs.push_back(*di); + } linkType = cmTarget::GENERAL; } } // Add the entries from this list. - this->AddLinkEntries(depender_index, libs); + this->AddLinkEntries(depender_index, actual_libs); } //---------------------------------------------------------------------------- -void cmComputeLinkDepends::AddLinkEntries(int depender_index, - LinkLibraryVectorType const& libs) +void +cmComputeLinkDepends::AddTargetLinkEntries(int depender_index, + LinkLibraryVectorType const& libs) { // Compute which library configuration to link. cmTarget::LinkLibraryType linkType = cmTarget::OPTIMIZED; @@ -324,23 +352,42 @@ void cmComputeLinkDepends::AddLinkEntries(int depender_index, linkType = cmTarget::DEBUG; } - // Track inferred dependency sets implied by this list. - std::map dependSets; - - // Loop over the libraries linked directly by the target. + // Look for entries meant for this configuration. + std::vector actual_libs; for(cmTarget::LinkLibraryVectorType::const_iterator li = libs.begin(); li != libs.end(); ++li) { - // Skip entries that will resolve to the target getting linked. - // Skip libraries not meant for the current configuration. - if(li->first == this->Target->GetName() || li->first.empty() || - !(li->second == cmTarget::GENERAL || li->second == linkType)) + if(li->second == cmTarget::GENERAL || li->second == linkType) + { + actual_libs.push_back(li->first); + } + } + + // Add these entries. + this->AddLinkEntries(depender_index, actual_libs); +} + +//---------------------------------------------------------------------------- +void +cmComputeLinkDepends::AddLinkEntries(int depender_index, + std::vector const& libs) +{ + // Track inferred dependency sets implied by this list. + std::map dependSets; + + // Loop over the libraries linked directly by the depender. + for(std::vector::const_iterator li = libs.begin(); + li != libs.end(); ++li) + { + // Skip entries that will resolve to the target getting linked or + // are empty. + if(*li == this->Target->GetName() || li->empty()) { continue; } // Add a link entry for this item. - int dependee_index = this->AddLinkEntry(li->first); + int dependee_index = this->AddLinkEntry(*li); // The depender must come before the dependee. if(depender_index >= 0) diff --git a/Source/cmComputeLinkDepends.h b/Source/cmComputeLinkDepends.h index 646adca04..881d50fc2 100644 --- a/Source/cmComputeLinkDepends.h +++ b/Source/cmComputeLinkDepends.h @@ -66,9 +66,12 @@ private: typedef cmTarget::LinkLibraryVectorType LinkLibraryVectorType; int AddLinkEntry(std::string const& item); + void AddImportedLinkEntries(int depender_index, cmTarget* target); void AddVarLinkEntries(int depender_index, const char* value); + void AddTargetLinkEntries(int depender_index, + LinkLibraryVectorType const& libs); void AddLinkEntries(int depender_index, - LinkLibraryVectorType const& libs); + std::vector const& libs); // One entry for each unique item. std::vector EntryList; diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx index 1ec1ba329..4d0d93a86 100644 --- a/Source/cmComputeLinkInformation.cxx +++ b/Source/cmComputeLinkInformation.cxx @@ -301,11 +301,8 @@ void cmComputeLinkInformation::AddItem(std::string const& item, { // Compute the proper name to use to link this library. const char* config = this->Config; - bool implib = this->UseImportLibrary; - bool impexe = (tgt && - tgt->GetType() == cmTarget::EXECUTABLE && - tgt->GetPropertyAsBool("ENABLE_EXPORTS")); - if(impexe && !implib && !this->LoaderFlag) + bool impexe = (tgt && tgt->IsExecutableWithExports()); + if(impexe && !this->UseImportLibrary && !this->LoaderFlag) { // Skip linking to executables on platforms with no import // libraries or loader flags. @@ -325,13 +322,18 @@ void cmComputeLinkInformation::AddItem(std::string const& item, // platform. Add it now. std::string linkItem; linkItem = this->LoaderFlag; - std::string exe = tgt->GetFullPath(config, implib); + std::string exe = tgt->GetFullPath(config, this->UseImportLibrary); linkItem += exe; this->Items.push_back(Item(linkItem, true)); this->Depends.push_back(exe); } else { + // Decide whether to use an import library. + bool implib = + (this->UseImportLibrary && + (impexe || tgt->GetType() == cmTarget::SHARED_LIBRARY)); + // Pass the full path to the target file. std::string lib = tgt->GetFullPath(config, implib); this->Depends.push_back(lib); @@ -950,18 +952,8 @@ cmComputeLinkInformation::AddLibraryRuntimeInfo(std::string const& fullPath, // Try to get the soname of the library. Only files with this name // could possibly conflict. - std::string soName; - const char* soname = 0; - if(!target->IsImported()) - { - std::string name; - std::string realName; - std::string impName; - std::string pdbName; - target->GetLibraryNames(name, soName, realName, impName, pdbName, - this->Config); - soname = soName.c_str(); - } + std::string soName = target->GetSOName(this->Config); + const char* soname = soName.empty()? 0 : soName.c_str(); // Add the library runtime entry. this->AddLibraryRuntimeInfo(fullPath, soname); diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx new file mode 100644 index 000000000..0412c905a --- /dev/null +++ b/Source/cmExportBuildFileGenerator.cxx @@ -0,0 +1,117 @@ +/*========================================================================= + + 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 "cmExportBuildFileGenerator.h" + +//---------------------------------------------------------------------------- +bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os) +{ + // Create all the imported targets. + for(std::vector::const_iterator + tei = this->Exports->begin(); + tei != this->Exports->end(); ++tei) + { + cmTarget* te = *tei; + this->ExportedTargets.insert(te); + this->GenerateImportTargetCode(os, te); + } + + // Generate import file content for each configuration. + for(std::vector::const_iterator + ci = this->Configurations.begin(); + ci != this->Configurations.end(); ++ci) + { + this->GenerateImportConfig(os, ci->c_str()); + } + + return true; +} + +//---------------------------------------------------------------------------- +void +cmExportBuildFileGenerator +::GenerateImportTargetsConfig(std::ostream& os, + const char* config, std::string const& suffix) +{ + for(std::vector::const_iterator + tei = this->Exports->begin(); + tei != this->Exports->end(); ++tei) + { + // Collect import properties for this target. + cmTarget* target = *tei; + ImportPropertyMap properties; + this->SetImportLocationProperty(config, suffix, target, properties); + if(!properties.empty()) + { + // Get the rest of the target details. + this->SetImportDetailProperties(config, suffix, + target, properties); + + // TOOD: PUBLIC_HEADER_LOCATION + // this->GenerateImportProperty(config, te->HeaderGenerator, + // properties); + + // Generate code in the export file. + this->GenerateImportPropertyCode(os, config, target, properties); + } + } +} + +//---------------------------------------------------------------------------- +void +cmExportBuildFileGenerator +::SetImportLocationProperty(const char* config, std::string const& suffix, + cmTarget* target, ImportPropertyMap& properties) +{ + // Get the makefile in which to lookup target information. + cmMakefile* mf = target->GetMakefile(); + + // Add the main target file. + { + std::string prop = "IMPORTED_LOCATION"; + prop += suffix; + std::string value = target->GetFullPath(config, false); + properties[prop] = value; + } + + // Check whether this is a DLL platform. + bool dll_platform = + (mf->IsOn("WIN32") || mf->IsOn("CYGWIN") || mf->IsOn("MINGW")); + + // Add the import library for windows DLLs. + if(dll_platform && + (target->GetType() == cmTarget::SHARED_LIBRARY || + target->IsExecutableWithExports()) && + mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) + { + std::string prop = "IMPORTED_IMPLIB"; + prop += suffix; + std::string value = target->GetFullPath(config, true); + properties[prop] = value; + } +} + +//---------------------------------------------------------------------------- +void +cmExportBuildFileGenerator +::ComplainAboutMissingTarget(cmTarget* target, const char* dep) +{ + cmOStringStream e; + e << "WARNING: EXPORT(...) includes target " << target->GetName() + << " which links to target \"" << dep + << "\" that is not in the export set."; + cmSystemTools::Message(e.str().c_str()); +} diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h new file mode 100644 index 000000000..0bb79cc2a --- /dev/null +++ b/Source/cmExportBuildFileGenerator.h @@ -0,0 +1,55 @@ +/*========================================================================= + + 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. + +=========================================================================*/ +#ifndef cmExportBuildFileGenerator_h +#define cmExportBuildFileGenerator_h + +#include "cmExportFileGenerator.h" + +/** \class cmExportBuildFileGenerator + * \brief Generate a file exporting targets from a build tree. + * + * cmExportBuildFileGenerator generates a file exporting targets from + * a build tree. A single file exports information for all + * configurations built. + * + * This is used to implement the EXPORT() command. + */ +class cmExportBuildFileGenerator: public cmExportFileGenerator +{ +public: + /** Set the list of targets to export. */ + void SetExports(std::vector const* exports) + { this->Exports = exports; } + +protected: + // Implement virtual methods from the superclass. + virtual bool GenerateMainFile(std::ostream& os); + virtual void GenerateImportTargetsConfig(std::ostream& os, + const char* config, + std::string const& suffix); + virtual void ComplainAboutMissingTarget(cmTarget* target, const char* dep); + + /** Fill in properties indicating built file locations. */ + void SetImportLocationProperty(const char* config, + std::string const& suffix, + cmTarget* target, + ImportPropertyMap& properties); + + std::vector const* Exports; +}; + +#endif diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index 3dca7d8cf..70c024e5c 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -20,14 +20,13 @@ #include "cmGeneratedFileStream.h" #include "cmake.h" -#include +#include "cmExportBuildFileGenerator.h" cmExportCommand::cmExportCommand() :cmCommand() ,ArgumentGroup() ,Targets(&Helper, "TARGETS") -,Append(&Helper, "APPEND", &ArgumentGroup) -,Prefix(&Helper, "PREFIX", &ArgumentGroup) +,Namespace(&Helper, "NAMESPACE", &ArgumentGroup) ,Filename(&Helper, "FILE", &ArgumentGroup) { // at first TARGETS @@ -53,151 +52,131 @@ bool cmExportCommand if (!unknownArgs.empty()) { this->SetError("Unknown arguments."); - cmSystemTools::SetFatalErrorOccured(); return false; } if (this->Targets.WasFound() == false) { this->SetError("TARGETS option missing."); - cmSystemTools::SetFatalErrorOccured(); return false; } - - if ( !this->Makefile->CanIWriteThisFile(this->Filename.GetString().c_str()) ) + if(!this->Filename.WasFound()) { - std::string e = "attempted to write a file: " + this->Filename.GetString() - + " into a source directory."; - this->SetError(e.c_str()); - cmSystemTools::SetFatalErrorOccured(); + this->SetError("FILE option missing."); return false; } - if((this->Targets.GetVector().empty())||(this->Filename.GetString().empty())) + // Make sure the file has a .cmake extension. + if(cmSystemTools::GetFilenameLastExtension(this->Filename.GetCString()) + != ".cmake") { - return true; + cmOStringStream e; + e << "FILE option given filename \"" << this->Filename.GetString() + << "\" which does not have an extension of \".cmake\".\n"; + this->SetError(e.str().c_str()); + return false; } - // Use copy-if-different if not appending. - cmsys::auto_ptr foutPtr; - if(this->Append.IsEnabled()) + // Get the file to write. + std::string fname = this->Filename.GetString(); + if(cmSystemTools::FileIsFullPath(fname.c_str())) { - cmsys::auto_ptr ap( - new std::ofstream(this->Filename.GetString().c_str(), std::ios::app)); - foutPtr = ap; + if(!this->Makefile->CanIWriteThisFile(fname.c_str())) + { + cmOStringStream e; + e << "FILE option given filename \"" << fname + << "\" which is in the source tree.\n"; + this->SetError(e.str().c_str()); + return false; + } } else { - cmsys::auto_ptr ap( - new cmGeneratedFileStream(this->Filename.GetString().c_str(), true)); - ap->SetCopyIfDifferent(true); - foutPtr = ap; + // Interpret relative paths with respect to the current build dir. + fname = this->Makefile->GetCurrentOutputDirectory(); + fname += "/"; + fname += this->Filename.GetString(); } - std::ostream& fout = *foutPtr.get(); - if (!fout) + // If no targets are to be exported we are done. + if(this->Targets.GetVector().empty()) { - cmSystemTools::Error("Error Writing ", this->Filename.GetString().c_str()); - cmSystemTools::ReportLastSystemError(""); return true; } - // the following code may move into an "export generator" - // Compute the set of configurations. - std::vector configurationTypes; - if(const char* types = - this->Makefile->GetDefinition("CMAKE_CONFIGURATION_TYPES")) - { - cmSystemTools::ExpandListArgument(types, configurationTypes); - } - if(configurationTypes.empty()) - { - const char* config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE"); - if (config!=0) - { - configurationTypes.push_back(config); - } - } - - for(std::vector::const_iterator + // Collect the targets to be exported. + std::vector targets; + for(std::vector::const_iterator currentTarget = this->Targets.GetVector().begin(); currentTarget != this->Targets.GetVector().end(); ++currentTarget) { - cmTarget* target = this->Makefile->GetLocalGenerator()-> - GetGlobalGenerator()->FindTarget(0, currentTarget->c_str(), true); - if (target == 0) + if(cmTarget* target = + this->Makefile->GetLocalGenerator()-> + GetGlobalGenerator()->FindTarget(0, currentTarget->c_str())) { - std::string e = "detected unknown target: " + *currentTarget; - this->SetError(e.c_str()); - cmSystemTools::SetFatalErrorOccured(); + if((target->GetType() == cmTarget::EXECUTABLE) || + (target->GetType() == cmTarget::STATIC_LIBRARY) || + (target->GetType() == cmTarget::SHARED_LIBRARY) || + (target->GetType() == cmTarget::MODULE_LIBRARY)) + { + targets.push_back(target); + } + else + { + cmOStringStream e; + e << "given target \"" << *currentTarget + << "\" which is not an executable or library."; + this->SetError(e.str().c_str()); + return false; + } + } + else + { + cmOStringStream e; + e << "given target \"" << *currentTarget + << "\" which is not built by this project."; + this->SetError(e.str().c_str()); return false; } } - for(std::vector::const_iterator - currentTarget = this->Targets.GetVector().begin(); - currentTarget != this->Targets.GetVector().end(); - ++currentTarget) - { - // Look for a CMake target with the given name, which is an executable - // and which can be run - cmTarget* target = this->Makefile->GetLocalGenerator()-> - GetGlobalGenerator()->FindTarget(0, currentTarget->c_str(), true); - if ((target != 0) - && ((target->GetType() == cmTarget::EXECUTABLE) - || (target->GetType() == cmTarget::STATIC_LIBRARY) - || (target->GetType() == cmTarget::SHARED_LIBRARY) - || (target->GetType() == cmTarget::MODULE_LIBRARY))) - { - switch (target->GetType()) - { - case cmTarget::EXECUTABLE: - fout << "ADD_EXECUTABLE(" - << this->Prefix.GetString().c_str() << currentTarget->c_str() - << " IMPORT )\n"; - break; - case cmTarget::STATIC_LIBRARY: - fout << "ADD_LIBRARY(" - << this->Prefix.GetString().c_str() << currentTarget->c_str() - << " STATIC IMPORT )\n"; - break; - case cmTarget::SHARED_LIBRARY: - fout << "ADD_LIBRARY(" - << this->Prefix.GetString().c_str() << currentTarget->c_str() - << " SHARED IMPORT )\n"; - break; - case cmTarget::MODULE_LIBRARY: - fout << "ADD_LIBRARY(" - << this->Prefix.GetString().c_str() << currentTarget->c_str() - << " MODULE IMPORT )\n"; - break; - default: // should never happen - break; - } + // Setup export file generation. + cmExportBuildFileGenerator ebfg; + ebfg.SetExportFile(fname.c_str()); + ebfg.SetNamespace(this->Namespace.GetCString()); + ebfg.SetExports(&targets); - fout << "SET_TARGET_PROPERTIES(" << this->Prefix.GetString().c_str() - << currentTarget->c_str() << " PROPERTIES \n" - <<" LOCATION \""<< target->GetLocation(0)<<"\"\n"; - for(std::vector::const_iterator - currentConfig = configurationTypes.begin(); - currentConfig != configurationTypes.end(); - ++currentConfig) - { - if (!currentConfig->empty()) - { - const char* loc = target->GetLocation(currentConfig->c_str()); - if (loc && *loc) - { - fout << " " << currentConfig->c_str() - << "_LOCATION \"" << loc << "\"\n"; - } - } - } - fout << " )\n\n"; + // Compute the set of configurations exported. + if(const char* types = + this->Makefile->GetDefinition("CMAKE_CONFIGURATION_TYPES")) + { + std::vector configurationTypes; + cmSystemTools::ExpandListArgument(types, configurationTypes); + for(std::vector::const_iterator + ci = configurationTypes.begin(); + ci != configurationTypes.end(); ++ci) + { + ebfg.AddConfiguration(ci->c_str()); } } + else if(const char* config = + this->Makefile->GetDefinition("CMAKE_BUILD_TYPE")) + { + ebfg.AddConfiguration(config); + } + else + { + ebfg.AddConfiguration(""); + } + + // Generate the import file. + if(!ebfg.GenerateImportFile()) + { + this->SetError("could not write export file."); + return false; + } return true; } diff --git a/Source/cmExportCommand.h b/Source/cmExportCommand.h index c79f46d20..f42450118 100644 --- a/Source/cmExportCommand.h +++ b/Source/cmExportCommand.h @@ -55,7 +55,7 @@ public: virtual const char* GetTerseDocumentation() { return - "Write out the dependency information for all targets of a project."; + "Export targets from the build tree for use by outside projects."; } /** @@ -64,15 +64,23 @@ public: virtual const char* GetFullDocumentation() { return - " export(TARGETS tgt1 ... tgtN [PREFIX ] FILE " - "[APPEND])\n" - "Create a file that can be included into a CMake listfile with the " - "INCLUDE command. The file will contain a number of SET commands " - "that will set all the variables needed for library dependency " - "information. This should be the last command in the top level " - "CMakeLists.txt file of the project. If the APPEND option is " - "specified, the SET commands will be appended to the given file " - "instead of replacing it."; + " export(TARGETS [target1 [target2 [...]]] [NAMESPACE ]\n" + " FILE )\n" + "Create a file that may be included by outside projects to " + "import targets from the current project's build tree. " + "This is useful during cross-compiling to build utility executables " + "that can run on the host platform in one project and then import " + "them into another project being compiled for the target platform. " + "If the NAMESPACE option is given the string will be " + "prepended to all target names written to the file. " + "If a library target is included in the export but " + "a target to which it links is not included the behavior is " + "unspecified." + "\n" + "The file created by this command is specific to the build tree and " + "should never be installed. " + "See the install(EXPORT) command to export targets from an " + "installation tree."; } cmTypeMacro(cmExportCommand, cmCommand); @@ -80,8 +88,7 @@ public: private: cmCommandArgumentGroup ArgumentGroup; cmCAStringVector Targets; - cmCAEnabler Append; - cmCAString Prefix; + cmCAString Namespace; cmCAString Filename; }; diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx new file mode 100644 index 000000000..ec4bc3346 --- /dev/null +++ b/Source/cmExportFileGenerator.cxx @@ -0,0 +1,281 @@ +/*========================================================================= + + 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 "cmExportFileGenerator.h" + +#include "cmGeneratedFileStream.h" +#include "cmMakefile.h" +#include "cmSystemTools.h" +#include "cmTarget.h" + +//---------------------------------------------------------------------------- +void cmExportFileGenerator::AddConfiguration(const char* config) +{ + this->Configurations.push_back(config); +} + +//---------------------------------------------------------------------------- +void cmExportFileGenerator::SetExportFile(const char* mainFile) +{ + this->MainImportFile = mainFile; + this->FileDir = + cmSystemTools::GetFilenamePath(this->MainImportFile); + this->FileBase = + cmSystemTools::GetFilenameWithoutLastExtension(this->MainImportFile); + this->FileExt = + cmSystemTools::GetFilenameLastExtension(this->MainImportFile); +} + +//---------------------------------------------------------------------------- +bool cmExportFileGenerator::GenerateImportFile() +{ + // Open the output file to generate it. + cmGeneratedFileStream exportFileStream(this->MainImportFile.c_str(), true); + if(!exportFileStream) + { + std::string se = cmSystemTools::GetLastSystemError(); + cmOStringStream e; + e << "cannot write to file \"" << this->MainImportFile + << "\": " << se; + cmSystemTools::Error(e.str().c_str()); + return false; + } + std::ostream& os = exportFileStream; + + // Start with the import file header. + this->GenerateImportHeaderCode(os); + + // Create all the imported targets. + bool result = this->GenerateMainFile(os); + + // End with the import file footer. + this->GenerateImportFooterCode(os); + + return result; +} + +//---------------------------------------------------------------------------- +void cmExportFileGenerator::GenerateImportConfig(std::ostream& os, + const char* config) +{ + // Construct the property configuration suffix. + std::string suffix = "_"; + if(config && *config) + { + suffix += cmSystemTools::UpperCase(config); + } + else + { + suffix += "NOCONFIG"; + } + + // Generate the per-config target information. + this->GenerateImportTargetsConfig(os, config, suffix); +} + +//---------------------------------------------------------------------------- +void +cmExportFileGenerator +::SetImportDetailProperties(const char* config, std::string const& suffix, + cmTarget* target, ImportPropertyMap& properties) +{ + // Get the makefile in which to lookup target information. + cmMakefile* mf = target->GetMakefile(); + + // Add the soname for unix shared libraries. + if(target->GetType() == cmTarget::SHARED_LIBRARY || + target->GetType() == cmTarget::MODULE_LIBRARY) + { + // Check whether this is a DLL platform. + bool dll_platform = + (mf->IsOn("WIN32") || mf->IsOn("CYGWIN") || mf->IsOn("MINGW")); + if(!dll_platform) + { + std::string soname = target->GetSOName(config); + std::string prop = "IMPORTED_SONAME"; + prop += suffix; + properties[prop] = soname; + } + } + + // Add the transitive link dependencies for this configuration. + { + // Compute which library configuration to link. + cmTarget::LinkLibraryType linkType = cmTarget::OPTIMIZED; + if(config && cmSystemTools::UpperCase(config) == "DEBUG") + { + linkType = cmTarget::DEBUG; + } + + // Construct the property value. + cmTarget::LinkLibraryVectorType const& libs = + target->GetOriginalLinkLibraries(); + std::string link_libs; + const char* sep = ""; + for(cmTarget::LinkLibraryVectorType::const_iterator li = libs.begin(); + li != libs.end(); ++li) + { + // Skip entries that will resolve to the target itself, are empty, + // or are not meant for this configuration. + if(li->first == target->GetName() || li->first.empty() || + !(li->second == cmTarget::GENERAL || li->second == linkType)) + { + continue; + } + + // Separate this from the previous entry. + link_libs += sep; + sep = ";"; + + // Append this entry. + if(cmTarget* tgt = mf->FindTargetToUse(li->first.c_str())) + { + // This is a target. Make sure it is included in the export. + if(this->ExportedTargets.find(tgt) != this->ExportedTargets.end()) + { + // The target is in the export. Append it with the export + // namespace. + link_libs += this->Namespace; + link_libs += li->first; + } + else + { + // The target is not in the export. This is probably + // user-error. Warn but add it anyway. + this->ComplainAboutMissingTarget(target, li->first.c_str()); + link_libs += li->first; + } + } + else + { + // Append the raw name. + link_libs += li->first; + } + } + + // Store the property. + std::string prop = "IMPORTED_LINK_LIBRARIES"; + prop += suffix; + properties[prop] = link_libs; + } +} + +//---------------------------------------------------------------------------- +void cmExportFileGenerator::GenerateImportHeaderCode(std::ostream& os, + const char* config) +{ + os << "#----------------------------------------------------------------\n" + << "# Generated CMake target import file"; + if(config) + { + os << " for configuration \"" << config << "\".\n"; + } + else + { + os << ".\n"; + } + os << "#----------------------------------------------------------------\n" + << "\n"; + this->GenerateImportVersionCode(os); +} + +//---------------------------------------------------------------------------- +void cmExportFileGenerator::GenerateImportFooterCode(std::ostream& os) +{ + os << "# Commands beyond this point should not need to know the version.\n" + << "SET(CMAKE_IMPORT_FILE_VERSION)\n"; +} + +//---------------------------------------------------------------------------- +void cmExportFileGenerator::GenerateImportVersionCode(std::ostream& os) +{ + // Store an import file format version. This will let us change the + // format later while still allowing old import files to work. + os << "# Commands may need to know the format version.\n" + << "SET(CMAKE_IMPORT_FILE_VERSION 1)\n" + << "\n"; +} + +//---------------------------------------------------------------------------- +void +cmExportFileGenerator +::GenerateImportTargetCode(std::ostream& os, cmTarget* target) +{ + // Construct the imported target name. + std::string targetName = this->Namespace; + targetName += target->GetName(); + + // Create the imported target. + os << "# Create imported target " << targetName << "\n"; + switch(target->GetType()) + { + case cmTarget::EXECUTABLE: + os << "ADD_EXECUTABLE(" << targetName << " IMPORTED)\n"; + break; + case cmTarget::STATIC_LIBRARY: + os << "ADD_LIBRARY(" << targetName << " STATIC IMPORTED)\n"; + break; + case cmTarget::SHARED_LIBRARY: + os << "ADD_LIBRARY(" << targetName << " SHARED IMPORTED)\n"; + break; + case cmTarget::MODULE_LIBRARY: + os << "ADD_LIBRARY(" << targetName << " MODULE IMPORTED)\n"; + break; + default: // should never happen + break; + } + if(target->IsExecutableWithExports()) + { + os << "SET_PROPERTY(TARGET " << targetName + << " PROPERTY IMPORTED_ENABLE_EXPORTS 1)\n"; + } + os << "\n"; +} + +//---------------------------------------------------------------------------- +void +cmExportFileGenerator +::GenerateImportPropertyCode(std::ostream& os, const char* config, + cmTarget* target, + ImportPropertyMap const& properties) +{ + // Construct the imported target name. + std::string targetName = this->Namespace; + targetName += target->GetName(); + + // Set the import properties. + os << "# Import target \"" << targetName << "\" for configuration \"" + << config << "\"\n"; + os << "SET_PROPERTY(TARGET " << targetName + << " APPEND PROPERTY IMPORTED_CONFIGURATIONS "; + if(config && *config) + { + os << cmSystemTools::UpperCase(config); + } + else + { + os << "NOCONFIG"; + } + os << ")\n"; + os << "SET_TARGET_PROPERTIES(" << targetName << " PROPERTIES\n"; + for(ImportPropertyMap::const_iterator pi = properties.begin(); + pi != properties.end(); ++pi) + { + os << " " << pi->first << " \"" << pi->second << "\"\n"; + } + os << " )\n" + << "\n"; +} diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h new file mode 100644 index 000000000..22592d72a --- /dev/null +++ b/Source/cmExportFileGenerator.h @@ -0,0 +1,96 @@ +/*========================================================================= + + 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. + +=========================================================================*/ +#ifndef cmExportFileGenerator_h +#define cmExportFileGenerator_h + +#include "cmCommand.h" + +/** \class cmExportFileGenerator + * \brief Generate a file exporting targets from a build or install tree. + * + * cmExportFileGenerator is the superclass for + * cmExportBuildFileGenerator and cmExportInstallFileGenerator. It + * contains common code generation routines for the two kinds of + * export implementations. + */ +class cmExportFileGenerator +{ +public: + /** Set the full path to the export file to generate. */ + void SetExportFile(const char* mainFile); + + /** Set the namespace in which to place exported target names. */ + void SetNamespace(const char* ns) { this->Namespace = ns; } + + /** Add a configuration to be exported. */ + void AddConfiguration(const char* config); + + /** Actually generate the export file. Returns whether there was an + error. */ + bool GenerateImportFile(); +protected: + + typedef std::map ImportPropertyMap; + + // Generate per-configuration target information to the given output + // stream. + void GenerateImportConfig(std::ostream& os, const char* config); + + // Methods to implement export file code generation. + void GenerateImportHeaderCode(std::ostream& os, const char* config = 0); + void GenerateImportFooterCode(std::ostream& os); + void GenerateImportVersionCode(std::ostream& os); + void GenerateImportTargetCode(std::ostream& os, cmTarget* target); + void GenerateImportPropertyCode(std::ostream& os, const char* config, + cmTarget* target, + ImportPropertyMap const& properties); + + // Collect properties with detailed information about targets beyond + // their location on disk. + void SetImportDetailProperties(const char* config, + std::string const& suffix, cmTarget* target, + ImportPropertyMap& properties); + + /** Each subclass knows how to generate its kind of export file. */ + virtual bool GenerateMainFile(std::ostream& os) = 0; + + /** Each subclass knows where the target files are located. */ + virtual void GenerateImportTargetsConfig(std::ostream& os, + const char* config, + std::string const& suffix) = 0; + + /** Each subclass knows how to complain about a target that is + missing from an export set. */ + virtual void ComplainAboutMissingTarget(cmTarget*, const char* dep) = 0; + + // The namespace in which the exports are placed in the generated file. + std::string Namespace; + + // The set of configurations to export. + std::vector Configurations; + + // The file to generate. + std::string MainImportFile; + std::string FileDir; + std::string FileBase; + std::string FileExt; + + // The set of targets included in the export. + std::set ExportedTargets; +}; + +#endif diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx new file mode 100644 index 000000000..8266a9605 --- /dev/null +++ b/Source/cmExportInstallFileGenerator.cxx @@ -0,0 +1,259 @@ +/*========================================================================= + + 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 "cmExportInstallFileGenerator.h" + +#include "cmGeneratedFileStream.h" +#include "cmInstallExportGenerator.h" +#include "cmInstallTargetGenerator.h" + +//---------------------------------------------------------------------------- +cmExportInstallFileGenerator +::cmExportInstallFileGenerator(cmInstallExportGenerator* iegen): + InstallExportGenerator(iegen) +{ +} + +//---------------------------------------------------------------------------- +bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os) +{ + // Create all the imported targets. + for(std::vector::const_iterator + tei = this->ExportSet->begin(); + tei != this->ExportSet->end(); ++tei) + { + cmTargetExport* te = *tei; + this->ExportedTargets.insert(te->Target); + this->GenerateImportTargetCode(os, te->Target); + } + + // Now load per-configuration properties for them. + os << "# Load information for each installed configuration.\n" + << "GET_FILENAME_COMPONENT(_DIR \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n" + << "FILE(GLOB CONFIG_FILES \"${_DIR}/" + << this->FileBase << "-*" << this->FileExt << "\")\n" + << "FOREACH(f ${CONFIG_FILES})\n" + << " INCLUDE(${f})\n" + << "ENDFOREACH(f)\n" + << "\n"; + + // Generate an import file for each configuration. + bool result = true; + for(std::vector::const_iterator + ci = this->Configurations.begin(); + ci != this->Configurations.end(); ++ci) + { + if(!this->GenerateImportFileConfig(ci->c_str())) + { + result = false; + } + } + return result; +} + +//---------------------------------------------------------------------------- +bool +cmExportInstallFileGenerator::GenerateImportFileConfig(const char* config) +{ + // Skip configurations not enabled for this export. + if(!this->InstallExportGenerator->InstallsForConfig(config)) + { + return true; + } + + // Construct the name of the file to generate. + std::string fileName = this->FileDir; + fileName += "/"; + fileName += this->FileBase; + fileName += "-"; + if(config && *config) + { + fileName += cmSystemTools::LowerCase(config); + } + else + { + fileName += "noconfig"; + } + fileName += this->FileExt; + + // Open the output file to generate it. + cmGeneratedFileStream exportFileStream(fileName.c_str(), true); + if(!exportFileStream) + { + std::string se = cmSystemTools::GetLastSystemError(); + cmOStringStream e; + e << "cannot write to file \"" << fileName.c_str() + << "\": " << se; + cmSystemTools::Error(e.str().c_str()); + return false; + } + std::ostream& os = exportFileStream; + + // Start with the import file header. + this->GenerateImportHeaderCode(os, config); + + // Generate the per-config target information. + this->GenerateImportConfig(os, config); + + // End with the import file footer. + this->GenerateImportFooterCode(os); + + // Record this per-config import file. + this->ConfigImportFiles[config] = fileName; + + return true; +} + +//---------------------------------------------------------------------------- +void +cmExportInstallFileGenerator +::GenerateImportTargetsConfig(std::ostream& os, + const char* config, std::string const& suffix) +{ + // Add code to compute the installation prefix relative to the + // import file location. + const char* installDest = this->InstallExportGenerator->GetDestination(); + if(!cmSystemTools::FileIsFullPath(installDest)) + { + std::string dest = installDest; + os << "# Compute the installation prefix relative to this file.\n" + << "GET_FILENAME_COMPONENT(_IMPORT_PREFIX " + << "\"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n"; + while(!dest.empty()) + { + os << + "GET_FILENAME_COMPONENT(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" PATH)\n"; + dest = cmSystemTools::GetFilenamePath(dest); + } + os << "\n"; + + // Import location properties may reference this variable. + this->ImportPrefix = "${_IMPORT_PREFIX}/"; + } + + // Add each target in the set to the export. + for(std::vector::const_iterator + tei = this->ExportSet->begin(); + tei != this->ExportSet->end(); ++tei) + { + // Collect import properties for this target. + cmTargetExport* te = *tei; + ImportPropertyMap properties; + this->SetImportLocationProperty(config, suffix, + te->ArchiveGenerator, properties); + this->SetImportLocationProperty(config, suffix, + te->LibraryGenerator, properties); + this->SetImportLocationProperty(config, suffix, + te->RuntimeGenerator, properties); + + // TODO: Frameworks? + // TODO: Bundles? + + // If any file location was set for the target add it to the + // import file. + if(!properties.empty()) + { + // Get the rest of the target details. + this->SetImportDetailProperties(config, suffix, + te->Target, properties); + + // TOOD: PUBLIC_HEADER_LOCATION + // this->GenerateImportProperty(config, te->HeaderGenerator, + // properties); + + // Generate code in the export file. + this->GenerateImportPropertyCode(os, config, te->Target, properties); + } + } + + // Cleanup the import prefix variable. + if(!this->ImportPrefix.empty()) + { + os << "# Cleanup temporary variables.\n" + << "SET(_IMPORT_PREFIX)\n" + << "\n"; + } +} + +//---------------------------------------------------------------------------- +void +cmExportInstallFileGenerator +::SetImportLocationProperty(const char* config, std::string const& suffix, + cmInstallTargetGenerator* itgen, + ImportPropertyMap& properties) +{ + // Skip rules that do not match this configuration. + if(!(itgen && itgen->InstallsForConfig(config))) + { + return; + } + + { + // Construct the property name. + std::string prop = (itgen->IsImportLibrary()? + "IMPORTED_IMPLIB" : "IMPORTED_LOCATION"); + prop += suffix; + + // Construct the installed location of the target. + std::string dest = itgen->GetDestination(); + std::string value; + if(!cmSystemTools::FileIsFullPath(dest.c_str())) + { + // The target is installed relative to the installation prefix. + if(this->ImportPrefix.empty()) + { + this->ComplainAboutImportPrefix(itgen); + } + value = this->ImportPrefix; + } + value += dest; + value += "/"; + + // Append the installed file name. + value += itgen->GetInstallFilename(config); + + // Store the property. + properties[prop] = value; + } +} + +//---------------------------------------------------------------------------- +void +cmExportInstallFileGenerator +::ComplainAboutImportPrefix(cmInstallTargetGenerator* itgen) +{ + const char* installDest = this->InstallExportGenerator->GetDestination(); + cmOStringStream e; + e << "INSTALL(EXPORT \"" << this->Name << "\") given absolute " + << "DESTINATION \"" << installDest << "\" but the export " + << "references an installation of target \"" + << itgen->GetTarget()->GetName() << "\" which has relative " + << "DESTINATION \"" << itgen->GetDestination() << "\"."; + cmSystemTools::Error(e.str().c_str()); +} + +//---------------------------------------------------------------------------- +void +cmExportInstallFileGenerator +::ComplainAboutMissingTarget(cmTarget* target, const char* dep) +{ + cmOStringStream e; + e << "WARNING: INSTALL(EXPORT \"" << this->Name << "\" ...) " + << "includes target " << target->GetName() + << " which links to target \"" << dep + << "\" that is not in the export set."; + cmSystemTools::Message(e.str().c_str()); +} diff --git a/Source/cmExportInstallFileGenerator.h b/Source/cmExportInstallFileGenerator.h new file mode 100644 index 000000000..ecae0b7c2 --- /dev/null +++ b/Source/cmExportInstallFileGenerator.h @@ -0,0 +1,122 @@ +/*========================================================================= + + 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. + +=========================================================================*/ +#ifndef cmExportInstallFileGenerator_h +#define cmExportInstallFileGenerator_h + +#include "cmExportFileGenerator.h" + +class cmInstallExportGenerator; +class cmInstallFilesGenerator; +class cmInstallTargetGenerator; +class cmTargetExport; + +/** \class cmExportInstallFileGenerator + * \brief Generate a file exporting targets from an install tree. + * + * cmExportInstallFileGenerator generates files exporting targets from + * install an installation tree. The files are placed in a temporary + * location for installation by cmInstallExportGenerator. One main + * file is generated that creates the imported targets and loads + * per-configuration files. Target locations and settings for each + * configuration are written to these per-configuration files. After + * installation the main file loads the configurations that have been + * installed. + * + * This is used to implement the INSTALL(EXPORT) command. + */ +class cmExportInstallFileGenerator: public cmExportFileGenerator +{ +public: + /** Construct with the export installer that will install the + files. */ + cmExportInstallFileGenerator(cmInstallExportGenerator* iegen); + + /** Set the name of the export associated with the files. This is + the name given to the install(EXPORT) command mode. */ + void SetName(const char* name) { this->Name = name; } + + /** Set the set of targets to be exported. These are the targets + associated with the export name. */ + void SetExportSet(std::vector const* eSet) + { this->ExportSet = eSet; } + + /** Get the per-config file generated for each configuraiton. This + maps from the configuration name to the file temporary location + for installation. */ + std::map const& GetConfigImportFiles() + { return this->ConfigImportFiles; } +protected: + + // Implement virtual methods from the superclass. + virtual bool GenerateMainFile(std::ostream& os); + virtual void GenerateImportTargetsConfig(std::ostream& os, + const char* config, + std::string const& suffix); + virtual void ComplainAboutMissingTarget(cmTarget* target, const char* dep); + + /** Generate a per-configuration file for the targets. */ + bool GenerateImportFileConfig(const char* config); + + /** Fill in properties indicating installed file locations. */ + void SetImportLocationProperty(const char* config, + std::string const& suffix, + cmInstallTargetGenerator* itgen, + ImportPropertyMap& properties); + + void ComplainAboutImportPrefix(cmInstallTargetGenerator* itgen); + + cmInstallExportGenerator* InstallExportGenerator; + std::string Name; + std::vector const* ExportSet; + + std::string ImportPrefix; + + // The import file generated for each configuration. + std::map ConfigImportFiles; +}; + +/* + cmTargetExport is used in cmGlobalGenerator to collect the install + generators for targets associated with an export. +*/ +class cmTargetExport +{ +public: + cmTargetExport(cmTarget* tgt, + cmInstallTargetGenerator* archive, + cmInstallTargetGenerator* runtime, + cmInstallTargetGenerator* library, + cmInstallTargetGenerator* framework, + cmInstallTargetGenerator* bundle, + cmInstallFilesGenerator* headers + ) : Target(tgt), ArchiveGenerator(archive), + RuntimeGenerator(runtime), LibraryGenerator(library), + FrameworkGenerator(framework), BundleGenerator(bundle), + HeaderGenerator(headers) {} + + cmTarget* Target; + cmInstallTargetGenerator* ArchiveGenerator; + cmInstallTargetGenerator* RuntimeGenerator; + cmInstallTargetGenerator* LibraryGenerator; + cmInstallTargetGenerator* FrameworkGenerator; + cmInstallTargetGenerator* BundleGenerator; + cmInstallFilesGenerator* HeaderGenerator; +private: + cmTargetExport(); +}; + +#endif diff --git a/Source/cmGetPropertyCommand.cxx b/Source/cmGetPropertyCommand.cxx index 0a37b9749..7622cf231 100644 --- a/Source/cmGetPropertyCommand.cxx +++ b/Source/cmGetPropertyCommand.cxx @@ -260,9 +260,7 @@ bool cmGetPropertyCommand::HandleTargetMode() return false; } - if(cmTarget* target = - this->Makefile->GetLocalGenerator()->GetGlobalGenerator() - ->FindTarget(0, this->Name.c_str(), true)) + if(cmTarget* target = this->Makefile->FindTargetToUse(this->Name.c_str())) { return this->StoreResult(target->GetProperty(this->PropertyName.c_str())); } diff --git a/Source/cmGetTargetPropertyCommand.cxx b/Source/cmGetTargetPropertyCommand.cxx index 963e16ad5..97b4d7f50 100644 --- a/Source/cmGetTargetPropertyCommand.cxx +++ b/Source/cmGetTargetPropertyCommand.cxx @@ -28,9 +28,7 @@ bool cmGetTargetPropertyCommand std::string var = args[0].c_str(); const char* targetName = args[1].c_str(); - cmTarget *tgt = this->Makefile->GetLocalGenerator()->GetGlobalGenerator() - ->FindTarget(0, targetName, true); - if (tgt) + if(cmTarget* tgt = this->Makefile->FindTargetToUse(targetName)) { cmTarget& target = *tgt; const char *prop = target.GetProperty(args[2].c_str()); diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 448d4575c..6c1af5321 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -25,7 +25,7 @@ #include "cmMakefile.h" #include "cmSourceFile.h" #include "cmVersion.h" -#include "cmInstallExportGenerator.h" +#include "cmExportInstallFileGenerator.h" #include @@ -693,7 +693,6 @@ void cmGlobalGenerator::Configure() this->LocalGenerators.clear(); this->TargetDependencies.clear(); this->TotalTargets.clear(); - this->ImportedTotalTargets.clear(); this->LocalGeneratorToTargetMap.clear(); this->ProjectMap.clear(); @@ -1321,9 +1320,8 @@ cmLocalGenerator* cmGlobalGenerator::FindLocalGenerator(const char* start_dir) //---------------------------------------------------------------------------- -cmTarget* cmGlobalGenerator::FindTarget(const char* project, - const char* name, - bool useImportedTargets) +cmTarget* +cmGlobalGenerator::FindTarget(const char* project, const char* name) { // if project specific if(project) @@ -1331,8 +1329,7 @@ cmTarget* cmGlobalGenerator::FindTarget(const char* project, std::vector* gens = &this->ProjectMap[project]; for(unsigned int i = 0; i < gens->size(); ++i) { - cmTarget* ret = (*gens)[i]->GetMakefile()->FindTarget(name, - useImportedTargets); + cmTarget* ret = (*gens)[i]->GetMakefile()->FindTarget(name); if(ret) { return ret; @@ -1348,16 +1345,6 @@ cmTarget* cmGlobalGenerator::FindTarget(const char* project, { return i->second; } - - if ( useImportedTargets ) - { - std::map::iterator importedTarget = - this->ImportedTotalTargets.find ( name ); - if ( importedTarget != this->ImportedTotalTargets.end() ) - { - return importedTarget->second; - } - } } return 0; } @@ -1370,7 +1357,7 @@ bool cmGlobalGenerator::NameResolvesToFramework(const std::string& libname) return true; } - if(cmTarget* tgt = this->FindTarget(0, libname.c_str(), true)) + if(cmTarget* tgt = this->FindTarget(0, libname.c_str())) { if(tgt->GetType() == cmTarget::SHARED_LIBRARY && tgt->GetPropertyAsBool("FRAMEWORK")) @@ -1758,12 +1745,12 @@ cmGlobalGenerator::ConsiderTargetDepends(cmTarget const* depender, { // Check the target's makefile first. cmTarget const* dependee = - depender->GetMakefile()->FindTarget(dependee_name, false); + depender->GetMakefile()->FindTarget(dependee_name); // Then search globally. if(!dependee) { - dependee = this->FindTarget(0, dependee_name, false); + dependee = this->FindTarget(0, dependee_name); } // If not found then skip then the dependee. @@ -1842,14 +1829,8 @@ cmGlobalGenerator void cmGlobalGenerator::AddTarget(cmTargets::value_type &v) { - if (v.second.IsImported()) - { - this->ImportedTotalTargets[v.first] = &v.second; - } - else - { - this->TotalTargets[v.first] = &v.second; - } + assert(!v.second.IsImported()); + this->TotalTargets[v.first] = &v.second; } void cmGlobalGenerator::SetExternalMakefileProjectGenerator( diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index dfbf1ad21..4654a410a 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -186,9 +186,7 @@ public: void FindMakeProgram(cmMakefile*); ///! Find a target by name by searching the local generators. - cmTarget* FindTarget(const char* project, - const char* name, - bool useImportedTargets); + cmTarget* FindTarget(const char* project, const char* name); /** Determine if a name resolves to a framework on disk or a built target that is a framework. */ @@ -297,9 +295,8 @@ private: std::map ExtensionToLanguage; std::map LanguageToLinkerPreference; - // this is used to improve performance + // this is used to improve performance std::map TotalTargets; - std::map ImportedTotalTargets; cmExternalMakefileProjectGenerator* ExtraGenerator; diff --git a/Source/cmGlobalVisualStudio6Generator.cxx b/Source/cmGlobalVisualStudio6Generator.cxx index f7b4137b9..723ac7cf0 100644 --- a/Source/cmGlobalVisualStudio6Generator.cxx +++ b/Source/cmGlobalVisualStudio6Generator.cxx @@ -399,7 +399,7 @@ void cmGlobalVisualStudio6Generator::WriteProject(std::ostream& fout, if(j->first != dspname) { // is the library part of this DSW ? If so add dependency - if(this->FindTarget(0, j->first.c_str(), false)) + if(this->FindTarget(0, j->first.c_str())) { fout << "Begin Project Dependency\n"; fout << "Project_Dep_Name " << j->first.c_str() << "\n"; diff --git a/Source/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx index 2c35a418f..48efee19a 100644 --- a/Source/cmGlobalVisualStudio71Generator.cxx +++ b/Source/cmGlobalVisualStudio71Generator.cxx @@ -338,8 +338,7 @@ cmGlobalVisualStudio71Generator if(j->first != dspname) { // is the library part of this SLN ? If so add dependency - if(this->FindTarget(this->CurrentProject.c_str(), - j->first.c_str(), false)) + if(this->FindTarget(this->CurrentProject.c_str(), j->first.c_str())) { fout << "\t\t{" << this->GetGUID(j->first.c_str()) << "} = {" << this->GetGUID(j->first.c_str()) << "}\n"; diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index 1b95e2ac6..f3dcc60f3 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -611,8 +611,7 @@ cmGlobalVisualStudio7Generator if(j->first != dspname) { // is the library part of this SLN ? If so add dependency - if(this->FindTarget(this->CurrentProject.c_str(), - j->first.c_str(), false)) + if(this->FindTarget(this->CurrentProject.c_str(), j->first.c_str())) { std::string guid = this->GetGUID(j->first.c_str()); if(guid.size() == 0) diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx index 31b3ab468..7a394595d 100644 --- a/Source/cmGlobalVisualStudio8Generator.cxx +++ b/Source/cmGlobalVisualStudio8Generator.cxx @@ -138,7 +138,7 @@ void cmGlobalVisualStudio8Generator::Generate() mf->AddUtilityCommand(CMAKE_CHECK_BUILD_SYSTEM_TARGET, false, no_working_directory, no_depends, noCommandLines); - cmTarget* tgt = mf->FindTarget(CMAKE_CHECK_BUILD_SYSTEM_TARGET, false); + cmTarget* tgt = mf->FindTarget(CMAKE_CHECK_BUILD_SYSTEM_TARGET); if(!tgt) { cmSystemTools::Error("Error adding target " diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index 2319b047b..b824590cb 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -270,7 +270,7 @@ cmGlobalVisualStudioGenerator::FixUtilityDependsForTarget(cmTarget& target) target.GetUtilities().begin(); ui != target.GetUtilities().end(); ++ui) { - if(cmTarget* depTarget = this->FindTarget(0, ui->c_str(), false)) + if(cmTarget* depTarget = this->FindTarget(0, ui->c_str())) { if(depTarget->GetType() == cmTarget::STATIC_LIBRARY || depTarget->GetType() == cmTarget::SHARED_LIBRARY || @@ -315,7 +315,7 @@ cmGlobalVisualStudioGenerator::CreateUtilityDependTarget(cmTarget& target) this->CreateGUID(altNameStr.c_str()); // The intermediate target should depend on the original target. - if(cmTarget* alt = this->FindTarget(0, altNameStr.c_str(), false)) + if(cmTarget* alt = this->FindTarget(0, altNameStr.c_str())) { alt->AddUtility(target.GetName()); } @@ -371,7 +371,7 @@ cmGlobalVisualStudioGenerator::GetUtilityForTarget(cmTarget& target, { // The depender is a target that links. Lookup the dependee to // see if it provides an alternative dependency name. - if(cmTarget* depTarget = this->FindTarget(0, name, false)) + if(cmTarget* depTarget = this->FindTarget(0, name)) { // Check for an alternative name created by FixUtilityDepends. if(const char* altName = diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index cc7e7f8cf..19394f253 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -279,7 +279,7 @@ cmGlobalXCodeGenerator::AddExtraTargets(cmLocalGenerator* root, mf->AddUtilityCommand("ALL_BUILD", true, no_depends, no_working_directory, "echo", "Build all projects"); - cmTarget* allbuild = mf->FindTarget("ALL_BUILD", false); + cmTarget* allbuild = mf->FindTarget("ALL_BUILD"); // Add XCODE depend helper std::string dir = mf->GetCurrentOutputDirectory(); @@ -1346,7 +1346,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmTarget& target, std::string pnprefix; std::string pnbase; std::string pnsuffix; - target.GetFullName(pnprefix, pnbase, pnsuffix, configName); + target.GetFullNameComponents(pnprefix, pnbase, pnsuffix, configName); // Store the product name for all target types. buildSettings->AddAttribute("PRODUCT_NAME", @@ -2046,7 +2046,7 @@ void cmGlobalXCodeGenerator { // Add this dependency. cmTarget* t = this->FindTarget(this->CurrentProject.c_str(), - lib->first.c_str(), false); + lib->first.c_str()); cmXCodeObject* dptarget = this->FindXCodeTarget(t); if(dptarget) { @@ -2062,7 +2062,7 @@ void cmGlobalXCodeGenerator i != cmtarget->GetUtilities().end(); ++i) { cmTarget* t = this->FindTarget(this->CurrentProject.c_str(), - i->c_str(), false); + i->c_str()); // if the target is in this project then make target depend // on it. It may not be in this project if this is a sub // project from the top. diff --git a/Source/cmIncludeExternalMSProjectCommand.cxx b/Source/cmIncludeExternalMSProjectCommand.cxx index d1f2a78ae..2ab46c830 100644 --- a/Source/cmIncludeExternalMSProjectCommand.cxx +++ b/Source/cmIncludeExternalMSProjectCommand.cxx @@ -51,8 +51,7 @@ bool cmIncludeExternalMSProjectCommand // Create a target instance for this utility. cmTarget* target=this->Makefile->AddNewTarget(cmTarget::UTILITY, - utility_name.c_str(), - false); + utility_name.c_str()); target->SetProperty("EXCLUDE_FROM_ALL","FALSE"); std::vector no_outputs; cmCustomCommandLines commandLines; diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 915cd4b3f..6c742b948 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -300,7 +300,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector const& args) ++targetIt) { // Lookup this target in the current directory. - if(cmTarget* target=this->Makefile->FindTarget(targetIt->c_str(), false)) + if(cmTarget* target=this->Makefile->FindTarget(targetIt->c_str())) { // Found the target. Check its type. if(target->GetType() != cmTarget::EXECUTABLE && @@ -489,7 +489,7 @@ bool cmInstallCommand::HandleTargetsMode(std::vector const& args) // library. Install it to the archive destination if it // exists. if(dll_platform && !archiveArgs.GetDestination().empty() && - target.GetPropertyAsBool("ENABLE_EXPORTS")) + target.IsExecutableWithExports()) { // The import library uses the ARCHIVE properties. archiveGenerator = CreateInstallTargetGenerator(target, @@ -1069,13 +1069,9 @@ cmInstallCommand::HandleDirectoryMode(std::vector const& args) return false; } - // Compute destination path. - std::string dest; - cmInstallCommandArguments::ComputeDestination(destination, dest); - // Create the directory install generator. this->Makefile->AddInstallGenerator( - new cmInstallDirectoryGenerator(dirs, dest.c_str(), + new cmInstallDirectoryGenerator(dirs, destination, permissions_file.c_str(), permissions_dir.c_str(), configurations, @@ -1095,12 +1091,12 @@ bool cmInstallCommand::HandleExportMode(std::vector const& args) { // This is the EXPORT mode. cmInstallCommandArguments ica; - cmCAStringVector exports(&ica.Parser, "EXPORT"); - cmCAString prefix(&ica.Parser, "PREFIX", &ica.ArgumentGroup); + cmCAString exp(&ica.Parser, "EXPORT"); + cmCAString name_space(&ica.Parser, "NAMESPACE", &ica.ArgumentGroup); cmCAString filename(&ica.Parser, "FILE", &ica.ArgumentGroup); - exports.Follows(0); + exp.Follows(0); - ica.ArgumentGroup.Follows(&exports); + ica.ArgumentGroup.Follows(&exp); std::vector unknownArgs; ica.Parse(&args, &unknownArgs); @@ -1118,44 +1114,66 @@ bool cmInstallCommand::HandleExportMode(std::vector const& args) return false; } - std::string cmakeDir = this->Makefile->GetHomeOutputDirectory(); - cmakeDir += cmake::GetCMakeFilesDirectory(); - for(std::vector::const_iterator - exportIt = exports.GetVector().begin(); - exportIt != exports.GetVector().end(); - ++exportIt) + // Make sure there is a destination. + if(ica.GetDestination().empty()) { - const std::vector* exportSet = this-> - Makefile->GetLocalGenerator()->GetGlobalGenerator()-> - GetExportSet(exportIt->c_str()); - if (exportSet == 0) + // A destination is required. + cmOStringStream e; + e << args[0] << " given no DESTINATION!"; + this->SetError(e.str().c_str()); + return false; + } + + // Check the file name. + std::string fname = filename.GetString(); + if(fname.find_first_of(":/\\") != fname.npos) + { + cmOStringStream e; + e << args[0] << " given invalid export file name \"" << fname << "\". " + << "The FILE argument may not contain a path. " + << "Specify the path in the DESTINATION argument."; + this->SetError(e.str().c_str()); + return false; + } + + // Check the file extension. + if(!fname.empty() && + cmSystemTools::GetFilenameLastExtension(fname) != ".cmake") + { + cmOStringStream e; + e << args[0] << " given invalid export file name \"" << fname << "\". " + << "The FILE argument must specify a name ending in \".cmake\"."; + this->SetError(e.str().c_str()); + return false; + } + + // Construct the file name. + if(fname.empty()) + { + fname = exp.GetString(); + fname += ".cmake"; + + if(fname.find_first_of(":/\\") != fname.npos) { cmOStringStream e; - e << "EXPORT given unknown export name \"" << exportIt->c_str() << "\"."; + e << args[0] << " given export name \"" << exp.GetString() << "\". " + << "This name cannot be safely converted to a file name. " + << "Specify a different export name or use the FILE option to set " + << "a file name explicitly."; this->SetError(e.str().c_str()); return false; } - - // Create the export install generator. - cmInstallExportGenerator* exportGenerator = new cmInstallExportGenerator( - ica.GetDestination().c_str(), ica.GetPermissions().c_str(), - ica.GetConfigurations(),0 , filename.GetCString(), - prefix.GetCString(), cmakeDir.c_str()); - - if (exportGenerator->SetExportSet(exportIt->c_str(),exportSet)) - { - this->Makefile->AddInstallGenerator(exportGenerator); - } - else - { - cmOStringStream e; - e << "EXPORT failed, maybe a target is exported more than once."; - this->SetError(e.str().c_str()); - delete exportGenerator; - return false; - } } + // Create the export install generator. + cmInstallExportGenerator* exportGenerator = + new cmInstallExportGenerator( + exp.GetCString(), ica.GetDestination().c_str(), + ica.GetPermissions().c_str(), ica.GetConfigurations(), + ica.GetComponent().c_str(), fname.c_str(), + name_space.GetCString(), this->Makefile); + this->Makefile->AddInstallGenerator(exportGenerator); + return true; } diff --git a/Source/cmInstallCommand.h b/Source/cmInstallCommand.h index f0f69e049..0f5eb16fa 100644 --- a/Source/cmInstallCommand.h +++ b/Source/cmInstallCommand.h @@ -98,7 +98,7 @@ public: "file to be installed does not exist. " "\n" "The TARGETS signature:\n" - " install(TARGETS targets...\n" + " install(TARGETS targets... [EXPORT ]\n" " [[ARCHIVE|LIBRARY|RUNTIME]\n" " [DESTINATION ]\n" " [PERMISSIONS permissions...]\n" @@ -145,6 +145,12 @@ public: "On non-DLL platforms mySharedLib will be installed to /lib " "and /some/full/path." "\n" + "The EXPORT option associates the installed target files with an " + "export called . " + "It must appear before any RUNTIME, LIBRARY, or ARCHIVE options. " + "See documentation of the install(EXPORT ...) signature below for " + "details." + "\n" "Installing a target with EXCLUDE_FROM_ALL set to true has " "undefined behavior." "\n" @@ -248,6 +254,45 @@ public: "For example, the code\n" " install(CODE \"MESSAGE(\\\"Sample install message.\\\")\")\n" "will print a message during installation.\n" + "" + "The EXPORT signature:\n" + " install(EXPORT DESTINATION \n" + " [NAMESPACE ] [FILE .cmake]\n" + " [PERMISSIONS permissions...]\n" + " [CONFIGURATIONS [Debug|Release|...]]\n" + " [COMPONENT ])\n" + "The EXPORT form generates and installs a CMake file containing code " + "to import targets from the installation tree into another project. " + "Target installations are associated with the export " + "using the EXPORT option of the install(TARGETS ...) signature " + "documented above. The NAMESPACE option will prepend to " + "the target names as they are written to the import file. " + "By default the generated file will be called .cmake but " + "the FILE option may be used to specify a different name. The value " + "given to the FILE option must be a file name with the \".cmake\" " + "extension. " + "If a CONFIGURATIONS option is given then the file will only be " + "installed when one of the named configurations is installed. " + "Additionally, the generated import file will reference only the " + "matching target configurations. " + "If a COMPONENT option is specified that does not match that given " + "to the targets associated with the behavior is " + "undefined. " + "If a library target is included in the export but " + "a target to which it links is not included the behavior is " + "unspecified." + "\n" + "The EXPORT form is useful to help outside projects use targets built " + "and installed by the current project. For example, the code\n" + " install(TARGETS myexe EXPORT myproj DESTINATION bin)\n" + " install(EXPORT myproj NAMESPACE mp_ DESTINATION lib/myproj)\n" + "will install the executable myexe to /bin and code to import " + "it in the file \"/lib/myproj/myproj.cmake\". " + "An outside project may load this file with the include command " + "and reference the myexe executable from the installation tree using " + "the imported target name mp_myexe as if the target were built " + "in its own tree." + "\n" "NOTE: This command supercedes the INSTALL_TARGETS command and the " "target properties PRE_INSTALL_SCRIPT and POST_INSTALL_SCRIPT. " "It also replaces the FILES forms of the INSTALL_FILES and " diff --git a/Source/cmInstallCommandArguments.cxx b/Source/cmInstallCommandArguments.cxx index 9cba5bf89..506954c8b 100644 --- a/Source/cmInstallCommandArguments.cxx +++ b/Source/cmInstallCommandArguments.cxx @@ -44,9 +44,9 @@ cmInstallCommandArguments::cmInstallCommandArguments() const std::string& cmInstallCommandArguments::GetDestination() const { - if (!this->AbsDestination.empty()) + if (!this->DestinationString.empty()) { - return this->AbsDestination; + return this->DestinationString; } if (this->GenericArguments!=0) { @@ -128,8 +128,8 @@ bool cmInstallCommandArguments::Finalize() { return false; } - this->ComputeDestination(this->Destination.GetString(),this->AbsDestination); - + this->DestinationString = this->Destination.GetString(); + cmSystemTools::ConvertToUnixSlashes(this->DestinationString); return true; } @@ -174,23 +174,3 @@ bool cmInstallCommandArguments::CheckPermissions( // This is not a valid permission. return false; } - -//---------------------------------------------------------------------------- -void cmInstallCommandArguments::ComputeDestination(const std::string& inDest, - std::string& absDest) -{ - if((inDest.size()>0) && !(cmSystemTools::FileIsFullPath(inDest.c_str()))) - { - // Relative paths are treated with respect to the installation prefix. - absDest = "${CMAKE_INSTALL_PREFIX}/"; - absDest += inDest; - } - else - { - // Full paths are absolute. - absDest = inDest; - } - // Format the path nicely. Note this also removes trailing slashes. - cmSystemTools::ConvertToUnixSlashes(absDest); -} - diff --git a/Source/cmInstallCommandArguments.h b/Source/cmInstallCommandArguments.h index 430e5378d..7fc35a0c1 100644 --- a/Source/cmInstallCommandArguments.h +++ b/Source/cmInstallCommandArguments.h @@ -43,8 +43,6 @@ class cmInstallCommandArguments // once HandleDirectoryMode() is also switched to using // cmInstallCommandArguments then these two functions can become non-static // private member functions without arguments - static void ComputeDestination(const std::string& inDest, - std::string& absDest); static bool CheckPermissions(const std::string& onePerm, std::string& perm); cmCommandArgumentsHelper Parser; @@ -57,7 +55,7 @@ class cmInstallCommandArguments cmCAStringVector Configurations; cmCAEnabler Optional; - std::string AbsDestination; + std::string DestinationString; std::string PermissionsString; cmInstallCommandArguments* GenericArguments; diff --git a/Source/cmInstallDirectoryGenerator.cxx b/Source/cmInstallDirectoryGenerator.cxx index 350a4fdd2..110e6b628 100644 --- a/Source/cmInstallDirectoryGenerator.cxx +++ b/Source/cmInstallDirectoryGenerator.cxx @@ -48,8 +48,7 @@ cmInstallDirectoryGenerator::GenerateScriptActions(std::ostream& os, bool not_optional = false; const char* no_properties = 0; const char* no_rename = 0; - this->AddInstallRule(os, this->Destination.c_str(), - cmTarget::INSTALL_DIRECTORY, + this->AddInstallRule(os, cmTarget::INSTALL_DIRECTORY, this->Directories, not_optional, no_properties, this->FilePermissions.c_str(), diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx index ed04ebced..81971aaac 100644 --- a/Source/cmInstallExportGenerator.cxx +++ b/Source/cmInstallExportGenerator.cxx @@ -14,259 +14,198 @@ PURPOSE. See the above copyright notices for more information. =========================================================================*/ +#include "cmInstallExportGenerator.h" #include +#include "cmake.h" #include "cmInstallTargetGenerator.h" #include "cmGeneratedFileStream.h" #include "cmTarget.h" +#include "cmMakefile.h" +#include "cmLocalGenerator.h" +#include "cmGlobalGenerator.h" -#include "cmInstallExportGenerator.h" #include "cmInstallFilesGenerator.h" +#include "cmExportInstallFileGenerator.h" + +//---------------------------------------------------------------------------- cmInstallExportGenerator::cmInstallExportGenerator( + const char* name, const char* destination, const char* file_permissions, std::vector const& configurations, const char* component, - const char* filename, const char* prefix, const char* tempOutputDir) + const char* filename, const char* name_space, + cmMakefile* mf) :cmInstallGenerator(destination, configurations, component) + ,Name(name) ,FilePermissions(file_permissions) - ,Filename(filename) - ,Prefix(prefix) - ,TempOutputDir(tempOutputDir) + ,FileName(filename) + ,Namespace(name_space) + ,Makefile(mf) { + this->EFGen = new cmExportInstallFileGenerator(this); } -/* Helper function which adds the install locations from the generator -to the properties for this target. -*/ -bool cmInstallExportGenerator::AddInstallLocations(cmTargetWithProperties* twp, - cmInstallTargetGenerator* generator, - const char* prefix) +//---------------------------------------------------------------------------- +cmInstallExportGenerator::~cmInstallExportGenerator() { - if (generator == 0) // nothing to do + delete this->EFGen; +} + +//---------------------------------------------------------------------------- +void cmInstallExportGenerator::ComputeTempDir() +{ + // Choose a temporary directory in which to generate the import + // files to be installed. + this->TempDir = this->Makefile->GetCurrentOutputDirectory(); + this->TempDir += cmake::GetCMakeFilesDirectory(); + this->TempDir += "/Export"; + if(this->Destination.empty()) { - return true; + return; + } + else + { + this->TempDir += "/"; } - if (prefix == 0) + // Enforce a maximum length. + bool useMD5 = false; +#if defined(_WIN32) || defined(__CYGWIN__) + std::string::size_type const max_total_len = 250; +#else + std::string::size_type const max_total_len = 1000; +#endif + if(this->TempDir.size() < max_total_len) { - prefix = ""; - } - - const std::vector& configs = generator->GetConfigurations(); - if (configs.empty()) - { - std::string propertyName = prefix; - propertyName += "LOCATION"; - // check that this property doesn't exist yet and add it then - if (twp->Properties.find(propertyName.c_str()) == twp->Properties.end()) + // Keep the total path length below the limit. + std::string::size_type max_len = max_total_len - this->TempDir.size(); + if(this->Destination.size() > max_len) { - std::string destinationFilename = generator->GetDestination(); - destinationFilename += "/"; - destinationFilename += generator->GetInstallFilename(0); - twp->Properties[propertyName.c_str()] = destinationFilename; - } - else - { - return false; + useMD5 = true; } } else { - for(std::vector::const_iterator configIt = configs.begin(); - configIt != configs.end(); - ++configIt) - { - std::string propertyName = configIt->c_str(); - propertyName += "_"; - propertyName += prefix; - propertyName += "LOCATION"; - // check that this property doesn't exist yet and add it then - if (twp->Properties.find(propertyName.c_str()) == twp->Properties.end()) - { - std::string destinationFilename = generator->GetDestination(); - destinationFilename += "/"; - destinationFilename +=generator->GetInstallFilename(configIt->c_str()); - twp->Properties[propertyName.c_str()] = destinationFilename; - } - else - { - return false; - } - } + useMD5 = true; + } + if(useMD5) + { + // Replace the destination path with a hash to keep it short. + this->TempDir += + cmSystemTools::ComputeStringMD5(this->Destination.c_str()); + } + else + { + std::string dest = this->Destination; + // Avoid unix full paths. + if(dest[0] == '/') + { + dest[0] = '_'; + } + // Avoid windows full paths by removing colons. + cmSystemTools::ReplaceString(dest, ":", "_"); + // Avoid relative paths that go up the tree. + cmSystemTools::ReplaceString(dest, "../", "__/"); + // Avoid spaces. + cmSystemTools::ReplaceString(dest, " ", "_"); + this->TempDir += dest; } - return true; -} - - -bool cmInstallExportGenerator::AddInstallLocations(cmTargetWithProperties* twp, - cmInstallFilesGenerator* generator, - const char* propertyName) -{ - if (generator == 0) // nothing to do - { - return true; - } - - if ((propertyName == 0) || (*propertyName == '\0')) - { - return false; - } - - // check that this property doesn't exist yet and add it then - if (twp->Properties.find(propertyName) == twp->Properties.end()) - { - twp->Properties[propertyName] = generator->GetDestination(); - return true; - } - - return false; -} - - -bool cmInstallExportGenerator::SetExportSet(const char* name, - const std::vector* set) -{ - if ((name == 0) || (*name == 0) || (set==0)) - { - return false; - } - - this->Name = name; - - /* iterate over all targets in the set. - If a cmTargetWithProperties with the same name already exists in this - generator, add the new properties to it. If the property already exists, - fail with an error. - If no cmTargetWithProperties exists, create a new one. - */ - for(std::vector::const_iterator it=set->begin(); - it != set->end(); - ++it) - { - std::string targetName = (*it)->Target->GetName(); - - cmTargetWithProperties* targetWithProps = 0; - for(unsigned int i=0; iTargets.size(); i++) - { - if (targetName == this->Targets[i]->Target->GetName()) - { - targetWithProps = this->Targets[i]; - break; - } - } - - if (targetWithProps == 0) - { - targetWithProps = new cmTargetWithProperties((*it)->Target); - this->Targets.push_back(targetWithProps); - } - - if (this->AddInstallLocations(targetWithProps, (*it)->ArchiveGenerator, - "ARCHIVE_") == false) - { - return false; - } - if (this->AddInstallLocations(targetWithProps, (*it)->RuntimeGenerator, - "") == false) - { - return false; - } - if (this->AddInstallLocations(targetWithProps, (*it)->LibraryGenerator, - "LIBRARY_") == false) - { - return false; - } - if (this->AddInstallLocations(targetWithProps, (*it)->HeaderGenerator, - "PUBLIC_HEADER_LOCATION") == false) - { - return false; - } - } - - return true; } +//---------------------------------------------------------------------------- void cmInstallExportGenerator::GenerateScript(std::ostream& os) { - // for the case that somebody exports the same set with the same file name - // to different locations make the temp filename unique - char buf[64]; - sprintf(buf, "%p", this); - this->ExportFilename = this->TempOutputDir; - this->ExportFilename += "/"; - this->ExportFilename += this->Filename; - this->ExportFilename += "."; - this->ExportFilename += buf; + // Get the export set requested. + ExportSet const* exportSet = + this->Makefile->GetLocalGenerator()->GetGlobalGenerator() + ->GetExportSet(this->Name.c_str()); - cmGeneratedFileStream exportFileStream(this->ExportFilename.c_str()); - if(!exportFileStream) + // Skip empty sets. + if(!exportSet) { + cmOStringStream e; + e << "INSTALL(EXPORT) given unknown export \"" << this->Name << "\""; + cmSystemTools::Error(e.str().c_str()); return; } - /* for every target add the IMPORT statements and set the properties - of the target. */ - for(std::vector::const_iterator - targetIt = this->Targets.begin(); - targetIt != this->Targets.end(); - ++targetIt) + // Create the temporary directory in which to store the files. + this->ComputeTempDir(); + cmSystemTools::MakeDirectory(this->TempDir.c_str()); + + // Construct a temporary location for the file. + this->MainImportFile = this->TempDir; + this->MainImportFile += "/"; + this->MainImportFile += this->FileName; + + // Generate the import file for this export set. + this->EFGen->SetName(this->Name.c_str()); + this->EFGen->SetExportSet(exportSet); + this->EFGen->SetExportFile(this->MainImportFile.c_str()); + this->EFGen->SetNamespace(this->Namespace.c_str()); + if(this->ConfigurationTypes->empty()) { - switch ((*targetIt)->Target->GetType()) - { - case cmTarget::EXECUTABLE: - exportFileStream << "ADD_EXECUTABLE(" << this->Prefix.c_str() - << (*targetIt)->Target->GetName() - << " IMPORT )\n"; - break; - case cmTarget::STATIC_LIBRARY: - exportFileStream << "ADD_LIBRARY(" << this->Prefix.c_str() - << (*targetIt)->Target->GetName() - << " STATIC IMPORT )\n"; - break; - case cmTarget::SHARED_LIBRARY: - exportFileStream << "ADD_LIBRARY(" << this->Prefix.c_str() - << (*targetIt)->Target->GetName() - << " SHARED IMPORT )\n"; - break; - case cmTarget::MODULE_LIBRARY: - exportFileStream << "ADD_LIBRARY(" << this->Prefix.c_str() - << (*targetIt)->Target->GetName() - << " MODULE IMPORT )\n"; - break; - default: // should never happen - break; - } - - exportFileStream << "SET_TARGET_PROPERTIES ( " << this->Prefix.c_str() - << (*targetIt)->Target->GetName() << " PROPERTIES \n"; - - for (std::map::const_iterator - propIt = (*targetIt)->Properties.begin(); - propIt != (*targetIt)->Properties.end(); - ++propIt) - { - exportFileStream << " " << propIt->first - << " \"" << propIt->second << "\"\n"; - } - exportFileStream << " )\n\n"; + if(this->ConfigurationName && *this->ConfigurationName) + { + this->EFGen->AddConfiguration(this->ConfigurationName); + } + else + { + this->EFGen->AddConfiguration(""); + } } + else + { + for(std::vector::const_iterator + ci = this->ConfigurationTypes->begin(); + ci != this->ConfigurationTypes->end(); ++ci) + { + this->EFGen->AddConfiguration(ci->c_str()); + } + } + this->EFGen->GenerateImportFile(); // Perform the main install script generation. this->cmInstallGenerator::GenerateScript(os); } +//---------------------------------------------------------------------------- +void +cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os, + Indent const& indent) +{ + // Create the main install rules first. + this->cmInstallGenerator::GenerateScriptConfigs(os, indent); + + // Now create a configuration-specific install rule for the import + // file of each configuration. + std::vector files; + for(std::map::const_iterator + i = this->EFGen->GetConfigImportFiles().begin(); + i != this->EFGen->GetConfigImportFiles().end(); ++i) + { + files.push_back(i->second); + std::string config_test = this->CreateConfigTest(i->first.c_str()); + os << indent << "IF(" << config_test << ")\n"; + this->AddInstallRule(os, cmTarget::INSTALL_FILES, files, false, 0, + this->FilePermissions.c_str(), 0, 0, 0, + indent.Next()); + os << indent << "ENDIF(" << config_test << ")\n"; + files.clear(); + } +} + //---------------------------------------------------------------------------- void cmInstallExportGenerator::GenerateScriptActions(std::ostream& os, Indent const& indent) { - // install rule for the file created above - std::vector exportFile; - exportFile.push_back(this->ExportFilename); - this->AddInstallRule(os, this->Destination.c_str(), cmTarget::INSTALL_FILES, - exportFile, false, 0, - this->FilePermissions.c_str(), - 0, this->Filename.c_str(), 0, indent); + // Install the main export file. + std::vector files; + files.push_back(this->MainImportFile); + this->AddInstallRule(os, cmTarget::INSTALL_FILES, files, false, 0, + this->FilePermissions.c_str(), 0, 0, 0, indent); } diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h index ee0ae3e1f..24f4aaca8 100644 --- a/Source/cmInstallExportGenerator.h +++ b/Source/cmInstallExportGenerator.h @@ -19,42 +19,12 @@ #include "cmInstallGenerator.h" -class cmTarget; - - -class cmInstallTargetGenerator; +class cmExportInstallFileGenerator; class cmInstallFilesGenerator; - -/* cmInstallExportTarget is used in cmGlobalGenerator to collect the -install generators for the exported targets. These are then used by the -cmInstallExportGenerator. -*/ -class cmTargetExport -{ -public: - cmTargetExport(cmTarget* tgt, - cmInstallTargetGenerator* archive, - cmInstallTargetGenerator* runtime, - cmInstallTargetGenerator* library, - cmInstallTargetGenerator* framework, - cmInstallTargetGenerator* bundle, - cmInstallFilesGenerator* headers - ) : Target(tgt), ArchiveGenerator(archive), - RuntimeGenerator(runtime), LibraryGenerator(library), - FrameworkGenerator(framework), BundleGenerator(bundle), - HeaderGenerator(headers) {} - - cmTarget* Target; - cmInstallTargetGenerator* ArchiveGenerator; - cmInstallTargetGenerator* RuntimeGenerator; - cmInstallTargetGenerator* LibraryGenerator; - cmInstallTargetGenerator* FrameworkGenerator; - cmInstallTargetGenerator* BundleGenerator; - cmInstallFilesGenerator* HeaderGenerator; -private: - cmTargetExport(); -}; - +class cmInstallTargetGenerator; +class cmTarget; +class cmTargetExport; +class cmMakefile; /** \class cmInstallExportGenerator * \brief Generate rules for creating an export files. @@ -62,45 +32,33 @@ private: class cmInstallExportGenerator: public cmInstallGenerator { public: - cmInstallExportGenerator(const char* dest, const char* file_permissions, + cmInstallExportGenerator(const char* name, + const char* dest, const char* file_permissions, const std::vector& configurations, const char* component, - const char* filename, const char* prefix, - const char* tempOutputDir); - - bool SetExportSet(const char* name, - const std::vector* exportSet); + const char* filename, const char* name_space, + cmMakefile* mf); + ~cmInstallExportGenerator(); protected: - // internal class which collects all the properties which will be set - // in the export file for the target - class cmTargetWithProperties - { - public: - cmTargetWithProperties(cmTarget* target):Target(target) {} - cmTarget* Target; - std::map Properties; - private: - cmTargetWithProperties(); - }; + typedef std::vector ExportSet; typedef cmInstallGeneratorIndent Indent; virtual void GenerateScript(std::ostream& os); + virtual void GenerateScriptConfigs(std::ostream& os, Indent const& indent); virtual void GenerateScriptActions(std::ostream& os, Indent const& indent); - static bool AddInstallLocations(cmTargetWithProperties *twp, - cmInstallTargetGenerator* generator, - const char* prefix); - static bool AddInstallLocations(cmTargetWithProperties* twp, - cmInstallFilesGenerator* generator, - const char* propertyName); + void GenerateImportFile(ExportSet const* exportSet); + void GenerateImportFile(const char* config, ExportSet const* exportSet); + void ComputeTempDir(); std::string Name; std::string FilePermissions; - std::string Filename; - std::string Prefix; - std::string TempOutputDir; - std::string ExportFilename; + std::string FileName; + std::string Namespace; + cmMakefile* Makefile; - std::vector Targets; + std::string TempDir; + std::string MainImportFile; + cmExportInstallFileGenerator* EFGen; }; #endif diff --git a/Source/cmInstallFilesCommand.cxx b/Source/cmInstallFilesCommand.cxx index 4b09aceed..36e7e2581 100644 --- a/Source/cmInstallFilesCommand.cxx +++ b/Source/cmInstallFilesCommand.cxx @@ -119,9 +119,8 @@ void cmInstallFilesCommand::FinalPass() void cmInstallFilesCommand::CreateInstallGenerator() const { // Construct the destination. This command always installs under - // the prefix. - std::string destination = "${CMAKE_INSTALL_PREFIX}"; - destination += this->Destination; + // the prefix. We skip the leading slash given by the user. + std::string destination = this->Destination.substr(1); cmSystemTools::ConvertToUnixSlashes(destination); // Use a file install generator. diff --git a/Source/cmInstallFilesGenerator.cxx b/Source/cmInstallFilesGenerator.cxx index ef7eac7f5..843e31593 100644 --- a/Source/cmInstallFilesGenerator.cxx +++ b/Source/cmInstallFilesGenerator.cxx @@ -47,7 +47,7 @@ void cmInstallFilesGenerator::GenerateScriptActions(std::ostream& os, // Write code to install the files. const char* no_properties = 0; const char* no_dir_permissions = 0; - this->AddInstallRule(os, this->Destination.c_str(), + this->AddInstallRule(os, (this->Programs ? cmTarget::INSTALL_PROGRAMS : cmTarget::INSTALL_FILES), diff --git a/Source/cmInstallGenerator.cxx b/Source/cmInstallGenerator.cxx index 928b241c3..cd159651a 100644 --- a/Source/cmInstallGenerator.cxx +++ b/Source/cmInstallGenerator.cxx @@ -55,7 +55,6 @@ cmInstallGenerator void cmInstallGenerator ::AddInstallRule( std::ostream& os, - const char* dest, int type, std::vector const& files, bool optional /* = false */, @@ -81,6 +80,7 @@ void cmInstallGenerator default: stype = "FILE"; break; } os << indent; + std::string dest = this->GetInstallDestination(); os << "FILE(INSTALL DESTINATION \"" << dest << "\" TYPE " << stype.c_str(); if(optional) { @@ -238,3 +238,40 @@ void cmInstallGenerator::GenerateScriptActions(std::ostream&, Indent const&) { // No actions for this generator. } + +//---------------------------------------------------------------------------- +bool cmInstallGenerator::InstallsForConfig(const char* config) +{ + // If this is not a configuration-specific rule then we install. + if(this->Configurations.empty()) + { + return true; + } + + // This is a configuration-specific rule. Check if the config + // matches this rule. + std::string config_upper = cmSystemTools::UpperCase(config?config:""); + for(std::vector::const_iterator i = + this->Configurations.begin(); + i != this->Configurations.end(); ++i) + { + if(cmSystemTools::UpperCase(*i) == config_upper) + { + return true; + } + } + return false; +} + +//---------------------------------------------------------------------------- +std::string cmInstallGenerator::GetInstallDestination() const +{ + std::string result; + if(!this->Destination.empty() && + !cmSystemTools::FileIsFullPath(this->Destination.c_str())) + { + result = "${CMAKE_INSTALL_PREFIX}/"; + } + result += this->Destination; + return result; +} diff --git a/Source/cmInstallGenerator.h b/Source/cmInstallGenerator.h index 3abcf8649..904bb2be9 100644 --- a/Source/cmInstallGenerator.h +++ b/Source/cmInstallGenerator.h @@ -62,8 +62,8 @@ public: void Generate(std::ostream& os, const char* config, std::vector const& configurationTypes); - static void AddInstallRule( - std::ostream& os, const char* dest, int type, + void AddInstallRule( + std::ostream& os, int type, std::vector const& files, bool optional = false, const char* properties = 0, @@ -78,6 +78,14 @@ public: { return this->Destination.c_str(); } const std::vector& GetConfigurations() const { return this->Configurations; } + + /** Get the install destination as it should appear in the + installation script. */ + std::string GetInstallDestination() const; + + /** Test if this generator installs something for a given configuration. */ + bool InstallsForConfig(const char*); + protected: typedef cmInstallGeneratorIndent Indent; virtual void GenerateScript(std::ostream& os); diff --git a/Source/cmInstallProgramsCommand.cxx b/Source/cmInstallProgramsCommand.cxx index 8a29665ee..013728a28 100644 --- a/Source/cmInstallProgramsCommand.cxx +++ b/Source/cmInstallProgramsCommand.cxx @@ -80,9 +80,8 @@ void cmInstallProgramsCommand::FinalPass() } // Construct the destination. This command always installs under - // the prefix. - std::string destination = "${CMAKE_INSTALL_PREFIX}"; - destination += this->Destination; + // the prefix. We skip the leading slash given by the user. + std::string destination = this->Destination.substr(1); cmSystemTools::ConvertToUnixSlashes(destination); // Use a file install generator. diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx index d6a934516..8a56329ba 100644 --- a/Source/cmInstallTargetGenerator.cxx +++ b/Source/cmInstallTargetGenerator.cxx @@ -113,22 +113,11 @@ void cmInstallTargetGenerator::GenerateScriptForConfig(std::ostream& os, if(config && *config) { - std::string config_upper = cmSystemTools::UpperCase(config); // Skip this configuration for config-specific installation that // does not match it. - if(!this->Configurations.empty()) + if(!this->InstallsForConfig(config)) { - bool found = false; - for(std::vector::const_iterator i = - this->Configurations.begin(); - !found && i != this->Configurations.end(); ++i) - { - found = found || (cmSystemTools::UpperCase(*i) == config_upper); - } - if(!found) - { - return; - } + return; } // Generate a per-configuration block. @@ -154,7 +143,7 @@ cmInstallTargetGenerator Indent const& indent) { // Compute the full path to the main installed file for this target. - std::string toInstallPath = this->Destination; + std::string toInstallPath = this->GetInstallDestination(); toInstallPath += "/"; toInstallPath += this->GetInstallFilename(this->Target, config, this->ImportLibrary, false); @@ -279,7 +268,7 @@ cmInstallTargetGenerator const char* no_rename = 0; const char* no_properties = 0; bool optional = this->Optional || this->ImportLibrary; - this->AddInstallRule(os, this->Destination.c_str(), type, files, + this->AddInstallRule(os, type, files, optional, no_properties, this->FilePermissions.c_str(), no_dir_permissions, no_rename, literal_args.c_str(), @@ -412,7 +401,7 @@ cmInstallTargetGenerator { if(cmTarget* tgt = this->Target->GetMakefile()-> GetLocalGenerator()->GetGlobalGenerator()-> - FindTarget(0, lib.c_str(), false)) + FindTarget(0, lib.c_str())) { if(tgt->GetType() == cmTarget::SHARED_LIBRARY) { diff --git a/Source/cmInstallTargetGenerator.h b/Source/cmInstallTargetGenerator.h index e37a5ef01..01d36bb83 100644 --- a/Source/cmInstallTargetGenerator.h +++ b/Source/cmInstallTargetGenerator.h @@ -40,6 +40,9 @@ public: static std::string GetInstallFilename(cmTarget*target, const char* config, bool implib, bool useSOName); + cmTarget* GetTarget() const { return this->Target; } + bool IsImportLibrary() const { return this->ImportLibrary; } + protected: typedef cmInstallGeneratorIndent Indent; virtual void GenerateScript(std::ostream& os); diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index f49e1bd25..481257256 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -1809,7 +1809,7 @@ std::string cmLocalGenerator::GetRealDependency(const char* inName, } // Look for a CMake target with the given name. - if(cmTarget* target = this->GlobalGenerator->FindTarget(0,name.c_str(),true)) + if(cmTarget* target = this->Makefile->FindTargetToUse(name.c_str())) { // make sure it is not just a coincidence that the target name // found is part of the inName @@ -1876,7 +1876,7 @@ std::string cmLocalGenerator::GetRealLocation(const char* inName, std::string outName=inName; // Look for a CMake target with the given name, which is an executable // and which can be run - cmTarget* target = this->GlobalGenerator->FindTarget(0, inName, true); + cmTarget* target = this->Makefile->FindTargetToUse(inName); if ((target != 0) && (target->GetType() == cmTarget::EXECUTABLE) && ((this->Makefile->IsOn("CMAKE_CROSSCOMPILING") == false) @@ -2348,8 +2348,8 @@ cmLocalGenerator { // Compute the full install destination. Note that converting // to unix slashes also removes any trailing slash. - std::string destination = "${CMAKE_INSTALL_PREFIX}"; - destination += l->second.GetInstallPath(); + // We also skip over the leading slash given by the user. + std::string destination = l->second.GetInstallPath().substr(1); cmSystemTools::ConvertToUnixSlashes(destination); // Generate the proper install generator for this target type. @@ -2372,8 +2372,8 @@ cmLocalGenerator // destination. cmInstallTargetGenerator g1(l->second, destination.c_str(), true); g1.Generate(os, config, configurationTypes); - destination = "${CMAKE_INSTALL_PREFIX}"; - destination += l->second.GetRuntimeInstallPath(); + // We also skip over the leading slash given by the user. + destination = l->second.GetRuntimeInstallPath().substr(1); cmSystemTools::ConvertToUnixSlashes(destination); cmInstallTargetGenerator g2(l->second, destination.c_str(), false); g2.Generate(os, config, configurationTypes); diff --git a/Source/cmLocalVisualStudio6Generator.cxx b/Source/cmLocalVisualStudio6Generator.cxx index b462cd3ce..258d02732 100644 --- a/Source/cmLocalVisualStudio6Generator.cxx +++ b/Source/cmLocalVisualStudio6Generator.cxx @@ -1061,8 +1061,7 @@ void cmLocalVisualStudio6Generator // Compute the proper name to use to link this library. std::string lib; std::string libDebug; - cmTarget* tgt = this->GlobalGenerator->FindTarget(0, j->first.c_str(), - false); + cmTarget* tgt = this->GlobalGenerator->FindTarget(0, j->first.c_str()); if(tgt) { lib = cmSystemTools::GetFilenameWithoutExtension diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index d75e9eb95..1abd467b4 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -37,6 +37,8 @@ #include +#include + #include // for isspace // default is not to be building executables @@ -176,6 +178,12 @@ cmMakefile::~cmMakefile() { delete *i; } + for(std::vector::iterator + i = this->ImportedTargetsOwned.begin(); + i != this->ImportedTargetsOwned.end(); ++i) + { + delete *i; + } for(unsigned int i=0; i < this->UsedCommands.size(); i++) { delete this->UsedCommands[i]; @@ -824,7 +832,7 @@ void cmMakefile::AddUtilityCommand(const char* utilityName, bool escapeOldStyle, const char* comment) { // Create a target instance for this utility. - cmTarget* target = this->AddNewTarget(cmTarget::UTILITY, utilityName, false); + cmTarget* target = this->AddNewTarget(cmTarget::UTILITY, utilityName); if (excludeFromAll) { target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); @@ -1005,7 +1013,7 @@ void cmMakefile::AddLinkLibraryForTarget(const char *target, if ( i != this->Targets.end()) { cmTarget* tgt = - this->GetCMakeInstance()->GetGlobalGenerator()->FindTarget(0,lib,false); + this->GetCMakeInstance()->GetGlobalGenerator()->FindTarget(0,lib); if(tgt) { bool allowModules = true; @@ -1018,8 +1026,7 @@ void cmMakefile::AddLinkLibraryForTarget(const char *target, // if it is not a static or shared library then you can not link to it if(!((tgt->GetType() == cmTarget::STATIC_LIBRARY) || (tgt->GetType() == cmTarget::SHARED_LIBRARY) || - (tgt->GetType() == cmTarget::EXECUTABLE && - tgt->GetPropertyAsBool("ENABLE_EXPORTS")))) + tgt->IsExecutableWithExports())) { cmOStringStream e; e << "Attempt to add link target " << lib << " of type: " @@ -1162,6 +1169,9 @@ void cmMakefile::InitializeFromParent() // Copy include regular expressions. this->IncludeFileRegularExpression = parent->IncludeFileRegularExpression; this->ComplainFileRegularExpression = parent->ComplainFileRegularExpression; + + // Imported targets. + this->ImportedTargets = parent->ImportedTargets; } void cmMakefile::ConfigureSubDirectory(cmLocalGenerator *lg2) @@ -1467,7 +1477,7 @@ void cmMakefile::AddLibrary(const char* lname, cmTarget::TargetType type, type = cmTarget::STATIC_LIBRARY; } - cmTarget* target = this->AddNewTarget(type, lname, false); + cmTarget* target = this->AddNewTarget(type, lname); // Clear its dependencies. Otherwise, dependencies might persist // over changes in CMakeLists.txt, making the information stale and // hence useless. @@ -1484,7 +1494,7 @@ cmTarget* cmMakefile::AddExecutable(const char *exeName, const std::vector &srcs, bool excludeFromAll) { - cmTarget* target = this->AddNewTarget(cmTarget::EXECUTABLE, exeName, false); + cmTarget* target = this->AddNewTarget(cmTarget::EXECUTABLE, exeName); if(excludeFromAll) { target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); @@ -1494,26 +1504,16 @@ cmTarget* cmMakefile::AddExecutable(const char *exeName, return target; } - -cmTarget* cmMakefile::AddNewTarget(cmTarget::TargetType type, - const char* name, - bool isImported) +//---------------------------------------------------------------------------- +cmTarget* +cmMakefile::AddNewTarget(cmTarget::TargetType type, const char* name) { cmTargets::iterator it; cmTarget target; target.SetType(type, name); target.SetMakefile(this); - if (isImported) - { - target.MarkAsImported(); - it=this->ImportedTargets.insert( - cmTargets::value_type(target.GetName(), target)).first; - } - else - { - it=this->Targets.insert( - cmTargets::value_type(target.GetName(), target)).first; - } + it=this->Targets.insert( + cmTargets::value_type(target.GetName(), target)).first; this->LocalGenerator->GetGlobalGenerator()->AddTarget(*it); return &it->second; } @@ -2869,7 +2869,7 @@ bool cmMakefile::GetPropertyAsBool(const char* prop) } -cmTarget* cmMakefile::FindTarget(const char* name, bool useImportedTargets) +cmTarget* cmMakefile::FindTarget(const char* name) { cmTargets& tgts = this->GetTargets(); @@ -2879,15 +2879,6 @@ cmTarget* cmMakefile::FindTarget(const char* name, bool useImportedTargets) return &i->second; } - if (useImportedTargets) - { - cmTargets::iterator impTarget = this->ImportedTargets.find(name); - if (impTarget != this->ImportedTargets.end()) - { - return &impTarget->second; - } - } - return 0; } @@ -3091,3 +3082,37 @@ void cmMakefile::DefineProperties(cmake *cm) "The same concept applies to the default build of other generators.", false); } + +//---------------------------------------------------------------------------- +cmTarget* +cmMakefile::AddImportedTarget(const char* name, cmTarget::TargetType type) +{ + // Create the target. + cmsys::auto_ptr target(new cmTarget); + target->SetType(type, name); + target->SetMakefile(this); + target->MarkAsImported(); + + // Add to the set of available imported targets. + this->ImportedTargets[name] = target.get(); + + // Transfer ownership to this cmMakefile object. + this->ImportedTargetsOwned.push_back(target.get()); + return target.release(); +} + +//---------------------------------------------------------------------------- +cmTarget* cmMakefile::FindTargetToUse(const char* name) +{ + // Look for an imported target. These take priority because they + // are more local in scope and do not have to be globally unique. + std::map::const_iterator + imported = this->ImportedTargets.find(name); + if(imported != this->ImportedTargets.end()) + { + return imported->second; + } + + // Look for a target built in this project. + return this->LocalGenerator->GetGlobalGenerator()->FindTarget(0, name); +} diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index e511851e6..5664e2587 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -172,9 +172,10 @@ public: void AddDefineFlag(const char* definition); void RemoveDefineFlag(const char* definition); - cmTarget* AddNewTarget(cmTarget::TargetType type, - const char* name, - bool isImported); + /** Create a new imported target with the name and type given. */ + cmTarget* AddImportedTarget(const char* name, cmTarget::TargetType type); + + cmTarget* AddNewTarget(cmTarget::TargetType type, const char* name); /** * Add an executable to the build. @@ -436,10 +437,12 @@ public: * Get the list of targets, const version */ const cmTargets &GetTargets() const { return this->Targets; } - const cmTargets &GetImportedTargets() const { return this->ImportedTargets; } - cmTarget* FindTarget(const char* name, bool useImportedTargets); + cmTarget* FindTarget(const char* name); + /** Find a target to use in place of the given name. The target + returned may be imported or built within the project. */ + cmTarget* FindTargetToUse(const char* name); /** * Get a list of include directories in the build. @@ -766,7 +769,6 @@ protected: // libraries, classes, and executables cmTargets Targets; - cmTargets ImportedTargets; std::vector SourceFiles; // Tests @@ -842,6 +844,10 @@ private: // stack of list files being read std::deque ListFileStack; + + cmTarget* FindBasicTarget(const char* name); + std::vector ImportedTargetsOwned; + std::map ImportedTargets; }; diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx index 85d8565f3..e67b991f1 100644 --- a/Source/cmMakefileExecutableTargetGenerator.cxx +++ b/Source/cmMakefileExecutableTargetGenerator.cxx @@ -259,7 +259,7 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) } // Add symbol export flags if necessary. - if(this->Target->GetPropertyAsBool("ENABLE_EXPORTS")) + if(this->Target->IsExecutableWithExports()) { std::string export_flag_var = "CMAKE_EXE_EXPORTS_"; export_flag_var += linkLanguage; @@ -351,7 +351,7 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink) this->Makefile->GetRequiredDefinition(linkRuleVar.c_str()); std::vector commands1; cmSystemTools::ExpandListArgument(linkRule, real_link_commands); - if(this->Target->GetPropertyAsBool("ENABLE_EXPORTS")) + if(this->Target->IsExecutableWithExports()) { // If a separate rule for creating an import library is specified // add it now. diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx index 37bd5f94c..0c550efde 100644 --- a/Source/cmMakefileTargetGenerator.cxx +++ b/Source/cmMakefileTargetGenerator.cxx @@ -869,7 +869,7 @@ void cmMakefileTargetGenerator::WriteTargetDependRules() for(cmTarget::LinkLibraryVectorType::const_iterator j = libs.begin(); j != libs.end(); ++j) { - if(cmTarget const* linkee = gg->FindTarget(0, j->first.c_str(), false)) + if(cmTarget const* linkee = gg->FindTarget(0, j->first.c_str())) { if(emitted.insert(linkee).second) { @@ -1371,7 +1371,7 @@ void cmMakefileTargetGenerator { // Depend on other CMake targets. if(cmTarget* tgt = - this->GlobalGenerator->FindTarget(0, lib->first.c_str(), false)) + this->GlobalGenerator->FindTarget(0, lib->first.c_str())) { if(const char* location = tgt->GetLocation(this->LocalGenerator->ConfigurationName.c_str())) diff --git a/Source/cmSetPropertyCommand.cxx b/Source/cmSetPropertyCommand.cxx index 0922c1dd2..dbf71c2b8 100644 --- a/Source/cmSetPropertyCommand.cxx +++ b/Source/cmSetPropertyCommand.cxx @@ -220,9 +220,7 @@ bool cmSetPropertyCommand::HandleTargetMode() for(std::set::const_iterator ni = this->Names.begin(); ni != this->Names.end(); ++ni) { - if(cmTarget* target = - this->Makefile->GetLocalGenerator()->GetGlobalGenerator() - ->FindTarget(0, ni->c_str(), true)) + if(cmTarget* target = this->Makefile->FindTargetToUse(ni->c_str())) { // Handle the current target. if(!this->HandleTarget(target)) diff --git a/Source/cmSetTargetPropertiesCommand.cxx b/Source/cmSetTargetPropertiesCommand.cxx index ccbe0d5fa..26615dc3a 100644 --- a/Source/cmSetTargetPropertiesCommand.cxx +++ b/Source/cmSetTargetPropertiesCommand.cxx @@ -95,9 +95,7 @@ bool cmSetTargetPropertiesCommand std::vector &propertyPairs, cmMakefile *mf) { - cmTarget* target = - mf->GetLocalGenerator()->GetGlobalGenerator()->FindTarget(0, tname, true); - if ( target) + if(cmTarget* target = mf->FindTargetToUse(tname)) { // now loop through all the props and set them unsigned int k; diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 274c3af45..0fb6f7b27 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -153,6 +153,96 @@ void cmTarget::DefineProperties(cmake *cm) "A target property that can be set to override the suffix " "(such as \".lib\") on an import library name."); + cm->DefineProperty + ("IMPORTED", cmProperty::TARGET, + "Read-only indication of whether a target is IMPORTED.", + "The boolean value of this property is true for targets created with " + "the IMPORTED option to add_executable or add_library. " + "It is false for targets built within the project."); + + cm->DefineProperty + ("IMPORTED_CONFIGURATIONS", cmProperty::TARGET, + "Configurations provided for an IMPORTED target.", + "Lists configuration names available for an IMPORTED target. " + "The names correspond to configurations defined in the project from " + "which the target is imported. " + "If the importing project uses a different set of configurations " + "the names may be mapped using the MAP_IMPORTED_CONFIG_ " + "property. " + "Ignored for non-imported targets."); + + cm->DefineProperty + ("IMPORTED_ENABLE_EXPORTS", cmProperty::TARGET, + "Enable linking to an IMPORTED executable target.", + "Indicates that an IMPORTED executable target exports symbols for " + "use by plugin modules. " + "This is the imported target equivalent of the ENABLE_EXPORTS " + "property."); + + cm->DefineProperty + ("IMPORTED_IMPLIB", cmProperty::TARGET, + "Full path to the import library for an IMPORTED target.", + "Specifies the location of the \".lib\" part of a windows DLL. " + "Ignored for non-imported targets."); + + cm->DefineProperty + ("IMPORTED_IMPLIB_", cmProperty::TARGET, + "Per-configuration version of IMPORTED_IMPLIB property.", + "This property is used when loading settings for the " + "configuration of an imported target. " + "Configuration names correspond to those provided by the project " + "from which the target is imported."); + + cm->DefineProperty + ("IMPORTED_LINK_LIBRARIES", cmProperty::TARGET, + "Transitive link dependencies of an IMPORTED target.", + "Lists dependencies that must be linked when an IMPORTED library " + "target is linked to another target. " + "Ignored for non-imported targets."); + + cm->DefineProperty + ("IMPORTED_LINK_LIBRARIES_", cmProperty::TARGET, + "Per-configuration version of IMPORTED_LINK_LIBRARIES property.", + "This property is used when loading settings for the " + "configuration of an imported target. " + "Configuration names correspond to those provided by the project " + "from which the target is imported."); + + cm->DefineProperty + ("IMPORTED_LOCATION", cmProperty::TARGET, + "Full path to the main file on disk for an IMPORTED target.", + "Specifies the location of an IMPORTED target file on disk. " + "For executables this is the location of the executable file. " + "For static libraries and modules this is the location of the " + "library or module. " + "For shared libraries on non-DLL platforms this is the location of " + "the shared library. " + "For DLLs this is the location of the \".dll\" part of the library. " + "Ignored for non-imported targets."); + + cm->DefineProperty + ("IMPORTED_LOCATION_", cmProperty::TARGET, + "Per-configuration version of IMPORTED_LOCATION property.", + "This property is used when loading settings for the " + "configuration of an imported target. " + "Configuration names correspond to those provided by the project " + "from which the target is imported."); + + cm->DefineProperty + ("IMPORTED_SONAME", cmProperty::TARGET, + "The \"soname\" of an IMPORTED target of shared library type.", + "Specifies the \"soname\" embedded in an imported shared library. " + "This is meaningful only on platforms supporting the feature. " + "Ignored for non-imported targets."); + + cm->DefineProperty + ("IMPORTED_SONAME_", cmProperty::TARGET, + "Per-configuration version of IMPORTED_SONAME property.", + "This property is used when loading settings for the " + "configuration of an imported target. " + "Configuration names correspond to those provided by the project " + "from which the target is imported."); + cm->DefineProperty ("EXCLUDE_FROM_ALL", cmProperty::TARGET, "Exclude the target from the all target.", @@ -206,11 +296,37 @@ void cmTarget::DefineProperties(cmake *cm) cm->DefineProperty ("LOCATION", cmProperty::TARGET, - "Where a target will be written on disk.", - "A read only property on a target that indicates where that target " - "will be written. For libraries and executables this will be where " - "the file is written on disk. This property is computed based on a " - "number of other settings."); + "Deprecated. Use LOCATION_ or avoid altogether.", + "This property is provided for compatibility with CMake 2.4 and below. " + "It was meant to get the location of an executable target's output file " + "for use in add_custom_command. " + "In CMake 2.6 and above add_custom_command automatically recognizes a " + "target name in its COMMAND and DEPENDS options and computes the " + "target location. Therefore this property need not be used. " + "This property is not defined for IMPORTED targets because they " + "were not available in CMake 2.4 or below anyway."); + + cm->DefineProperty + ("LOCATION_", cmProperty::TARGET, + "Read-only property providing a target location on disk.", + "A read-only property that indicates where a target's main file is " + "located on disk for the configuration . " + "The property is defined only for library and executable targets."); + + cm->DefineProperty + ("MAP_IMPORTED_CONFIG_", cmProperty::TARGET, + "Map from project configuration to IMPORTED target's configuration.", + "List configurations of an imported target that may be used for " + "the current project's configuration. " + "Targets imported from another project may not provide the same set " + "of configuration names available in the current project. " + "Setting this property tells CMake what imported configurations are " + "suitable for use when building the configuration. " + "The first configuration in the list found to be provided by the " + "imported target is selected. If no matching configurations are " + "available the imported target is considered to be not found. " + "This property is ignored for non-imported targets.", + false /* TODO: make this chained */ ); cm->DefineProperty ("OUTPUT_NAME", cmProperty::TARGET, @@ -480,6 +596,25 @@ void cmTarget::SetMakefile(cmMakefile* mf) } } +//---------------------------------------------------------------------------- +bool cmTarget::IsExecutableWithExports() +{ + if(this->GetType() == cmTarget::EXECUTABLE) + { + if(this->IsImported()) + { + // The "IMPORTED_" namespace is used for properties exported + // from the project providing imported targets. + return this->GetPropertyAsBool("IMPORTED_ENABLE_EXPORTS"); + } + else + { + return this->GetPropertyAsBool("ENABLE_EXPORTS"); + } + } + return false; +} + //---------------------------------------------------------------------------- class cmTargetTraceDependencies { @@ -603,8 +738,7 @@ bool cmTargetTraceDependencies::IsUtility(std::string const& dep) } // Check for a non-imported target with this name. - if(cmTarget* t = - this->GlobalGenerator->FindTarget(0, util.c_str(), false)) + if(cmTarget* t = this->GlobalGenerator->FindTarget(0, util.c_str())) { // If we find the target and the dep was given as a full path, // then make sure it was not a full path to something else, and @@ -671,8 +805,7 @@ cmTargetTraceDependencies { std::string const& command = *cit->begin(); // Look for a non-imported target with this name. - if(cmTarget* t = - this->GlobalGenerator->FindTarget(0, command.c_str(), false)) + if(cmTarget* t = this->GlobalGenerator->FindTarget(0, command.c_str())) { if(t->GetType() == cmTarget::EXECUTABLE) { @@ -1287,6 +1420,13 @@ void cmTarget::SetProperty(const char* prop, const char* value) } this->Properties.SetProperty(prop, value, cmProperty::TARGET); + + // If imported information is being set, wipe out cached + // information. + if(this->IsImported() && strncmp(prop, "IMPORTED", 8) == 0) + { + this->ImportInfoMap.clear(); + } } //---------------------------------------------------------------------------- @@ -1297,6 +1437,13 @@ void cmTarget::AppendProperty(const char* prop, const char* value) return; } this->Properties.AppendProperty(prop, value, cmProperty::TARGET); + + // If imported information is being set, wipe out cached + // information. + if(this->IsImported() && strncmp(prop, "IMPORTED", 8) == 0) + { + this->ImportInfoMap.clear(); + } } //---------------------------------------------------------------------------- @@ -1319,10 +1466,11 @@ const char* cmTarget::GetDirectory(const char* config, bool implib) } //---------------------------------------------------------------------------- -const char* cmTarget::ImportedGetDirectory(const char* config, bool) +const char* cmTarget::ImportedGetDirectory(const char* config, bool implib) { - const char* location=this->GetLocation(config); - this->Directory = cmSystemTools::GetFilenamePath(location); + this->Directory = + cmSystemTools::GetFilenamePath( + this->ImportedGetFullPath(config, implib)); return this->Directory.c_str(); } @@ -1360,18 +1508,8 @@ const char* cmTarget::GetLocation(const char* config) //---------------------------------------------------------------------------- const char* cmTarget::ImportedGetLocation(const char* config) { - if ((config) && (strlen(config))) - { - std::string propertyName=cmSystemTools::UpperCase(config); - propertyName+="_LOCATION"; - const char* configLocation=this->GetProperty(propertyName.c_str()); - if ((configLocation) && (strlen(configLocation))) - { - return configLocation; - } - } - - return this->GetProperty("LOCATION"); + this->Location = this->ImportedGetFullPath(config, false); + return this->Location.c_str(); } //---------------------------------------------------------------------------- @@ -1484,38 +1622,41 @@ const char *cmTarget::GetProperty(const char* prop, return 0; } - // don't use GetLocation() for imported targets, because there this - // calls GetProperty() to get the location... - if (!this->IsImported()) + // Watch for special "computed" properties that are dependent on + // other properties or variables. Always recompute them. + if(this->GetType() == cmTarget::EXECUTABLE || + this->GetType() == cmTarget::STATIC_LIBRARY || + this->GetType() == cmTarget::SHARED_LIBRARY || + this->GetType() == cmTarget::MODULE_LIBRARY) { - // watch for special "computed" properties that are dependent on other - // properties or variables, always recompute them - if (!strcmp(prop,"LOCATION")) + if(!this->IsImported() && strcmp(prop,"LOCATION") == 0) { // Set the LOCATION property of the target. Note that this // cannot take into account the per-configuration name of the // target because the configuration type may not be known at - // CMake time. We should deprecate this feature and instead - // support transforming an executable target name given as the - // command part of custom commands into the proper path at - // build time. Alternatively we could put environment - // variable settings in all custom commands that hold the name - // of the target for each configuration and then give a - // reference to the variable in the location. + // CMake time. It is now deprecated as described in the + // documentation. this->SetProperty("LOCATION", this->GetLocation(0)); } - // Per-configuration location can be computed. - int len = static_cast(strlen(prop)); - if(len > 9 && strcmp(prop+len-9, "_LOCATION") == 0) + // Support "LOCATION_". + if(strncmp(prop, "LOCATION_", 9) == 0) { - std::string configName(prop, len-9); + std::string configName = prop+9; this->SetProperty(prop, this->GetLocation(configName.c_str())); } - - if(strcmp(prop, "OBJECT_FILES") == 0) + else { - this->ComputeObjectFiles(); + // Support "_LOCATION" for compatiblity. + int len = static_cast(strlen(prop)); + if(len > 9 && strcmp(prop+len-9, "_LOCATION") == 0) + { + std::string configName(prop, len-9); + if(configName != "IMPORTED") + { + this->SetProperty(prop, this->GetLocation(configName.c_str())); + } + } } } @@ -1750,15 +1891,57 @@ std::string cmTarget::GetPDBName(const char* config) } //---------------------------------------------------------------------------- -std::string cmTarget::GetFullName(const char* config, bool implib) +std::string cmTarget::GetSOName(const char* config) { - return this->GetFullNameInternal(this->GetType(), config, implib); + if(this->IsImported()) + { + // Lookup the imported soname. + if(cmTarget::ImportInfo const* info = this->GetImportInfo(config)) + { + return info->SOName; + } + else + { + return ""; + } + } + else + { + // Compute the soname that will be built. + std::string name; + std::string soName; + std::string realName; + std::string impName; + std::string pdbName; + this->GetLibraryNames(name, soName, realName, impName, pdbName, config); + return soName; + } } //---------------------------------------------------------------------------- -void cmTarget::GetFullName(std::string& prefix, std::string& base, - std::string& suffix, const char* config, - bool implib) +std::string cmTarget::GetFullName(const char* config, bool implib) +{ + if(this->IsImported()) + { + return this->GetFullNameImported(config, implib); + } + else + { + return this->GetFullNameInternal(this->GetType(), config, implib); + } +} + +//---------------------------------------------------------------------------- +std::string cmTarget::GetFullNameImported(const char* config, bool implib) +{ + return cmSystemTools::GetFilenameName( + this->ImportedGetFullPath(config, implib)); +} + +//---------------------------------------------------------------------------- +void cmTarget::GetFullNameComponents(std::string& prefix, std::string& base, + std::string& suffix, const char* config, + bool implib) { this->GetFullNameInternal(this->GetType(), config, implib, prefix, base, suffix); @@ -1766,6 +1949,19 @@ void cmTarget::GetFullName(std::string& prefix, std::string& base, //---------------------------------------------------------------------------- std::string cmTarget::GetFullPath(const char* config, bool implib) +{ + if(this->IsImported()) + { + return this->ImportedGetFullPath(config, implib); + } + else + { + return this->NormalGetFullPath(config, implib); + } +} + +//---------------------------------------------------------------------------- +std::string cmTarget::NormalGetFullPath(const char* config, bool implib) { // Start with the output directory for the target. std::string fpath = this->GetDirectory(config, implib); @@ -1777,8 +1973,31 @@ std::string cmTarget::GetFullPath(const char* config, bool implib) } //---------------------------------------------------------------------------- -std::string cmTarget::GetFullNameInternal(TargetType type, const char* config, - bool implib) +std::string cmTarget::ImportedGetFullPath(const char* config, bool implib) +{ + if(cmTarget::ImportInfo const* info = this->GetImportInfo(config)) + { + if(implib) + { + return info->ImportLibrary; + } + else + { + return info->Location; + } + } + else + { + std::string result = this->GetName(); + result += "-NOTFOUND"; + return result; + } +} + +//---------------------------------------------------------------------------- +std::string +cmTarget::GetFullNameInternal(TargetType type, const char* config, + bool implib) { std::string prefix; std::string base; @@ -1794,43 +2013,6 @@ void cmTarget::GetFullNameInternal(TargetType type, std::string& outPrefix, std::string& outBase, std::string& outSuffix) -{ - if (this->IsImported()) - { - this->ImportedGetFullNameInternal(type, config, implib, - outPrefix, outBase, outSuffix); - } - else - { - this->NormalGetFullNameInternal(type, config, implib, - outPrefix, outBase, outSuffix); - } -} - -//---------------------------------------------------------------------------- -void cmTarget::ImportedGetFullNameInternal(TargetType , - const char* config, - bool , - std::string& outPrefix, - std::string& outBase, - std::string& outSuffix) -{ - // find the basename, suffix and prefix from getLocation() - // implib ? - std::string location=this->GetLocation(config); - outBase=cmSystemTools::GetFilenameWithoutExtension(location); - outSuffix = cmSystemTools::GetFilenameExtension(location); - outPrefix = ""; -} - - -//---------------------------------------------------------------------------- -void cmTarget::NormalGetFullNameInternal(TargetType type, - const char* config, - bool implib, - std::string& outPrefix, - std::string& outBase, - std::string& outSuffix) { // Use just the target name for non-main target types. if(type != cmTarget::STATIC_LIBRARY && @@ -2019,6 +2201,14 @@ void cmTarget::GetLibraryNamesInternal(std::string& name, TargetType type, const char* config) { + // This should not be called for imported targets. + // TODO: Split cmTarget into a class hierarchy to get compile-time + // enforcement of the limited imported target API. + if(this->IsImported()) + { + abort(); + } + // Construct the name of the soname flag variable for this language. const char* ll = this->GetLinkerLanguage( @@ -2140,6 +2330,14 @@ void cmTarget::GetExecutableNamesInternal(std::string& name, TargetType type, const char* config) { + // This should not be called for imported targets. + // TODO: Split cmTarget into a class hierarchy to get compile-time + // enforcement of the limited imported target API. + if(this->IsImported()) + { + abort(); + } + // This versioning is supported only for executables and then only // when the platform supports symbolic links. #if defined(_WIN32) && !defined(__CYGWIN__) @@ -2554,8 +2752,7 @@ const char* cmTarget::GetExportMacro() // Define the symbol for targets that export symbols. if(this->GetType() == cmTarget::SHARED_LIBRARY || this->GetType() == cmTarget::MODULE_LIBRARY || - this->GetType() == cmTarget::EXECUTABLE && - this->GetPropertyAsBool("ENABLE_EXPORTS")) + this->IsExecutableWithExports()) { if(const char* custom_export_name = this->GetProperty("DEFINE_SYMBOL")) { @@ -2618,3 +2815,208 @@ bool cmTarget::IsChrpathAvailable() return true; } + +//---------------------------------------------------------------------------- +cmTarget::ImportInfo const* +cmTarget::GetImportInfo(const char* config) +{ + // There is no imported information for non-imported targets. + if(!this->IsImported()) + { + return 0; + } + + // Lookup/compute/cache the import information for this + // configuration. + std::string config_upper; + if(config && *config) + { + config_upper = cmSystemTools::UpperCase(config); + } + else + { + config_upper = "NOCONFIG"; + } + ImportInfoMapType::const_iterator i = + this->ImportInfoMap.find(config_upper); + if(i == this->ImportInfoMap.end()) + { + ImportInfo info; + this->ComputeImportInfo(config_upper, info); + ImportInfoMapType::value_type entry(config_upper, info); + i = this->ImportInfoMap.insert(entry).first; + } + + // If the location is empty then the target is not available for + // this configuration. + if(i->second.Location.empty()) + { + return 0; + } + + // Return the import information. + return &i->second; +} + +//---------------------------------------------------------------------------- +void cmTarget::ComputeImportInfo(std::string const& desired_config, + ImportInfo& info) +{ + // This method finds information about an imported target from its + // properties. The "IMPORTED_" namespace is reserved for properties + // defined by the project exporting the target. + + // Track the configuration-specific property suffix. + std::string suffix = "_"; + suffix += desired_config; + + // Look for a mapping from the current project's configuration to + // the imported project's configuration. + std::vector mappedConfigs; + { + std::string mapProp = "MAP_IMPORTED_CONFIG_"; + mapProp += desired_config; + if(const char* mapValue = this->GetProperty(mapProp.c_str())) + { + cmSystemTools::ExpandListArgument(mapValue, mappedConfigs); + } + } + + // If a mapping was found, check its configurations. + const char* loc = 0; + for(std::vector::const_iterator mci = mappedConfigs.begin(); + !loc && mci != mappedConfigs.end(); ++mci) + { + // Look for this configuration. + std::string mcUpper = cmSystemTools::UpperCase(mci->c_str()); + std::string locProp = "IMPORTED_LOCATION_"; + locProp += mcUpper; + loc = this->GetProperty(locProp.c_str()); + + // If it was found, use it for all properties below. + if(loc) + { + suffix = "_"; + suffix += mcUpper; + } + } + + // If we needed to find one of the mapped configurations but did not + // then the target is not found. The project does not want any + // other configuration. + if(!mappedConfigs.empty() && !loc) + { + return; + } + + // If we have not yet found it then there are no mapped + // configurations. Look for an exact-match. + if(!loc) + { + std::string locProp = "IMPORTED_LOCATION"; + locProp += suffix; + loc = this->GetProperty(locProp.c_str()); + } + + // If we have not yet found it then there are no mapped + // configurations and no exact match. + if(!loc) + { + // The suffix computed above is not useful. + suffix = ""; + + // Look for a configuration-less location. This may be set by + // manually-written code. + loc = this->GetProperty("IMPORTED_LOCATION"); + } + + // If we have not yet found it then the project is willing to try + // any available configuration. + if(!loc) + { + std::vector availableConfigs; + if(const char* iconfigs = this->GetProperty("IMPORTED_CONFIGURATIONS")) + { + cmSystemTools::ExpandListArgument(iconfigs, availableConfigs); + } + for(std::vector::const_iterator + aci = availableConfigs.begin(); + !loc && aci != availableConfigs.end(); ++aci) + { + suffix = "_"; + suffix += cmSystemTools::UpperCase(availableConfigs[0]); + std::string locProp = "IMPORTED_LOCATION"; + locProp += suffix; + loc = this->GetProperty(locProp.c_str()); + } + } + + // If we have not yet found it then the target is not available. + if(!loc) + { + return; + } + + // A provided configuration has been chosen. Load the + // configuration's properties. + info.Location = loc; + + // Get the soname. + if(this->GetType() == cmTarget::SHARED_LIBRARY) + { + std::string soProp = "IMPORTED_SONAME"; + soProp += suffix; + if(const char* config_soname = this->GetProperty(soProp.c_str())) + { + info.SOName = config_soname; + } + else if(const char* soname = this->GetProperty("IMPORTED_SONAME")) + { + info.SOName = soname; + } + } + + // Get the import library. + if(this->GetType() == cmTarget::SHARED_LIBRARY || + this->IsExecutableWithExports()) + { + std::string impProp = "IMPORTED_IMPLIB"; + impProp += suffix; + if(const char* config_implib = this->GetProperty(impProp.c_str())) + { + info.ImportLibrary = config_implib; + } + else if(const char* implib = this->GetProperty("IMPORTED_IMPLIB")) + { + info.ImportLibrary = implib; + } + } + + // Get the link dependencies. + { + std::string linkProp = "IMPORTED_LINK_LIBRARIES"; + linkProp += suffix; + if(const char* config_libs = this->GetProperty(linkProp.c_str())) + { + cmSystemTools::ExpandListArgument(config_libs, info.LinkLibraries); + } + else if(const char* libs = this->GetProperty("IMPORTED_LINK_LIBRARIES")) + { + cmSystemTools::ExpandListArgument(libs, info.LinkLibraries); + } + } +} + +//---------------------------------------------------------------------------- +std::vector const* +cmTarget::GetImportedLinkLibraries(const char* config) +{ + if(cmTarget::ImportInfo const* info = this->GetImportInfo(config)) + { + return &info->LinkLibraries; + } + else + { + return 0; + } +} diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 50e8429b6..b98362986 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -194,6 +194,11 @@ public: bool IsImported() const {return this->IsImportedTarget;} + /** Get link libraries for the given configuration of an imported + target. */ + std::vector const* + GetImportedLinkLibraries(const char* config); + /** Get the directory in which this target will be built. If the configuration name is given then the generator will add its subdirectory for that configuration. Otherwise just the canonical @@ -226,13 +231,16 @@ public: /** Get the full name of the target according to the settings in its makefile. */ std::string GetFullName(const char* config=0, bool implib = false); - void GetFullName(std::string& prefix, - std::string& base, std::string& suffix, - const char* config=0, bool implib = false); + void GetFullNameComponents(std::string& prefix, + std::string& base, std::string& suffix, + const char* config=0, bool implib = false); /** Get the name of the pdb file for the target. */ std::string GetPDBName(const char* config=0); + /** Get the soname of the target. Allowed only for a shared library. */ + std::string GetSOName(const char* config); + /** Get the full path to the target according to the settings in its makefile and the configuration type. */ std::string GetFullPath(const char* config=0, bool implib = false); @@ -308,6 +316,10 @@ public: // until we have per-target object file properties. void GetLanguages(std::set& languages) const; + /** Return whether this target is an executable with symbol exports + enabled. */ + bool IsExecutableWithExports(); + private: /** * A list of direct dependencies. Use in conjunction with DependencyMap. @@ -393,20 +405,14 @@ private: const char* ImportedGetLocation(const char* config); const char* NormalGetLocation(const char* config); - void NormalGetFullNameInternal(TargetType type, const char* config, - bool implib, - std::string& outPrefix, - std::string& outBase, - std::string& outSuffix); - void ImportedGetFullNameInternal(TargetType type, const char* config, - bool implib, - std::string& outPrefix, - std::string& outBase, - std::string& outSuffix); + std::string GetFullNameImported(const char* config, bool implib); const char* ImportedGetDirectory(const char* config, bool implib); const char* NormalGetDirectory(const char* config, bool implib); + std::string ImportedGetFullPath(const char* config, bool implib); + std::string NormalGetFullPath(const char* config, bool implib); + private: std::string Name; std::vector PreBuildCommands; @@ -436,6 +442,19 @@ private: bool DLLPlatform; bool IsImportedTarget; + // Cache import information from properties for each configuration. + struct ImportInfo + { + std::string Location; + std::string SOName; + std::string ImportLibrary; + std::vector LinkLibraries; + }; + typedef std::map ImportInfoMapType; + ImportInfoMapType ImportInfoMap; + ImportInfo const* GetImportInfo(const char* config); + void ComputeImportInfo(std::string const& desired_config, ImportInfo& info); + // The cmMakefile instance that owns this target. This should // always be set. cmMakefile* Makefile;