diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bbf331d05d..41f3ed7dcef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### New features * [#4637](https://github.com/xmake-io/xmake/issues/4637): Add mix generator for xpack +* [#5107](https://github.com/xmake-io/xmake/issues/5107): Add deb generator for xpack * [#5148](https://github.com/xmake-io/xmake/issues/5148): Add on_source in package ### Changes @@ -1828,6 +1829,7 @@ ### 新特性 * [#4637](https://github.com/xmake-io/xmake/issues/4637): 为 xpack 添加 mix 支持 +* [#5107](https://github.com/xmake-io/xmake/issues/5107): 为 xpack 添加 deb 支持 * [#5148](https://github.com/xmake-io/xmake/issues/5148): 为包添加 on_source 配置域 ### 改进 diff --git a/core/xpack.lua b/core/xpack.lua index 1a204e6b63f..e4999775ad1 100644 --- a/core/xpack.lua +++ b/core/xpack.lua @@ -3,7 +3,7 @@ xpack("xmake") 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_author("waruqi@gmail.com") + set_author("ruki ") set_licensefile("../LICENSE.md") set_formats("nsis", "wix", "zip") add_targets("demo") @@ -81,8 +81,8 @@ xpack("xmakesrc") 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_author("waruqi@gmail.com") - set_formats("srczip", "srctargz", "runself", "srpm") + set_author("ruki ") + set_formats("srczip", "srctargz", "runself", "srpm", "deb") set_basename("xmake-v$(version)") set_prefixdir("xmake-$(version)") set_license("Apache-2.0") @@ -115,7 +115,7 @@ xpack("xmakesrc") on_buildcmd(function (package, batchcmds) local format = package:format() - if format == "srpm" then + if format == "srpm" or format == "deb" then batchcmds:runv("./configure") batchcmds:runv("make") end @@ -125,7 +125,7 @@ xpack("xmakesrc") local format = package:format() if format == "runself" then batchcmds:runv("./scripts/get.sh", {"__local__"}) - elseif format == "srpm" then + elseif format == "srpm" or format == "deb" then batchcmds:runv("make", {"install", path(package:install_rootdir(), function (p) return "PREFIX=" .. p end)}) end end) diff --git a/tests/plugins/pack/xmake.lua b/tests/plugins/pack/xmake.lua index 437ae8a3714..cf07d8ca619 100644 --- a/tests/plugins/pack/xmake.lua +++ b/tests/plugins/pack/xmake.lua @@ -19,9 +19,9 @@ target("foo") add_packages("zlib") xpack("test") - set_formats("nsis", "srpm", "rpm", "zip", "targz", "srczip", "srctargz", "runself", "wix") + set_formats("nsis", "srpm", "rpm", "deb", "zip", "targz", "srczip", "srctargz", "runself", "wix") set_title("hello") - set_author("ruki") + set_author("ruki ") set_description("A test installer.") set_homepage("https://xmake.io") set_license("Apache-2.0") @@ -34,7 +34,7 @@ xpack("test") add_components("LongPath") on_load(function (package) - if package:from_source() then + if package:with_source() then package:set("basename", "test-$(plat)-src-v$(version)") else package:set("basename", "test-$(plat)-$(arch)-v$(version)") diff --git a/xmake/plugins/pack/batchcmds.lua b/xmake/plugins/pack/batchcmds.lua index 3d9229ccefa..8091134b2b5 100644 --- a/xmake/plugins/pack/batchcmds.lua +++ b/xmake/plugins/pack/batchcmds.lua @@ -186,13 +186,13 @@ end -- on install source target command function _on_target_installcmd_source(target, batchcmds_, opt) local package = opt.package - batchcmds_:vrunv("xmake", {"install", "-o", path(package:install_rootdir()), target:name()}) + batchcmds_:vrunv("xmake", {"install", "-P", ".", "-y", "-o", path(package:install_rootdir()), target:name()}) end -- on build target command function _on_target_buildcmd(target, batchcmds_, opt) local package = opt.package - batchcmds_:vrunv("xmake", {"build", "-y", target:name()}) + batchcmds_:vrunv("xmake", {"build", "-P", ".", "-y", target:name()}) end -- on install target command diff --git a/xmake/plugins/pack/deb/main.lua b/xmake/plugins/pack/deb/main.lua new file mode 100644 index 00000000000..680531ff62e --- /dev/null +++ b/xmake/plugins/pack/deb/main.lua @@ -0,0 +1,259 @@ +--!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("core.base.hashset") +import("lib.detect.find_tool") +import("lib.detect.find_file") +import("utils.archive") +import(".batchcmds") + +-- get the debuild +function _get_debuild() + local debuild = find_tool("debuild", {force = true}) + assert(debuild, "debuild not found!") + return debuild +end + +-- get archive file +function _get_archivefile(package) + return path.absolute(path.join(path.directory(package:sourcedir()), package:name() .. "_" .. package:version() .. ".orig.tar.gz")) +end + +-- translate the file path +function _translate_filepath(package, filepath) + return filepath:replace(package:install_rootdir(), "$(PREFIX)", {plain = true}) +end + +-- get install command +function _get_customcmd(package, installcmds, cmd) + local opt = cmd.opt or {} + local kind = cmd.kind + if kind == "cp" then + local srcfiles = os.files(cmd.srcpath) + for _, srcfile in ipairs(srcfiles) do + -- the destination is directory? append the filename + local dstfile = _translate_filepath(package, cmd.dstpath) + if #srcfiles > 1 or 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 + table.insert(installcmds, string.format("install -Dpm0644 \"%s\" \"%s\"", srcfile, dstfile)) + end + elseif kind == "rm" then + local filepath = _translate_filepath(package, cmd.filepath) + table.insert(installcmds, string.format("rm -f \"%s\"", filepath)) + elseif kind == "rmdir" then + local dir = _translate_filepath(package, cmd.dir) + table.insert(installcmds, string.format("rm -rf \"%s\"", dir)) + elseif kind == "mv" then + local srcpath = _translate_filepath(package, cmd.srcpath) + local dstpath = _translate_filepath(package, cmd.dstpath) + table.insert(installcmds, string.format("mv \"%s\" \"%s\"", srcfile, dstfile)) + elseif kind == "cd" then + local dir = _translate_filepath(package, cmd.dir) + table.insert(installcmds, string.format("cd \"%s\"", dir)) + elseif kind == "mkdir" then + local dir = _translate_filepath(package, cmd.dir) + table.insert(installcmds, string.format("mkdir -p \"%s\"", dir)) + elseif cmd.program then + local argv = {} + for _, arg in ipairs(cmd.argv) do + if path.instance_of(arg) then + arg = arg:clone():set(_translate_filepath(package, arg:rawstr())):str() + elseif path.is_absolute(arg) then + arg = _translate_filepath(package, arg) + end + table.insert(argv, arg) + end + table.insert(installcmds, string.format("%s", os.args(table.join(cmd.program, argv)))) + end +end + +-- get build commands +function _get_buildcmds(package, buildcmds, cmds) + for _, cmd in ipairs(cmds) do + _get_customcmd(package, buildcmds, cmd) + end +end + +-- get install commands +function _get_installcmds(package, installcmds, cmds) + for _, cmd in ipairs(cmds) do + _get_customcmd(package, installcmds, cmd) + end +end + +-- get uninstall commands +function _get_uninstallcmds(package, uninstallcmds, cmds) + for _, cmd in ipairs(cmds) do + _get_customcmd(package, uninstallcmds, cmd) + end +end + +-- get specvars +function _get_specvars(package) + local specvars = table.clone(package:specvars()) + local datestr = os.iorunv("date", {"-u", "+%a, %d %b %Y %H:%M:%S +0000"}, {envs = {LC_TIME = "en_US"}}) + if datestr then + datestr = datestr:trim() + end + specvars.PACKAGE_DATE = datestr or "" + local author = package:get("author") or "unknown " + specvars.PACKAGE_COPYRIGHT = os.date("%Y") .. " " .. author + specvars.PACKAGE_INSTALLCMDS = function () + local prefixdir = package:get("prefixdir") + package:set("prefixdir", nil) + local installcmds = {} + _get_installcmds(package, installcmds, batchcmds.get_installcmds(package):cmds()) + for _, component in table.orderpairs(package:components()) do + if component:get("default") ~= false then + _get_installcmds(package, installcmds, batchcmds.get_installcmds(component):cmds()) + end + end + package:set("prefixdir", prefixdir) + return table.concat(installcmds, "\n\t") + end + specvars.PACKAGE_UNINSTALLCMDS = function () + local uninstallcmds = {} + _get_uninstallcmds(package, uninstallcmds, batchcmds.get_uninstallcmds(package):cmds()) + for _, component in table.orderpairs(package:components()) do + if component:get("default") ~= false then + _get_uninstallcmds(package, uninstallcmds, batchcmds.get_uninstallcmds(component):cmds()) + end + end + return table.concat(uninstallcmds, "\n\t") + end + specvars.PACKAGE_BUILDCMDS = function () + local buildcmds = {} + _get_buildcmds(package, buildcmds, batchcmds.get_buildcmds(package):cmds()) + return table.concat(buildcmds, "\n\t") + end + specvars.PACKAGE_BUILDREQUIRES = function () + local requires = {} + local buildrequires = package:get("buildrequires") + if buildrequires then + for _, buildrequire in ipairs(buildrequires) do + table.insert(requires, buildrequire) + end + else + local programs = hashset.new() + for _, cmd in ipairs(batchcmds.get_buildcmds(package):cmds()) do + local program = cmd.program + if program then + programs:insert(program) + end + end + local map = { + xmake = "xmake", + cmake = "cmake", + make = "make" + } + for _, program in programs:keys() do + local requirename = map[program] + if requirename then + table.insert(requires, requirename) + end + end + end + return table.concat(requires, ", ") + end + return specvars +end + +-- pack deb package +function _pack_deb(debuild, package) + + -- install the initial debian directory + local sourcedir = package:sourcedir() + local debiandir = path.join(sourcedir, "debian") + if not os.isdir(debiandir) then + local debiandir_template = package:get("specfile") or path.join(os.programdir(), "scripts", "xpack", "deb", "debian") + os.cp(debiandir_template, debiandir) + end + + -- replace variables in specfile + local specvars = _get_specvars(package) + local pattern = package:extraconf("specfile", "pattern") or "%${([^\n]-)}" + for _, specfile in ipairs(os.files(path.join(debiandir, "**"))) do + 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("[%s]: > replace %s -> %s", path.filename(specfile), name, value) + end + if type(value) == "table" then + dprint("invalid variable value", value) + end + return value + end) + end + + -- archive source files + local srcfiles, dstfiles = package:sourcefiles() + for idx, srcfile in ipairs(srcfiles) do + os.vcp(srcfile, dstfiles[idx]) + end + for _, component in table.orderpairs(package:components()) do + if component:get("default") ~= false then + local srcfiles, dstfiles = component:sourcefiles() + for idx, srcfile in ipairs(srcfiles) do + os.vcp(srcfile, dstfiles[idx]) + end + end + end + + -- archive install files + local rootdir = package:source_rootdir() + local oldir = os.cd(rootdir) + local archivefiles = os.files("**") + os.cd(oldir) + local archivefile = _get_archivefile(package) + os.tryrm(archivefile) + archive.archive(archivefile, archivefiles, {curdir = rootdir, compress = "best"}) + + -- build package + os.vrunv(debuild, {"-us", "-uc"}, {curdir = sourcedir}) + + -- copy deb file + os.vcp(path.join(path.directory(sourcedir), "*.deb"), package:outputfile()) +end + +function main(package) + if not is_host("linux") then + return + end + + cprint("packing %s", package:outputfile()) + + -- get debuild + local debuild = _get_debuild() + + -- pack deb package + _pack_deb(debuild.program, package) +end diff --git a/xmake/plugins/pack/nsis/main.lua b/xmake/plugins/pack/nsis/main.lua index b8f6d4dc494..c24c718feff 100644 --- a/xmake/plugins/pack/nsis/main.lua +++ b/xmake/plugins/pack/nsis/main.lua @@ -250,9 +250,9 @@ end function _pack_nsis(makensis, package) -- install the initial specfile - local specfile = package:specfile() + local specfile = path.join(package:buildir(), package:basename() .. ".nsi") if not os.isfile(specfile) then - local specfile_template = path.join(os.programdir(), "scripts", "xpack", "nsis", "makensis.nsi") + local specfile_template = package:get("specfile") or path.join(os.programdir(), "scripts", "xpack", "nsis", "makensis.nsi") os.cp(specfile_template, specfile) end diff --git a/xmake/plugins/pack/runself/main.lua b/xmake/plugins/pack/runself/main.lua index 8f25c3e80b7..eb5c01fb1eb 100644 --- a/xmake/plugins/pack/runself/main.lua +++ b/xmake/plugins/pack/runself/main.lua @@ -108,9 +108,9 @@ end function _pack_runself(makeself, package) -- install the initial specfile - local specfile = package:specfile() + local specfile = path.join(package:buildir(), package:basename() .. ".lsm") if not os.isfile(specfile) then - local specfile_template = path.join(os.programdir(), "scripts", "xpack", "runself", "makeself.lsm") + local specfile_template = package:get("specfile") or path.join(os.programdir(), "scripts", "xpack", "runself", "makeself.lsm") os.cp(specfile_template, specfile) end diff --git a/xmake/plugins/pack/srpm/main.lua b/xmake/plugins/pack/srpm/main.lua index b6b5467c8dd..e8630ba58c0 100644 --- a/xmake/plugins/pack/srpm/main.lua +++ b/xmake/plugins/pack/srpm/main.lua @@ -202,9 +202,9 @@ function _pack_srpm(rpmbuild, package) end -- install the initial specfile - local specfile = package:specfile() + local specfile = path.join(package:buildir(), package:basename() .. ".spec") if not os.isfile(specfile) then - local specfile_template = path.join(os.programdir(), "scripts", "xpack", "srpm", "srpm.spec") + local specfile_template = package:get("specfile") or path.join(os.programdir(), "scripts", "xpack", "srpm", "srpm.spec") os.cp(specfile_template, specfile) end diff --git a/xmake/plugins/pack/wix/main.lua b/xmake/plugins/pack/wix/main.lua index 95b6a4a7f62..a99ecdb6e1d 100644 --- a/xmake/plugins/pack/wix/main.lua +++ b/xmake/plugins/pack/wix/main.lua @@ -258,9 +258,9 @@ end function _pack_wix(wix, package) -- install the initial specfile - local specfile = package:specfile() + local specfile = path.join(package:buildir(), package:basename() .. ".wxs") if not os.isfile(specfile) then - local specfile_template = path.join(os.programdir(), "scripts", "xpack", "wix", "msi.wxs") + local specfile_template = package:get("specfile") or path.join(os.programdir(), "scripts", "xpack", "wix", "msi.wxs") os.cp(specfile_template, specfile) end diff --git a/xmake/plugins/pack/xpack.lua b/xmake/plugins/pack/xpack.lua index 8af36cdd900..89ee5a54bd6 100644 --- a/xmake/plugins/pack/xpack.lua +++ b/xmake/plugins/pack/xpack.lua @@ -206,6 +206,24 @@ function xpack:inputkind() return inputkind end +-- get the output kind +function xpack:outputkind() + local outputkinds = { + wix = "binary", + nsis = "binary", + zip = "binary", + targz = "binary", + srczip = "source", + srctargz = "source", + runself = "source", + deb = "binary", + srpm = "source", + rpm = "binary" + } + local outputkind = outputkinds[self:format()] or "binary" + return outputkind +end + -- pack from source files? function xpack:from_source() return self:inputkind() == "source" @@ -216,6 +234,16 @@ function xpack:from_binary() return self:inputkind() == "binary" end +-- pack with source files? +function xpack:with_source() + return self:outputkind() == "source" +end + +-- pack with binary files? +function xpack:with_binary() + return self:outputkind() == "binary" +end + -- get the build directory function xpack:buildir() return path.join(config.buildir(), ".xpack", self:name()) @@ -235,7 +263,7 @@ function xpack:basename() local basename = option.get("basename") or self:get("basename") if basename == nil then basename = self:name() - if self:from_source() then + if self:with_source() then basename = basename .. "-src" end local version = self:version() @@ -319,19 +347,6 @@ function xpack:specvars() return specvars end --- get the specfile path -function xpack:specfile() - local extensions = { - wix = ".wxs", - nsis = ".nsi", - srpm = ".spec", - rpm = ".spec", - runself = ".lsm" - } - local extension = extensions[self:format()] or ".spec" - return self:get("specfile") or path.join(self:buildir(), self:basename() .. extension) -end - -- get the extension function xpack:extension() local extension = self:get("extension") diff --git a/xmake/scripts/xpack/deb/debian/README.Debian b/xmake/scripts/xpack/deb/debian/README.Debian new file mode 100644 index 00000000000..39c70ba801c --- /dev/null +++ b/xmake/scripts/xpack/deb/debian/README.Debian @@ -0,0 +1,6 @@ +${PACKAGE_NAME} for Debian +--------------- + + + + -- ${PACKAGE_MAINTAINER} ${PACKAGE_DATE} diff --git a/xmake/scripts/xpack/deb/debian/changelog b/xmake/scripts/xpack/deb/debian/changelog new file mode 100644 index 00000000000..f583d964f82 --- /dev/null +++ b/xmake/scripts/xpack/deb/debian/changelog @@ -0,0 +1,5 @@ +${PACKAGE_NAME} (${PACKAGE_VERSION}-1) UNRELEASED; urgency=medium + + * Initial release (Closes: #nnnn) + + -- ${PACKAGE_MAINTAINER} ${PACKAGE_DATE} diff --git a/xmake/scripts/xpack/deb/debian/control b/xmake/scripts/xpack/deb/debian/control new file mode 100644 index 00000000000..13e634b131a --- /dev/null +++ b/xmake/scripts/xpack/deb/debian/control @@ -0,0 +1,15 @@ +Source: ${PACKAGE_NAME} +Section: devel +Priority: optional +Maintainer: ${PACKAGE_MAINTAINER} +Build-Depends: debhelper-compat (= 13) +Build-Depends-Arch: ${PACKAGE_BUILDREQUIRES} +Standards-Version: 4.6.0 +Homepage: ${PACKAGE_HOMEPAGE} +Rules-Requires-Root: no + +Package: ${PACKAGE_NAME} +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: ${PACKAGE_TITLE} + ${PACKAGE_DESCRIPTION} diff --git a/xmake/scripts/xpack/deb/debian/copyright b/xmake/scripts/xpack/deb/debian/copyright new file mode 100644 index 00000000000..0056dbd39ed --- /dev/null +++ b/xmake/scripts/xpack/deb/debian/copyright @@ -0,0 +1,10 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: ${PACKAGE_NAME} +Upstream-Contact: ${PACKAGE_AUTHOR} +Source: ${PACKAGE_HOMEPAGE} + +Files: * +Copyright: ${PACKAGE_COPYRIGHT} +License: ${PACKAGE_LICENSE} + . + diff --git a/xmake/scripts/xpack/deb/debian/rules b/xmake/scripts/xpack/deb/debian/rules new file mode 100755 index 00000000000..50b1f377a75 --- /dev/null +++ b/xmake/scripts/xpack/deb/debian/rules @@ -0,0 +1,60 @@ +#!/usr/bin/make -f + +#export DH_VERBOSE = 1 + +PREFIX=$(CURDIR)/debian/${PACKAGE_NAME}/usr + +configure: configure-stamp +configure-stamp: + dh_testdir + touch configure-stamp + +build-arch: + # pass + +build-indep: + # pass + +build: build-stamp +build-stamp: configure-stamp + dh_testdir + ${PACKAGE_BUILDCMDS} + touch $@ + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + dh_clean + +install: build + dh_testdir + dh_testroot + dh_installdirs + ${PACKAGE_INSTALLCMDS} + +distclean: clean + +uninstall: + ${PACKAGE_UNINSTALLCMDS} + +binary-indep: build install +# We have nothing to do by default. +# # Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installdocs + dh_installexamples + dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install uninstall configure diff --git a/xmake/scripts/xpack/deb/debian/source/format b/xmake/scripts/xpack/deb/debian/source/format new file mode 100644 index 00000000000..163aaf8d82b --- /dev/null +++ b/xmake/scripts/xpack/deb/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt)