You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
481 lines
15 KiB
481 lines
15 KiB
/* 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 <utility>
|
|
|
|
#include <cm/string_view>
|
|
#include <cmext/string_view>
|
|
|
|
#include "cmGlobalGenerator.h"
|
|
#include "cmListFileCache.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmMessageType.h"
|
|
#include "cmPolicies.h"
|
|
#include "cmProperty.h"
|
|
#include "cmState.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmValue.h"
|
|
#include "cmake.h"
|
|
|
|
cmSourceFile::cmSourceFile(cmMakefile* mf, const std::string& name,
|
|
bool generated, cmSourceFileLocationKind kind)
|
|
: Location(mf, name, (!generated) ? kind : cmSourceFileLocationKind::Known)
|
|
{
|
|
if (generated) {
|
|
this->MarkAsGenerated();
|
|
}
|
|
}
|
|
|
|
std::string const& cmSourceFile::GetExtension() const
|
|
{
|
|
return this->Extension;
|
|
}
|
|
|
|
const std::string propTRUE = "1";
|
|
const std::string propFALSE = "0";
|
|
const std::string cmSourceFile::propLANGUAGE = "LANGUAGE";
|
|
const std::string cmSourceFile::propLOCATION = "LOCATION";
|
|
const std::string cmSourceFile::propGENERATED = "GENERATED";
|
|
const std::string cmSourceFile::propCOMPILE_DEFINITIONS =
|
|
"COMPILE_DEFINITIONS";
|
|
const std::string cmSourceFile::propCOMPILE_OPTIONS = "COMPILE_OPTIONS";
|
|
const std::string cmSourceFile::propINCLUDE_DIRECTORIES =
|
|
"INCLUDE_DIRECTORIES";
|
|
|
|
void cmSourceFile::SetObjectLibrary(std::string const& objlib)
|
|
{
|
|
this->ObjectLibrary = objlib;
|
|
}
|
|
|
|
std::string cmSourceFile::GetObjectLibrary() const
|
|
{
|
|
return this->ObjectLibrary;
|
|
}
|
|
|
|
std::string const& cmSourceFile::GetOrDetermineLanguage()
|
|
{
|
|
// If the language was set explicitly by the user then use it.
|
|
if (cmValue lang = this->GetProperty(propLANGUAGE)) {
|
|
// Assign to member in order to return a reference.
|
|
this->Language = *lang;
|
|
return this->Language;
|
|
}
|
|
|
|
// Perform computation needed to get the language if necessary.
|
|
if (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->FullPath.empty() && this->Location.ExtensionIsAmbiguous() &&
|
|
this->Location.DirectoryIsAmbiguous()) {
|
|
// Finalize the file location to get the extension and set the language.
|
|
this->ResolveFullPath();
|
|
} else {
|
|
// Use the known extension to get the language if possible.
|
|
std::string ext =
|
|
cmSystemTools::GetFilenameLastExtension(this->Location.GetName());
|
|
this->CheckLanguage(ext);
|
|
}
|
|
}
|
|
|
|
// Use the language determined from the file extension.
|
|
return this->Language;
|
|
}
|
|
|
|
std::string cmSourceFile::GetLanguage() const
|
|
{
|
|
// If the language was set explicitly by the user then use it.
|
|
if (cmValue lang = this->GetProperty(propLANGUAGE)) {
|
|
return *lang;
|
|
}
|
|
|
|
// Use the language determined from the file extension.
|
|
return this->Language;
|
|
}
|
|
|
|
cmSourceFileLocation const& cmSourceFile::GetLocation() const
|
|
{
|
|
return this->Location;
|
|
}
|
|
|
|
std::string const& cmSourceFile::ResolveFullPath(std::string* error,
|
|
std::string* cmp0115Warning)
|
|
{
|
|
if (this->FullPath.empty()) {
|
|
if (this->FindFullPath(error, cmp0115Warning)) {
|
|
this->CheckExtension();
|
|
}
|
|
}
|
|
return this->FullPath;
|
|
}
|
|
|
|
std::string const& cmSourceFile::GetFullPath() const
|
|
{
|
|
return this->FullPath;
|
|
}
|
|
|
|
bool cmSourceFile::FindFullPath(std::string* error,
|
|
std::string* cmp0115Warning)
|
|
{
|
|
// If the file is generated compute the location without checking on disk.
|
|
// Note: We also check for a locally set GENERATED property, because
|
|
// it might have been set before policy CMP0118 was set to NEW.
|
|
if (this->GetIsGenerated(CheckScope::GlobalAndLocal)) {
|
|
// 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.GetFullPath();
|
|
this->FindFullPathFailed = false;
|
|
return true;
|
|
}
|
|
|
|
// If this method has already failed once do not try again.
|
|
if (this->FindFullPathFailed) {
|
|
return false;
|
|
}
|
|
|
|
// The file is not generated. It must exist on disk.
|
|
cmMakefile const* makefile = this->Location.GetMakefile();
|
|
// Location path
|
|
std::string const& lPath = this->Location.GetFullPath();
|
|
// List of extension lists
|
|
std::vector<std::string> exts =
|
|
makefile->GetCMakeInstance()->GetAllExtensions();
|
|
auto cmp0115 = makefile->GetPolicyStatus(cmPolicies::CMP0115);
|
|
auto cmp0118 = makefile->GetPolicyStatus(cmPolicies::CMP0118);
|
|
bool const cmp0118new =
|
|
cmp0118 != cmPolicies::OLD && cmp0118 != cmPolicies::WARN;
|
|
|
|
// Tries to find the file in a given directory
|
|
auto findInDir = [this, &exts, &lPath, cmp0115, cmp0115Warning, cmp0118new,
|
|
makefile](std::string const& dir) -> bool {
|
|
// Compute full path
|
|
std::string const fullPath = cmSystemTools::CollapseFullPath(lPath, dir);
|
|
// Try full path
|
|
if (cmp0118new &&
|
|
makefile->GetGlobalGenerator()->IsGeneratedFile(fullPath)) {
|
|
this->IsGenerated = true;
|
|
}
|
|
if (this->IsGenerated || cmSystemTools::FileExists(fullPath)) {
|
|
this->FullPath = fullPath;
|
|
return true;
|
|
}
|
|
// This has to be an if statement due to a bug in Oracle Developer Studio.
|
|
// See https://community.oracle.com/tech/developers/discussion/4476246/
|
|
// for details.
|
|
if (cmp0115 == cmPolicies::OLD || cmp0115 == cmPolicies::WARN) {
|
|
// Try full path with extension
|
|
for (std::string const& ext : exts) {
|
|
if (!ext.empty()) {
|
|
std::string extPath = cmStrCat(fullPath, '.', ext);
|
|
if (cmp0118new &&
|
|
makefile->GetGlobalGenerator()->IsGeneratedFile(extPath)) {
|
|
this->IsGenerated = true;
|
|
}
|
|
if (this->IsGenerated || cmSystemTools::FileExists(extPath)) {
|
|
this->FullPath = extPath;
|
|
if (cmp0115 == cmPolicies::WARN) {
|
|
std::string warning =
|
|
cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0115),
|
|
"\nFile:\n ", extPath);
|
|
if (cmp0115Warning) {
|
|
*cmp0115Warning = std::move(warning);
|
|
} else {
|
|
makefile->GetCMakeInstance()->IssueMessage(
|
|
MessageType::AUTHOR_WARNING, warning);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// File not found
|
|
return false;
|
|
};
|
|
|
|
// Try to find the file in various directories
|
|
if (this->Location.DirectoryIsAmbiguous()) {
|
|
if (findInDir(makefile->GetCurrentSourceDirectory()) ||
|
|
findInDir(makefile->GetCurrentBinaryDirectory())) {
|
|
return true;
|
|
}
|
|
} else {
|
|
if (findInDir({})) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Compose error
|
|
std::string err = cmStrCat("Cannot find source file:\n ", lPath);
|
|
switch (cmp0115) {
|
|
case cmPolicies::OLD:
|
|
case cmPolicies::WARN:
|
|
err = cmStrCat(err, "\nTried extensions");
|
|
for (auto const& ext : exts) {
|
|
err = cmStrCat(err, " .", ext);
|
|
}
|
|
break;
|
|
case cmPolicies::REQUIRED_IF_USED:
|
|
case cmPolicies::REQUIRED_ALWAYS:
|
|
case cmPolicies::NEW:
|
|
break;
|
|
}
|
|
if (lPath == "FILE_SET"_s) {
|
|
err += "\nHint: the FILE_SET keyword may only appear after a visibility "
|
|
"specifier or another FILE_SET within the target_sources() "
|
|
"command.";
|
|
}
|
|
if (error != nullptr) {
|
|
*error = std::move(err);
|
|
} else {
|
|
makefile->IssueMessage(MessageType::FATAL_ERROR, err);
|
|
}
|
|
this->FindFullPathFailed = true;
|
|
|
|
// File not found
|
|
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, cmValue value)
|
|
{
|
|
if (prop == propINCLUDE_DIRECTORIES) {
|
|
this->IncludeDirectories.clear();
|
|
if (value) {
|
|
cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
|
|
this->IncludeDirectories.emplace_back(value, lfbt);
|
|
}
|
|
} else if (prop == propCOMPILE_OPTIONS) {
|
|
this->CompileOptions.clear();
|
|
if (value) {
|
|
cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
|
|
this->CompileOptions.emplace_back(value, lfbt);
|
|
}
|
|
} else if (prop == propCOMPILE_DEFINITIONS) {
|
|
this->CompileDefinitions.clear();
|
|
if (value) {
|
|
cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
|
|
this->CompileDefinitions.emplace_back(value, lfbt);
|
|
}
|
|
} else {
|
|
this->Properties.SetProperty(prop, value);
|
|
}
|
|
}
|
|
|
|
void cmSourceFile::AppendProperty(const std::string& prop,
|
|
const std::string& value, bool asString)
|
|
{
|
|
if (prop == propINCLUDE_DIRECTORIES) {
|
|
if (!value.empty()) {
|
|
cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
|
|
this->IncludeDirectories.emplace_back(value, lfbt);
|
|
}
|
|
} else if (prop == propCOMPILE_OPTIONS) {
|
|
if (!value.empty()) {
|
|
cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
|
|
this->CompileOptions.emplace_back(value, lfbt);
|
|
}
|
|
} else if (prop == propCOMPILE_DEFINITIONS) {
|
|
if (!value.empty()) {
|
|
cmListFileBacktrace lfbt = this->Location.GetMakefile()->GetBacktrace();
|
|
this->CompileDefinitions.emplace_back(value, lfbt);
|
|
}
|
|
} else {
|
|
this->Properties.AppendProperty(prop, value, asString);
|
|
}
|
|
}
|
|
|
|
cmValue 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 == propLOCATION) {
|
|
// Commit to a location.
|
|
this->ResolveFullPath();
|
|
}
|
|
|
|
// Similarly, LANGUAGE can be determined by the file extension
|
|
// if it is requested by the user.
|
|
if (prop == propLANGUAGE) {
|
|
// The pointer is valid until `this->Language` is modified.
|
|
return cmValue(this->GetOrDetermineLanguage());
|
|
}
|
|
|
|
// Special handling for GENERATED property.
|
|
if (prop == propGENERATED) {
|
|
// We need to check policy CMP0118 in order to determine if we need to
|
|
// possibly consider the value of a locally set GENERATED property, too.
|
|
auto policyStatus =
|
|
this->Location.GetMakefile()->GetPolicyStatus(cmPolicies::CMP0118);
|
|
if (this->GetIsGenerated(
|
|
(policyStatus == cmPolicies::WARN || policyStatus == cmPolicies::OLD)
|
|
? CheckScope::GlobalAndLocal
|
|
: CheckScope::Global)) {
|
|
return cmValue(propTRUE);
|
|
}
|
|
return cmValue(propFALSE);
|
|
}
|
|
|
|
// Perform the normal property lookup.
|
|
return this->GetProperty(prop);
|
|
}
|
|
|
|
cmValue cmSourceFile::GetProperty(const std::string& prop) const
|
|
{
|
|
// Check for computed properties.
|
|
if (prop == propLOCATION) {
|
|
if (this->FullPath.empty()) {
|
|
return nullptr;
|
|
}
|
|
return cmValue(this->FullPath);
|
|
}
|
|
|
|
// Check for the properties with backtraces.
|
|
if (prop == propINCLUDE_DIRECTORIES) {
|
|
if (this->IncludeDirectories.empty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
static std::string output;
|
|
output = cmJoin(this->IncludeDirectories, ";");
|
|
return cmValue(output);
|
|
}
|
|
|
|
if (prop == propCOMPILE_OPTIONS) {
|
|
if (this->CompileOptions.empty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
static std::string output;
|
|
output = cmJoin(this->CompileOptions, ";");
|
|
return cmValue(output);
|
|
}
|
|
|
|
if (prop == propCOMPILE_DEFINITIONS) {
|
|
if (this->CompileDefinitions.empty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
static std::string output;
|
|
output = cmJoin(this->CompileDefinitions, ";");
|
|
return cmValue(output);
|
|
}
|
|
|
|
cmValue 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 nullptr;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
const std::string& cmSourceFile::GetSafeProperty(const std::string& prop) const
|
|
{
|
|
cmValue ret = this->GetProperty(prop);
|
|
if (ret) {
|
|
return *ret;
|
|
}
|
|
|
|
static std::string const s_empty;
|
|
return s_empty;
|
|
}
|
|
|
|
bool cmSourceFile::GetPropertyAsBool(const std::string& prop) const
|
|
{
|
|
return cmIsOn(this->GetProperty(prop));
|
|
}
|
|
|
|
void cmSourceFile::MarkAsGenerated()
|
|
{
|
|
this->IsGenerated = true;
|
|
const auto& mf = *this->Location.GetMakefile();
|
|
mf.GetGlobalGenerator()->MarkAsGeneratedFile(this->ResolveFullPath());
|
|
}
|
|
|
|
bool cmSourceFile::GetIsGenerated(CheckScope checkScope) const
|
|
{
|
|
if (this->IsGenerated) {
|
|
// Globally marked as generated!
|
|
return true;
|
|
}
|
|
if (checkScope == CheckScope::GlobalAndLocal) {
|
|
// Check locally stored properties.
|
|
return this->GetPropertyAsBool(propGENERATED);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void cmSourceFile::SetProperties(cmPropertyMap properties)
|
|
{
|
|
this->Properties = std::move(properties);
|
|
}
|
|
|
|
cmCustomCommand* cmSourceFile::GetCustomCommand() const
|
|
{
|
|
return this->CustomCommand.get();
|
|
}
|
|
|
|
void cmSourceFile::SetCustomCommand(std::unique_ptr<cmCustomCommand> cc)
|
|
{
|
|
this->CustomCommand = std::move(cc);
|
|
}
|