1383 lines
46 KiB
OCaml
1383 lines
46 KiB
OCaml
(**************************************************************************)
|
|
(* *)
|
|
(* OCaml *)
|
|
(* *)
|
|
(* Sebastien Hinderer, projet Gallium, INRIA Paris *)
|
|
(* *)
|
|
(* Copyright 2017 Institut National de Recherche en Informatique et *)
|
|
(* en Automatique. *)
|
|
(* *)
|
|
(* All rights reserved. This file is distributed under the terms of *)
|
|
(* the GNU Lesser General Public License version 2.1, with the *)
|
|
(* special exception on linking described in the file LICENSE. *)
|
|
(* *)
|
|
(**************************************************************************)
|
|
|
|
(* Actions specific to the OCaml compilers *)
|
|
|
|
open Ocamltest_stdlib
|
|
open Actions
|
|
|
|
(* Extracting information from environment *)
|
|
|
|
let native_support = Ocamltest_config.arch <> "none"
|
|
|
|
let no_native_compilers _log env =
|
|
(Result.skip_with_reason "native compilers disabled", env)
|
|
|
|
let native_action a =
|
|
if native_support then a else (Actions.update a no_native_compilers)
|
|
|
|
let get_backend_value_from_env env bytecode_var native_var =
|
|
Ocaml_backends.make_backend_function
|
|
(Environments.safe_lookup bytecode_var env)
|
|
(Environments.safe_lookup native_var env)
|
|
|
|
let modules env =
|
|
Actions_helpers.words_of_variable env Ocaml_variables.modules
|
|
|
|
let plugins env =
|
|
Actions_helpers.words_of_variable env Ocaml_variables.plugins
|
|
|
|
let directories env =
|
|
Actions_helpers.words_of_variable env Ocaml_variables.directories
|
|
|
|
let directory_flags env =
|
|
let f dir = ("-I " ^ dir) in
|
|
let l = List.map f (directories env) in
|
|
String.concat " " l
|
|
|
|
let flags env = Environments.safe_lookup Ocaml_variables.flags env
|
|
|
|
let last_flags env = Environments.safe_lookup Ocaml_variables.last_flags env
|
|
|
|
let ocamllex_flags env =
|
|
Environments.safe_lookup Ocaml_variables.ocamllex_flags env
|
|
|
|
let ocamlyacc_flags env =
|
|
Environments.safe_lookup Ocaml_variables.ocamlyacc_flags env
|
|
|
|
let filelist env variable extension =
|
|
let value = Environments.safe_lookup variable env in
|
|
let filenames = String.words value in
|
|
let add_extension filename = Filename.make_filename filename extension in
|
|
String.concat " " (List.map add_extension filenames)
|
|
|
|
let libraries backend env =
|
|
let extension = Ocaml_backends.library_extension backend in
|
|
filelist env Ocaml_variables.libraries extension
|
|
|
|
let binary_modules backend env =
|
|
let extension = Ocaml_backends.module_extension backend in
|
|
filelist env Ocaml_variables.binary_modules extension
|
|
|
|
let backend_default_flags env =
|
|
get_backend_value_from_env env
|
|
Ocaml_variables.ocamlc_default_flags
|
|
Ocaml_variables.ocamlopt_default_flags
|
|
|
|
let backend_flags env =
|
|
get_backend_value_from_env env
|
|
Ocaml_variables.ocamlc_flags
|
|
Ocaml_variables.ocamlopt_flags
|
|
|
|
let env_setting env_reader default_setting =
|
|
Printf.sprintf "%s=%s"
|
|
env_reader.Clflags.env_var
|
|
(env_reader.Clflags.print default_setting)
|
|
|
|
let default_ocaml_env = [|
|
|
"TERM=dumb";
|
|
env_setting Clflags.color_reader Misc.Color.default_setting;
|
|
env_setting Clflags.error_style_reader Misc.Error_style.default_setting;
|
|
|]
|
|
|
|
type module_generator = {
|
|
description : string;
|
|
command : string;
|
|
flags : Environments.t -> string;
|
|
generated_compilation_units :
|
|
string -> (string * Ocaml_filetypes.t) list
|
|
}
|
|
|
|
let ocamllex =
|
|
{
|
|
description = "lexer";
|
|
command = Ocaml_commands.ocamlrun_ocamllex;
|
|
flags = ocamllex_flags;
|
|
generated_compilation_units =
|
|
fun lexer_name -> [(lexer_name, Ocaml_filetypes.Implementation)]
|
|
}
|
|
|
|
let ocamlyacc =
|
|
{
|
|
description = "parser";
|
|
command = Ocaml_files.ocamlyacc;
|
|
flags = ocamlyacc_flags;
|
|
generated_compilation_units =
|
|
fun parser_name ->
|
|
[
|
|
(parser_name, Ocaml_filetypes.Interface);
|
|
(parser_name, Ocaml_filetypes.Implementation)
|
|
]
|
|
}
|
|
|
|
let generate_module generator output_variable input log env =
|
|
let basename = fst input in
|
|
let input_file = Ocaml_filetypes.make_filename input in
|
|
let what =
|
|
Printf.sprintf "Generating %s module from %s"
|
|
generator.description input_file
|
|
in
|
|
Printf.fprintf log "%s\n%!" what;
|
|
let commandline =
|
|
[
|
|
generator.command;
|
|
generator.flags env;
|
|
input_file
|
|
] in
|
|
let expected_exit_status = 0 in
|
|
let exit_status =
|
|
Actions_helpers.run_cmd
|
|
~environment:default_ocaml_env
|
|
~stdin_variable: Ocaml_variables.compiler_stdin
|
|
~stdout_variable:output_variable
|
|
~stderr_variable:output_variable
|
|
~append:true
|
|
log env commandline in
|
|
if exit_status=expected_exit_status
|
|
then generator.generated_compilation_units basename
|
|
else begin
|
|
let reason =
|
|
(Actions_helpers.mkreason
|
|
what (String.concat " " commandline) exit_status) in
|
|
Printf.fprintf log "%s\n%!" reason;
|
|
[]
|
|
end
|
|
|
|
let generate_lexer = generate_module ocamllex
|
|
|
|
let generate_parser = generate_module ocamlyacc
|
|
|
|
let prepare_module output_variable log env input =
|
|
let input_type = snd input in
|
|
let open Ocaml_filetypes in
|
|
match input_type with
|
|
| Implementation | Interface | C | Obj -> [input]
|
|
| Binary_interface -> [input]
|
|
| Backend_specific _ -> [input]
|
|
| C_minus_minus -> assert false
|
|
| Lexer ->
|
|
generate_lexer output_variable input log env
|
|
| Grammar ->
|
|
generate_parser output_variable input log env
|
|
| Text -> assert false
|
|
|
|
let get_program_file backend env =
|
|
let testfile = Actions_helpers.testfile env in
|
|
let testfile_basename = Filename.chop_extension testfile in
|
|
let program_filename =
|
|
Filename.mkexe
|
|
(Filename.make_filename
|
|
testfile_basename (Ocaml_backends.executable_extension backend)) in
|
|
let test_build_directory =
|
|
Actions_helpers.test_build_directory env in
|
|
Filename.make_path [test_build_directory; program_filename]
|
|
|
|
let is_c_file (_filename, filetype) = filetype=Ocaml_filetypes.C
|
|
|
|
let cmas_need_dynamic_loading directories libraries =
|
|
let loads_c_code library =
|
|
let library = Misc.find_in_path directories library in
|
|
let ic = open_in_bin library in
|
|
try
|
|
let len_magic_number = String.length Config.cma_magic_number in
|
|
let magic_number = really_input_string ic len_magic_number in
|
|
if magic_number = Config.cma_magic_number then
|
|
let toc_pos = input_binary_int ic in
|
|
seek_in ic toc_pos;
|
|
let toc = (input_value ic : Cmo_format.library) in
|
|
close_in ic;
|
|
if toc.Cmo_format.lib_dllibs <> [] then Some (Ok ()) else None
|
|
else
|
|
raise End_of_file
|
|
with End_of_file
|
|
| Sys_error _ ->
|
|
begin try close_in ic with Sys_error _ -> () end;
|
|
Some (Error ("Corrupt or non-CMA file: " ^ library))
|
|
in
|
|
List.find_map loads_c_code (String.words libraries)
|
|
|
|
let compile_program (compiler : Ocaml_compilers.compiler) log env =
|
|
let program_variable = compiler#program_variable in
|
|
let program_file = Environments.safe_lookup program_variable env in
|
|
let all_modules =
|
|
Actions_helpers.words_of_variable env Ocaml_variables.all_modules in
|
|
let output_variable = compiler#output_variable in
|
|
let prepare = prepare_module output_variable log env in
|
|
let modules =
|
|
List.concatmap prepare (List.map Ocaml_filetypes.filetype all_modules) in
|
|
let has_c_file = List.exists is_c_file modules in
|
|
let c_headers_flags =
|
|
if has_c_file then Ocaml_flags.c_includes else "" in
|
|
let expected_exit_status =
|
|
Ocaml_tools.expected_exit_status env (compiler :> Ocaml_tools.tool) in
|
|
let module_names =
|
|
(binary_modules compiler#target env) ^ " " ^
|
|
(String.concat " " (List.map Ocaml_filetypes.make_filename modules)) in
|
|
let what = Printf.sprintf "Compiling program %s from modules %s"
|
|
program_file module_names in
|
|
Printf.fprintf log "%s\n%!" what;
|
|
let compile_only =
|
|
Environments.lookup_as_bool Ocaml_variables.compile_only env = Some true
|
|
in
|
|
let compile_flags =
|
|
if compile_only then " -c " else ""
|
|
in
|
|
let output = if compile_only then "" else "-o " ^ program_file in
|
|
let libraries = libraries compiler#target env in
|
|
let cmas_need_dynamic_loading =
|
|
if not Config.supports_shared_libraries &&
|
|
compiler#target = Ocaml_backends.Bytecode then
|
|
cmas_need_dynamic_loading (directories env) libraries
|
|
else
|
|
None
|
|
in
|
|
match cmas_need_dynamic_loading with
|
|
| Some (Error reason) ->
|
|
(Result.fail_with_reason reason, env)
|
|
| _ ->
|
|
let bytecode_links_c_code = (cmas_need_dynamic_loading = Some (Ok ())) in
|
|
let commandline =
|
|
[
|
|
compiler#name;
|
|
Ocaml_flags.runtime_flags env compiler#target
|
|
(has_c_file || bytecode_links_c_code);
|
|
c_headers_flags;
|
|
Ocaml_flags.stdlib;
|
|
directory_flags env;
|
|
flags env;
|
|
libraries;
|
|
backend_default_flags env compiler#target;
|
|
backend_flags env compiler#target;
|
|
compile_flags;
|
|
output;
|
|
(Environments.safe_lookup Ocaml_variables.ocaml_filetype_flag env);
|
|
module_names;
|
|
last_flags env
|
|
] in
|
|
let exit_status =
|
|
Actions_helpers.run_cmd
|
|
~environment:default_ocaml_env
|
|
~stdin_variable: Ocaml_variables.compiler_stdin
|
|
~stdout_variable:compiler#output_variable
|
|
~stderr_variable:compiler#output_variable
|
|
~append:true
|
|
log env commandline in
|
|
if exit_status=expected_exit_status
|
|
then (Result.pass, env)
|
|
else begin
|
|
let reason =
|
|
(Actions_helpers.mkreason
|
|
what (String.concat " " commandline) exit_status) in
|
|
(Result.fail_with_reason reason, env)
|
|
end
|
|
|
|
let compile_module compiler module_ log env =
|
|
let expected_exit_status =
|
|
Ocaml_tools.expected_exit_status env (compiler :> Ocaml_tools.tool) in
|
|
let what = Printf.sprintf "Compiling module %s" module_ in
|
|
Printf.fprintf log "%s\n%!" what;
|
|
let module_with_filetype = Ocaml_filetypes.filetype module_ in
|
|
let is_c = is_c_file module_with_filetype in
|
|
let c_headers_flags =
|
|
if is_c then Ocaml_flags.c_includes else "" in
|
|
let commandline =
|
|
[
|
|
compiler#name;
|
|
Ocaml_flags.stdlib;
|
|
c_headers_flags;
|
|
directory_flags env;
|
|
flags env;
|
|
libraries compiler#target env;
|
|
backend_default_flags env compiler#target;
|
|
backend_flags env compiler#target;
|
|
"-c " ^ module_;
|
|
] in
|
|
let exit_status =
|
|
Actions_helpers.run_cmd
|
|
~environment:default_ocaml_env
|
|
~stdin_variable: Ocaml_variables.compiler_stdin
|
|
~stdout_variable:compiler#output_variable
|
|
~stderr_variable:compiler#output_variable
|
|
~append:true
|
|
log env commandline in
|
|
if exit_status=expected_exit_status
|
|
then (Result.pass, env)
|
|
else begin
|
|
let reason =
|
|
(Actions_helpers.mkreason
|
|
what (String.concat " " commandline) exit_status) in
|
|
(Result.fail_with_reason reason, env)
|
|
end
|
|
|
|
let module_has_interface directory module_name =
|
|
let interface_name =
|
|
Ocaml_filetypes.make_filename (module_name, Ocaml_filetypes.Interface) in
|
|
let interface_fullpath = Filename.make_path [directory;interface_name] in
|
|
Sys.file_exists interface_fullpath
|
|
|
|
let add_module_interface directory module_description =
|
|
match module_description with
|
|
| (filename, Ocaml_filetypes.Implementation) when
|
|
module_has_interface directory filename ->
|
|
[(filename, Ocaml_filetypes.Interface); module_description]
|
|
| _ -> [module_description]
|
|
|
|
let print_module_names log description modules =
|
|
Printf.fprintf log "%s modules: %s\n%!"
|
|
description
|
|
(String.concat " " (List.map Ocaml_filetypes.make_filename modules))
|
|
|
|
let find_source_modules log env =
|
|
let source_directory = Actions_helpers.test_source_directory env in
|
|
let specified_modules =
|
|
List.map Ocaml_filetypes.filetype
|
|
((plugins env) @ (modules env) @ [(Actions_helpers.testfile env)]) in
|
|
print_module_names log "Specified" specified_modules;
|
|
let source_modules =
|
|
List.concatmap
|
|
(add_module_interface source_directory)
|
|
specified_modules in
|
|
print_module_names log "Source" source_modules;
|
|
Environments.add
|
|
Ocaml_variables.all_modules
|
|
(String.concat " " (List.map Ocaml_filetypes.make_filename source_modules))
|
|
env
|
|
|
|
let setup_tool_build_env tool log env =
|
|
let source_directory = Actions_helpers.test_source_directory env in
|
|
let testfile = Actions_helpers.testfile env in
|
|
let testfile_basename = Filename.chop_extension testfile in
|
|
let tool_reference_variable =
|
|
tool#reference_variable in
|
|
let tool_reference_prefix =
|
|
Filename.make_path [source_directory; testfile_basename] in
|
|
let tool_reference_file =
|
|
tool#reference_file env tool_reference_prefix
|
|
in
|
|
let env =
|
|
Environments.add_if_undefined
|
|
tool_reference_variable
|
|
tool_reference_file env
|
|
in
|
|
let source_modules =
|
|
Actions_helpers.words_of_variable env Ocaml_variables.all_modules in
|
|
let tool_directory_suffix =
|
|
Environments.safe_lookup Ocaml_variables.compiler_directory_suffix env in
|
|
let tool_directory_name =
|
|
tool#directory ^ tool_directory_suffix in
|
|
let build_dir = Filename.concat
|
|
(Environments.safe_lookup
|
|
Builtin_variables.test_build_directory_prefix env)
|
|
tool_directory_name in
|
|
let tool_output_variable = tool#output_variable in
|
|
let tool_output_filename =
|
|
Filename.make_filename tool#directory "output" in
|
|
let tool_output_file =
|
|
Filename.make_path [build_dir; tool_output_filename]
|
|
in
|
|
let env =
|
|
Environments.add_if_undefined
|
|
tool_output_variable
|
|
tool_output_file env
|
|
in
|
|
Sys.force_remove tool_output_file;
|
|
let env =
|
|
Environments.add Builtin_variables.test_build_directory build_dir env in
|
|
Actions_helpers.setup_build_env false source_modules log env
|
|
|
|
let setup_compiler_build_env (compiler : Ocaml_compilers.compiler) log env =
|
|
let (r, env) = setup_tool_build_env compiler log env in
|
|
if Result.is_pass r then
|
|
begin
|
|
let prog_var = compiler#program_variable in
|
|
let prog_output_var = compiler#program_output_variable in
|
|
let default_prog_file = get_program_file compiler#target env in
|
|
let env = Environments.add_if_undefined prog_var default_prog_file env in
|
|
let prog_file = Environments.safe_lookup prog_var env in
|
|
let prog_output_file = prog_file ^ ".output" in
|
|
let env = match prog_output_var with
|
|
| None -> env
|
|
| Some outputvar ->
|
|
Environments.add_if_undefined outputvar prog_output_file env
|
|
in
|
|
(r, env)
|
|
end else (r, env)
|
|
|
|
let setup_toplevel_build_env (toplevel : Ocaml_toplevels.toplevel) log env =
|
|
setup_tool_build_env toplevel log env
|
|
|
|
let mk_compiler_env_setup name (compiler : Ocaml_compilers.compiler) =
|
|
Actions.make name (setup_compiler_build_env compiler)
|
|
|
|
let mk_toplevel_env_setup name (toplevel : Ocaml_toplevels.toplevel) =
|
|
Actions.make name (setup_toplevel_build_env toplevel)
|
|
|
|
let setup_ocamlc_byte_build_env =
|
|
mk_compiler_env_setup
|
|
"setup-ocamlc.byte-build-env"
|
|
Ocaml_compilers.ocamlc_byte
|
|
|
|
let setup_ocamlc_opt_build_env =
|
|
native_action
|
|
(mk_compiler_env_setup
|
|
"setup-ocamlc.opt-build-env"
|
|
Ocaml_compilers.ocamlc_opt)
|
|
|
|
let setup_ocamlopt_byte_build_env =
|
|
native_action
|
|
(mk_compiler_env_setup
|
|
"setup-ocamlopt.byte-build-env"
|
|
Ocaml_compilers.ocamlopt_byte)
|
|
|
|
let setup_ocamlopt_opt_build_env =
|
|
native_action
|
|
(mk_compiler_env_setup
|
|
"setup-ocamlopt.opt-build-env"
|
|
Ocaml_compilers.ocamlopt_opt)
|
|
|
|
let setup_ocaml_build_env =
|
|
mk_toplevel_env_setup
|
|
"setup-ocaml-build-env"
|
|
Ocaml_toplevels.ocaml
|
|
|
|
let setup_ocamlnat_build_env =
|
|
native_action
|
|
(mk_toplevel_env_setup
|
|
"setup-ocamlnat-build-env"
|
|
Ocaml_toplevels.ocamlnat)
|
|
|
|
let compile (compiler : Ocaml_compilers.compiler) log env =
|
|
match Environments.lookup_nonempty Builtin_variables.commandline env with
|
|
| None ->
|
|
begin
|
|
match Environments.lookup_nonempty Ocaml_variables.module_ env with
|
|
| None -> compile_program compiler log env
|
|
| Some module_ -> compile_module compiler module_ log env
|
|
end
|
|
| Some cmdline ->
|
|
let expected_exit_status =
|
|
Ocaml_tools.expected_exit_status env (compiler :> Ocaml_tools.tool) in
|
|
let what = Printf.sprintf "Compiling using commandline %s" cmdline in
|
|
Printf.fprintf log "%s\n%!" what;
|
|
let commandline = [compiler#name; cmdline] in
|
|
let exit_status =
|
|
Actions_helpers.run_cmd
|
|
~environment:default_ocaml_env
|
|
~stdin_variable: Ocaml_variables.compiler_stdin
|
|
~stdout_variable:compiler#output_variable
|
|
~stderr_variable:compiler#output_variable
|
|
~append:true
|
|
log env commandline in
|
|
if exit_status=expected_exit_status
|
|
then (Result.pass, env)
|
|
else begin
|
|
let reason =
|
|
(Actions_helpers.mkreason
|
|
what (String.concat " " commandline) exit_status) in
|
|
(Result.fail_with_reason reason, env)
|
|
end
|
|
|
|
(* Compile actions *)
|
|
|
|
let ocamlc_byte =
|
|
Actions.make
|
|
"ocamlc.byte"
|
|
(compile Ocaml_compilers.ocamlc_byte)
|
|
|
|
let ocamlc_opt =
|
|
native_action
|
|
(Actions.make
|
|
"ocamlc.opt"
|
|
(compile Ocaml_compilers.ocamlc_opt))
|
|
|
|
let ocamlopt_byte =
|
|
native_action
|
|
(Actions.make
|
|
"ocamlopt.byte"
|
|
(compile Ocaml_compilers.ocamlopt_byte))
|
|
|
|
let ocamlopt_opt =
|
|
native_action
|
|
(Actions.make
|
|
"ocamlopt.opt"
|
|
(compile Ocaml_compilers.ocamlopt_opt))
|
|
|
|
let env_with_lib_unix env =
|
|
let libunixdir = Ocaml_directories.libunix in
|
|
let newlibs =
|
|
match Environments.lookup Ocaml_variables.caml_ld_library_path env with
|
|
| None -> libunixdir
|
|
| Some libs -> libunixdir ^ " " ^ libs
|
|
in
|
|
Environments.add Ocaml_variables.caml_ld_library_path newlibs env
|
|
|
|
let debug log env =
|
|
let program = Environments.safe_lookup Builtin_variables.program env in
|
|
let what = Printf.sprintf "Debugging program %s" program in
|
|
Printf.fprintf log "%s\n%!" what;
|
|
let commandline =
|
|
[
|
|
Ocaml_commands.ocamlrun_ocamldebug;
|
|
Ocaml_flags.ocamldebug_default_flags;
|
|
program
|
|
] in
|
|
let systemenv =
|
|
Array.append
|
|
default_ocaml_env
|
|
(Environments.to_system_env (env_with_lib_unix env))
|
|
in
|
|
let expected_exit_status = 0 in
|
|
let exit_status =
|
|
Actions_helpers.run_cmd
|
|
~environment:systemenv
|
|
~stdin_variable: Ocaml_variables.ocamldebug_script
|
|
~stdout_variable:Builtin_variables.output
|
|
~stderr_variable:Builtin_variables.output
|
|
~append:true
|
|
log (env_with_lib_unix env) commandline in
|
|
if exit_status=expected_exit_status
|
|
then (Result.pass, env)
|
|
else begin
|
|
let reason =
|
|
(Actions_helpers.mkreason
|
|
what (String.concat " " commandline) exit_status) in
|
|
(Result.fail_with_reason reason, env)
|
|
end
|
|
|
|
let ocamldebug = Actions.make "ocamldebug" debug
|
|
|
|
let objinfo log env =
|
|
let tools_directory = Ocaml_directories.tools in
|
|
let program = Environments.safe_lookup Builtin_variables.program env in
|
|
let what = Printf.sprintf "Running ocamlobjinfo on %s" program in
|
|
Printf.fprintf log "%s\n%!" what;
|
|
let commandline =
|
|
[
|
|
Ocaml_commands.ocamlrun_ocamlobjinfo;
|
|
Ocaml_flags.ocamlobjinfo_default_flags;
|
|
program
|
|
] in
|
|
let ocamllib = [| (Printf.sprintf "OCAMLLIB=%s" tools_directory) |] in
|
|
let systemenv =
|
|
Array.concat
|
|
[
|
|
default_ocaml_env;
|
|
ocamllib;
|
|
(Environments.to_system_env (env_with_lib_unix env))
|
|
]
|
|
in
|
|
let expected_exit_status = 0 in
|
|
let exit_status =
|
|
Actions_helpers.run_cmd
|
|
~environment:systemenv
|
|
~stdout_variable:Builtin_variables.output
|
|
~stderr_variable:Builtin_variables.output
|
|
~append:true
|
|
log (env_with_lib_unix env) commandline in
|
|
if exit_status=expected_exit_status
|
|
then (Result.pass, env)
|
|
else begin
|
|
let reason =
|
|
(Actions_helpers.mkreason
|
|
what (String.concat " " commandline) exit_status) in
|
|
(Result.fail_with_reason reason, env)
|
|
end
|
|
|
|
let ocamlobjinfo = Actions.make "ocamlobjinfo" objinfo
|
|
|
|
let mklib log env =
|
|
let program = Environments.safe_lookup Builtin_variables.program env in
|
|
let what = Printf.sprintf "Running ocamlmklib to produce %s" program in
|
|
Printf.fprintf log "%s\n%!" what;
|
|
let ocamlc_command =
|
|
String.concat " "
|
|
[
|
|
Ocaml_commands.ocamlrun_ocamlc;
|
|
Ocaml_flags.stdlib;
|
|
]
|
|
in
|
|
let commandline =
|
|
[
|
|
Ocaml_commands.ocamlrun_ocamlmklib;
|
|
"-ocamlc '" ^ ocamlc_command ^ "'";
|
|
"-o " ^ program
|
|
] @ modules env in
|
|
let expected_exit_status = 0 in
|
|
let exit_status =
|
|
Actions_helpers.run_cmd
|
|
~environment:default_ocaml_env
|
|
~stdout_variable:Ocaml_variables.compiler_output
|
|
~stderr_variable:Ocaml_variables.compiler_output
|
|
~append:true
|
|
log env commandline in
|
|
if exit_status=expected_exit_status
|
|
then (Result.pass, env)
|
|
else begin
|
|
let reason =
|
|
(Actions_helpers.mkreason
|
|
what (String.concat " " commandline) exit_status) in
|
|
(Result.fail_with_reason reason, env)
|
|
end
|
|
|
|
let ocamlmklib = Actions.make "ocamlmklib" mklib
|
|
|
|
let finalise_codegen_cc test_basename _log env =
|
|
let test_module =
|
|
Filename.make_filename test_basename "s"
|
|
in
|
|
let archmod = Ocaml_files.asmgen_archmod in
|
|
let modules = test_module ^ " " ^ archmod in
|
|
let program = Filename.make_filename test_basename "out" in
|
|
let env = Environments.add_bindings
|
|
[
|
|
Ocaml_variables.modules, modules;
|
|
Builtin_variables.program, program;
|
|
] env in
|
|
(Result.pass, env)
|
|
|
|
let finalise_codegen_msvc test_basename log env =
|
|
let obj = Filename.make_filename test_basename Ocamltest_config.objext in
|
|
let src = Filename.make_filename test_basename "s" in
|
|
let what = "Running Microsoft assembler" in
|
|
Printf.fprintf log "%s\n%!" what;
|
|
let commandline = [Ocamltest_config.asm; obj; src] in
|
|
let expected_exit_status = 0 in
|
|
let exit_status =
|
|
Actions_helpers.run_cmd
|
|
~environment:default_ocaml_env
|
|
~stdout_variable:Ocaml_variables.compiler_output
|
|
~stderr_variable:Ocaml_variables.compiler_output
|
|
~append:true
|
|
log env commandline in
|
|
if exit_status=expected_exit_status
|
|
then begin
|
|
let archmod = Ocaml_files.asmgen_archmod in
|
|
let modules = obj ^ " " ^ archmod in
|
|
let program = Filename.make_filename test_basename "out" in
|
|
let env = Environments.add_bindings
|
|
[
|
|
Ocaml_variables.modules, modules;
|
|
Builtin_variables.program, program;
|
|
] env in
|
|
(Result.pass, env)
|
|
end else begin
|
|
let reason =
|
|
(Actions_helpers.mkreason
|
|
what (String.concat " " commandline) exit_status) in
|
|
(Result.fail_with_reason reason, env)
|
|
end
|
|
|
|
let run_codegen log env =
|
|
let testfile = Actions_helpers.testfile env in
|
|
let testfile_basename = Filename.chop_extension testfile in
|
|
let what = Printf.sprintf "Running codegen on %s" testfile in
|
|
Printf.fprintf log "%s\n%!" what;
|
|
let test_build_directory =
|
|
Actions_helpers.test_build_directory env in
|
|
let compiler_output =
|
|
Filename.make_path [test_build_directory; "compiler-output"]
|
|
in
|
|
let env =
|
|
Environments.add_if_undefined
|
|
Ocaml_variables.compiler_output
|
|
compiler_output
|
|
env
|
|
in
|
|
let output_file = Filename.make_filename testfile_basename "output" in
|
|
let output = Filename.make_path [test_build_directory; output_file] in
|
|
let env = Environments.add Builtin_variables.output output env in
|
|
let commandline =
|
|
[
|
|
Ocaml_commands.ocamlrun_codegen;
|
|
flags env;
|
|
"-S " ^ testfile
|
|
] in
|
|
let expected_exit_status = 0 in
|
|
let exit_status =
|
|
Actions_helpers.run_cmd
|
|
~environment:default_ocaml_env
|
|
~stdout_variable:Ocaml_variables.compiler_output
|
|
~stderr_variable:Ocaml_variables.compiler_output
|
|
~append:true
|
|
log env commandline in
|
|
if exit_status=expected_exit_status
|
|
then begin
|
|
let finalise =
|
|
if Ocamltest_config.ccomptype="msvc"
|
|
then finalise_codegen_msvc
|
|
else finalise_codegen_cc
|
|
in
|
|
finalise testfile_basename log env
|
|
end else begin
|
|
let reason =
|
|
(Actions_helpers.mkreason
|
|
what (String.concat " " commandline) exit_status) in
|
|
(Result.fail_with_reason reason, env)
|
|
end
|
|
|
|
let codegen = Actions.make "codegen" run_codegen
|
|
|
|
let run_cc log env =
|
|
let program = Environments.safe_lookup Builtin_variables.program env in
|
|
let what = Printf.sprintf "Running C compiler to build %s" program in
|
|
Printf.fprintf log "%s\n%!" what;
|
|
let output_exe =
|
|
if Ocamltest_config.ccomptype="msvc" then "/Fe" else "-o "
|
|
in
|
|
let commandline =
|
|
[
|
|
Ocamltest_config.cc;
|
|
Ocamltest_config.cflags;
|
|
"-I" ^ Ocaml_directories.runtime;
|
|
output_exe ^ program;
|
|
Environments.safe_lookup Builtin_variables.arguments env;
|
|
] @ modules env in
|
|
let expected_exit_status = 0 in
|
|
let exit_status =
|
|
Actions_helpers.run_cmd
|
|
~environment:default_ocaml_env
|
|
~stdout_variable:Ocaml_variables.compiler_output
|
|
~stderr_variable:Ocaml_variables.compiler_output
|
|
~append:true
|
|
log env commandline in
|
|
if exit_status=expected_exit_status
|
|
then (Result.pass, env)
|
|
else begin
|
|
let reason =
|
|
(Actions_helpers.mkreason
|
|
what (String.concat " " commandline) exit_status) in
|
|
(Result.fail_with_reason reason, env)
|
|
end
|
|
|
|
let cc = Actions.make "cc" run_cc
|
|
|
|
let run_expect_once input_file principal log env =
|
|
let expect_flags = Sys.safe_getenv "EXPECT_FLAGS" in
|
|
let repo_root = "-repo-root " ^ Ocaml_directories.srcdir in
|
|
let principal_flag = if principal then "-principal" else "" in
|
|
let commandline =
|
|
[
|
|
Ocaml_commands.ocamlrun_expect_test;
|
|
expect_flags;
|
|
flags env;
|
|
repo_root;
|
|
principal_flag;
|
|
input_file
|
|
] in
|
|
let exit_status =
|
|
Actions_helpers.run_cmd ~environment:default_ocaml_env log env commandline
|
|
in
|
|
if exit_status=0 then (Result.pass, env)
|
|
else begin
|
|
let reason = (Actions_helpers.mkreason
|
|
"expect" (String.concat " " commandline) exit_status) in
|
|
(Result.fail_with_reason reason, env)
|
|
end
|
|
|
|
let run_expect_twice input_file log env =
|
|
let corrected filename = Filename.make_filename filename "corrected" in
|
|
let (result1, env1) = run_expect_once input_file false log env in
|
|
if Result.is_pass result1 then begin
|
|
let intermediate_file = corrected input_file in
|
|
let (result2, env2) =
|
|
run_expect_once intermediate_file true log env1 in
|
|
if Result.is_pass result2 then begin
|
|
let output_file = corrected intermediate_file in
|
|
let output_env = Environments.add_bindings
|
|
[
|
|
Builtin_variables.reference, input_file;
|
|
Builtin_variables.output, output_file
|
|
] env2 in
|
|
(Result.pass, output_env)
|
|
end else (result2, env2)
|
|
end else (result1, env1)
|
|
|
|
let run_expect log env =
|
|
let input_file = Actions_helpers.testfile env in
|
|
run_expect_twice input_file log env
|
|
|
|
let run_expect = Actions.make "run-expect" run_expect
|
|
|
|
let make_check_tool_output name tool = Actions.make
|
|
name
|
|
(Actions_helpers.check_output
|
|
tool#family
|
|
tool#output_variable
|
|
tool#reference_variable)
|
|
|
|
let check_ocamlc_byte_output = make_check_tool_output
|
|
"check-ocamlc.byte-output" Ocaml_compilers.ocamlc_byte
|
|
|
|
let check_ocamlc_opt_output =
|
|
native_action
|
|
(make_check_tool_output
|
|
"check-ocamlc.opt-output" Ocaml_compilers.ocamlc_opt)
|
|
|
|
let check_ocamlopt_byte_output =
|
|
native_action
|
|
(make_check_tool_output
|
|
"check-ocamlopt.byte-output" Ocaml_compilers.ocamlopt_byte)
|
|
|
|
let check_ocamlopt_opt_output =
|
|
native_action
|
|
(make_check_tool_output
|
|
"check-ocamlopt.opt-output" Ocaml_compilers.ocamlopt_opt)
|
|
|
|
let really_compare_programs backend comparison_tool log env =
|
|
let program = Environments.safe_lookup Builtin_variables.program env in
|
|
let program2 = Environments.safe_lookup Builtin_variables.program2 env in
|
|
let what = Printf.sprintf "Comparing %s programs %s and %s"
|
|
(Ocaml_backends.string_of_backend backend) program program2 in
|
|
Printf.fprintf log "%s\n%!" what;
|
|
let files = {
|
|
Filecompare.filetype = Filecompare.Binary;
|
|
Filecompare.reference_filename = program;
|
|
Filecompare.output_filename = program2
|
|
} in
|
|
match Filecompare.compare_files ~tool:comparison_tool files with
|
|
| Filecompare.Same -> (Result.pass, env)
|
|
| Filecompare.Different ->
|
|
let reason = Printf.sprintf "Files %s and %s are different"
|
|
program program2 in
|
|
(Result.fail_with_reason reason, env)
|
|
| Filecompare.Unexpected_output -> assert false
|
|
| Filecompare.Error (commandline, exitcode) ->
|
|
let reason = Actions_helpers.mkreason what commandline exitcode in
|
|
(Result.fail_with_reason reason, env)
|
|
|
|
let compare_programs backend comparison_tool log env =
|
|
let compare_programs =
|
|
Environments.lookup_as_bool Ocaml_variables.compare_programs env in
|
|
if compare_programs = Some false then begin
|
|
let reason = "program comparison disabled" in
|
|
(Result.pass_with_reason reason, env)
|
|
end else really_compare_programs backend comparison_tool log env
|
|
|
|
let make_bytecode_programs_comparison_tool =
|
|
let ocamlrun = Ocaml_files.ocamlrun in
|
|
let cmpbyt = Ocaml_files.cmpbyt in
|
|
let tool_name = ocamlrun ^ " " ^ cmpbyt in
|
|
Filecompare.make_comparison_tool tool_name ""
|
|
|
|
let native_programs_comparison_tool = Filecompare.default_comparison_tool
|
|
|
|
let compare_bytecode_programs_code log env =
|
|
let bytecode_programs_comparison_tool =
|
|
make_bytecode_programs_comparison_tool in
|
|
compare_programs
|
|
Ocaml_backends.Bytecode bytecode_programs_comparison_tool log env
|
|
|
|
let compare_bytecode_programs =
|
|
native_action
|
|
(Actions.make
|
|
"compare-bytecode-programs"
|
|
compare_bytecode_programs_code)
|
|
|
|
let compare_binary_files =
|
|
native_action
|
|
(Actions.make
|
|
"compare-binary-files"
|
|
(compare_programs Ocaml_backends.Native native_programs_comparison_tool))
|
|
|
|
let compile_module compiler compilername compileroutput log env
|
|
(module_basename, module_filetype) =
|
|
let backend = compiler#target in
|
|
let filename =
|
|
Ocaml_filetypes.make_filename (module_basename, module_filetype) in
|
|
let expected_exit_status =
|
|
Ocaml_tools.expected_exit_status env (compiler :> Ocaml_tools.tool) in
|
|
let what = Printf.sprintf "%s for file %s (expected exit status: %d)"
|
|
(Ocaml_filetypes.action_of_filetype module_filetype) filename
|
|
(expected_exit_status) in
|
|
let compile_commandline input_file output_file optional_flags =
|
|
let compile = "-c " ^ input_file in
|
|
let output = match output_file with
|
|
| None -> ""
|
|
| Some file -> "-o " ^ file in
|
|
[
|
|
compilername;
|
|
Ocaml_flags.stdlib;
|
|
flags env;
|
|
backend_flags env backend;
|
|
optional_flags;
|
|
compile;
|
|
output;
|
|
] in
|
|
let exec commandline =
|
|
Printf.fprintf log "%s\n%!" what;
|
|
let exit_status =
|
|
Actions_helpers.run_cmd
|
|
~stdin_variable: Ocaml_variables.compiler_stdin
|
|
~stdout_variable:compileroutput
|
|
~stderr_variable:compileroutput
|
|
~append:true log env commandline in
|
|
if exit_status=expected_exit_status
|
|
then (Result.pass, env)
|
|
else begin
|
|
let reason =
|
|
(Actions_helpers.mkreason
|
|
what (String.concat " " commandline) exit_status) in
|
|
(Result.fail_with_reason reason, env)
|
|
end in
|
|
match module_filetype with
|
|
| Ocaml_filetypes.Interface ->
|
|
let interface_name =
|
|
Ocaml_filetypes.make_filename
|
|
(module_basename, Ocaml_filetypes.Interface) in
|
|
let commandline = compile_commandline interface_name None "" in
|
|
exec commandline
|
|
| Ocaml_filetypes.Implementation ->
|
|
let module_extension = Ocaml_backends.module_extension backend in
|
|
let module_output_name =
|
|
Filename.make_filename module_basename module_extension in
|
|
let commandline =
|
|
compile_commandline filename (Some module_output_name) "" in
|
|
exec commandline
|
|
| Ocaml_filetypes.C ->
|
|
let object_extension = Config.ext_obj in
|
|
let _object_filename = module_basename ^ object_extension in
|
|
let commandline =
|
|
compile_commandline filename None
|
|
Ocaml_flags.c_includes in
|
|
exec commandline
|
|
| _ ->
|
|
let reason = Printf.sprintf "File %s of type %s not supported yet"
|
|
filename (Ocaml_filetypes.string_of_filetype module_filetype) in
|
|
(Result.fail_with_reason reason, env)
|
|
|
|
let compile_modules compiler compilername compileroutput
|
|
modules_with_filetypes log initial_env
|
|
=
|
|
let compile_mod env mod_ =
|
|
compile_module compiler compilername compileroutput
|
|
log env mod_ in
|
|
let rec compile_mods env = function
|
|
| [] -> (Result.pass, env)
|
|
| m::ms ->
|
|
(let (result, newenv) = compile_mod env m in
|
|
if Result.is_pass result then (compile_mods newenv ms)
|
|
else (result, newenv)) in
|
|
compile_mods initial_env modules_with_filetypes
|
|
|
|
let run_test_program_in_toplevel (toplevel : Ocaml_toplevels.toplevel) log env =
|
|
let backend = toplevel#backend in
|
|
let libraries = libraries backend env in
|
|
(* This is a sub-optimal check - skip the test if any libraries requiring
|
|
C stubs are loaded. It would be better at this point to build a custom
|
|
toplevel. *)
|
|
let toplevel_supports_dynamic_loading =
|
|
Config.supports_shared_libraries || backend <> Ocaml_backends.Bytecode
|
|
in
|
|
match cmas_need_dynamic_loading (directories env) libraries with
|
|
| Some (Error reason) ->
|
|
(Result.fail_with_reason reason, env)
|
|
| Some (Ok ()) when not toplevel_supports_dynamic_loading ->
|
|
(Result.skip, env)
|
|
| _ ->
|
|
let testfile = Actions_helpers.testfile env in
|
|
let expected_exit_status =
|
|
Ocaml_tools.expected_exit_status env (toplevel :> Ocaml_tools.tool) in
|
|
let compiler_output_variable = toplevel#output_variable in
|
|
let compiler = toplevel#compiler in
|
|
let compiler_name = compiler#name in
|
|
let modules_with_filetypes =
|
|
List.map Ocaml_filetypes.filetype (modules env) in
|
|
let (result, env) = compile_modules
|
|
compiler compiler_name compiler_output_variable
|
|
modules_with_filetypes log env in
|
|
if Result.is_pass result then begin
|
|
let what =
|
|
Printf.sprintf "Running %s in %s toplevel \
|
|
(expected exit status: %d)"
|
|
testfile
|
|
(Ocaml_backends.string_of_backend backend)
|
|
expected_exit_status in
|
|
Printf.fprintf log "%s\n%!" what;
|
|
let toplevel_name = toplevel#name in
|
|
let ocaml_script_as_argument =
|
|
match
|
|
Environments.lookup_as_bool
|
|
Ocaml_variables.ocaml_script_as_argument env
|
|
with
|
|
| None -> false
|
|
| Some b -> b
|
|
in
|
|
let commandline =
|
|
[
|
|
toplevel_name;
|
|
Ocaml_flags.toplevel_default_flags;
|
|
toplevel#flags;
|
|
Ocaml_flags.stdlib;
|
|
directory_flags env;
|
|
Ocaml_flags.include_toplevel_directory;
|
|
flags env;
|
|
libraries;
|
|
binary_modules backend env;
|
|
if ocaml_script_as_argument then testfile else "";
|
|
Environments.safe_lookup Builtin_variables.arguments env
|
|
] in
|
|
let exit_status =
|
|
if ocaml_script_as_argument
|
|
then Actions_helpers.run_cmd
|
|
~environment:default_ocaml_env
|
|
~stdout_variable:compiler_output_variable
|
|
~stderr_variable:compiler_output_variable
|
|
log env commandline
|
|
else Actions_helpers.run_cmd
|
|
~environment:default_ocaml_env
|
|
~stdin_variable:Builtin_variables.test_file
|
|
~stdout_variable:compiler_output_variable
|
|
~stderr_variable:compiler_output_variable
|
|
log env commandline
|
|
in
|
|
if exit_status=expected_exit_status
|
|
then (Result.pass, env)
|
|
else begin
|
|
let reason =
|
|
(Actions_helpers.mkreason
|
|
what (String.concat " " commandline) exit_status) in
|
|
(Result.fail_with_reason reason, env)
|
|
end
|
|
end else (result, env)
|
|
|
|
let ocaml = Actions.make
|
|
"ocaml"
|
|
(run_test_program_in_toplevel Ocaml_toplevels.ocaml)
|
|
|
|
let ocamlnat =
|
|
native_action
|
|
(Actions.make
|
|
"ocamlnat"
|
|
(run_test_program_in_toplevel Ocaml_toplevels.ocamlnat))
|
|
|
|
let check_ocaml_output = make_check_tool_output
|
|
"check-ocaml-output" Ocaml_toplevels.ocaml
|
|
|
|
let check_ocamlnat_output =
|
|
native_action
|
|
(make_check_tool_output
|
|
"check-ocamlnat-output" Ocaml_toplevels.ocamlnat)
|
|
|
|
let config_variables _log env =
|
|
Environments.add_bindings
|
|
[
|
|
Ocaml_variables.arch, Ocamltest_config.arch;
|
|
Ocaml_variables.ocamlrun, Ocaml_files.ocamlrun;
|
|
Ocaml_variables.ocamlc_byte, Ocaml_files.ocamlc;
|
|
Ocaml_variables.ocamlopt_byte, Ocaml_files.ocamlopt;
|
|
Ocaml_variables.bytecc_libs, Ocamltest_config.bytecc_libs;
|
|
Ocaml_variables.nativecc_libs, Ocamltest_config.nativecc_libs;
|
|
Ocaml_variables.mkdll,
|
|
Sys.getenv_with_default_value "MKDLL" Ocamltest_config.mkdll;
|
|
Ocaml_variables.mkexe, Ocamltest_config.mkexe;
|
|
Ocaml_variables.c_preprocessor, Ocamltest_config.c_preprocessor;
|
|
Ocaml_variables.csc, Ocamltest_config.csc;
|
|
Ocaml_variables.csc_flags, Ocamltest_config.csc_flags;
|
|
Ocaml_variables.shared_library_cflags,
|
|
Ocamltest_config.shared_library_cflags;
|
|
Ocaml_variables.objext, Ocamltest_config.objext;
|
|
Ocaml_variables.asmext, Ocamltest_config.asmext;
|
|
Ocaml_variables.sharedobjext, Ocamltest_config.sharedobjext;
|
|
Ocaml_variables.ocamlc_default_flags,
|
|
Ocamltest_config.ocamlc_default_flags;
|
|
Ocaml_variables.ocamlopt_default_flags,
|
|
Ocamltest_config.ocamlopt_default_flags;
|
|
Ocaml_variables.ocamlrunparam, Sys.safe_getenv "OCAMLRUNPARAM";
|
|
Ocaml_variables.ocamlsrcdir, Ocaml_directories.srcdir;
|
|
Ocaml_variables.os_type, Sys.os_type;
|
|
] env
|
|
|
|
let flat_float_array = Actions.make
|
|
"flat-float-array"
|
|
(Actions_helpers.pass_or_skip Ocamltest_config.flat_float_array
|
|
"compiler configured with -flat-float-array"
|
|
"compiler configured with -no-flat-float-array")
|
|
|
|
let no_flat_float_array = make
|
|
"no-flat-float-array"
|
|
(Actions_helpers.pass_or_skip (not Ocamltest_config.flat_float_array)
|
|
"compiler configured with -no-flat-float-array"
|
|
"compiler configured with -flat-float-array")
|
|
|
|
let flambda = Actions.make
|
|
"flambda"
|
|
(Actions_helpers.pass_or_skip Ocamltest_config.flambda
|
|
"support for flambda enabled"
|
|
"support for flambda disabled")
|
|
|
|
let no_flambda = make
|
|
"no-flambda"
|
|
(Actions_helpers.pass_or_skip (not Ocamltest_config.flambda)
|
|
"support for flambda disabled"
|
|
"support for flambda enabled")
|
|
|
|
let shared_libraries = Actions.make
|
|
"shared-libraries"
|
|
(Actions_helpers.pass_or_skip Ocamltest_config.shared_libraries
|
|
"Shared libraries are supported."
|
|
"Shared libraries are not supported.")
|
|
|
|
let no_shared_libraries = Actions.make
|
|
"no-shared-libraries"
|
|
(Actions_helpers.pass_or_skip (not Ocamltest_config.shared_libraries)
|
|
"Shared libraries are not supported."
|
|
"Shared libraries are supported.")
|
|
|
|
let native_compiler = Actions.make
|
|
"native-compiler"
|
|
(Actions_helpers.pass_or_skip (Ocamltest_config.arch <> "none")
|
|
"native compiler available"
|
|
"native compiler not available")
|
|
|
|
let native_dynlink = Actions.make
|
|
"native-dynlink"
|
|
(Actions_helpers.pass_or_skip (Ocamltest_config.native_dynlink)
|
|
"native dynlink support available"
|
|
"native dynlink support not available")
|
|
|
|
let debugger = Actions.make
|
|
"debugger"
|
|
(Actions_helpers.pass_or_skip Ocamltest_config.ocamldebug
|
|
"debugger available"
|
|
"debugger not available")
|
|
|
|
let csharp_compiler = Actions.make
|
|
"csharp-compiler"
|
|
(Actions_helpers.pass_or_skip (Ocamltest_config.csc<>"")
|
|
"C# compiler available"
|
|
"C# compiler not available")
|
|
|
|
let windows_unicode = Actions.make
|
|
"windows-unicode"
|
|
(Actions_helpers.pass_or_skip (Ocamltest_config.windows_unicode )
|
|
"Windows Unicode support available"
|
|
"Windows Unicode support not available")
|
|
|
|
let afl_instrument = Actions.make
|
|
"afl-instrument"
|
|
(Actions_helpers.pass_or_skip Ocamltest_config.afl_instrument
|
|
"AFL instrumentation enabled"
|
|
"AFL instrumentation disabled")
|
|
|
|
let no_afl_instrument = Actions.make
|
|
"no-afl-instrument"
|
|
(Actions_helpers.pass_or_skip (not Ocamltest_config.afl_instrument)
|
|
"AFL instrumentation disabled"
|
|
"AFL instrumentation enabled")
|
|
|
|
let ocamldoc = Ocaml_tools.ocamldoc
|
|
|
|
let ocamldoc_output_file env prefix =
|
|
let backend =
|
|
Environments.safe_lookup Ocaml_variables.ocamldoc_backend env in
|
|
let suffix = match backend with
|
|
| "latex" -> ".tex"
|
|
| "html" -> ".html"
|
|
| "man" -> ".3o"
|
|
| _ -> ".result" in
|
|
prefix ^ suffix
|
|
|
|
let check_ocamldoc_output = make_check_tool_output
|
|
"check-ocamldoc-output" ocamldoc
|
|
|
|
let ocamldoc_flags env =
|
|
Environments.safe_lookup Ocaml_variables.ocamldoc_flags env
|
|
|
|
let compiled_doc_name input = input ^ ".odoc"
|
|
|
|
(* The compiler used for compiling both cmi file
|
|
and plugins *)
|
|
let compiler_for_ocamldoc =
|
|
let compiler = Ocaml_compilers.ocamlc_byte in
|
|
compile_modules compiler compiler#name
|
|
compiler#output_variable
|
|
|
|
(* Within ocamldoc tests,
|
|
modules="a.ml b.ml" is interpreted as a list of
|
|
secondaries documentation modules that need to be
|
|
compiled into cmi files and odoc file (serialized ocamldoc information)
|
|
before the main documentation is generated *)
|
|
let compile_ocamldoc (basename,filetype as module_) log env =
|
|
let expected_exit_status =
|
|
Ocaml_tools.expected_exit_status env (ocamldoc :> Ocaml_tools.tool) in
|
|
let what = Printf.sprintf "Compiling documentation for module %s" basename in
|
|
Printf.fprintf log "%s\n%!" what;
|
|
let filename =
|
|
Ocaml_filetypes.make_filename (basename, filetype) in
|
|
let (r,env) = compiler_for_ocamldoc [module_] log env in
|
|
if not (Result.is_pass r) then (r,env) else
|
|
let commandline =
|
|
(* currently, we are ignoring the global ocamldoc_flags, since we
|
|
don't have per-module flags *)
|
|
[
|
|
Ocaml_commands.ocamlrun_ocamldoc;
|
|
Ocaml_flags.stdlib;
|
|
"-dump " ^ compiled_doc_name basename;
|
|
filename;
|
|
] in
|
|
let exit_status =
|
|
Actions_helpers.run_cmd
|
|
~environment:(Environments.to_system_env env)
|
|
~stdin_variable: Ocaml_variables.compiler_stdin
|
|
~stdout_variable:ocamldoc#output_variable
|
|
~stderr_variable:ocamldoc#output_variable
|
|
~append:true
|
|
log env commandline in
|
|
if exit_status=expected_exit_status
|
|
then (Result.pass, env)
|
|
else begin
|
|
let reason =
|
|
(Actions_helpers.mkreason
|
|
what (String.concat " " commandline) exit_status) in
|
|
(Result.fail_with_reason reason, env)
|
|
end
|
|
|
|
let rec ocamldoc_compile_all log env = function
|
|
| [] -> (Result.pass, env)
|
|
| a :: q ->
|
|
let (r,env) = compile_ocamldoc a log env in
|
|
if Result.is_pass r then
|
|
ocamldoc_compile_all log env q
|
|
else
|
|
(r,env)
|
|
|
|
let setup_ocamldoc_build_env =
|
|
Actions.make "setup_ocamldoc_build_env" @@ fun log env ->
|
|
let (r,env) = setup_tool_build_env ocamldoc log env in
|
|
if not (Result.is_pass r) then (r,env) else
|
|
let source_directory = Actions_helpers.test_source_directory env in
|
|
let root_file = Filename.chop_extension (Actions_helpers.testfile env) in
|
|
let reference_prefix = Filename.make_path [source_directory; root_file] in
|
|
let output = ocamldoc_output_file env root_file in
|
|
let reference= reference_prefix ^ ocamldoc#reference_filename_suffix env in
|
|
let backend = Environments.safe_lookup Ocaml_variables.ocamldoc_backend env in
|
|
let env =
|
|
Environments.apply_modifiers env Ocaml_modifiers.(str @ unix)
|
|
|> Environments.add Builtin_variables.reference reference
|
|
|> Environments.add Builtin_variables.output output in
|
|
let env =
|
|
if backend = "man" then Environments.add_if_undefined
|
|
Builtin_variables.skip_header_lines "1" env
|
|
else env in
|
|
Result.pass, env
|
|
|
|
let ocamldoc_plugin name = name ^ ".cmo"
|
|
|
|
let ocamldoc_backend_flag env =
|
|
let backend = Environments.safe_lookup Ocaml_variables.ocamldoc_backend env in
|
|
if backend = "" then "" else "-" ^ backend
|
|
|
|
let ocamldoc_o_flag env =
|
|
let output = Environments.safe_lookup Builtin_variables.output env in
|
|
match Environments.safe_lookup Ocaml_variables.ocamldoc_backend env with
|
|
| "html" | "manual" -> "index"
|
|
| _ -> output
|
|
|
|
let run_ocamldoc =
|
|
Actions.make "ocamldoc" @@ fun log env ->
|
|
(* modules corresponds to secondaries modules of which the
|
|
documentation and cmi files need to be build before the main
|
|
module documentation *)
|
|
let modules = List.map Ocaml_filetypes.filetype @@ modules env in
|
|
(* plugins are used for custom documentation generators *)
|
|
let plugins = List.map Ocaml_filetypes.filetype @@ plugins env in
|
|
let (r,env) = compiler_for_ocamldoc plugins log env in
|
|
if not (Result.is_pass r) then r, env else
|
|
let (r,env) = ocamldoc_compile_all log env modules in
|
|
if not (Result.is_pass r) then r, env else
|
|
let input_file = Actions_helpers.testfile env in
|
|
Printf.fprintf log "Generating documentation for %s\n%!" input_file;
|
|
let load_all =
|
|
List.map (fun name -> "-load " ^ compiled_doc_name (fst name))
|
|
@@ (* sort module in alphabetical order *)
|
|
List.sort Stdlib.compare modules in
|
|
let with_plugins =
|
|
List.map (fun name -> "-g " ^ ocamldoc_plugin (fst name)) plugins in
|
|
let commandline =
|
|
[
|
|
Ocaml_commands.ocamlrun_ocamldoc;
|
|
ocamldoc_backend_flag env;
|
|
Ocaml_flags.stdlib;
|
|
ocamldoc_flags env]
|
|
@ load_all @ with_plugins @
|
|
[ input_file;
|
|
"-o"; ocamldoc_o_flag env
|
|
] in
|
|
let exit_status =
|
|
Actions_helpers.run_cmd ~environment:(Environments.to_system_env env)
|
|
~stdin_variable: Ocaml_variables.compiler_stdin
|
|
~stdout_variable:ocamldoc#output_variable
|
|
~stderr_variable:ocamldoc#output_variable
|
|
~append:true
|
|
log env commandline in
|
|
if exit_status=0 then
|
|
(Result.pass, env)
|
|
else begin
|
|
let reason = (Actions_helpers.mkreason
|
|
"ocamldoc" (String.concat " " commandline) exit_status) in
|
|
(Result.fail_with_reason reason, env)
|
|
end
|
|
|
|
let _ =
|
|
Environments.register_initializer Environments.Post
|
|
"find_source_modules" find_source_modules;
|
|
Environments.register_initializer Environments.Pre
|
|
"config_variables" config_variables;
|
|
List.iter register
|
|
[
|
|
setup_ocamlc_byte_build_env;
|
|
ocamlc_byte;
|
|
check_ocamlc_byte_output;
|
|
setup_ocamlc_opt_build_env;
|
|
ocamlc_opt;
|
|
check_ocamlc_opt_output;
|
|
setup_ocamlopt_byte_build_env;
|
|
ocamlopt_byte;
|
|
check_ocamlopt_byte_output;
|
|
setup_ocamlopt_opt_build_env;
|
|
ocamlopt_opt;
|
|
check_ocamlopt_opt_output;
|
|
run_expect;
|
|
compare_bytecode_programs;
|
|
compare_binary_files;
|
|
setup_ocaml_build_env;
|
|
ocaml;
|
|
check_ocaml_output;
|
|
setup_ocamlnat_build_env;
|
|
ocamlnat;
|
|
check_ocamlnat_output;
|
|
flat_float_array;
|
|
no_flat_float_array;
|
|
flambda;
|
|
no_flambda;
|
|
shared_libraries;
|
|
no_shared_libraries;
|
|
native_compiler;
|
|
native_dynlink;
|
|
debugger;
|
|
csharp_compiler;
|
|
windows_unicode;
|
|
afl_instrument;
|
|
no_afl_instrument;
|
|
setup_ocamldoc_build_env;
|
|
run_ocamldoc;
|
|
check_ocamldoc_output;
|
|
ocamldebug;
|
|
ocamlmklib;
|
|
codegen;
|
|
cc;
|
|
ocamlobjinfo
|
|
]
|