summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2023-11-22 11:21:34 +0800
committerGitHub <[email protected]>2023-11-22 11:21:34 +0800
commit3cf88c4347b0605396b3d5c75f0b34a1c67128ab (patch)
treea5b4c18625db8cdf4b27acf4a26e42b73cf8c6dc
parentd3a27747a757200dc6d0454469a18e9469219f59 (diff)
parent93101340da3886b608aefcacba66b20c6142be91 (diff)
Merge pull request #4406 from xmake-io/xpack
Support xpack components
-rw-r--r--core/xpack.lua8
-rw-r--r--tests/plugins/pack/xmake.lua16
-rw-r--r--xmake/includes/xpack/xmake.lua42
-rw-r--r--xmake/modules/private/utils/batchcmds.lua5
-rw-r--r--xmake/plugins/pack/archive.lua5
-rw-r--r--xmake/plugins/pack/main.lua10
-rw-r--r--xmake/plugins/pack/nsis/main.lua63
-rw-r--r--xmake/plugins/pack/xpack.lua46
-rw-r--r--xmake/plugins/pack/xpack_component.lua273
-rw-r--r--xmake/scripts/xpack/nsis/makensis.nsi2
10 files changed, 405 insertions, 65 deletions
diff --git a/core/xpack.lua b/core/xpack.lua
index 07e77c1ca..18b7073d0 100644
--- a/core/xpack.lua
+++ b/core/xpack.lua
@@ -1,5 +1,6 @@
xpack("xmake")
set_homepage("https://xmake.io")
+-- set_title("Xmake build utility ($(arch))")
set_description("A cross-platform build utility based on Lua.")
set_copyright("Copyright (C) 2015-present, TBOOX Open Source Group")
set_licensefile("../LICENSE.md")
@@ -7,7 +8,6 @@ xpack("xmake")
add_targets("demo")
set_bindir(".")
set_iconfile("src/demo/xmake.ico")
- set_nsis_displayname("Xmake build utility ($(arch))")
on_load(function (package)
local arch = package:arch()
@@ -63,12 +63,6 @@ xpack("xmake")
end
end)
- add_nsis_installcmds("Enable Long Path", [[
- ${If} $NoAdmin == "false"
- ; Enable long path
- WriteRegDWORD ${HKLM} "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1
- ${EndIf}]], {description = "Increases the maximum path length limit, up to 32,767 characters (before 256)."})
-
xpack("xmakesrc")
set_formats("src_zip", "src_targz")
set_basename("xmake-v$(version)")
diff --git a/tests/plugins/pack/xmake.lua b/tests/plugins/pack/xmake.lua
index d10af4d54..a93fc18f6 100644
--- a/tests/plugins/pack/xmake.lua
+++ b/tests/plugins/pack/xmake.lua
@@ -20,12 +20,15 @@ target("foo")
xpack("test")
set_formats("nsis", "zip", "targz")
- set_description("hello")
+ set_title("hello")
+ set_description("A test installer.")
+ set_homepage("https://xmake.io")
set_licensefile("LICENSE.md")
add_targets("test", "foo")
set_basename("test-$(plat)-$(arch)-v$(version)")
add_installfiles("src/(assets/*.png)", {prefixdir = "images"})
set_iconfile("src/assets/xmake.ico")
+ add_components("LongPath")
after_installcmd(function (package, batchcmds)
batchcmds:mkdir(package:installdir("resources"))
@@ -38,9 +41,14 @@ xpack("test")
batchcmds:rmdir(package:installdir("stub"))
end)
- add_nsis_installcmds("Enable Long Path", [[
+xpack_component("LongPath")
+ set_default(false)
+ set_title("Enable Long Path")
+ set_description("Increases the maximum path length limit, up to 32,767 characters (before 256).")
+ on_installcmd(function (component, batchcmds)
+ batchcmds:rawcmd("nsis", [[
${If} $NoAdmin == "false"
; Enable long path
WriteRegDWORD ${HKLM} "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1
- ${EndIf}]], {description = "Increases the maximum path length limit, up to 32,767 characters (before 256)."})
-
+ ${EndIf}]])
+ end)
diff --git a/xmake/includes/xpack/xmake.lua b/xmake/includes/xpack/xmake.lua
index 3eb0d86cb..345431ffc 100644
--- a/xmake/includes/xpack/xmake.lua
+++ b/xmake/includes/xpack/xmake.lua
@@ -30,8 +30,10 @@ local apis = {
values = {
-- set package version, we will also get it from project/target
"xpack.set_version",
- -- set pacakge homepage url
+ -- set package homepage url
"xpack.set_homepage",
+ -- set package title
+ "xpack.set_title",
-- set package description
"xpack.set_description",
-- set package copyright
@@ -47,6 +49,8 @@ local apis = {
"xpack.set_extension",
-- add targets to be packaged
"xpack.add_targets",
+ -- add package components
+ "xpack.add_components",
-- set installed binary directory, e.g. bin
"xpack.set_bindir",
-- set installed library directory, e.g. lib
@@ -55,10 +59,14 @@ local apis = {
"xpack.set_includedir",
-- set prefix directory, e.g. prefixdir/bin, prefixdir/lib ..
"xpack.set_prefixdir",
- -- set nsis display name
- "xpack.set_nsis_displayname",
-- set nsis display icon
- "xpack.set_nsis_displayicon"
+ "xpack.set_nsis_displayicon",
+ -- set package component title
+ "xpack_component.set_title",
+ -- set package component description
+ "xpack_component.set_description",
+ -- enable/disable this component by default
+ "xpack_component.set_default"
},
paths = {
-- set the spec file path, support the custom variable pattern, e.g. set_specfile("", {pattern = "%${([^\n]-)}"})
@@ -90,19 +98,25 @@ local apis = {
-- add custom commands script after installing
"xpack.after_installcmd",
-- add custom commands script after uninstalling
- "xpack.after_uninstallcmd"
+ "xpack.after_uninstallcmd",
+ -- add custom load script in component
+ "xpack_component.on_load",
+ -- add custom commands script before installing in component
+ "xpack_component.before_installcmd",
+ -- add custom commands script before uninstalling in component
+ "xpack_component.before_uninstallcmd",
+ -- rewrite custom install commands script in component, we will also get it from target/rules
+ "xpack_component.on_installcmd",
+ -- rewrite custom uninstall commands script in component, we will also get it from target/rules
+ "xpack_component.on_uninstallcmd",
+ -- add custom commands script after installing in component
+ "xpack_component.after_installcmd",
+ -- add custom commands script after uninstalling in component
+ "xpack_component.after_uninstallcmd"
},
keyvalues = {
-- set the spec variable
- "xpack.set_specvar",
- -- add nsis sections, e.g.
- -- add NSIS install commands that will be added to the end of the install Section, e.g.
- --[[
- add_nsis_installcmds("Enable Long Path",
- 'WriteRegDWORD ${HKLM} "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1',
- {description = "Increases the maximum path length limit, up to 32,767 characters (before 256)."})
- --]]
- "xpack.add_nsis_installcmds"
+ "xpack.set_specvar"
}
}
interp_add_scopeapis(apis)
diff --git a/xmake/modules/private/utils/batchcmds.lua b/xmake/modules/private/utils/batchcmds.lua
index eda3887c8..4fa2514c9 100644
--- a/xmake/modules/private/utils/batchcmds.lua
+++ b/xmake/modules/private/utils/batchcmds.lua
@@ -378,6 +378,11 @@ function batchcmds:show(format, ...)
table.insert(self:cmds(), {kind = "show", showtext = showtext})
end
+-- add raw command for the specific generator or xpack format
+function batchcmds:rawcmd(kind, rawstr)
+ table.insert(self:cmds(), {kind = kind, rawstr = rawstr})
+end
+
-- add command: show progress
function batchcmds:show_progress(progress, format, ...)
if progress then
diff --git a/xmake/plugins/pack/archive.lua b/xmake/plugins/pack/archive.lua
index b48cdb7ed..818404345 100644
--- a/xmake/plugins/pack/archive.lua
+++ b/xmake/plugins/pack/archive.lua
@@ -28,6 +28,11 @@ function _pack_archive(package)
-- do install
batchcmds.get_installcmds(package):runcmds()
+ for _, component in table.orderpairs(package:components()) do
+ if component:get("default") ~= false then
+ batchcmds.get_installcmds(component):runcmds()
+ end
+ end
-- archive install files
local rootdir = package:rootdir()
diff --git a/xmake/plugins/pack/main.lua b/xmake/plugins/pack/main.lua
index 76cc77912..3f145cf86 100644
--- a/xmake/plugins/pack/main.lua
+++ b/xmake/plugins/pack/main.lua
@@ -27,6 +27,12 @@ import("actions.build.main", {rootdir = os.programdir(), alias = "build_action"}
import("xpack")
function _load_package(package)
+
+ -- ensure build and output directories
+ os.tryrm(package:buildir())
+ os.mkdir(package:outputdir())
+
+ -- load it
local script = package:script("load")
if script then
script(package)
@@ -39,10 +45,6 @@ end
function _pack_package(package)
- -- ensure build and output directories
- os.tryrm(package:buildir())
- os.mkdir(package:outputdir())
-
-- get need formats
local formats_need = option.get("formats")
if formats_need then
diff --git a/xmake/plugins/pack/nsis/main.lua b/xmake/plugins/pack/nsis/main.lua
index 854bc46e7..de091430d 100644
--- a/xmake/plugins/pack/nsis/main.lua
+++ b/xmake/plugins/pack/nsis/main.lua
@@ -137,6 +137,8 @@ function _get_command_strings(package, cmd, opt)
elseif kind == "mkdir" then
local dir = _translate_filepath(package, cmd.dir)
table.insert(result, string.format("CreateDirectory \"%s\"", dir))
+ elseif kind == "nsis" then
+ table.insert(result, cmd.rawstr)
end
return result
end
@@ -150,6 +152,16 @@ function _get_commands_string(package, cmds, opt)
return table.concat(cmdstrs, "\n ")
end
+-- get install commands of component
+function _get_component_installcmds(component)
+ return _get_commands_string(component, batchcmds.get_installcmds(component):cmds(), {install = true})
+end
+
+-- get uninstall commands of component
+function _get_component_uninstallcmds(component)
+ return _get_commands_string(component, batchcmds.get_uninstallcmds(component):cmds(), {install = false})
+end
+
-- get install commands
function _get_installcmds(package)
return _get_commands_string(package, batchcmds.get_installcmds(package):cmds(), {install = true})
@@ -195,7 +207,6 @@ function _get_specvars(package)
specvars.PACKAGE_UNINSTALLCMDS = function ()
return _get_uninstallcmds(package)
end
- specvars.PACKAGE_NSIS_DISPLAY_NAME = _get_filter_value(package, "nsis_displayname") or package:name()
specvars.PACKAGE_NSIS_DISPLAY_ICON = function ()
local iconpath = _get_filter_value(package, "nsis_displayicon")
if iconpath then
@@ -206,36 +217,32 @@ function _get_specvars(package)
end
return _translate_filepath(package, iconpath)
end
- specvars.PACKAGE_NSIS_INSTALL_SECTIONS = function ()
- local result = {}
- local cmds = package:get("nsis_installcmds")
- for name, cmd in pairs(cmds) do
- local tag = "Install" .. _get_unique_tag(name)
- table.insert(result, string.format('Section "%s" %s', name, tag))
- table.insert(result, cmd)
- table.insert(result, "SectionEnd")
- end
- return table.concat(result, "\n ")
- end
- specvars.PACKAGE_NSIS_INSTALL_DESCS = function ()
- local result = {}
- local cmds = package:get("nsis_installcmds")
- for name, cmd in pairs(cmds) do
- local tag = "Install" .. _get_unique_tag(name)
- local description = package:extraconf("nsis_installcmds." .. name, cmd, "description") or name
- table.insert(result, string.format('LangString DESC_%s ${LANG_ENGLISH} "%s"', tag, description))
+
+ -- install sections
+ local install_sections = {}
+ local install_descs = {}
+ local install_description_texts = {}
+ for name, component in table.orderpairs(package:components()) do
+ local installcmds = _get_component_installcmds(component)
+ if installcmds then
+ local tag = "Install" .. name
+ table.insert(install_sections, string.format('Section%s "%s" %s', component:get("default") == false and " /o" or "", component:title(), tag))
+ table.insert(install_sections, installcmds)
+ table.insert(install_sections, "SectionEnd")
+ table.insert(install_descs, string.format('LangString DESC_%s ${LANG_ENGLISH} "%s"', tag, description))
+ table.insert(install_description_texts, string.format('!insertmacro MUI_DESCRIPTION_TEXT ${%s} $(DESC_%s)', tag, tag))
end
- return table.concat(result, "\n ")
- end
- specvars.PACKAGE_NSIS_INSTALL_DESCRIPTION_TEXTS = function ()
- local result = {}
- local cmds = package:get("nsis_installcmds")
- for name, _ in pairs(cmds) do
- local tag = "Install" .. _get_unique_tag(name)
- table.insert(result, string.format('!insertmacro MUI_DESCRIPTION_TEXT ${%s} $(DESC_%s)', tag, tag))
+ local uninstallcmds = _get_component_uninstallcmds(component)
+ if uninstallcmds then
+ local tag = "Uninstall" .. name
+ table.insert(install_sections, string.format('Section "un.%s" %s', component:title(), tag))
+ table.insert(install_sections, uninstallcmds)
+ table.insert(install_sections, "SectionEnd")
end
- return table.concat(result, "\n ")
end
+ specvars.PACKAGE_NSIS_INSTALL_SECTIONS = table.concat(install_sections, "\n ")
+ specvars.PACKAGE_NSIS_INSTALL_DESCS = table.concat(install_descs, "\n ")
+ specvars.PACKAGE_NSIS_INSTALL_DESCRIPTION_TEXTS = table.concat(install_description_texts, "\n ")
return specvars
end
diff --git a/xmake/plugins/pack/xpack.lua b/xmake/plugins/pack/xpack.lua
index 07e162c67..e5829fd45 100644
--- a/xmake/plugins/pack/xpack.lua
+++ b/xmake/plugins/pack/xpack.lua
@@ -27,6 +27,7 @@ import("core.project.config")
import("core.project.project")
import("lib.detect.find_tool")
import("filter")
+import("xpack_component")
-- define module
local xpack = xpack or object {_init = {"_name", "_info"}}
@@ -69,6 +70,15 @@ function xpack:license()
return self:get("license")
end
+-- get the package title
+function xpack:title()
+ local title = self:get("title")
+ if title == nil then
+ title = self:name()
+ end
+ return filter.handle(title, self)
+end
+
-- get the package description
function xpack:description()
return self:get("description")
@@ -251,6 +261,7 @@ function xpack:specvars()
PACKAGE_ARCH = self:arch(),
PACKAGE_PLAT = self:plat(),
PACKAGE_NAME = self:name(),
+ PACKAGE_TITLE = self:title() or "",
PACKAGE_DESCRIPTION = self:description() or "",
PACKAGE_FILENAME = self:filename(),
PACKAGE_HOMEPAGE = self:get("homepage") or "",
@@ -497,9 +508,35 @@ function xpack:includedir()
return self:installdir(includedir)
end
--- new a xpack
+-- get the components
+function xpack:components()
+ local components = _g.components
+ if components == nil then
+ components = {}
+ local xpack_component_scope = project.scope("xpack_component")
+ for _, component_name in ipairs(self:get("components")) do
+ local scope = xpack_component_scope[component_name]
+ if scope then
+ local instance = xpack_component.new(component_name, scope, self)
+ components[component_name] = instance
+ else
+ raise("unknown xpack component(%s) in xpack(%s)", component_name, self:name())
+ end
+ end
+ _g.components = components
+ end
+ return components
+end
+
+-- get the given component
+function xpack:component(name)
+ return self:components()[name]
+end
+
+-- new a xpack, and we need to clone scope info,
+-- because two different format packages maybe have same scope
function _new(name, info)
- return xpack {name, info}
+ return xpack {name, info:clone()}
end
-- get xpack packages
@@ -538,8 +575,3 @@ function packages()
end
return packages
end
-
--- get the given package
-function package(name)
- return packages()[name]
-end
diff --git a/xmake/plugins/pack/xpack_component.lua b/xmake/plugins/pack/xpack_component.lua
new file mode 100644
index 000000000..59d2fae8f
--- /dev/null
+++ b/xmake/plugins/pack/xpack_component.lua
@@ -0,0 +1,273 @@
+--!A cross-platform build utility based on Lua
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-- Copyright (C) 2015-present, TBOOX Open Source Group.
+--
+-- @author ruki
+-- @file xpack_component.lua
+--
+
+-- imports
+import("core.base.object")
+import("core.base.option")
+import("core.project.config")
+import("core.project.project")
+
+-- define module
+local xpack_component = xpack_component or object {_init = {"_name", "_info", "_package"}}
+
+-- get name
+function xpack_component:name()
+ return self._name
+end
+
+-- get values
+function xpack_component:get(name)
+ if self._info then
+ return self._info:get(name)
+ end
+end
+
+-- set values
+function xpack_component:set(name, ...)
+ if self._info then
+ self._info:apival_set(name, ...)
+ end
+end
+
+-- add values
+function xpack_component:add(name, ...)
+ if self._info then
+ self._info:apival_add(name, ...)
+ end
+end
+
+-- get the extra configuration
+function xpack_component:extraconf(name, item, key)
+ if self._info then
+ return self._info:extraconf(name, item, key)
+ end
+end
+
+-- get the component title
+function xpack_component:title()
+ return self:get("title") or self:name()
+end
+
+-- get the component description
+function xpack_component:description()
+ return self:get("description")
+end
+
+-- get xxx_script
+function xpack_component:script(name, generic)
+
+ -- get script
+ local script = self:get(name)
+ local result = nil
+ if type(script) == "function" then
+ result = script
+ elseif type(script) == "table" then
+
+ -- get plat and arch
+ local plat = self:plat()
+ local arch = self:arch()
+
+ -- match pattern
+ --
+ -- `@linux`
+ -- `@linux|x86_64`
+ -- `@macosx,linux`
+ -- `android@macosx,linux`
+ -- `android|armeabi-v7a@macosx,linux`
+ -- `android|armeabi-v7a@macosx,linux|x86_64`
+ -- `android|armeabi-v7a@linux|x86_64`
+ --
+ for _pattern, _script in pairs(script) do
+ local hosts = {}
+ local hosts_spec = false
+ _pattern = _pattern:gsub("@(.+)", function (v)
+ for _, host in ipairs(v:split(',')) do
+ hosts[host] = true
+ hosts_spec = true
+ end
+ return ""
+ end)
+ if not _pattern:startswith("__") and (not hosts_spec or hosts[os.subhost() .. '|' .. os.subarch()] or hosts[os.subhost()])
+ and (_pattern:trim() == "" or (plat .. '|' .. arch):find('^' .. _pattern .. '$') or plat:find('^' .. _pattern .. '$')) then
+ result = _script
+ break
+ end
+ end
+
+ -- get generic script
+ result = result or script["__generic__"] or generic
+ end
+
+ -- only generic script
+ return result or generic
+end
+
+-- get targets
+function xpack_component:targets()
+ local targets = self._targets
+ if not targets then
+ targets = {}
+ local targetnames = self:get("targets")
+ if targetnames then
+ for _, name in ipairs(targetnames) do
+ local target = project.target(name)
+ if target then
+ table.insert(targets, target)
+ else
+ raise("xpack_component(%s): target(%s) not found!", self:name(), name)
+ end
+ end
+ end
+ self._targets = targets
+ end
+ return targets
+end
+
+-- get the given target
+function xpack_component:target(name)
+ local targetnames = self:get("targets")
+ if targetnames and table.contains(table.wrap(targetnames), name) then
+ return project.target(name)
+ end
+end
+
+-- get the copied files
+function xpack_component:_copiedfiles(filetype, outputdir)
+
+ -- no copied files?
+ local copiedfiles = self:get(filetype)
+ if not copiedfiles then return end
+
+ -- get the extra information
+ local extrainfo = table.wrap(self:get("__extra_" .. filetype))
+
+ -- get the source paths and destinate paths
+ local srcfiles = {}
+ local dstfiles = {}
+ local fileinfos = {}
+ for _, copiedfile in ipairs(table.wrap(copiedfiles)) do
+
+ -- get the root directory
+ local rootdir, count = copiedfile:gsub("|.*$", ""):gsub("%(.*%)$", "")
+ if count == 0 then
+ rootdir = nil
+ end
+ if rootdir and rootdir:trim() == "" then
+ rootdir = "."
+ end
+
+ -- remove '(' and ')'
+ local srcpaths = copiedfile:gsub("[%(%)]", "")
+ if srcpaths then
+
+ -- get the source paths
+ srcpaths = os.match(srcpaths)
+ if srcpaths and #srcpaths > 0 then
+
+ -- add the source copied files
+ table.join2(srcfiles, srcpaths)
+
+ -- the copied directory exists?
+ if outputdir then
+
+ -- get the file info
+ local fileinfo = extrainfo[copiedfile] or {}
+
+ -- get the prefix directory
+ local prefixdir = fileinfo.prefixdir
+ if fileinfo.rootdir then
+ rootdir = fileinfo.rootdir
+ end
+
+ -- add the destinate copied files
+ for _, srcpath in ipairs(srcpaths) do
+
+ -- get the destinate directory
+ local dstdir = outputdir
+ if prefixdir then
+ dstdir = path.join(dstdir, prefixdir)
+ end
+
+ -- the destinate file
+ local dstfile = nil
+ if rootdir then
+ dstfile = path.absolute(path.relative(srcpath, rootdir), dstdir)
+ else
+ dstfile = path.join(dstdir, path.filename(srcpath))
+ end
+ assert(dstfile)
+
+ -- modify filename
+ if fileinfo.filename then
+ dstfile = path.join(path.directory(dstfile), fileinfo.filename)
+ end
+
+ -- add it
+ table.insert(dstfiles, dstfile)
+ table.insert(fileinfos, fileinfo)
+ end
+ end
+ end
+ end
+ end
+ return srcfiles, dstfiles, fileinfos
+end
+
+-- get the package
+function xpack_component:package()
+ return self._package
+end
+
+-- get the install files
+function xpack_component:installfiles()
+ return self:_copiedfiles("installfiles", self:package():installdir())
+end
+
+-- get the root directory, this is just a temporary sandbox installation path,
+-- we may replace it with the actual installation path in the specfile
+function xpack_component:rootdir()
+ return self:package():rootdir()
+end
+
+-- get the installed directory
+function xpack_component:installdir(...)
+ return self:package():installdir(...)
+end
+
+-- get the binary directory
+function xpack_component:bindir()
+ return self:package():bindir()
+end
+
+-- get the library directory
+function xpack_component:libdir()
+ return self:package():libdir()
+end
+
+-- get the include directory
+function xpack_component:includedir()
+ return self:package():includedir()
+end
+
+-- new a xpack_component
+function new(name, info, package)
+ return xpack_component {name, info:clone(), package}
+end
+
diff --git a/xmake/scripts/xpack/nsis/makensis.nsi b/xmake/scripts/xpack/nsis/makensis.nsi
index 7be50dd0b..53cd82e99 100644
--- a/xmake/scripts/xpack/nsis/makensis.nsi
+++ b/xmake/scripts/xpack/nsis/makensis.nsi
@@ -287,7 +287,7 @@ Section "${PACKAGE_NAME} (required)" InstallExeutable
; Write the uninstall keys for Windows
!macro AddReg RootKey
WriteRegStr ${RootKey} ${RegUninstall} "NoAdmin" "$NoAdmin"
- WriteRegStr ${RootKey} ${RegUninstall} "DisplayName" "${PACKAGE_NSIS_DISPLAY_NAME}"
+ WriteRegStr ${RootKey} ${RegUninstall} "DisplayName" "${PACKAGE_TITLE}"
!if "${PACKAGE_NSIS_DISPLAY_ICON}" != ""
WriteRegStr ${RootKey} ${RegUninstall} "DisplayIcon" '"${PACKAGE_NSIS_DISPLAY_ICON}"'
!endif