ocaml/ocamltest/ocaml_actions.ml

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
]