Files
mercury/compiler/source_file_map.m
Simon Taylor 89a4d190c5 Library changes required to make the compiler work on Windows
Estimated hours taken: 120
Branches: main

Library changes required to make the compiler work on Windows
without Cygwin.  (Compiler changes to come separately).

library/dir.m:
	Handle Windows-style paths.

	Change the determinism of dir.basename and dir.split_name.
	dir.basename now fails for root directories (a new function
	dir.basename_det calls error/1 rather than failing).
	dir.split_name fails for root directories or if the pathname
	passed doesn't contain a directory separator.

	Add predicates dir.make_directory, dir.path_name_is_absolute
	and dir.path_name_is_root_directory.

	Add a multi predicate dir.is_directory separator which
	returns all separators for the platform (including '/' on
	Windows), not just the standard one.

	Add a function dir.parent_directory (returns "..").

	Add dir.foldl2 and dir.recursive_foldl2, to iterate through
	the entries in a directory (and maybe its subdirectories).

	Change '/' to correctly handle Windows paths of the form
	"C:"/"foo" and "\"/"foo".

	Don't add repeated directory separators in '/'.

library/io.m:
	Add io.file_type and io.check_file_accessibility.

	Add predicates to deal with symlinks -- io.have_symlinks,
	io.make_symlink and io.follow_symlink.

	Add io.file_id for use by dir.foldl2 to detect
	symlink loops. This is a bit low level, so it's
	not included in the user documentation.

	Add io.(binary_)input_stream_foldl2_io_maybe_stop, which
	is like io.(binary_)input_stream_foldl2_io, but allows
	stopping part of the way through. This is useful for
	implementing `diff'-like functionality to replace
	mercury_update_interface.

	Use Windows-style paths when generating temporary file
	names on Windows.

	Add versions of the predicates to generate error messages
	to handle Win32 errors.

	Add versions of the predicates to generate error messages
	which take a system error code, rather than looking up
	errno. This simplifies things where the error we
	are interested in was not from the last system call.

library/exception.m:
	Add a predicate finally/6 which performs the same function
	as the `finally' clause in languages such as C# and Java.

	Add predicates try_det, try_io_det and try_store_det,
	which only have one mode so they are more convenient
	to pass to promise_only solution.

library/Mmakefile:
	Add dependencies on runtime/mercury_conf.h for files which
	use the MR_HAVE_* macros.

library/string.m:
	Add a function version of string__index.

NEWS:
	Document the new predicates and functions and the change
	of determinism of dir.split_name and dir.basename.

configure.in:
runtime/mercury_conf.h.in:
	Test for lstat, mkdir, symlink and readlink.

runtime/mercury_conf_param.h:
	Add a macro MR_BROKEN_ST_INO, which is true if the st_ino
	field of `struct stat' is garbage. Currently defined iff
	MR_WIN32 is defined.

compiler/compile_target_code.m:
compiler/modules.m:
compiler/options_file.m:
compiler/prog_io.m:
compiler/source_file_map.m:
	Changes required by the change of determinism of
	dir.split_name and dir.basename.

tests/hard_coded/Mmakefile:
tests/hard_coded/dir_test.{m,exp,exp2}:
	Test case.
2003-07-21 14:08:42 +00:00

217 lines
6.4 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 2002-2003 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: source_file_map.m
% Author: stayl
%
% Maintain a mapping from module name to source file name.
%-----------------------------------------------------------------------------%
:- module parse_tree__source_file_map.
:- interface.
:- import_module parse_tree__prog_data.
:- import_module parse_tree__prog_io.
:- import_module bool, io, list.
% lookup_module_source_file(ModuleName, FileName, FileNameIsMapped).
%
% FileNameIsMapped is `yes' if ModuleName is in
% the Mercury.modules file.
:- pred lookup_module_source_file(module_name::in, file_name::out,
io__state::di, io__state::uo) is det.
% Return `yes' if there is a valid Mercury.modules file.
:- pred have_source_file_map(bool::out, io__state::di, io__state::uo) is det.
% Return the default fully-qualified source file name.
:- func default_source_file(module_name) = file_name.
% Given a list of file names, produce the Mercury.modules file.
:- pred write_source_file_map(list(string)::in,
io__state::di, io__state::uo) is det.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module libs__globals.
:- import_module libs__options.
:- import_module parse_tree__modules.
:- import_module parse_tree__prog_out.
:- import_module parse_tree__prog_util.
:- import_module char, dir, map, std_util, string.
lookup_module_source_file(ModuleName, FileName) -->
get_source_file_map(SourceFileMap),
{ map__search(SourceFileMap, ModuleName, FileName0) ->
FileName = FileName0
;
FileName = default_source_file(ModuleName)
}.
default_source_file(ModuleName) = BaseFileName ++ ".m" :-
prog_out__sym_name_to_string(ModuleName, ".", BaseFileName).
have_source_file_map(HaveMap) -->
get_source_file_map(_),
globals__io_get_globals(Globals),
{ globals__get_source_file_map(Globals, MaybeSourceFileMap) },
{ MaybeSourceFileMap = yes(Map), \+ map__is_empty(Map) ->
HaveMap = yes
;
HaveMap = no
}.
% Read the Mercury.modules file (if it exists) to find
% the mapping from module name to file name.
:- pred get_source_file_map(source_file_map::out,
io__state::di, io__state::uo) is det.
get_source_file_map(SourceFileMap) -->
globals__io_get_globals(Globals0),
{ globals__get_source_file_map(Globals0, MaybeSourceFileMap0) },
( { MaybeSourceFileMap0 = yes(SourceFileMap0) } ->
{ SourceFileMap = SourceFileMap0 }
;
io__open_input(modules_file_name, OpenRes),
(
{ OpenRes = ok(Stream) },
io__set_input_stream(Stream, OldStream),
read_source_file_map([], map__init, SourceFileMap),
io__set_input_stream(OldStream, _),
io__close_input(Stream)
;
{ OpenRes = error(_) },
% If the file doesn't exist, then the mapping is empty.
{ SourceFileMap = map__init }
),
globals__io_get_globals(Globals1),
{ globals__set_source_file_map(Globals1,
yes(SourceFileMap), Globals2) },
{ unsafe_promise_unique(Globals2, Globals) },
globals__io_set_globals(Globals)
).
:- pred read_source_file_map(list(char)::in, source_file_map::in,
source_file_map::out, io__state::di, io__state::uo) is det.
read_source_file_map(ModuleChars, Map0, Map) -->
read_until_char('\t', [], ModuleCharsResult),
(
{ ModuleCharsResult = ok(RevModuleChars) },
{ string__from_rev_char_list(RevModuleChars, ModuleStr) },
{ string_to_sym_name(ModuleStr, ".", ModuleName) },
read_until_char('\n', [], FileNameCharsResult),
(
{ FileNameCharsResult = ok(FileNameChars) },
{ string__from_rev_char_list(FileNameChars,
FileName) },
{ map__set(Map0, ModuleName, FileName, Map1) },
read_source_file_map(ModuleChars, Map1, Map)
;
{ FileNameCharsResult = eof },
{ Map = Map0 },
io__set_exit_status(1),
io__write_string(
"mercury_compile: unexpected end of file in Mercury.modules file.\n")
;
{ FileNameCharsResult = error(Error) },
{ Map = Map0 },
io__set_exit_status(1),
io__write_string(
"mercury_compile: error in Mercury.modules file: "),
io__write_string(io__error_message(Error)),
io__nl
)
;
{ ModuleCharsResult = eof },
{ Map = Map0 }
;
{ ModuleCharsResult = error(Error) },
{ Map = Map0 },
io__set_exit_status(1),
io__write_string(
"mercury_compile: error in Mercury.modules file: "),
io__write_string(io__error_message(Error)),
io__nl
).
:- pred read_until_char(char::in, list(char)::in, io__result(list(char))::out,
io__state::di, io__state::uo) is det.
read_until_char(EndChar, Chars0, Result) -->
io__read_char(CharRes),
(
{ CharRes = ok(Char) },
( { Char = EndChar } ->
{ Result = ok(Chars0) }
;
read_until_char(EndChar, [Char | Chars0], Result)
)
;
{ CharRes = eof },
{ Result = ( Chars0 = [] -> eof ; ok(Chars0) ) }
;
{ CharRes = error(Error) },
{ Result = error(Error) }
).
write_source_file_map(FileNames) -->
{ ModulesFileName = modules_file_name },
io__open_output(ModulesFileName, OpenRes),
(
{ OpenRes = ok(Stream) },
list__foldl(write_source_file_map_2(Stream), FileNames),
io__close_output(Stream)
;
{ OpenRes = error(Error) },
io__set_exit_status(1),
io__write_string("mercury_compile: error opening `"),
io__write_string(ModulesFileName),
io__write_string("' for output: "),
io__write_string(io__error_message(Error))
).
:- pred write_source_file_map_2(io__output_stream::in, file_name::in,
io__state::di, io__state::uo) is det.
write_source_file_map_2(MapStream, FileName) -->
find_module_name(FileName, MaybeModuleName),
(
{ MaybeModuleName = yes(ModuleName) },
{ string__remove_suffix(FileName, ".m", PartialFileName0) ->
PartialFileName = PartialFileName0
;
PartialFileName = FileName
},
{ file_name_to_module_name(dir__basename_det(PartialFileName),
DefaultModuleName) },
(
% Only include a module in the mapping if the
% name doesn't match the default.
{ dir__dirname(PartialFileName) =
dir__this_directory `with_type` string },
{ ModuleName = DefaultModuleName }
->
[]
;
io__set_output_stream(MapStream, OldStream),
prog_out__write_sym_name(ModuleName),
io__write_string("\t"),
io__write_string(FileName),
io__nl,
io__set_output_stream(OldStream, _)
)
;
{ MaybeModuleName = no }
).
:- func modules_file_name = string.
modules_file_name = "Mercury.modules".