From b73ff9649fb643d437832dcaad113b1c607db0f1 Mon Sep 17 00:00:00 2001 From: Hendrik Ranocha Date: Wed, 31 Jan 2024 14:27:34 +0100 Subject: [PATCH] Compilation message with MPI extension (#5) * draft MPI extension * fix extension including precompilation Co-authored-by: Michael Schlottke-Lakemper * precompile extension * MPI compat * fix tests * remove dummy test file * format * just use at-test_nowarn_mod * format --------- Co-authored-by: Michael Schlottke-Lakemper --- Project.toml | 13 ++++++++++++ ext/TrixiBaseMPIExt.jl | 20 ++++++++++++++++++ src/trixi_include.jl | 9 ++++++++ test/test_util.jl | 48 ++++++++++++++++++++++++++++++++++++++++++ test/trixi_include.jl | 17 ++++++++------- 5 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 ext/TrixiBaseMPIExt.jl diff --git a/Project.toml b/Project.toml index 9b13411..50e3262 100644 --- a/Project.toml +++ b/Project.toml @@ -2,3 +2,16 @@ name = "TrixiBase" uuid = "9a0f1c46-06d5-4909-a5a3-ce25d3fa3284" authors = ["Michael Schlottke-Lakemper "] version = "0.1.0" + +[weakdeps] +MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" + +[extensions] +TrixiBaseMPIExt = "MPI" + +[compat] +MPI = "0.20" +julia = "1.8" + +[extras] +MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" diff --git a/ext/TrixiBaseMPIExt.jl b/ext/TrixiBaseMPIExt.jl new file mode 100644 index 0000000..ff714f6 --- /dev/null +++ b/ext/TrixiBaseMPIExt.jl @@ -0,0 +1,20 @@ +# Package extension for adding MPI-based features to TrixiBase.jl +module TrixiBaseMPIExt + +# Load package extension code on Julia v1.9 and newer +if isdefined(Base, :get_extension) + using MPI: MPI +end +import TrixiBase + +# This is a really working version - assuming the same +# communication pattern etc. used in Trixi.jl. +function TrixiBase.mpi_isparallel(::Val{:MPIExt}) + if MPI.Initialized() + return MPI.Comm_size(MPI.COMM_WORLD) > 1 + else + return false + end +end + +end diff --git a/src/trixi_include.jl b/src/trixi_include.jl index 9ba6e9b..1d823b4 100644 --- a/src/trixi_include.jl +++ b/src/trixi_include.jl @@ -39,6 +39,10 @@ function trixi_include(mod::Module, elixir::AbstractString; kwargs...) find_assignment(expr, key) end + # Print information on potential wait time only in non-parallel case + if !mpi_isparallel(Val{:MPIExt}()) + @info "You just called `trixi_include`. Julia may now compile the code, please be patient." + end Base.include(ex -> replace_assignments(insert_maxiters(ex); kwargs...), mod, elixir) end @@ -143,3 +147,8 @@ function find_assignment(expr, destination) result end + +# This is just a dummy function. We only implement a real +# version if MPI.jl is loaded to avoid letting TrixiBase.jl +# depend explicitly on MPI.jl. +mpi_isparallel(x) = false diff --git a/test/test_util.jl b/test/test_util.jl index 7e0912c..4130cfa 100644 --- a/test/test_util.jl +++ b/test/test_util.jl @@ -20,9 +20,57 @@ macro trixi_testset(name, expr) using Test using TrixiBase + # We also include this file again to provide the definition of + # the other testing macros. This allows to use `@trixi_testset` + # in a nested fashion and also call `@test_nowarn_mod` from + # there. + include(@__FILE__) + @testset verbose=true $name $expr end nothing end end + +""" + @test_nowarn_mod expr + +Modified version of `@test_nowarn expr` that prints the content of `stderr` when +it is not empty and ignores some common info statements printed in Trixi.jl +uses. +""" +macro test_nowarn_mod(expr, additional_ignore_content = String[]) + quote + let fname = tempname() + try + ret = open(fname, "w") do f + redirect_stderr(f) do + $(esc(expr)) + end + end + stderr_content = read(fname, String) + if !isempty(stderr_content) + println("Content of `stderr`:\n", stderr_content) + end + + # Patterns matching the following ones will be ignored. Additional patterns + # passed as arguments can also be regular expressions, so we just use the + # type `Any` for `ignore_content`. + ignore_content = Any["[ Info: You just called `trixi_include`. Julia may now compile the code, please be patient.\n"] + append!(ignore_content, $additional_ignore_content) + for pattern in ignore_content + stderr_content = replace(stderr_content, pattern => "") + end + + # We also ignore simple module redefinitions for convenience. Thus, we + # check whether every line of `stderr_content` is of the form of a + # module replacement warning. + @test occursin(r"^(WARNING: replacing module .+\.\n)*$", stderr_content) + ret + finally + rm(fname, force = true) + end + end + end +end diff --git a/test/trixi_include.jl b/test/trixi_include.jl index 089642f..fb243d6 100644 --- a/test/trixi_include.jl +++ b/test/trixi_include.jl @@ -12,15 +12,16 @@ # Use `@trixi_testset`, which wraps code in a temporary module, and call # `trixi_include` with `@__MODULE__` in order to isolate this test. - @test_nowarn trixi_include(@__MODULE__, filename) + @test_nowarn_mod trixi_include(@__MODULE__, filename) @test @isdefined x @test x == 4 - @test_nowarn trixi_include(@__MODULE__, filename, x = 7) + @test_nowarn_mod trixi_include(@__MODULE__, filename, x = 7) + @test x == 7 # Verify default version (that includes in `Main`) - @test_nowarn trixi_include(filename, x = 11) + @test_nowarn_mod trixi_include(filename, x = 11) @test Main.x == 11 @test_throws "assignment `y` not found in expression" trixi_include(@__MODULE__, @@ -101,22 +102,22 @@ # Use `@trixi_testset`, which wraps code in a temporary module, and call # `Base.include` and `trixi_include` with `@__MODULE__` in order to isolate this test. Base.include(@__MODULE__, filename1) - @test_nowarn trixi_include(@__MODULE__, filename2) + @test_nowarn_mod trixi_include(@__MODULE__, filename2) @test @isdefined x # This is the default `maxiters` inserted by `trixi_include` @test x == 10^5 - @test_nowarn trixi_include(@__MODULE__, filename2, - maxiters = 7) + @test_nowarn_mod trixi_include(@__MODULE__, filename2, + maxiters = 7) # Test that `maxiters` got overwritten @test x == 7 # Verify that adding `maxiters` to `maxiters` results in exactly one of them # case 1) `maxiters` is *before* semicolon in included file - @test_nowarn trixi_include(@__MODULE__, filename3, maxiters = 11) + @test_nowarn_mod trixi_include(@__MODULE__, filename3, maxiters = 11) @test y == 11 # case 2) `maxiters` is *after* semicolon in included file - @test_nowarn trixi_include(@__MODULE__, filename3, maxiters = 14) + @test_nowarn_mod trixi_include(@__MODULE__, filename3, maxiters = 14) @test y == 14 finally rm(filename1, force = true)