Skip to content

Commit

Permalink
Rest of the uninstall support. Should be good to go on Unix. Windows …
Browse files Browse the repository at this point in the history
…will need

 more work to hook into Add/Remove Programs, but there's lots of other Windows
 work to do before that...
  • Loading branch information
icculus committed Jan 24, 2008
1 parent d5ff3ad commit ce0f347
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 20 deletions.
36 changes: 36 additions & 0 deletions docs.txt
Expand Up @@ -313,6 +313,42 @@ Here are the elements, and the attributes they can possess.
(!!! FIXME) This attribute is for future expansion.


write_manifest (default true, mustBeBool)

If true, MojoSetup will create a hidden metadata directory in the
destination folder with lists of all files installed and some other
pertinent facts about the installed package. This allows other tools to
operate on the installation later, such as a software updater program or
an uninstaller. MojoSetup will also install tools in the metadata
directory to aid in manifest management and uninstallation.

Unless your package is a well-defined, static installation, you probably
want this. It adds a couple hundred kilobytes to the final install in the
filesystem (but not your download package), and puts an extra directory
in there (Usually called ".mojosetup", and hidden from the end-user).


support_uninstall (default true, mustBeBool)

If true, MojoSetup will include a means for the end-user to uninstall
this package. On Unix, this takes the form of a shell script in the
destination folder, on Windows, this hooks your package into the
"Add/Remove Programs" control panel.

If you enable support_uninstall, you must enable write_manifest, or the
installer will refuse to run to alert you to the problem immediately.

Please note that if you haven't anything outside the destination folder
to clean up, uninstall is merely a user-friendly formality; MojoSetup
doesn't implicitly write anything to the system outside this folder, so
the user can just drag it to the trash in the basic usage scenario. Indeed,
on Mac OS X, this is Apple's recommended "uninstall" procedure. On Windows,
hooking into Add/Remove Programs is probably desirable in all cases.

Enabling this option adds very little overhead to the normal install, once
you swallow the cost from write_manifest.


Setup.Eula:

This element can be a child of Setup.Package or Setup.Option. It can be
Expand Down
14 changes: 8 additions & 6 deletions examples/duke3d/scripts/config.lua
Expand Up @@ -27,6 +27,8 @@ Setup.Package
version = "1.5",
splash = "splash.bmp",
superuser = false,
write_manifest = true,
support_uninstall = true,
recommended_destinations =
{
MojoSetup.info.homedir,
Expand Down Expand Up @@ -88,15 +90,15 @@ Setup.Package
},
Setup.File
{
source = "http://icculus.org/mojosetup/examples/duke3d/data/duke3d_shareware_bins_" .. ostype() .. "_" .. arch() .. ".zip"
source = "http://centralserver/mojosetup/examples/duke3d/data/duke3d_shareware_bins_" .. ostype() .. "_" .. arch() .. ".zip"
},
Setup.File
{
source = "http://icculus.org/mojosetup/examples/duke3d/data/duke3d_shareware_data.zip"
source = "http://centralserver/mojosetup/examples/duke3d/data/duke3d_shareware_data.zip"
},
Setup.File
{
source = "http://icculus.org/mojosetup/examples/duke3d/data/duke3d_unified_content.zip"
source = "http://centralserver/mojosetup/examples/duke3d/data/duke3d_unified_content.zip"
},
},

Expand All @@ -116,17 +118,17 @@ Setup.Package

Setup.File
{
source = "http://icculus.org/mojosetup/examples/duke3d/data/duke3d_retail_bins_" .. ostype() .. "_" .. arch() .. ".zip"
source = "http://centralserver/mojosetup/examples/duke3d/data/duke3d_retail_bins_" .. ostype() .. "_" .. arch() .. ".zip"
},

Setup.File
{
source = "http://icculus.org/mojosetup/examples/duke3d/data/duke3d_unified_content.zip"
source = "http://centralserver/mojosetup/examples/duke3d/data/duke3d_unified_content.zip"
},

Setup.File
{
source = "http://icculus.org/mojosetup/examples/duke3d/data/duke3d_retail_demos.zip"
source = "http://centralserver/mojosetup/examples/duke3d/data/duke3d_retail_demos.zip"
},

Setup.File
Expand Down
2 changes: 2 additions & 0 deletions examples/ut3-dedicated/scripts/config.lua
Expand Up @@ -6,6 +6,8 @@ Setup.Package
id = "ut3-dedicated",
description = "Unreal Tournament 3 Dedicated Server",
version = "3487",
write_manifest = false, -- don't want to update...
support_uninstall = false, -- ...or uninstall. This is just a fancy unzip.

recommended_destinations =
{
Expand Down
2 changes: 2 additions & 0 deletions scripts/config.lua
Expand Up @@ -43,6 +43,8 @@ Setup.Package
destination = "/usr/local/bin",
recommended_destinations = { "/opt/games", "/usr/local/games" },
updateurl = "http://localhost/updates/",
write_manifest = true,
support_uninstall = true,

-- Things named Setup.Something are internal functions we supply.
-- Generally these return the table you pass to them, but they
Expand Down
7 changes: 7 additions & 0 deletions scripts/localization.lua
Expand Up @@ -287,6 +287,13 @@ MojoSetup.localization = {
se = "FEL: inget option",
};

-- This is shown if the config file wants us to add an installer to the
-- files we write to disk, but didn't enable the manifest support the
-- installer needs. This is a bug the developer must fix before shipping
-- her installer.
["BUG: support_uninstall requires write_manifest"] = {
};

-- This is a file's permissions. Programmers give these as strings, and
-- if one isn't valid, the program will report this. So, on Unix, they
-- might specify "0600" as a valid string, but "sdfksjdfk" wouldn't be
Expand Down
2 changes: 2 additions & 0 deletions scripts/mojosetup_init.lua
Expand Up @@ -263,6 +263,8 @@ function Setup.Package(tab)
{ "binarypath", nil, mustBeString, cantBeEmpty },
{ "updateurl", nil, mustBeString, mustBeUrl },
{ "superuser", false, mustBeBool },
{ "write_manifest", true, mustBeBool },
{ "support_uninstall", true, mustBeBool },
})

if MojoSetup.installs == nil then
Expand Down
78 changes: 64 additions & 14 deletions scripts/mojosetup_mainline.lua
Expand Up @@ -15,6 +15,13 @@ local _ = MojoSetup.translate

MojoSetup.metadatakey = ".mojosetup_metadata."
MojoSetup.metadatadesc = _("Metadata")
MojoSetup.metadatadirname = ".mojosetup"

if MojoSetup.info.platform == "windows" then
MojoSetup.controlappname = "mojosetup.exe"
else
MojoSetup.controlappname = "mojosetup"
end


local function badcmdline()
Expand Down Expand Up @@ -376,7 +383,7 @@ local function install_file(dest, perms, writefn, desc, manifestkey)
percent = calc_percent(MojoSetup.written, MojoSetup.totalwrite)
item = MojoSetup.format(_("%0: %1%%"), fname, calc_percent(bw, total))
end
keepgoing = MojoSetup.gui.progress(ptype, component, percent, item)
keepgoing = MojoSetup.gui.progress(ptype, component, percent, item, true)
return keepgoing
end

Expand Down Expand Up @@ -704,7 +711,7 @@ local function set_destination(dest)

MojoSetup.loginfo("Install dest: '" .. dest .. "'")
MojoSetup.destination = dest
MojoSetup.metadatadir = MojoSetup.destination .. "/.mojosetup"
MojoSetup.metadatadir = MojoSetup.destination .. "/" .. MojoSetup.metadatadirname
MojoSetup.controldir = MojoSetup.metadatadir -- .. "/control"
MojoSetup.manifestdir = MojoSetup.metadatadir .. "/manifest"
MojoSetup.scratchdir = MojoSetup.metadatadir .. "/tmp"
Expand Down Expand Up @@ -865,8 +872,7 @@ local function install_control_app(desc, key)
local maxbytes = -1 -- copy whole thing by default.
local base = MojoSetup.archive.base

-- !!! FIXME: This needs an ".exe" appended on Windows.
dst = MojoSetup.controldir .. "/mojosetup"
dst = MojoSetup.controldir .. "/" .. MojoSetup.controlappname
src = MojoSetup.info.binarypath
if src == MojoSetup.info.basearchivepath then
maxbytes = MojoSetup.archive.offsetofstart(base)
Expand Down Expand Up @@ -913,6 +919,29 @@ local function install_control_app(desc, key)
end


local function install_unix_uninstaller(desc, key)
-- Write a script out that calls the uninstaller.
local fname = MojoSetup.destination .. "/" ..
"uninstall-" .. MojoSetup.install.id .. ".sh"

-- Man, I hate escaping shell strings...
local bin = "\"`dirname $0`\"'/" .. MojoSetup.metadatadirname .. "/" ..
MojoSetup.controlappname .. "'"
string.gsub(bin, "'", "'\\''") -- Escape single quotes for shell.

local id = MojoSetup.install.id
string.gsub(id, "'", "'\\''")

local script =
"#!/bin/sh\n" ..
"exec " .. bin .. " uninstall '" .. id .. "' $*\n" ..
"exit 1\n\n"

install_parent_dirs(fname, key)
install_file_from_string(fname, script, "0755", desc, key)
end


local function install_manifests(desc, key)
-- We write out a Lua script as a data definition language, a
-- loki_setup-compatible XML manifest, and a straight text file of
Expand Down Expand Up @@ -989,6 +1018,11 @@ local function do_install(install)
MojoSetup.fatal(_("BUG: no options"))
end

-- The uninstaller support needs a manifest to know what to do. Force it on.
if (install.support_uninstall) and (not install.write_manifest) then
MojoSetup.fatal(_("BUG: support_uninstall requires write_manifest"))
end

-- This is to save us the trouble of a loop every time we have to
-- find media by id...
MojoSetup.media = {}
Expand Down Expand Up @@ -1291,7 +1325,7 @@ local function do_install(install)
calc_percent(bw, total),
ratestr)
end
return MojoSetup.gui.progress(ptype, component, percent, item)
return MojoSetup.gui.progress(ptype, component, percent, item, true)
end

MojoSetup.loginfo("Download '" .. url .. "' to '" .. f .. "'")
Expand Down Expand Up @@ -1370,11 +1404,20 @@ local function do_install(install)
end
end

install_control_app(MojoSetup.metadatadesc, MojoSetup.metadatakey)

run_config_defined_hook(install.postinstall, install)

install_manifests(MojoSetup.metadatadesc, MojoSetup.metadatakey) -- write out manifest.
if install.support_uninstall then
if MojoSetup.info.platform == "windows" then
MojoSetup.fatal(_("Unimplemented")) -- !!! FIXME: write me.
else -- Unix, Mac OS X, BeOS...
install_unix_uninstaller(MojoSetup.metadatadesc, MojoSetup.metadatakey)
end
end

if install.write_manifest then
install_control_app(MojoSetup.metadatadesc, MojoSetup.metadatakey)
install_manifests(MojoSetup.metadatadesc, MojoSetup.metadatakey)
end

return 1 -- go to next stage.
end
Expand Down Expand Up @@ -1590,9 +1633,17 @@ local function uninstaller()
local package = load_manifest(MojoSetup.info.argv[3])

local title = _("Uninstall")
local question = _("Are you sure you want to uninstall '%0'?")
question = MojoSetup.format(question, package.description)
if MojoSetup.promptyn(title, question, false) then

local uninstall_permitted = false
if MojoSetup.cmdline("force") then
uninstall_permitted = true
else
local question = _("Are you sure you want to uninstall '%0'?")
question = MojoSetup.format(question, package.description)
uninstall_permitted = MojoSetup.promptyn(title, question, false)
end

if uninstall_permitted then
start_gui(package.description, package.splash)

-- Upvalued so we don't look this up each time...
Expand All @@ -1608,14 +1659,13 @@ local function uninstaller()
end

local item = string.gsub(fname, "^.*/", "", 1) -- chop off dirs...
MojoSetup.gui.progress(ptype, component, percent, item)
MojoSetup.gui.progress(ptype, component, percent, item, false)
return true -- !!! FIXME: need to disable cancel button in UI...
end

local filelist = flatten_manifest(package.manifest, prepend_dest_dir)
delete_files(filelist, callback, true)

MojoSetup.msgbox(title, _("Uninstall complete"))
MojoSetup.gui.final(_("Uninstall complete"))
stop_gui()
end

Expand Down

0 comments on commit ce0f347

Please sign in to comment.