Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ebb3ded
juliac: log entrypoints and nonstandard typedefs
timholy Jul 27, 2025
1099753
minor Makefile improvements
timholy Jul 27, 2025
e90786b
Nicer printing
timholy Jul 28, 2025
ce0f007
Use IdSet instead of Union
timholy Jul 29, 2025
334f613
Try using the library
timholy Jul 29, 2025
612350a
Remove Array from C-friendly types
timholy Jul 30, 2025
c2dffaf
Get the shared library working
timholy Jul 30, 2025
ff8931c
Clean up after trimming tests
timholy Jul 30, 2025
52878f3
Update to JuliaLibWrapping .h file
timholy Jul 30, 2025
106868a
Split `write_logfile` into separate function
timholy Jul 30, 2025
cb068c2
Update contrib/juliac/juliac-buildscript.jl
timholy Jul 31, 2025
c0e1c3e
Export ABI metadata in JSON format
topolarity Aug 1, 2025
a1b8349
Move ABI export functionality to `abi_export.jl`
topolarity Aug 1, 2025
0a744d6
Print field / argument info more compactly
topolarity Aug 1, 2025
e1d3052
Add `--export-abi` arg to `juliac.jl`
topolarity Aug 1, 2025
d51511a
Add extra metadata for `primitive` types
topolarity Aug 1, 2025
76057d4
Minor fix-up for makefile paths
topolarity Aug 4, 2025
f5ffa0c
Merge remote-tracking branch 'julialang/master' into teh/juliac_heade…
topolarity Aug 4, 2025
0c38e91
Update header file to latest from `JuliaLibWrapping.jl`
topolarity Aug 4, 2025
6fd68ec
Adjust to `Core.MethodTable` -> `Core.methodtable`
topolarity Aug 4, 2025
77b1916
Minor fix-up for makefile paths
topolarity Aug 4, 2025
c8a0c0b
Update `test/trimming/trimming.jl` for JSON ABI format
topolarity Aug 4, 2025
af8e042
Rename `type` to `type_id`
topolarity Aug 19, 2025
6161dab
Add recursive type tests
topolarity Aug 19, 2025
cb503ee
Update contrib/juliac/abi_export.jl
timholy Aug 31, 2025
93a0557
Add isptr and isfieldatomic
timholy Aug 31, 2025
d9ea0eb
Merge branch 'master' into teh/juliac_headerlogs
timholy Aug 31, 2025
be561b7
Fix spacing
timholy Aug 31, 2025
7adc37b
Debug tests
timholy Aug 31, 2025
53b84e6
Remove project
timholy Aug 31, 2025
4dfc45b
Remove module qualification
timholy Sep 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
/test/results_*.json
/test/results_*.dat
/test/deps
/test/trimming/Manifest.toml

*.expmap
*.exe
Expand Down
64 changes: 64 additions & 0 deletions contrib/juliac/juliac-buildscript.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,62 @@ if Base.JLOptions().trim != 0
include(joinpath(@__DIR__, "juliac-trim-base.jl"))
end

const C_friendly_types = Base.IdSet{Any}([ # a few of these are redundant to make it easier to maintain
Int8, Int16, Int32, Int64, UInt8, UInt16, UInt32, UInt64, Float32, Float64, Bool,
Cvoid, Cint, Cshort, Clong, Cuint, Cushort, Culong, Cssize_t, Csize_t,
Cchar, Cwchar_t, Cstring, Cwstring,
RawFD,
])

function recursively_add_types!(types::Base.IdSet{DataType}, @nospecialize(T::DataType))
if T ∉ C_friendly_types
if T <: Ptr
return recursively_add_types!(types, T.parameters[1])
end
T.name.module === Core && error("invalid type for juliac: ", T) # exclude internals (they may change)
push!(types, T)
end
for list in (T.parameters, fieldtypes(T))
for S in list
recursively_add_types!(types, S)
end
end
return types
end

function write_logfile(io::IO)
iotmp = IOBuffer()
types = Base.IdSet{DataType}()
Base.visit(Core.GlobalMethods) do method
if isdefined(method, :ccallable)
rt, sig = method.ccallable
name = length(method.ccallable) > 2 ? Symbol(method.ccallable[3]) : method.name
print(IOContext(iotmp, :print_method_signature_only => true), method)
methodstr = String(take!(iotmp))
if name !== method.name
methodstr = replace(methodstr, String(method.name) => String(name))
end
println(io, methodstr, "::", rt)
for T in sig.parameters[2:end]
recursively_add_types!(types, T)
end
end
end
println(io) # blank line separates methods from types
for T in types
println(io, T)
dtfd = Base.DataTypeFieldDesc(T)
local fd
for i = 1:Base.datatype_nfields(T)
fd = dtfd[i]
fn = fieldname(T, i)
ft = fieldtype(T, i)
println(io, " ", fn, "::", ft, "[", fd.offset, "]")
end
println(io, fd.offset + fd.size, " bytes")
end
end

# Load user code

import Base.Experimental.entrypoint
Expand Down Expand Up @@ -73,6 +129,14 @@ let mod = Base.include(Main, ARGS[1])
if ARGS[3] == "true"
ccall(:jl_add_ccallable_entrypoints, Cvoid, ())
end

# Export info about entrypoints and structs needed to create header files
if length(ARGS) >= 4
logfile = ARGS[4]
open(logfile, "w") do io
write_logfile(io)
end
end
end

if Base.JLOptions().trim != 0
Expand Down
1 change: 1 addition & 0 deletions test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ gcext:

trimming:
@$(MAKE) -C $(SRCDIR)/$@ check $(TRIMMING_ARGS)
@$(MAKE) -C $(SRCDIR)/$@ clean $(TRIMMING_ARGS)

clangsa:
@$(MAKE) -C $(SRCDIR)/$@
Expand Down
12 changes: 9 additions & 3 deletions test/trimming/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ JULIAC_BUILDSCRIPT := $(shell $(JULIA) -e 'print(joinpath(Sys.BINDIR, Base.DATAR

#=============================================================================

release: $(BIN)/hello$(EXE) $(BIN)/basic_jll$(EXE)
release: $(BIN)/hello$(EXE) $(BIN)/basic_jll$(EXE) $(BIN)/capplication$(EXE)

$(BIN)/hello-o.a: $(SRCDIR)/hello.jl $(JULIAC_BUILDSCRIPT)
$(JULIA) -t 1 -J $(JULIA_LIBDIR)/julia/sys.$(SHLIB_EXT) --startup-file=no --history-file=no --output-o $@ --output-incremental=no --strip-ir --strip-metadata --experimental --trim $(JULIAC_BUILDSCRIPT) $< --output-exe true
Expand All @@ -42,17 +42,23 @@ $(BIN)/basic_jll-o.a: $(SRCDIR)/basic_jll.jl $(JULIAC_BUILDSCRIPT)
$(JULIA) -t 1 -J $(JULIA_LIBDIR)/julia/sys.$(SHLIB_EXT) --startup-file=no --history-file=no --project=$(SRCDIR) -e "using Pkg; Pkg.instantiate()"
$(JULIA) -t 1 -J $(JULIA_LIBDIR)/julia/sys.$(SHLIB_EXT) --startup-file=no --history-file=no --project=$(SRCDIR) --output-o $@ --output-incremental=no --strip-ir --strip-metadata --experimental --trim $(JULIAC_BUILDSCRIPT) $< --output-exe true

$(BIN)/libsimple-o.a: $(SRCDIR)/libsimple.jl $(JULIAC_BUILDSCRIPT)
$(JULIA) -t 1 -J $(JULIA_LIBDIR)/julia/sys.$(SHLIB_EXT) --startup-file=no --history-file=no --output-o $@ --output-incremental=no --strip-ir --strip-metadata --experimental --trim $(JULIAC_BUILDSCRIPT) $< --output-lib true $(BIN)/bindinginfo_libsimple.log

$(BIN)/hello$(EXE): $(BIN)/hello-o.a
$(CC) -o $@ $(WHOLE_ARCHIVE) $< $(NO_WHOLE_ARCHIVE) $(CPPFLAGS_ADD) $(CPPFLAGS) $(CFLAGS_ADD) $(CFLAGS) $(LDFLAGS_ADD) $(LDFLAGS)

$(BIN)/basic_jll$(EXE): $(BIN)/basic_jll-o.a
$(CC) -o $@ $(WHOLE_ARCHIVE) $< $(NO_WHOLE_ARCHIVE) $(CPPFLAGS_ADD) $(CPPFLAGS) $(CFLAGS_ADD) $(CFLAGS) $(LDFLAGS_ADD) $(LDFLAGS)

check: $(BIN)/hello$(EXE) $(BIN)/basic_jll$(EXE)
$(BIN)/capplication$(EXE): $(BIN)/capplication.c $(BIN)/libsimple.h $(BIN)/libsimple-o.a
$(CC) -I$(BIN) -I$(SRCDIR) -I$(JULIA_LIBDIR) -o $@ $< -Wl,--whole-archive libsimple-o.a -Wl,--no-whole-archive $(LDFLAGS_ADD) $(LDFLAGS) $(CPPFLAGS_ADD) $(CPPFLAGS) $(CFLAGS_ADD) $(CFLAGS)

check: $(BIN)/hello$(EXE) $(BIN)/basic_jll$(EXE) $(BIN)/capplication$(EXE)
$(JULIA) --depwarn=error $(SRCDIR)/trimming.jl $<

clean:
-rm -f $(BIN)/hello$(EXE) $(BIN)/basic_jll$(EXE) $(BIN)/hello-o.a $(BIN)/basic_jll-o.a
-rm -f $(BIN)/hello$(EXE) $(BIN)/basic_jll$(EXE) $(BIN)/hello-o.a $(BIN)/basic_jll-o.a $(BIN)/libsimple-o.a $(BIN)/libsimple.$(SHLIB_EXT) $(BIN)/capplication$(EXE) $(BIN)/bindinginfo_libsimple.log

.PHONY: release clean check

Expand Down
20 changes: 20 additions & 0 deletions test/trimming/capplication.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include <stdio.h>
#include "libsimple.h"

int main() {
// Example usage of the functions defined in libsimple.h
CVectorPair_float_ vecPair;
vecPair.from.length = 3;
vecPair.from.data = (float[]){1.0f, 2.0f, 3.0f};
vecPair.to.length = 3;
vecPair.to.data = (float[]){4.0f, 5.0f, 6.0f};

float sum = copyto_and_sum(vecPair);
printf("Sum of copied values: %f\n", sum);

MyTwoVec list[] = {{1, 2}, {5, 5}, {3, 4}};
int32_t count = countsame(list, 3);
printf("Count of same vectors: %d\n", count);

return 0;
}
22 changes: 22 additions & 0 deletions test/trimming/libsimple.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef JULIALIB_LIBSIMPLE_H
#define JULIALIB_LIBSIMPLE_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>

typedef struct {
int32_t length;
float * data;
} CVector_float_;
typedef struct {
CVector_float_ from;
CVector_float_ to;
} CVectorPair_float_;
typedef struct {
int32_t x;
int32_t y;
} MyTwoVec;

int32_t countsame(MyTwoVec * list, int32_t n);
float copyto_and_sum(CVectorPair_float_ fromto);
#endif // JULIALIB_LIBSIMPLE_H
44 changes: 44 additions & 0 deletions test/trimming/libsimple.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module SimpleLib
# Test the logging of entrypoints and types in a C-callable Julia library.

struct CVector{T}
length::Cint
data::Ptr{T}
end

struct CVectorPair{T}
from::CVector{T}
to::CVector{T}
end

struct MyTwoVec
x::Int32
y::Int32
end

Base.@ccallable "copyto_and_sum" function badname(fromto::CVectorPair{Float32})::Float32
from, to = unsafe_wrap(Array, fromto.from.data, fromto.from.length), unsafe_wrap(Array, fromto.to.data, fromto.to.length)
copyto!(to, from)
return sum(to)
end

Base.@ccallable function countsame(list::Ptr{MyTwoVec}, n::Int32)::Int32
list = unsafe_wrap(Array, list, n)
count = 0
for v in list
count += v.x == v.y
end
return count
end

export countsame, copyto_and_sum

# FIXME? varargs
# Base.@ccallable function printints(x::Cint...)::Nothing
# for i in 1:length(x)
# print(x[i], " ")
# end
# println()
# end

end
33 changes: 33 additions & 0 deletions test/trimming/trimming.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,37 @@ let exe_suffix = splitext(Base.julia_exename())[2]
@test lines[2] == lines[3]
@test Base.VersionNumber(lines[2]) ≥ v"1.5.7"
@test filesize(basic_jll_exe) < filesize(unsafe_string(Base.JLOptions().image_file))/10

# Test that the shared library can be used in a C application
capplication_exe = joinpath(bindir, "capplication" * exe_suffix)
lines = split(readchomp(`$capplication_exe`), "\n")
@test length(lines) == 2
@test lines[1] == "Sum of copied values: 6.000000"
@test lines[2] == "Count of same vectors: 1"

# Test that the logging of entrypoints and types works correctly
str = read(joinpath(bindir, "bindinginfo_libsimple.log"), String)
@test occursin("copyto_and_sum(fromto::CVectorPair{Float32})::Float32", str)
@test occursin(
"""
CVector{Float32}
length::Int32[0]
data::Ptr{Float32}[8]
16 bytes""", str
)
@test occursin(
"""
CVectorPair{Float32}
from::CVector{Float32}[0]
to::CVector{Float32}[16]
32 bytes""", str
)
# ensure that there is a blank line between methods and types
lines = split(str, '\n'; keepempty=true)
nblanks = 0
for line in lines
nblanks += isempty(line)
occursin("length", line) && break
end
@test nblanks == 1
end