diff options
| author | ruki <[email protected]> | 2022-09-28 20:42:39 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-09-28 20:42:39 +0800 |
| commit | 73c22237fb886379beb347fd9a2908e31f82ee9c (patch) | |
| tree | 14715c2bbc90cc75f84994be127f77058be8795f | |
| parent | 575706825c48cfb9af0d22cc88ff4f826a917455 (diff) | |
| parent | 82bfc71e7810157e8f71d43d4a2d82b609c17bf9 (diff) | |
Merge pull request #2874 from xmake-io/rule
Support for dynamic creation and injection of rules and targets
| -rw-r--r-- | tests/apis/clone_target/src/main.cpp | 9 | ||||
| -rw-r--r-- | tests/apis/clone_target/test.lua | 3 | ||||
| -rw-r--r-- | tests/apis/clone_target/xmake.lua | 21 | ||||
| -rw-r--r-- | tests/apis/rules_inject_deps/src/main.cpp | 9 | ||||
| -rw-r--r-- | tests/apis/rules_inject_deps/src/main.cpp2 | 0 | ||||
| -rw-r--r-- | tests/apis/rules_inject_deps/test.lua | 3 | ||||
| -rw-r--r-- | tests/apis/rules_inject_deps/xmake.lua | 23 | ||||
| -rw-r--r-- | tests/apis/rules_override_cxx/src/main.xx | 6 | ||||
| -rw-r--r-- | tests/apis/rules_override_cxx/src/test.cc | 0 | ||||
| -rw-r--r-- | tests/apis/rules_override_cxx/test.lua | 3 | ||||
| -rw-r--r-- | tests/apis/rules_override_cxx/xmake.lua | 34 | ||||
| -rw-r--r-- | xmake/actions/build/kinds/object.lua | 17 | ||||
| -rw-r--r-- | xmake/core/base/private/instance_deps.lua | 77 | ||||
| -rw-r--r-- | xmake/core/base/scopeinfo.lua | 2 | ||||
| -rw-r--r-- | xmake/core/base/table.lua | 14 | ||||
| -rw-r--r-- | xmake/core/project/project.lua | 151 | ||||
| -rw-r--r-- | xmake/core/project/rule.lua | 277 | ||||
| -rw-r--r-- | xmake/core/project/target.lua | 92 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/core/project/project.lua | 1 |
19 files changed, 505 insertions, 237 deletions
diff --git a/tests/apis/clone_target/src/main.cpp b/tests/apis/clone_target/src/main.cpp new file mode 100644 index 000000000..db2361659 --- /dev/null +++ b/tests/apis/clone_target/src/main.cpp @@ -0,0 +1,9 @@ +#include <iostream> + +using namespace std; + +int main(int argc, char** argv) +{ + cout << "hello world!" << endl; + return 0; +} diff --git a/tests/apis/clone_target/test.lua b/tests/apis/clone_target/test.lua new file mode 100644 index 000000000..a4a38b0ce --- /dev/null +++ b/tests/apis/clone_target/test.lua @@ -0,0 +1,3 @@ +function main() + os.exec("xmake") +end diff --git a/tests/apis/clone_target/xmake.lua b/tests/apis/clone_target/xmake.lua new file mode 100644 index 000000000..13d460eeb --- /dev/null +++ b/tests/apis/clone_target/xmake.lua @@ -0,0 +1,21 @@ +target("test") + set_kind("binary") + add_files("src/*.cpp") + add_defines("TEST") + after_load(function (target) + import("core.project.project") + local t = target:clone() + t:name_set("test2") + t:add("deps", "test") + t:add("defines", "TEST2") + t:set("link_before", function (target) + print("link1", target:name()) + assert(target:dep("test"):data("linked")) + end) + project.target_add(t) + end) + before_link(function (target) + print("link2", target:name()) + target:data_set("linked", true) + end) + diff --git a/tests/apis/rules_inject_deps/src/main.cpp b/tests/apis/rules_inject_deps/src/main.cpp new file mode 100644 index 000000000..db2361659 --- /dev/null +++ b/tests/apis/rules_inject_deps/src/main.cpp @@ -0,0 +1,9 @@ +#include <iostream> + +using namespace std; + +int main(int argc, char** argv) +{ + cout << "hello world!" << endl; + return 0; +} diff --git a/tests/apis/rules_inject_deps/src/main.cpp2 b/tests/apis/rules_inject_deps/src/main.cpp2 new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/apis/rules_inject_deps/src/main.cpp2 diff --git a/tests/apis/rules_inject_deps/test.lua b/tests/apis/rules_inject_deps/test.lua new file mode 100644 index 000000000..a750f2cf4 --- /dev/null +++ b/tests/apis/rules_inject_deps/test.lua @@ -0,0 +1,3 @@ +function main() + os.exec("xmake -j1") +end diff --git a/tests/apis/rules_inject_deps/xmake.lua b/tests/apis/rules_inject_deps/xmake.lua new file mode 100644 index 000000000..cf73449bb --- /dev/null +++ b/tests/apis/rules_inject_deps/xmake.lua @@ -0,0 +1,23 @@ +rule("cppfront") + set_extensions(".cpp2") + on_load(function (target) + local rule = target:rule("c++.build"):clone() + rule:add("deps", "cppfront", {order = true}) + target:rule_add(rule) + end) + on_build_file(function (target, sourcefile, opt) + print("build cppfront file") + local objectfile = target:objectfile(sourcefile:gsub("cpp2", "cpp")) + assert(not os.isfile(objectfile), "invalid rule order!") + end) + +target("test") + set_kind("binary") + add_rules("cppfront") + add_files("src/*.cpp") + add_files("src/*.cpp2") + before_build_file(function (target, sourcefile, opt) + local objectfile = target:objectfile(sourcefile) + os.tryrm(objectfile) + end) + diff --git a/tests/apis/rules_override_cxx/src/main.xx b/tests/apis/rules_override_cxx/src/main.xx new file mode 100644 index 000000000..d612d8782 --- /dev/null +++ b/tests/apis/rules_override_cxx/src/main.xx @@ -0,0 +1,6 @@ +#include <stdio.h> + +int main(int argc, char** argv) +{ + return 0; +} diff --git a/tests/apis/rules_override_cxx/src/test.cc b/tests/apis/rules_override_cxx/src/test.cc new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/apis/rules_override_cxx/src/test.cc diff --git a/tests/apis/rules_override_cxx/test.lua b/tests/apis/rules_override_cxx/test.lua new file mode 100644 index 000000000..a4a38b0ce --- /dev/null +++ b/tests/apis/rules_override_cxx/test.lua @@ -0,0 +1,3 @@ +function main() + os.exec("xmake") +end diff --git a/tests/apis/rules_override_cxx/xmake.lua b/tests/apis/rules_override_cxx/xmake.lua new file mode 100644 index 000000000..564a55989 --- /dev/null +++ b/tests/apis/rules_override_cxx/xmake.lua @@ -0,0 +1,34 @@ +rule("xx") + add_deps("c++") + on_load(function (target) + + -- add .xx + local rule = target:rule("c++.build"):clone() + rule:set("extensions", ".xx") + target:rule_add(rule) + + -- patch sourcebatch for .xx + local sourcebatch = target:sourcebatches()["c++.build"] + sourcebatch.sourcekind = "cxx" + sourcebatch.objectfiles = {} + sourcebatch.dependfiles = {} + for _, sourcefile in ipairs(sourcebatch.sourcefiles) do + local objectfile = target:objectfile(sourcefile) + local dependfile = target:dependfile(objectfile) + table.insert(sourcebatch.objectfiles, objectfile) + table.insert(sourcebatch.dependfiles, dependfile) + end + + -- force as c++ source file + if target:is_plat("windows") then + target:add("cxxflags", "/TP") + else + target:add("cxxflags", "-x c++") + end + end) + +target("test") + set_kind("binary") + add_rules("xx") + add_files("src/*.xx", "src/*.cc") + diff --git a/xmake/actions/build/kinds/object.lua b/xmake/actions/build/kinds/object.lua index 0498cf0d7..01fbb5a05 100644 --- a/xmake/actions/build/kinds/object.lua +++ b/xmake/actions/build/kinds/object.lua @@ -27,21 +27,22 @@ import("private.async.runjobs") import("private.utils.batchcmds") -- get rule -function _get_rule(rulename) - local ruleinst = assert(project.rule(rulename) or rule.rule(rulename), "unknown rule: %s", rulename) +-- @note we need get rule from target first, because we maybe will inject and replace builtin rule in target +function _get_rule(target, rulename) + local ruleinst = assert(target:rule(rulename) or project.rule(rulename) or rule.rule(rulename), "unknown rule: %s", rulename) return ruleinst end -- get max depth of rule -function _get_rule_max_depth(ruleinst, depth) +function _get_rule_max_depth(target, ruleinst, depth) local max_depth = depth for _, depname in ipairs(ruleinst:get("deps")) do - local dep = _get_rule(depname) + local dep = _get_rule(target, depname) local dep_depth = depth if ruleinst:extraconf("deps", depname, "order") then dep_depth = dep_depth + 1 end - local cur_depth = _get_rule_max_depth(dep, dep_depth) + local cur_depth = _get_rule_max_depth(target, dep, dep_depth) if cur_depth > max_depth then max_depth = cur_depth end @@ -113,7 +114,7 @@ function _add_batchjobs_for_rule(batchjobs, rootjob, target, sourcebatch, suffix -- get rule local rulename = assert(sourcebatch.rulename, "unknown rule for sourcebatch!") - local ruleinst = _get_rule(rulename) + local ruleinst = _get_rule(target, rulename) -- add batch jobs for xx_build_files local scriptname = "build_files" .. (suffix and ("_" .. suffix) or "") @@ -233,8 +234,8 @@ end function _build_sourcebatch_groups_for_rules(groups, target, sourcebatches) for _, sourcebatch in pairs(sourcebatches) do local rulename = assert(sourcebatch.rulename, "unknown rule for sourcebatch!") - local ruleinst = _get_rule(rulename) - local depth = _get_rule_max_depth(ruleinst, 1) + local ruleinst = _get_rule(target, rulename) + local depth = _get_rule_max_depth(target, ruleinst, 1) local group = groups[depth] if group == nil then group = {} diff --git a/xmake/core/base/private/instance_deps.lua b/xmake/core/base/private/instance_deps.lua new file mode 100644 index 000000000..535b2d2bb --- /dev/null +++ b/xmake/core/base/private/instance_deps.lua @@ -0,0 +1,77 @@ +--!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, TBOOX Open Source Group. +-- +-- @author ruki +-- @file instance_deps.lua +-- + +-- define module +local instance_deps = instance_deps or {} + +-- load modules +local option = require("base/option") +local string = require("base/string") +local table = require("base/table") + +-- load deps for instance: e.g. option, instance and rule +-- +-- e.g. +-- +-- a.deps = b +-- b.deps = c +-- +-- orderdeps: c -> b -> a +-- +function instance_deps.load_deps(instance, instances, deps, orderdeps, depspath) + for _, dep in ipairs(table.wrap(instance:get("deps"))) do + local depinst = instances[dep] + if depinst then + local depspath_sub + if depspath then + for idx, name in ipairs(depspath) do + if name == dep then + local circular_deps = table.slice(depspath, idx) + table.insert(circular_deps, dep) + os.raise("circular dependency(%s) detected!", table.concat(circular_deps, ", ")) + end + end + depspath_sub = table.join(depspath, dep) + end + instance_deps.load_deps(depinst, instances, deps, orderdeps, depspath_sub) + if not deps[dep] then + deps[dep] = depinst + table.insert(orderdeps, depinst) + end + end + end +end + +-- sort instances for all deps +function instance_deps.sort_deps(instances, orderinstances, instancerefs, instance) + for _, depname in ipairs(table.wrap(instance:get("deps"))) do + local instanceinst = instances[depname] + if instanceinst then + instance_deps.sort_deps(instances, orderinstances, instancerefs, instanceinst) + end + end + if not instancerefs[instance:name()] then + instancerefs[instance:name()] = true + table.insert(orderinstances, instance) + end +end + +-- return module +return instance_deps diff --git a/xmake/core/base/scopeinfo.lua b/xmake/core/base/scopeinfo.lua index ef15fc2e1..f6dd7d475 100644 --- a/xmake/core/base/scopeinfo.lua +++ b/xmake/core/base/scopeinfo.lua @@ -666,7 +666,7 @@ end -- clone a new instance from the current function _instance:clone() - return _instance.new(self:kind(), self:info(), {interpreter = self:interpreter(), deduplicate = self._DEDUPLICATE, enable_filter = self._ENABLE_FILTER}) + return _instance.new(self:kind(), table.clone(self:info()), {interpreter = self:interpreter(), deduplicate = self._DEDUPLICATE, enable_filter = self._ENABLE_FILTER}) end -- new a scope instance diff --git a/xmake/core/base/table.lua b/xmake/core/base/table.lua index b3e8840e3..5e04b0256 100644 --- a/xmake/core/base/table.lua +++ b/xmake/core/base/table.lua @@ -153,7 +153,19 @@ function table.append(array, ...) return array end --- copy the table to self +-- clone table +function table.clone(self) + local result + if type(self) == "table" then + result = {} + for k, v in pairs(self) do + result[k] = v + end + end + return result +end + +-- copy the table (deprecated, please use table.clone) function table.copy(copied) local result = {} copied = copied or {} diff --git a/xmake/core/project/project.lua b/xmake/core/project/project.lua index f48a45205..e47463a89 100644 --- a/xmake/core/project/project.lua +++ b/xmake/core/project/project.lua @@ -34,6 +34,7 @@ local hashset = require("base/hashset") local baseoption = require("base/option") local deprecated = require("base/deprecated") local interpreter = require("base/interpreter") +local instance_deps = require("base/private/instance_deps") local memcache = require("cache/memcache") local rule = require("project/rule") local target = require("project/target") @@ -49,8 +50,10 @@ local language = require("language/language") local sandbox_os = require("sandbox/modules/os") local sandbox_module = require("sandbox/modules/import/core/sandbox/module") --- register project to platform +-- register project to platform, rule and target platform._PROJECT = project +target._PROJECT = project +rule._PROJECT = project -- the current os is belong to the given os? function project._api_is_os(interp, ...) @@ -207,39 +210,6 @@ function project._load(force, disable_filter) return true end --- load deps for instance: e.g. option, target and rule --- --- e.g. --- --- a.deps = b --- b.deps = c --- --- orderdeps: c -> b -> a --- -function project._load_deps(instance, instances, deps, orderdeps, depspath) - for _, dep in ipairs(table.wrap(instance:get("deps"))) do - local depinst = instances[dep] - if depinst then - local depspath_sub - if depspath then - for idx, name in ipairs(depspath) do - if name == dep then - local circular_deps = table.slice(depspath, idx) - table.insert(circular_deps, dep) - os.raise("circular dependency(%s) detected!", table.concat(circular_deps, ", ")) - end - end - depspath_sub = table.join(depspath, dep) - end - project._load_deps(depinst, instances, deps, orderdeps, depspath_sub) - if not deps[dep] then - deps[dep] = depinst - table.insert(orderdeps, depinst) - end - end - end -end - -- load scope from the project file function project._load_scope(scope_kind, deduplicate, enable_filter) @@ -320,14 +290,6 @@ function project._load_rules() for rulename, ruleinfo in pairs(results) do rules[rulename] = rule.new(rulename, ruleinfo) end - - -- load rule deps - local instances = table.join(rule.rules(), rules) - for _, instance in pairs(instances) do - instance._DEPS = instance._DEPS or {} - instance._ORDERDEPS = instance._ORDERDEPS or {} - project._load_deps(instance, instances, instance._DEPS, instance._ORDERDEPS, {instance:name()}) - end return rules end @@ -366,19 +328,19 @@ function project._load_targets() local requires = project.required_packages() local ok, errors = project._load(true) if not ok then - return nil, nil, errors + return nil, errors end -- load targets local results, errors = project._load_scope("target", true, true) if not results then - return nil, nil, errors + return nil, errors end -- make targets local targets = {} for targetname, targetinfo in pairs(results) do - local t = target.new(targetname, targetinfo, project) + local t = target.new(targetname, targetinfo) if t and (t:get("enabled") == nil or t:get("enabled") == true) then targets[targetname] = t end @@ -388,16 +350,7 @@ function project._load_targets() for _, t in pairs(targets) do -- load rules from target and language - -- - -- e.g. - -- - -- a.deps = b - -- b.deps = c - -- - -- orderules: c -> b -> a - -- - t._RULES = t._RULES or {} - t._ORDERULES = t._ORDERULES or {} + t._RULES = t._RULES or {} local rulenames = {} local extensions = {} table.join2(rulenames, t:get("rules")) @@ -419,53 +372,28 @@ function project._load_targets() if r:kind() == "target" then t._RULES[rulename] = r for _, deprule in ipairs(r:orderdeps()) do - local name = deprule:name() - if not t._RULES[name] then - t._RULES[name] = deprule - table.insert(t._ORDERULES, deprule) - end + t._RULES[deprule:name()] = deprule end - table.insert(t._ORDERULES, r) end else - return nil, nil, string.format("unknown rule(%s) in target(%s)!", rulename, t:name()) + return nil, string.format("unknown rule(%s) in target(%s)!", rulename, t:name()) end end -- @note it's deprecated, please use on_load instead of before_load ok, errors = t:_load_before() if not ok then - return nil, nil, errors + return nil, errors end -- we need call on_load() before building deps/rules, -- so we can use `target:add("deps", "xxx")` to add deps in on_load ok, errors = t:_load() if not ok then - return nil, nil, errors - end - - -- load deps - t._DEPS = t._DEPS or {} - t._ORDERDEPS = t._ORDERDEPS or {} - project._load_deps(t, targets, t._DEPS, t._ORDERDEPS, {t:name()}) - end - - -- sort targets for all deps - local targetrefs = {} - local ordertargets = {} - for _, t in pairs(targets) do - project._sort_targets(targets, ordertargets, targetrefs, t) - end - - -- do after_load() for targets - for _, t in ipairs(ordertargets) do - ok, errors = t:_load_after() - if not ok then - return nil, nil, errors + return nil, errors end end - return targets, ordertargets + return targets end -- load options @@ -541,7 +469,7 @@ function project._load_options(disable_filter) for _, opt in pairs(options) do opt._DEPS = opt._DEPS or {} opt._ORDERDEPS = opt._ORDERDEPS or {} - project._load_deps(opt, options, opt._DEPS, opt._ORDERDEPS, {opt:name()}) + instance_deps.load_deps(opt, options, opt._DEPS, opt._ORDERDEPS, {opt:name()}) end return options end @@ -601,20 +529,6 @@ function project._load_packages() return project._load_scope("package", true, false) end --- sort targets for all deps -function project._sort_targets(targets, ordertargets, targetrefs, target) - for _, depname in ipairs(table.wrap(target:get("deps"))) do - local targetinst = targets[depname] - if targetinst then - project._sort_targets(targets, ordertargets, targetrefs, targetinst) - end - end - if not targetrefs[target:name()] then - targetrefs[target:name()] = true - table.insert(ordertargets, target) - end -end - -- get project memcache function project._memcache() return memcache.cache("core.project.project") @@ -950,17 +864,38 @@ function project.target(name) return targets and targets[name] end +-- add the given target, @note if the target name is the same, it will be replaced +function project.target_add(t) + local targets = project.targets() + if targets then + targets[t:name()] = t + project._memcache():set("ordertargets", nil) + end +end + -- get targets function project.targets() + local loading = false local targets = project._memcache():get("targets") if not targets then - local ordertargets, errors - targets, ordertargets, errors = project._load_targets() + local errors + targets, errors = project._load_targets() if errors then os.raise(errors) end project._memcache():set("targets", targets) - project._memcache():set("ordertargets", ordertargets) + loading = true + end + if loading then + -- do after_load() for targets + -- @note we must call it after finishing to cache targets + -- because we maybe will call project.targets() in after_load, we need avoid dead recursion loop + for _, t in ipairs(project.ordertargets()) do + local ok, errors = t:_load_after() + if not ok then + os.raise(errors or string.format("load target %s failed", t:name())) + end + end end return targets end @@ -969,9 +904,13 @@ end function project.ordertargets() local ordertargets = project._memcache():get("ordertargets") if not ordertargets then - -- ensure ordertargets to be cached - project.targets() - ordertargets = project._memcache():get("ordertargets") + local targets = project.targets() + ordertargets = {} + local targetrefs = {} + for _, t in pairs(targets) do + instance_deps.sort_deps(targets, ordertargets, targetrefs, t) + end + project._memcache():set("ordertargets", ordertargets) end return ordertargets end diff --git a/xmake/core/project/rule.lua b/xmake/core/project/rule.lua index 599294a45..155f96754 100644 --- a/xmake/core/project/rule.lua +++ b/xmake/core/project/rule.lua @@ -20,6 +20,7 @@ -- define module local rule = rule or {} +local _instance = _instance or {} -- load modules local os = require("base/os") @@ -28,11 +29,165 @@ local utils = require("base/utils") local table = require("base/table") local global = require("base/global") local interpreter = require("base/interpreter") +local instance_deps = require("base/private/instance_deps") local config = require("project/config") local sandbox = require("sandbox/sandbox") local sandbox_os = require("sandbox/modules/os") local sandbox_module = require("sandbox/modules/import/core/sandbox/module") +-- invalidate the previous cache +function _instance:_invalidate(name) + if name == "deps" then + self._DEPS = nil + self._ORDERDEPS = nil + end +end + +-- build deps +function _instance:_build_deps() + local instances = rule.rules() + if rule._project() then + instances = table.join(rule.rules(), rule._project().rules()) + end + self._DEPS = self._DEPS or {} + self._ORDERDEPS = self._ORDERDEPS or {} + instance_deps.load_deps(self, instances, self._DEPS, self._ORDERDEPS, {self:name()}) +end + +-- clone rule +function _instance:clone() + local instance = rule.new(self:name(), self._INFO:clone()) + instance._DEPS = self._DEPS + instance._ORDERDEPS = self._ORDERDEPS + return instance +end + +-- get the rule info +function _instance:get(name) + return self._INFO:get(name) +end + +-- set the value to the rule info +function _instance:set(name, ...) + self._INFO:apival_set(name, ...) + self:_invalidate(name) +end + +-- add the value to the rule info +function _instance:add(name, ...) + self._INFO:apival_add(name, ...) + self:_invalidate(name) +end + +-- get the extra configuration +function _instance:extraconf(name, item, key) + return self._INFO:extraconf(name, item, key) +end + +-- get the rule name +function _instance:name() + return self._NAME +end + +-- set the rule name +function _instance:name_set(name) + self._NAME = name +end + +-- get the rule kind +-- +-- current supported kind: +-- - target: default, only for each target +-- - project: global rule, for whole project +-- +function _instance:kind() + return self:get("kind") or "target" +end + +-- get the given dependent rule +function _instance:dep(name) + local deps = self:deps() + if deps then + return deps[name] + end +end + +-- get rule deps +function _instance:deps() + if self._DEPS == nil then + self:_build_deps() + end + return self._DEPS +end + +-- get rule order deps +function _instance:orderdeps() + if self._DEPS == nil then + self:_build_deps() + end + return self._ORDERDEPS +end + +-- get xxx_script +function _instance:script(name, generic) + + -- get script + local script = self:get(name) + local result = nil + if type(script) == "function" then + result = script + elseif type(script) == "table" then + + -- get plat and arch + local plat = config.get("plat") or "" + local arch = config.get("arch") or "" + + -- match pattern + -- + -- `@linux` + -- `@linux|x86_64` + -- `@macosx,linux` + -- `android@macosx,linux` + -- `android|armeabi-v7a@macosx,linux` + -- `android|armeabi-v7a@macosx,linux|x86_64` + -- `android|armeabi-v7a@linux|x86_64` + -- + for _pattern, _script in pairs(script) do + local hosts = {} + local hosts_spec = false + _pattern = _pattern:gsub("@(.+)", function (v) + for _, host in ipairs(v:split(',')) do + hosts[host] = true + hosts_spec = true + end + return "" + end) + if not _pattern:startswith("__") and (not hosts_spec or hosts[os.subhost() .. '|' .. os.subarch()] or hosts[os.subhost()]) + and (_pattern:trim() == "" or (plat .. '|' .. arch):find('^' .. _pattern .. '$') or plat:find('^' .. _pattern .. '$')) then + result = _script + break + end + end + + -- get generic script + result = result or script["__generic__"] or generic + end + + -- only generic script + result = result or generic + + -- imports some modules first + if result and result ~= generic then + local scope = getfenv(result) + if scope then + for _, modulename in ipairs(table.wrap(self:get("imports"))) do + scope[sandbox_module.name(modulename)] = sandbox_module.import(modulename, {anonymous = true}) + end + end + end + return result +end + -- the directories of rule function rule._directories() return { path.join(global.directory(), "rules") @@ -94,6 +249,11 @@ function rule._interpreter() return interp end +-- get project +function rule._project() + return rule._PROJECT +end + -- load rule function rule._load(filepath) @@ -208,115 +368,12 @@ end -- new a rule instance function rule.new(name, info) - local instance = table.inherit(rule) + local instance = table.inherit(_instance) instance._NAME = name instance._INFO = info return instance end --- get the rule info -function rule:get(name) - return self._INFO:get(name) -end - --- get the extra configuration -function rule:extraconf(name, item, key) - return self._INFO:extraconf(name, item, key) -end - --- get the rule name -function rule:name() - return self._NAME -end - --- get the rule kind --- --- current supported kind: --- - target: default, only for each target --- - project: global rule, for whole project --- -function rule:kind() - return self:get("kind") or "target" -end - --- get the given dependent rule -function rule:dep(name) - local deps = self:deps() - if deps then - return deps[name] - end -end - --- get rule deps -function rule:deps() - return self._DEPS -end - --- get rule order deps -function rule:orderdeps() - return self._ORDERDEPS -end - --- get xxx_script -function rule:script(name, generic) - - -- get script - local script = self:get(name) - local result = nil - if type(script) == "function" then - result = script - elseif type(script) == "table" then - - -- get plat and arch - local plat = config.get("plat") or "" - local arch = config.get("arch") or "" - - -- match pattern - -- - -- `@linux` - -- `@linux|x86_64` - -- `@macosx,linux` - -- `android@macosx,linux` - -- `android|armeabi-v7a@macosx,linux` - -- `android|armeabi-v7a@macosx,linux|x86_64` - -- `android|armeabi-v7a@linux|x86_64` - -- - for _pattern, _script in pairs(script) do - local hosts = {} - local hosts_spec = false - _pattern = _pattern:gsub("@(.+)", function (v) - for _, host in ipairs(v:split(',')) do - hosts[host] = true - hosts_spec = true - end - return "" - end) - if not _pattern:startswith("__") and (not hosts_spec or hosts[os.subhost() .. '|' .. os.subarch()] or hosts[os.subhost()]) - and (_pattern:trim() == "" or (plat .. '|' .. arch):find('^' .. _pattern .. '$') or plat:find('^' .. _pattern .. '$')) then - result = _script - break - end - end - - -- get generic script - result = result or script["__generic__"] or generic - end - - -- only generic script - result = result or generic - - -- imports some modules first - if result and result ~= generic then - local scope = getfenv(result) - if scope then - for _, modulename in ipairs(table.wrap(self:get("imports"))) do - scope[sandbox_module.name(modulename)] = sandbox_module.import(modulename, {anonymous = true}) - end - end - end - return result -end - -- get the given global rule function rule.rule(name) return rule.rules()[name] @@ -343,19 +400,11 @@ function rule.rules() end -- make rule instances - local instances = {} + rules = {} for rulename, ruleinfo in pairs(ruleinfos) do local instance = rule.new(rulename, ruleinfo) - instances[rulename] = instance - end - - -- load rule deps - for _, instance in pairs(instances) do - instance._DEPS = instance._DEPS or {} - instance._ORDERDEPS = instance._ORDERDEPS or {} - rule._load_deps(instance, instances, instance._DEPS, instance._ORDERDEPS) + rules[rulename] = instance end - rules = instances rule._RULES = rules end return rules diff --git a/xmake/core/project/target.lua b/xmake/core/project/target.lua index 329eb53e3..1c6914743 100644 --- a/xmake/core/project/target.lua +++ b/xmake/core/project/target.lua @@ -31,6 +31,7 @@ local table = require("base/table") local baseoption = require("base/option") local hashset = require("base/hashset") local deprecated = require("base/deprecated") +local instance_deps = require("base/private/instance_deps") local memcache = require("cache/memcache") local rule = require("project/rule") local option = require("project/option") @@ -48,11 +49,10 @@ local sandbox = require("sandbox/sandbox") local sandbox_module = require("sandbox/modules/import/core/sandbox/module") -- new a target instance -function _instance.new(name, info, project) +function _instance.new(name, info) local instance = table.inherit(_instance) instance._NAME = name instance._INFO = info - instance._PROJECT = project instance._CACHEID = 1 return instance end @@ -275,6 +275,19 @@ function _instance:_invalidate(name) -- we need flush the source files cache if target/files are modified, e.g. `target:add("files", "xxx.c")` if name == "files" then self._SOURCEFILES = nil + elseif name == "deps" then + self._DEPS = nil + self._ORDERDEPS = nil + end +end + +-- build deps +function _instance:_build_deps() + if target._project() then + local instances = target._project().targets() + self._DEPS = self._DEPS or {} + self._ORDERDEPS = self._ORDERDEPS or {} + instance_deps.load_deps(self, instances, self._DEPS, self._ORDERDEPS, {self:name()}) end end @@ -283,6 +296,35 @@ function _instance:_is_loaded() return self._LOADED end +-- clone target, @note we can just call it in after_load() +function _instance:clone() + if not self:_is_loaded() or self._LOADED_AFTER then + os.raise("please call target:clone() in after_load().", self:name()) + end + local instance = target.new(self:name(), self._INFO:clone()) + if self._DEPS then + instance._DEPS = table.clone(self._DEPS) + end + if self._ORDERDEPS then + instance._ORDERDEPS = table.clone(self._ORDERDEPS) + end + if self._RULES then + instance._RULES = table.clone(self._RULES) + end + if self._ORDERULES then + instance._ORDERULES = table.clone(self._ORDERULES) + end + if self._DATA then + instance._DATA = table.clone(self._DATA) + end + if self._SOURCEFILES then + instance._SOURCEFILES = table.clone(self._SOURCEFILES) + end + instance._LOADED = self._LOADED + instance._LOADED_AFTER = true + return instance +end + -- get the target info -- -- e.g. @@ -491,6 +533,11 @@ function _instance:name() return self._NAME end +-- set the target name +function _instance:name_set(name) + self._NAME = name +end + -- get the target kind function _instance:kind() return self:get("kind") or "binary" @@ -667,6 +714,9 @@ function _instance:deps() if not self:_is_loaded() then os.raise("please call target:deps() or target:dep() in after_load()!") end + if self._DEPS == nil then + self:_build_deps() + end return self._DEPS end @@ -675,6 +725,9 @@ function _instance:orderdeps() if not self:_is_loaded() then os.raise("please call target:orderdeps() in after_load()!") end + if self._DEPS == nil then + self:_build_deps() + end return self._ORDERDEPS end @@ -685,7 +738,17 @@ end -- get target ordered rules function _instance:orderules() - return self._ORDERULES + local rules = self._RULES + local orderules = self._ORDERULES + if orderules == nil and rules then + orderules = {} + local rulerefs = {} + for _, r in pairs(rules) do + instance_deps.sort_deps(rules, orderules, rulerefs, r) + end + self._ORDERULES = orderules + end + return orderules end -- get target rule from the given rule name @@ -695,6 +758,16 @@ function _instance:rule(name) end end +-- add rule +-- +-- @note If a rule has the same name as a built-in rule, +-- it will be replaced in the target:rules() and target:orderules(), but will be not replaced globally in the project.rules() +function _instance:rule_add(r) + self._RULES = self._RULES or {} + self._RULES[r:name()] = r + self._ORDERULES = nil +end + -- is phony target? function _instance:is_phony() local targetkind = self:kind() @@ -835,7 +908,7 @@ function _instance:orderpkgs(opt) local packages = self:_memcache():get(cachekey) if not packages then packages = {} - local requires = self._PROJECT.required_packages() + local requires = target._project().required_packages() if requires then for _, packagename in ipairs(table.wrap(self:get("packages", opt))) do local pkg = requires[packagename] @@ -1196,7 +1269,7 @@ function _instance:filerules(sourcefile) if filerules then override = filerules.override for _, rulename in ipairs(table.wrap(filerules)) do - local r = self._PROJECT.rule(rulename) or rule.rule(rulename) + local r = target._project().rule(rulename) or rule.rule(rulename) if r then table.insert(rules, r) end @@ -2038,8 +2111,8 @@ function _instance:toolchains() toolchain_opt.plat = self:plat() local toolchain_inst, errors = toolchain.load(name, toolchain_opt) -- attempt to load toolchain from project - if not toolchain_inst and self._PROJECT then - toolchain_inst = self._PROJECT.toolchain(name, toolchain_opt) + if not toolchain_inst and target._project() then + toolchain_inst = target._project().toolchain(name, toolchain_opt) end if not toolchain_inst then os.raise(errors) @@ -2136,6 +2209,11 @@ function _instance:has_tool(toolkind, ...) end end +-- get project +function target._project() + return target._PROJECT +end + -- get target apis function target.apis() diff --git a/xmake/core/sandbox/modules/import/core/project/project.lua b/xmake/core/sandbox/modules/import/core/project/project.lua index 46c9b973d..a5778bc9e 100644 --- a/xmake/core/sandbox/modules/import/core/project/project.lua +++ b/xmake/core/sandbox/modules/import/core/project/project.lua @@ -43,6 +43,7 @@ sandbox_core_project.rules = project.rules sandbox_core_project.toolchain = project.toolchain sandbox_core_project.toolchains = project.toolchains sandbox_core_project.target = project.target +sandbox_core_project.target_add = project.target_add sandbox_core_project.targets = project.targets sandbox_core_project.ordertargets = project.ordertargets sandbox_core_project.option = project.option |
