Skip to content
16 changes: 14 additions & 2 deletions src/PackageCompiler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@

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")


##############
Expand Down Expand Up @@ -711,7 +715,15 @@
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`

Check warning on line 720 in src/PackageCompiler.jl

View check run for this annotation

Codecov / codecov/patch

src/PackageCompiler.jl#L720

Added line #L720 was not covered by tests
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)
Expand Down
61 changes: 61 additions & 0 deletions src/xcode.jl
Original file line number Diff line number Diff line change
@@ -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)

Check warning on line 30 in src/xcode.jl

View check run for this annotation

Codecov / codecov/patch

src/xcode.jl#L23-L30

Added lines #L23 - L30 were not covered by tests
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)

Check warning on line 36 in src/xcode.jl

View check run for this annotation

Codecov / codecov/patch

src/xcode.jl#L32-L36

Added lines #L32 - L36 were not covered by tests
end
b = true

Check warning on line 38 in src/xcode.jl

View check run for this annotation

Codecov / codecov/patch

src/xcode.jl#L38

Added line #L38 was not covered by tests
else
b = false
ver = nothing

Check warning on line 41 in src/xcode.jl

View check run for this annotation

Codecov / codecov/patch

src/xcode.jl#L40-L41

Added lines #L40 - L41 were not covered by tests
end
return (; b, ver)

Check warning on line 43 in src/xcode.jl

View check run for this annotation

Codecov / codecov/patch

src/xcode.jl#L43

Added line #L43 was not covered by tests
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"

Check warning on line 57 in src/xcode.jl

View check run for this annotation

Codecov / codecov/patch

src/xcode.jl#L57

Added line #L57 was not covered by tests
else
return false
end
end
Loading