Skip to content

Commit

Permalink
feat: allow Linux dependencies to be specified via a file path (#254)
Browse files Browse the repository at this point in the history
This makes it possible to dynamically generate the list of dependencies for
Linux Debian and pacman package formats.
  • Loading branch information
kevinaboos authored Jul 2, 2024
1 parent de4dcca commit c6207bb
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 54 deletions.
9 changes: 9 additions & 0 deletions .changes/pr254.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"cargo-packager": "minor"
"@crabnebula/packager": "minor"
---

Allow Linux dependencies to be specified via a file path instead of just a direct String.
This enables the list of dependencies to by dynamically generated for both Debian `.deb` packages and pacman packages,
which can relieve the app developer from the burden of manually maintaining a fixed list of dependencies.

52 changes: 35 additions & 17 deletions bindings/packager/nodejs/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -771,18 +771,19 @@
"additionalProperties": false
},
"DebianConfig": {
"description": "The Linux debian configuration.",
"description": "The Linux Debian configuration.",
"type": "object",
"properties": {
"depends": {
"description": "The list of debian dependencies.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
"description": "The list of Debian dependencies.",
"anyOf": [
{
"$ref": "#/definitions/Dependencies"
},
{
"type": "null"
}
]
},
"desktopTemplate": {
"description": "Path to a custom desktop file Handlebars template.\n\nAvailable variables: `categories`, `comment` (optional), `exec`, `icon` and `name`.\n\nDefault file contents: ```text [Desktop Entry] Categories={{categories}} {{#if comment}} Comment={{comment}} {{/if}} Exec={{exec}} {{exec_arg}} Icon={{icon}} Name={{name}} Terminal=false Type=Application {{#if mime_type}} MimeType={{mime_type}} {{/if}} ```\n\nThe `{{exec_arg}}` will be set to: * \"%F\", if at least one [Config::file_associations] was specified but no deep link protocols were given. * The \"%F\" arg means that your application can be invoked with multiple file paths. * \"%U\", if at least one [Config::deep_link_protocols] was specified. * The \"%U\" arg means that your application can be invoked with multiple URLs. * If both [Config::file_associations] and [Config::deep_link_protocols] were specified, the \"%U\" arg will be used, causing the file paths to be passed to your app as `file://` URLs. * An empty string \"\" (nothing) if neither are given. * This means that your application will never be invoked with any URLs or file paths.\n\nTo specify a custom `exec_arg`, just use plaintext directly instead of `{{exec_arg}}`: ```text Exec={{exec}} %u ```\n\nSee more here: <https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables>.",
Expand Down Expand Up @@ -818,6 +819,22 @@
},
"additionalProperties": false
},
"Dependencies": {
"description": "A list of dependencies specified as either a list of Strings or as a path to a file that lists the dependencies, one per line.",
"anyOf": [
{
"description": "The list of dependencies provided directly as a vector of Strings.",
"type": "array",
"items": {
"type": "string"
}
},
{
"description": "A path to the file containing the list of dependences, formatted as one per line: ```text libc6 libxcursor1 libdbus-1-3 libasyncns0 ... ```",
"type": "string"
}
]
},
"AppImageConfig": {
"description": "The Linux AppImage configuration.",
"type": "object",
Expand Down Expand Up @@ -890,14 +907,15 @@
}
},
"depends": {
"description": "List of softwares that must be installed for the app to build and run.\n\nSee : <https://wiki.archlinux.org/title/PKGBUILD#provides>",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
"description": "List of softwares that must be installed for the app to build and run.\n\nSee : <https://wiki.archlinux.org/title/PKGBUILD#depends>",
"anyOf": [
{
"$ref": "#/definitions/Dependencies"
},
{
"type": "null"
}
]
},
"provides": {
"description": "Additional packages that are provided by this app.\n\nSee : <https://wiki.archlinux.org/title/PKGBUILD#provides>",
Expand Down
52 changes: 35 additions & 17 deletions crates/packager/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -771,18 +771,19 @@
"additionalProperties": false
},
"DebianConfig": {
"description": "The Linux debian configuration.",
"description": "The Linux Debian configuration.",
"type": "object",
"properties": {
"depends": {
"description": "The list of debian dependencies.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
"description": "The list of Debian dependencies.",
"anyOf": [
{
"$ref": "#/definitions/Dependencies"
},
{
"type": "null"
}
]
},
"desktopTemplate": {
"description": "Path to a custom desktop file Handlebars template.\n\nAvailable variables: `categories`, `comment` (optional), `exec`, `icon` and `name`.\n\nDefault file contents: ```text [Desktop Entry] Categories={{categories}} {{#if comment}} Comment={{comment}} {{/if}} Exec={{exec}} {{exec_arg}} Icon={{icon}} Name={{name}} Terminal=false Type=Application {{#if mime_type}} MimeType={{mime_type}} {{/if}} ```\n\nThe `{{exec_arg}}` will be set to: * \"%F\", if at least one [Config::file_associations] was specified but no deep link protocols were given. * The \"%F\" arg means that your application can be invoked with multiple file paths. * \"%U\", if at least one [Config::deep_link_protocols] was specified. * The \"%U\" arg means that your application can be invoked with multiple URLs. * If both [Config::file_associations] and [Config::deep_link_protocols] were specified, the \"%U\" arg will be used, causing the file paths to be passed to your app as `file://` URLs. * An empty string \"\" (nothing) if neither are given. * This means that your application will never be invoked with any URLs or file paths.\n\nTo specify a custom `exec_arg`, just use plaintext directly instead of `{{exec_arg}}`: ```text Exec={{exec}} %u ```\n\nSee more here: <https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables>.",
Expand Down Expand Up @@ -818,6 +819,22 @@
},
"additionalProperties": false
},
"Dependencies": {
"description": "A list of dependencies specified as either a list of Strings or as a path to a file that lists the dependencies, one per line.",
"anyOf": [
{
"description": "The list of dependencies provided directly as a vector of Strings.",
"type": "array",
"items": {
"type": "string"
}
},
{
"description": "A path to the file containing the list of dependences, formatted as one per line: ```text libc6 libxcursor1 libdbus-1-3 libasyncns0 ... ```",
"type": "string"
}
]
},
"AppImageConfig": {
"description": "The Linux AppImage configuration.",
"type": "object",
Expand Down Expand Up @@ -890,14 +907,15 @@
}
},
"depends": {
"description": "List of softwares that must be installed for the app to build and run.\n\nSee : <https://wiki.archlinux.org/title/PKGBUILD#provides>",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
"description": "List of softwares that must be installed for the app to build and run.\n\nSee : <https://wiki.archlinux.org/title/PKGBUILD#depends>",
"anyOf": [
{
"$ref": "#/definitions/Dependencies"
},
{
"type": "null"
}
]
},
"provides": {
"description": "Additional packages that are provided by this app.\n\nSee : <https://wiki.archlinux.org/title/PKGBUILD#provides>",
Expand Down
88 changes: 77 additions & 11 deletions crates/packager/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,14 @@ impl DeepLinkProtocol {
}
}

/// The Linux debian configuration.
/// The Linux Debian configuration.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[non_exhaustive]
pub struct DebianConfig {
/// The list of debian dependencies.
pub depends: Option<Vec<String>>,
/// The list of Debian dependencies.
pub depends: Option<Dependencies>,
/// Path to a custom desktop file Handlebars template.
///
/// Available variables: `categories`, `comment` (optional), `exec`, `icon` and `name`.
Expand Down Expand Up @@ -238,14 +238,25 @@ impl DebianConfig {
Self::default()
}

/// Set the list of debian dependencies.
/// Set the list of Debian dependencies directly using an iterator of strings.
pub fn depends<I, S>(mut self, depends: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.depends
.replace(depends.into_iter().map(Into::into).collect());
self.depends.replace(Dependencies::List(
depends.into_iter().map(Into::into).collect(),
));
self
}

/// Set the list of Debian dependencies indirectly via a path to a file,
/// which must contain one dependency (a package name) per line.
pub fn depends_path<P>(mut self, path: P) -> Self
where
P: Into<PathBuf>,
{
self.depends.replace(Dependencies::Path(path.into()));
self
}

Expand Down Expand Up @@ -305,6 +316,48 @@ impl DebianConfig {
}
}

/// A list of dependencies specified as either a list of Strings
/// or as a path to a file that lists the dependencies, one per line.
#[derive(Debug, Clone, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(untagged)]
#[non_exhaustive]
pub enum Dependencies {
/// The list of dependencies provided directly as a vector of Strings.
List(Vec<String>),
/// A path to the file containing the list of dependences, formatted as one per line:
/// ```text
/// libc6
/// libxcursor1
/// libdbus-1-3
/// libasyncns0
/// ...
/// ```
Path(PathBuf),
}
impl Dependencies {
/// Returns the dependencies as a list of Strings.
pub fn to_list(&self) -> crate::Result<Vec<String>> {
match self {
Self::List(v) => Ok(v.clone()),
Self::Path(path) => {
let trimmed_lines = std::fs::read_to_string(path)?
.lines()
.filter_map(|line| {
let trimmed = line.trim();
if !trimmed.is_empty() {
Some(trimmed.to_owned())
} else {
None
}
})
.collect();
Ok(trimmed_lines)
}
}
}
}

/// The Linux AppImage configuration.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
Expand Down Expand Up @@ -414,8 +467,8 @@ pub struct PacmanConfig {
pub files: Option<HashMap<String, String>>,
/// List of softwares that must be installed for the app to build and run.
///
/// See : <https://wiki.archlinux.org/title/PKGBUILD#provides>
pub depends: Option<Vec<String>>,
/// See : <https://wiki.archlinux.org/title/PKGBUILD#depends>
pub depends: Option<Dependencies>,
/// Additional packages that are provided by this app.
///
/// See : <https://wiki.archlinux.org/title/PKGBUILD#provides>
Expand Down Expand Up @@ -456,16 +509,29 @@ impl PacmanConfig {
);
self
}
/// Set the list of pacman dependencies.

/// Set the list of pacman dependencies directly using an iterator of strings.
pub fn depends<I, S>(mut self, depends: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.depends
.replace(depends.into_iter().map(Into::into).collect());
self.depends.replace(Dependencies::List(
depends.into_iter().map(Into::into).collect(),
));
self
}

/// Set the list of pacman dependencies indirectly via a path to a file,
/// which must contain one dependency (a package name) per line.
pub fn depends_path<P>(mut self, path: P) -> Self
where
P: Into<PathBuf>,
{
self.depends.replace(Dependencies::Path(path.into()));
self
}

/// Set the list of additional packages that are provided by this app.
pub fn provides<I, S>(mut self, provides: I) -> Self
where
Expand Down
12 changes: 5 additions & 7 deletions crates/packager/src/package/deb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,13 +294,11 @@ fn generate_control_file(
if let Some(homepage) = &config.homepage {
writeln!(file, "Homepage: {}", homepage)?;
}
let dependencies = config
.deb()
.cloned()
.and_then(|d| d.depends)
.unwrap_or_default();
if !dependencies.is_empty() {
writeln!(file, "Depends: {}", dependencies.join(", "))?;
if let Some(depends) = config.deb().and_then(|d| d.depends.as_ref()) {
let dependencies = depends.to_list()?;
if !dependencies.is_empty() {
writeln!(file, "Depends: {}", dependencies.join(", "))?;
}
}

writeln!(
Expand Down
4 changes: 2 additions & 2 deletions crates/packager/src/package/pacman/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ fn generate_pkgbuild_file(

let dependencies = config
.pacman()
.and_then(|d| d.depends.clone())
.unwrap_or_default();
.and_then(|d| d.depends.as_ref())
.map_or_else(|| Ok(Vec::new()), |d| d.to_list())?;
writeln!(file, "depends=({})", dependencies.join(" \n"))?;

let provides = config
Expand Down

0 comments on commit c6207bb

Please sign in to comment.