From ffac4d6b57599cb96f5ddcb8fbe558ada2c920d5 Mon Sep 17 00:00:00 2001 From: Valentin Haudiquet Date: Fri, 18 Sep 2026 14:57:53 +0200 Subject: [PATCH] new: port the shell, empty, makefile and go templates to manifests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the four templates' static file bodies into .tpl files under data/templates//, referenced by their manifests' files: lists — the shell skeleton script (executable, {command}-named) with its skeleton-only debian/install mapping, the empty template's stub README, the makefile hello.c/Makefile skeleton with its skeleton-only install mapping, and go's go.mod/main.go skeleton (the go directive of go.mod stays a literal: nothing about it is answer-derived). The empty template ends up hookless — zero Rust, its registry entry points at no hooks — and src/new/templates/empty.rs is deleted. The shell and go hooks shrink to their probes (plus go's {go_import_path} context value); the makefile hooks keep only the existing-tree hint probing the packaged Makefile for a phony install: target, since that heuristic reads the tree and cannot be data. --- data/templates/empty/README.tpl | 1 + data/templates/empty/manifest.yml | 9 ++- data/templates/go/go.mod.tpl | 3 + data/templates/go/main.go.tpl | 8 ++ data/templates/go/manifest.yml | 10 ++- data/templates/makefile/Makefile.tpl | 16 ++++ data/templates/makefile/hello.c.tpl | 8 ++ data/templates/makefile/install.tpl | 1 + data/templates/makefile/manifest.yml | 15 +++- data/templates/shell/install.tpl | 1 + data/templates/shell/manifest.yml | 13 +++- data/templates/shell/script.tpl | 3 + src/new/templates/empty.rs | 84 --------------------- src/new/templates/go.rs | 48 +++--------- src/new/templates/makefile.rs | 108 +++++++++------------------ src/new/templates/mod.rs | 66 ++++++++++++++-- src/new/templates/shell.rs | 60 ++++++--------- 17 files changed, 204 insertions(+), 250 deletions(-) create mode 100644 data/templates/empty/README.tpl create mode 100644 data/templates/go/go.mod.tpl create mode 100644 data/templates/go/main.go.tpl create mode 100644 data/templates/makefile/Makefile.tpl create mode 100644 data/templates/makefile/hello.c.tpl create mode 100644 data/templates/makefile/install.tpl create mode 100644 data/templates/shell/install.tpl create mode 100644 data/templates/shell/script.tpl delete mode 100644 src/new/templates/empty.rs diff --git a/data/templates/empty/README.tpl b/data/templates/empty/README.tpl new file mode 100644 index 0000000..5cf5c3c --- /dev/null +++ b/data/templates/empty/README.tpl @@ -0,0 +1 @@ +{name} - empty base tree scaffolded by `pkh new`; there is intentionally no upstream build system here. diff --git a/data/templates/empty/manifest.yml b/data/templates/empty/manifest.yml index 2938e01..45c716a 100644 --- a/data/templates/empty/manifest.yml +++ b/data/templates/empty/manifest.yml @@ -1,6 +1,9 @@ ## The `empty` template: a metapackage (non-empty Depends list) or an ## empty base package with no build system at all — pure `dh $@` plumbing -## as a starting point for hand-written rules. +## as a starting point for hand-written rules. Pure data: no hooks, the +## metapackage Depends payload travels in the wizard answers, and the +## only upstream file is the stub README marking the tree as +## intentionally empty. ## ## Schema: see src/new/templates/mod.rs. @@ -11,4 +14,6 @@ detect: build_depends: [] architecture: all rules_dh_line: "dh $@" -files: [] +files: + - path: README + template: README.tpl diff --git a/data/templates/go/go.mod.tpl b/data/templates/go/go.mod.tpl new file mode 100644 index 0000000..7456f76 --- /dev/null +++ b/data/templates/go/go.mod.tpl @@ -0,0 +1,3 @@ +module {name} + +go 1.21 diff --git a/data/templates/go/main.go.tpl b/data/templates/go/main.go.tpl new file mode 100644 index 0000000..5be1265 --- /dev/null +++ b/data/templates/go/main.go.tpl @@ -0,0 +1,8 @@ +// Placeholder for {name}, generated by `pkh new`. +package main + +import "fmt" + +func main() { + fmt.Println("Hello from {command}!") +} diff --git a/data/templates/go/manifest.yml b/data/templates/go/manifest.yml index dbb3aec..fe59fa7 100644 --- a/data/templates/go/manifest.yml +++ b/data/templates/go/manifest.yml @@ -1,6 +1,8 @@ ## The `go` template: a Go module built through dh-golang. The logic half ## — the go.mod module-line probe and the `{go_import_path}` value below — -## lives in src/new/templates/go.rs. +## lives in src/new/templates/go.rs; the skeleton bodies are static data +## (the `go` directive of go.mod stays a literal: nothing about it is +## answer-derived, so it has no {placeholder}). ## ## Schema: see src/new/templates/mod.rs. @@ -15,4 +17,8 @@ architecture: any rules_dh_line: "dh $@ --buildsystem=golang" source_fields: XS-Go-Import-Path: "{go_import_path}" -files: [] +files: + - path: go.mod + template: go.mod.tpl + - path: main.go + template: main.go.tpl diff --git a/data/templates/makefile/Makefile.tpl b/data/templates/makefile/Makefile.tpl new file mode 100644 index 0000000..f27339a --- /dev/null +++ b/data/templates/makefile/Makefile.tpl @@ -0,0 +1,16 @@ +CC ?= cc +CFLAGS ?= -O2 -Wall -Wextra +PREFIX ?= /usr + +all: {command} + +{command}: hello.c + $(CC) $(CFLAGS) -o $@ hello.c + +install: {command} + install -Dm755 {command} $(DESTDIR)$(PREFIX)/bin/{command} + +clean: + rm -f {command} + +.PHONY: all install clean diff --git a/data/templates/makefile/hello.c.tpl b/data/templates/makefile/hello.c.tpl new file mode 100644 index 0000000..bdeb90b --- /dev/null +++ b/data/templates/makefile/hello.c.tpl @@ -0,0 +1,8 @@ +#include + +/* Placeholder for {name}, generated by `pkh new`. */ +int main(void) +{ + printf("Hello from {command}!\n"); + return 0; +} diff --git a/data/templates/makefile/install.tpl b/data/templates/makefile/install.tpl new file mode 100644 index 0000000..35d2f1c --- /dev/null +++ b/data/templates/makefile/install.tpl @@ -0,0 +1 @@ +{command} usr/bin/{command} diff --git a/data/templates/makefile/manifest.yml b/data/templates/makefile/manifest.yml index 5947115..6790fd9 100644 --- a/data/templates/makefile/manifest.yml +++ b/data/templates/makefile/manifest.yml @@ -2,7 +2,11 @@ ## debhelper's makefile buildsystem runs `make` for the build and ## `make install DESTDIR=...` when the Makefile carries an `install:` ## target (missing targets are skipped gracefully), so plain `dh $@` -## plumbing is enough here. +## plumbing is enough here. The phony-install hint of +## src/new/templates/makefile.rs (whether dh_auto_install will run +## `make install` for an existing tree) is the only logic; the skeleton +## bodies below are static data (the install mapping is rendered for +## skeletons only, whose phony install target is known by construction). ## ## Schema: see src/new/templates/mod.rs. @@ -14,4 +18,11 @@ build_depends: - build-essential architecture: any rules_dh_line: "dh $@" -files: [] +files: + - path: hello.c + template: hello.c.tpl + - path: Makefile + template: Makefile.tpl + - path: debian/install + template: install.tpl + skeleton_only: true diff --git a/data/templates/shell/install.tpl b/data/templates/shell/install.tpl new file mode 100644 index 0000000..87c20a8 --- /dev/null +++ b/data/templates/shell/install.tpl @@ -0,0 +1 @@ +{command}.sh usr/bin/{command} diff --git a/data/templates/shell/manifest.yml b/data/templates/shell/manifest.yml index 5bec072..10e9e10 100644 --- a/data/templates/shell/manifest.yml +++ b/data/templates/shell/manifest.yml @@ -2,7 +2,10 @@ ## /usr/bin with plain `dh $@` plumbing. Detection is not marker-based: the ## single-script heuristic of src/new/detect.rs (a lone *.sh or shebang ## file) maps here. The probe pre-filling the wizard answers from the -## script file name lives in src/new/templates/shell.rs. +## script file name lives in src/new/templates/shell.rs; everything else +## is the data below (the skeleton script is executable, the install +## mapping exists for skeletons only — packaging an existing tree leaves +## the mapping to the user). ## ## Schema: see src/new/templates/mod.rs. @@ -13,4 +16,10 @@ detect: build_depends: [] architecture: all rules_dh_line: "dh $@" -files: [] +files: + - path: "{command}.sh" + template: script.tpl + executable: true + - path: debian/install + template: install.tpl + skeleton_only: true diff --git a/data/templates/shell/script.tpl b/data/templates/shell/script.tpl new file mode 100644 index 0000000..b3f04e6 --- /dev/null +++ b/data/templates/shell/script.tpl @@ -0,0 +1,3 @@ +#!/bin/sh +# Placeholder for {name}, generated by `pkh new`. +echo "Hello from {command}!" diff --git a/src/new/templates/empty.rs b/src/new/templates/empty.rs deleted file mode 100644 index d06ae13..0000000 --- a/src/new/templates/empty.rs +++ /dev/null @@ -1,84 +0,0 @@ -//! The `empty` template: a metapackage or an empty base package with no -//! build system at all. -//! -//! One template with two flavors: a non-empty `Depends` list selects the -//! **metapackage** flavor (the canonical `Architecture: all`, nothing -//! compiled, the Depends list *is* the payload shape), while an empty list -//! selects the **empty base** — pure `dh $@` plumbing as a starting point -//! for hand-written rules. Both flavors are pure manifest data except for -//! the stub `README` of the skeleton, which lives here until its static -//! body moves into the manifest's `files:` list. - -use super::{OutputFile, TemplateHooks}; -use crate::new::options::NewOptions; - -/// The logic half of the empty template. -pub struct Hooks; - -/// The empty template's hooks, registered in the template registry. -pub static HOOKS: Hooks = Hooks; - -impl TemplateHooks for Hooks { - /// No upstream files beyond a stub `README` marking the tree as - /// intentionally empty. - fn skeleton_files(&self, opts: &NewOptions) -> Vec { - vec![OutputFile::new( - "README", - format!( - "{} - empty base tree scaffolded by `pkh new`; there is \ - intentionally no upstream build system here.\n", - opts.name - ), - )] - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::new::options::{License, SourceDir, TemplateId}; - - fn opts(depends: Vec) -> NewOptions { - NewOptions { - name: "metapkg".into(), - template: TemplateId::EMPTY, - source_dir: SourceDir::Skeleton, - upstream_version: "0.1.0".into(), - revision: 1, - summary: "A metapackage".into(), - long_description: "A metapackage".into(), - homepage: None, - license: License::Custom("unknown".into()), - command: "ignored".into(), - maintainer: ("Jane".into(), "jane@example.com".into()), - dist: "debian".into(), - series: "sid".into(), - release: false, - depends, - source_format: crate::new::options::SourceFormat::Quilt, - orig: Some(crate::new::options::OrigOrigin::Snapshot), - git: true, - autopkgtest: false, - pkg_config: false, - watch: None, - } - } - - #[test] - fn empty_template_shape() { - let template = super::super::get(TemplateId::EMPTY).unwrap(); - - // Metapackage flavor: the depends list travels in the options. - let o = opts(vec!["hello".into(), "hello-data (>= 1.0)".into()]); - assert!(template.debian(&o).is_empty()); - assert_eq!(template.architecture(&o), "all"); - assert!(template.build_depends(&o).is_empty()); - assert!(template.rules_extra(&o).is_empty()); - - let skeleton = template.skeleton(&o); - assert_eq!(skeleton.len(), 1); - assert_eq!(skeleton[0].path, "README"); - assert!(!skeleton[0].executable); - assert!(skeleton[0].contents.contains("metapkg")); - } -} diff --git a/src/new/templates/go.rs b/src/new/templates/go.rs index 41839b8..1f6baeb 100644 --- a/src/new/templates/go.rs +++ b/src/new/templates/go.rs @@ -1,16 +1,16 @@ //! The `go` template: a Go module built through dh-golang. //! -//! The source stanza carries `XS-Go-Import-Path`, declared as the -//! `{go_import_path}` placeholder by the manifest and filled from the -//! `module` line of `go.mod` when the packaged tree has one, defaulting to -//! the package name (fresh skeletons embed the package name in their own -//! `go.mod`). The module line is also the probe of an existing project; the -//! skeleton bodies stay here until they move into the manifest's `files:` -//! list. +//! The skeleton bodies (`go.mod`, `main.go`) are manifest data +//! (`data/templates/go/manifest.yml`); the source stanza carries +//! `XS-Go-Import-Path`, declared as the `{go_import_path}` placeholder by +//! the manifest and filled here from the `module` line of `go.mod` when +//! the packaged tree has one, defaulting to the package name (fresh +//! skeletons embed the package name in their own `go.mod`). The module +//! line is also the probe of an existing project. use std::path::Path; -use super::{OutputFile, ProbeResult, TemplateHooks, source_dir_of}; +use super::{ProbeResult, TemplateHooks, source_dir_of}; use crate::new::options::NewOptions; /// The logic half of the go template. @@ -20,37 +20,6 @@ pub struct Hooks; pub static HOOKS: Hooks = Hooks; impl TemplateHooks for Hooks { - /// A stdlib-only `main.go` (no archive dependencies needed to build) and - /// the matching `go.mod` whose module path is the package name. - fn skeleton_files(&self, opts: &NewOptions) -> Vec { - vec![ - OutputFile::new( - "go.mod", - format!( - "module {name}\n\ - \n\ - go 1.21\n", - name = opts.name, - ), - ), - OutputFile::new( - "main.go", - format!( - "// Placeholder for {name}, generated by `pkh new`.\n\ - package main\n\ - \n\ - import \"fmt\"\n\ - \n\ - func main() {{\n\ - \tfmt.Println(\"Hello from {command}!\")\n\ - }}\n", - name = opts.name, - command = opts.command, - ), - ), - ] - } - /// The `{go_import_path}` value of the manifest's `XS-Go-Import-Path` /// source field (see [`import_path`]). fn context(&self, opts: &NewOptions) -> Vec<(String, String)> { @@ -143,6 +112,7 @@ mod tests { assert_eq!(template.rules_dh_line(), "dh $@ --buildsystem=golang"); assert!(template.debian(&o).is_empty()); + // The skeleton bodies are manifest data now. let skeleton = template.skeleton(&o); assert!( skeleton diff --git a/src/new/templates/makefile.rs b/src/new/templates/makefile.rs index d255c66..5534305 100644 --- a/src/new/templates/makefile.rs +++ b/src/new/templates/makefile.rs @@ -3,16 +3,17 @@ //! debhelper's makefile buildsystem runs `make` for the build and `make //! install DESTDIR=...` when the Makefile carries an `install:` target //! (missing targets are skipped gracefully), so plain `dh $@` plumbing is -//! enough here. The metadata is manifest data; the logic half here is the -//! phony-`install:` heuristic advising the user whether `dh_auto_install` -//! will run `make install`, plus (until their bodies move into the -//! manifest's `files:` list) the `hello.c`/`Makefile` skeleton and its -//! `debian/install` mapping. +//! enough here. Everything is manifest data +//! (`data/templates/makefile/manifest.yml`: the `hello.c`/`Makefile` +//! skeleton and the skeleton-only `debian/install` mapping) except the +//! hint logged here for an existing tree, which probes its Makefile for a +//! phony `install:` target to tell whether `dh_auto_install` will run +//! `make install` (a skeleton's target is known by construction, so +//! skeletons need no code). use std::path::Path; -use super::meson::hello_c; -use super::{OutputFile, TemplateHooks, source_dir_of}; +use super::TemplateHooks; use crate::new::options::{NewOptions, SourceDir}; /// The logic half of the makefile template. @@ -22,69 +23,32 @@ pub struct Hooks; pub static HOOKS: Hooks = Hooks; impl TemplateHooks for Hooks { - /// A `hello.c` plus a `Makefile` with `all`/`install`/`clean` targets; - /// `install` honors `DESTDIR` and copies the binary to - /// `$(DESTDIR)/usr/bin/`. - fn skeleton_files(&self, opts: &NewOptions) -> Vec { - vec![ - hello_c(opts), - OutputFile::new( - "Makefile", - format!( - "CC ?= cc\n\ - CFLAGS ?= -O2 -Wall -Wextra\n\ - PREFIX ?= /usr\n\ - \n\ - all: {command}\n\ - \n\ - {command}: hello.c\n\ - \t$(CC) $(CFLAGS) -o $@ hello.c\n\ - \n\ - install: {command}\n\ - \tinstall -Dm755 {command} $(DESTDIR)$(PREFIX)/bin/{command}\n\ - \n\ - clean:\n\ - \trm -f {command}\n\ - \n\ - .PHONY: all install clean\n", - command = opts.command, - ), - ), - ] - } - - /// `debian/install` mapping the built binary into `/usr/bin`, generated - /// only when the packaged Makefile carries a phony `install:` target: - /// for skeletons that is known by construction; when packaging an - /// existing tree the Makefile is probed instead (no `debian/install` is - /// emitted there — the source-relative mapping of an unknown artifact is - /// only the project's to write, and `make install` already ran). - fn debian_files(&self, opts: &NewOptions) -> Vec { - match &opts.source_dir { - SourceDir::Skeleton => { - vec![OutputFile::new( - "debian/install", - format!("{} usr/bin/{}\n", opts.command, opts.command), - )] - } - _ => { - if let Some(dir) = source_dir_of(opts) - && phony_install_target(&dir.join("Makefile")).is_some() - { - log::info!( - "Makefile carries a phony 'install:' target: \ - dh_auto_install will run 'make install DESTDIR=...'" - ); - } else { - log::info!( - "Makefile has no phony 'install:' target: \ - dh_auto_install will skip the install step; write a \ - debian/install file to map build artifacts manually" - ); - } - Vec::new() - } + /// No files of its own: the manifest carries the skeleton-only + /// `debian/install` mapping. When packaging an existing tree, probe + /// its Makefile for a phony `install:` target and say which install + /// step `dh_auto_install` will take (no `debian/install` is emitted + /// there — the source-relative mapping of an unknown artifact is only + /// the project's to write, and `make install` already ran). + fn debian_files(&self, opts: &NewOptions) -> Vec { + if matches!(opts.source_dir, SourceDir::Skeleton) { + return Vec::new(); } + let dir = super::source_dir_of(opts); + if let Some(dir) = dir.as_deref() + && phony_install_target(&dir.join("Makefile")).is_some() + { + log::info!( + "Makefile carries a phony 'install:' target: \ + dh_auto_install will run 'make install DESTDIR=...'" + ); + } else { + log::info!( + "Makefile has no phony 'install:' target: \ + dh_auto_install will skip the install step; write a \ + debian/install file to map build artifacts manually" + ); + } + Vec::new() } } @@ -157,8 +121,8 @@ mod tests { assert_eq!(template.rules_dh_line(), "dh $@"); assert!(template.rules_extra(&o).is_empty()); - // Skeleton: hello.c + Makefile with all/install/clean, and the - // phony install target maps to debian/install. + // Skeleton (manifest data): hello.c + Makefile with all/install/clean + // targets, and the phony install target maps to debian/install. let skeleton = template.skeleton(&o); assert!(skeleton.iter().any(|f| f.path == "hello.c")); let makefile = skeleton @@ -183,7 +147,7 @@ mod tests { assert_eq!(debian[0].path, "debian/install"); assert_eq!(debian[0].contents, "mytool usr/bin/mytool\n"); - // Existing tree: nothing is emitted (probe log only). + // Existing tree: nothing is emitted (the probe log only). let o = opts(SourceDir::Here); assert!(template.debian(&o).is_empty()); } diff --git a/src/new/templates/mod.rs b/src/new/templates/mod.rs index f4fd722..d5d1360 100644 --- a/src/new/templates/mod.rs +++ b/src/new/templates/mod.rs @@ -44,7 +44,6 @@ pub mod autotools; pub mod cmake; -pub mod empty; pub mod go; pub mod makefile; pub mod meson; @@ -370,26 +369,61 @@ static TEMPLATE_SOURCES: &[TemplateSources] = &[ TemplateSources { id: TemplateId::GO, manifest: include_str!("../../../data/templates/go/manifest.yml"), - tpls: &[], + tpls: &[ + ( + "go.mod.tpl", + include_str!("../../../data/templates/go/go.mod.tpl"), + ), + ( + "main.go.tpl", + include_str!("../../../data/templates/go/main.go.tpl"), + ), + ], hooks: Some(&go::HOOKS), }, TemplateSources { id: TemplateId::SHELL, manifest: include_str!("../../../data/templates/shell/manifest.yml"), - tpls: &[], + tpls: &[ + ( + "script.tpl", + include_str!("../../../data/templates/shell/script.tpl"), + ), + ( + "install.tpl", + include_str!("../../../data/templates/shell/install.tpl"), + ), + ], hooks: Some(&shell::HOOKS), }, TemplateSources { id: TemplateId::MAKEFILE, manifest: include_str!("../../../data/templates/makefile/manifest.yml"), - tpls: &[], + tpls: &[ + ( + "hello.c.tpl", + include_str!("../../../data/templates/makefile/hello.c.tpl"), + ), + ( + "Makefile.tpl", + include_str!("../../../data/templates/makefile/Makefile.tpl"), + ), + ( + "install.tpl", + include_str!("../../../data/templates/makefile/install.tpl"), + ), + ], hooks: Some(&makefile::HOOKS), }, TemplateSources { id: TemplateId::EMPTY, manifest: include_str!("../../../data/templates/empty/manifest.yml"), - tpls: &[], - hooks: Some(&empty::HOOKS), + tpls: &[( + "README.tpl", + include_str!("../../../data/templates/empty/README.tpl"), + )], + // Pure data: the empty template ships without any logic half. + hooks: None, }, ]; @@ -897,8 +931,8 @@ mod tests { #[test] fn probe_defaults_to_none() { - // The empty and shell templates alike: the root directory is no - // single-script project. + // Hookless (empty) and probe-carrying (shell) templates alike: the + // root directory is no single-script project. assert!( get(TemplateId::EMPTY) .unwrap() @@ -988,6 +1022,22 @@ mod tests { assert_eq!(files[2].contents, "extra"); } + /// The empty template is pure data: a stub `README` skeleton, no extra + /// debian/ files (the metapackage `Depends` payload travels in the + /// options, not in the template). + #[test] + fn empty_template_shape() { + let o = opts(TemplateId::EMPTY); + let template = get(TemplateId::EMPTY).unwrap(); + assert!(template.debian(&o).is_empty()); + assert!(template.build_depends(&o).is_empty()); + let skeleton = template.skeleton(&o); + assert_eq!(skeleton.len(), 1); + assert_eq!(skeleton[0].path, "README"); + assert!(!skeleton[0].executable); + assert!(skeleton[0].contents.contains("mytool")); + } + /// Only the rust template contributes root `.gitignore` entries of its /// own — exactly the vendoring pair, unconditionally (its vendoring /// hook runs in every mode); every other template contributes nothing. diff --git a/src/new/templates/shell.rs b/src/new/templates/shell.rs index acc5a54..6293271 100644 --- a/src/new/templates/shell.rs +++ b/src/new/templates/shell.rs @@ -1,17 +1,17 @@ //! The `shell` template: a single interpreted script installed to //! `/usr/bin` with plain `dh $@` plumbing. //! -//! The data half lives in `data/templates/shell/manifest.yml`; the logic -//! half here is the single-script probe pre-filling the wizard answers from -//! the script file name (the same heuristic [`crate::new::detect`] bases -//! its shell detection on) plus, for now, the skeleton script and its -//! skeleton-only `debian/install` mapping (the manifest `files:` list takes -//! those over once their bodies are static data). +//! Everything except the probe is manifest data +//! (`data/templates/shell/manifest.yml`: the skeleton script, its +//! skeleton-only `debian/install` mapping, the plain `dh $@` plumbing). +//! The logic half here pre-fills the wizard answers from the file name of +//! the single top-level script — the same heuristic [`crate::new::detect`] +//! bases its shell detection on. use std::path::Path; -use super::{OutputFile, ProbeResult, TemplateHooks}; -use crate::new::options::{self, NewOptions, SourceDir}; +use super::{ProbeResult, TemplateHooks}; +use crate::new::options; /// The logic half of the shell template. pub struct Hooks; @@ -20,34 +20,6 @@ pub struct Hooks; pub static HOOKS: Hooks = Hooks; impl TemplateHooks for Hooks { - /// A minimal executable script named after the command, with a `#!/bin/sh` - /// shebang and an `echo` placeholder. - fn skeleton_files(&self, opts: &NewOptions) -> Vec { - vec![OutputFile::executable( - format!("{}.sh", opts.command), - format!( - "#!/bin/sh\n# Placeholder for {}, generated by `pkh new`.\n\ - echo \"Hello from {}!\"\n", - opts.name, opts.command - ), - )] - } - - /// `debian/install` mapping the script into `/usr/bin/` - /// (debian/install renames when the destination carries a file name). - /// Only for the skeleton mode: when packaging an existing tree the - /// generated mapping would reference the non-existent skeleton script, - /// so the user writes their own install file instead. - fn debian_files(&self, opts: &NewOptions) -> Vec { - if !matches!(opts.source_dir, SourceDir::Skeleton) { - return Vec::new(); - } - vec![OutputFile::new( - "debian/install", - format!("{}.sh usr/bin/{}\n", opts.command, opts.command), - )] - } - /// The file name of the single top-level script (sanitized) pre-fills the /// package name and command questions. fn probe(&self, dir: &Path) -> Option { @@ -64,12 +36,11 @@ impl TemplateHooks for Hooks { #[cfg(test)] mod tests { - use super::*; use crate::new::options::{License, SourceDir, TemplateId}; use tempfile::tempdir; - fn opts() -> NewOptions { - NewOptions { + fn opts() -> crate::new::options::NewOptions { + crate::new::options::NewOptions { name: "mytool".into(), template: TemplateId::SHELL, source_dir: SourceDir::Skeleton, @@ -103,16 +74,27 @@ mod tests { assert!(template.build_depends(&o).is_empty()); assert!(template.rules_extra(&o).is_empty()); + // The skeleton bodies are manifest data now: the executable script + // and its skeleton-only install mapping. let skeleton = template.skeleton(&o); assert_eq!(skeleton.len(), 1); assert_eq!(skeleton[0].path, "mytool.sh"); assert!(skeleton[0].executable); assert!(skeleton[0].contents.starts_with("#!/bin/sh\n")); + assert!(skeleton[0].contents.contains("echo \"Hello from mytool!\"")); let debian = template.debian(&o); assert_eq!(debian.len(), 1); assert_eq!(debian[0].path, "debian/install"); assert_eq!(debian[0].contents, "mytool.sh usr/bin/mytool\n"); + + // Packaging an existing tree: no install mapping is generated (it + // would reference the non-existent skeleton script). + let existing = crate::new::options::NewOptions { + source_dir: SourceDir::Here, + ..opts() + }; + assert!(template.debian(&existing).is_empty()); } #[test]