diff --git a/src/build.m b/src/build.m index 5a9ea331..98afdf6b 100644 --- a/src/build.m +++ b/src/build.m @@ -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 @@ -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). @@ -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, diff --git a/src/util.result.m b/src/util.result.m index ddc211f6..4dfcc8bb 100644 --- a/src/util.result.m +++ b/src/util.result.m @@ -16,6 +16,7 @@ :- import_module io. :- import_module cord. :- import_module list. +:- import_module map. :- import_module maybe. :- import_module context. @@ -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). @@ -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). diff --git a/tests/build/options_compiler_03.build b/tests/build/options_compiler_03.build new file mode 100644 index 00000000..3e163b2b --- /dev/null +++ b/tests/build/options_compiler_03.build @@ -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 ] + diff --git a/tests/build/options_compiler_03.exp b/tests/build/options_compiler_03.exp new file mode 100644 index 00000000..3e89c4f5 --- /dev/null +++ b/tests/build/options_compiler_03.exp @@ -0,0 +1,2 @@ +options_compiler_03.build:11: Flags set for the same module in different + programs do not match