@@ -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
235276def 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
288334def 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
0 commit comments