Browse Source

VS: Add StartupObject property for managed .NET projects

Florian Schweiger 3 years ago
parent
commit
d89af11f89

+ 1 - 0
Auxiliary/vim/syntax/cmake.vim

@@ -370,6 +370,7 @@ syn keyword cmakeProperty contained
             \ VS_DOTNET_DOCUMENTATION_FILE
             \ VS_DOTNET_REFERENCES
             \ VS_DOTNET_REFERENCES_COPY_LOCAL
+            \ VS_DOTNET_STARTUP_OBJECT
             \ VS_DOTNET_TARGET_FRAMEWORK_VERSION
             \ VS_DPI_AWARE
             \ VS_GLOBAL_KEYWORD

+ 1 - 0
Help/manual/cmake-properties.7.rst

@@ -394,6 +394,7 @@ Properties on Targets
    /prop_tgt/VS_DOTNET_REFERENCEPROP_refname_TAG_tagname
    /prop_tgt/VS_DOTNET_REFERENCES
    /prop_tgt/VS_DOTNET_REFERENCES_COPY_LOCAL
+   /prop_tgt/VS_DOTNET_STARTUP_OBJECT
    /prop_tgt/VS_DOTNET_TARGET_FRAMEWORK_VERSION
    /prop_tgt/VS_DPI_AWARE
    /prop_tgt/VS_GLOBAL_KEYWORD

+ 21 - 0
Help/prop_tgt/VS_DOTNET_STARTUP_OBJECT.rst

@@ -0,0 +1,21 @@
+VS_DOTNET_STARTUP_OBJECT
+------------------------
+
+.. versionadded:: 3.24
+
+Sets the startup object property in Visual Studio .NET targets.
+The property value defines a full qualified class name (including package
+name), for example: ``MyCompany.Package.MyStarterClass``.
+
+If the property is unset, Visual Studio uses the first matching
+``static void Main(string[])`` function signature by default. When more
+than one ``Main()`` method is available in the current project, the property
+becomes mandatory for building the project.
+
+This property only works for Visual Studio 2010 and above;
+it is ignored on other generators.
+
+.. code-block:: cmake
+
+  set_property(TARGET ${TARGET_NAME} PROPERTY
+    VS_DOTNET_STARTUP_OBJECT "MyCompany.Package.MyStarterClass")

+ 8 - 0
Help/release/dev/vs_dotnet_startup_object_support.rst

@@ -0,0 +1,8 @@
+vs_dotnet_startup_object_support
+--------------------------------
+
+* The :prop_tgt:`VS_DOTNET_STARTUP_OBJECT` target property was added to
+  tell :ref:`Visual Studio Generators` which startup class shall be used
+  when the program or project is executed. This is necessary when more
+  than one ``static void Main(string[])`` function signature is available
+  in a managed .NET project.

+ 14 - 0
Source/cmVisualStudio10TargetGenerator.cxx

@@ -664,6 +664,14 @@ void cmVisualStudio10TargetGenerator::WriteClassicMsBuildProjectFile(
       }
     }
 
+    cmValue startupObject =
+      this->GeneratorTarget->GetProperty("VS_DOTNET_STARTUP_OBJECT");
+
+    if (startupObject && this->Managed) {
+      Elem e1(e0, "PropertyGroup");
+      e1.Element("StartupObject", *startupObject);
+    }
+
     switch (this->ProjectType) {
       case VsProjectType::vcxproj: {
         std::string const& props =
@@ -927,6 +935,12 @@ void cmVisualStudio10TargetGenerator::WriteSdkStyleProjectFile(
         break;
     }
     e1.Element("OutputType", outputType);
+
+    cmValue startupObject =
+      this->GeneratorTarget->GetProperty("VS_DOTNET_STARTUP_OBJECT");
+    if (startupObject) {
+      e1.Element("StartupObject", *startupObject);
+    }
   }
 
   for (const std::string& config : this->Configurations) {

+ 1 - 0
Tests/RunCMake/VS10Project/RunCMakeTest.cmake

@@ -83,6 +83,7 @@ else()
   run_UnityBuildPCH()
 endif()
 
+run_cmake(VsDotnetStartupObject)
 run_cmake(VsDotnetTargetFramework)
 run_cmake(VsDotnetTargetFrameworkVersion)
 run_cmake(VsNoCompileBatching)

+ 22 - 0
Tests/RunCMake/VS10Project/VsDotnetStartupObject-check.cmake

@@ -0,0 +1,22 @@
+set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.csproj")
+if(NOT EXISTS "${vcProjectFile}")
+  set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
+  return()
+endif()
+
+set(startupObjectSet FALSE)
+
+file(STRINGS "${vcProjectFile}" lines)
+foreach(line IN LISTS lines)
+  if(line MATCHES "^ *<StartupObject[^>]*>([^<>]+)</StartupObject>$")
+    if("${CMAKE_MATCH_1}" STREQUAL "MyCompany.Package.MyStarterClass")
+        message(STATUS "foo.csproj has StartupObject class set")
+        set(startupObjectSet TRUE)
+    endif()
+  endif()
+endforeach()
+
+if(NOT startupObjectSet)
+  set(RunCMake_TEST_FAILED "StartupObject not found or not set correctly.")
+  return()
+endif()

+ 10 - 0
Tests/RunCMake/VS10Project/VsDotnetStartupObject.cmake

@@ -0,0 +1,10 @@
+enable_language(CSharp)
+if(NOT CMAKE_CSharp_COMPILER)
+    return()
+endif()
+
+set(CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION "v4.6.1")
+
+add_executable(foo foo.cs)
+
+set_target_properties(foo PROPERTIES VS_DOTNET_STARTUP_OBJECT "MyCompany.Package.MyStarterClass")

+ 1 - 0
Tests/RunCMake/VsDotnetSdk/RunCMakeTest.cmake

@@ -3,6 +3,7 @@ include(RunCMake)
 
 run_cmake(VsDotnetSdkCustomCommandsTarget)
 run_cmake(VsDotnetSdkCustomCommandsSource)
+run_cmake(VsDotnetSdkStartupObject)
 run_cmake(DotnetSdkVariables)
 
 function(run_VsDotnetSdk)

+ 22 - 0
Tests/RunCMake/VsDotnetSdk/VsDotnetSdkStartupObject-check.cmake

@@ -0,0 +1,22 @@
+set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.csproj")
+if(NOT EXISTS "${vcProjectFile}")
+  set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
+  return()
+endif()
+
+set(startupObjectSet FALSE)
+
+file(STRINGS "${vcProjectFile}" lines)
+foreach(line IN LISTS lines)
+  if(line MATCHES "^ *<StartupObject[^>]*>([^<>]+)</StartupObject>$")
+    if("${CMAKE_MATCH_1}" STREQUAL "CSharpOnly.CSharpOnly")
+        message(STATUS "foo.csproj has StartupObject class set")
+        set(startupObjectSet TRUE)
+    endif()
+  endif()
+endforeach()
+
+if(NOT startupObjectSet)
+  set(RunCMake_TEST_FAILED "StartupObject not found or not set correctly.")
+  return()
+endif()

+ 11 - 0
Tests/RunCMake/VsDotnetSdk/VsDotnetSdkStartupObject.cmake

@@ -0,0 +1,11 @@
+enable_language(CSharp)
+if(NOT CMAKE_CSharp_COMPILER)
+    return()
+endif()
+
+set(CMAKE_DOTNET_SDK "Microsoft.NET.Sdk")
+set(CMAKE_DOTNET_TARGET_FRAMEWORK_VERSION "net5.0")
+
+add_executable(foo csharponly.cs lib1.cs)
+
+set_target_properties(foo PROPERTIES VS_DOTNET_STARTUP_OBJECT "CSharpOnly.CSharpOnly")