Appearance
| 1 | namespace Driver is | |
| 2 | use System.Exception; | |
| 3 | ||
| 4 | use IO.Std; | |
| 5 | use IO.Path; | |
| 6 | use IO.File; | |
| 7 | use IO.Directory; | |
| 8 | ||
| 9 | use Collections.Iterable; | |
| 10 | use Collections.LIST; | |
| 11 | use Collections.SET; | |
| 12 | ||
| 13 | use System.Text.RegularExpressions.Regex; | |
| 14 | use System.Text.RegularExpressions.Match; | |
| 15 | ||
| 16 | use System.Runtime.InteropServices.RuntimeInformation; | |
| 17 | use System.Runtime.InteropServices.OSPlatform; | |
| 18 | ||
| 19 | use Ghul.Pipes; | |
| 20 | ||
| 21 | use IoC; | |
| 22 | use Logging; | |
| 23 | use Compiler; | |
| 24 | ||
| 25 | use Analysis.ANALYSER; | |
| 26 | ||
| 27 | class MAIN is | |
| 28 | paths: Driver.PATH_CONFIG; | |
| 29 | project_name: string; | |
| 30 | container: IoC.CONTAINER; | |
| 31 | compiler: COMPILER; | |
| 32 | flags: GLOBAL_BUILD_FLAGS; | |
| 33 | output_file_name_generator: OUTPUT_FILE_NAME_GENERATOR; | |
| 34 | assemblies: LIST[string]; | |
| 35 | ghul_source_files: LIST[string]; | |
| 36 | analyse_files: LIST[string]; | |
| 37 | module_version: string; | |
| 38 | assembly_info: Semantic.DotNet.ASSEMBLY_INFO; | |
| 39 | ||
| 40 | emit_boilerplate_il: string?; | |
| 41 | ||
| 42 | want_format: bool; | |
| 43 | ||
| 44 | compiler_version: string => | |
| 45 | let assembly_version = | |
| 46 | System.Reflection.Assembly | |
| 47 | .get_entry_assembly()! | |
| 48 | .get_custom_attributes(typeof System.Reflection.AssemblyInformationalVersionAttribute, false) | |
| 49 | |> map(va => cast System.Reflection.AssemblyInformationalVersionAttribute?(va)!) | |
| 50 | |> first() | |
| 51 | ||
| 52 | in | |
| 53 | if assembly_version? then | |
| 54 | "{assembly_version.informational_version}" | |
| 55 | else | |
| 56 | "v0.0.0-unknown.1" | |
| 57 | fi; | |
| 58 | ||
| 59 | entry(arguments: string[]) static is | |
| 60 | let full_arguments = LIST[string](arguments.count + 1); | |
| 61 | ||
| 62 | add_arguments(full_arguments, arguments); | |
| 63 | ||
| 64 | let instance = MAIN(LIST[string](full_arguments)); | |
| 65 | si | |
| 66 | ||
| 67 | add_arguments(result: LIST[string], source: Iterable[string]) static is | |
| 68 | let seen_files = SET(); | |
| 69 | ||
| 70 | add_arguments(seen_files, result, source); | |
| 71 | si | |
| 72 | ||
| 73 | add_arguments(seen_files: SET[string], result: LIST[string], source: Iterable[string]) static is | |
| 74 | for argument in source do | |
| 75 | if argument.starts_with('@') then | |
| 76 | read_arguments_from_file(seen_files, result, argument.substring(1)); | |
| 77 | elif argument.length > 0 then | |
| 78 | result.add(argument); | |
| 79 | fi | |
| 80 | od | |
| 81 | si | |
| 82 | ||
| 83 | read_arguments_from_file(seen_files: SET[string], result: LIST[string], path: string) static is | |
| 84 | if seen_files.contains(path) then | |
| 85 | return; | |
| 86 | fi | |
| 87 | ||
| 88 | let parser = ARGUMENTS_PARSER(); | |
| 89 | ||
| 90 | let text = File.read_all_text(path); | |
| 91 | ||
| 92 | let arguments = parser.parse_arguments(text); | |
| 93 | ||
| 94 | add_arguments(seen_files, result, arguments); | |
| 95 | si | |
| 96 | ||
| 97 | init(arguments: LIST[string]) is | |
| 98 | let result mut = 1; | |
| 99 | ||
| 100 | try | |
| 101 | Std.input_encoding = System.Text.UTF8Encoding(false); | |
| 102 | Std.output_encoding = System.Text.UTF8Encoding(false); | |
| 103 | ||
| 104 | if arguments.count == 0 then | |
| 105 | Std.out.write("ghūl {compiler_version}\n"); | |
| 106 | Std.out.flush(); | |
| 107 | ||
| 108 | System.Environment.exit(0); | |
| 109 | fi | |
| 110 | ||
| 111 | container = IoC.CONTAINER.instance; | |
| 112 | ||
| 113 | paths = container.path_config; | |
| 114 | flags = container.build_flags; | |
| 115 | compiler = COMPILER(); | |
| 116 | output_file_name_generator = OUTPUT_FILE_NAME_GENERATOR(); | |
| 117 | assembly_info = container.assembly_info; | |
| 118 | module_version = "0:0:0:0"; | |
| 119 | ||
| 120 | parse_flags(arguments); | |
| 121 | ||
| 122 | IoC.CONTAINER.instance.assemblies.import(assemblies); | |
| 123 | ||
| 124 | if flags.want_analyse then | |
| 125 | analyse(); | |
| 126 | ||
| 127 | System.Environment.exit(0); | |
| 128 | fi | |
| 129 | ||
| 130 | if want_format then | |
| 131 | format_files(); | |
| 132 | ||
| 133 | System.Environment.exit(0); | |
| 134 | fi | |
| 135 | ||
| 136 | start_build(); | |
| 137 | ||
| 138 | compiler.post_parse(); | |
| 139 | ||
| 140 | compiler.build(); | |
| 141 | ||
| 142 | result = finish_build(); | |
| 143 | catch e: Exception | |
| 144 | Std.error.write(e); | |
| 145 | Std.error.write("\n"); | |
| 146 | Std.error.flush(); | |
| 147 | ||
| 148 | result = 1; | |
| 149 | yrt | |
| 150 | ||
| 151 | if result == 0 then | |
| 152 | Std.error.write("*** succeeded ***"); | |
| 153 | else | |
| 154 | Std.error.write("!!! failed !!!"); | |
| 155 | fi | |
| 156 | ||
| 157 | Std.error.write("\n"); | |
| 158 | ||
| 159 | System.Environment.exit(result); | |
| 160 | si | |
| 161 | ||
| 162 | parse_flags(args: Iterable[string]) is | |
| 163 | assemblies = LIST(); | |
| 164 | ghul_source_files = LIST(); | |
| 165 | ||
| 166 | let args_iterator = args.iterator; | |
| 167 | ||
| 168 | flags.want_compile_up_to_expressions = true; | |
| 169 | flags.want_compile_expressions = true; | |
| 170 | flags.want_assembler = true; | |
| 171 | flags.want_executable = true; | |
| 172 | ||
| 173 | let want_type_check = false; | |
| 174 | let do_not_want_type_check = false; | |
| 175 | ||
| 176 | let get_next_argument = (a: Collections.Iterator[string]) is | |
| 177 | a.move_next(); | |
| 178 | return a.current.trim(); | |
| 179 | si; | |
| 180 | ||
| 181 | let deprecated_no_warn = (flag: string, slug: string) is | |
| 182 | Std.error.write_line("warning: {flag} is deprecated; use --suppress {slug} instead"); | |
| 183 | container.logger.suppress(slug); | |
| 184 | si; | |
| 185 | ||
| 186 | let conditional_defines = Collections.LIST[string](); | |
| 187 | let emit_boilerplate_il_path: string? mut = null; | |
| 188 | ||
| 189 | for s in args_iterator do | |
| 190 | if s =~ "-A" \/ s =~ "--analyse" then | |
| 191 | flags.want_analyse = true; | |
| 192 | flags.want_assembler = false; | |
| 193 | flags.want_executable = false; | |
| 194 | elif s =~ "-G" \/ s =~ "--type-check" then | |
| 195 | flags.want_assembler = false; | |
| 196 | flags.want_executable = false; | |
| 197 | elif s =~ "-g" \/ s =~ "--no-type-check" then | |
| 198 | flags.want_compile_up_to_expressions = false; | |
| 199 | flags.want_compile_expressions = false; | |
| 200 | flags.want_assembler = false; | |
| 201 | flags.want_executable = false; | |
| 202 | elif s =~ "-E" \/ s =~ "--ignore-errors" then | |
| 203 | flags.ignore_errors = true; | |
| 204 | elif s =~ "-S" \/ s =~ "--assembler" then | |
| 205 | flags.want_executable = false; | |
| 206 | elif s =~ "--library" then | |
| 207 | flags.want_library = true; | |
| 208 | elif s =~ "--debug" then | |
| 209 | flags.want_debug = true; | |
| 210 | elif s =~ "--define" then | |
| 211 | conditional_defines.add(get_next_argument(args_iterator)); | |
| 212 | elif s =~ "--entry" then | |
| 213 | container.ir_context.entry_point_name = get_next_argument(args_iterator); | |
| 214 | elif s =~ "--v3" then | |
| 215 | conditional_defines.add("v3"); | |
| 216 | elif s =~ "--test-run" then | |
| 217 | flags.is_test_run = true; | |
| 218 | elif s =~ "-N" \/ s =~ "--dotnet" then | |
| 219 | // do nothing - .NET is the only supported option | |
| 220 | elif s =~ "-o" \/ s =~ "--output" then | |
| 221 | output_file_name_generator.force(get_next_argument(args_iterator)); | |
| 222 | elif s =~ "-p" \/ s =~ "--library-prefix" then | |
| 223 | paths.library_prefix = get_next_argument(args_iterator); | |
| 224 | elif s =~ "-a" \/ s =~ "--assembly" then | |
| 225 | assemblies.add(get_next_argument(args_iterator)); | |
| 226 | elif s =~ "--assembly-info-string" then | |
| 227 | let info_arg = get_next_argument(args_iterator); | |
| 228 | let info = info_arg.split(['=']); | |
| 229 | ||
| 230 | if info.count == 2 then | |
| 231 | assembly_info.add_attribute(info[0].trim(), info[1].trim()); | |
| 232 | else | |
| 233 | Std.error.write_line("warning: ignoring garbled assembly info: {info_arg}"); | |
| 234 | fi | |
| 235 | elif s =~ "--version" then | |
| 236 | let version_string = get_next_argument(args_iterator); | |
| 237 | ||
| 238 | let version_string_first_part = version_string.split(['-', '+']) |> first(); | |
| 239 | ||
| 240 | let version: System.Version mut; | |
| 241 | ||
| 242 | try | |
| 243 | if | |
| 244 | version_string_first_part? | |
| 245 | then | |
| 246 | let version_parts = version_string_first_part.split(['.']); | |
| 247 | ||
| 248 | if version_parts.count > 4 then | |
| 249 | throw Exception("invalid version number"); | |
| 250 | fi | |
| 251 | ||
| 252 | let b = System.Text.StringBuilder(); | |
| 253 | let seen_any mut = false; | |
| 254 | ||
| 255 | for p in version_parts do | |
| 256 | if seen_any then | |
| 257 | b.append(":"); | |
| 258 | fi | |
| 259 | ||
| 260 | b.append(int.parse(p)); | |
| 261 | ||
| 262 | seen_any = true; | |
| 263 | od | |
| 264 | ||
| 265 | for i in version_parts.count..4 do | |
| 266 | b.append(":0"); | |
| 267 | od | |
| 268 | ||
| 269 | module_version = b.to_string(); | |
| 270 | else | |
| 271 | throw Exception("invalid version number"); | |
| 272 | fi | |
| 273 | ||
| 274 | catch ex: Exception | |
| 275 | Std.error.write_line("warning: ignoring garbled version number: {version_string}"); | |
| 276 | yrt | |
| 277 | elif s =~ "--ilasm-packages" then | |
| 278 | let package_paths = get_next_argument(args_iterator); | |
| 279 | ||
| 280 | paths.add_ilasm_packages(package_paths.split([';'])); | |
| 281 | elif s =~ "--sdk-ref-path" then | |
| 282 | paths.sdk_reference_path = get_next_argument(args_iterator); | |
| 283 | elif s =~ "--target-framework" then | |
| 284 | paths.target_framework = get_next_argument(args_iterator); | |
| 285 | elif s =~ "--exclude-runtime-symbols" then | |
| 286 | flags.exclude_runtime_symbols = true; | |
| 287 | elif s =~ "--keep-out-il" then | |
| 288 | flags.want_keep_out_il = true; | |
| 289 | elif s =~ "--emit-boilerplate-il" then | |
| 290 | emit_boilerplate_il_path = get_next_argument(args_iterator); | |
| 291 | elif s =~ "--show-analysis-stats" then | |
| 292 | flags.want_analysis_stats = true; | |
| 293 | elif s =~ "--no-analysis-heap-watchdog" then | |
| 294 | flags.no_analysis_heap_watchdog = true; | |
| 295 | elif s =~ "--incremental-analysis" then | |
| 296 | flags.want_incremental_analysis = true; | |
| 297 | elif s =~ "--global-namespace" then | |
| 298 | flags.want_global_namespace = true; | |
| 299 | elif s =~ "--underscore-access" then | |
| 300 | let mode = get_next_argument(args_iterator); | |
| 301 | if mode =~ "private" then | |
| 302 | flags.underscore_access = UnderscoreAccess.PRIVATE; | |
| 303 | elif mode =~ "protected" then | |
| 304 | flags.underscore_access = UnderscoreAccess.PROTECTED; | |
| 305 | elif mode =~ "legacy" then | |
| 306 | flags.underscore_access = UnderscoreAccess.LEGACY; | |
| 307 | else | |
| 308 | Std.error.write_line("warning: unrecognized --underscore-access mode: {mode} (expected legacy, private or protected)"); | |
| 309 | fi | |
| 310 | elif s =~ "--format" then | |
| 311 | want_format = true; | |
| 312 | elif s =~ "--warn-implicit-mutable-let" then | |
| 313 | Std.error.write_line("warning: --warn-implicit-mutable-let is deprecated; the implicit-mutable-let warning is now an error and the flag has no effect"); | |
| 314 | elif s =~ "--no-warn-definite-return" then | |
| 315 | deprecated_no_warn("--no-warn-definite-return", "definite-return"); | |
| 316 | elif s =~ "--no-warn-definite-assignment" then | |
| 317 | deprecated_no_warn("--no-warn-definite-assignment", "definite-assignment"); | |
| 318 | elif s =~ "--no-warn-null-deref" then | |
| 319 | deprecated_no_warn("--no-warn-null-deref", "null-deref"); | |
| 320 | elif s =~ "--no-warn-non-optional" then | |
| 321 | deprecated_no_warn("--no-warn-non-optional", "non-optional"); | |
| 322 | elif s =~ "--no-warn-impossible-cast" then | |
| 323 | deprecated_no_warn("--no-warn-impossible-cast", "impossible-cast"); | |
| 324 | elif s =~ "--no-warn-narrowing-always-succeeds" then | |
| 325 | deprecated_no_warn("--no-warn-narrowing-always-succeeds", "narrowing-always-succeeds"); | |
| 326 | elif s =~ "--no-warn-likely-override-mismatch" then | |
| 327 | deprecated_no_warn("--no-warn-likely-override-mismatch", "likely-override-mismatch"); | |
| 328 | elif s =~ "--suppress" then | |
| 329 | for slug in get_next_argument(args_iterator).split([',']) do | |
| 330 | let trimmed = slug.trim(); | |
| 331 | if trimmed.length > 0 then | |
| 332 | container.logger.suppress(trimmed); | |
| 333 | fi | |
| 334 | od | |
| 335 | elif s =~ "--warn-as-error" then | |
| 336 | for slug in get_next_argument(args_iterator).split([',']) do | |
| 337 | let trimmed = slug.trim(); | |
| 338 | if trimmed =~ "all" then | |
| 339 | container.logger.set_all_warnings_are_errors(true); | |
| 340 | elif trimmed.length > 0 then | |
| 341 | container.logger.promote_to_error(trimmed); | |
| 342 | fi | |
| 343 | od | |
| 344 | elif s =~ "--warn-as-hint" then | |
| 345 | for slug in get_next_argument(args_iterator).split([',']) do | |
| 346 | let trimmed = slug.trim(); | |
| 347 | if trimmed.length > 0 then | |
| 348 | container.logger.demote_to_hint(trimmed); | |
| 349 | fi | |
| 350 | od | |
| 351 | elif s =~ "--warn-as-info" then | |
| 352 | for slug in get_next_argument(args_iterator).split([',']) do | |
| 353 | let trimmed = slug.trim(); | |
| 354 | if trimmed.length > 0 then | |
| 355 | container.logger.demote_to_info(trimmed); | |
| 356 | fi | |
| 357 | od | |
| 358 | elif s.starts_with('-') then | |
| 359 | Std.error.write_line("warning: ignoring unknown option: {s}"); | |
| 360 | elif SOURCE_FILE_CATEGORIZER.is_ghul(s) then | |
| 361 | output_file_name_generator.seen_file(s); | |
| 362 | ghul_source_files.add(s); | |
| 363 | else | |
| 364 | Std.error.write_line("warning: ignoring unrecognized argument: {s}"); | |
| 365 | fi | |
| 366 | od | |
| 367 | ||
| 368 | if emit_boilerplate_il_path? then | |
| 369 | if !File.exists(emit_boilerplate_il_path) then | |
| 370 | Std.error.write_line("warning: library boilerplate IL file not found: {emit_boilerplate_il_path}"); | |
| 371 | else | |
| 372 | emit_boilerplate_il = File.read_all_text(emit_boilerplate_il_path); | |
| 373 | fi | |
| 374 | fi | |
| 375 | ||
| 376 | flags.mark_valid(); | |
| 377 | ||
| 378 | if flags.is_test_run then | |
| 379 | container.want_human_readable_logger(Std.error); | |
| 380 | fi | |
| 381 | ||
| 382 | container.conditional_compilation.set_is_enabled(conditional_defines); | |
| 383 | si | |
| 384 | ||
| 385 | format_files() is | |
| 386 | for path in ghul_source_files do | |
| 387 | try | |
| 388 | let source_file = | |
| 389 | compiler.parse(path, File.open_text(path), flags.want_compile_up_to_expressions, flags.want_compile_expressions, false); | |
| 390 | ||
| 391 | let formatter = | |
| 392 | Syntax.Process.Printer.FORMATTER(source_file.trivia, 100); | |
| 393 | ||
| 394 | Std.out.write(formatter.format(source_file.definition)); | |
| 395 | Std.out.flush(); | |
| 396 | catch ex: Exception | |
| 397 | Std.error.write_line("error: could not format {path}: {ex.message}"); | |
| 398 | yrt | |
| 399 | od | |
| 400 | si | |
| 401 | ||
| 402 | start_build() is | |
| 403 | queue_source_files(); | |
| 404 | ||
| 405 | if flags.want_assembler then | |
| 406 | container.value_boxer.want_boxing = true; | |
| 407 | container.ir_context.enter_file("out.il", true); | |
| 408 | ||
| 409 | let module_name = Path.get_file_name_without_extension(output_file_name_generator.result) ?? "module"; | |
| 410 | ||
| 411 | container.boilerplate_generator.gen( | |
| 412 | "header-top", | |
| 413 | [module_name, module_version] | |
| 414 | ); | |
| 415 | ||
| 416 | assembly_info.gen(); | |
| 417 | ||
| 418 | container.boilerplate_generator.gen( | |
| 419 | "header-tail", | |
| 420 | [module_name] | |
| 421 | ); | |
| 422 | fi | |
| 423 | si | |
| 424 | ||
| 425 | finish_build() -> int is | |
| 426 | container.logger.write_all_diagnostics(container.logger_writer, container.logger_formatter); | |
| 427 | ||
| 428 | if container.logger.any_errors /\ !flags.ignore_errors then | |
| 429 | return 1; | |
| 430 | elif container.logger.is_poisoned then | |
| 431 | container.logger.write_poison_messages(); | |
| 432 | ||
| 433 | Std.error.write_line("internal error"); | |
| 434 | return 2; | |
| 435 | fi | |
| 436 | ||
| 437 | if flags.want_assembler then | |
| 438 | return finish_build_dotnet(); | |
| 439 | else | |
| 440 | return 0; | |
| 441 | fi | |
| 442 | si | |
| 443 | ||
| 444 | finish_build_dotnet() -> int is | |
| 445 | let ir_context = container.ir_context; | |
| 446 | let output_file mut = output_file_name_generator.result; | |
| 447 | ||
| 448 | if emit_boilerplate_il? then | |
| 449 | container.ir_context.write(emit_boilerplate_il!); | |
| 450 | fi | |
| 451 | ||
| 452 | if output_file.last_index_of('.') < 0 then | |
| 453 | output_file = "{output_file}.{if flags.want_library then "dll" else "exe" fi}"; | |
| 454 | fi | |
| 455 | ||
| 456 | ir_context.leave_file("out.il"); | |
| 457 | ||
| 458 | if !flags.want_executable then | |
| 459 | return 0; | |
| 460 | fi | |
| 461 | ||
| 462 | let ilasm_path = paths.ilasm_path; | |
| 463 | ||
| 464 | if !File.exists(ilasm_path) then | |
| 465 | Std.error.write_line("error: ilasm not found at '{ilasm_path}'"); | |
| 466 | return 0; | |
| 467 | fi | |
| 468 | ||
| 469 | let ilasm_args mut = | |
| 470 | "-quiet out.il -output={output_file}"; | |
| 471 | ||
| 472 | if flags.want_library then | |
| 473 | ilasm_args = "{ilasm_args} -DLL"; | |
| 474 | fi | |
| 475 | ||
| 476 | if flags.want_debug then | |
| 477 | ilasm_args = "{ilasm_args} -debug -pdbfmt=portable"; | |
| 478 | fi | |
| 479 | ||
| 480 | let ilasm = System.Diagnostics.Process.start(ilasm_path, ilasm_args); | |
| 481 | ||
| 482 | ilasm.wait_for_exit(); | |
| 483 | ||
| 484 | let result mut = ilasm.exit_code; | |
| 485 | ||
| 486 | if result != 0 then | |
| 487 | Std.error.write_line("error: ilasm failed"); | |
| 488 | ||
| 489 | return result; | |
| 490 | fi | |
| 491 | ||
| 492 | // A library assembly is loaded into a host's runtime config, | |
| 493 | // not run directly, so it needs neither its own | |
| 494 | // `.runtimeconfig.json` nor the executable bit. | |
| 495 | if !flags.want_library then | |
| 496 | let runtime_config_file = | |
| 497 | if output_file.last_index_of('.') == output_file.length - 4 then | |
| 498 | "{output_file.substring(0, output_file.last_index_of('.'))}.runtimeconfig.json"; | |
| 499 | else | |
| 500 | "{output_file}.runtimeconfig.json"; | |
| 501 | fi; | |
| 502 | ||
| 503 | let resolver = TARGET_FRAMEWORK_RESOLVER(); | |
| 504 | let tfm = resolver.resolve_tfm(paths.target_framework, paths.sdk_reference_path, paths.host_target_framework); | |
| 505 | let framework_version = resolver.resolve_framework_version(tfm); | |
| 506 | ||
| 507 | let runtime_config = File.create_text(runtime_config_file); | |
| 508 | runtime_config.write("{{\"runtimeOptions\":{{\"tfm\":\"{tfm}\",\"framework\":{{\"name\":\"Microsoft.NETCore.App\",\"version\":\"{framework_version}\"}} }} }}"); | |
| 509 | runtime_config.close(); | |
| 510 | ||
| 511 | if !RuntimeInformation.is_o_s_platform(OSPlatform.windows) then | |
| 512 | let chmod = System.Diagnostics.Process.start("/bin/chmod", "+x {output_file}"); | |
| 513 | ||
| 514 | chmod.wait_for_exit(); | |
| 515 | ||
| 516 | result = chmod.exit_code; | |
| 517 | ||
| 518 | if result != 0 then | |
| 519 | Std.error.write_line("compiled successfully but failed to set executable bit on resulting binary: {output_file}"); | |
| 520 | fi | |
| 521 | fi | |
| 522 | fi | |
| 523 | ||
| 524 | @IF.release() | |
| 525 | if !flags.want_keep_out_il then | |
| 526 | File.delete("out.il"); | |
| 527 | fi | |
| 528 | ||
| 529 | return result; | |
| 530 | si | |
| 531 | ||
| 532 | queue_source_files() is | |
| 533 | for file in ghul_source_files do | |
| 534 | if file.ends_with(".ghul") then | |
| 535 | queue_source_file(file, false); | |
| 536 | fi | |
| 537 | od | |
| 538 | si | |
| 539 | ||
| 540 | queue_source_file(path: string, is_internal_file: bool) is | |
| 541 | if flags.want_analyse then | |
| 542 | analyse_files.add(path); | |
| 543 | ||
| 544 | return; | |
| 545 | fi | |
| 546 | ||
| 547 | let use reader = File.open_text(path); | |
| 548 | ||
| 549 | compiler.parse_and_queue(path, reader, flags.want_compile_up_to_expressions, flags.want_compile_expressions, is_internal_file); | |
| 550 | si | |
| 551 | ||
| 552 | analyse() is | |
| 553 | analyse_files = LIST(); | |
| 554 | ||
| 555 | container.watchdog.heap_check_disabled = flags.no_analysis_heap_watchdog; | |
| 556 | ||
| 557 | // The analyser reads protocol frames and EDIT payloads from stdin a | |
| 558 | // character at a time. Console.In (Std.`in`) buffers only 256 bytes, | |
| 559 | // so a large EDIT payload costs thousands of read() syscalls. Read | |
| 560 | // instead through a 256 KB buffer over the raw standard-input | |
| 561 | // stream: that exceeds every compiler source file (largest ~230 KB, | |
| 562 | // p99 ~44 KB), so a single-file EDIT is drained in one read() and a | |
| 563 | // whole-project EDIT in few. A buffered read still returns as soon | |
| 564 | // as any bytes are available — a fully-received request is never | |
| 565 | // delayed waiting for the buffer to fill. | |
| 566 | let analyser_input = | |
| 567 | IO.StreamReader( | |
| 568 | Std.open_standard_input(), | |
| 569 | System.Text.UTF8Encoding(false), | |
| 570 | false, | |
| 571 | 262144 | |
| 572 | ); | |
| 573 | ||
| 574 | let analyser = ANALYSER( | |
| 575 | compiler, | |
| 576 | container.timers, | |
| 577 | container.symbol_table, | |
| 578 | container.symbol_use_locations, | |
| 579 | container.symbol_definition_locations, | |
| 580 | container.completer, | |
| 581 | container.signature_help, | |
| 582 | analyser_input, | |
| 583 | Std.out, | |
| 584 | analyse_files, | |
| 585 | flags, | |
| 586 | container.watchdog | |
| 587 | ); | |
| 588 | ||
| 589 | Std.error.write_line("ghūl compiler {compiler_version}: serving analysis requests"); | |
| 590 | ||
| 591 | analyser.run(); | |
| 592 | si | |
| 593 | si | |
| 594 | si | |
| 595 |