title: Compiler Guide sidebar_position: 3 id: compiler_guide license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
This guide covers installation, usage, and integration of the Fory IDL compiler.
cd compiler pip install -e .
foryc --help
foryc [OPTIONS] FILES...
foryc --scan-generated [OPTIONS]
Compile options:
| Option | Description | Default |
|---|---|---|
--lang | Comma-separated target languages | all |
--output, -o | Output directory | ./generated |
-I, --proto_path, --import_path | Add directory to import search path (can be repeated) | (none) |
--java_out=DST_DIR | Generate Java code in DST_DIR | (none) |
--python_out=DST_DIR | Generate Python code in DST_DIR | (none) |
--cpp_out=DST_DIR | Generate C++ code in DST_DIR | (none) |
--go_out=DST_DIR | Generate Go code in DST_DIR | (none) |
--rust_out=DST_DIR | Generate Rust code in DST_DIR | (none) |
--csharp_out=DST_DIR | Generate C# code in DST_DIR | (none) |
--javascript_out=DST_DIR | Generate JavaScript/TypeScript code in DST_DIR | (none) |
--swift_out=DST_DIR | Generate Swift code in DST_DIR | (none) |
--dart_out=DST_DIR | Generate Dart code in DST_DIR | (none) |
--scala_out=DST_DIR | Generate Scala 3 code in DST_DIR | (none) |
--kotlin_out=DST_DIR | Generate Kotlin code in DST_DIR | (none) |
--go_nested_type_style | Go nested type naming: camelcase or underscore | underscore |
--swift_namespace_style | Swift namespace style: enum or flatten | enum |
--emit-fdl | Emit translated FDL (for non-FDL inputs) | false |
--emit-fdl-path | Write translated FDL to this path (file or directory) | (stdout) |
--grpc | Generate gRPC service companions for supported outputs | false |
--grpc-python-mode=MODE | Python gRPC mode: async or sync | async |
--grpc-web | Generate JavaScript gRPC-Web client companions | false |
Schema-level file options are supported for language-specific generation choices. For go_nested_type_style and swift_namespace_style, the CLI flag overrides the schema option when both are present. Rust temporal codegen has no CLI flag: set option rust_use_chrono_temporal_types = true; in the schema to generate chrono::NaiveDate, chrono::NaiveDateTime, and chrono::Duration instead of the default fory::Date, fory::Timestamp, and fory::Duration. Crates that compile generated chrono-based Rust code must depend on chrono and enable Fory's chrono feature.
Scan options (with --scan-generated):
| Option | Description | Default |
|---|---|---|
--root | Root directory to scan | . |
--relative | Print paths relative to root | false |
--delete | Delete matched generated files | false |
--dry-run | Scan/print only, do not delete | false |
Use --scan-generated to find files produced by foryc. The scanner walks the tree recursively, skips build/, target/, and hidden directories, and prints each generated file as it is found.
# Scan current directory foryc --scan-generated # Scan a specific root foryc --scan-generated --root ./src # Print paths relative to the scan root foryc --scan-generated --root ./src --relative # Delete scanned generated files foryc --scan-generated --root ./src --delete # Dry-run (scan and print only) foryc --scan-generated --root ./src --dry-run
Compile for all languages:
foryc schema.fdl
Compile for a selected subset of languages:
foryc schema.fdl --lang java,python,csharp,javascript,swift,dart,kotlin
Specify output directory:
foryc schema.fdl --output ./src/generated
Compile multiple files:
foryc user.fdl order.fdl product.fdl --output ./generated
Compile a simple schema containing service definitions (Java + Python + Go + Rust + C# + Dart + Scala + Kotlin + JavaScript models):
foryc compiler/examples/service.fdl --java_out=./generated/java --python_out=./generated/python --go_out=./generated/go --rust_out=./generated/rust --csharp_out=./generated/csharp --dart_out=./generated/dart --scala_out=./generated/scala --kotlin_out=./generated/kotlin --javascript_out=./generated/javascript
Generate Java, Python, Go, Rust, C#, Dart, Scala, Kotlin, and Node.js JavaScript gRPC service companions:
foryc compiler/examples/service.fdl --java_out=./generated/java --python_out=./generated/python --go_out=./generated/go --rust_out=./generated/rust --csharp_out=./generated/csharp --dart_out=./generated/dart --scala_out=./generated/scala --kotlin_out=./generated/kotlin --javascript_out=./generated/javascript --grpc
The generated gRPC service code uses Fory to serialize request and response payloads. Java output imports grpc-java APIs, Python output defaults to grpc.aio, Go output imports grpc-go, Rust output imports tonic and bytes, Scala output imports grpc-java APIs, and Kotlin output imports grpc-java and grpc-kotlin APIs and uses coroutine stubs. C# output imports Grpc.Core.Api types and can be hosted with normal .NET gRPC packages such as Grpc.AspNetCore or called through Grpc.Net.Client. Dart output imports package:grpc. JavaScript output imports @grpc/grpc-js. Applications that compile or run those generated service files must provide their own gRPC dependencies. Fory packages do not add a hard gRPC dependency for this feature.
Generate synchronous Python gRPC companions for existing sync grpcio applications with:
foryc compiler/examples/service.fdl --python_out=./generated/python --grpc --grpc-python-mode=sync
Generate JavaScript gRPC-Web browser clients:
foryc compiler/examples/service.fdl --javascript_out=./generated/javascript --grpc-web
Use --grpc and --grpc-web together when the same JavaScript output should include both Node.js and browser companions. The browser companion imports grpc-web; the application must provide the package and a gRPC-Web compatible server or proxy.
Use import search paths:
# Add a single import path foryc src/main.fdl -I libs/common # Add multiple import paths (repeated option) foryc src/main.fdl -I libs/common -I libs/types # Add multiple import paths (comma-separated) foryc src/main.fdl -I libs/common,libs/types,third_party/ # Using --proto_path (protoc-compatible alias) foryc src/main.fdl --proto_path=libs/common # Mix all styles foryc src/main.fdl -I libs/common,libs/types --proto_path third_party/
Language-specific output directories (protoc-style):
# Generate only Java code to a specific directory foryc schema.fdl --java_out=./src/main/java # Generate multiple languages to different directories foryc schema.fdl --java_out=./java/gen --python_out=./python/src --cpp_out=./cpp/gen --go_out=./go/gen --rust_out=./rust/gen --csharp_out=./csharp/gen --javascript_out=./javascript/src --swift_out=./swift/gen --dart_out=./dart/gen --scala_out=./scala/gen --kotlin_out=./kotlin/gen # Combine with import paths foryc schema.fdl --java_out=./gen/java -I proto/ -I common/ # Generate Scala 3 code to a specific directory foryc schema.fdl --scala_out=./src/main/scala # Generate Scala 3 models and gRPC service companions foryc service.fdl --scala_out=./src/main/scala --grpc # Generate Kotlin code to a specific directory foryc schema.fdl --kotlin_out=./src/main/kotlin
When using --{lang}_out options:
Inspect translated Fory IDL from proto/fbs input:
# Print translated Fory IDL to stdout foryc schema.proto --emit-fdl # Write translated Fory IDL to a directory foryc schema.fbs --emit-fdl --emit-fdl-path ./translated
When compiling Fory IDL files with imports, the compiler searches for imported files in this order:
-I flag needed for same-directory imports.-I path in order - Additional search paths specified on the command lineSame-directory imports work automatically:
// main.fdl import "common.fdl"; // Found if common.fdl is in the same directory
# No -I needed for same-directory imports foryc main.fdl
Example project structure:
project/
├── src/
│ └── main.fdl # import "common.fdl";
└── libs/
└── common.fdl
Without -I (fails):
$ foryc src/main.fdl Import error: Import not found: common.fdl Searched in: /project/src
With -I (succeeds):
$ foryc src/main.fdl -I libs/ Compiling src/main.fdl... Resolved 1 import(s)
| Language | Flag | Output Extension | Description |
|---|---|---|---|
| Java | java | .java | POJOs with Fory annotations |
| Python | python | .py | Dataclasses with type hints |
| Go | go | .go | Structs with struct tags |
| Rust | rust | .rs | Structs with derive macros |
| C++ | cpp | .h | Structs with FORY macros |
| C# | csharp | .cs | Classes with Fory attributes |
| JavaScript/TypeScript | javascript | .ts | Interfaces with schema module helpers |
| Swift | swift | .swift | Fory Swift model macros |
| Dart | dart | .dart | @ForyStruct classes with annotations |
| Scala | scala | .scala | Scala 3 models with macro derivation |
| Kotlin | kotlin | .kt | Kotlin models with KSP serializers |
generated/
└── java/
└── com/
└── example/
├── User.java
├── Order.java
├── Status.java
└── ExampleForyModule.java
generated/
└── python/
└── example.py
generated/
└── go/
└── example/
└── example.go
go_package or the Fory IDL packagegenerated/
└── rust/
└── example.rs
generated/
└── cpp/
└── example.h
::)generated/
└── javascript/
├── example.ts
├── example_grpc.ts # with --grpc
└── example_grpc_web.ts # with --grpc-web
.ts file per schemaexport interface declarations for messagesexport enum declarations for enumsregisterXxxTypes(fory) plus default serializeX and deserializeX helpers included--grpc emits a Node.js companion using @grpc/grpc-js--grpc-web emits a browser client companion using grpc-webgenerated/
└── csharp/
└── example/
└── Example.cs
.cs file per schema named from the normalized PascalCase source file stemcsharp_namespace (if set) or Fory IDL packageXXXForyModule installation helper and ToBytes/FromBytes methodsroot.idl importing addressbook.fdl and tree.fdl)--grpc, one <ServiceName>Grpc.cs companion per service next to the schema file outputgenerated/
└── swift/
└── addressbook/
└── addressbook.swift
.swift file per schemaaddressbook.* -> Addressbook.*)@ForyStruct, enums use @ForyEnum, and unions use @ForyUnion/@ForyCasetoBytes/fromBytes helpersgenerated/
└── dart/
└── package/
├── package.dart
└── package.fory.dart
.dart file with annotated types, and a .fory.dart part file with generated serializersdemo.foo → demo/foo/)Int32List)--grpc, one <stem>_grpc.dart companion per schema is generated next to the model file, containing each service's Client and ServiceBase; it imports package:grpcgenerated/
└── scala/
└── example/
├── User.scala
├── Status.scala
├── Animal.scala
├── ExampleServiceGrpc.scala
└── ExampleForyModule.scala
org.apache.fory.scala.ForySerializeroptional T fields use Option[T]enumenum with @ForyUnion, @ForyCase, and an Unknown--grpc, one <ServiceName>Grpc.scala service companion is generated per local service definitiongenerated/
└── kotlin/
└── example/
├── User.kt
├── Status.kt
├── Animal.kt
└── ExampleForyModule.kt
kotlin_package when set, otherwise the Fory IDL package@ForyStruct and KSP-generated serializers@ForyUnion, @ForyCase, and an unknown-case carrier--grpc, one <ServiceName>GrpcKt.kt coroutine service companion per serviceRun the end-to-end C# IDL matrix (FDL/IDL/Proto/FBS generation plus roundtrip tests):
cd integration_tests/idl_tests ./run_csharp_tests.sh
This runner executes same-schema and compatible roundtrips across:
addressbook, auto_id, complex_pb primitivescollection and union/list variantsoptional_typesany_example (.fdl) and any_example (.proto)tree and graph reference-tracking casesmonster.fbs and complex_fbs.fbsroot.idl cross-package import coverageRun the end-to-end Swift IDL matrix (FDL/IDL/Proto/FBS generation plus roundtrip tests):
cd integration_tests/idl_tests ./run_swift_tests.sh
This runs:
IDL_PEER_LANG=swiftThe script also sets DATA_FILE* variables so file-based roundtrip paths are exercised.
Add to your pom.xml:
<build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <id>generate-fory-types</id> <phase>generate-sources</phase> <goals> <goal>exec</goal> </goals> <configuration> <executable>foryc</executable> <arguments> <argument>${project.basedir}/src/main/fdl/schema.fdl</argument> <argument>--java_out</argument> <argument>${project.build.directory}/generated-sources/fory</argument> </arguments> </configuration> </execution> </executions> </plugin> </plugins> </build>
Add generated sources:
<build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>build-helper-maven-plugin</artifactId> <version>3.4.0</version> <executions> <execution> <phase>generate-sources</phase> <goals> <goal>add-source</goal> </goals> <configuration> <sources> <source>${project.build.directory}/generated-sources/fory</source> </sources> </configuration> </execution> </executions> </plugin> </plugins> </build>
Add to build.gradle:
task generateForyTypes(type: Exec) { commandLine 'foryc', "${projectDir}/src/main/fdl/schema.fdl", '--java_out', "${buildDir}/generated/sources/fory" } compileJava.dependsOn generateForyTypes sourceSets { main { java { srcDir "${buildDir}/generated/sources/fory" } } }
Add to setup.py or pyproject.toml:
# setup.py from setuptools import setup from setuptools.command.build_py import build_py import subprocess class BuildWithForyIdl(build_py): def run(self): subprocess.run([ 'foryc', 'schema.fdl', '--python_out', 'src/generated' ], check=True) super().run() setup( cmdclass={'build_py': BuildWithForyIdl}, # ... )
Add to your Go file:
//go:generate foryc ../schema.fdl --lang go --output . package models
Run:
go generate ./...
Add to build.rs:
use std::process::Command; fn main() { println!("cargo:rerun-if-changed=schema.fdl"); let status = Command::new("foryc") .args(&["schema.fdl", "--rust_out", "src/generated"]) .status() .expect("Failed to run foryc"); if !status.success() { panic!("Fory IDL compilation failed"); } }
Add to CMakeLists.txt:
find_program(FORY_COMPILER foryc) add_custom_command( OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/generated/example.h COMMAND ${FORY_COMPILER} ${CMAKE_CURRENT_SOURCE_DIR}/schema.fdl --cpp_out ${CMAKE_CURRENT_SOURCE_DIR}/generated DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/schema.fdl COMMENT "Generating Fory IDL types" ) add_custom_target(generate_fory_idl DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/generated/example.h) add_library(mylib ...) add_dependencies(mylib generate_fory_idl) target_include_directories(mylib PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/generated)
Create a rule in BUILD:
genrule( name = "generate_fdl", srcs = ["schema.fdl"], outs = ["generated/example.h"], cmd = "$(location //:fory_compiler) $(SRCS) --cpp_out $(RULEDIR)/generated", tools = ["//:fory_compiler"], ) cc_library( name = "models", hdrs = [":generate_fdl"], # ... )
Add the Fory dependency to pubspec.yaml:
dependencies: fory: ^1.3.0 dev_dependencies: build_runner: ^2.4.0
Generate schema types with the compiler:
foryc schema.fdl --dart_out=lib/generated
Then run build_runner to generate the serializers:
dart run build_runner build
Error: Line 5, Column 12: Expected ';' after field declaration
Fix: Check the indicated line for missing semicolons or syntax issues.
Error: Duplicate type name: User
Fix: Ensure each enum and message has a unique name within the file.
Error: Duplicate type ID 100: User and Order
Fix: Assign unique type IDs to each type.
Error: Unknown type 'Address' in Customer.address
Fix: Define the referenced type before using it, or check for typos.
Service RPC request and response types are validated in the same way: an RPC such as rpc SayHello (HelloRequest) returns (HelloReply); must reference defined message types, otherwise the validator reports an Unknown type '...' error on the RPC line.
Error: Duplicate field number 1 in User: name and id
Fix: Assign unique field numbers within each message.
project/ ├── fdl/ │ ├── common.fdl # Shared types │ ├── user.fdl # User domain │ └── order.fdl # Order domain ├── src/ │ └── generated/ # Generated code (git-ignored) └── build.gradle
Add to .gitignore:
# Generated Fory IDL code src/generated/ generated/
Always regenerate during builds:
# GitHub Actions example steps: - name: Install Fory IDL Compiler run: pip install ./compiler - name: Generate Types run: foryc fdl/*.fdl --output src/generated - name: Build run: ./gradlew build
When modifying schemas:
optional - For backward compatibilitymessage User [id=100] { string id = 1; string name = 2; // Field 3 was removed, don't reuse optional string email = 4; // New field }
foryc: command not found
Solution: Ensure the compiler is installed and in your PATH:
pip install -e ./compiler # Or add to PATH export PATH=$PATH:~/.local/bin
Permission denied: ./generated
Solution: Ensure write permissions on the output directory:
chmod -R u+w ./generated
Java: Ensure Fory dependency is in your project:
<dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-core</artifactId> <version>${fory.version}</version> </dependency>
Python: Ensure pyfory is installed:
pip install pyfory
Go: Ensure fory module is available:
go get github.com/apache/fory/go/fory
Rust: Ensure fory crate is in Cargo.toml:
[dependencies] fory = "x.y.z"
C++: Ensure Fory headers are in include path.
Dart: Ensure the fory package is in pubspec.yaml:
dependencies: fory: ^1.3.0