Browse Source

Merge topic 'genex-generate-file'

b983a58 file: Add GENERATE command to produce files at generate time
Brad King 12 years ago
parent
commit
be85fa4a89
27 changed files with 430 additions and 0 deletions
  1. 1 0
      Source/cmBootstrapCommands.cxx
  2. 78 0
      Source/cmFileCommand.cxx
  3. 19 0
      Source/cmFileCommand.h
  4. 151 0
      Source/cmGeneratorExpressionEvaluationFile.cxx
  5. 48 0
      Source/cmGeneratorExpressionEvaluationFile.h
  6. 51 0
      Source/cmGlobalGenerator.cxx
  7. 11 0
      Source/cmGlobalGenerator.h
  8. 2 0
      Tests/RunCMake/CMakeLists.txt
  9. 1 0
      Tests/RunCMake/File_Generate/BadCondition-result.txt
  10. 3 0
      Tests/RunCMake/File_Generate/BadCondition-stderr.txt
  11. 5 0
      Tests/RunCMake/File_Generate/BadCondition.cmake
  12. 3 0
      Tests/RunCMake/File_Generate/CMakeLists.txt
  13. 1 0
      Tests/RunCMake/File_Generate/CommandConflict-result.txt
  14. 1 0
      Tests/RunCMake/File_Generate/CommandConflict-stderr.txt
  15. 9 0
      Tests/RunCMake/File_Generate/CommandConflict.cmake
  16. 5 0
      Tests/RunCMake/File_Generate/DebugEvaluate.cmake
  17. 1 0
      Tests/RunCMake/File_Generate/EmptyCondition1-result.txt
  18. 4 0
      Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt
  19. 5 0
      Tests/RunCMake/File_Generate/EmptyCondition1.cmake
  20. 1 0
      Tests/RunCMake/File_Generate/EmptyCondition2-result.txt
  21. 4 0
      Tests/RunCMake/File_Generate/EmptyCondition2-stderr.txt
  22. 5 0
      Tests/RunCMake/File_Generate/EmptyCondition2.cmake
  23. 1 0
      Tests/RunCMake/File_Generate/OutputConflict-result.txt
  24. 5 0
      Tests/RunCMake/File_Generate/OutputConflict-stderr.txt
  25. 4 0
      Tests/RunCMake/File_Generate/OutputConflict.cmake
  26. 10 0
      Tests/RunCMake/File_Generate/RunCMakeTest.cmake
  27. 1 0
      Tests/RunCMake/File_Generate/input.txt

+ 1 - 0
Source/cmBootstrapCommands.cxx

@@ -52,6 +52,7 @@
 #include "cmFindProgramCommand.cxx"
 #include "cmForEachCommand.cxx"
 #include "cmFunctionCommand.cxx"
+#include "cmGeneratorExpressionEvaluationFile.cxx"
 #include "cmGetCMakePropertyCommand.cxx"
 #include "cmGetDirectoryPropertyCommand.cxx"
 #include "cmGetFilenameComponentCommand.cxx"

+ 78 - 0
Source/cmFileCommand.cxx

@@ -167,6 +167,10 @@ bool cmFileCommand
     {
     return this->HandleTimestampCommand(args);
     }
+  else if ( subCommand == "GENERATE" )
+    {
+    return this->HandleGenerateCommand(args);
+    }
 
   std::string e = "does not recognize sub-command "+subCommand;
   this->SetError(e.c_str());
@@ -3249,6 +3253,80 @@ cmFileCommand::HandleUploadCommand(std::vector<std::string> const& args)
 #endif
 }
 
+//----------------------------------------------------------------------------
+void cmFileCommand::AddEvaluationFile(const std::string &inputName,
+                                      const std::string &outputExpr,
+                                      const std::string &condition,
+                                      bool inputIsContent
+                                     )
+{
+  cmListFileBacktrace lfbt;
+  this->Makefile->GetBacktrace(lfbt);
+
+  cmGeneratorExpression outputGe(lfbt);
+  cmsys::auto_ptr<cmCompiledGeneratorExpression> outputCge
+                                                = outputGe.Parse(outputExpr);
+
+  cmGeneratorExpression conditionGe(lfbt);
+  cmsys::auto_ptr<cmCompiledGeneratorExpression> conditionCge
+                                              = conditionGe.Parse(condition);
+
+  this->Makefile->GetLocalGenerator()
+                ->GetGlobalGenerator()->AddEvaluationFile(inputName,
+                                                          outputCge,
+                                                          this->Makefile,
+                                                          conditionCge,
+                                                          inputIsContent);
+}
+
+//----------------------------------------------------------------------------
+bool cmFileCommand::HandleGenerateCommand(
+  std::vector<std::string> const& args)
+{
+  if (args.size() < 5)
+    {
+    this->SetError("Incorrect arguments to GENERATE subcommand.");
+    return false;
+    }
+  if (args[1] != "OUTPUT")
+    {
+    this->SetError("Incorrect arguments to GENERATE subcommand.");
+    return false;
+    }
+  std::string condition;
+  if (args.size() > 5)
+    {
+    if (args[5] != "CONDITION")
+      {
+      this->SetError("Incorrect arguments to GENERATE subcommand.");
+      return false;
+      }
+    if (args.size() != 7)
+      {
+      this->SetError("Incorrect arguments to GENERATE subcommand.");
+      return false;
+      }
+    condition = args[6];
+    if (condition.empty())
+      {
+      this->SetError("CONDITION of sub-command GENERATE must not be empty if "
+        "specified.");
+      return false;
+      }
+    }
+  std::string output = args[2];
+  const bool inputIsContent = args[3] != "INPUT";
+  if (inputIsContent && args[3] != "CONTENT")
+    {
+    this->SetError("Incorrect arguments to GENERATE subcommand.");
+    return false;
+    }
+  std::string input = args[4];
+
+  this->AddEvaluationFile(input, output, condition, inputIsContent);
+  return true;
+}
+
 //----------------------------------------------------------------------------
 bool cmFileCommand::HandleTimestampCommand(
   std::vector<std::string> const& args)

+ 19 - 0
Source/cmFileCommand.h

@@ -88,6 +88,9 @@ public:
       "  file(UPLOAD filename url [INACTIVITY_TIMEOUT timeout]\n"
       "       [TIMEOUT timeout] [STATUS status] [LOG log] [SHOW_PROGRESS])\n"
       "  file(TIMESTAMP filename variable [<format string>] [UTC])\n"
+      "  file(GENERATE OUTPUT output_file\n"
+      "       <INPUT input_file|CONTENT input_content>\n"
+      "       CONDITION expression)\n"
       "WRITE will write a message into a file called 'filename'. It "
       "overwrites the file if it already exists, and creates the file "
       "if it does not exist. (If the file is a build input, use "
@@ -231,6 +234,15 @@ public:
       "it prints status messages, and NO_SOURCE_PERMISSIONS is default.  "
       "Installation scripts generated by the install() command use this "
       "signature (with some undocumented options for internal use)."
+      "\n"
+      "GENERATE will write an <output_file> with content from an "
+      "<input_file>, or from <input_content>.  The output is generated "
+      "conditionally based on the content of the <condition>.  The file is "
+      "written at CMake generate-time and the input may contain generator "
+      "expressions.  The <condition>, <output_file> and <input_file> may "
+      "also contain generator expressions.  The <condition> must evaluate to "
+      "either '0' or '1'.  The <output_file> must evaluate to a unique name "
+      "among all configurations and among all invocations of file(GENERATE)."
       // Undocumented INSTALL options:
       //  - RENAME <name>
       //  - OPTIONAL
@@ -269,6 +281,13 @@ protected:
   bool HandleUploadCommand(std::vector<std::string> const& args);
 
   bool HandleTimestampCommand(std::vector<std::string> const& args);
+  bool HandleGenerateCommand(std::vector<std::string> const& args);
+
+private:
+  void AddEvaluationFile(const std::string &inputName,
+                         const std::string &outputExpr,
+                         const std::string &condition,
+                         bool inputIsContent);
 };
 
 

+ 151 - 0
Source/cmGeneratorExpressionEvaluationFile.cxx

@@ -0,0 +1,151 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2013 Stephen Kelly <[email protected]>
+
+  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 "cmGeneratorExpressionEvaluationFile.h"
+
+#include "cmMakefile.h"
+
+#include <assert.h>
+
+//----------------------------------------------------------------------------
+cmGeneratorExpressionEvaluationFile::cmGeneratorExpressionEvaluationFile(
+        const std::string &input,
+        cmsys::auto_ptr<cmCompiledGeneratorExpression> outputFileExpr,
+        cmMakefile *makefile,
+        cmsys::auto_ptr<cmCompiledGeneratorExpression> condition,
+        bool inputIsContent)
+  : Input(input),
+    OutputFileExpr(outputFileExpr),
+    Makefile(makefile),
+    Condition(condition),
+    InputIsContent(inputIsContent)
+{
+}
+
+//----------------------------------------------------------------------------
+void cmGeneratorExpressionEvaluationFile::Generate(const char *config,
+              cmCompiledGeneratorExpression* inputExpression,
+              std::map<std::string, std::string> &outputFiles)
+{
+  std::string rawCondition = this->Condition->GetInput();
+  if (!rawCondition.empty())
+    {
+    std::string condResult = this->Condition->Evaluate(this->Makefile, config);
+    if (condResult == "0")
+      {
+      return;
+      }
+    if (condResult != "1")
+      {
+      cmOStringStream e;
+      e << "Evaluation file condition \"" << rawCondition << "\" did "
+          "not evaluate to valid content. Got \"" << condResult << "\".";
+      this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str().c_str());
+      return;
+      }
+    }
+
+  const std::string outputFileName
+                    = this->OutputFileExpr->Evaluate(this->Makefile, config);
+  const std::string outputContent
+                          = inputExpression->Evaluate(this->Makefile, config);
+
+  std::map<std::string, std::string>::iterator it
+                                          = outputFiles.find(outputFileName);
+
+  if(it != outputFiles.end())
+    {
+    if (it->second == outputContent)
+      {
+      return;
+      }
+    cmOStringStream e;
+    e << "Evaluation file to be written multiple times for different "
+         "configurations with different content:\n  " << outputFileName;
+    this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str().c_str());
+    return;
+    }
+
+  this->Files.push_back(outputFileName);
+  outputFiles[outputFileName] = outputContent;
+
+  std::ofstream fout(outputFileName.c_str());
+
+  if(!fout)
+    {
+    cmOStringStream e;
+    e << "Evaluation file \"" << outputFileName << "\" cannot be written.";
+    this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str().c_str());
+    return;
+    }
+
+  fout << outputContent;
+
+  fout.close();
+}
+
+//----------------------------------------------------------------------------
+void cmGeneratorExpressionEvaluationFile::Generate()
+{
+  std::string inputContent;
+  if (this->InputIsContent)
+    {
+    inputContent = this->Input;
+    }
+  else
+    {
+    std::ifstream fin(this->Input.c_str());
+    if(!fin)
+      {
+      cmOStringStream e;
+      e << "Evaluation file \"" << this->Input << "\" cannot be read.";
+      this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str().c_str());
+      return;
+      }
+
+    std::string line;
+    std::string sep;
+    while(cmSystemTools::GetLineFromStream(fin, line))
+      {
+      inputContent += sep + line;
+      sep = "\n";
+      }
+    inputContent += sep;
+    }
+
+  cmListFileBacktrace lfbt = this->OutputFileExpr->GetBacktrace();
+  cmGeneratorExpression contentGE(lfbt);
+  cmsys::auto_ptr<cmCompiledGeneratorExpression> inputExpression
+                                              = contentGE.Parse(inputContent);
+
+  std::map<std::string, std::string> outputFiles;
+
+  std::vector<std::string> allConfigs;
+  this->Makefile->GetConfigurations(allConfigs);
+
+  if (allConfigs.empty())
+    {
+    this->Generate(0, inputExpression.get(), outputFiles);
+    }
+  else
+    {
+    for(std::vector<std::string>::const_iterator li = allConfigs.begin();
+        li != allConfigs.end(); ++li)
+      {
+      this->Generate(li->c_str(), inputExpression.get(), outputFiles);
+      if(cmSystemTools::GetFatalErrorOccured())
+        {
+        return;
+        }
+      }
+    }
+}

+ 48 - 0
Source/cmGeneratorExpressionEvaluationFile.h

@@ -0,0 +1,48 @@
+/*============================================================================
+  CMake - Cross Platform Makefile Generator
+  Copyright 2013 Stephen Kelly <[email protected]>
+
+  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.
+============================================================================*/
+#ifndef cmGeneratorExpressionEvaluationFile_h
+#define cmGeneratorExpressionEvaluationFile_h
+
+#include "cmStandardIncludes.h"
+#include <cmsys/auto_ptr.hxx>
+
+#include "cmGeneratorExpression.h"
+
+//----------------------------------------------------------------------------
+class cmGeneratorExpressionEvaluationFile
+{
+public:
+  cmGeneratorExpressionEvaluationFile(const std::string &input,
+        cmsys::auto_ptr<cmCompiledGeneratorExpression> outputFileExpr,
+        cmMakefile *makefile,
+        cmsys::auto_ptr<cmCompiledGeneratorExpression> condition,
+        bool inputIsContent);
+
+  void Generate();
+
+  std::vector<std::string> GetFiles() const { return this->Files; }
+
+private:
+  void Generate(const char *config,
+              cmCompiledGeneratorExpression* inputExpression,
+              std::map<std::string, std::string> &outputFiles);
+
+private:
+  const std::string Input;
+  const cmsys::auto_ptr<cmCompiledGeneratorExpression> OutputFileExpr;
+  cmMakefile *Makefile;
+  const cmsys::auto_ptr<cmCompiledGeneratorExpression> Condition;
+  std::vector<std::string> Files;
+  const bool InputIsContent;
+};
+
+#endif

+ 51 - 0
Source/cmGlobalGenerator.cxx

@@ -26,6 +26,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorTarget.h"
 #include "cmGeneratorExpression.h"
+#include "cmGeneratorExpressionEvaluationFile.h"
 
 #include <cmsys/Directory.hxx>
 
@@ -69,6 +70,13 @@ cmGlobalGenerator::~cmGlobalGenerator()
     {
     delete this->LocalGenerators[i];
     }
+  for(std::vector<cmGeneratorExpressionEvaluationFile*>::const_iterator
+      li = this->EvaluationFiles.begin();
+      li != this->EvaluationFiles.end();
+      ++li)
+    {
+    delete *li;
+    }
   this->LocalGenerators.clear();
 
   if (this->ExtraGenerator)
@@ -981,6 +989,8 @@ void cmGlobalGenerator::Generate()
   // Create per-target generator information.
   this->CreateGeneratorTargets();
 
+  this->ProcessEvaluationFiles();
+
   // Compute the inter-target dependencies.
   if(!this->ComputeTargetDepends())
     {
@@ -2560,3 +2570,44 @@ std::string cmGlobalGenerator::EscapeJSON(const std::string& s) {
   }
   return result;
 }
+
+//----------------------------------------------------------------------------
+void cmGlobalGenerator::AddEvaluationFile(const std::string &inputFile,
+                    cmsys::auto_ptr<cmCompiledGeneratorExpression> outputExpr,
+                    cmMakefile *makefile,
+                    cmsys::auto_ptr<cmCompiledGeneratorExpression> condition,
+                    bool inputIsContent)
+{
+  this->EvaluationFiles.push_back(
+              new cmGeneratorExpressionEvaluationFile(inputFile, outputExpr,
+                                                      makefile, condition,
+                                                      inputIsContent));
+}
+
+//----------------------------------------------------------------------------
+void cmGlobalGenerator::ProcessEvaluationFiles()
+{
+  std::set<std::string> generatedFiles;
+  for(std::vector<cmGeneratorExpressionEvaluationFile*>::const_iterator
+      li = this->EvaluationFiles.begin();
+      li != this->EvaluationFiles.end();
+      ++li)
+    {
+    (*li)->Generate();
+    if (cmSystemTools::GetFatalErrorOccured())
+      {
+      return;
+      }
+    std::vector<std::string> files = (*li)->GetFiles();
+    for(std::vector<std::string>::const_iterator fi = files.begin();
+        fi != files.end(); ++fi)
+      {
+      if (!generatedFiles.insert(*fi).second)
+        {
+        cmSystemTools::Error("File to be generated by multiple different "
+          "commands: ", fi->c_str());
+        return;
+        }
+      }
+    }
+}

+ 11 - 0
Source/cmGlobalGenerator.h

@@ -20,9 +20,11 @@
 #include "cmSystemTools.h" // for cmSystemTools::OutputOption
 #include "cmExportSetMap.h" // For cmExportSetMap
 #include "cmGeneratorTarget.h"
+#include "cmGeneratorExpression.h"
 
 class cmake;
 class cmGeneratorTarget;
+class cmGeneratorExpressionEvaluationFile;
 class cmMakefile;
 class cmLocalGenerator;
 class cmExternalMakefileProjectGenerator;
@@ -279,6 +281,14 @@ public:
 
   static std::string EscapeJSON(const std::string& s);
 
+  void AddEvaluationFile(const std::string &inputFile,
+                  cmsys::auto_ptr<cmCompiledGeneratorExpression> outputName,
+                  cmMakefile *makefile,
+                  cmsys::auto_ptr<cmCompiledGeneratorExpression> condition,
+                  bool inputIsContent);
+
+  void ProcessEvaluationFiles();
+
 protected:
   typedef std::vector<cmLocalGenerator*> GeneratorVector;
   // for a project collect all its targets by following depend
@@ -338,6 +348,7 @@ protected:
   // All targets in the entire project.
   std::map<cmStdString,cmTarget *> TotalTargets;
   std::map<cmStdString,cmTarget *> ImportedTargets;
+  std::vector<cmGeneratorExpressionEvaluationFile*> EvaluationFiles;
 
   virtual const char* GetPredefinedTargetsFolder();
   virtual bool UseFolderProperty();

+ 2 - 0
Tests/RunCMake/CMakeLists.txt

@@ -87,3 +87,5 @@ if("${CMAKE_TEST_GENERATOR}" MATCHES "Visual Studio [^6]")
   add_RunCMake_test(include_external_msproject)
   add_RunCMake_test(SolutionGlobalSections)
 endif()
+
+add_RunCMake_test(File_Generate)

+ 1 - 0
Tests/RunCMake/File_Generate/BadCondition-result.txt

@@ -0,0 +1 @@
+1

+ 3 - 0
Tests/RunCMake/File_Generate/BadCondition-stderr.txt

@@ -0,0 +1,3 @@
+CMake Error in CMakeLists.txt:
+  Evaluation file condition \"\$<1:Bad>\" did not evaluate to valid content.
+  Got \"Bad\".

+ 5 - 0
Tests/RunCMake/File_Generate/BadCondition.cmake

@@ -0,0 +1,5 @@
+
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output.txt"
+              INPUT "${CMAKE_CURRENT_SOURCE_DIR}/input.txt"
+              CONDITION $<1:Bad>
+)

+ 3 - 0
Tests/RunCMake/File_Generate/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 2.8)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 1 - 0
Tests/RunCMake/File_Generate/CommandConflict-result.txt

@@ -0,0 +1 @@
+1

+ 1 - 0
Tests/RunCMake/File_Generate/CommandConflict-stderr.txt

@@ -0,0 +1 @@
+CMake Error: File to be generated by multiple different commands: .*CommandConflict-build/output_.*.txt

+ 9 - 0
Tests/RunCMake/File_Generate/CommandConflict.cmake

@@ -0,0 +1,9 @@
+
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output_$<CONFIGURATION>.txt"
+              INPUT "${CMAKE_CURRENT_SOURCE_DIR}/input.txt"
+              CONDITION $<CONFIG:$<CONFIGURATION>>
+)
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output_$<CONFIGURATION>.txt"
+              INPUT "${CMAKE_CURRENT_SOURCE_DIR}/input.txt"
+              CONDITION $<CONFIG:$<CONFIGURATION>>
+)

+ 5 - 0
Tests/RunCMake/File_Generate/DebugEvaluate.cmake

@@ -0,0 +1,5 @@
+
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output.txt"
+              INPUT "${CMAKE_CURRENT_SOURCE_DIR}/input.txt"
+              CONDITION $<CONFIG:Debug>
+)

+ 1 - 0
Tests/RunCMake/File_Generate/EmptyCondition1-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/File_Generate/EmptyCondition1-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at EmptyCondition1.cmake:2 \(file\):
+  file Incorrect arguments to GENERATE subcommand.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 5 - 0
Tests/RunCMake/File_Generate/EmptyCondition1.cmake

@@ -0,0 +1,5 @@
+
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output.txt"
+              INPUT "${CMAKE_CURRENT_SOURCE_DIR}/input.txt"
+              CONDITION
+)

+ 1 - 0
Tests/RunCMake/File_Generate/EmptyCondition2-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/File_Generate/EmptyCondition2-stderr.txt

@@ -0,0 +1,4 @@
+CMake Error at EmptyCondition2.cmake:2 \(file\):
+  file CONDITION of sub-command GENERATE must not be empty if specified.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)

+ 5 - 0
Tests/RunCMake/File_Generate/EmptyCondition2.cmake

@@ -0,0 +1,5 @@
+
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output.txt"
+              INPUT "${CMAKE_CURRENT_SOURCE_DIR}/input.txt"
+              CONDITION ""
+)

+ 1 - 0
Tests/RunCMake/File_Generate/OutputConflict-result.txt

@@ -0,0 +1 @@
+1

+ 5 - 0
Tests/RunCMake/File_Generate/OutputConflict-stderr.txt

@@ -0,0 +1,5 @@
+CMake Error in CMakeLists.txt:
+  Evaluation file to be written multiple times for different configurations
+  with different content:
+
+  .*output.txt

+ 4 - 0
Tests/RunCMake/File_Generate/OutputConflict.cmake

@@ -0,0 +1,4 @@
+
+file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/output.txt"
+              INPUT "${CMAKE_CURRENT_SOURCE_DIR}/input.txt"
+)

+ 10 - 0
Tests/RunCMake/File_Generate/RunCMakeTest.cmake

@@ -0,0 +1,10 @@
+include(RunCMake)
+
+run_cmake(CommandConflict)
+if("${RunCMake_GENERATOR}" MATCHES "Visual Studio" OR "${RunCMake_GENERATOR}" MATCHES "XCode" )
+    run_cmake(OutputConflict)
+endif()
+run_cmake(EmptyCondition1)
+run_cmake(EmptyCondition2)
+run_cmake(BadCondition)
+run_cmake(DebugEvaluate)

+ 1 - 0
Tests/RunCMake/File_Generate/input.txt

@@ -0,0 +1 @@
+Some $<$<CONFIG:Debug>:conflicting> $<$<NOT:$<CONFIG:Debug>>:content>