Using Hare modules

Hare modules are implicitly defined by the directory structure of your projects. For instance, when importing foo::bar, this becomes foo/bar/, which is appended to each element of the search path to find the source files.

The search path always starts with the current working directory, then checks each of the default search paths in order. You can list the default search paths with hare version -v. Conventionally, these include:

  • /usr/src/hare/third-party

  • /usr/src/hare/stdlib

Your local installation may differ.

You can override the search paths by exporting the HAREPATH environment variable, set to a list of paths separated by colons (:).

Warning

If the HAREPATH environment variable is set, the default search paths are not used at all. If you want to add search paths, rather than replacing the entire search path, make sure your HAREPATH includes the default paths as well as your additions.

Vendoring

Note

We have established the convention that HAREPATH should, by default, include vendor/*/, allowing you to vendor git subtrees or submodules in the vendor/ directory and globbing them into the path. However, this has not yet been implemented.

What files are included?

The first directory that includes at least one Hare source file (*.ha), formed by combining the module path with each search path in order, is considered the location of the module for this build.

Each Hare file is compiled as a part of the module, and all local (unexported) declarations are visible between the source files. Only exported declarations are visible to other modules.

The build driver will also pick up assembly sources (.s, see also Linking with assembly code) and object files (.o). The latter may be useful for combining Hare modules with code written in other langauges or objects produced by other tools as part of a higher-level build system (e.g. make).

If the “rt” module includes a linker script (*.sc – possibly including a tagset), it will be passed to the linker. If you don’t know why you would need this, you do not need this.

A README file can be included in the module directory to provide an introduction for haredoc, see API documentation.

Conditional file inclusion

Hare supports build tags, which are a set of labels which define which files to include or exclude in a Hare module. These take the form of +label or -label to respectively signal inclusion and exclusion as applied to the given label. A “tag set” is a concatenation of these tags, such as “+linux+x86_64” or “+openbsd-aarch64”.

Hare builds have a set of tags enabled by default, depending on your environment, typically one for the platform and another for the architecture. For example, many users build for +linux +x86_64 by default. You can see the default build tags with hare version -v. To add new tags, pass -T$tagset to hare build, hare run, etc; to remove the defaults use -T^.

When Hare scans a directory to build a list of files included in a Hare module, it looks for tags in the filename. The format it expects is $name$tagset.ha. For instance, platform+linux.ha will only be included on Linux, whereas platform+freebsd.ha would be included on FreeBSD. platform-aarch64.ha would be excluded on builds for the aarch64 architecture. The most specific file with a given $name will be included, so platform+linux.ha would take precedence over platform.ha if +linux applies to this build.

If you have many files to include or exclude for a given tag set, you may place them in a separate directory. This directory name must simply be $tagset, such as +linux/.

Example program

Consider the following Hare source code:

./cmd/hello/main.ha
use hello;
use os;

export fn main() void = {
     hello::greetuser(os::stdout);
};

Assuming a conventional Hare install to /usr, the build will look for “hello” in the following paths:

  • ./hello

  • /usr/src/hare/third-party/hello

  • /usr/src/hare/stdlib/hello

So, if you have:

./hello/hello.ha
use fmt;
use io;

export fn greetuser(out: io::handle) void = {
     fmt::fprintln(out, "Hello!")!;
};

Then this will work as expected.

You could also add this:

./hello/hello+linux.ha
use fmt;
use io;

export fn greetuser(out: io::handle) void = {
     fmt::fprintln(out, "Hello Linux user!")!;
};

Which will be selected for Linux builds, but not for other platforms.