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:
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:
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:
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.