Commit 96f02662 authored by Brad King's avatar Brad King 💬
Browse files

ENH: Created new install script generation framework. The INSTALL command...

ENH: Created new install script generation framework.  The INSTALL command creates the generators which are later used by cmLocalGenerator to create the cmake_install.cmake files.  A new target installation interface is provided by the INSTALL command which fixes several problems with the INSTALL_TARGETS command.  See bug#2691.  Bugs 1481 and 1695 are addressed by these changes.
parent 90c8ea1c
......@@ -115,6 +115,12 @@ SET(SRCS
cmGlobalGenerator.h
cmGlobalUnixMakefileGenerator3.cxx
cmGlobalUnixMakefileGenerator3.h
cmInstallGenerator.h
cmInstallGenerator.cxx
cmInstallScriptGenerator.h
cmInstallScriptGenerator.cxx
cmInstallTargetGenerator.h
cmInstallTargetGenerator.cxx
cmListFileCache.cxx
cmListFileCache.h
cmListFileLexer.c
......
......@@ -16,8 +16,38 @@
=========================================================================*/
#include "cmInstallCommand.h"
#include "cmInstallScriptGenerator.h"
#include "cmInstallTargetGenerator.h"
// cmInstallCommand
bool cmInstallCommand::InitialPass(std::vector<std::string> const& args)
{
// Allow calling with no arguments so that arguments may be built up
// using a variable that may be left empty.
if(args.empty())
{
return true;
}
// Switch among the command modes.
if(args[0] == "SCRIPT")
{
return this->HandleScriptMode(args);
}
else if(args[0] == "TARGETS")
{
return this->HandleTargetsMode(args);
}
// Unknown mode.
cmStdString e = "called with unknown mode ";
e += args[0];
this->SetError(e.c_str());
return false;
}
//----------------------------------------------------------------------------
bool cmInstallCommand::HandleScriptMode(std::vector<std::string> const& args)
{
bool doing_script = false;
for(size_t i=0; i < args.size(); ++i)
......@@ -41,7 +71,8 @@ bool cmInstallCommand::InitialPass(std::vector<std::string> const& args)
this->SetError("given a directory as value of SCRIPT argument.");
return false;
}
m_Makefile->AddInstallScript(script.c_str());
m_Makefile->AddInstallGenerator(
new cmInstallScriptGenerator(script.c_str()));
}
}
if(doing_script)
......@@ -49,6 +80,235 @@ bool cmInstallCommand::InitialPass(std::vector<std::string> const& args)
this->SetError("given no value for SCRIPT argument.");
return false;
}
return true;
}
//----------------------------------------------------------------------------
bool cmInstallCommand::HandleTargetsMode(std::vector<std::string> const& args)
{
// This is the TARGETS mode.
bool doing_targets = true;
bool doing_destination = false;
bool library_settings = true;
bool runtime_settings = true;
std::vector<cmTarget*> targets;
const char* library_destination = 0;
const char* runtime_destination = 0;
cmLocalGenerator* lg = m_Makefile->GetLocalGenerator();
cmGlobalGenerator* gg = lg->GetGlobalGenerator();
for(unsigned int i=1; i < args.size(); ++i)
{
if(args[i] == "DESTINATION")
{
// Switch to setting the destination property.
doing_targets = false;
doing_destination = true;
}
else if(args[i] == "LIBRARY")
{
// Switch to setting only library properties.
doing_targets = false;
doing_destination = false;
library_settings = true;
runtime_settings = false;
}
else if(args[i] == "RUNTIME")
{
// Switch to setting only runtime properties.
doing_targets = false;
doing_destination = false;
library_settings = false;
runtime_settings = true;
}
else if(doing_targets)
{
// Lookup this target in the current project.
if(cmTarget* target = m_Makefile->FindTarget(args[i].c_str()))
{
// Found the target. Check its type.
if(target->GetType() != cmTarget::EXECUTABLE &&
target->GetType() != cmTarget::STATIC_LIBRARY &&
target->GetType() != cmTarget::SHARED_LIBRARY &&
target->GetType() != cmTarget::MODULE_LIBRARY)
{
cmOStringStream e;
e << "TARGETS given target \"" << args[i]
<< "\" which is not an executable, library, or module.";
this->SetError(e.str().c_str());
return false;
}
// Store the target in the list to be installed.
targets.push_back(target);
}
else
{
// Did not find the target.
cmOStringStream e;
e << "TARGETS given target \"" << args[i]
<< "\" which does not exist in this directory.";
this->SetError(e.str().c_str());
return false;
}
}
else if(doing_destination)
{
// Set the destination in the active set(s) of properties.
if(library_settings)
{
library_destination = args[i].c_str();
}
if(runtime_settings)
{
runtime_destination = args[i].c_str();
}
doing_destination = false;
}
else
{
// Unknown argument.
cmOStringStream e;
e << "TARGETS given unknown argument \"" << args[i] << "\".";
this->SetError(e.str().c_str());
return false;
}
}
// Check if there is something to do.
if(targets.empty())
{
return true;
}
if(!library_destination && !runtime_destination)
{
this->SetError("TARGETS given no DESTINATION!");
return false;
}
// Compute destination paths.
std::string library_dest;
std::string runtime_dest;
this->ComputeDestination(library_destination, library_dest);
this->ComputeDestination(runtime_destination, runtime_dest);
// Generate install script code to install the given targets.
for(std::vector<cmTarget*>::iterator ti = targets.begin();
ti != targets.end(); ++ti)
{
// Handle each target type.
cmTarget& target = *(*ti);
switch(target.GetType())
{
case cmTarget::SHARED_LIBRARY:
{
// Shared libraries are handled differently on DLL and non-DLL
// platforms. All windows platforms are DLL platforms
// including cygwin. Currently no other platform is a DLL
// platform.
#if defined(_WIN32) || defined(__CYGWIN__)
// This is a DLL platform.
if(library_destination)
{
// The import library uses the LIBRARY properties.
m_Makefile->AddInstallGenerator(
new cmInstallTargetGenerator(target, library_dest.c_str(), true));
}
if(runtime_destination)
{
// The DLL uses the RUNTIME properties.
m_Makefile->AddInstallGenerator(
new cmInstallTargetGenerator(target, runtime_dest.c_str(), false));
}
#else
// This is a non-DLL platform.
if(library_destination)
{
// The shared library uses the LIBRARY properties.
m_Makefile->AddInstallGenerator(
new cmInstallTargetGenerator(target, library_dest.c_str()));
}
#endif
}
break;
case cmTarget::STATIC_LIBRARY:
case cmTarget::MODULE_LIBRARY:
{
// Static libraries and modules use LIBRARY properties.
if(library_destination)
{
m_Makefile->AddInstallGenerator(
new cmInstallTargetGenerator(target, library_dest.c_str()));
}
else
{
cmOStringStream e;
e << "TARGETS given no LIBRARY DESTINATION for ";
if(target.GetType() == cmTarget::STATIC_LIBRARY)
{
e << "static library";
}
else
{
e << "module";
}
e << " target \"" << target.GetName() << "\".";
this->SetError(e.str().c_str());
return false;
}
}
break;
case cmTarget::EXECUTABLE:
{
// Executables use the RUNTIME properties.
if(runtime_destination)
{
m_Makefile->AddInstallGenerator(
new cmInstallTargetGenerator(target, runtime_dest.c_str()));
}
else
{
cmOStringStream e;
e << "TARGETS given no RUNTIME DESTINATION for executable target \""
<< target.GetName() << "\".";
this->SetError(e.str().c_str());
return false;
}
}
break;
default:
// This should never happen due to the above type check.
// Ignore the case.
break;
}
}
return true;
}
//----------------------------------------------------------------------------
void cmInstallCommand::ComputeDestination(const char* destination,
std::string& dest)
{
if(destination)
{
if(cmSystemTools::FileIsFullPath(destination))
{
// Full paths are absolute.
dest = destination;
}
else
{
// Relative paths are treated with respect to the installation prefix.
dest = "${CMAKE_INSTALL_PREFIX}/";
dest += destination;
}
// Format the path nicely. Note this also removes trailing
// slashes.
cmSystemTools::ConvertToUnixSlashes(dest);
}
else
{
dest = "";
}
}
......@@ -52,7 +52,7 @@ public:
*/
virtual const char* GetTerseDocumentation()
{
return "Install rule specification interface command.";
return "Specify rules to run at install time.";
}
/**
......@@ -61,19 +61,65 @@ public:
virtual const char* GetFullDocumentation()
{
return
" INSTALL(SCRIPT <script1> [SCRIPT <script2> [...]])\n"
"Specify rules to run at install time. If SCRIPT is given the "
"file named after it will be included in the install scripts. "
"Multiple SCRIPTs may be given within a single source directory "
"and the scripts will be run in the order given. "
"The processing order of these scripts relative to install rules "
"This command generates installation rules for a project. "
"Rules specified by calls to this command within a source directory "
"are executed in order during installation. "
"The order across directories is not defined.\n"
"There are multiple signatures for this command:\n"
" INSTALL(TARGETS [targets...] [[LIBRARY|RUNTIME]\n"
" [DESTINATION <destination>]\n"
" ] [...])\n"
"The TARGETS form specifies rules for installing targets from a "
"project. There are two kinds of target files that may be "
"installed: library and runtime. Static libraries and modules "
"are always treated as library targets. Executables are always "
"treated as runtime targets. For non-DLL platforms, shared libraries "
"are treated as library targets. For DLL platforms, the DLL part of "
"a shared library is treated as a runtime target and the corresponding "
"import library is treated as a library target. All Windows-based "
"systems including Cygwin are DLL platforms. The LIBRARY and RUNTIME "
"arguments change the type of target to which the following properties "
"apply. If neither is given the installation properties apply to "
"both target types. If only one is given then only targets of that "
"type will be installed (which can be used to install just a DLL or "
"just an import library).\n"
"DESTINATION arguments specify the directory on disk to which the "
"target file will be installed. "
"If a full path (with a leading slash or drive letter) is given it "
"is used directly. If a relative path is given it is interpreted "
"relative to the value of CMAKE_INSTALL_PREFIX.\n"
"One or more groups of properties may be specified in a single call "
"to the TARGETS form of this command. A target may be installed more "
"than once to different locations. Consider hypothetical "
"targets \"myExe\", \"mySharedLib\", and \"myStaticLib\". The code\n"
" INSTALL(TARGETS myExe mySharedLib myStaticLib\n"
" RUNTIME DESTINATION bin\n"
" LIBRARY DESTINATION lib)\n"
" INSTALL(TARGETS mySharedLib DESTINATION /some/full/path)\n"
"will install myExe to <prefix>/bin and myStaticLib to <prefix>/lib. "
"On non-DLL platforms mySharedLib will be installed to <prefix>/lib and "
"/some/full/path. On DLL platforms the mySharedLib DLL will be "
"installed to <prefix>/bin and /some/full/path and its import library "
"will be installed to <prefix>/lib and /some/full/path. On non-DLL "
"platforms mySharedLib will be installed to <prefix>/lib and "
"/some/full/path.\n"
" INSTALL(SCRIPT <file1> [SCRIPT <file2> [...]])\n"
"The SCRIPT form will invoke the given CMake script files during "
"installation.\n"
"NOTE: This command supercedes the INSTALL_TARGETS command and the "
"target properties PRE_INSTALL_SCRIPT and POST_INSTALL_SCRIPT. "
"The processing order of these install rules relative to those "
"generated by INSTALL_TARGETS, INSTALL_FILES, and INSTALL_PROGRAMS "
"commands is not specified.\n"
"This command is a placeholder for a future larger interface."
"commands is not defined.\n"
;
}
cmTypeMacro(cmInstallCommand, cmCommand);
private:
bool HandleScriptMode(std::vector<std::string> const& args);
bool HandleTargetsMode(std::vector<std::string> const& args);
void ComputeDestination(const char* destination, std::string& dest);
};
......
/*=========================================================================
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 "cmInstallGenerator.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
//----------------------------------------------------------------------------
cmInstallGenerator
::cmInstallGenerator()
{
this->ConfigurationName = 0;
this->ConfigurationTypes = 0;
}
//----------------------------------------------------------------------------
cmInstallGenerator
::~cmInstallGenerator()
{
}
//----------------------------------------------------------------------------
void
cmInstallGenerator
::Generate(std::ostream& os, const char* config,
std::vector<std::string> const& configurationTypes)
{
this->ConfigurationName = config;
this->ConfigurationTypes = &configurationTypes;
this->GenerateScript(os);
this->ConfigurationName = 0;
this->ConfigurationTypes = 0;
}
//----------------------------------------------------------------------------
void cmInstallGenerator::AddInstallRule(std::ostream& os,
const char* dest,
int type,
const char* files,
bool optional /* = false */,
const char* properties /* = 0 */)
{
// TODO: Make optional files use IF(EXISTS) to not report if not
// installing.
std::string sfiles = files;
std::string destination = dest;
std::string stype;
switch(type)
{
case cmTarget::INSTALL_PROGRAMS: stype = "PROGRAM"; break;
case cmTarget::EXECUTABLE: stype = "EXECUTABLE"; break;
case cmTarget::STATIC_LIBRARY: stype = "STATIC_LIBRARY"; break;
case cmTarget::SHARED_LIBRARY: stype = "SHARED_LIBRARY"; break;
case cmTarget::MODULE_LIBRARY: stype = "MODULE"; break;
case cmTarget::INSTALL_FILES:
default: stype = "FILE"; break;
}
std::string fname = cmSystemTools::GetFilenameName(sfiles.c_str());
os << "MESSAGE(STATUS \"Installing " << destination.c_str()
<< "/" << fname.c_str() << "\")\n"
<< "FILE(INSTALL DESTINATION \"" << destination.c_str()
<< "\" TYPE " << stype.c_str() << (optional?" OPTIONAL":"") ;
if(properties && *properties)
{
os << " PROPERTIES" << properties;
}
os << " FILES \"" << sfiles.c_str() << "\")\n";
}
/*=========================================================================
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 cmInstallGenerator_h
#define cmInstallGenerator_h
#include "cmStandardIncludes.h"
class cmLocalGenerator;
/** \class cmInstallGenerator
* \brief Support class for generating install scripts.
*
*/
class cmInstallGenerator
{
public:
cmInstallGenerator();
virtual ~cmInstallGenerator();
void Generate(std::ostream& os, const char* config,
std::vector<std::string> const& configurationTypes);
static void AddInstallRule(std::ostream& os, const char* dest, int type,
const char* files, bool optional = false,
const char* properties = 0);
protected:
virtual void GenerateScript(std::ostream& os)=0;
const char* ConfigurationName;
std::vector<std::string> const* ConfigurationTypes;
};
#endif
/*=========================================================================
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 "cmInstallScriptGenerator.h"
//----------------------------------------------------------------------------
cmInstallScriptGenerator
::cmInstallScriptGenerator(const char* script): Script(script)
{
}
//----------------------------------------------------------------------------
cmInstallScriptGenerator
::~cmInstallScriptGenerator()
{
}
//----------------------------------------------------------------------------
void cmInstallScriptGenerator::GenerateScript(std::ostream& os)
{
os << "INCLUDE(\"" << this->Script << "\")\n";
}
/*=========================================================================
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 cmInstallScriptGenerator_h
#define cmInstallScriptGenerator_h
#include "cmInstallGenerator.h"
/** \class cmInstallScriptGenerator
* \brief Generate target installation rules.
*/
class cmInstallScriptGenerator: public cmInstallGenerator
{
public:
cmInstallScriptGenerator(const char* script);
virtual ~cmInstallScriptGenerator();
protected:
virtual void GenerateScript(std::ostream& os);
std::string Script;
};
#endif
/*=========================================================================
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 "cmInstallTargetGenerator.h"
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmTarget.h"
//----------------------------------------------------------------------------
cmInstallTargetGenerator
::cmInstallTargetGenerator(cmTarget& t, const char* dest, bool implib):
Target(&t), Destination(dest), ImportLibrary(implib)
{
}
//----------------------------------------------------------------------------
cmInstallTargetGenerator