From 0a8f469af921d64431ed237022a02d39cac3ebb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20St=C3=BCrmer?= Date: Thu, 15 Jun 2017 14:30:29 +0200 Subject: Vs: refactor WriteCustomRule for preparation of CSharp support --- Source/cmVisualStudio10TargetGenerator.cxx | 48 +++++++++++++++++++----------- Source/cmVisualStudio10TargetGenerator.h | 4 +++ 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 6d796be..d163ddb 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -1218,43 +1218,55 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule( std::string comment = lg->ConstructComment(ccg); comment = cmVS10EscapeComment(comment); std::string script = cmVS10EscapeXML(lg->ConstructScript(ccg)); - this->WritePlatformConfigTag("Message", i->c_str(), 3); - (*this->BuildFileStream) << cmVS10EscapeXML(comment) << "\n"; - this->WritePlatformConfigTag("Command", i->c_str(), 3); - (*this->BuildFileStream) << script << "\n"; - this->WritePlatformConfigTag("AdditionalInputs", i->c_str(), 3); - - (*this->BuildFileStream) << cmVS10EscapeXML(source->GetFullPath()); + // input files for custom command + std::stringstream inputs; + inputs << cmVS10EscapeXML(source->GetFullPath()); for (std::vector::const_iterator d = ccg.GetDepends().begin(); d != ccg.GetDepends().end(); ++d) { std::string dep; if (this->LocalGenerator->GetRealDependency(d->c_str(), i->c_str(), dep)) { this->ConvertToWindowsSlash(dep); - (*this->BuildFileStream) << ";" << cmVS10EscapeXML(dep); + inputs << ";" << cmVS10EscapeXML(dep); } } - (*this->BuildFileStream) << ";%(AdditionalInputs)\n"; - this->WritePlatformConfigTag("Outputs", i->c_str(), 3); + // output files for custom command + std::stringstream outputs; const char* sep = ""; for (std::vector::const_iterator o = ccg.GetOutputs().begin(); o != ccg.GetOutputs().end(); ++o) { std::string out = *o; this->ConvertToWindowsSlash(out); - (*this->BuildFileStream) << sep << cmVS10EscapeXML(out); + outputs << sep << cmVS10EscapeXML(out); sep = ";"; } - (*this->BuildFileStream) << "\n"; - if (this->LocalGenerator->GetVersion() > - cmGlobalVisualStudioGenerator::VS10) { - // VS >= 11 let us turn off linking of custom command outputs. - this->WritePlatformConfigTag("LinkObjects", i->c_str(), 3); - (*this->BuildFileStream) << "false\n"; - } + this->WriteCustomRuleCpp(*i, script, inputs.str(), outputs.str(), comment); } this->WriteString("\n", 2); } +void cmVisualStudio10TargetGenerator::WriteCustomRuleCpp( + std::string const& config, std::string const& script, + std::string const& inputs, std::string const& outputs, + std::string const& comment) +{ + this->WritePlatformConfigTag("Message", config, 3); + (*this->BuildFileStream) << cmVS10EscapeXML(comment) << "\n"; + this->WritePlatformConfigTag("Command", config, 3); + (*this->BuildFileStream) << script << "\n"; + this->WritePlatformConfigTag("AdditionalInputs", config, 3); + (*this->BuildFileStream) << inputs; + (*this->BuildFileStream) << ";%(AdditionalInputs)\n"; + this->WritePlatformConfigTag("Outputs", config, 3); + (*this->BuildFileStream) << outputs << "\n"; + if (this->LocalGenerator->GetVersion() > + cmGlobalVisualStudioGenerator::VS10) { + // VS >= 11 let us turn off linking of custom command outputs. + this->WritePlatformConfigTag("LinkObjects", config, 3); + (*this->BuildFileStream) << "false\n"; + } +} + std::string cmVisualStudio10TargetGenerator::ConvertPath( std::string const& path, bool forceRelative) { diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 0852459..007c6fa 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -127,6 +127,10 @@ private: void OutputLinkIncremental(std::string const& configName); void WriteCustomRule(cmSourceFile const* source, cmCustomCommand const& command); + void WriteCustomRuleCpp(std::string const& config, std::string const& script, + std::string const& inputs, + std::string const& outputs, + std::string const& comment); void WriteCustomCommands(); void WriteCustomCommand(cmSourceFile const* sf); void WriteGroups(); -- cgit v0.12 From dcdab5cf23b3777761d6c6dcaf0dfcefdd821792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20St=C3=BCrmer?= Date: Sat, 17 Jun 2017 09:32:19 +0200 Subject: Vs: factor out computation of tag for CSharp source files --- Source/cmVisualStudio10TargetGenerator.cxx | 29 +++++++++++++++++++++-------- Source/cmVisualStudio10TargetGenerator.h | 1 + 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index d163ddb..9612c90 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -2056,14 +2056,10 @@ bool cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( typedef std::map CsPropMap; CsPropMap sourceFileTags; // set tag if necessary - if (!this->InSourceBuild) { - const std::string stripFromPath = - this->Makefile->GetCurrentSourceDirectory(); - if (f.find(stripFromPath) != std::string::npos) { - std::string link = f.substr(stripFromPath.length() + 1); - this->ConvertToWindowsSlash(link); - sourceFileTags["Link"] = link; - } + std::string link; + this->GetCSharpSourceLink(source, link); + if (!link.empty()) { + sourceFileTags["Link"] = link; } this->GetCSharpSourceProperties(&sf, sourceFileTags); // write source file specific tags @@ -4380,6 +4376,23 @@ void cmVisualStudio10TargetGenerator::WriteCSharpSourceProperties( } } +void cmVisualStudio10TargetGenerator::GetCSharpSourceLink( + cmSourceFile const* sf, std::string& link) +{ + std::string f = sf->GetFullPath(); + if (!this->InSourceBuild) { + const std::string stripFromPath = + this->Makefile->GetCurrentSourceDirectory(); + if (f.find(stripFromPath) != std::string::npos) { + link = f.substr(stripFromPath.length() + 1); + if (const char* l = sf->GetProperty("VS_CSHARP_Link")) { + link = l; + } + this->ConvertToWindowsSlash(link); + } + } +} + std::string cmVisualStudio10TargetGenerator::GetCMakeFilePath( const char* relativeFilePath) const { diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 007c6fa..2518d5e 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -162,6 +162,7 @@ private: std::map& tags); void WriteCSharpSourceProperties( const std::map& tags); + void GetCSharpSourceLink(cmSourceFile const* sf, std::string& link); private: typedef cmVisualStudioGeneratorOptions Options; -- cgit v0.12 From ec409a116fd58a541a7700df12dfdfc045f0df17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20St=C3=BCrmer?= Date: Thu, 22 Jun 2017 21:40:48 +0200 Subject: Vs: fix CSharp custom command by introducing inline MSBuild s The custom command implementation is based on the Microsoft support article: https://docs.microsoft.com/en-us/visualstudio/msbuild/how-to-extend-the-visual-studio-build-process Fixes: #16960 --- Source/cmVisualStudio10TargetGenerator.cxx | 86 ++++++++++++++++++++-- Source/cmVisualStudio10TargetGenerator.h | 7 ++ Tests/RunCMake/CMakeLists.txt | 4 + Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt | 3 + .../CommandWithOutput-check.cmake | 21 ++++++ .../CSharpCustomCommand/CommandWithOutput.cmake | 13 ++++ .../CSharpCustomCommand/RunCMakeTest.cmake | 34 +++++++++ Tests/RunCMake/CSharpCustomCommand/dummy.cs | 0 Tests/RunCMake/CSharpCustomCommand/test.cs.in | 8 ++ 9 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt create mode 100644 Tests/RunCMake/CSharpCustomCommand/CommandWithOutput-check.cmake create mode 100644 Tests/RunCMake/CSharpCustomCommand/CommandWithOutput.cmake create mode 100644 Tests/RunCMake/CSharpCustomCommand/RunCMakeTest.cmake create mode 100644 Tests/RunCMake/CSharpCustomCommand/dummy.cs create mode 100644 Tests/RunCMake/CSharpCustomCommand/test.cs.in diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 9612c90..dbd4571 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -27,6 +27,12 @@ static std::string cmVS10EscapeXML(std::string arg) return arg; } +static std::string cmVS10EscapeQuotes(std::string arg) +{ + cmSystemTools::ReplaceString(arg, "\"", """); + return arg; +} + static std::string cmVS10EscapeComment(std::string comment) { // MSBuild takes the CDATA of a element and just @@ -578,6 +584,18 @@ void cmVisualStudio10TargetGenerator::Generate() this->WriteEvents(*i); this->WriteString("\n", 1); } + // make sure custom commands are executed before build (if necessary) + this->WriteString("\n", 1); + this->WriteString("\n", 2); + for (std::set::const_iterator i = + this->CSharpCustomCommandNames.begin(); + i != this->CSharpCustomCommandNames.end(); ++i) { + this->WriteString(i->c_str(), 3); + (*this->BuildFileStream) << ";\n"; + } + this->WriteString("$(BuildDependsOn)\n", 3); + this->WriteString("\n", 2); + this->WriteString("\n", 1); } this->WriteString("", 0); // The groups are stored in a separate file for VS 10 @@ -1151,6 +1169,7 @@ void cmVisualStudio10TargetGenerator::WriteNsightTegraConfigurationValues( void cmVisualStudio10TargetGenerator::WriteCustomCommands() { this->SourcesVisited.clear(); + this->CSharpCustomCommandNames.clear(); std::vector customCommands; this->GeneratorTarget->GetCustomCommands(customCommands, ""); for (std::vector::const_iterator si = @@ -1172,9 +1191,14 @@ void cmVisualStudio10TargetGenerator::WriteCustomCommand( } } if (cmCustomCommand const* command = sf->GetCustomCommand()) { - this->WriteString("\n", 1); + // C# projects write their within WriteCustomRule() + if (this->ProjectType != csproj) { + this->WriteString("\n", 1); + } this->WriteCustomRule(sf, *command); - this->WriteString("\n", 1); + if (this->ProjectType != csproj) { + this->WriteString("\n", 1); + } } } } @@ -1209,8 +1233,20 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule( } cmLocalVisualStudio7Generator* lg = this->LocalGenerator; - this->WriteSource("CustomBuild", source, ">\n"); - + if (this->ProjectType != csproj) { + this->WriteSource("CustomBuild", source, ">\n"); + } else { + this->WriteString("\n", 1); + std::string link; + this->GetCSharpSourceLink(source, link); + this->WriteSource("None", source, ">\n"); + if (!link.empty()) { + this->WriteString("", 3); + (*this->BuildFileStream) << link << "\n"; + } + this->WriteString("\n", 2); + this->WriteString("\n", 1); + } for (std::vector::const_iterator i = this->Configurations.begin(); i != this->Configurations.end(); ++i) { @@ -1240,9 +1276,25 @@ void cmVisualStudio10TargetGenerator::WriteCustomRule( outputs << sep << cmVS10EscapeXML(out); sep = ";"; } - this->WriteCustomRuleCpp(*i, script, inputs.str(), outputs.str(), comment); + if (this->ProjectType == csproj) { + std::string name = "CustomCommand_" + *i + "_" + + cmSystemTools::ComputeStringMD5(sourcePath); + std::string inputs_s = inputs.str(); + std::string outputs_s = outputs.str(); + comment = cmVS10EscapeQuotes(comment); + script = cmVS10EscapeQuotes(script); + inputs_s = cmVS10EscapeQuotes(inputs_s); + outputs_s = cmVS10EscapeQuotes(outputs_s); + this->WriteCustomRuleCSharp(*i, name, script, inputs_s, outputs_s, + comment); + } else { + this->WriteCustomRuleCpp(*i, script, inputs.str(), outputs.str(), + comment); + } + } + if (this->ProjectType != csproj) { + this->WriteString("\n", 2); } - this->WriteString("\n", 2); } void cmVisualStudio10TargetGenerator::WriteCustomRuleCpp( @@ -1267,6 +1319,28 @@ void cmVisualStudio10TargetGenerator::WriteCustomRuleCpp( } } +void cmVisualStudio10TargetGenerator::WriteCustomRuleCSharp( + std::string const& config, std::string const& name, + std::string const& script, std::string const& inputs, + std::string const& outputs, std::string const& comment) +{ + this->CSharpCustomCommandNames.insert(name); + std::stringstream attributes; + attributes << "\n Name=\"" << name << "\""; + attributes << "\n Inputs=\"" << inputs << "\""; + attributes << "\n Outputs=\"" << outputs << "\""; + this->WritePlatformConfigTag("Target", config, 1, attributes.str().c_str(), + "\n"); + if (!comment.empty()) { + this->WriteString("BuildFileStream) << "echo " << cmVS10EscapeXML(comment) + << "\" />\n"; + } + this->WriteString("BuildFileStream) << script << "\" />\n"; + this->WriteString("\n", 1); +} + std::string cmVisualStudio10TargetGenerator::ConvertPath( std::string const& path, bool forceRelative) { diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 2518d5e..2d98994 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -131,6 +131,12 @@ private: std::string const& inputs, std::string const& outputs, std::string const& comment); + void WriteCustomRuleCSharp(std::string const& config, + std::string const& commandName, + std::string const& script, + std::string const& inputs, + std::string const& outputs, + std::string const& comment); void WriteCustomCommands(); void WriteCustomCommand(cmSourceFile const* sf); void WriteGroups(); @@ -198,6 +204,7 @@ private: cmGeneratedFileStream* BuildFileStream; cmLocalVisualStudio7Generator* LocalGenerator; std::set SourcesVisited; + std::set CSharpCustomCommandNames; bool IsMissingFiles; std::vector AddedFiles; std::string DefaultArtifactDir; diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 0369fa4..702ca17 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -376,3 +376,7 @@ if(CMake_TEST_ANDROID_NDK OR CMake_TEST_ANDROID_STANDALONE_TOOLCHAIN) endif() set_property(TEST RunCMake.Android PROPERTY TIMEOUT ${CMake_TEST_ANDROID_TIMEOUT}) endif() + +if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^89]|[89][0-9])") + add_RunCMake_test(CSharpCustomCommand) +endif() diff --git a/Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt b/Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt new file mode 100644 index 0000000..74b3ff8 --- /dev/null +++ b/Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.3) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CSharpCustomCommand/CommandWithOutput-check.cmake b/Tests/RunCMake/CSharpCustomCommand/CommandWithOutput-check.cmake new file mode 100644 index 0000000..60d77eb --- /dev/null +++ b/Tests/RunCMake/CSharpCustomCommand/CommandWithOutput-check.cmake @@ -0,0 +1,21 @@ +if(checkLevel EQUAL 0) + message("checking generation (${srcName} does not exist)") + if(EXISTS "${generatedFileName}") + set(RunCMake_TEST_FAILED "file \"${generatedFileName}\" should not exist") + endif() +elseif(checkLevel EQUAL 1) + message("checking build 1 (generate ${srcName})") + if(NOT "${actual_stdout}" MATCHES "${commandComment}") + set(RunCMake_TEST_FAILED "command not executed") + endif() +elseif(checkLevel EQUAL 2) + message("checking build 2 (no change in ${srcName}.in)") + if("${actual_stdout}" MATCHES "${commandComment}") + set(RunCMake_TEST_FAILED "command executed") + endif() +elseif(checkLevel EQUAL 3) + message("checking build 3 (update ${srcName})") + if(NOT "${actual_stdout}" MATCHES "${commandComment}") + set(RunCMake_TEST_FAILED "command not executed") + endif() +endif() diff --git a/Tests/RunCMake/CSharpCustomCommand/CommandWithOutput.cmake b/Tests/RunCMake/CSharpCustomCommand/CommandWithOutput.cmake new file mode 100644 index 0000000..68341fa --- /dev/null +++ b/Tests/RunCMake/CSharpCustomCommand/CommandWithOutput.cmake @@ -0,0 +1,13 @@ +enable_language(CSharp) + +add_executable(CSharpCustomCommand dummy.cs) + +add_custom_command(OUTPUT ${generatedFileName} + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${inputFileName} ${generatedFileName} + MAIN_DEPENDENCY ${inputFileName} + COMMENT "${commandComment}") + +target_sources(CSharpCustomCommand PRIVATE + ${inputFileName} + ${generatedFileName}) diff --git a/Tests/RunCMake/CSharpCustomCommand/RunCMakeTest.cmake b/Tests/RunCMake/CSharpCustomCommand/RunCMakeTest.cmake new file mode 100644 index 0000000..fa5618a --- /dev/null +++ b/Tests/RunCMake/CSharpCustomCommand/RunCMakeTest.cmake @@ -0,0 +1,34 @@ +include(RunCMake) + +# Use a single build tree for a few tests without cleaning. +set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CommandWithOutput-build) +set(RunCMake_TEST_NO_CLEAN 1) +file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") +file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}") +set(RunCMake-check-file CommandWithOutput-check.cmake) + +set(srcName "test.cs") +set(srcFileName "${CMAKE_CURRENT_LIST_DIR}/${srcName}.in") +set(inputFileName "${RunCMake_TEST_BINARY_DIR}/${srcName}.in") +set(generatedFileName "${RunCMake_TEST_BINARY_DIR}/${srcName}") +set(commandComment "Generating ${srcName}") + +# copy the input file to build dir to avoid changing files in cmake +# source tree. +file(COPY "${srcFileName}" DESTINATION "${RunCMake_TEST_BINARY_DIR}") + +set(RunCMake_TEST_OPTIONS ${RunCMake_TEST_OPTIONS} + "-DinputFileName=${inputFileName}" + "-DgeneratedFileName=${generatedFileName}" + "-DcommandComment=${commandComment}") + +set(checkLevel 0) +run_cmake(CommandWithOutput) +set(checkLevel 1) +run_cmake_command(CommandWithOutput-build1 ${CMAKE_COMMAND} --build . --config Debug) +set(checkLevel 2) +run_cmake_command(CommandWithOutput-build2 ${CMAKE_COMMAND} --build . --config Debug) +# change file content to trigger custom command with next build +file(APPEND ${inputFileName} "\n") +set(checkLevel 3) +run_cmake_command(CommandWithOutput-build3 ${CMAKE_COMMAND} --build . --config Debug) diff --git a/Tests/RunCMake/CSharpCustomCommand/dummy.cs b/Tests/RunCMake/CSharpCustomCommand/dummy.cs new file mode 100644 index 0000000..e69de29 diff --git a/Tests/RunCMake/CSharpCustomCommand/test.cs.in b/Tests/RunCMake/CSharpCustomCommand/test.cs.in new file mode 100644 index 0000000..05a7531 --- /dev/null +++ b/Tests/RunCMake/CSharpCustomCommand/test.cs.in @@ -0,0 +1,8 @@ +class TestCs +{ + public static int Main(string[] args) + { + System.Console.WriteLine("Test C#"); + return 0; + } +} -- cgit v0.12