Skip to content

Commit 57aa56a

Browse files
authored
Merge pull request #20 from JuliaLang/sd/tests
add tests and build_executable
2 parents 2fc5d86 + a2f51eb commit 57aa56a

File tree

11 files changed

+156
-86
lines changed

11 files changed

+156
-86
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ sysimg
1414
*.dll
1515
hello
1616
hello.exe
17+
packages

README.md

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ revert()
2828

2929
# Or if you simply want to get a native system image e.g. when you have downloaded the generic Julia install:
3030
build_native_image()
31+
32+
# building an executable
33+
34+
build_executable(
35+
"hello.jl", # julia file containing a julia main, e.g. like examples/hello.jl
36+
snoopfile = "call_functions.jl", # julia file that calls functions that you want to make sure to have precompiled [optional]
37+
builddir = "folder/you/want/the/build/artifacts" # that's where hello.exe will end up
38+
)
3139
```
3240

3341

@@ -49,6 +57,7 @@ build_native_image()
4957

5058
# Static Julia Compiler
5159

60+
5261
Building shared libraries and executables from Julia code.
5362

5463
Run `juliac.jl -h` for help:
@@ -110,10 +119,7 @@ examples:
110119

111120
### Notes
112121

113-
1. The `juliac.jl` script uses the `ArgParse` package, make sure it is installed.
114-
115-
3. On Windows install `Cygwin` and the `mingw64-x86_64-gcc-core` package, see:\
116-
https://github.com/JuliaLang/julia/blob/master/README.windows.md
122+
1. The `juliac.jl` script is located in the PackageCompiler root folder (Pkg.dir("PackageCompiler"))
117123

118124
2. A shared library containing the system image `libhello.so`, and a
119125
driver binary `hello` are created in the `builddir` directory.
@@ -122,25 +128,26 @@ examples:
122128
```
123129
$ ./hello
124130
hello, world
125-
f() = -0.37549581296986956
126-
┌─────────────────────────────────────────────────┐
127-
100 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠎│
128-
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀│
129-
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠊⠀⠀⠀│
130-
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠁⠀⠀⠀⠀│
131-
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠁⠀⠀⠀⠀⠀⠀│
132-
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀│
133-
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
134-
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
135-
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠤⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
136-
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
137-
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠤⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
138-
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠤⠒⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
139-
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠔⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
140-
│⠀⠀⠀⠀⠀⠀⢀⣀⠤⠔⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
141-
0 │⣀⠤⠤⠔⠒⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
142-
└─────────────────────────────────────────────────┘
143-
1 10
131+
sin(0.0) = 0.0
132+
┌────────────────────────────────────────┐
133+
1 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠔⠉⠉⠉⠉⠉⠒⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
134+
│⠀⠀⠀⠀⠀⠀⠀⠀⢠⠔⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
135+
│⠀⠀⠀⠀⠀⠀⠀⡔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
136+
│⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
137+
│⠀⠀⠀⢀⠎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
138+
│⠀⠀⡠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠣⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
139+
│⠀⡔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀│
140+
│⠮⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠬⢦⠤⠤⠤⠤⠤⠤⠤⠄│
141+
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠣⡀⠀⠀⠀⠀⠀⠀│
142+
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢄⠀⠀⠀⠀⠀│
143+
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢢⠀⠀⠀⠀│
144+
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⢄⠀⠀│
145+
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠣⡀│
146+
│⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈│
147+
-1 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│
148+
└────────────────────────────────────────┘
149+
1 10
150+
144151
```
145152

146153
## Under the hood

examples/hello.jl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
module Hello
22

33
using UnicodePlots
4-
using Distributions
5-
f() = rand(Normal())
64

75
Base.@ccallable function julia_main(ARGS::Vector{String})::Cint
86
println("hello, world")
9-
@show f()
10-
println(lineplot(1:10, (1:10).^2))
7+
@show sin(0.0)
8+
println(lineplot(1:10, sin.(linspace(0, 4, 10))))
119
return 0
1210
end
1311

examples/program.c

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,11 @@ JULIA_DEFINE_FAST_TLS()
1616
extern int julia_main(jl_array_t*);
1717

1818
// main function (windows UTF16 -> UTF8 argument conversion code copied from julia's ui/repl.c)
19-
#ifndef _OS_WINDOWS_
2019
int main(int argc, char *argv[])
2120
{
2221
int retcode;
2322
int i;
2423
uv_setup_args(argc, argv); // no-op on Windows
25-
#else
26-
int wmain(int argc, wchar_t *wargv[], wchar_t *envp[])
27-
{
28-
int retcode;
29-
int i;
30-
char **argv;
31-
for (i = 0; i < argc; i++) { // convert the command line to UTF8
32-
wchar_t *warg = argv[i];
33-
size_t len = WideCharToMultiByte(CP_UTF8, 0, warg, -1, NULL, 0, NULL, NULL);
34-
if (!len) return 1;
35-
char *arg = (char*)alloca(len);
36-
if (!WideCharToMultiByte(CP_UTF8, 0, warg, -1, arg, len, NULL, NULL)) return 1;
37-
argv[i] = arg;
38-
}
39-
#endif
40-
4124
// initialization
4225
libsupport_init();
4326
// jl_options.compile_enabled = JL_OPTIONS_COMPILE_OFF;

juliac.jl

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
## Assumptions:
2-
## 1. gcc / x86_64-w64-mingw32-gcc is available and is in path
3-
## 2. Package ArgParse is installed
4-
51
using PackageCompiler, ArgParse
62

7-
function main(args)
3+
Base.@ccallable function julia_main(args::Vector{String})::Cint
84

95
s = ArgParseSettings("Static Julia Compiler",
106
version = "$(basename(@__FILE__)) version 0.7-DEV",
@@ -128,4 +124,6 @@ function main(args)
128124
)
129125
end
130126

131-
main(ARGS)
127+
if get(ENV, "COMPILE_STATIC", "false") == "false"
128+
julia_main(ARGS)
129+
end

src/PackageCompiler.jl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ const julia_v07 = VERSION > v"0.7-"
77
if julia_v07
88
using Libdl
99
import Sys: iswindows, isunix, isapple
10+
const contains07 = contains
1011
else
1112
const iswindows = is_windows
1213
const isunix = is_unix
1314
const isapple = is_apple
15+
contains07(str, reg) = ismatch(reg, str)
1416
end
1517

1618
using SnoopCompile
@@ -176,7 +178,16 @@ function compile_package(packages::Tuple{String, String}...; force = false, reus
176178
end
177179

178180

179-
export compile_package, revert, build_native_image
180181

181182

183+
function __init__()
184+
if Base.julia_cmd().exec[2] != "-Cnative"
185+
warn("Your Julia system image is not compiled natively for this CPU architecture.
186+
Please run `PackageCompiler.build_native_image()` for optimal Julia performance"
187+
)
188+
end
189+
end
190+
191+
export compile_package, revert, build_native_image, executable_ext, build_executable
192+
182193
end # module

src/api.jl

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ end
3535
function build_shared_lib(
3636
library, library_name;
3737
verbose = false, quiet = false,
38-
cpu_target = "native", optimize = nothing, debug = nothing,
38+
cpu_target = nothing, optimize = nothing, debug = nothing,
3939
inline = nothing, check_bounds = nothing, math_mode = nothing
4040
)
4141
julia_compile(
@@ -53,13 +53,66 @@ function build_shared_lib(
5353
)
5454
end
5555

56+
"""
57+
build_executable(
58+
library,
59+
library_name = splitext(basename(library))[1],
60+
cprogram = joinpath(@__DIR__, "..", "examples", "program.c");
61+
62+
snoopfile = nothing, builddir = "build",
63+
verbose = false, quiet = false,
64+
cpu_target = nothing, optimize = nothing, debug = nothing,
65+
inline = nothing, check_bounds = nothing, math_mode = nothing
66+
)
67+
68+
`library` needs to be a julia file containing a julia main, e.g. like examples/hello.jl
69+
`snoopfile` is optional and can be julia file that calls functions that you want to make sure to have precompiled
70+
`builddir` is where library_name.exe and shared libraries will end up
71+
"""
72+
function build_executable(
73+
library,
74+
library_name = splitext(basename(library))[1],
75+
cprogram = joinpath(@__DIR__, "..", "examples", "program.c");
76+
77+
snoopfile = nothing, builddir = "build",
78+
verbose = false, quiet = false,
79+
cpu_target = nothing, optimize = nothing, debug = nothing,
80+
inline = nothing, check_bounds = nothing, math_mode = nothing
81+
)
82+
if snoopfile != nothing
83+
precompfile = joinpath(builddir, "precompiled.jl")
84+
snoop(snoopfile, precompfile, joinpath(builddir, "snoop.csv"))
85+
jlmain = joinpath(builddir, "julia_main.jl")
86+
open(jlmain, "w") do io
87+
println(io, "include(\"$(escape_string(precompfile))\")")
88+
println(io, "include(\"$(escape_string(library))\")")
89+
end
90+
library = jlmain
91+
end
92+
julia_compile(
93+
94+
library, julia_program_basename = library_name,
95+
96+
cpu_target = cpu_target, optimize = optimize,
97+
debug = debug, inline = inline, check_bounds = check_bounds,
98+
math_mode = math_mode, verbose = verbose, quiet = quiet,
99+
100+
cprog = cprogram, builddir = builddir,
101+
clean = false, sysimage = nothing,
102+
compile = nothing, depwarn = nothing, autodeps = false,
103+
object = true, shared = true, executable = true, julialibs = true,
104+
)
105+
end
106+
56107

57108
"""
109+
build_native_image()
58110
Builds a clean system image, similar to a fresh Julia install.
59111
Can also be used to build a native system image for a downloaded cross compiled julia binary.
60112
"""
61-
function build_native_image(debug = false)
113+
function build_native_image(debug = false) # debug is ignored right now
62114
backup = sysimgbackup_folder()
115+
# Build in backup dir, so we have a clean backup!
63116
compile_system_image(joinpath(backup, "sys"), "native")
64117
copy_system_image(backup, default_sysimg_path(debug))
65118
end

src/static_julia.jl

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ end
1616

1717
system_compiler() = gcc
1818
bitness_flag() = Int == Int32 ? "-m32" : "-m64"
19+
executable_ext() = (iswindows() ? ".exe" : "")
1920

2021
function mingw_dir(folders...)
2122
joinpath(
@@ -78,7 +79,6 @@ function julia_compile(
7879
julia_program = abspath(julia_program)
7980
isfile(julia_program) || error("Cannot find file:\n \"$julia_program\"")
8081
quiet || println("Julia program file:\n \"$julia_program\"")
81-
8282
if executable
8383
cprog = cprog == nothing ? joinpath(@__DIR__, "..", "examples", "program.c") : abspath(cprog)
8484
isfile(cprog) || error("Cannot find file:\n \"$cprog\"")
@@ -113,7 +113,7 @@ function julia_compile(
113113

114114
o_file = julia_program_basename * ".o"
115115
s_file = julia_program_basename * ".$(Libdl.dlext)"
116-
e_file = julia_program_basename * (iswindows() ? ".exe" : "")
116+
e_file = julia_program_basename * executable_ext()
117117
tmp_dir = "tmp_v$VERSION"
118118

119119
object && build_object(
@@ -124,7 +124,7 @@ function julia_compile(
124124

125125
shared && build_shared(s_file, joinpath(tmp_dir, o_file), verbose)
126126

127-
executable && build_executable(s_file, e_file, cprog, verbose)
127+
executable && compile_executable(s_file, e_file, cprog, verbose)
128128

129129
julialibs && sync_julia_files(verbose)
130130

@@ -151,7 +151,7 @@ function build_shared(s_file, o_file, verbose = false)
151151
flags = julia_flags()
152152
command = `$cc $bitness -shared -o $s_file $o_file $flags`
153153
if isapple()
154-
command = `$command -Wl,-install_name,@rpath/\"$s_file\"`
154+
command = `$command -Wl,-install_name,@rpath/$s_file`
155155
elseif iswindows()
156156
command = `$command -Wl,--export-all-symbols`
157157
end
@@ -160,7 +160,7 @@ function build_shared(s_file, o_file, verbose = false)
160160
end
161161
162162
163-
function build_executable(s_file, e_file, cprog, verbose = false)
163+
function compile_executable(s_file, e_file, cprog, verbose = false)
164164
bitness = bitness_flag()
165165
cc = system_compiler()
166166
flags = julia_flags()
@@ -241,16 +241,12 @@ function sync_julia_files(verbose)
241241
if iswindows() || isapple()
242242
append!(libfiles, joinpath.(dir, filter(x -> endswith(x, dlext), readdir(dir))))
243243
else
244-
append!(libfiles, joinpath.(dir, filter(x -> contains(x, r"^lib.+\.so(?:\.\d+)*$"), readdir(dir))))
244+
append!(libfiles, joinpath.(dir, filter(x -> contains07(x, r"^lib.+\.so(?:\.\d+)*$"), readdir(dir))))
245245
end
246246
end
247247
sync = false
248248
for src in libfiles
249-
if julia_v07
250-
contains(src, r"debug") && continue
251-
else
252-
ismatch(r"debug", src) && continue
253-
end
249+
contains07(src, r"debug") && continue
254250
dst = basename(src)
255251
if filesize(src) != filesize(dst) || ctime(src) > ctime(dst) || mtime(src) > mtime(dst)
256252
verbose && println(" $dst")

src/system_image.jl

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ function compile_system_image(sysimg_path, cpu_target; debug = false)
1111
# Enter base and setup some useful paths
1212
base_dir = dirname(Base.find_source_file("sysimg.jl"))
1313
cd(base_dir) do
14-
julia = "julia"
14+
# This can probably get merged with build_object.
15+
# At some point, I will need to understand build_object a bit better before doing that move, though!
16+
julia = Base.julia_cmd().exec[1]
1517
cc = system_compiler()
1618
# Ensure we have write-permissions to wherever we're trying to write to
1719
try
@@ -32,26 +34,6 @@ function compile_system_image(sysimg_path, cpu_target; debug = false)
3234
info("$julia -C $cpu_target --output-ji $sysimg_path.ji --output-o $sysimg_path.o -J $inference_path.ji --startup-file=no sysimg.jl")
3335
run(`$julia -C $cpu_target --output-ji $sysimg_path.ji --output-o $sysimg_path.o -J $inference_path.ji --startup-file=no sysimg.jl`)
3436

35-
link_sysimg(sysimg_path, cc, debug)
37+
build_shared("$sysimg_path.$(Libdl.dlext)", "$sysimg_path.o", true)
3638
end
3739
end
38-
39-
# Link sys.o into sys.$(dlext)
40-
function link_sysimg(sysimg_path, cc = system_compiler(), debug = false)
41-
42-
julia_libdir = dirname(Libdl.dlpath(debug ? "libjulia-debug" : "libjulia"))
43-
44-
FLAGS = ["-L$julia_libdir"]
45-
46-
push!(FLAGS, "-shared")
47-
push!(FLAGS, debug ? "-ljulia-debug" : "-ljulia")
48-
if is_windows()
49-
push!(FLAGS, "-lssp")
50-
end
51-
52-
sysimg_file = "$sysimg_path.$(Libdl.dlext)"
53-
info("Linking sys.$(Libdl.dlext)")
54-
info("$cc $(join(FLAGS, ' ')) -o $sysimg_file $sysimg_path.o")
55-
# Windows has difficulties overwriting a file in use so we first link to a temp file
56-
run(`$cc $FLAGS -o $sysimg_file $sysimg_path.o`)
57-
end

test/REQUIRE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
Matcha
22
UnicodeFun
3+
UnicodePlots

0 commit comments

Comments
 (0)