--!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, Xmake Open Source Community. -- -- @author ruki -- @file meson.lua -- -- imports import("core.base.option") import("core.project.config") import("core.platform.platform") import("lib.detect.find_file") import("lib.detect.find_tool") -- get build directory function _get_builddir() return config.builddir() or "build" end -- get artifacts directory function _get_artifacts_dir() return path.absolute(path.join(_get_builddir(), "artifacts")) end -- is cross compilation? function _is_cross_compilation() if not is_plat(os.subhost()) then return true end if is_plat("macosx") and not is_arch(os.subarch()) then return true end return false end -- get pkg-config function _get_pkgconfig() if is_plat("windows") then local pkgconf = find_tool("pkgconf") if pkgconf then return pkgconf.program end end local pkgconfig = find_tool("pkg-config") if pkgconfig then return pkgconfig.program end end -- get the build environment function _get_buildenv(key) local value = config.get(key) if value == nil then value = platform.toolconfig(key, config.plat()) end if value == nil then value = platform.tool(key, config.plat()) end return value end -- get cross file function _get_cross_file(builddir) local crossfile = path.join(builddir, "cross_file.txt") if not os.isfile(crossfile) then local file = io.open(crossfile, "w") -- binaries file:print("[binaries]") local cc = _get_buildenv("cc") if cc then -- we need to split it, maybe is `xcrun -sdk iphoneos clang` file:print("c=['%s']", table.concat(os.argv(cc), "', '")) end local cxx = _get_buildenv("cxx") if cxx then file:print("cpp=['%s']", table.concat(os.argv(cxx), "', '")) end local ld = _get_buildenv("ld") if ld then file:print("ld=['%s']", table.concat(os.argv(ld), "', '")) end local ar = _get_buildenv("ar") if ar then file:print("ar=['%s']", table.concat(os.argv(ar), "', '")) end local strip = _get_buildenv("strip") if strip then file:print("strip='%s'", strip) end local ranlib = _get_buildenv("ranlib") if ranlib then file:print("ranlib='%s'", ranlib) end if is_plat("mingw") then local mrc = _get_buildenv("mrc") if mrc then file:print("windres='%s'", mrc) end end local cmake = find_tool("cmake") if cmake then file:print("cmake='%s'", cmake.program) end local pkgconfig = _get_pkgconfig() if pkgconfig then file:print("pkgconfig='%s'", pkgconfig) end file:print("") -- built-in options file:print("[built-in options]") local cflags = table.join(table.wrap(_get_buildenv("cxflags")), _get_buildenv("cflags")) local cxxflags = table.join(table.wrap(_get_buildenv("cxflags")), _get_buildenv("cxxflags")) local asflags = table.wrap(_get_buildenv("asflags")) local arflags = table.wrap(_get_buildenv("arflags")) local ldflags = table.wrap(_get_buildenv("ldflags")) local shflags = table.wrap(_get_buildenv("shflags")) if #cflags > 0 then file:print("c_args=['%s']", table.concat(cflags, "', '")) end if #cxxflags > 0 then file:print("cpp_args=['%s']", table.concat(cxxflags, "', '")) end local linkflags = table.join(ldflags or {}, shflags) if #linkflags > 0 then file:print("c_link_args=['%s']", table.concat(linkflags, "', '")) file:print("cpp_link_args=['%s']", table.concat(linkflags, "', '")) end file:print("") -- host machine file:print("[host_machine]") if is_plat("iphoneos", "macosx") then local cpu local cpu_family if is_arch("arm64") then cpu = "aarch64" cpu_family = "aarch64" elseif is_arch("armv7") then cpu = "arm" cpu_family = "arm" elseif is_arch("x64", "x86_64") then cpu = "x86_64" cpu_family = "x86_64" elseif is_arch("x86", "i386") then cpu = "i686" cpu_family = "x86" else raise("unsupported arch(%s)", config.arch()) end file:print("system = 'darwin'") file:print("cpu_family = '%s'", cpu_family) file:print("cpu = '%s'", cpu) file:print("endian = 'little'") elseif is_plat("android") then -- TODO raise("android has been not supported now!") elseif is_plat("mingw") then local cpu local cpu_family if is_arch("x64", "x86_64") then cpu = "x86_64" cpu_family = "x86_64" elseif is_arch("x86", "i386") then cpu = "i686" cpu_family = "x86" else raise("unsupported arch(%s)", config.arch()) end file:print("system = 'windows'") file:print("cpu_family = '%s'", cpu_family) file:print("cpu = '%s'", cpu) file:print("endian = 'little'") elseif is_plat("wasm") then file:print("system = 'emscripten'") file:print("cpu_family = 'wasm32'") file:print("cpu = 'wasm32'") file:print("endian = 'little'") elseif is_plat("cross") then local cpu = config.arch() if is_arch("arm64") then cpu = "aarch64" elseif is_arch("arm.*") then cpu = "arm" end local cpu_family = cpu file:print("system = '%s'", get_config("target_os") or "linux") file:print("cpu_family = '%s'", cpu_family) file:print("cpu = '%s'", cpu) file:print("endian = 'little'") end file:print("") file:close() end return crossfile end -- get configs function _get_configs(artifacts_dir, builddir) -- add prefix local configs = {"--prefix=" .. artifacts_dir} if configfile then table.insert(configs, "--reconfigure") end -- add extra user configs local tryconfigs = config.get("tryconfigs") if tryconfigs then for _, opt in ipairs(os.argv(tryconfigs)) do table.insert(configs, tostring(opt)) end end -- add cross file if _is_cross_compilation() then table.insert(configs, "--cross-file=" .. _get_cross_file(builddir)) end -- add build directory table.insert(configs, builddir) return configs end -- detect build-system and configuration file function detect() return find_file("meson.build", os.curdir()) end -- do clean function clean() local builddir = _get_builddir() if os.isdir(builddir) then local configfile = find_file("build.ninja", builddir) if configfile then local ninja = assert(find_tool("ninja"), "ninja not found!") local ninja_argv = {"-C", builddir} if option.get("verbose") or option.get("diagnosis") then table.insert(ninja_argv, "-v") end table.insert(ninja_argv, "-t") table.insert(ninja_argv, "clean") os.vexecv(ninja.program, ninja_argv) if option.get("all") then os.tryrm(builddir) end end end end -- do build function build() -- get artifacts directory local artifacts_dir = _get_artifacts_dir() if not os.isdir(artifacts_dir) then os.mkdir(artifacts_dir) end -- generate makefile local builddir = _get_builddir() local meson = assert(find_tool("meson"), "meson not found!") local configfile = find_file("build.ninja", builddir) if not configfile or os.mtime(config.filepath()) > os.mtime(configfile) then os.vexecv(meson.program, _get_configs(artifacts_dir, builddir)) end -- do build local ninja = assert(find_tool("ninja"), "ninja not found!") local ninja_argv = {"-C", builddir} if option.get("verbose") then table.insert(ninja_argv, "-v") end table.insert(ninja_argv, "-j") table.insert(ninja_argv, option.get("jobs")) os.vexecv(ninja.program, ninja_argv) os.vexecv(ninja.program, table.join("install", ninja_argv)) cprint("output to ${bright}%s", artifacts_dir) cprint("${color.success}build ok!") end