source/command/serverCommand.ml (193 lines of code) (raw):
(*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open Core
open Server
module ServerConfiguration = struct
type t = {
base: CommandStartup.BaseConfiguration.t;
socket_path: PyrePath.t;
strict: bool;
show_error_traces: bool;
additional_logging_sections: string list;
watchman_root: PyrePath.t option;
taint_model_paths: PyrePath.t list;
store_type_check_resolution: bool;
critical_files: CriticalFile.t list;
saved_state_action: SavedStateAction.t option;
}
[@@deriving sexp, compare, hash]
let of_yojson json =
let open Yojson.Safe.Util in
let open JsonParsing in
try
match CommandStartup.BaseConfiguration.of_yojson json with
| Result.Error _ as error -> error
| Result.Ok base ->
let critial_file_list_member =
let to_critical_file json = CriticalFile.of_yojson json |> Result.ok_or_failwith in
list_member ~f:to_critical_file
in
let socket_path = json |> path_member "socket_path" in
let watchman_root = json |> optional_path_member "watchman_root" in
let taint_model_paths = json |> path_list_member "taint_model_paths" ~default:[] in
let strict = json |> bool_member "strict" ~default:false in
let show_error_traces = json |> bool_member "show_error_traces" ~default:false in
let critical_files = json |> critial_file_list_member "critical_files" ~default:[] in
let saved_state_action =
json
|> member "saved_state_action"
|> function
| `Null -> None
| _ as json -> SavedStateAction.of_yojson json |> Result.ok_or_failwith |> Option.some
in
let store_type_check_resolution =
json |> bool_member "store_type_check_resolution" ~default:false
in
let additional_logging_sections =
json |> string_list_member "additional_logging_sections" ~default:[]
in
Result.Ok
{
base;
socket_path;
watchman_root;
taint_model_paths;
strict;
show_error_traces;
critical_files;
saved_state_action;
store_type_check_resolution;
additional_logging_sections;
}
with
| Type_error (message, _)
| Undefined (message, _) ->
Result.Error message
| other_exception -> Result.Error (Exn.to_string other_exception)
let start_options_of
{
base = { CommandStartup.BaseConfiguration.source_paths; _ };
socket_path;
watchman_root;
critical_files;
saved_state_action;
_;
}
=
{ StartOptions.source_paths; socket_path; watchman_root; critical_files; saved_state_action }
let analysis_configuration_of
{
base =
{
CommandStartup.BaseConfiguration.source_paths;
search_paths;
excludes;
checked_directory_allowlist;
checked_directory_blocklist;
extensions;
log_path;
global_root;
local_root;
debug;
enable_type_comments;
python_version = { Configuration.PythonVersion.major; minor; micro };
parallel;
number_of_workers;
shared_memory =
{ Configuration.SharedMemory.heap_size; dependency_table_power; hash_table_power };
remote_logging = _;
profiling_output = _;
memory_profiling_output = _;
};
strict;
socket_path = _;
taint_model_paths;
show_error_traces;
store_type_check_resolution;
watchman_root = _;
critical_files = _;
saved_state_action = _;
additional_logging_sections = _;
}
=
Configuration.Analysis.create
~parallel
~analyze_external_sources:false
~filter_directories:checked_directory_allowlist
~ignore_all_errors:checked_directory_blocklist
~number_of_workers
~local_root:(Option.value local_root ~default:global_root)
~project_root:global_root
~search_paths:(List.map search_paths ~f:SearchPath.normalize)
~taint_model_paths
~strict
~debug
~show_error_traces
~excludes
~extensions
~store_type_check_resolution
~incremental_style:Configuration.Analysis.FineGrained
~log_directory:(PyrePath.absolute log_path)
~python_major_version:major
~python_minor_version:minor
~python_micro_version:micro
~shared_memory_heap_size:heap_size
~shared_memory_dependency_table_power:dependency_table_power
~shared_memory_hash_table_power:hash_table_power
~enable_type_comments
~source_paths:(Configuration.SourcePaths.to_search_paths source_paths)
()
end
let run_server configuration_file =
match CommandStartup.read_and_parse_json configuration_file ~f:ServerConfiguration.of_yojson with
| Result.Error message ->
Log.error "%s" message;
exit 1
| Result.Ok
({
ServerConfiguration.base =
{
CommandStartup.BaseConfiguration.log_path;
global_root;
local_root;
debug;
remote_logging;
profiling_output;
memory_profiling_output;
_;
};
additional_logging_sections;
_;
} as server_configuration) ->
CommandStartup.setup_global_states
~global_root
~local_root
~debug
~additional_logging_sections
~remote_logging
~profiling_output
~memory_profiling_output
();
(* Show start up notification. *)
StartupNotification.consume ~log_path ()
|> Option.iter ~f:(fun message -> Log.warning "%s" message);
(* Ignore SIGPIPE since >99% of the time they are non-fatal but the default Unix behavior is
for it to terminate the server, which is not ideal. Besides, individual callsites can
mostly detect the same class of issue by handling the EPIPE unix errno. *)
Signal.Expert.(set Signal.pipe `Ignore);
let exit_status =
let start_options = ServerConfiguration.start_options_of server_configuration in
let configuration = ServerConfiguration.analysis_configuration_of server_configuration in
Lwt_main.run
(Start.start_server_and_wait ~event_channel:Lwt_io.stdout ~configuration start_options)
in
exit (Start.ExitStatus.exit_code exit_status)
let command =
let filename_argument = Command.Param.(anon ("filename" %: Filename.arg_type)) in
Command.basic
~summary:"Starts a new Pyre server."
(Command.Param.map filename_argument ~f:(fun filename () -> run_server filename))