mirror of
https://github.com/reactos/CMake.git
synced 2024-12-14 23:29:57 +00:00
Vs: fix CSharp custom command by introducing inline MSBuild <Targets>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
This commit is contained in:
parent
dcdab5cf23
commit
ec409a116f
@ -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 <Message></Message> element and just
|
||||
@ -578,6 +584,18 @@ void cmVisualStudio10TargetGenerator::Generate()
|
||||
this->WriteEvents(*i);
|
||||
this->WriteString("</PropertyGroup>\n", 1);
|
||||
}
|
||||
// make sure custom commands are executed before build (if necessary)
|
||||
this->WriteString("<PropertyGroup>\n", 1);
|
||||
this->WriteString("<BuildDependsOn>\n", 2);
|
||||
for (std::set<std::string>::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("</BuildDependsOn>\n", 2);
|
||||
this->WriteString("</PropertyGroup>\n", 1);
|
||||
}
|
||||
this->WriteString("</Project>", 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<cmSourceFile const*> customCommands;
|
||||
this->GeneratorTarget->GetCustomCommands(customCommands, "");
|
||||
for (std::vector<cmSourceFile const*>::const_iterator si =
|
||||
@ -1172,9 +1191,14 @@ void cmVisualStudio10TargetGenerator::WriteCustomCommand(
|
||||
}
|
||||
}
|
||||
if (cmCustomCommand const* command = sf->GetCustomCommand()) {
|
||||
this->WriteString("<ItemGroup>\n", 1);
|
||||
// C# projects write their <Target> within WriteCustomRule()
|
||||
if (this->ProjectType != csproj) {
|
||||
this->WriteString("<ItemGroup>\n", 1);
|
||||
}
|
||||
this->WriteCustomRule(sf, *command);
|
||||
this->WriteString("</ItemGroup>\n", 1);
|
||||
if (this->ProjectType != csproj) {
|
||||
this->WriteString("</ItemGroup>\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("<ItemGroup>\n", 1);
|
||||
std::string link;
|
||||
this->GetCSharpSourceLink(source, link);
|
||||
this->WriteSource("None", source, ">\n");
|
||||
if (!link.empty()) {
|
||||
this->WriteString("<Link>", 3);
|
||||
(*this->BuildFileStream) << link << "</Link>\n";
|
||||
}
|
||||
this->WriteString("</None>\n", 2);
|
||||
this->WriteString("</ItemGroup>\n", 1);
|
||||
}
|
||||
for (std::vector<std::string>::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("</CustomBuild>\n", 2);
|
||||
}
|
||||
this->WriteString("</CustomBuild>\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("<Exec Command=\"", 2);
|
||||
(*this->BuildFileStream) << "echo " << cmVS10EscapeXML(comment)
|
||||
<< "\" />\n";
|
||||
}
|
||||
this->WriteString("<Exec Command=\"", 2);
|
||||
(*this->BuildFileStream) << script << "\" />\n";
|
||||
this->WriteString("</Target>\n", 1);
|
||||
}
|
||||
|
||||
std::string cmVisualStudio10TargetGenerator::ConvertPath(
|
||||
std::string const& path, bool forceRelative)
|
||||
{
|
||||
|
@ -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<cmSourceFile const*> SourcesVisited;
|
||||
std::set<std::string> CSharpCustomCommandNames;
|
||||
bool IsMissingFiles;
|
||||
std::vector<std::string> AddedFiles;
|
||||
std::string DefaultArtifactDir;
|
||||
|
@ -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()
|
||||
|
3
Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt
Normal file
3
Tests/RunCMake/CSharpCustomCommand/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.3)
|
||||
project(${RunCMake_TEST} NONE)
|
||||
include(${RunCMake_TEST}.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()
|
13
Tests/RunCMake/CSharpCustomCommand/CommandWithOutput.cmake
Normal file
13
Tests/RunCMake/CSharpCustomCommand/CommandWithOutput.cmake
Normal file
@ -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})
|
34
Tests/RunCMake/CSharpCustomCommand/RunCMakeTest.cmake
Normal file
34
Tests/RunCMake/CSharpCustomCommand/RunCMakeTest.cmake
Normal file
@ -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)
|
0
Tests/RunCMake/CSharpCustomCommand/dummy.cs
Normal file
0
Tests/RunCMake/CSharpCustomCommand/dummy.cs
Normal file
8
Tests/RunCMake/CSharpCustomCommand/test.cs.in
Normal file
8
Tests/RunCMake/CSharpCustomCommand/test.cs.in
Normal file
@ -0,0 +1,8 @@
|
||||
class TestCs
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
System.Console.WriteLine("Test C#");
|
||||
return 0;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user