[m-rev.] [for review 1/4] Add mfilterjavac utility

Paul Bone paul at bone.id.au
Thu Apr 18 22:55:39 AEST 2013


For review by Julien.

Julien and I have already discussed the naming of the new directory.

---

Add mfilterjavac utility

Add the mfilterjavac utility to filter javac's output and re-write the error
locations.

mfilterjavac/mfilterjavac.m:
    As above

Mmakefile:
configure.ac:
mfilterjavac/Mmakefile
mfilterjavac/MFILTERJAVAC_FLAGS.in:
    Adjust the build system to support the new mfilterjavac directory.
---
 Mmakefile                          |  27 +++-
 configure.ac                       |   1 +
 mfilterjavac/MFILTERJAVAC_FLAGS.in |  25 +++
 mfilterjavac/Mmakefile             | 152 +++++++++++++++++++
 mfilterjavac/mfilterjavac.m        | 301 +++++++++++++++++++++++++++++++++++++
 5 files changed, 502 insertions(+), 4 deletions(-)
 create mode 100644 mfilterjavac/MFILTERJAVAC_FLAGS.in
 create mode 100644 mfilterjavac/Mmakefile
 create mode 100644 mfilterjavac/mfilterjavac.m

diff --git a/Mmakefile b/Mmakefile
index 3d99a0a..7e24a8a 100644
--- a/Mmakefile
+++ b/Mmakefile
@@ -45,7 +45,8 @@ SUBDIRS = \
 		slice \
 		profiler \
 		deep_profiler \
-		tools
+		tools \
+		mfilterjavac
 
 MMAKEFLAGS =
 
@@ -86,7 +87,8 @@ dep:	dep_library \
 	dep_compiler \
 	dep_slice \
 	dep_profiler \
-	dep_deep_profiler
+	dep_deep_profiler \
+	dep_mfilterjavac
 
 .PHONY: dep_library
 dep_library: library/$(deps_subdir)$(STD_LIB_NAME).dep
@@ -171,6 +173,13 @@ deep_profiler/$(deps_subdir)mdprof_procrep.dep: \
 		library/$(deps_subdir)$(STD_LIB_NAME).dep
 	+cd deep_profiler && $(SUBDIR_MMAKE) depend
 
+.PHONY: dep_mfilterjavac
+dep_mfilterjavac : mfilterjavac/$(deps_subdir)mfilterjavac.dep
+
+mfilterjavac/$(deps_subdir)mfilterjavac.dep: \
+		library/$(deps_subdir)$(STD_LIB_NAME).dep
+	+cd mfilterjavac && $(SUBDIR_MMAKE) depend
+
 # depend_library MUST be done before depend_compiler and depend_profiler
 
 .PHONY: depend
@@ -181,7 +190,8 @@ depend: depend_library \
 	depend_compiler \
 	depend_slice \
 	depend_profiler \
-	depend_deep_profiler
+	depend_deep_profiler \
+	depend_mfilterjavac
 
 .PHONY: depend_library
 depend_library:
@@ -215,6 +225,10 @@ depend_profiler:
 depend_deep_profiler:
 	+cd deep_profiler && $(SUBDIR_MMAKE) depend
 
+.PHONY: depend_mfilterjavac
+depend_mfilterjavac:
+	+cd mfilterjavac && $(SUBDIR_MMAKE) depend
+
 #-----------------------------------------------------------------------------#
 
 # NOTE: there are two targets that concern the util directory here.  The first
@@ -233,7 +247,7 @@ util_no_rt: scripts
 	+cd util && $(SUBDIR_MMAKE) mfiltercc$(EXT_FOR_EXE)
 
 .PHONY: util
-util: 	scripts runtime
+util:	scripts runtime
 	+cd util && $(SUBDIR_MMAKE)
 
 .PHONY: scripts
@@ -302,6 +316,11 @@ deep_profiler: dep_deep_profiler scripts util boehm_gc runtime library \
 		mdbcomp browser ssdb trace
 	+cd deep_profiler && $(SUBDIR_MMAKE)
 
+.PHONY: mfilterjavac
+mfilterjavac: dep_mfilterjavac scripts util boehm_gc runtime library \
+		mdbcomp browser ssdb trace
+	+cd mfilterjavac && $(SUBDIR_MMAKE)
+
 #-----------------------------------------------------------------------------#
 
 .PHONY: tags
diff --git a/configure.ac b/configure.ac
index a1d0dee..471418d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5390,6 +5390,7 @@ slice/SLICE_FLAGS
 profiler/PROF_FLAGS
 deep_profiler/DEEP_FLAGS
 tests/TESTS_FLAGS
+mfilterjavac/MFILTERJAVAC_FLAGS
 '
 
 # The order in which we output files matters, because in some cases, one of the
diff --git a/mfilterjavac/MFILTERJAVAC_FLAGS.in b/mfilterjavac/MFILTERJAVAC_FLAGS.in
new file mode 100644
index 0000000..d73d03a
--- /dev/null
+++ b/mfilterjavac/MFILTERJAVAC_FLAGS.in
@@ -0,0 +1,25 @@
+ at BOOTSTRAP_MC_ARGS@
+--no-infer-all
+--halt-at-warn
+--no-warn-inferred-erroneous
+--no-mercury-stdlib-dir
+-I../library
+-I../browser
+-I../ssdb
+--c-include-directory ../boehm_gc
+--c-include-directory ../boehm_gc/include
+--c-include-directory ../runtime
+--c-include-directory ../library
+--c-include-directory ../library/Mercury/mihs
+--c-include-directory ../browser
+--c-include-directory ../browser/Mercury/mihs
+--c-include-directory ../ssdb
+--c-include-directory ../ssdb/Mercury/mihs
+--c-include-directory ../trace
+--csharp-flag -keyfile:../mercury.snk
+--no-java-classpath
+--java-classpath ../library/mer_rt.jar
+--java-classpath ../library/mer_std.jar
+--java-classpath ../browser/mer_browser.jar
+--java-classpath ../mdbcomp/mer_mdbcomp.jar
+--config-file ../scripts/Mercury.config.bootstrap
diff --git a/mfilterjavac/Mmakefile b/mfilterjavac/Mmakefile
new file mode 100644
index 0000000..fc46535
--- /dev/null
+++ b/mfilterjavac/Mmakefile
@@ -0,0 +1,152 @@
+#-----------------------------------------------------------------------------#
+# Copyright (C) 2013 The University of Melbourne.
+# This file may only be copied under the terms of the GNU General
+# Public Licence - see the file COPYING in the Mercury distribution.
+#-----------------------------------------------------------------------------#
+
+# This is the Mmakefile for building the mfilterjavac tool.
+
+MERCURY_DIR=..
+LINK_STATIC=yes
+include $(MERCURY_DIR)/Mmake.common
+
+#----------------------------------------------------------------------------#
+
+-include Mmake.mfilterjavac.params
+
+# Override the default rule in `mmake --use-mmc-make' that asks `mmc' to
+# create a missing optional params file.
+Mmake.mfilterjavac.params:
+
+# Module-specific options should go in Mercury.options so they
+# can be found by `mmc --make'.  But this hasn't been used in this directory
+# so it's commented out.
+# include Mercury.options
+
+MAIN_TARGET = all
+
+ALL_MODULES = mfilterjavac
+
+# Always compile the deep profiler, even if it is not enabled.
+# 
+MAIN_TARGET=all
+MERCURY_MAIN_MODULES=$(ALL_MODULES)
+DEPEND=$(patsubst %,%.depend,$(ALL_MODULES))
+
+VPATH = $(LIBRARY_DIR) $(SSDB_DIR)
+
+#-----------------------------------------------------------------------------#
+
+MLFLAGS += --shared
+MCFLAGS += --flags MFILTERJAVAC_FLAGS $(CONFIG_OVERRIDE)
+
+#-----------------------------------------------------------------------------#
+
+# Tell the C# compiler where the stdlib assembly is.
+#
+ifneq ("$(filter csharp%,$(GRADE))","")
+CSCFLAGS=-lib:../library -r:mer_std.dll
+endif
+
+#-----------------------------------------------------------------------------#
+
+ifneq ("$(filter il% csharp% java% erlang%,$(GRADE))","")
+MLOBJS =
+endif
+
+#-----------------------------------------------------------------------------#
+
+# The deep profiler contains quite a lot of C code for which there are
+# currently not C#, IL, Java or Erlang implementations.  We need to pass
+# `--allow-stubs' in order to compile it.
+#
+ifneq ("$(filter il% csharp% java% erlang%,$(GRADE))","")
+MCFLAGS += --allow-stubs --no-warn-stubs
+endif
+
+#-----------------------------------------------------------------------------#
+
+.PHONY: nothing
+nothing:
+
+.PHONY: depend
+depend:	$(DEPEND)
+
+$(DEPEND): MFILTERJAVAC_FLAGS
+
+.PHONY: all
+all:	$(ALL_MODULES) $(TAGS_FILE_EXISTS)
+
+#-----------------------------------------------------------------------------#
+
+# Add some additional dependencies, so that Mmake knows to remake the
+# profiler if one of the libraries changes.
+
+ifeq ("$(filter il% csharp% java% erlang%,$(GRADE))","")
+mfilterjavac:		$(RUNTIME_DIR)/lib$(RT_LIB_NAME).$A
+mfilterjavac:		$(LIBRARY_DIR)/lib$(STD_LIB_NAME).$A
+endif
+
+$(cs_subdir)mfilterjavac.c:			$(UTIL_DIR)/mkinit$(EXT_FOR_EXE)
+
+#-----------------------------------------------------------------------------#
+
+.PHONY: check
+check:	DEPEND=$(patsubst %,%.check,$(ALL_MODULES))
+
+.PHONY: ints 
+ints:	DEPEND=$(patsubst %,%.ints,$(ALL_MODULES))
+
+#-----------------------------------------------------------------------------#
+
+# We need the shenanigans with .deep_tags to avoid situations in which an
+# "mmake tags" in this directory does nothing even in the absence of a tags
+# file in this directory, because mmake uses VPATH to find ../library/tags
+# and believes it to be the tags file we are asking for.
+
+.PHONY: tags
+tags:	.mfilterjavac_tags
+
+MS = \
+	$(mfilterjavac.ms)
+
+.mfilterjavac_tags: $(MTAGS) $(MS) \
+		$(wildcard $(LIBRARY_DIR)/*.m)
+	$(MTAGS) $(MS) $(LIBRARY_DIR)/*.m
+	@touch .mfilterjavac_tags
+
+.PHONY: tags_file_exists
+tags_file_exists:
+	@if test ! -f tags; then echo making tags; \
+	$(MTAGS) $(MS) $(LIBRARY_DIR)/*.m; \
+	touch .mfilterjavac_tags; \
+	fi
+
+#-----------------------------------------------------------------------------#
+
+.PHONY: dates
+dates:
+	touch 	$(mfilterjavac.dates)
+
+#-----------------------------------------------------------------------------#
+
+.PHONY: os cs
+os: $(mfilterjavac.os) $(os_subdir)mfilterjavac_init.o
+cs: $(mfilterjavac.cs) $(cs_subdir)mfilterjavac_init.c
+
+#-----------------------------------------------------------------------------#
+
+realclean_local:
+	rm -f .mfilterjavac_tags tags MFILTERJAVAC_FLAGS MFILTERJAVAC_FLAGS.date \
+
+#-----------------------------------------------------------------------------#
+
+# Installation target
+
+.PHONY: install
+install: mfilterjavac
+	-[ -d $(INSTALL_MERC_BIN_DIR) ] || mkdir -p $(INSTALL_MERC_BIN_DIR)
+	cp `vpath_find mfilterjavac$(EXT_FOR_EXE)` \
+		$(INSTALL_MERC_BIN_DIR)/mfilterjavac
+
+#-----------------------------------------------------------------------------#
diff --git a/mfilterjavac/mfilterjavac.m b/mfilterjavac/mfilterjavac.m
new file mode 100644
index 0000000..6aecc07
--- /dev/null
+++ b/mfilterjavac/mfilterjavac.m
@@ -0,0 +1,301 @@
+%----------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%----------------------------------------------------------------------------%
+% Copyright (C) 2013 The University of Melbourne.
+% This file may only be copied under the terms of the GNU General
+% Public License - see the file COPYING in the Mercury distribution.
+%----------------------------------------------------------------------------%
+%
+% File: mfilterjavac.m
+% Author: pbone
+%
+% Java does not support pragmas that effect it's tokenisation such as the
+% #line macro that can be used with C compilers to affect their error
+% output.  This means that foreign java code that is written in Mercury is
+% that causes compilation errors has the wrong filename and line number
+% reported for it.  Making use of Mercury's Java backend more tedious.
+%
+% This program post processes the output of javac, translating file and line
+% number references to references within the original Mercury file.  It
+% recognizes comments placed in the Java code by the Mercury compiler,
+% specifically compiler/mlds_to_java.m
+%
+%-----------------------------------------------------------------------------%
+
+:- module mfilterjavac.
+:- interface.
+
+:- import_module io.
+
+%-----------------------------------------------------------------------------%
+
+:- pred main(io::di, io::uo) is det.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module char.
+:- import_module int.
+:- import_module list.
+:- import_module maybe.
+:- import_module require.
+:- import_module string.
+
+%-----------------------------------------------------------------------------%
+
+main(!IO) :-
+    filter_lines(MaybeError, !IO),
+    (
+        MaybeError = ok
+    ;
+        MaybeError = error(Error),
+        io.write_string(io.stderr_stream, Error, !IO),
+        io.set_exit_status(1, !IO)
+    ).
+
+:- pred filter_lines(maybe_error::out, io::di, io::uo) is det.
+
+filter_lines(MaybeError, !IO) :-
+    io.read_line_as_string(Result, !IO),
+    (
+        Result = ok(Line),
+        filter_line(Line, MaybeOutLine, !IO),
+        (
+            MaybeOutLine = ok(OutLine),
+            io.write_string(OutLine, !IO),
+            filter_lines(MaybeError, !IO)
+        ;
+            MaybeOutLine = error(Error),
+            MaybeError = error(Error)
+        )
+    ;
+        Result = eof,
+        MaybeError = ok
+    ;
+        Result = error(Error),
+        ErrorStr = format("stdin: %s\n", [s(error_message(Error))]),
+        MaybeError = error(ErrorStr)
+    ).
+
+:- pred filter_line(string::in, maybe_error(string)::out, io::di, io::uo)
+    is det.
+
+filter_line(Line, MaybeOutLine, !IO) :-
+    (
+        PartsA = split_at_separator(char.is_whitespace, Line),
+        PartsA = [PartAA | OtherPartsA],
+        PartsAA = split_at_char(':', PartAA),
+        PartsAA = [Filename, LineStr, Empty],
+        string.to_int(LineStr, LineNo)
+    ->
+        maybe_get_line_info(Filename, MaybeLineInfo, !IO),
+        (
+            MaybeLineInfo = ok(LineInfo),
+            line_info_translate(LineInfo, Filename, LineNo,
+                MerFileName, MerLineNo),
+            Rest = string.join_list(" ", OtherPartsA),
+            OutLine = string.format("%s:%d:%s %s\n",
+                [s(MerFileName), i(MerLineNo), s(Empty), s(Rest)]),
+            MaybeOutLine = ok(OutLine)
+        ;
+            MaybeLineInfo = error(Error),
+            MaybeOutLine = error(Error)
+        )
+    ;
+        MaybeOutLine = ok(Line)
+    ).
+
+%-----------------------------------------------------------------------------%
+
+:- type line_info
+    --->    line_info(
+                li_start        :: int, % inclusive
+                li_end          :: int, % not inclusive
+                li_delta        :: int,
+                li_orig_file    :: string
+            ).
+
+:- type line_info_error
+    --->    line_info_error(
+                li_filename     :: string,
+                li_lineno       :: int,
+                li_error        :: line_info_error_type
+            ).
+
+:- type line_info_error_type
+    --->    lie_end_without_beginning
+    ;       lie_beginning_without_end
+    ;       lie_duplicate_beginning.
+
+:- pred line_info_translate(list(line_info)::in, string::in, int::in,
+    string::out, int::out) is det.
+
+line_info_translate([], Name, Line, Name, Line).
+line_info_translate([Info | Infos], Name0, Line0, Name, Line) :-
+    Info = line_info(Start, End, Delta, File),
+    (
+        Line0 < Start
+    ->
+        % No translation.
+        Name = Name0,
+        Line = Line0
+    ;
+        Line0 < End
+    ->
+        Line = Line0 + Delta,
+        Name = File
+    ;
+        line_info_translate(Infos, Name0, Line0, Name, Line)
+    ).
+
+:- func error_type_string(line_info_error_type) = string.
+
+error_type_string(lie_end_without_beginning) =
+    "END token without BEGIN token".
+error_type_string(lie_beginning_without_end) =
+    "BEGIN token without END token".
+error_type_string(lie_duplicate_beginning) =
+    "BEGIN token followed by another BEGIN token".
+
+%----------------------------------------------------------------------------%
+
+:- pred maybe_get_line_info(string::in, maybe_error(list(line_info))::out,
+    io::di, io::uo) is det.
+
+maybe_get_line_info(Filename, MaybeInfo, !IO) :-
+    io.open_input(Filename, Res, !IO),
+    (
+        Res = ok(Stream),
+        read_line_marks(Stream, 1, [], MaybeMarksRev, !IO),
+        io.close_input(Stream, !IO),
+        (
+            MaybeMarksRev = ok(MarksRev),
+            reverse(MarksRev, Marks),
+            create_line_info(Marks, Filename, [], MaybeInfo0),
+            (
+                MaybeInfo0 = ok(Infos),
+                MaybeInfo = ok(Infos)
+            ;
+                MaybeInfo0 = error(LineInfoError),
+                LineInfoError = line_info_error(ErrFilename, ErrLine, Error),
+                StringError = format(
+                    "%s:%d: Error understanding line number declration: %s",
+                    [s(ErrFilename), i(ErrLine), s(error_type_string(Error))]),
+                MaybeInfo = error(StringError)
+            )
+        ;
+            MaybeMarksRev = error(Msg),
+            MaybeInfo = error(format("%s: %s", [s(Filename), s(Msg)]))
+        )
+    ;
+        Res = error(_Error),
+        % We ignore errors here as our parsing of javac's output could cause
+        % false errors.
+        MaybeInfo = ok([])
+    ).
+
+:- type line_mark
+    --->    line_mark(
+                lm_type             :: begin_or_end_block,
+                lm_mer_file         :: string,
+                lm_java_line_no     :: int,
+                lm_mer_line_no      :: int
+            ).
+
+:- type begin_or_end_block
+    --->    begin_block
+    ;       end_block.
+
+:- pred read_line_marks(input_stream::in, int::in, list(line_mark)::in,
+    maybe_error(list(line_mark))::out, io::di, io::uo) is det.
+
+read_line_marks(Stream, JavaLineNo, Marks0, MaybeMarks, !IO) :-
+    read_line_as_string(Stream, Result, !IO),
+    (
+        Result = ok(Line),
+        % The format string in mlds_to_java specificaly uses spaces
+        % rather than any other whitespace.
+        Parts = string.split_at_char(' ', strip(Line)),
+        (
+            Parts = ["//", Marker, PathLine],
+            (
+                Marker = "MER_FOREIGN_BEGIN",
+                Type = begin_block
+            ;
+                Marker = "MER_FOREIGN_END",
+                Type = end_block
+            ),
+            PartsB = string.split_at_char(':', PathLine),
+            PartsB = [MerFile, MerLineNoStr],
+            string.to_int(MerLineNoStr, MerLineNo)
+        ->
+            Mark = line_mark(Type, MerFile, JavaLineNo, MerLineNo),
+            Marks = [Mark | Marks0]
+        ;
+            Marks = Marks0
+        ),
+        read_line_marks(Stream, JavaLineNo+1, Marks, MaybeMarks, !IO)
+    ;
+        Result = eof,
+        MaybeMarks = ok(Marks0)
+    ;
+        Result = error(Error),
+        MaybeMarks = error(error_message(Error))
+    ).
+
+:- pred create_line_info(list(line_mark)::in, string::in,
+    list(line_info)::in, maybe_error(list(line_info), line_info_error)::out)
+    is det.
+
+create_line_info([], _JavaFile, Infos, ok(InfosRev)) :-
+    reverse(Infos, InfosRev).
+create_line_info([Mark | Marks0], JavaFile, Infos0, MaybeInfos) :-
+    Mark = line_mark(Type, MerFile, JavaLineNo, MerLineNo),
+    (
+        Type = begin_block,
+        create_line_info_in_block(InfoEnd, Marks0, Marks),
+        (
+            InfoEnd = line_info_end(End),
+            Delta = MerLineNo - JavaLineNo,
+            Info = line_info(JavaLineNo, End, Delta, MerFile),
+            Infos = [Info | Infos0],
+            create_line_info(Marks, JavaFile, Infos, MaybeInfos)
+        ;
+            InfoEnd = line_info_no_end,
+            MaybeInfos = error(line_info_error(JavaFile, JavaLineNo,
+                lie_beginning_without_end))
+        ;
+            InfoEnd = line_info_duplicate_begin(SecondBeginLine),
+            MaybeInfos = error(line_info_error(JavaFile, SecondBeginLine,
+                lie_duplicate_beginning))
+        )
+    ;
+        Type = end_block,
+        MaybeInfos = error(line_info_error(JavaFile, JavaLineNo,
+            lie_end_without_beginning))
+    ).
+
+:- type line_info_end
+    --->    line_info_end(int)
+    ;       line_info_no_end
+    ;       line_info_duplicate_begin(int).
+
+:- pred create_line_info_in_block(line_info_end::out,
+    list(line_mark)::in, list(line_mark)::out) is det.
+
+create_line_info_in_block(line_info_no_end, [], []).
+create_line_info_in_block(Info, [Mark | Marks], Marks) :-
+    Mark = line_mark(Type, _, End, _),
+    (
+        Type = begin_block,
+        Info = line_info_duplicate_begin(End)
+    ;
+        Type = end_block,
+        Info = line_info_end(End)
+    ).
+
+%-----------------------------------------------------------------------------%
+:- end_module mfilterjavac.
+%-----------------------------------------------------------------------------%
-- 
1.8.1.3




More information about the reviews mailing list