let link_units table extensions cmX_ext cma_ext a_ext linker tagger contents_list cmX env build =
  let cmX = env cmX in
  let tags = tagger (tags_of_pathname cmX) in
  let _ = Rule.build_deps_of_tags build tags in
  let dir =
    let dir1 = Pathname.remove_extensions cmX in
    if Resource.exists_in_source_dir dir1 then dir1
    else Pathname.dirname cmX in
  let include_dirs = Pathname.include_dirs_of dir in
  let extension_keys = List.map fst extensions in
  let libs = prepare_libs cma_ext a_ext cmX build in
  let results =
    build begin
      List.map begin fun module_name ->
        expand_module include_dirs module_name extension_keys
      end contents_list
    end in
  let module_paths =
    List.map begin function
      | Good p ->
          let extension_values = List.assoc (Pathname.get_extensions p) extensions in
          List.iter begin fun ext ->
            List.iter ignore_good (build [[Pathname.update_extensions ext p]])
          end extension_values; p
      | Bad exn -> raise exn
    end results in
  Hashtbl.replace table cmX module_paths;
  let hidden_packages = List.map (fun x -> x-.-cmX_ext) !hidden_packages in
  let deps =
    caml_transitive_closure
      ~caml_obj_ext:cmX_ext ~caml_lib_ext:cma_ext
      ~hidden_packages ~pack_mode:true module_paths in
  let full_contents = libs @ module_paths in
  let deps = List.filter (fun x -> List.mem x full_contents) deps in
  let deps = (List.filter (fun l -> not (List.mem l deps)) libs) @ deps in

  (* Hack to avoid linking twice with the standard library. *)
  let stdlib = "stdlib/stdlib"-.-cma_ext in
  let is_not_stdlib x = x <> stdlib in
  let deps = List.filter is_not_stdlib deps in

  linker tags deps cmX