Files
mercury/compiler/make.util.m
Julien Fischer 14713ee3ce Fix compilation of the compiler in non C grades.
Branches: main

Fix compilation of the compiler in non C grades.

compiler/equiv_type_hlds.m:
compiler/hlds_out_mode.m:
compiler/inst_match.m:
compiler/make.util.m:
	Provide Mercury clauses for various foreign_procs that have
	been added recently.
2012-06-22 17:20:11 +00:00

2050 lines
71 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2002-2012 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: make.util.m.
% Authors: stayl, wangp.
%
% Assorted predicates used to implement `mmc --make'.
%
%-----------------------------------------------------------------------------%
:- module make.util.
:- interface.
:- import_module libs.globals.
%-----------------------------------------------------------------------------%
%
% Versions of foldl which stop if the supplied predicate returns `no'
% for any element of the list.
%
% foldl2_pred_with_status(Globals, T, Succeeded, !Info).
%
:- type foldl2_pred_with_status(T, Info, IO) ==
pred(globals, T, bool, Info, Info, IO, IO).
:- inst foldl2_pred_with_status == (pred(in, in, out, in, out, di, uo) is det).
% foldl2_maybe_stop_at_error(KeepGoing, P, Globals, List, Succeeded,
% !Info, !IO).
%
:- pred foldl2_maybe_stop_at_error(bool::in,
foldl2_pred_with_status(T, Info, IO)::in(foldl2_pred_with_status),
globals::in, list(T)::in, bool::out, Info::in, Info::out,
IO::di, IO::uo) is det.
% foldl3_pred_with_status(Globals, T, Succeeded, !Acc, !Info).
%
:- type foldl3_pred_with_status(T, Acc, Info, IO) ==
pred(globals, T, bool, Acc, Acc, Info, Info, IO, IO).
:- inst foldl3_pred_with_status ==
(pred(in, in, out, in, out, in, out, di, uo) is det).
% foldl3_maybe_stop_at_error(KeepGoing, P, Globals, List, Succeeded, !Acc,
% !Info).
%
:- pred foldl3_maybe_stop_at_error(bool::in,
foldl3_pred_with_status(T, Acc, Info, IO)::in(foldl3_pred_with_status),
globals::in, list(T)::in, bool::out, Acc::in, Acc::out,
Info::in, Info::out, IO::di, IO::uo) is det.
%-----------------------------------------------------------------------------%
% foldl2_maybe_stop_at_error_maybe_parallel(KeepGoing, P, Globals,
% List, Succeeded, !Info, !IO).
%
% Like foldl2_maybe_stop_at_error, but if parallel make is enabled, it
% tries to perform a first pass that overlaps execution of P(elem) in
% separate threads or processes. Updates to !Info in the first pass are
% ignored. If the first pass succeeds, a second sequential pass is made in
% which updates !Info are kept. Hence it must be safe to execute P(elem)
% concurrently, in any order, and multiple times.
%
:- pred foldl2_maybe_stop_at_error_maybe_parallel(bool::in,
foldl2_pred_with_status(T, make_info, io)::in(foldl2_pred_with_status),
globals::in, list(T)::in, bool::out, make_info::in, make_info::out,
io::di, io::uo) is det.
%-----------------------------------------------------------------------------%
:- type build(T, Info1, Info2) == pred(globals, T, bool, Info1, Info2, io, io).
:- type build(T, Info) == build(T, Info, Info).
:- type build(T) == build(T, make_info).
:- inst build == (pred(in, in, out, in, out, di, uo) is det).
% build_with_module_options(Globals, ModuleName, ExtraArgs, Builder,
% Succeeded, !Info, !IO).
%
% Perform the given closure after updating the option_table in the globals
% to contain the module-specific options for the specified module and
% the extra options given in the ExtraArgs.
% Adds `--invoked-by-mmc-make' and `--use-subdirs' to the option list.
%
:- pred build_with_module_options(globals::in, module_name::in,
list(string)::in, build(list(string))::in(build), bool::out,
make_info::in, make_info::out, io::di, io::uo) is det.
% build_with_module_options_args(Globals, ModuleName, OptionsVariables,
% OptionArgs, ExtraArgs, Builder, Succeeded, !Info, !IO).
%
% Perform the given closure after updating the option_table in the globals
% to contain the module-specific options for the specified module and
% the extra options given in ExtraArgs and OptionArgs.
% Does not add `--invoked-by-mmc-make' and `--use-subdirs' to the
% option list.
%
:- pred build_with_module_options_args(globals::in, module_name::in,
options_variables::in, list(string)::in, list(string)::in,
build(list(string), Info1, Info2)::in(build),
bool::out, Info1::in, maybe(Info2)::out, io::di, io::uo) is det.
% Perform the given closure with an output stream created to append to
% the error file for the given module.
%
:- pred build_with_output_redirect(globals::in, module_name::in,
build(io.output_stream)::in(build), bool::out,
make_info::in, make_info::out, io::di, io::uo) is det.
% Produce an output stream which writes to the error file
% for the given module.
%
:- pred redirect_output(module_name::in, maybe(io.output_stream)::out,
make_info::in, make_info::out, io::di, io::uo) is det.
% Close the module error output stream.
%
:- pred unredirect_output(globals::in, module_name::in, io.output_stream::in,
make_info::in, make_info::out, io::di, io::uo) is det.
%-----------------------------------------------------------------------------%
:- type build2(T, U) ==
pred(globals, T, U, bool, make_info, make_info, io, io).
:- inst build2 == (pred(in, in, in, out, in, out, di, uo) is det).
:- pred build_with_module_options_and_output_redirect(globals::in,
module_name::in, list(string)::in,
build2(list(string), io.output_stream)::in(build2),
bool::out, make_info::in, make_info::out, io::di, io::uo) is det.
%-----------------------------------------------------------------------------%
%
% Timestamp handling
%
% Find the timestamp updated when a target is produced.
%
:- pred get_timestamp_file_timestamp(globals::in, target_file::in,
maybe_error(timestamp)::out, make_info::in, make_info::out,
io::di, io::uo) is det.
% Find the timestamp for the given dependency file.
%
:- pred get_dependency_timestamp(globals::in, dependency_file::in,
maybe_error(timestamp)::out, make_info::in, make_info::out,
io::di, io::uo) is det.
% get_target_timestamp(Globals, Search, TargetFile, Timestamp)
%
% Find the timestamp for the given target file.
% `Search' should be `do_search' if the file could be part of an
% installed library.
%
:- pred get_target_timestamp(globals::in, maybe_search::in, target_file::in,
maybe_error(timestamp)::out, make_info::in, make_info::out,
io::di, io::uo) is det.
% get_file_name(Globals, Search, TargetFile, FileName, !IO):
%
% Compute a file name for the given target file.
% `Search' should be `do_search' if the file could be part of an
% installed library.
%
:- pred get_file_name(globals::in, maybe_search::in, target_file::in,
file_name::out, make_info::in, make_info::out, io::di, io::uo) is det.
% Find the timestamp of the first file matching the given
% file name in one of the given directories.
%
:- pred get_file_timestamp(list(dir_name)::in, file_name::in,
maybe_error(timestamp)::out, make_info::in, make_info::out,
io::di, io::uo) is det.
% Return the oldest of the timestamps if both are of the form
% `ok(Timestamp)', returning `error(Error)' otherwise.
%
:- func find_oldest_timestamp(maybe_error(timestamp),
maybe_error(timestamp)) = maybe_error(timestamp).
%-----------------------------------------------------------------------------%
%
% Remove file a file, deleting the cached timestamp
% The removal is reported to the user if the given boolean option is set.
% In general the option given should be `--very-verbose' when making a
% `.clean' or `.realclean target', and `--verbose-make' when cleaning
% after an interrupted build.
%
% Remove the target file and the corresponding timestamp file.
%
:- pred make_remove_target_file(globals::in, option::in, target_file::in,
make_info::in, make_info::out, io::di, io::uo) is det.
% Remove the target file and the corresponding timestamp file.
%
:- pred make_remove_target_file_by_name(globals::in, option::in,
module_name::in, module_target_type::in, make_info::in, make_info::out,
io::di, io::uo) is det.
% make_remove_module_file(Globals, VerboseOption, ModuleName, Extension,
% !Info, !IO).
%
:- pred make_remove_module_file(globals::in, option::in, module_name::in,
string::in, make_info::in, make_info::out, io::di, io::uo) is det.
:- pred make_remove_file(globals::in, option::in, file_name::in,
make_info::in, make_info::out, io::di, io::uo) is det.
%-----------------------------------------------------------------------------%
:- func make_target_file_list(list(module_name), module_target_type) =
list(target_file).
:- func make_dependency_list(list(module_name), module_target_type)
= list(dependency_file).
:- func target_extension(globals, module_target_type) = maybe(string).
:- mode target_extension(in, in) = out is det.
:- mode target_extension(in, out) = in(bound(yes(ground))) is nondet.
:- pred target_extension_synonym(string::in, module_target_type::out)
is semidet.
:- pred linked_target_file_name(globals::in, module_name::in,
linked_target_type::in, file_name::out, io::di, io::uo) is det.
% Find the extension for the timestamp file for the
% given target type, if one exists.
%
:- func timestamp_extension(globals, module_target_type) = string is semidet.
:- pred target_is_grade_or_arch_dependent(module_target_type::in) is semidet.
%-----------------------------------------------------------------------------%
%
% Debugging, verbose and error messages
%
% A lock to prevent interleaved output to standard output from parallel
% processes.
%
:- type stdout_lock.
% Apply the given predicate if `--debug-make' is set.
% XXX Do we need this, now that we have trace goals?
%
:- pred debug_msg(globals::in, pred(io, io)::(pred(di, uo) is det),
io::di, io::uo) is det.
% Apply the given predicate if `--verbose-make' is set.
% XXX Do we need this, now that we have trace goals?
%
:- pred verbose_msg(globals::in, pred(io, io)::(pred(di, uo) is det),
io::di, io::uo) is det.
% Apply the given predicate if the given boolean option is set to `yes'.
% XXX Do we need this, now that we have trace goals?
%
:- pred verbose_msg_option(globals::in, option::in,
pred(io, io)::(pred(di, uo) is det), io::di, io::uo) is det.
% Write a debugging message relating to a given target file.
%
:- pred debug_file_msg(globals::in, target_file::in, string::in,
io::di, io::uo) is det.
:- pred make_write_dependency_file(globals::in, dependency_file::in,
io::di, io::uo) is det.
:- pred make_write_dependency_file_list(globals::in, list(dependency_file)::in,
io::di, io::uo) is det.
:- pred make_write_target_file(globals::in, target_file::in,
io::di, io::uo) is det.
:- pred make_write_target_file_wrapped(globals::in, string::in,
target_file::in, string::in, io::di, io::uo) is det.
% Write a message "Making <filename>" if `--verbose-make' is set.
%
:- pred maybe_make_linked_target_message(globals::in, file_name::in,
io::di, io::uo) is det.
% Write a message "Making <filename>" if `--verbose-make' is set.
%
:- pred maybe_make_target_message(globals::in, target_file::in,
io::di, io::uo) is det.
:- pred maybe_make_target_message_to_stream(globals::in, io.output_stream::in,
target_file::in, io::di, io::uo) is det.
% Write a message "Reanalysing invalid/suboptimal modules" if
% `--verbose-make' is set.
%
:- pred maybe_reanalyse_modules_message(globals::in, io::di, io::uo) is det.
% Write a message "** Error making <filename>".
%
:- pred target_file_error(make_info::in, globals::in, target_file::in,
io::di, io::uo) is det.
% Write a message "** Error making <filename>".
%
:- pred file_error(make_info::in, file_name::in, io::di, io::uo) is det.
% If the given target was specified on the command line, warn that it
% was already up to date.
%
:- pred maybe_warn_up_to_date_target(globals::in,
pair(module_name, target_type)::in,
make_info::in, make_info::out, io::di, io::uo) is det.
% Write a message "Made symlink/copy of <filename>" if
% `--verbose-make' is set.
%
:- pred maybe_symlink_or_copy_linked_target_message(globals::in,
pair(module_name, target_type)::in, io::di, io::uo) is det.
%-----------------------------------------------------------------------------%
%
% Timing
%
:- pred get_real_milliseconds(int::out, io::di, io::uo) is det.
%-----------------------------------------------------------------------------%
%
% Hash functions
%
:- pred module_name_hash(module_name::in, int::out) is det.
:- pred dependency_file_hash(dependency_file::in, int::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module analysis.
:- import_module libs.handle_options.
:- import_module libs.process_util.
:- import_module parse_tree.file_names.
:- import_module parse_tree.prog_foreign.
:- import_module transform_hlds.
:- import_module transform_hlds.mmc_analysis.
:- import_module bool.
:- import_module char.
:- import_module dir.
:- import_module getopt_io.
:- import_module maybe.
:- import_module require.
:- import_module set.
%-----------------------------------------------------------------------------%
foldl2_maybe_stop_at_error(KeepGoing, MakeTarget, Globals, Targets, Success,
!Info, !IO) :-
foldl2_maybe_stop_at_error_2(KeepGoing, MakeTarget, Globals, Targets, yes,
Success, !Info, !IO).
:- pred foldl2_maybe_stop_at_error_2(bool::in,
foldl2_pred_with_status(T, Info, IO)::in(foldl2_pred_with_status),
globals::in, list(T)::in, bool::in, bool::out, Info::in, Info::out,
IO::di, IO::uo) is det.
foldl2_maybe_stop_at_error_2(_KeepGoing, _P, _Globals, [], !Success,
!Info, !IO).
foldl2_maybe_stop_at_error_2(KeepGoing, P, Globals, [T | Ts], !Success,
!Info, !IO) :-
P(Globals, T, NewSuccess, !Info, !IO),
(
( NewSuccess = yes
; KeepGoing = yes
)
->
!:Success = !.Success `and` NewSuccess,
foldl2_maybe_stop_at_error_2(KeepGoing, P, Globals, Ts, !Success,
!Info, !IO)
;
!:Success = no
).
foldl3_maybe_stop_at_error(KeepGoing, P, Globals, Ts, Success,
!Acc, !Info, !IO) :-
foldl3_maybe_stop_at_error_2(KeepGoing, P, Globals, Ts, yes, Success,
!Acc, !Info, !IO).
:- pred foldl3_maybe_stop_at_error_2(bool::in,
foldl3_pred_with_status(T, Acc, Info, IO)::in(foldl3_pred_with_status),
globals::in, list(T)::in, bool::in, bool::out, Acc::in, Acc::out,
Info::in, Info::out, IO::di, IO::uo) is det.
foldl3_maybe_stop_at_error_2(_KeepGoing, _P, _Globals, [],
!Success, !Acc, !Info, !IO).
foldl3_maybe_stop_at_error_2(KeepGoing, P, Globals, [T | Ts],
!Success, !Acc, !Info, !IO) :-
P(Globals, T, NewSuccess, !Acc, !Info, !IO),
(
( NewSuccess = yes
; KeepGoing = yes
)
->
!:Success = !.Success `and` NewSuccess,
foldl3_maybe_stop_at_error_2(KeepGoing, P, Globals, Ts, !Success, !Acc,
!Info, !IO)
;
!:Success = no
).
%-----------------------------------------------------------------------------%
%
% Parallel (concurrent) fold.
%
foldl2_maybe_stop_at_error_maybe_parallel(KeepGoing, MakeTarget, Globals,
Targets, Success, !Info, !IO) :-
globals.lookup_int_option(Globals, jobs, Jobs),
(
Jobs > 1,
process_util.can_fork,
have_job_ctl_ipc
->
% First pass.
foldl2_maybe_stop_at_error_parallel_processes(KeepGoing, Jobs,
MakeTarget, Globals, Targets, Success0, !Info, !IO),
% Second pass (sequential).
(
Success0 = yes,
% Disable the `--rebuild' option during the sequential pass
% otherwise all the targets will be built a second time.
globals.set_option(rebuild, bool(no), Globals, NoRebuildGlobals),
foldl2_maybe_stop_at_error(KeepGoing, MakeTarget, NoRebuildGlobals,
Targets, Success, !Info, !IO)
;
Success0 = no,
Success = no
)
;
foldl2_maybe_stop_at_error(KeepGoing, MakeTarget, Globals,
Targets, Success, !Info, !IO)
).
:- pred foldl2_maybe_stop_at_error_parallel_processes(bool::in, int::in,
foldl2_pred_with_status(T, make_info, io)::in(foldl2_pred_with_status),
globals::in, list(T)::in, bool::out, make_info::in, make_info::out,
io::di, io::uo) is det.
foldl2_maybe_stop_at_error_parallel_processes(KeepGoing, Jobs, MakeTarget,
Globals, Targets, Success, !Info, !IO) :-
TotalTasks = list.length(Targets),
create_job_ctl(TotalTasks, MaybeJobCtl, !IO),
(
MaybeJobCtl = yes(JobCtl),
!Info ^ maybe_stdout_lock := yes(JobCtl),
list.foldl2(
start_worker_process(Globals, KeepGoing, MakeTarget, Targets,
JobCtl, !.Info),
2 .. Jobs, [], Pids, !IO),
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
build_with_check_for_interrupt(VeryVerbose,
worker_loop(Globals, KeepGoing, MakeTarget, Targets, JobCtl, yes),
worker_loop_signal_cleanup(JobCtl, Pids), Success0, !Info, !IO),
list.foldl2(reap_worker_process, Pids, Success0, Success, !IO),
!Info ^ maybe_stdout_lock := no,
destroy_job_ctl(JobCtl, !IO)
;
MaybeJobCtl = no,
Success = no
).
:- pred start_worker_process(globals::in, bool::in,
foldl2_pred_with_status(T, Info, io)::in(foldl2_pred_with_status),
list(T)::in, job_ctl::in, Info::in, int::in, list(pid)::in, list(pid)::out,
io::di, io::uo) is det.
start_worker_process(Globals, KeepGoing, MakeTarget, Targets, JobCtl, Info,
_ChildN, !Pids, !IO) :-
start_in_forked_process(
child_worker(Globals, KeepGoing, MakeTarget, Targets, JobCtl, Info),
MaybePid, !IO),
(
MaybePid = yes(Pid),
!:Pids = [Pid | !.Pids]
;
MaybePid = no
).
:- pred child_worker(globals::in, bool::in,
foldl2_pred_with_status(T, Info, io)::in(foldl2_pred_with_status),
list(T)::in, job_ctl::in, Info::in, bool::out, io::di, io::uo) is det.
child_worker(Globals, KeepGoing, MakeTarget, Targets, JobCtl, Info0, Success,
!IO) :-
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
build_with_check_for_interrupt(VeryVerbose,
worker_loop(Globals, KeepGoing, MakeTarget, Targets, JobCtl, yes),
worker_loop_signal_cleanup(JobCtl, []), Success, Info0, _Info, !IO).
:- pred worker_loop(globals::in, bool::in,
foldl2_pred_with_status(T, Info, io)::in(foldl2_pred_with_status),
list(T)::in, job_ctl::in, bool::in, bool::out, Info::in, Info::out,
io::di, io::uo) is det.
worker_loop(Globals, KeepGoing, MakeTarget, Targets, JobCtl, !Success,
!Info, !IO) :-
accept_task(JobCtl, TaskNumber, !IO),
( TaskNumber >= 0 ->
Target = list.det_index0(Targets, TaskNumber),
MakeTarget(Globals, Target, TargetSuccess, !Info, !IO),
(
TargetSuccess = yes,
mark_task_done(JobCtl, TaskNumber, !IO)
;
TargetSuccess = no,
mark_task_error(JobCtl, TaskNumber, KeepGoing, !IO),
!:Success = no
),
worker_loop(Globals, KeepGoing, MakeTarget, Targets, JobCtl, !Success,
!Info, !IO)
;
% No more tasks.
true
).
:- pred worker_loop_signal_cleanup(job_ctl::in, list(pid)::in,
Info::in, Info::out, io::di, io::uo) is det.
worker_loop_signal_cleanup(JobCtl, Pids, !Info, !IO) :-
mark_abort(JobCtl, !IO),
list.foldl(send_signal(sigint), Pids, !IO).
:- pred reap_worker_process(pid::in, bool::in, bool::out,
io::di, io::uo) is det.
reap_worker_process(Pid, !Success, !IO) :-
wait_pid(Pid, Status, !IO),
(
!.Success = yes,
Status = ok(exited(0))
->
true
;
!:Success = no
).
%-----------------------------------------------------------------------------%
%
% Shared memory IPC for parallel workers
%
:- pragma foreign_decl("C", "
typedef struct MC_JobCtl MC_JobCtl;
").
:- pragma foreign_decl("C", local,
"
#ifdef MR_HAVE_SYS_MMAN_H
#include <sys/mman.h>
/* Just in case. */
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
#define MAP_ANONYMOUS MAP_ANON
#endif
#endif
#ifdef MAP_ANONYMOUS
/*
** Darwin 5.x doesn't implement unnamed POSIX semaphores nor process-shared
** POSIX mutexes; the functions fail when you try to create them.
** System V semaphores do work however.
*/
#if !defined(__APPLE__) && defined(MR_HAVE_PTHREAD_H) && \
defined(MR_HAVE_PTHREAD_MUTEXATTR_SETPSHARED)
#include <pthread.h>
#define MC_HAVE_JOBCTL_IPC 1
#elif defined(MR_HAVE_SYS_SEM_H)
#include <sys/sem.h>
#define MC_USE_SYSV_SEMAPHORE 1
#define MC_HAVE_JOBCTL_IPC 1
#endif
#endif
#ifdef MC_HAVE_JOBCTL_IPC
typedef enum MC_TaskStatus MC_TaskStatus;
enum MC_TaskStatus {
TASK_NEW, /* task not yet attempted */
TASK_ACCEPTED, /* someone is working on this task */
TASK_DONE, /* task successfully completed */
TASK_ERROR /* error occurred when working on the task */
};
/* This structure is placed in shared memory. */
struct MC_JobCtl {
/* Static data. */
MR_Integer jc_total_tasks;
/* Dynamic data. The mutex protects the rest. */
#ifdef MC_USE_SYSV_SEMAPHORE
int jc_semid;
#else
pthread_mutex_t jc_mutex;
#endif
MR_bool jc_abort;
MC_TaskStatus jc_tasks[MR_VARIABLE_SIZED];
};
#define MC_JOB_CTL_SIZE(N) (sizeof(MC_JobCtl) + (N) * sizeof(MC_TaskStatus))
static MC_JobCtl * MC_create_job_ctl(MR_Integer total_tasks);
static void MC_lock_job_ctl(MC_JobCtl *job_ctl);
static void MC_unlock_job_ctl(MC_JobCtl *job_ctl);
#endif /* MC_HAVE_JOBCTL_IPC */
").
:- pragma foreign_code("C", "
#ifdef MC_HAVE_JOBCTL_IPC
static MC_JobCtl *
MC_create_job_ctl(MR_Integer total_tasks)
{
size_t size;
MC_JobCtl *job_ctl;
MR_Integer i;
size = MC_JOB_CTL_SIZE(total_tasks);
/* Create the shared memory segment. */
job_ctl = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_SHARED, -1, 0);
if (job_ctl == (void *) -1) {
perror(""MC_create_job_ctl: mmap"");
return NULL;
}
#ifdef MC_USE_SYSV_SEMAPHORE
{
struct sembuf sb;
job_ctl->jc_semid = semget(IPC_PRIVATE, 1, 0600);
if (job_ctl->jc_semid == -1) {
perror(""MC_create_sem: semget"");
munmap(job_ctl, size);
return NULL;
}
sb.sem_num = 0;
sb.sem_op = 1;
sb.sem_flg = 0;
if (semop(job_ctl->jc_semid, &sb, 1) == -1) {
perror(""MC_create_sem: semop"");
semctl(job_ctl->jc_semid, 0, IPC_RMID);
munmap(job_ctl, size);
return NULL;
}
}
#else
{
pthread_mutexattr_t mutex_attr;
pthread_mutexattr_init(&mutex_attr);
if (pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED)
!= 0)
{
perror(""MC_create_job_ctl: pthread_mutexattr_setpshared"");
pthread_mutexattr_destroy(&mutex_attr);
munmap(job_ctl, size);
return NULL;
}
if (pthread_mutex_init(&job_ctl->jc_mutex, &mutex_attr) != 0) {
perror(""MC_create_job_ctl: sem_init"");
pthread_mutexattr_destroy(&mutex_attr);
munmap(job_ctl, size);
return NULL;
}
pthread_mutexattr_destroy(&mutex_attr);
}
#endif
job_ctl->jc_total_tasks = total_tasks;
job_ctl->jc_abort = MR_FALSE;
for (i = 0; i < total_tasks; i++) {
job_ctl->jc_tasks[i] = TASK_NEW;
}
return job_ctl;
}
static void
MC_lock_job_ctl(MC_JobCtl *job_ctl)
{
#ifdef MC_USE_SYSV_SEMAPHORE
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = -1;
sb.sem_flg = 0;
if (semop(job_ctl->jc_semid, &sb, 1) == -1) {
perror(""MC_lock_job_ctl: semop"");
}
#else
pthread_mutex_lock(&job_ctl->jc_mutex);
#endif
}
static void
MC_unlock_job_ctl(MC_JobCtl *job_ctl)
{
#ifdef MC_USE_SYSV_SEMAPHORE
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = 1;
sb.sem_flg = 0;
if (semop(job_ctl->jc_semid, &sb, 1) == -1) {
perror(""MC_unlock_job_ctl: semop"");
}
#else
pthread_mutex_unlock(&job_ctl->jc_mutex);
#endif
}
#endif /* MC_HAVE_JOBCTL_IPC */
").
:- type job_ctl.
:- pragma foreign_type("C", job_ctl, "MC_JobCtl *").
:- pragma foreign_type("C#", job_ctl, "object"). % stub
:- pragma foreign_type("Java", job_ctl, "java.lang.Object"). % stub
:- pragma foreign_type("Erlang", job_ctl, ""). % stub
:- pred have_job_ctl_ipc is semidet.
have_job_ctl_ipc :-
semidet_fail.
:- pragma foreign_proc("C",
have_job_ctl_ipc,
[will_not_call_mercury, promise_pure, thread_safe, may_not_duplicate],
"
#ifdef MC_HAVE_JOBCTL_IPC
SUCCESS_INDICATOR = MR_TRUE;
#else
SUCCESS_INDICATOR = MR_FALSE;
#endif
").
:- pred create_job_ctl(int::in, maybe(job_ctl)::out, io::di, io::uo) is det.
:- pragma foreign_proc("C",
create_job_ctl(TotalJobs::in, MaybeJobCtl::out, _IO0::di, _IO::uo),
[may_call_mercury, promise_pure, thread_safe, tabled_for_io,
may_not_duplicate],
"
#ifdef MC_HAVE_JOBCTL_IPC
MC_JobCtl *job_ctl;
job_ctl = MC_create_job_ctl(TotalJobs);
if (job_ctl != NULL) {
MaybeJobCtl = MC_make_yes_job_ctl(job_ctl);
} else {
MaybeJobCtl = MC_make_no_job_ctl();
}
#else
MaybeJobCtl = MC_make_no_job_ctl();
#endif
").
create_job_ctl(_, _, _, _) :-
unexpected($file, $pred, "non-C backend").
:- pred destroy_job_ctl(job_ctl::in, io::di, io::uo) is det.
:- pragma foreign_proc("C",
destroy_job_ctl(JobCtl::in, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io,
may_not_duplicate],
"
#ifdef MC_HAVE_JOBCTL_IPC
#ifdef MC_USE_SYSV_SEMAPHORE
semctl(JobCtl->jc_semid, 0, IPC_RMID);
#else
pthread_mutex_destroy(&JobCtl->jc_mutex);
#endif
munmap(JobCtl, MC_JOB_CTL_SIZE(JobCtl->jc_total_tasks));
#endif
").
destroy_job_ctl(_, _, _) :-
unexpected($file, $pred, "non-C backend").
:- pred accept_task(job_ctl::in, int::out, io::di, io::uo) is det.
:- pragma foreign_proc("C",
accept_task(JobCtl::in, TaskNumber::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io,
may_not_duplicate],
"
TaskNumber = -1;
#ifdef MC_HAVE_JOBCTL_IPC
MC_lock_job_ctl(JobCtl);
if (!JobCtl->jc_abort) {
MR_Integer i;
for (i = 0; i < JobCtl->jc_total_tasks; i++) {
if (JobCtl->jc_tasks[i] == TASK_NEW) {
JobCtl->jc_tasks[i] = TASK_ACCEPTED;
TaskNumber = i;
break;
}
}
}
MC_unlock_job_ctl(JobCtl);
#endif
").
accept_task(_, _, _, _) :-
unexpected($file, $pred, "non-C backend").
:- pred mark_task_done(job_ctl::in, int::in, io::di, io::uo) is det.
:- pragma foreign_proc("C",
mark_task_done(JobCtl::in, TaskNumber::in, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io,
may_not_duplicate],
"
#ifdef MC_HAVE_JOBCTL_IPC
MC_lock_job_ctl(JobCtl);
JobCtl->jc_tasks[TaskNumber] = TASK_DONE;
MC_unlock_job_ctl(JobCtl);
#endif
").
mark_task_done(_, _, _, _) :-
unexpected($file, $pred, "non-C backend").
:- pred mark_task_error(job_ctl::in, int::in, bool::in, io::di, io::uo) is det.
:- pragma foreign_proc("C",
mark_task_error(JobCtl::in, TaskNumber::in, KeepGoing::in,
_IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io,
may_not_duplicate],
"
#ifdef MC_HAVE_JOBCTL_IPC
MC_lock_job_ctl(JobCtl);
JobCtl->jc_tasks[TaskNumber] = TASK_ERROR;
if (!KeepGoing) {
JobCtl->jc_abort = MR_TRUE;
}
MC_unlock_job_ctl(JobCtl);
#endif
").
mark_task_error(_, _, _, _, _) :-
unexpected($file, $pred, "non-C backend").
:- pred mark_abort(job_ctl::in, io::di, io::uo) is det.
:- pragma foreign_proc("C",
mark_abort(JobCtl::in, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io,
may_not_duplicate],
"
#ifdef MC_HAVE_JOBCTL_IPC
MC_lock_job_ctl(JobCtl);
JobCtl->jc_abort = MR_TRUE;
MC_unlock_job_ctl(JobCtl);
#endif
").
mark_abort(_, _, _) :-
unexpected($file, $pred, "non-C backend").
:- func make_yes_job_ctl(job_ctl) = maybe(job_ctl).
:- pragma foreign_export("C", make_yes_job_ctl(in) = out,
"MC_make_yes_job_ctl").
make_yes_job_ctl(JobCtl) = yes(JobCtl).
:- func make_no_job_ctl = maybe(job_ctl).
:- pragma foreign_export("C", make_no_job_ctl = out,
"MC_make_no_job_ctl").
make_no_job_ctl = no.
%-----------------------------------------------------------------------------%
%
% Prevent interleaved error output
%
% We reuse the job_ctl type.
%
:- type stdout_lock == job_ctl.
:- pred lock_stdout(stdout_lock::in, io::di, io::uo) is det.
:- pragma foreign_proc("C",
lock_stdout(JobCtl::in, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io,
may_not_duplicate],
"
#ifdef MC_HAVE_JOBCTL_IPC
MC_lock_job_ctl(JobCtl);
#endif
").
lock_stdout(_, !IO).
:- pred unlock_stdout(stdout_lock::in, io::di, io::uo) is det.
:- pragma foreign_proc("C",
unlock_stdout(JobCtl::in, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io,
may_not_duplicate],
"
#ifdef MC_HAVE_JOBCTL_IPC
MC_unlock_job_ctl(JobCtl);
#endif
").
unlock_stdout(_, !IO).
:- pred with_locked_stdout(make_info::in,
pred(io, io)::in(pred(di, uo) is det), io::di, io::uo) is det.
with_locked_stdout(Info, Pred, !IO) :-
MaybeLock = Info ^ maybe_stdout_lock,
(
MaybeLock = yes(Lock),
lock_stdout(Lock, !IO),
Pred(!IO),
unlock_stdout(Lock, !IO)
;
MaybeLock = no,
Pred(!IO)
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
build_with_module_options_and_output_redirect(Globals, ModuleName,
ExtraOptions, Build, Succeeded, !Info, !IO) :-
build_with_module_options(Globals, ModuleName, ExtraOptions,
build_with_module_options_and_output_redirect_2(ModuleName, Build),
Succeeded, !Info, !IO).
:- pred build_with_module_options_and_output_redirect_2(module_name::in,
build2(list(string), io.output_stream)::in(build2), globals::in,
list(string)::in, bool::out, make_info::in, make_info::out,
io::di, io::uo) is det.
build_with_module_options_and_output_redirect_2(ModuleName, Build, Globals,
AllOptions, Succeeded, !Info, !IO) :-
build_with_output_redirect(Globals, ModuleName,
build_with_module_options_and_output_redirect_3(AllOptions, Build),
Succeeded, !Info, !IO).
:- pred build_with_module_options_and_output_redirect_3(list(string)::in,
build2(list(string), io.output_stream)::in(build2), globals::in,
io.output_stream::in, bool::out, make_info::in, make_info::out,
io::di, io::uo) is det.
build_with_module_options_and_output_redirect_3(AllOptions, Build, Globals,
ErrorStream, Succeeded, !Info, !IO) :-
Build(Globals, AllOptions, ErrorStream, Succeeded, !Info, !IO).
build_with_output_redirect(Globals, ModuleName, Build, Succeeded, !Info,
!IO) :-
redirect_output(ModuleName, RedirectResult, !Info, !IO),
(
RedirectResult = no,
Succeeded = no
;
RedirectResult = yes(ErrorStream),
Build(Globals, ErrorStream, Succeeded, !Info, !IO),
unredirect_output(Globals, ModuleName, ErrorStream, !Info, !IO)
).
build_with_module_options(Globals, ModuleName, ExtraOptions, Build, Succeeded,
!Info, !IO) :-
build_with_module_options_args_invoked(Globals, yes, ModuleName,
!.Info ^ options_variables, !.Info ^ option_args, ExtraOptions, Build,
Succeeded, !.Info, MaybeInfo, !IO),
(
MaybeInfo = yes(!:Info)
;
MaybeInfo = no
).
build_with_module_options_args(Globals, ModuleName, OptionVariables,
OptionArgs, ExtraOptions, Build, Succeeded, !Info, !IO) :-
build_with_module_options_args_invoked(Globals, no, ModuleName,
OptionVariables, OptionArgs, ExtraOptions, Build, Succeeded,
!Info, !IO).
:- pred build_with_module_options_args_invoked(globals::in, bool::in,
module_name::in, options_variables::in, list(string)::in, list(string)::in,
build(list(string), Info1, Info2)::in(build),
bool::out, Info1::in, maybe(Info2)::out, io::di, io::uo) is det.
build_with_module_options_args_invoked(Globals, InvokedByMmcMake, ModuleName,
OptionVariables, OptionArgs, ExtraOptions, Build, Succeeded,
Info0, MaybeInfo, !IO) :-
lookup_mmc_module_options(Globals, OptionVariables, ModuleName,
OptionsResult, !IO),
(
OptionsResult = no,
MaybeInfo = no,
Succeeded = no
;
OptionsResult = yes(ModuleOptionArgs),
% --invoked-by-mmc-make disables reading DEFAULT_MCFLAGS from the
% environment (DEFAULT_MCFLAGS is included in OptionArgs) and
% generation of `.d' files. --use-subdirs is needed because
% the code to install libraries uses `--use-grade-subdirs' and
% assumes the interface files were built with `--use-subdirs'.
(
InvokedByMmcMake = yes,
UseSubdirs = ["--use-subdirs"],
InvokedByMake = ["--invoked-by-mmc-make"]
;
InvokedByMmcMake = no,
UseSubdirs = [],
InvokedByMake = []
),
AllOptionArgs = InvokedByMake ++ ModuleOptionArgs ++
OptionArgs ++ ExtraOptions ++ UseSubdirs,
handle_given_options(AllOptionArgs, _, _, _,
OptionsErrors, BuildGlobals, !IO),
(
OptionsErrors = [_ | _],
Succeeded = no,
MaybeInfo = no,
usage_errors(OptionsErrors, !IO)
;
OptionsErrors = [],
Build(BuildGlobals, AllOptionArgs, Succeeded, Info0, Info, !IO),
MaybeInfo = yes(Info)
)
).
redirect_output(_ModuleName, MaybeErrorStream, !Info, !IO) :-
% Write the output to a temporary file first, so it's easy to just print
% the part of the error file that relates to the current command. It will
% be appended to the error file later.
io.make_temp(ErrorFileName, !IO),
io.open_output(ErrorFileName, ErrorFileRes, !IO),
(
ErrorFileRes = ok(ErrorOutputStream),
MaybeErrorStream = yes(ErrorOutputStream)
;
ErrorFileRes = error(IOError),
MaybeErrorStream = no,
with_locked_stdout(!.Info,
write_error_opening_output(ErrorFileName, IOError), !IO)
).
unredirect_output(Globals, ModuleName, ErrorOutputStream, !Info, !IO) :-
io.output_stream_name(ErrorOutputStream, TmpErrorFileName, !IO),
io.close_output(ErrorOutputStream, !IO),
io.open_input(TmpErrorFileName, TmpErrorInputRes, !IO),
(
TmpErrorInputRes = ok(TmpErrorInputStream),
module_name_to_file_name(Globals, ModuleName, ".err", do_create_dirs,
ErrorFileName, !IO),
( set.member(ModuleName, !.Info ^ error_file_modules) ->
io.open_append(ErrorFileName, ErrorFileRes, !IO)
;
io.open_output(ErrorFileName, ErrorFileRes, !IO)
),
(
ErrorFileRes = ok(ErrorFileOutputStream),
globals.lookup_int_option(Globals, output_compile_error_lines,
LinesToWrite),
io.output_stream(CurrentOutputStream, !IO),
with_locked_stdout(!.Info,
make_write_error_streams(TmpErrorFileName, TmpErrorInputStream,
ErrorFileOutputStream, CurrentOutputStream, LinesToWrite),
!IO),
io.close_output(ErrorFileOutputStream, !IO),
!Info ^ error_file_modules :=
set.insert(!.Info ^ error_file_modules, ModuleName)
;
ErrorFileRes = error(Error),
with_locked_stdout(!.Info,
write_error_opening_file(TmpErrorFileName, Error), !IO)
),
io.close_input(TmpErrorInputStream, !IO)
;
TmpErrorInputRes = error(Error),
with_locked_stdout(!.Info,
write_error_opening_file(TmpErrorFileName, Error), !IO)
),
io.remove_file(TmpErrorFileName, _, !IO).
:- pred make_write_error_streams(string::in, io.input_stream::in,
io.output_stream::in, io.output_stream::in, int::in, io::di, io::uo)
is det.
make_write_error_streams(FileName, InputStream, FullOutputStream,
PartialOutputStream, LinesToWrite, !IO) :-
io.input_stream_foldl2_io(InputStream,
make_write_error_char(FullOutputStream, PartialOutputStream),
LinesToWrite, Res, !IO),
(
Res = ok(_)
;
Res = error(_, Error),
io.format("Error reading `%s': %s\n",
[s(FileName), s(io.error_message(Error))], !IO)
).
:- pred make_write_error_char(io.output_stream::in, io.output_stream::in,
char::in, int::in, int::out, io::di, io::uo) is det.
make_write_error_char(FullOutputStream, PartialOutputStream, Char,
!LinesRemaining, !IO) :-
io.write_char(FullOutputStream, Char, !IO),
( !.LinesRemaining > 0 ->
io.write_char(PartialOutputStream, Char, !IO),
( Char = '\n' ->
!:LinesRemaining = !.LinesRemaining - 1
;
true
)
; !.LinesRemaining = 0 ->
io.output_stream_name(FullOutputStream, FullOutputFileName, !IO),
io.write_string(PartialOutputStream, "... error log truncated, see `",
!IO),
io.write_string(PartialOutputStream, FullOutputFileName, !IO),
io.write_string(PartialOutputStream, "' for the complete log.\n", !IO),
% Only write the above message once.
!:LinesRemaining = -1
;
true
).
:- pred write_error_opening_output(string::in, io.error::in,
io::di, io::uo) is det.
write_error_opening_output(FileName, Error, !IO) :-
io.format("** Error opening `%s' for output: %s\n",
[s(FileName), s(io.error_message(Error))], !IO).
:- pred write_error_opening_file(string::in, io.error::in, io::di, io::uo)
is det.
write_error_opening_file(FileName, Error, !IO) :-
io.format("Error opening `%s': %s\n",
[s(FileName), s(io.error_message(Error))], !IO).
%-----------------------------------------------------------------------------%
get_timestamp_file_timestamp(Globals, target_file(ModuleName, FileType),
MaybeTimestamp, !Info, !IO) :-
( TimestampExt = timestamp_extension(Globals, FileType) ->
module_name_to_file_name(Globals, ModuleName, TimestampExt,
do_not_create_dirs, FileName, !IO)
;
module_target_to_file_name(Globals, ModuleName, FileType,
do_not_create_dirs, FileName, !IO)
),
% We should only ever look for timestamp files in the current directory.
% Timestamp files are only used when processing a module, and only modules
% in the current directory are processed.
SearchDirs = [dir.this_directory],
get_file_timestamp(SearchDirs, FileName, MaybeTimestamp, !Info, !IO).
get_dependency_timestamp(Globals, DependencyFile, MaybeTimestamp, !Info,
!IO) :-
(
DependencyFile = dep_file(FileName, MaybeOption),
(
MaybeOption = yes(Option),
globals.lookup_accumulating_option(Globals, Option, SearchDirs)
;
MaybeOption = no,
SearchDirs = [dir.this_directory]
),
get_file_timestamp(SearchDirs, FileName, MaybeTimestamp, !Info, !IO)
;
DependencyFile = dep_target(Target),
get_target_timestamp(Globals, do_search, Target, MaybeTimestamp0,
!Info, !IO),
(
Target = target_file(_, module_target_c_header(header_mih)),
MaybeTimestamp0 = ok(_)
->
% Don't rebuild the `.o' file if an irrelevant part of a
% `.mih' file has changed. If a relevant part of a `.mih'
% file changed, the interface files of the imported module
% must have changed in a way that would force the `.c' and
% `.o' files of the current module to be rebuilt.
MaybeTimestamp = ok(oldest_timestamp)
;
MaybeTimestamp = MaybeTimestamp0
)
).
get_target_timestamp(Globals, Search, TargetFile, MaybeTimestamp, !Info,
!IO) :-
TargetFile = target_file(_ModuleName, FileType),
get_file_name(Globals, Search, TargetFile, FileName, !Info, !IO),
( FileType = module_target_analysis_registry ->
get_target_timestamp_analysis_registry(Globals, Search, TargetFile,
FileName, MaybeTimestamp, !Info, !IO)
;
get_target_timestamp_2(Globals, Search, TargetFile,
FileName, MaybeTimestamp, !Info, !IO)
).
% Special treatment for `.analysis' files. If the corresponding
% `.analysis_status' file says the `.analysis' file is invalid then we
% treat it as out of date.
%
:- pred get_target_timestamp_analysis_registry(globals::in, maybe_search::in,
target_file::in, file_name::in, maybe_error(timestamp)::out,
make_info::in, make_info::out, io::di, io::uo) is det.
get_target_timestamp_analysis_registry(Globals, Search, TargetFile, FileName,
MaybeTimestamp, !Info, !IO) :-
TargetFile = target_file(ModuleName, _FileType),
( MaybeTimestamp0 = !.Info ^ file_timestamps ^ elem(FileName) ->
MaybeTimestamp = MaybeTimestamp0
;
do_read_module_overall_status(mmc, Globals, ModuleName, Status, !IO),
(
( Status = optimal
; Status = suboptimal
),
get_target_timestamp_2(Globals, Search, TargetFile, FileName,
MaybeTimestamp, !Info, !IO)
;
Status = invalid,
MaybeTimestamp = error("invalid module"),
!Info ^ file_timestamps ^ elem(FileName) := MaybeTimestamp
)
).
:- pred get_target_timestamp_2(globals::in, maybe_search::in, target_file::in,
file_name::in, maybe_error(timestamp)::out, make_info::in, make_info::out,
io::di, io::uo) is det.
get_target_timestamp_2(Globals, Search, TargetFile, FileName, MaybeTimestamp,
!Info, !IO) :-
TargetFile = target_file(ModuleName, FileType),
(
Search = do_search,
get_search_directories(Globals, FileType, SearchDirs)
;
Search = do_not_search,
SearchDirs = [dir.this_directory]
),
get_file_timestamp(SearchDirs, FileName, MaybeTimestamp0, !Info, !IO),
(
MaybeTimestamp0 = error(_),
( FileType = module_target_intermodule_interface
; FileType = module_target_analysis_registry
)
->
% If a `.opt' file in another directory doesn't exist,
% it just means that a library wasn't compiled with
% `--intermodule-optimization'.
% Similarly for `.analysis' files.
get_module_dependencies(Globals, ModuleName, MaybeImports, !Info, !IO),
(
MaybeImports = yes(Imports),
Imports ^ mai_module_dir \= dir.this_directory
->
MaybeTimestamp = ok(oldest_timestamp),
!:Info = !.Info ^ file_timestamps ^ elem(FileName)
:= MaybeTimestamp
;
MaybeTimestamp = MaybeTimestamp0
)
;
MaybeTimestamp = MaybeTimestamp0
).
get_file_name(Globals, Search, TargetFile, FileName, !Info, !IO) :-
TargetFile = target_file(ModuleName, FileType),
( FileType = module_target_source ->
% In some cases the module name won't match the file name
% (module mdb.parse might be in parse.m or mdb.m), so we need to
% look up the file name here.
get_module_dependencies(Globals, ModuleName, MaybeImports, !Info, !IO),
(
MaybeImports = yes(Imports),
FileName = Imports ^ mai_source_file_name
;
MaybeImports = no,
% Something has gone wrong generating the dependencies,
% so just take a punt (which probably won't work).
module_name_to_file_name(Globals, ModuleName, ".m",
do_not_create_dirs, FileName, !IO)
)
;
MaybeExt = target_extension(Globals, FileType),
(
MaybeExt = yes(Ext),
(
Search = do_search,
module_name_to_search_file_name_cache(Globals, ModuleName, Ext,
FileName, !Info, !IO)
;
Search = do_not_search,
% Not common enough to cache.
module_name_to_file_name(Globals, ModuleName, Ext,
do_not_create_dirs, FileName, !IO)
)
;
MaybeExt = no,
module_target_to_file_name_maybe_search(Globals, ModuleName,
FileType, do_not_create_dirs, Search, FileName, !IO)
)
).
:- pred module_name_to_search_file_name_cache(globals::in, module_name::in,
string::in, string::out, make_info::in, make_info::out, io::di, io::uo)
is det.
module_name_to_search_file_name_cache(Globals, ModuleName, Ext, FileName,
!Info, !IO) :-
Key = ModuleName - Ext,
( map.search(!.Info ^ search_file_name_cache, Key, FileName0) ->
FileName = FileName0
;
module_name_to_search_file_name(Globals, ModuleName, Ext, FileName,
!IO),
!Info ^ search_file_name_cache ^ elem(Key) := FileName
).
get_file_timestamp(SearchDirs, FileName, MaybeTimestamp, !Info, !IO) :-
( MaybeTimestamp0 = !.Info ^ file_timestamps ^ elem(FileName) ->
MaybeTimestamp = MaybeTimestamp0
;
search_for_file_mod_time(SearchDirs, FileName, SearchResult, !IO),
(
SearchResult = ok(TimeT),
Timestamp = time_t_to_timestamp(TimeT),
MaybeTimestamp = ok(Timestamp),
!Info ^ file_timestamps ^ elem(FileName) := MaybeTimestamp
;
SearchResult = error(_),
MaybeTimestamp = error("file `" ++ FileName ++ "' not found")
)
).
:- pred get_search_directories(globals::in, module_target_type::in,
list(dir_name)::out) is det.
get_search_directories(Globals, FileType, SearchDirs) :-
MaybeOpt = search_for_file_type(FileType),
(
MaybeOpt = yes(SearchDirOpt),
globals.lookup_accumulating_option(Globals, SearchDirOpt, SearchDirs0),
% Make sure the current directory is searched for C headers
% and libraries.
( list.member(dir.this_directory, SearchDirs0) ->
SearchDirs = SearchDirs0
;
SearchDirs = [dir.this_directory | SearchDirs0]
)
;
MaybeOpt = no,
SearchDirs = [dir.this_directory]
).
find_oldest_timestamp(error(_) @ MaybeTimestamp, _) = MaybeTimestamp.
find_oldest_timestamp(ok(_), error(_) @ MaybeTimestamp) = MaybeTimestamp.
find_oldest_timestamp(ok(Timestamp1), ok(Timestamp2)) = ok(Timestamp) :-
( compare((<), Timestamp1, Timestamp2) ->
Timestamp = Timestamp1
;
Timestamp = Timestamp2
).
%-----------------------------------------------------------------------------%
make_remove_target_file(Globals, VerboseOption, Target, !Info, !IO) :-
Target = target_file(ModuleName, FileType),
make_remove_target_file_by_name(Globals, VerboseOption,
ModuleName, FileType, !Info, !IO).
make_remove_target_file_by_name(Globals, VerboseOption, ModuleName, FileType,
!Info, !IO) :-
module_target_to_file_name(Globals, ModuleName, FileType,
do_not_create_dirs, FileName, !IO),
make_remove_file(Globals, VerboseOption, FileName, !Info, !IO),
( TimestampExt = timestamp_extension(Globals, FileType) ->
make_remove_module_file(Globals, VerboseOption, ModuleName,
TimestampExt, !Info, !IO)
;
true
).
make_remove_module_file(Globals, VerboseOption, ModuleName, Ext, !Info, !IO) :-
module_name_to_file_name(Globals, ModuleName, Ext,
do_not_create_dirs, FileName, !IO),
make_remove_file(Globals, VerboseOption, FileName, !Info, !IO).
make_remove_file(Globals, VerboseOption, FileName, !Info, !IO) :-
verbose_msg_option(Globals, VerboseOption, report_remove_file(FileName),
!IO),
io.remove_file_recursively(FileName, _, !IO),
!Info ^ file_timestamps :=
map.delete(!.Info ^ file_timestamps, FileName).
:- pred report_remove_file(string::in, io::di, io::uo) is det.
report_remove_file(FileName, !IO) :-
io.write_string("Removing ", !IO),
io.write_string(FileName, !IO),
io.nl(!IO).
%-----------------------------------------------------------------------------%
make_target_file_list(ModuleNames, FileType) =
list.map((func(ModuleName) = target_file(ModuleName, FileType)),
ModuleNames).
make_dependency_list(ModuleNames, FileType) =
list.map((func(Module) = dep_target(target_file(Module, FileType))),
ModuleNames).
target_extension(_, module_target_source) = yes(".m").
target_extension(_, module_target_errors) = yes(".err").
target_extension(_, module_target_private_interface) = yes(".int0").
target_extension(_, module_target_long_interface) = yes(".int").
target_extension(_, module_target_short_interface) = yes(".int2").
target_extension(_, module_target_unqualified_short_interface) = yes(".int3").
target_extension(_, module_target_intermodule_interface) = yes(".opt").
target_extension(_, module_target_analysis_registry) = yes(".analysis").
target_extension(_, module_target_track_flags) = yes(".track_flags").
target_extension(_, module_target_c_header(header_mih)) = yes(".mih").
target_extension(_, module_target_c_header(header_mh)) = yes(".mh").
target_extension(_, module_target_c_code) = yes(".c").
target_extension(_, module_target_il_code) = yes(".il").
% XXX ".exe" if the module contains main.
target_extension(_, module_target_il_asm) = yes(".dll").
target_extension(_, module_target_csharp_code) = yes(".cs").
target_extension(_, module_target_java_code) = yes(".java").
target_extension(_, module_target_java_class_code) = yes(".class").
target_extension(_, module_target_erlang_header) = yes(".hrl").
target_extension(_, module_target_erlang_code) = yes(".erl").
target_extension(_, module_target_erlang_beam_code) = yes(".beam").
target_extension(_, module_target_asm_code(non_pic)) = yes(".s").
target_extension(_, module_target_asm_code(link_with_pic)) = yes(".s").
target_extension(_, module_target_asm_code(pic)) = yes(".pic_s").
target_extension(Globals, module_target_object_code(PIC)) = yes(Ext) :-
maybe_pic_object_file_extension(Globals, PIC, Ext).
target_extension(_, module_target_xml_doc) = yes(".xml").
% These all need to be handled as special cases.
target_extension(_, module_target_foreign_object(_, _)) = no.
target_extension(_, module_target_foreign_il_asm(_)) = no.
target_extension(_, module_target_fact_table_object(_, _)) = no.
% Currently the .cs extension is still treated as the build-all target for
% C files, so we accept .csharp for C# files.
target_extension_synonym(".csharp", module_target_csharp_code).
linked_target_file_name(Globals, ModuleName, TargetType, FileName, !IO) :-
(
TargetType = executable,
globals.lookup_string_option(Globals, executable_file_extension, Ext),
module_name_to_file_name(Globals, ModuleName, Ext,
do_not_create_dirs, FileName, !IO)
;
TargetType = static_library,
globals.lookup_string_option(Globals, library_extension, Ext),
module_name_to_lib_file_name(Globals, "lib", ModuleName, Ext,
do_not_create_dirs, FileName, !IO)
;
TargetType = shared_library,
globals.lookup_string_option(Globals, shared_library_extension, Ext),
module_name_to_lib_file_name(Globals, "lib", ModuleName, Ext,
do_not_create_dirs, FileName, !IO)
;
TargetType = csharp_executable,
module_name_to_file_name(Globals, ModuleName, ".exe",
do_not_create_dirs, FileName, !IO)
;
TargetType = csharp_library,
module_name_to_file_name(Globals, ModuleName, ".dll",
do_not_create_dirs, FileName, !IO)
;
( TargetType = java_launcher
; TargetType = erlang_launcher
),
% These are shell scripts.
module_name_to_file_name(Globals, ModuleName, "",
do_not_create_dirs, FileName, !IO)
;
TargetType = java_archive,
module_name_to_file_name(Globals, ModuleName, ".jar",
do_not_create_dirs, FileName, !IO)
;
TargetType = erlang_archive,
module_name_to_lib_file_name(Globals, "lib", ModuleName, ".beams",
do_not_create_dirs, FileName, !IO)
).
:- pred module_target_to_file_name(globals::in, module_name::in,
module_target_type::in, maybe_create_dirs::in, file_name::out,
io::di, io::uo) is det.
module_target_to_file_name(Globals, ModuleName, TargetType, MkDir, FileName,
!IO) :-
module_target_to_file_name_maybe_search(Globals, ModuleName, TargetType,
MkDir, do_not_search, FileName, !IO).
:- pred module_target_to_search_file_name(globals::in, module_name::in,
module_target_type::in, file_name::out, io::di, io::uo) is det.
module_target_to_search_file_name(Globals, ModuleName, TargetType, FileName,
!IO) :-
module_target_to_file_name_maybe_search(Globals, ModuleName, TargetType,
do_not_create_dirs, do_search, FileName, !IO).
:- pred module_target_to_file_name_maybe_search(globals::in, module_name::in,
module_target_type::in, maybe_create_dirs::in, maybe_search::in,
file_name::out, io::di, io::uo) is det.
module_target_to_file_name_maybe_search(Globals, ModuleName, TargetType,
MkDir, Search, FileName, !IO) :-
target_extension(Globals, TargetType) = MaybeExt,
(
MaybeExt = yes(Ext),
(
Search = do_search,
module_name_to_search_file_name(Globals, ModuleName, Ext, FileName,
!IO)
;
Search = do_not_search,
module_name_to_file_name(Globals, ModuleName, Ext, MkDir,
FileName, !IO)
)
;
MaybeExt = no,
(
TargetType = module_target_foreign_object(PIC, Lang),
(
ForeignModuleName =
foreign_language_module_name(ModuleName, Lang)
->
module_target_to_file_name_maybe_search(Globals,
ForeignModuleName, module_target_object_code(PIC), MkDir,
Search, FileName, !IO)
;
unexpected($module, $pred, "object test failed")
)
;
TargetType = module_target_foreign_il_asm(Lang),
(
ForeignModuleName =
foreign_language_module_name(ModuleName, Lang)
->
module_target_to_file_name_maybe_search(Globals,
ForeignModuleName, module_target_il_asm, MkDir,
Search, FileName, !IO)
;
unexpected($module, $pred, "ilasm test failed")
)
;
TargetType = module_target_fact_table_object(PIC, FactFile),
maybe_pic_object_file_extension(Globals, PIC, Ext),
fact_table_file_name(Globals, ModuleName, FactFile, Ext, MkDir,
FileName, !IO)
;
( TargetType = module_target_analysis_registry
; TargetType = module_target_asm_code(_)
; TargetType = make.module_target_c_code
; TargetType = module_target_c_header(_)
; TargetType = module_target_erlang_beam_code
; TargetType = module_target_erlang_code
; TargetType = module_target_erlang_header
; TargetType = module_target_errors
; TargetType = make.module_target_il_asm
; TargetType = module_target_il_code
; TargetType = module_target_intermodule_interface
; TargetType = module_target_csharp_code
; TargetType = module_target_java_code
; TargetType = module_target_java_class_code
; TargetType = module_target_long_interface
; TargetType = module_target_object_code(_)
; TargetType = module_target_private_interface
; TargetType = module_target_short_interface
; TargetType = module_target_source
; TargetType = module_target_unqualified_short_interface
; TargetType = module_target_xml_doc
; TargetType = module_target_track_flags
),
unexpected($module, $pred, "unexpected TargetType")
)
).
% Note that we need a timestamp file for `.err' files because
% errors are written to the `.err' file even when writing interfaces.
% The timestamp is only updated when compiling to target code.
%
% We need a timestamp file for `.analysis' files because they
% can be modified in the process of analysing _another_ module.
% The timestamp is only updated after actually analysing the module that
% the `.analysis' file corresponds to.
%
% Header files share a timestamp file with their corresponding target code
% files.
%
timestamp_extension(_, module_target_errors) = ".err_date".
timestamp_extension(_, module_target_private_interface) = ".date0".
timestamp_extension(_, module_target_long_interface) = ".date".
timestamp_extension(_, module_target_short_interface) = ".date".
timestamp_extension(_, module_target_unqualified_short_interface) = ".date3".
timestamp_extension(_, module_target_intermodule_interface) = ".optdate".
timestamp_extension(_, module_target_analysis_registry) = ".analysis_date".
timestamp_extension(_, module_target_c_code) = ".c_date".
timestamp_extension(Globals, module_target_c_header(_)) = Ext :-
globals.get_target(Globals, Target),
(
Target = target_asm,
ModuleTargetType = module_target_asm_code(non_pic)
;
% XXX Some of these alternatives don't look right.
( Target = target_c
; Target = target_x86_64
; Target = target_il
; Target = target_csharp
; Target = target_java
; Target = target_erlang
),
ModuleTargetType = module_target_c_code
),
Ext = timestamp_extension(Globals, ModuleTargetType).
timestamp_extension(_, module_target_il_code) = ".il_date".
timestamp_extension(_, module_target_csharp_code) = ".cs_date".
timestamp_extension(_, module_target_java_code) = ".java_date".
timestamp_extension(_, module_target_erlang_code) = ".erl_date".
timestamp_extension(Globals, module_target_erlang_header) =
timestamp_extension(Globals, module_target_erlang_code).
timestamp_extension(_, module_target_asm_code(non_pic)) = ".s_date".
timestamp_extension(_, module_target_asm_code(pic)) = ".pic_s_date".
:- func search_for_file_type(module_target_type) = maybe(option).
search_for_file_type(module_target_source) = no.
search_for_file_type(module_target_errors) = no.
% XXX only for inter-module optimization.
search_for_file_type(module_target_private_interface) =
yes(search_directories).
search_for_file_type(module_target_long_interface) = yes(search_directories).
search_for_file_type(module_target_short_interface) = yes(search_directories).
search_for_file_type(module_target_unqualified_short_interface) =
yes(search_directories).
search_for_file_type(module_target_intermodule_interface) =
yes(intermod_directories).
search_for_file_type(module_target_analysis_registry) =
yes(intermod_directories).
search_for_file_type(module_target_track_flags) = no.
search_for_file_type(module_target_c_header(_)) = yes(c_include_directory).
search_for_file_type(module_target_c_code) = no.
search_for_file_type(module_target_il_code) = no.
search_for_file_type(module_target_il_asm) = no.
search_for_file_type(module_target_csharp_code) = no.
search_for_file_type(module_target_java_code) = no.
search_for_file_type(module_target_java_class_code) = no.
search_for_file_type(module_target_erlang_header) =
yes(erlang_include_directory).
search_for_file_type(module_target_erlang_code) = no.
search_for_file_type(module_target_erlang_beam_code) = no.
search_for_file_type(module_target_asm_code(_)) = no.
search_for_file_type(module_target_object_code(_)) = no.
search_for_file_type(module_target_foreign_object(_, _)) = no.
search_for_file_type(module_target_foreign_il_asm(_)) = no.
search_for_file_type(module_target_fact_table_object(_, _)) = no.
search_for_file_type(module_target_xml_doc) = no.
target_is_grade_or_arch_dependent(Target) :-
is_target_grade_or_arch_dependent(Target) = yes.
:- func is_target_grade_or_arch_dependent(module_target_type) = bool.
is_target_grade_or_arch_dependent(Target) = IsDependent :-
(
( Target = module_target_source
; Target = module_target_errors
; Target = module_target_private_interface
; Target = module_target_long_interface
; Target = module_target_short_interface
; Target = module_target_unqualified_short_interface
; Target = module_target_c_header(header_mh)
; Target = module_target_xml_doc
),
IsDependent = no
;
( Target = module_target_intermodule_interface
; Target = module_target_analysis_registry
; Target = module_target_track_flags
; Target = module_target_c_header(header_mih)
; Target = module_target_c_code
; Target = module_target_il_code
; Target = module_target_il_asm
; Target = module_target_csharp_code
; Target = module_target_java_code
; Target = module_target_java_class_code
; Target = module_target_erlang_code
; Target = module_target_erlang_beam_code
; Target = module_target_erlang_header
; Target = module_target_asm_code(_)
; Target = module_target_object_code(_)
; Target = module_target_foreign_object(_, _)
; Target = module_target_foreign_il_asm(_)
; Target = module_target_fact_table_object(_, _)
),
IsDependent = yes
).
%-----------------------------------------------------------------------------%
debug_msg(Globals, P, !IO) :-
verbose_msg_option(Globals, debug_make, P, !IO).
verbose_msg(Globals, P, !IO) :-
verbose_msg_option(Globals, verbose_make, P, !IO).
verbose_msg_option(Globals, Option, P, !IO) :-
globals.lookup_bool_option(Globals, Option, OptionValue),
(
OptionValue = yes,
P(!IO),
io.flush_output(!IO)
;
OptionValue = no
).
debug_file_msg(Globals, TargetFile, Msg, !IO) :-
debug_msg(Globals,
(pred(!.IO::di, !:IO::uo) is det :-
make_write_target_file(Globals, TargetFile, !IO),
io.write_string(": ", !IO),
io.write_string(Msg, !IO),
io.nl(!IO)
), !IO).
make_write_dependency_file(Globals, dep_target(TargetFile), !IO) :-
make_write_target_file(Globals, TargetFile, !IO).
make_write_dependency_file(_Globals, dep_file(FileName, _), !IO) :-
io.write_string(FileName, !IO).
make_write_dependency_file_list(_, [], !IO).
make_write_dependency_file_list(Globals, [DepFile | DepFiles], !IO) :-
io.write_string("\t", !IO),
make_write_dependency_file(Globals, DepFile, !IO),
io.nl(!IO),
make_write_dependency_file_list(Globals, DepFiles, !IO).
make_write_target_file(Globals, TargetFile, !IO) :-
make_write_target_file_wrapped(Globals, "", TargetFile, "", !IO).
make_write_target_file_wrapped(Globals, Prefix, TargetFile, Suffix, !IO) :-
TargetFile = target_file(ModuleName, FileType),
module_target_to_file_name(Globals, ModuleName, FileType,
do_not_create_dirs, FileName, !IO),
(
Prefix = "",
Suffix = ""
->
io.write_string(FileName, !IO)
;
% Try to write this with one call to avoid interleaved output when
% doing parallel builds.
io.write_string(Prefix ++ FileName ++ Suffix, !IO)
).
maybe_make_linked_target_message(Globals, TargetFile, !IO) :-
verbose_msg(Globals,
(pred(!.IO::di, !:IO::uo) is det :-
% Try to write this with one call to avoid interleaved output
% when doing parallel builds.
io.write_string("Making " ++ TargetFile ++ "\n", !IO)
), !IO).
maybe_make_target_message(Globals, TargetFile, !IO) :-
io.output_stream(OutputStream, !IO),
maybe_make_target_message_to_stream(Globals, OutputStream, TargetFile,
!IO).
maybe_make_target_message_to_stream(Globals, OutputStream, TargetFile, !IO) :-
verbose_msg(Globals,
(pred(!.IO::di, !:IO::uo) is det :-
io.set_output_stream(OutputStream, OldOutputStream, !IO),
make_write_target_file_wrapped(Globals, "Making ", TargetFile,
"\n", !IO),
io.set_output_stream(OldOutputStream, _, !IO)
), !IO).
maybe_reanalyse_modules_message(Globals, !IO) :-
verbose_msg(Globals,
(pred(!.IO::di, !:IO::uo) is det :-
io.output_stream(OutputStream, !IO),
io.write_string(OutputStream,
"Reanalysing invalid/suboptimal modules\n", !IO)
), !IO).
target_file_error(Info, Globals, TargetFile, !IO) :-
with_locked_stdout(Info,
make_write_target_file_wrapped(Globals,
"** Error making `", TargetFile, "'.\n"), !IO).
file_error(Info, TargetFile, !IO) :-
with_locked_stdout(Info,
io.write_string("** Error making `" ++ TargetFile ++ "'.\n"), !IO).
maybe_warn_up_to_date_target(Globals, Target, !Info, !IO) :-
globals.lookup_bool_option(Globals, warn_up_to_date, Warn),
(
Warn = yes,
( set.member(Target, !.Info ^ command_line_targets) ->
io.write_string("** Nothing to be done for `", !IO),
make_write_module_or_linked_target(Globals, Target, !IO),
io.write_string("'.\n", !IO)
;
true
)
;
Warn = no
),
!Info ^ command_line_targets :=
set.delete(!.Info ^ command_line_targets, Target).
maybe_symlink_or_copy_linked_target_message(Globals, Target, !IO) :-
verbose_msg(Globals,
(pred(!.IO::di, !:IO::uo) is det :-
io.write_string("Made symlink/copy of ", !IO),
make_write_module_or_linked_target(Globals, Target, !IO),
io.write_string("\n", !IO)
), !IO).
:- pred make_write_module_or_linked_target(globals::in,
pair(module_name, target_type)::in, io::di, io::uo) is det.
make_write_module_or_linked_target(Globals, ModuleName - FileType, !IO) :-
(
FileType = module_target(ModuleTargetType),
TargetFile = target_file(ModuleName, ModuleTargetType),
make_write_target_file(Globals, TargetFile, !IO)
;
FileType = linked_target(LinkedTargetType),
linked_target_file_name(Globals, ModuleName, LinkedTargetType,
FileName, !IO),
io.write_string(FileName, !IO)
;
FileType = misc_target(_),
unexpected($module, $pred, "misc_target")
).
%-----------------------------------------------------------------------------%
%
% Timing
%
:- pragma foreign_proc("C",
get_real_milliseconds(Time::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
"
Time = MR_get_real_milliseconds();
").
:- pragma foreign_proc("C#",
get_real_milliseconds(Time::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
"
Time = System.Environment.TickCount;
").
:- pragma foreign_proc("Java",
get_real_milliseconds(Time::out, _IO0::di, _IO::uo),
[will_not_call_mercury, promise_pure, thread_safe, tabled_for_io],
"
// The loss of precision is acceptable for mmc --make.
Time = (int) System.currentTimeMillis();
").
get_real_milliseconds(_, _, _) :-
sorry($file, $pred).
%-----------------------------------------------------------------------------%
%
% Hash functions
%
module_name_hash(SymName, Hash) :-
(
SymName = unqualified(String),
Hash = string.hash(String)
;
SymName = qualified(_Qual, String),
% Hashing the the module qualifier seems to be not worthwhile.
Hash = string.hash(String)
).
dependency_file_hash(DepFile, Hash) :-
(
DepFile = dep_target(TargetFile),
Hash = target_file_hash(TargetFile)
;
DepFile = dep_file(FileName, _MaybeOption),
Hash = string.hash(FileName)
).
:- func target_file_hash(target_file) = int.
target_file_hash(TargetFile) = Hash :-
TargetFile = target_file(ModuleName, Type),
module_name_hash(ModuleName, Hash0),
Hash1 = module_target_type_to_nonce(Type),
Hash = mix(Hash0, Hash1).
:- func module_target_type_to_nonce(module_target_type) = int.
module_target_type_to_nonce(Type) = X :-
(
Type = module_target_source,
X = 1
;
Type = module_target_errors,
X = 2
;
Type = module_target_private_interface,
X = 3
;
Type = module_target_long_interface,
X = 4
;
Type = module_target_short_interface,
X = 5
;
Type = module_target_unqualified_short_interface,
X = 6
;
Type = module_target_intermodule_interface,
X = 7
;
Type = module_target_analysis_registry,
X = 8
;
Type = module_target_c_header(header_mh),
X = 9
;
Type = module_target_c_header(header_mih),
X = 10
;
Type = module_target_c_code,
X = 11
;
Type = module_target_il_code,
X = 12
;
Type = module_target_il_asm,
X = 13
;
Type = module_target_java_code,
X = 14
;
Type = module_target_erlang_header,
X = 15
;
Type = module_target_erlang_code,
X = 16
;
Type = module_target_erlang_beam_code,
X = 17
;
Type = module_target_asm_code(_PIC),
X = 18
;
Type = module_target_object_code(PIC),
X = 19 `mix` pic_to_nonce(PIC)
;
Type = module_target_foreign_il_asm(_ForeignLang),
X = 20
;
Type = module_target_foreign_object(_PIC, _ForeignLang),
X = 21
;
Type = module_target_fact_table_object(_PIC, _FileName),
X = 22
;
Type = module_target_xml_doc,
X = 23
;
Type = module_target_track_flags,
X = 24
;
Type = module_target_java_class_code,
X = 25
;
Type = module_target_csharp_code,
X = 26
).
:- func pic_to_nonce(pic) = int.
pic_to_nonce(pic) = 1.
pic_to_nonce(link_with_pic) = 2.
pic_to_nonce(non_pic) = 3.
:- func mix(int, int) = int.
mix(H0, X) = H :-
H1 = H0 `xor` (H0 `unchecked_left_shift` 5),
H = H1 `xor` X.
:- func concoct_second_hash(int) = int.
concoct_second_hash(H) = mix(H, 0xfe3dbe7f). % whatever
%-----------------------------------------------------------------------------%
:- end_module make.util.
%-----------------------------------------------------------------------------%