Repository: JuliaLang/julia
File: contrib/juliac/juliac-buildscript.jl (Julia)

Code before:
if Base.get_bool_env("JULIA_USE_FLISP_PARSER", false) === false
    Base.JuliaSyntax.enable_in_core!()
end

if Base.JLOptions().trim != 0
    include(joinpath(@__DIR__, "juliac-trim-base.jl"))
end

const C_friendly_types = Union{    # 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 is_c_friendly(@nospecialize(T::DataType))
    T <: Ptr && return is_c_friendly(T.parameters[1])
    return T <: C_friendly_types
end

function recursively_add_types!(types::Base.IdSet{DataType}, @nospecialize(T::DataType))
    if !is_c_friendly(T)
        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
end

# Load user code

import Base.Experimental.entrypoint

# for use as C main if needed
function _main(argc::Cint, argv::Ptr{Ptr{Cchar}})::Cint
    args = ccall(:jl_set_ARGS, Any, (Cint, Ptr{Ptr{Cchar}}), argc, argv)::Vector{String}
    return Main.main(args)
end

let mod = Base.include(Main, ARGS[1])
    Core.@latestworld
    if ARGS[2] == "--output-exe"
        have_cmain = false
        if isdefined(Main, :main)
            for m in methods(Main.main)
                if isdefined(m, :ccallable)
                    # TODO: possibly check signature and return type
                    have_cmain = true

Review comment (suggestion):
For e.g. C, we'll have to do our own name mangling to handle parameters, or maybe just disallow them. Either way the parameters don't really matter here and don't need to be c-compatible.

Code after:

include(joinpath(@__DIR__, "abi_export.jl"))

# Load user code

import Base.Experimental.entrypoint

# for use as C main if needed
function _main(argc::Cint, argv::Ptr{Ptr{Cchar}})::Cint
    args = ccall(:jl_set_ARGS, Any, (Cint, Ptr{Ptr{Cchar}}), argc, argv)::Vector{String}
    setglobal!(Base, :PROGRAM_FILE, args[1])
    popfirst!(args)
    append!(Base.ARGS, args)
    return Main.main(args)
end

let include_result = Base.include(Main, ARGS[1])
    Core.@latestworld
    if ARGS[2] == "--output-exe"
        have_cmain = false
        if isdefined(Main, :main)
            for m in methods(Main.main)
                if isdefined(m, :ccallable)
                    # TODO: possibly check signature and return type
                    have_cmain = true
                    break
                end
            end
        elseif include_result isa Module && isdefined(include_result, :main)
            error("""
                  The `main` function must be defined in `Main`. If you are defining it inside a
                  module, try adding `import .$(nameof(include_result)).main` to $(ARGS[1]).
                  """)
        end
        if !have_cmain
            if Base.should_use_main_entrypoint()
                if hasmethod(Main.main, Tuple{Vector{String}})
                    entrypoint(_main, (Cint, Ptr{Ptr{Cchar}}))
                    Base._ccallable("main", Cint, Tuple{typeof(_main), Cint, Ptr{Ptr{Cchar}}})
                else
                    error("`@main` must accept a `Vector{String}` argument.")
                end
            else
                error("To generate an executable a `@main` function must be defined in the `Main` module.")
            end
        end
    end
    #entrypoint(join, (Base.GenericIOBuffer{Memory{UInt8}}, Array{Base.SubString{String}, 1}, String))
    #entrypoint(join, (Base.GenericIOBuffer{Memory{UInt8}}, Array{String, 1}, Char))
    entrypoint(Base.task_done_hook, (Task,))
    entrypoint(Base.wait, ())