Created
November 28, 2017 14:44
-
-
Save fguevaravas/a419948d8789da87303e72c7f17b830c to your computer and use it in GitHub Desktop.
Minimal Julia Compilation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Better way for creating standalone EXE files using Julia, | |
## Taken from: https://github.com/JuliaComputing/static-julia | |
## Assumptions: | |
## 1. g++ / x86_64-w64-mingw32-gcc is available and is in path | |
## 2. patchelf is in the path | |
module MinimalBuild | |
# Assumption: the application main module is in MyApplication.jl | |
# this module must export a function called julia_main that is ccallable | |
appname = "MyApplication" | |
# Optional: load the module, the only reason we do this is to get | |
# MyApplication.VERSION and use it to give a nice name to the directory | |
# to distribute | |
using MyApplication | |
""" | |
pretty print commands as we are running them | |
""" | |
function run_with_echo(cmd) | |
info("running: $cmd") | |
run(cmd) | |
end | |
""" | |
creates shared object + exe in the current directory | |
""" | |
function compile(julia_program_file, julia_install_path, | |
julia_pkgdir=joinpath(Pkg.dir(), "..")) | |
filename = split(julia_program_file, ".")[1] | |
O_FILE = "$(filename).o" | |
SYS_LIB = joinpath(julia_install_path, "lib", "julia", "sys.$(Libdl.dlext)") | |
JULIA_EXE = joinpath(julia_install_path, "bin", "julia") | |
LIB_PATH = joinpath(julia_install_path, "lib") | |
IMAGE_FILE = "lib$(filename).$(Libdl.dlext)" | |
SO_FILE = IMAGE_FILE | |
EXE_FILE = is_windows() ? "$(filename).exe" : "$(filename)" | |
if is_windows() | |
julia_pkgdir = replace(julia_pkgdir, "\\", "\\\\") | |
julia_program_file = replace(julia_program_file, "\\", "\\\\") | |
end | |
# the actual compilation of julia code happens here | |
run_with_echo(`"$(Base.julia_cmd())" "-J$(SYS_LIB)" "-Ccore2" "--startup-file=no" "--output-o" "$(O_FILE)" "-e" " | |
vers = \""v$(VERSION.major).$(VERSION.minor)"\" | |
const DIR_NAME = \"".julia"\" | |
push!(Base.LOAD_CACHE_PATH, abspath(\""$julia_pkgdir"\", \""lib"\", vers)) | |
include(\""$(julia_program_file)"\") | |
empty!(Base.LOAD_CACHE_PATH) | |
"`) | |
# julia-config.jl gives automatically the C and LD flags for compilation | |
cflags = Base.shell_split(readstring(`$(JULIA_EXE) $(joinpath(julia_install_path, "share", "julia", "julia-config.jl")) --cflags`)) | |
ldflags = Base.shell_split(readstring(`$(JULIA_EXE) $(joinpath(julia_install_path, "share", "julia", "julia-config.jl")) --ldflags`)) | |
ldlibs = Base.shell_split(readstring(`$(JULIA_EXE) $(joinpath(julia_install_path, "share", "julia", "julia-config.jl")) --ldlibs`)) | |
if is_windows() | |
# we need to setup a sane developping environemnt. | |
# take the one from msys2.org and do pacman -S mingw-w64-x86_64 | |
# (see julia/README.md for more info) | |
run_with_echo(`x86_64-w64-mingw32-gcc -m64 -fPIC -Wl,-export-all-symbols -shared -o $(SO_FILE) $(O_FILE) $(ldflags) $(ldlibs)`) | |
run_with_echo(`x86_64-w64-mingw32-gcc -m64 -DIMAGE_FILE=\"$(IMAGE_FILE)\" program.c -o $(EXE_FILE) -L. -Wl,-rpath,. -l$(filename) $(cflags) $(ldflags) $(ldlibs) -lopenlibm -Wl,-rpath,\$ORIGIN`) | |
else | |
run_with_echo(`g++ -m64 -fPIC -shared -o $(SO_FILE) $(O_FILE) $(ldflags) $(ldlibs)`) | |
run_with_echo(`gcc -m64 -DIMAGE_FILE=\"$(IMAGE_FILE)\" program.c -o $(EXE_FILE) $(cflags) $(ldflags) -L. -Wl,-rpath,. -l$(filename) $(ldlibs) -lm -Wl,-rpath,\$ORIGIN`) | |
end | |
end#function | |
""" | |
relocate shared object and dependencies to target directory | |
""" | |
function do_compile() | |
tic() | |
# figure out julia install path | |
target = "$(appname)_$(MyApplication.VERSION)_$(Sys.MACHINE)" | |
try mkdir(target) catch warn("directory $target exists") end | |
# julia install dir | |
install_path = joinpath(JULIA_HOME,"..") | |
# julia package dir | |
julia_pkgdir=joinpath(Pkg.dir(), "..") | |
# do actual compilation | |
compile("$(appname).jl",install_path,julia_pkgdir) | |
# move files to right place | |
info("Moving files ...") | |
SO_FILE = "lib$(appname).$(Libdl.dlext)" | |
EXE_FILE = is_windows() ? "$(appname).exe" : appname | |
for f in [SO_FILE,EXE_FILE] | |
mv(f,joinpath(target,f),remove_destination=true) | |
end | |
# Copy needed shared libraries to the target directory | |
if is_windows() | |
paths = [JULIA_HOME] | |
end | |
if is_unix() | |
# libjulia is in a different directory than all the other libraries | |
paths = [joinpath(JULIA_HOME,"../lib/julia"), joinpath(JULIA_HOME,"../lib")] | |
end | |
for path in paths | |
# keep files of form *.so.* or *.dylib.* or *.dll | |
shlibs = filter(Regex(".*\.$(Libdl.dlext).*"),readdir(path)) | |
# except sys.[so,dylib,dll] | |
filter!(x->!ismatch(Regex("sys.*"),x),shlibs) | |
for shlib in shlibs | |
cp(joinpath(path, shlib), joinpath(target, shlib), remove_destination=true) | |
end | |
end | |
# on unix systems the generated exe and .so need to be patched in order for the | |
# binary to find the libdfasim library | |
if is_unix() | |
info("patching exe and .so to make them relocatable") | |
end | |
if is_linux() | |
# use patchelf | |
run_with_echo(`patchelf --set-rpath \$ORIGIN/ $(joinpath(target, EXE_FILE))`) | |
run_with_echo(`patchelf --set-rpath \$ORIGIN/ $(joinpath(target, SO_FILE))`) | |
end | |
if is_apple() | |
#See http://voidnoise.co.uk/blog/?p=299 for an explanation | |
run_with_echo(`install_name_tool -id @executable_path/$(SO_FILE) $target/$(SO_FILE)`) | |
run_with_echo(`install_name_tool -change $(SO_FILE) @executable_path/$(SO_FILE) $target/$(appname)`) | |
# to check use: | |
# otool -D $target/libdfasim.dylib | |
# otool -L $target/dfasim | |
end | |
info("Binary package is in: $target. Compilation took $(toq()) seconds") | |
end | |
end#module |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This file is a part of Julia. License is MIT: http://julialang.org/license | |
// Standard headers | |
#include <string.h> | |
#include <stdint.h> | |
// Julia headers (for initialization and gc commands) | |
#include "uv.h" | |
#include "julia.h" | |
// Declare C prototype of a function defined in Julia | |
extern int julia_main(jl_array_t*); | |
// main function | |
int main(int argc, char *argv[]) | |
{ | |
int retcode; | |
int i; | |
uv_setup_args(argc, argv); // no-op on Windows | |
// initialization | |
libsupport_init(); | |
// jl_options.compile_enabled = JL_OPTIONS_COMPILE_OFF; | |
jl_options.julia_home = NULL; | |
jl_options.image_file = IMAGE_FILE; | |
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_value_t*)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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This example combines the approach in BuildExecutable.jl with that of static-julia to compile a distributable package for your julia program. We use ArgParse.jl to handle arguments. This is a usage case where (unfortunately and as of writing this) precompilation does not speed up things.
Note: static-julia has changed a lot since the last time I checked, it now is a standalone CLI utility that compiles your code and I would say makes this example obsolete. However this example automatically packages the binary file in a directory and has been tried out in linux, mac and windows with Julia 0.6.0
Executing the CLI program directly, gives unnaturally slow startup times. (Note how the command line arguments for
MyApplication
are given after the double dash--
).To get the built version, make sure your
~/.juliarc.jl
file containspush!(LOAD_PATH,".")
. Then start an interactive session and write:This takes a while and creates a directory named e.g.
MyApplication_0.1.0_x86_64-apple-darwin13.4.0
with the binary in there. The directory name depends onMyApplication.VERSION
and on the platformSys.MACHINE
. Using this binary gives much more reasonable startup times (though still not as fast as e.g. python or C...)