After version 2.5.8, we can also directly integrate the source library with CMakeLists.txt in our project through the package mode of xmake, instead of downloading and installing it remotely.
Related issues: #1714
For example, we have the following project structure:
├── foo
│ ├── CMakeLists.txt
│ └── src
│ ├── foo.c
│ └── foo.h
├── src
│ └── main.c
├── test.lua
└── xmake.lua
The foo directory is a static library maintained by cmake, and the root directory is maintained by xmake. We can
define the package("foo")
package in xmake.lua to describe how to build the foo code
library.
add_rules("mode.debug", "mode.release")
package("foo")
add_deps("cmake")
set_sourcedir(path.join(os.scriptdir(), "foo"))
on_install(function (package)
local configs = {}
table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:debug() and "Debug" or "Release"))
table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF"))
import("package.tools.cmake").install(package, configs)
end)
on_test(function (package)
assert(package:has_cfuncs("add", {includes = "foo.h"}))
end)
package_end()
add_requires("foo")
target("demo")
set_kind("binary")
add_files("src/main.c")
add_packages("foo")
Among them, we set the code directory location of the foo package through set_sourcedir()
, and then
import the auxiliary module of package.tools.cmake
through import to call cmake to build the code,
xmake will automatically obtain the generated libfoo.a and the corresponding header document.
!> If only the local source code is integrated, we don't need to set additional add_urls
and
add_versions
.
For the configuration description of the package, see: Package description description.
After defining the package, we can integrate it with add_requires("foo")
and
add_packages("foo")
, just like integrating remote packages.
In addition, on_test
is optional. If you want to strictly check whether the package is compiled and
installed successfully, you can do some tests in it.
For a complete example, see: Library with CMakeLists
xmake supports the integration of more third-party source code libraries maintained by other build systems, such
as meson. You only need to import and use the package.tools.meson
auxiliary building module to call
meson to build them.
For example, we select a package built with meson from the xmake-repo repository as an example:
package("harfbuzz")
set_sourcedir(path.join(os.scriptdir(), "3rd/harfbuzz"))
add_deps("meson")
if not is_plat("windows") then
add_deps("freetype")
end
on_load("windows", "linux", "macosx", function (package)
if package:config("icu") then
package:add("deps", "icu4c")
end
end)
on_install("windows", "linux", "macosx", function (package)
local configs = {"-Dtests=disabled", "-Ddocs=disabled", "-Dbenchmark=disabled", "-Dcairo=disabled", "-Dfontconfig=disabled", "-Dglib=disabled", "-Dgobject= disabled"}
table.insert(configs, "-Ddefault_library=" .. (package:config("shared") and "shared" or "static"))
if package:is_plat("windows") then
table.insert(configs, "-Dfreetype=disabled")
end
import("package.tools.meson").install(package, configs)
end)
on_test(function (package)
assert(package:has_cfuncs("hb_buffer_add_utf8", {includes = "harfbuzz/hb.h"}))
end)
We can also use package.tools.autoconf
to locally integrate third-party code libraries maintained by
autoconf.
package("pcre2")
set_sourcedir(path.join(os.scriptdir(), "3rd/pcre2"))
add_configs("jit", {description = "Enable jit.", default = true, type = "boolean"})
add_configs("bitwidth", {description = "Set the code unit width.", default = "8", values = {"8", "16", "32"}})
on_load(function (package)
local bitwidth = package:config("bitwidth") or "8"
package:add("links", "pcre2-" .. bitwidth)
package:add("defines", "PCRE2_CODE_UNIT_WIDTH=" .. bitwidth)
if not package:config("shared") then
package:add("defines", "PCRE2_STATIC")
end
end)
on_install("macosx", "linux", "mingw", function (package)
local configs = {}
table.insert(configs, "--enable-shared=" .. (package:config("shared") and "yes" or "no"))
table.insert(configs, "--enable-static=" .. (package:config("shared") and "no" or "yes"))
if package:debug() then
table.insert(configs, "--enable-debug")
end
if package:config("pic") ~= false then
table.insert(configs, "--with-pic")
end
if package:config("jit") then
table.insert(configs, "--enable-jit")
end
local bitwidth = package:config("bitwidth") or "8"
if bitwidth ~= "8" then
table.insert(configs, "--disable-pcre2-8")
table.insert(configs, "--enable-pcre2-" .. bitwidth)
end
import("package.tools.autoconf").install(package, configs)
end)
on_test(function (package)
assert(package:has_cfuncs("pcre2_compile", {includes = "pcre2.h"}))
end)
Both package.tools.autoconf
and package.tools.cmake
modules can support
cross-compilation platforms and toolchains such as mingw/cross/iphoneos/android, xmake will automatically pass
the corresponding toolchain into it, and the user does not need to do Anything else.
We can also use package.tools.scons
to locally integrate third-party code libraries maintained by
Scons.
package("godotcpp")
set_sourcedir(path.join(os.scriptdir(), "3rd/godotcpp"))
add_deps("scons")
add_includedirs("include", "include/core", "include/gen")
on_install("linux", "windows", "macosx", "mingw", "cygwin", "iphoneos", "android", "msys", function (package)
local configs = {"generate_bindings=yes"}
table.insert(configs, "bits=" .. ((package:is_arch("x64") or package:is_arch("x86_64")) and "64" or "32"))
if package:is_plat("windows") then
io.replace("SConstruct", "/MD", "/" .. package:config("vs_runtime"), {plain = true})
end
- this fixes an error on ios and osx (https://godotengine.org/qa/65616/problems-compiling-gdnative-c-example-on-osx)
if package:is_plat("macosx", "iphoneos") then
io.replace("SConstruct", "-std=c++14", "-std=c++17", {plain = true})
end
- fix to use correct ranlib, @see https://github.com/godotengine/godot-cpp/issues/510
if package:is_plat("android") then
io.replace("SConstruct",
[[env['AR'] = toolchain + "/bin/" + arch_info['tool_path'] + "-ar"]],
[[env['AR'] = toolchain + "/bin/" + arch_info['tool_path'] + "-ar"
env['RANLIB'] = toolchain + "/bin/" + arch_info['tool_path'] + "-ranlib"]], {plain = true})
end
import("package.tools.scons").build(package, configs)
os.cp("bin/*." .. (package:is_plat("windows") and "lib" or "a"), package:installdir("lib"))
os.cp("include/core/*.hpp", package:installdir("include/core"))
os.cp("include/gen/*.hpp", package:installdir("include/gen"))
os.cp("godot-headers/android", package:installdir("include"))
os.cp("godot-headers/arvr", package:installdir("include"))
os.cp("godot-headers/gdnative", package:installdir("include"))
os.cp("godot-headers/nativescript", package:installdir("include"))
os.cp("godot-headers/net", package:installdir("include"))
os.cp("godot-headers/pluginscript", package:installdir("include"))
os.cp("godot-headers/videodecoder", package:installdir("include"))
os.cp("godot-headers/*.h", package:installdir("include"))
end)
We can also use package.tools.nmake
to locally integrate third-party code libraries maintained by
nmake.
nmake.install
will automatically bind the msvc build environment of the current user to ensure that
the user can successfully call nmake.exe, msbuild and cl.exe and other programs.
package("libxml2")
set_sourcedir(path.join(os.scriptdir(), "3rd/libxml2"))
add_includedirs("include/libxml2")
if is_plat("windows") then
add_syslinks("wsock32", "ws2_32")
end
on_load("windows", function (package)
if not package:config("shared") then
package:add("defines", "LIBXML_STATIC")
end
end)
on_install("windows", function (package)
os.cd("win32")
os.vrun("cscript configure.js iso8859x=yes iconv=no compiler=msvc cruntime=/%s debug=%s prefix=\"%s\"", package:config("vs_runtime"), package:debug( ) and "yes" or "no", package:installdir())
import("package.tools.nmake").install(package, {"/f", "Makefile.msvc"})
os.tryrm(path.join(package:installdir("lib"), "libxml2_a_dll.lib"))
if package:config("shared") then
os.tryrm(path.join(package:installdir("lib"), "libxml2_a.lib"))
else
os.tryrm(path.join(package:installdir("lib"), "libxml2.lib"))
os.tryrm(path.join(package:installdir("bin"), "libxml2.dll"))
end
end)
on_test(function (package)
assert(package:has_cfuncs("xmlNewNode", {includes = {"libxml/parser.h", "libxml/tree.h"}}))
end)
We can also use package.tools.make
to locally integrate third-party code libraries maintained by
gnumake.
package("openssl")
set_sourcedir(path.join(os.scriptdir(), "3rd/openssl"))
add_links("ssl", "crypto")
if is_plat("linux", "cross") then
add_syslinks("dl")
end
on_install("linux", "macosx", function (package)
- https://wiki.openssl.org/index.php/Compilation_and_Installation#PREFIX_and_OPENSSLDIR
os.vrun("./config %s --openssldir=\"%s\" --prefix=\"%s\"", package:debug() and "--debug" or "", package:installdir (), package:installdir())
import("package.tools.make").install(package)
end)
on_test(function (package)
assert(package:has_cfuncs("SSL_new", {includes = "openssl/ssl.h"}))
end)
!> We can also directly use os.vrunv("make", {})
to call the make/gmake program to
build the library.
We can also use package.tools.gn
to natively integrate third-party code maintained by GN.
package("skia")
set_sourcedir(path.join(os.scriptdir(), "3rd/openssl"))
add_deps("gn", "ninja")
add_deps("python", {kind = "binary"})
on_install("linux", "macosx", "windows", function (package)
import("package.tools.gn").install(package)
end)
on_test(function (package)
-- TODO
end)
Here is the complete script example: Skia with GN