Building and Running a Ceylon Module on the JVM with the CLI
A task guide to declaring a Ceylon module descriptor, resolving dependencies, compiling, running, and packaging a self-contained JVM archive with the Ceylon CLI.
15 Jul 2025, 21:08 UTC

You have Ceylon sources and a module.ceylon descriptor, but turning that into a running JVM process requires a specific CLI sequence and repository checks. The useful takeaway: the Ceylon toolchain resolves explicit module versions from configured repositories, compiles to standard JVM bytecode, and can emit a self-contained fat-jar—provided every direct dependency is already visible to the resolver.
Desired outcome
Produce a compiled Ceylon module that runs on the JVM via ceylon run, and package it as a self-contained archive executable with java -jar. The build must honor the module.ceylon descriptor and resolve all declared dependencies from a module repository.
Prerequisites
- The Ceylon command-line toolchain installed and available on
PATH. - A supported JDK for the target platform.
- A project directory containing a
module.ceylondescriptor at the source root. - Normal user permissions to write build output in the project directory; no elevated privileges are needed unless installing the toolchain system-wide.
Declare the module descriptor and shared package
Ceylon organizes code into modules described by a module.ceylon file at the source root. The descriptor declares the module name, version, and imported dependencies. Package visibility is controlled by shared package declarations; only explicitly shared packages are visible to downstream modules.
Create the following minimal layout for a module named com.example.greeter version 1.0.0:
greeter/
module.ceylon
source/
com/
example/
greeter/
package.ceylon
run.ceylon
module.ceylon:
module com.example.greeter "1.0.0" {
}
source/com/example/greeter/package.ceylon:
shared package com.example.greeter;
source/com/example/greeter/run.ceylon:
package com.example.greeter;
shared void run() {
print("Hello from Ceylon module com.example.greeter");
}
Resolve dependencies before compiling
Dependencies are declared inside the descriptor with explicit module names and versions. Ceylon does not resolve transitive dependencies by default, so every direct dependency must be listed. The toolchain caches resolved modules in a local repository before linking them into the build.
To add a dependency, insert an import line into module.ceylon:
module com.example.greeter "1.0.0" {
import com.example.util "1.0.0";
}
Before compiling, confirm that com.example.util version 1.0.0 is available in a repository known to the toolchain. If the resolver cannot find it, ceylon compile will fail with an unresolved-module error.
Compile the module
Run the compiler from the directory containing module.ceylon:
ceylon compile
The toolchain reads the descriptor, resolves declared dependencies, and compiles .ceylon sources to JVM bytecode. A successful build exits with status 0 and produces compiled output in the module's output directory.
Run the compiled entry point
Execute the module using its descriptor name:
ceylon run com.example.greeter
The runtime resolves the same module graph used during compilation. If the module contains a single shared void run() function, the toolchain invokes it. Expected output for the example above is the printed greeting string.
Package a self-contained archive
To produce an artifact that runs without the Ceylon CLI on PATH, create a fat-jar:
ceylon fat-jar com.example.greeter
Then run the generated archive with the standard Java launcher:
java -jar <generated-jar>
The fat-jar bundles the compiled module and its dependencies into a single executable archive. Verify that it prints the same greeting as ceylon run.
Expected checks and recovery
| Step | Command / location | Expected check | Recovery |
|---|---|---|---|
| Toolchain check | ceylon --version (any directory) | Version string printed | Install or repair the Ceylon CLI distribution. |
| Compile | ceylon compile (project root) | Exit code 0, no unresolved-module errors | Verify descriptor syntax and that each imported module version exists in a configured repository. |
| Run | ceylon run com.example.greeter (project root) | Program output appears | Ensure compilation succeeded and the module name matches the descriptor. |
| Fat-jar | ceylon fat-jar com.example.greeter then java -jar <generated-jar> | Same output as ceylon run | Recompile first; packaging depends on compiled output. Delete stale jars before retrying. |
Because compilation and packaging create files, rollback is straightforward: remove the generated output directory and any fat-jar files. These are build artifacts and can be deleted without affecting source files.
Limitations to verify before production
- Community maintenance: Ceylon's toolchain and module repository are community-maintained with historically long gaps between releases. Confirm current availability and supported JDK versions before production adoption.
- Explicit version resolution: Upgrading a shared dependency may require coordinated descriptor updates across modules, because resolution is explicit rather than transitive-by-default.
- Artifact compatibility: Ceylon module archives are not ordinary JARs consumed by standard Java tooling. Use the Ceylon CLI for module operations; the fat-jar is the supported path for
java -jarexecution. - Build integration: Avoid assuming Maven or Gradle plugins fully replace the Ceylon CLI. Verify that any build integration still honors
module.ceylondescriptors and repository layout.
A practical verification path is to run ceylon --version, create the minimal module above, execute ceylon compile, then ceylon run com.example.greeter, and finally package and launch the fat-jar with java -jar.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.