From 9ec8e340b35bb258e166a9a73f2583731227ba01 Mon Sep 17 00:00:00 2001 From: SimonDanisch Date: Tue, 13 Feb 2018 13:06:07 +0100 Subject: [PATCH] merge files from static-julia --- src/staticjulia/.gitignore | 8 + src/staticjulia/.mailmap | 8 + src/staticjulia/README.md | 120 +++++++++++++ src/staticjulia/hello.jl | 14 ++ src/staticjulia/juliac.jl | 347 +++++++++++++++++++++++++++++++++++++ src/staticjulia/program.c | 35 ++++ src/staticjulia/program2.c | 63 +++++++ 7 files changed, 595 insertions(+) create mode 100644 src/staticjulia/.gitignore create mode 100644 src/staticjulia/.mailmap create mode 100644 src/staticjulia/README.md create mode 100644 src/staticjulia/hello.jl create mode 100644 src/staticjulia/juliac.jl create mode 100644 src/staticjulia/program.c create mode 100644 src/staticjulia/program2.c diff --git a/src/staticjulia/.gitignore b/src/staticjulia/.gitignore new file mode 100644 index 00000000..dbad9a70 --- /dev/null +++ b/src/staticjulia/.gitignore @@ -0,0 +1,8 @@ +*.ji +*.o +*.so +*.so.* +*.dylib +*.dll +hello +hello.exe diff --git a/src/staticjulia/.mailmap b/src/staticjulia/.mailmap new file mode 100644 index 00000000..f546accf --- /dev/null +++ b/src/staticjulia/.mailmap @@ -0,0 +1,8 @@ +Luca Trevisani +Luca Trevisani + +Viral B. Shah +Viral B. Shah +Viral B. Shah + +Simon Danisch diff --git a/src/staticjulia/README.md b/src/staticjulia/README.md new file mode 100644 index 00000000..3e73e87b --- /dev/null +++ b/src/staticjulia/README.md @@ -0,0 +1,120 @@ +# Static Julia Compiler + +Building shared libraries and executables from Julia code. + +Run `juliac.jl -h` for help: + +``` +usage: juliac.jl [-v] [-q] [-c] [-J ] + [--compile {yes|no|all|min}] [-C ] + [-O {0,1,2,3}] [-g {0,1,2}] [--inline {yes|no}] + [--check-bounds {yes|no}] [--math-mode {ieee,fast}] + [--depwarn {yes|no|error}] [-a] [-o] [-s] [-e] [-j] + [--version] [-h] juliaprog [cprog] [builddir] + +Static Julia Compiler + +positional arguments: + juliaprog Julia program to compile + cprog C program to compile (required only when + building an executable; if not provided a + minimal standard program is used) + builddir build directory, either absolute or relative + to the Julia program directory (default: + "builddir") + +optional arguments: + -v, --verbose increase verbosity + -q, --quiet suppress non-error messages + -c, --clean delete builddir + -J, --sysimage + start up with the given system image file + --compile {yes|no|all|min} + enable or disable JIT compiler, or request + exhaustive compilation + -C, --cpu-target + limit usage of CPU features up to + -O, --optimize {0,1,2,3} + set optimization level (type: Int64) + -g {0,1,2} set debugging information level (type: Int64) + --inline {yes|no} control whether inlining is permitted + --check-bounds {yes|no} + emit bounds checks always or never + --math-mode {ieee,fast} + set floating point optimizations + --depwarn {yes|no|error} + set syntax and method deprecation warnings + -a, --autodeps automatically build required dependencies + -o, --object build object file + -s, --shared build shared library + -e, --executable build executable file + -j, --julialibs sync Julia libraries to builddir + --version show version information and exit + -h, --help show this help message and exit + +examples: + juliac.jl -vae hello.jl # verbose, build executable and deps + juliac.jl -vae hello.jl prog.c # embed into user defined C program + juliac.jl -qo hello.jl # quiet, build object file only + juliac.jl -vosej hello.jl # build all and sync Julia libs +``` + +### Notes + +1. The `juliac.jl` script uses the `ArgParse` package, make sure it is installed. + +3. On Windows install `Cygwin` and the `mingw64-x86_64-gcc-core` package, see:\ + https://github.com/JuliaLang/julia/blob/master/README.windows.md + +2. A shared library containing the system image `libhello.so`, and a + driver binary `hello` are created in the `builddir` directory. + Running `hello` produces the following output: + +``` + $ ./hello + hello, world + f() = -0.37549581296986956 + ┌─────────────────────────────────────────────────┐ + 100 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠎│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠊⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠁⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠁⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠤⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠤⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠤⠒⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠔⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + │⠀⠀⠀⠀⠀⠀⢀⣀⠤⠔⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + 0 │⣀⠤⠤⠔⠒⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ + └─────────────────────────────────────────────────┘ + 1 10 +``` + +## Under the hood + +The `juliac.jl` script uses the `--output-o` switch to compile the user +script into object code, and then builds it into the system image +specified by the `-J` switch. This prepares an object file, which is +then linked into a shared library containing the system image and user +code. A driver script such as the one in `program.c` can then be used to +build a binary that runs the julia code. + +Instead of a driver script, the generated system image can be embedded +into a larger program following the embedding examples and relevant +sections in the Julia manual. Note that the name of the generated system +image (`"libhello"` for `hello.jl`) is accessible from C in the +preprocessor macro `JULIAC_PROGRAM_LIBNAME`. + +With Julia 0.7, a single large binary can be created, which does not +require the driver program to load the shared library. An example of +that is in `program2.c`, where the image file is the binary itself. + +For more information on static Julia compilation see:\ +https://juliacomputing.com/blog/2016/02/09/static-julia.html + +For more information on embedding Julia see:\ +https://github.com/JuliaLang/julia/blob/master/doc/src/manual/embedding.md diff --git a/src/staticjulia/hello.jl b/src/staticjulia/hello.jl new file mode 100644 index 00000000..23a1dd2e --- /dev/null +++ b/src/staticjulia/hello.jl @@ -0,0 +1,14 @@ +module Hello + +using UnicodePlots +using Distributions +f() = rand(Normal()) + +Base.@ccallable function julia_main(ARGS::Vector{String})::Cint + println("hello, world") + @show f() + println(lineplot(1:10, (1:10).^2)) + return 0 +end + +end diff --git a/src/staticjulia/juliac.jl b/src/staticjulia/juliac.jl new file mode 100644 index 00000000..778f254a --- /dev/null +++ b/src/staticjulia/juliac.jl @@ -0,0 +1,347 @@ +## Assumptions: +## 1. gcc / x86_64-w64-mingw32-gcc is available and is in path +## 2. Package ArgParse is installed + +# TODO: remove once Julia v0.7 is released +julia_v07 = VERSION > v"0.7-" + +using ArgParse +julia_v07 && using Libdl + +function main(args) + + s = ArgParseSettings("Static Julia Compiler", + version = "$(basename(@__FILE__)) version 0.7-DEV", + add_version = true) + + @add_arg_table s begin + "juliaprog" + arg_type = String + required = true + help = "Julia program to compile" + "cprog" + arg_type = String + default = nothing + help = "C program to compile (required only when building an executable; if not provided a minimal driver program is used)" + "builddir" + arg_type = String + default = "builddir" + help = "build directory, either absolute or relative to the Julia program directory" + "--verbose", "-v" + action = :store_true + help = "increase verbosity" + "--quiet", "-q" + action = :store_true + help = "suppress non-error messages" + "--clean", "-c" + action = :store_true + help = "delete builddir" + "--sysimage", "-J" + arg_type = String + default = nothing + metavar = "" + help = "start up with the given system image file" + "--compile" + arg_type = String + default = nothing + metavar = "{yes|no|all|min}" + range_tester = (x -> x == "yes" || x == "no" || x == "all" || x == "min") + help = "enable or disable JIT compiler, or request exhaustive compilation" + "--cpu-target", "-C" + arg_type = String + default = nothing + metavar = "" + help = "limit usage of CPU features up to " + "--optimize", "-O" + arg_type = Int + default = nothing + metavar = "{0,1,2,3}" + range_tester = (x -> 0 <= x <= 3) + help = "set optimization level" + "-g" + arg_type = Int + default = nothing + dest_name = "debug" + metavar = "{0,1,2}" + range_tester = (x -> 0 <= x <= 2) + help = "set debugging information level" + "--inline" + arg_type = String + default = nothing + metavar = "{yes|no}" + range_tester = (x -> x == "yes" || x == "no") + help = "control whether inlining is permitted" + "--check-bounds" + arg_type = String + default = nothing + metavar = "{yes|no}" + range_tester = (x -> x == "yes" || x == "no") + help = "emit bounds checks always or never" + "--math-mode" + arg_type = String + default = nothing + metavar = "{ieee,fast}" + range_tester = (x -> x == "ieee" || x == "fast") + help = "set floating point optimizations" + "--depwarn" + arg_type = String + default = nothing + metavar = "{yes|no|error}" + range_tester = (x -> x == "yes" || x == "no" || x == "error") + help = "set syntax and method deprecation warnings" + "--autodeps", "-a" + action = :store_true + help = "automatically build required dependencies" + "--object", "-o" + action = :store_true + help = "build object file" + "--shared", "-s" + action = :store_true + help = "build shared library" + "--executable", "-e" + action = :store_true + help = "build executable file" + "--julialibs", "-j" + action = :store_true + help = "sync Julia libraries to builddir" + end + + s.epilog = """ + examples:\n + \ua0\ua0juliac.jl -vae hello.jl # verbose, build executable and deps\n + \ua0\ua0juliac.jl -vae hello.jl prog.c # embed into user defined C program\n + \ua0\ua0juliac.jl -qo hello.jl # quiet, build object file only\n + \ua0\ua0juliac.jl -vosej hello.jl # build all and sync Julia libs\n + """ + + parsed_args = parse_args(args, s) + + # TODO: in future it may be possible to broadcast dictionary indexing, see: https://discourse.julialang.org/t/accessing-multiple-values-of-a-dictionary/8648 + if !any(getindex.(parsed_args, ["clean", "object", "shared", "executable", "julialibs"])) + parsed_args["quiet"] || println("Nothing to do, exiting\nTry \"$(basename(@__FILE__)) -h\" for more information") + exit(0) + end + + julia_compile( + parsed_args["juliaprog"], + parsed_args["cprog"], + parsed_args["builddir"], + parsed_args["verbose"], + parsed_args["quiet"], + parsed_args["clean"], + parsed_args["sysimage"], + parsed_args["compile"], + parsed_args["cpu-target"], + parsed_args["optimize"], + parsed_args["debug"], + parsed_args["inline"], + parsed_args["check-bounds"], + parsed_args["math-mode"], + parsed_args["depwarn"], + parsed_args["autodeps"], + parsed_args["object"], + parsed_args["shared"], + parsed_args["executable"], + parsed_args["julialibs"] + ) +end + +function julia_compile(julia_program, c_program=nothing, build_dir="builddir", verbose=false, quiet=false, + clean=false, sysimage = nothing, compile=nothing, cpu_target=nothing, optimize=nothing, + debug=nothing, inline=nothing, check_bounds=nothing, math_mode=nothing, depwarn=nothing, + autodeps=false, object=false, shared=false, executable=true, julialibs=true) + + verbose && quiet && (quiet = false) + + if autodeps + executable && (shared = true) + shared && (object = true) + end + + julia_program = abspath(julia_program) + isfile(julia_program) || error("Cannot find file:\n \"$julia_program\"") + quiet || println("Julia program file:\n \"$julia_program\"") + + if executable + c_program = c_program == nothing ? joinpath(@__DIR__, "program.c") : abspath(c_program) + isfile(c_program) || error("Cannot find file:\n \"$c_program\"") + quiet || println("C program file:\n \"$c_program\"") + end + + cd(dirname(julia_program)) + + build_dir = abspath(build_dir) + quiet || println("Build directory:\n \"$build_dir\"") + + if clean + if isdir(build_dir) + verbose && println("Delete build directory") + rm(build_dir, recursive=true) + else + verbose && println("Build directory does not exist, nothing to delete") + end + end + + if !isdir(build_dir) + verbose && println("Make build directory") + mkpath(build_dir) + end + + if pwd() != build_dir + verbose && println("Change to build directory") + cd(build_dir) + else + verbose && println("Already in build directory") + end + + julia_program_basename = splitext(basename(julia_program))[1] + o_file = julia_program_basename * ".o" + s_file = "lib" * julia_program_basename * ".$(Libdl.dlext)" + if julia_v07 + e_file = julia_program_basename * (Sys.iswindows() ? ".exe" : "") + else + e_file = julia_program_basename * (is_windows() ? ".exe" : "") + end + tmp_dir = "tmp_v$VERSION" + + # TODO: these should probably be emitted from julia-config also: + if julia_v07 + shlibdir = Sys.iswindows() ? Sys.BINDIR : abspath(Sys.BINDIR, Base.LIBDIR) + private_shlibdir = abspath(Sys.BINDIR, Base.PRIVATE_LIBDIR) + else + shlibdir = is_windows() ? JULIA_HOME : abspath(JULIA_HOME, Base.LIBDIR) + private_shlibdir = abspath(JULIA_HOME, Base.PRIVATE_LIBDIR) + end + + if object + julia_cmd = `$(Base.julia_cmd())` + if length(julia_cmd.exec) != 5 || !all(startswith.(julia_cmd.exec[2:5], ["-C", "-J", "--compile", "--depwarn"])) + error("Unexpected format of \"Base.julia_cmd()\", you may be using an incompatible version of Julia") + end + sysimage == nothing || (julia_cmd.exec[3] = "-J$sysimage") + push!(julia_cmd.exec, "--startup-file=no") + compile == nothing || (julia_cmd.exec[4] = "--compile=$compile") + cpu_target == nothing || (julia_cmd.exec[2] = "-C$cpu_target") + optimize == nothing || push!(julia_cmd.exec, "-O$optimize") + debug == nothing || push!(julia_cmd.exec, "-g$debug") + inline == nothing || push!(julia_cmd.exec, "--inline=$inline") + check_bounds == nothing || push!(julia_cmd.exec, "--check-bounds=$check_bounds") + math_mode == nothing || push!(julia_cmd.exec, "--math-mode=$math_mode") + depwarn == nothing || (julia_cmd.exec[5] = "--depwarn=$depwarn") + if julia_v07 + Sys.iswindows() && (julia_program = replace(julia_program, "\\", "\\\\")) + expr = " + Base.init_depot_path() # initialize package depots + Base.init_load_path() # initialize location of site-packages + empty!(Base.LOAD_CACHE_PATH) # reset / remove any builtin paths + push!(Base.LOAD_CACHE_PATH, abspath(\"$tmp_dir\")) # enable usage of precompiled files + include(\"$julia_program\") # include \"julia_program\" file + empty!(Base.LOAD_CACHE_PATH) # reset / remove build-system-relative paths" + else + is_windows() && (julia_program = replace(julia_program, "\\", "\\\\")) + expr = " + empty!(Base.LOAD_CACHE_PATH) # reset / remove any builtin paths + push!(Base.LOAD_CACHE_PATH, abspath(\"$tmp_dir\")) # enable usage of precompiled files + include(\"$julia_program\") # include \"julia_program\" file + empty!(Base.LOAD_CACHE_PATH) # reset / remove build-system-relative paths" + end + isdir(tmp_dir) || mkpath(tmp_dir) + command = `$julia_cmd -e $expr` + verbose && println("Build module image files \".ji\" in subdirectory \"$tmp_dir\":\n $command") + run(command) + command = `$julia_cmd --output-o $(joinpath(tmp_dir, o_file)) -e $expr` + verbose && println("Build object file \"$o_file\" in subdirectory \"$tmp_dir\":\n $command") + run(command) + end + + if shared || executable + if julia_v07 + cc = Sys.iswindows() ? "x86_64-w64-mingw32-gcc" : "gcc" + command = `$(Base.julia_cmd()) --startup-file=no $(joinpath(dirname(Sys.BINDIR), "share", "julia", "julia-config.jl"))` + flags = `$(Base.shell_split(read(\`$command --allflags\`, String)))` + else + cc = is_windows() ? "x86_64-w64-mingw32-gcc" : "gcc" + command = `$(Base.julia_cmd()) --startup-file=no $(joinpath(dirname(JULIA_HOME), "share", "julia", "julia-config.jl"))` + cflags = `$(Base.shell_split(readstring(\`$command --cflags\`)))` + ldflags = `$(Base.shell_split(readstring(\`$command --ldflags\`)))` + ldlibs = `$(Base.shell_split(readstring(\`$command --ldlibs\`)))` + flags = `$cflags $ldflags $ldlibs` + end + end + + if shared + command = `$cc -m64 -shared -o $s_file $(joinpath(tmp_dir, o_file)) $flags` + if julia_v07 + if Sys.isapple() + command = `$command -Wl,-install_name,@rpath/\"$s_file\"` + elseif Sys.iswindows() + command = `$command -Wl,--export-all-symbols` + end + else + if is_apple() + command = `$command -Wl,-install_name,@rpath/\"$s_file\"` + elseif is_windows() + command = `$command -Wl,--export-all-symbols` + end + end + verbose && println("Build shared library \"$s_file\" in build directory:\n $command") + run(command) + end + + if executable + command = `$cc -m64 -DJULIAC_PROGRAM_LIBNAME=\"lib$julia_program_basename\" -o $e_file $c_program $s_file $flags` + if julia_v07 + if Sys.isapple() + command = `$command -Wl,-rpath,@executable_path` + elseif Sys.isunix() + command = `$command -Wl,-rpath,\$ORIGIN` + end + else + if is_apple() + command = `$command -Wl,-rpath,@executable_path` + elseif is_unix() + command = `$command -Wl,-rpath,\$ORIGIN` + end + end + verbose && println("Build executable file \"$e_file\" in build directory:\n $command") + run(command) + end + + if julialibs + verbose && println("Sync Julia libraries to build directory:") + libfiles = String[] + dlext = "." * Libdl.dlext + for dir in (shlibdir, private_shlibdir) + if julia_v07 + if Sys.iswindows() || Sys.isapple() + append!(libfiles, joinpath.(dir, filter(x -> endswith(x, dlext), readdir(dir)))) + else + append!(libfiles, joinpath.(dir, filter(x -> contains(x, r"^lib.+\.so(?:\.\d+)*$"), readdir(dir)))) + end + else + if is_windows() || is_apple() + append!(libfiles, joinpath.(dir, filter(x -> endswith(x, dlext), readdir(dir)))) + else + append!(libfiles, joinpath.(dir, filter(x -> ismatch(r"^lib.+\.so(?:\.\d+)*$", x), readdir(dir)))) + end + end + end + sync = false + for src in libfiles + if julia_v07 + contains(src, r"debug") && continue + else + ismatch(r"debug", src) && continue + end + dst = basename(src) + if filesize(src) != filesize(dst) || ctime(src) > ctime(dst) || mtime(src) > mtime(dst) + verbose && println(" $dst") + cp(src, dst, remove_destination=true, follow_symlinks=false) + sync = true + end + end + sync || verbose && println(" none") + end +end + +main(ARGS) diff --git a/src/staticjulia/program.c b/src/staticjulia/program.c new file mode 100644 index 00000000..aa5737c3 --- /dev/null +++ b/src/staticjulia/program.c @@ -0,0 +1,35 @@ +// This file is a part of Julia. License is MIT: http://julialang.org/license + +// Standard headers +#include +#include + +// Julia headers (for initialization and gc commands) +#include "uv.h" +#include "julia.h" + +#ifdef JULIA_DEFINE_FAST_TLS // only available in Julia v0.7 and above +JULIA_DEFINE_FAST_TLS() +#endif + +// Declare C prototype of a function defined in Julia +extern void julia_main(); + +int main(int argc, char *argv[]) +{ + intptr_t v; + + // Initialize Julia + uv_setup_args(argc, argv); // no-op on Windows + libsupport_init(); + // JULIAC_PROGRAM_LIBNAME defined on command-line for compilation + jl_options.image_file = JULIAC_PROGRAM_LIBNAME; + julia_init(JL_IMAGE_JULIA_HOME); + + // Do some work + julia_main(); + + // Cleanup and graceful exit + jl_atexit_hook(0); + return 0; +} diff --git a/src/staticjulia/program2.c b/src/staticjulia/program2.c new file mode 100644 index 00000000..1d214d78 --- /dev/null +++ b/src/staticjulia/program2.c @@ -0,0 +1,63 @@ +// This file is a part of Julia. License is MIT: http://julialang.org/license + +// Standard headers +#include +#include + +// Julia headers (for initialization and gc commands) +#include "uv.h" +#include "julia.h" + +#ifdef JULIA_DEFINE_FAST_TLS // only available in Julia v0.7 and above +JULIA_DEFINE_FAST_TLS() +#endif + +// Declare C prototype of a function defined in Julia +extern int julia_main(jl_array_t*); + +// main function (windows UTF16 -> UTF8 argument conversion code copied from julia's ui/repl.c) +#ifndef _OS_WINDOWS_ +int main(int argc, char *argv[]) +{ + int retcode; + int i; + uv_setup_args(argc, argv); // no-op on Windows +#else +int wmain(int argc, wchar_t *wargv[], wchar_t *envp[]) +{ + int retcode; + int i; + char **argv; + for (i = 0; i < argc; i++) { // convert the command line to UTF8 + wchar_t *warg = argv[i]; + size_t len = WideCharToMultiByte(CP_UTF8, 0, warg, -1, NULL, 0, NULL, NULL); + if (!len) return 1; + char *arg = (char*)alloca(len); + if (!WideCharToMultiByte(CP_UTF8, 0, warg, -1, arg, len, NULL, NULL)) return 1; + argv[i] = arg; + } +#endif + + // initialization + libsupport_init(); + // jl_options.compile_enabled = JL_OPTIONS_COMPILE_OFF; + // JULIAC_PROGRAM_LIBNAME defined on command-line for compilation + jl_options.image_file = JULIAC_PROGRAM_LIBNAME; + julia_init(JL_IMAGE_JULIA_HOME); + + // build arguments array: `String[ unsafe_string(argv[i]) for i in 1:argc ]` + jl_array_t *ARGS = jl_alloc_array_1d(jl_apply_array_type(jl_string_type, 1), 0); + JL_GC_PUSH1(&ARGS); + jl_array_grow_end(ARGS, argc - 1); + for (i = 1; i < argc; i++) { + jl_value_t *s = (jl_value_t*)jl_cstr_to_string(argv[i]); + jl_arrayset(ARGS, s, i - 1); + } + // call the work function, and get back a value + retcode = julia_main(ARGS); + JL_GC_POP(); + + // Cleanup and gracefully exit + jl_atexit_hook(retcode); + return retcode; +}