Implemented multi-binary app packaging (#648)
This commit is contained in:
committed by
GitHub
parent
2496dea5c2
commit
8556103eb1
@@ -12,8 +12,8 @@ extern "C" {
|
||||
/**
|
||||
* Computes the install directory for @a app_id (does not check whether anything is actually
|
||||
* installed there).
|
||||
* @param[out] path always NULL-terminated on return, even on failure (empty string if
|
||||
* @a path_size == 0 - nothing is written in that case; otherwise at least "" is written)
|
||||
* @param[out] path always NULL-terminated on return, even on failure. Empty when @a path_size
|
||||
* is 0, since nothing is written in that case.
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_BUFFER_OVERFLOW @a path_size is too small to hold the path (including the
|
||||
* NULL terminator)
|
||||
@@ -22,14 +22,15 @@ extern "C" {
|
||||
error_t app_get_install_path(const char* app_id, char* path, size_t path_size);
|
||||
|
||||
/**
|
||||
* Installs an app from a tarball at @a source_path: extracts it into the app install directory,
|
||||
* parses the extracted manifest.properties (see app/metadata.h) to determine its id, then
|
||||
* registers it with app_manager_add() as an AppLocation{APP_LOCATION_PATH, <install dir>} app.
|
||||
* If an app with the same id is already installed (via a previous app_install() call), it is
|
||||
* uninstalled first - stopped if running, its old install directory removed - before the new
|
||||
* one takes its place.
|
||||
* @param[in] source_path path to a tar file containing the app (must have manifest.properties
|
||||
* at its root)
|
||||
* Installs a package from a tarball at @a source_path: extracts it into the package's install
|
||||
* directory, parses the extracted manifest.properties (see app/package_manifest.h) into a
|
||||
* PackageManifest and one or more AppManifestBindings, then registers each with app_manager_add()
|
||||
* as an AppLocation{APP_LOCATION_PATH, <binary path>} app.
|
||||
* If a package with the same id is already installed (via a previous app_install() call), it is
|
||||
* uninstalled first: every one of its apps stopped if running, then its old install directory
|
||||
* removed, before the new one takes its place.
|
||||
* @param[in] source_path path to a tar file containing the package (must have
|
||||
* manifest.properties at its root)
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_FOUND @a source_path doesn't exist / can't be read
|
||||
* @retval ERROR_INVALID_ARGUMENT the tarball has no valid manifest.properties at its root
|
||||
@@ -37,11 +38,11 @@ error_t app_get_install_path(const char* app_id, char* path, size_t path_size);
|
||||
error_t app_install(const char* source_path);
|
||||
|
||||
/**
|
||||
* Uninstalls a previously app_install()-ed app: stops it if currently running, deletes its
|
||||
* install directory, and unregisters it (app_manager_remove()).
|
||||
* @param[in] app_id the id the app was installed under (AppMetadata::app_id)
|
||||
* Uninstalls a previously app_install()-ed package: stops every one of its apps if currently
|
||||
* running, deletes its install directory, and unregisters all of them (app_manager_remove()).
|
||||
* @param[in] app_id the package id it was installed under (PackageManifest::id)
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_FOUND no such app was installed via app_install()
|
||||
* @retval ERROR_NOT_FOUND no such package was installed via app_install()
|
||||
*/
|
||||
error_t app_uninstall(const char* app_id);
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include "location.h"
|
||||
#include <app/manifest.h>
|
||||
#include <tactility/error.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "location.h"
|
||||
#include <tactility/error.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <app/instance.h>
|
||||
#include <app/manifest.h>
|
||||
#include <app/package_manifest.h>
|
||||
#include <app/stream.h>
|
||||
|
||||
#include <tactility/error.h>
|
||||
@@ -44,6 +45,48 @@ error_t app_manager_find_manifest(const char* id, struct AppManifest* out_manife
|
||||
typedef void (*AppManifestVisitorFn)(const struct AppManifest* manifest, void* context);
|
||||
void app_manager_for_each_manifest(AppManifestVisitorFn visitor, void* context);
|
||||
|
||||
/**
|
||||
* Registers a package for enumeration via app_manager_for_each_package(). Separate from
|
||||
* registering its apps - the caller still calls app_manager_add() for each AppManifest.
|
||||
* @param[in] app_ids the ids of the AppManifest(s) this package registered
|
||||
* @retval ERROR_INVALID_ARGUMENT a package with the same id is already registered
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t app_manager_add_package(const struct PackageManifest* package, const char* const* app_ids, size_t app_id_count);
|
||||
|
||||
/**
|
||||
* Unregisters a previously-added package. Does not touch its apps' own registrations.
|
||||
* @retval ERROR_NOT_FOUND no package with this id is registered
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t app_manager_remove_package(const char* package_id);
|
||||
|
||||
/**
|
||||
* @param[out] out_package set to a copy of the package on success
|
||||
* @retval ERROR_NOT_FOUND no package with this id is registered
|
||||
* @retval ERROR_NONE on success
|
||||
*/
|
||||
error_t app_manager_find_package(const char* package_id, struct PackageManifest* out_package);
|
||||
|
||||
/** One registered package, handed to AppPackageVisitorFn - see app_manager_for_each_package(). */
|
||||
struct AppPackage {
|
||||
struct PackageManifest package;
|
||||
/** How many entries @a app_ids points to. */
|
||||
size_t app_id_count;
|
||||
/** Valid only for the duration of the app_manager_for_each_package() call that produced
|
||||
* this - copy out what's needed before returning from the visitor. */
|
||||
const char* const* app_ids;
|
||||
};
|
||||
|
||||
typedef void (*AppPackageVisitorFn)(const struct AppPackage* pkg, void* context);
|
||||
|
||||
/**
|
||||
* Calls @a visitor once for every registered package. Iteration order is unspecified.
|
||||
* @warning Same threading contract as app_manager_for_each_manifest(): runs with an internal
|
||||
* lock held - do not call any app_manager_*() function from inside @a visitor.
|
||||
*/
|
||||
void app_manager_for_each_package(AppPackageVisitorFn visitor, void* context);
|
||||
|
||||
/** One fd-to-stream binding for app_start_with_streams() (app/start.h). Every field is passed
|
||||
* through to app_stream_subscribe() as-is; see its own doc for the ownership contracts. */
|
||||
struct AppStreamBinding {
|
||||
@@ -90,8 +133,8 @@ error_t app_manager_get_topmost_app_id(char* buffer, size_t buffer_size);
|
||||
|
||||
/**
|
||||
* Registers @a path as a directory to scan for app manifests - each direct subdirectory of
|
||||
* @a path is expected to hold a manifest.properties (see app/metadata.h), matching the layout
|
||||
* app_install() creates ({install dir}/{app_id}/manifest.properties), though this is not
|
||||
* @a path is expected to hold a manifest.properties (see app/package_manifest.h), matching the
|
||||
* layout app_install() creates ({install dir}/{package id}/manifest.properties), though this is not
|
||||
* install/uninstall - it only ever adds/removes manifest registrations, never touches files on
|
||||
* disk or running instances. No-op if @a path is already registered. Does not scan immediately -
|
||||
* call app_manager_install_path_scan() to do that.
|
||||
|
||||
@@ -11,7 +11,10 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
// Character count, excluding null terminator
|
||||
#define APP_ID_LENGTH 32
|
||||
#define APP_MANIFEST_ID_LENGTH 32
|
||||
|
||||
// Character count, excluding null terminator
|
||||
#define APP_MANIFEST_NAME_LENGTH 32
|
||||
|
||||
/** Broad classification of an app, used for grouping/launcher presentation. */
|
||||
enum AppCategory {
|
||||
@@ -45,10 +48,10 @@ struct AppStackConfig {
|
||||
|
||||
/** Describes a registrable app. One manifest exists per app id. */
|
||||
struct AppManifest {
|
||||
/** Unique app identifier. Should never be NULL. */
|
||||
const char* id;
|
||||
/** Human-readable name. Should never be NULL. */
|
||||
const char* name;
|
||||
/** Unique app identifier. Must be NULL-terminated. */
|
||||
char id[APP_MANIFEST_ID_LENGTH + 1];
|
||||
/** Human-readable name. Must be NULL-terminated. */
|
||||
char name[APP_MANIFEST_NAME_LENGTH + 1];
|
||||
enum AppCategory category;
|
||||
struct AppLocation location;
|
||||
/** Bitmask of AppManifestFlags. Most apps should leave this 0. */
|
||||
@@ -57,7 +60,9 @@ struct AppManifest {
|
||||
struct AppStackConfig stack;
|
||||
};
|
||||
|
||||
bool app_id_is_valid(const char* id);
|
||||
bool app_manifest_id_is_valid(const char* id);
|
||||
bool app_manifest_name_is_valid(const char* name);
|
||||
bool app_manifest_stack_size_is_valid(const char* value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <tactility/error.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define APP_METADATA_TARGET_SDK_LENGTH 16
|
||||
#define APP_METADATA_APP_ID_LENGTH 32
|
||||
#define APP_METADATA_APP_NAME_LENGTH 32
|
||||
#define APP_METADATA_APP_VERSION_NAME_LENGTH 16
|
||||
#define APP_METADATA_REQUIRES_DEVICE_ID_LENGTH 64
|
||||
|
||||
struct AppMetadata {
|
||||
|
||||
/**
|
||||
* The SDK version that was used to compile this app. (e.g. "0.6.0")
|
||||
* Must be NULL-terminated.
|
||||
*/
|
||||
char target_sdk[APP_METADATA_TARGET_SDK_LENGTH + 1];
|
||||
|
||||
/**
|
||||
* The identifier by which the app is launched by the system and other apps.
|
||||
* Must be NULL-terminated.
|
||||
*/
|
||||
char app_id[APP_METADATA_APP_ID_LENGTH + 1];
|
||||
|
||||
/**
|
||||
* The user-readable name of the app. Used in UI.
|
||||
* Must be NULL-terminated.
|
||||
*/
|
||||
char app_name[APP_METADATA_APP_NAME_LENGTH + 1];
|
||||
|
||||
/**
|
||||
* The version as it is displayed to the user (e.g. "1.2.0")
|
||||
* Must be NULL-terminated.
|
||||
*/
|
||||
char app_version_name[APP_METADATA_APP_VERSION_NAME_LENGTH + 1];
|
||||
|
||||
/** The technical version (must be incremented with new releases of the app) */
|
||||
uint64_t app_version_code;
|
||||
|
||||
/**
|
||||
* Comma-separated list of device ids the app is restricted to (e.g. "m5stack-tab5"), matching
|
||||
* the folder names under Devices/. Empty means unrestricted.
|
||||
* Must be NULL-terminated.
|
||||
*/
|
||||
char requires_device_id[APP_METADATA_REQUIRES_DEVICE_ID_LENGTH + 1];
|
||||
|
||||
/**
|
||||
* Stack depth (in words) for the app's task. Optional; 0 means scheduler default.
|
||||
* @warning Avoid default values: the default is conservative, which wastes memory.
|
||||
*/
|
||||
uint32_t stack_depth;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a manifest.properties file at @a path into @a out_metadata, auto-detecting the V1
|
||||
* (sectioned, e.g. "[app]id=...") or V2 (flat dot-notation, e.g. "app.id=...") format from its
|
||||
* first line.
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_FOUND the file doesn't exist / couldn't be opened
|
||||
* @retval ERROR_INVALID_ARGUMENT the file isn't a valid manifest, or a field's value doesn't fit
|
||||
* @a out_metadata's fixed-size buffers
|
||||
*/
|
||||
error_t app_metadata_parse(const char* path, struct AppMetadata* out_metadata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
#pragma once
|
||||
|
||||
#include <app/manifest.h>
|
||||
|
||||
#include <tactility/error.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PACKAGE_MANIFEST_TARGET_SDK_LENGTH 16
|
||||
#define PACKAGE_MANIFEST_ID_LENGTH 32
|
||||
#define PACKAGE_MANIFEST_VERSION_NAME_LENGTH 16
|
||||
#define PACKAGE_MANIFEST_REQUIRES_DEVICE_ID_LENGTH 64
|
||||
|
||||
/** Character count, excluding null terminator, for AppManifestBinding::binary. */
|
||||
#define APP_MANIFEST_BINARY_LENGTH 31
|
||||
|
||||
/** Largest number of AppManifest entries package_manifest_parse() can produce from a single
|
||||
* manifest.properties file. */
|
||||
#define PACKAGE_MANIFEST_MAX_APP_MANIFESTS 32
|
||||
|
||||
/** A package-level manifest.properties: SDK/version metadata that applies to the whole package,
|
||||
* not to any one of its (possibly several) apps. Not kept in memory at runtime - only used to
|
||||
* create and cache the AppManifest(s) it describes. */
|
||||
struct PackageManifest {
|
||||
/**
|
||||
* The package identifier (e.g. the install directory name). Distinct from any of its own
|
||||
* AppManifest ids.
|
||||
* Must be NULL-terminated.
|
||||
*/
|
||||
char id[PACKAGE_MANIFEST_ID_LENGTH + 1];
|
||||
|
||||
/**
|
||||
* The package version as it is displayed to the user (e.g. "1.2.0")
|
||||
* Must be NULL-terminated.
|
||||
*/
|
||||
char version_name[PACKAGE_MANIFEST_VERSION_NAME_LENGTH + 1];
|
||||
|
||||
/** The package's technical version (must be incremented with new releases). */
|
||||
uint64_t version_code;
|
||||
|
||||
/**
|
||||
* The SDK version that was used to compile this package. (e.g. "0.6.0")
|
||||
* Must be NULL-terminated.
|
||||
*/
|
||||
char target_sdk[PACKAGE_MANIFEST_TARGET_SDK_LENGTH + 1];
|
||||
|
||||
/**
|
||||
* Comma-separated list of device ids the package is restricted to (e.g. "m5stack-tab5"),
|
||||
* matching the folder names under Devices/. Empty means unrestricted.
|
||||
* Must be NULL-terminated.
|
||||
*/
|
||||
char requires_device_id[PACKAGE_MANIFEST_REQUIRES_DEVICE_ID_LENGTH + 1];
|
||||
|
||||
/** How many AppManifest entries this package's manifest.properties declared. */
|
||||
uint32_t app_manifest_count;
|
||||
};
|
||||
|
||||
/** Pairs a parsed AppManifest with the filename (without extension) it installs as under
|
||||
* bin/<platform>/ - e.g. "main" resolves to bin/posix-x86_64/main.so. Not kept anywhere at
|
||||
* runtime - same lifetime as PackageManifest, only used to hand parse results to the installer/
|
||||
* scanner, which resolve `binary` into AppManifest::location::location. */
|
||||
struct AppManifestBinding {
|
||||
struct AppManifest manifest;
|
||||
char binary[APP_MANIFEST_BINARY_LENGTH + 1];
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a manifest.properties file at @a path (flat dot-notation, e.g. "app.id=...") into
|
||||
* @a out_package and @a out_bindings.
|
||||
* @param[out] out_bindings written with up to @a bindings_capacity entries (see
|
||||
* PackageManifest::app_manifest_count for how many)
|
||||
* @param[in] bindings_capacity the capacity of @a out_bindings
|
||||
* @retval ERROR_NONE on success
|
||||
* @retval ERROR_NOT_FOUND the file doesn't exist / couldn't be opened
|
||||
* @retval ERROR_INVALID_ARGUMENT the file isn't a valid manifest, or a field's value doesn't fit
|
||||
* its fixed-size buffer
|
||||
* @retval ERROR_BUFFER_OVERFLOW the manifest declares more apps than @a bindings_capacity
|
||||
*/
|
||||
error_t app_package_manifest_parse(const char* path, struct PackageManifest* out_package, struct AppManifestBinding* out_bindings, size_t bindings_capacity);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user