From a04cde9bc98a2bfc0e22ffbc4391ed1ea2127dda Mon Sep 17 00:00:00 2001 From: Kristoffer Date: Fri, 16 Feb 2024 18:04:49 +0100 Subject: [PATCH] implement a julia specific debug mode with `--debug` This PR adds a new flag to control when a "julia specific" debug mode is active. When the debug mode is active the following happens: - The `@assert` macro is active - The `isdebug` function returns `true`. Currently, the `--debug` has three options: - `"yes"`: debug mode is active in scripts and packages - `"script": debug mode is active in scripts - `"no": debug is not active anywhere The debug mode is a "compile time option" meaning that it will recompile packages if it changes. --- base/Base.jl | 18 +++++++++++++ base/error.jl | 6 ++++- base/exports.jl | 3 ++- base/initdefs.jl | 8 ++++++ base/loading.jl | 42 ++++++++++++++++------------- base/options.jl | 1 + base/util.jl | 6 +++-- doc/man/julia.1 | 6 +++++ pkgimage.mk | 4 +-- src/jloptions.c | 38 +++++++++++++++++++++----- src/jloptions.h | 1 + src/julia.h | 4 +++ src/staticdata.c | 8 +++--- src/staticdata_utils.c | 29 ++++++++++---------- test/precompile.jl | 61 ++++++++++++++++++++++++++++++++++++++++++ 15 files changed, 186 insertions(+), 49 deletions(-) diff --git a/base/Base.jl b/base/Base.jl index c729ab9ca707e..c305c3a39b039 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -121,6 +121,23 @@ function setpropertyonce!(x::Module, f::Symbol, desired, success_order::Symbol=: return Core.setglobalonce!(x, f, val, success_order, fail_order) end +julia_debug::Bool = true + +""" + isdebug() -> Bool + +Return whether the julia session is running in debug mode. +In debug mode `@assert`s are enabled and `isdebug()` returns `true`. +This is a compile time setting so and hence a useful pattern is the following: + +``` +@static if isdebug() + # code that is only enabled in debug mode +end +``` +""" +isdebug() = julia_debug + convert(::Type{Any}, Core.@nospecialize x) = x convert(::Type{T}, x::T) where {T} = x @@ -649,6 +666,7 @@ end function __init__() # Base library init global _atexit_hooks_finished = false + init_julia_debug() Filesystem.__postinit__() reinit_stdio() Multimedia.reinit_displays() # since Multimedia.displays uses stdout as fallback diff --git a/base/error.jl b/base/error.jl index 37ceb39253e38..6980787f753ef 100644 --- a/base/error.jl +++ b/base/error.jl @@ -203,12 +203,13 @@ might decide to check anyways, as an aid to debugging if they fail. The optional message `text` is displayed upon assertion failure. !!! warning - An assert might be disabled at some optimization levels. + Assertions can be enabled/disabled depending on the `--debug` flag passed to Julia. Assert should therefore only be used as a debugging tool and not used for authentication verification (e.g., verifying passwords or checking array bounds). The code must not rely on the side effects of running `cond` for the correct behavior of a function. + # Examples ```jldoctest julia> @assert iseven(3) "3 is an odd number!" @@ -218,6 +219,9 @@ julia> @assert isodd(3) "What even are numbers?" ``` """ macro assert(ex, msgs...) + enable_asserts = !isdefined(Main, :Base) || Main.Base.julia_debug + enable_asserts || return nothing + msg = isempty(msgs) ? ex : msgs[1] if isa(msg, AbstractString) msg = msg # pass-through diff --git a/base/exports.jl b/base/exports.jl index a31ea1b0fa842..9b7c5cda0a669 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1177,4 +1177,5 @@ public # misc notnothing, runtests, - text_colors + text_colors, + isdebug diff --git a/base/initdefs.jl b/base/initdefs.jl index 56c2c0c587272..9fd8b11762937 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -504,3 +504,11 @@ function disable_library_threading() end return end + +function init_julia_debug() + opt_julia_debug = Base.JLOptions().julia_debug + # `julia_debug` is disabled everywhere or we are in a package and `--debug="script"` is used (default) + if opt_julia_debug == 0 || (Base.generating_output() && opt_julia_debug == 1) + global julia_debug = false + end +end diff --git a/base/loading.jl b/base/loading.jl index f4db43d80640b..238bc2de81635 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -1497,31 +1497,36 @@ end struct CacheFlags - # OOICCDDP - see jl_cache_flags + # OOJICCDDP - see jl_cache_flags use_pkgimages::Bool debug_level::Int check_bounds::Int inline::Bool + julia_debug::Bool opt_level::Int end -function CacheFlags(f::UInt8) + +function CacheFlags(f::UInt16) use_pkgimages = Bool(f & 1) debug_level = Int((f >> 1) & 3) check_bounds = Int((f >> 3) & 3) inline = Bool((f >> 5) & 1) - opt_level = Int((f >> 6) & 3) # define OPT_LEVEL in statiddata_utils - CacheFlags(use_pkgimages, debug_level, check_bounds, inline, opt_level) -end -CacheFlags(f::Int) = CacheFlags(UInt8(f)) -CacheFlags() = CacheFlags(ccall(:jl_cache_flags, UInt8, ())) - -function _cacheflag_to_uint8(cf::CacheFlags)::UInt8 - f = UInt8(0) - f |= cf.use_pkgimages << 0 - f |= cf.debug_level << 1 - f |= cf.check_bounds << 3 - f |= cf.inline << 5 - f |= cf.opt_level << 6 + julia_debug = Bool((f >> 6) & 1) + opt_level = Int((f >> 7) & 3) + CacheFlags(use_pkgimages, debug_level, check_bounds, inline, julia_debug, opt_level) +end + +CacheFlags(f::Int) = CacheFlags(UInt16(f)) +CacheFlags() = CacheFlags(ccall(:jl_cache_flags, UInt16, ())) + +function _cacheflag_to_uint16(cf::CacheFlags)::UInt16 + f = UInt16(0) + f |= UInt16(cf.use_pkgimages) << 0 + f |= UInt16(cf.debug_level) << 1 + f |= UInt16(cf.check_bounds) << 3 + f |= UInt16(cf.inline) << 5 + f |= UInt16(cf.julia_debug) << 6 + f |= UInt16(cf.opt_level) << 7 return f end @@ -1530,6 +1535,7 @@ function show(io::IO, cf::CacheFlags) print(io, ", debug_level = ", cf.debug_level) print(io, ", check_bounds = ", cf.check_bounds) print(io, ", inline = ", cf.inline) + print(io, ", julia_debug = ", cf.julia_debug) print(io, ", opt_level = ", cf.opt_level) end @@ -2948,7 +2954,7 @@ end function parse_cache_header(f::IO, cachefile::AbstractString) - flags = read(f, UInt8) + flags = read(f, UInt16) modules = Vector{Pair{PkgId, UInt64}}() while true n = read(f, Int32) @@ -3404,10 +3410,10 @@ end if isempty(modules) return true # ignore empty file end - if @ccall(jl_match_cache_flags(_cacheflag_to_uint8(requested_flags)::UInt8, actual_flags::UInt8)::UInt8) == 0 + if @ccall(jl_match_cache_flags(_cacheflag_to_uint16(requested_flags)::UInt16, actual_flags::UInt16)::UInt16) == 0 @debug """ Rejecting cache file $cachefile for $modkey since the flags are mismatched - requested flags: $(requested_flags) [$(_cacheflag_to_uint8(requested_flags))] + requested flags: $(requested_flags) [$(_cacheflag_to_uint16(requested_flags))] cache file: $(CacheFlags(actual_flags)) [$actual_flags] """ record_reason(reasons, "mismatched flags") diff --git a/base/options.jl b/base/options.jl index a94936391fa8d..924417b215dd9 100644 --- a/base/options.jl +++ b/base/options.jl @@ -27,6 +27,7 @@ struct JLOptions tracked_path::Ptr{UInt8} opt_level::Int8 opt_level_min::Int8 + julia_debug::Int8 debug_level::Int8 check_bounds::Int8 depwarn::Int8 diff --git a/base/util.jl b/base/util.jl index a26a874c7a461..83640af92cfe9 100644 --- a/base/util.jl +++ b/base/util.jl @@ -148,8 +148,8 @@ See also [`print`](@ref), [`println`](@ref), [`show`](@ref). Return a julia command similar to the one of the running process. Propagates any of the `--cpu-target`, `--sysimage`, `--compile`, `--sysimage-native-code`, -`--compiled-modules`, `--pkgimages`, `--inline`, `--check-bounds`, `--optimize`, `--min-optlevel`, `-g`, -`--code-coverage`, `--track-allocation`, `--color`, `--startup-file`, and `--depwarn` +`--compiled-modules`, `--pkgimages`, `--inline`, `--check-bounds`, `--optimize`, `--min-optlevel`, `--debug`, +`-g`, `--code-coverage`, `--track-allocation`, `--color`, `--startup-file`, and `--depwarn` command line arguments that are not at their default values. Among others, `--math-mode`, `--warn-overwrite`, and `--trace-compile` are notably not propagated currently. @@ -212,6 +212,8 @@ function julia_cmd(julia=joinpath(Sys.BINDIR, julia_exename()); cpu_target::Unio opts.use_pkgimages == 2 && push!(addflags, "--pkgimages=existing") opts.opt_level == 2 || push!(addflags, "-O$(opts.opt_level)") opts.opt_level_min == 0 || push!(addflags, "--min-optlevel=$(opts.opt_level_min)") + opts.julia_debug == 0 && push!(addflags, "--debug=no") + opts.julia_debug == 2 && push!(addflags, "--debug=yes") push!(addflags, "-g$(opts.debug_level)") if opts.code_coverage != 0 # Forward the code-coverage flag only if applicable (if the filename is pid-dependent) diff --git a/doc/man/julia.1 b/doc/man/julia.1 index c58b00120b7ec..e68ec7952247e 100644 --- a/doc/man/julia.1 +++ b/doc/man/julia.1 @@ -182,6 +182,12 @@ Set the optimization level (level 3 if `-O` is used without a level) --min-optlevel={0*,1,2,3} Set a lower bound on the optimization level +.TP +--debug={no|script*|yes} +Control if the `@assert` macro and `isdebug` is `true` nowhere, only in scripts (not packages)\n +or everywhere, respectively + + .TP -g {0,1*,2} Set the level of debug info generation (level 2 if `-g` is used without a level) diff --git a/pkgimage.mk b/pkgimage.mk index c9de49d2f8421..9cfda56a1e2ec 100644 --- a/pkgimage.mk +++ b/pkgimage.mk @@ -34,11 +34,11 @@ $$(BUILDDIR)/stdlib/$1.release.image: export JULIA_CPU_TARGET=$(JULIA_CPU_TARGET $$(BUILDDIR)/stdlib/$1.debug.image: export JULIA_CPU_TARGET=$(JULIA_CPU_TARGET) ifneq ($(filter $(1),$(INDEPENDENT_STDLIBS)),) $$(BUILDDIR)/stdlib/$1.release.image: $$($1_SRCS) $$(addsuffix .release.image,$$(addprefix $$(BUILDDIR)/stdlib/,$2)) $(build_private_libdir)/sys.$(SHLIB_EXT) - @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes -e 'Base.compilecache(Base.identify_package("$1"))') + @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes --debug=yes -e 'Base.compilecache(Base.identify_package("$1"))') @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.compilecache(Base.identify_package("$1"))') touch $$@ $$(BUILDDIR)/stdlib/$1.debug.image: $$($1_SRCS) $$(addsuffix .debug.image,$$(addprefix $$(BUILDDIR)/stdlib/,$2)) $(build_private_libdir)/sys-debug.$(SHLIB_EXT) - @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes -e 'Base.compilecache(Base.identify_package("$1"))') + @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no --check-bounds=yes --debug=yes -e 'Base.compilecache(Base.identify_package("$1"))') @$$(call PRINT_JULIA, $$(call spawn,$$(JULIA_EXECUTABLE)) --startup-file=no -e 'Base.compilecache(Base.identify_package("$1"))') touch $$@ endif diff --git a/src/jloptions.c b/src/jloptions.c index 4b3f7209dc56d..8c3ef9c5df67b 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -56,6 +56,7 @@ JL_DLLEXPORT void jl_init_options(void) NULL, // tracked_path 2, // opt_level 0, // opt_level_min + JL_OPTIONS_JULIA_DEBUG_SCRIPT, // julia_debug #ifdef JL_DEBUG_BUILD 2, // debug_level [debug build] #else @@ -113,12 +114,14 @@ static const char opts[] = " --compiled-modules={yes*|no|existing|strict}\n" " Enable or disable incremental precompilation of modules\n" " --pkgimages={yes*|no|existing}\n" - " Enable or disable usage of native code caching in the form of pkgimages ($)\n\n" + " Enable or disable usage of native code caching in the form of pkgimages ($)\n" + "\n" // actions " -e, --eval Evaluate \n" " -E, --print Evaluate and display the result\n" - " -L, --load Load immediately on all processors\n\n" + " -L, --load Load immediately on all processors\n" + "\n" // parallel options " -t, --threads {auto|N[,auto|M]}\n" @@ -135,7 +138,8 @@ static const char opts[] = " N is set to half of the number of compute threads and M is set to 0 if unspecified.\n" " -p, --procs {N|auto} Integer value N launches N additional local worker processes\n" " \"auto\" launches as many workers as the number of local CPU threads (logical cores)\n" - " --machine-file Run processes on hosts listed in \n\n" + " --machine-file Run processes on hosts listed in \n" + "\n" // interactive options " -i, --interactive Interactive mode; REPL runs and `isinteractive()` is true\n" @@ -143,17 +147,21 @@ static const char opts[] = " --banner={yes|no|short|auto*}\n" " Enable or disable startup banner\n" " --color={yes|no|auto*} Enable or disable color text\n" - " --history-file={yes*|no} Load or save history\n\n" + " --history-file={yes*|no} Load or save history\n" + "\n" // error and warning options " --depwarn={yes|no*|error} Enable or disable syntax and method deprecation warnings (`error` turns warnings into errors)\n" " --warn-overwrite={yes|no*} Enable or disable method overwrite warnings\n" - " --warn-scope={yes*|no} Enable or disable warning for ambiguous top-level scope\n\n" + " --warn-scope={yes*|no} Enable or disable warning for ambiguous top-level scope\n" + "\n" // code generation options " -C, --cpu-target Limit usage of CPU features up to ; set to `help` to see the available options\n" " -O, --optimize={0,1,2*,3} Set the optimization level (level 3 if `-O` is used without a level) ($)\n" " --min-optlevel={0*,1,2,3} Set a lower bound on the optimization level\n" + " --debug={no|script*|yes} Control if the `@assert` macro and `isdebug` is `true` nowhere, only in scripts (not packages)\n" + " or everywhere, respectively\n" #ifdef JL_DEBUG_BUILD " -g, --debug-info=[{0,1,2*}] Set the level of debug info generation in the julia-debug build ($)\n" #else @@ -165,6 +173,7 @@ static const char opts[] = #ifdef USE_POLLY " --polly={yes*|no} Enable or disable the polyhedral optimizer Polly (overrides @polly declaration)\n" #endif + "\n" // instrumentation options " --code-coverage[={none*|user|all}]\n" @@ -183,6 +192,8 @@ static const char opts[] = " Count bytes but only in files that fall under the given file path/directory.\n" " The `@` prefix is required to select this option. A `@` with no path will track the\n" " current directory.\n" + "\n" + " --bug-report=KIND Launch a bug report session. It can be used to start a REPL, run a script, or evaluate\n" " expressions. It first tries to use BugReporting.jl installed in current environment and\n" " fallbacks to the latest compatible BugReporting.jl if not. For more information, see\n" @@ -259,9 +270,9 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) opt_strip_ir, opt_heap_size_hint, opt_gc_threads, - opt_permalloc_pkgimg + opt_permalloc_pkgimg, }; - static const char* const shortopts = "+vhqH:e:E:L:J:C:it:p:O:g:"; + static const char* const shortopts = "+vhqHd:e:E:L:J:C:it:p:O:g:"; static const struct option longopts[] = { // exposed command line options // NOTE: This set of required arguments need to be kept in sync @@ -295,6 +306,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) { "track-allocation",optional_argument, 0, opt_track_allocation }, { "optimize", optional_argument, 0, 'O' }, { "min-optlevel", optional_argument, 0, opt_optlevel_min }, + { "debug", required_argument, 0, 'd' }, { "debug-info", optional_argument, 0, 'g' }, { "check-bounds", required_argument, 0, opt_check_bounds }, { "output-bc", required_argument, 0, opt_output_bc }, @@ -469,6 +481,18 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) else jl_errorf("julia: invalid argument to --compiled-modules={yes|no|existing|strict} (%s)", optarg); break; + case 'd': + if (!strcmp(optarg,"yes")) + jl_options.julia_debug = JL_OPTIONS_JULIA_DEBUG_YES; + else if (!strcmp(optarg,"no")) + jl_options.julia_debug = JL_OPTIONS_JULIA_DEBUG_NO; + else if (!strcmp(optarg,"script")) { + jl_options.julia_debug = JL_OPTIONS_JULIA_DEBUG_SCRIPT; + printf("opt: %d", jl_options.julia_debug); + } + else + jl_errorf("julia: invalid argument to --debug={yes|script|no} (%s)", optarg); + break; case opt_pkgimages: if (!strcmp(optarg,"yes")) jl_options.use_pkgimages = JL_OPTIONS_USE_PKGIMAGES_YES; diff --git a/src/jloptions.h b/src/jloptions.h index 8649c405112d7..a736352bd3556 100644 --- a/src/jloptions.h +++ b/src/jloptions.h @@ -31,6 +31,7 @@ typedef struct { const char *tracked_path; int8_t opt_level; int8_t opt_level_min; + int8_t julia_debug; int8_t debug_level; int8_t check_bounds; int8_t depwarn; diff --git a/src/julia.h b/src/julia.h index de5146aed7065..89f0b32bad501 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2505,6 +2505,10 @@ JL_DLLEXPORT int jl_generating_output(void) JL_NOTSAFEPOINT; #define JL_OPTIONS_USE_PKGIMAGES_YES 1 #define JL_OPTIONS_USE_PKGIMAGES_NO 0 +#define JL_OPTIONS_JULIA_DEBUG_YES 2 +#define JL_OPTIONS_JULIA_DEBUG_SCRIPT 1 +#define JL_OPTIONS_JULIA_DEBUG_NO 0 + // Version information #include // Generated file diff --git a/src/staticdata.c b/src/staticdata.c index abcdd76dac507..a9949af2941b6 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -2898,7 +2898,7 @@ static void jl_write_header_for_incremental(ios_t *f, jl_array_t *worklist, jl_a jl_precompile_toplevel_module = (jl_module_t*)jl_array_ptr_ref(worklist, jl_array_len(worklist)-1); *checksumpos = write_header(f, 0); - write_uint8(f, jl_cache_flags()); + write_uint16(f, jl_cache_flags()); // write description of contents (name, uuid, buildid) write_worklist_for_header(f, worklist); // Determine unique (module, abspath, fsize, hash, mtime) dependencies for the files defining modules in the worklist @@ -2957,7 +2957,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli jl_write_header_for_incremental(f, worklist, mod_array, udeps, srctextpos, &checksumpos); if (emit_split) { checksumpos_ff = write_header(ff, 1); - write_uint8(ff, jl_cache_flags()); + write_uint16(ff, jl_cache_flags()); write_mod_list(ff, mod_array); } else { @@ -3569,9 +3569,9 @@ static jl_value_t *jl_validate_cache_file(ios_t *f, jl_array_t *depmods, uint64_ return jl_get_exceptionf(jl_errorexception_type, "Precompile file header verification checks failed."); } - uint8_t flags = read_uint8(f); + uint16_t flags = read_uint16(f); if (pkgimage && !jl_match_cache_flags_current(flags)) { - return jl_get_exceptionf(jl_errorexception_type, "Pkgimage flags mismatch"); + return jl_get_exceptionf(jl_errorexception_type, "Pkgimage flags mismatch, current:%d, got:%d", jl_cache_flags(), flags); } if (!pkgimage) { // skip past the worklist diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 9542e54182644..032eea0317be8 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -589,25 +589,26 @@ static void write_mod_list(ios_t *s, jl_array_t *a) } // OPT_LEVEL should always be the upper bits -#define OPT_LEVEL 6 +#define OPT_LEVEL 7 -JL_DLLEXPORT uint8_t jl_cache_flags(void) +JL_DLLEXPORT uint16_t jl_cache_flags(void) { - // OOICCDDP - uint8_t flags = 0; - flags |= (jl_options.use_pkgimages & 1); // 0-bit - flags |= (jl_options.debug_level & 3) << 1; // 1-2 bit - flags |= (jl_options.check_bounds & 3) << 3; // 3-4 bit - flags |= (jl_options.can_inline & 1) << 5; // 5-bit - flags |= (jl_options.opt_level & 3) << OPT_LEVEL; // 6-7 bit + // OOJICCDDP + uint16_t flags = 0; + flags |= (jl_options.use_pkgimages & 1) << 0; // 0-bit + flags |= (jl_options.debug_level & 3) << 1; // 1-2 bits + flags |= (jl_options.check_bounds & 3) << 3; // 3-4 bits + flags |= (jl_options.can_inline & 1) << 5; // 5-bit + flags |= ((jl_options.julia_debug == JL_OPTIONS_JULIA_DEBUG_YES) ? 1 : 0) << 6; // 6-bit + flags |= (jl_options.opt_level & 3) << OPT_LEVEL; // 7-8 bits return flags; } -JL_DLLEXPORT uint8_t jl_match_cache_flags(uint8_t requested_flags, uint8_t actual_flags) +JL_DLLEXPORT uint16_t jl_match_cache_flags(uint16_t requested_flags, uint16_t actual_flags) { - uint8_t supports_pkgimage = (requested_flags & 1); - uint8_t is_pkgimage = (actual_flags & 1); + uint16_t supports_pkgimage = (requested_flags & 1); + uint16_t is_pkgimage = (actual_flags & 1); // For .ji packages ignore other flags if (!supports_pkgimage && !is_pkgimage) { @@ -620,7 +621,7 @@ JL_DLLEXPORT uint8_t jl_match_cache_flags(uint8_t requested_flags, uint8_t actua } // 2. Check all flags, execept opt level must be exact - uint8_t mask = (1 << OPT_LEVEL)-1; + uint16_t mask = (1 << OPT_LEVEL)-1; if ((actual_flags & mask) != (requested_flags & mask)) return 0; // 3. allow for higher optimization flags in cache @@ -629,7 +630,7 @@ JL_DLLEXPORT uint8_t jl_match_cache_flags(uint8_t requested_flags, uint8_t actua return actual_flags >= requested_flags; } -JL_DLLEXPORT uint8_t jl_match_cache_flags_current(uint8_t flags) +JL_DLLEXPORT uint16_t jl_match_cache_flags_current(uint16_t flags) { return jl_match_cache_flags(jl_cache_flags(), flags); } diff --git a/test/precompile.jl b/test/precompile.jl index a68d8936d1ed1..7e8defcaaf9c6 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -2094,6 +2094,67 @@ precompile_test_harness("Test flags") do load_path @test !Base.isprecompiled(id, ;flags=current_flags) end + +# Debug mode +precompile_test_harness("Test flags") do load_path + write(joinpath(load_path, "Project.toml"), """ + [deps] + A = "065497a9-3da1-45c2-901d-78aba5544f04" + B = "d52a13bc-ce4c-4a58-a416-e1c8b2d677de" + """) + + write(joinpath(load_path, "Manifest.toml"), """ + [[A]] + deps = ["B"] + path = "A" + uuid = "065497a9-3da1-45c2-901d-78aba5544f04" + [[B]] + path = "B" + uuid = "d52a13bc-ce4c-4a58-a416-e1c8b2d677de" + """) + + mkpath(joinpath(load_path, "A/src/")) + write(joinpath(load_path, "A/src/A.jl"), """ + module A + using B + const is_debug = Base.julia_debug + maybe_assert() = @assert false + end + """) + + mkpath(joinpath(load_path, "B/src/")) + write(joinpath(load_path, "B/src/B.jl"), """ + module B + const is_debug = Base.julia_debug + maybe_assert() = @assert false + end + """) + + s = """ + using Test + using A + @test A.is_debug == true + @test A.B.is_debug == true + @test_throws AssertionError A.maybe_assert() + @test_throws AssertionError A.B.maybe_assert() + """ + # Assert in packages should not be removed with `--debug=yes` + @test success(Cmd(`$(Base.julia_cmd()) --project=$load_path --debug=yes -e $s`)) + + s = """ + using Test + using A + @test A.is_debug == false + @test A.B.is_debug == false + @test A.maybe_assert() === nothing + @test A.B.maybe_assert() === nothing + """ + # Assert in packages should be removed with `--debug=no` or default + @test success(Cmd(`$(Base.julia_cmd()) --project=$load_path -e $s`)) + @test success(Cmd(`$(Base.julia_cmd()) --project=$load_path --debug=no -e $s`)) +end + + empty!(Base.DEPOT_PATH) append!(Base.DEPOT_PATH, original_depot_path) empty!(Base.LOAD_PATH)