aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/src/commands_integration.cpp
diff options
context:
space:
mode:
authorAlexander Karatarakis <alkarata@microsoft.com>2016-09-18 20:50:08 -0700
committerAlexander Karatarakis <alkarata@microsoft.com>2016-09-18 20:54:03 -0700
commitccca198c1b1730b0241911cb56dc8e3504958b2a (patch)
treea2dd9b8b087a09afdcecc5cbb3377bed15127eb2 /toolsrc/src/commands_integration.cpp
downloadvcpkg-ccca198c1b1730b0241911cb56dc8e3504958b2a.tar.gz
vcpkg-ccca198c1b1730b0241911cb56dc8e3504958b2a.zip
Initial commit
Diffstat (limited to 'toolsrc/src/commands_integration.cpp')
-rw-r--r--toolsrc/src/commands_integration.cpp297
1 files changed, 297 insertions, 0 deletions
diff --git a/toolsrc/src/commands_integration.cpp b/toolsrc/src/commands_integration.cpp
new file mode 100644
index 000000000..e1b63038a
--- /dev/null
+++ b/toolsrc/src/commands_integration.cpp
@@ -0,0 +1,297 @@
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <shellapi.h>
+#include "vcpkg_Commands.h"
+#include "vcpkg.h"
+#include <fstream>
+#include <iostream>
+#include <regex>
+#include "vcpkg_Environment.h"
+#include "vcpkg_Checks.h"
+#include "vcpkg_System.h"
+
+namespace vcpkg
+{
+ static const fs::path old_system_wide_targets_file = "C:/Program Files (x86)/MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.nuget.targets";
+ static const fs::path system_wide_targets_file = "C:/Program Files (x86)/MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.system.targets";
+
+ static std::string create_appdata_targets_shortcut(const std::string& target_path) noexcept
+ {
+ return Strings::format(R"###(
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Condition="Exists('%s') and '$(VCPkgLocalAppDataDisabled)' == ''" Project="%s" />
+</Project>
+)###", target_path, target_path);
+ }
+
+ static std::string create_system_targets_shortcut() noexcept
+ {
+ return R"###(
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <VCLibPackagePath Condition="'$(VCLibPackagePath)' == ''">$(LOCALAPPDATA)\vcpkg\vcpkg.user</VCLibPackagePath>
+ </PropertyGroup>
+ <Import Condition="'$(VCLibPackagePath)' != '' and Exists('$(VCLibPackagePath).props')" Project="$(VCLibPackagePath).props" />
+</Project>
+)###";
+ }
+
+ static std::string create_nuget_targets_file(const fs::path& msbuild_vcpkg_targets_file) noexcept
+ {
+ const std::string as_string = msbuild_vcpkg_targets_file.string();
+
+ return Strings::format(R"###(
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="%s" Condition="Exists('%s')" />
+ <Target Name="CheckValidPlatform" BeforeTargets="Build">
+ <Error Text="Unsupported architecture combination. Remove the 'vcpkg' nuget package." Condition="'$(VCPkgEnabled)' != 'true' and '$(VCPkgDisableError)' == ''"/>
+ </Target>
+</Project>
+)###", as_string, as_string);
+ }
+
+ static std::string create_nuget_props_file() noexcept
+ {
+ return R"###(
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <VCPkgLocalAppDataDisabled>true</VCPkgLocalAppDataDisabled>
+ </PropertyGroup>
+</Project>
+)###";
+ }
+
+ static std::string get_nuget_id(const fs::path& vcpkg_root_dir)
+ {
+ std::string dir_id = vcpkg_root_dir.generic_string();
+ std::replace(dir_id.begin(), dir_id.end(), '/', '.');
+ dir_id.erase(1, 1); // Erasing the ":"
+
+ // NuGet id cannot have invalid characters. We will only use alphanumeric and dot.
+ dir_id.erase(std::remove_if(dir_id.begin(), dir_id.end(), [](char c)
+ {
+ return !isalnum(c) && (c != '.');
+ }), dir_id.end());
+
+ const std::string nuget_id = "vcpkg." + dir_id;
+ return nuget_id;
+ }
+
+ static std::string create_nuspec_file(const fs::path& vcpkg_root_dir, const std::string& nuget_id, const std::string& nupkg_version)
+ {
+ const std::string nuspec_file_content_template = R"(
+<package>
+ <metadata>
+ <id>@NUGET_ID@</id>
+ <version>@VERSION@</version>
+ <authors>cpp-packages</authors>
+ <description>
+ This package imports all libraries currently installed in @VCPKG_DIR@. This package does not contain any libraries and instead refers to the folder directly (like a symlink).
+ </description>
+ </metadata>
+ <files>
+ <file src="vcpkg.nuget.props" target="build\native\@NUGET_ID@.props" />
+ <file src="vcpkg.nuget.targets" target="build\native\@NUGET_ID@.targets" />
+ </files>
+</package>
+)";
+
+ std::string nuspec_file_content = std::regex_replace(nuspec_file_content_template, std::regex("@NUGET_ID@"), nuget_id);
+ nuspec_file_content = std::regex_replace(nuspec_file_content, std::regex("@VCPKG_DIR@"), vcpkg_root_dir.string());
+ nuspec_file_content = std::regex_replace(nuspec_file_content, std::regex("@VERSION@"), nupkg_version);
+ return nuspec_file_content;
+ }
+
+ enum class elevation_prompt_user_choice
+ {
+ yes,
+ no
+ };
+
+ static elevation_prompt_user_choice elevated_cmd_execute(const std::string& param)
+ {
+ SHELLEXECUTEINFO shExInfo = {0};
+ shExInfo.cbSize = sizeof(shExInfo);
+ shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
+ shExInfo.hwnd = nullptr;
+ shExInfo.lpVerb = "runas";
+ shExInfo.lpFile = "cmd"; // Application to start
+
+ shExInfo.lpParameters = param.c_str(); // Additional parameters
+ shExInfo.lpDirectory = nullptr;
+ shExInfo.nShow = SW_HIDE;
+ shExInfo.hInstApp = nullptr;
+
+ if (!ShellExecuteExA(&shExInfo))
+ {
+ return elevation_prompt_user_choice::no;
+ }
+ if (shExInfo.hProcess == nullptr)
+ {
+ return elevation_prompt_user_choice::no;
+ }
+ WaitForSingleObject(shExInfo.hProcess, INFINITE);
+ CloseHandle(shExInfo.hProcess);
+ return elevation_prompt_user_choice::yes;
+ }
+
+ static fs::path get_appdata_targets_path()
+ {
+ return fs::path(System::wdupenv_str(L"LOCALAPPDATA")) / "vcpkg" / "vcpkg.user.targets";
+ }
+
+ static void integrate_install(const vcpkg_paths& paths)
+ {
+ // TODO: This block of code should eventually be removed
+ {
+ if (fs::exists(old_system_wide_targets_file))
+ {
+ const std::string param = Strings::format(R"(/c DEL "%s" /Q > nul)", old_system_wide_targets_file.string());
+ elevation_prompt_user_choice user_choice = elevated_cmd_execute(param);
+ switch (user_choice)
+ {
+ case elevation_prompt_user_choice::yes:
+ break;
+ case elevation_prompt_user_choice::no:
+ System::println(System::color::warning, "Warning: Previous integration file was not removed");
+ exit(EXIT_FAILURE);
+ default:
+ Checks::unreachable();
+ }
+ }
+ }
+
+ const fs::path tmp_dir = paths.buildsystems / "tmp";
+ fs::create_directory(paths.buildsystems);
+ fs::create_directory(tmp_dir);
+
+ if (!fs::exists(system_wide_targets_file))
+ {
+ const fs::path sys_src_path = tmp_dir / "vcpkg.system.targets";
+ std::ofstream(sys_src_path) << create_system_targets_shortcut();
+
+ const std::string param = Strings::format(R"(/c COPY "%s" "%s" /Y > nul)", sys_src_path.string(), system_wide_targets_file.string());
+ elevation_prompt_user_choice user_choice = elevated_cmd_execute(param);
+ switch (user_choice)
+ {
+ case elevation_prompt_user_choice::yes:
+ break;
+ case elevation_prompt_user_choice::no:
+ System::println(System::color::warning, "Warning: integration was not applied");
+ exit(EXIT_FAILURE);
+ default:
+ Checks::unreachable();
+ }
+ }
+
+ const fs::path appdata_src_path = tmp_dir / "vcpkg.user.targets";
+ std::ofstream(appdata_src_path) << create_appdata_targets_shortcut(paths.buildsystems_msbuild_targets.string());
+ auto appdata_dst_path = get_appdata_targets_path();
+
+ if (!fs::copy_file(appdata_src_path, appdata_dst_path, fs::copy_options::overwrite_existing))
+ {
+ System::println(System::color::error, "Error: Failed to copy file: %s -> %s", appdata_src_path.string(), appdata_dst_path.string());
+ exit(EXIT_FAILURE);
+ }
+ System::println(System::color::success, "Applied user-wide integration for this vcpkg root.");
+ System::println("\n"
+ "All C++ projects can now #include any installed libraries.\n"
+ "Linking will be handled automatically.\n"
+ "Installing new libraries will make them instantly available.");
+
+ exit(EXIT_SUCCESS);
+ }
+
+ static void integrate_remove()
+ {
+ auto path = get_appdata_targets_path();
+ if (!fs::exists(path))
+ {
+ System::println(System::color::success, "User-wide integration is not installed");
+ exit(EXIT_SUCCESS);
+ }
+
+ const std::wstring cmd_line = Strings::format(LR"(DEL "%s")", get_appdata_targets_path().native());
+ const int exit_code = System::cmd_execute(cmd_line);
+ if (exit_code)
+ {
+ System::println(System::color::error, "Error: Unable to remove user-wide integration: %d", exit_code);
+ exit(exit_code);
+ }
+ System::println(System::color::success, "User-wide integration was removed");
+ exit(EXIT_SUCCESS);
+ }
+
+ static void integrate_project(const vcpkg_paths& paths)
+ {
+ Environment::ensure_nuget_on_path(paths);
+
+ const fs::path& buildsystems_dir = paths.buildsystems;
+ const fs::path tmp_dir = buildsystems_dir / "tmp";
+ fs::create_directory(buildsystems_dir);
+ fs::create_directory(tmp_dir);
+
+ const fs::path targets_file_path = tmp_dir / "vcpkg.nuget.targets";
+ const fs::path props_file_path = tmp_dir / "vcpkg.nuget.props";
+ const fs::path nuspec_file_path = tmp_dir / "vcpkg.nuget.nuspec";
+ const std::string nuget_id = get_nuget_id(paths.root);
+ const std::string nupkg_version = "1.0.0";
+
+ std::ofstream(targets_file_path) << create_nuget_targets_file(paths.buildsystems_msbuild_targets);
+ std::ofstream(props_file_path) << create_nuget_props_file();
+ std::ofstream(nuspec_file_path) << create_nuspec_file(paths.root, nuget_id, nupkg_version);
+
+ // Using all forward slashes for the command line
+ const std::wstring cmd_line = Strings::format(LR"(nuget.exe pack -OutputDirectory "%s" "%s" > nul)", buildsystems_dir.native(), nuspec_file_path.native());
+
+ const int exit_code = System::cmd_execute(cmd_line);
+
+ const fs::path nuget_package = buildsystems_dir / Strings::format("%s.%s.nupkg", nuget_id, nupkg_version);
+ if (exit_code != 0 || !fs::exists(nuget_package))
+ {
+ System::println(System::color::error, "Error: NuGet package creation failed");
+ exit(EXIT_FAILURE);
+ }
+
+ System::println(System::color::success, "Created nupkg: %s", nuget_package.string());
+
+ System::println(R"(
+With a project open, go to Tools->NuGet Package Manager->Package Manager Console and paste:
+ Install-Package %s -Source "%s"
+)", nuget_id, buildsystems_dir.generic_string());
+
+ exit(EXIT_SUCCESS);
+ }
+
+ const char* const INTEGRATE_COMMAND_HELPSTRING =
+ " vcpkg integrate install Make installed packages available user-wide. Requires admin privileges on first use\n"
+ " vcpkg integrate remove Remove user-wide integration\n"
+ " vcpkg integrate project Generate a referencing nuget package for individual VS project use\n";
+
+ void integrate_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths)
+ {
+ if (args.command_arguments.size() != 1)
+ {
+ std::cout << "Commands:\n" <<
+ INTEGRATE_COMMAND_HELPSTRING <<
+ "\n";
+ exit(EXIT_FAILURE);
+ }
+
+ if (args.command_arguments[0] == "install")
+ {
+ return integrate_install(paths);
+ }
+ if (args.command_arguments[0] == "remove")
+ {
+ return integrate_remove();
+ }
+ if (args.command_arguments[0] == "project")
+ {
+ return integrate_project(paths);
+ }
+
+ System::println(System::color::error, "Unknown parameter %s for integrate", args.command_arguments[0]);
+ exit(EXIT_FAILURE);
+ }
+}