CMake/Source/cmFileCommand.cxx
Brad King 22c62c9e65 BUG: Sweeping changes to cleanup computation of target names. This should
fix many bugs related to target names being computed inconsistently.

- Centralized computation of a target's file name to a method in
  cmTarget.  Now that global knowledge is always available the
  *_CMAKE_PATH cache variables are no longer needed.

- Centralized computation of link library command lines and link
  directory search order.

- Moved computation of link directories needed to link CMake targets
  to be after evaluation of linking dependencies.

This also removed alot of duplicate code in which each version had its
own bugs.

This commit is surrounded by the tags

  CMake-TargetNameCentralization1-pre

and

  CMake-TargetNameCentralization1-post

so make the large set of changes easy to identify.
2006-01-13 18:18:32 -05:00

727 lines
20 KiB
C++

/*=========================================================================
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 "cmFileCommand.h"
#include "cmGlob.h"
#include <sys/types.h>
#include <sys/stat.h>
// cmLibraryCommand
bool cmFileCommand::InitialPass(std::vector<std::string> const& args)
{
if(args.size() < 2 )
{
this->SetError("must be called with at least two arguments.");
return false;
}
std::string subCommand = args[0];
if ( subCommand == "WRITE" )
{
return this->HandleWriteCommand(args, false);
}
else if ( subCommand == "APPEND" )
{
return this->HandleWriteCommand(args, true);
}
else if ( subCommand == "READ" )
{
return this->HandleReadCommand(args);
}
else if ( subCommand == "GLOB" )
{
return this->HandleGlobCommand(args, false);
}
else if ( subCommand == "GLOB_RECURSE" )
{
return this->HandleGlobCommand(args, true);
}
else if ( subCommand == "MAKE_DIRECTORY" )
{
return this->HandleMakeDirectoryCommand(args);
}
else if ( subCommand == "INSTALL" )
{
return this->HandleInstallCommand(args);
}
else if ( subCommand == "RELATIVE_PATH" )
{
return this->HandleRelativePathCommand(args);
}
std::string e = "does not recognize sub-command "+subCommand;
this->SetError(e.c_str());
return false;
}
//----------------------------------------------------------------------------
bool cmFileCommand::HandleWriteCommand(std::vector<std::string> const& args,
bool append)
{
std::string message;
std::vector<std::string>::const_iterator i = args.begin();
i++; // Get rid of subcommand
std::string fileName = *i;
if ( !cmsys::SystemTools::FileIsFullPath(i->c_str()) )
{
fileName = m_Makefile->GetCurrentDirectory();
fileName += "/" + *i;
}
i++;
for(;i != args.end(); ++i)
{
message += *i;
}
std::string dir = cmSystemTools::GetFilenamePath(fileName);
cmSystemTools::MakeDirectory(dir.c_str());
mode_t mode =
#if defined( _MSC_VER ) || defined( __MINGW32__ )
S_IREAD | S_IWRITE
#elif defined( __BORLANDC__ )
S_IRUSR | S_IWUSR
#else
S_IRUSR | S_IWUSR |
S_IRGRP |
S_IROTH
#endif
;
// Set permissions to writable
if ( cmSystemTools::GetPermissions(fileName.c_str(), mode) )
{
cmSystemTools::SetPermissions(fileName.c_str(),
#if defined( _MSC_VER ) || defined( __MINGW32__ )
S_IREAD | S_IWRITE
#else
S_IRUSR | S_IWUSR
#endif
);
}
// If GetPermissions fails, pretend like it is ok. File open will fail if
// the file is not writable
std::ofstream file(fileName.c_str(), append?std::ios::app: std::ios::out);
if ( !file )
{
std::string error = "Internal CMake error when trying to open file: ";
error += fileName.c_str();
error += " for writing.";
this->SetError(error.c_str());
return false;
}
file << message;
file.close();
cmSystemTools::SetPermissions(fileName.c_str(), mode);
m_Makefile->AddWrittenFile(fileName.c_str());
return true;
}
//----------------------------------------------------------------------------
bool cmFileCommand::HandleReadCommand(std::vector<std::string> const& args)
{
if ( args.size() != 3 )
{
this->SetError("READ must be called with two additional arguments");
return false;
}
std::string fileName = args[1];
if ( !cmsys::SystemTools::FileIsFullPath(args[1].c_str()) )
{
fileName = m_Makefile->GetCurrentDirectory();
fileName += "/" + args[1];
}
std::string variable = args[2];
std::ifstream file(fileName.c_str(), std::ios::in);
if ( !file )
{
std::string error = "Internal CMake error when trying to open file: ";
error += fileName.c_str();
error += " for reading.";
this->SetError(error.c_str());
return false;
}
std::string output;
std::string line;
bool has_newline = false;
while ( cmSystemTools::GetLineFromStream(file, line, &has_newline) )
{
output += line;
if ( has_newline )
{
output += "\n";
}
}
m_Makefile->AddDefinition(variable.c_str(), output.c_str());
return true;
}
//----------------------------------------------------------------------------
bool cmFileCommand::HandleGlobCommand(std::vector<std::string> const& args,
bool recurse)
{
if ( args.size() < 2 )
{
this->SetError("GLOB requires at least a variable name");
return false;
}
std::vector<std::string>::const_iterator i = args.begin();
i++; // Get rid of subcommand
std::string variable = *i;
i++;
cmGlob g;
g.SetRecurse(recurse);
std::string output = "";
bool first = true;
for ( ; i != args.end(); ++i )
{
if ( !cmsys::SystemTools::FileIsFullPath(i->c_str()) )
{
std::string expr = m_Makefile->GetCurrentDirectory();
// Handle script mode
if ( expr.size() > 0 )
{
expr += "/" + *i;
g.FindFiles(expr);
}
else
{
g.FindFiles(*i);
}
}
else
{
g.FindFiles(*i);
}
std::vector<std::string>::size_type cc;
std::vector<std::string>& files = g.GetFiles();
for ( cc = 0; cc < files.size(); cc ++ )
{
if ( !first )
{
output += ";";
}
output += files[cc];
first = false;
}
}
m_Makefile->AddDefinition(variable.c_str(), output.c_str());
return true;
}
//----------------------------------------------------------------------------
bool cmFileCommand::HandleMakeDirectoryCommand(
std::vector<std::string> const& args)
{
if(args.size() < 2 )
{
this->SetError("called with incorrect number of arguments");
return false;
}
std::vector<std::string>::const_iterator i = args.begin();
i++; // Get rid of subcommand
std::string expr;
for ( ; i != args.end(); ++i )
{
const std::string* cdir = &(*i);
if ( !cmsys::SystemTools::FileIsFullPath(i->c_str()) )
{
expr = m_Makefile->GetCurrentDirectory();
expr += "/" + *i;
cdir = &expr;
}
if ( !cmSystemTools::MakeDirectory(cdir->c_str()) )
{
std::string error = "problem creating directory: " + *cdir;
this->SetError(error.c_str());
return false;
}
}
return true;
}
//----------------------------------------------------------------------------
bool cmFileCommand::HandleInstallCommand(
std::vector<std::string> const& args)
{
if ( args.size() < 6 )
{
this->SetError("called with incorrect number of arguments");
return false;
}
std::string destination = "";
std::string stype = "FILES";
const char* build_type = m_Makefile->GetDefinition("BUILD_TYPE");
if ( build_type && strcmp(build_type, ".") == 0 )
{
build_type = 0;
}
if ( build_type && strncmp(build_type, ".\\", 2) == 0 )
{
build_type += 2;
}
const char* destdir = cmSystemTools::GetEnv("DESTDIR");
std::string extra_dir = "";
int debug = 0;
if ( build_type )
{
extra_dir = build_type;
std::string btype = cmSystemTools::LowerCase(build_type);
if ( m_Makefile->GetDefinition("WIN32")
&& strncmp(btype.c_str(), "debug", strlen("debug")) == 0 )
{
debug = 1;
}
}
std::vector<std::string> files;
int itype = cmTarget::INSTALL_FILES;
std::vector<std::string>::size_type i = 0;
i++; // Get rid of subcommand
std::map<cmStdString, const char*> properties;
bool in_files = false;
bool in_properties = false;
bool optional = false;
for ( ; i != args.size(); ++i )
{
const std::string* cstr = &args[i];
if ( *cstr == "DESTINATION" && i < args.size()-1 )
{
i++;
destination = args[i];
in_files = false;
in_properties = false;
}
else if ( *cstr == "TYPE" && i < args.size()-1 )
{
i++;
stype = args[i];
if ( args[i+1] == "OPTIONAL" )
{
i++;
optional = true;
}
in_properties = false;
in_files = false;
}
else if ( *cstr == "PROPERTIES" )
{
in_properties = true;
in_files = false;
}
else if ( *cstr == "FILES" && !in_files)
{
in_files = true;
in_properties = false;
}
else if ( in_properties && i < args.size()-1 )
{
properties[args[i]] = args[i+1].c_str();
i++;
}
else if ( in_files )
{
files.push_back(*cstr);
}
else
{
this->SetError("called with inappropriate arguments");
return false;
}
}
if ( destination.size() < 2 )
{
this->SetError("called with inapropriate arguments. "
"No DESTINATION provided or .");
return false;
}
int destDirLength = 0;
if ( destdir && *destdir )
{
std::string sdestdir = destdir;
cmSystemTools::ConvertToUnixSlashes(sdestdir);
char ch1 = destination[0];
char ch2 = destination[1];
char ch3 = 0;
if ( destination.size() > 2 )
{
ch3 = destination[2];
}
int skip = 0;
if ( ch1 != '/' )
{
int relative = 0;
if ( ( ch1 >= 'a' && ch1 <= 'z' || ch1 >= 'a' && ch1 <= 'z' ) &&
ch2 == ':' )
{
// Assume windows
// let's do some destdir magic:
skip = 2;
if ( ch3 != '/' )
{
relative = 1;
}
}
else
{
relative = 1;
}
if ( relative )
{
// This is relative path on unix or windows. Since we are doing
// destdir, this case does not make sense.
this->SetError("called with relative DESTINATION. This "
"does not make sense when using DESTDIR. Specify "
"absolute path or remove DESTDIR environment variable.");
return false;
}
}
else
{
if ( ch2 == '/' )
{
// looks like a network path.
this->SetError("called with network path DESTINATION. This "
"does not make sense when using DESTDIR. Specify local "
"absolute path or remove DESTDIR environment variable.");
return false;
}
}
destination = sdestdir + (destination.c_str() + skip);
destDirLength = int(sdestdir.size());
}
if ( files.size() == 0 )
{
this->SetError(
"called with inapropriate arguments. No FILES provided.");
return false;
}
if ( stype == "EXECUTABLE" )
{
itype = cmTarget::EXECUTABLE;
}
else if ( stype == "PROGRAM" )
{
itype = cmTarget::INSTALL_PROGRAMS;
}
else if ( stype == "STATIC_LIBRARY" )
{
itype = cmTarget::STATIC_LIBRARY;
}
else if ( stype == "SHARED_LIBRARY" )
{
itype = cmTarget::SHARED_LIBRARY;
}
else if ( stype == "MODULE" )
{
itype = cmTarget::MODULE_LIBRARY;
}
if ( !cmSystemTools::FileExists(destination.c_str()) )
{
if ( !cmSystemTools::MakeDirectory(destination.c_str()) )
{
std::string errstring = "cannot create directory: " + destination +
". Maybe need administrative privileges.";
this->SetError(errstring.c_str());
return false;
}
}
if ( !cmSystemTools::FileIsDirectory(destination.c_str()) )
{
std::string errstring = "found file: " + destination +
" where expecting directory with the same name.";
this->SetError(errstring.c_str());
return false;
}
const char* manifest_files =
m_Makefile->GetDefinition("CMAKE_INSTALL_MANIFEST_FILES");
std::string smanifest_files;
if ( manifest_files )
{
smanifest_files = manifest_files;
}
for ( i = 0; i < files.size(); i ++ )
{
std::string destfilewe
= destination + "/"
+ cmSystemTools::GetFilenameWithoutExtension(files[i]);
std::string ctarget = files[i].c_str();
std::string fname = cmSystemTools::GetFilenameName(ctarget);
std::string ext = cmSystemTools::GetFilenameExtension(ctarget);
std::string fnamewe
= cmSystemTools::GetFilenameWithoutExtension(ctarget);
std::string destfile = destfilewe;
if ( ext.size() )
{
destfile += ext;
}
switch( itype )
{
case cmTarget::MODULE_LIBRARY:
case cmTarget::STATIC_LIBRARY:
case cmTarget::SHARED_LIBRARY:
{
// Handle shared library versioning
const char* lib_version = 0;
const char* lib_soversion = 0;
if ( properties.find("VERSION") != properties.end() )
{
lib_version = properties["VERSION"];
}
if ( properties.find("SOVERSION") != properties.end() )
{
lib_soversion = properties["SOVERSION"];
}
if ( !lib_version && lib_soversion )
{
lib_version = lib_soversion;
}
if ( !lib_soversion && lib_version )
{
lib_soversion = lib_version;
}
if ( lib_version && lib_soversion )
{
std::string libname = destfile;
std::string soname = destfile;
std::string soname_nopath = fname;
soname += ".";
soname += lib_soversion;
soname_nopath += ".";
soname_nopath += lib_soversion;
fname += ".";
fname += lib_version;
destfile += ".";
destfile += lib_version;
cmSystemTools::RemoveFile(soname.c_str());
cmSystemTools::RemoveFile(libname.c_str());
if (!cmSystemTools::CreateSymlink(soname_nopath.c_str(), libname.c_str()) )
{
std::string errstring = "error when creating symlink from: " + libname + " to " + soname_nopath;
this->SetError(errstring.c_str());
return false;
}
smanifest_files += ";";
smanifest_files += libname.substr(destDirLength);;
if ( destfile != soname )
{
if ( !cmSystemTools::CreateSymlink(fname.c_str(), soname.c_str()) )
{
std::string errstring = "error when creating symlink from: " + soname + " to " + fname;
this->SetError(errstring.c_str());
return false;
}
smanifest_files += ";";
smanifest_files += soname.substr(destDirLength);
}
}
// Reconstruct the source file path taking into account the
// extra directory and possible new file name.
cmOStringStream str;
str << cmSystemTools::GetFilenamePath(ctarget) << "/";
if ( extra_dir.size() > 0 )
{
str << extra_dir << "/";
}
str << fname;
ctarget = str.str();
}
break;
case cmTarget::EXECUTABLE:
{
// Handle executable versioning
const char* exe_version = 0;
if ( properties.find("VERSION") != properties.end() )
{
exe_version = properties["VERSION"];
}
if ( exe_version )
{
std::string exename = destfile;
std::string exename_nopath = fname;
exename_nopath += "-";
exename_nopath += exe_version;
fname += "-";
fname += exe_version;
destfile += "-";
destfile += exe_version;
cmSystemTools::RemoveFile(exename.c_str());
if (!cmSystemTools::CreateSymlink(exename_nopath.c_str(), exename.c_str()) )
{
std::string errstring = "error when creating symlink from: " + exename + " to " + exename_nopath;
this->SetError(errstring.c_str());
return false;
}
smanifest_files += ";";
smanifest_files += exename.substr(destDirLength);
}
// Reconstruct the source file path taking into account the
// extra directory and possible new file name.
cmOStringStream str;
str << cmSystemTools::GetFilenamePath(ctarget) << "/";
if ( extra_dir.size() > 0 )
{
str << extra_dir << "/";
}
str << fname;
ctarget = str.str();
}
break;
}
if ( !cmSystemTools::SameFile(ctarget.c_str(), destfile.c_str()) )
{
if ( cmSystemTools::FileExists(ctarget.c_str()) )
{
cmSystemTools::RemoveFile(destfile.c_str());
if ( !cmSystemTools::CopyFileAlways(ctarget.c_str(),
destination.c_str()) )
{
std::string errstring = "cannot copy file: " + ctarget +
" to directory : " + destination + ".";
this->SetError(errstring.c_str());
return false;
}
switch( itype )
{
case cmTarget::STATIC_LIBRARY:
#if defined(__APPLE_CC__)
{
std::string ranlib = "ranlib ";
ranlib += cmSystemTools::ConvertToOutputPath(destfile.c_str());
if(!cmSystemTools::RunSingleCommand(ranlib.c_str()))
{
std::string err = "ranlib failed: ";
err += ranlib;
this->SetError(err.c_str());
}
}
#endif
break;
case cmTarget::MODULE_LIBRARY:
case cmTarget::SHARED_LIBRARY:
case cmTarget::EXECUTABLE:
case cmTarget::INSTALL_PROGRAMS:
if ( !cmSystemTools::SetPermissions(destfile.c_str(),
#if defined( _MSC_VER ) || defined( __MINGW32__ )
S_IREAD | S_IWRITE | S_IEXEC
#elif defined( __BORLANDC__ )
S_IRUSR | S_IWUSR | S_IXUSR
#else
S_IRUSR | S_IWUSR | S_IXUSR |
S_IRGRP | S_IXGRP |
S_IROTH | S_IXOTH
#endif
) )
{
cmOStringStream err;
err << "Problem setting permissions on file: "
<< destfile.c_str();
perror(err.str().c_str());
}
}
smanifest_files += ";";
smanifest_files += destfile.substr(destDirLength);
}
else
{
if ( !optional )
{
std::string errstring = "cannot find file: " +
ctarget + " to install.";
this->SetError(errstring.c_str());
return false;
}
}
}
}
m_Makefile->AddDefinition("CMAKE_INSTALL_MANIFEST_FILES",
smanifest_files.c_str());
return true;
}
//----------------------------------------------------------------------------
bool cmFileCommand::HandleRelativePathCommand(
std::vector<std::string> const& args)
{
if(args.size() != 4 )
{
this->SetError("called with incorrect number of arguments");
return false;
}
const std::string& outVar = args[1];
const std::string& directoryName = args[2];
const std::string& fileName = args[3];
if(!cmSystemTools::FileIsFullPath(directoryName.c_str()))
{
std::string errstring = "RelativePath must be passed a full path to the directory: " + directoryName;
this->SetError(errstring.c_str());
return false;
}
if(!cmSystemTools::FileIsFullPath(fileName.c_str()))
{
std::string errstring = "RelativePath must be passed a full path to the directory: " + directoryName;
this->SetError(errstring.c_str());
return false;
}
std::string res = cmSystemTools::RelativePath(directoryName.c_str(), fileName.c_str());
m_Makefile->AddDefinition(outVar.c_str(),
res.c_str());
return true;
}