--!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 autoconf.lua -- -- imports import("core.base.option") import("core.project.config") import("core.platform.platform") import("lib.detect.find_file") import("lib.detect.find_tool") -- translate path function _translate_path(p) if p and is_host("windows") and is_plat("mingw") then p = p:gsub("\\", "/") end return p end -- translate windows bin path function _translate_windows_bin_path(bin_path) if bin_path then local argv = os.argv(bin_path) argv[1] = argv[1]:gsub("\\", "/") .. ".exe" return os.args(argv) end end -- get build directory function _get_buildir() return config.builddir() or "build" end -- get artifacts directory function _get_artifacts_dir() return path.absolute(path.join(_get_buildir(), "artifacts")) 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 -- 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 the build environments function _get_buildenvs() local envs = {} local cross = false if not _is_cross_compilation() then 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.copy(table.wrap(_get_buildenv("asflags"))) local ldflags = table.copy(table.wrap(_get_buildenv("ldflags"))) if is_plat("linux") and is_arch("i386") then table.insert(cflags, "-m32") table.insert(cxxflags, "-m32") table.insert(asflags, "-m32") table.insert(ldflags, "-m32") end envs.CFLAGS = table.concat(cflags, ' ') envs.CXXFLAGS = table.concat(cxxflags, ' ') envs.ASFLAGS = table.concat(asflags, ' ') envs.LDFLAGS = table.concat(ldflags, ' ') else cross = true local cflags = table.join(table.wrap(_get_buildenv("cxflags")), _get_buildenv("cflags")) local cxxflags = table.join(table.wrap(_get_buildenv("cxflags")), _get_buildenv("cxxflags")) envs.CC = _get_buildenv("cc") envs.AS = _get_buildenv("as") envs.AR = _get_buildenv("ar") envs.LD = _get_buildenv("ld") envs.LDSHARED = _get_buildenv("sh") envs.CPP = _get_buildenv("cpp") envs.RANLIB = _get_buildenv("ranlib") envs.CFLAGS = table.concat(cflags, ' ') envs.CXXFLAGS = table.concat(cxxflags, ' ') envs.ASFLAGS = table.concat(table.wrap(_get_buildenv("asflags")), ' ') envs.ARFLAGS = table.concat(table.wrap(_get_buildenv("arflags")), ' ') envs.LDFLAGS = table.concat(table.wrap(_get_buildenv("ldflags")), ' ') envs.SHFLAGS = table.concat(table.wrap(_get_buildenv("shflags")), ' ') end -- cross-compilation? pass the full build environments if cross then local ar = envs.AR if is_plat("mingw") then -- fix linker error, @see https://github.com/xmake-io/xmake/issues/574 -- libtool: line 1855: lib: command not found envs.ARFLAGS = nil local ld = envs.LD if ld then if ld:endswith("x86_64-w64-mingw32-g++") then envs.LD = path.join(path.directory(ld), is_host("windows") and "ld" or "x86_64-w64-mingw32-ld") elseif ld:endswith("i686-w64-mingw32-g++") then envs.LD = path.join(path.directory(ld), is_host("windows") and "ld" or "i686-w64-mingw32-ld") end end if is_host("windows") then envs.CC = _translate_windows_bin_path(envs.CC) envs.AS = _translate_windows_bin_path(envs.AS) envs.AR = _translate_windows_bin_path(envs.AR) envs.LD = _translate_windows_bin_path(envs.LD) envs.LDSHARED = _translate_windows_bin_path(envs.LDSHARED) envs.CPP = _translate_windows_bin_path(envs.CPP) envs.RANLIB = _translate_windows_bin_path(envs.RANLIB) end elseif is_plat("cross") or (ar and ar:find("ar")) then -- only for cross-toolchain envs.CXX = _get_buildenv("cxx") if not envs.ARFLAGS or envs.ARFLAGS == "" then envs.ARFLAGS = "-cr" end end -- we should use ld as linker -- -- @see -- https://github.com/xmake-io/xmake-repo/pull/1043 -- https://github.com/libexpat/libexpat/issues/312 -- https://github.com/xmake-io/xmake/issues/2195 local ld = envs.LD if ld then local dir = path.directory(ld) local name = path.filename(ld) name = name:gsub("clang%+%+$", "ld") name = name:gsub("clang%+%+%-%d+", "ld") name = name:gsub("clang$", "ld") name = name:gsub("clang%-%d+", "ld") name = name:gsub("gcc$", "ld") name = name:gsub("gcc-%d+", "ld") name = name:gsub("g%+%+$", "ld") name = name:gsub("g%+%+%-%d+", "ld") envs.LD = dir and path.join(dir, name) or name end -- we need to use clang++ as cxx, autoconf will use it as linker -- https://github.com/xmake-io/xmake/issues/2170 local cxx = envs.CXX if cxx then local dir = path.directory(cxx) local name = path.filename(cxx) name = name:gsub("clang$", "clang++") name = name:gsub("clang%-", "clang++-") name = name:gsub("gcc$", "g++") name = name:gsub("gcc%-", "g++-") envs.CXX = dir and path.join(dir, name) or name end end if option.get("verbose") then print(envs) end return envs end -- get configs function _get_configs(artifacts_dir) -- add prefix local configs = {} table.insert(configs, "--prefix=" .. _translate_path(artifacts_dir)) -- 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 host for cross-compilation if _is_cross_compilation() then if is_plat("iphoneos", "macosx") then local triples = { arm64 = "aarch64-apple-darwin", arm64e = "aarch64-apple-darwin", armv7 = "armv7-apple-darwin", armv7s = "armv7s-apple-darwin", i386 = "i386-apple-darwin", x86_64 = "x86_64-apple-darwin" } table.insert(configs, "--host=" .. (triples[config.arch()] or triples.arm64)) elseif is_plat("android") then -- @see https://developer.android.com/ndk/guides/other_build_systems#autoconf local triples = { ["armv5te"] = "arm-linux-androideabi", -- deprecated ["armv7-a"] = "arm-linux-androideabi", -- deprecated ["armeabi"] = "arm-linux-androideabi", -- removed in ndk r17 ["armeabi-v7a"] = "arm-linux-androideabi", ["arm64-v8a"] = "aarch64-linux-android", i386 = "i686-linux-android", -- deprecated x86 = "i686-linux-android", x86_64 = "x86_64-linux-android", mips = "mips-linux-android", -- removed in ndk r17 mips64 = "mips64-linux-android" -- removed in ndk r17 } table.insert(configs, "--host=" .. (triples[config.arch()] or triples["armeabi-v7a"])) elseif is_plat("mingw") then local triples = { i386 = "i686-w64-mingw32", x86_64 = "x86_64-w64-mingw32" } table.insert(configs, "--host=" .. (triples[config.arch()] or triples.i386)) elseif is_plat("linux") then local triples = { ["arm64-v8a"] = "aarch64-linux-gnu", arm64 = "aarch64-linux-gnu", i386 = "i686-linux-gnu", x86_64 = "x86_64-linux-gnu", armv7 = "arm-linux-gnueabihf", mips = "mips-linux-gnu", mips64 = "mips64-linux-gnu", mipsel = "mipsel-linux-gnu", mips64el = "mips64el-linux-gnu", loong64 = "loongarch64-linux-gnu" } table.insert(configs, "--host=" .. (triples[config.arch()] or triples.i386)) elseif is_plat("cross") then local host = config.arch() if is_arch("arm64") then host = "aarch64" elseif is_arch("arm.*") then host = "arm" end host = host .. "-" .. (get_config("target_os") or "linux") table.insert(configs, "--host=" .. host) else raise("autoconf: unknown platform(%s)!", config.plat()) end end return configs end -- detect build-system and configuration file function detect() if not is_subhost("windows") then return find_file("configure", os.curdir()) or find_file("configure.ac", os.curdir()) or find_file("autogen.sh", os.curdir()) end end -- do clean function clean() if find_file("[mM]akefile", os.curdir()) then if option.get("all") then os.vexec("make distclean") os.tryrm(_get_artifacts_dir()) else os.vexec("make clean") 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 configure if not os.isfile("configure") then if os.isfile("autogen.sh") then os.vexecv("./autogen.sh", {}, {shell = true}) elseif os.isfile("configure.ac") or os.isfile("configure.in") then local autoreconf = find_tool("autoreconf") assert(autoreconf, "autoreconf not found!") os.vexecv(autoreconf.program, {"--install", "--symlink"}, {shell = true}) end end -- do configure local configfile = find_file("[mM]akefile", os.curdir()) if not configfile or os.mtime(config.filepath()) > os.mtime(configfile) then os.vexecv("./configure", _get_configs(artifacts_dir), {shell = true, envs = _get_buildenvs()}) end -- do build local argv = {"-j" .. option.get("jobs")} if option.get("verbose") then table.insert(argv, "V=1") end if is_host("bsd") then os.vexecv("gmake", argv) os.vexec("gmake install") else os.vexecv("make", argv) os.vexec("make install") end cprint("output to ${bright}%s", artifacts_dir) cprint("${color.success}build ok!") end