new.lua 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. import("core.base.option")
  2. import("core.base.semver")
  3. import("core.base.json")
  4. import("core.base.hashset")
  5. import("lib.detect.find_tool")
  6. import("lib.detect.find_file")
  7. import("net.http")
  8. import("devel.git")
  9. import("utils.archive")
  10. local options = {
  11. {nil, "repo", "v", nil, "Set repository name.",
  12. "e.g. ",
  13. " - github:xmake-io/xmake",
  14. " - gitlab:xmake-io/xmake"}
  15. }
  16. -- function to get Gitlab data
  17. function get_gitlab_data(reponame)
  18. local glab = assert(find_tool("glab"), "glab not found!")
  19. local host = os.iorunv(glab.program, {"config", "get", "host"}):trim()
  20. local graphql_query = 'query={ project(fullPath: "' .. reponame .. '") { description webUrl sshUrlToRepo name } }'
  21. local repoinfo = os.iorunv(glab.program, {"api", "graphql", "-f", graphql_query})
  22. local data = {}
  23. if repoinfo then
  24. repoinfo = json.decode(repoinfo)
  25. if repoinfo.data and repoinfo.data.project then
  26. -- extract required data and restructure it
  27. local project_data = repoinfo.data.project
  28. data = {
  29. description = project_data.description,
  30. homepageUrl = project_data.webUrl,
  31. licenseInfo = "MIT", -- NOTE: Find a way to get the project license in gitlab
  32. url = project_data.webUrl,
  33. sshUrl = project_data.sshUrlToRepo,
  34. name = project_data.name,
  35. }
  36. repoinfo.data.project = data
  37. end
  38. end
  39. return {host = host, data = data}
  40. end
  41. local function get_github_data(reponame)
  42. local gh = assert(find_tool("gh"), "gh not found!")
  43. local host = "github.com"
  44. local data = os.iorunv(gh.program, {
  45. "repo",
  46. "view",
  47. reponame,
  48. "--json",
  49. "description,homepageUrl,licenseInfo,url,sshUrl,name,latestRelease",
  50. })
  51. if data then
  52. data = json.decode(data)
  53. end
  54. return {data = data, host = host}
  55. end
  56. local function get_license_spdx_id(key)
  57. local licenses = {
  58. ["apache-2.0"] = "Apache-2.0",
  59. ["lgpl-2.0"] = "LGPL-2.0",
  60. ["lgpl-2.1"] = "LGPL-2.1",
  61. ["agpl-3.0"] = "AGPL-3.0",
  62. ["bsd-2-clause"] = "BSD-2-Clause",
  63. ["bsd-3-clause"] = "BSD-3-Clause",
  64. ["bsl-1.0"] = "BSL-1.0",
  65. ["cc0-1.0"] = "CC0-1.0",
  66. ["epl-2.0"] = "EPL-2.0",
  67. ["gpl-2.0"] = "GPL-2.0",
  68. ["gpl-3.0"] = "GPL-3.0",
  69. ["mpl-2.0"] = "MPL-2.0",
  70. zlib = "zlib",
  71. mit = "MIT",
  72. }
  73. local license = licenses[key]
  74. if license then
  75. return license
  76. end
  77. local url = string.format("https://api.github.com/licenses/%s", key)
  78. local tmpfile = os.tmpfile({ramdisk = false})
  79. local ok = try { function () http.download(url, tmpfile); return true end }
  80. if not ok then
  81. os.tryrm(tmpfile)
  82. return nil
  83. end
  84. local license_detail = json.loadfile(tmpfile)
  85. license = license_detail["spdx_id"]
  86. os.tryrm(tmpfile)
  87. return license
  88. end
  89. function generate_package(reponame, get_data)
  90. local repo_data = get_data(reponame)
  91. local data = repo_data.data
  92. local host = repo_data.host
  93. -- generate package header
  94. local packagename = assert(data.name, "package name not found!"):lower()
  95. local packagefile = path.join("packages", string.sub(packagename, 1, 1), packagename, "xmake.lua")
  96. local file = io.open(packagefile, "w")
  97. -- define package and homepage
  98. file:print('package("%s")', packagename)
  99. local homepage = data.homepageUrl and data.homepageUrl ~= "" and data.homepageUrl or data.url
  100. if homepage then
  101. file:print(' set_homepage("%s")', homepage)
  102. end
  103. local description = data.description or ("The " .. packagename .. " package")
  104. file:print(' set_description("%s")', description)
  105. -- define license if available
  106. if type(data.licenseInfo) == "table" and data.licenseInfo.key then
  107. local license = get_license_spdx_id(data.licenseInfo.key)
  108. if license then
  109. file:print(' set_license("%s")', license)
  110. end
  111. end
  112. file:print("")
  113. -- define package URLs and versions
  114. local repodir
  115. local has_xmake, has_cmake, has_meson, has_bazel, has_autoconf, need_autogen
  116. local latest_release = data.latestRelease
  117. if type(latest_release) == "table" then
  118. local url = string.format("https://%s/%s/archive/refs/tags/%s.tar.gz", host, reponame, latest_release.tagName)
  119. local giturl = string.format("https://%s/%s.git", host, reponame)
  120. local tmpfile = os.tmpfile({ramdisk = false}) .. ".tar.gz"
  121. repodir = tmpfile .. ".dir"
  122. file:write(' add_urls("https://' .. host .. '/' .. reponame .. '/archive/refs/tags/$(version).tar.gz",\n')
  123. file:print(' "%s")\n', giturl)
  124. print("downloading %s", url)
  125. http.download(url, tmpfile)
  126. file:print(' add_versions("%s", "%s")', latest_release.tagName, hash.sha256(tmpfile))
  127. archive.extract(tmpfile, repodir)
  128. os.rm(tmpfile)
  129. else
  130. local giturl = string.format("https://%s/%s.git", host, reponame)
  131. repodir = os.tmpfile({ ramdisk = false })
  132. file:print(' add_urls("%s")', giturl)
  133. print("downloading %s", giturl)
  134. git.clone(giturl, { outputdir = repodir, depth = 1 })
  135. local commit = git.lastcommit({ repodir = repodir })
  136. local version = try {
  137. function()
  138. return os.iorunv("git", {
  139. "log",
  140. "-1",
  141. "--date=format:%Y.%m.%d",
  142. "--format=%ad",
  143. }, { curdir = repodir })
  144. end
  145. }
  146. if version then
  147. file:print(' add_versions("%s", "%s")', version:trim(), commit)
  148. end
  149. end
  150. local build_systems = {
  151. ["xmake.lua"] = {
  152. deps = {},
  153. priority = 1,
  154. install = function(configs, package)
  155. return [=[
  156. io.writefile("xmake.lua", [[
  157. add_rules("mode.release", "mode.debug")
  158. target("%s")
  159. set_kind("$(kind)")
  160. add_files("src/*.c")
  161. add_headerfiles("src/(*.h)")
  162. ]])
  163. if package:config("shared") then
  164. configs.kind = "shared"
  165. end
  166. import("package.tools.xmake").install(package, configs)]=]
  167. end,
  168. },
  169. ["CMakeLists.txt"] = {
  170. deps = {"cmake"},
  171. priority = 2,
  172. install = function(configs, package)
  173. return [[
  174. table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:is_debug() and "Debug" or "Release"))
  175. table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF"))
  176. import("package.tools.cmake").install(package, configs)]]
  177. end,
  178. },
  179. ["configure,configure.ac,autogen.sh"] = {
  180. deps = {"autoconf", "automake", "libtool"},
  181. priority = 3,
  182. install = function(configs, package)
  183. return [[
  184. table.insert(configs, "--enable-shared=" .. (package:config("shared") and "yes" or "no"))
  185. if package:is_debug() then
  186. table.insert(configs, "--enable-debug")
  187. end
  188. import("package.tools.autoconf").install(package, configs)]]
  189. end,
  190. },
  191. ["meson.build"] = {
  192. deps = {"meson", "ninja"},
  193. priority = 4,
  194. install = function(configs, package)
  195. return [[
  196. table.insert(configs, "-Ddefault_library=" .. (package:config("shared") and "shared" or "static"))
  197. import("package.tools.meson").install(package, configs)]]
  198. end,
  199. },
  200. ["BUILD,BUILD.bazel"] = {
  201. deps = {"bazel"},
  202. priority = 5,
  203. install = function(configs, package)
  204. return [[
  205. import("package.tools.bazel").install(package, configs)]]
  206. end,
  207. }
  208. }
  209. -- detect build system
  210. local build_system_detected = {}
  211. if repodir then
  212. local files = os.files(path.join(repodir, "*")) or {}
  213. table.join2(files, os.files(path.join(repodir, "*", "*")))
  214. for _, file in ipairs(files) do
  215. local filename = path.filename(file)
  216. for k, v in pairs(build_systems) do
  217. local filenames = hashset.from(k:split(","))
  218. if filenames:has(filename) then
  219. table.insert(build_system_detected, v)
  220. end
  221. end
  222. end
  223. os.rm(repodir)
  224. end
  225. local build_system
  226. if #build_system_detected > 0 then
  227. table.sort(build_system_detected, function (a, b) return a.priority < b.priority end)
  228. build_system = build_system_detected[1]
  229. end
  230. if not build_system then
  231. build_system = build_systems["xmake.lua"]
  232. end
  233. -- add dependencies
  234. if build_system then
  235. local deps = table.wrap(build_system.deps)
  236. if deps and #deps > 0 then
  237. file:print('')
  238. file:print(' add_deps("' .. table.concat(deps, '", "') .. '")')
  239. end
  240. end
  241. -- generate install scripts
  242. file:print('')
  243. file:print(' on_install(function (package)')
  244. file:print(' local configs = {}')
  245. if build_system then
  246. file:print(build_system.install(configs, package))
  247. end
  248. file:print(' end)')
  249. -- generate test scripts
  250. file:print('')
  251. file:print(' on_test(function (package)')
  252. file:print(' assert(package:has_cfuncs("foo", {includes = "foo.h"}))')
  253. file:print(' end)')
  254. file:close()
  255. io.cat(packagefile)
  256. cprint("${bright}%s generated!", packagefile)
  257. end
  258. function main(...)
  259. local opt = option.parse(table.pack(...), options, "New a package.", "", "Usage: xmake l scripts/new.lua [options]")
  260. local repo = assert(opt.repo, "repository name must be set!")
  261. local reponame = repo:sub(8)
  262. if repo:startswith("github:") then
  263. generate_package(reponame, get_github_data)
  264. return
  265. end
  266. if repo:startswith("gitlab:") then
  267. generate_package(reponame, get_gitlab_data)
  268. return
  269. end
  270. raise("unsupported repository source. only 'github' and 'gitlab' are supported.")
  271. end