summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2022-09-28 20:42:39 +0800
committerGitHub <[email protected]>2022-09-28 20:42:39 +0800
commit73c22237fb886379beb347fd9a2908e31f82ee9c (patch)
tree14715c2bbc90cc75f84994be127f77058be8795f
parent575706825c48cfb9af0d22cc88ff4f826a917455 (diff)
parent82bfc71e7810157e8f71d43d4a2d82b609c17bf9 (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.cpp9
-rw-r--r--tests/apis/clone_target/test.lua3
-rw-r--r--tests/apis/clone_target/xmake.lua21
-rw-r--r--tests/apis/rules_inject_deps/src/main.cpp9
-rw-r--r--tests/apis/rules_inject_deps/src/main.cpp20
-rw-r--r--tests/apis/rules_inject_deps/test.lua3
-rw-r--r--tests/apis/rules_inject_deps/xmake.lua23
-rw-r--r--tests/apis/rules_override_cxx/src/main.xx6
-rw-r--r--tests/apis/rules_override_cxx/src/test.cc0
-rw-r--r--tests/apis/rules_override_cxx/test.lua3
-rw-r--r--tests/apis/rules_override_cxx/xmake.lua34
-rw-r--r--xmake/actions/build/kinds/object.lua17
-rw-r--r--xmake/core/base/private/instance_deps.lua77
-rw-r--r--xmake/core/base/scopeinfo.lua2
-rw-r--r--xmake/core/base/table.lua14
-rw-r--r--xmake/core/project/project.lua151
-rw-r--r--xmake/core/project/rule.lua277
-rw-r--r--xmake/core/project/target.lua92
-rw-r--r--xmake/core/sandbox/modules/import/core/project/project.lua1
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