Compiler (onionc)¶
The onionc command compiles Onion source files into JVM class files.
Usage¶
Options¶
-classpath <classpath>¶
Set the classpath for compilation. Used when your code references external Java libraries or other compiled Onion classes.
-encoding <encoding>¶
Specify the character encoding of source files. Default is UTF-8 on every platform and JDK (also on JDK 17, whose platform default is MS932 under a Japanese Windows locale).
-d <output directory>¶
Set the output directory for generated class files. If not specified, classes are written to the current directory.
Class files are organized by module name:
- Unix-like: org/onion_lang/MyClass.class
- Windows: org\onion_lang\MyClass.class
-maxErrorReport <count>¶
Limit the number of compilation errors reported. Useful for large projects with many errors.
-super <super class>¶
Specify the class a top-level script's synthesized class extends. Only meaningful when the source has no explicit class declaration.
--verbose¶
Show timing for each compilation phase (parsing, rewriting, type checking, code generation) as it runs.
--dump-ast¶
Print the parsed AST to stderr. Useful when debugging syntax and parsing.
--dump-typed-ast¶
Print a typed AST summary (classes, fields, methods) to stderr.
--profile-compile¶
Emit a compile profile with per-phase timing, source count, classpath size, and generated class count.
--profile-format <text|json>¶
Choose how the compile profile is rendered.
--profile-output <target>¶
Write the compile profile to stderr, stdout, or a file path.
onionc --profile-compile --profile-format json \
--profile-output target/profile.json \
MyProgram.on
--warn <off|on|error>¶
Control warning reporting. error treats warnings as compilation errors.
--Wno <codes>¶
Suppress specific warning categories by code or name.
Every code accepts either its W#### form or the --Wno name shown below. A
description marked (reserved) names a code the compiler accepts here but never
actually reports -- there is no pass yet that detects it, so suppressing it is a no-op.
| Code | Name | Description |
|---|---|---|
W0001 |
unused-variable |
Unused variable |
W0002 |
unused-import |
Unused import (reserved) |
W0003 |
unreachable-code |
Unreachable code |
W0004 |
deprecated, deprecated-feature |
Deprecated feature (reserved) |
W0005 |
shadowed-variable |
Shadowed variable |
W0006 |
unused-parameter |
Unused parameter |
W0007 |
empty-block |
Empty block (reserved) |
W0008 |
redundant-cast |
Redundant cast (reserved) |
W0009 |
possible-null-deref, null-deref |
Possible null dereference (reserved) |
W0010 |
unnecessary-conversion |
Unnecessary type conversion (reserved) |
W0011 |
unchecked-cast |
Unchecked cast (reserved) |
W0012 |
null-to-non-nullable |
Null assigned to non-nullable type |
W0013 |
suspicious-interpolation |
Suspicious string interpolation syntax |
W0014 |
discarded-toplevel |
Top-level statements ignored because a main is defined |
W0015 |
platform-unboxing |
Boxed platform value implicitly unboxed to a non-null primitive |
W0016 |
ineffective-tail-recursive |
@TailRecursive annotation present but its mutual-recursion group could not be optimized |
W0017 |
malformed-effect-table |
A library jar's effect table is malformed and was ignored |
W0018 |
foreign-effect-entry |
A library jar's effect table names classes outside the jar; those entries were ignored |
--no-check-laws¶
Do not execute a record's law / example clauses.
They run at compile time by default, which means the compiler executes that code.
Turn them off when compiling a file you do not want to run — the trade-off is that
invariants like parse ∘ format == id stop being checked.
--law-seed <n> / --law-samples <n>¶
Control how law clauses are sampled. A falsified law reports the settings that
produced its counterexample, so the run can be repeated exactly; raising the sample
count widens the search for others.
--effects¶
Print each compiled method's inferred effect set (read write net exec env clock rand
console unknown; empty means pure) to stderr.
-g:none¶
Omit the LocalVariableTable from the generated class files.
Without it a JVM debugger can still step through .on source — line numbers are always
emitted — but shows nothing for any variable, which is most of the way to useless. The
table is on by default and costs a few bytes per method; this is the way out for anyone
who wants the smaller class file.
Running a script with onion always emits the table, since a script is compiled in
memory and there is no artefact to keep small.
--print-classpath¶
Print the classpath to run the compiled classes with, and exit without compiling anything.
The line goes to stdout, separated with the platform's path separator (:, or ; on
Windows), and holds, in this order and each entry once:
- the output directory: the
-dvalue, or.when there is none, since that is whereonioncwrites the classes; - the
-classpathentries, when-classpathis given; - the jars the given files'
//> using depdirectives resolve to, transitives included (see Dependencies below).
The directives are resolved exactly as a compile resolves them, through the same cache, so asking costs nothing extra afterwards. Download progress and errors go to stderr; a malformed directive or a file that cannot be read exits non-zero with no classpath printed.
Dependencies (//> using dep)¶
onionc reads the //> using dep and //> using repository directives of the files it
compiles, exactly as the script runner does: the same parser, the
same exact-version rule, the same resolver and the same classpath cache. The resolved
jars, transitives included, join the compile classpath after any -classpath entries.
//> using dep "com.google.code.gson:gson:2.11.0"
import { com.google.gson.Gson }
class ToJson {
public:
static def of(value: Object): String { return new Gson().toJson(value) }
}
When several files are compiled together, their directives are unioned: a library
any one of them declares is on the classpath for all of them, and repositories are
searched in the order the files and their lines name them. The same module at two
versions is an error whether the two declarations are in one file or in two, and the
error names both files. A directive error stops onionc before anything is compiled.
onionc only compiles: to run the classes, the same jars must be on the java
classpath. --print-classpath prints that classpath; a
project (onion run) or the script runner puts it there for you.
Examples¶
Basic Compilation¶
Compile a single file:
This creates Hello.class in the current directory.
Multiple Files¶
Compile multiple source files:
With Output Directory¶
Organize output:
Class files appear in out/classes/.
With Classpath¶
Reference external libraries:
Complete Example¶
onionc \
-d build/classes \
-classpath lib/external.jar \
-encoding UTF-8 \
-maxErrorReport 20 \
src/*.on
Running Compiled Programs¶
After compilation, run with Java:
Or with a JAR:
# Compile
onionc -d build Main.on Helper.on
# Create JAR
jar cvfe program.jar Main -C build .
# Run JAR
java -jar program.jar
Module Organization¶
Onion uses module names (packages) similar to Java:
MyClass.on:
module com.example.myapp
class MyClass {
public:
static def main(args :String[]): void {
println("Hello")
}
}
Compile:
Output:
Run:
Compilation Errors¶
Common Errors¶
Type mismatch:
Undefined variable:
Method not found:
Incremental Compilation¶
onionc compiles all specified files each time. For large projects, consider:
- Compile only changed files
- Use a build tool (Make, SBT, Gradle)
- Organize code into modules
Build Integration¶
Makefile Example¶
SRC_DIR = src
OUT_DIR = build/classes
SOURCES = $(wildcard $(SRC_DIR)/*.on)
all: compile
compile:
mkdir -p $(OUT_DIR)
onionc -d $(OUT_DIR) $(SOURCES)
clean:
rm -rf $(OUT_DIR)
run: compile
java -cp $(OUT_DIR) Main
Shell Script Example¶
#!/bin/bash
SRC_DIR="src"
OUT_DIR="build/classes"
CLASSPATH="lib/*"
mkdir -p "$OUT_DIR"
echo "Compiling Onion sources..."
onionc -d "$OUT_DIR" -classpath "$CLASSPATH" "$SRC_DIR"/*.on
if [ $? -eq 0 ]; then
echo "Compilation successful"
echo "Running program..."
java -cp "$OUT_DIR:$CLASSPATH" Main
else
echo "Compilation failed"
exit 1
fi
Compiler Output¶
Successful Compilation¶
No output typically means success:
Compilation Errors¶
Errors are written to standard error:
$ onionc BadProgram.on
Error: Type mismatch at BadProgram.on:5
Error: Undefined variable at BadProgram.on:10
Compilation failed with 2 errors
Separate Compilation and Linking¶
Onion units can be compiled independently and linked at load time through the
classpath — you do not need all sources in one onionc invocation. Compile a
library, then compile clients against the emitted .class files.
Compile the library first:
// greeter/Greeter.on
class Greeter {
public:
def this {}
def greet(name: String): String = "Hello, " + name
}
Then compile a client against it by putting the library output on the classpath:
// app/Main.on
class Main {
public:
static def main(args: String[]): void {
IO::println((new Greeter()).greet("Onion"))
}
}
Linking is the JVM's job — run with every output directory (and onion.jar for
the runtime) on the classpath:
Classes, interfaces, records, enums, inheritance, static members and generic
types all cross unit boundaries. A generic type keeps its type parameters,
because onionc writes JVM generic signatures into the .class file:
onionc -d out/lib Container.on # class Container[T]
onionc -d out/app -classpath out/lib App.on # new Container[String](x) resolves
Compile units in dependency order (a unit must be compiled after the units it references). There is no incremental build cache — recompile a unit's dependents when its public API changes.
Next Steps¶
- Script Runner - Run Onion scripts directly
- REPL Shell - Interactive programming
- Building from Source - Build the compiler