diff --git a/src/PackageCompiler.jl b/src/PackageCompiler.jl index 07d7395f..19ce85d8 100644 --- a/src/PackageCompiler.jl +++ b/src/PackageCompiler.jl @@ -14,9 +14,13 @@ using p7zip_jll: p7zip_path export create_sysimage, create_app, create_library -include("juliaconfig.jl") +# Vendored: include("../ext/TerminalSpinners.jl") + +# Source code for this package: +include("juliaconfig.jl") include("library_selection.jl") +include("xcode.jl") ############## @@ -711,7 +715,15 @@ function create_sysimg_from_object_file(object_files::Vector{String}, end mkpath(dirname(sysimage_path)) # Prevent compiler from stripping all symbols from the shared lib. - o_file_flags = Sys.isapple() ? `-Wl,-all_load $object_files` : `-Wl,--whole-archive $object_files -Wl,--no-whole-archive` + if Sys.isapple() + if _is_xcode_clt_and_is_gte_xcode_15() + o_file_flags = `-Wl,-all_load $object_files -Wl,-ld_classic` + else + o_file_flags = `-Wl,-all_load $object_files` + end + else + o_file_flags = `-Wl,--whole-archive $object_files -Wl,--no-whole-archive` + end extra = get_extra_linker_flags(version, compat_level, soname) cmd = `$(bitflag()) $(march()) -shared -L$(julia_libdir()) -L$(julia_private_libdir()) -o $sysimage_path $o_file_flags $(Base.shell_split(ldlibs())) $extra` run_compiler(cmd; cplusplus=true) diff --git a/src/xcode.jl b/src/xcode.jl new file mode 100644 index 00000000..bed4aa48 --- /dev/null +++ b/src/xcode.jl @@ -0,0 +1,61 @@ +# Get the compiler command from `get_compiler_cmd()`, and run `cc --version`, and parse the +# output to determine whether or not the compiler is an Xcode Clang. +# +# The return value is a NamedTuple of the form (; b, ver) +# b = a Bool that is true iff the compiler is an Xcode Clang. +# ver = the version number of the Xcode Clang. If it's not Xcode Clang, ver is nothing. +function _is_xcode_clt() + cmd = `$(get_compiler_cmd()) --version` + @debug "_active_compiler_is_xcode_clt(): Attempting to run command" cmd + # The `ignorestatus` allows us to proceed if the command does not run successfully. + output = "\n" * strip(read(ignorestatus(cmd), String)) * "\n" + + # If this is an Xcode Clang compiler, example output would be: + # > Apple clang version 16.0.0 (clang-1600.0.26.3) + # > Target: arm64-apple-darwin23.6.0 + # > Thread model: posix + # > InstalledDir: /Library/Developer/CommandLineTools/usr/bin + + installed_dir_m = match(r"\nInstalledDir: ([\w\/]*?)\n", output) + if isnothing(installed_dir_m) + return (; b=false, ver=nothing) + end + installed_dir_str = strip(installed_dir_m[1]) + is_xcode_app = startswith(installed_dir_str, "/Applications/Xcode.app") + is_xcode_clt = startswith(installed_dir_str, "/Library/Developer/CommandLineTools") + if is_xcode_app || is_xcode_clt + m = match(r"\nApple clang version ([0-9\.]*?) ", output) + if isnothing(m) + @warn "Could not determine the version of the Xcode Command Line Tools" + (; b=false, ver=nothing) + end + ver_str = strip(m[1]) + ver = tryparse(VersionNumber, ver_str) + if isnothing(ver) + @warn "Could not determine the version of the Xcode Command Line Tools" ver_str + (; b=false, ver=nothing) + end + b = true + else + b = false + ver = nothing + end + return (; b, ver) +end + +# Return true iff the compiler is Xcode Clang AND the Xcode CLT version is >= 15. +# +# If the user sets the JULIA_PACKAGECOMPILER_XCODE_CLT_MAJOR_VERSION environment variable, +# we skip our attempt at auto-detection, and instead use whatever value the user gave us. +function _is_xcode_clt_and_is_gte_xcode_15() + str = strip(get(ENV, "JULIA_PACKAGECOMPILER_XCODE_CLT_MAJOR_VERSION", "")) + ver_int = tryparse(Int, str) + (ver_int isa Int) && return (ver_int >= 15) + + result = _is_xcode_clt() + if result.b + return result.ver >= v"15" + else + return false + end +end