Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion Manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,4 @@ uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
deps = ["BinDeps", "Compat", "HTTPClient", "LibExpat", "Libdl", "Libz", "URIParser"]
git-tree-sha1 = "2a889d320f3b77d17c037f295859fe570133cfbf"
uuid = "c17dfb99-b4f7-5aad-8812-456da1ad7187"
version = "0.4.2"
version = "0.4.2"
16 changes: 13 additions & 3 deletions src/ApplicationBuilder.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ export build_app_bundle
include("mac_commandline_app.jl")
end

@static if Sys.iswindows()
include("win_commandline_app.jl")
include("win-installer.jl")
end

"""
build_app_bundle(juliaprog_main;
appname, builddir, binary_name, resources, libraries, verbose, bundle_identifier,
Expand All @@ -32,8 +37,9 @@ function build_app_bundle(juliaprog_main;
resources = String[], libraries = String[], verbose = false,
bundle_identifier = nothing, app_version = "0.1", icns_file = nothing,
certificate = nothing, entitlements_file = nothing,
snoopfile = nothing, autosnoop = false, cpu_target="x86-64",
snoopfile = nothing, autosnoop = false, cpu_target=nothing,
create_installer = false, commandline_app = false,
installer_compiler="iss"
)

# ----------- Input sanity checking --------------
Expand All @@ -48,6 +54,10 @@ function build_app_bundle(juliaprog_main;
if occursin(r"\s", bundle_identifier) throw(ArgumentError("Bundle identifier must not contain whitespace.")) end
if occursin(r"[^A-Za-z0-9-.]", bundle_identifier) throw(ArgumentError("Bundle identifier must contain only alphanumeric characters (A-Z,a-z,0-9), hyphen (-), and period (.).")) end

elseif Sys.iswindows()
if commandline_app
@warn "Will create windows script"
end
else
if commandline_app
@warn "Ignore `commandline_app=true` on non-macOS system."
Expand Down Expand Up @@ -83,7 +93,7 @@ function build_app_bundle(juliaprog_main;
end

applet_name = nothing
if commandline_app # MacOS only
if commandline_app # MacOS and Windows only
# TODO: What if the user specifies Resources that could overwrite
# applet resources? (ie Scripts/ or applet.rsrc)
applet_name = build_commandline_app_bundle(builddir, binary_name, appname, verbose)
Expand Down Expand Up @@ -267,7 +277,7 @@ function build_app_bundle(juliaprog_main;
end
end
elseif Sys.iswindows()
create_installer && win_installer(builddir, name = appname)
create_installer && win_installer(builddir, name=appname, installer_compiler=installer_compiler)
end

println("~~~~~~ Done building '$appbundle'! ~~~~~~~")
Expand Down
155 changes: 134 additions & 21 deletions src/win-installer.jl
Original file line number Diff line number Diff line change
@@ -1,39 +1,152 @@
JULIA_HOME = get(ENV, "JULIA_HOME", "")
LICENSE_PATH = joinpath(abspath(JULIA_HOME, ".."), "License.md")

baremodule SetupCompilers
iss="iss"
nsis="nsis"
end

function _hasfilesin(path::String)::Bool
for (root, dir, files) in walkdir(path)
if length(files) > 0
return true
end
end
return false
end

function win_installer(builddir; name = "nothing",
license = "$JULIA_HOME/../License.md")
license = LICENSE_PATH, installer_compiler=SetupCompilers.iss)

# check = success(`makensis`)
# !check && throw(ErrorException("NSIS not found in path. Exiting."))

nsis_commands = """
# set the name of the installer
Outfile "$(name)_Installer.exe"
commands = if installer_compiler == SetupCompilers.nsis
"""
# set the name of the installer
Name "$(name)"
Outfile "$(name)_Installer.exe"

# Default install directory
InstallDir "\$LOCALAPPDATA"

# Default install directory
InstallDir "\$LOCALAPPDATA"
Page license
Page directory
Page instfiles

Page license
Page directory
Page instfiles
LicenseData "$license"

LicenseData "$license"
# create a default section.
Section "Install"

# create a default section.
Section "Install"
SetOutPath "$(joinpath("\$INSTDIR", name))"
File /nonfatal /a /r "$builddir"

SetOutPath "$(joinpath("\$INSTDIR", name))"
File /nonfatal /a /r "$builddir"
CreateShortcut "$(joinpath("\$INSTDIR", name, "$name.lnk"))" "$(joinpath(builddir, "core", "blink.exe"))"

CreateShortcut "$(joinpath("\$INSTDIR", name, "$name.lnk"))" "$(joinpath(builddir, "core", "blink.exe"))"
SectionEnd
"""
elseif installer_compiler == SetupCompilers.iss
res_path = joinpath(builddir, name, "res")
lib_path = joinpath(builddir, name, "lib")
files =
"""
[Files]
Source: "$(joinpath(builddir, name, "bin") * "\\*")"; DestDir: "{app}\\bin"; Flags: ignoreversion
"""

SectionEnd
"""
if isdir(res_path) && _hasfilesin(res_path)
files = "$files\nSource: \"$(res_path * "\\*")\"; DestDir: \"{app}\\res\"; Flags: ignoreversion"
end

if isdir(lib_path) && _hasfilesin(lib_path)
files = "$files\nSource: \"$(lib_path * "\\*")\"; DestDir: \"{app}\\lib\"; Flags: ignoreversion"
end

"""
; $name InnoSetup Compiler
; This software is property of Gabriel Freire. All Rights reserved.
; Copyright 2019
; Requires InnoSetup Latest (5.5 tested)
; This script compiles the setup file for $name in the SETUP folder

#define MyAppName "$name"
#define MyAppVersion "1.0"
#define ApplicationVersion GetStringFileInfo("$(joinpath(builddir, name, "bin", "$(name).exe"))", "FileVersion")
#define MyAppExeName "$name.exe"


[Setup]
AppId={{802D0907-22CE-4E43-8FAB-017F687159C4}
AppName={#MyAppName}
AppVersion={#ApplicationVersion}
AppVerName={#MyAppName}
VersionInfoVersion={#ApplicationVersion}
DefaultDirName={pf}\\{#MyAppName}
DisableDirPage=yes
DisableProgramGroupPage=yes
OutputDir=.\\
OutputBaseFilename=$(name * "Setup")
UninstallDisplayIcon={app}\\{#MyAppExeName}
Compression=lzma
SolidCompression=yes
; Tell Windows Explorer to reload the environment
ChangesEnvironment=yes
UsePreviousAppDir=False

[CustomMessages]
AppAddPath=Add application directory to your environmental path (required)

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: modifypath; Description:{cm:AppAddPath}; Flags: unchecked

[Registry]
Root: HKCU; Subkey: "Environment"; ValueType:expandsz; ValueName: "Path"; ValueData: "{olddata};{app}\\bin"; Flags: preservestringtype

$(files)

[Code]

var CancelWithoutPrompt: boolean;

function InitializeSetup(): Boolean;
begin
CancelWithoutPrompt := false;
result := true;
Log('{#ApplicationVersion}');
end;

procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
begin
if CurPageID=wpInstalling then
Confirm := not CancelWithoutPrompt;
end;

[Icons]
Name: "{commonprograms}\\{#MyAppName}"; Filename: "{app}\\{#MyAppExeName}"
Name: "{commondesktop}\\{#MyAppName}"; Filename: "{app}\\{#MyAppExeName}"; Tasks: desktopicon
Name: "{commonstartup}\\{#MyAppName}"; Filename: "{app}\\{#MyAppExeName}";
"""
else
throw(ArgumentError("Unknown compiler: $installer_compiler"))
end

ext = if installer_compiler == SetupCompilers.nsis
"nsi"
elseif installer_compiler == SetupCompilers.iss
"iss"
end

@info "Creating installer at $builddir"
nsis_file = joinpath(builddir, "..", "$name.nsi")
open(nsis_file, "w") do f
write(f, nsis_commands)
compiler_file = joinpath(abspath(builddir, ".."), "$name.$ext")
open(compiler_file, "w") do f
write(f, commands)
end
run(`makensis $nsis_file`)
# run(`makensis $nsis_file`)

@info "Created installer successfully."

Expand Down
30 changes: 30 additions & 0 deletions src/win_commandline_app.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
function get_commandline_sh_script(appname)
"""
@echo off
set source_dir=%~dp0
call "%source_dir%$(appname).exe" %*
"""
end

# Build a wrapper app that opens a terminal and runs the provided binary.
# Returns a new script name only if binary_name is already "appname" (to prevent collision).
function build_commandline_app_bundle(builddir, binary_name, appname, verbose)
println("~~~~~~ Creating commandline-app wrapper script. ~~~~~~~")

mkpath(builddir) # Create builddir if it doesn't already exist.

app_path = joinpath(builddir, appname)
exe_dir = "bin" # Put the binaries next to the applet in MacOS.
script_name = "$(binary_name).bat"
if binary_name == appname # Prevent collisions.
script_name = "$(binary_name)_.bat"
end
script_path = joinpath(app_path, exe_dir, script_name)
mkpath(dirname(script_path))

verbose && println(" Creating wrapper script: $script_path")
write(script_path, get_commandline_sh_script(appname))
run(`chmod u+x $script_path`)

return script_name
end
52 changes: 36 additions & 16 deletions test/build_app-cli.jl
Original file line number Diff line number Diff line change
@@ -1,28 +1,48 @@

using Test

# prevent shell_split on removing all slashes
path_to_cmd(path::String) = replace(path, "\\" => "\\\\")

julia = Base.julia_cmd().exec[1]
build_app_jl = joinpath(@__DIR__, "..", "build_app.jl")
examples_hello = joinpath(@__DIR__, "..", "examples", "hello.jl")
build_app_jl = joinpath(abspath(@__DIR__, ".."), "build_app.jl")
examples_hello = joinpath(abspath(@__DIR__, ".."), "examples", "hello.jl")

builddir = mktempdir()
@assert isdir(builddir)

@testset "Basic file resource args" begin
# Test the build_app.jl script CLI args.
res1 = @__FILE__ # haha copy this file itself as a "resource"!
res2 = joinpath(@__DIR__, "runtests.jl") # lol sure this is a resource, why not.
NEWARGS = Base.shell_split("""--verbose
-R $res1 --resource $res2 -L $res1 --lib $res2
$examples_hello "HelloWorld" $builddir""")
eval(:(ARGS = $NEWARGS))
@test 0 == include("$build_app_jl")
@test isdir("$builddir/HelloWorld.app")
@test isfile("$builddir/HelloWorld.app/Contents/MacOS/hello")

# Make sure the specified resources and libs were copied:
@test isfile("$builddir/HelloWorld.app/Contents/Resources/$(basename(res1))")
@test isfile("$builddir/HelloWorld.app/Contents/Resources/$(basename(res2))")
@test isfile("$builddir/HelloWorld.app/Contents/Libraries/$(basename(res1))")
res1 = @__FILE__ # haha copy this file itself as a "resource"!
res2 = joinpath(@__DIR__, "runtests.jl") # lol sure this is a resource, why not.

NEWARGS = Base.shell_split("""--verbose
-R $(path_to_cmd(res1)) --resource $(path_to_cmd(res2)) -L $(path_to_cmd(res1)) --lib $(path_to_cmd(res2))
$(path_to_cmd(examples_hello)) "HelloWorld" $(path_to_cmd(builddir))""")
eval(:(ARGS = $NEWARGS))

@test 0 == include(build_app_jl)

if Sys.isapple()
@test isdir("$builddir/HelloWorld.app")
@test isfile("$builddir/HelloWorld.app/Contents/MacOS/hello")

# Make sure the specified resources and libs were copied:
@test isfile("$builddir/HelloWorld.app/Contents/Resources/$(basename(res1))")
@test isfile("$builddir/HelloWorld.app/Contents/Resources/$(basename(res2))")
@test isfile("$builddir/HelloWorld.app/Contents/Libraries/$(basename(res1))")
else
@test isdir("$builddir/HelloWorld")
@test isdir("$builddir/HelloWorld/bin")
@test isfile("$builddir/HelloWorld/bin/hello.exe")
@test isfile("$builddir/HelloWorld/bin/hello.dll")
@test isfile("$builddir/HelloWorld/bin/hello.a")

# Make sure the specified resources and libs were copied:
@test isfile("$builddir/HelloWorld/res/$(basename(res1))")
@test isfile("$builddir/HelloWorld/res/$(basename(res2))")
@test isfile("$builddir/HelloWorld/lib/$(basename(res1))")
end
end

@testset "Exits without juliaprog_main" begin
Expand Down
2 changes: 1 addition & 1 deletion test/build_examples/blink.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ using ApplicationBuilder

using Pkg

examples_blink = joinpath(@__DIR__, "..", "..", "examples", "blink.jl")
examples_blink = joinpath(abspath(@__DIR__, "..", ".."), "examples", "blink.jl")

# Allow this file to be called either as a standalone file to build the above
# example, or from runtests.jl using a provided builddir.
Expand Down
2 changes: 1 addition & 1 deletion test/build_examples/commandline_hello.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ using ApplicationBuilder
# example, or from runtests.jl using a provided builddir.
@isdefined(builddir) || (builddir="builddir")

build_app_bundle(joinpath(@__DIR__,"..","..","examples","commandline_hello.jl"),
build_app_bundle(joinpath(abspath(@__DIR__,"..",".."),"examples","commandline_hello.jl"),
appname="hello", binary_name="hello",
commandline_app=true, builddir=builddir)
2 changes: 1 addition & 1 deletion test/build_examples/hello.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using ApplicationBuilder

examples_hello = joinpath(@__DIR__, "..", "..", "examples", "hello.jl")
examples_hello = joinpath(abspath(@__DIR__, "..", ".."), "examples", "hello.jl")

# Allow this file to be called either as a standalone file to build the above
# example, or from runtests.jl using a provided builddir.
Expand Down
2 changes: 1 addition & 1 deletion test/build_examples/libui.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ libUIPkg = Pkg.dir("Libui")

using Libui

ApplicationBuilder.build_app_bundle(joinpath(@__DIR__, "..", "..", "examples", "libui.jl");
ApplicationBuilder.build_app_bundle(joinpath(abspath(@__DIR__, "..", ".."), "examples", "libui.jl");
verbose = true,
resources = [],
libraries = [ Libui.libui ],
Expand Down
2 changes: 1 addition & 1 deletion test/build_examples/sdl.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using ApplicationBuilder
using Pkg

examples_blink = joinpath(@__DIR__, "..", "..", "examples", "sdl.jl")
examples_blink = joinpath(abspath(@__DIR__, "..", ".."), "examples", "sdl.jl")

# Allow this file to be called either as a standalone file to build the above
# example, or from runtests.jl using a globally-defined builddir.
Expand Down
3 changes: 2 additions & 1 deletion test/bundle.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ builddir = mktempdir()
@testset "HelloWorld.app" begin
@test 0 == include("build_examples/commandline_hello.jl")
@test isdir(joinpath(builddir, "hello"))
@test success(`$builddir/hello/bin/hello`)
p = joinpath(builddir,"hello", "bin", "hello")
@test success(`$p`)
#@test success(`open $builddir/hello.app`)
end
Loading