[m-rev.] for review: fix problems with --use-grade-subdirs with C# and Java backends

Julien Fischer jfischer at opturion.com
Fri May 29 15:53:17 AEST 2015


For review by anyone.

------------------------------

Fix problems with --use-grade-subdirs with C# and Java backends.

Fix problems that occur when generating executables in the C# and Java grades
with --use-grade-subdirs enabled, namely:

* the compiler aborts when it tries to build the launcher script for a Java
grade executable.
* the compiler does not place generated Java archive files under the Mercury
subdirectory.
* in C# grades, the compiler always places the launcher script in the user's
directory rather than under the Mercury subdirectory.

The above problems were mainly caused by the "link" step for executables being
incorrect for the C# and Java backends.  In other grades, the link step
consists of:

1. use the linker to create the executable in C grades, or generate a launcher
script in the Erlang grade.  This executable or script is placed under the
Mercury subdirectory.

2. create a symlink (or copy if --no-use-symlinks is enabled) the executable
or script into the user's directory.

This does not work for the C# and Java backends since their principle "link
target" is not the launcher script (as is the case with the Erlang backend),
but a ".exe" or ".jar" file.  The launcher script is generated as a side-effect
of creating that.   (This has long been the case for the C# backend and has
recently become the case for the Java backend since we moved to generating
archives for executables.)  The correct sequence of "linking" steps for these
two backends should be:

1. create the .exe or .jar under the Mercury subdirectory.
2. create the launcher script file, if required, under the Mercury subdirectory.
3. symlink or copy the .exe or .jar file to the user's directory.
4. symlink or copy the launcher script to the user's directory, if required.

This diff makes the compiler carry out the latter sequence of steps for the C#
and Java backends.

compiler/compile_target_code.m:
 	Make the executable target for the Java backend work more like that of
 	the C# backend.  Merge it with the code for handling library targets in the
 	Java backend since, apart from the creation of the launcher script, it is
 	identical.

 	We may need to do multiple create symlink or copy operations when
 	creating an executable using the C# or Java backends: one for the
 	".exe" or".jar" and another for the launcher script.

 	Separate out the code for determining the launcher script extension into
 	a new function.

compiler/make.util.m:
 	Conform to the above change.

 	Add an XXX comment about the Erlang backend; I'll look into this
 	separately.

compiler/file_names.m:
 	Update the list of architecture or grade dependent files to include those
 	with the extensions ".jar" and ".bat", and also those files with no
 	extension at all.  (For Mercury, the latter will either be launcher scripts
 	or executables.)   Not covering these cases had a number of untoward
 	effects: ".jar" files were not placed in grade specific directories and the
 	launcher script in the C# grade was always placed in the user's directory.

compiler/module_cmds.m:
 	Create any required subdirectories under the Mercury directory when
 	generating launcher scripts.

 	If --verbose-commands is enabled, then print a message when creating
 	a symlink or copying a file.  I needed this when debugging the above
 	problems.

 	Use io.format/4 to generate an error message rather than multiple
 	calls to io.write_string/3.

Julien.

diff --git a/compiler/compile_target_code.m b/compiler/compile_target_code.m
index 4a82e8c..c710fcf 100644
--- a/compiler/compile_target_code.m
+++ b/compiler/compile_target_code.m
@@ -1752,13 +1752,11 @@ link(ErrorStream, LinkTargetType, ModuleName, ObjectsList, Globals, Succeeded,
          create_csharp_exe_or_lib(Globals, ErrorStream, LinkTargetType,
              ModuleName, OutputFileName, ObjectsList, LinkSucceeded, !IO)
      ;
-        LinkTargetType = java_executable,
-        create_java_executable(Globals, ErrorStream, ModuleName,
-            OutputFileName, ObjectsList, LinkSucceeded, !IO)
-    ;
-        LinkTargetType = java_archive,
-        create_java_archive(Globals, ErrorStream, OutputFileName, ObjectsList,
-            LinkSucceeded, !IO)
+        ( LinkTargetType = java_executable
+        ; LinkTargetType = java_archive
+        ),
+        create_java_exe_or_lib(Globals, ErrorStream, LinkTargetType,
+            ModuleName, OutputFileName, ObjectsList, LinkSucceeded, !IO)
      ;
          LinkTargetType = erlang_launcher,
          create_erlang_shell_script(Globals, ModuleName, LinkSucceeded, !IO)
@@ -1809,21 +1807,7 @@ link_output_filename(Globals, LinkTargetType, ModuleName, Ext, OutputFileName,
              do_create_dirs, OutputFileName, !IO)
      ;
          LinkTargetType = erlang_launcher,
-        % These may be shell scripts or batch files.
-        globals.get_target_env_type(Globals, TargetEnvType),
-        (
-            % XXX we should actually generate a .ps1 file for PowerShell.
-            ( TargetEnvType = env_type_win_cmd
-            ; TargetEnvType = env_type_powershell
-            ),
-            Ext = ".bat"
-        ;
-            ( TargetEnvType = env_type_posix
-            ; TargetEnvType = env_type_cygwin
-            ; TargetEnvType = env_type_msys
-            ),
-            Ext = ""
-        ),
+        Ext = get_launcher_script_extension(Globals),
          module_name_to_file_name(Globals, ModuleName, Ext,
              do_create_dirs, OutputFileName, !IO)
      ;
@@ -1843,6 +1827,24 @@ link_output_filename(Globals, LinkTargetType, ModuleName, Ext, OutputFileName,
              do_create_dirs, OutputFileName, !IO)
      ).

+:- func get_launcher_script_extension(globals) = string.
+
+get_launcher_script_extension(Globals) = Ext :-
+    globals.get_target_env_type(Globals, TargetEnvType),
+    (
+        % XXX we should actually generate a .ps1 file for PowerShell.
+        ( TargetEnvType = env_type_win_cmd
+        ; TargetEnvType = env_type_powershell
+        ),
+        Ext = ".bat"
+    ;
+        ( TargetEnvType = env_type_posix
+        ; TargetEnvType = env_type_cygwin
+        ; TargetEnvType = env_type_msys
+        ),
+        Ext = ""
+    ).
+
  :- pred link_exe_or_shared_lib(globals::in, io.output_stream::in,
      linked_target_type::in(bound(executable ; shared_library)),
      module_name::in, file_name::in, list(string)::in, bool::out,
@@ -2589,7 +2591,7 @@ post_link_make_symlink_or_copy(ErrorStream, LinkTargetType, ModuleName,
          same_timestamp(OutputFileName, UserDirFileName, SameTimestamp, !IO),
          (
              SameTimestamp = yes,
-            Succeeded = yes,
+            Succeeded0 = yes,
              MadeSymlinkOrCopy = no
          ;
              SameTimestamp = no,
@@ -2602,13 +2604,54 @@ post_link_make_symlink_or_copy(ErrorStream, LinkTargetType, ModuleName,
              % (on systems on which symbolic links are not available).
              ( if LinkTargetType = erlang_archive then
                  make_symlink_or_copy_dir(Globals, OutputFileName,
-                    UserDirFileName, Succeeded, !IO)
-              else
+                    UserDirFileName, Succeeded0, !IO)
+            else
                  make_symlink_or_copy_file(Globals, OutputFileName,
-                    UserDirFileName, Succeeded, !IO)
+                    UserDirFileName, Succeeded0, !IO)
              ),
              io.set_output_stream(OutputStream, _, !IO),
              MadeSymlinkOrCopy = yes
+        ),
+
+        % For the Java and C# grades we also need to symlink or copy the
+        % launcher scripts or batch files.
+        ( if
+            Succeeded0 = yes,
+            (
+                LinkTargetType = csharp_executable,
+                % NOTE: we don't generate a launcher script for C# executables
+                % on Windows -- it isn't necessary since they can be executed
+                % directly.
+                globals.get_target_env_type(Globals, TargetEnvType),
+                TargetEnvType = env_type_posix
+            ;
+                LinkTargetType = java_executable
+            )
+        then
+            ScriptExt = get_launcher_script_extension(Globals),
+            module_name_to_file_name(Globals, ModuleName, "",
+                do_not_create_dirs, OutputScriptName0, !IO),
+            OutputScriptName = OutputScriptName0 ++ ScriptExt,
+            module_name_to_file_name(NoSubdirGlobals, ModuleName, "",
+                do_not_create_dirs, UserDirScriptName0, !IO),
+            UserDirScriptName = UserDirScriptName0 ++ ScriptExt,
+
+            same_timestamp(OutputScriptName, UserDirScriptName,
+                ScriptSameTimestamp, !IO),
+            (
+                ScriptSameTimestamp = yes,
+                Succeeded = yes
+            ;
+                ScriptSameTimestamp = no,
+                io.set_output_stream(ErrorStream, ScriptOutputStream, !IO),
+                % Remove the target of the symlink/copy in case it already exists.
+                io.remove_file_recursively(UserDirScriptName, _, !IO),
+                make_symlink_or_copy_file(Globals, OutputScriptName,
+                    UserDirScriptName, Succeeded, !IO),
+                io.set_output_stream(ScriptOutputStream, _, !IO)
+            )
+        else
+            Succeeded = Succeeded0
          )
      ;
          UseGradeSubdirs = no,
@@ -3035,27 +3078,13 @@ write_cli_shell_script(Globals, ExeFileName, Stream, !IO) :-
  % Create Java "executables" or archives.
  %

-:- pred create_java_executable(globals::in, io.text_output_stream::in,
+:- pred create_java_exe_or_lib(globals::in, io.text_output_stream::in,
+    linked_target_type::in,
      module_name::in, file_name::in, list(file_name)::in, bool::out,
      io::di, io::uo) is det.

-create_java_executable(Globals, ErrorStream, MainModuleName, JarFileName,
-        ObjectList, Succeeded, !IO) :-
-    create_java_archive(Globals, ErrorStream, JarFileName, ObjectList,
-        CreateJarSucceeded, !IO),
-    (
-        CreateJarSucceeded = yes,
-        create_java_shell_script(Globals, MainModuleName, Succeeded, !IO)
-    ;
-        CreateJarSucceeded = no,
-        Succeeded = no
-    ).
-
-:- pred create_java_archive(globals::in, io.output_stream::in, file_name::in,
-    list(file_name)::in, bool::out, io::di, io::uo) is det.
-
-create_java_archive(Globals, ErrorStream, JarFileName, ObjectList, Succeeded,
-        !IO) :-
+create_java_exe_or_lib(Globals, ErrorStream, LinkTargetType, MainModuleName,
+        JarFileName, ObjectList, Succeeded, !IO) :-
      globals.lookup_string_option(Globals, java_archive_command, Jar),

      list_class_files_for_jar(Globals, ObjectList, ClassSubDir, ListClassFiles,
@@ -3083,17 +3112,17 @@ create_java_archive(Globals, ErrorStream, JarFileName, ObjectList, Succeeded,
          Cmd = string.append_list(
              [Jar, " cf ", JarFileName, " @", TempFileName]),
          invoke_system_command(Globals, ErrorStream, cmd_verbose_commands, Cmd,
-            Succeeded, !IO),
+            Succeeded0, !IO),
          io.remove_file(TempFileName, _, !IO),

          (
-            Succeeded = yes,
+            Succeeded0 = yes,
              % Add an index, which is supposed to speed up class loading.
              IndexCmd = string.append_list([Jar, " i ", JarFileName]),
              invoke_system_command(Globals, ErrorStream, cmd_verbose_commands,
                  IndexCmd, _, !IO)
          ;
-            Succeeded = no,
+            Succeeded0 = no,
              io.remove_file(JarFileName, _, !IO)
          )
      ;
@@ -3101,7 +3130,16 @@ create_java_archive(Globals, ErrorStream, JarFileName, ObjectList, Succeeded,
          io.error_message(Error, ErrorMsg),
          io.format(ErrorStream, "Error creating `%s': %s\n",
              [s(TempFileName), s(ErrorMsg)], !IO),
-        Succeeded = no
+        Succeeded0 = no
+    ),
+    ( if
+        Succeeded0 = yes,
+        LinkTargetType = java_executable
+    then
+        create_java_shell_script(Globals, MainModuleName,
+            Succeeded, !IO)
+    else
+        Succeeded = Succeeded0
      ).

  :- pred write_jar_class_argument(io.output_stream::in, string::in, string::in,
diff --git a/compiler/file_names.m b/compiler/file_names.m
index 50b515c..fb8ab1f 100644
--- a/compiler/file_names.m
+++ b/compiler/file_names.m
@@ -524,6 +524,8 @@ file_is_arch_or_grade_dependent(Globals, Ext0) :-
      % The `.used' file isn't grade dependent itself, but it contains
      % information collected while compiling a grade-dependent `.c', `il',
      % etc file.
+file_is_arch_or_grade_dependent_2("").
+file_is_arch_or_grade_dependent_2(".bat").
  file_is_arch_or_grade_dependent_2(".used").
  file_is_arch_or_grade_dependent_2(".opt").
  file_is_arch_or_grade_dependent_2(".optdate").
@@ -549,6 +551,7 @@ file_is_arch_or_grade_dependent_2(".cs").
  file_is_arch_or_grade_dependent_2(".cs_date").
  file_is_arch_or_grade_dependent_2(".java").
  file_is_arch_or_grade_dependent_2(".java_date").
+file_is_arch_or_grade_dependent_2(".jar").
  file_is_arch_or_grade_dependent_2(".class").
  file_is_arch_or_grade_dependent_2(".erl").
  file_is_arch_or_grade_dependent_2(".erl_date").
diff --git a/compiler/make.util.m b/compiler/make.util.m
index c6bcf7b..b78f6db 100644
--- a/compiler/make.util.m
+++ b/compiler/make.util.m
@@ -1516,14 +1516,16 @@ linked_target_file_name(Globals, ModuleName, TargetType, FileName, !IO) :-
          module_name_to_file_name(Globals, ModuleName, ".dll",
              do_not_create_dirs, FileName, !IO)
      ;
-        ( TargetType = java_executable
-        ; TargetType = erlang_launcher
-        ),
+        TargetType = erlang_launcher,
          % These are shell scripts.
+        % XXX Shouldn't the extension be ".bat" when --target-env-type
+        % is windows?
          module_name_to_file_name(Globals, ModuleName, "",
              do_not_create_dirs, FileName, !IO)
      ;
-        TargetType = java_archive,
+        ( TargetType = java_archive
+        ; TargetType = java_executable
+        ),
          module_name_to_file_name(Globals, ModuleName, ".jar",
              do_not_create_dirs, FileName, !IO)
      ;
diff --git a/compiler/module_cmds.m b/compiler/module_cmds.m
index 2b67abc..72fff72 100644
--- a/compiler/module_cmds.m
+++ b/compiler/module_cmds.m
@@ -426,13 +426,30 @@ maybe_make_symlink(Globals, LinkTarget, LinkName, Result, !IO) :-
  make_symlink_or_copy_file(Globals, SourceFileName, DestinationFileName,
          Succeeded, !IO) :-
      globals.lookup_bool_option(Globals, use_symlinks, UseSymLinks),
+    globals.lookup_bool_option(Globals, verbose_commands, PrintCommand),
      (
          UseSymLinks = yes,
          LinkOrCopy = "linking",
+        (
+            PrintCommand = yes,
+            io.format("%% Linking file `%s' -> `%s'\n",
+                [s(SourceFileName), s(DestinationFileName)], !IO),
+            io.flush_output(!IO)
+        ;
+            PrintCommand = no
+        ),
          io.make_symlink(SourceFileName, DestinationFileName, Result, !IO)
      ;
          UseSymLinks = no,
          LinkOrCopy = "copying",
+        (
+            PrintCommand = yes,
+            io.format("%% Copying file `%s' -> `%s'\n",
+                [s(SourceFileName), s(DestinationFileName)], !IO),
+            io.flush_output(!IO)
+        ;
+            PrintCommand = no
+        ),
          copy_file(Globals, SourceFileName, DestinationFileName, Result, !IO)
      ),
      (
@@ -442,16 +459,10 @@ make_symlink_or_copy_file(Globals, SourceFileName, DestinationFileName,
          Result = error(Error),
          Succeeded = no,
          io.progname_base("mercury_compile", ProgName, !IO),
-        io.write_string(ProgName, !IO),
-        io.write_string(": error ", !IO),
-        io.write_string(LinkOrCopy, !IO),
-        io.write_string(" `", !IO),
-        io.write_string(SourceFileName, !IO),
-        io.write_string("' to `", !IO),
-        io.write_string(DestinationFileName, !IO),
-        io.write_string("': ", !IO),
-        io.write_string(io.error_message(Error), !IO),
-        io.nl(!IO),
+        io.error_message(Error, ErrorMsg),
+        io.format("%s: error %s `%s' to `%s', %s\n",
+            [s(ProgName), s(LinkOrCopy), s(SourceFileName),
+             s(DestinationFileName), s(ErrorMsg)], !IO),
          io.flush_output(!IO)
      ).

@@ -1142,7 +1153,7 @@ pa_option(BreakLines, Quote, Dir0) = Option :-
  create_launcher_shell_script(Globals, MainModuleName, Pred, Succeeded, !IO) :-
      Extension = "",
      module_name_to_file_name(Globals, MainModuleName, Extension,
-        do_not_create_dirs, FileName, !IO),
+        do_create_dirs, FileName, !IO),

      globals.lookup_bool_option(Globals, verbose, Verbose),
      maybe_write_string(Verbose, "% Generating shell script `" ++
@@ -1181,7 +1192,7 @@ create_launcher_shell_script(Globals, MainModuleName, Pred, Succeeded, !IO) :-
  create_launcher_batch_file(Globals, MainModuleName, Pred, Succeeded, !IO) :-
      Extension = ".bat",
      module_name_to_file_name(Globals, MainModuleName, Extension,
-        do_not_create_dirs, FileName, !IO),
+        do_create_dirs, FileName, !IO),

      globals.lookup_bool_option(Globals, verbose, Verbose),
      maybe_write_string(Verbose, "% Generating batch file `" ++



More information about the reviews mailing list