[cmake-developers] [CMake 0012245]: Add CLR support for MSVC C++ Projects

Mantis Bug Tracker mantis at public.kitware.com
Wed Jun 1 05:43:00 EDT 2011


The following issue has been SUBMITTED. 
====================================================================== 
http://www.cmake.org/Bug/view.php?id=12245 
====================================================================== 
Reported By:                alexander_schmid
Assigned To:                
====================================================================== 
Project:                    CMake
Issue ID:                   12245
Category:                   CMake
Reproducibility:            N/A
Severity:                   feature
Priority:                   normal
Status:                     backlog
====================================================================== 
Date Submitted:             2011-06-01 05:43 EDT
Last Modified:              2011-06-01 05:43 EDT
====================================================================== 
Summary:                    Add CLR support for MSVC C++ Projects
Description: 
Hi,
I would like to use CMake to create MSVC .vcproj files that have the "Common
Language Runtime support" enabled.
After doing some research and hacking on my own, I would like to present the
results to you; maybe you´d like to polish and integrate these changes into a
future release of CMake.

Basically, I did these things:
- Add a CMake option called "ENABLE_CLR"
- Add the "ManagedExtensions" item below the "CharacterSet" item in the .vcproj;
  Set the value of "ManagedExtensions" to "1" if CLR support is desired, "0"
else
- Remove the /EHsc compiler flag (does not work with CLR)
- Remove the /RTC1 compiler flag (does not work with CLR)

The code snippet sent as additional information shows my modifications in
cmLocalVisualStudio7Generator.cxx.

If you have any questions, feel free to ask.


Additional Information: 
void cmLocalVisualStudio7Generator::WriteConfiguration(std::ostream& fout,
                                                       const char* configName,
                                                       const char *libName,
                                                       cmTarget &target)
{
  const char* mfcFlag = this->Makefile->GetDefinition("CMAKE_MFC_FLAG");
  if(!mfcFlag)
    {
    mfcFlag = "0";
    }
  fout << "\t\t<Configuration\n"
       << "\t\t\tName=\"" << configName << "|" << this->PlatformName << "\"\n"
       << "\t\t\tOutputDirectory=\"" << configName << "\"\n";
  // This is an internal type to Visual Studio, it seems that:
  // 4 == static library
  // 2 == dll
  // 1 == executable
  // 10 == utility
  const char* configType = "10";
  const char* projectType = 0;
  bool targetBuilds = true;
  switch(target.GetType())
    {
    case cmTarget::STATIC_LIBRARY:
      projectType = "typeStaticLibrary";
      configType = "4";
      break;
    case cmTarget::SHARED_LIBRARY:
    case cmTarget::MODULE_LIBRARY:
      projectType = "typeDynamicLibrary";
      configType = "2";
      break;
    case cmTarget::EXECUTABLE:
      configType = "1";
      break;
    case cmTarget::UTILITY:
    case cmTarget::GLOBAL_TARGET:
      configType = "10";
    default:
      targetBuilds = false;
      break;
    }
  if(this->FortranProject && projectType)
    {
    configType = projectType;
    }
  std::string flags;
  if(strcmp(configType, "10") != 0)
    {
    const char* linkLanguage = (this->FortranProject? "Fortran":
                                target.GetLinkerLanguage(configName));
    if(!linkLanguage)
      {
      cmSystemTools::Error
        ("CMake can not determine linker language for target:",
         target.GetName());
      return;
      }
    if(strcmp(linkLanguage, "C") == 0 || strcmp(linkLanguage, "CXX") == 0
      || strcmp(linkLanguage, "Fortran") == 0)
      {
      std::string baseFlagVar = "CMAKE_";
      baseFlagVar += linkLanguage;
      baseFlagVar += "_FLAGS";
      flags = this->Makefile->GetRequiredDefinition(baseFlagVar.c_str());
      std::string flagVar = baseFlagVar + std::string("_") +
        cmSystemTools::UpperCase(configName);
      flags += " ";
      flags += this->Makefile->GetRequiredDefinition(flagVar.c_str());
      }
    // set the correct language
    if(strcmp(linkLanguage, "C") == 0)
      {
      flags += " /TC ";
      }
    if(strcmp(linkLanguage, "CXX") == 0)
      {
      flags += " /TP ";
      }
    }

  // Add the target-specific flags.
  if(const char* targetFlags = target.GetProperty("COMPILE_FLAGS"))
    {
    flags += " ";
    flags += targetFlags;
    }

  const char* sClr =
this->GetGlobalGenerator()->GetCMakeInstance()->GetCacheDefinition("ENABLE_CLR");
   
  bool bIsClr = ( sClr != NULL ) && ( strcmp( sClr, "ON" ) == 0 );
  if( bIsClr )
  {
    // if it is a CLR build, no /EHsc or RTC Flags shall be set!
    std::string sEHsc( "/EHsc" );
    size_t ehscPos = flags.find( sEHsc );
    while( ehscPos != std::string::npos )
    {
        flags.erase( ehscPos, sEHsc.size() );
        ehscPos = flags.find( sEHsc );
    }

    std::string sRTC1( "/RTC1" );
    size_t RTC1Pos = flags.find( sRTC1 );
    while( RTC1Pos != std::string::npos )
    {
        flags.erase( RTC1Pos, sRTC1.size() );
        RTC1Pos = flags.find( sRTC1 );
    }
  }

  std::string configUpper = cmSystemTools::UpperCase(configName);
  std::string defPropName = "COMPILE_DEFINITIONS_";
  defPropName += configUpper;

  // Get preprocessor definitions for this directory.
  std::string defineFlags = this->Makefile->GetDefineFlags();
  Options::Tool t = Options::Compiler;
  cmVS7FlagTable const* table = cmLocalVisualStudio7GeneratorFlagTable;
  if(this->FortranProject)
    {
    t = Options::FortranCompiler;
    table = cmLocalVisualStudio7GeneratorFortranFlagTable;
    }
  Options targetOptions(this, this->Version, t, 
                        table,
                        this->ExtraFlagTable);
  targetOptions.FixExceptionHandlingDefault();
  targetOptions.Parse(flags.c_str());
  targetOptions.Parse(defineFlags.c_str());
  targetOptions.AddDefines
    (this->Makefile->GetProperty("COMPILE_DEFINITIONS"));
  targetOptions.AddDefines(target.GetProperty("COMPILE_DEFINITIONS"));
  targetOptions.AddDefines(this->Makefile->GetProperty(defPropName.c_str()));
  targetOptions.AddDefines(target.GetProperty(defPropName.c_str()));
  targetOptions.SetVerboseMakefile(
    this->Makefile->IsOn("CMAKE_VERBOSE_MAKEFILE"));

  // Add a definition for the configuration name.
  std::string configDefine = "CMAKE_INTDIR=\"";
  configDefine += configName;
  configDefine += "\"";
  targetOptions.AddDefine(configDefine);

  // Add the export symbol definition for shared library objects.
  if(const char* exportMacro = target.GetExportMacro())
    {
    targetOptions.AddDefine(exportMacro);
    }

  // The intermediate directory name consists of a directory for the
  // target and a subdirectory for the configuration name.
  std::string intermediateDir = this->GetTargetDirectory(target);
  intermediateDir += "/";
  intermediateDir += configName;
  fout << "\t\t\tIntermediateDirectory=\""
       << this->ConvertToXMLOutputPath(intermediateDir.c_str())
       << "\"\n"
       << "\t\t\tConfigurationType=\"" << configType << "\"\n"
       << "\t\t\tUseOfMFC=\"" << mfcFlag << "\"\n"
       << "\t\t\tATLMinimizesCRunTimeLibraryUsage=\"FALSE\"\n";

  // If unicode is enabled change the character set to unicode, if not
  // then default to MBCS.
  if(targetOptions.UsingUnicode())
    {
    fout << "\t\t\tCharacterSet=\"1\"\n";
    }
  else
    {
    fout << "\t\t\tCharacterSet=\"2\"\n";
    }

  if( bIsClr )
    {
    fout << "\t\t\tManagedExtensions=\"1\">\n";
    }
  else
    {
    fout << "\t\t\tManagedExtensions=\"0\">\n";
    }    
  const char* tool = "VCCLCompilerTool";
  if(this->FortranProject)
    {
    tool = "VFFortranCompilerTool";
    }
  fout << "\t\t\t<Tool\n"
       << "\t\t\t\tName=\"" << tool << "\"\n";
  if(this->FortranProject)
    {
    const char* target_mod_dir =
      target.GetProperty("Fortran_MODULE_DIRECTORY"); 
    std::string modDir;
    if(target_mod_dir)
      {
      modDir = this->Convert(target_mod_dir,
                             cmLocalGenerator::START_OUTPUT,
                             cmLocalGenerator::UNCHANGED);
      }
    else
      {
      modDir = ".";
      }
    fout << "\t\t\t\tModulePath=\"" 
         << this->ConvertToXMLOutputPath(modDir.c_str())
         << "\\$(ConfigurationName)\"\n";
    }
  targetOptions.OutputAdditionalOptions(fout, "\t\t\t\t", "\n");
  fout << "\t\t\t\tAdditionalIncludeDirectories=\"";
  std::vector<std::string> includes;
  this->GetIncludeDirectories(includes);
  std::vector<std::string>::iterator i = includes.begin();
  for(;i != includes.end(); ++i)
    {
    // output the include path
    std::string ipath = this->ConvertToXMLOutputPath(i->c_str());
    fout << ipath << ";";
    // if this is fortran then output the include with 
    // a ConfigurationName on the end of it.
    if(this->FortranProject)
      {
      ipath = i->c_str();
      ipath += "/$(ConfigurationName)";
      ipath = this->ConvertToXMLOutputPath(ipath.c_str());
      fout << ipath << ";";
      }
    }
  fout << "\"\n";
  targetOptions.OutputFlagMap(fout, "\t\t\t\t");
  targetOptions.OutputPreprocessorDefinitions(fout, "\t\t\t\t", "\n");
  fout << "\t\t\t\tAssemblerListingLocation=\"" << configName << "\"\n";
  fout << "\t\t\t\tObjectFile=\"$(IntDir)\\\"\n";
  if(targetBuilds)
    {
    // We need to specify a program database file name even for
    // non-debug configurations because VS still creates .idb files.
    fout <<  "\t\t\t\tProgramDataBaseFileName=\""
         << this->ConvertToXMLOutputPathSingle(
              target.GetDirectory(configName).c_str())
         << "/"
         << target.GetPDBName(configName) << "\"\n";
    }
  fout << "/>\n";  // end of <Tool Name=VCCLCompilerTool
  tool = "VCCustomBuildTool";
  if(this->FortranProject)
    {
    tool = "VFCustomBuildTool";
    }
  fout << "\t\t\t<Tool\n\t\t\t\tName=\"" << tool << "\"/>\n";
  tool = "VCResourceCompilerTool";
  if(this->FortranProject)
    {
    tool = "VFResourceCompilerTool";
    }
  fout << "\t\t\t<Tool\n\t\t\t\tName=\"" << tool << "\"\n"
       << "\t\t\t\tAdditionalIncludeDirectories=\"";
  for(i = includes.begin();i != includes.end(); ++i)
    {
    std::string ipath = this->ConvertToXMLOutputPath(i->c_str());
    fout << ipath << ";";
    }
  // add the -D flags to the RC tool
  fout << "\"";
  targetOptions.OutputPreprocessorDefinitions(fout, "\n\t\t\t\t", "");
  fout << "/>\n";
  tool = "VCMIDLTool";
  if(this->FortranProject)
    {
    tool = "VFMIDLTool";
    }
  fout << "\t\t\t<Tool\n\t\t\t\tName=\"" << tool << "\"\n";
  targetOptions.OutputPreprocessorDefinitions(fout, "\t\t\t\t", "\n");
  fout << "\t\t\t\tMkTypLibCompatible=\"FALSE\"\n";
  if( this->PlatformName == "x64" )
    {
    fout << "\t\t\t\tTargetEnvironment=\"3\"\n";
    }
  else if( this->PlatformName == "ia64" )
    {
    fout << "\t\t\t\tTargetEnvironment=\"2\"\n";
    }
  else
    {
    fout << "\t\t\t\tTargetEnvironment=\"1\"\n";
    }
  fout << "\t\t\t\tGenerateStublessProxies=\"TRUE\"\n";
  fout << "\t\t\t\tTypeLibraryName=\"$(InputName).tlb\"\n";
  fout << "\t\t\t\tOutputDirectory=\"$(IntDir)\"\n";
  fout << "\t\t\t\tHeaderFileName=\"$(InputName).h\"\n";
  fout << "\t\t\t\tDLLDataFileName=\"\"\n";
  fout << "\t\t\t\tInterfaceIdentifierFileName=\"$(InputName)_i.c\"\n";
  fout << "\t\t\t\tProxyFileName=\"$(InputName)_p.c\"/>\n";
  // end of <Tool Name=VCMIDLTool

  // Check if we need the FAT32 workaround.
  if(targetBuilds && this->Version >= 8)
    {
    // Check the filesystem type where the target will be written.
    if(cmLVS6G_IsFAT(target.GetDirectory(configName).c_str()))
      {
      // Add a flag telling the manifest tool to use a workaround
      // for FAT32 file systems, which can cause an empty manifest
      // to be embedded into the resulting executable.  See CMake
      // bug http://www.cmake.org/Bug/view.php?id=2617. 
      const char* tool  = "VCManifestTool";
      if(this->FortranProject)
        {
        tool = "VFManifestTool";
        }
      fout << "\t\t\t<Tool\n\t\t\t\tName=\"" << tool << "\"\n"
           << "\t\t\t\tUseFAT32Workaround=\"true\"\n"
           << "\t\t\t/>\n";
      }
    }

  this->OutputTargetRules(fout, configName, target, libName);
  this->OutputBuildTool(fout, configName, target, targetOptions.IsDebug());
  fout << "\t\t</Configuration>\n";
}
====================================================================== 

Issue History 
Date Modified    Username       Field                    Change               
====================================================================== 
2011-06-01 05:43 alexander_schmidNew Issue                                    
======================================================================




More information about the cmake-developers mailing list