[m-rev.] for review: io.make_temp now reterns a result rather than throw exceptions

Paul Bone paul at bone.id.au
Wed Apr 20 12:44:38 AEST 2016


For review by Peter Wang who suggested this change.

---

io.make_temp now reterns a result rather than throw exceptions

The make_temp predicates would throw an exception if they were unsuccessful,
New versions of make_temp/3 and make_temp/5 have been created (named
make_temp_res) to avoid braking code.  make_temp/3 and make_temp/5 have been
marked as obsolete.  make_temp_directory/3 and make_temp_directory/5 have
been modified directly as they are very new.  This closes bug #408

Some routines in compiler/compile_target_code.m used the name returned by
make_temp and added a suffix before using the resulting file, this sould
create unnecessary temporary files that I beleive were never cleaned up.  So
I've added a suffix argument to make_temp_res/5 and make_temp_directory/5
(now they're make_temp_res/6 and make_temp_directory/6).

library/io.m:
    As above.

    Remove out-of-date comment.

compiler/compile_target_code.m:
    Conform to changes in io.m.

    Use the new suffix argument to reduce the number of temporary files
    created during linking.

compiler/fact_table.m:
compiler/make.module_target.m:
compiler/make.util.m:
compiler/module_cmds.m:
compiler/prog_event.m:
compiler/write_deps_file.m:
deep_profiler/conf.m:
    Conform to changes in io.m.
---
 compiler/compile_target_code.m |  200 +++++---
 compiler/fact_table.m          |   57 ++-
 compiler/make.module_target.m  |   96 ++--
 compiler/make.util.m           |   28 +-
 compiler/module_cmds.m         |  172 ++++---
 compiler/prog_event.m          |   82 +--
 compiler/write_deps_file.m     | 1105 ++++++++++++++++++++--------------------
 deep_profiler/conf.m           |   73 +--
 library/io.m                   |  190 ++++---
 9 files changed, 1092 insertions(+), 911 deletions(-)

diff --git a/compiler/compile_target_code.m b/compiler/compile_target_code.m
index 48453b4..ce11561 100644
--- a/compiler/compile_target_code.m
+++ b/compiler/compile_target_code.m
@@ -1254,27 +1254,37 @@ invoke_mkinit(Globals, InitFileStream, Verbosity,
     % mkinit expects unquoted file names.
     join_string_list(FileNames, "", "\n", "", TargetFileNames),
 
-    io.make_temp(TmpFile, !IO),
-    io.open_output(TmpFile, OpenResult, !IO),
+    io.make_temp_res(TmpFileResult, !IO),
     (
-        OpenResult = ok(TmpStream),
-        io.write_string(TmpStream, TargetFileNames, !IO),
-        io.close_output(TmpStream, !IO),
+        TmpFileResult = ok(TmpFile),
+        io.open_output(TmpFile, OpenResult, !IO),
+        (
+            OpenResult = ok(TmpStream),
+            io.write_string(TmpStream, TargetFileNames, !IO),
+            io.close_output(TmpStream, !IO),
 
-        MkInitCmd = string.append_list([MkInit, " ", Args, " -f ", TmpFile]),
-        invoke_system_command(Globals, InitFileStream, Verbosity,
-            MkInitCmd, MkInitOK0, !IO),
+            MkInitCmd = string.append_list([MkInit, " ", Args, " -f ", TmpFile]),
+            invoke_system_command(Globals, InitFileStream, Verbosity,
+                MkInitCmd, MkInitOK0, !IO),
 
-        io.remove_file(TmpFile, RemoveResult, !IO),
-        (
-            RemoveResult = ok,
-            MkInitOK = MkInitOK0
+            io.remove_file(TmpFile, RemoveResult, !IO),
+            (
+                RemoveResult = ok,
+                MkInitOK = MkInitOK0
+            ;
+                RemoveResult = error(_),
+                MkInitOK = no
+            )
         ;
-            RemoveResult = error(_),
+            OpenResult = error(Error),
+            io.format(io.stderr_stream, "%s: %s\n",
+                [s(TmpFile), s(error_message(Error))], !IO),
             MkInitOK = no
         )
     ;
-        OpenResult = error(_),
+        TmpFileResult = error(Error),
+        io.format(io.stderr_stream, "Could not create temporary file: %s\n",
+            [s(error_message(Error))], !IO),
         MkInitOK = no
     ).
 
@@ -1977,18 +1987,31 @@ link_exe_or_shared_lib(Globals, ErrorStream, LinkTargetType, ModuleName,
             % and thus overflow the command line, so in this case
             % we first create an archive of all of the object files.
             RestrictedCommandLine = yes,
-            io.make_temp(TmpFile, !IO),
             globals.lookup_string_option(Globals, library_extension, LibExt),
-            TmpArchive = TmpFile ++ LibExt,
-            % Only include actual object files in the temporary archive,
-            % not other files such as other archives.
-            filter_object_files(Globals, ObjectsList,
-                ProperObjectFiles, NonObjectFiles),
-            create_archive(Globals, ErrorStream, TmpArchive, yes,
-                ProperObjectFiles, ArchiveSucceeded, !IO),
-            MaybeDeleteTmpArchive = yes(TmpArchive),
-            join_quoted_string_list([TmpArchive | NonObjectFiles],
-                "", "", " ", Objects)
+            io.get_temp_directory(TempDir, !IO),
+            io.make_temp_res(TempDir, "", "." ++ LibExt, TmpFileResult, !IO),
+            (
+                TmpFileResult = ok(TmpFile),
+                % XXX: This is why my /tmp directory is always full of crap.
+                TmpArchive = TmpFile ++ LibExt,
+                % Only include actual object files in the temporary archive,
+                % not other files such as other archives.
+                filter_object_files(Globals, ObjectsList,
+                    ProperObjectFiles, NonObjectFiles),
+                create_archive(Globals, ErrorStream, TmpArchive, yes,
+                    ProperObjectFiles, ArchiveSucceeded, !IO),
+                MaybeDeleteTmpArchive = yes(TmpArchive),
+                join_quoted_string_list([TmpArchive | NonObjectFiles],
+                    "", "", " ", Objects)
+            ;
+                TmpFileResult = error(Error),
+                io.format(stderr_stream,
+                    "Could not create temporary file: %s\n",
+                    [s(error_message(Error))], !IO),
+                ArchiveSucceeded = no,
+                MaybeDeleteTmpArchive = no,
+                join_quoted_string_list(ObjectsList, "", "", " ", Objects)
+            )
         ;
             RestrictedCommandLine = no,
             ArchiveSucceeded = yes,
@@ -3043,30 +3066,37 @@ create_java_exe_or_lib(Globals, ErrorStream, LinkTargetType, MainModuleName,
     % extremely long. We create the temporary file in the current directory to
     % avoid problems under Cygwin, where absolute paths will be interpreted
     % incorrectly when passed to a non-Cygwin jar program.
-    io.make_temp(".", "mtmp", TempFileName, !IO),
-    io.open_output(TempFileName, OpenResult, !IO),
-    (
-        OpenResult = ok(Stream),
-        list.foldl(write_jar_class_argument(Stream, ClassSubDir),
-            ListClassFiles, !IO),
-        io.close_output(Stream, !IO),
-
-        Cmd = string.append_list(
-            [Jar, " cf ", JarFileName, " @", TempFileName]),
-        invoke_system_command(Globals, ErrorStream, cmd_verbose_commands, Cmd,
-            Succeeded0, !IO),
-        io.remove_file(TempFileName, _, !IO),
+    io.make_temp_res(".", "mtmp", "", TempFileNameResult, !IO),
+    (
+        TempFileNameResult = ok(TempFileName),
+        io.open_output(TempFileName, OpenResult, !IO),
         (
-            Succeeded0 = yes
+            OpenResult = ok(Stream),
+            list.foldl(write_jar_class_argument(Stream, ClassSubDir),
+                ListClassFiles, !IO),
+            io.close_output(Stream, !IO),
+
+            Cmd = string.append_list(
+                [Jar, " cf ", JarFileName, " @", TempFileName]),
+            invoke_system_command(Globals, ErrorStream,
+                cmd_verbose_commands, Cmd, Succeeded0, !IO),
+            io.remove_file(TempFileName, _, !IO),
+            (
+                Succeeded0 = yes
+            ;
+                Succeeded0 = no,
+                io.remove_file(JarFileName, _, !IO)
+            )
         ;
-            Succeeded0 = no,
-            io.remove_file(JarFileName, _, !IO)
+            OpenResult = error(Error),
+            io.format(ErrorStream, "Error creating `%s': %s\n",
+                [s(TempFileName), s(error_message(Error))], !IO),
+            Succeeded0 = no
         )
     ;
-        OpenResult = error(Error),
-        io.error_message(Error, ErrorMsg),
-        io.format(ErrorStream, "Error creating `%s': %s\n",
-            [s(TempFileName), s(ErrorMsg)], !IO),
+        TempFileNameResult = error(Error),
+        io.format(ErrorStream, "Could not create temporary file: %s\n",
+            [s(error_message(Error))], !IO),
         Succeeded0 = no
     ),
     ( if
@@ -3463,48 +3493,58 @@ invoke_long_system_command_maybe_filter_output(Globals, ErrorStream, Verbosity,
         RestrictedCommandLine = yes,
 
         % Avoid generating very long command lines by using @files.
-        io.make_temp(TmpFile, !IO),
-        io.open_output(TmpFile, OpenResult, !IO),
+        io.make_temp_res(TmpFileResult, !IO),
         (
-            OpenResult = ok(TmpStream),
-
-            % We need to escape any \ before writing them to the file,
-            % otherwise we lose them.
-            TmpFileArgs = string.replace_all(Args, "\\", "\\\\"),
-
-            io.write_string(TmpStream, TmpFileArgs, !IO),
-            io.close_output(TmpStream, !IO),
-
-            globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
-            AtFileName = at_file_name(Globals, TmpFile),
+            TmpFileResult = ok(TmpFile),
+            io.open_output(TmpFile, OpenResult, !IO),
             (
-                VeryVerbose = yes,
-                io.format("%% Args placed in %s: `%s'\n",
-                    [s(AtFileName), s(TmpFileArgs)], !IO),
-                io.flush_output(!IO)
-            ;
-                VeryVerbose = no
-            ),
-
-            ( if NonAtArgs = "" then
-                FullCmd = Cmd ++ " " ++ AtFileName
-            else
-                string.append_list([Cmd, " ", NonAtArgs, " ", AtFileName],
-                    FullCmd)
-            ),
-            invoke_system_command_maybe_filter_output(Globals, ErrorStream,
-                Verbosity, FullCmd, MaybeProcessOutput, Succeeded0, !IO),
+                OpenResult = ok(TmpStream),
+
+                % We need to escape any \ before writing them to the file,
+                % otherwise we lose them.
+                TmpFileArgs = string.replace_all(Args, "\\", "\\\\"),
+
+                io.write_string(TmpStream, TmpFileArgs, !IO),
+                io.close_output(TmpStream, !IO),
+
+                globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
+                AtFileName = at_file_name(Globals, TmpFile),
+                (
+                    VeryVerbose = yes,
+                    io.format("%% Args placed in %s: `%s'\n",
+                        [s(AtFileName), s(TmpFileArgs)], !IO),
+                    io.flush_output(!IO)
+                ;
+                    VeryVerbose = no
+                ),
 
-            io.remove_file(TmpFile, RemoveResult, !IO),
-            (
-                RemoveResult = ok,
-                Succeeded = Succeeded0
+                ( if NonAtArgs = "" then
+                    FullCmd = Cmd ++ " " ++ AtFileName
+                else
+                    string.append_list([Cmd, " ", NonAtArgs, " ", AtFileName],
+                        FullCmd)
+                ),
+                invoke_system_command_maybe_filter_output(Globals, ErrorStream,
+                    Verbosity, FullCmd, MaybeProcessOutput, Succeeded0, !IO),
+
+                io.remove_file(TmpFile, RemoveResult, !IO),
+                (
+                    RemoveResult = ok,
+                    Succeeded = Succeeded0
+                ;
+                    RemoveResult = error(_),
+                    Succeeded = no
+                )
             ;
-                RemoveResult = error(_),
+                OpenResult = error(Error),
+                io.format(stderr_stream, "%s: %s\n",
+                    [s(TmpFile), s(error_message(Error))], !IO),
                 Succeeded = no
             )
         ;
-            OpenResult = error(_),
+            TmpFileResult = error(Error),
+            io.format(stderr_stream, "Could not create temporary file: %s\n",
+                [s(error_message(Error))], !IO),
             Succeeded = no
         )
     ;
diff --git a/compiler/fact_table.m b/compiler/fact_table.m
index cf01d59..7b4559a 100644
--- a/compiler/fact_table.m
+++ b/compiler/fact_table.m
@@ -309,15 +309,27 @@ fact_table_compile_facts_2(PredName, Arity, FileName, !PredInfo, Context,
             infer_determinism_pass_2(ProcFiles, Globals, ExistsAllInMode,
                 ProcTable0, ProcTable, !IO),
             pred_info_set_proc_table(ProcTable, !PredInfo),
-            io.make_temp(DataFileName, !IO),
-            write_fact_table_arrays(ProcFiles, DataFileName, StructName,
-                ProcTable, ModuleInfo, NumFacts, FactArgInfos, WriteHashTables,
-                WriteDataAfterSorting, OutputStream, C_HeaderCode1,
-                PrimaryProcID, !IO),
-            write_fact_table_numfacts(PredName, NumFacts, OutputStream,
-                C_HeaderCode3, !IO),
-            string.append_list([C_HeaderCode0, C_HeaderCode1,
-                C_HeaderCode2, C_HeaderCode3], C_HeaderCode)
+            io.make_temp_res(DataFileNameResult, !IO),
+            (
+                DataFileNameResult = ok(DataFileName),
+                write_fact_table_arrays(ProcFiles, DataFileName, StructName,
+                    ProcTable, ModuleInfo, NumFacts, FactArgInfos,
+                    WriteHashTables, WriteDataAfterSorting, OutputStream,
+                    C_HeaderCode1, PrimaryProcID, !IO),
+                write_fact_table_numfacts(PredName, NumFacts, OutputStream,
+                    C_HeaderCode3, !IO),
+                string.append_list([C_HeaderCode0, C_HeaderCode1,
+                    C_HeaderCode2, C_HeaderCode3], C_HeaderCode)
+            ;
+                DataFileNameResult = error(Error),
+                ErrorReport = no - [
+                    words("Could not create temporary file:"),
+                    quote(error_message(Error))],
+                print_error_reports(Globals, [ErrorReport], !IO),
+                C_HeaderCode = C_HeaderCode0,
+                PrimaryProcID = invalid_proc_id,
+                DataFileName = ""
+            )
         ;
             OpenCompileErrors = [_ | _],
             print_error_reports(Globals, OpenCompileErrors, !IO),
@@ -967,18 +979,27 @@ fact_table_mode_type([Mode | Modes], ModuleInfo, ModeType) :-
 
 open_sort_files([], [], !Errors, !IO).
 open_sort_files([ProcID | ProcIDs], ProcStreams, !Errors, !IO) :-
-    io.make_temp(SortFileName, !IO),
-    io.open_output(SortFileName, Result, !IO),
+    io.make_temp_res(SortFileNameResult, !IO),
     (
-        Result = ok(Stream),
-        open_sort_files(ProcIDs, ProcStreams0, !Errors, !IO),
-        ProcStreams = [proc_stream(ProcID, Stream) | ProcStreams0]
+        SortFileNameResult = ok(SortFileName),
+        io.open_output(SortFileName, Result, !IO),
+        (
+            Result = ok(Stream),
+            open_sort_files(ProcIDs, ProcStreams0, !Errors, !IO),
+            ProcStreams = [proc_stream(ProcID, Stream) | ProcStreams0]
+        ;
+            Result = error(ErrorCode),
+            ProcStreams = [],
+            io.error_message(ErrorCode, Message),
+            string.format("Error opening file `%s' for output: %s.",
+                [s(SortFileName), s(Message)], Msg),
+            add_error_report([words(Msg)], !Errors)
+        )
     ;
-        Result = error(ErrorCode),
+        SortFileNameResult = error(Error),
         ProcStreams = [],
-        io.error_message(ErrorCode, Message),
-        string.format("Error opening file `%s' for output: %s.",
-            [s(SortFileName), s(Message)], Msg),
+        string.format("Could not create temporary file: %s.",
+            [s(error_message(Error))], Msg),
         add_error_report([words(Msg)], !Errors)
     ).
 
diff --git a/compiler/make.module_target.m b/compiler/make.module_target.m
index 6d1f22f..0a9d037 100644
--- a/compiler/make.module_target.m
+++ b/compiler/make.module_target.m
@@ -362,51 +362,69 @@ build_target(Globals, CompilationTask, TargetFile, Imports, TouchedTargetFiles,
         % We need a temporary file to pass the arguments to the mmc process
         % which will do the compilation. It is created here (not in invoke_mmc)
         % so it can be cleaned up by build_with_check_for_interrupt.
-        io.make_temp(ArgFileName, !IO),
-        MaybeArgFileName = yes(ArgFileName)
+        io.make_temp_res(ArgFileNameResult, !IO),
+        (
+            ArgFileNameResult = ok(ArgFileName),
+            MaybeArgFileName = yes(ArgFileName),
+            ArgFileNameSuccess = ok `with_type` io.res
+        ;
+            ArgFileNameResult = error(Error),
+            MaybeArgFileName = no,
+            ArgFileNameSuccess = error(Error)
+        )
     else
-        MaybeArgFileName = no
+        MaybeArgFileName = no,
+        ArgFileNameSuccess = ok
     ),
-    Cleanup =
-        ( pred(!.MakeInfo::in, !:MakeInfo::out, !.IO::di, !:IO::uo) is det :-
-            % XXX Remove `.int.tmp' files.
-            list.foldl2(make_remove_target_file(Globals, very_verbose),
-                TouchedTargetFiles, !MakeInfo, !IO),
-            list.foldl2(make_remove_file(Globals, very_verbose), TouchedFiles,
-                !MakeInfo, !IO),
-            (
-                MaybeArgFileName = yes(ArgFileName2),
-                io.remove_file(ArgFileName2, _, !IO)
-            ;
-                MaybeArgFileName = no
-            )
-        ),
-
-    get_real_milliseconds(Time0, !IO),
-    globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
-    build_with_check_for_interrupt(VeryVerbose,
-        build_with_module_options_and_output_redirect(Globals, ModuleName,
-            ExtraOptions ++ TaskOptions,
-            build_target_2(ModuleName, Task, MaybeArgFileName, Imports)),
-        Cleanup, Succeeded, !Info, !IO),
-    record_made_target_2(Globals, Succeeded, TargetFile, TouchedTargetFiles,
-        TouchedFiles, !Info, !IO),
-    get_real_milliseconds(Time, !IO),
 
-    globals.lookup_bool_option(Globals, show_make_times, ShowMakeTimes),
     (
-        ShowMakeTimes = yes,
-        DiffSecs = float(Time - Time0) / 1000.0,
-        % Avoid cluttering the screen with short running times.
-        ( if DiffSecs >= 0.4 then
-            io.write_string("Making ", !IO),
-            make_write_target_file(Globals, TargetFile, !IO),
-            io.format(" took %.2fs\n", [f(DiffSecs)], !IO)
-        else
-            true
+        ArgFileNameSuccess = ok,
+        Cleanup = (pred(!.MakeInfo::in, !:MakeInfo::out, !.IO::di, !:IO::uo)
+                    is det :-
+                % XXX Remove `.int.tmp' files.
+                list.foldl2(make_remove_target_file(Globals, very_verbose),
+                    TouchedTargetFiles, !MakeInfo, !IO),
+                list.foldl2(make_remove_file(Globals, very_verbose),
+                    TouchedFiles, !MakeInfo, !IO),
+                (
+                    MaybeArgFileName = yes(ArgFileName2),
+                    io.remove_file(ArgFileName2, _, !IO)
+                ;
+                    MaybeArgFileName = no
+                )
+            ),
+
+        get_real_milliseconds(Time0, !IO),
+        globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
+        build_with_check_for_interrupt(VeryVerbose,
+            build_with_module_options_and_output_redirect(Globals, ModuleName,
+                ExtraOptions ++ TaskOptions,
+                build_target_2(ModuleName, Task, MaybeArgFileName, Imports)),
+            Cleanup, Succeeded, !Info, !IO),
+        record_made_target_2(Globals, Succeeded, TargetFile, TouchedTargetFiles,
+            TouchedFiles, !Info, !IO),
+        get_real_milliseconds(Time, !IO),
+
+        globals.lookup_bool_option(Globals, show_make_times, ShowMakeTimes),
+        (
+            ShowMakeTimes = yes,
+            DiffSecs = float(Time - Time0) / 1000.0,
+            % Avoid cluttering the screen with short running times.
+            ( if DiffSecs >= 0.4 then
+                io.write_string("Making ", !IO),
+                make_write_target_file(Globals, TargetFile, !IO),
+                io.format(" took %.2fs\n", [f(DiffSecs)], !IO)
+            else
+                true
+            )
+        ;
+            ShowMakeTimes = no
         )
     ;
-        ShowMakeTimes = no
+        ArgFileNameSuccess = error(ArgFileError),
+        io.format(stderr_stream, "Could not create temporary file: %s\n",
+            [s(error_message(ArgFileError))], !IO),
+        Succeeded = no
     ).
 
 :- pred build_target_2(module_name::in, compilation_task_type::in,
diff --git a/compiler/make.util.m b/compiler/make.util.m
index 4b2abc1..7488416 100644
--- a/compiler/make.util.m
+++ b/compiler/make.util.m
@@ -1074,16 +1074,24 @@ redirect_output(_ModuleName, MaybeErrorStream, !Info, !IO) :-
     % the part of the error file that relates to the current command. It will
     % be appended to the error file later.
 
-    io.make_temp(ErrorFileName, !IO),
-    io.open_output(ErrorFileName, ErrorFileRes, !IO),
+    io.make_temp_res(ErrorFileNameResult, !IO),
     (
-        ErrorFileRes = ok(ErrorOutputStream),
-        MaybeErrorStream = yes(ErrorOutputStream)
+        ErrorFileNameResult = ok(ErrorFileName),
+        io.open_output(ErrorFileName, ErrorFileRes, !IO),
+        (
+            ErrorFileRes = ok(ErrorOutputStream),
+            MaybeErrorStream = yes(ErrorOutputStream)
+        ;
+            ErrorFileRes = error(IOError),
+            MaybeErrorStream = no,
+            with_locked_stdout(!.Info,
+                write_error_opening_output(ErrorFileName, IOError), !IO)
+        )
     ;
-        ErrorFileRes = error(IOError),
+        ErrorFileNameResult = error(IOError),
         MaybeErrorStream = no,
-        with_locked_stdout(!.Info,
-            write_error_opening_output(ErrorFileName, IOError), !IO)
+        with_locked_stdout(!.Info, write_error_creating_temp_file(IOError),
+            !IO)
     ).
 
 unredirect_output(Globals, ModuleName, ErrorOutputStream, !Info, !IO) :-
@@ -1182,6 +1190,12 @@ write_error_opening_file(FileName, Error, !IO) :-
     io.format("Error opening `%s': %s\n",
         [s(FileName), s(io.error_message(Error))], !IO).
 
+:- pred write_error_creating_temp_file(io.error::in, io::di, io::uo) is det.
+
+write_error_creating_temp_file(Error, !IO) :-
+    io.format("Error creating temporary file: %s\n",
+        [s(io.error_message(Error))], !IO).
+
 %-----------------------------------------------------------------------------%
 
 get_timestamp_file_timestamp(Globals, target_file(ModuleName, FileType),
diff --git a/compiler/module_cmds.m b/compiler/module_cmds.m
index 2e56455..b54a199 100644
--- a/compiler/module_cmds.m
+++ b/compiler/module_cmds.m
@@ -575,46 +575,56 @@ invoke_system_command_maybe_filter_output(Globals, ErrorStream, Verbosity,
     % the output from the command would go to the current C output
     % and error streams.
 
-    io.make_temp(TmpFile, !IO),
-    ( if use_dotnet then
-        % XXX can't use Bourne shell syntax to redirect on .NET
-        % XXX the output will go to the wrong place!
-        CommandRedirected = Command
-    else if use_win32 then
-        % On windows we can't in general redirect standard error in the
-        % shell.
-        CommandRedirected = Command ++ " > " ++ TmpFile
-    else
-        CommandRedirected =
-            string.append_list([Command, " > ", TmpFile, " 2>&1"])
-    ),
-    io.call_system_return_signal(CommandRedirected, Result, !IO),
+    io.make_temp_res(TmpFileResult, !IO),
     (
-        Result = ok(exited(Status)),
-        maybe_write_string(PrintCommand, "% done.\n", !IO),
-        ( if Status = 0 then
-            CommandSucceeded = yes
+        TmpFileResult = ok(TmpFile),
+        ( if use_dotnet then
+            % XXX can't use Bourne shell syntax to redirect on .NET
+            % XXX the output will go to the wrong place!
+            CommandRedirected = Command
+        else if use_win32 then
+            % On windows we can't in general redirect standard error in the
+            % shell.
+            CommandRedirected = Command ++ " > " ++ TmpFile
         else
-            % The command should have produced output describing the error.
+            CommandRedirected =
+                string.append_list([Command, " > ", TmpFile, " 2>&1"])
+        ),
+        io.call_system_return_signal(CommandRedirected, Result, !IO),
+        (
+            Result = ok(exited(Status)),
+            maybe_write_string(PrintCommand, "% done.\n", !IO),
+            ( if Status = 0 then
+                CommandSucceeded = yes
+            else
+                % The command should have produced output describing the error.
+                CommandSucceeded = no
+            )
+        ;
+            Result = ok(signalled(Signal)),
+            report_error_to_stream(ErrorStream,
+                "system command received signal "
+                ++ int_to_string(Signal) ++ ".", !IO),
+            % Also report the error to standard output, because if we raise the
+            % signal this error may not ever been seen, the process stops and
+            % the user is confused.
+            report_error("system command received signal "
+                ++ int_to_string(Signal) ++ ".", !IO),
+
+            % Make sure the current process gets the signal. Some systems (e.g.
+            % Linux) ignore SIGINT during a call to system().
+            raise_signal(Signal, !IO),
+            CommandSucceeded = no
+        ;
+            Result = error(Error),
+            report_error_to_stream(ErrorStream, io.error_message(Error), !IO),
             CommandSucceeded = no
         )
     ;
-        Result = ok(signalled(Signal)),
-        report_error_to_stream(ErrorStream, "system command received signal "
-            ++ int_to_string(Signal) ++ ".", !IO),
-        % Also report the error to standard output, because if we raise the
-        % signal this error may not ever been seen, the process stops and
-        % the user is confused.
-        report_error("system command received signal "
-            ++ int_to_string(Signal) ++ ".", !IO),
-
-        % Make sure the current process gets the signal. Some systems (e.g.
-        % Linux) ignore SIGINT during a call to system().
-        raise_signal(Signal, !IO),
-        CommandSucceeded = no
-    ;
-        Result = error(Error),
-        report_error_to_stream(ErrorStream, io.error_message(Error), !IO),
+        TmpFileResult = error(Error),
+        report_error_to_stream(ErrorStream,
+            "Could not create temporary file: " ++ error_message(Error), !IO),
+        TmpFile = "",
         CommandSucceeded = no
     ),
 
@@ -623,54 +633,64 @@ invoke_system_command_maybe_filter_output(Globals, ErrorStream, Verbosity,
         not use_dotnet,
         MaybeProcessOutput = yes(ProcessOutput)
     then
-        io.make_temp(ProcessedTmpFile, !IO),
-
-        % XXX we should get rid of use_win32
-        ( if use_win32 then
-            get_system_env_type(Globals, SystemEnvType),
-            ( if SystemEnvType = env_type_powershell then
-                ProcessOutputRedirected = string.append_list(
-                    ["Get-Content ", TmpFile, " | ", ProcessOutput,
-                        " > ", ProcessedTmpFile, " 2>&1"])
+        io.make_temp_res(ProcessedTmpFileResult, !IO),
+        (
+            ProcessedTmpFileResult = ok(ProcessedTmpFile),
+
+            % XXX we should get rid of use_win32
+            ( if use_win32 then
+                get_system_env_type(Globals, SystemEnvType),
+                ( if SystemEnvType = env_type_powershell then
+                    ProcessOutputRedirected = string.append_list(
+                        ["Get-Content ", TmpFile, " | ", ProcessOutput,
+                            " > ", ProcessedTmpFile, " 2>&1"])
+                else
+                    % On windows we can't in general redirect standard
+                    % error in the shell.
+                    ProcessOutputRedirected = string.append_list(
+                        [ProcessOutput, " < ", TmpFile, " > ",
+                            ProcessedTmpFile])
+                )
             else
-                % On windows we can't in general redirect standard
-                % error in the shell.
                 ProcessOutputRedirected = string.append_list(
                     [ProcessOutput, " < ", TmpFile, " > ",
-                        ProcessedTmpFile])
-            )
-        else
-            ProcessOutputRedirected = string.append_list(
-                [ProcessOutput, " < ", TmpFile, " > ",
-                    ProcessedTmpFile, " 2>&1"])
-        ),
-        io.call_system_return_signal(ProcessOutputRedirected,
-            ProcessOutputResult, !IO),
-        io.remove_file(TmpFile, _, !IO),
-        (
-            ProcessOutputResult = ok(exited(ProcessOutputStatus)),
-            maybe_write_string(PrintCommand, "% done.\n", !IO),
-            ( if ProcessOutputStatus = 0 then
-                ProcessOutputSucceeded = yes
-            else
-                % The command should have produced output
-                % describing the error.
+                        ProcessedTmpFile, " 2>&1"])
+            ),
+            io.call_system_return_signal(ProcessOutputRedirected,
+                ProcessOutputResult, !IO),
+            io.remove_file(TmpFile, _, !IO),
+            (
+                ProcessOutputResult = ok(exited(ProcessOutputStatus)),
+                maybe_write_string(PrintCommand, "% done.\n", !IO),
+                ( if ProcessOutputStatus = 0 then
+                    ProcessOutputSucceeded = yes
+                else
+                    % The command should have produced output
+                    % describing the error.
+                    ProcessOutputSucceeded = no
+                )
+            ;
+                ProcessOutputResult = ok(signalled(ProcessOutputSignal)),
+                % Make sure the current process gets the signal. Some
+                % systems (e.g. Linux) ignore SIGINT during a call to
+                % system().
+                raise_signal(ProcessOutputSignal, !IO),
+                report_error_to_stream(ErrorStream,
+                    "system command received signal "
+                    ++ int_to_string(ProcessOutputSignal) ++ ".", !IO),
+                ProcessOutputSucceeded = no
+            ;
+                ProcessOutputResult = error(ProcessOutputError),
+                report_error_to_stream(ErrorStream,
+                    io.error_message(ProcessOutputError), !IO),
                 ProcessOutputSucceeded = no
             )
         ;
-            ProcessOutputResult = ok(signalled(ProcessOutputSignal)),
-            % Make sure the current process gets the signal. Some systems
-            % (e.g. Linux) ignore SIGINT during a call to system().
-            raise_signal(ProcessOutputSignal, !IO),
-            report_error_to_stream(ErrorStream,
-                "system command received signal "
-                ++ int_to_string(ProcessOutputSignal) ++ ".", !IO),
-            ProcessOutputSucceeded = no
-        ;
-            ProcessOutputResult = error(ProcessOutputError),
+            ProcessedTmpFileResult = error(ProcessTmpError),
             report_error_to_stream(ErrorStream,
-                io.error_message(ProcessOutputError), !IO),
-            ProcessOutputSucceeded = no
+                io.error_message(ProcessTmpError), !IO),
+            ProcessOutputSucceeded = no,
+            ProcessedTmpFile = ""
         )
     else
         ProcessOutputSucceeded = yes,
diff --git a/compiler/prog_event.m b/compiler/prog_event.m
index 65a72c0..717c06c 100644
--- a/compiler/prog_event.m
+++ b/compiler/prog_event.m
@@ -92,58 +92,74 @@ read_event_set(SpecsFileName, EventSetName, EventSpecMap, ErrorSpecs, !IO) :-
     % those tools are not yet mature enough. When they are, we should switch
     % to using them.
 
-    io.make_temp(TermFileName, !IO),
-    read_specs_file(SpecsFileName, TermFileName, Problem, !IO),
-    ( if Problem = "" then
-        io.open_input(TermFileName, TermOpenRes, !IO),
-        (
-            TermOpenRes = ok(TermStream),
-            io.read(TermStream, TermReadRes, !IO),
+    io.make_temp_res(TermFileNameResult, !IO),
+    (
+        TermFileNameResult = ok(TermFileName),
+        read_specs_file(SpecsFileName, TermFileName, Problem, !IO),
+        ( if Problem = "" then
+            io.open_input(TermFileName, TermOpenRes, !IO),
             (
-                TermReadRes = ok(EventSetTerm),
-                EventSetTerm = event_set_spec(EventSetName, EventSpecsTerm),
-                convert_list_to_spec_map(SpecsFileName, EventSpecsTerm,
-                    map.init, EventSpecMap, [], ErrorSpecs)
+                TermOpenRes = ok(TermStream),
+                io.read(TermStream, TermReadRes, !IO),
+                (
+                    TermReadRes = ok(EventSetTerm),
+                    EventSetTerm = event_set_spec(EventSetName,
+                        EventSpecsTerm),
+                    convert_list_to_spec_map(SpecsFileName, EventSpecsTerm,
+                        map.init, EventSpecMap, [], ErrorSpecs)
+                ;
+                    TermReadRes = eof,
+                    EventSetName = "",
+                    EventSpecMap = map.init,
+                    Pieces = [words("eof in term specification file"), nl],
+                    ErrorSpec = error_spec(severity_error,
+                        phase_term_to_parse_tree,
+                        [error_msg(no, do_not_treat_as_first, 0,
+                            [always(Pieces)])]),
+                    ErrorSpecs = [ErrorSpec]
+                ;
+                    TermReadRes = error(TermReadMsg, LineNumber),
+                    EventSetName = "",
+                    EventSpecMap = map.init,
+                    Pieces = [words(TermReadMsg), nl],
+                    ErrorSpec = error_spec(severity_error,
+                        phase_term_to_parse_tree,
+                        [simple_msg(context(TermFileName, LineNumber),
+                            [always(Pieces)])]),
+                    ErrorSpecs = [ErrorSpec]
+                ),
+                io.close_input(TermStream, !IO)
             ;
-                TermReadRes = eof,
+                TermOpenRes = error(TermOpenError),
                 EventSetName = "",
                 EventSpecMap = map.init,
-                Pieces = [words("eof in term specification file"), nl],
+                Pieces = [words("Could not open"), quote(TermFileName),
+                    words(":"), words_quote(error_message(TermOpenError)), nl],
                 ErrorSpec = error_spec(severity_error,
                     phase_term_to_parse_tree,
                     [error_msg(no, do_not_treat_as_first, 0,
                         [always(Pieces)])]),
                 ErrorSpecs = [ErrorSpec]
-            ;
-                TermReadRes = error(TermReadMsg, LineNumber),
-                EventSetName = "",
-                EventSpecMap = map.init,
-                Pieces = [words(TermReadMsg), nl],
-                ErrorSpec = error_spec(severity_error,
-                    phase_term_to_parse_tree,
-                    [simple_msg(context(TermFileName, LineNumber),
-                        [always(Pieces)])]),
-                ErrorSpecs = [ErrorSpec]
-            ),
-            io.close_input(TermStream, !IO)
-        ;
-            TermOpenRes = error(TermOpenError),
+            )
+        else
             EventSetName = "",
             EventSpecMap = map.init,
-            Pieces = [words(io.error_message(TermOpenError)), nl],
+            Pieces = [words(Problem), nl],
             ErrorSpec = error_spec(severity_error, phase_term_to_parse_tree,
                 [error_msg(no, do_not_treat_as_first, 0, [always(Pieces)])]),
             ErrorSpecs = [ErrorSpec]
-        )
-    else
+        ),
+        io.remove_file(TermFileName, _RemoveRes, !IO)
+    ;
+        TermFileNameResult= error(TermFileNameError),
         EventSetName = "",
         EventSpecMap = map.init,
-        Pieces = [words(Problem), nl],
+        Pieces = [words("Could not create temporary file:"),
+            words_quote(error_message(TermFileNameError)), nl],
         ErrorSpec = error_spec(severity_error, phase_term_to_parse_tree,
             [error_msg(no, do_not_treat_as_first, 0, [always(Pieces)])]),
         ErrorSpecs = [ErrorSpec]
-    ),
-    io.remove_file(TermFileName, _RemoveRes, !IO).
+    ).
 
 :- pred read_specs_file(string::in, string::in, string::out,
     io::di, io::uo) is det.
diff --git a/compiler/write_deps_file.m b/compiler/write_deps_file.m
index e661e06..caac87b 100644
--- a/compiler/write_deps_file.m
+++ b/compiler/write_deps_file.m
@@ -159,606 +159,627 @@ write_dependency_file(Globals, ModuleAndImports, AllDeps,
     % parallel makes, we first create the file with a temporary name,
     % and then rename it to the desired name when we've finished.
 
-    io.make_temp(dir.dirname(DependencyFileName), "tmp_d",
-        TmpDependencyFileName, !IO),
-    maybe_write_string(Verbose, "% Writing auto-dependency file `", !IO),
-    maybe_write_string(Verbose, DependencyFileName, !IO),
-    maybe_write_string(Verbose, "'...", !IO),
-    maybe_flush_output(Verbose, !IO),
-    io.open_output(TmpDependencyFileName, Result, !IO),
+    io.make_temp_res(dir.dirname(DependencyFileName), "tmp_d",
+        "", TmpDependencyFileNameRes, !IO),
     (
-        Result = error(IOError),
-        maybe_write_string(Verbose, " failed.\n", !IO),
-        maybe_flush_output(Verbose, !IO),
-        io.error_message(IOError, IOErrorMessage),
-        string.append_list(["error opening temporary file `",
-            TmpDependencyFileName, "' for output: ",
-            IOErrorMessage], Message),
+        TmpDependencyFileNameRes = error(Error),
+        Message = "Could not create temporary file: " ++ error_message(Error),
         report_error(Message, !IO)
     ;
-        Result = ok(DepStream),
-        set.union(IntDeps, ImpDeps, LongDeps0),
-        ShortDeps0 = IndirectDeps,
-        set.delete(ModuleName, LongDeps0, LongDeps),
-        set.difference(ShortDeps0, LongDeps, ShortDeps1),
-        set.delete(ModuleName, ShortDeps1, ShortDeps),
-        list.sort_and_remove_dups(FactDeps0, FactDeps),
-
+        TmpDependencyFileNameRes = ok(TmpDependencyFileName),
+        maybe_write_string(Verbose, "% Writing auto-dependency file `", !IO),
+        maybe_write_string(Verbose, DependencyFileName, !IO),
+        maybe_write_string(Verbose, "'...", !IO),
+        maybe_flush_output(Verbose, !IO),
+        io.open_output(TmpDependencyFileName, Result, !IO),
         (
-            MaybeTransOptDeps = yes(TransOptDeps0),
-            set.intersect(set.list_to_set(TransOptDeps0), LongDeps,
-                TransOptDateDeps),
-
-            % Note that maybe_read_dependency_file searches for
-            % this exact pattern.
-            io.write_strings(DepStream, [TransOptDateFileName, " :"], !IO),
-            write_dependencies_set(Globals, DepStream, ".trans_opt",
-                TransOptDateDeps, !IO)
+            Result = error(IOError),
+            maybe_write_string(Verbose, " failed.\n", !IO),
+            maybe_flush_output(Verbose, !IO),
+            io.error_message(IOError, IOErrorMessage),
+            string.append_list(["error opening temporary file `",
+                TmpDependencyFileName, "' for output: ",
+                IOErrorMessage], Message),
+            report_error(Message, !IO)
         ;
-            MaybeTransOptDeps = no
-        ),
+            Result = ok(DepStream),
+            set.union(IntDeps, ImpDeps, LongDeps0),
+            ShortDeps0 = IndirectDeps,
+            set.delete(ModuleName, LongDeps0, LongDeps),
+            set.difference(ShortDeps0, LongDeps, ShortDeps1),
+            set.delete(ModuleName, ShortDeps1, ShortDeps),
+            list.sort_and_remove_dups(FactDeps0, FactDeps),
 
-        (
-            FactDeps = [_ | _],
-            io.write_strings(DepStream,
-                ["\n\n", MakeVarName, ".fact_tables ="], !IO),
-            write_file_dependencies_list(DepStream, "", FactDeps, !IO),
-            io.nl(DepStream, !IO),
-            globals.lookup_bool_option(Globals, assume_gmake, AssumeGmake),
             (
-                AssumeGmake = yes,
-                io.write_strings(DepStream, [
-                    "\n\n", MakeVarName,
-                    ".fact_tables.os = $(", MakeVarName,
-                    ".fact_tables:%=$(os_subdir)%.$O)\n\n",
-                    MakeVarName,
-                    ".fact_tables.cs = $(", MakeVarName,
-                    ".fact_tables:%=$(cs_subdir)%.c)\n\n"
-                ], !IO)
+                MaybeTransOptDeps = yes(TransOptDeps0),
+                set.intersect(set.list_to_set(TransOptDeps0), LongDeps,
+                    TransOptDateDeps),
+
+                % Note that maybe_read_dependency_file searches for
+                % this exact pattern.
+                io.write_strings(DepStream, [TransOptDateFileName, " :"], !IO),
+                write_dependencies_set(Globals, DepStream, ".trans_opt",
+                    TransOptDateDeps, !IO)
             ;
-                AssumeGmake = no,
-                io.write_strings(DepStream,
-                    [MakeVarName, ".fact_tables.cs ="], !IO),
-                write_fact_table_dependencies_list(Globals, DepStream,
-                    ".c", ModuleName, FactDeps, !IO),
-                io.write_strings(DepStream, ["\n\n", MakeVarName,
-                    ".fact_tables.os ="], !IO),
-                write_fact_table_dependencies_list(Globals, DepStream,
-                    ".$O", ModuleName, FactDeps, !IO),
-                io.nl(DepStream, !IO)
-            )
-        ;
-            FactDeps = []
-        ),
-
-        ( if string.remove_suffix(SourceFileName, ".m", SourceFileBase) then
-            ErrFileName = SourceFileBase ++ ".err"
-        else
-            unexpected($module, $pred, "source file doesn't end in `.m'")
-        ),
-        module_name_to_file_name(Globals, ModuleName, ".optdate",
-            do_not_create_dirs, OptDateFileName, !IO),
-        module_name_to_file_name(Globals, ModuleName, ".c_date",
-            do_not_create_dirs, CDateFileName, !IO),
-        module_name_to_file_name(Globals, ModuleName, ".$O",
-            do_not_create_dirs, ObjFileName, !IO),
-        module_name_to_file_name(Globals, ModuleName, ".java_date",
-            do_not_create_dirs, JavaDateFileName, !IO),
-        % XXX Why is the extension hardcoded to .pic_o here?  That looks wrong.
-        % It should probably be .$(EXT_FOR_PIC_OBJECT) - juliensf.
-        module_name_to_file_name(Globals, ModuleName, ".pic_o",
-            do_not_create_dirs, PicObjFileName, !IO),
-        module_name_to_file_name(Globals, ModuleName, ".int0",
-            do_not_create_dirs, Int0FileName, !IO),
-        io.write_strings(DepStream, ["\n\n",
-            OptDateFileName, " ",
-            TransOptDateFileName, " ",
-            ErrFileName, " ",
-            CDateFileName, " ",
-            JavaDateFileName
-        ], !IO),
-        io.write_strings(DepStream, [" : ", SourceFileName], !IO),
-        % If the module contains nested sub-modules then the `.int0' file
-        % must first be built.
-        ( if set.is_empty(InclDeps) then
-            true
-        else
-            io.write_strings(DepStream, [" ", Int0FileName], !IO)
-        ),
-        write_dependencies_set(Globals, DepStream, ".int0", ParentDeps, !IO),
-        write_dependencies_set(Globals, DepStream, ".int", LongDeps, !IO),
-        write_dependencies_set(Globals, DepStream, ".int2", ShortDeps, !IO),
-
-        NestedExts = [
-            ".optdate",
-            ".trans_opt_date",
-            ".c_date",
-            ".dir/*.$O",
-            ".java_date"],
-
-        % If a module contains nested-submodules then we need to build
-        % the nested children before attempting to build the parent module.
-        ( if set.is_empty(NestedDeps) then
-            true
-        else
-            Write = (pred(Ext::in, !.LIO::di, !:LIO::uo) is det :-
-                module_name_to_file_name(Globals, ModuleName, Ext,
-                    do_not_create_dirs, ExtName, !LIO),
-                io.write_strings(DepStream, ["\n\n", ExtName, " : "], !LIO),
-                write_dependencies_set(Globals, DepStream, Ext, NestedDeps,
-                    !LIO)
+                MaybeTransOptDeps = no
             ),
-            list.foldl(Write, NestedExts, !IO)
-        ),
-
-        ForeignIncludeFiles = cord.list(ForeignIncludeFilesCord),
-        % This is conservative: a target file for foreign language A does not
-        % does not truly depend on a file included for foreign language B.
-        write_foreign_include_file_dependencies_list(DepStream,
-            SourceFileName, ForeignIncludeFiles, !IO),
 
-        (
-            FactDeps = [_ | _],
-            io.write_strings(DepStream, [
-                " \\\n\t$(", MakeVarName, ".fact_tables)\n\n",
-                "$(", MakeVarName, ".fact_tables.os) : $(",
-                MakeVarName, ".fact_tables) ",
-                SourceFileName, "\n\n",
-                "$(", MakeVarName, ".fact_tables.cs) : ",
-                ObjFileName, "\n"
-            ], !IO)
-        ;
-            FactDeps = []
-        ),
-
-        globals.lookup_bool_option(Globals, use_opt_files, UseOptFiles),
-        globals.lookup_bool_option(Globals, intermodule_optimization,
-            Intermod),
-        globals.lookup_accumulating_option(Globals, intermod_directories,
-            IntermodDirs),
+            (
+                FactDeps = [_ | _],
+                io.write_strings(DepStream,
+                    ["\n\n", MakeVarName, ".fact_tables ="], !IO),
+                write_file_dependencies_list(DepStream, "", FactDeps, !IO),
+                io.nl(DepStream, !IO),
+                globals.lookup_bool_option(Globals, assume_gmake, AssumeGmake),
+                (
+                    AssumeGmake = yes,
+                    io.write_strings(DepStream, [
+                        "\n\n", MakeVarName,
+                        ".fact_tables.os = $(", MakeVarName,
+                        ".fact_tables:%=$(os_subdir)%.$O)\n\n",
+                        MakeVarName,
+                        ".fact_tables.cs = $(", MakeVarName,
+                        ".fact_tables:%=$(cs_subdir)%.c)\n\n"
+                    ], !IO)
+                ;
+                    AssumeGmake = no,
+                    io.write_strings(DepStream,
+                        [MakeVarName, ".fact_tables.cs ="], !IO),
+                    write_fact_table_dependencies_list(Globals, DepStream,
+                        ".c", ModuleName, FactDeps, !IO),
+                    io.write_strings(DepStream, ["\n\n", MakeVarName,
+                        ".fact_tables.os ="], !IO),
+                    write_fact_table_dependencies_list(Globals, DepStream,
+                        ".$O", ModuleName, FactDeps, !IO),
+                    io.nl(DepStream, !IO)
+                )
+            ;
+                FactDeps = []
+            ),
 
-        % If intermodule_optimization is enabled then all the .mh files
-        % must exist because it is possible that the .c file imports them
-        % directly or indirectly.
-        (
-            Intermod = yes,
-            io.write_strings(DepStream, ["\n\n", ObjFileName, " : "], !IO),
-            write_dependencies_list(Globals, DepStream, ".mh",
-                set.to_sorted_list(AllDeps), !IO)
-        ;
-            Intermod = no
-        ),
-        ( if
-            ( Intermod = yes
-            ; UseOptFiles = yes
-            )
-        then
-            io.write_strings(DepStream, [
-                "\n\n",
+            ( if string.remove_suffix(SourceFileName, ".m", SourceFileBase) then
+                ErrFileName = SourceFileBase ++ ".err"
+            else
+                unexpected($module, $pred, "source file doesn't end in `.m'")
+            ),
+            module_name_to_file_name(Globals, ModuleName, ".optdate",
+                do_not_create_dirs, OptDateFileName, !IO),
+            module_name_to_file_name(Globals, ModuleName, ".c_date",
+                do_not_create_dirs, CDateFileName, !IO),
+            module_name_to_file_name(Globals, ModuleName, ".$O",
+                do_not_create_dirs, ObjFileName, !IO),
+            module_name_to_file_name(Globals, ModuleName, ".java_date",
+                do_not_create_dirs, JavaDateFileName, !IO),
+            % XXX Why is the extension hardcoded to .pic_o here?  That looks
+            % wrong.  It should probably be .$(EXT_FOR_PIC_OBJECT) -
+            % juliensf.
+            module_name_to_file_name(Globals, ModuleName, ".pic_o",
+                do_not_create_dirs, PicObjFileName, !IO),
+            module_name_to_file_name(Globals, ModuleName, ".int0",
+                do_not_create_dirs, Int0FileName, !IO),
+            io.write_strings(DepStream, ["\n\n",
+                OptDateFileName, " ",
                 TransOptDateFileName, " ",
                 ErrFileName, " ",
                 CDateFileName, " ",
-                JavaDateFileName, " : "
+                JavaDateFileName
             ], !IO),
+            io.write_strings(DepStream, [" : ", SourceFileName], !IO),
+            % If the module contains nested sub-modules then the `.int0' file
+            % must first be built.
+            ( if set.is_empty(InclDeps) then
+                true
+            else
+                io.write_strings(DepStream, [" ", Int0FileName], !IO)
+            ),
+            write_dependencies_set(Globals, DepStream, ".int0", ParentDeps, !IO),
+            write_dependencies_set(Globals, DepStream, ".int", LongDeps, !IO),
+            write_dependencies_set(Globals, DepStream, ".int2", ShortDeps, !IO),
+
+            NestedExts = [
+                ".optdate",
+                ".trans_opt_date",
+                ".c_date",
+                ".dir/*.$O",
+                ".java_date"],
+
+            % If a module contains nested-submodules then we need to build
+            % the nested children before attempting to build the parent module.
+            ( if set.is_empty(NestedDeps) then
+                true
+            else
+                Write = (pred(Ext::in, !.LIO::di, !:LIO::uo) is det :-
+                    module_name_to_file_name(Globals, ModuleName, Ext,
+                        do_not_create_dirs, ExtName, !LIO),
+                    io.write_strings(DepStream, ["\n\n", ExtName, " : "], !LIO),
+                    write_dependencies_set(Globals, DepStream, Ext, NestedDeps,
+                        !LIO)
+                ),
+                list.foldl(Write, NestedExts, !IO)
+            ),
 
-            % The target (e.g. C) file only depends on the .opt files
-            % from the current directory, so that inter-module optimization
-            % works when the .opt files for the library are unavailable.
-            % This is only necessary because make doesn't allow conditional
-            % dependencies. The dependency on the current module's .opt file
-            % is to make sure the module gets type-checked without having
-            % the definitions of abstract types from other modules.
-            %
-            % XXX The code here doesn't correctly handle dependencies
-            % on `.int' and `.int2' files needed by the `.opt' files.
-            globals.lookup_bool_option(Globals, transitive_optimization,
-                TransOpt),
-            globals.lookup_bool_option(Globals, use_trans_opt_files,
-                UseTransOpt),
+            ForeignIncludeFiles = cord.list(ForeignIncludeFilesCord),
+            % This is conservative: a target file for foreign language A
+            % does not does not truly depend on a file included for foreign
+            % language B.
+            write_foreign_include_file_dependencies_list(DepStream,
+                SourceFileName, ForeignIncludeFiles, !IO),
+
+            (
+                FactDeps = [_ | _],
+                io.write_strings(DepStream, [
+                    " \\\n\t$(", MakeVarName, ".fact_tables)\n\n",
+                    "$(", MakeVarName, ".fact_tables.os) : $(",
+                    MakeVarName, ".fact_tables) ",
+                    SourceFileName, "\n\n",
+                    "$(", MakeVarName, ".fact_tables.cs) : ",
+                    ObjFileName, "\n"
+                ], !IO)
+            ;
+                FactDeps = []
+            ),
+
+            globals.lookup_bool_option(Globals, use_opt_files, UseOptFiles),
+            globals.lookup_bool_option(Globals, intermodule_optimization,
+                Intermod),
+            globals.lookup_accumulating_option(Globals, intermod_directories,
+                IntermodDirs),
 
+            % If intermodule_optimization is enabled then all the .mh files
+            % must exist because it is possible that the .c file imports them
+            % directly or indirectly.
+            (
+                Intermod = yes,
+                io.write_strings(DepStream, ["\n\n", ObjFileName, " : "], !IO),
+                write_dependencies_list(Globals, DepStream, ".mh",
+                    set.to_sorted_list(AllDeps), !IO)
+            ;
+                Intermod = no
+            ),
             ( if
-                ( TransOpt = yes
-                ; UseTransOpt = yes
+                ( Intermod = yes
+                ; UseOptFiles = yes
                 )
             then
-                bool.not(UseTransOpt, BuildOptFiles),
-                get_both_opt_deps(Globals, BuildOptFiles, IntermodDirs,
-                    [ModuleName | set.to_sorted_list(LongDeps)],
-                    OptDeps, TransOptDeps, !IO),
-                OptInt0Deps = set.union_list(
-                    list.map(get_ancestors_set, OptDeps)),
-                write_dependencies_list(Globals, DepStream, ".opt",
-                    OptDeps, !IO),
-                write_dependencies_set(Globals, DepStream, ".int0",
-                    OptInt0Deps, !IO),
-
                 io.write_strings(DepStream, [
                     "\n\n",
+                    TransOptDateFileName, " ",
                     ErrFileName, " ",
                     CDateFileName, " ",
                     JavaDateFileName, " : "
                 ], !IO),
-                write_dependencies_list(Globals, DepStream, ".trans_opt",
-                    TransOptDeps, !IO)
+
+                % The target (e.g. C) file only depends on the .opt files
+                % from the current directory, so that inter-module optimization
+                % works when the .opt files for the library are unavailable.
+                % This is only necessary because make doesn't allow conditional
+                % dependencies. The dependency on the current module's .opt file
+                % is to make sure the module gets type-checked without having
+                % the definitions of abstract types from other modules.
+                %
+                % XXX The code here doesn't correctly handle dependencies
+                % on `.int' and `.int2' files needed by the `.opt' files.
+                globals.lookup_bool_option(Globals, transitive_optimization,
+                    TransOpt),
+                globals.lookup_bool_option(Globals, use_trans_opt_files,
+                    UseTransOpt),
+
+                ( if
+                    ( TransOpt = yes
+                    ; UseTransOpt = yes
+                    )
+                then
+                    bool.not(UseTransOpt, BuildOptFiles),
+                    get_both_opt_deps(Globals, BuildOptFiles, IntermodDirs,
+                        [ModuleName | set.to_sorted_list(LongDeps)],
+                        OptDeps, TransOptDeps, !IO),
+                    OptInt0Deps = set.union_list(
+                        list.map(get_ancestors_set, OptDeps)),
+                    write_dependencies_list(Globals, DepStream, ".opt",
+                        OptDeps, !IO),
+                    write_dependencies_set(Globals, DepStream, ".int0",
+                        OptInt0Deps, !IO),
+
+                    io.write_strings(DepStream, [
+                        "\n\n",
+                        ErrFileName, " ",
+                        CDateFileName, " ",
+                        JavaDateFileName, " : "
+                    ], !IO),
+                    write_dependencies_list(Globals, DepStream, ".trans_opt",
+                        TransOptDeps, !IO)
+                else
+                    bool.not(UseOptFiles, BuildOptFiles),
+                    get_opt_deps(Globals, BuildOptFiles, IntermodDirs, ".opt",
+                        [ModuleName | set.to_sorted_list(LongDeps)],
+                        OptDeps, !IO),
+                    OptInt0Deps = set.union_list(
+                        list.map(get_ancestors_set, OptDeps)),
+                    write_dependencies_list(Globals, DepStream, ".opt",
+                        OptDeps, !IO),
+                    write_dependencies_set(Globals, DepStream, ".int0",
+                        OptInt0Deps, !IO)
+                )
             else
-                bool.not(UseOptFiles, BuildOptFiles),
-                get_opt_deps(Globals, BuildOptFiles, IntermodDirs, ".opt",
-                    [ModuleName | set.to_sorted_list(LongDeps)], OptDeps, !IO),
-                OptInt0Deps = set.union_list(
-                    list.map(get_ancestors_set, OptDeps)),
-                write_dependencies_list(Globals, DepStream, ".opt",
-                    OptDeps, !IO),
-                write_dependencies_set(Globals, DepStream, ".int0",
-                    OptInt0Deps, !IO)
-            )
-        else
-            true
-        ),
+                true
+            ),
+
+            globals.lookup_bool_option(Globals, highlevel_code, HighLevelCode),
+            globals.get_target(Globals, CompilationTarget),
+            ( if
+                HighLevelCode = yes,
+                CompilationTarget = target_c
+            then
+                % For --high-level-code with --target c, we need to make
+                % sure that we generate the header files for imported
+                % modules before compiling the C files, since the generated
+                % C files #include those header files.
 
-        globals.lookup_bool_option(Globals, highlevel_code, HighLevelCode),
-        globals.get_target(Globals, CompilationTarget),
-        ( if
-            HighLevelCode = yes,
-            CompilationTarget = target_c
-        then
-            % For --high-level-code with --target c, we need to make sure that
-            % we generate the header files for imported modules before
-            % compiling the C files, since the generated C files
-            % #include those header files.
+                io.write_strings(DepStream, [
+                    "\n\n",
+                    PicObjFileName, " ",
+                    ObjFileName, " :"
+                ], !IO),
+                write_dependencies_set(Globals, DepStream, ".mih", AllDeps, !IO)
+            else
+                true
+            ),
 
+            % We need to tell make how to make the header files. The header
+            % files are actually built by the same command that creates the
+            % .c or .s file, so we just make them depend on the .c or .s
+            % files.  This is needed for the --high-level-code rule above,
+            % and for the rules introduced for `:- pragma
+            % foreign_import_module' declarations. In some grades the header
+            % file won't actually be built (e.g. LLDS grades for modules not
+            % containing `:- pragma export' declarations), but this rule
+            % won't do any harm.
+
+            module_name_to_file_name(Globals, ModuleName, ".c",
+                do_not_create_dirs, CFileName, !IO),
+            module_name_to_file_name(Globals, ModuleName, ".mh",
+                do_not_create_dirs, HeaderFileName, !IO),
+            module_name_to_file_name(Globals, ModuleName, ".mih",
+                do_not_create_dirs, HeaderFileName2, !IO),
             io.write_strings(DepStream, [
                 "\n\n",
-                PicObjFileName, " ",
-                ObjFileName, " :"
+                HeaderFileName, " ",  HeaderFileName2, " : ", CFileName, "\n"
             ], !IO),
-            write_dependencies_set(Globals, DepStream, ".mih", AllDeps, !IO)
-        else
-            true
-        ),
-
-        % We need to tell make how to make the header files. The header files
-        % are actually built by the same command that creates the .c or .s
-        % file, so we just make them depend on the .c or .s files.
-        % This is needed for the --high-level-code rule above, and for
-        % the rules introduced for `:- pragma foreign_import_module'
-        % declarations. In some grades the header file won't actually be built
-        % (e.g. LLDS grades for modules not containing `:- pragma export'
-        % declarations), but this rule won't do any harm.
-
-        module_name_to_file_name(Globals, ModuleName, ".c",
-            do_not_create_dirs, CFileName, !IO),
-        module_name_to_file_name(Globals, ModuleName, ".mh",
-            do_not_create_dirs, HeaderFileName, !IO),
-        module_name_to_file_name(Globals, ModuleName, ".mih",
-            do_not_create_dirs, HeaderFileName2, !IO),
-        io.write_strings(DepStream, [
-            "\n\n",
-            HeaderFileName, " ",  HeaderFileName2, " : ", CFileName, "\n"
-        ], !IO),
-
-        % The `.module_dep' file is made as a side effect of
-        % creating the `.c', `.s', or `.java'.
-
-        module_name_to_file_name(Globals, ModuleName, ".java",
-            do_not_create_dirs, JavaFileName, !IO),
-        module_name_to_file_name(Globals, ModuleName,
-            make_module_dep_file_extension, do_not_create_dirs,
-            ModuleDepFileName, !IO),
-        io.write_strings(DepStream, [
-            "\n\n",
-            "ifeq ($(findstring java,$(GRADE)),java)\n",
-            ModuleDepFileName, " : ", JavaFileName, "\n",
-            "else\n",
-            ModuleDepFileName, " : ", CFileName, "\n",
-            "endif\n"
-        ], !IO),
-
-        % The .date and .date0 files depend on the .int0 files for the parent
-        % modules, and the .int3 files for the directly and indirectly imported
-        % modules.
-        %
-        % For nested sub-modules, the `.date' files for the parent modules
-        % also depend on the same things as the `.date' files for this module,
-        % since all the `.date' files will get produced by a single mmc
-        % command. Similarly for `.date0' files, except these don't depend
-        % on the `.int0' files, because when doing the
-        % `--make-private-interface' for nested modules, mmc will process
-        % the modules in outermost to innermost order so as to produce each
-        % `.int0' file before it is needed.
-
-        module_name_to_file_name(Globals, ModuleName, ".date",
-            do_not_create_dirs, DateFileName, !IO),
-        module_name_to_file_name(Globals, ModuleName, ".date0",
-            do_not_create_dirs, Date0FileName, !IO),
-        io.write_strings(DepStream, [
-            "\n\n", DateFileName, " ", Date0FileName
-        ], !IO),
-        write_dependencies_set(Globals, DepStream, ".date", ParentDeps, !IO),
-        io.write_strings(DepStream, [" : ", SourceFileName], !IO),
-        write_dependencies_set(Globals, DepStream, ".int0", ParentDeps, !IO),
-        write_dependencies_set(Globals, DepStream, ".int3", LongDeps, !IO),
-        write_dependencies_set(Globals, DepStream, ".int3", ShortDeps, !IO),
-
-        io.write_strings(DepStream, ["\n\n", Date0FileName], !IO),
-        write_dependencies_set(Globals, DepStream, ".date0", ParentDeps, !IO),
-        io.write_strings(DepStream, [" : ", SourceFileName], !IO),
-        write_dependencies_set(Globals, DepStream, ".int3", LongDeps, !IO),
-        write_dependencies_set(Globals, DepStream, ".int3", ShortDeps, !IO),
-        io.write_string(DepStream, "\n\n", !IO),
-
-        % If we can pass the module name rather than the file name, then do so.
-        % `--smart-recompilation' doesn't work if the file name is passed
-        % and the module name doesn't match the file name.
-
-        have_source_file_map(HaveMap, !IO),
-        (
-            HaveMap = yes,
-            module_name_to_file_name_stem(SourceFileModuleName, ModuleArg)
-        ;
-            HaveMap = no,
-            ModuleArg = SourceFileName
-        ),
 
-        globals.get_target(Globals, Target),
-        module_name_to_file_name(Globals, ModuleName, ".class",
-            do_not_create_dirs, ClassFileName, !IO),
-        module_name_to_file_name(Globals, ModuleName, ".beam",
-            do_not_create_dirs, BeamFileName, !IO),
+            % The `.module_dep' file is made as a side effect of
+            % creating the `.c', `.s', or `.java'.
 
-        (
-            ContainsForeignCode = contains_foreign_code(_LangSet),
-            ForeignImportModules = ForeignImportModules0
-        ;
-            ContainsForeignCode = contains_foreign_code_unknown,
-            get_foreign_code_indicators_from_item_blocks(Globals,
-                SrcItemBlocks,
-                _SrcLangSet, SrcForeignImportModules, _, _),
-            % XXX ITEM_LIST DirectIntItemBlocksCord should not be needed
-            % XXX ITEM_LIST IndirectIntItemBlocksCord should not be needed
-            IntItemBlocksCord =
-                DirectIntItemBlocksCord ++ IndirectIntItemBlocksCord,
-            get_foreign_code_indicators_from_item_blocks(Globals,
-                cord.list(IntItemBlocksCord),
-                _IntLangSet, IntForeignImportModules, _, _),
-            get_foreign_code_indicators_from_item_blocks(Globals,
-                cord.list(OptItemBlocksCord),
-                _OptLangSet, OptForeignImportModules, _, _),
-            get_foreign_code_indicators_from_item_blocks(Globals,
-                cord.list(IntForOptItemBlocksCord),
-                _IntForOptLangSet, IntForOptForeignImportModules, _, _),
-            % If we are generating the `.dep' file, ForeignImportModuless0
-            % will contain a conservative approximation to the set of
-            % foreign imports needed which will include imports
-            % required by imported modules.
-            % XXX ITEM_LIST What is the correctness argument that supports
-            % the above assertion?
-            ( if
-                ForeignImportModules0 = foreign_import_modules(
-                    C0, CSharp0, Java0, Erlang0),
-                set.is_empty(C0),
-                set.is_empty(CSharp0),
-                set.is_empty(Java0),
-                set.is_empty(Erlang0)
-            then
-                SrcForeignImportModules = foreign_import_modules(
-                    SrcC, SrcCSharp, SrcJava, SrcErlang),
-                IntForeignImportModules = foreign_import_modules(
-                    IntC, IntCSharp, IntJava, IntErlang),
-                OptForeignImportModules = foreign_import_modules(
-                    OptC, OptCSharp, OptJava, OptErlang),
-                IntForOptForeignImportModules = foreign_import_modules(
-                    IntForOptC, IntForOptCSharp, IntForOptJava,
-                    IntForOptErlang),
-                C = set.union_list([
-                    SrcC, IntC, OptC, IntForOptC]),
-                CSharp = set.union_list([
-                    SrcCSharp, IntCSharp, OptCSharp, IntForOptCSharp]),
-                Java= set.union_list([
-                    SrcJava, IntJava, OptJava, IntForOptJava]),
-                Erlang = set.union_list([
-                    SrcErlang, IntErlang, OptErlang, IntForOptErlang]),
-                ForeignImportModules = foreign_import_modules(
-                    C, CSharp, Java, Erlang)
-            else
-                ForeignImportModules = ForeignImportModules0
-            )
-        ;
-            ContainsForeignCode = contains_no_foreign_code,
-            ForeignImportModules = ForeignImportModules0
-        ),
-
-        ForeignImports =
-            get_all_foreign_import_module_infos(ForeignImportModules),
+            module_name_to_file_name(Globals, ModuleName, ".java",
+                do_not_create_dirs, JavaFileName, !IO),
+            module_name_to_file_name(Globals, ModuleName,
+                make_module_dep_file_extension, do_not_create_dirs,
+                ModuleDepFileName, !IO),
+            io.write_strings(DepStream, [
+                "\n\n",
+                "ifeq ($(findstring java,$(GRADE)),java)\n",
+                ModuleDepFileName, " : ", JavaFileName, "\n",
+                "else\n",
+                ModuleDepFileName, " : ", CFileName, "\n",
+                "endif\n"
+            ], !IO),
 
-        % Handle dependencies introduced by
-        % `:- pragma foreign_import_module' declarations.
+            % The .date and .date0 files depend on the .int0 files for the
+            % parent modules, and the .int3 files for the directly and
+            % indirectly imported modules.
+            %
+            % For nested sub-modules, the `.date' files for the parent
+            % modules also depend on the same things as the `.date' files
+            % for this module, since all the `.date' files will get produced
+            % by a single mmc command. Similarly for `.date0' files, except
+            % these don't depend on the `.int0' files, because when doing
+            % the `--make-private-interface' for nested modules, mmc will
+            % process the modules in outermost to innermost order so as to
+            % produce each `.int0' file before it is needed.
+
+            module_name_to_file_name(Globals, ModuleName, ".date",
+                do_not_create_dirs, DateFileName, !IO),
+            module_name_to_file_name(Globals, ModuleName, ".date0",
+                do_not_create_dirs, Date0FileName, !IO),
+            io.write_strings(DepStream, [
+                "\n\n", DateFileName, " ", Date0FileName
+            ], !IO),
+            write_dependencies_set(Globals, DepStream, ".date", ParentDeps,
+                !IO),
+            io.write_strings(DepStream, [" : ", SourceFileName], !IO),
+            write_dependencies_set(Globals, DepStream, ".int0", ParentDeps,
+                !IO),
+            write_dependencies_set(Globals, DepStream, ".int3", LongDeps, !IO),
+            write_dependencies_set(Globals, DepStream, ".int3", ShortDeps,
+                !IO),
+
+            io.write_strings(DepStream, ["\n\n", Date0FileName], !IO),
+            write_dependencies_set(Globals, DepStream, ".date0", ParentDeps,
+                !IO),
+            io.write_strings(DepStream, [" : ", SourceFileName], !IO),
+            write_dependencies_set(Globals, DepStream, ".int3", LongDeps, !IO),
+            write_dependencies_set(Globals, DepStream, ".int3", ShortDeps, !IO),
+            io.write_string(DepStream, "\n\n", !IO),
+
+            % If we can pass the module name rather than the file name, then
+            % do so.  `--smart-recompilation' doesn't work if the file name
+            % is passed and the module name doesn't match the file name.
+
+            have_source_file_map(HaveMap, !IO),
+            (
+                HaveMap = yes,
+                module_name_to_file_name_stem(SourceFileModuleName, ModuleArg)
+            ;
+                HaveMap = no,
+                ModuleArg = SourceFileName
+            ),
 
-        set.filter_map(
-            ( pred(ForeignImportMod::in, ImportModuleName::out) is semidet :-
-                ImportModuleName = foreign_import_module_name_from_module(
-                    ForeignImportMod, SourceFileModuleName),
+            globals.get_target(Globals, Target),
+            module_name_to_file_name(Globals, ModuleName, ".class",
+                do_not_create_dirs, ClassFileName, !IO),
+            module_name_to_file_name(Globals, ModuleName, ".beam",
+                do_not_create_dirs, BeamFileName, !IO),
 
-                % XXX We can't include mercury.dll as mmake can't find it,
-                % but we know that it exists.
-                ImportModuleName \= unqualified("mercury")
-            ), ForeignImports, ForeignImportedModuleNames),
-        ( if set.is_empty(ForeignImportedModuleNames) then
-            true
-        else
             (
-                Target = target_csharp,
-                % XXX don't know enough about C# yet
-                ForeignImportTargets = [],
-                ForeignImportExt = ".cs"
-            ;
-                Target = target_java,
-                ForeignImportTargets = [ClassFileName],
-                ForeignImportExt = ".java"
+                ContainsForeignCode = contains_foreign_code(_LangSet),
+                ForeignImportModules = ForeignImportModules0
             ;
-                Target = target_erlang,
-                ForeignImportTargets = [BeamFileName],
-                ForeignImportExt = ".hrl"
+                ContainsForeignCode = contains_foreign_code_unknown,
+                get_foreign_code_indicators_from_item_blocks(Globals,
+                    SrcItemBlocks,
+                    _SrcLangSet, SrcForeignImportModules, _, _),
+                % XXX ITEM_LIST DirectIntItemBlocksCord should not be needed
+                % XXX ITEM_LIST IndirectIntItemBlocksCord should not be needed
+                IntItemBlocksCord =
+                    DirectIntItemBlocksCord ++ IndirectIntItemBlocksCord,
+                get_foreign_code_indicators_from_item_blocks(Globals,
+                    cord.list(IntItemBlocksCord),
+                    _IntLangSet, IntForeignImportModules, _, _),
+                get_foreign_code_indicators_from_item_blocks(Globals,
+                    cord.list(OptItemBlocksCord),
+                    _OptLangSet, OptForeignImportModules, _, _),
+                get_foreign_code_indicators_from_item_blocks(Globals,
+                    cord.list(IntForOptItemBlocksCord),
+                    _IntForOptLangSet, IntForOptForeignImportModules, _, _),
+                % If we are generating the `.dep' file,
+                % ForeignImportModuless0 will contain a conservative
+                % approximation to the set of foreign imports needed which
+                % will include imports required by imported modules.  XXX
+                % ITEM_LIST What is the correctness argument that supports
+                % the above assertion?
+                ( if
+                    ForeignImportModules0 = foreign_import_modules(
+                        C0, CSharp0, Java0, Erlang0),
+                    set.is_empty(C0),
+                    set.is_empty(CSharp0),
+                    set.is_empty(Java0),
+                    set.is_empty(Erlang0)
+                then
+                    SrcForeignImportModules = foreign_import_modules(
+                        SrcC, SrcCSharp, SrcJava, SrcErlang),
+                    IntForeignImportModules = foreign_import_modules(
+                        IntC, IntCSharp, IntJava, IntErlang),
+                    OptForeignImportModules = foreign_import_modules(
+                        OptC, OptCSharp, OptJava, OptErlang),
+                    IntForOptForeignImportModules = foreign_import_modules(
+                        IntForOptC, IntForOptCSharp, IntForOptJava,
+                        IntForOptErlang),
+                    C = set.union_list([
+                        SrcC, IntC, OptC, IntForOptC]),
+                    CSharp = set.union_list([
+                        SrcCSharp, IntCSharp, OptCSharp, IntForOptCSharp]),
+                    Java= set.union_list([
+                        SrcJava, IntJava, OptJava, IntForOptJava]),
+                    Erlang = set.union_list([
+                        SrcErlang, IntErlang, OptErlang, IntForOptErlang]),
+                    ForeignImportModules = foreign_import_modules(
+                        C, CSharp, Java, Erlang)
+                else
+                    ForeignImportModules = ForeignImportModules0
+                )
             ;
-                Target = target_c,
-                % NOTE: for C the possible targets might be a .o file _or_ a
-                % .pic_o file. We need to include dependencies for the latter
-                % otherwise invoking mmake with a <module>.pic_o target will
-                % break.
-                ForeignImportTargets = [ObjFileName, PicObjFileName],
-                ForeignImportExt = ".mh"
+                ContainsForeignCode = contains_no_foreign_code,
+                ForeignImportModules = ForeignImportModules0
             ),
-            WriteForeignImportTarget =
-                ( pred(ForeignImportTarget::in, !.IO::di, !:IO::uo) is det :-
-                    io.write_string(DepStream, "\n\n", !IO),
-                    io.write_string(DepStream, ForeignImportTarget, !IO),
-                    io.write_string(DepStream, " : ", !IO),
-                    write_dependencies_list(Globals, DepStream,
-                        ForeignImportExt,
-                        set.to_sorted_list(ForeignImportedModuleNames), !IO),
-                    io.write_string(DepStream, "\n\n", !IO)
-                ),
-            list.foldl(WriteForeignImportTarget, ForeignImportTargets, !IO)
-        ),
 
-        module_name_to_file_name(Globals, ModuleName, ".int",
-            do_not_create_dirs, IntFileName, !IO),
-        module_name_to_file_name(Globals, ModuleName, ".int2",
-            do_not_create_dirs, Int2FileName, !IO),
-        module_name_to_file_name(Globals, ModuleName, ".int3",
-            do_not_create_dirs, Int3FileName, !IO),
-        module_name_to_file_name(Globals, ModuleName, ".opt",
-            do_not_create_dirs, OptFileName, !IO),
-        module_name_to_file_name(Globals, ModuleName, ".trans_opt",
-            do_not_create_dirs, TransOptFileName, !IO),
-        module_name_to_file_name(Globals, ModuleName, ".date3",
-            do_not_create_dirs, Date3FileName, !IO),
-
-        % We add some extra dependencies to the generated `.d' files, so that
-        % local `.int', `.opt', etc. files shadow the installed versions
-        % properly (e.g. for when you're trying to build a new version
-        % of an installed library). This saves the user from having to add
-        % these explicitly if they have multiple libraries installed
-        % in the same installation hierarchy which aren't independent (e.g.
-        % one uses another). These extra dependencies are necessary due to
-        % the way the combination of search paths and pattern rules
-        % works in Make.
-        %
-        % Be very careful about changing the following rules. The `@:' is a
-        % silent do-nothing command. It is used to force GNU Make to recheck
-        % the timestamp on the target file. (It is a pity that GNU Make
-        % doesn't have a way of handling these sorts of rules in a
-        % nicer manner.)
+            ForeignImports =
+                get_all_foreign_import_module_infos(ForeignImportModules),
 
-        io.write_strings(DepStream, [
-            "\n",
-            Int0FileName, " : ", Date0FileName, "\n",
-            "\t@:\n",
-            IntFileName, " : ", DateFileName, "\n",
-            "\t@:\n",
-            Int2FileName, " : ", DateFileName, "\n",
-            "\t@:\n",
-            Int3FileName, " : ", Date3FileName, "\n",
-            "\t@:\n",
-            OptFileName, " : ", OptDateFileName, "\n",
-            "\t@:\n",
-            TransOptFileName, " : ", TransOptDateFileName, "\n",
-            "\t@:\n"
-        ], !IO),
-
-        globals.lookup_bool_option(Globals, use_subdirs, UseSubdirs),
-        (
-            UseSubdirs = yes,
-            io.nl(DepStream, !IO),
-            list.foldl(
-                write_subdirs_shorthand_rule(Globals, DepStream, ModuleName),
-                [".c", ".$O", ".pic_o", ".java", ".class", ".dll"], !IO)
-        ;
-            UseSubdirs = no
-        ),
+            % Handle dependencies introduced by
+            % `:- pragma foreign_import_module' declarations.
 
-        ( if SourceFileName = default_source_file(ModuleName) then
-            true
-        else
-            % The pattern rules in Mmake.rules won't work, since the source
-            % file name doesn't match the expected source file name for this
-            % module name. This can occur due to just the use of different
-            % source file names, or it can be due to the use of nested modules.
-            % So we need to output hard-coded rules in this case.
-            %
-            % The rules output below won't work in the case of nested modules
-            % with parallel makes, because it will end up invoking the same
-            % command twice (since it produces two output files) at the same
-            % time.
+            set.filter_map(
+                ( pred(ForeignImportMod::in, ImportModuleName::out)
+                        is semidet :-
+                    ImportModuleName = foreign_import_module_name_from_module(
+                        ForeignImportMod, SourceFileModuleName),
+
+                    % XXX We can't include mercury.dll as mmake can't find
+                    % it, but we know that it exists.
+                    ImportModuleName \= unqualified("mercury")
+                ), ForeignImports, ForeignImportedModuleNames),
+            ( if set.is_empty(ForeignImportedModuleNames) then
+                true
+            else
+                (
+                    Target = target_csharp,
+                    % XXX don't know enough about C# yet
+                    ForeignImportTargets = [],
+                    ForeignImportExt = ".cs"
+                ;
+                    Target = target_java,
+                    ForeignImportTargets = [ClassFileName],
+                    ForeignImportExt = ".java"
+                ;
+                    Target = target_erlang,
+                    ForeignImportTargets = [BeamFileName],
+                    ForeignImportExt = ".hrl"
+                ;
+                    Target = target_c,
+                    % NOTE: for C the possible targets might be a .o file
+                    % _or_ a .pic_o file. We need to include dependencies
+                    % for the latter otherwise invoking mmake with a
+                    % <module>.pic_o target will break.
+                    ForeignImportTargets = [ObjFileName, PicObjFileName],
+                    ForeignImportExt = ".mh"
+                ),
+                WriteForeignImportTarget =
+                    ( pred(ForeignImportTarget::in, !.IO::di, !:IO::uo)
+                            is det :-
+                        io.write_string(DepStream, "\n\n", !IO),
+                        io.write_string(DepStream, ForeignImportTarget, !IO),
+                        io.write_string(DepStream, " : ", !IO),
+                        write_dependencies_list(Globals, DepStream,
+                            ForeignImportExt,
+                            set.to_sorted_list(ForeignImportedModuleNames),
+                            !IO),
+                        io.write_string(DepStream, "\n\n", !IO)
+                    ),
+                list.foldl(WriteForeignImportTarget, ForeignImportTargets,
+                    !IO)
+            ),
+
+            module_name_to_file_name(Globals, ModuleName, ".int",
+                do_not_create_dirs, IntFileName, !IO),
+            module_name_to_file_name(Globals, ModuleName, ".int2",
+                do_not_create_dirs, Int2FileName, !IO),
+            module_name_to_file_name(Globals, ModuleName, ".int3",
+                do_not_create_dirs, Int3FileName, !IO),
+            module_name_to_file_name(Globals, ModuleName, ".opt",
+                do_not_create_dirs, OptFileName, !IO),
+            module_name_to_file_name(Globals, ModuleName, ".trans_opt",
+                do_not_create_dirs, TransOptFileName, !IO),
+            module_name_to_file_name(Globals, ModuleName, ".date3",
+                do_not_create_dirs, Date3FileName, !IO),
+
+            % We add some extra dependencies to the generated `.d' files, so
+            % that local `.int', `.opt', etc. files shadow the installed
+            % versions properly (e.g. for when you're trying to build a new
+            % version of an installed library). This saves the user from
+            % having to add these explicitly if they have multiple libraries
+            % installed in the same installation hierarchy which aren't
+            % independent (e.g.  one uses another). These extra dependencies
+            % are necessary due to the way the combination of search paths
+            % and pattern rules works in Make.
             %
-            % Any changes here will require corresponding changes to
-            % scripts/Mmake.rules. See that file for documentation
-            % on these rules.
+            % Be very careful about changing the following rules. The `@:'
+            % is a silent do-nothing command. It is used to force GNU Make
+            % to recheck the timestamp on the target file. (It is a pity
+            % that GNU Make doesn't have a way of handling these sorts of
+            % rules in a nicer manner.)
 
             io.write_strings(DepStream, [
                 "\n",
-                Date0FileName, " : ", SourceFileName, "\n",
-                "\t$(MCPI) $(ALL_GRADEFLAGS) ",
-                    "$(ALL_MCPIFLAGS) ", ModuleArg, "\n",
-                DateFileName, " : ", SourceFileName, "\n",
-                "\t$(MCI) $(ALL_GRADEFLAGS) $(ALL_MCIFLAGS) ",
-                    ModuleArg, "\n",
-                Date3FileName, " : ", SourceFileName, "\n",
-                "\t$(MCSI) $(ALL_GRADEFLAGS) $(ALL_MCSIFLAGS) ",
-                    ModuleArg, "\n",
-                OptDateFileName, " : ", SourceFileName, "\n",
-                "\t$(MCOI) $(ALL_GRADEFLAGS) $(ALL_MCOIFLAGS) ",
-                    ModuleArg, "\n",
-                TransOptDateFileName, " : ", SourceFileName,
-                    "\n",
-                "\t$(MCTOI) $(ALL_GRADEFLAGS) ",
-                    "$(ALL_MCTOIFLAGS) ", ModuleArg, "\n",
-                CDateFileName, " : ", SourceFileName, "\n",
-                "\t$(MCG) $(ALL_GRADEFLAGS) $(ALL_MCGFLAGS) ",
-                    ModuleArg, " $(ERR_REDIRECT)\n",
-                JavaDateFileName, " : ", SourceFileName, "\n",
-                "\t$(MCG) $(ALL_GRADEFLAGS) $(ALL_MCGFLAGS) ",
-                    "--java-only ", ModuleArg,
-                    " $(ERR_REDIRECT)\n"
-            ], !IO)
-        ),
+                Int0FileName, " : ", Date0FileName, "\n",
+                "\t@:\n",
+                IntFileName, " : ", DateFileName, "\n",
+                "\t@:\n",
+                Int2FileName, " : ", DateFileName, "\n",
+                "\t@:\n",
+                Int3FileName, " : ", Date3FileName, "\n",
+                "\t@:\n",
+                OptFileName, " : ", OptDateFileName, "\n",
+                "\t@:\n",
+                TransOptFileName, " : ", TransOptDateFileName, "\n",
+                "\t@:\n"
+            ], !IO),
 
-        io.close_output(DepStream, !IO),
-        io.rename_file(TmpDependencyFileName, DependencyFileName, Result3,
-            !IO),
-        (
-            Result3 = error(_),
-            % On some systems, we need to remove the existing file
-            % first, if any. So try again that way.
-            io.remove_file(DependencyFileName, Result4, !IO),
+            globals.lookup_bool_option(Globals, use_subdirs, UseSubdirs),
             (
-                Result4 = error(Error4),
-                maybe_write_string(Verbose, " failed.\n", !IO),
-                maybe_flush_output(Verbose, !IO),
-                io.error_message(Error4, ErrorMsg),
-                string.append_list(["can't remove file `", DependencyFileName,
-                    "': ", ErrorMsg], Message),
-                report_error(Message, !IO)
+                UseSubdirs = yes,
+                io.nl(DepStream, !IO),
+                list.foldl(
+                    write_subdirs_shorthand_rule(Globals, DepStream,
+                        ModuleName),
+                    [".c", ".$O", ".pic_o", ".java", ".class", ".dll"], !IO)
             ;
-                Result4 = ok,
-                io.rename_file(TmpDependencyFileName, DependencyFileName,
-                    Result5, !IO),
+                UseSubdirs = no
+            ),
+
+            ( if SourceFileName = default_source_file(ModuleName) then
+                true
+            else
+                % The pattern rules in Mmake.rules won't work, since the
+                % source file name doesn't match the expected source file
+                % name for this module name. This can occur due to just the
+                % use of different source file names, or it can be due to
+                % the use of nested modules.  So we need to output
+                % hard-coded rules in this case.
+                %
+                % The rules output below won't work in the case of nested
+                % modules with parallel makes, because it will end up
+                % invoking the same command twice (since it produces two
+                % output files) at the same time.
+                %
+                % Any changes here will require corresponding changes to
+                % scripts/Mmake.rules. See that file for documentation on
+                % these rules.
+
+                io.write_strings(DepStream, [
+                    "\n",
+                    Date0FileName, " : ", SourceFileName, "\n",
+                    "\t$(MCPI) $(ALL_GRADEFLAGS) ",
+                        "$(ALL_MCPIFLAGS) ", ModuleArg, "\n",
+                    DateFileName, " : ", SourceFileName, "\n",
+                    "\t$(MCI) $(ALL_GRADEFLAGS) $(ALL_MCIFLAGS) ",
+                        ModuleArg, "\n",
+                    Date3FileName, " : ", SourceFileName, "\n",
+                    "\t$(MCSI) $(ALL_GRADEFLAGS) $(ALL_MCSIFLAGS) ",
+                        ModuleArg, "\n",
+                    OptDateFileName, " : ", SourceFileName, "\n",
+                    "\t$(MCOI) $(ALL_GRADEFLAGS) $(ALL_MCOIFLAGS) ",
+                        ModuleArg, "\n",
+                    TransOptDateFileName, " : ", SourceFileName,
+                        "\n",
+                    "\t$(MCTOI) $(ALL_GRADEFLAGS) ",
+                        "$(ALL_MCTOIFLAGS) ", ModuleArg, "\n",
+                    CDateFileName, " : ", SourceFileName, "\n",
+                    "\t$(MCG) $(ALL_GRADEFLAGS) $(ALL_MCGFLAGS) ",
+                        ModuleArg, " $(ERR_REDIRECT)\n",
+                    JavaDateFileName, " : ", SourceFileName, "\n",
+                    "\t$(MCG) $(ALL_GRADEFLAGS) $(ALL_MCGFLAGS) ",
+                        "--java-only ", ModuleArg,
+                        " $(ERR_REDIRECT)\n"
+                ], !IO)
+            ),
+
+            io.close_output(DepStream, !IO),
+            io.rename_file(TmpDependencyFileName, DependencyFileName,
+                Result3, !IO),
+            (
+                Result3 = error(_),
+                % On some systems, we need to remove the existing file
+                % first, if any. So try again that way.
+                io.remove_file(DependencyFileName, Result4, !IO),
                 (
-                    Result5 = error(Error5),
+                    Result4 = error(Error4),
                     maybe_write_string(Verbose, " failed.\n", !IO),
                     maybe_flush_output(Verbose, !IO),
-                    io.error_message(Error5, ErrorMsg),
-                    string.append_list(["can't rename file `",
-                        TmpDependencyFileName, "' as `", DependencyFileName,
-                        "': ", ErrorMsg], Message),
+                    io.error_message(Error4, ErrorMsg),
+                    string.append_list(["can't remove file `",
+                        DependencyFileName, "': ", ErrorMsg], Message),
                     report_error(Message, !IO)
                 ;
-                    Result5 = ok,
-                    maybe_write_string(Verbose, " done.\n", !IO)
+                    Result4 = ok,
+                    io.rename_file(TmpDependencyFileName, DependencyFileName,
+                        Result5, !IO),
+                    (
+                        Result5 = error(Error5),
+                        maybe_write_string(Verbose, " failed.\n", !IO),
+                        maybe_flush_output(Verbose, !IO),
+                        io.error_message(Error5, ErrorMsg),
+                        string.append_list(["can't rename file `",
+                            TmpDependencyFileName, "' as `",
+                            DependencyFileName, "': ", ErrorMsg], Message),
+                        report_error(Message, !IO)
+                    ;
+                        Result5 = ok,
+                        maybe_write_string(Verbose, " done.\n", !IO)
+                    )
                 )
+            ;
+                Result3 = ok,
+                maybe_write_string(Verbose, " done.\n", !IO)
             )
-        ;
-            Result3 = ok,
-            maybe_write_string(Verbose, " done.\n", !IO)
         )
     ).
 
diff --git a/deep_profiler/conf.m b/deep_profiler/conf.m
index dc42450..11bdd1c 100644
--- a/deep_profiler/conf.m
+++ b/deep_profiler/conf.m
@@ -82,47 +82,54 @@ server_name(ServerName, !IO) :-
 :- pred server_name_2(string::out, io::di, io::uo) is det.
 
 server_name_2(ServerName, !IO) :-
-    io.make_temp(TmpFile, !IO),
-    hostname_cmd(HostnameCmd),
-    ServerRedirectCmd =
-        string.format("%s > %s", [s(HostnameCmd), s(TmpFile)]),
-    io.call_system(ServerRedirectCmd, Res1, !IO),
-    (
-        Res1 = ok(ResCode),
-        ( if ResCode = 0 then
-            io.open_input(TmpFile, TmpRes, !IO),
-            (
-                TmpRes = ok(TmpStream),
-                io.read_file_as_string(TmpStream, TmpReadRes, !IO),
+    io.make_temp_res(TmpFileResult, !IO),
+    ( TmpFileResult = ok(TmpFile),
+        hostname_cmd(HostnameCmd),
+        ServerRedirectCmd =
+            string.format("%s > %s", [s(HostnameCmd), s(TmpFile)]),
+        io.call_system(ServerRedirectCmd, Res1, !IO),
+        (
+            Res1 = ok(ResCode),
+            ( if ResCode = 0 then
+                io.open_input(TmpFile, TmpRes, !IO),
                 (
-                    TmpReadRes = ok(ServerNameNl),
-                    ( if
-                        string.remove_suffix(ServerNameNl, "\n",
-                            ServerNamePrime)
-                    then
-                        ServerName = ServerNamePrime
-                    else
-                        unexpected($module, $pred, "malformed server name")
-                    )
+                    TmpRes = ok(TmpStream),
+                    io.read_file_as_string(TmpStream, TmpReadRes, !IO),
+                    (
+                        TmpReadRes = ok(ServerNameNl),
+                        ( if
+                            string.remove_suffix(ServerNameNl, "\n",
+                                ServerNamePrime)
+                        then
+                            ServerName = ServerNamePrime
+                        else
+                            unexpected($module, $pred,
+                                "malformed server name")
+                        )
+                    ;
+                        TmpReadRes = error(_, _),
+                        unexpected($module, $pred,
+                            "cannot read server's name")
+                    ),
+                    io.close_input(TmpStream, !IO)
                 ;
-                    TmpReadRes = error(_, _),
-                    unexpected($module, $pred, "cannot read server's name")
+                    TmpRes = error(_),
+                    unexpected($module, $pred,
+                        "cannot open file to find the server's name")
                 ),
-                io.close_input(TmpStream, !IO)
-            ;
-                TmpRes = error(_),
+                io.remove_file(TmpFile, _, !IO)
+            else
                 unexpected($module, $pred,
-                    "cannot open file to find the server's name")
-            ),
-            io.remove_file(TmpFile, _, !IO)
-        else
+                    "cannot execute cmd to find the server's name")
+            )
+        ;
+            Res1 = error(_),
             unexpected($module, $pred,
                 "cannot execute cmd to find the server's name")
         )
     ;
-        Res1 = error(_),
-        unexpected($module, $pred,
-            "cannot execute cmd to find the server's name")
+        TmpFileResult = error(_),
+        unexpected($module, $pred, "Cannot create temporary file")
     ).
 
 :- pred maybe_server_port(maybe(string)::out, io::di, io::uo) is det.
diff --git a/library/io.m b/library/io.m
index 41a3f10..a944075 100644
--- a/library/io.m
+++ b/library/io.m
@@ -1319,46 +1319,54 @@
 % File handling predicates.
 %
 
-    % make_temp(Name, !IO) creates an empty file whose name is different
-    % to the name of any existing file. Name is bound to the name of the file.
-    % It is the responsibility of the caller to delete the file when it
-    % is no longer required.
+    % make_temp(Result, !IO) creates an empty file whose name is different
+    % to the name of any existing file.  If successful Result returns the name
+    % of the file.  It is the responsibility of the caller to delete the file
+    % when it is no longer required.
     %
     % The file is placed in the directory returned by get_temp_directory/3.
     %
-    % Throws an io.error exception if the temporary file could not be created.
-    %
     % On the Erlang and Java backends, this does not attempt to create the file
     % with restrictive permissions (600 on Unix-like systems) and therefore
     % should not be used when security is required.
     %
+:- pred make_temp_res(io.res(string)::out, io::di, io::uo) is det.
+
+    % Like make_temp_res/3 except it throws an io.error exception if the
+    % temporary file could not be created.
+    %
+:- pragma obsolete(make_temp/3).
 :- pred make_temp(string::out, io::di, io::uo) is det.
 
-    % make_temp(Dir, Prefix, Name, !IO) creates an empty file whose
+    % make_temp(Dir, Prefix, Suffix, Result, !IO) creates an empty file whose
     % name is different to the name of any existing file. The file will reside
     % in the directory specified by Dir and will have a prefix using up to
-    % the first 5 characters of Prefix. Name is bound to the name of the
-    % file.  It is the responsibility of the caller to delete the file when it
-    % is no longer required.
-    %
-    % Throws an io.error exception if the temporary file could not be created.
+    % the first 5 characters of Prefix. If successful, Result returns the name
+    % of the file.  It is the responsibility of the caller to delete the file
+    % when it is no longer required.
     %
     % The C# backend has the following limitations:
     %   - Dir is ignored.
     %   - Prefix is ignored.
+    %   - Suffix is ignored.
+    %
+    % On the Erlang backend Suffix is ignored.
     %
     % On the Erlang and Java backends, this does not attempt to create the file
     % with restrictive permissions (600 on Unix-like systems) and therefore
     % should not be used when security is required.
     %
-:- pred make_temp(string::in, string::in, string::out, io::di, io::uo)
-    is det.
+:- pred make_temp_res(string::in, string::in, string::in, io.res(string)::out,
+    io::di, io::uo) is det.
 
-    % make_temp_directory(DirName, !IO) creates an empty directory whose name
-    % is different from the name of any existing directory.
+    % Same as make_temp_res except it does not take a suffix argument and
+    % throws an io.error exception if the temporary file could not be created.
     %
-    % Throws an io.error exception if the temporary directory could not be
-    % created.
+:- pragma obsolete(make_temp/5).
+:- pred make_temp(string::in, string::in, string::out, io::di, io::uo) is det.
+
+    % make_temp_directory(Result, !IO) creates an empty directory whose name
+    % is different from the name of any existing directory.
     %
     % On the C# backend this is insecure as the file permissions are not set
     % and this call does not test for an existing directory.
@@ -1367,31 +1375,29 @@
     %
     % This is unimplemented on the Erlang backend.
     %
-:- pred make_temp_directory(string::out, io::di, io::uo) is det.
+:- pred make_temp_directory(io.res(string)::out, io::di, io::uo) is det.
 
-    % make_temp_directory(Dir, Prefix, DirName, !IO) creates an empty directory
-    % whose name is different from the name of any existing directory.  The new
-    % directory will reside in the existing directory specified by `Dir' and
-    % will have a prefix using up to the first 5 characters of `Prefix'.
-    % DirName is bound to the name of the new directory. It is the
-    % responsibility of the program to delete the directory when it is no
-    % longer needed.
-    %
-    % Throws an io.error exception if the temporary directory could not be
-    % created.
+    % make_temp_directory(Dir, Prefix, Suffix, Result, !IO) creates an empty
+    % directory whose name is different from the name of any existing
+    % directory.  The new directory will reside in the existing directory
+    % specified by Dir and will have a prefix using up to the first 5
+    % characters of Prefix and a Suffix.  Result returns the name of the
+    % new directory. It is the responsibility of the program to delete the
+    % directory when it is no longer needed.
     %
     % The C# backend has the following limitations:
     %   - It does not attempt to create the file with restrictive permissions
     %     (600 on Unix-like systems) and therefore should not be used when
     %     security is required.
     %   - Prefix is ignored.
+    %   - Suffix is ignored.
     %
     % On the Java backend this is insecure as the file permissions are not set.
     %
     % This is unimplemented on the Erlang backend.
     %
-:- pred make_temp_directory(string::in, string::in, string::out,
-    io::di, io::uo) is det.
+:- pred make_temp_directory(string::in, string::in, string::in,
+    io.res(string)::out, io::di, io::uo) is det.
 
     % Test if the make_temp_directory predicates are available.  This is false
     % for the Erlang backends and either C backend without support for
@@ -10399,32 +10405,52 @@ command_line_argument(_, "") :-
 
 %---------------------------------------------------------------------------%
 
-make_temp(Name, !IO) :-
+make_temp_res(Result, !IO) :-
     get_temp_directory(Dir, !IO),
-    make_temp(Dir, "mtmp", Name, !IO).
+    make_temp_res(Dir, "mtmp", "", Result, !IO).
 
-make_temp(Dir, Prefix, Name, !IO) :-
-    do_make_temp(Dir, Prefix, char_to_string(dir.directory_separator),
+make_temp(Name, !IO) :-
+    make_temp_res(Result, !IO),
+    (
+        Result = ok(Name)
+    ;
+        Result = error(Error),
+        throw(Error)
+    ).
+
+make_temp_res(Dir, Prefix, Suffix, Result, !IO) :-
+    do_make_temp(Dir, Prefix, Suffix, char_to_string(dir.directory_separator),
         Name, Okay, Message, !IO),
     (
-        Okay = yes
+        Okay = yes,
+        Result = ok(Name)
     ;
         Okay = no,
-        throw_io_error(Message)
+        Result = error(make_io_error(Message))
     ).
 
-make_temp_directory(DirName, !IO) :-
+make_temp(Dir, Prefix, Name, !IO) :-
+    make_temp_res(Dir, Prefix, "", Result, !IO),
+    (
+        Result = ok(Name)
+    ;
+        Result = error(Error),
+        throw(Error)
+    ).
+
+make_temp_directory(Result, !IO) :-
     get_temp_directory(Dir, !IO),
-    make_temp_directory(Dir, "mtmp", DirName, !IO).
+    make_temp_directory(Dir, "mtmp", "", Result, !IO).
 
-make_temp_directory(Dir, Prefix, DirName, !IO) :-
-    do_make_temp_directory(Dir, Prefix,
+make_temp_directory(Dir, Prefix, Suffix, Result, !IO) :-
+    do_make_temp_directory(Dir, Prefix, Suffix,
         char_to_string(dir.directory_separator), DirName, Okay, Message, !IO),
     (
-        Okay = yes
+        Okay = yes,
+        Result = ok(DirName)
     ;
         Okay = no,
-        throw_io_error(Message)
+        Result = error(make_io_error(Message))
     ).
 
 %-----------------------------------------------------------------------%
@@ -10440,12 +10466,11 @@ import java.util.Random;
 "
     public static Random ML_rand = new Random();
 
-    public static String makeTempName(String prefix)
+    public static String makeTempName(String prefix, String suffix)
     {
         StringBuilder sb = new StringBuilder();
 
         sb.append(prefix);
-
         // Make an 8-digit mixed case alpha-numeric code.
         for (int i = 0; i < 8; i++) {
             char c;
@@ -10460,13 +10485,14 @@ import java.util.Random;
             c = (char)c_num;
             sb.append(c);
         }
+        sb.append(suffix);
 
         return sb.toString();
     }
 ").
 
 :- pred do_make_temp(string::in, string::in, string::in,
-    string::out, bool::out, string::out, io::di, io::uo) is det.
+    string::in, string::out, bool::out, string::out, io::di, io::uo) is det.
 
 % XXX The code for io.make_temp assumes POSIX. It uses the functions open(),
 % close(), and getpid() and the macros EEXIST, O_WRONLY, O_CREAT, and O_EXCL.
@@ -10491,7 +10517,7 @@ import java.util.Random;
 ").
 
 :- pragma foreign_proc("C",
-    do_make_temp(Dir::in, Prefix::in, Sep::in, FileName::out,
+    do_make_temp(Dir::in, Prefix::in, Suffix::in, Sep::in, FileName::out,
         Okay::out, ErrorMessage::out, _IO0::di, _IO::uo),
     [will_not_call_mercury, promise_pure, tabled_for_io,
         does_not_affect_liveness],
@@ -10499,8 +10525,8 @@ import java.util.Random;
 #ifdef MR_HAVE_MKSTEMP
     int err, fd;
 
-    FileName = MR_make_string(MR_ALLOC_ID, ""%s%s%.5sXXXXXX"",
-        Dir, Sep, Prefix);
+    FileName = MR_make_string(MR_ALLOC_ID, ""%s%s%.5sXXXXXX%s"",
+        Dir, Sep, Prefix, Suffix);
     fd = mkstemp(FileName);
     if (fd == -1) {
         ML_maybe_make_err_msg(MR_TRUE, errno,
@@ -10519,18 +10545,18 @@ import java.util.Random;
 #else
     /*
     ** Constructs a temporary name by concatenating Dir, `/', the first 5 chars
-    ** of Prefix, three hex digits, '.', and 3 more hex digits. The six digit
-    ** hex number is generated by starting with the pid of this process.
-    ** Uses `open(..., O_CREATE | O_EXCL, ...)' to create the file, checking
-    ** that there was no existing file with that name.
+    ** of Prefix, six hex digits, and Suffix. The six digit hex number is
+    ** generated by starting with the pid of this process.  Uses
+    ** `open(..., O_CREATE | O_EXCL, ...)' to create the file, checking that
+    ** there was no existing file with that name.
     */
     int     len, err, fd, num_tries;
     char    countstr[256];
     MR_Word filename_word;
     int     flags;
 
-    len = strlen(Dir) + 1 + 5 + 3 + 1 + 3 + 1;
-    /* Dir + / + Prefix + counter_high + . + counter_low + \\0 */
+    len = strlen(Dir) + 1 + 5 + 6 + strlen(Suffix) + 1;
+    /* Dir + / + Prefix + counter + Suffix + \\0 */
     MR_offset_incr_hp_atomic_msg(filename_word, 0,
         (len + sizeof(MR_Word)) / sizeof(MR_Word),
         MR_ALLOC_ID, ""string.string/0"");
@@ -10544,9 +10570,8 @@ import java.util.Random;
         strcpy(FileName, Dir);
         strcat(FileName, Sep);
         strncat(FileName, Prefix, 5);
-        strncat(FileName, countstr, 3);
-        strcat(FileName, ""."");
-        strncat(FileName, countstr + 3, 3);
+        strncat(FileName, countstr, 6);
+        strcat(FileName, Suffix);
         flags = O_WRONLY | O_CREAT | O_EXCL;
         do {
             #ifdef MR_WIN32
@@ -10577,7 +10602,7 @@ import java.util.Random;
 ").
 
 :- pragma foreign_proc("C#",
-    do_make_temp(_Dir::in, _Prefix::in, _Sep::in, FileName::out,
+    do_make_temp(_Dir::in, _Prefix::in, _Suffix::in, _Sep::in, FileName::out,
         Okay::out, ErrorMessage::out, _IO0::di, _IO::uo),
     [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
 "{
@@ -10594,11 +10619,8 @@ import java.util.Random;
     }
 }").
 
-% For the Java implementation, io.make_temp/3 is overwritten directly,
-% since Java is capable of locating the default temp directory itself.
-
 :- pragma foreign_proc("Java",
-    do_make_temp(Dir::in, Prefix::in, _Sep::in, FileName::out,
+    do_make_temp(Dir::in, Prefix::in, Suffix::in, _Sep::in, FileName::out,
         Okay::out, ErrorMessage::out, _IO0::di, _IO::uo),
     [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
         may_not_duplicate],
@@ -10612,7 +10634,7 @@ import java.util.Random;
             Prefix = Prefix.substring(0, 5);
         }
 
-        new_file = new File(new File(Dir), makeTempName(Prefix));
+        new_file = new File(new File(Dir), makeTempName(Prefix, Suffix));
         if (new_file.createNewFile()) {
             FileName = new_file.getAbsolutePath();
             Okay = bool.YES;
@@ -10630,13 +10652,14 @@ import java.util.Random;
 ").
 
 :- pragma foreign_proc("Erlang",
-    do_make_temp(Dir::in, Prefix::in, Sep::in, FileName::out,
+    do_make_temp(Dir::in, Prefix::in, Suffix::in, Sep::in, FileName::out,
         Okay::out, ErrorMessage::out, _IO0::di, _IO::uo),
     [will_not_call_mercury, promise_pure, tabled_for_io,
         does_not_affect_liveness],
 "
     DirStr = binary_to_list(Dir),
     PrefixStr = binary_to_list(Prefix),
+    SuffixStr = binary_to_list(Suffix),
     SepStr = binary_to_list(Sep),
 
     % Constructs a temporary name by concatenating Dir, Sep, Prefix
@@ -10658,7 +10681,7 @@ import java.util.Random;
     Seed = {A1 + Pid, A2, A3},
 
     case
-        mercury__io:'ML_do_make_temp_2'(DirStr, PrefixStr, SepStr,
+        mercury__io:'ML_do_make_temp_2'(DirStr, PrefixStr, SuffixStr, SepStr,
             MaxTries, Seed)
     of
         {ok, FileName0} ->
@@ -10673,19 +10696,19 @@ import java.util.Random;
 ").
 
 :- pragma foreign_decl("Erlang", local, "
-    -export(['ML_do_make_temp_2'/5]).
+    -export(['ML_do_make_temp_2'/6]).
 ").
 :- pragma foreign_code("Erlang", "
-    'ML_do_make_temp_2'(_, _, _, 0, _) ->
+    'ML_do_make_temp_2'(_, _, _, _, 0, _) ->
         {error, ""error opening temporary file""};
-    'ML_do_make_temp_2'(Dir, Prefix, Sep, Tries, Seed0) ->
+    'ML_do_make_temp_2'(Dir, Prefix, Suffix, Sep, Tries, Seed0) ->
         {R1, Seed1} = random:uniform_s(16#1000, Seed0),
         {R2, Seed}  = random:uniform_s(16#1000, Seed1),
-        FileName = lists:flatten(io_lib:format(""~s~s~s~3.16.0B.~3.16.0B"",
-            [Dir, Sep, Prefix, R1, R2])),
+        FileName = lists:flatten(io_lib:format(""~s~s~s~3.16.0B.~3.16.0B~s"",
+            [Dir, Sep, Prefix, R1, R2, Suffix])),
         case filelib:is_file(FileName) of
             true ->
-                'ML_do_make_temp_2'(Dir, Prefix, Sep, Tries - 1, Seed);
+                'ML_do_make_temp_2'(Dir, Prefix, Suffix, Sep, Tries - 1, Seed);
             false ->
                 case file:open(FileName, [write, {encoding, utf8}]) of
                     {ok, IoDevice} ->
@@ -10696,27 +10719,28 @@ import java.util.Random;
                                 {error, file:format_error(Reason)}
                         end;
                     {error, _} ->
-                        'ML_do_make_temp_2'(Dir, Prefix, Sep, Tries - 1, Seed)
+                        'ML_do_make_temp_2'(Dir, Prefix, Suffix, Sep,
+                            Tries - 1, Seed)
                 end
         end.
 ").
 
 %-----------------------------------------------------------------------%
 
-:- pred do_make_temp_directory(string::in, string::in, string::in,
+:- pred do_make_temp_directory(string::in, string::in, string::in, string::in,
     string::out, bool::out, string::out, io::di, io::uo) is det.
 
 :- pragma foreign_proc("C",
-    do_make_temp_directory(Dir::in, Prefix::in, Sep::in, DirName::out,
-        Okay::out, ErrorMessage::out, _IO0::di, _IO::uo),
+    do_make_temp_directory(Dir::in, Prefix::in, Suffix::in, Sep::in,
+        DirName::out, Okay::out, ErrorMessage::out, _IO0::di, _IO::uo),
     [will_not_call_mercury, promise_pure, tabled_for_io,
         does_not_affect_liveness],
 "
 #ifdef MR_HAVE_MKDTEMP
     int err;
 
-    DirName = MR_make_string(MR_ALLOC_ID, ""%s%s%.5sXXXXXX"",
-        Dir, Sep, Prefix);
+    DirName = MR_make_string(MR_ALLOC_ID, ""%s%s%.5sXXXXXX%s"",
+        Dir, Sep, Prefix, Suffix);
     DirName = mkdtemp(DirName);
     if (DirName == NULL) {
         ML_maybe_make_err_msg(MR_TRUE, errno,
@@ -10737,8 +10761,8 @@ import java.util.Random;
 ").
 
 :- pragma foreign_proc("C#",
-    do_make_temp_directory(Dir::in, _Prefix::in, _Sep::in, DirName::out,
-        Okay::out, ErrorMessage::out, _IO0::di, _IO::uo),
+    do_make_temp_directory(Dir::in, _Prefix::in, _Suffix::in, _Sep::in,
+        DirName::out, Okay::out, ErrorMessage::out, _IO0::di, _IO::uo),
     [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
 "{
     try {
@@ -10761,8 +10785,8 @@ import java.util.Random;
 }").
 
 :- pragma foreign_proc("Java",
-    do_make_temp_directory(Dir::in, Prefix::in, _Sep::in, DirName::out,
-        Okay::out, ErrorMessage::out, _IO0::di, _IO::uo),
+    do_make_temp_directory(Dir::in, Prefix::in, Suffix::in, _Sep::in,
+        DirName::out, Okay::out, ErrorMessage::out, _IO0::di, _IO::uo),
     [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
         may_not_duplicate],
 "
@@ -10774,7 +10798,7 @@ import java.util.Random;
         Prefix = Prefix.substring(0, 5);
     }
 
-    new_dir = new File(new File(Dir), makeTempName(Prefix));
+    new_dir = new File(new File(Dir), makeTempName(Prefix, Suffix));
     if (new_dir.mkdir()) {
         DirName = new_dir.getAbsolutePath();
         Okay = bool.YES;
-- 
2.8.0.rc3



More information about the reviews mailing list