Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

environment lockfile parsing & usage in create and install sub-commands #1577

Merged
merged 2 commits into from
Apr 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions libmamba/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ set(LIBMAMBA_SOURCES
${LIBMAMBA_SOURCE_DIR}/core/util_os.cpp
${LIBMAMBA_SOURCE_DIR}/core/validate.cpp
${LIBMAMBA_SOURCE_DIR}/core/virtual_packages.cpp
${LIBMAMBA_SOURCE_DIR}/core/env_lockfile.cpp
# API (high-level)
${LIBMAMBA_SOURCE_DIR}/api/c_api.cpp
${LIBMAMBA_SOURCE_DIR}/api/channel_loader.cpp
Expand Down Expand Up @@ -192,8 +193,10 @@ set(LIBMAMBA_HEADERS
${LIBMAMBA_INCLUDE_DIR}/mamba/core/util.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/util_os.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/util_random.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/util_scope.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/validate.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/virtual_packages.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/core/env_lockfile.hpp
# API (high-level)
${LIBMAMBA_INCLUDE_DIR}/mamba/api/c_api.h
${LIBMAMBA_INCLUDE_DIR}/mamba/api/channel_loader.hpp
Expand Down
1 change: 1 addition & 0 deletions libmamba/include/mamba/api/install.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ namespace mamba
int is_retry = 0);

void install_explicit_specs(const std::vector<std::string>& specs, bool create_env = false);
void install_lockfile_specs(const fs::path& lockfile_specs, bool create_env = false);

namespace detail
{
Expand Down
2 changes: 2 additions & 0 deletions libmamba/include/mamba/core/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <map>
#include <string>
#include <vector>
#include <optional>

#define ROOT_ENV_NAME "base"

Expand Down Expand Up @@ -111,6 +112,7 @@ namespace mamba
// TODO check writable and add other potential dirs
std::vector<fs::path> envs_dirs;
std::vector<fs::path> pkgs_dirs;
std::optional<fs::path> env_lockfile;

bool use_index_cache = false;
std::size_t local_repodata_ttl = 1; // take from header
Expand Down
123 changes: 123 additions & 0 deletions libmamba/include/mamba/core/env_lockfile.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright (c) 2022, 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_ENVIRONMENT_LOCKFILE_HPP
#define MAMBA_CORE_ENVIRONMENT_LOCKFILE_HPP

#include <string>
#include <typeindex>
#include <optional>
#include <unordered_map>

#include <tl/expected.hpp>

#include "fsutil.hpp"
#include "package_info.hpp"
#include "error_handling.hpp"

namespace mamba
{

enum class file_parsing_error_code
{
unknown_failure, /// Something failed while parsing but we can't identify what.
unsuported_version, /// The version of the file does not matched supported ver.
parsing_failure, /// The content of the file doesnt match the expected format/language
/// structure or constraints.
invalid_data, /// The structure of the data in the file is fine but some fields have
/// invalid values for our purpose.
};

struct EnvLockFileError
{
file_parsing_error_code parsing_error_code = file_parsing_error_code::unknown_failure;
std::optional<std::type_index> yaml_error_type{};

static const EnvLockFileError& get_details(const mamba_error& error)
{
return std::any_cast<const EnvLockFileError&>(error.data());
}

template <typename StringT>
static mamba_error make_error(file_parsing_error_code error_code,
StringT&& msg,
std::optional<std::type_index> yaml_error_type = std::nullopt)
{
return mamba_error{ std::forward<StringT>(msg),
mamba_error_code::env_lockfile_parsing_failed,
EnvLockFileError{ error_code, yaml_error_type } };
}
};

class EnvironmentLockFile
{
public:
struct Channel
{
std::string url;
std::vector<std::string> used_env_vars;
};

struct Meta
{
std::unordered_map<std::string, std::string> content_hash;
std::vector<Channel> channels;
std::vector<std::string> platforms;
std::vector<std::string> sources;
};

struct Package
{
mamba::PackageInfo info;
bool is_optional = false;
std::string category;
std::string manager;
std::string platform;
};

EnvironmentLockFile(Meta metadata, std::vector<Package> packages)
: metadata(std::move(metadata))
, packages(std::move(packages))
{
}

std::vector<PackageInfo> get_packages_for(std::string_view category,
std::string_view platform,
std::string_view manager) const;

const std::vector<Package>& get_all_packages() const
{
return packages;
}
const Meta& get_metadata() const
{
return metadata;
}

private:
Meta metadata;
std::vector<Package> packages;
};

/// Read an environment lock YAML file and returns it's structured content or an error if
/// failed.
tl::expected<EnvironmentLockFile, mamba_error> read_environment_lockfile(
const fs::path& lockfile_location);


/// Returns `true` if the filename matches names of files which should be interpreted as conda
/// environment lockfile. NOTE: this does not check if the file exists.
inline bool is_env_lockfile_name(const std::string_view filename)
{
return ends_with(filename, "-lock.yml") || ends_with(filename, "-lock.yaml");
}


}


#endif
3 changes: 2 additions & 1 deletion libmamba/include/mamba/core/error_handling.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ namespace mamba
enum class mamba_error_code
{
unknown,
aggregated,
prefix_data_not_loaded,
subdirdata_not_loaded,
cache_not_loaded,
repodata_not_loaded,
configurable_bad_cast,
aggregated
env_lockfile_parsing_failed,
};

class mamba_error : public std::runtime_error
Expand Down
10 changes: 10 additions & 0 deletions libmamba/include/mamba/core/transaction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "repo.hpp"
#include "thread_utils.hpp"
#include "transaction_context.hpp"
#include "env_lockfile.hpp"

extern "C"
{
Expand Down Expand Up @@ -122,6 +123,11 @@ namespace mamba
MultiPackageCache& caches);
MTransaction(MSolver& solver, MultiPackageCache& caches);

// Only use if the packages have been solved previously already.
MTransaction(MPool& pool,
const std::vector<PackageInfo>& packages,
MultiPackageCache& caches);

~MTransaction();

MTransaction(const MTransaction&) = delete;
Expand Down Expand Up @@ -166,6 +172,10 @@ namespace mamba
MTransaction create_explicit_transaction_from_urls(MPool& pool,
const std::vector<std::string>& urls,
MultiPackageCache& package_caches);

MTransaction create_explicit_transaction_from_lockfile(MPool& pool,
const fs::path& env_lockfile_path,
MultiPackageCache& package_caches);
} // namespace mamba

#endif // MAMBA_TRANSACTION_HPP
6 changes: 6 additions & 0 deletions libmamba/include/mamba/core/util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,12 @@ namespace mamba
std::tuple<std::vector<std::string>, std::unique_ptr<TemporaryFile>> prepare_wrapped_call(
const fs::path& prefix, const std::vector<std::string>& cmd);

/// Returns `true` if the filename matches names of files which should be interpreted as YAML.
/// NOTE: this does not check if the file exists.
inline bool is_yaml_file_name(const std::string_view filename)
{
return ends_with(filename, ".yml") || ends_with(filename, ".yaml");
}

} // namespace mamba

Expand Down
46 changes: 46 additions & 0 deletions libmamba/include/mamba/core/util_scope.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

#ifndef MAMBA_CORE_UTIL_SCOPE_HPP
#define MAMBA_CORE_UTIL_SCOPE_HPP

#include <stdexcept>
#include "spdlog/fmt/fmt.h"
#include "mamba/core/output.hpp"

namespace mamba
{

template <typename F>
struct on_scope_exit
{
F func;

explicit on_scope_exit(F&& f)
: func(std::forward<F>(f))
{
}

~on_scope_exit()
{
try
{
func();
}
catch (const std::exception& ex)
{
LOG_ERROR << fmt::format("Scope exit error (catched and ignored): {}", ex.what());
}
catch (...)
{
LOG_ERROR << "Scope exit unknown error (catched and ignored)";
}
}

// Deactivate copy & move until we implement moves
on_scope_exit(const on_scope_exit&) = delete;
on_scope_exit& operator=(const on_scope_exit&) = delete;
on_scope_exit(on_scope_exit&&) = delete;
on_scope_exit& operator=(on_scope_exit&&) = delete;
};
}

#endif
8 changes: 7 additions & 1 deletion libmamba/src/api/create.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,13 @@ namespace mamba
detail::store_platform_config(ctx.target_prefix, ctx.platform);
}

if (!create_specs.empty())
if (Context::instance().env_lockfile)
{
const auto lockfile_path = Context::instance().env_lockfile.value();
LOG_DEBUG << "Lockfile: " << lockfile_path.string();
install_lockfile_specs(lockfile_path, true);
}
else if (!create_specs.empty())
{
if (use_explicit)
install_explicit_specs(create_specs, true);
Expand Down
Loading