diff --git a/data/templates/autotools/Makefile.am.tpl b/data/templates/autotools/Makefile.am.tpl new file mode 100644 index 0000000..67e1222 --- /dev/null +++ b/data/templates/autotools/Makefile.am.tpl @@ -0,0 +1,2 @@ +bin_PROGRAMS = {command} +{command}_SOURCES = hello.c diff --git a/data/templates/autotools/configure.ac.tpl b/data/templates/autotools/configure.ac.tpl new file mode 100644 index 0000000..fee8678 --- /dev/null +++ b/data/templates/autotools/configure.ac.tpl @@ -0,0 +1,5 @@ +AC_INIT([{name}], [{upstream_version}]) +AM_INIT_AUTOMAKE([foreign]) +AC_PROG_CC +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/data/templates/autotools/hello.c.tpl b/data/templates/autotools/hello.c.tpl new file mode 100644 index 0000000..bdeb90b --- /dev/null +++ b/data/templates/autotools/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/autotools/manifest.yml b/data/templates/autotools/manifest.yml index 073302e..d3ad819 100644 --- a/data/templates/autotools/manifest.yml +++ b/data/templates/autotools/manifest.yml @@ -1,8 +1,13 @@ ## The `autotools` template: a C project with a configure.ac built through ## debhelper's auto-detection (dh runs autoreconf itself when it finds -## configure.ac, debhelper >= 10 — no override needed). The logic half — -## the AC_INIT probe, the skeleton bodies and the gettext detection -## (appended to Build-Depends) — lives in src/new/templates/autotools.rs. +## configure.ac, debhelper >= 10 — no override needed). The skeleton bodies +## below are static data (the first source build runs `autoreconf`, +## integrated in the dh sequence, so no generated configure script is +## committed); the logic half — the AC_INIT probe and the GNU-gettext +## detection (appended to Build-Depends) — lives in +## src/new/templates/autotools.rs. hello.c.tpl duplicates the shared C +## skeleton of the other C/C++ template directories (see +## meson/manifest.yml for why). ## ## Schema: see src/new/templates/mod.rs. @@ -16,4 +21,10 @@ build_depends: - libtool architecture: any rules_dh_line: "dh $@" -files: [] +files: + - path: configure.ac + template: configure.ac.tpl + - path: Makefile.am + template: Makefile.am.tpl + - path: hello.c + template: hello.c.tpl diff --git a/data/templates/cmake/CMakeLists.txt.tpl b/data/templates/cmake/CMakeLists.txt.tpl new file mode 100644 index 0000000..9fc9a2a --- /dev/null +++ b/data/templates/cmake/CMakeLists.txt.tpl @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.16) +project({name} VERSION {upstream_version}) + +add_executable({command} hello.c) +install(TARGETS {command} RUNTIME DESTINATION bin) diff --git a/data/templates/cmake/hello.c.tpl b/data/templates/cmake/hello.c.tpl new file mode 100644 index 0000000..bdeb90b --- /dev/null +++ b/data/templates/cmake/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/cmake/manifest.yml b/data/templates/cmake/manifest.yml index 28bd032..eed06ca 100644 --- a/data/templates/cmake/manifest.yml +++ b/data/templates/cmake/manifest.yml @@ -1,7 +1,9 @@ ## The `cmake` template: a C/C++ project built with CMake through the -## debhelper cmake buildsystem. The logic half — the project() probe, the -## skeleton bodies and the wizard's pkg-config opt-in (appended to -## Build-Depends) — lives in src/new/templates/cmake.rs. +## debhelper cmake buildsystem. The skeleton bodies below are static data; +## the logic half — the project() probe and the wizard's pkg-config +## opt-in (appended to Build-Depends) — lives in src/new/templates/cmake.rs. +## hello.c.tpl duplicates the shared C skeleton of the other C/C++ +## template directories (see meson/manifest.yml for why). ## ## Schema: see src/new/templates/mod.rs. @@ -13,4 +15,8 @@ build_depends: - cmake architecture: any rules_dh_line: "dh $@ --buildsystem=cmake" -files: [] +files: + - path: CMakeLists.txt + template: CMakeLists.txt.tpl + - path: hello.c + template: hello.c.tpl diff --git a/data/templates/meson/hello.c.tpl b/data/templates/meson/hello.c.tpl new file mode 100644 index 0000000..bdeb90b --- /dev/null +++ b/data/templates/meson/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/meson/manifest.yml b/data/templates/meson/manifest.yml index 190bdc5..ad8a133 100644 --- a/data/templates/meson/manifest.yml +++ b/data/templates/meson/manifest.yml @@ -1,7 +1,15 @@ ## The `meson` template: a C/C++ project built with Meson through the -## debhelper meson buildsystem. The logic half — the project() probe, the -## skeleton bodies and the wizard's pkg-config opt-in (appended to -## Build-Depends) — lives in src/new/templates/meson.rs. +## debhelper meson buildsystem. The skeleton bodies below are static data; +## the logic half — the project() probe and the wizard's pkg-config +## opt-in (appended to Build-Depends) — lives in src/new/templates/meson.rs. +## +## hello.c.tpl is deliberately duplicated (byte-identical) across the +## makefile, cmake and autotools template directories: every template +## directory is self-contained — the registry embeds each directory's +## bodies under its own entry — so a shared body would need +## cross-directory references the manifest schema has no machinery for. +## The duplication replaces the Rust hello_c() helper meson.rs used to +## lend cmake.rs and autotools.rs. ## ## Schema: see src/new/templates/mod.rs. @@ -13,4 +21,8 @@ build_depends: - meson architecture: any rules_dh_line: "dh $@ --buildsystem=meson" -files: [] +files: + - path: meson.build + template: meson.build.tpl + - path: hello.c + template: hello.c.tpl diff --git a/data/templates/meson/meson.build.tpl b/data/templates/meson/meson.build.tpl new file mode 100644 index 0000000..0e859c5 --- /dev/null +++ b/data/templates/meson/meson.build.tpl @@ -0,0 +1,3 @@ +project('{name}', version: '{upstream_version}', license: '{license}', default_options: ['c_std=c11']) + +executable('{command}', 'hello.c', install: true) diff --git a/src/new/templates/autotools.rs b/src/new/templates/autotools.rs index 8cfe0dc..7f37461 100644 --- a/src/new/templates/autotools.rs +++ b/src/new/templates/autotools.rs @@ -2,17 +2,16 @@ //! debhelper's auto-detection (dh runs `autoreconf` itself when it finds //! `configure.ac`, debhelper ≥ 10 — no override needed). //! -//! The metadata is manifest data; the logic half here is the `AC_INIT` -//! probe, the GNU-gettext detection (appending `gettext` to the manifest's -//! Build-Depends) and — until the bodies move into the manifest's `files:` -//! list — the `configure.ac`/`Makefile.am`/`hello.c` skeleton. +//! The metadata and the `configure.ac`/`Makefile.am`/`hello.c` skeleton +//! bodies are manifest data (`data/templates/autotools/manifest.yml`); the +//! logic half here is the `AC_INIT` probe and the GNU-gettext detection +//! (appending `gettext` to the manifest's Build-Depends). use std::path::Path; use regex::Regex; -use super::meson::hello_c; -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 autotools template. @@ -22,35 +21,6 @@ pub struct Hooks; pub static HOOKS: Hooks = Hooks; impl TemplateHooks for Hooks { - /// A minimal `configure.ac`, the matching `Makefile.am` and `hello.c`. - /// The first source build runs `autoreconf` (integrated in the dh - /// sequence), so no generated configure script is committed. - fn skeleton_files(&self, opts: &NewOptions) -> Vec { - vec![ - OutputFile::new( - "configure.ac", - format!( - "AC_INIT([{name}], [{version}])\n\ - AM_INIT_AUTOMAKE([foreign])\n\ - AC_PROG_CC\n\ - AC_CONFIG_FILES([Makefile])\n\ - AC_OUTPUT\n", - name = opts.name, - version = opts.upstream_version, - ), - ), - OutputFile::new( - "Makefile.am", - format!( - "bin_PROGRAMS = {command}\n\ - {command}_SOURCES = hello.c\n", - command = opts.command, - ), - ), - hello_c(opts), - ] - } - /// A packaged `configure.ac` that sets up GNU gettext (the /// `AM_GNU_GETTEXT` macro, see [`uses_gettext`]) appends `gettext` to /// the manifest's Build-Depends. @@ -149,23 +119,45 @@ mod tests { assert!(template.rules_extra(&o).is_empty()); assert!(template.debian(&o).is_empty()); + // Skeleton (manifest data): configure.ac + Makefile.am + hello.c, + // byte-exact. let skeleton = template.skeleton(&o); + assert_eq!(skeleton.len(), 3); let configure = skeleton .iter() .find(|f| f.path == "configure.ac") .expect("configure.ac skeleton"); - assert!( - configure - .contents - .starts_with("AC_INIT([mytool], [0.1.0])\n") + assert_eq!( + configure.contents, + "AC_INIT([mytool], [0.1.0])\n\ + AM_INIT_AUTOMAKE([foreign])\n\ + AC_PROG_CC\n\ + AC_CONFIG_FILES([Makefile])\n\ + AC_OUTPUT\n" ); let makefile_am = skeleton .iter() .find(|f| f.path == "Makefile.am") .expect("Makefile.am skeleton"); - assert!(makefile_am.contents.contains("bin_PROGRAMS = mytool")); - assert!(makefile_am.contents.contains("mytool_SOURCES = hello.c")); - assert!(skeleton.iter().any(|f| f.path == "hello.c")); + assert_eq!( + makefile_am.contents, + "bin_PROGRAMS = mytool\nmytool_SOURCES = hello.c\n" + ); + let hello = skeleton + .iter() + .find(|f| f.path == "hello.c") + .expect("hello.c skeleton"); + assert_eq!( + hello.contents, + "#include \n\ + \n\ + /* Placeholder for mytool, generated by `pkh new`. */\n\ + int main(void)\n\ + {\n\ + \tprintf(\"Hello from mytool!\\n\");\n\ + \treturn 0;\n\ + }\n" + ); } #[test] diff --git a/src/new/templates/cmake.rs b/src/new/templates/cmake.rs index 9b6c283..f0d0779 100644 --- a/src/new/templates/cmake.rs +++ b/src/new/templates/cmake.rs @@ -1,16 +1,15 @@ //! The `cmake` template: a C/C++ project built with CMake through the //! debhelper cmake buildsystem. //! -//! The metadata is manifest data; the logic half here is the `project()` -//! probe, the wizard's pkg-config opt-in (appended to the manifest's -//! Build-Depends) and — until the bodies move into the manifest's `files:` -//! list — the `CMakeLists.txt`/`hello.c` skeleton. +//! The metadata and the `CMakeLists.txt`/`hello.c` skeleton bodies are +//! manifest data (`data/templates/cmake/manifest.yml`); the logic half +//! here is the `project()` probe and the wizard's pkg-config opt-in +//! (appended to the manifest's Build-Depends). use std::path::Path; use regex::Regex; -use super::meson::hello_c; use super::{ProbeResult, TemplateHooks}; use crate::new::options::NewOptions; @@ -21,27 +20,6 @@ pub struct Hooks; pub static HOOKS: Hooks = Hooks; impl TemplateHooks for Hooks { - /// A minimal `CMakeLists.txt` (project declaration + one installed - /// executable) and the classic `hello.c`. - fn skeleton_files(&self, opts: &NewOptions) -> Vec { - vec![ - super::OutputFile::new( - "CMakeLists.txt", - format!( - "cmake_minimum_required(VERSION 3.16)\n\ - project({name} VERSION {version})\n\ - \n\ - add_executable({command} hello.c)\n\ - install(TARGETS {command} RUNTIME DESTINATION bin)\n", - name = opts.name, - version = opts.upstream_version, - command = opts.command, - ), - ), - hello_c(opts), - ] - } - /// The wizard's pkg-config opt-in ([`NewOptions::pkg_config`], offered /// when the project's build file hints at `pkg_check_modules` usage) /// adds `pkg-config` to the manifest's Build-Depends. @@ -116,22 +94,36 @@ mod tests { assert!(template.rules_extra(&o).is_empty()); assert!(template.debian(&o).is_empty()); + // Skeleton (manifest data): CMakeLists.txt + hello.c, byte-exact. let skeleton = template.skeleton(&o); + assert_eq!(skeleton.len(), 2); let cmakelists = skeleton .iter() .find(|f| f.path == "CMakeLists.txt") .expect("CMakeLists.txt skeleton"); - assert!( - cmakelists - .contents - .contains("project(mytool VERSION 0.1.0)") + assert_eq!( + cmakelists.contents, + "cmake_minimum_required(VERSION 3.16)\n\ + project(mytool VERSION 0.1.0)\n\ + \n\ + add_executable(mytool hello.c)\n\ + install(TARGETS mytool RUNTIME DESTINATION bin)\n" ); - assert!( - cmakelists - .contents - .contains("add_executable(mytool hello.c)") + let hello = skeleton + .iter() + .find(|f| f.path == "hello.c") + .expect("hello.c skeleton"); + assert_eq!( + hello.contents, + "#include \n\ + \n\ + /* Placeholder for mytool, generated by `pkh new`. */\n\ + int main(void)\n\ + {\n\ + \tprintf(\"Hello from mytool!\\n\");\n\ + \treturn 0;\n\ + }\n" ); - assert!(skeleton.iter().any(|f| f.path == "hello.c")); } #[test] diff --git a/src/new/templates/meson.rs b/src/new/templates/meson.rs index 9f9e4d4..5987607 100644 --- a/src/new/templates/meson.rs +++ b/src/new/templates/meson.rs @@ -1,17 +1,16 @@ //! The `meson` template: a C/C++ project built with Meson through the //! debhelper meson buildsystem. //! -//! The metadata is manifest data; the logic half here is the `project()` -//! probe, the wizard's pkg-config opt-in (appended to the manifest's -//! Build-Depends) and — until the bodies move into the manifest's `files:` -//! list — the `meson.build`/`hello.c` skeleton. [`hello_c`] is the shared -//! placeholder of the C/C++ skeletons. +//! The metadata and the `meson.build`/`hello.c` skeleton bodies are +//! manifest data (`data/templates/meson/manifest.yml`); the logic half +//! here is the `project()` probe and the wizard's pkg-config opt-in +//! (appended to the manifest's Build-Depends). use std::path::Path; use regex::Regex; -use super::{OutputFile, ProbeResult, TemplateHooks}; +use super::{ProbeResult, TemplateHooks}; use crate::new::options::NewOptions; /// The logic half of the meson template. @@ -21,27 +20,6 @@ pub struct Hooks; pub static HOOKS: Hooks = Hooks; impl TemplateHooks for Hooks { - /// A minimal `meson.build` (project declaration + one installed - /// executable) and the classic `hello.c`. - fn skeleton_files(&self, opts: &NewOptions) -> Vec { - vec![ - OutputFile::new( - "meson.build", - format!( - "project('{name}', version: '{version}', license: '{license}', \ - default_options: ['c_std=c11'])\n\ - \n\ - executable('{command}', 'hello.c', install: true)\n", - name = opts.name, - version = opts.upstream_version, - license = opts.license.spdx(), - command = opts.command, - ), - ), - hello_c(opts), - ] - } - /// The wizard's pkg-config opt-in ([`NewOptions::pkg_config`], offered /// when the project's build file hints at `dependency(` usage) adds /// `pkg-config` to the manifest's Build-Depends. @@ -69,25 +47,6 @@ impl TemplateHooks for Hooks { } } -/// The shared `hello.c` placeholder of the C/C++ skeletons. -pub(super) fn hello_c(opts: &NewOptions) -> OutputFile { - OutputFile::new( - "hello.c", - format!( - "#include \n\ - \n\ - /* Placeholder for {name}, generated by `pkh new`. */\n\ - int main(void)\n\ - {{\n\ - \tprintf(\"Hello from {command}!\\n\");\n\ - \treturn 0;\n\ - }}\n", - name = opts.name, - command = opts.command, - ), - ) -} - #[cfg(test)] mod tests { use super::*; @@ -131,19 +90,35 @@ mod tests { assert!(template.rules_extra(&o).is_empty()); assert!(template.debian(&o).is_empty()); + // Skeleton (manifest data): meson.build + hello.c, byte-exact. let skeleton = template.skeleton(&o); + assert_eq!(skeleton.len(), 2); let meson_build = skeleton .iter() .find(|f| f.path == "meson.build") .expect("meson.build skeleton"); - assert!(meson_build.contents.contains("project('mytool'")); - assert!(meson_build.contents.contains("version: '0.1.0'")); - assert!( - meson_build - .contents - .contains("executable('mytool', 'hello.c', install: true)") + assert_eq!( + meson_build.contents, + "project('mytool', version: '0.1.0', license: 'MIT', \ + default_options: ['c_std=c11'])\n\ + \n\ + executable('mytool', 'hello.c', install: true)\n" + ); + let hello = skeleton + .iter() + .find(|f| f.path == "hello.c") + .expect("hello.c skeleton"); + assert_eq!( + hello.contents, + "#include \n\ + \n\ + /* Placeholder for mytool, generated by `pkh new`. */\n\ + int main(void)\n\ + {\n\ + \tprintf(\"Hello from mytool!\\n\");\n\ + \treturn 0;\n\ + }\n" ); - assert!(skeleton.iter().any(|f| f.path == "hello.c")); } #[test] diff --git a/src/new/templates/mod.rs b/src/new/templates/mod.rs index 48b3607..c53fefc 100644 --- a/src/new/templates/mod.rs +++ b/src/new/templates/mod.rs @@ -362,19 +362,50 @@ static TEMPLATE_SOURCES: &[TemplateSources] = &[ TemplateSources { id: TemplateId::MESON, manifest: include_str!("../../../data/templates/meson/manifest.yml"), - tpls: &[], + tpls: &[ + ( + "meson.build.tpl", + include_str!("../../../data/templates/meson/meson.build.tpl"), + ), + ( + "hello.c.tpl", + include_str!("../../../data/templates/meson/hello.c.tpl"), + ), + ], hooks: Some(&meson::HOOKS), }, TemplateSources { id: TemplateId::CMAKE, manifest: include_str!("../../../data/templates/cmake/manifest.yml"), - tpls: &[], + tpls: &[ + ( + "CMakeLists.txt.tpl", + include_str!("../../../data/templates/cmake/CMakeLists.txt.tpl"), + ), + ( + "hello.c.tpl", + include_str!("../../../data/templates/cmake/hello.c.tpl"), + ), + ], hooks: Some(&cmake::HOOKS), }, TemplateSources { id: TemplateId::AUTOTOOLS, manifest: include_str!("../../../data/templates/autotools/manifest.yml"), - tpls: &[], + tpls: &[ + ( + "configure.ac.tpl", + include_str!("../../../data/templates/autotools/configure.ac.tpl"), + ), + ( + "Makefile.am.tpl", + include_str!("../../../data/templates/autotools/Makefile.am.tpl"), + ), + ( + "hello.c.tpl", + include_str!("../../../data/templates/autotools/hello.c.tpl"), + ), + ], hooks: Some(&autotools::HOOKS), }, TemplateSources {