소스 검색

Implement install-yourself for Windows

default_compile_flags
vector-of-bool 3 년 전
부모
커밋
f896c950c6
1개의 변경된 파일144개의 추가작업 그리고 45개의 파일을 삭제
  1. +144
    -45
      src/dds/cli/cmd/install_yourself.cpp

+ 144
- 45
src/dds/cli/cmd/install_yourself.cpp 파일 보기

@@ -8,7 +8,6 @@

#include <boost/leaf/handle_exception.hpp>
#include <fansi/styled.hpp>
#include <fmt/ostream.h>
#include <neo/assert.hpp>
#include <neo/platform.hpp>
#include <neo/scope.hpp>
@@ -89,12 +88,96 @@ fs::path system_binaries_dir() noexcept {
#endif
}

#if _WIN32
void fixup_path_env(const wil::unique_hkey& env_hkey, fs::path want_path) {
DWORD len = 0;
// Get the length
auto err = ::RegGetValueW(env_hkey.get(),
nullptr,
L"PATH",
RRF_RT_REG_EXPAND_SZ | RRF_RT_REG_SZ | RRF_NOEXPAND,
nullptr,
nullptr,
&len);
if (err != ERROR_SUCCESS) {
throw std::system_error(std::error_code(err, std::system_category()),
"Failed to access PATH environment variable [1]");
}
// Now get the value
std::wstring buffer;
buffer.resize(len / 2);
err = ::RegGetValueW(env_hkey.get(),
nullptr,
L"PATH",
RRF_RT_REG_EXPAND_SZ | RRF_RT_REG_SZ | RRF_NOEXPAND,
nullptr,
buffer.data(),
&len);
if (err != ERROR_SUCCESS) {
throw std::system_error(std::error_code(err, std::system_category()),
"Failed to access PATH environment variable [2]");
}
// Strip null-term
buffer.resize(len);
while (!buffer.empty() && buffer.back() == 0) {
buffer.pop_back();
}
// Check if we need to append the user-local binaries dir to the path
const auto want_entry = fs::path(want_path).make_preferred().lexically_normal();
const auto path_env_str = fs::path(buffer).string();
auto path_elems = split_view(path_env_str, ";");
const bool any_match = std::any_of(path_elems.cbegin(), path_elems.cend(), [&](auto view) {
auto existing = fs::weakly_canonical(view).make_preferred().lexically_normal();
dds_log(trace, "Existing PATH entry: '{}'", existing.string());
return existing.native() == want_entry.native();
});
if (any_match) {
dds_log(info, "PATH is up-to-date");
return;
}
// It's not there. Add it now.
auto want_str = want_entry.string();
path_elems.insert(path_elems.begin(), want_str);
auto joined = joinstr(";", path_elems);
buffer = fs::path(joined).native();
// Put the new PATH entry back into the environment
err = ::RegSetValueExW(env_hkey.get(),
L"Path",
0,
REG_EXPAND_SZ,
reinterpret_cast<const BYTE*>(buffer.data()),
(buffer.size() + 1) * 2);
if (err != ERROR_SUCCESS) {
throw std::system_error(std::error_code(err, std::system_category()),
"Failed to modify PATH environment variable");
}
dds_log(
info,
"The directory [.br.cyan[{}]] has been added to your PATH environment variables."_styled,
want_path.string());
dds_log(
info,
".bold.cyan[NOTE:] You may need to restart running applications to see this change!"_styled);
}
#endif

void fixup_system_path(const options&) {
#if !_WIN32
// We install into /usr/local/bin, and every nix-like system we support already has that on the
// global PATH
#else // Windows!
#error "Not yet implemented"
wil::unique_hkey env_hkey;
auto err = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment",
0,
KEY_WRITE | KEY_READ,
&env_hkey);
if (err != ERROR_SUCCESS) {
throw std::system_error(std::error_code(err, std::system_category()),
"Failed to open user-local environment variables registry "
"entry");
}
fixup_path_env(env_hkey, "C:/bin");
#endif
}

@@ -104,7 +187,7 @@ void fixup_user_path(const options& opts) {
auto profile_content = dds::slurp_file(profile_file);
if (dds::contains(profile_content, "$HOME/.local/bin")) {
// We'll assume that this is properly loading .local/bin for .profile
dds_log(info, ".br.cyan[{}] is okay"_styled, profile_file);
dds_log(info, "[.br.cyan[{}]] is okay"_styled, profile_file.string());
} else {
// Let's add it
profile_content
@@ -112,12 +195,12 @@ void fixup_user_path(const options& opts) {
"binaries\nPATH=$HOME/bin:$HOME/.local/bin:$PATH\n");
if (opts.dry_run) {
dds_log(info,
"Would update .br.cyan[{}] to have ~/.local/bin on $PATH"_styled,
profile_file);
"Would update [.br.cyan[{}]] to have ~/.local/bin on $PATH"_styled,
profile_file.string());
} else {
dds_log(info,
"Updating .br.cyan[{}] with a user-local binaries PATH entry"_styled,
profile_file);
"Updating [.br.cyan[{}]] with a user-local binaries PATH entry"_styled,
profile_file.string());
auto tmp_file = profile_file;
tmp_file += ".tmp";
auto bak_file = profile_file;
@@ -136,9 +219,9 @@ void fixup_user_path(const options& opts) {
safe_rename(tmp_file, profile_file);
// Okay!
dds_log(info,
".br.green[{}] was updated. Prior contents are safe in .br.cyan[{}]"_styled,
profile_file,
bak_file);
"[.br.green[{}]] was updated. Prior contents are safe in [.br.cyan[{}]]"_styled,
profile_file.string(),
bak_file.string());
}
}

@@ -147,12 +230,14 @@ void fixup_user_path(const options& opts) {
auto fish_config_content = slurp_file(fish_config);
if (dds::contains(fish_config_content, "$HOME/.local/bin")) {
// Assume that this is up-to-date
dds_log(info, "Fish configuration in .br.cyan[{}] is okay"_styled, fish_config);
dds_log(info,
"Fish configuration in [.br.cyan[{}]] is okay"_styled,
fish_config.string());
} else {
dds_log(
info,
"Updating Fish shell configuration .br.cyan[{}] with user-local binaries PATH entry"_styled,
fish_config);
"Updating Fish shell configuration [.br.cyan[{}]] with user-local binaries PATH entry"_styled,
fish_config.string());
fish_config_content
+= ("\n# This line was added by 'dds install-yourself' to add the usre-local "
"binaries directory to $PATH\nset -x PATH $PATH \"$HOME/.local/bin\"\n");
@@ -173,12 +258,21 @@ void fixup_user_path(const options& opts) {
safe_rename(tmp_file, fish_config);
// Okay!
dds_log(info,
".br.green[{}] was updated. Prior contents are safe in .br.cyan[{}]"_styled,
fish_config,
bak_file);
"[.br.green[{}]] was updated. Prior contents are safe in [.br.cyan[{}]]"_styled,
fish_config.string(),
bak_file.string());
}
}
#else // _WIN32
wil::unique_hkey env_hkey;
auto err
= ::RegOpenKeyExW(HKEY_CURRENT_USER, L"Environment", 0, KEY_WRITE | KEY_READ, &env_hkey);
if (err != ERROR_SUCCESS) {
throw std::system_error(std::error_code(err, std::system_category()),
"Failed to open user-local environment variables registry "
"entry");
}
fixup_path_env(env_hkey, "%LocalAppData%/bin");
#endif
}

@@ -202,67 +296,72 @@ int _install_yourself(const options& opts) {
dest_path += ".exe";
}

if (fs::canonical(dest_path) == fs::canonical(self_exe)) {
dds_log(error, "We cannot install over our own executable (.br.red[{}])"_styled, self_exe);
if (fs::weakly_canonical(dest_path) == fs::canonical(self_exe)) {
dds_log(error,
"We cannot install over our own executable (.br.red[{}])"_styled,
self_exe.string());
return 1;
}

if (!fs::is_directory(dest_dir)) {
if (opts.dry_run) {
dds_log(info, "Would create directory .br.cyan[{}]"_styled, dest_dir);
dds_log(info, "Would create directory [.br.cyan[{}]]"_styled, dest_dir.string());
} else {
dds_log(info, "Creating directory .br.cyan[{}]"_styled, dest_dir);
dds_log(info, "Creating directory [.br.cyan[{}]]"_styled, dest_dir.string());
fs::create_directories(dest_dir);
}
}

if (opts.dry_run) {
if (fs::is_symlink(dest_path)) {
dds_log(info, "Would remove symlink .br.cyan[{}]"_styled, dest_path);
dds_log(info, "Would remove symlink [.br.cyan[{}]]"_styled, dest_path.string());
}
if (fs::exists(dest_path) && !fs::is_symlink(dest_path)) {
if (opts.install_yourself.symlink) {
dds_log(
info,
"Would overwrite .br.yellow[{0}] with a symlink .br.green[{0}] -> .br.cyan[{1}]"_styled,
dest_path,
self_exe);
dest_path.string(),
self_exe.string());
} else {
dds_log(info,
"Would overwrite .br.yellow[{}] with .br.cyan[{}]"_styled,
dest_path,
self_exe);
"Would overwrite .br.yellow[{}] with [.br.cyan[{}]]"_styled,
dest_path.string(),
self_exe.string());
}
} else {
if (opts.install_yourself.symlink) {
dds_log(info,
"Would create a symlink .br.green[{}] -> .br.cyan[{}]"_styled,
dest_path,
self_exe);
"Would create a symlink [.br.green[{}]] -> [.br.cyan[{}]]"_styled,
dest_path.string(),
self_exe.string());
} else {
dds_log(info,
"Would install .br.cyan[{}] to .br.yellow[{}]"_styled,
self_exe,
dest_path);
"Would install [.br.cyan[{}]] to .br.yellow[{}]"_styled,
self_exe.string(),
dest_path.string());
}
}
} else {
if (fs::is_symlink(dest_path)) {
dds_log(info, "Removing old symlink file .br.cyan[{}]"_styled, dest_path);
dds_log(info, "Removing old symlink file [.br.cyan[{}]]"_styled, dest_path.string());
dds::remove_file(dest_path).value();
}
if (opts.install_yourself.symlink) {
if (fs::exists(dest_path)) {
dds_log(info, "Removing previous file .br.cyan[{}]"_styled, dest_path);
dds_log(info, "Removing previous file [.br.cyan[{}]]"_styled, dest_path.string());
dds::remove_file(dest_path).value();
}
dds_log(info,
"Creating symbolic link .br.green[{}] -> .br.cyan[{}]"_styled,
dest_path,
self_exe);
"Creating symbolic link [.br.green[{}]] -> [.br.cyan[{}]]"_styled,
dest_path.string(),
self_exe.string());
dds::create_symlink(self_exe, dest_path).value();
} else {
dds_log(info, "Installing .br.cyan[{}] to .br.green[{}]"_styled, self_exe, dest_path);
dds_log(info,
"Installing [.br.cyan[{}]] to [.br.green[{}]]"_styled,
self_exe.string(),
dest_path.string());
dds::copy_file(self_exe, dest_path, fs::copy_options::overwrite_existing).value();
}
}
@@ -290,25 +389,25 @@ int install_yourself(const options& opts) {
},
[](std::error_code ec, e_copy_file copy) {
dds_log(error,
"Failed to copy file .br.cyan[{}] to .br.yellow[{}]: .bold.red[{}]"_styled,
copy.source,
copy.dest,
"Failed to copy file [.br.cyan[{}]] to .br.yellow[{}]: .bold.red[{}]"_styled,
copy.source.string(),
copy.dest.string(),
ec.message());
return 1;
},
[](std::error_code ec, e_remove_file file) {
dds_log(error,
"Failed to delete file .br.yellow[{}]: .bold.red[{}]"_styled,
file.value,
file.value.string(),
ec.message());
return 1;
},
[](std::error_code ec, e_symlink oper) {
dds_log(
error,
"Failed to create symlink from .br.yellow[{}] to .br.cyan[{}]: .bold.red[{}]"_styled,
oper.symlink,
oper.target,
"Failed to create symlink from .br.yellow[{}] to [.br.cyan[{}]]: .bold.red[{}]"_styled,
oper.symlink.string(),
oper.target.string(),
ec.message());
return 1;
},

Loading…
취소
저장