[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