| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #include "cmSourceFile.h" |
| |
| #include <sstream> |
| |
| #include "cmCustomCommand.h" |
| #include "cmGlobalGenerator.h" |
| #include "cmMakefile.h" |
| #include "cmProperty.h" |
| #include "cmState.h" |
| #include "cmSystemTools.h" |
| #include "cmake.h" |
| |
| cmSourceFile::cmSourceFile(cmMakefile* mf, const std::string& name, |
| cmSourceFileLocationKind kind) |
| : Location(mf, name, kind) |
| { |
| this->CustomCommand = nullptr; |
| this->FindFullPathFailed = false; |
| } |
| |
| cmSourceFile::~cmSourceFile() |
| { |
| this->SetCustomCommand(nullptr); |
| } |
| |
| std::string const& cmSourceFile::GetExtension() const |
| { |
| return this->Extension; |
| } |
| |
| const std::string cmSourceFile::propLANGUAGE = "LANGUAGE"; |
| |
| void cmSourceFile::SetObjectLibrary(std::string const& objlib) |
| { |
| this->ObjectLibrary = objlib; |
| } |
| |
| std::string cmSourceFile::GetObjectLibrary() const |
| { |
| return this->ObjectLibrary; |
| } |
| |
| std::string cmSourceFile::GetLanguage() |
| { |
| // If the language was set explicitly by the user then use it. |
| if (const char* lang = this->GetProperty(propLANGUAGE)) { |
| return lang; |
| } |
| |
| // Perform computation needed to get the language if necessary. |
| if (this->FullPath.empty() && this->Language.empty()) { |
| // If a known extension is given or a known full path is given |
| // then trust that the current extension is sufficient to |
| // determine the language. This will fail only if the user |
| // specifies a full path to the source but leaves off the |
| // extension, which is kind of weird. |
| if (this->Location.ExtensionIsAmbiguous() && |
| this->Location.DirectoryIsAmbiguous()) { |
| // Finalize the file location to get the extension and set the |
| // language. |
| this->GetFullPath(); |
| } else { |
| // Use the known extension to get the language if possible. |
| std::string ext = |
| cmSystemTools::GetFilenameLastExtension(this->Location.GetName()); |
| this->CheckLanguage(ext); |
| } |
| } |
| |
| // Now try to determine the language. |
| return static_cast<cmSourceFile const*>(this)->GetLanguage(); |
| } |
| |
| std::string cmSourceFile::GetLanguage() const |
| { |
| // If the language was set explicitly by the user then use it. |
| if (const char* lang = this->GetProperty(propLANGUAGE)) { |
| return lang; |
| } |
| |
| // If the language was determined from the source file extension use it. |
| if (!this->Language.empty()) { |
| return this->Language; |
| } |
| |
| // The language is not known. |
| return ""; |
| } |
| |
| cmSourceFileLocation const& cmSourceFile::GetLocation() const |
| { |
| return this->Location; |
| } |
| |
| std::string const& cmSourceFile::GetFullPath(std::string* error) |
| { |
| if (this->FullPath.empty()) { |
| if (this->FindFullPath(error)) { |
| this->CheckExtension(); |
| } |
| } |
| return this->FullPath; |
| } |
| |
| std::string const& cmSourceFile::GetFullPath() const |
| { |
| return this->FullPath; |
| } |
| |
| bool cmSourceFile::FindFullPath(std::string* error) |
| { |
| // If this method has already failed once do not try again. |
| if (this->FindFullPathFailed) { |
| return false; |
| } |
| |
| // If the file is generated compute the location without checking on |
| // disk. |
| if (this->GetPropertyAsBool("GENERATED")) { |
| // The file is either already a full path or is relative to the |
| // build directory for the target. |
| this->Location.DirectoryUseBinary(); |
| this->FullPath = this->Location.GetDirectory(); |
| this->FullPath += "/"; |
| this->FullPath += this->Location.GetName(); |
| return true; |
| } |
| |
| // The file is not generated. It must exist on disk. |
| cmMakefile const* mf = this->Location.GetMakefile(); |
| const char* tryDirs[3] = { nullptr, nullptr, nullptr }; |
| if (this->Location.DirectoryIsAmbiguous()) { |
| tryDirs[0] = mf->GetCurrentSourceDirectory(); |
| tryDirs[1] = mf->GetCurrentBinaryDirectory(); |
| } else { |
| tryDirs[0] = ""; |
| } |
| |
| cmake const* const cmakeInst = mf->GetCMakeInstance(); |
| std::vector<std::string> const& srcExts = cmakeInst->GetSourceExtensions(); |
| std::vector<std::string> const& hdrExts = cmakeInst->GetHeaderExtensions(); |
| for (const char* const* di = tryDirs; *di; ++di) { |
| std::string tryPath = this->Location.GetDirectory(); |
| if (!tryPath.empty()) { |
| tryPath += "/"; |
| } |
| tryPath += this->Location.GetName(); |
| tryPath = cmSystemTools::CollapseFullPath(tryPath, *di); |
| if (this->TryFullPath(tryPath, "")) { |
| return true; |
| } |
| for (std::string const& ext : srcExts) { |
| if (this->TryFullPath(tryPath, ext)) { |
| return true; |
| } |
| } |
| for (std::string const& ext : hdrExts) { |
| if (this->TryFullPath(tryPath, ext)) { |
| return true; |
| } |
| } |
| } |
| |
| std::ostringstream e; |
| std::string missing = this->Location.GetDirectory(); |
| if (!missing.empty()) { |
| missing += "/"; |
| } |
| missing += this->Location.GetName(); |
| e << "Cannot find source file:\n " << missing << "\nTried extensions"; |
| for (std::string const& srcExt : srcExts) { |
| e << " ." << srcExt; |
| } |
| for (std::string const& ext : hdrExts) { |
| e << " ." << ext; |
| } |
| if (error) { |
| *error = e.str(); |
| } else { |
| this->Location.GetMakefile()->IssueMessage(cmake::FATAL_ERROR, e.str()); |
| } |
| this->FindFullPathFailed = true; |
| return false; |
| } |
| |
| bool cmSourceFile::TryFullPath(const std::string& path, const std::string& ext) |
| { |
| std::string tryPath = path; |
| if (!ext.empty()) { |
| tryPath += "."; |
| tryPath += ext; |
| } |
| if (cmSystemTools::FileExists(tryPath)) { |
| this->FullPath = tryPath; |
| return true; |
| } |
| return false; |
| } |
| |
| void cmSourceFile::CheckExtension() |
| { |
| // Compute the extension. |
| std::string realExt = |
| cmSystemTools::GetFilenameLastExtension(this->FullPath); |
| if (!realExt.empty()) { |
| // Store the extension without the leading '.'. |
| this->Extension = realExt.substr(1); |
| } |
| |
| // Look for object files. |
| if (this->Extension == "obj" || this->Extension == "o" || |
| this->Extension == "lo") { |
| this->SetProperty("EXTERNAL_OBJECT", "1"); |
| } |
| |
| // Try to identify the source file language from the extension. |
| if (this->Language.empty()) { |
| this->CheckLanguage(this->Extension); |
| } |
| } |
| |
| void cmSourceFile::CheckLanguage(std::string const& ext) |
| { |
| // Try to identify the source file language from the extension. |
| cmMakefile const* mf = this->Location.GetMakefile(); |
| cmGlobalGenerator* gg = mf->GetGlobalGenerator(); |
| std::string l = gg->GetLanguageFromExtension(ext.c_str()); |
| if (!l.empty()) { |
| this->Language = l; |
| } |
| } |
| |
| bool cmSourceFile::Matches(cmSourceFileLocation const& loc) |
| { |
| return this->Location.Matches(loc); |
| } |
| |
| void cmSourceFile::SetProperty(const std::string& prop, const char* value) |
| { |
| this->Properties.SetProperty(prop, value); |
| } |
| |
| void cmSourceFile::AppendProperty(const std::string& prop, const char* value, |
| bool asString) |
| { |
| this->Properties.AppendProperty(prop, value, asString); |
| } |
| |
| const char* cmSourceFile::GetPropertyForUser(const std::string& prop) |
| { |
| // This method is a consequence of design history and backwards |
| // compatibility. GetProperty is (and should be) a const method. |
| // Computed properties should not be stored back in the property map |
| // but instead reference information already known. If they need to |
| // cache information in a mutable ivar to provide the return string |
| // safely then so be it. |
| // |
| // The LOCATION property is particularly problematic. The CMake |
| // language has very loose restrictions on the names that will match |
| // a given source file (for historical reasons). Implementing |
| // lookups correctly with such loose naming requires the |
| // cmSourceFileLocation class to commit to a particular full path to |
| // the source file as late as possible. If the users requests the |
| // LOCATION property we must commit now. |
| if (prop == "LOCATION") { |
| // Commit to a location. |
| this->GetFullPath(); |
| } |
| |
| // Perform the normal property lookup. |
| return this->GetProperty(prop); |
| } |
| |
| const char* cmSourceFile::GetProperty(const std::string& prop) const |
| { |
| // Check for computed properties. |
| if (prop == "LOCATION") { |
| if (this->FullPath.empty()) { |
| return nullptr; |
| } |
| return this->FullPath.c_str(); |
| } |
| |
| const char* retVal = this->Properties.GetPropertyValue(prop); |
| if (!retVal) { |
| cmMakefile const* mf = this->Location.GetMakefile(); |
| const bool chain = |
| mf->GetState()->IsPropertyChained(prop, cmProperty::SOURCE_FILE); |
| if (chain) { |
| return mf->GetProperty(prop, chain); |
| } |
| } |
| |
| return retVal; |
| } |
| |
| bool cmSourceFile::GetPropertyAsBool(const std::string& prop) const |
| { |
| return cmSystemTools::IsOn(this->GetProperty(prop)); |
| } |
| |
| cmCustomCommand* cmSourceFile::GetCustomCommand() |
| { |
| return this->CustomCommand; |
| } |
| |
| cmCustomCommand const* cmSourceFile::GetCustomCommand() const |
| { |
| return this->CustomCommand; |
| } |
| |
| void cmSourceFile::SetCustomCommand(cmCustomCommand* cc) |
| { |
| cmCustomCommand* old = this->CustomCommand; |
| this->CustomCommand = cc; |
| delete old; |
| } |