diff --git a/Cargo.lock b/Cargo.lock index e2cb2e55..429ca0a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -315,6 +315,25 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" +[[package]] +name = "cbindgen" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb" +dependencies = [ + "clap", + "heck 0.4.1", + "indexmap", + "log", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 2.0.87", + "tempfile", + "toml 0.8.19", +] + [[package]] name = "cc" version = "1.1.37" @@ -380,7 +399,7 @@ version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn 2.0.87", @@ -885,6 +904,12 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "heck" version = "0.5.0" @@ -1746,12 +1771,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scratch" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" - [[package]] name = "semver" version = "1.0.23" @@ -1796,6 +1815,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "sha1" version = "0.10.6" @@ -1904,7 +1932,7 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", @@ -2066,11 +2094,26 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -2079,6 +2122,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] @@ -2134,12 +2179,6 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - [[package]] name = "untrusted" version = "0.9.0" @@ -2279,7 +2318,9 @@ name = "velopack_libc" version = "0.0.0-local" dependencies = [ "anyhow", + "cbindgen", "lazy_static", + "libc", "log", "velopack", ] @@ -2773,7 +2814,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" dependencies = [ - "toml", + "toml 0.5.11", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 32108b78..b7d8a9af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,7 @@ fs_extra = "1.3" memmap2 = "0.9" webview2-com = "0.33" windows = "0.58" +cbindgen = "0.27" # default to small, optimized workspace release binaries [profile.release] diff --git a/src/lib-cpp/Cargo.toml b/src/lib-cpp/Cargo.toml index f1d145ef..4ae9e14f 100644 --- a/src/lib-cpp/Cargo.toml +++ b/src/lib-cpp/Cargo.toml @@ -21,4 +21,8 @@ crate-type = ["cdylib"] velopack.workspace = true anyhow.workspace = true lazy_static.workspace = true -log.workspace = true \ No newline at end of file +log.workspace = true +libc.workspace = true + +[build-dependencies] +cbindgen.workspace = true \ No newline at end of file diff --git a/src/lib-cpp/build.rs b/src/lib-cpp/build.rs new file mode 100644 index 00000000..4dada213 --- /dev/null +++ b/src/lib-cpp/build.rs @@ -0,0 +1,18 @@ +extern crate cbindgen; + +use std::env; + +fn main() { + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + cbindgen::Builder::new() + .with_crate(crate_dir) + .with_documentation(true) + .with_language(cbindgen::Language::C) + .with_autogen_warning("// === THIS FILE IS AUTO-GENERATED - DO NOT EDIT ===") + .with_include_guard("VELOPACK_H") + .with_cpp_compat(true) + .with_include_version(true) + .generate() + .expect("Unable to generate bindings") + .write_to_file("include/Velopack.h"); +} \ No newline at end of file diff --git a/src/lib-cpp/include/Velopack.h b/src/lib-cpp/include/Velopack.h index 44106b8f..3b0f9b46 100644 --- a/src/lib-cpp/include/Velopack.h +++ b/src/lib-cpp/include/Velopack.h @@ -1,620 +1,216 @@ -//! This header provides the C and C++ API for the Velopack library. -//! All the C constructs are prefixed by `vpkc_` and all the C++ constructs are in the `Velopack` namespace. -//! The C++ API is a thin wrapper around the C API, providing a more idiomatic C++ interface. -//! You should not mix and match the C and C++ APIs in the same program. #ifndef VELOPACK_H #define VELOPACK_H -#include // For size_t -#include // For bool -#include // For uint64_t, uint32_t +/* Generated with cbindgen:0.27.0 */ +// === THIS FILE IS AUTO-GENERATED - DO NOT EDIT === + +#include +#include +#include +#include + +enum vpkc_update_check_t #ifdef __cplusplus -#include -#include -#include -#include -#endif + : int16_t +#endif // __cplusplus + { + UPDATE_ERROR = -1, + UPDATE_AVAILABLE = 0, + NO_UPDATE_AVAILABLE = 1, + REMOTE_IS_EMPTY = 2, +}; +#ifndef __cplusplus +typedef int16_t vpkc_update_check_t; +#endif // __cplusplus -#if !defined(_WIN32) -#include -#endif +/** + * Options to customise the behaviour of UpdateManager. + */ +typedef struct vpkc_update_options_t { + /** + * Allows UpdateManager to update to a version that's lower than the current version (i.e. downgrading). + * This could happen if a release has bugs and was retracted from the release feed, or if you're using + * ExplicitChannel to switch channels to another channel where the latest version on that + * channel is lower than the current version. + */ + bool AllowVersionDowngrade; + /** + * **This option should usually be left None**.
+ * Overrides the default channel used to fetch updates. + * The default channel will be whatever channel was specified on the command line when building this release. + * For example, if the current release was packaged with '--channel beta', then the default channel will be 'beta'. + * This allows users to automatically receive updates from the same channel they installed from. This options + * allows you to explicitly switch channels, for example if the user wished to switch back to the 'stable' channel + * without having to reinstall the application. + */ + char *ExplicitChannel; +} vpkc_update_options_t; + +/** + * VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth). + */ +typedef struct vpkc_locator_config_t { + /** + * The root directory of the current app. + */ + char *RootAppDir; + /** + * The path to the Update.exe binary. + */ + char *UpdateExePath; + /** + * The path to the packages' directory. + */ + char *PackagesDir; + /** + * The current app manifest. + */ + char *ManifestPath; + /** + * The directory containing the application's user binaries. + */ + char *CurrentBinaryDir; + /** + * Whether the current application is portable or installed. + */ + bool IsPortable; +} vpkc_locator_config_t; + +typedef void vpkc_update_manager_t; + +/** + * An individual Velopack asset, could refer to an asset on-disk or in a remote package feed. + */ +typedef struct vpkc_asset_t { + /** + * The name or Id of the package containing this release. + */ + char *PackageId; + /** + * The version of this release. + */ + char *Version; + /** + * The type of asset (eg. "Full" or "Delta"). + */ + char *Type; + /** + * The filename of the update package containing this release. + */ + char *FileName; + /** + * The SHA1 checksum of the update package containing this release. + */ + char *SHA1; + /** + * The SHA256 checksum of the update package containing this release. + */ + char *SHA256; + /** + * The size in bytes of the update package containing this release. + */ + uint64_t Size; + /** + * The release notes in markdown format, as passed to Velopack when packaging the release. This may be an empty string. + */ + char *NotesMarkdown; + /** + * The release notes in HTML format, transformed from Markdown when packaging the release. This may be an empty string. + */ + char *NotesHtml; +} vpkc_asset_t; + +/** + * Holds information about the current version and pending updates, such as how many there are, and access to release notes. + */ +typedef struct vpkc_update_info_t { + /** + * The available version that we are updating to. + */ + struct vpkc_asset_t TargetFullRelease; + /** + * True if the update is a version downgrade or lateral move (such as when switching channels to the same version number). + * In this case, only full updates are allowed, and any local packages on disk newer than the downloaded version will be + * deleted. + */ + bool IsDowngrade; +} vpkc_update_info_t; + +typedef void (*vpkc_progress_callback_t)(void *p_user_data, size_t progress); + +typedef void (*vpkc_hook_callback_t)(void *p_user_data, const char *psz_app_version); + +typedef void (*vpkc_log_callback_t)(void *p_user_data, + const char *psz_level, + const char *psz_message); #ifdef __cplusplus extern "C" { -#endif - -typedef void vpkc_update_manager_t; -typedef void (*vpkc_progress_callback_t)(void* pUserData, size_t progress); -typedef void (*vpkc_log_callback_t)(void* pUserData, const char* pszLevel, const char* pszMessage); -typedef void (*vpkc_hook_callback_t)(void* pUserData, const char* pszAppVersion); - -typedef enum vpkc_update_check_t { - UPDATE_ERROR = -1, - UPDATE_AVAILABLE = 0, - NO_UPDATE_AVAILABLE = 1, - REMOTE_IS_EMPTY = 2, -} vpkc_update_check_t; - -// !! AUTO-GENERATED-START C_TYPES - -/// VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth). -typedef struct vpkc_locator_config_t { - /// The root directory of the current app. - char* RootAppDir; - /// The path to the Update.exe binary. - char* UpdateExePath; - /// The path to the packages' directory. - char* PackagesDir; - /// The current app manifest. - char* ManifestPath; - /// The directory containing the application's user binaries. - char* CurrentBinaryDir; - /// Whether the current application is portable or installed. - bool IsPortable; -} vpkc_locator_config_t; - -/// An individual Velopack asset, could refer to an asset on-disk or in a remote package feed. -typedef struct vpkc_asset_t { - /// The name or Id of the package containing this release. - char* PackageId; - /// The version of this release. - char* Version; - /// The type of asset (eg. "Full" or "Delta"). - char* Type; - /// The filename of the update package containing this release. - char* FileName; - /// The SHA1 checksum of the update package containing this release. - char* SHA1; - /// The SHA256 checksum of the update package containing this release. - char* SHA256; - /// The size in bytes of the update package containing this release. - uint64_t Size; - /// The release notes in markdown format, as passed to Velopack when packaging the release. This may be an empty string. - char* NotesMarkdown; - /// The release notes in HTML format, transformed from Markdown when packaging the release. This may be an empty string. - char* NotesHtml; -} vpkc_asset_t; - -/// Holds information about the current version and pending updates, such as how many there are, and access to release notes. -typedef struct vpkc_update_info_t { - /// The available version that we are updating to. - vpkc_asset_t TargetFullRelease; - /// True if the update is a version downgrade or lateral move (such as when switching channels to the same version number). - /// In this case, only full updates are allowed, and any local packages on disk newer than the downloaded version will be - /// deleted. - bool IsDowngrade; -} vpkc_update_info_t; - -/// Options to customise the behaviour of UpdateManager. -typedef struct vpkc_update_options_t { - /// Allows UpdateManager to update to a version that's lower than the current version (i.e. downgrading). - /// This could happen if a release has bugs and was retracted from the release feed, or if you're using - /// ExplicitChannel to switch channels to another channel where the latest version on that - /// channel is lower than the current version. - bool AllowVersionDowngrade; - /// **This option should usually be left None**.
- /// Overrides the default channel used to fetch updates. - /// The default channel will be whatever channel was specified on the command line when building this release. - /// For example, if the current release was packaged with '--channel beta', then the default channel will be 'beta'. - /// This allows users to automatically receive updates from the same channel they installed from. This options - /// allows you to explicitly switch channels, for example if the user wished to switch back to the 'stable' channel - /// without having to reinstall the application. - char* ExplicitChannel; -} vpkc_update_options_t; -// !! AUTO-GENERATED-END C_TYPES - -/// Creates a new vpkc_update_manager_t. Free with vpkc_free_update_manager. -/// \group UpdateManager -bool vpkc_new_update_manager(const char* pszUrlOrString, vpkc_update_options_t* pOptions, vpkc_locator_config_t* pLocator, vpkc_update_manager_t** pManager); -/// \group UpdateManager -size_t vpkc_get_current_version(vpkc_update_manager_t* pManager, char* pszVersion, size_t cVersion); -/// \group UpdateManager -size_t vpkc_get_app_id(vpkc_update_manager_t* pManager, char* pszId, size_t cId); -/// \group UpdateManager -bool vpkc_is_portable(vpkc_update_manager_t* pManager); -/// \group UpdateManager -bool vpkc_update_pending_restart(vpkc_update_manager_t* pManager, vpkc_asset_t* pAsset); -/// \group UpdateManager -vpkc_update_check_t vpkc_check_for_updates(vpkc_update_manager_t* pManager, vpkc_update_info_t* pUpdate); -/// \group UpdateManager -bool vpkc_download_updates(vpkc_update_manager_t* pManager, vpkc_update_info_t* pUpdate, vpkc_progress_callback_t cbProgress, void* pUserData = 0); -/// \group UpdateManager -bool vpkc_wait_exit_then_apply_update(vpkc_update_manager_t* pManager, vpkc_asset_t* pAsset, bool bSilent, bool bRestart, char** pRestartArgs, size_t cRestartArgs); -/// \group UpdateManager -void vpkc_free_update_manager(vpkc_update_manager_t* pManager); -/// \group UpdateManager -void vpkc_free_update_info(vpkc_update_info_t* pUpdateInfo); -/// \group UpdateManager -void vpkc_free_asset(vpkc_asset_t* pAsset); - -/// Should be run at the beginning of your application to handle Velopack events. -/// \group VelopackApp -void vpkc_app_run(void* pUserData = 0); -/// \group VelopackApp -void vpkc_app_set_auto_apply_on_startup(bool bAutoApply); -/// \group VelopackApp -void vpkc_app_set_args(char** pArgs, size_t cArgs); -/// \group VelopackApp -void vpkc_app_set_locator(vpkc_locator_config_t* pLocator); -/// \group VelopackApp -void vpkc_app_set_hook_after_install(vpkc_hook_callback_t cbAfterInstall); -/// \group VelopackApp -void vpkc_app_set_hook_before_uninstall(vpkc_hook_callback_t cbBeforeUninstall); -/// \group VelopackApp -void vpkc_app_set_hook_before_update(vpkc_hook_callback_t cbBeforeUpdate); -/// \group VelopackApp -void vpkc_app_set_hook_after_update(vpkc_hook_callback_t cbAfterUpdate); -/// \group VelopackApp -void vpkc_app_set_hook_first_run(vpkc_hook_callback_t cbFirstRun); -/// \group VelopackApp -void vpkc_app_set_hook_restarted(vpkc_hook_callback_t cbRestarted); - -/// Given a function has returned a failure, this function will return the last error message as a string. -size_t vpkc_get_last_error(char* pszError, size_t cError); - -/// Sets the callback to be used/called with log messages from Velopack. -void vpkc_set_logger(vpkc_log_callback_t cbLog, void* pUserData = 0); - -#ifdef __cplusplus // end of extern "C" -} -#endif - -#ifdef __cplusplus - -namespace Velopack { - -static inline void throw_last_error() { - size_t neededSize = vpkc_get_last_error(nullptr, 0); - std::string strError(neededSize, '\0'); - vpkc_get_last_error(&strError[0], neededSize); - throw std::runtime_error(strError); -} - -static inline std::string to_cppstring(const char* psz) { - return psz == nullptr ? "" : psz; -} - -static inline char* to_cstring(const std::string& str) { - return const_cast(str.c_str()); -} - -static inline char* to_cstring_opt(const std::optional& str) { - return str.has_value() ? to_cstring(str.value()) : nullptr; -} - -static inline std::optional to_cppstring_opt(const char* psz) { - return psz == nullptr ? std::nullopt : std::optional(psz); -} - -static inline bool to_cppbool(bool b) { return b; } -static inline bool to_cbool(bool b) { return b; } -static inline uint64_t to_cu64(uint64_t i) { return i; } -static inline uint64_t to_cppu64(uint64_t i) { return i; } - -// !! AUTO-GENERATED-START CPP_TYPES - -/// VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth). -struct VelopackLocatorConfig { - /// The root directory of the current app. - std::string RootAppDir; - /// The path to the Update.exe binary. - std::string UpdateExePath; - /// The path to the packages' directory. - std::string PackagesDir; - /// The current app manifest. - std::string ManifestPath; - /// The directory containing the application's user binaries. - std::string CurrentBinaryDir; - /// Whether the current application is portable or installed. - bool IsPortable; -}; - -/// An individual Velopack asset, could refer to an asset on-disk or in a remote package feed. -struct VelopackAsset { - /// The name or Id of the package containing this release. - std::string PackageId; - /// The version of this release. - std::string Version; - /// The type of asset (eg. "Full" or "Delta"). - std::string Type; - /// The filename of the update package containing this release. - std::string FileName; - /// The SHA1 checksum of the update package containing this release. - std::string SHA1; - /// The SHA256 checksum of the update package containing this release. - std::string SHA256; - /// The size in bytes of the update package containing this release. - uint64_t Size; - /// The release notes in markdown format, as passed to Velopack when packaging the release. This may be an empty string. - std::string NotesMarkdown; - /// The release notes in HTML format, transformed from Markdown when packaging the release. This may be an empty string. - std::string NotesHtml; -}; - -/// Holds information about the current version and pending updates, such as how many there are, and access to release notes. -struct UpdateInfo { - /// The available version that we are updating to. - VelopackAsset TargetFullRelease; - /// True if the update is a version downgrade or lateral move (such as when switching channels to the same version number). - /// In this case, only full updates are allowed, and any local packages on disk newer than the downloaded version will be - /// deleted. - bool IsDowngrade; -}; - -/// Options to customise the behaviour of UpdateManager. -struct UpdateOptions { - /// Allows UpdateManager to update to a version that's lower than the current version (i.e. downgrading). - /// This could happen if a release has bugs and was retracted from the release feed, or if you're using - /// ExplicitChannel to switch channels to another channel where the latest version on that - /// channel is lower than the current version. - bool AllowVersionDowngrade; - /// **This option should usually be left None**.
- /// Overrides the default channel used to fetch updates. - /// The default channel will be whatever channel was specified on the command line when building this release. - /// For example, if the current release was packaged with '--channel beta', then the default channel will be 'beta'. - /// This allows users to automatically receive updates from the same channel they installed from. This options - /// allows you to explicitly switch channels, for example if the user wished to switch back to the 'stable' channel - /// without having to reinstall the application. - std::optional ExplicitChannel; -}; - -static inline vpkc_locator_config_t to_c(const VelopackLocatorConfig& dto) { - return { - to_cstring(dto.RootAppDir), - to_cstring(dto.UpdateExePath), - to_cstring(dto.PackagesDir), - to_cstring(dto.ManifestPath), - to_cstring(dto.CurrentBinaryDir), - to_cbool(dto.IsPortable), - }; -} - -static inline VelopackLocatorConfig to_cpp(const vpkc_locator_config_t& dto) { - return { - to_cppstring(dto.RootAppDir), - to_cppstring(dto.UpdateExePath), - to_cppstring(dto.PackagesDir), - to_cppstring(dto.ManifestPath), - to_cppstring(dto.CurrentBinaryDir), - to_cppbool(dto.IsPortable), - }; -} - -static inline vpkc_asset_t to_c(const VelopackAsset& dto) { - return { - to_cstring(dto.PackageId), - to_cstring(dto.Version), - to_cstring(dto.Type), - to_cstring(dto.FileName), - to_cstring(dto.SHA1), - to_cstring(dto.SHA256), - to_cu64(dto.Size), - to_cstring(dto.NotesMarkdown), - to_cstring(dto.NotesHtml), - }; -} - -static inline VelopackAsset to_cpp(const vpkc_asset_t& dto) { - return { - to_cppstring(dto.PackageId), - to_cppstring(dto.Version), - to_cppstring(dto.Type), - to_cppstring(dto.FileName), - to_cppstring(dto.SHA1), - to_cppstring(dto.SHA256), - to_cppu64(dto.Size), - to_cppstring(dto.NotesMarkdown), - to_cppstring(dto.NotesHtml), - }; -} - -static inline vpkc_update_info_t to_c(const UpdateInfo& dto) { - return { - to_c(dto.TargetFullRelease), - to_cbool(dto.IsDowngrade), - }; -} - -static inline UpdateInfo to_cpp(const vpkc_update_info_t& dto) { - return { - to_cpp(dto.TargetFullRelease), - to_cppbool(dto.IsDowngrade), - }; -} - -static inline vpkc_update_options_t to_c(const UpdateOptions& dto) { - return { - to_cbool(dto.AllowVersionDowngrade), - to_cstring_opt(dto.ExplicitChannel), - }; -} - -static inline UpdateOptions to_cpp(const vpkc_update_options_t& dto) { - return { - to_cppbool(dto.AllowVersionDowngrade), - to_cppstring_opt(dto.ExplicitChannel), - }; -} -// !! AUTO-GENERATED-END CPP_TYPES - -static inline char** to_cstring_array(const std::vector& vec) { - char** result = new char*[vec.size()]; - for (size_t i = 0; i < vec.size(); ++i) { - result[i] = new char[vec[i].size() + 1]; // +1 for null-terminator -#ifdef _WIN32 - strcpy_s(result[i], vec[i].size() + 1, vec[i].c_str()); // Copy string content -#else - strcpy(result[i], vec[i].c_str()); // Copy string content -#endif - } - return result; -} - -static inline void free_cstring_array(char** arr, size_t size) { - for (size_t i = 0; i < size; ++i) { - delete[] arr[i]; - } - delete[] arr; -} - -/** - * VelopackApp helps you to handle app activation events correctly. - * This should be used as early as possible in your application startup code. - * (eg. the beginning of main() or wherever your entry point is) - */ -class VelopackApp { -private: - VelopackApp() {}; -public: - /** - * Build a new VelopackApp instance. - */ - static VelopackApp Build() { - return VelopackApp(); - }; - - /** - * Set whether to automatically apply downloaded updates on startup. This is ON by default. - */ - VelopackApp& SetAutoApplyOnStartup(bool bAutoApply) { - vpkc_app_set_auto_apply_on_startup(bAutoApply); - return *this; - }; - - /** - * Override the command line arguments used by VelopackApp. (by default this is env::args().skip(1)) - */ - VelopackApp& SetArgs(const std::vector& args) { - char** pArgs = to_cstring_array(args); - vpkc_app_set_args(pArgs, args.size()); - free_cstring_array(pArgs, args.size()); - return *this; - }; - - /** - * VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth). - */ - VelopackApp& SetLocator(const VelopackLocatorConfig& locator) { - vpkc_locator_config_t vpkc_locator = to_c(locator); - vpkc_app_set_locator(&vpkc_locator); - return *this; - }; - - /** - * WARNING: FastCallback hooks are run during critical stages of Velopack operations. - * Your code will be run and then the process will exit. - * If your code has not completed within 30 seconds, it will be terminated. - * Only supported on windows; On other operating systems, this will never be called. - */ - VelopackApp& OnAfterInstall(vpkc_hook_callback_t cbInstall) { - vpkc_app_set_hook_after_install(cbInstall); - return *this; - }; - - /** - * WARNING: FastCallback hooks are run during critical stages of Velopack operations. - * Your code will be run and then the process will exit. - * If your code has not completed within 30 seconds, it will be terminated. - * Only supported on windows; On other operating systems, this will never be called. - */ - VelopackApp& OnBeforeUninstall(vpkc_hook_callback_t cbInstall) { - vpkc_app_set_hook_before_uninstall(cbInstall); - return *this; - }; - - /** - * WARNING: FastCallback hooks are run during critical stages of Velopack operations. - * Your code will be run and then the process will exit. - * If your code has not completed within 30 seconds, it will be terminated. - * Only supported on windows; On other operating systems, this will never be called. - */ - VelopackApp& OnBeforeUpdate(vpkc_hook_callback_t cbInstall) { - vpkc_app_set_hook_before_update(cbInstall); - return *this; - }; - - /** - * WARNING: FastCallback hooks are run during critical stages of Velopack operations. - * Your code will be run and then the process will exit. - * If your code has not completed within 30 seconds, it will be terminated. - * Only supported on windows; On other operating systems, this will never be called. - */ - VelopackApp& OnAfterUpdate(vpkc_hook_callback_t cbInstall) { - vpkc_app_set_hook_after_update(cbInstall); - return *this; - }; - - /** - * This hook is triggered when the application is started for the first time after installation. - */ - VelopackApp& OnFirstRun(vpkc_hook_callback_t cbInstall) { - vpkc_app_set_hook_first_run(cbInstall); - return *this; - }; - - /** - * This hook is triggered when the application is restarted by Velopack after installing updates. - */ - VelopackApp& OnRestarted(vpkc_hook_callback_t cbInstall) { - vpkc_app_set_hook_restarted(cbInstall); - return *this; - }; - - /** - * Runs the Velopack startup logic. This should be the first thing to run in your app. - * In some circumstances it may terminate/restart the process to perform tasks. - */ - void Run(void* pUserData = 0) { - vpkc_app_run(pUserData); - }; -}; - -/** - * Provides functionality for checking for updates, downloading updates, and applying updates to the current application. - */ -class UpdateManager { -private: - vpkc_update_manager_t* m_pManager = 0; -public: - /** - * Create a new UpdateManager instance. - * @param urlOrPath Location of the update server or path to the local update directory. - * @param options Optional extra configuration for update manager. - * @param locator Override the default locator configuration (usually used for testing / mocks). - */ - UpdateManager(const std::string& urlOrPath, const UpdateOptions* options = nullptr, const VelopackLocatorConfig* locator = nullptr) { - vpkc_update_options_t* pOptions = nullptr; - if (options != nullptr) { - vpkc_update_options_t vpkc_options = to_c(*options); - pOptions = &vpkc_options; - } - - vpkc_locator_config_t* pLocator = nullptr; - if (locator != nullptr) { - vpkc_locator_config_t vpkc_locator = to_c(*locator); - pLocator = &vpkc_locator; - } - - if (!vpkc_new_update_manager(urlOrPath.c_str(), pOptions, pLocator, &m_pManager)) { - throw_last_error(); - } - }; - - /** - * Destructor for UpdateManager. - */ - ~UpdateManager() { - vpkc_free_update_manager(m_pManager); - }; - - /** - * Returns whether the app is in portable mode. On Windows this can be true or false. - * On MacOS and Linux this will always be true. - */ - bool IsPortable() noexcept { - return vpkc_is_portable(m_pManager); - }; - - /** - * Returns the currently installed version of the app. - */ - std::string GetCurrentVersion() noexcept { - size_t neededSize = vpkc_get_current_version(m_pManager, nullptr, 0); - std::string strVersion(neededSize, '\0'); - vpkc_get_current_version(m_pManager, &strVersion[0], neededSize); - return strVersion; - }; - - /** - * Returns the currently installed app id. - */ - std::string GetAppId() noexcept { - size_t neededSize = vpkc_get_app_id(m_pManager, nullptr, 0); - std::string strId(neededSize, '\0'); - vpkc_get_app_id(m_pManager, &strId[0], neededSize); - return strId; - }; - - /** - * Returns an UpdateInfo object if there is an update downloaded which still needs to be applied. - * You can pass the UpdateInfo object to waitExitThenApplyUpdate to apply the update. - */ - std::optional UpdatePendingRestart() noexcept { - vpkc_asset_t asset; - if (vpkc_update_pending_restart(m_pManager, &asset)) { - VelopackAsset cpp_asset = to_cpp(asset); - vpkc_free_asset(&asset); - return cpp_asset; - } - return std::nullopt; - }; - - /** - * Checks for updates, returning None if there are none available. If there are updates available, this method will return an - * UpdateInfo object containing the latest available release, and any delta updates that can be applied if they are available. - */ - std::optional CheckForUpdates() { - vpkc_update_info_t update; - vpkc_update_check_t result = vpkc_check_for_updates(m_pManager, &update); - switch (result) { - case vpkc_update_check_t::UPDATE_ERROR: - throw_last_error(); - return std::nullopt; - case vpkc_update_check_t::NO_UPDATE_AVAILABLE: - case vpkc_update_check_t::REMOTE_IS_EMPTY: - return std::nullopt; - case vpkc_update_check_t::UPDATE_AVAILABLE: - UpdateInfo cpp_info = to_cpp(update); - vpkc_free_update_info(&update); - return cpp_info; - } - return std::nullopt; - }; - - /** - * Downloads the specified updates to the local app packages directory. Progress is reported back to the caller via an optional Sender. - * This function will acquire a global update lock so may fail if there is already another update operation in progress. - * - If the update contains delta packages and the delta feature is enabled - * this method will attempt to unpack and prepare them. - * - If there is no delta update available, or there is an error preparing delta - * packages, this method will fall back to downloading the full version of the update. - */ - void DownloadUpdates(const UpdateInfo& update, vpkc_progress_callback_t progress = nullptr, void* pUserData = 0) { - vpkc_update_info_t vpkc_update = to_c(update); - if (!vpkc_download_updates(m_pManager, &vpkc_update, progress, pUserData)) { - throw_last_error(); - } - }; - - /** - * This will launch the Velopack updater and tell it to wait for this program to exit gracefully. - * You should then clean up any state and exit your app. The updater will apply updates and then - * optionally restart your app. The updater will only wait for 60 seconds before giving up. - */ - void WaitExitThenApplyUpdate(const VelopackAsset& asset, bool silent = false, bool restart = true, std::vector restartArgs = {}) { - char** pRestartArgs = to_cstring_array(restartArgs); - vpkc_asset_t vpkc_asset = to_c(asset); - bool result = vpkc_wait_exit_then_apply_update(m_pManager, &vpkc_asset, silent, restart, pRestartArgs, restartArgs.size()); - free_cstring_array(pRestartArgs, restartArgs.size()); - - if (!result) { - throw_last_error(); - } - }; - - /** - * This will launch the Velopack updater and tell it to wait for this program to exit gracefully. - * You should then clean up any state and exit your app. The updater will apply updates and then - * optionally restart your app. The updater will only wait for 60 seconds before giving up. - */ - void WaitExitThenApplyUpdate(const UpdateInfo& asset, bool silent = false, bool restart = true, std::vector restartArgs = {}) { - this->WaitExitThenApplyUpdate(asset.TargetFullRelease, silent, restart, restartArgs); - }; -}; - -} // namespace Velopack - #endif // __cplusplus -#endif // VELOPACK_H \ No newline at end of file +bool vpkc_new_update_manager(const char *psz_url_or_string, + struct vpkc_update_options_t *p_options, + struct vpkc_locator_config_t *p_locator, + vpkc_update_manager_t **p_manager); + +size_t vpkc_get_current_version(vpkc_update_manager_t *p_manager, + char *psz_version, + size_t c_version); + +size_t vpkc_get_app_id(vpkc_update_manager_t *p_manager, char *psz_id, size_t c_id); + +bool vpkc_is_portable(vpkc_update_manager_t *p_manager); + +bool vpkc_update_pending_restart(vpkc_update_manager_t *p_manager, struct vpkc_asset_t *p_asset); + +vpkc_update_check_t vpkc_check_for_updates(vpkc_update_manager_t *p_manager, + struct vpkc_update_info_t *p_update); + +bool vpkc_download_updates(vpkc_update_manager_t *p_manager, + struct vpkc_update_info_t *p_update, + vpkc_progress_callback_t cb_progress, + void *p_user_data); + +bool vpkc_wait_exit_then_apply_update(vpkc_update_manager_t *p_manager, + struct vpkc_asset_t *p_asset, + bool b_silent, + bool b_restart, + char **p_restart_args, + size_t c_restart_args); + +void vpkc_free_update_manager(vpkc_update_manager_t *p_manager); + +void vpkc_free_update_info(struct vpkc_update_info_t *p_update_info); + +void vpkc_free_asset(struct vpkc_asset_t *p_asset); + +void vpkc_app_run(void *p_user_data); + +void vpkc_app_set_auto_apply_on_startup(bool b_auto_apply); + +void vpkc_app_set_args(char **p_args, size_t c_args); + +void vpkc_app_set_locator(struct vpkc_locator_config_t *p_locator); + +void vpkc_app_set_hook_after_install(vpkc_hook_callback_t cb_after_install); + +void vpkc_app_set_hook_before_uninstall(vpkc_hook_callback_t cb_before_uninstall); + +void vpkc_app_set_hook_before_update(vpkc_hook_callback_t cb_before_update); + +void vpkc_app_set_hook_after_update(vpkc_hook_callback_t cb_after_update); + +void vpkc_app_set_hook_first_run(vpkc_hook_callback_t cb_first_run); + +void vpkc_app_set_hook_restarted(vpkc_hook_callback_t cb_restarted); + +size_t vpkc_get_last_error(char *psz_error, size_t c_error); + +void vpkc_set_logger(vpkc_log_callback_t cb_log, void *p_user_data); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif /* VELOPACK_H */ diff --git a/src/lib-cpp/include/Velopack.hpp b/src/lib-cpp/include/Velopack.hpp new file mode 100644 index 00000000..52b42c8d --- /dev/null +++ b/src/lib-cpp/include/Velopack.hpp @@ -0,0 +1,471 @@ +//! This header provides the C++ API for the Velopack library. +//! This C++ API is a thin wrapper around the C API, providing a more idiomatic C++ interface. +//! You should not mix and match the C and C++ APIs in the same program. +#ifndef VELOPACK_HPP +#define VELOPACK_HPP + +#include +#include +#include +#include + +#include "Velopack.h" + +#if !defined(_WIN32) +#include +#endif + +namespace Velopack { + +static inline void throw_last_error() { + size_t neededSize = vpkc_get_last_error(nullptr, 0); + std::string strError(neededSize, '\0'); + vpkc_get_last_error(&strError[0], neededSize); + throw std::runtime_error(strError); +} + +static inline std::string to_cppstring(const char* psz) { + return psz == nullptr ? "" : psz; +} + +static inline char* to_cstring(const std::string& str) { + return const_cast(str.c_str()); +} + +static inline char* to_cstring_opt(const std::optional& str) { + return str.has_value() ? to_cstring(str.value()) : nullptr; +} + +static inline std::optional to_cppstring_opt(const char* psz) { + return psz == nullptr ? std::nullopt : std::optional(psz); +} + +static inline bool to_cppbool(bool b) { return b; } +static inline bool to_cbool(bool b) { return b; } +static inline uint64_t to_cu64(uint64_t i) { return i; } +static inline uint64_t to_cppu64(uint64_t i) { return i; } + +// !! AUTO-GENERATED-START CPP_TYPES + +/// VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth). +struct VelopackLocatorConfig { + /// The root directory of the current app. + std::string RootAppDir; + /// The path to the Update.exe binary. + std::string UpdateExePath; + /// The path to the packages' directory. + std::string PackagesDir; + /// The current app manifest. + std::string ManifestPath; + /// The directory containing the application's user binaries. + std::string CurrentBinaryDir; + /// Whether the current application is portable or installed. + bool IsPortable; +}; + +/// An individual Velopack asset, could refer to an asset on-disk or in a remote package feed. +struct VelopackAsset { + /// The name or Id of the package containing this release. + std::string PackageId; + /// The version of this release. + std::string Version; + /// The type of asset (eg. "Full" or "Delta"). + std::string Type; + /// The filename of the update package containing this release. + std::string FileName; + /// The SHA1 checksum of the update package containing this release. + std::string SHA1; + /// The SHA256 checksum of the update package containing this release. + std::string SHA256; + /// The size in bytes of the update package containing this release. + uint64_t Size; + /// The release notes in markdown format, as passed to Velopack when packaging the release. This may be an empty string. + std::string NotesMarkdown; + /// The release notes in HTML format, transformed from Markdown when packaging the release. This may be an empty string. + std::string NotesHtml; +}; + +/// Holds information about the current version and pending updates, such as how many there are, and access to release notes. +struct UpdateInfo { + /// The available version that we are updating to. + VelopackAsset TargetFullRelease; + /// True if the update is a version downgrade or lateral move (such as when switching channels to the same version number). + /// In this case, only full updates are allowed, and any local packages on disk newer than the downloaded version will be + /// deleted. + bool IsDowngrade; +}; + +/// Options to customise the behaviour of UpdateManager. +struct UpdateOptions { + /// Allows UpdateManager to update to a version that's lower than the current version (i.e. downgrading). + /// This could happen if a release has bugs and was retracted from the release feed, or if you're using + /// ExplicitChannel to switch channels to another channel where the latest version on that + /// channel is lower than the current version. + bool AllowVersionDowngrade; + /// **This option should usually be left None**.
+ /// Overrides the default channel used to fetch updates. + /// The default channel will be whatever channel was specified on the command line when building this release. + /// For example, if the current release was packaged with '--channel beta', then the default channel will be 'beta'. + /// This allows users to automatically receive updates from the same channel they installed from. This options + /// allows you to explicitly switch channels, for example if the user wished to switch back to the 'stable' channel + /// without having to reinstall the application. + std::optional ExplicitChannel; +}; + +static inline vpkc_locator_config_t to_c(const VelopackLocatorConfig& dto) { + return { + to_cstring(dto.RootAppDir), + to_cstring(dto.UpdateExePath), + to_cstring(dto.PackagesDir), + to_cstring(dto.ManifestPath), + to_cstring(dto.CurrentBinaryDir), + to_cbool(dto.IsPortable), + }; +} + +static inline VelopackLocatorConfig to_cpp(const vpkc_locator_config_t& dto) { + return { + to_cppstring(dto.RootAppDir), + to_cppstring(dto.UpdateExePath), + to_cppstring(dto.PackagesDir), + to_cppstring(dto.ManifestPath), + to_cppstring(dto.CurrentBinaryDir), + to_cppbool(dto.IsPortable), + }; +} + +static inline vpkc_asset_t to_c(const VelopackAsset& dto) { + return { + to_cstring(dto.PackageId), + to_cstring(dto.Version), + to_cstring(dto.Type), + to_cstring(dto.FileName), + to_cstring(dto.SHA1), + to_cstring(dto.SHA256), + to_cu64(dto.Size), + to_cstring(dto.NotesMarkdown), + to_cstring(dto.NotesHtml), + }; +} + +static inline VelopackAsset to_cpp(const vpkc_asset_t& dto) { + return { + to_cppstring(dto.PackageId), + to_cppstring(dto.Version), + to_cppstring(dto.Type), + to_cppstring(dto.FileName), + to_cppstring(dto.SHA1), + to_cppstring(dto.SHA256), + to_cppu64(dto.Size), + to_cppstring(dto.NotesMarkdown), + to_cppstring(dto.NotesHtml), + }; +} + +static inline vpkc_update_info_t to_c(const UpdateInfo& dto) { + return { + to_c(dto.TargetFullRelease), + to_cbool(dto.IsDowngrade), + }; +} + +static inline UpdateInfo to_cpp(const vpkc_update_info_t& dto) { + return { + to_cpp(dto.TargetFullRelease), + to_cppbool(dto.IsDowngrade), + }; +} + +static inline vpkc_update_options_t to_c(const UpdateOptions& dto) { + return { + to_cbool(dto.AllowVersionDowngrade), + to_cstring_opt(dto.ExplicitChannel), + }; +} + +static inline UpdateOptions to_cpp(const vpkc_update_options_t& dto) { + return { + to_cppbool(dto.AllowVersionDowngrade), + to_cppstring_opt(dto.ExplicitChannel), + }; +} +// !! AUTO-GENERATED-END CPP_TYPES + +static inline char** to_cstring_array(const std::vector& vec) { + char** result = new char*[vec.size()]; + for (size_t i = 0; i < vec.size(); ++i) { + result[i] = new char[vec[i].size() + 1]; // +1 for null-terminator +#ifdef _WIN32 + strcpy_s(result[i], vec[i].size() + 1, vec[i].c_str()); // Copy string content +#else + strcpy(result[i], vec[i].c_str()); // Copy string content +#endif + } + return result; +} + +static inline void free_cstring_array(char** arr, size_t size) { + for (size_t i = 0; i < size; ++i) { + delete[] arr[i]; + } + delete[] arr; +} + +/** + * VelopackApp helps you to handle app activation events correctly. + * This should be used as early as possible in your application startup code. + * (eg. the beginning of main() or wherever your entry point is) + */ +class VelopackApp { +private: + VelopackApp() {}; +public: + /** + * Build a new VelopackApp instance. + */ + static VelopackApp Build() { + return VelopackApp(); + }; + + /** + * Set whether to automatically apply downloaded updates on startup. This is ON by default. + */ + VelopackApp& SetAutoApplyOnStartup(bool bAutoApply) { + vpkc_app_set_auto_apply_on_startup(bAutoApply); + return *this; + }; + + /** + * Override the command line arguments used by VelopackApp. (by default this is env::args().skip(1)) + */ + VelopackApp& SetArgs(const std::vector& args) { + char** pArgs = to_cstring_array(args); + vpkc_app_set_args(pArgs, args.size()); + free_cstring_array(pArgs, args.size()); + return *this; + }; + + /** + * VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth). + */ + VelopackApp& SetLocator(const VelopackLocatorConfig& locator) { + vpkc_locator_config_t vpkc_locator = to_c(locator); + vpkc_app_set_locator(&vpkc_locator); + return *this; + }; + + /** + * WARNING: FastCallback hooks are run during critical stages of Velopack operations. + * Your code will be run and then the process will exit. + * If your code has not completed within 30 seconds, it will be terminated. + * Only supported on windows; On other operating systems, this will never be called. + */ + VelopackApp& OnAfterInstall(vpkc_hook_callback_t cbInstall) { + vpkc_app_set_hook_after_install(cbInstall); + return *this; + }; + + /** + * WARNING: FastCallback hooks are run during critical stages of Velopack operations. + * Your code will be run and then the process will exit. + * If your code has not completed within 30 seconds, it will be terminated. + * Only supported on windows; On other operating systems, this will never be called. + */ + VelopackApp& OnBeforeUninstall(vpkc_hook_callback_t cbInstall) { + vpkc_app_set_hook_before_uninstall(cbInstall); + return *this; + }; + + /** + * WARNING: FastCallback hooks are run during critical stages of Velopack operations. + * Your code will be run and then the process will exit. + * If your code has not completed within 30 seconds, it will be terminated. + * Only supported on windows; On other operating systems, this will never be called. + */ + VelopackApp& OnBeforeUpdate(vpkc_hook_callback_t cbInstall) { + vpkc_app_set_hook_before_update(cbInstall); + return *this; + }; + + /** + * WARNING: FastCallback hooks are run during critical stages of Velopack operations. + * Your code will be run and then the process will exit. + * If your code has not completed within 30 seconds, it will be terminated. + * Only supported on windows; On other operating systems, this will never be called. + */ + VelopackApp& OnAfterUpdate(vpkc_hook_callback_t cbInstall) { + vpkc_app_set_hook_after_update(cbInstall); + return *this; + }; + + /** + * This hook is triggered when the application is started for the first time after installation. + */ + VelopackApp& OnFirstRun(vpkc_hook_callback_t cbInstall) { + vpkc_app_set_hook_first_run(cbInstall); + return *this; + }; + + /** + * This hook is triggered when the application is restarted by Velopack after installing updates. + */ + VelopackApp& OnRestarted(vpkc_hook_callback_t cbInstall) { + vpkc_app_set_hook_restarted(cbInstall); + return *this; + }; + + /** + * Runs the Velopack startup logic. This should be the first thing to run in your app. + * In some circumstances it may terminate/restart the process to perform tasks. + */ + void Run(void* pUserData = 0) { + vpkc_app_run(pUserData); + }; +}; + +/** + * Provides functionality for checking for updates, downloading updates, and applying updates to the current application. + */ +class UpdateManager { +private: + vpkc_update_manager_t* m_pManager = 0; +public: + /** + * Create a new UpdateManager instance. + * @param urlOrPath Location of the update server or path to the local update directory. + * @param options Optional extra configuration for update manager. + * @param locator Override the default locator configuration (usually used for testing / mocks). + */ + UpdateManager(const std::string& urlOrPath, const UpdateOptions* options = nullptr, const VelopackLocatorConfig* locator = nullptr) { + vpkc_update_options_t* pOptions = nullptr; + if (options != nullptr) { + vpkc_update_options_t vpkc_options = to_c(*options); + pOptions = &vpkc_options; + } + + vpkc_locator_config_t* pLocator = nullptr; + if (locator != nullptr) { + vpkc_locator_config_t vpkc_locator = to_c(*locator); + pLocator = &vpkc_locator; + } + + if (!vpkc_new_update_manager(urlOrPath.c_str(), pOptions, pLocator, &m_pManager)) { + throw_last_error(); + } + }; + + /** + * Destructor for UpdateManager. + */ + ~UpdateManager() { + vpkc_free_update_manager(m_pManager); + }; + + /** + * Returns whether the app is in portable mode. On Windows this can be true or false. + * On MacOS and Linux this will always be true. + */ + bool IsPortable() noexcept { + return vpkc_is_portable(m_pManager); + }; + + /** + * Returns the currently installed version of the app. + */ + std::string GetCurrentVersion() noexcept { + size_t neededSize = vpkc_get_current_version(m_pManager, nullptr, 0); + std::string strVersion(neededSize, '\0'); + vpkc_get_current_version(m_pManager, &strVersion[0], neededSize); + return strVersion; + }; + + /** + * Returns the currently installed app id. + */ + std::string GetAppId() noexcept { + size_t neededSize = vpkc_get_app_id(m_pManager, nullptr, 0); + std::string strId(neededSize, '\0'); + vpkc_get_app_id(m_pManager, &strId[0], neededSize); + return strId; + }; + + /** + * Returns an UpdateInfo object if there is an update downloaded which still needs to be applied. + * You can pass the UpdateInfo object to waitExitThenApplyUpdate to apply the update. + */ + std::optional UpdatePendingRestart() noexcept { + vpkc_asset_t asset; + if (vpkc_update_pending_restart(m_pManager, &asset)) { + VelopackAsset cpp_asset = to_cpp(asset); + vpkc_free_asset(&asset); + return cpp_asset; + } + return std::nullopt; + }; + + /** + * Checks for updates, returning None if there are none available. If there are updates available, this method will return an + * UpdateInfo object containing the latest available release, and any delta updates that can be applied if they are available. + */ + std::optional CheckForUpdates() { + vpkc_update_info_t update; + vpkc_update_check_t result = vpkc_check_for_updates(m_pManager, &update); + switch (result) { + case vpkc_update_check_t::UPDATE_ERROR: + throw_last_error(); + return std::nullopt; + case vpkc_update_check_t::NO_UPDATE_AVAILABLE: + case vpkc_update_check_t::REMOTE_IS_EMPTY: + return std::nullopt; + case vpkc_update_check_t::UPDATE_AVAILABLE: + UpdateInfo cpp_info = to_cpp(update); + vpkc_free_update_info(&update); + return cpp_info; + } + return std::nullopt; + }; + + /** + * Downloads the specified updates to the local app packages directory. Progress is reported back to the caller via an optional Sender. + * This function will acquire a global update lock so may fail if there is already another update operation in progress. + * - If the update contains delta packages and the delta feature is enabled + * this method will attempt to unpack and prepare them. + * - If there is no delta update available, or there is an error preparing delta + * packages, this method will fall back to downloading the full version of the update. + */ + void DownloadUpdates(const UpdateInfo& update, vpkc_progress_callback_t progress = nullptr, void* pUserData = 0) { + vpkc_update_info_t vpkc_update = to_c(update); + if (!vpkc_download_updates(m_pManager, &vpkc_update, progress, pUserData)) { + throw_last_error(); + } + }; + + /** + * This will launch the Velopack updater and tell it to wait for this program to exit gracefully. + * You should then clean up any state and exit your app. The updater will apply updates and then + * optionally restart your app. The updater will only wait for 60 seconds before giving up. + */ + void WaitExitThenApplyUpdate(const VelopackAsset& asset, bool silent = false, bool restart = true, std::vector restartArgs = {}) { + char** pRestartArgs = to_cstring_array(restartArgs); + vpkc_asset_t vpkc_asset = to_c(asset); + bool result = vpkc_wait_exit_then_apply_update(m_pManager, &vpkc_asset, silent, restart, pRestartArgs, restartArgs.size()); + free_cstring_array(pRestartArgs, restartArgs.size()); + + if (!result) { + throw_last_error(); + } + }; + + /** + * This will launch the Velopack updater and tell it to wait for this program to exit gracefully. + * You should then clean up any state and exit your app. The updater will apply updates and then + * optionally restart your app. The updater will only wait for 60 seconds before giving up. + */ + void WaitExitThenApplyUpdate(const UpdateInfo& asset, bool silent = false, bool restart = true, std::vector restartArgs = {}) { + this->WaitExitThenApplyUpdate(asset.TargetFullRelease, silent, restart, restartArgs); + }; +}; + +} // namespace Velopack + +#endif // VELOPACK_HPP \ No newline at end of file diff --git a/src/lib-cpp/src/lib.rs b/src/lib-cpp/src/lib.rs index 1a073cc2..50d9aa7d 100644 --- a/src/lib-cpp/src/lib.rs +++ b/src/lib-cpp/src/lib.rs @@ -9,23 +9,16 @@ mod types; use types::*; use anyhow::{anyhow, bail}; -use std::ffi::{c_char, c_void, CString}; +use std::ffi::CString; +use libc::{size_t, c_char, c_void}; use velopack::{sources, Error as VelopackError, UpdateCheck, UpdateManager, VelopackApp}; -#[repr(C)] -pub enum vpkc_update_check_t { - UPDATE_ERROR = -1, - UPDATE_AVAILABLE = 0, - NO_UPDATE_AVAILABLE = 1, - REMOTE_IS_EMPTY = 2, -} - #[no_mangle] pub extern "C" fn vpkc_new_update_manager( psz_url_or_string: *const c_char, p_options: *mut vpkc_update_options_t, p_locator: *mut vpkc_locator_config_t, - p_manager: *mut *mut c_void, + p_manager: *mut *mut vpkc_update_manager_t, ) -> bool { wrap_error(|| { let update_url = c_to_string_opt(psz_url_or_string).ok_or(anyhow!("URL or path is null"))?; @@ -34,13 +27,13 @@ pub extern "C" fn vpkc_new_update_manager( let locator = c_to_velopacklocatorconfig_opt(p_locator); let manager = UpdateManager::new(source, options, locator)?; let opaque = Box::new(UpdateManagerOpaque::new(manager)); - unsafe { *p_manager = Box::into_raw(opaque) as *mut c_void }; + unsafe { *p_manager = Box::into_raw(opaque) as *mut vpkc_update_manager_t }; Ok(()) }) } #[no_mangle] -pub extern "C" fn vpkc_get_current_version(p_manager: *mut c_void, psz_version: *mut c_char, c_version: usize) -> usize { +pub extern "C" fn vpkc_get_current_version(p_manager: *mut vpkc_update_manager_t, psz_version: *mut c_char, c_version: size_t) -> size_t { if p_manager.is_null() { return 0; } @@ -51,7 +44,7 @@ pub extern "C" fn vpkc_get_current_version(p_manager: *mut c_void, psz_version: } #[no_mangle] -pub extern "C" fn vpkc_get_app_id(p_manager: *mut c_void, psz_id: *mut c_char, c_id: usize) -> usize { +pub extern "C" fn vpkc_get_app_id(p_manager: *mut vpkc_update_manager_t, psz_id: *mut c_char, c_id: size_t) -> size_t { if p_manager.is_null() { return 0; } @@ -62,7 +55,7 @@ pub extern "C" fn vpkc_get_app_id(p_manager: *mut c_void, psz_id: *mut c_char, c } #[no_mangle] -pub extern "C" fn vpkc_is_portable(p_manager: *mut c_void) -> bool { +pub extern "C" fn vpkc_is_portable(p_manager: *mut vpkc_update_manager_t) -> bool { if p_manager.is_null() { return false; } @@ -72,7 +65,7 @@ pub extern "C" fn vpkc_is_portable(p_manager: *mut c_void) -> bool { } #[no_mangle] -pub extern "C" fn vpkc_update_pending_restart(p_manager: *mut c_void, p_asset: *mut vpkc_asset_t) -> bool { +pub extern "C" fn vpkc_update_pending_restart(p_manager: *mut vpkc_update_manager_t, p_asset: *mut vpkc_asset_t) -> bool { if p_manager.is_null() { return false; } @@ -89,7 +82,7 @@ pub extern "C" fn vpkc_update_pending_restart(p_manager: *mut c_void, p_asset: * } #[no_mangle] -pub extern "C" fn vpkc_check_for_updates(p_manager: *mut c_void, p_update: *mut vpkc_update_info_t) -> vpkc_update_check_t { +pub extern "C" fn vpkc_check_for_updates(p_manager: *mut vpkc_update_manager_t, p_update: *mut vpkc_update_info_t) -> vpkc_update_check_t { if p_manager.is_null() { set_last_error("pManager must not be null"); return vpkc_update_check_t::UPDATE_ERROR; @@ -115,7 +108,7 @@ pub extern "C" fn vpkc_check_for_updates(p_manager: *mut c_void, p_update: *mut #[no_mangle] pub extern "C" fn vpkc_download_updates( - p_manager: *mut c_void, + p_manager: *mut vpkc_update_manager_t, p_update: *mut vpkc_update_info_t, cb_progress: vpkc_progress_callback_t, p_user_data: *mut c_void, @@ -143,7 +136,7 @@ pub extern "C" fn vpkc_download_updates( // Try to receive progress updates without blocking match progress_receiver.try_recv() { Ok(progress) => { - cb_progress(p_user_data, progress as usize); + cb_progress(p_user_data, progress as size_t); } _ => { // No progress updates available, sleep for a short time to avoid busy-waiting @@ -171,12 +164,12 @@ pub extern "C" fn vpkc_download_updates( #[no_mangle] pub extern "C" fn vpkc_wait_exit_then_apply_update( - p_manager: *mut c_void, + p_manager: *mut vpkc_update_manager_t, p_asset: *mut vpkc_asset_t, b_silent: bool, b_restart: bool, p_restart_args: *mut *mut c_char, - c_restart_args: usize, + c_restart_args: size_t, ) -> bool { wrap_error(|| { if p_manager.is_null() { @@ -192,7 +185,7 @@ pub extern "C" fn vpkc_wait_exit_then_apply_update( } #[no_mangle] -pub extern "C" fn vpkc_free_update_manager(p_manager: *mut c_void) { +pub extern "C" fn vpkc_free_update_manager(p_manager: *mut vpkc_update_manager_t) { if !p_manager.is_null() { // Convert the raw pointer back into a Box to deallocate it properly let _ = unsafe { Box::from_raw(p_manager as *mut UpdateManagerOpaque) }; @@ -283,7 +276,7 @@ pub extern "C" fn vpkc_app_set_auto_apply_on_startup(b_auto_apply: bool) { } #[no_mangle] -pub extern "C" fn vpkc_app_set_args(p_args: *mut *mut c_char, c_args: usize) { +pub extern "C" fn vpkc_app_set_args(p_args: *mut *mut c_char, c_args: size_t) { update_app_options(|opt| { opt.args = c_to_string_array_opt(p_args, c_args); }); @@ -339,7 +332,7 @@ pub extern "C" fn vpkc_app_set_hook_restarted(cb_restarted: vpkc_hook_callback_t } #[no_mangle] -pub extern "C" fn vpkc_get_last_error(psz_error: *mut c_char, c_error: usize) -> usize { +pub extern "C" fn vpkc_get_last_error(psz_error: *mut c_char, c_error: size_t) -> size_t { let error = get_last_error(); return_cstr(psz_error, c_error, &error) } diff --git a/src/lib-cpp/src/types.rs b/src/lib-cpp/src/types.rs index 5849d973..1a40352f 100644 --- a/src/lib-cpp/src/types.rs +++ b/src/lib-cpp/src/types.rs @@ -1,4 +1,5 @@ -use std::ffi::{c_char, c_void, CStr, CString}; +use std::ffi::{CStr, CString}; +use libc::{c_char, c_void, size_t}; use std::path::PathBuf; use velopack::{locator::VelopackLocatorConfig, UpdateInfo, UpdateOptions, VelopackAsset}; @@ -56,7 +57,7 @@ pub unsafe fn free_pathbuf(psz: *mut *mut c_char) { free_string(psz); } -pub fn c_to_string_array_opt(p_args: *mut *mut c_char, c_args: usize) -> Option> { +pub fn c_to_string_array_opt(p_args: *mut *mut c_char, c_args: size_t) -> Option> { if p_args.is_null() || c_args == 0 { return None; } @@ -71,7 +72,7 @@ pub fn c_to_string_array_opt(p_args: *mut *mut c_char, c_args: usize) -> Option< Some(args) } -pub fn return_cstr(psz: *mut c_char, c: usize, s: &str) -> usize { +pub fn return_cstr(psz: *mut c_char, c: size_t, s: &str) -> size_t { if !psz.is_null() && c > 0 { let cstr = CString::new(s).unwrap(); let bytes = cstr.as_bytes_with_nul(); @@ -85,7 +86,17 @@ pub fn return_cstr(psz: *mut c_char, c: usize, s: &str) -> usize { return s.len(); } -pub type vpkc_progress_callback_t = extern "C" fn(p_user_data: *mut c_void, progress: usize); +#[repr(i16)] +pub enum vpkc_update_check_t { + UPDATE_ERROR = -1, + UPDATE_AVAILABLE = 0, + NO_UPDATE_AVAILABLE = 1, + REMOTE_IS_EMPTY = 2, +} + +pub type vpkc_update_manager_t = c_void; + +pub type vpkc_progress_callback_t = extern "C" fn(p_user_data: *mut c_void, progress: size_t); pub type vpkc_log_callback_t = extern "C" fn(p_user_data: *mut c_void, psz_level: *const c_char, psz_message: *const c_char); @@ -94,12 +105,19 @@ pub type vpkc_hook_callback_t = extern "C" fn(p_user_data: *mut c_void, psz_app_ // !! AUTO-GENERATED-START RUST_TYPES #[rustfmt::skip] #[repr(C)] +/// VelopackLocator provides some utility functions for locating the current app important paths (eg. path to packages, update binary, and so forth). pub struct vpkc_locator_config_t { + /// The root directory of the current app. pub RootAppDir: *mut c_char, + /// The path to the Update.exe binary. pub UpdateExePath: *mut c_char, + /// The path to the packages' directory. pub PackagesDir: *mut c_char, + /// The current app manifest. pub ManifestPath: *mut c_char, + /// The directory containing the application's user binaries. pub CurrentBinaryDir: *mut c_char, + /// Whether the current application is portable or installed. pub IsPortable: bool, } @@ -144,15 +162,25 @@ pub unsafe fn free_velopacklocatorconfig(obj: *mut vpkc_locator_config_t) { #[rustfmt::skip] #[repr(C)] +/// An individual Velopack asset, could refer to an asset on-disk or in a remote package feed. pub struct vpkc_asset_t { + /// The name or Id of the package containing this release. pub PackageId: *mut c_char, + /// The version of this release. pub Version: *mut c_char, + /// The type of asset (eg. "Full" or "Delta"). pub Type: *mut c_char, + /// The filename of the update package containing this release. pub FileName: *mut c_char, + /// The SHA1 checksum of the update package containing this release. pub SHA1: *mut c_char, + /// The SHA256 checksum of the update package containing this release. pub SHA256: *mut c_char, + /// The size in bytes of the update package containing this release. pub Size: u64, + /// The release notes in markdown format, as passed to Velopack when packaging the release. This may be an empty string. pub NotesMarkdown: *mut c_char, + /// The release notes in HTML format, transformed from Markdown when packaging the release. This may be an empty string. pub NotesHtml: *mut c_char, } @@ -206,8 +234,13 @@ pub unsafe fn free_velopackasset(obj: *mut vpkc_asset_t) { #[rustfmt::skip] #[repr(C)] +/// Holds information about the current version and pending updates, such as how many there are, and access to release notes. pub struct vpkc_update_info_t { + /// The available version that we are updating to. pub TargetFullRelease: vpkc_asset_t, + /// True if the update is a version downgrade or lateral move (such as when switching channels to the same version number). + /// In this case, only full updates are allowed, and any local packages on disk newer than the downloaded version will be + /// deleted. pub IsDowngrade: bool, } @@ -240,8 +273,20 @@ pub unsafe fn free_updateinfo(obj: *mut vpkc_update_info_t) { #[rustfmt::skip] #[repr(C)] +/// Options to customise the behaviour of UpdateManager. pub struct vpkc_update_options_t { + /// Allows UpdateManager to update to a version that's lower than the current version (i.e. downgrading). + /// This could happen if a release has bugs and was retracted from the release feed, or if you're using + /// ExplicitChannel to switch channels to another channel where the latest version on that + /// channel is lower than the current version. pub AllowVersionDowngrade: bool, + /// **This option should usually be left None**.
+ /// Overrides the default channel used to fetch updates. + /// The default channel will be whatever channel was specified on the command line when building this release. + /// For example, if the current release was packaged with '--channel beta', then the default channel will be 'beta'. + /// This allows users to automatically receive updates from the same channel they installed from. This options + /// allows you to explicitly switch channels, for example if the user wished to switch back to the 'stable' channel + /// without having to reinstall the application. pub ExplicitChannel: *mut c_char, } diff --git a/src/lib-cpp/type-generator/Program.cs b/src/lib-cpp/type-generator/Program.cs index 96ce8b53..0f545a94 100644 --- a/src/lib-cpp/type-generator/Program.cs +++ b/src/lib-cpp/type-generator/Program.cs @@ -50,7 +50,7 @@ if (desiredStructs.Length != availableStructs.Count) { // string rustCppLib = Path.Combine(libcppDir, "src", "lib.rs"); string rustTypes = Path.Combine(libcppDir, "src", "types.rs"); //string rustCppMap = Path.Combine(libcppDir, "src", "map.rs"); -string rustCppInclude = Path.Combine(libcppDir, "include", "Velopack.h"); +string rustCppInclude = Path.Combine(libcppDir, "include", "Velopack.hpp"); //string rustBridgeC = Path.Combine(libcppDir, "src", "bridge.cc"); //Console.WriteLine("Generating bridge dtos"); @@ -65,12 +65,12 @@ string rustCppInclude = Path.Combine(libcppDir, "include", "Velopack.h"); // Templates.WriteBridgeToCoreMapping(desiredStructs, sbBridgeMapping, rs); //} -Console.WriteLine("Generating C types"); -var cTypes = new IndentStringBuilder(); -cTypes.AppendLine(); -foreach (var rs in availableStructs) { - Templates.WriteBasicC(basic_libc_names, cTypes, rs); -} +// Console.WriteLine("Generating C types"); +// var cTypes = new IndentStringBuilder(); +// cTypes.AppendLine(); +// foreach (var rs in availableStructs) { +// Templates.WriteBasicC(basic_libc_names, cTypes, rs); +// } Console.WriteLine("Generating C++ types"); var cppTypes = new IndentStringBuilder(); @@ -98,7 +98,7 @@ Console.WriteLine("Writing all to file"); //Util.ReplaceTextInFile(rustCppLib, "BRIDGE_DTOS", sbBridgeDto.ToString()); //Util.ReplaceTextInFile(rustCppMap, "CORE_MAPPING", sbBridgeMapping.ToString()); Util.ReplaceTextInFile(rustTypes, "RUST_TYPES", rustCTypes.ToString()); -Util.ReplaceTextInFile(rustCppInclude, "C_TYPES", cTypes.ToString()); +// Util.ReplaceTextInFile(rustCppInclude, "C_TYPES", cTypes.ToString()); Util.ReplaceTextInFile(rustCppInclude, "CPP_TYPES", cppTypes.ToString()); //Util.ReplaceTextInFile(rustBridgeC, "BRIDGE_MAPPING", cToBridgeMapping.ToString()); diff --git a/src/lib-cpp/type-generator/Templates.cs b/src/lib-cpp/type-generator/Templates.cs index 0e6e60d6..2d573390 100644 --- a/src/lib-cpp/type-generator/Templates.cs +++ b/src/lib-cpp/type-generator/Templates.cs @@ -55,9 +55,11 @@ var cName = nameMap[rs.Name]; sb.AppendLine("#[rustfmt::skip]"); sb.AppendLine($"#[repr(C)]"); + sb.AppendDocComment(rs.DocComment); sb.AppendLine($"pub struct {cName} {{"); using (sb.Indent()) { foreach (var field in rs.Fields) { + sb.AppendDocComment(field.DocComment); sb.AppendLine($"pub {field.Name}: {GetBasicCTypeInRust(nameMap, field.Type)},"); } }