ENH: Add ability to call Visual Studio macros from CMake. Add a CMake Visual Studio macro to reload a solution file automatically if CMake makes changes to .sln files or .vcproj files. Add code to call the macro automatically for any running Visual Studio instances with the .sln file open at the end of the Visual Studio Generate call. Only call the macro if some .sln or .vcproj file changed during Generate. Also, add handling for REG_EXPAND_SZ type to SystemTools::ReadRegistryValue - returned string has environment variable references expanded.

This commit is contained in:
David Cole 2007-11-16 07:01:58 -05:00
parent 0a001f1567
commit 867de7fc67
18 changed files with 1064 additions and 15 deletions

View File

@ -208,6 +208,11 @@ ENDIF(APPLE)
IF (WIN32) IF (WIN32)
SET(SRCS ${SRCS}
cmCallVisualStudioMacro.cxx
cmCallVisualStudioMacro.h
)
IF(NOT UNIX) IF(NOT UNIX)
SET(SRCS ${SRCS} SET(SRCS ${SRCS}
cmGlobalBorlandMakefileGenerator.cxx cmGlobalBorlandMakefileGenerator.cxx

View File

@ -0,0 +1,464 @@
/*=========================================================================
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 "cmCallVisualStudioMacro.h"
#include "cmSystemTools.h"
#if defined(_MSC_VER)
#define HAVE_COMDEF_H
#endif
#if defined(HAVE_COMDEF_H)
#include <comdef.h>
//----------------------------------------------------------------------------
///! Use ReportHRESULT to make a cmSystemTools::Error after calling
///! a COM method that may have failed.
#define ReportHRESULT(hr, context) \
if (FAILED(hr)) \
{ \
std::ostringstream oss; \
oss.flags(std::ios::hex); \
oss << context << " failed HRESULT, hr = 0x" << hr << std::endl; \
oss.flags(std::ios::dec); \
oss << __FILE__ << "(" << __LINE__ << ")"; \
cmSystemTools::Error(oss.str().c_str()); \
}
//----------------------------------------------------------------------------
///! Using the given instance of Visual Studio, call the named macro
HRESULT InstanceCallMacro(
IDispatch* vsIDE,
const std::string& macro,
const std::string& args)
{
HRESULT hr = E_POINTER;
_bstr_t macroName(macro.c_str());
_bstr_t macroArgs(args.c_str());
if (0 != vsIDE)
{
DISPID dispid = (DISPID) -1;
OLECHAR *name = L"ExecuteCommand";
hr = vsIDE->GetIDsOfNames(IID_NULL, &name, 1,
LOCALE_USER_DEFAULT, &dispid);
ReportHRESULT(hr, "GetIDsOfNames(ExecuteCommand)");
if (SUCCEEDED(hr))
{
VARIANTARG vargs[2];
DISPPARAMS params;
VARIANT result;
EXCEPINFO excep;
UINT arg = (UINT) -1;
// No VariantInit or VariantClear calls are necessary for
// these two vargs. They are both local _bstr_t variables
// that remain in scope for the duration of the Invoke call.
//
V_VT(&vargs[1]) = VT_BSTR;
V_BSTR(&vargs[1]) = macroName;
V_VT(&vargs[0]) = VT_BSTR;
V_BSTR(&vargs[0]) = macroArgs;
params.rgvarg = &vargs[0];
params.rgdispidNamedArgs = 0;
params.cArgs = sizeof(vargs)/sizeof(vargs[0]);
params.cNamedArgs = 0;
VariantInit(&result);
memset(&excep, 0, sizeof(excep));
hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_METHOD, &params, &result, &excep, &arg);
ReportHRESULT(hr, "Invoke(ExecuteCommand)");
VariantClear(&result);
}
}
return hr;
}
//----------------------------------------------------------------------------
///! Get the Solution object from the IDE object
HRESULT GetSolutionObject(
IDispatch* vsIDE,
IDispatchPtr& vsSolution)
{
HRESULT hr = E_POINTER;
if (0 != vsIDE)
{
DISPID dispid = (DISPID) -1;
OLECHAR *name = L"Solution";
hr = vsIDE->GetIDsOfNames(IID_NULL, &name, 1,
LOCALE_USER_DEFAULT, &dispid);
ReportHRESULT(hr, "GetIDsOfNames(Solution)");
if (SUCCEEDED(hr))
{
DISPPARAMS params;
VARIANT result;
EXCEPINFO excep;
UINT arg = (UINT) -1;
params.rgvarg = 0;
params.rgdispidNamedArgs = 0;
params.cArgs = 0;
params.cNamedArgs = 0;
VariantInit(&result);
memset(&excep, 0, sizeof(excep));
hr = vsIDE->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYGET, &params, &result, &excep, &arg);
ReportHRESULT(hr, "Invoke(Solution)");
if (SUCCEEDED(hr))
{
vsSolution = V_DISPATCH(&result);
}
VariantClear(&result);
}
}
return hr;
}
//----------------------------------------------------------------------------
///! Get the FullName property from the Solution object
HRESULT GetSolutionFullName(
IDispatch* vsSolution,
std::string& fullName)
{
HRESULT hr = E_POINTER;
if (0 != vsSolution)
{
DISPID dispid = (DISPID) -1;
OLECHAR *name = L"FullName";
hr = vsSolution->GetIDsOfNames(IID_NULL, &name, 1,
LOCALE_USER_DEFAULT, &dispid);
ReportHRESULT(hr, "GetIDsOfNames(FullName)");
if (SUCCEEDED(hr))
{
DISPPARAMS params;
VARIANT result;
EXCEPINFO excep;
UINT arg = (UINT) -1;
params.rgvarg = 0;
params.rgdispidNamedArgs = 0;
params.cArgs = 0;
params.cNamedArgs = 0;
VariantInit(&result);
memset(&excep, 0, sizeof(excep));
hr = vsSolution->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYGET, &params, &result, &excep, &arg);
ReportHRESULT(hr, "Invoke(FullName)");
if (SUCCEEDED(hr))
{
fullName = (std::string) (_bstr_t) V_BSTR(&result);
}
VariantClear(&result);
}
}
return hr;
}
//----------------------------------------------------------------------------
///! Get the FullName property from the Solution object, given the IDE object
HRESULT GetIDESolutionFullName(
IDispatch* vsIDE,
std::string& fullName)
{
IDispatchPtr vsSolution;
HRESULT hr = GetSolutionObject(vsIDE, vsSolution);
ReportHRESULT(hr, "GetSolutionObject");
if (SUCCEEDED(hr))
{
GetSolutionFullName(vsSolution, fullName);
ReportHRESULT(hr, "GetSolutionFullName");
}
return hr;
}
//----------------------------------------------------------------------------
///! Get all running objects from the Windows running object table.
///! Save them in a map by their display names.
HRESULT GetRunningInstances(std::map<std::string, IUnknownPtr>& mrot)
{
// mrot == Map of the Running Object Table
IRunningObjectTablePtr runningObjectTable;
IEnumMonikerPtr monikerEnumerator;
IMonikerPtr moniker;
ULONG numFetched = 0;
HRESULT hr = GetRunningObjectTable(0, &runningObjectTable);
ReportHRESULT(hr, "GetRunningObjectTable");
if(SUCCEEDED(hr))
{
hr = runningObjectTable->EnumRunning(&monikerEnumerator);
ReportHRESULT(hr, "EnumRunning");
}
if(SUCCEEDED(hr))
{
hr = monikerEnumerator->Reset();
ReportHRESULT(hr, "Reset");
}
if(SUCCEEDED(hr))
{
while (S_OK == monikerEnumerator->Next(1, &moniker, &numFetched))
{
std::string runningObjectName;
IUnknownPtr runningObjectVal;
IBindCtxPtr ctx;
hr = CreateBindCtx(0, &ctx);
ReportHRESULT(hr, "CreateBindCtx");
if(SUCCEEDED(hr))
{
LPOLESTR displayName = 0;
hr = moniker->GetDisplayName(ctx, 0, &displayName);
ReportHRESULT(hr, "GetDisplayName");
if (displayName)
{
runningObjectName = (std::string) (_bstr_t) displayName;
CoTaskMemFree(displayName);
}
hr = runningObjectTable->GetObject(moniker, &runningObjectVal);
ReportHRESULT(hr, "GetObject");
if(SUCCEEDED(hr))
{
mrot.insert(std::make_pair(runningObjectName, runningObjectVal));
}
}
numFetched = 0;
moniker = 0;
}
}
return hr;
}
//----------------------------------------------------------------------------
///! Do the two file names refer to the same Visual Studio solution? Or are
///! we perhaps looking for any and all solutions?
bool FilesSameSolution(
const std::string& slnFile,
const std::string& slnName)
{
if (slnFile == "ALL" || slnName == "ALL")
{
return true;
}
// Otherwise, make lowercase local copies, convert to Unix slashes, and
// see if the resulting strings are the same:
std::string s1 = cmSystemTools::LowerCase(slnFile);
std::string s2 = cmSystemTools::LowerCase(slnName);
cmSystemTools::ConvertToUnixSlashes(s1);
cmSystemTools::ConvertToUnixSlashes(s2);
return s1 == s2;
}
//----------------------------------------------------------------------------
///! Find instances of Visual Studio with the given solution file
///! open. Pass "ALL" for slnFile to gather all running instances
///! of Visual Studio.
HRESULT FindVisualStudioInstances(
const std::string& slnFile,
std::vector<IDispatchPtr>& instances)
{
std::map<std::string, IUnknownPtr> mrot;
HRESULT hr = GetRunningInstances(mrot);
ReportHRESULT(hr, "GetRunningInstances");
if(SUCCEEDED(hr))
{
std::map<std::string, IUnknownPtr>::iterator it;
for(it = mrot.begin(); it != mrot.end(); ++it)
{
if (cmSystemTools::StringStartsWith(it->first.c_str(),
"!VisualStudio.DTE."))
{
IDispatchPtr disp(it->second);
if (disp != (IDispatch*) 0)
{
std::string slnName;
hr = GetIDESolutionFullName(disp, slnName);
ReportHRESULT(hr, "GetIDESolutionFullName");
if (FilesSameSolution(slnFile, slnName))
{
instances.push_back(disp);
//std::cout << "Found Visual Studio instance." << std::endl;
//std::cout << " ROT entry name: " << it->first << std::endl;
//std::cout << " ROT entry object: " << (IUnknown*) it->second << std::endl;
//std::cout << " slnFile: " << slnFile << std::endl;
//std::cout << " slnName: " << slnName << std::endl;
}
}
}
}
}
return hr;
}
#endif //defined(HAVE_COMDEF_H)
//----------------------------------------------------------------------------
int cmCallVisualStudioMacro::GetNumberOfRunningVisualStudioInstances(
const std::string& slnFile)
{
int count = 0;
#if defined(HAVE_COMDEF_H)
HRESULT hr = CoInitialize(0);
ReportHRESULT(hr, "CoInitialize");
if(SUCCEEDED(hr))
{
std::vector<IDispatchPtr> instances;
hr = FindVisualStudioInstances(slnFile, instances);
ReportHRESULT(hr, "FindVisualStudioInstances");
if(SUCCEEDED(hr))
{
count = instances.size();
}
// Force release all COM pointers before CoUninitialize:
instances.clear();
CoUninitialize();
}
#endif
return count;
}
//----------------------------------------------------------------------------
///! Get all running objects from the Windows running object table.
///! Save them in a map by their display names.
int cmCallVisualStudioMacro::CallMacro(
const std::string& slnFile,
const std::string& macro,
const std::string& args)
{
int err = 1; // no comdef.h
#if defined(HAVE_COMDEF_H)
err = 2; // error initializing
HRESULT hr = CoInitialize(0);
ReportHRESULT(hr, "CoInitialize");
if(SUCCEEDED(hr))
{
std::vector<IDispatchPtr> instances;
hr = FindVisualStudioInstances(slnFile, instances);
ReportHRESULT(hr, "FindVisualStudioInstances");
if(SUCCEEDED(hr))
{
err = 0; // no error
std::vector<IDispatchPtr>::iterator it;
for(it = instances.begin(); it != instances.end(); ++it)
{
hr = InstanceCallMacro(*it, macro, args);
ReportHRESULT(hr, "InstanceCallMacro");
if (FAILED(hr))
{
err = 3; // error attempting to call the macro
}
}
if(0 == instances.size())
{
// no instances to call
//cmSystemTools::Message(
// "cmCallVisualStudioMacro::CallMacro no instances found to call",
// "Warning");
}
}
// Force release all COM pointers before CoUninitialize:
instances.clear();
CoUninitialize();
}
#else
cmSystemTools::Error("cmCallVisualStudioMacro::CallMacro is not "
"supported on this platform");
#endif
if (err)
{
std::ostringstream oss;
oss << "cmCallVisualStudioMacro::CallMacro failed, err = " << err;
cmSystemTools::Error(oss.str().c_str());
}
return err;
}

View File

@ -0,0 +1,49 @@
/*=========================================================================
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 cmCallVisualStudioMacro_h
#define cmCallVisualStudioMacro_h
#include "cmStandardIncludes.h"
/** \class cmCallVisualStudioMacro
* \brief Control class for communicating with CMake's Visual Studio macros
*
* Find running instances of Visual Studio by full path solution name.
* Call a Visual Studio IDE macro in any of those instances.
*/
class cmCallVisualStudioMacro
{
public:
///! Call the named macro in instances of Visual Studio with the
///! given solution file open. Pass "ALL" for slnFile to call the
///! macro in each Visual Studio instance.
static int CallMacro(const std::string& slnFile,
const std::string& macro,
const std::string& args);
///! Count the number of running instances of Visual Studio with the
///! given solution file open. Pass "ALL" for slnFile to count all
///! running Visual Studio instances.
static int GetNumberOfRunningVisualStudioInstances(
const std::string& slnFile);
protected:
private:
};
#endif

View File

@ -90,7 +90,7 @@ cmGeneratedFileStream::Open(const char* name, bool quiet, bool binaryFlag)
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
cmGeneratedFileStream& bool
cmGeneratedFileStream::Close() cmGeneratedFileStream::Close()
{ {
// Save whether the temporary output file is valid before closing. // Save whether the temporary output file is valid before closing.
@ -100,9 +100,7 @@ cmGeneratedFileStream::Close()
this->Stream::close(); this->Stream::close();
// Remove the temporary file (possibly by renaming to the real file). // Remove the temporary file (possibly by renaming to the real file).
this->cmGeneratedFileStreamBase::Close(); return this->cmGeneratedFileStreamBase::Close();
return *this;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
@ -170,8 +168,10 @@ void cmGeneratedFileStreamBase::Open(const char* name)
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmGeneratedFileStreamBase::Close() bool cmGeneratedFileStreamBase::Close()
{ {
bool replaced = false;
std::string resname = this->Name; std::string resname = this->Name;
if ( this->Compress && this->CompressExtraExtension ) if ( this->Compress && this->CompressExtraExtension )
{ {
@ -200,12 +200,16 @@ void cmGeneratedFileStreamBase::Close()
{ {
this->RenameFile(this->TempName.c_str(), resname.c_str()); this->RenameFile(this->TempName.c_str(), resname.c_str());
} }
replaced = true;
} }
// Else, the destination was not replaced. // Else, the destination was not replaced.
// //
// Always delete the temporary file. We never want it to stay around. // Always delete the temporary file. We never want it to stay around.
cmSystemTools::RemoveFile(this->TempName.c_str()); cmSystemTools::RemoveFile(this->TempName.c_str());
return replaced;
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

@ -44,7 +44,7 @@ protected:
// after the real stream is closed and Okay is set to whether the // after the real stream is closed and Okay is set to whether the
// real stream was still valid for writing when it was closed. // real stream was still valid for writing when it was closed.
void Open(const char* name); void Open(const char* name);
void Close(); bool Close();
// Internal file replacement implementation. // Internal file replacement implementation.
int RenameFile(const char* oldname, const char* newname); int RenameFile(const char* oldname, const char* newname);
@ -123,7 +123,7 @@ public:
* destionation file if the stream is still valid when this method * destionation file if the stream is still valid when this method
* is called. * is called.
*/ */
cmGeneratedFileStream& Close(); bool Close();
/** /**
* Set whether copy-if-different is done. * Set whether copy-if-different is done.

View File

@ -737,6 +737,10 @@ void cmGlobalGenerator::Configure()
void cmGlobalGenerator::Generate() void cmGlobalGenerator::Generate()
{ {
// Some generators track files replaced during the Generate.
// Start with an empty vector:
this->FilesReplacedDuringGenerate.clear();
// For each existing cmLocalGenerator // For each existing cmLocalGenerator
unsigned int i; unsigned int i;
@ -1785,3 +1789,17 @@ const char* cmGlobalGenerator::GetExtraGeneratorName() const
{ {
return this->ExtraGenerator==0 ? 0 : this->ExtraGenerator->GetName(); return this->ExtraGenerator==0 ? 0 : this->ExtraGenerator->GetName();
} }
void cmGlobalGenerator::FileReplacedDuringGenerate(const std::string& filename)
{
this->FilesReplacedDuringGenerate.push_back(filename);
}
void cmGlobalGenerator::GetFilesReplacedDuringGenerate(std::vector<std::string>& filenames)
{
filenames.clear();
std::copy(
this->FilesReplacedDuringGenerate.begin(),
this->FilesReplacedDuringGenerate.end(),
std::back_inserter(filenames));
}

View File

@ -230,6 +230,11 @@ public:
const std::map<cmStdString, std::vector<cmLocalGenerator*> >& GetProjectMap() const std::map<cmStdString, std::vector<cmLocalGenerator*> >& GetProjectMap()
const {return this->ProjectMap;} const {return this->ProjectMap;}
// track files replaced during a Generate
void FileReplacedDuringGenerate(const std::string& filename);
void GetFilesReplacedDuringGenerate(std::vector<std::string>& filenames);
protected: protected:
void SetLanguageEnabledFlag(const char* l, cmMakefile* mf); void SetLanguageEnabledFlag(const char* l, cmMakefile* mf);
void SetLanguageEnabledMaps(const char* l, cmMakefile* mf); void SetLanguageEnabledMaps(const char* l, cmMakefile* mf);
@ -287,6 +292,9 @@ private:
std::map<cmStdString, std::vector<cmTarget *> > TargetDependencies; std::map<cmStdString, std::vector<cmTarget *> > TargetDependencies;
cmExternalMakefileProjectGenerator* ExtraGenerator; cmExternalMakefileProjectGenerator* ExtraGenerator;
// track files replaced during a Generate
std::vector<std::string> FilesReplacedDuringGenerate;
}; };
#endif #endif

View File

@ -16,8 +16,8 @@
=========================================================================*/ =========================================================================*/
#include "windows.h" // this must be first to define GetCurrentDirectory #include "windows.h" // this must be first to define GetCurrentDirectory
#include "cmGlobalVisualStudio7Generator.h" #include "cmGlobalVisualStudio7Generator.h"
#include "cmLocalVisualStudio7Generator.h"
#include "cmGeneratedFileStream.h" #include "cmGeneratedFileStream.h"
#include "cmLocalVisualStudio7Generator.h"
#include "cmMakefile.h" #include "cmMakefile.h"
#include "cmake.h" #include "cmake.h"
@ -202,7 +202,7 @@ void cmGlobalVisualStudio7Generator::GenerateConfigurations(cmMakefile* mf)
configs += ";"; configs += ";";
configs += this->Configurations[i]; configs += this->Configurations[i];
} }
mf->AddCacheDefinition( mf->AddCacheDefinition(
"CMAKE_CONFIGURATION_TYPES", "CMAKE_CONFIGURATION_TYPES",
configs.c_str(), configs.c_str(),
@ -219,6 +219,13 @@ void cmGlobalVisualStudio7Generator::Generate()
// Now write out the DSW // Now write out the DSW
this->OutputSLNFile(); this->OutputSLNFile();
// If any solution or project files changed during the generation,
// tell Visual Studio to reload them...
if(!cmSystemTools::GetErrorOccuredFlag())
{
this->CallVisualStudioReloadMacro();
}
} }
void cmGlobalVisualStudio7Generator void cmGlobalVisualStudio7Generator
@ -241,11 +248,15 @@ void cmGlobalVisualStudio7Generator
return; return;
} }
this->WriteSLNFile(fout, root, generators); this->WriteSLNFile(fout, root, generators);
if (fout.Close())
{
this->FileReplacedDuringGenerate(fname);
}
} }
// output the SLN file // output the SLN file
void cmGlobalVisualStudio7Generator::OutputSLNFile() void cmGlobalVisualStudio7Generator::OutputSLNFile()
{ {
std::map<cmStdString, std::vector<cmLocalGenerator*> >::iterator it; std::map<cmStdString, std::vector<cmLocalGenerator*> >::iterator it;
for(it = this->ProjectMap.begin(); it!= this->ProjectMap.end(); ++it) for(it = this->ProjectMap.begin(); it!= this->ProjectMap.end(); ++it)
{ {

View File

@ -71,6 +71,34 @@ void cmGlobalVisualStudio8Generator::Configure()
this->CreateGUID(CMAKE_CHECK_BUILD_SYSTEM_TARGET); this->CreateGUID(CMAKE_CHECK_BUILD_SYSTEM_TARGET);
} }
//----------------------------------------------------------------------------
std::string cmGlobalVisualStudio8Generator::GetUserMacrosDirectory()
{
std::string base;
std::string path;
// base begins with the VisualStudioProjectsLocation reg value...
if (cmSystemTools::ReadRegistryValue(
"HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\8.0;VisualStudioProjectsLocation",
base))
{
cmSystemTools::ConvertToUnixSlashes(base);
// 7.0 macros folder:
//path = base + "/VSMacros";
// 7.1 macros folder:
//path = base + "/VSMacros71";
// 8.0 macros folder:
path = base + "/VSMacros80";
}
// path is (correctly) still empty if we did not read the base value from
// the Registry value
return path;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmGlobalVisualStudio8Generator::Generate() void cmGlobalVisualStudio8Generator::Generate()
{ {

View File

@ -49,6 +49,14 @@ public:
*/ */
virtual void Configure(); virtual void Configure();
virtual void Generate(); virtual void Generate();
/**
* Where does this version of Visual Studio look for macros for the
* current user? Returns the empty string if this version of Visual
* Studio does not implement support for VB macros.
*/
virtual std::string GetUserMacrosDirectory();
protected: protected:
// Utility target fix is not needed for VS8. // Utility target fix is not needed for VS8.

View File

@ -53,9 +53,36 @@ void cmGlobalVisualStudio9Generator
entry.Full = ""; entry.Full = "";
} }
//----------------------------------------------------------------------------
void cmGlobalVisualStudio9Generator void cmGlobalVisualStudio9Generator
::EnableLanguage(std::vector<std::string>const & lang, ::EnableLanguage(std::vector<std::string>const & lang,
cmMakefile *mf, bool optional) cmMakefile *mf, bool optional)
{ {
cmGlobalVisualStudio8Generator::EnableLanguage(lang, mf, optional); cmGlobalVisualStudio8Generator::EnableLanguage(lang, mf, optional);
} }
//----------------------------------------------------------------------------
std::string cmGlobalVisualStudio9Generator::GetUserMacrosDirectory()
{
std::string base;
std::string path;
// base begins with the VisualStudioProjectsLocation reg value...
if (cmSystemTools::ReadRegistryValue(
"HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\9.0;VisualStudioProjectsLocation",
base))
{
cmSystemTools::ConvertToUnixSlashes(base);
// 9.0 macros folder:
path = base + "/VSMacros80";
// *NOT* a typo; right now in Visual Studio 2008 beta the macros
// folder is VSMacros80... They may change it to 90 before final
// release of 2008 or they may not... we'll have to keep our eyes
// on it
}
// path is (correctly) still empty if we did not read the base value from
// the Registry value
return path;
}

View File

@ -51,5 +51,12 @@ public:
virtual void EnableLanguage(std::vector<std::string>const& languages, virtual void EnableLanguage(std::vector<std::string>const& languages,
cmMakefile *, bool optional); cmMakefile *, bool optional);
virtual void WriteSLNHeader(std::ostream& fout); virtual void WriteSLNHeader(std::ostream& fout);
/**
* Where does this version of Visual Studio look for macros for the
* current user? Returns the empty string if this version of Visual
* Studio does not implement support for VB macros.
*/
virtual std::string GetUserMacrosDirectory();
}; };
#endif #endif

View File

@ -16,6 +16,7 @@
=========================================================================*/ =========================================================================*/
#include "cmGlobalVisualStudioGenerator.h" #include "cmGlobalVisualStudioGenerator.h"
#include "cmCallVisualStudioMacro.h"
#include "cmLocalGenerator.h" #include "cmLocalGenerator.h"
#include "cmMakefile.h" #include "cmMakefile.h"
#include "cmTarget.h" #include "cmTarget.h"
@ -57,10 +58,99 @@ void cmGlobalVisualStudioGenerator::Generate()
// Fix utility dependencies to avoid linking to libraries. // Fix utility dependencies to avoid linking to libraries.
this->FixUtilityDepends(); this->FixUtilityDepends();
// Configure CMake Visual Studio macros, for this user on this version
// of Visual Studio.
this->ConfigureCMakeVisualStudioMacros();
// Run all the local generators. // Run all the local generators.
this->cmGlobalGenerator::Generate(); this->cmGlobalGenerator::Generate();
} }
//----------------------------------------------------------------------------
void RegisterVisualStudioMacros(const std::string& macrosFile);
//----------------------------------------------------------------------------
#define CMAKE_VSMACROS_FILENAME \
"CMakeVSMacros1.vsmacros"
#define CMAKE_VSMACROS_RELOAD_MACRONAME \
"Macros.CMakeVSMacros1.Macros.ReloadProjects"
//----------------------------------------------------------------------------
void cmGlobalVisualStudioGenerator::ConfigureCMakeVisualStudioMacros()
{
cmMakefile* mf = this->LocalGenerators[0]->GetMakefile();
std::string dir = this->GetUserMacrosDirectory();
if (mf != 0 && dir != "")
{
std::string src = mf->GetRequiredDefinition("CMAKE_ROOT");
src += "/Templates/" CMAKE_VSMACROS_FILENAME;
std::string dst = dir + "/CMakeMacros/" CMAKE_VSMACROS_FILENAME;
// Only copy if dst does not already exist. Write this file initially,
// but never overwrite local mods.
if (!cmSystemTools::FileExists(dst.c_str()))
{
if (!cmSystemTools::CopyFileAlways(src.c_str(), dst.c_str()))
{
std::ostringstream oss;
oss << "Could not copy from: " << src << std::endl;
oss << " to: " << dst << std::endl;
cmSystemTools::Message(oss.str().c_str(), "Warning");
}
}
RegisterVisualStudioMacros(dst);
}
}
//----------------------------------------------------------------------------
void cmGlobalVisualStudioGenerator::CallVisualStudioReloadMacro()
{
// If any solution or project files changed during the generation,
// tell Visual Studio to reload them...
cmMakefile* mf = this->LocalGenerators[0]->GetMakefile();
std::string dir = this->GetUserMacrosDirectory();
if (mf != 0 && dir != "")
{
std::vector<std::string> filenames;
this->GetFilesReplacedDuringGenerate(filenames);
if (filenames.size() > 0)
{
// Convert vector to semi-colon delimited string of filenames:
std::string projects;
std::vector<std::string>::iterator it = filenames.begin();
if (it != filenames.end())
{
projects = *it;
++it;
}
for (; it != filenames.end(); ++it)
{
projects += ";";
projects += *it;
}
std::string topLevelSlnName = mf->GetStartOutputDirectory();
topLevelSlnName += "/";
topLevelSlnName += mf->GetProjectName();
topLevelSlnName += ".sln";
cmCallVisualStudioMacro::CallMacro(topLevelSlnName,
CMAKE_VSMACROS_RELOAD_MACRONAME, projects);
}
}
}
//----------------------------------------------------------------------------
std::string cmGlobalVisualStudioGenerator::GetUserMacrosDirectory()
{
return "";
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmGlobalVisualStudioGenerator::FixUtilityDepends() void cmGlobalVisualStudioGenerator::FixUtilityDepends()
{ {
@ -224,3 +314,268 @@ cmGlobalVisualStudioGenerator::GetUtilityForTarget(cmTarget& target,
// No special case. Just use the original dependency name. // No special case. Just use the original dependency name.
return name; return name;
} }
//----------------------------------------------------------------------------
#include <windows.h>
//----------------------------------------------------------------------------
bool IsVisualStudioMacrosFileRegistered(const std::string& macrosFile,
std::string& nextAvailableSubKeyName)
{
bool macrosRegistered = false;
std::string s1;
std::string s2;
// Make lowercase local copies, convert to Unix slashes, and
// see if the resulting strings are the same:
s1 = cmSystemTools::LowerCase(macrosFile);
cmSystemTools::ConvertToUnixSlashes(s1);
std::string keyname;
HKEY hkey = NULL;
LONG result = ERROR_SUCCESS;
DWORD index = 0;
keyname = "Software\\Microsoft\\VisualStudio\\8.0\\vsmacros\\OtherProjects7";
hkey = NULL;
result = RegOpenKeyEx(HKEY_CURRENT_USER, keyname.c_str(), 0, KEY_READ, &hkey);
if (ERROR_SUCCESS == result)
{
// Iterate the subkeys and look for the values of interest in each subkey:
CHAR subkeyname[256];
DWORD cch_subkeyname = sizeof(subkeyname)/sizeof(subkeyname[0]);
CHAR keyclass[256];
DWORD cch_keyclass = sizeof(keyclass)/sizeof(keyclass[0]);
FILETIME lastWriteTime;
lastWriteTime.dwHighDateTime = 0;
lastWriteTime.dwLowDateTime = 0;
while (ERROR_SUCCESS == RegEnumKeyEx(hkey, index, subkeyname, &cch_subkeyname,
0, keyclass, &cch_keyclass, &lastWriteTime))
{
// Open the subkey and query the values of interest:
HKEY hsubkey = NULL;
result = RegOpenKeyEx(hkey, subkeyname, 0, KEY_READ, &hsubkey);
if (ERROR_SUCCESS == result)
{
DWORD valueType = REG_SZ;
CHAR data1[256];
DWORD cch_data1 = sizeof(data1)/sizeof(data1[0]);
RegQueryValueEx(hsubkey, "Path", 0, &valueType, (LPBYTE) &data1[0], &cch_data1);
DWORD data2 = 0;
DWORD cch_data2 = sizeof(data2);
RegQueryValueEx(hsubkey, "Security", 0, &valueType, (LPBYTE) &data2, &cch_data2);
DWORD data3 = 0;
DWORD cch_data3 = sizeof(data3);
RegQueryValueEx(hsubkey, "StorageFormat", 0, &valueType, (LPBYTE) &data3, &cch_data3);
s2 = cmSystemTools::LowerCase(data1);
cmSystemTools::ConvertToUnixSlashes(s2);
if (s2 == s1)
{
macrosRegistered = true;
}
std::string fullname(data1);
std::string filename;
std::string filepath;
std::string filepathname;
std::string filepathpath;
if (cmSystemTools::FileExists(fullname.c_str()))
{
filename = cmSystemTools::GetFilenameName(fullname);
filepath = cmSystemTools::GetFilenamePath(fullname);
filepathname = cmSystemTools::GetFilenameName(filepath);
filepathpath = cmSystemTools::GetFilenamePath(filepath);
}
//std::cout << keyname << "\\" << subkeyname << ":" << std::endl;
//std::cout << " Path: " << data1 << std::endl;
//std::cout << " Security: " << data2 << std::endl;
//std::cout << " StorageFormat: " << data3 << std::endl;
//std::cout << " filename: " << filename << std::endl;
//std::cout << " filepath: " << filepath << std::endl;
//std::cout << " filepathname: " << filepathname << std::endl;
//std::cout << " filepathpath: " << filepathpath << std::endl;
//std::cout << std::endl;
RegCloseKey(hsubkey);
}
else
{
std::cout << "error opening subkey: " << subkeyname << std::endl;
std::cout << std::endl;
}
++index;
cch_subkeyname = sizeof(subkeyname)/sizeof(subkeyname[0]);
cch_keyclass = sizeof(keyclass)/sizeof(keyclass[0]);
lastWriteTime.dwHighDateTime = 0;
lastWriteTime.dwLowDateTime = 0;
}
RegCloseKey(hkey);
}
else
{
std::cout << "error opening key: " << keyname << std::endl;
std::cout << std::endl;
}
// Pass back next available sub key name, assuming sub keys always
// follow the expected naming scheme. Expected naming scheme is that
// the subkeys of OtherProjects7 is 0 to n-1, so it's ok to use "n"
// as the name of the next subkey.
std::ostringstream ossNext;
ossNext << index;
nextAvailableSubKeyName = ossNext.str();
keyname = "Software\\Microsoft\\VisualStudio\\8.0\\vsmacros\\RecordingProject7";
hkey = NULL;
result = RegOpenKeyEx(HKEY_CURRENT_USER, keyname.c_str(), 0, KEY_READ, &hkey);
if (ERROR_SUCCESS == result)
{
DWORD valueType = REG_SZ;
CHAR data1[256];
DWORD cch_data1 = sizeof(data1)/sizeof(data1[0]);
RegQueryValueEx(hkey, "Path", 0, &valueType, (LPBYTE) &data1[0], &cch_data1);
DWORD data2 = 0;
DWORD cch_data2 = sizeof(data2);
RegQueryValueEx(hkey, "Security", 0, &valueType, (LPBYTE) &data2, &cch_data2);
DWORD data3 = 0;
DWORD cch_data3 = sizeof(data3);
RegQueryValueEx(hkey, "StorageFormat", 0, &valueType, (LPBYTE) &data3, &cch_data3);
s2 = cmSystemTools::LowerCase(data1);
cmSystemTools::ConvertToUnixSlashes(s2);
if (s2 == s1)
{
macrosRegistered = true;
}
//std::cout << keyname << ":" << std::endl;
//std::cout << " Path: " << data1 << std::endl;
//std::cout << " Security: " << data2 << std::endl;
//std::cout << " StorageFormat: " << data3 << std::endl;
//std::cout << std::endl;
RegCloseKey(hkey);
}
else
{
std::cout << "error opening key: " << keyname << std::endl;
std::cout << std::endl;
}
return macrosRegistered;
}
//----------------------------------------------------------------------------
void WriteVSMacrosFileRegistryEntry(
const std::string& nextAvailableSubKeyName,
const std::string& macrosFile)
{
std::string keyname = "Software\\Microsoft\\VisualStudio\\8.0\\vsmacros\\OtherProjects7";
HKEY hkey = NULL;
LONG result = RegOpenKeyEx(HKEY_CURRENT_USER, keyname.c_str(), 0,
KEY_READ|KEY_WRITE, &hkey);
if (ERROR_SUCCESS == result)
{
// Create the subkey and set the values of interest:
HKEY hsubkey = NULL;
result = RegCreateKeyEx(hkey, nextAvailableSubKeyName.c_str(), 0, "", 0,
KEY_READ|KEY_WRITE, 0, &hsubkey, 0);
if (ERROR_SUCCESS == result)
{
DWORD dw = 0;
std::string s(macrosFile);
cmSystemTools::ReplaceString(s, "/", "\\");
result = RegSetValueEx(hsubkey, "Path", 0, REG_SZ, (LPBYTE) s.c_str(),
strlen(s.c_str()) + 1);
if (ERROR_SUCCESS != result)
{
std::cout << "error result 1: " << result << std::endl;
std::cout << std::endl;
}
// Security value is always "1" for sample macros files (seems to be "2"
// if you put the file somewhere outside the standard VSMacros folder)
dw = 1;
result = RegSetValueEx(hsubkey, "Security", 0, REG_DWORD, (LPBYTE) &dw, sizeof(DWORD));
if (ERROR_SUCCESS != result)
{
std::cout << "error result 2: " << result << std::endl;
std::cout << std::endl;
}
// StorageFormat value is always "0" for sample macros files
dw = 0;
result = RegSetValueEx(hsubkey, "StorageFormat", 0, REG_DWORD, (LPBYTE) &dw, sizeof(DWORD));
if (ERROR_SUCCESS != result)
{
std::cout << "error result 3: " << result << std::endl;
std::cout << std::endl;
}
RegCloseKey(hsubkey);
}
else
{
std::cout << "error creating subkey: " << nextAvailableSubKeyName << std::endl;
std::cout << std::endl;
}
RegCloseKey(hkey);
}
else
{
std::cout << "error opening key: " << keyname << std::endl;
std::cout << std::endl;
}
}
//----------------------------------------------------------------------------
void RegisterVisualStudioMacros(const std::string& macrosFile)
{
bool macrosRegistered;
std::string nextAvailableSubKeyName;
macrosRegistered = IsVisualStudioMacrosFileRegistered(macrosFile,
nextAvailableSubKeyName);
if (!macrosRegistered)
{
int count = cmCallVisualStudioMacro::
GetNumberOfRunningVisualStudioInstances("ALL");
// Only register the macros file if there are *no* instances of Visual
// Studio running. If we register it while one is running, first, it has
// no effect on the running instance; second, and worse, Visual Studio
// removes our newly added registration entry when it quits. Instead,
// emit a warning instructing the user to re-run the CMake configure step
// after exiting all running Visual Studio instances...
//
if (0 == count)
{
WriteVSMacrosFileRegistryEntry(nextAvailableSubKeyName, macrosFile);
}
else
{
std::ostringstream oss;
oss << "Could not register Visual Studio macros file '" << macrosFile
<< "' with instances of Visual Studio running. Please exit all"
<< " running instances of Visual Studio and rerun this CMake"
<< " configure to register CMake's Visual Studio macros file."
<< std::endl;
cmSystemTools::Message(oss.str().c_str(), "Warning");
}
}
}

View File

@ -36,10 +36,30 @@ public:
*/ */
virtual void Generate(); virtual void Generate();
/**
* Configure CMake's Visual Studio macros file into the user's Visual
* Studio macros directory.
*/
virtual void ConfigureCMakeVisualStudioMacros();
/**
* Where does this version of Visual Studio look for macros for the
* current user? Returns the empty string if this version of Visual
* Studio does not implement support for VB macros.
*/
virtual std::string GetUserMacrosDirectory();
/**
* Call the ReloadProjects macro if necessary based on
* GetFilesReplacedDuringGenerate results.
*/
virtual void CallVisualStudioReloadMacro();
protected: protected:
virtual void CreateGUID(const char*) {} virtual void CreateGUID(const char*) {}
virtual void FixUtilityDepends(); virtual void FixUtilityDepends();
const char* GetUtilityForTarget(cmTarget& target, const char*); const char* GetUtilityForTarget(cmTarget& target, const char*);
private: private:
void FixUtilityDependsForTarget(cmTarget& target); void FixUtilityDependsForTarget(cmTarget& target);
void CreateUtilityDependTarget(cmTarget& target); void CreateUtilityDependTarget(cmTarget& target);

View File

@ -199,6 +199,10 @@ void cmLocalVisualStudio7Generator
cmGeneratedFileStream fout(fname.c_str()); cmGeneratedFileStream fout(fname.c_str());
fout.SetCopyIfDifferent(true); fout.SetCopyIfDifferent(true);
this->WriteVCProjFile(fout,lname,target); this->WriteVCProjFile(fout,lname,target);
if (fout.Close())
{
this->GlobalGenerator->FileReplacedDuringGenerate(fname);
}
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

@ -76,6 +76,10 @@
#endif #endif
#include "cmGlobalUnixMakefileGenerator3.h" #include "cmGlobalUnixMakefileGenerator3.h"
#if defined(_WIN32)
#include "cmCallVisualStudioMacro.h"
#endif
#if !defined(__CYGWIN__) && !defined(CMAKE_BOOT_MINGW) #if !defined(__CYGWIN__) && !defined(CMAKE_BOOT_MINGW)
# include "cmExtraCodeBlocksGenerator.h" # include "cmExtraCodeBlocksGenerator.h"
#endif #endif
@ -1322,6 +1326,31 @@ int cmake::ExecuteCMakeCommand(std::vector<std::string>& args)
return result; return result;
} }
#if defined(_WIN32)
// Internal CMake support for calling Visual Studio macros.
else if (args[1] == "cmake_call_visual_studio_macro" && args.size() >= 4)
{
// args[2] = full path to .sln file or "ALL"
// args[3] = name of Visual Studio macro to call
// args[4..args.size()-1] = [optional] args for Visual Studio macro
std::string macroArgs;
if (args.size() > 4)
{
macroArgs = args[4];
for (size_t i = 5; i < args.size(); ++i)
{
macroArgs += " ";
macroArgs += args[i];
}
}
return cmCallVisualStudioMacro::CallMacro(args[2], args[3], macroArgs);
}
#endif
// Internal CMake dependency scanning support. // Internal CMake dependency scanning support.
else if (args[1] == "cmake_depends" && args.size() >= 6) else if (args[1] == "cmake_depends" && args.size() >= 6)
{ {

View File

@ -492,11 +492,11 @@ void SystemTools::ReplaceString(kwsys_stl::string& source,
#if defined(_WIN32) && !defined(__CYGWIN__) #if defined(_WIN32) && !defined(__CYGWIN__)
bool SystemTools::ReadRegistryValue(const char *key, kwsys_stl::string &value) bool SystemTools::ReadRegistryValue(const char *key, kwsys_stl::string &value)
{ {
bool valueset = false;
kwsys_stl::string primary = key; kwsys_stl::string primary = key;
kwsys_stl::string second; kwsys_stl::string second;
kwsys_stl::string valuename; kwsys_stl::string valuename;
size_t start = primary.find("\\"); size_t start = primary.find("\\");
if (start == kwsys_stl::string::npos) if (start == kwsys_stl::string::npos)
{ {
@ -558,12 +558,24 @@ bool SystemTools::ReadRegistryValue(const char *key, kwsys_stl::string &value)
if (dwType == REG_SZ) if (dwType == REG_SZ)
{ {
value = data; value = data;
RegCloseKey(hKey); valueset = true;
return true; }
else if (dwType == REG_EXPAND_SZ)
{
char expanded[1024];
DWORD dwExpandedSize = sizeof(expanded)/sizeof(expanded[0]);
if(ExpandEnvironmentStrings(data, expanded, dwExpandedSize))
{
value = expanded;
valueset = true;
}
} }
} }
RegCloseKey(hKey);
} }
return false;
return valueset;
} }
#else #else
bool SystemTools::ReadRegistryValue(const char *, kwsys_stl::string &) bool SystemTools::ReadRegistryValue(const char *, kwsys_stl::string &)

Binary file not shown.