# Operating Systems

### Operating Systems

The operating system part of a target tells Zig what kind of system the program will run on.

In this target:

```text
x86_64-linux-gnu
```

the operating system is:

```text
linux
```

In this target:

```text
aarch64-macos-none
```

the operating system is:

```text
macos
```

In this target:

```text
wasm32-freestanding-none
```

the operating system is:

```text
freestanding
```

The operating system affects many things: executable format, system calls, file paths, dynamic libraries, threads, environment variables, and process startup.

A program can inspect the operating system at compile time:

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

pub fn main() void {
    std.debug.print("os = {s}\n", .{@tagName(builtin.os.tag)});
}
```

The value of `builtin.os.tag` is known before the program runs. That means it can be used to choose different code for different systems.

```zig
const builtin = @import("builtin");

pub fn pathSeparator() u8 {
    if (builtin.os.tag == .windows) {
        return '\\';
    } else {
        return '/';
    }
}
```

This function returns a backslash on Windows and a slash on most other systems.

The test is done at compile time because `builtin.os.tag` is a compile-time value. Zig compiles the branch that applies to the selected target.

A common pattern is to use `switch`:

```zig
const builtin = @import("builtin");

pub fn pathSeparator() u8 {
    return switch (builtin.os.tag) {
        .windows => '\\',
        else => '/',
    };
}
```

This is clearer when there are several operating systems.

```zig
const builtin = @import("builtin");

pub fn defaultConfigName() []const u8 {
    return switch (builtin.os.tag) {
        .windows => "app.ini",
        .linux => "app.conf",
        .macos => "app.plist",
        else => "app.conf",
    };
}
```

Some targets have no operating system.

```text
wasm32-freestanding-none
```

`freestanding` means there is no normal host OS. There may be no files, no process, no environment variables, and no system allocator.

A freestanding program must be written with fewer assumptions.

For example, this code assumes a hosted operating system:

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

pub fn main() !void {
    const cwd = std.fs.cwd();
    try cwd.makeDir("out");
}
```

It uses the current working directory and the filesystem. That makes sense on Linux, macOS, and Windows. It may not make sense on a bare-metal or freestanding target.

For target-specific code, keep the difference small and local.

```zig
const builtin = @import("builtin");

pub fn homeName() []const u8 {
    return switch (builtin.os.tag) {
        .windows => "USERPROFILE",
        else => "HOME",
    };
}
```

The rest of the program can call `homeName` without knowing which operating system is being used.

This is the main rule: isolate operating-system differences behind small functions.

Zig gives access to the target at compile time, but it does not make operating systems the same. Linux, Windows, macOS, WebAssembly, and freestanding targets have different rules. The program must still respect those rules.

Exercise 17-5. Write a function that returns the path separator for the current target.

Exercise 17-6. Write a function that returns the default executable suffix: `.exe` on Windows and an empty string elsewhere.

Exercise 17-7. Compile a program with `-target x86_64-windows-gnu` and inspect the output file name.

Exercise 17-8. Find one standard-library function that depends on the operating system.

