diff options
Diffstat (limited to 'xmake/core/base/task.lua')
| -rw-r--r-- | xmake/core/base/task.lua | 498 |
1 files changed, 498 insertions, 0 deletions
diff --git a/xmake/core/base/task.lua b/xmake/core/base/task.lua new file mode 100644 index 000000000..6da3c26ee --- /dev/null +++ b/xmake/core/base/task.lua @@ -0,0 +1,498 @@ +--!The Make-like Build Utility based on Lua +-- +-- Licensed to the Apache Software Foundation (ASF) under one +-- or more contributor license agreements. See the NOTICE file +-- distributed with this work for additional information +-- regarding copyright ownership. The ASF licenses this file +-- to you 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 - 2017, TBOOX Open Source Group. +-- +-- @author ruki +-- @file task.lua +-- + +-- define module: task +local task = task or {} + +-- load modules +local os = require("base/os") +local table = require("base/table") +local utils = require("base/utils") +local string = require("base/string") +local global = require("base/global") +local interpreter = require("base/interpreter") +local sandbox = require("sandbox/sandbox") +local config = require("project/config") +local project = require("project/project") +local sandbox_os = require("sandbox/modules/os") + +-- the directories of tasks +function task._directories() + + return { path.join(global.directory(), "plugins") + , path.join(os.programdir(), "plugins") + , path.join(os.programdir(), "actions") + } +end + +-- translate menu +function task._translate_menu(menu) + + -- check + assert(menu) + + -- the interpreter + local interp = task._interpreter() + assert(interp) + + -- translate options + local options = menu.options + if options then + + -- make full options + local options_full = {} + for _, opt in ipairs(options) do + + -- this option is function? translate it + if type(opt) == "function" then + + -- call menu script in the sandbox + local ok, results = sandbox.load(opt) + if ok then + if results then + for _, opt in ipairs(results) do + table.insert(options_full, opt) + end + end + else + -- errors + return nil, string.format("taskmenu: %s", results) + end + else + table.insert(options_full, opt) + end + end + + -- update the options + options = options_full + menu.options = options_full + + -- filter options + if interp:filter() then + + -- filter option + for _, opt in ipairs(options) do + + -- filter default + local default = opt[4] + if type(default) == "string" then + opt[4] = interp:filter():handle(default) + end + + -- filter description + for i = 5, 64 do + + -- the description, @note some option may be nil + local description = opt[i] + if not description then break end + + -- the description is string? + if type(description) == "string" then + opt[i] = interp:filter():handle(description) + + -- the description is function? wrap it for calling it in the sandbox + elseif type(description) == "function" then + opt[i] = function () + + -- call it in the sandbox + local ok, results = sandbox.load(description) + if not ok then + -- errors + return nil, string.format("taskmenu: %s", results) + end + + -- ok + return results + end + end + end + end + end + + -- add common options + table.insert(options, 1, {'q', "quiet", "k", nil, "Quiet operation." }) + table.insert(options, 2, {'v', "verbose", "k", nil, "Print lots of verbose information." }) + table.insert(options, 3, {nil, "root", "k", nil, "Allow to run xmake as root." }) + table.insert(options, 4, {nil, "backtrace", "k", nil, "Print backtrace information for debugging." }) + table.insert(options, 5, {nil, "profile", "k", nil, "Print performance data for debugging." }) + table.insert(options, 6, {nil, "version", "k", nil, "Print the version number and exit." }) + table.insert(options, 7, {'h', "help", "k", nil, "Print this help message and exit." }) + table.insert(options, 8, {}) + table.insert(options, 9, {'F', "file", "kv", nil, "Read a given xmake.lua file." }) + table.insert(options, 10, {'P', "project", "kv", nil, "Change to the given project directory." + , "Search priority:" + , " 1. The Given Command Argument" + , " 2. The Envirnoment Variable: XMAKE_PROJECT_DIR" + , " 3. The Current Directory" }) + table.insert(options, 11, {}) + + end + + -- ok + return menu +end + +-- the interpreter +function task._interpreter() + + -- the interpreter has been initialized? return it directly + if task._INTERPRETER then + return task._INTERPRETER + end + + -- init interpreter + local interp = interpreter.new() + assert(interp) + + -- define apis + interp:api_define(task.apis()) + + -- set filter + interp:filter():register("task", function (variable) + + -- check + assert(variable) + + -- attempt to get it directly from the configure + local result = config.get(variable) + if not result or type(result) ~= "string" then + + -- init maps + local maps = + { + host = os.host() + , tmpdir = function () return os.tmpdir() end + , curdir = function () return os.curdir() end + , scriptdir = function () return sandbox_os.scriptdir() end + , globaldir = global.directory() + , configdir = config.directory() + , projectdir = os.projectdir() + , programdir = os.programdir() + } + + -- map it + result = maps[variable] + if type(result) == "function" then + result = result() + end + end + + -- ok? + return result + end) + + -- save interpreter + task._INTERPRETER = interp + + -- ok? + return interp +end + +-- bind tasks for menu with an sandbox instance +function task._bind(tasks, interp) + + -- check + assert(tasks) + + -- get interpreter + interp = interp or task._interpreter() + assert(interp) + + -- bind sandbox for menus + for _, taskinfo in pairs(tasks) do + + -- has menu? + if taskinfo.menu then + + -- translate options + local options = taskinfo.menu.options + if options then + + -- make full options + local options_full = {} + for _, opt in ipairs(options) do + + -- this option is function? translate it + if type(opt) == "function" then + + -- make sandbox instance with the given script + local instance, errors = sandbox.new(opt, interp:filter(), interp:rootdir()) + if not instance then + return false, errors + end + + -- update option script + opt = instance:script() + end + + -- insert option + table.insert(options_full, opt) + end + + -- update the options + options = options_full + taskinfo.menu.options = options_full + + -- bind sandbox for option description + for _, opt in ipairs(options) do + + -- bind description + if type(opt) == "table" then + for i = 5, 64 do + + -- the description, @note some option may be nil + local description = opt[i] + if not description then break end + + -- the description is function? wrap it for calling it in the sandbox + if type(description) == "function" then + + -- make sandbox instance with the given script + local instance, errors = sandbox.new(description, interp:filter(), interp:rootdir()) + if not instance then + return false, errors + end + + -- check + assert(instance:script()) + + -- update option script + opt[i] = instance:script() + end + end + end + end + end + end + end + + -- ok + return true +end + +-- load the given task script file +function task._load(filepath) + + -- get interpreter + local interp = task._interpreter() + assert(interp) + + -- load tasks + local tasks, errors = interp:load(filepath, "task", true, true) + if not tasks and os.isfile(filepath) then + return nil, errors + end + + -- bind tasks for menu with an sandbox instance + local ok, errors = task._bind(tasks) + if not ok then + return nil, errors + end + + -- ok? + return tasks +end + +-- get task apis +function task.apis() + + return + { + values = + { + -- task.set_xxx + "task.set_category" -- main, action, plugin, task (default) + , "task.set_menu" + } + , script = + { + -- task.on_xxx + "task.on_run" + } + } +end + +-- get all tasks +function task.tasks() + + -- return it directly if exists + if task._TASKS then + return task._TASKS + end + + -- load tasks + local tasks = {} + local dirs = task._directories() + for _, dir in ipairs(dirs) do + + -- get files + local files = os.match(path.join(dir, "**/xmake.lua")) + if files then + for _, filepath in ipairs(files) do + + -- load tasks + local results, errors = task._load(filepath) + + -- save tasks + if results then + table.join2(tasks, results) + else + return nil, errors + end + end + end + end + + -- merge project tasks if exists + local projectasks, errors = project.tasks() + if projectasks then + + -- the project interpreter + local interp = errors + + -- bind tasks for menu with an sandbox instance + local ok, errors = task._bind(projectasks, interp) + if not ok then + return nil, errors + end + + -- save tasks + for taskname, taskinfo in pairs(projectasks) do + if tasks[taskname] == nil then + tasks[taskname] = taskinfo + else + utils.warning("task(\"%s\") has been defined!", taskname) + end + end + else + return nil, errors + end + + -- save it + task._TASKS = tasks + + -- ok? + return tasks +end + +-- run task with given name +function task.run(name, ...) + + -- check + assert(name) + + -- load tasks + local tasks, errors = task.tasks() + if not tasks then + return false, errors + end + + -- the interpreter + local interp = task._interpreter() + assert(interp) + + -- get the task info + local taskinfo = tasks[name] + if not taskinfo then + return false, string.format("task(\"%s\"): unknown task", name) + end + + -- check + if not taskinfo.run then + return false, string.format("task(\"%s\"): no run script, please call on_task_run() first!", name) + end + + -- save the current directory + local curdir = os.curdir() + + -- run task + local ok, errors = sandbox.load(taskinfo.run, ...) + + -- restore the current directory + os.cd(curdir) + + -- ok? + return ok, errors +end + +-- the menu +function task.menu() + + -- load tasks + local tasks, errors = task.tasks() + if not tasks then + return nil, errors + end + + -- make menu + local menu = {} + for taskname, taskinfo in pairs(tasks) do + + -- has menu? + if taskinfo.menu then + + -- main? + if taskinfo.category == "main" then + + -- delay to load main menu + menu.main = function () + + -- translate main menu + local mainmenu = task._translate_menu(taskinfo.menu) + + -- make tasks for the main menu + mainmenu.tasks = {} + for name, info in pairs(tasks) do + + -- has menu? + if info.menu then + + -- add task + mainmenu.tasks[name] = + { + category = info.category + , shortname = info.menu.shortname + , description = info.menu.description + } + end + end + + -- ok + return mainmenu + end + end + + -- delay to load task menu + menu[taskname] = function () + return task._translate_menu(taskinfo.menu) + end + end + end + + -- ok? + return menu +end + +-- return module: task +return task |
