Building a project
Starting one
lyric new myapp # a program
lyric new mylib --lib # a module someone imports
An app arrives ready to build:
myapp/
├── lyric.json where the modules live
├── build.lyr what to build
├── .gitignore
└── src/main.lyr
cd myapp && lyric build && lyric run out/myapp.lyrbc
A library has no build.lyr, because there is nothing to build: it is source another project points
its sourceRoot at and imports. Its module file is named after it, so import mylib finds
src/mylib.lyr.
The name becomes a module name, so it has to be one: letters, digits and _, not starting with a
digit. lyric new refuses to write into a directory that already holds something.
Building one
For a single file there is nothing to set up:
lyric build app.lyr -o app.lyrbc
A project with more than one program, or one that generates part of its own source, puts a
build.lyr at its root and runs lyric build without naming a file.
The script
import std.build { addExecutable };
pub fn build() {
let app = addExecutable("src/main.lyr", "out/app.lyrbc");
app.sourceMap(false);
addExecutable("tools/mktex.lyr", "out/mktex.lyrbc");
}
lyric build # the working directory
lyric build ../game # somewhere else
Every artifact is compiled on its own, whole, from its entry file. There is no link step and nothing is shared between two of them but the source on disk.
lyric build with a file still means "compile this file" and goes to the compiler, as it always
did. Only a directory, or no argument at all, looks for a build.lyr.
Nothing is compiled while the script runs
addExecutable collects; the compiles happen once build has returned. That is why sourceMap on
the next line still applies — and why a file the script writes is finished before anything reads it.
It is a program, not a list
A build script runs with every capability and has the whole standard library. It may write files, read them, and start processes:
import std.build { addExecutable };
import std.io.file { writeText };
pub fn build() {
writeText("src/version.lyr", "module version; pub fn text(): string { return \"1.2.0\"; }");
addExecutable("src/main.lyr", "out/app.lyrbc");
}
Relative paths mean the same everywhere in the script: they are resolved against the directory
holding build.lyr, not against the directory you started the build from.
This is code you are running. lyric build in a repository you did not write executes a program
you did not write, exactly as make and cmake do.
What the script does not say
Where modules live is a property of the project, not of a build, so it stays in
lyric.json:
{
"sourceRoot": "src",
"nativeRoots": { "engine": "sdk" },
}
Both files are read for every artifact. The script never repeats a root, and an editor learns the
layout from lyric.json without running anything.
When something goes wrong
No build.lyr in the directory |
LYR-CLI0011 |
| The script does not compile | its own diagnostics, with file, line and column |
No build function, or it panics |
LYR-CLI0012 |
build declared nothing to compile |
LYR-CLI0012 — silence would look like success |
| An entry file is not there | named before anything is written |