Every Mica compilation is described by two kinds of information, and telling them apart is the whole idea of this page.

The facts belong to the source tree itself: which files form the program, which string encoding they are written against, which libraries they consume. Move the tree to another machine and the facts travel with it — they were never about the machine.

The choices belong to one invocation: which architecture to target, which optimization tier, where the build artifacts go. The same tree builds for ARM64 at the debug tier this minute and for x86-64 fully optimized the next, and no fact changed.

Flags are the right home for choices. The project file is the home for facts.

A few lines

A mica.project sits beside the sources it describes. This is a typical shape — a program consuming the starter repository’s Utilities library through the contract its build emits:

{
    "version": "1.0.0",
    "sources": ["Consumer.mica"],
    "contracts": ["../build/Utilities"]
}

Five kinds of entry. version is the one every project file states; the other four are written where they have something to say:

  • sources — the files of the compilation, relative to the project file. A program built from several units lists them all.
  • encoding — "utf-32" (the default) or "utf-8". This is a fact, not a flag: the sources are written against one encoding — their format strings say %ls or %s accordingly — so the file that owns the sources owns the encoding.
  • contracts — the libraries this program consumes, each named by the location of the contract the compiler emitted when that library was built. Several entries mean several libraries, each imported through its own contract.
  • version — the version of the program or library the sources are, as Semantic Versioning writes it: "2.0.0", or "2.1.0-rc.1" before a release. Every build from a project file states one, and so does every library. A program compiled from its sources with flags alone, or run as a script, is built without one.
  • requires — the versions of the libraries it is built against that the program or library accepts, by library name: {"sqlite": "2.0"}.

The editor reads it — that is the point

Open any source file in VS Code and the Mica language server looks for a mica.project, walking up from the file’s directory — the familiar module-file convention. No settings, no configuration. Whatever it finds governs the analysis: the companion sources are enrolled, the encoding is applied, the contracts are loaded.

The consequence is simple to state: the diagnostics in your editor are the diagnostics of your build. A program importing a library shows no false namespace not found on lines that link fine. A utf-8 source is checked under utf-8 rules while you type. Every example in the starter repository carries its project file, so this is the behavior you meet from the first git clone.

The file itself is checked while you type it, too. In VS Code, a mica.project offers its five keys with what each one means, completes the encodings, and underlines a key the compiler would refuse, or a version it would not read, with the compiler’s own sentence. The rules come from the compiler, which generates the file’s JSON Schema from its own reader, so any editor that reads JSON Schema can check a project file the same way.

The build reads the same file

--project points the compiler at a project file or the directory holding one, here from the example’s own directory:

cd mica-container/examples/MicaCallsLinux
mica --compile --project . --build ../build/MicaCallsLinux --platform linux,arm64
Compiling source '.../MicaCallsLinux.mica' to assembly code for platform 'linux arm64 AArch64_V8 utf-8 AArch64_ELF_GNU_AAPCS64'

Look at what happened there. The command named no source files — the project supplied them. And --platform linux,arm64 named no encoding — the project’s "encoding": "utf-8" completed it, and the platform line of the compile confirms it.

The precedence rule is one sentence: explicit flags override project entries, fact by fact. Pass --source and it wins over the project’s sources; the other entries still apply. The file describes; the invocation decides.

A library’s version

A library states its version once, in its own project file, and a library is linked only under one; a build that names no project file is answered with

A library as output binary states its version as "version" in the mica.project it is built from: linking aborted

The sqlite library in the starter repository says "version": "2.0.0", and its build carries the version into the contract it emits:

Emitted interop contract '../build/sqlite/mica.external' for library 'sqlite' 2.0.0

Every program built against that contract names the library with its version, in its build and in its manifest, which mica --inspect prints without starting the program:

Using library 'sqlite' 2.0.0
Using library 'sqlite3' 3.46.1
"libraries":[{"name":"sqlite","language":"mica","version":"2.0.0"},{"name":"sqlite3","language":"c","version":"3.46.1"}]

In the editor, the hover over a name the library brings ends with the same line, Mica library sqlite 2.0.0. The C library beneath it is named with the version the machine holds: its contract names the sqlite3 package, and the build asks the machine’s pkg-config, so another machine names its own version. A C library whose contract names no package and states no version is named without one.

The versions a program accepts

A program states which versions of a library it accepts in its own project file, under requires, and reads a requirement the way Cargo and npm read one: "2.0" accepts every version from 2.0.0 up to, not including, 3.0.0 — the releases Semantic Versioning promises compatible. Below 1.0.0 the leftmost number that is not zero is the one a compatible release keeps, so "0.2" accepts the 0.2 releases and "0.0.3" accepts 0.0.3 alone. A pre-release is accepted only by a requirement that names a pre-release of the same three numbers.

{
    "sources": ["demo.mica"],
    "contracts": ["../build/sqlite"],
    "version": "1.0.0",
    "requires": {"sqlite": "2.0"}
}

The build checks every library its contracts bring against the requirement before it reads a source against them, and a library outside it answers with the range beside the version the contract states:

interop error 562: the project requires the library 'sqlite' at '2.0', which accepts the versions from 2.0.0 up to 3.0.0, and the library is at version '1.4.0'

The editor shows the same sentence at the top of the file, and a script run with mica --run beside the project answers it too.

The contract and the archive are checked against each other as well. A library’s build states its version and the digest of its interface in the archive’s manifest and in the contract it emits, and a program’s link compares them with the contract the program was built against, so a library rebuilt beside a program that still reads the old contract answers by name instead of linking:

Failed to link: the archive 'lib/Meter.a' is another build of the library 'Meter' than the contract the program was built against describes: the contract states the interface b9c78ed36ab7, the archive 38b4e6d6c348; rebuild the program against the contract the archive's build emitted

Try it

Open examples/MicaCallsLinux/mica.project in the starter repository and change "utf-8" to "utf-16". The editor reacts first: one error diagnostic at the top of the file, carrying the message below — the language server refuses to analyze under a project it cannot honor. Now ask the compiler directly, from the example’s directory, where the build above ran:

mica --compile --project . --build ../build/MicaCallsLinux --platform linux,arm64
project file mica.project: unknown encoding "utf-16" (valid: utf-32, utf-8)

A project file that cannot be honored is refused loudly, in the editor and in the build alike — the compiler never falls back to defaults you did not ask for, because building something other than what the file describes would be worse than stopping. Change it back.

When you do not need one

A single file compiled with the default encoding and no libraries needs no project file at all — examples/Hello would work without one. The file earns its place the moment any fact departs from the trivial: a second source unit, a utf-8 surface, a consumed library. The starter repository gives one to every example anyway, so the editor’s behavior is uniform across the whole learning path.

Where this is going

A file that both the build and the editor read is also the natural place for a future dependency description — that is the direction the project model is built to grow in, and the announcement article sketches it.

Next