mirror of https://github.com/mamba-org/mamba.git
add menu install shortcut and more long paths support for Windows (#975)
This commit is contained in:
parent
fec4633c8c
commit
81a490a046
|
@ -392,7 +392,7 @@ jobs:
|
|||
run: |
|
||||
conda config --add channels conda-forge
|
||||
conda config --set channel_priority strict
|
||||
conda create -q -y -n mamba-tests vs2017_win-64 python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cpp-filesystem conda cmake gtest gmock ninja reproc-cpp yaml-cpp pyyaml cli11 pytest
|
||||
conda create -q -y -n mamba-tests vs2017_win-64 python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cpp-filesystem conda cmake gtest gmock ninja reproc-cpp yaml-cpp pyyaml cli11 pytest menuinst
|
||||
env:
|
||||
PYTHON_VERSION: ${{ matrix.python-version }}
|
||||
- name: micromamba python based tests
|
||||
|
@ -433,7 +433,7 @@ jobs:
|
|||
run: |
|
||||
conda config --add channels conda-forge
|
||||
conda config --set channel_priority strict
|
||||
conda create -q -y -n mamba-tests vs2017_win-64 python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cpp-filesystem conda cmake gtest gmock ninja reproc-cpp yaml-cpp pyyaml cli11 pytest
|
||||
conda create -q -y -n mamba-tests vs2017_win-64 python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cpp-filesystem conda cmake gtest gmock ninja reproc-cpp yaml-cpp pyyaml cli11 pytest menuinst
|
||||
env:
|
||||
PYTHON_VERSION: ${{ matrix.python-version }}
|
||||
- name: micromamba python based tests with pwsh
|
||||
|
@ -472,7 +472,7 @@ jobs:
|
|||
run: |
|
||||
conda config --add channels conda-forge
|
||||
conda config --set channel_priority strict
|
||||
conda create -q -y -n mamba-tests vs2017_win-64 python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cpp-filesystem conda cmake gtest gmock ninja reproc-cpp yaml-cpp pyyaml cli11 pytest
|
||||
conda create -q -y -n mamba-tests vs2017_win-64 python=$PYTHON_VERSION pip pybind11 libsolv libsodium libarchive "libcurl=7.76.1=*_0" nlohmann_json cpp-filesystem conda cmake gtest gmock ninja reproc-cpp yaml-cpp pyyaml cli11 pytest menuinst
|
||||
env:
|
||||
PYTHON_VERSION: ${{ matrix.python-version }}
|
||||
- name: micromamba python based tests
|
||||
|
|
|
@ -92,6 +92,7 @@ set(MAMBA_SOURCES
|
|||
${MAMBA_SOURCE_DIR}/core/link.cpp
|
||||
${MAMBA_SOURCE_DIR}/core/history.cpp
|
||||
${MAMBA_SOURCE_DIR}/core/match_spec.cpp
|
||||
${MAMBA_SOURCE_DIR}/core/menuinst.cpp
|
||||
${MAMBA_SOURCE_DIR}/core/url.cpp
|
||||
${MAMBA_SOURCE_DIR}/core/output.cpp
|
||||
${MAMBA_SOURCE_DIR}/core/package_handling.cpp
|
||||
|
@ -110,6 +111,7 @@ set(MAMBA_SOURCES
|
|||
${MAMBA_SOURCE_DIR}/core/thread_utils.cpp
|
||||
${MAMBA_SOURCE_DIR}/core/transaction.cpp
|
||||
${MAMBA_SOURCE_DIR}/core/util.cpp
|
||||
${MAMBA_SOURCE_DIR}/core/util_os.cpp
|
||||
${MAMBA_SOURCE_DIR}/core/validate.cpp
|
||||
${MAMBA_SOURCE_DIR}/core/version.cpp
|
||||
${MAMBA_SOURCE_DIR}/core/virtual_packages.cpp
|
||||
|
@ -146,6 +148,7 @@ set(MAMBA_HEADERS
|
|||
${MAMBA_INCLUDE_DIR}/mamba/core/link.hpp
|
||||
${MAMBA_INCLUDE_DIR}/mamba/core/mamba_fs.hpp
|
||||
${MAMBA_INCLUDE_DIR}/mamba/core/match_spec.hpp
|
||||
${MAMBA_INCLUDE_DIR}/mamba/core/menuinst.hpp
|
||||
${MAMBA_INCLUDE_DIR}/mamba/core/output.hpp
|
||||
${MAMBA_INCLUDE_DIR}/mamba/core/package_cache.hpp
|
||||
${MAMBA_INCLUDE_DIR}/mamba/core/package_handling.hpp
|
||||
|
@ -165,6 +168,7 @@ set(MAMBA_HEADERS
|
|||
${MAMBA_INCLUDE_DIR}/mamba/core/transaction_context.hpp
|
||||
${MAMBA_INCLUDE_DIR}/mamba/core/url.hpp
|
||||
${MAMBA_INCLUDE_DIR}/mamba/core/util.hpp
|
||||
${MAMBA_INCLUDE_DIR}/mamba/core/util_os.hpp
|
||||
${MAMBA_INCLUDE_DIR}/mamba/core/validate.hpp
|
||||
${MAMBA_INCLUDE_DIR}/mamba/core/version.hpp
|
||||
${MAMBA_INCLUDE_DIR}/mamba/core/virtual_packages.hpp
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
R"MAMBARAW(
|
||||
@REM Copyright (C) 2012 Anaconda, Inc
|
||||
@REM Copyright (C) 2021 QuantStack
|
||||
@REM SPDX-License-Identifier: BSD-3-Clause
|
||||
@CALL "%~dp0..\condabin\conda_hook.bat"
|
||||
conda.bat activate %*
|
||||
@CALL "%~dp0..\condabin\mamba_hook.bat"
|
||||
micromamba activate %*
|
||||
)MAMBARAW"
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
R"MAMBARAW(
|
||||
@REM Copyright (C) 2012 Anaconda, Inc
|
||||
@REM Copyright (C) 2021 QuantStack
|
||||
@REM SPDX-License-Identifier: BSD-3-Clause
|
||||
@REM The file name is conda_hook.bat rather than conda-hook.bat because conda will see
|
||||
@REM the latter as a 'conda hook' command.
|
||||
@REM This file is derived from conda_hook.bat
|
||||
|
||||
@IF DEFINED CONDA_SHLVL GOTO :EOF
|
||||
|
||||
|
|
|
@ -46,3 +46,23 @@ The workflow for that operation is:
|
|||
click link href "./more_concepts.html#linking"
|
||||
|
||||
See also: :ref:`package tarball<tarball>`
|
||||
|
||||
|
||||
The package installation process
|
||||
--------------------------------
|
||||
|
||||
When a package gets installed, several steps are executed:
|
||||
|
||||
- the package is downloaded and placed into the ``$ROOT_PREFIX/pkgs`` folder
|
||||
- the package is extracted
|
||||
- the package is "linked" from the `pkgs` folder into the final destination
|
||||
|
||||
When the package is linked to the final destination (for example, some newly created environment), most files are "hard"-linked. That means, there is no copy of the file created. This saves a considerable amount of disk-space when re-using the same package in multiple environments.
|
||||
|
||||
However, sometimes a text-replacement is necessary. This is the case when a file contains a reference to the prefix. On Unix systems a very long prefix is used during build (you might have seen something like ``_h_123123_placeholder_placeholder_placeholder...``). This very long prefix is stored in the metadata of the package. When a file contains this prefix, the file is copied into the target prefix and the prefix it is replaced. In text files, this is done via a simple string replacement, and in binary files the string is padded with ``\0`` bytes (to keep the same total length of the file). Binary replacement on Windows is usually not necessary because dynamic library loading on Windows follows different rules.
|
||||
|
||||
When installing a ``noarch: python`` package, the installation process will compile all ``.py``-files into Python bytecode (``.pyc``) files.
|
||||
|
||||
All installed files are later referenced in the ``$TARGET_PREFIX/conda-meta/mypkg-version-build.json`` file, to facilitate the removal (e.g. when upgrading or removing a package).
|
||||
|
||||
If the package contains a ``menu/*.json`` entry that follows the spec introduced by ``menuinst``, a start-menu entry is created on Windows. This is currently not implemented on Linux or macOS but that might change in the future.
|
||||
|
|
|
@ -132,6 +132,9 @@ namespace mamba
|
|||
bool always_copy = false;
|
||||
bool always_softlink = false;
|
||||
|
||||
// add start menu shortcuts on Windows (not implemented on Linux / macOS)
|
||||
bool shortcuts = true;
|
||||
|
||||
VerificationLevel safety_checks = VerificationLevel::kWarn;
|
||||
bool extra_safety_checks = false;
|
||||
bool verify_artifacts = false;
|
||||
|
|
|
@ -222,15 +222,6 @@ namespace mamba
|
|||
return p;
|
||||
}
|
||||
|
||||
inline bool is_admin()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return IsUserAnAdmin();
|
||||
#else
|
||||
return geteuid() == 0 || getegid() == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// inline fs::path expand_vars(const fs::path& path)
|
||||
// {
|
||||
// #ifndef _WIN32
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#include "mamba_fs.hpp"
|
||||
#include "transaction_context.hpp"
|
||||
|
||||
namespace mamba
|
||||
{
|
||||
void remove_menu_from_json(const fs::path& json_file, TransactionContext* context);
|
||||
void create_menu_from_json(const fs::path& json_file, TransactionContext* context);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2019, QuantStack and Mamba Contributors
|
||||
//
|
||||
// Distributed under the terms of the BSD 3-Clause License.
|
||||
//
|
||||
// The full license is in the file LICENSE, distributed with this software.
|
||||
|
||||
#ifndef MAMBA_CORE_UTIL_OS_HPP
|
||||
#define MAMBA_CORE_UTIL_OS_HPP
|
||||
|
||||
#include "mamba/core/fsutil.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace mamba
|
||||
{
|
||||
bool is_admin();
|
||||
fs::path get_self_exe_path();
|
||||
|
||||
#ifndef _WIN32
|
||||
std::string get_process_name_by_pid(const int pid);
|
||||
#else
|
||||
DWORD getppid();
|
||||
std::string get_process_name_by_pid(DWORD processId);
|
||||
#endif
|
||||
|
||||
void run_as_admin(const std::string& args);
|
||||
bool enable_long_paths_support(bool force);
|
||||
std::string windows_version();
|
||||
std::string macos_version();
|
||||
std::string linux_version();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -19,13 +19,11 @@
|
|||
|
||||
namespace mamba
|
||||
{
|
||||
std::string macos_version();
|
||||
std::vector<PackageInfo> get_virtual_packages();
|
||||
|
||||
namespace detail
|
||||
{
|
||||
std::string cuda_version();
|
||||
std::string macos_version();
|
||||
std::string get_arch();
|
||||
|
||||
PackageInfo make_virtual_package(const std::string& name,
|
||||
|
|
6
setup.py
6
setup.py
|
@ -83,7 +83,7 @@ else:
|
|||
|
||||
libraries = ["archive", "solv", "solvext", "reproc++", CURL_LIB, CRYPTO_LIB]
|
||||
if sys.platform == "win32":
|
||||
libraries += ["advapi32"]
|
||||
libraries += ["advapi32", "ole32", "shell32"]
|
||||
|
||||
ext_modules = [
|
||||
Extension(
|
||||
|
@ -96,6 +96,7 @@ ext_modules = [
|
|||
"src/core/fetch.cpp",
|
||||
"src/core/history.cpp",
|
||||
"src/core/match_spec.cpp",
|
||||
"src/core/menuinst.cpp",
|
||||
"src/core/output.cpp",
|
||||
"src/core/package_handling.cpp",
|
||||
"src/core/package_cache.cpp",
|
||||
|
@ -106,14 +107,15 @@ ext_modules = [
|
|||
"src/core/pool.cpp",
|
||||
"src/core/query.cpp",
|
||||
"src/core/repo.cpp",
|
||||
"src/core/solver.cpp",
|
||||
"src/core/shell_init.cpp",
|
||||
"src/core/solver.cpp",
|
||||
"src/core/subdirdata.cpp",
|
||||
"src/core/thread_utils.cpp",
|
||||
"src/core/transaction.cpp",
|
||||
"src/core/transaction_context.cpp",
|
||||
"src/core/url.cpp",
|
||||
"src/core/util.cpp",
|
||||
"src/core/util_os.cpp",
|
||||
"src/core/validate.cpp",
|
||||
"src/core/version.cpp",
|
||||
"src/core/link.cpp",
|
||||
|
|
|
@ -679,6 +679,14 @@ namespace mamba
|
|||
!WARNING: Using this option can result in corruption of long-lived
|
||||
environments due to broken links (deleted cache).)")));
|
||||
|
||||
insert(
|
||||
Configurable("shortcuts", &ctx.shortcuts)
|
||||
.group("Link & Install")
|
||||
.set_rc_configurable()
|
||||
.set_env_var_name()
|
||||
.description(
|
||||
"Install start-menu shortcuts on Windows (not implemented on Linux / macOS)"));
|
||||
|
||||
insert(Configurable("safety_checks", &ctx.safety_checks)
|
||||
.group("Link & Install")
|
||||
.set_rc_configurable()
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "mamba/core/environment.hpp"
|
||||
#include "mamba/core/mamba_fs.hpp"
|
||||
#include "mamba/core/shell_init.hpp"
|
||||
#include "mamba/core/util_os.hpp"
|
||||
|
||||
|
||||
namespace mamba
|
||||
|
@ -131,6 +132,15 @@ namespace mamba
|
|||
{
|
||||
std::cout << activator->deactivate();
|
||||
}
|
||||
#ifdef _WIN32
|
||||
else if (action == "enable-long-paths-support")
|
||||
{
|
||||
if (!enable_long_paths_support(true))
|
||||
{
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Need an action (activate, deactivate or hook)");
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <reproc++/run.hpp>
|
||||
|
||||
#include "mamba/core/environment.hpp"
|
||||
#include "mamba/core/menuinst.hpp"
|
||||
#include "mamba/core/link.hpp"
|
||||
#include "mamba/core/match_spec.hpp"
|
||||
#include "mamba/core/output.hpp"
|
||||
|
@ -28,6 +29,8 @@
|
|||
|
||||
namespace mamba
|
||||
{
|
||||
static const std::regex MENU_PATH_REGEX("^menu[/\\\\].*\\.json$", std::regex_constants::icase);
|
||||
|
||||
void python_entry_point_template(std::ostream& out, const python_entry_point_parsed& p)
|
||||
{
|
||||
auto import_name = split(p.func, ".")[0];
|
||||
|
@ -701,6 +704,12 @@ namespace mamba
|
|||
|
||||
for (auto& path : json_record["paths_data"]["paths"])
|
||||
{
|
||||
std::string fpath = path["_path"];
|
||||
if (std::regex_match(fpath, MENU_PATH_REGEX))
|
||||
{
|
||||
remove_menu_from_json(m_context->target_prefix / fpath, m_context);
|
||||
}
|
||||
|
||||
unlink_path(path);
|
||||
}
|
||||
|
||||
|
@ -1094,7 +1103,6 @@ namespace mamba
|
|||
|
||||
std::vector<std::string> files_record;
|
||||
|
||||
// for (auto& path : paths_json["paths"])
|
||||
nlohmann::json paths_json = nlohmann::json::object();
|
||||
paths_json["paths"] = nlohmann::json::array();
|
||||
paths_json["paths_version"] = 1;
|
||||
|
@ -1211,6 +1219,19 @@ namespace mamba
|
|||
}
|
||||
}
|
||||
|
||||
// Create all start menu shortcuts if prefix name doesn't start with underscore
|
||||
if (on_win && Context::instance().shortcuts
|
||||
&& m_context->target_prefix.filename().string()[0] != '_')
|
||||
{
|
||||
for (auto& path : paths_data)
|
||||
{
|
||||
if (std::regex_match(path.path, MENU_PATH_REGEX))
|
||||
{
|
||||
create_menu_from_json(m_context->target_prefix / path.path, m_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
run_script(m_context->target_prefix, m_pkg_info, "post-link", "", true);
|
||||
|
||||
fs::path prefix_meta = m_context->target_prefix / "conda-meta";
|
||||
|
|
|
@ -0,0 +1,462 @@
|
|||
#include <string>
|
||||
#include <regex>
|
||||
|
||||
#include "mamba/core/context.hpp"
|
||||
#include "mamba/core/transaction_context.hpp"
|
||||
#include "mamba/core/mamba_fs.hpp"
|
||||
#include "mamba/core/util.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
namespace mamba
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
std::string get_formatted_env_name(const fs::path& target_prefix)
|
||||
{
|
||||
std::string name = env_name(target_prefix);
|
||||
if (name.find_first_of("\\/") != std::string::npos)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace win
|
||||
{
|
||||
/*
|
||||
* Create a shortcut on a Windows Machine (using COM)
|
||||
* Place the shortcut in `programs` directory to make it appear in the
|
||||
* startmenu.
|
||||
*
|
||||
* Args:
|
||||
* path: Path to the executable
|
||||
* description: string that is displayed
|
||||
* filename: Link destination filename
|
||||
* arguments: args to the executable (optional)
|
||||
* work_dir: workdir for the executable
|
||||
* icon_path: path to an .ico file
|
||||
* icon_index: index for icon
|
||||
*/
|
||||
void create_shortcut(const fs::path& path,
|
||||
const std::string& description,
|
||||
const fs::path& filename,
|
||||
const std::string& arguments,
|
||||
const fs::path& work_dir,
|
||||
const fs::path& icon_path,
|
||||
int icon_index)
|
||||
{
|
||||
IShellLink* pShellLink = nullptr;
|
||||
IPersistFile* pPersistFile = nullptr;
|
||||
|
||||
HRESULT hres;
|
||||
LOG_DEBUG << "Creating shortcut with "
|
||||
<< "\n Path: " << path << "\n Description: " << description
|
||||
<< "\n Filename: " << filename << "\n Arguments: " << arguments
|
||||
<< "\n Workdir: " << work_dir << "\n Icon Path: " << icon_path
|
||||
<< "\n Icon Index: " << icon_index;
|
||||
try
|
||||
{
|
||||
hres = CoInitialize(nullptr);
|
||||
if (FAILED(hres))
|
||||
{
|
||||
throw std::runtime_error("Could not initialize COM");
|
||||
}
|
||||
hres = CoCreateInstance(CLSID_ShellLink,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IShellLink,
|
||||
(void**) &pShellLink);
|
||||
if (FAILED(hres))
|
||||
{
|
||||
throw std::runtime_error("CoCreateInstance failed.");
|
||||
}
|
||||
|
||||
hres = pShellLink->QueryInterface(IID_IPersistFile, (void**) &pPersistFile);
|
||||
if (FAILED(hres))
|
||||
{
|
||||
throw std::runtime_error("QueryInterface(IPersistFile) error 0x"
|
||||
+ std::to_string(hres));
|
||||
}
|
||||
|
||||
hres = pShellLink->SetPath(path.c_str());
|
||||
if (FAILED(hres))
|
||||
{
|
||||
throw std::runtime_error("SetPath() failed, error 0x" + std::to_string(hres));
|
||||
}
|
||||
|
||||
hres = pShellLink->SetDescription(description.c_str());
|
||||
if (FAILED(hres))
|
||||
{
|
||||
throw std::runtime_error("SetDescription() failed, error 0x"
|
||||
+ std::to_string(hres));
|
||||
}
|
||||
|
||||
if (!arguments.empty())
|
||||
{
|
||||
hres = pShellLink->SetArguments(arguments.c_str());
|
||||
if (FAILED(hres))
|
||||
{
|
||||
throw std::runtime_error("SetArguments() error 0x" + std::to_string(hres));
|
||||
}
|
||||
}
|
||||
|
||||
if (!icon_path.empty())
|
||||
{
|
||||
hres = pShellLink->SetIconLocation(icon_path.c_str(), icon_index);
|
||||
if (FAILED(hres))
|
||||
{
|
||||
throw std::runtime_error("SetIconLocation() error 0x"
|
||||
+ std::to_string(hres));
|
||||
}
|
||||
}
|
||||
|
||||
if (!work_dir.empty())
|
||||
{
|
||||
hres = pShellLink->SetWorkingDirectory(work_dir.c_str());
|
||||
if (FAILED(hres))
|
||||
{
|
||||
throw std::runtime_error("SetWorkingDirectory() error 0x"
|
||||
+ std::to_string(hres));
|
||||
}
|
||||
}
|
||||
|
||||
hres = pPersistFile->Save(filename.wstring().c_str(), true);
|
||||
if (FAILED(hres))
|
||||
{
|
||||
throw std::runtime_error(concat(
|
||||
"Failed to create shortcut: ", filename.string(), std::to_string(hres)));
|
||||
}
|
||||
}
|
||||
catch (const std::runtime_error& e)
|
||||
{
|
||||
if (pPersistFile)
|
||||
{
|
||||
pPersistFile->Release();
|
||||
}
|
||||
if (pShellLink)
|
||||
{
|
||||
pShellLink->Release();
|
||||
}
|
||||
CoUninitialize();
|
||||
|
||||
LOG_ERROR << e.what();
|
||||
}
|
||||
|
||||
pPersistFile->Release();
|
||||
pShellLink->Release();
|
||||
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
const std::map<std::string, KNOWNFOLDERID> knownfolders = {
|
||||
{ "programs", FOLDERID_Programs },
|
||||
{ "profile", FOLDERID_Profile },
|
||||
{ "documents", FOLDERID_Documents },
|
||||
};
|
||||
|
||||
fs::path get_folder(const std::string& id)
|
||||
{
|
||||
wchar_t* localAppData;
|
||||
HRESULT hres;
|
||||
|
||||
hres = SHGetKnownFolderPath(
|
||||
knownfolders.at(id), KF_FLAG_DONT_VERIFY, nullptr, &localAppData);
|
||||
|
||||
if (FAILED(hres))
|
||||
{
|
||||
throw std::runtime_error("Could not retrieve known folder");
|
||||
}
|
||||
|
||||
std::wstring tmp(localAppData);
|
||||
fs::path res(tmp);
|
||||
CoTaskMemFree(localAppData);
|
||||
return res;
|
||||
}
|
||||
|
||||
void remove_shortcut(const fs::path& filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (fs::exists(filename))
|
||||
{
|
||||
fs::remove(filename);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_ERROR << "Could not remove shortcut";
|
||||
}
|
||||
if (fs::is_empty(filename.parent_path()))
|
||||
{
|
||||
fs::remove(filename.parent_path());
|
||||
}
|
||||
}
|
||||
} // namespace win
|
||||
#endif
|
||||
|
||||
|
||||
void replace_variables(std::string& text, TransactionContext* transaction_context)
|
||||
{
|
||||
auto& ctx = mamba::Context::instance();
|
||||
fs::path root_prefix = ctx.root_prefix;
|
||||
|
||||
fs::path target_prefix;
|
||||
std::string py_ver;
|
||||
if (transaction_context)
|
||||
{
|
||||
target_prefix = transaction_context->target_prefix;
|
||||
py_ver = transaction_context->python_version;
|
||||
}
|
||||
|
||||
std::string distribution_name = root_prefix.filename();
|
||||
if (distribution_name.size() > 1)
|
||||
{
|
||||
distribution_name[0] = std::toupper(distribution_name[0]);
|
||||
}
|
||||
|
||||
auto to_forward_slash = [](const fs::path& p) {
|
||||
std::string ps = p.string();
|
||||
replace_all(ps, "\\", "/");
|
||||
return ps;
|
||||
};
|
||||
|
||||
auto platform_split = split(ctx.platform, "-");
|
||||
std::string platform_bitness;
|
||||
if (platform_split.size() >= 2)
|
||||
{
|
||||
platform_bitness = concat("(", platform_split.back(), "-bit)");
|
||||
}
|
||||
|
||||
if (py_ver.size())
|
||||
{
|
||||
py_ver = split(py_ver, ".")[0];
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> vars = {
|
||||
{ "${PREFIX}", to_forward_slash(target_prefix) },
|
||||
{ "${ROOT_PREFIX}", to_forward_slash(root_prefix) },
|
||||
{ "${PY_VER}", py_ver },
|
||||
{ "${MENU_DIR}", to_forward_slash(target_prefix / "Menu") },
|
||||
{ "${DISTRIBUTION_NAME}", distribution_name },
|
||||
{ "${ENV_NAME}", detail::get_formatted_env_name(target_prefix) },
|
||||
{ "${PLATFORM}", platform_bitness },
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
vars["${PERSONALDIR}"] = to_forward_slash(win::get_folder("documents"));
|
||||
vars["${USERPROFILE}"] = to_forward_slash(win::get_folder("profile"));
|
||||
#endif
|
||||
|
||||
for (auto& [key, val] : vars)
|
||||
{
|
||||
replace_all(text, key, val);
|
||||
}
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
void create_remove_shortcut_impl(const fs::path& json_file,
|
||||
TransactionContext* transaction_context,
|
||||
bool remove)
|
||||
{
|
||||
std::string json_content = mamba::read_contents(json_file);
|
||||
replace_variables(json_content, transaction_context);
|
||||
auto j = nlohmann::json::parse(json_content);
|
||||
|
||||
std::string menu_name = j.value("menu_name", "Mamba Shortcuts");
|
||||
|
||||
std::string name_suffix;
|
||||
std::string e_name = detail::get_formatted_env_name(transaction_context->target_prefix);
|
||||
|
||||
if (e_name.size())
|
||||
{
|
||||
name_suffix = concat(" (", e_name, ")");
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
// {
|
||||
// "menu_name": "Miniforge${PY_VER}",
|
||||
// "menu_items":
|
||||
// [
|
||||
// {
|
||||
// "name": "Miniforge Prompt",
|
||||
// "system": "%windir%\\system32\\cmd.exe",
|
||||
// "scriptarguments": ["/K", "${ROOT_PREFIX}\\Scripts\\activate.bat",
|
||||
// "${PREFIX}"], "icon": "${MENU_DIR}/console_shortcut.ico"
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
|
||||
auto& ctx = mamba::Context::instance();
|
||||
fs::path root_prefix = ctx.root_prefix;
|
||||
fs::path target_prefix = ctx.target_prefix;
|
||||
|
||||
// using legacy stuff here
|
||||
fs::path root_py = root_prefix / "python.exe";
|
||||
fs::path root_pyw = root_prefix / "pythonw.exe";
|
||||
fs::path env_py = target_prefix / "python.exe";
|
||||
fs::path env_pyw = target_prefix / "pythonw.exe";
|
||||
std::vector<std::string> cwp_py_args({ root_prefix / "cwp.py", target_prefix, env_py });
|
||||
std::vector<std::string> cwp_pyw_args(
|
||||
{ root_prefix / "cwp.py", target_prefix, env_pyw });
|
||||
|
||||
fs::path target_dir = win::get_folder("programs") / menu_name;
|
||||
if (!fs::exists(target_dir))
|
||||
{
|
||||
fs::create_directories(target_dir);
|
||||
}
|
||||
|
||||
auto extend_script_args = [](const auto& item, auto& arguments) {
|
||||
// this is pretty bad ...
|
||||
if (item.contains("scriptargument"))
|
||||
{
|
||||
arguments.push_back(item["scriptargument"]);
|
||||
}
|
||||
|
||||
if (item.contains("scriptarguments"))
|
||||
{
|
||||
std::vector<std::string> tmp_args = item["scriptarguments"];
|
||||
for (auto& arg : tmp_args)
|
||||
{
|
||||
arguments.push_back(arg);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (auto& item : j["menu_items"])
|
||||
{
|
||||
std::string name = item["name"];
|
||||
std::string full_name = concat(name, name_suffix);
|
||||
|
||||
std::vector<std::string> arguments;
|
||||
fs::path script;
|
||||
if (item.contains("pywscript"))
|
||||
{
|
||||
script = root_pyw;
|
||||
arguments = cwp_pyw_args;
|
||||
auto tmp = split(item["pywscript"], " ");
|
||||
std::copy(tmp.begin(), tmp.end(), back_inserter(arguments));
|
||||
}
|
||||
else if (item.contains("pyscript"))
|
||||
{
|
||||
script = root_py;
|
||||
arguments = cwp_py_args;
|
||||
auto tmp = split(item["pyscript"], " ");
|
||||
std::copy(tmp.begin(), tmp.end(), back_inserter(arguments));
|
||||
}
|
||||
else if (item.contains("webbrowser"))
|
||||
{
|
||||
script = root_pyw;
|
||||
arguments = { "-m", "webbrowser", "-t", item["webbrowser"] };
|
||||
}
|
||||
else if (item.contains("script"))
|
||||
{
|
||||
script = root_py;
|
||||
arguments = { root_prefix / "cwp.py", target_prefix };
|
||||
auto tmp = split(item["script"], " ");
|
||||
std::copy(tmp.begin(), tmp.end(), back_inserter(arguments));
|
||||
extend_script_args(item, arguments);
|
||||
}
|
||||
else if (item.contains("system"))
|
||||
{
|
||||
auto tmp = split(item["system"], " ");
|
||||
script = tmp[0];
|
||||
if (tmp.size() > 1)
|
||||
{
|
||||
std::copy(tmp.begin() + 1, tmp.end(), back_inserter(arguments));
|
||||
}
|
||||
extend_script_args(item, arguments);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR << "Unknown shortcut type found in " << json_file;
|
||||
throw std::runtime_error("Unknown shortcut type.");
|
||||
}
|
||||
|
||||
fs::path dst = target_dir / (full_name + ".lnk");
|
||||
fs::path workdir = item.value("workdir", "");
|
||||
fs::path iconpath = item.value("icon", "");
|
||||
if (remove == false)
|
||||
{
|
||||
std::string argstring;
|
||||
std::string lscript = to_lower(script.string());
|
||||
|
||||
for (auto& arg : arguments)
|
||||
{
|
||||
if (arg.size() >= 1 && arg[0] != '/')
|
||||
{
|
||||
mamba::replace_all(arg, "/", "\\");
|
||||
}
|
||||
}
|
||||
|
||||
if (lscript.find("cmd.exe") != std::string::npos
|
||||
|| lscript.find("%comspec%") != std::string::npos)
|
||||
{
|
||||
if (arguments.size() > 1)
|
||||
{
|
||||
if (to_upper(arguments[0]) == "/K" || to_upper(arguments[0]) == "/C")
|
||||
{
|
||||
}
|
||||
}
|
||||
argstring = quote_for_shell(arguments, "cmdexe");
|
||||
}
|
||||
else
|
||||
{
|
||||
argstring = quote_for_shell(arguments, "");
|
||||
}
|
||||
|
||||
if (workdir.string().size())
|
||||
{
|
||||
if (!(fs::exists(workdir) && fs::is_directory(workdir)))
|
||||
{
|
||||
fs::create_directories(workdir);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
workdir = "%HOMEPATH%";
|
||||
}
|
||||
|
||||
mamba::win::create_shortcut(
|
||||
script, full_name, dst, argstring, workdir, iconpath, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
mamba::win::remove_shortcut(dst);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void remove_menu_from_json(const fs::path& json_file, TransactionContext* context)
|
||||
{
|
||||
try
|
||||
{
|
||||
detail::create_remove_shortcut_impl(json_file, context, true);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR << "Removal of shortcut was not successful " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
void create_menu_from_json(const fs::path& json_file, TransactionContext* context)
|
||||
{
|
||||
try
|
||||
{
|
||||
detail::create_remove_shortcut_impl(json_file, context, false);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LOG_ERROR << "Creation of shortcut was not successful " << e.what();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,28 +9,16 @@
|
|||
#include "mamba/core/shell_init.hpp"
|
||||
#include "mamba/core/output.hpp"
|
||||
#include "mamba/core/util.hpp"
|
||||
#include "mamba/core/util_os.hpp"
|
||||
#include "mamba/core/activation.hpp"
|
||||
#include "mamba/core/environment.hpp"
|
||||
#include "mamba/core/virtual_packages.hpp"
|
||||
|
||||
#include "thirdparty/termcolor.hpp"
|
||||
|
||||
#include <reproc++/run.hpp>
|
||||
|
||||
#ifndef _WIN32
|
||||
#if defined(__APPLE__)
|
||||
#include <mach-o/dyld.h>
|
||||
#include <libProc.h>
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#if defined(__linux__)
|
||||
#include <linux/limits.h>
|
||||
#else
|
||||
#include <limits.h>
|
||||
#endif
|
||||
#else
|
||||
#include <windows.h>
|
||||
#include <intrin.h>
|
||||
#include <tlhelp32.h>
|
||||
#ifdef _WIN32
|
||||
#include "thirdparty/WinReg.hpp"
|
||||
#endif
|
||||
|
||||
|
@ -45,6 +33,9 @@ namespace mamba
|
|||
constexpr const char mamba_bat[] =
|
||||
#include "../data/micromamba.bat"
|
||||
;
|
||||
constexpr const char activate_bat[] =
|
||||
#include "../data/activate.bat"
|
||||
;
|
||||
constexpr const char _mamba_activate_bat[] =
|
||||
#include "../data/_mamba_activate.bat"
|
||||
;
|
||||
|
@ -70,94 +61,6 @@ namespace mamba
|
|||
"#endregion(?:\n|\r\n)?");
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD getppid()
|
||||
{
|
||||
HANDLE hSnapshot;
|
||||
PROCESSENTRY32 pe32;
|
||||
DWORD ppid = 0, pid = GetCurrentProcessId();
|
||||
|
||||
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
__try
|
||||
{
|
||||
if (hSnapshot == INVALID_HANDLE_VALUE)
|
||||
__leave;
|
||||
|
||||
ZeroMemory(&pe32, sizeof(pe32));
|
||||
pe32.dwSize = sizeof(pe32);
|
||||
if (!Process32First(hSnapshot, &pe32))
|
||||
__leave;
|
||||
|
||||
do
|
||||
{
|
||||
if (pe32.th32ProcessID == pid)
|
||||
{
|
||||
ppid = pe32.th32ParentProcessID;
|
||||
break;
|
||||
}
|
||||
} while (Process32Next(hSnapshot, &pe32));
|
||||
}
|
||||
__finally
|
||||
{
|
||||
if (hSnapshot != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(hSnapshot);
|
||||
}
|
||||
return ppid;
|
||||
}
|
||||
|
||||
std::string get_process_name_by_pid(DWORD processId)
|
||||
{
|
||||
std::string ret;
|
||||
HANDLE handle = OpenProcess(
|
||||
PROCESS_QUERY_LIMITED_INFORMATION,
|
||||
FALSE,
|
||||
processId /* This is the PID, you can find one from windows task manager */
|
||||
);
|
||||
if (handle)
|
||||
{
|
||||
DWORD buffSize = 1024;
|
||||
CHAR buffer[1024];
|
||||
if (QueryFullProcessImageNameA(handle, 0, buffer, &buffSize))
|
||||
{
|
||||
ret = buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error GetModuleBaseNameA : %lu", GetLastError());
|
||||
}
|
||||
CloseHandle(handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error OpenProcess : %lu", GetLastError());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
std::string get_process_name_by_pid(const int pid)
|
||||
{
|
||||
std::string ret;
|
||||
char name[1024];
|
||||
proc_name(pid, name, sizeof(name));
|
||||
ret = name;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
std::string get_process_name_by_pid(const int pid)
|
||||
{
|
||||
std::ifstream f(concat("/proc/", std::to_string(pid), "/status"));
|
||||
if (f.good())
|
||||
{
|
||||
std::string l;
|
||||
std::getline(f, l);
|
||||
return l;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string guess_shell()
|
||||
{
|
||||
std::string parent_process_name = get_process_name_by_pid(getppid());
|
||||
|
@ -198,7 +101,7 @@ namespace mamba
|
|||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << e.what() << '\n';
|
||||
LOG_INFO << "No AutoRun key detected.";
|
||||
}
|
||||
// std::wstring hook_path = '"%s"' % join(conda_prefix, 'condabin', 'conda_hook.bat')
|
||||
std::wstring hook_string = std::wstring(L"\"")
|
||||
|
@ -241,54 +144,14 @@ namespace mamba
|
|||
std::cout << termcolor::reset << std::endl;
|
||||
key.SetStringValue(L"AutoRun", new_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Heavily inspired by https://github.com/gpakosz/whereami/
|
||||
// check their source to add support for other OS
|
||||
fs::path get_self_exe_path()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD size;
|
||||
std::wstring buffer(MAX_PATH, '\0');
|
||||
size = GetModuleFileNameW(NULL, (wchar_t*) buffer.c_str(), (DWORD) buffer.size());
|
||||
if (size == 0)
|
||||
{
|
||||
throw std::runtime_error("Could find location of the micromamba executable!");
|
||||
}
|
||||
else if (size == buffer.size())
|
||||
{
|
||||
DWORD new_size = size;
|
||||
do
|
||||
else
|
||||
{
|
||||
new_size *= 2;
|
||||
buffer.reserve(new_size);
|
||||
size = GetModuleFileNameW(NULL, (wchar_t*) buffer.c_str(), (DWORD) buffer.size());
|
||||
} while (new_size == size);
|
||||
}
|
||||
buffer.resize(buffer.find(L'\0'));
|
||||
return fs::absolute(buffer);
|
||||
#elif defined(__APPLE__)
|
||||
uint32_t size = PATH_MAX;
|
||||
std::vector<char> buffer(size);
|
||||
if (_NSGetExecutablePath(buffer.data(), &size) == -1)
|
||||
{
|
||||
buffer.reserve(size);
|
||||
if (!_NSGetExecutablePath(buffer.data(), &size))
|
||||
{
|
||||
throw std::runtime_error("Couldn't find location the micromamba executable!");
|
||||
std::cout << termcolor::green << "cmd.exe already initialized." << termcolor::reset
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
return fs::absolute(buffer.data());
|
||||
#else
|
||||
#if defined(__sun)
|
||||
return fs::read_symlink("/proc/self/path/a.out");
|
||||
#else
|
||||
return fs::read_symlink("/proc/self/exe");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// this function calls cygpath to convert win path to unix
|
||||
std::string native_path_to_unix(const std::string& path, bool is_a_path_env)
|
||||
|
@ -492,7 +355,10 @@ namespace mamba
|
|||
void init_root_prefix_cmdexe(const fs::path& root_prefix)
|
||||
{
|
||||
fs::path exe = get_self_exe_path();
|
||||
|
||||
fs::create_directories(root_prefix / "condabin");
|
||||
fs::create_directories(root_prefix / "Scripts");
|
||||
|
||||
std::ofstream mamba_bat_f(root_prefix / "condabin" / "micromamba.bat");
|
||||
std::string mamba_bat_contents(mamba_bat);
|
||||
replace_all(mamba_bat_contents,
|
||||
|
@ -506,6 +372,22 @@ namespace mamba
|
|||
std::ofstream _mamba_activate_bat_f(root_prefix / "condabin" / "_mamba_activate.bat");
|
||||
_mamba_activate_bat_f << _mamba_activate_bat;
|
||||
|
||||
|
||||
std::string activate_bat_contents(activate_bat);
|
||||
replace_all(activate_bat_contents,
|
||||
std::string("__MAMBA_INSERT_ROOT_PREFIX__"),
|
||||
std::string("@SET \"MAMBA_ROOT_PREFIX=" + root_prefix.string() + "\""));
|
||||
replace_all(activate_bat_contents,
|
||||
std::string("__MAMBA_INSERT_MAMBA_EXE__"),
|
||||
std::string("@SET \"MAMBA_EXE=" + exe.string() + "\""));
|
||||
|
||||
|
||||
std::ofstream condabin_activate_bat_f(root_prefix / "condabin" / "activate.bat");
|
||||
condabin_activate_bat_f << activate_bat_contents;
|
||||
|
||||
std::ofstream scripts_activate_bat_f(root_prefix / "Scripts" / "activate.bat");
|
||||
scripts_activate_bat_f << activate_bat_contents;
|
||||
|
||||
std::string hook_content = mamba_hook_bat;
|
||||
replace_all(hook_content,
|
||||
std::string("__MAMBA_INSERT_MAMBA_EXE__"),
|
||||
|
@ -707,5 +589,8 @@ namespace mamba
|
|||
{
|
||||
throw std::runtime_error("Support for other shells not yet implemented.");
|
||||
}
|
||||
#ifdef _WIN32
|
||||
enable_long_paths_support(false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,393 @@
|
|||
#include <regex>
|
||||
|
||||
#include "thirdparty/termcolor.hpp"
|
||||
#include "mamba/core/environment.hpp"
|
||||
#include "mamba/core/util_os.hpp"
|
||||
|
||||
#include <reproc++/run.hpp>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#if defined(__APPLE__)
|
||||
#include <mach-o/dyld.h>
|
||||
#include <libProc.h>
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#if defined(__linux__)
|
||||
#include <linux/limits.h>
|
||||
#else
|
||||
#include <limits.h>
|
||||
#endif
|
||||
#else
|
||||
#include <windows.h>
|
||||
#include <intrin.h>
|
||||
#include <tlhelp32.h>
|
||||
#include "thirdparty/WinReg.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
namespace mamba
|
||||
{
|
||||
// Heavily inspired by https://github.com/gpakosz/whereami/
|
||||
// check their source to add support for other OS
|
||||
fs::path get_self_exe_path()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD size;
|
||||
std::wstring buffer(MAX_PATH, '\0');
|
||||
size = GetModuleFileNameW(NULL, (wchar_t*) buffer.c_str(), (DWORD) buffer.size());
|
||||
if (size == 0)
|
||||
{
|
||||
throw std::runtime_error("Could find location of the micromamba executable!");
|
||||
}
|
||||
else if (size == buffer.size())
|
||||
{
|
||||
DWORD new_size = size;
|
||||
do
|
||||
{
|
||||
new_size *= 2;
|
||||
buffer.reserve(new_size);
|
||||
size = GetModuleFileNameW(NULL, (wchar_t*) buffer.c_str(), (DWORD) buffer.size());
|
||||
} while (new_size == size);
|
||||
}
|
||||
buffer.resize(buffer.find(L'\0'));
|
||||
return fs::absolute(buffer);
|
||||
#elif defined(__APPLE__)
|
||||
uint32_t size = PATH_MAX;
|
||||
std::vector<char> buffer(size);
|
||||
if (_NSGetExecutablePath(buffer.data(), &size) == -1)
|
||||
{
|
||||
buffer.reserve(size);
|
||||
if (!_NSGetExecutablePath(buffer.data(), &size))
|
||||
{
|
||||
throw std::runtime_error("Couldn't find location the micromamba executable!");
|
||||
}
|
||||
}
|
||||
return fs::absolute(buffer.data());
|
||||
#else
|
||||
#if defined(__sun)
|
||||
return fs::read_symlink("/proc/self/path/a.out");
|
||||
#else
|
||||
return fs::read_symlink("/proc/self/exe");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_admin()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return IsUserAnAdmin();
|
||||
#else
|
||||
return geteuid() == 0 || getegid() == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
bool run_as_admin(const std::string& exe, const std::string& args)
|
||||
{
|
||||
SHELLEXECUTEINFO excinfo;
|
||||
|
||||
// I've tried for quite some time and could not get this to work at all
|
||||
// excinfo.lpFile="micromamba.exe";
|
||||
// excinfo.lpParameters="shell enable-long-paths-support";
|
||||
|
||||
ZeroMemory(&excinfo, sizeof(SHELLEXECUTEINFO));
|
||||
excinfo.cbSize = sizeof(SHELLEXECUTEINFO);
|
||||
excinfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NOASYNC;
|
||||
excinfo.lpVerb = "runas";
|
||||
excinfo.lpFile = exe.c_str();
|
||||
excinfo.lpParameters = args.c_str();
|
||||
excinfo.lpDirectory = NULL;
|
||||
excinfo.nShow = SW_HIDE;
|
||||
|
||||
if (!ShellExecuteEx(&excinfo))
|
||||
{
|
||||
LOG_WARNING << "Could not start process as admin.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for the process to complete, then get its exit code
|
||||
DWORD ExitCode = 0;
|
||||
WaitForSingleObject(excinfo.hProcess, INFINITE);
|
||||
GetExitCodeProcess(excinfo.hProcess, &ExitCode);
|
||||
CloseHandle(excinfo.hProcess);
|
||||
if (ExitCode != 0)
|
||||
{
|
||||
LOG_WARNING << "Process exited with code != 0.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool enable_long_paths_support(bool force)
|
||||
{
|
||||
// Needs to be set system-wide & can only be run as admin ...
|
||||
std::string win_ver = windows_version();
|
||||
auto splitted = split(win_ver, ".");
|
||||
if (!(splitted.size() >= 3 && std::stoull(splitted[0]) >= 10
|
||||
&& std::stoull(splitted[2]) >= 14352))
|
||||
{
|
||||
LOG_WARNING
|
||||
<< "Not setting long path registry key; Windows version must be at least 10 "
|
||||
"with the fall 2016 \"Anniversary update\" or newer.";
|
||||
return false;
|
||||
}
|
||||
|
||||
winreg::RegKey key;
|
||||
key.Open(
|
||||
HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\FileSystem", KEY_QUERY_VALUE);
|
||||
DWORD prev_value;
|
||||
try
|
||||
{
|
||||
prev_value = key.GetDwordValue(L"LongPathsEnabled");
|
||||
}
|
||||
catch (const winreg::RegException& e)
|
||||
{
|
||||
LOG_INFO << "No LongPathsEnabled key detected.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prev_value == 1)
|
||||
{
|
||||
std::cout << termcolor::green << "Windows long-path support already enabled."
|
||||
<< termcolor::reset << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (force || is_admin())
|
||||
{
|
||||
winreg::RegKey key_for_write(HKEY_LOCAL_MACHINE,
|
||||
L"SYSTEM\\CurrentControlSet\\Control\\FileSystem");
|
||||
key_for_write.SetDwordValue(L"LongPathsEnabled", 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Console::prompt("Enter admin mode to enable long paths support?", 'n'))
|
||||
{
|
||||
if (!run_as_admin(
|
||||
"reg.exe",
|
||||
"ADD HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\FileSystem /v LongPathsEnabled /d 1 /t REG_DWORD /f"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_WARNING << "Did not enable long paths support.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
prev_value = key.GetDwordValue(L"LongPathsEnabled");
|
||||
if (prev_value == 1)
|
||||
{
|
||||
std::cout << termcolor::green << "Windows long-path support enabled."
|
||||
<< termcolor::reset << std::endl;
|
||||
return true;
|
||||
}
|
||||
LOG_WARNING << "Changing registry value did not succeed.";
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string windows_version()
|
||||
{
|
||||
if (!env::get("CONDA_OVERRIDE_WIN").empty())
|
||||
{
|
||||
return env::get("CONDA_OVERRIDE_WIN");
|
||||
}
|
||||
|
||||
if (!on_win)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string out, err;
|
||||
std::vector<std::string> args = { env::get("COMSPEC"), "/c", "ver" };
|
||||
auto [status, ec] = reproc::run(
|
||||
args, reproc::options{}, reproc::sink::string(out), reproc::sink::string(err));
|
||||
|
||||
if (ec)
|
||||
{
|
||||
LOG_WARNING << "Could not find Windows version by calling 'ver'\n"
|
||||
<< "Please file a bug report.\nError: " << ec.message();
|
||||
return "";
|
||||
}
|
||||
std::string xout(strip(out));
|
||||
|
||||
// from python
|
||||
std::regex ver_output_regex("(?:([\\w ]+) ([\\w.]+) .*\\[.* ([\\d.]+)\\])");
|
||||
|
||||
std::smatch rmatch;
|
||||
|
||||
std::string full_version, norm_version;
|
||||
if (std::regex_match(xout, rmatch, ver_output_regex))
|
||||
{
|
||||
full_version = rmatch[3];
|
||||
auto version_els = split(full_version, ".");
|
||||
norm_version = concat(version_els[0], ".", version_els[1], ".", version_els[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
norm_version = "0.0.0";
|
||||
}
|
||||
return norm_version;
|
||||
}
|
||||
|
||||
std::string macos_version()
|
||||
{
|
||||
if (!env::get("CONDA_OVERRIDE_OSX").empty())
|
||||
{
|
||||
return env::get("CONDA_OVERRIDE_OSX");
|
||||
}
|
||||
|
||||
if (!on_mac)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string out, err;
|
||||
// Note: we could also inspect /System/Library/CoreServices/SystemVersion.plist which is
|
||||
// an XML file
|
||||
// that contains the same information. However, then we'd either need an xml
|
||||
// parser or some other crude method to read the data
|
||||
std::vector<std::string> args = { "sw_vers", "-productVersion" };
|
||||
auto [status, ec] = reproc::run(
|
||||
args, reproc::options{}, reproc::sink::string(out), reproc::sink::string(err));
|
||||
|
||||
if (ec)
|
||||
{
|
||||
LOG_WARNING
|
||||
<< "Could not find macOS version by calling 'sw_vers -productVersion'\nPlease file a bug report.\nError: "
|
||||
<< ec.message();
|
||||
return "";
|
||||
}
|
||||
return std::string(strip(out));
|
||||
}
|
||||
|
||||
std::string linux_version()
|
||||
{
|
||||
if (!env::get("CONDA_OVERRIDE_LINUX").empty())
|
||||
{
|
||||
return env::get("CONDA_OVERRIDE_LINUX");
|
||||
}
|
||||
if (!on_linux)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string out, err;
|
||||
std::vector<std::string> args = { "uname", "-r" };
|
||||
auto [status, ec] = reproc::run(
|
||||
args, reproc::options{}, reproc::sink::string(out), reproc::sink::string(err));
|
||||
|
||||
if (ec)
|
||||
{
|
||||
LOG_INFO << "Could not find linux version by calling 'uname -r' (skipped)";
|
||||
return "";
|
||||
}
|
||||
|
||||
std::regex re("([0-9]+\\.[0-9]+\\.[0-9]+)-.*");
|
||||
std::smatch m;
|
||||
|
||||
if (std::regex_search(out, m, re))
|
||||
{
|
||||
if (m.size() == 2)
|
||||
{
|
||||
std::ssub_match linux_version = m[1];
|
||||
LOG_DEBUG << "linux version found: " << linux_version;
|
||||
return linux_version.str();
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
DWORD getppid()
|
||||
{
|
||||
HANDLE hSnapshot;
|
||||
PROCESSENTRY32 pe32;
|
||||
DWORD ppid = 0, pid = GetCurrentProcessId();
|
||||
|
||||
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
__try
|
||||
{
|
||||
if (hSnapshot == INVALID_HANDLE_VALUE)
|
||||
__leave;
|
||||
|
||||
ZeroMemory(&pe32, sizeof(pe32));
|
||||
pe32.dwSize = sizeof(pe32);
|
||||
if (!Process32First(hSnapshot, &pe32))
|
||||
__leave;
|
||||
|
||||
do
|
||||
{
|
||||
if (pe32.th32ProcessID == pid)
|
||||
{
|
||||
ppid = pe32.th32ParentProcessID;
|
||||
break;
|
||||
}
|
||||
} while (Process32Next(hSnapshot, &pe32));
|
||||
}
|
||||
__finally
|
||||
{
|
||||
if (hSnapshot != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(hSnapshot);
|
||||
}
|
||||
return ppid;
|
||||
}
|
||||
|
||||
std::string get_process_name_by_pid(DWORD processId)
|
||||
{
|
||||
std::string ret;
|
||||
HANDLE handle = OpenProcess(
|
||||
PROCESS_QUERY_LIMITED_INFORMATION,
|
||||
FALSE,
|
||||
processId /* This is the PID, you can find one from windows task manager */
|
||||
);
|
||||
if (handle)
|
||||
{
|
||||
DWORD buffSize = 1024;
|
||||
CHAR buffer[1024];
|
||||
if (QueryFullProcessImageNameA(handle, 0, buffer, &buffSize))
|
||||
{
|
||||
ret = buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error GetModuleBaseNameA : %lu", GetLastError());
|
||||
}
|
||||
CloseHandle(handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error OpenProcess : %lu", GetLastError());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
std::string get_process_name_by_pid(const int pid)
|
||||
{
|
||||
std::string ret;
|
||||
char name[1024];
|
||||
proc_name(pid, name, sizeof(name));
|
||||
ret = name;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
std::string get_process_name_by_pid(const int pid)
|
||||
{
|
||||
std::ifstream f(concat("/proc/", std::to_string(pid), "/status"));
|
||||
if (f.good())
|
||||
{
|
||||
std::string l;
|
||||
std::getline(f, l);
|
||||
return l;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
#include "mamba/core/context.hpp"
|
||||
#include "mamba/core/util.hpp"
|
||||
#include "mamba/core/output.hpp"
|
||||
#include "mamba/core/util_os.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
@ -18,75 +19,6 @@ namespace mamba
|
|||
{
|
||||
namespace detail
|
||||
{
|
||||
std::string macos_version()
|
||||
{
|
||||
if (!env::get("CONDA_OVERRIDE_OSX").empty())
|
||||
{
|
||||
return env::get("CONDA_OVERRIDE_OSX");
|
||||
}
|
||||
|
||||
if (!on_mac)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string out, err;
|
||||
// Note: we could also inspect /System/Library/CoreServices/SystemVersion.plist which is
|
||||
// an XML file
|
||||
// that contains the same information. However, then we'd either need an xml
|
||||
// parser or some other crude method to read the data
|
||||
std::vector<std::string> args = { "sw_vers", "-productVersion" };
|
||||
auto [status, ec] = reproc::run(
|
||||
args, reproc::options{}, reproc::sink::string(out), reproc::sink::string(err));
|
||||
|
||||
if (ec)
|
||||
{
|
||||
LOG_DEBUG
|
||||
<< "Could not find macOS version by calling 'sw_vers -productVersion'\nPlease file a bug report.\nError: "
|
||||
<< ec.message();
|
||||
return "";
|
||||
}
|
||||
return std::string(strip(out));
|
||||
}
|
||||
|
||||
std::string linux_version()
|
||||
{
|
||||
if (!env::get("CONDA_OVERRIDE_LINUX").empty())
|
||||
{
|
||||
return env::get("CONDA_OVERRIDE_LINUX");
|
||||
}
|
||||
if (!on_linux)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string out, err;
|
||||
std::vector<std::string> args = { "uname", "-r" };
|
||||
auto [status, ec] = reproc::run(
|
||||
args, reproc::options{}, reproc::sink::string(out), reproc::sink::string(err));
|
||||
|
||||
if (ec)
|
||||
{
|
||||
LOG_INFO << "Could not find linux version by calling 'uname -r' (skipped)";
|
||||
return "";
|
||||
}
|
||||
|
||||
std::regex re("([0-9]+\\.[0-9]+\\.[0-9]+)-.*");
|
||||
std::smatch m;
|
||||
|
||||
if (std::regex_search(out, m, re))
|
||||
{
|
||||
if (m.size() == 2)
|
||||
{
|
||||
std::ssub_match linux_version = m[1];
|
||||
LOG_DEBUG << "linux version found: " << linux_version;
|
||||
return linux_version.str();
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string glibc_version()
|
||||
{
|
||||
if (!env::get("CONDA_OVERRIDE_GLIBC").empty())
|
||||
|
@ -232,7 +164,7 @@ namespace mamba
|
|||
{
|
||||
res.push_back(make_virtual_package("__unix"));
|
||||
|
||||
std::string linux_ver = detail::linux_version();
|
||||
std::string linux_ver = linux_version();
|
||||
if (!linux_ver.empty())
|
||||
{
|
||||
res.push_back(make_virtual_package("__linux", linux_ver));
|
||||
|
@ -256,7 +188,7 @@ namespace mamba
|
|||
{
|
||||
res.push_back(make_virtual_package("__unix"));
|
||||
|
||||
std::string osx_ver = detail::macos_version();
|
||||
std::string osx_ver = macos_version();
|
||||
if (!osx_ver.empty())
|
||||
{
|
||||
res.push_back(make_virtual_package("__osx", osx_ver));
|
||||
|
|
|
@ -321,6 +321,10 @@ init_install_options(CLI::App* subcom)
|
|||
extra_safety_checks.set_cli_config(0),
|
||||
extra_safety_checks.description());
|
||||
|
||||
auto& shortcuts = config.at("shortcuts").get_wrapped<bool>();
|
||||
subcom->add_flag(
|
||||
"--shortcuts,!--no-shortcuts", shortcuts.set_cli_config(0), shortcuts.description());
|
||||
|
||||
auto& safety_checks = config.at("safety_checks").get_wrapped<VerificationLevel>();
|
||||
subcom->add_set("--safety-checks",
|
||||
safety_checks.set_cli_config(""),
|
||||
|
|
|
@ -43,7 +43,17 @@ init_shell_parser(CLI::App* subcom)
|
|||
.description("The action to complete"));
|
||||
subcom->add_set("action",
|
||||
action.set_cli_config(""),
|
||||
{ "init", "activate", "deactivate", "hook", "reactivate", "deactivate" },
|
||||
{ "init",
|
||||
"activate",
|
||||
"deactivate",
|
||||
"hook",
|
||||
"reactivate",
|
||||
"deactivate"
|
||||
#ifdef _WIN32
|
||||
,
|
||||
"enable-long-paths-support"
|
||||
#endif
|
||||
},
|
||||
action.description());
|
||||
|
||||
auto& prefix = config.insert(
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
if not sys.platform.startswith("win"):
|
||||
pytest.skip("skipping windows-only tests", allow_module_level=True)
|
||||
|
||||
import menuinst
|
||||
import win32com.client
|
||||
|
||||
from .helpers import create, get_env, get_umamba, random_string, remove, umamba_list
|
||||
|
||||
|
||||
class TestMenuinst:
|
||||
root_prefix = os.environ["MAMBA_ROOT_PREFIX"]
|
||||
current_prefix = os.environ["CONDA_PREFIX"]
|
||||
|
||||
dirs = menuinst.win32.dirs_src
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def teardown_class(cls):
|
||||
pass
|
||||
|
||||
def test_simple_shortcut(self):
|
||||
env_name = random_string()
|
||||
# "--json"
|
||||
create("miniforge_console_shortcut", "-n", env_name, no_dry_run=True)
|
||||
prefix = os.path.join(self.root_prefix, "envs", env_name)
|
||||
d = self.dirs["user"]["start"][0]
|
||||
lnk = os.path.join(d, "Miniforge", "Miniforge Prompt (" + env_name + ").lnk")
|
||||
|
||||
assert os.path.exists(lnk)
|
||||
|
||||
shell = win32com.client.Dispatch("WScript.Shell")
|
||||
shortcut = shell.CreateShortCut(lnk)
|
||||
|
||||
assert shortcut.TargetPath.lower() == os.getenv("COMSPEC").lower()
|
||||
icon_location = shortcut.IconLocation
|
||||
icon_location_path, icon_location_index = icon_location.split(",")
|
||||
assert Path(icon_location_path) == (
|
||||
Path(prefix) / "Menu" / "console_shortcut.ico"
|
||||
)
|
||||
assert (icon_location_index, "0")
|
||||
|
||||
assert shortcut.Description == "Miniforge Prompt (" + env_name + ")"
|
||||
assert shortcut.Arguments == "/K " + str(
|
||||
Path(self.root_prefix, "Scripts", "activate.bat")
|
||||
) + " " + str(Path(prefix))
|
||||
|
||||
remove("miniforge_console_shortcut", "-n", env_name, no_dry_run=True)
|
||||
assert not os.path.exists(lnk)
|
||||
|
||||
def test_shortcut_weird_env(self):
|
||||
# note Umlauts do not work yet
|
||||
os.environ["MAMBA_ROOT_PREFIX"] = str(Path("./compl i c ted").absolute())
|
||||
root_prefix = os.environ["MAMBA_ROOT_PREFIX"]
|
||||
|
||||
env_name = random_string()
|
||||
# "--json"
|
||||
create("miniforge_console_shortcut", "-n", env_name, no_dry_run=True)
|
||||
prefix = os.path.join(root_prefix, "envs", env_name)
|
||||
d = self.dirs["user"]["start"][0]
|
||||
lnk = os.path.join(d, "Miniforge", "Miniforge Prompt (" + env_name + ").lnk")
|
||||
|
||||
assert os.path.exists(lnk)
|
||||
|
||||
shell = win32com.client.Dispatch("WScript.Shell")
|
||||
shortcut = shell.CreateShortCut(lnk)
|
||||
|
||||
assert shortcut.TargetPath.lower() == os.getenv("COMSPEC").lower()
|
||||
|
||||
icon_location = shortcut.IconLocation
|
||||
icon_location_path, icon_location_index = icon_location.split(",")
|
||||
assert Path(icon_location_path) == (
|
||||
Path(prefix) / "Menu" / "console_shortcut.ico"
|
||||
)
|
||||
assert (icon_location_index, "0")
|
||||
|
||||
assert shortcut.Description == "Miniforge Prompt (" + env_name + ")"
|
||||
assert (
|
||||
shortcut.Arguments
|
||||
== '/K "'
|
||||
+ str(Path(root_prefix) / "Scripts" / "activate.bat")
|
||||
+ '" "'
|
||||
+ str(Path(prefix))
|
||||
+ '"'
|
||||
)
|
||||
|
||||
remove("miniforge_console_shortcut", "-n", env_name, no_dry_run=True)
|
||||
assert not os.path.exists(lnk)
|
||||
|
||||
shutil.rmtree(root_prefix)
|
||||
os.environ["MAMBA_ROOT_PREFIX"] = self.root_prefix
|
|
@ -229,10 +229,11 @@ class TestShell:
|
|||
|
||||
if multiple_time:
|
||||
if same_prefix and shell_type == "cmd.exe":
|
||||
assert (
|
||||
shell("-y", "init", "-s", shell_type, "-p", TestShell.root_prefix)
|
||||
== ""
|
||||
)
|
||||
res = shell("-y", "init", "-s", shell_type, "-p", TestShell.root_prefix)
|
||||
assert res.splitlines() == [
|
||||
"cmd.exe already initialized.",
|
||||
"Windows long-path support already enabled.",
|
||||
]
|
||||
else:
|
||||
assert shell(
|
||||
"-y",
|
||||
|
|
Loading…
Reference in New Issue