diff options
Diffstat (limited to 'xmake/core/tool/toolchain.lua')
| -rw-r--r-- | xmake/core/tool/toolchain.lua | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/xmake/core/tool/toolchain.lua b/xmake/core/tool/toolchain.lua new file mode 100644 index 000000000..e2220127e --- /dev/null +++ b/xmake/core/tool/toolchain.lua @@ -0,0 +1,383 @@ +--!A cross-toolchain 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-2020, TBOOX Open Source Group. +-- +-- @author ruki +-- @file toolchain.lua +-- + +-- define module +local toolchain = toolchain or {} +local _instance = _instance or {} + +-- load modules +local os = require("base/os") +local path = require("base/path") +local utils = require("base/utils") +local table = require("base/table") +local global = require("base/global") +local option = require("base/option") +local interpreter = require("base/interpreter") +local config = require("project/config") +local language = require("language/language") +local sandbox = require("sandbox/sandbox") +local sandbox_module = require("sandbox/modules/import/core/sandbox/module") + +-- new an instance +function _instance.new(name, info) + local instance = table.inherit(_instance) + instance._NAME = name + instance._INFO = info + return instance +end + +-- get toolchain name +function _instance:name() + return self._NAME +end + +-- get toolchain platform +function _instance:plat() + return config.get("plat") or os.host() +end + +-- get toolchain architecture +function _instance:arch() + return config.get("arch") or os.arch() +end + +-- get toolchain info +function _instance:info() + local arch = self:arch() + local infos = self._INFOS + if not infos then + infos = {} + self._INFOS = infos + end + local info = infos[arch] + if not info then + -- we need multiple info objects for different architectures + info = self._INFO:clone() + infos[arch] = info + end + return info +end + +-- set the value to the toolchain configuration +function _instance:set(name, ...) + self:info():apival_set(name, ...) +end + +-- add the value to the toolchain configuration +function _instance:add(name, ...) + self:info():apival_add(name, ...) +end + +-- get the toolchain configuration +function _instance:get(name) + + -- attempt to get the static configure value + local value = self:info():get(name) + if value ~= nil then + return value + end + + -- lazy loading platform + self:_load() + + -- get other platform info + return self:info():get(name) +end + +-- get toolchain kind +function _instance:kind() + return self:info():get("kind") +end + +-- is standalone toolchain? +function _instance:standalone() + return self:kind() == "standalone" +end + +-- get the program and name of the given tool kind +function _instance:tool(toolkind) + local toolpathes = self:get("toolsets." .. toolkind) + if toolpathes then + for _, toolpath in ipairs(table.wrap(toolpathes)) do + local program, toolname = self:_checktool(toolkind, toolpath) + if program then + return program, toolname + end + end + end +end + +-- get the toolchain script +function _instance:script(name) + return self:info():get(name) +end + +-- get the bin directory +function _instance:bindir() + return config.get("bin") or self:get("bindir") +end + +-- get the sdk directory +function _instance:sdkdir() + return config.get("sdk") or self:get("sdkdir") +end + +-- do load, @note we need load it repeatly for each architectures +function _instance:_load() + local info = self:info() + if not info:get("__loaded") and not info:get("__loading") then + local on_load = info:get("load") + if on_load then + info:set("__loading", true) + local ok, errors = sandbox.load(on_load, self) + info:set("__loading", false) + if not ok then + os.raise(errors) + end + end + info:set("__loaded", true) + end +end + +-- do check, we only check it once for all architectures +function _instance:check() + local checkok = true + if not self._CHECKED then + local on_check = self:info():get("check") + if on_check then + local ok, results_or_errors = sandbox.load(on_check, self) + if ok then + checkok = results_or_errors + else + os.raise(results_or_errors) + end + end + self._CHECKED = true + end + return checkok +end + +-- get the tool description from the tool kind +function _instance:_description(toolkind) + local descriptions = self._DESCRIPTIONS + if not descriptions then + descriptions = + { + cc = "the c compiler", + cxx = "the c++ compiler", + ld = "the linker", + sh = "the shared library linker", + ar = "the static library archiver", + ex = "the static library extractor", + strip = "the symbols stripper", + dsymutil = "the symbols generator", + mm = "the objc compiler", + mxx = "the objc++ compiler", + as = "the assember", + sc = "the swift compiler", + scld = "the swift linker", + scsh = "the swift shared library linker", + gc = "the golang compiler", + gcld = "the golang linker", + gcar = "the golang static library archiver", + dc = "the dlang compiler", + dcld = "the dlang linker", + dcsh = "the dlang shared library linker", + dcar = "the dlang static library archiver", + rc = "the rust compiler", + rcld = "the rust linker", + rcsh = "the rust shared library linker", + rcar = "the rust static library archiver", + cu = "the cuda compiler", + culd = "the cuda linker", + cuccbin = "the cuda host c++ compiler", + } + self._DESCRIPTIONS = descriptions + end + return descriptions[toolkind] +end + +-- check the given tool path +function _instance:_checktool(toolkind, toolpath) + + -- get find_tool + local find_tool = self._find_tool + if not find_tool then + find_tool = sandbox_module.import("lib.detect.find_tool", {anonymous = true}) + self._find_tool = find_tool + end + + -- do filter for toolpath variables, e.g. set_toolsets("cc", "$(env CC)") + local sandbox_inst = sandbox.instance() + if sandbox_inst then + local filter = sandbox_inst:filter() + if filter then + local value = filter:handle(toolpath) + if value and value:trim() ~= "" then + toolpath = value + else + return + end + end + end + + -- find tool program + local program, toolname + local tool = find_tool(toolpath, {program = toolpath, pathes = self:bindir()}) + if tool then + program = tool.program + toolname = tool.name + end + + -- get tool description from the tool kind + local description = self:_description(toolkind) or ("unknown toolkind " .. toolkind) + + -- trace + if option.get("verbose") then + if program then + utils.cprint("${dim}checking for %s (%s) ... ${color.success}%s", description, toolkind, path.filename(program)) + else + utils.cprint("${dim}checking for %s (%s: ${bright}%s${clear}) ... ${color.nothing}${text.nothing}", description, toolkind, toolpath) + end + end + return program, toolname +end + +-- the interpreter +function toolchain._interpreter() + + -- the interpreter has been initialized? return it directly + if toolchain._INTERPRETER then + return toolchain._INTERPRETER + end + + -- init interpreter + local interp = interpreter.new() + assert(interp) + + -- define apis + interp:api_define(toolchain.apis()) + + -- define apis for language + interp:api_define(language.apis()) + + -- save interpreter + toolchain._INTERPRETER = interp + + -- ok? + return interp +end + +-- get toolchain apis +function toolchain.apis() + return + { + values = + { + "toolchain.set_kind" + , "toolchain.set_bindir" + , "toolchain.set_sdkdir" + , "toolchain.set_archs" + } + , keyvalues = + { + -- toolchain.set_xxx + "toolchain.set_formats" + , "toolchain.set_toolsets" + -- toolchain.add_xxx + , "toolchain.add_runenvs" + } + , script = + { + -- toolchain.on_xxx + "toolchain.on_load" + , "toolchain.on_check" + } + } +end + +-- get toolchain directories +function toolchain.directories() + + -- init directories + local dirs = toolchain._DIRS or { path.join(global.directory(), "toolchains") + , path.join(os.programdir(), "toolchains") + } + + -- save directories to cache + toolchain._DIRS = dirs + return dirs +end + +-- load the given toolchain +function toolchain.load(name) + + -- get it directly from cache dirst + toolchain._TOOLCHAINS = toolchain._TOOLCHAINS or {} + if toolchain._TOOLCHAINS[name] then + return toolchain._TOOLCHAINS[name] + end + + -- find the toolchain script path + local scriptpath = nil + for _, dir in ipairs(toolchain.directories()) do + scriptpath = path.join(dir, name, "xmake.lua") + if os.isfile(scriptpath) then + break + end + end + if not scriptpath or not os.isfile(scriptpath) then + return nil, string.format("the toolchain %s not found!", name) + end + + -- get interpreter + local interp = toolchain._interpreter() + + -- load script + local ok, errors = interp:load(scriptpath) + if not ok then + return nil, errors + end + + -- load toolchain + local results, errors = interp:make("toolchain", true, false) + if not results and os.isfile(scriptpath) then + return nil, errors + end + + -- check the toolchain name + local result = results[name] + if not result then + return nil, string.format("the toolchain %s not found!", name) + end + + -- save instance to the cache + local instance = _instance.new(name, result) + toolchain._TOOLCHAINS[name] = instance + return instance +end + +-- new toolchain +function toolchain.new(name, info) + return _instance.new(name, info) +end + +-- return module +return toolchain |
