From 78ec04613063653008f221eed644f1c2566fd900 Mon Sep 17 00:00:00 2001 From: Davy Durham Date: Thu, 17 Mar 2016 16:12:02 -0400 Subject: [PATCH 1/4] VS: Add option to choose the `.sln` startup project (#15578) Add a `VS_STARTUP_PROJECT` directory property to specify the project that should be placed first in the `.sln` file so that it will be selected as the default startup project. Co-Author: Taylor Braun-Jones --- Help/manual/cmake-properties.7.rst | 1 + Help/prop_dir/VS_STARTUP_PROJECT.rst | 12 +++++++++ Help/release/dev/vs-startup-project.rst | 6 +++++ Source/cmGlobalVisualStudio71Generator.cxx | 3 ++- Source/cmGlobalVisualStudioGenerator.cxx | 26 +++++++++++++++++++ Source/cmGlobalVisualStudioGenerator.h | 2 ++ Tests/RunCMake/VSSolution/RunCMakeTest.cmake | 2 ++ .../VSSolution/StartupProject-check.cmake | 4 +++ .../RunCMake/VSSolution/StartupProject.cmake | 2 ++ .../StartupProjectMissing-check.cmake | 4 +++ .../StartupProjectMissing-stderr.txt | 4 +++ .../VSSolution/StartupProjectMissing.cmake | 1 + .../VSSolution/solution_parsing.cmake | 14 ++++++++++ 13 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 Help/prop_dir/VS_STARTUP_PROJECT.rst create mode 100644 Help/release/dev/vs-startup-project.rst create mode 100644 Tests/RunCMake/VSSolution/StartupProject-check.cmake create mode 100644 Tests/RunCMake/VSSolution/StartupProject.cmake create mode 100644 Tests/RunCMake/VSSolution/StartupProjectMissing-check.cmake create mode 100644 Tests/RunCMake/VSSolution/StartupProjectMissing-stderr.txt create mode 100644 Tests/RunCMake/VSSolution/StartupProjectMissing.cmake diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index fbde4eb68..dadbc6e51 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -76,6 +76,7 @@ Properties on Directories /prop_dir/VARIABLES /prop_dir/VS_GLOBAL_SECTION_POST_section /prop_dir/VS_GLOBAL_SECTION_PRE_section + /prop_dir/VS_STARTUP_PROJECT .. _`Target Properties`: diff --git a/Help/prop_dir/VS_STARTUP_PROJECT.rst b/Help/prop_dir/VS_STARTUP_PROJECT.rst new file mode 100644 index 000000000..edd483243 --- /dev/null +++ b/Help/prop_dir/VS_STARTUP_PROJECT.rst @@ -0,0 +1,12 @@ +VS_STARTUP_PROJECT +------------------ + +Specify the default startup project in a Visual Studio solution. + +The property must be set to the name of an existing target. This +will cause that project to be listed first in the generated solution +file causing Visual Studio to make it the startup project if the +solution has never been opened before. + +If this property is not specified, then the "ALL_BUILD" project +will be the default. diff --git a/Help/release/dev/vs-startup-project.rst b/Help/release/dev/vs-startup-project.rst new file mode 100644 index 000000000..f4674001a --- /dev/null +++ b/Help/release/dev/vs-startup-project.rst @@ -0,0 +1,6 @@ +vs-startup-project +------------------ + +* The :ref:`Visual Studio Generators` learned to honor a new + :prop_dir:`VS_STARTUP_PROJECT` directory property that specifies + the default startup project for generated solutions (``.sln`` files). diff --git a/Source/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx index 8227b82da..f6796a543 100644 --- a/Source/cmGlobalVisualStudio71Generator.cxx +++ b/Source/cmGlobalVisualStudio71Generator.cxx @@ -94,7 +94,8 @@ void cmGlobalVisualStudio71Generator TargetDependSet projectTargets; TargetDependSet originalTargets; this->GetTargetSets(projectTargets, originalTargets, root, generators); - OrderedTargetDependSet orderedProjectTargets(projectTargets, "ALL_BUILD"); + OrderedTargetDependSet orderedProjectTargets( + projectTargets, this->GetStartupProjectName(root)); this->WriteTargetsToSolution(fout, root, orderedProjectTargets); diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index 00bb511c4..b11b49a7c 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -518,6 +518,32 @@ cmGlobalVisualStudioGenerator::GetUtilityDepend( return i->second; } +//---------------------------------------------------------------------------- +std::string +cmGlobalVisualStudioGenerator::GetStartupProjectName( + cmLocalGenerator const* root) const +{ + const char* n = root->GetMakefile()->GetProperty("VS_STARTUP_PROJECT"); + if (n && *n) + { + std::string startup = n; + if (this->FindTarget(startup)) + { + return startup; + } + else + { + root->GetMakefile()->IssueMessage( + cmake::AUTHOR_WARNING, + "Directory property VS_STARTUP_PROJECT specifies target " + "'" + startup + "' that does not exist. Ignoring."); + } + } + + // default, if not specified + return this->GetAllTargetName(); +} + //---------------------------------------------------------------------------- #include diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index ac9111ee3..723a75ff0 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h @@ -106,6 +106,8 @@ public: void ComputeTargetObjectDirectory(cmGeneratorTarget* gt) const; + std::string GetStartupProjectName(cmLocalGenerator const* root) const; + void AddSymbolExportCommand( cmGeneratorTarget*, std::vector& commands, std::string const& configName); diff --git a/Tests/RunCMake/VSSolution/RunCMakeTest.cmake b/Tests/RunCMake/VSSolution/RunCMakeTest.cmake index 6ae158d29..8ae959878 100644 --- a/Tests/RunCMake/VSSolution/RunCMakeTest.cmake +++ b/Tests/RunCMake/VSSolution/RunCMakeTest.cmake @@ -8,3 +8,5 @@ run_cmake(MorePost) run_cmake(PrePost) run_cmake(Override1) run_cmake(Override2) +run_cmake(StartupProject) +run_cmake(StartupProjectMissing) diff --git a/Tests/RunCMake/VSSolution/StartupProject-check.cmake b/Tests/RunCMake/VSSolution/StartupProject-check.cmake new file mode 100644 index 000000000..352bbd53f --- /dev/null +++ b/Tests/RunCMake/VSSolution/StartupProject-check.cmake @@ -0,0 +1,4 @@ +getFirstProject(first_project StartupProject) +if(NOT first_project STREQUAL "TestStartup") + error("TestStartup is not the startup project") +endif() diff --git a/Tests/RunCMake/VSSolution/StartupProject.cmake b/Tests/RunCMake/VSSolution/StartupProject.cmake new file mode 100644 index 000000000..7192f3dea --- /dev/null +++ b/Tests/RunCMake/VSSolution/StartupProject.cmake @@ -0,0 +1,2 @@ +add_custom_target(TestStartup) +set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT "TestStartup") diff --git a/Tests/RunCMake/VSSolution/StartupProjectMissing-check.cmake b/Tests/RunCMake/VSSolution/StartupProjectMissing-check.cmake new file mode 100644 index 000000000..95fede754 --- /dev/null +++ b/Tests/RunCMake/VSSolution/StartupProjectMissing-check.cmake @@ -0,0 +1,4 @@ +getFirstProject(first_project StartupProjectMissing) +if(NOT first_project STREQUAL "ALL_BUILD") + error("ALL_BUILD is not the startup project") +endif() diff --git a/Tests/RunCMake/VSSolution/StartupProjectMissing-stderr.txt b/Tests/RunCMake/VSSolution/StartupProjectMissing-stderr.txt new file mode 100644 index 000000000..da92c6d0a --- /dev/null +++ b/Tests/RunCMake/VSSolution/StartupProjectMissing-stderr.txt @@ -0,0 +1,4 @@ +^CMake Warning \(dev\) in CMakeLists.txt: + Directory property VS_STARTUP_PROJECT specifies target 'DoesNotExist' that + does not exist. Ignoring. +This warning is for project developers. Use -Wno-dev to suppress it.$ diff --git a/Tests/RunCMake/VSSolution/StartupProjectMissing.cmake b/Tests/RunCMake/VSSolution/StartupProjectMissing.cmake new file mode 100644 index 000000000..907a877f8 --- /dev/null +++ b/Tests/RunCMake/VSSolution/StartupProjectMissing.cmake @@ -0,0 +1 @@ +set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT "DoesNotExist") diff --git a/Tests/RunCMake/VSSolution/solution_parsing.cmake b/Tests/RunCMake/VSSolution/solution_parsing.cmake index dd158efe9..001b584a8 100644 --- a/Tests/RunCMake/VSSolution/solution_parsing.cmake +++ b/Tests/RunCMake/VSSolution/solution_parsing.cmake @@ -50,6 +50,20 @@ macro(parseGlobalSections arg_out_pre arg_out_post testName) endmacro() +macro(getFirstProject arg_out_first_project testName) + set(${arg_out_first_project} "") + set(sln "${RunCMake_TEST_BINARY_DIR}/${testName}.sln") + if(NOT EXISTS "${sln}") + error("Expected solution file ${sln} does not exist") + endif() + file(STRINGS "${sln}" project_lines REGEX "^Project\\(") + list(GET project_lines 0 first_project) + string(REGEX REPLACE ".* = \"" "" first_project "${first_project}") + string(REGEX REPLACE "\", .*" "" first_project "${first_project}") + set(${arg_out_first_project} "${first_project}") +endmacro() + + macro(testGlobalSection prefix sectionName) if(NOT DEFINED ${prefix}_${sectionName}) error("Section ${sectionName} does not exist") From c05ea48545f04dee8666094b0731366b64dfc9ad Mon Sep 17 00:00:00 2001 From: Taylor Braun-Jones Date: Mon, 21 Mar 2016 14:34:34 -0400 Subject: [PATCH 2/4] VS: Improve unit test macros Change `getFirstProject` macro to more flexible version `getProjectNames` --- .../VSSolution/StartupProject-check.cmake | 3 ++- .../VSSolution/StartupProjectMissing-check.cmake | 3 ++- Tests/RunCMake/VSSolution/solution_parsing.cmake | 15 ++++++++------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Tests/RunCMake/VSSolution/StartupProject-check.cmake b/Tests/RunCMake/VSSolution/StartupProject-check.cmake index 352bbd53f..f36aab275 100644 --- a/Tests/RunCMake/VSSolution/StartupProject-check.cmake +++ b/Tests/RunCMake/VSSolution/StartupProject-check.cmake @@ -1,4 +1,5 @@ -getFirstProject(first_project StartupProject) +getProjectNames(projects) +list(GET projects 0 first_project) if(NOT first_project STREQUAL "TestStartup") error("TestStartup is not the startup project") endif() diff --git a/Tests/RunCMake/VSSolution/StartupProjectMissing-check.cmake b/Tests/RunCMake/VSSolution/StartupProjectMissing-check.cmake index 95fede754..b1017dd07 100644 --- a/Tests/RunCMake/VSSolution/StartupProjectMissing-check.cmake +++ b/Tests/RunCMake/VSSolution/StartupProjectMissing-check.cmake @@ -1,4 +1,5 @@ -getFirstProject(first_project StartupProjectMissing) +getProjectNames(projects) +list(GET projects 0 first_project) if(NOT first_project STREQUAL "ALL_BUILD") error("ALL_BUILD is not the startup project") endif() diff --git a/Tests/RunCMake/VSSolution/solution_parsing.cmake b/Tests/RunCMake/VSSolution/solution_parsing.cmake index 001b584a8..4e5bb5908 100644 --- a/Tests/RunCMake/VSSolution/solution_parsing.cmake +++ b/Tests/RunCMake/VSSolution/solution_parsing.cmake @@ -50,17 +50,18 @@ macro(parseGlobalSections arg_out_pre arg_out_post testName) endmacro() -macro(getFirstProject arg_out_first_project testName) - set(${arg_out_first_project} "") - set(sln "${RunCMake_TEST_BINARY_DIR}/${testName}.sln") +macro(getProjectNames arg_out_projects) + set(${arg_out_projects} "") + set(sln "${RunCMake_TEST_BINARY_DIR}/${test}.sln") if(NOT EXISTS "${sln}") error("Expected solution file ${sln} does not exist") endif() file(STRINGS "${sln}" project_lines REGEX "^Project\\(") - list(GET project_lines 0 first_project) - string(REGEX REPLACE ".* = \"" "" first_project "${first_project}") - string(REGEX REPLACE "\", .*" "" first_project "${first_project}") - set(${arg_out_first_project} "${first_project}") + foreach(project_line IN LISTS project_lines) + string(REGEX REPLACE ".* = \"" "" project_line "${project_line}") + string(REGEX REPLACE "\", .*" "" project_line "${project_line}") + list(APPEND ${arg_out_projects} "${project_line}") + endforeach() endmacro() From f069be054833f465ab469b534fddbc52c5c9913b Mon Sep 17 00:00:00 2001 From: Taylor Braun-Jones Date: Mon, 21 Mar 2016 16:01:20 -0400 Subject: [PATCH 3/4] VS: Fix default target support for targets nested inside a folder It's not actually the first target in a `.sln` file that is treated as the default startup project, but rather the first fully defined target. --- Source/cmGlobalVisualStudio71Generator.cxx | 19 ++++++++++++++++++- Tests/RunCMake/CMakeLists.txt | 5 ++++- Tests/RunCMake/VSSolution/RunCMakeTest.cmake | 4 ++++ .../StartupProjectUseFolders-check.cmake | 9 +++++++++ .../VSSolution/StartupProjectUseFolders.cmake | 3 +++ 5 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 Tests/RunCMake/VSSolution/StartupProjectUseFolders-check.cmake create mode 100644 Tests/RunCMake/VSSolution/StartupProjectUseFolders.cmake diff --git a/Source/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx index f6796a543..7b51fdf49 100644 --- a/Source/cmGlobalVisualStudio71Generator.cxx +++ b/Source/cmGlobalVisualStudio71Generator.cxx @@ -97,14 +97,31 @@ void cmGlobalVisualStudio71Generator OrderedTargetDependSet orderedProjectTargets( projectTargets, this->GetStartupProjectName(root)); - this->WriteTargetsToSolution(fout, root, orderedProjectTargets); + // Generate the targets specification to a string. We will put this in + // the actual .sln file later. As a side effect, this method also + // populates the set of folders. + std::ostringstream targetsSlnString; + this->WriteTargetsToSolution(targetsSlnString, root, orderedProjectTargets); + // VS 7 does not support folders specified first. + if (this->GetVersion() <= VS71) + { + fout << targetsSlnString.str(); + } + + // Generate folder specification. bool useFolderProperty = this->UseFolderProperty(); if (useFolderProperty) { this->WriteFolders(fout); } + // Now write the actual target specification content. + if (this->GetVersion() > VS71) + { + fout << targetsSlnString.str(); + } + // Write out the configurations information for the solution fout << "Global\n"; // Write out the configurations for the solution diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index c7fe6493e..0207753da 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -234,7 +234,10 @@ endif() if("${CMAKE_GENERATOR}" MATCHES "Visual Studio") add_RunCMake_test(include_external_msproject) - add_RunCMake_test(VSSolution) + if("${CMAKE_GENERATOR}" MATCHES "Visual Studio ([789]|10)" AND NOT CMAKE_VS_DEVENV_COMMAND) + set(NO_USE_FOLDERS 1) + endif() + add_RunCMake_test(VSSolution -DNO_USE_FOLDERS=${NO_USE_FOLDERS}) endif() if("${CMAKE_GENERATOR}" MATCHES "Visual Studio ([^789]|[789][0-9])") diff --git a/Tests/RunCMake/VSSolution/RunCMakeTest.cmake b/Tests/RunCMake/VSSolution/RunCMakeTest.cmake index 8ae959878..afd74a1cc 100644 --- a/Tests/RunCMake/VSSolution/RunCMakeTest.cmake +++ b/Tests/RunCMake/VSSolution/RunCMakeTest.cmake @@ -10,3 +10,7 @@ run_cmake(Override1) run_cmake(Override2) run_cmake(StartupProject) run_cmake(StartupProjectMissing) + +if(RunCMake_GENERATOR MATCHES "Visual Studio ([^7]|[7][0-9])" AND NOT NO_USE_FOLDERS) + run_cmake(StartupProjectUseFolders) +endif() diff --git a/Tests/RunCMake/VSSolution/StartupProjectUseFolders-check.cmake b/Tests/RunCMake/VSSolution/StartupProjectUseFolders-check.cmake new file mode 100644 index 000000000..c0a545a27 --- /dev/null +++ b/Tests/RunCMake/VSSolution/StartupProjectUseFolders-check.cmake @@ -0,0 +1,9 @@ +getProjectNames(projects) +list(GET projects 0 first_project) +if(NOT first_project STREQUAL "CMakePredefinedTargets") + error("CMakePredefinedTargets is not the first project") +endif() +list(GET projects 1 second_project) +if(NOT second_project STREQUAL "TestStartup") + error("TestStartup does not immediately follow the CMakePredefinedTargets project") +endif() diff --git a/Tests/RunCMake/VSSolution/StartupProjectUseFolders.cmake b/Tests/RunCMake/VSSolution/StartupProjectUseFolders.cmake new file mode 100644 index 000000000..8e422a495 --- /dev/null +++ b/Tests/RunCMake/VSSolution/StartupProjectUseFolders.cmake @@ -0,0 +1,3 @@ +add_custom_target(TestStartup) +set_property(GLOBAL PROPERTY USE_FOLDERS ON) +set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT "TestStartup") From ad140c6e1bb6eda5d17a199304578dbd92bedbad Mon Sep 17 00:00:00 2001 From: Taylor Braun-Jones Date: Mon, 21 Mar 2016 16:05:41 -0400 Subject: [PATCH 4/4] VS: Put ALL_BUILD in the PREDEFINED_TARGETS_FOLDER --- Source/cmGlobalVisualStudioGenerator.cxx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index b11b49a7c..04146fb08 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -89,19 +89,13 @@ void cmGlobalVisualStudioGenerator::AddExtraIDETargets() cmGeneratorTarget* gt = new cmGeneratorTarget(allBuild, gen[0]); gen[0]->AddGeneratorTarget(gt); -#if 0 - // Can't activate this code because we want ALL_BUILD - // selected as the default "startup project" when first - // opened in Visual Studio... And if it's nested in a - // folder, then that doesn't happen. // // Organize in the "predefined targets" folder: // - if (this->UseFolderProperty()) + if (this->UseFolderProperty() && this->GetVersion() > VS71) { allBuild->SetProperty("FOLDER", this->GetPredefinedTargetsFolder()); } -#endif // Now make all targets depend on the ALL_BUILD target for(std::vector::iterator i = gen.begin();