CMake/Source/cmSourceFile.cxx
Alex Neundorf 9dbba1b464 Fix #12342: Add APPEND_STRING option to set_property()
set_property() has APPEND, which creates a list. E.g. when
appending to COMPILE_FLAGS a string is needed, not a list.
With the APPEND_STRING option the value is append as string,
not as list.

Alex
2011-07-15 20:57:33 +02:00

549 lines
19 KiB
C++

/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#include "cmSourceFile.h"
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmSystemTools.h"
#include "cmake.h"
#include "cmDocumentCompileDefinitions.h"
//----------------------------------------------------------------------------
cmSourceFile::cmSourceFile(cmMakefile* mf, const char* name):
Location(mf, name)
{
this->CustomCommand = 0;
this->Properties.SetCMakeInstance(mf->GetCMakeInstance());
this->FindFullPathFailed = false;
}
//----------------------------------------------------------------------------
cmSourceFile::~cmSourceFile()
{
this->SetCustomCommand(0);
}
//----------------------------------------------------------------------------
std::string const& cmSourceFile::GetExtension() const
{
return this->Extension;
}
//----------------------------------------------------------------------------
const char* cmSourceFile::GetLanguage()
{
// If the language was set explicitly by the user then use it.
if(const char* lang = this->GetProperty("LANGUAGE"))
{
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();
}
//----------------------------------------------------------------------------
const char* cmSourceFile::GetLanguage() const
{
// If the language was set explicitly by the user then use it.
if(const char* lang = this->GetProperty("LANGUAGE"))
{
return lang;
}
// If the language was determined from the source file extension use it.
if(!this->Language.empty())
{
return this->Language.c_str();
}
// The language is not known.
return 0;
}
//----------------------------------------------------------------------------
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 thie 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* mf = this->Location.GetMakefile();
const char* tryDirs[3] = {0, 0, 0};
if(this->Location.DirectoryIsAmbiguous())
{
tryDirs[0] = mf->GetCurrentDirectory();
tryDirs[1] = mf->GetCurrentOutputDirectory();
}
else
{
tryDirs[0] = "";
}
const std::vector<std::string>& srcExts = mf->GetSourceExtensions();
const std::vector<std::string>& hdrExts = mf->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.c_str(), *di);
if(this->TryFullPath(tryPath.c_str(), 0))
{
return true;
}
for(std::vector<std::string>::const_iterator ei = srcExts.begin();
ei != srcExts.end(); ++ei)
{
if(this->TryFullPath(tryPath.c_str(), ei->c_str()))
{
return true;
}
}
for(std::vector<std::string>::const_iterator ei = hdrExts.begin();
ei != hdrExts.end(); ++ei)
{
if(this->TryFullPath(tryPath.c_str(), ei->c_str()))
{
return true;
}
}
}
cmOStringStream 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::vector<std::string>::const_iterator ext = srcExts.begin();
ext != srcExts.end(); ++ext)
{
e << " ." << *ext;
}
for(std::vector<std::string>::const_iterator ext = hdrExts.begin();
ext != hdrExts.end(); ++ext)
{
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 char* tp, const char* ext)
{
std::string tryPath = tp;
if(ext && *ext)
{
tryPath += ".";
tryPath += ext;
}
if(cmSystemTools::FileExists(tryPath.c_str()))
{
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* mf = this->Location.GetMakefile();
cmGlobalGenerator* gg = mf->GetLocalGenerator()->GetGlobalGenerator();
if(const char* l = gg->GetLanguageFromExtension(ext.c_str()))
{
this->Language = l;
}
}
//----------------------------------------------------------------------------
bool cmSourceFile::Matches(cmSourceFileLocation const& loc)
{
return this->Location.Matches(loc);
}
//----------------------------------------------------------------------------
void cmSourceFile::SetProperty(const char* prop, const char* value)
{
if (!prop)
{
return;
}
this->Properties.SetProperty(prop, value, cmProperty::SOURCE_FILE);
}
//----------------------------------------------------------------------------
void cmSourceFile::AppendProperty(const char* prop, const char* value,
bool asString)
{
if (!prop)
{
return;
}
this->Properties.AppendProperty(prop, value, cmProperty::SOURCE_FILE,
asString);
}
//----------------------------------------------------------------------------
const char* cmSourceFile::GetPropertyForUser(const char *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(strcmp(prop, "LOCATION") == 0)
{
// Commit to a location.
this->GetFullPath();
}
// Perform the normal property lookup.
return this->GetProperty(prop);
}
//----------------------------------------------------------------------------
const char* cmSourceFile::GetProperty(const char* prop) const
{
// Check for computed properties.
if(strcmp(prop, "LOCATION") == 0)
{
if(this->FullPath.empty())
{
return 0;
}
else
{
return this->FullPath.c_str();
}
}
bool chain = false;
const char *retVal =
this->Properties.GetPropertyValue(prop, cmProperty::SOURCE_FILE, chain);
if (chain)
{
cmMakefile* mf = this->Location.GetMakefile();
return mf->GetProperty(prop,cmProperty::SOURCE_FILE);
}
return retVal;
}
//----------------------------------------------------------------------------
bool cmSourceFile::GetPropertyAsBool(const char* 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;
}
//----------------------------------------------------------------------------
void cmSourceFile::DefineProperties(cmake *cm)
{
// define properties
cm->DefineProperty
("ABSTRACT", cmProperty::SOURCE_FILE,
"Is this source file an abstract class.",
"A property on a source file that indicates if the source file "
"represents a class that is abstract. This only makes sense for "
"languages that have a notion of an abstract class and it is "
"only used by some tools that wrap classes into other languages.");
cm->DefineProperty
("COMPILE_FLAGS", cmProperty::SOURCE_FILE,
"Additional flags to be added when compiling this source file.",
"These flags will be added to the list of compile flags when "
"this source file builds. Use COMPILE_DEFINITIONS to pass additional "
"preprocessor definitions.");
cm->DefineProperty
("COMPILE_DEFINITIONS", cmProperty::SOURCE_FILE,
"Preprocessor definitions for compiling a source file.",
"The COMPILE_DEFINITIONS property may be set to a "
"semicolon-separated list of preprocessor "
"definitions using the syntax VAR or VAR=value. Function-style "
"definitions are not supported. CMake will automatically escape "
"the value correctly for the native build system (note that CMake "
"language syntax may require escapes to specify some values). "
"This property may be set on a per-configuration basis using the name "
"COMPILE_DEFINITIONS_<CONFIG> where <CONFIG> is an upper-case name "
"(ex. \"COMPILE_DEFINITIONS_DEBUG\").\n"
"CMake will automatically drop some definitions that "
"are not supported by the native build tool. "
"The VS6 IDE does not support definition values with spaces "
"(but NMake does). Xcode does not support per-configuration "
"definitions on source files.\n"
CM_DOCUMENT_COMPILE_DEFINITIONS_DISCLAIMER);
cm->DefineProperty
("COMPILE_DEFINITIONS_<CONFIG>", cmProperty::SOURCE_FILE,
"Per-configuration preprocessor definitions on a source file.",
"This is the configuration-specific version of "
"COMPILE_DEFINITIONS. Note that Xcode does not support "
"per-configuration source file flags so this property will "
"be ignored by the Xcode generator.");
cm->DefineProperty
("EXTERNAL_OBJECT", cmProperty::SOURCE_FILE,
"If set to true then this is an object file.",
"If this property is set to true then the source file "
"is really an object file and should not be compiled. "
"It will still be linked into the target though.");
cm->DefineProperty
("GENERATED", cmProperty::SOURCE_FILE,
"Is this source file generated as part of the build process.",
"If a source file is generated by the build process CMake will "
"handle it differently in terms of dependency checking etc. "
"Otherwise having a non-existent source file could create problems.");
cm->DefineProperty
("HEADER_FILE_ONLY", cmProperty::SOURCE_FILE,
"Is this source file only a header file.",
"A property on a source file that indicates if the source file "
"is a header file with no associated implementation. This is "
"set automatically based on the file extension and is used by "
"CMake to determine is certain dependency information should be "
"computed.");
cm->DefineProperty
("KEEP_EXTENSION", cmProperty::SOURCE_FILE,
"Make the output file have the same extension as the source file.",
"If this property is set then the file extension of the output "
"file will be the same as that of the source file. Normally "
"the output file extension is computed based on the language "
"of the source file, for example .cxx will go to a .o extension.");
cm->DefineProperty
("LABELS", cmProperty::SOURCE_FILE,
"Specify a list of text labels associated with a source file.",
"This property has meaning only when the source file is listed in "
"a target whose LABELS property is also set. "
"No other semantics are currently specified.");
cm->DefineProperty
("LANGUAGE", cmProperty::SOURCE_FILE,
"What programming language is the file.",
"A property that can be set to indicate what programming language "
"the source file is. If it is not set the language is determined "
"based on the file extension. Typical values are CXX C etc. Setting "
"this property for a file means this file will be compiled. "
"Do not set this for header or files that should not be compiled.");
cm->DefineProperty
("LOCATION", cmProperty::SOURCE_FILE,
"The full path to a source file.",
"A read only property on a SOURCE FILE that contains the full path "
"to the source file.");
cm->DefineProperty
("MACOSX_PACKAGE_LOCATION", cmProperty::SOURCE_FILE,
"Place a source file inside a Mac OS X bundle, CFBundle, or framework.",
"Executable targets with the MACOSX_BUNDLE property set are built "
"as Mac OS X application bundles on Apple platforms. "
"Shared library targets with the FRAMEWORK property set are built "
"as Mac OS X frameworks on Apple platforms. "
"Module library targets with the BUNDLE property set are built "
"as Mac OS X CFBundle bundles on Apple platforms. "
"Source files listed in the target with this property set will "
"be copied to a directory inside the bundle or framework content "
"folder specified by the property value. "
"For bundles the content folder is \"<name>.app/Contents\". "
"For frameworks the content folder is "
"\"<name>.framework/Versions/<version>\". "
"For cfbundles the content folder is "
"\"<name>.bundle/Contents\" (unless the extension is changed). "
"See the PUBLIC_HEADER, PRIVATE_HEADER, and RESOURCE target "
"properties for specifying files meant for Headers, PrivateHeaders, "
"or Resources directories.");
cm->DefineProperty
("OBJECT_DEPENDS", cmProperty::SOURCE_FILE,
"Additional files on which a compiled object file depends.",
"Specifies a semicolon-separated list of full-paths to files on which "
"any object files compiled from this source file depend. "
"An object file will be recompiled if any of the named files is newer "
"than it.\n"
"This property need not be used to specify the dependency of a "
"source file on a generated header file that it includes. "
"Although the property was originally introduced for this purpose, it "
"is no longer necessary. "
"If the generated header file is created by a custom command in the "
"same target as the source file, the automatic dependency scanning "
"process will recognize the dependency. "
"If the generated header file is created by another target, an "
"inter-target dependency should be created with the add_dependencies "
"command (if one does not already exist due to linking relationships).");
cm->DefineProperty
("OBJECT_OUTPUTS", cmProperty::SOURCE_FILE,
"Additional outputs for a Makefile rule.",
"Additional outputs created by compilation of this source file. "
"If any of these outputs is missing the object will be recompiled. "
"This is supported only on Makefile generators and will be ignored "
"on other generators.");
cm->DefineProperty
("SYMBOLIC", cmProperty::SOURCE_FILE,
"Is this just a name for a rule.",
"If SYMBOLIC (boolean) is set to true the build system will be "
"informed that the source file is not actually created on disk but "
"instead used as a symbolic name for a build rule.");
cm->DefineProperty
("WRAP_EXCLUDE", cmProperty::SOURCE_FILE,
"Exclude this source file from any code wrapping techniques.",
"Some packages can wrap source files into alternate languages "
"to provide additional functionality. For example, C++ code "
"can be wrapped into Java or Python etc using SWIG etc. "
"If WRAP_EXCLUDE is set to true (1 etc) that indicates then "
"this source file should not be wrapped.");
}