Skip to content

Commit 641d61a

Browse files
committed
support specify output dir for different languages
1 parent 4ca8fb8 commit 641d61a

File tree

3 files changed

+122
-9
lines changed

3 files changed

+122
-9
lines changed

compiler/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ fory compile schema.fdl --lang java,python --output ./generated
6868

6969
# Override package name
7070
fory compile schema.fdl --package myapp.models --output ./generated
71+
72+
# Language-specific output directories (protoc-style)
73+
fory compile schema.fdl --java_out=./src/main/java --python_out=./python/src
74+
75+
# Combine with other options
76+
fory compile schema.fdl --java_out=./gen --go_out=./gen/go -I ./proto
7177
```
7278

7379
### 3. Use Generated Code

compiler/fory_compiler/cli.py

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,47 @@ def parse_args(args: Optional[List[str]] = None) -> argparse.Namespace:
212212
help="Add a directory to the import search path. Can be specified multiple times.",
213213
)
214214

215+
# Language-specific output directories (protoc-style)
216+
compile_parser.add_argument(
217+
"--java_out",
218+
type=Path,
219+
default=None,
220+
metavar="DST_DIR",
221+
help="Generate Java code in DST_DIR",
222+
)
223+
224+
compile_parser.add_argument(
225+
"--python_out",
226+
type=Path,
227+
default=None,
228+
metavar="DST_DIR",
229+
help="Generate Python code in DST_DIR",
230+
)
231+
232+
compile_parser.add_argument(
233+
"--cpp_out",
234+
type=Path,
235+
default=None,
236+
metavar="DST_DIR",
237+
help="Generate C++ code in DST_DIR",
238+
)
239+
240+
compile_parser.add_argument(
241+
"--go_out",
242+
type=Path,
243+
default=None,
244+
metavar="DST_DIR",
245+
help="Generate Go code in DST_DIR",
246+
)
247+
248+
compile_parser.add_argument(
249+
"--rust_out",
250+
type=Path,
251+
default=None,
252+
metavar="DST_DIR",
253+
help="Generate Rust code in DST_DIR",
254+
)
255+
215256
return parser.parse_args(args)
216257

217258

@@ -234,12 +275,18 @@ def get_languages(lang_arg: str) -> List[str]:
234275

235276
def compile_file(
236277
file_path: Path,
237-
languages: List[str],
238-
output_dir: Path,
278+
lang_output_dirs: Dict[str, Path],
239279
package_override: Optional[str] = None,
240280
import_paths: Optional[List[Path]] = None,
241281
) -> bool:
242-
"""Compile a single FDL file with import resolution."""
282+
"""Compile a single FDL file with import resolution.
283+
284+
Args:
285+
file_path: Path to the FDL file
286+
lang_output_dirs: Dictionary mapping language name to output directory
287+
package_override: Optional package name override
288+
import_paths: List of import search paths
289+
"""
243290
print(f"Compiling {file_path}...")
244291

245292
# Parse and resolve imports
@@ -267,8 +314,7 @@ def compile_file(
267314
return False
268315

269316
# Generate code for each language
270-
for lang in languages:
271-
lang_output = output_dir / lang
317+
for lang, lang_output in lang_output_dirs.items():
272318
options = GeneratorOptions(
273319
output_dir=lang_output,
274320
package_override=package_override,
@@ -287,7 +333,44 @@ def compile_file(
287333

288334
def cmd_compile(args: argparse.Namespace) -> int:
289335
"""Handle the compile command."""
290-
languages = get_languages(args.lang)
336+
# Build language -> output directory mapping
337+
# Language-specific --{lang}_out options take precedence
338+
lang_specific_outputs = {
339+
"java": args.java_out,
340+
"python": args.python_out,
341+
"cpp": args.cpp_out,
342+
"go": args.go_out,
343+
"rust": args.rust_out,
344+
}
345+
346+
# Determine which languages to generate
347+
lang_output_dirs: Dict[str, Path] = {}
348+
349+
# First, add languages specified via --{lang}_out (these use direct paths)
350+
for lang, out_dir in lang_specific_outputs.items():
351+
if out_dir is not None:
352+
lang_output_dirs[lang] = out_dir
353+
354+
# Then, add languages from --lang that don't have specific output dirs
355+
# These use output_dir/lang pattern
356+
if args.lang != "all" or not lang_output_dirs:
357+
# Only use --lang if no language-specific outputs are set, or if --lang is explicit
358+
languages_from_arg = get_languages(args.lang)
359+
for lang in languages_from_arg:
360+
if lang not in lang_output_dirs:
361+
lang_output_dirs[lang] = args.output / lang
362+
363+
if not lang_output_dirs:
364+
print("Error: No target languages specified.", file=sys.stderr)
365+
print("Use --lang or --{lang}_out options.", file=sys.stderr)
366+
return 1
367+
368+
# Validate that all languages are supported
369+
invalid = [l for l in lang_output_dirs.keys() if l not in GENERATORS]
370+
if invalid:
371+
print(f"Error: Unknown language(s): {', '.join(invalid)}", file=sys.stderr)
372+
print(f"Available: {', '.join(GENERATORS.keys())}", file=sys.stderr)
373+
return 1
291374

292375
# Resolve and validate import paths (support comma-separated paths)
293376
import_paths = []
@@ -302,8 +385,9 @@ def cmd_compile(args: argparse.Namespace) -> int:
302385
print(f"Warning: Import path is not a directory: {part}", file=sys.stderr)
303386
import_paths.append(resolved)
304387

305-
# Create output directory
306-
args.output.mkdir(parents=True, exist_ok=True)
388+
# Create output directories
389+
for out_dir in lang_output_dirs.values():
390+
out_dir.mkdir(parents=True, exist_ok=True)
307391

308392
success = True
309393
for file_path in args.files:
@@ -312,7 +396,7 @@ def cmd_compile(args: argparse.Namespace) -> int:
312396
success = False
313397
continue
314398

315-
if not compile_file(file_path, languages, args.output, args.package, import_paths):
399+
if not compile_file(file_path, lang_output_dirs, args.package, import_paths):
316400
success = False
317401

318402
return 0 if success else 1

docs/schema/compiler-guide.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ fory compile [OPTIONS] FILES...
5454
| `--output`, `-o` | Output directory | `./generated` |
5555
| `--package` | Override package name from FDL file | (from file) |
5656
| `-I`, `--proto_path`, `--import_path` | Add directory to import search path (can be repeated) | (none) |
57+
| `--java_out=DST_DIR` | Generate Java code in DST_DIR | (none) |
58+
| `--python_out=DST_DIR` | Generate Python code in DST_DIR | (none) |
59+
| `--cpp_out=DST_DIR` | Generate C++ code in DST_DIR | (none) |
60+
| `--go_out=DST_DIR` | Generate Go code in DST_DIR | (none) |
61+
| `--rust_out=DST_DIR` | Generate Rust code in DST_DIR | (none) |
5762

5863
### Examples
5964

@@ -106,6 +111,24 @@ fory compile src/main.fdl --proto_path=libs/common
106111
fory compile src/main.fdl -I libs/common,libs/types --proto_path third_party/
107112
```
108113

114+
**Language-specific output directories (protoc-style):**
115+
116+
```bash
117+
# Generate only Java code to a specific directory
118+
fory compile schema.fdl --java_out=./src/main/java
119+
120+
# Generate multiple languages to different directories
121+
fory compile schema.fdl --java_out=./java/gen --python_out=./python/src --go_out=./go/gen
122+
123+
# Combine with import paths
124+
fory compile schema.fdl --java_out=./gen/java -I proto/ -I common/
125+
```
126+
127+
When using `--{lang}_out` options:
128+
- Only the specified languages are generated (not all languages)
129+
- Files are placed directly in the specified directory (not in a `{lang}/` subdirectory)
130+
- This is compatible with protoc-style workflows
131+
109132
## Import Path Resolution
110133

111134
When compiling FDL files with imports, the compiler searches for imported files in this order:

0 commit comments

Comments
 (0)