Compiling Executables
Build standalone executables from your Lean code. Lean compiles to efficient native code through C.
The Main Function
Every executable needs a main function:
lean
1-- Simplest main2def main : IO Unit :=3 IO.println "Hello, World!"45-- Main with arguments6def main (args : List String) : IO Unit := do7 IO.println s!"Arguments: {args}"89-- Main with exit code10def main : IO UInt32 := do11 IO.println "Running..."12 return 0 -- 0 = success, non-zero = failureBuilding with Lake
Define an executable in your lakefile:
lean
1-- lakefile.lean2import Lake3open Lake DSL45package myapp67@[default_target]8lean_exe myapp where9 root := `Mainbash
1# Build the executable2lake build34# Run it5lake exe myapp67# Or run directly8./.lake/build/bin/myappRelease Builds
For production, build with optimizations:
bash
1# Build with release optimizations2lake build -Krelease34# The executable is now optimized๐ก
Release builds are significantly faster. Always use
-Kreleasefor production executables.Command Line Argument Parsing
lean
1def main (args : List String) : IO UInt32 := do2 match args with3 | [] => 4 IO.println "Usage: myapp <command> [options]"5 return 16 | ["--help"] =>7 IO.println "myapp - A sample application"8 IO.println ""9 IO.println "Commands:"10 IO.println " run Run the main task"11 IO.println " check Check configuration"12 return 013 | ["run"] =>14 IO.println "Running..."15 return 016 | ["check"] =>17 IO.println "Checking configuration..."18 return 019 | cmd :: _ =>20 IO.println s!"Unknown command: {cmd}"21 return 1Multiple Executables
lean
1-- lakefile.lean2import Lake3open Lake DSL45package myproject67-- Library with shared code8lean_lib MyProject910-- Main CLI11lean_exe myapp where12 root := `Main1314-- Additional tool15lean_exe mytool where16 root := `Tool.Main1718-- Test runner19lean_exe tests where20 root := `Tests.Mainbash
1# Build all2lake build34# Build specific executable5lake build myapp6lake build mytool78# Run specific executable9lake exe myapp10lake exe mytoolKey Takeaway
Each executable has its own root file with a
main function. They can share code through library targets.Cross-Platform Considerations
lean
1def main : IO Unit := do2 -- Check platform3 if System.Platform.isWindows then4 IO.println "Running on Windows"5 else if System.Platform.isOSX then6 IO.println "Running on macOS"7 else8 IO.println "Running on Linux/Unix"9 10 -- Platform-specific paths11 let sep := if System.Platform.isWindows then "\\" else "/"12 IO.println s!"Path separator: {sep}"Environment Variables
lean
1def main : IO Unit := do2 -- Read environment variable3 let home โ IO.getEnv "HOME"4 match home with5 | none => IO.println "HOME not set"6 | some path => IO.println s!"Home: {path}"7 8 -- Set environment variable (for child processes)9 -- Note: This affects the current process10 IO.Process.run {11 cmd := "echo"12 args := #["$MY_VAR"]13 env := #[("MY_VAR", some "hello")]14 }Running External Commands
lean
1def main : IO Unit := do2 -- Run a command and get output3 let output โ IO.Process.output {4 cmd := "ls"5 args := #["-la"]6 }7 IO.println s!"Exit code: {output.exitCode}"8 IO.println s!"Output: {output.stdout}"9 10 -- Run interactively (inherits stdin/stdout)11 let exitCode โ IO.Process.spawn {12 cmd := "vim"13 args := #["file.txt"]14 stdin := .inherit15 stdout := .inherit16 stderr := .inherit17 } >>= (ยท.wait)18 IO.println s!"Editor exited with: {exitCode}"Deep Dive: Compilation Pipeline
Lean compilation works in stages:
- Lean โ IR: Lean code to internal representation
- IR โ C: IR to C code (in
.lake/build/ir/) - C โ Object: C to object files via system C compiler
- Link: Object files linked to final executable
The generated C is heavily optimized and uses reference counting for memory management.
Distribution
For distributing your executable:
bash
1# Build release2lake build -Krelease34# The executable is self-contained5# Copy from .lake/build/bin/myapp to wherever needed67# On Windows, it's myapp.exeโน
Lean executables link against the system C library. They're generally portable across machines with the same OS and architecture.
Example: Complete CLI Application
lean
1def version := "1.0.0"23def printHelp : IO Unit := do4 IO.println "myapp - A command line tool"5 IO.println ""6 IO.println "Usage: myapp [options] <command>"7 IO.println ""8 IO.println "Options:"9 IO.println " --help, -h Show this help"10 IO.println " --version, -v Show version"11 IO.println ""12 IO.println "Commands:"13 IO.println " init Initialize a new project"14 IO.println " build Build the project"15 IO.println " run Run the project"1617def doInit : IO UInt32 := do18 IO.println "Initializing..."19 IO.FS.writeFile "config.json" "{}"20 IO.println "Created config.json"21 return 02223def doBuild : IO UInt32 := do24 IO.println "Building..."25 -- Build logic here26 IO.println "Done!"27 return 02829def main (args : List String) : IO UInt32 := do30 match args with31 | [] | ["--help"] | ["-h"] => 32 printHelp33 return 034 | ["--version"] | ["-v"] =>35 IO.println s!"myapp version {version}"36 return 037 | ["init"] => doInit38 | ["build"] => doBuild39 | ["run"] => 40 IO.println "Running..."41 return 042 | cmd :: _ =>43 IO.println s!"Error: Unknown command '{cmd}'"44 IO.println "Run 'myapp --help' for usage"45 return 1