# Packaging Applications

### Packaging Applications

Packaging means preparing your program so another person can download it, install it, and run it.

Building creates the program. Packaging prepares the program for distribution.

For a small Zig command-line tool, packaging may be as simple as:

```bash
zig build -Doptimize=ReleaseFast --prefix dist
```

This creates an output directory such as:

```text
dist/
  bin/
    myapp
```

You can then compress `dist/` and publish it.

#### What Should Go Into a Package

A package should include everything the user needs.

For a simple command-line application, that usually means:

```text
myapp
README.md
LICENSE
```

For a larger application, it may include:

```text
bin/
  myapp
share/
  myapp/
    config.toml
    templates/
    assets/
README.md
LICENSE
```

The binary alone may not be enough if your program needs data files, default configuration, documentation, or runtime assets.

#### Build a Release Binary

Most packages should use a release mode.

For speed:

```bash
zig build -Doptimize=ReleaseFast
```

For smaller size:

```bash
zig build -Doptimize=ReleaseSmall
```

For safer optimized builds:

```bash
zig build -Doptimize=ReleaseSafe
```

A practical default for many beginner projects is:

```bash
zig build -Doptimize=ReleaseSafe --prefix dist
```

This gives an optimized binary while keeping safety checks.

#### Package for a Specific Target

A release package should usually name its target.

For Linux x86_64:

```bash
zig build -Dtarget=x86_64-linux -Doptimize=ReleaseFast --prefix dist/linux-x86_64
```

For Linux ARM64:

```bash
zig build -Dtarget=aarch64-linux -Doptimize=ReleaseFast --prefix dist/linux-aarch64
```

For Windows x86_64:

```bash
zig build -Dtarget=x86_64-windows -Doptimize=ReleaseFast --prefix dist/windows-x86_64
```

For macOS ARM64:

```bash
zig build -Dtarget=aarch64-macos -Doptimize=ReleaseFast --prefix dist/macos-aarch64
```

This creates separate output directories for separate platforms.

That avoids overwriting files and makes distribution clearer.

#### Use Clear Package Names

A package name should tell the user what it contains.

For example:

```text
myapp-0.1.0-linux-x86_64.tar.gz
myapp-0.1.0-linux-aarch64.tar.gz
myapp-0.1.0-windows-x86_64.zip
myapp-0.1.0-macos-aarch64.tar.gz
```

This name includes:

```text
program name
version
operating system
CPU architecture
archive format
```

That is better than:

```text
release.zip
```

A clear package name prevents mistakes.

#### Add a Version String

It is useful for a program to know its own version.

In `build.zig`, define a version option:

```zig
const version = b.option(
    []const u8,
    "version",
    "Application version string",
) orelse "dev";
```

Pass it into the program:

```zig
const options = b.addOptions();
options.addOption([]const u8, "version", version);

exe.root_module.addOptions("build_options", options);
```

Then in `src/main.zig`:

```zig
const std = @import("std");
const build_options = @import("build_options");

pub fn main() void {
    std.debug.print("version: {s}\n", .{build_options.version});
}
```

Now build with:

```bash
zig build -Dversion=0.1.0 -Doptimize=ReleaseFast
```

The binary can print its version.

#### Include License and README

A package should include a license file.

Even if the source code is public, the release archive should contain the license. Users should not need to search the repository to know what they are allowed to do.

A package should also include basic usage instructions.

For example:

```text
README.md
LICENSE
```

At minimum, the README should explain:

```text
what the program does
how to run it
basic examples
where to report issues
```

A small README is better than none.

#### Static vs Dynamic Dependencies

Before publishing a package, check whether the binary depends on shared libraries.

On Linux:

```bash
ldd dist/linux-x86_64/bin/myapp
```

On macOS:

```bash
otool -L dist/macos-aarch64/bin/myapp
```

A dynamically linked program may need extra libraries installed on the user’s machine.

A statically linked or mostly self-contained program is usually easier to distribute.

This matters most for Linux releases, because different Linux distributions may have different library versions.

#### Packaging Data Files

If your program needs data files, install them into the package.

A common layout is:

```text
dist/
  bin/
    myapp
  share/
    myapp/
      default.conf
      templates/
      assets/
```

Your program then needs a way to find those files.

For simple tools, you may let the user pass a path:

```bash
myapp --config share/myapp/default.conf
```

For installed applications, you may search relative to the executable or use platform-specific data directories.

Do not assume the current working directory is the project root. Once packaged, the program may be run from anywhere.

#### Archive the Package

After building into `dist/`, create an archive.

On Linux or macOS:

```bash
tar -czf myapp-0.1.0-linux-x86_64.tar.gz -C dist/linux-x86_64 .
```

For Windows releases, `.zip` is common:

```bash
zip -r myapp-0.1.0-windows-x86_64.zip dist/windows-x86_64
```

The archive should unpack into a clean directory. Avoid archives that scatter files into the current folder.

Better:

```text
myapp-0.1.0-linux-x86_64/
  bin/
    myapp
  README.md
  LICENSE
```

Worse:

```text
bin/
README.md
LICENSE
```

The first form is safer for users.

#### Add a Package Step

You can add a package step to `build.zig`.

For example:

```zig
const package_step = b.step("package", "Build release package");
package_step.dependOn(b.getInstallStep());
```

Now this works:

```bash
zig build package -Doptimize=ReleaseFast --prefix dist
```

This simple version only depends on installation. A more advanced package step might run `tar`, copy license files, or create several target-specific packages.

For beginners, start with a clear install directory. Add archive creation later.

#### Build Several Release Outputs

A project can build several release directories:

```bash
zig build -Dtarget=x86_64-linux -Doptimize=ReleaseFast --prefix dist/linux-x86_64
zig build -Dtarget=aarch64-linux -Doptimize=ReleaseFast --prefix dist/linux-aarch64
zig build -Dtarget=x86_64-windows -Doptimize=ReleaseFast --prefix dist/windows-x86_64
```

Then archive each one separately:

```bash
tar -czf myapp-0.1.0-linux-x86_64.tar.gz -C dist/linux-x86_64 .
tar -czf myapp-0.1.0-linux-aarch64.tar.gz -C dist/linux-aarch64 .
zip -r myapp-0.1.0-windows-x86_64.zip dist/windows-x86_64
```

This is enough for many small open-source tools.

#### Test the Package

A package should be tested after packaging.

Do not test only the binary in the build cache.

Test the installed output:

```bash
./dist/linux-x86_64/bin/myapp --help
```

Then test the archive:

```bash
mkdir /tmp/test-myapp
tar -xzf myapp-0.1.0-linux-x86_64.tar.gz -C /tmp/test-myapp
/tmp/test-myapp/bin/myapp --help
```

This catches missing data files, bad paths, missing shared libraries, and wrong executable names.

#### Common Mistakes

A common mistake is packaging from the build cache instead of the install directory.

Use `zig-out` or your `--prefix` directory. Do not copy random files from `.zig-cache`.

Another mistake is forgetting runtime files. If your program needs templates, schemas, config files, or assets, include them.

Another mistake is using one `dist/` directory for several targets and overwriting binaries.

Another mistake is publishing a dynamically linked binary without checking its runtime library dependencies.

Another mistake is shipping a binary with no version information. Users and bug reports become harder to manage.

#### The Important Idea

Packaging turns build output into something another person can use.

A good beginner workflow is:

```bash
zig build -Dtarget=x86_64-linux -Doptimize=ReleaseFast -Dversion=0.1.0 --prefix dist/linux-x86_64
```

Then include basic files:

```text
README.md
LICENSE
```

Then archive the directory with a clear name:

```text
myapp-0.1.0-linux-x86_64.tar.gz
```

A package should be predictable, complete, and easy to test.

