summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2023-11-15 16:34:08 +0800
committerGitHub <[email protected]>2023-11-15 16:34:08 +0800
commit6677fd77ee78b53a1f9bd4ce9665ce8b60083d47 (patch)
tree0094dfb752ac8e8816973f0db25b328c844d550f
parent4b102cbd2221494395e6e279ed453eee81ba45a4 (diff)
parent7c37e9b9b92b53e4384daea5193f826bf8ac9492 (diff)
Merge pull request #4373 from xmake-io/xpack
Support `xmake pack`
-rw-r--r--tests/plugins/pack/.gitignore8
-rw-r--r--tests/plugins/pack/LICENSE.md203
-rw-r--r--tests/plugins/pack/include/foo/foo.h0
-rw-r--r--tests/plugins/pack/include/test.h0
-rw-r--r--tests/plugins/pack/src/assets/file1.txt1
-rw-r--r--tests/plugins/pack/src/assets/file2.txt1
-rw-r--r--tests/plugins/pack/src/assets/img1.png0
-rw-r--r--tests/plugins/pack/src/assets/img2.png0
-rw-r--r--tests/plugins/pack/src/assets/xmake.icobin0 -> 119943 bytes
-rw-r--r--tests/plugins/pack/src/main.cpp9
-rw-r--r--tests/plugins/pack/src/xmake.rc1
-rw-r--r--tests/plugins/pack/xmake.lua45
-rw-r--r--xmake/core/base/interpreter.lua12
-rw-r--r--xmake/core/project/project.lua2
-rw-r--r--xmake/core/project/rule.lua6
-rw-r--r--xmake/core/project/target.lua6
-rw-r--r--xmake/includes/xpack/xmake.lua93
-rw-r--r--xmake/modules/private/action/require/impl/utils/filter.lua13
-rw-r--r--xmake/modules/private/utils/batchcmds.lua28
-rw-r--r--xmake/plugins/pack/filter.lua120
-rw-r--r--xmake/plugins/pack/main.lua97
-rw-r--r--xmake/plugins/pack/nsis/main.lua618
-rw-r--r--xmake/plugins/pack/xmake.lua37
-rw-r--r--xmake/plugins/pack/xpack.lua495
-rw-r--r--xmake/plugins/project/cmake/cmakelists.lua2
-rw-r--r--xmake/plugins/project/make/makefile.lua11
-rw-r--r--xmake/plugins/project/vstudio/impl/vs201x.lua2
-rw-r--r--xmake/scripts/xpack/nsis/makensis.nsi301
28 files changed, 2093 insertions, 18 deletions
diff --git a/tests/plugins/pack/.gitignore b/tests/plugins/pack/.gitignore
new file mode 100644
index 000000000..152105761
--- /dev/null
+++ b/tests/plugins/pack/.gitignore
@@ -0,0 +1,8 @@
+# Xmake cache
+.xmake/
+build/
+
+# MacOS Cache
+.DS_Store
+
+
diff --git a/tests/plugins/pack/LICENSE.md b/tests/plugins/pack/LICENSE.md
new file mode 100644
index 000000000..d2b373af7
--- /dev/null
+++ b/tests/plugins/pack/LICENSE.md
@@ -0,0 +1,203 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2015-present TBOOX Open Source Group
+
+ 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.
+
diff --git a/tests/plugins/pack/include/foo/foo.h b/tests/plugins/pack/include/foo/foo.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/plugins/pack/include/foo/foo.h
diff --git a/tests/plugins/pack/include/test.h b/tests/plugins/pack/include/test.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/plugins/pack/include/test.h
diff --git a/tests/plugins/pack/src/assets/file1.txt b/tests/plugins/pack/src/assets/file1.txt
new file mode 100644
index 000000000..ce0136250
--- /dev/null
+++ b/tests/plugins/pack/src/assets/file1.txt
@@ -0,0 +1 @@
+hello
diff --git a/tests/plugins/pack/src/assets/file2.txt b/tests/plugins/pack/src/assets/file2.txt
new file mode 100644
index 000000000..ce0136250
--- /dev/null
+++ b/tests/plugins/pack/src/assets/file2.txt
@@ -0,0 +1 @@
+hello
diff --git a/tests/plugins/pack/src/assets/img1.png b/tests/plugins/pack/src/assets/img1.png
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/plugins/pack/src/assets/img1.png
diff --git a/tests/plugins/pack/src/assets/img2.png b/tests/plugins/pack/src/assets/img2.png
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/plugins/pack/src/assets/img2.png
diff --git a/tests/plugins/pack/src/assets/xmake.ico b/tests/plugins/pack/src/assets/xmake.ico
new file mode 100644
index 000000000..52c8ef389
--- /dev/null
+++ b/tests/plugins/pack/src/assets/xmake.ico
Binary files differ
diff --git a/tests/plugins/pack/src/main.cpp b/tests/plugins/pack/src/main.cpp
new file mode 100644
index 000000000..7c435d251
--- /dev/null
+++ b/tests/plugins/pack/src/main.cpp
@@ -0,0 +1,9 @@
+#include <iostream>
+
+using namespace std;
+
+int main(int argc, char** argv)
+{
+ cout << "hello world!" << endl;
+ return 0;
+}
diff --git a/tests/plugins/pack/src/xmake.rc b/tests/plugins/pack/src/xmake.rc
new file mode 100644
index 000000000..3a2783607
--- /dev/null
+++ b/tests/plugins/pack/src/xmake.rc
@@ -0,0 +1 @@
+IDI_APP ICON DISCARDABLE "assets/xmake.ico"
diff --git a/tests/plugins/pack/xmake.lua b/tests/plugins/pack/xmake.lua
new file mode 100644
index 000000000..fc436f8b7
--- /dev/null
+++ b/tests/plugins/pack/xmake.lua
@@ -0,0 +1,45 @@
+set_version("1.0.0")
+add_rules("mode.debug", "mode.release")
+
+includes("@builtin/xpack")
+
+add_requires("zlib", {configs = {shared = true}})
+
+target("test")
+ set_kind("binary")
+ add_files("src/*.cpp")
+ if is_plat("windows") then
+ add_files("src/*.rc")
+ end
+
+target("foo")
+ set_kind("shared")
+ add_files("src/*.cpp")
+ add_headerfiles("include/(*.h)")
+ add_packages("zlib")
+
+xpack("test")
+ set_formats("nsis")
+ set_description("hello")
+ 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")
+
+ after_installcmd(function (package, batchcmds)
+ batchcmds:cp("src/assets/*.txt", "resources/", {rootdir = "src"})
+ batchcmds:mkdir("stub")
+ end)
+
+ after_uninstallcmd(function (package, batchcmds)
+ batchcmds:rmdir("resources")
+ batchcmds:rmdir("stub")
+ 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)."})
+
diff --git a/xmake/core/base/interpreter.lua b/xmake/core/base/interpreter.lua
index 7ba677a8f..e4d6c3046 100644
--- a/xmake/core/base/interpreter.lua
+++ b/xmake/core/base/interpreter.lua
@@ -1897,11 +1897,15 @@ function interpreter:api_interp_add_scopeapis(...)
else
extra_config = nil
end
- local kind = "values"
- if extra_config and extra_config.kind then
- kind = extra_config.kind
+ if extra_config and #apis == 0 then
+ self:api_define(extra_config)
+ else
+ local kind = "values"
+ if extra_config and extra_config.kind then
+ kind = extra_config.kind
+ end
+ return self:api_define({[kind] = apis})
end
- return self:api_define({[kind] = apis})
end
-- get api function
diff --git a/xmake/core/project/project.lua b/xmake/core/project/project.lua
index 020ca80f4..502ea5f3d 100644
--- a/xmake/core/project/project.lua
+++ b/xmake/core/project/project.lua
@@ -716,8 +716,6 @@ function project.interpreter()
result = platform.tool(variable)
end
end
-
- -- ok?
return result
end)
diff --git a/xmake/core/project/rule.lua b/xmake/core/project/rule.lua
index 57510b45a..c6f476d61 100644
--- a/xmake/core/project/rule.lua
+++ b/xmake/core/project/rule.lua
@@ -318,7 +318,9 @@ function rule.apis()
, "rule.on_clean"
, "rule.on_package"
, "rule.on_install"
+ , "rule.on_installcmd"
, "rule.on_uninstall"
+ , "rule.on_uninstallcmd"
, "rule.on_linkcmd"
, "rule.on_buildcmd"
, "rule.on_buildcmd_file"
@@ -334,7 +336,9 @@ function rule.apis()
, "rule.before_clean"
, "rule.before_package"
, "rule.before_install"
+ , "rule.before_installcmd"
, "rule.before_uninstall"
+ , "rule.before_uninstallcmd"
, "rule.before_linkcmd"
, "rule.before_buildcmd"
, "rule.before_buildcmd_file"
@@ -350,7 +354,9 @@ function rule.apis()
, "rule.after_clean"
, "rule.after_package"
, "rule.after_install"
+ , "rule.after_installcmd"
, "rule.after_uninstall"
+ , "rule.after_uninstallcmd"
, "rule.after_linkcmd"
, "rule.after_buildcmd"
, "rule.after_buildcmd_file"
diff --git a/xmake/core/project/target.lua b/xmake/core/project/target.lua
index 3f4a4efc0..54e5c03ab 100644
--- a/xmake/core/project/target.lua
+++ b/xmake/core/project/target.lua
@@ -2768,7 +2768,9 @@ function target.apis()
, "target.on_clean"
, "target.on_package"
, "target.on_install"
+ , "target.on_installcmd"
, "target.on_uninstall"
+ , "target.on_uninstallcmd"
-- target.before_xxx
, "target.before_run"
, "target.before_test"
@@ -2779,7 +2781,9 @@ function target.apis()
, "target.before_clean"
, "target.before_package"
, "target.before_install"
+ , "target.before_installcmd"
, "target.before_uninstall"
+ , "target.before_uninstallcmd"
-- target.after_xxx
, "target.after_run"
, "target.after_test"
@@ -2791,7 +2795,9 @@ function target.apis()
, "target.after_clean"
, "target.after_package"
, "target.after_install"
+ , "target.after_installcmd"
, "target.after_uninstall"
+ , "target.after_uninstallcmd"
}
}
end
diff --git a/xmake/includes/xpack/xmake.lua b/xmake/includes/xpack/xmake.lua
new file mode 100644
index 000000000..bb7d03435
--- /dev/null
+++ b/xmake/includes/xpack/xmake.lua
@@ -0,0 +1,93 @@
+--!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 xmake.lua
+--
+
+-- Define xpack interfaces to generate installation package. e.g. nsis, deb, rpm, ...
+--
+-- And we can call `xmake pack` plugin to pack them.
+--
+-- @see https://github.com/xmake-io/xmake/issues/1433
+--
+
+-- define apis
+local apis = {
+ values = {
+ -- set package version, we will also get it from project/target
+ "xpack.set_version",
+ -- set pacakge homepage url
+ "xpack.set_homepage",
+ -- set package description
+ "xpack.set_description",
+ -- set package copyright
+ "xpack.set_copyright",
+ -- set company name
+ "xpack.set_company",
+ -- set package formats, e.g. nsis, deb, rpm, targz, zip, runself, ...
+ "xpack.set_formats",
+ -- set the base name of the output file
+ "xpack.set_basename",
+ -- add targets to be packaged
+ "xpack.add_targets",
+ -- set installed binary directory, e.g. bin
+ "xpack.set_bindir",
+ -- set installed library directory, e.g. lib
+ "xpack.set_libdir",
+ -- set installed include directory, e.g. include
+ "xpack.set_includedir"
+ },
+ paths = {
+ -- set the spec file path, support the custom variable pattern, e.g. set_specfile("", {pattern = "%${([^\n]-)}"})
+ "xpack.set_specfile",
+ -- set icon file path, e.g foo.ico
+ "xpack.set_iconfile",
+ -- set package license file, we will also get them from target
+ "xpack.set_licensefile",
+ -- add install files, we will also get them from target
+ "xpack.add_installfiles"
+ },
+ script = {
+ -- add custom load script
+ "xpack.on_load",
+ -- add custom commands script before installing
+ "xpack.before_installcmd",
+ -- add custom commands script before uninstalling
+ "xpack.before_uninstallcmd",
+ -- add custom install commands script, we will also get it from target/rules
+ "xpack.on_installcmd",
+ -- add custom uninstall commands script, we will also get it from target/rules
+ "xpack.on_uninstallcmd",
+ -- add custom commands script after installing
+ "xpack.after_installcmd",
+ -- add custom commands script after uninstalling
+ "xpack.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"
+ }
+}
+interp_add_scopeapis(apis)
diff --git a/xmake/modules/private/action/require/impl/utils/filter.lua b/xmake/modules/private/action/require/impl/utils/filter.lua
index 75bba1d42..0eee765b2 100644
--- a/xmake/modules/private/action/require/impl/utils/filter.lua
+++ b/xmake/modules/private/action/require/impl/utils/filter.lua
@@ -28,8 +28,6 @@ import("core.sandbox.sandbox")
-- get filter
function _filter()
-
- -- init filter
if _g.filter == nil then
_g.filter = filter.new()
_g.filter:register("common", function (variable)
@@ -60,13 +58,9 @@ function _filter()
if type(result) == "function" then
result = result()
end
-
- -- ok?
return result
end)
end
-
- -- ok
return _g.filter
end
@@ -75,10 +69,7 @@ function _handler(package, strval)
-- @note cannot cache it, because the package instance will be changed
return function (variable)
-
- -- init maps
- local maps =
- {
+ local maps = {
version = function ()
if strval then
-- set_urls("https://sqlite.org/2018/sqlite-autoconf-$(version)000.tar.gz",
@@ -102,8 +93,6 @@ function _handler(package, strval)
if type(result) == "function" then
result = result()
end
-
- -- ok?
return result
end
end
diff --git a/xmake/modules/private/utils/batchcmds.lua b/xmake/modules/private/utils/batchcmds.lua
index 6edd1d8f3..fe518cbec 100644
--- a/xmake/modules/private/utils/batchcmds.lua
+++ b/xmake/modules/private/utils/batchcmds.lua
@@ -144,6 +144,22 @@ function _runcmd_rm(cmd, opt)
end
end
+-- run command: os.tryrm
+function _runcmd_tryrm(cmd, opt)
+ local filepath = cmd.filepath
+ if not opt.dryrun then
+ os.tryrm(filepath)
+ end
+end
+
+-- run command: os.rmdir
+function _runcmd_rmdir(cmd, opt)
+ local dir = cmd.dir
+ if not opt.dryrun and os.isdir(dir) then
+ os.tryrm(dir)
+ end
+end
+
-- run command: os.cp
function _runcmd_cp(cmd, opt)
if not opt.dryrun then
@@ -178,8 +194,10 @@ function _runcmd(cmd, opt)
execv = _runcmd_execv,
vexecv = _runcmd_vexecv,
mkdir = _runcmd_mkdir,
+ rmdir = _runcmd_rmdir,
cd = _runcmd_cd,
rm = _runcmd_rm,
+ tryrm = _runcmd_tryrm,
cp = _runcmd_cp,
mv = _runcmd_mv,
ln = _runcmd_ln
@@ -333,11 +351,21 @@ function batchcmds:mkdir(dir)
table.insert(self:cmds(), {kind = "mkdir", dir = dir})
end
+-- add command: os.rmdir
+function batchcmds:rmdir(dir)
+ table.insert(self:cmds(), {kind = "rmdir", dir = dir})
+end
+
-- add command: os.rm
function batchcmds:rm(filepath)
table.insert(self:cmds(), {kind = "rm", filepath = filepath})
end
+-- add command: os.tryrm
+function batchcmds:tryrm(filepath)
+ table.insert(self:cmds(), {kind = "tryrm", filepath = filepath})
+end
+
-- add command: os.cp
function batchcmds:cp(srcpath, dstpath, opt)
table.insert(self:cmds(), {kind = "cp", srcpath = srcpath, dstpath = dstpath, opt = opt})
diff --git a/xmake/plugins/pack/filter.lua b/xmake/plugins/pack/filter.lua
new file mode 100644
index 000000000..269dab2da
--- /dev/null
+++ b/xmake/plugins/pack/filter.lua
@@ -0,0 +1,120 @@
+--!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 filter.lua
+--
+
+-- imports
+import("core.base.filter")
+import("core.base.option")
+import("core.base.global")
+import("core.project.config")
+import("core.project.project")
+import("core.sandbox.sandbox")
+
+-- get filter
+function _filter()
+ if _g.filter == nil then
+ _g.filter = filter.new()
+ _g.filter:register("common", function (variable)
+
+ -- attempt to get it directly from the configure
+ local result = config.get(variable)
+ if result == nil then
+
+ -- init maps
+ _g.common_maps = _g.common_maps or
+ {
+ host = os.host()
+ , subhost = os.subhost()
+ , tmpdir = function () return os.tmpdir() end
+ , curdir = function () return os.curdir() end
+ , scriptdir = function () return os.scriptdir() end
+ , globaldir = global.directory()
+ , configdir = config.directory()
+ , projectdir = project.directory()
+ , programdir = os.programdir()
+ }
+
+ -- map it
+ result = _g.common_maps[variable]
+ end
+
+ -- is script? call it
+ if type(result) == "function" then
+ result = result()
+ end
+ return result
+ end)
+ end
+ return _g.filter
+end
+
+-- the package handler
+function _handler(package, strval)
+
+ -- @note cannot cache it, because the package instance will be changed
+ return function (variable)
+ local maps = {
+ arch = package:arch(),
+ plat = package:plat(),
+ version = package:version()
+ }
+
+ -- get value
+ local result = maps[variable]
+ if type(result) == "function" then
+ result = result()
+ end
+ return result
+ end
+end
+
+-- attach filter to the given script and call it
+function call(script, package, opt)
+
+ -- get sandbox filter and handlers of the given script
+ local sandbox_filter = sandbox.filter(script)
+ local sandbox_handlers = sandbox_filter:handlers()
+
+ -- switch to the handlers of the current filter
+ sandbox_filter:set_handlers(_filter():handlers())
+
+ -- register package handler
+ sandbox_filter:register("package", _handler(package))
+
+ -- call it
+ script(package, opt)
+
+ -- restore handlers
+ sandbox_filter:set_handlers(sandbox_handlers)
+end
+
+-- handle the string value of package
+function handle(strval, package)
+
+ -- register filter handler
+ _filter():register("package", _handler(package, strval))
+
+ -- handle string value
+ strval = _filter():handle(strval)
+
+ -- register filter handler
+ _filter():register("package", nil)
+ return strval
+end
+
diff --git a/xmake/plugins/pack/main.lua b/xmake/plugins/pack/main.lua
new file mode 100644
index 000000000..09160fda0
--- /dev/null
+++ b/xmake/plugins/pack/main.lua
@@ -0,0 +1,97 @@
+--!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 main.lua
+--
+
+-- imports
+import("core.base.task")
+import("core.base.option")
+import("core.project.project")
+import("private.service.remote_build.action", {alias = "remote_build_action"})
+import("actions.build.main", {rootdir = os.programdir(), alias = "build_action"})
+import("xpack")
+
+function _load_package(package, format)
+ package:format_set(format)
+ local script = package:script("load")
+ if script then
+ script(package)
+ end
+end
+
+function _pack_package(package)
+ assert(package:formats(), "xpack(%s): formats not found, please use `set_formats()` to set it.", package:name())
+ for _, format in package:formats():keys() do
+ _load_package(package, format)
+ import(format)(package)
+ end
+end
+
+function _pack_packages()
+ for _, package in pairs(xpack.packages()) do
+ _pack_package(package)
+ end
+end
+
+function _build_targets()
+ local targetnames = {}
+ for _, package in pairs(xpack.packages()) do
+ local targets = package:get("targets")
+ if targets then
+ table.join2(targetnames, targets)
+ end
+ end
+ if #targetnames > 0 then
+ build_action.build_targets(targetnames)
+ end
+end
+
+function main()
+
+ -- do action for remote?
+ if remote_build_action.enabled() then
+ return remote_build_action()
+ end
+
+ -- lock the whole project
+ project.lock()
+
+ -- load config first
+ task.run("config", {}, {disable_dump = true})
+
+ -- load targets
+ project.load_targets()
+
+ -- enter project directory
+ local oldir = os.cd(project.directory())
+
+ -- build targets first
+ _build_targets()
+
+ -- do pack
+ _pack_packages()
+
+ -- leave project directory
+ os.cd(oldir)
+
+ -- unlock the whole project
+ project.unlock()
+ cprint("${color.success}pack ok")
+end
+
+
diff --git a/xmake/plugins/pack/nsis/main.lua b/xmake/plugins/pack/nsis/main.lua
new file mode 100644
index 000000000..23749962a
--- /dev/null
+++ b/xmake/plugins/pack/nsis/main.lua
@@ -0,0 +1,618 @@
+--!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 main.lua
+--
+
+-- imports
+import("core.base.option")
+import("core.base.semver")
+import("lib.detect.find_tool")
+import("private.utils.batchcmds")
+import("private.action.require.impl.packagenv")
+import("private.action.require.impl.install_packages")
+
+-- get the makensis
+function _get_makensis()
+
+ -- enter the environments of nsis
+ local oldenvs = packagenv.enter("nsis")
+
+ -- find makensis
+ local packages = {}
+ local makensis = find_tool("makensis", {check = "/CMDHELP"})
+ if not makensis then
+ table.join2(packages, install_packages("nsis"))
+ end
+
+ -- enter the environments of installed packages
+ for _, instance in ipairs(packages) do
+ instance:envs_enter()
+ end
+
+ -- we need to force detect and flush detect cache after loading all environments
+ if not makensis then
+ makensis = find_tool("makensis", {check = "/CMDHELP", force = true})
+ end
+ assert(makensis, "makensis not found!")
+
+ return makensis, oldenvs
+end
+
+-- get unique tag
+function _get_unique_tag(content)
+ return hash.uuid(content):split("-", {plain = true})[1]:lower()
+end
+
+-- get command string
+function _get_command_strings(package, cmd)
+ local result = {}
+ local opt = cmd.opt or {}
+ local kind = cmd.kind
+ if kind == "cp" then
+ -- https://nsis.sourceforge.io/Reference/File
+ local srcfiles = os.files(cmd.srcpath)
+ for _, srcfile in ipairs(srcfiles) do
+ -- the destination is directory? append the filename
+ local dstfile = cmd.dstpath
+ if path.islastsep(dstfile) then
+ if opt.rootdir then
+ dstfile = path.join(dstfile, path.relative(srcfile, opt.rootdir))
+ else
+ dstfile = path.join(dstfile, path.filename(srcfile))
+ end
+ end
+ srcfile = path.normalize(srcfile)
+ local dstname = path.filename(dstfile)
+ local dstdir = path.normalize(path.directory(path.join("$InstDir", dstfile)))
+ table.insert(result, string.format("SetOutPath \"%s\"", dstdir))
+ table.insert(result, string.format("File /oname=%s \"%s\"", dstname, srcfile))
+ end
+ elseif kind == "rm" then
+ local filepath = path.normalize(path.join("$InstDir", cmd.filepath))
+ table.insert(result, string.format("Delete \"%s\"", filepath))
+ elseif kind == "tryrm" then
+ --[[
+ IfFileExists "$InstDir\file" file_found file_not_found_or_end
+ file_found:
+ Delete "$InstDir\file"
+ goto file_not_found_or_end
+ file_not_found_or_end:
+ --]]
+ local filepath = path.normalize(path.join("$InstDir", cmd.filepath))
+ local tag = _get_unique_tag(filepath)
+ table.insert(result, string.format("IfFileExists \"%s\" file_found_%s file_not_found_or_end_%s", filepath, tag, tag))
+ table.insert(result, string.format("file_found_%s:", tag))
+ table.insert(result, string.format(" Delete \"%s\"", filepath))
+ table.insert(result, string.format(" goto file_not_found_or_end_%s", tag))
+ table.insert(result, string.format("file_not_found_or_end_%s:", tag))
+ elseif kind == "rmdir" then
+ local dir = path.normalize(path.join("$InstDir", cmd.dir))
+ table.insert(result, string.format("RMDir /r \"%s\"", dir))
+ elseif kind == "mv" then
+ local srcpath = path.normalize(path.join("$InstDir", cmd.srcpath))
+ local dstpath = path.normalize(path.join("$InstDir", cmd.dstpath))
+ table.insert(result, string.format("Rename \"%s\" \"%s\"", srcpath, dstpath))
+ elseif kind == "cd" then
+ local dir = path.normalize(path.join("$InstDir", cmd.dir))
+ table.insert(result, string.format("SetOutPath \"%s\"", dir))
+ elseif kind == "mkdir" then
+ local dir = path.normalize(path.join("$InstDir", cmd.dir))
+ table.insert(result, string.format("CreateDirectory \"%s\"", dir))
+ end
+ return result
+end
+
+-- get commands string
+function _get_commands_string(package, cmds)
+ local cmdstrs = {}
+ for _, cmd in ipairs(cmds) do
+ table.join2(cmdstrs, _get_command_strings(package, cmd))
+ end
+ return table.concat(cmdstrs, "\n ")
+end
+
+-- install headers
+function _install_headers(target, batchcmds_, includedir)
+ local srcheaders, dstheaders = target:headerfiles(includedir, {installonly = true})
+ if srcheaders and dstheaders then
+ local i = 1
+ for _, srcheader in ipairs(srcheaders) do
+ local dstheader = dstheaders[i]
+ if dstheader then
+ batchcmds_:cp(srcheader, dstheader)
+ end
+ i = i + 1
+ end
+ end
+end
+
+-- install shared libraries for package
+function _install_shared_for_package(target, pkg, batchcmds_, outputdir)
+ _g.installed_dllfiles = _g.installed_dllfiles or {}
+ for _, dllpath in ipairs(table.wrap(pkg:get("libfiles"))) do
+ if dllpath:endswith(".dll") then
+ -- prevent packages using the same libfiles from overwriting each other
+ if not _g.installed_dllfiles[dllpath] then
+ local dllname = path.filename(dllpath)
+ batchcmds_:cp(dllpath, path.join(outputdir, dllname))
+ _g.installed_dllfiles[dllpath] = true
+ end
+ end
+ end
+end
+
+-- install shared libraries for packages
+function _install_shared_for_packages(target, batchcmds_, outputdir)
+ _g.installed_packages = _g.installed_packages or {}
+ for _, pkg in ipairs(target:orderpkgs()) do
+ if not _g.installed_packages[pkg:name()] then
+ if pkg:enabled() and pkg:get("libfiles") then
+ _install_shared_for_package(target, pkg, batchcmds_, outputdir)
+ end
+ _g.installed_packages[pkg:name()] = true
+ end
+ end
+end
+
+-- uninstall headers
+function _uninstall_headers(target, batchcmds_, includedir)
+ local _, dstheaders = target:headerfiles(includedir, {installonly = true})
+ for _, dstheader in ipairs(dstheaders) do
+ batchcmds_:rm(dstheader)
+ end
+end
+
+-- uninstall shared libraries for package
+function _uninstall_shared_for_package(target, pkg, batchcmds_, outputdir)
+ for _, dllpath in ipairs(table.wrap(pkg:get("libfiles"))) do
+ if dllpath:endswith(".dll") then
+ local dllname = path.filename(dllpath)
+ batchcmds_:rm(path.join(outputdir, dllname))
+ end
+ end
+end
+
+-- uninstall shared libraries for packages
+function _uninstall_shared_for_packages(target, batchcmds_, outputdir)
+ _g.uninstalled_packages = _g.uninstalled_packages or {}
+ for _, pkg in ipairs(target:orderpkgs()) do
+ if not _g.uninstalled_packages[pkg:name()] then
+ if pkg:enabled() and pkg:get("libfiles") then
+ _uninstall_shared_for_package(target, pkg, batchcmds_, outputdir)
+ end
+ _g.uninstalled_packages[pkg:name()] = true
+ end
+ end
+end
+
+-- on install binary target command
+function _on_target_installcmd_binary(target, batchcmds_, opt)
+ local package = opt.package
+ local bindir = package:bindir()
+
+ -- install target file
+ batchcmds_:cp(target:targetfile(), path.join(bindir, target:filename()))
+ if os.isfile(target:symbolfile()) then
+ batchcmds_:cp(target:symbolfile(), path.join(bindir, path.filename(target:symbolfile())))
+ end
+
+ -- install the dependent shared/windows (*.dll) target
+ -- @see https://github.com/xmake-io/xmake/issues/961
+ _g.installed_dllfiles = _g.installed_dllfiles or {}
+ for _, dep in ipairs(target:orderdeps()) do
+ if dep:kind() == "shared" then
+ local depfile = dep:targetfile()
+ if os.isfile(depfile) then
+ if not _g.installed_dllfiles[depfile] then
+ batchcmds_:cp(depfile, path.join(bindir, path.filename(depfile)))
+ _g.installed_dllfiles[depfile] = true
+ end
+ end
+ end
+ -- install all shared libraries in packages in all deps
+ _install_shared_for_packages(dep, batchcmds_, bindir)
+ end
+
+ -- install shared libraries for all packages
+ _install_shared_for_packages(target, batchcmds_, bindir)
+end
+
+-- on install shared target command
+function _on_target_installcmd_shared(target, batchcmds_, opt)
+ local package = opt.package
+ local bindir = package:bindir()
+ local libdir = package:libdir()
+ local includedir = package:includedir()
+
+ -- install target file
+ batchcmds_:cp(target:targetfile(), path.join(bindir, target:filename()))
+ if os.isfile(target:symbolfile()) then
+ batchcmds_:cp(target:symbolfile(), path.join(bindir, path.filename(target:symbolfile())))
+ end
+
+ -- install *.lib for shared/windows (*.dll) target
+ -- @see https://github.com/xmake-io/xmake/issues/714
+ local targetfile = target:targetfile()
+ local targetfile_lib = path.join(path.directory(targetfile), path.basename(targetfile) .. (target:is_plat("mingw") and ".dll.a" or ".lib"))
+ if os.isfile(targetfile_lib) then
+ batchcmds_:mkdir(libdir)
+ batchcmds_:cp(targetfile_lib, path.join(libdir, path.filename(targetfile_lib)))
+ end
+
+ -- install shared libraries for all packages
+ _install_shared_for_packages(target, batchcmds_, bindir)
+
+ -- install headers
+ _install_headers(target, batchcmds_, includedir)
+end
+
+-- on install static target command
+function _on_target_installcmd_static(target, batchcmds_, opt)
+ local package = opt.package
+ local libdir = package:libdir()
+ local includedir = package:includedir()
+
+ -- install target file
+ batchcmds_:cp(target:targetfile(), path.join(libdir, target:filename()))
+ if os.isfile(target:symbolfile()) then
+ batchcmds_:cp(target:symbolfile(), path.join(libdir, path.filename(target:symbolfile())))
+ end
+
+ -- install headers
+ _install_headers(target, batchcmds_, includedir)
+end
+
+-- on install headeronly target command
+function _on_target_installcmd_headeronly(target, batchcmds_, opt)
+ local package = opt.package
+ local includedir = package:includedir()
+end
+
+-- on install target command
+function _on_target_installcmd(target, batchcmds_, opt)
+
+ -- install target binaries
+ local scripts = {
+ binary = _on_target_installcmd_binary,
+ shared = _on_target_installcmd_shared,
+ static = _on_target_installcmd_static,
+ headeronly = _on_target_installcmd_headeronly
+ }
+ local script = scripts[target:kind()]
+ if script then
+ script(target, batchcmds_, opt)
+ end
+
+ -- install target files
+ local srcfiles, dstfiles = target:installfiles(".")
+ for idx, srcfile in ipairs(srcfiles) do
+ batchcmds_:cp(srcfile, dstfiles[idx])
+ end
+end
+
+-- on uninstall binary target command
+function _on_target_uninstallcmd_binary(target, batchcmds_, opt)
+ local package = opt.package
+ local bindir = package:bindir()
+
+ -- uninstall target file
+ batchcmds_:rm(path.join(bindir, target:filename()))
+ batchcmds_:tryrm(path.join(bindir, path.filename(target:symbolfile())))
+
+ -- remove the dependent shared/windows (*.dll) target
+ -- @see https://github.com/xmake-io/xmake/issues/961
+ for _, dep in ipairs(target:orderdeps()) do
+ if dep:is_shared() then
+ batchcmds_:rm(path.join(bindir, path.filename(dep:targetfile())))
+ end
+ _uninstall_shared_for_packages(dep, batchcmds_, bindir)
+ end
+
+ -- uninstall shared libraries for packages
+ _uninstall_shared_for_packages(target, batchcmds_, bindir)
+end
+
+-- on uninstall shared target command
+function _on_target_uninstallcmd_shared(target, batchcmds_, opt)
+ local package = opt.package
+ local bindir = package:bindir()
+ local libdir = package:libdir()
+ local includedir = package:includedir()
+
+ -- uninstall target file
+ batchcmds_:rm(path.join(bindir, target:filename()))
+ batchcmds_:tryrm(path.join(bindir, path.filename(target:symbolfile())))
+
+ -- remove *.lib for shared/windows (*.dll) target
+ -- @see https://github.com/xmake-io/xmake/issues/714
+ local targetfile = target:targetfile()
+ batchcmds_:rm(path.join(libdir, path.basename(targetfile) .. (target:is_plat("mingw") and ".dll.a" or ".lib")))
+
+ -- remove headers from the include directory
+ _uninstall_headers(target, batchcmds_, includedir)
+
+ -- uninstall shared libraries for packages
+ _uninstall_shared_for_packages(target, batchcmds_, bindir)
+end
+
+-- on uninstall static target command
+function _on_target_uninstallcmd_static(target, batchcmds_, opt)
+ local package = opt.package
+ local libdir = package:libdir()
+ local includedir = package:includedir()
+
+ -- uninstall target file
+ batchcmds_:rm(path.join(libdir, target:filename()))
+ batchcmds_:tryrm(path.join(libdir, path.filename(target:symbolfile())))
+
+ -- remove headers from the include directory
+ _uninstall_headers(target, batchcmds_, includedir)
+end
+
+-- on uninstall headeronly target command
+function _on_target_uninstallcmd_headeronly(target, batchcmds_, opt)
+ local package = opt.package
+ local includedir = package:includedir()
+ _uninstall_headers(target, batchcmds_, includedir)
+end
+
+-- on uninstall target command
+function _on_target_uninstallcmd(target, batchcmds_, opt)
+
+ -- uninstall target binaries
+ local scripts = {
+ binary = _on_target_uninstallcmd_binary,
+ shared = _on_target_uninstallcmd_shared,
+ static = _on_target_uninstallcmd_static,
+ headeronly = _on_target_uninstallcmd_headeronly
+ }
+ local script = scripts[target:kind()]
+ if script then
+ script(target, batchcmds_, opt)
+ end
+
+ -- uninstall target files
+ local _, dstfiles = target:installfiles(".")
+ for _, dstfile in ipairs(dstfiles) do
+ batchcmds_:rm(dstfile)
+ end
+end
+
+-- get install commands from targets
+function _get_target_installcmds(target, batchcmds_, opt)
+
+ -- call script to get install commands
+ local scripts = {
+ target:script("installcmd_before"),
+ function (target)
+ for _, r in ipairs(target:orderules()) do
+ local before_installcmd = r:script("installcmd_before")
+ if before_installcmd then
+ before_installcmd(target, batchcmds_, opt)
+ end
+ end
+ end,
+ target:script("installcmd", _on_target_installcmd),
+ function (target)
+ for _, r in ipairs(target:orderules()) do
+ local after_installcmd = r:script("installcmd_after")
+ if after_installcmd then
+ after_installcmd(target, batchcmds_, opt)
+ end
+ end
+ end,
+ target:script("installcmd_after")
+ }
+ for i = 1, 5 do
+ local script = scripts[i]
+ if script ~= nil then
+ script(target, batchcmds_, opt)
+ end
+ end
+end
+
+-- get uninstall commands from targets
+function _get_target_uninstallcmds(target, batchcmds_, opt)
+
+ -- call script to get uninstall commands
+ local scripts = {
+ target:script("uninstallcmd_before"),
+ function (target)
+ for _, r in ipairs(target:orderules()) do
+ local before_uninstallcmd = r:script("uninstallcmd_before")
+ if before_uninstallcmd then
+ before_uninstallcmd(target, batchcmds_, opt)
+ end
+ end
+ end,
+ target:script("uninstallcmd", _on_target_uninstallcmd),
+ function (target)
+ for _, r in ipairs(target:orderules()) do
+ local after_uninstallcmd = r:script("uninstallcmd_after")
+ if after_uninstallcmd then
+ after_uninstallcmd(target, batchcmds_, opt)
+ end
+ end
+ end,
+ target:script("uninstallcmd_after")
+ }
+ for i = 1, 5 do
+ local script = scripts[i]
+ if script ~= nil then
+ script(target, batchcmds_, opt)
+ end
+ end
+end
+
+-- on install command
+function _on_installcmd(package, batchcmds_)
+ local srcfiles, dstfiles = package:installfiles(".")
+ for idx, srcfile in ipairs(srcfiles) do
+ batchcmds_:cp(srcfile, dstfiles[idx])
+ end
+ for _, target in ipairs(package:targets()) do
+ _get_target_installcmds(target, batchcmds_, {package = package})
+ end
+end
+
+-- on uninstall command
+function _on_uninstallcmd(package, batchcmds_)
+ local _, dstfiles = package:installfiles(".")
+ for _, dstfile in ipairs(dstfiles) do
+ batchcmds_:rm(dstfile)
+ end
+ for _, target in ipairs(package:targets()) do
+ _get_target_uninstallcmds(target, batchcmds_, {package = package})
+ end
+end
+
+-- get install commands
+function _get_installcmds(package)
+ local batchcmds_ = batchcmds.new()
+
+ -- call script to get install commands
+ local scripts = {
+ package:script("installcmd_before"),
+ package:script("installcmd", _on_installcmd),
+ package:script("installcmd_after")
+ }
+ for i = 1, 3 do
+ local script = scripts[i]
+ if script ~= nil then
+ script(package, batchcmds_)
+ end
+ end
+
+ -- generate command string
+ return _get_commands_string(package, batchcmds_:cmds())
+end
+
+-- get uninstall commands
+function _get_uninstallcmds(package)
+ local batchcmds_ = batchcmds.new()
+
+ -- call script to get uninstall commands
+ local scripts = {
+ package:script("uninstallcmd_before"),
+ package:script("uninstallcmd", _on_uninstallcmd),
+ package:script("uninstallcmd_after")
+ }
+ for i = 1, 3 do
+ local script = scripts[i]
+ if script ~= nil then
+ script(package, batchcmds_)
+ end
+ end
+
+ -- generate command string
+ return _get_commands_string(package, batchcmds_:cmds())
+end
+
+-- get specvars
+function _get_specvars(package)
+ local specvars = table.clone(package:specvars())
+ specvars.PACKAGE_WORKDIR = path.absolute(os.projectdir())
+ specvars.PACKAGE_BINDIR = package:bindir()
+ specvars.PACKAGE_OUTPUTFILE = path.absolute(package:outputfile())
+ specvars.PACKAGE_INSTALLCMDS = function ()
+ return _get_installcmds(package)
+ end
+ specvars.PACKAGE_UNINSTALLCMDS = function ()
+ return _get_uninstallcmds(package)
+ 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))
+ 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))
+ end
+ return table.concat(result, "\n ")
+ end
+ return specvars
+end
+
+-- pack nsis package
+function _pack_nsis(makensis, package, opt)
+
+ -- install the initial specfile
+ local specfile = package:specfile()
+ if not os.isfile(specfile) then
+ local specfile_template = path.join(os.programdir(), "scripts", "xpack", "nsis", "makensis.nsi")
+ os.cp(specfile_template, specfile)
+ end
+
+ -- replace variables in specfile
+ local specvars = _get_specvars(package)
+ local pattern = package:extraconf("specfile", "pattern") or "%${([^\n]-)}"
+ io.gsub(specfile, "(" .. pattern .. ")", function(_, name)
+ name = name:trim()
+ local value = specvars[name]
+ if type(value) == "function" then
+ value = value()
+ end
+ if value ~= nil then
+ dprint(" > replace %s -> %s", name, value)
+ end
+ if type(value) == "table" then
+ dprint("invalid variable value", value)
+ end
+ return value
+ end)
+
+ -- make package
+ os.vrunv(makensis, {specfile})
+end
+
+function main(package)
+ cprint("packing %s", package:outputfile())
+
+ -- get makensis
+ local makensis, oldenvs = _get_makensis()
+
+ -- clean the build directory first
+ os.tryrm(package:buildir())
+
+ -- pack nsis package
+ os.mkdir(package:outputdir())
+ _pack_nsis(makensis.program, package, opt)
+
+ -- done
+ os.setenvs(oldenvs)
+end
diff --git a/xmake/plugins/pack/xmake.lua b/xmake/plugins/pack/xmake.lua
new file mode 100644
index 000000000..4bbd7f4bf
--- /dev/null
+++ b/xmake/plugins/pack/xmake.lua
@@ -0,0 +1,37 @@
+--!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 pack.lua
+--
+
+task("pack")
+ set_category("plugin")
+ on_run("main")
+ set_menu {
+ usage = "xmake pack [options] [names]",
+ description = "Pack binary installation packages.",
+ options = {
+ {'o', "outputdir", "kv", nil, "The output directory. (default: build/xpack)"},
+ {'f', "formats", "kv", "all", "Pack the given package formats.",
+ "e.g.",
+ " - xmake pack -f nsis,deb,rpm",
+ "values:",
+ values = {"nsis", "deb", "rpm", "runself", "targz", "zip"}},
+ {},
+ {nil, "packages", "vs", nil, "The package names."}
+ }
+ }
diff --git a/xmake/plugins/pack/xpack.lua b/xmake/plugins/pack/xpack.lua
new file mode 100644
index 000000000..c925a9af1
--- /dev/null
+++ b/xmake/plugins/pack/xpack.lua
@@ -0,0 +1,495 @@
+--!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.lua
+--
+
+-- imports
+import("core.base.object")
+import("core.base.option")
+import("core.base.semver")
+import("core.base.hashset")
+import("core.project.config")
+import("core.project.project")
+import("lib.detect.find_tool")
+import("filter")
+
+-- define module
+local xpack = xpack or object {_init = {"_name", "_info"}}
+
+-- get name
+function xpack:name()
+ return self._name
+end
+
+-- get value
+function xpack:get(name)
+ if self._info then
+ return self._info:get(name)
+ end
+end
+
+-- get the extra configuration
+function xpack:extraconf(name, item, key)
+ if self._info then
+ return self._info:extraconf(name, item, key)
+ end
+end
+
+-- get the package license
+function xpack:license()
+ return self:get("license")
+end
+
+-- get the package description
+function xpack:description()
+ return self:get("description")
+end
+
+-- get the platform of package
+function xpack:plat()
+ return config.get("plat") or os.subhost()
+end
+
+-- get the architecture of package
+function xpack:arch()
+ return config.get("arch") or os.subarch()
+end
+
+-- the current platform is belong to the given platforms?
+function xpack:is_plat(...)
+ local plat = self:plat()
+ for _, v in ipairs(table.pack(...)) do
+ if v and plat == v then
+ return true
+ end
+ end
+end
+
+-- the current architecture is belong to the given architectures?
+function xpack:is_arch(...)
+ local arch = self:arch()
+ for _, v in ipairs(table.pack(...)) do
+ if v and arch:find("^" .. v:gsub("%-", "%%-") .. "$") then
+ return true
+ end
+ end
+end
+
+-- get xxx_script
+function xpack: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: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(%s): target(%s) not found!", self:name(), name)
+ end
+ end
+ end
+ self._targets = targets
+ end
+ return targets
+end
+
+-- get formats
+function xpack:formats()
+ local formats = self._formats
+ if not formats then
+ formats = hashset.new()
+ for _, format in ipairs(self:get("formats")) do
+ formats:insert(format)
+ end
+ self._formats = formats
+ end
+ return formats
+end
+
+-- has the given format?
+function xpack:format_has(...)
+ local formats = self:formats()
+ for _, v in ipairs(table.pack(...)) do
+ if v and formats:has(v) then
+ return true
+ end
+ end
+end
+
+-- set the current format
+function xpack:format_set(format)
+ self._FORMAT = format
+end
+
+-- get the current format
+function xpack:format()
+ return self._FORMAT
+end
+
+-- get the build directory
+function xpack:buildir()
+ return path.join(config.buildir(), ".xpack", self:name())
+end
+
+-- get the output directory
+function xpack:outputdir()
+ local outputdir = option.get("outputdir")
+ if outputdir == nil then
+ outputdir = path.join(config.buildir(), "xpack", self:name())
+ end
+ return outputdir
+end
+
+-- get the basename
+function xpack:basename()
+ local basename = self:get("basename")
+ if basename == nil then
+ basename = self:name()
+ local version = self:version()
+ if version then
+ basename = basename .. "-" .. version
+ end
+ end
+ -- we need filter builtin variables, e.g. $(plat), $(arch), $(version) ...
+ return filter.handle(basename, self)
+end
+
+-- get the spec variables
+function xpack:specvars()
+ local specvars = self._specvars
+ if specvars == nil then
+ specvars = {
+ PACKAGE_ARCH = self:arch(),
+ PACKAGE_PLAT = self:plat(),
+ PACKAGE_NAME = self:name(),
+ PACKAGE_DESCRIPTION = self:description() or "",
+ PACKAGE_FILENAME = self:filename(),
+ PACKAGE_HOMEPAGE = self:get("homepage") or "",
+ PACKAGE_COPYRIGHT = self:get("copyright") or "",
+ PACKAGE_COMPANY = self:get("company") or "",
+ PACKAGE_ICONFILE = self:get("iconfile") or "",
+ PACKAGE_LICENSEFILE = self:get("licensefile") or ""
+ }
+
+ -- get version
+ local version, version_build = self:version()
+ if version then
+ specvars.PACKAGE_VERSION = version or "0.0.0"
+ try {function ()
+ local v = semver.new(version)
+ if v then
+ specvars.PACKAGE_VERSION_MAJOR = v:major() or "0"
+ specvars.PACKAGE_VERSION_MINOR = v:minor() or "0"
+ specvars.PACKAGE_VERSION_ALTER = v:patch() or "0"
+ end
+ end}
+ specvars.PACKAGE_VERSION_BUILD = version_build or ""
+ end
+
+ -- get git information
+ local cmds =
+ {
+ GIT_TAG = {"describe", "--tags"},
+ GIT_TAG_LONG = {"describe", "--tags", "--long"},
+ GIT_BRANCH = {"rev-parse", "--abbrev-ref", "HEAD"},
+ GIT_COMMIT = {"rev-parse", "--short", "HEAD"},
+ GIT_COMMIT_LONG = {"rev-parse", "HEAD"},
+ GIT_COMMIT_DATE = {"log", "-1", "--date=format:%Y%m%d%H%M%S", "--format=%ad"}
+ }
+ for name, argv in pairs(cmds) do
+ specvars[name] = function ()
+ local result
+ local git = find_tool("git")
+ if git then
+ result = try {function ()
+ return os.iorunv(git.program, argv)
+ end}
+ end
+ if not result then
+ result = "none"
+ end
+ return result:trim()
+ end
+ end
+
+ -- get user variables
+ local vars = self:get("specvar")
+ if vars then
+ table.join2(specvars, vars)
+ end
+ self._specvars = specvars
+ end
+ return specvars
+end
+
+-- get the specfile path
+function xpack:specfile()
+ local extensions = {
+ nsis = ".nsi"
+ }
+ local extension = extensions[self:format()] or ".spec"
+ return self:get("specfile") or path.join(self:buildir(), self:basename() .. extension)
+end
+
+-- get the output filename
+function xpack:filename()
+ local extensions = {
+ nsis = ".exe"
+ }
+ local extension = extensions[self:format()] or ""
+ return self:basename() .. extension
+end
+
+-- get the output file
+function xpack:outputfile()
+ return path.join(self:outputdir(), self:filename())
+end
+
+-- get the package version
+function xpack:version()
+ local version = self:get("version")
+ local version_build
+ if version == nil then
+ for _, target in ipairs(self:targets()) do
+ version, version_build = target:version()
+ if version then
+ break
+ end
+ end
+ if version == nil then
+ version, version_build = project.version()
+ end
+ else
+ version_build = self:extraconf("version", version, "build")
+ if type(version_build) == "string" then
+ version_build = os.date(version_build, os.time())
+ end
+ end
+ return version, version_build
+end
+
+-- get the copied files
+function xpack:_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
+
+ -- 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 install files
+function xpack:installfiles(outputdir)
+ return self:_copiedfiles("installfiles", outputdir)
+end
+
+-- get the binary directory
+function xpack:bindir()
+ local bindir = self:get("bindir")
+ if bindir == nil then
+ bindir = "bin"
+ end
+ return bindir
+end
+
+-- get the library directory
+function xpack:libdir()
+ local libdir = self:get("libdir")
+ if libdir == nil then
+ libdir = "lib"
+ end
+ return libdir
+end
+
+-- get the include directory
+function xpack:includedir()
+ local includedir = self:get("includedir")
+ if includedir == nil then
+ includedir = "include"
+ end
+ return includedir
+end
+
+-- new a xpack
+function _new(name, info)
+ return xpack {name, info}
+end
+
+-- get xpack packages
+function packages()
+ local packages = _g.packages
+ if not packages then
+ packages = {}
+ local packages_need = option.get("packages")
+ if packages_need then
+ packages_need = hashset.from(packages_need)
+ end
+ local formats_need = option.get("formats")
+ if formats_need then
+ formats_need = formats_need:split(",")
+ if formats_need[1] == "all" then
+ formats_need = nil
+ end
+ end
+ local xpack_scope = project.scope("xpack")
+ for name, scope in pairs(xpack_scope) do
+ local need = false
+ if packages_need then
+ if packages_need:has(name) then
+ need = true
+ end
+ else
+ need = true
+ end
+ if need then
+ local instance = _new(name, scope)
+ if not formats_need or instance:format_has(table.unpack(formats_need)) then
+ packages[name] = instance
+ end
+ end
+ end
+ _g.packages = packages
+ end
+ return packages
+end
+
+-- get the given package
+function package(name)
+ return packages()[name]
+end
diff --git a/xmake/plugins/project/cmake/cmakelists.lua b/xmake/plugins/project/cmake/cmakelists.lua
index fea241066..150720c1e 100644
--- a/xmake/plugins/project/cmake/cmakelists.lua
+++ b/xmake/plugins/project/cmake/cmakelists.lua
@@ -908,6 +908,8 @@ function _get_command_string(cmd, outputdir)
end
elseif kind == "rm" then
return string.format("${CMAKE_COMMAND} -E rm -rf %s", _get_relative_unix_path_to_cmake(cmd.filepath, outputdir))
+ elseif kind == "rmdir" then
+ return string.format("${CMAKE_COMMAND} -E rm -rf %s", _get_relative_unix_path_to_cmake(cmd.dir, outputdir))
elseif kind == "mv" then
return string.format("${CMAKE_COMMAND} -E rename %s %s",
_get_relative_unix_path_to_cmake(cmd.srcpath, outputdir), _get_relative_unix_path_to_cmake(cmd.dstpath, outputdir))
diff --git a/xmake/plugins/project/make/makefile.lua b/xmake/plugins/project/make/makefile.lua
index 7d2559ea1..7d462b138 100644
--- a/xmake/plugins/project/make/makefile.lua
+++ b/xmake/plugins/project/make/makefile.lua
@@ -215,6 +215,15 @@ function _get_cmd_rm(filedir)
end
end
+-- get command: rmdir
+function _get_cmd_rmdir(filedir)
+ if is_subhost("windows") then
+ return string.format("@rmdir /S /Q %s > NUL 2>&1", filedir)
+ else
+ return string.format("@rm -rf %s", filedir)
+ end
+end
+
-- get command: echo
function _get_cmd_echo(str)
return string.format("@echo %s", colors.ignore(str))
@@ -248,6 +257,8 @@ function _get_command_string(cmd, outputdir)
end
elseif kind == "rm" then
return _get_cmd_rm(_get_relative_unix_path(cmd.filepath, outputdir))
+ elseif kind == "rmdir" then
+ return _get_cmd_rmdir(_get_relative_unix_path(cmd.dir, outputdir))
elseif kind == "mv" then
return _get_cmd_mv(_get_relative_unix_path(cmd.srcpath, outputdir), _get_relative_unix_path(cmd.dstpath, outputdir))
elseif kind == "ln" then
diff --git a/xmake/plugins/project/vstudio/impl/vs201x.lua b/xmake/plugins/project/vstudio/impl/vs201x.lua
index 3b6f145f4..a8b48dada 100644
--- a/xmake/plugins/project/vstudio/impl/vs201x.lua
+++ b/xmake/plugins/project/vstudio/impl/vs201x.lua
@@ -102,6 +102,8 @@ function _get_command_string(cmd, vcxprojdir)
return string.format("copy /Y \"%s\" \"%s\"", _translate_path(cmd.srcpath, vcxprojdir), _translate_path(cmd.dstpath, vcxprojdir))
elseif kind == "rm" then
return string.format("del /F /Q \"%s\" || rmdir /S /Q \"%s\"", _translate_path(cmd.filepath, vcxprojdir), _translate_path(cmd.filepath, vcxprojdir))
+ elseif kind == "rmdir" then
+ return string.format("rmdir /S /Q \"%s\"", _translate_path(cmd.filepath, vcxprojdir))
elseif kind == "mv" then
return string.format("rename \"%s\" \"%s\"", _translate_path(cmd.srcpath, vcxprojdir), _translate_path(cmd.dstpath, vcxprojdir))
elseif kind == "cd" then
diff --git a/xmake/scripts/xpack/nsis/makensis.nsi b/xmake/scripts/xpack/nsis/makensis.nsi
new file mode 100644
index 000000000..3b79361bd
--- /dev/null
+++ b/xmake/scripts/xpack/nsis/makensis.nsi
@@ -0,0 +1,301 @@
+; this is a NSI spec file,
+; it is autogenerated by the xmake build system.
+; do not edit by hand.
+
+; includes
+!include "MUI2.nsh"
+!include "WordFunc.nsh"
+!include "WinMessages.nsh"
+!include "FileFunc.nsh"
+!include "UAC.nsh"
+
+; the version information
+!define VERSION "${PACKAGE_VERSION}"
+!if "${PACKAGE_VERSION_BUILD}" != ""
+!define VERSION_FULL "${PACKAGE_VERSION}+${PACKAGE_VERSION_BUILD}"
+!else
+!define VERSION_FULL "${PACKAGE_VERSION}"
+!endif
+
+; set the package name
+Name "${PACKAGE_NAME} - v${VERSION}"
+
+; set the output file path
+OutFile "${PACKAGE_OUTPUTFILE}"
+
+; set the working directory
+!cd "${PACKAGE_WORKDIR}"
+
+; use unicode
+Unicode true
+
+; use best compressor
+SetCompressor /FINAL /SOLID lzma
+SetCompressorDictSize 64
+SetDatablockOptimize ON
+
+; set the default installation directory
+!if "${PACKAGE_ARCH}" == "x64"
+ !define PROGRAMFILES $PROGRAMFILES64
+ !define HKLM HKLM64
+ !define HKCU HKCU64
+!else
+ !define PROGRAMFILES $PROGRAMFILES
+ !define HKLM HKLM
+ !define HKCU HKCU
+!endif
+
+; request application privileges for Windows Vista
+RequestExecutionLevel user
+
+; set DPI aware
+ManifestDPIAware true
+
+; set icon
+!if "${PACKAGE_ICONFILE}" != ""
+ !define MUI_ICON "${PACKAGE_ICONFILE}"
+!endif
+
+; UAC
+!macro Init thing
+ uac_tryagain:
+ !insertmacro UAC_RunElevated
+ ${Switch} $0
+ ${Case} 0
+ ${IfThen} $1 = 1 ${|} Quit ${|} ;we are the outer process, the inner process has done its work, we are done
+ ${IfThen} $3 <> 0 ${|} ${Break} ${|} ;we are admin, let the show go on
+ ${If} $1 = 3 ;RunAs completed successfully, but with a non-admin user
+ MessageBox mb_YesNo|mb_IconExclamation|mb_TopMost|mb_SetForeground "This ${thing} requires admin privileges, try again" /SD IDNO IDYES uac_tryagain IDNO 0
+ ${EndIf}
+ ;fall-through and die
+ ${Case} 1223
+ MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "This ${thing} requires admin privileges, aborting!"
+ Quit
+ ${Case} 1062
+ MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "Logon service not running, aborting!"
+ Quit
+ ${Default}
+ MessageBox mb_IconStop|mb_TopMost|mb_SetForeground "Unable to elevate, error $0"
+ Quit
+ ${EndSwitch}
+
+ ; The UAC plugin changes the error level even in the inner process, reset it.
+ ; note fix install exit code 1223 to 0 with slient /S
+ SetErrorLevel 0
+ SetShellVarContext all
+!macroend
+
+; add the install Pages
+!insertmacro MUI_PAGE_WELCOME
+!if "${PACKAGE_LICENSEFILE}" != ""
+ !insertmacro MUI_PAGE_LICENSE "${PACKAGE_LICENSEFILE}"
+!endif
+!insertmacro MUI_PAGE_COMPONENTS
+!insertmacro MUI_PAGE_DIRECTORY
+!insertmacro MUI_PAGE_INSTFILES
+!insertmacro MUI_PAGE_FINISH
+
+; add uninstall Pages
+!insertmacro MUI_UNPAGE_CONFIRM
+!insertmacro MUI_UNPAGE_INSTFILES
+!insertmacro MUI_UNPAGE_FINISH
+
+; add languages
+!insertmacro MUI_LANGUAGE "English"
+
+; set registry paths
+!define RegUninstall "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PACKAGE_NAME}"
+
+; set product information
+VIProductVersion "${VERSION}.0"
+VIFileVersion "${VERSION}.0"
+VIAddVersionKey /LANG=0 ProductName "${PACKAGE_NAME}"
+VIAddVersionKey /LANG=0 Comments "${PACKAGE_DESCRIPTION}"
+VIAddVersionKey /LANG=0 CompanyName "${PACKAGE_COMPANY}"
+VIAddVersionKey /LANG=0 LegalCopyright "${PACKAGE_COPYRIGHT}"
+VIAddVersionKey /LANG=0 FileDescription "${PACKAGE_NAME} Installer - v${VERSION}"
+VIAddVersionKey /LANG=0 OriginalFilename "${PACKAGE_FILENAME}"
+VIAddVersionKey /LANG=0 FileVersion "${VERSION_FULL}"
+VIAddVersionKey /LANG=0 ProductVersion "${VERSION_FULL}"
+
+; helper functions
+Function TrimQuote
+ Exch $R1 ; Original string
+ Push $R2
+
+Loop:
+ StrCpy $R2 "$R1" 1
+ StrCmp "$R2" "'" TrimLeft
+ StrCmp "$R2" "$\"" TrimLeft
+ StrCmp "$R2" "$\r" TrimLeft
+ StrCmp "$R2" "$\n" TrimLeft
+ StrCmp "$R2" "$\t" TrimLeft
+ StrCmp "$R2" " " TrimLeft
+ GoTo Loop2
+TrimLeft:
+ StrCpy $R1 "$R1" "" 1
+ Goto Loop
+
+Loop2:
+ StrCpy $R2 "$R1" 1 -1
+ StrCmp "$R2" "'" TrimRight
+ StrCmp "$R2" "$\"" TrimRight
+ StrCmp "$R2" "$\r" TrimRight
+ StrCmp "$R2" "$\n" TrimRight
+ StrCmp "$R2" "$\t" TrimRight
+ StrCmp "$R2" " " TrimRight
+ GoTo Done
+TrimRight:
+ StrCpy $R1 "$R1" -1
+ Goto Loop2
+
+Done:
+ Pop $R2
+ Exch $R1
+FunctionEnd
+
+; setup installer
+Var BinDir
+Var NoAdmin
+Function .onInit
+ ${GetOptions} $CMDLINE "/NOADMIN" $NoAdmin
+ ${If} ${Errors}
+ !insertmacro Init "installer"
+ StrCpy $NoAdmin "false"
+ ${Else}
+ StrCpy $NoAdmin "true"
+ ${EndIf}
+
+ ; get installation root directory
+ ${If} $InstDir == ""
+ ${If} $NoAdmin == "false"
+ ReadRegStr $R0 ${HKLM} ${RegUninstall} "InstallLocation"
+ ${Else}
+ ReadRegStr $R0 ${HKCU} ${RegUninstall} "InstallLocation"
+ ${EndIf}
+ ${If} $R0 != ""
+ Push $R0
+ Call TrimQuote
+ Pop $R0
+ StrCpy $InstDir $R0
+ ${EndIf}
+ ${EndIf}
+ ${If} $InstDir == ""
+ StrCpy $InstDir "${PROGRAMFILES}\${PACKAGE_NAME}"
+ ${EndIf}
+
+ ; get binary directory
+ StrCpy $BinDir "$InstDir\${PACKAGE_BINDIR}"
+
+FunctionEnd
+
+Section "${PACKAGE_NAME} (required)" InstallExeutable
+
+ SectionIn RO
+
+ ; set output path to the installation directory.
+ SetOutPath $InstDir
+
+ ; add install commands
+ ${PACKAGE_INSTALLCMDS}
+
+ ; add uninstaller
+ WriteUninstaller "uninstall.exe"
+ ; Write the uninstall keys for Windows
+ !macro AddReg RootKey
+ WriteRegStr ${RootKey} ${RegUninstall} "NoAdmin" "$NoAdmin"
+ WriteRegStr ${RootKey} ${RegUninstall} "DisplayName" "${PACKAGE_NAME} (${PACKAGE_ARCH})"
+ WriteRegStr ${RootKey} ${RegUninstall} "DisplayIcon" '"$InstDir\${PACKAGE_BINDIR}\${PACKAGE_FILENAME}"' ; TODO
+ WriteRegStr ${RootKey} ${RegUninstall} "Comments" "${PACKAGE_DESCRIPTION}"
+ WriteRegStr ${RootKey} ${RegUninstall} "Publisher" "${PACKAGE_COPYRIGHT}"
+ WriteRegStr ${RootKey} ${RegUninstall} "UninstallString" '"$InstDir\uninstall.exe"'
+ WriteRegStr ${RootKey} ${RegUninstall} "QuiteUninstallString" '"$InstDir\uninstall.exe" /S'
+ WriteRegStr ${RootKey} ${RegUninstall} "InstallLocation" $InstDir
+ WriteRegStr ${RootKey} ${RegUninstall} "HelpLink" "${PACKAGE_HOMEPAGE}"
+ WriteRegStr ${RootKey} ${RegUninstall} "URLInfoAbout" "${PACKAGE_HOMEPAGE}"
+ WriteRegStr ${RootKey} ${RegUninstall} "URLUpdateInfo" "${PACKAGE_HOMEPAGE}"
+ WriteRegDWORD ${RootKey} ${RegUninstall} "VersionMajor" ${PACKAGE_VERSION_MAJOR}
+ WriteRegDWORD ${RootKey} ${RegUninstall} "VersionMinor" ${PACKAGE_VERSION_MINOR}
+ WriteRegStr ${RootKey} ${RegUninstall} "DisplayVersion" ${VERSION_FULL}
+ WriteRegDWORD ${RootKey} ${RegUninstall} "NoModify" 1
+ WriteRegDWORD ${RootKey} ${RegUninstall} "NoRepair" 1
+
+ ; write size to reg
+ ${GetSize} "$InstDir" "/S=0K" $0 $1 $2
+ IntFmt $0 "0x%08X" $0
+ WriteRegDWORD ${RootKey} ${RegUninstall} "EstimatedSize" "$0"
+ !macroend
+
+ ${If} $NoAdmin == "false"
+ !insertmacro AddReg ${HKLM}
+ ${Else}
+ !insertmacro AddReg ${HKCU}
+ ${EndIf}
+SectionEnd
+
+; add the binary directory to %PATH%
+Section "Add to PATH" InstallPath
+ ${If} $NoAdmin == "false"
+ ReadRegStr $R0 ${HKLM} "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path"
+ ${WordReplace} $R0 ";$BinDir" "" "+" $R1
+ WriteRegExpandStr ${HKLM} "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path" "$R1;$BinDir"
+ ${Else}
+ ReadRegStr $R0 ${HKCU} "Environment" "Path"
+ ${WordReplace} $R0 ";$BinDir" "" "+" $R1
+ WriteRegExpandStr ${HKCU} "Environment" "Path" "$R1;$BinDir"
+ ${EndIf}
+ SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
+SectionEnd
+
+${PACKAGE_NSIS_INSTALL_SECTIONS}
+
+; define language strings
+LangString DESC_InstallExeutable ${LANG_ENGLISH} "${PACKAGE_DESCRIPTION}"
+LangString DESC_InstallPath ${LANG_ENGLISH} "Add ${PACKAGE_NAME} to PATH"
+${PACKAGE_NSIS_INSTALL_DESCS}
+
+; assign language strings to sections
+!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+!insertmacro MUI_DESCRIPTION_TEXT ${InstallExeutable} $(DESC_InstallExeutable)
+!insertmacro MUI_DESCRIPTION_TEXT ${InstallPath} $(DESC_InstallPath)
+${PACKAGE_NSIS_INSTALL_DESCRIPTION_TEXTS}
+!insertmacro MUI_FUNCTION_DESCRIPTION_END
+
+; setup uninstaller
+Function un.onInit
+ ; check if we need uac
+ ReadRegStr $NoAdmin ${HKLM} ${RegUninstall} "NoAdmin"
+ IfErrors 0 +2
+ ReadRegStr $NoAdmin ${HKCU} ${RegUninstall} "NoAdmin"
+
+ ${IfNot} $NoAdmin == "true"
+ !insertmacro Init "uninstaller"
+ ${EndIf}
+
+ ; get binary directory
+ StrCpy $BinDir "$InstDir\${PACKAGE_BINDIR}"
+
+FunctionEnd
+
+Section "Uninstall"
+
+ ; add uninstall commands
+ ${PACKAGE_UNINSTALLCMDS}
+
+ ; remove the binary directory from %Path%
+ ${If} $NoAdmin == "false"
+ DeleteRegKey ${HKLM} ${RegUninstall}
+ ReadRegStr $R0 ${HKLM} "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path"
+ ${WordReplace} $R0 ";$BinDir" "" "+" $R1
+ WriteRegExpandStr ${HKLM} "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path" "$R1"
+ ${Else}
+ DeleteRegKey ${HKCU} ${RegUninstall}
+ ReadRegStr $R0 ${HKCU} "Environment" "Path"
+ ${WordReplace} $R0 ";$BinDir" "" "+" $R1
+ WriteRegExpandStr ${HKCU} "Environment" "Path" "$R1"
+ ${EndIf}
+
+ ; remove uninstall.exe
+ Delete "$InstDir\uninstall.exe"
+SectionEnd
+