Skip to content

Commit

Permalink
[build] Gracefully error when a module has conflicting compiler options
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulBone committed Sep 2, 2024
1 parent f5c08a5 commit 18e8908
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 41 deletions.
93 changes: 52 additions & 41 deletions src/build.m
Original file line number Diff line number Diff line change
Expand Up @@ -417,36 +417,39 @@ not is_empty(DupModules)
io::di, io::uo) is det.

build_dependency_info(Targets, MaybeDeps, !DirInfo, !IO) :-
Modules0 = make_module_info(Targets),

% The term Target is overloaded here, it means both the whole things
% that plzbuild is trying to build, but also the steps that ninja does
% to build them.
map_foldl2(find_module_file, Modules0, MaybeModules0, !DirInfo, !IO),
MaybeModules = result_list_to_result(MaybeModules0),
find_foreign_sources(Targets, MaybeForeignSources, !DirInfo, !IO),

( MaybeModules = ok(Modules),
MaybeForeignSources = ok(ForeignSources),
ModuleTargets = map(make_module_targets, Modules),
ProgramTargets = map(make_program_target, Targets),
ForeignLinkTargets = condense(map(make_foreign_link_targets,
Targets)),
ForeignCompileTargets = map(make_foreign_target, ForeignSources),

MaybeDeps = ok(condense(ModuleTargets) ++
ForeignCompileTargets ++
ForeignLinkTargets ++ ProgramTargets)

; MaybeModules = ok(_),
MaybeForeignSources = errors(Errors),
MaybeDeps = errors(Errors)
; MaybeModules = errors(Errors),
MaybeForeignSources = ok(_),
MaybeModules0 = make_module_info(Targets),
( MaybeModules0 = ok(Modules0),
% The term Target is overloaded here, it means both the whole things
% that plzbuild is trying to build, but also the steps that ninja does
% to build them.
map_foldl2(find_module_file, Modules0, MaybeModules1, !DirInfo, !IO),
MaybeModules = result_list_to_result(MaybeModules1),
find_foreign_sources(Targets, MaybeForeignSources, !DirInfo, !IO),

( MaybeModules = ok(Modules),
MaybeForeignSources = ok(ForeignSources),
ModuleTargets = map(make_module_targets, Modules),
ProgramTargets = map(make_program_target, Targets),
ForeignLinkTargets = condense(map(make_foreign_link_targets,
Targets)),
ForeignCompileTargets = map(make_foreign_target, ForeignSources),

MaybeDeps = ok(condense(ModuleTargets) ++
ForeignCompileTargets ++
ForeignLinkTargets ++ ProgramTargets)

; MaybeModules = ok(_),
MaybeForeignSources = errors(Errors),
MaybeDeps = errors(Errors)
; MaybeModules = errors(Errors),
MaybeForeignSources = ok(_),
MaybeDeps = errors(Errors)
; MaybeModules = errors(ErrorsA),
MaybeForeignSources = errors(ErrorsB),
MaybeDeps = errors(ErrorsA ++ ErrorsB)
)
; MaybeModules0 = errors(Errors),
MaybeDeps = errors(Errors)
; MaybeModules = errors(ErrorsA),
MaybeForeignSources = errors(ErrorsB),
MaybeDeps = errors(ErrorsA ++ ErrorsB)
).

:- type module_info
Expand All @@ -457,12 +460,16 @@ not is_empty(DupModules)
mi_pcflags :: string
).

:- func make_module_info(list(target)) = list(module_info).
:- func make_module_info(list(target)) = result(list(module_info), string).

make_module_info(Targets) = Modules :-
Modules0 = condense(map(target_get_modules, Targets)),
foldl(resolve_duplicate_modules, Modules0, init, Modules1),
Modules = map.values(Modules1).
foldl_result(resolve_duplicate_modules, Modules0, init, MaybeModules1),
( MaybeModules1 = ok(Modules1),
Modules = ok(map.values(Modules1))
; MaybeModules1 = errors(Error),
Modules = errors(Error)
).

:- func target_get_modules(target) = list(module_info).

Expand All @@ -473,30 +480,34 @@ not is_empty(DupModules)
Target ^ t_modules).

:- pred resolve_duplicate_modules(module_info::in,
map(q_name, module_info)::in, map(q_name, module_info)::out) is det.
map(q_name, module_info)::in,
result(map(q_name, module_info), string)::out) is det.

resolve_duplicate_modules(Module, !Map) :-
Name = Module ^ mi_name,
map_set_or_update(func(M) = module_merge(M, Module),
map_set_or_update_result(func(M) = module_merge(M, Module),
Name, Module, !Map).

:- func module_merge(module_info, module_info) = module_info.
:- func module_merge(module_info, module_info) = result(module_info, string).

module_merge(Ma, Mb) =
module_info(Ma ^ mi_name,
( if Ma ^ mi_pcflags = Mb ^ mi_pcflags then
ok(module_info(Ma ^ mi_name,
context_earliest(Ma ^ mi_context, Mb ^ mi_context),
Ma ^ mi_file,
Ma ^ mi_pcflags) :-
expect(unify(Ma ^ mi_pcflags, Mb ^ mi_pcflags),
$module, $pred, "Flags set for module differently in different
programs").
Ma ^ mi_pcflags))
else
return_error(context_earliest(Ma ^ mi_context, Mb ^ mi_context),
"Flags set for the same module in different programs do not match")
).

:- pred find_module_file(module_info::in,
result(module_info, string)::out,
dir_info::in, dir_info::out, io::di, io::uo) is det.

find_module_file(Module, ModuleResult, !DirInfo, !IO) :-
find_module_file(".", source_extension, Module ^ mi_name, FileRes, !DirInfo, !IO),
find_module_file(".", source_extension, Module ^ mi_name, FileRes,
!DirInfo, !IO),
( FileRes = yes(File),
ModuleResult = ok(Module ^ mi_file := File)
; FileRes = no,
Expand Down
45 changes: 45 additions & 0 deletions src/util.result.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
:- import_module io.
:- import_module cord.
:- import_module list.
:- import_module map.
:- import_module maybe.

:- import_module context.
Expand Down Expand Up @@ -97,6 +98,23 @@ func error_or_warning(E) = error_or_warning

:- func result_map((func(T) = U), result(T, E)) = result(U, E).

%-----------------------------------------------------------------------%

% foldl over a list except the accumulator includes a result that must
% be unpact before processing the next item. If mercury had monads this
% would be bind.
%
:- pred foldl_result(pred(X, A, result(A, E)), list(X),
A, result(A, E)).
:- mode foldl_result(pred(in, in, out) is det, in, in, out) is det.

% Set or update the value within a map at the given key. if the update
% function fails then return that error.
%
:- pred map_set_or_update_result(func(V) = result(V, E),
K, V, map(K, V), result(map(K, V), E)).
:- mode map_set_or_update_result(in, in, in, in, out) is det.

%-----------------------------------------------------------------------%

:- func errors_map((func(E1) = E2), errors(E1)) = errors(E2).
Expand Down Expand Up @@ -205,6 +223,33 @@ func error_or_warning(E) = error_or_warning

%-----------------------------------------------------------------------%

foldl_result(_, [], Acc, ok(Acc)).
foldl_result(Pred, [X | Xs], Acc0, MaybeAcc) :-
Pred(X, Acc0, MaybeAcc1),
( MaybeAcc1 = ok(Acc1),
foldl_result(Pred, Xs, Acc1, MaybeAcc)
; MaybeAcc1 = errors(Error),
MaybeAcc = errors(Error)
).

%-----------------------------------------------------------------------%

map_set_or_update_result(UpdateFn, Key, Value, !.Map, MaybeMap) :-
( if search(!.Map, Key, Old) then
MaybeNew = UpdateFn(Old),
( MaybeNew = ok(New),
det_update(Key, New, !Map),
MaybeMap = ok(!.Map)
; MaybeNew = errors(Error),
MaybeMap = errors(Error)
)
else
set(Key, Value, !Map),
MaybeMap = ok(!.Map)
).

%-----------------------------------------------------------------------%

errors_map(Func, Errors) = map(error_map(Func), Errors).

:- func error_map((func(E1) = E2), error(E1)) = error(E2).
Expand Down
17 changes: 17 additions & 0 deletions tests/build/options_compiler_03.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# This is free and unencumbered software released into the public domain.
# See ../LICENSE.unlicense

# PLZTEST type compile_failure

# This test asks the build system to build the same module with different
# settings, it should fail.

[options_compiler_03a]
type = program
modules = [ OptionsCompiler03a, OptionsCompiler03 ]
compiler_opts = "--no-simplify"

[options_compiler_03b]
type = program
modules = [ OptionsCompiler03b, OptionsCompiler03 ]

2 changes: 2 additions & 0 deletions tests/build/options_compiler_03.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
options_compiler_03.build:11: Flags set for the same module in different
programs do not match

0 comments on commit 18e8908

Please sign in to comment.