Skip to main content
Documentation menu

cabin new and cabin init

cabin new and cabin init generate a minimal single-package project layout: a cabin.toml, a src/ tree, a public header tree (library scaffolds only), and a .gitignore. Both commands share the same scaffold logic, so the generated bytes match exactly for the same kind / name pair.

  • cabin new <path> creates a new directory at <path> and fills it with the scaffold. <path> must not already exist.
  • cabin init initializes the current directory. The directory may already contain user files; the scaffold never overwrites them.

The default scaffold kind is a binary package (an executable target). Pass --lib to generate a library package (a library target) instead. --bin and --lib are mutually exclusive; passing both produces a clear error and no filesystem mutation.

Binary scaffold (default)

<dest>/
  cabin.toml
  src/
    main.cc
  .gitignore

The manifest declares a single executable target named after the package:

[package]
name = "hello"
version = "0.1.0"
cxx-standard = "c++17"

[target.hello]
type = "executable"
sources = ["src/main.cc"]

src/main.cc is a minimal int main that prints a greeting and returns zero. An existing src/main.cc is preserved unchanged so re-running cabin init over a partially scaffolded project never clobbers user code.

Library scaffold (--lib)

<dest>/
  cabin.toml
  include/
    <package>/
      <package>.hpp
  src/
    <package>.cc
  .gitignore

The manifest declares a single library target with a public include/ directory:

[package]
name = "greeter"
version = "0.1.0"
cxx-standard = "c++17"

[target.greeter]
type = "library"
sources = ["src/greeter.cc"]
include-dirs = ["include"]

The generated header (include/<package>/<package>.hpp) and the implementation source (src/<package>.cc) define a minimal add(int, int) function inside a namespace derived from the package name. Hyphens in the package name are mapped to underscores when forming the C++ namespace identifier (so hello-world becomes namespace hello_world), and a C++ keyword gains a trailing underscore (class becomes namespace class_); the on-disk file paths keep the original package name.

Existing header / source files at those paths are preserved unchanged.

.gitignore

Both cabin new and cabin init create a .gitignore at the destination when one does not already exist. An existing .gitignore is preserved verbatim - Cabin never appends to it or rewrites it.

The generated file ignores Cabin’s default build-output directories:

/build/
/dist/

cabin.lock is intentionally not ignored. Cabin recommends committing the lockfile so collaborators and CI converge on the same resolution; see lockfile.md.

Package name

Package names follow the package-name grammar: [a-z0-9][a-z0-9_-]*. cabin new and cabin init scaffold a bare name; add a <scope>/ to [package].name before publishing.

When --name is omitted, the package name is derived from the final path component, lowercased (cabin new path/to/hello-world produces hello-world; cabin new Boa creates the directory Boa with the package boa). If the lowercased component is still not a valid name (it is empty, contains . or other characters outside the grammar, or starts with _ or -), Cabin falls back to the literal name cabin-package.

Pass --name <name> to override the derived name. The override is used verbatim: --name Boa is rejected with a suggestion of boa, as is any other name outside the grammar.

Errors

cabin new and cabin init produce actionable diagnostics for:

  • --bin used together with --lib;
  • an invalid or scoped --name (the message names the grammar and suggests the lowercase spelling when that alone would make it valid);
  • a destination that already exists (for cabin new - the message suggests cabin init for that case);
  • a parent directory that does not exist (for cabin new);
  • a pre-existing cabin.toml (rejected; nothing is overwritten).

When validation fails, cabin new removes the destination directory it created so a partially scaffolded layout does not survive a validation error.