diff --git a/Mmakefile b/Mmakefile index 88760a43f..365649d53 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 a1d0dee71..471418dda 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/.mgnuc_copts b/mfilterjavac/.mgnuc_copts new file mode 100644 index 000000000..0a44865a4 --- /dev/null +++ b/mfilterjavac/.mgnuc_copts @@ -0,0 +1,12 @@ +-I../boehm_gc +-I../boehm_gc/include +-I../runtime +-I../library +-I../library/Mercury/mihs +-I../mdbcomp +-I../mdbcomp/Mercury/mihs +-I../browser +-I../browser/Mercury/mihs +-I../ssdb +-I../ssdb/Mercury/mihs +-I../trace diff --git a/mfilterjavac/.mgnuc_opts b/mfilterjavac/.mgnuc_opts new file mode 100644 index 000000000..7f823e9bf --- /dev/null +++ b/mfilterjavac/.mgnuc_opts @@ -0,0 +1 @@ +--no-mercury-stdlib-dir diff --git a/mfilterjavac/MFILTERJAVAC_FLAGS.in b/mfilterjavac/MFILTERJAVAC_FLAGS.in new file mode 100644 index 000000000..5aa3b4adb --- /dev/null +++ b/mfilterjavac/MFILTERJAVAC_FLAGS.in @@ -0,0 +1,26 @@ +@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 +--erlang-include-directory ../library/Mercury/hrls +--config-file ../scripts/Mercury.config.bootstrap diff --git a/mfilterjavac/Mmakefile b/mfilterjavac/Mmakefile new file mode 100644 index 000000000..57a4b6802 --- /dev/null +++ b/mfilterjavac/Mmakefile @@ -0,0 +1,140 @@ +#-----------------------------------------------------------------------------# +# 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 + +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 + +#-----------------------------------------------------------------------------# + +.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 .mfilterjavac_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 000000000..190afd1ca --- /dev/null +++ b/mfilterjavac/mfilterjavac.m @@ -0,0 +1,297 @@ +%----------------------------------------------------------------------------% +% 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 +% +% This program processes the output of the Java compiler when compiling Java +% code generated by the Mercury compiler. It translates the error contexts +% reported by the Java compiler into the corresponding error contexts in the +% Mercury source file. This is done by looking for special comments +% inserted into the generated Java code by the Mercury compiler. (See +% compiler/mlds_to_java.m for details.) +% +%-----------------------------------------------------------------------------% + +:- 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 specifically 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. +%-----------------------------------------------------------------------------%