diff options
| author | ruki <[email protected]> | 2023-10-27 03:50:01 -0500 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-10-27 03:50:01 -0500 |
| commit | 6af4b8528bfae7536fc77a5e7fb4341b3f11bc9a (patch) | |
| tree | 45bb260efc422e8692fc664ed80d9ddc9b56cb3c | |
| parent | eeda646a4e32b3d672a8097fe995cbdcf101c7aa (diff) | |
| parent | 1cb74a5009d8fbcb792aa6cc787943453cc5234a (diff) | |
Merge pull request #4314 from xmake-io/package
Support linkgroups and linkorders for package
26 files changed, 685 insertions, 188 deletions
diff --git a/tests/apis/target_get_from/.gitignore b/tests/apis/target_get_from/.gitignore new file mode 100644 index 000000000..152105761 --- /dev/null +++ b/tests/apis/target_get_from/.gitignore @@ -0,0 +1,8 @@ +# Xmake cache +.xmake/ +build/ + +# MacOS Cache +.DS_Store + + diff --git a/tests/apis/target_get_from/src/foo.cpp b/tests/apis/target_get_from/src/foo.cpp new file mode 100644 index 000000000..1a1fb3425 --- /dev/null +++ b/tests/apis/target_get_from/src/foo.cpp @@ -0,0 +1,5 @@ +#include "foo.h" + +int add(int a, int b) { + return a + b; +} diff --git a/tests/apis/target_get_from/src/foo.h b/tests/apis/target_get_from/src/foo.h new file mode 100644 index 000000000..d2506bca9 --- /dev/null +++ b/tests/apis/target_get_from/src/foo.h @@ -0,0 +1,9 @@ +#ifdef __cplusplus +extern "C" { +#endif + +int add(int a, int b); + +#ifdef __cplusplus +} +#endif diff --git a/tests/apis/target_get_from/src/main.cpp b/tests/apis/target_get_from/src/main.cpp new file mode 100644 index 000000000..ed1924789 --- /dev/null +++ b/tests/apis/target_get_from/src/main.cpp @@ -0,0 +1,9 @@ +#include "foo.h" +#include <iostream> + +using namespace std; + +int main(int argc, char** argv) { + cout << "add(1, 2) = " << add(1, 2) << endl; + return 0; +} diff --git a/tests/apis/target_get_from/xmake.lua b/tests/apis/target_get_from/xmake.lua new file mode 100644 index 000000000..5e77a0382 --- /dev/null +++ b/tests/apis/target_get_from/xmake.lua @@ -0,0 +1,33 @@ +add_rules("mode.debug", "mode.release") + +add_requires("zlib") + +option("bar") + set_default(true) + add_defines("BAR") + +target("foo") + set_kind("static") + add_files("src/foo.cpp") + add_defines("foo") + add_defines("FOO", {public = true}) + add_options("bar") + add_linkgroups("m", "pthread", {group = true}) + +target("test") + set_kind("binary") + add_deps("foo") + add_files("src/main.cpp") + add_defines("TEST") + add_packages("zlib") + on_config(function (target) + print("self", target:get_from("defines", "self")) + print("dep::foo", target:get_from("defines", "dep::foo")) + print("dep::*", target:get_from("defines", "dep::*")) + print("dep::foo/option::bar", target:get_from("defines", "dep::foo/option::bar")) + print("dep::foo/option::*", target:get_from("defines", "dep::foo/option::*")) + print("*", target:get_from("defines", "*")) + print("package::zlib", target:get_from("links", "package::zlib")) + print("extraconf(dep::foo)", target:extraconf_from("dep::foo", "linkgroups")) + end) + diff --git a/xmake/core/base/table.lua b/xmake/core/base/table.lua index a8050f0a3..f954596b8 100644 --- a/xmake/core/base/table.lua +++ b/xmake/core/base/table.lua @@ -349,6 +349,7 @@ function table.wrap_lock(value) if type(value) == "table" then value.__wrap_locked__ = true end + return value end -- unlock table value to unwrap @@ -356,6 +357,7 @@ function table.wrap_unlock(value) if type(value) == "table" then value.__wrap_locked__ = nil end + return value end -- remove repeat from the given array @@ -375,6 +377,9 @@ function table.unique(array, barrier) table.insert(unique, v) end end + if array.__wrap_locked__ then + table.wrap_lock(unique) + end array = unique end end @@ -400,6 +405,9 @@ function table.reverse_unique(array, barrier) table.insert(unique, 1, v) end end + if array.__wrap_locked__ then + table.wrap_lock(unique) + end array = unique end end @@ -431,7 +439,15 @@ end function table.orderkeys(tbl, callback) local callback = type(callback) == "function" and callback or nil local keys = table.keys(tbl) - table.sort(keys, callback) + if callback then + table.sort(keys, callback) + else + local ok = pcall(table.sort, keys) + if not ok then + -- maybe sort strings and numbers, {1, 2, "a"} + table.sort(keys, function (a, b) return tostring(a) < tostring(b) end) + end + end return keys end diff --git a/xmake/core/package/package.lua b/xmake/core/package/package.lua index a51a3404e..187f5e2dd 100644 --- a/xmake/core/package/package.lua +++ b/xmake/core/package/package.lua @@ -902,31 +902,38 @@ function _instance:manifest_save() -- save global variables and component variables local vars + local extras local components local apis = language.apis() - for _, apiname in ipairs(table.join(apis.values, apis.paths)) do + for _, apiname in ipairs(table.join(apis.values, apis.paths, apis.groups)) do if apiname:startswith("package.add_") or apiname:startswith("package.set_") then local name = apiname:sub(13) - local value = self:get(name) - if value ~= nil then + local values = self:get(name) + if values ~= nil then vars = vars or {} - vars[name] = value + vars[name] = values + local extra = self:extraconf(name) + if extra then + extras = extras or {} + extras[name] = extra + end end for _, component_name in ipairs(table.wrap(self:get("components"))) do local comp = self:component(component_name) if comp then - local component_value = comp:get(name) - if component_value ~= nil then + local component_values = comp:get(name) + if component_values ~= nil then components = components or {} components.vars = components.vars or {} components.vars[component_name] = components.vars[component_name] or {} - components.vars[component_name][name] = component_value + components.vars[component_name][name] = component_values end end end end end manifest.vars = vars + manifest.extras = extras manifest.components = components -- save repository diff --git a/xmake/core/project/package.lua b/xmake/core/project/package.lua index 0e16a7e1b..63267a04e 100644 --- a/xmake/core/project/package.lua +++ b/xmake/core/project/package.lua @@ -161,23 +161,59 @@ function _instance:components_deps() return self:get("__components_deps") end --- get other extra information from package/on_fetch +-- get user extra configuration from package/on_fetch +-- @see https://github.com/xmake-io/xmake/issues/3106#issuecomment-1330143922 -- -- e.g. -- -- @code -- package("xxx") -- on_fetch(function (package) --- return {includedirs = "", links = "", extra = {foo = ""}} +-- return {includedirs = "", links = "", extras = {foo = ""}} -- end) -- -- @endcode -function _instance:extra(name) - local extra = self:get("extra") - if extra and name then - extra = extra[name] +-- +-- we can also get extra configuration from package/add_xxx +-- +-- e.g. +-- +-- @code +-- package("xxx") +-- add_linkgroups("foo", {group = true}) +-- +-- target:pkg("xxx"):extraconf("linkgroups", "foo", "group") +-- @endcode +-- +-- extras = { +-- linkgroups = { +-- z = { +-- group = true +-- } +-- } +-- } +-- +function _instance:extraconf(name, item, key) + local extraconfs = self:get("extras") + if not extraconfs then + return end - return extra + + -- get configuration + local extraconf = extraconfs[name] + + -- get configuration value + local value = extraconf + if item then + value = extraconf and extraconf[item] or nil + if value == nil and extraconf and type(item) == "table" then + value = extraconf[table.concat(item, "_")] + end + if value and key then + value = value[key] + end + end + return value end -- get order dependencies of the given component diff --git a/xmake/core/project/target.lua b/xmake/core/project/target.lua index 607bc68cc..c11ad8aaa 100644 --- a/xmake/core/project/target.lua +++ b/xmake/core/project/target.lua @@ -297,6 +297,178 @@ function _instance:_is_loaded() return self._LOADED end +-- get values from target deps with {interface|public = ...} +function _instance:_get_from_deps(name, result_values, result_sources, opt) + local orderdeps = self:orderdeps() + local total = #orderdeps + for idx, _ in ipairs(orderdeps) do + local dep = orderdeps[total + 1 - idx] + local depinherit = self:extraconf("deps", dep:name(), "inherit") + if depinherit == nil or depinherit then + local values = dep:get(name, opt) + if values ~= nil then + table.insert(result_values, values) + table.insert(result_sources, "dep::" .. dep:name()) + end + local dep_values = {} + local dep_sources = {} + dep:_get_from_options(name, dep_values, dep_sources, opt) + dep:_get_from_packages(name, dep_values, dep_sources, opt) + for idx, values in ipairs(dep_values) do + local dep_source = dep_sources[idx] + table.insert(result_values, values) + table.insert(result_sources, "dep::" .. dep:name() .. "/" .. dep_source) + end + end + end +end + +-- get values from target options with {interface|public = ...} +function _instance:_get_from_options(name, result_values, result_sources, opt) + for _, opt_ in ipairs(self:orderopts(opt)) do + local values = opt_:get(name) + if values ~= nil then + table.insert(result_values, values) + table.insert(result_sources, "option::" .. opt_:name()) + end + end +end + +-- get values from target packages with {interface|public = ...} +function _instance:_get_from_packages(name, result_values, result_sources, opt) + for _, pkg in ipairs(self:orderpkgs(opt)) do + local configinfo = self:pkgconfig(pkg:name()) + -- get values from package components + -- e.g. `add_packages("sfml", {components = {"graphics", "window"}})` + local selected_components = configinfo and configinfo.components or pkg:components_default() + if selected_components and pkg:components() then + local components_enabled = hashset.new() + for _, comp in ipairs(table.wrap(selected_components)) do + components_enabled:insert(comp) + for _, dep in ipairs(table.wrap(pkg:component_orderdeps(comp))) do + components_enabled:insert(dep) + end + end + components_enabled:insert("__base") + -- if we can't find the values from the component, we need to fall back to __base to find them. + -- it contains some common values of all components + local values = {} + local components = table.wrap(pkg:components()) + for _, component_name in ipairs(table.join(pkg:components_orderlist(), "__base")) do + if components_enabled:has(component_name) then + local info = components[component_name] + if info then + table.join2(values, info[name]) + else + local components_str = table.concat(table.wrap(configinfo.components), ", ") + utils.warning("unknown component(%s) in add_packages(%s, {components = {%s}})", component_name, pkg:name(), components_str) + end + end + end + if #values > 0 then + table.insert(result_values, values) + table.insert(result_sources, "package::" .. pkg:name()) + end + -- get values instead of the builtin configs if exists extra package config + -- e.g. `add_packages("xxx", {links = "xxx"})` + elseif configinfo and configinfo[name] then + local values = configinfo[name] + if values ~= nil then + table.insert(result_values, values) + table.insert(result_sources, "package::" .. pkg:name()) + end + else + -- get values from the builtin package configs + local values = pkg:get(name) + if values ~= nil then + table.insert(result_values, values) + table.insert(result_sources, "package::" .. pkg:name()) + end + end + end +end + +-- get values from the given source +function _instance:_get_from_source(name, source, result_values, result_sources, opt) + if source == "self" then + local values = self:get(name, opt) + if values ~= nil then + table.insert(result_values, values) + table.insert(result_sources, "self") + end + elseif source:startswith("dep::") then + local depname = source:split("::", {plain = true, limit = 2})[2] + if depname == "*" then + self:_get_from_deps(name, result_values, result_sources, opt) + else + local depsource + local splitinfo = depname:split("/", {plain = true}) + if #splitinfo == 2 then + depname = splitinfo[1] + depsource = splitinfo[2] + end + local dep = self:dep(depname) + if dep then + -- e.g. + -- dep::foo/option::bar + -- dep::foo/package::bar + if depsource then + local dep_values = {} + local dep_sources = {} + dep:_get_from_source(name, depsource, dep_values, dep_sources, opt) + for idx, values in ipairs(dep_values) do + local dep_source = dep_sources[idx] + table.insert(result_values, values) + table.insert(result_sources, "dep::" .. depname .. "/" .. dep_source) + end + else + -- dep::foo + local values = dep:get(name, opt) + if values ~= nil then + table.insert(result_values, values) + table.insert(result_sources, source) + end + end + end + end + elseif source:startswith("option::") then + local optname = source:split("::", {plain = true, limit = 2})[2] + if optname == "*" then + self:_get_from_options(name, result_values, result_sources, opt) + else + local opt_ = self:opt(optname, opt) + if opt_ then + local values = opt_:get(name) + if values ~= nil then + table.insert(result_values, values) + table.insert(result_sources, source) + end + end + end + elseif source:startswith("package::") then + local pkgname = source:split("::", {plain = true, limit = 2})[2] + if pkgname == "*" then + self:_get_from_packages(name, result_values, result_sources, opt) + else + local pkg = self:pkg(pkgname, opt) + if pkg then + local values = pkg:get(name) + if values ~= nil then + table.insert(result_values, values) + table.insert(result_sources, source) + end + end + end + elseif source == "*" then + self:_get_from_source(name, "self", result_values, result_sources, opt) + self:_get_from_source(name, "option::*", result_values, result_sources, opt) + self:_get_from_source(name, "package::*", result_values, result_sources, opt) + self:_get_from_source(name, "dep::*", result_values, result_sources, {interface = true}) + else + os.raise("target:get_from(): unknown source %s", source) + end +end + -- clone target, @note we can just call it in after_load() function _instance:clone() if not self:_is_loaded() then @@ -385,73 +557,92 @@ function _instance:get(name, opt) end end --- get values from target dependencies +-- deprecated: get values from target dependencies function _instance:get_from_deps(name, opt) - local values = {} - local orderdeps = self:orderdeps() - local total = #orderdeps - for idx, _ in ipairs(orderdeps) do - local dep = orderdeps[total + 1 - idx] - local depinherit = self:extraconf("deps", dep:name(), "inherit") - if depinherit == nil or depinherit then - table.join2(values, dep:get(name, opt)) - table.join2(values, dep:get_from_opts(name, opt)) - table.join2(values, dep:get_from_pkgs(name, opt)) + local result = {} + local values = self:get_from(name, "dep::*", opt) + if values then + for _, v in ipairs(values) do + table.join2(result, v) end end - return values + return result end --- get values from target options with {interface|public = ...} +-- deprecated: get values from target options with {interface|public = ...} function _instance:get_from_opts(name, opt) - local values = {} - for _, opt_ in ipairs(self:orderopts(opt)) do - table.join2(values, table.wrap(opt_:get(name))) + local result = {} + local values = self:get_from(name, "option::*", opt) + if values then + for _, v in ipairs(values) do + table.join2(result, v) + end end - return values + return result end --- get values from target packages with {interface|public = ...} +-- deprecated: get values from target packages with {interface|public = ...} function _instance:get_from_pkgs(name, opt) - local values = {} - for _, pkg in ipairs(self:orderpkgs(opt)) do - local configinfo = self:pkgconfig(pkg:name()) - -- get values from package components - -- e.g. `add_packages("sfml", {components = {"graphics", "window"}})` - local selected_components = configinfo and configinfo.components or pkg:components_default() - if selected_components and pkg:components() then - local components_enabled = hashset.new() - for _, comp in ipairs(table.wrap(selected_components)) do - components_enabled:insert(comp) - for _, dep in ipairs(table.wrap(pkg:component_orderdeps(comp))) do - components_enabled:insert(dep) - end - end - components_enabled:insert("__base") - -- if we can't find the values from the component, we need to fall back to __base to find them. - -- it contains some common values of all components - local components = table.wrap(pkg:components()) - for _, component_name in ipairs(table.join(pkg:components_orderlist(), "__base")) do - if components_enabled:has(component_name) then - local info = components[component_name] - if info then - table.join2(values, info[name]) - else - local components_str = table.concat(table.wrap(configinfo.components), ", ") - utils.warning("unknown component(%s) in add_packages(%s, {components = {%s}})", component_name, pkg:name(), components_str) - end - end - end - -- get values instead of the builtin configs if exists extra package config - -- e.g. `add_packages("xxx", {links = "xxx"})` - elseif configinfo and configinfo[name] then - table.join2(values, configinfo[name]) - else - -- get values from the builtin package configs - table.join2(values, pkg:get(name)) + local result = {} + local values = self:get_from(name, "package::*", opt) + if values then + for _, v in ipairs(values) do + table.join2(result, v) end end - return values + return result +end + +-- get values from the given sources +-- +-- e.g. +-- +-- only from the current target: +-- target:get_from("links") +-- target:get_from("links", "self") +-- +-- from the given dep: +-- target:get_from("links", "dep::foo") +-- target:get_from("links", "dep::foo", {interface = true}) +-- target:get_from("links", "dep::*") +-- +-- from the given option: +-- target:get_from("links", "option::foo") +-- target:get_from("links", "option::*") +-- +-- from the given package: +-- target:get_from("links", "package::foo") +-- target:get_from("links", "package::*") +-- +-- from the given dep/option, dep/package +-- target:get_from("links", "dep::foo/option::bar") +-- target:get_from("links", "dep::foo/option::*") +-- target:get_from("links", "dep::foo/package::bar") +-- target:get_from("links", "dep::foo/package::*") +-- +-- from the multiple sources: +-- target:get_from("links", {"self", "option::foo", "dep::bar", "package::zoo"}) +-- target:get_from("links", {"self", "option::*", "dep::*", "package::*"}) +-- +-- from all: +-- target:get_from("links", "*") +-- +-- return: +-- local values, sources = target:get_from("links", "*") +-- for idx, value in ipairs(values) do +-- local source = sources[idx] +-- end +-- +function _instance:get_from(name, sources, opt) + local result_values = {} + local result_sources = {} + sources = sources or "self" + for _, source in ipairs(table.wrap(sources)) do + self:_get_from_source(name, source, result_values, result_sources, opt) + end + if #result_values > 0 then + return result_values, result_sources + end end -- set the value to the target info @@ -488,6 +679,68 @@ function _instance:extraconf_set(name, item, key, value) self._INFO:extraconf_set(name, item, key, value) end +-- get the extra configuration from the given source +-- +-- e.g. +-- +-- only from the current target: +-- target:extraconf_from("links") +-- target:extraconf_from("links", "self") +-- +-- from the given dep: +-- target:extraconf_from("links", "dep::foo") +-- +-- from the given option: +-- target:extraconf_from("links", "option::foo") +-- +-- from the given package: +-- target:extraconf_from("links", "package::foo") +-- +-- from the given dep/option, dep/package +-- target:extraconf_from("links", "dep::foo/option::bar") +-- target:extraconf_from("links", "dep::foo/package::bar") +-- +function _instance:extraconf_from(source, name, item, key) + source = source or "self" + if source == "self" then + return self:extraconf(name, item, key) + elseif source:startswith("dep::") then + local depname = source:split("::", {plain = true, limit = 2})[2] + local depsource + local splitinfo = depname:split("/", {plain = true}) + if #splitinfo == 2 then + depname = splitinfo[1] + depsource = splitinfo[2] + end + local dep = self:dep(depname) + if dep then + -- e.g. + -- dep::foo/option::bar + -- dep::foo/package::bar + if depsource then + return dep:extraconf_from(dep_source, name, item, key) + else + -- dep::foo + return dep:extraconf(name, item, key) + end + end + elseif source:startswith("option::") then + local optname = source:split("::", {plain = true, limit = 2})[2] + local opt_ = self:opt(optname, opt) + if opt_ then + return opt_:extraconf(name, item, key) + end + elseif source:startswith("package::") then + local pkgname = source:split("::", {plain = true, limit = 2})[2] + local pkg = self:pkg(pkgname, opt) + if pkg then + return pkg:extraconf(name, item, key) + end + else + os.raise("target:extraconf_from(): unknown source %s", source) + end +end + -- get configuration source information of the given api item function _instance:sourceinfo(name, item) return self._INFO:sourceinfo(name, item) @@ -884,26 +1137,28 @@ function _instance:is_rebuilt() end -- get the enabled option -function _instance:opt(name) - return self:opts()[name] +function _instance:opt(name, opt) + return self:opts(opt)[name] end -- get the enabled options -function _instance:opts() - - -- attempt to get it from cache first - if self._OPTS_ENABLED then - return self._OPTS_ENABLED +function _instance:opts(opt) + opt = opt or {} + local cachekey = "opts" + if opt.public then + cachekey = cachekey .. "_public" + elseif opt.interface then + cachekey = cachekey .. "_interface" end - - -- load options if be enabled - self._OPTS_ENABLED = {} - for _, opt in ipairs(self:orderopts()) do - self._OPTS_ENABLED[opt:name()] = opt + local opts = self:_memcache():get(cachekey) + if not opts then + opts = {} + for _, opt_ in ipairs(self:orderopts(opt)) do + opts[opt_:name()] = opt_ + end + self:_memcache():set(cachekey, opts) end - - -- get it - return self._OPTS_ENABLED + return opts end -- get the enabled ordered options with {public|interface = ...} @@ -917,8 +1172,6 @@ function _instance:orderopts(opt) end local orderopts = self:_memcache():get(cachekey) if not orderopts then - - -- load options if be enabled orderopts = {} for _, name in ipairs(table.wrap(self:get("options", opt))) do local opt_ = nil @@ -927,17 +1180,6 @@ function _instance:orderopts(opt) table.insert(orderopts, opt_) end end - - -- load options from packages if no require info, be compatible with the option package in (*.pkg) - for _, name in ipairs(table.wrap(self:get("packages", opt))) do - if not project_package.load(name) then - local opt_ = nil - if config.get(name) then opt_ = option.load(name) end - if opt_ then - table.insert(orderopts, opt_) - end - end - end self:_memcache():set(cachekey, orderopts) end return orderopts diff --git a/xmake/core/tool/builder.lua b/xmake/core/tool/builder.lua index f07fc39d6..cfd3d19cf 100644 --- a/xmake/core/tool/builder.lua +++ b/xmake/core/tool/builder.lua @@ -125,6 +125,19 @@ function builder:_flagkinds() return self._FLAGKINDS end +-- get the extra configuration from value +function builder:_extraconf(extras, value) + local extra = extras + if extra then + if type(value) == "table" then + extra = extra[table.concat(value, "_")] + else + extra = extra[value] + end + end + return extra +end + -- inherit flags (only for public/interface) from target deps -- -- e.g. @@ -241,7 +254,7 @@ function builder:_add_flags_from_target(flags, target) -- add flags from language targetflags = {} - self:_add_flags_from_language(targetflags, target) + self:_add_flags_from_language(targetflags, {target = target}) -- add flags for the target if target_type == "target" then @@ -278,8 +291,20 @@ function builder:_add_flags_from_argument(flags, target, args) end -- add flags (named) from the language - self:_add_flags_from_language(flags, nil, { - target = function (name) return args[name] end, + self:_add_flags_from_language(flags, {linkorders = args.linkorders, linkgroups = args.linkgroups, getters = { + target = function (name) + -- we need also to get extra from arguments + -- @see https://github.com/xmake-io/xmake/issues/4274 + -- + -- e.g. + -- package/add_linkgroups("xxx", {group = true}) + -- {linkgroups = , extras = { + -- linkgroups = {z = {group = true}} + -- }} + local values = args[name] + local extras = args.extras and args.extras[name] + return values, extras + end, toolchain = function (name) local plat, arch if target and target.plat then @@ -289,14 +314,20 @@ function builder:_add_flags_from_argument(flags, target, args) arch = target:arch() end return platform.toolconfig(name, plat, arch) - end}) + end}}) end -- add items from getter function builder:_add_items_from_getter(items, name, opt) - local values = opt.getter(name) + local values, extras = opt.getter(name) if values then - table.insert(items, {name = name, values = table.wrap(values), check = opt.check, multival = opt.multival, mapper = opt.mapper}) + table.insert(items, { + name = name, + values = table.wrap(values), + check = opt.check, + multival = opt.multival, + mapper = opt.mapper, + extras = extras}) end end @@ -307,7 +338,12 @@ function builder:_add_items_from_config(items, name, opt) values = path.splitenv(values) end if values then - table.insert(items, {name = name, values = table.wrap(values), check = opt.check, multival = opt.multival, mapper = opt.mapper}) + table.insert(items, { + name = name, + values = table.wrap(values), + check = opt.check, + multival = opt.multival, + mapper = opt.mapper}) end end @@ -321,7 +357,12 @@ function builder:_add_items_from_toolchain(items, name, opt) values = platform.toolconfig(name) end if values then - table.insert(items, {name = name, values = table.wrap(values), check = opt.check, multival = opt.multival, mapper = opt.mapper}) + table.insert(items, { + name = name, + values = table.wrap(values), + check = opt.check, + multival = opt.multival, + mapper = opt.mapper}) end end @@ -333,35 +374,46 @@ function builder:_add_items_from_option(items, name, opt) values = target:get(name) end if values then - table.insert(items, {name = name, values = table.wrap(values), check = opt.check, multival = opt.multival, mapper = opt.mapper}) + table.insert(items, { + name = name, + values = table.wrap(values), + check = opt.check, + multival = opt.multival, + mapper = opt.mapper}) end end -- add items from target function builder:_add_items_from_target(items, name, opt) - local values = {} local target = opt.target if target then - -- get flagvalues of target with given flagname - table.join2(values, target:get(name)) - - -- get flagvalues of the attached options and packages - table.join2(values, target:get_from_opts(name)) - table.join2(values, target:get_from_pkgs(name)) - - -- get flagvalues (public or interface) of all dependent targets (contain packages/options) - table.join2(values, target:get_from_deps(name, {interface = true})) - end - if values and #values > 0 then - table.insert(items, {name = name, values = table.wrap(values), check = opt.check, multival = opt.multival, mapper = opt.mapper}) + local result, sources = target:get_from(name, "*") + if result then + for idx, values in ipairs(result) do + local source = sources[idx] + local extras = target:extraconf_from(source, name) + values = table.wrap(values) + if values and #values > 0 then + table.insert(items, { + name = name, + values = values, + extras = extras, + check = opt.check, + multival = opt.multival, + mapper = opt.mapper}) + end + end + end end end -- add flags from the language -function builder:_add_flags_from_language(flags, target, getters) +function builder:_add_flags_from_language(flags, opt) + opt = opt or {} -- get order named items local items = {} + local target = opt.target for _, flaginfo in ipairs(self:_nameflags()) do -- get flag info @@ -391,37 +443,57 @@ function builder:_add_flags_from_language(flags, target, getters) -- map named flags to real flags local mapper = self:_tool()["nf_" .. apiname] if mapper then - local opt = {target = target, check = checkstate, multival = multival, mapper = mapper} - if getters then - local getter = getters[flagscope] + local opt_ = {target = target, check = checkstate, multival = multival, mapper = mapper} + if opt.getters then + local getter = opt.getters[flagscope] if getter then - opt.getter = getter - self:_add_items_from_getter(items, flagname, opt) + opt_.getter = getter + self:_add_items_from_getter(items, flagname, opt_) end elseif flagscope == "target" and target and target:type() == "target" then - self:_add_items_from_target(items, flagname, opt) + self:_add_items_from_target(items, flagname, opt_) elseif flagscope == "target" and target and target:type() == "option" then - self:_add_items_from_option(items, flagname, opt) + self:_add_items_from_option(items, flagname, opt_) elseif flagscope == "config" then - self:_add_items_from_config(items, flagname, opt) + self:_add_items_from_config(items, flagname, opt_) elseif flagscope == "toolchain" then - self:_add_items_from_toolchain(items, flagname, opt) + self:_add_items_from_toolchain(items, flagname, opt_) end end end -- sort links local kind = self:kind() - if (kind == "ld" or kind == "sh") and target and target:type() == "target" then - self:_sort_links_of_items(target, items) + if kind == "ld" or kind == "sh" then + local linkorders = table.wrap(opt.linkorders) + local linkgroups = table.wrap(opt.linkgroups) + if target and target:type() == "target" then + local values = target:get_from("linkorders", "*") + if values then + for _, value in ipairs(values) do + table.join2(linkorders, value) + end + end + values = target:get_from("linkgroups", "*") + if values then + for _, value in ipairs(values) do + table.join2(linkgroups, value) + end + end + end + if #linkorders > 0 or #linkgroups > 0 then + self:_sort_links_of_items(items, {linkorders = linkorders, linkgroups = linkgroups}) + end end -- get flags from the items for _, item in ipairs(items) do local check = item.check local mapper = item.mapper + local extras = item.extras if item.multival then - local results = mapper(self:_tool(), item.values, target, self:_targetkind()) + local extra = self:_extraconf(extras, item.values) + local results = mapper(self:_tool(), item.values, {target = target, targetkind = self:_targetkind(), extra = extra}) for _, flag in ipairs(table.wrap(results)) do if flag and flag ~= "" and (not check or self:has_flags(flag)) then table.insert(flags, flag) @@ -429,7 +501,8 @@ function builder:_add_flags_from_language(flags, target, getters) end else for _, flagvalue in ipairs(item.values) do - local flag = mapper(self:_tool(), flagvalue, target, self:_targetkind()) + local extra = self:_extraconf(extras, flagvalue) + local flag = mapper(self:_tool(), flagvalue, {target = target, targetkind = self:_targetkind(), extra = extra}) if flag and flag ~= "" and (not check or self:has_flags(flag)) then table.insert(flags, flag) end @@ -439,14 +512,15 @@ function builder:_add_flags_from_language(flags, target, getters) end -- sort links of items -function builder:_sort_links_of_items(target, items) +function builder:_sort_links_of_items(items, opt) + opt = opt or {} local sortlinks = false local makegroups = false - local linkorders = table.wrap(target:get("linkorders")) + local linkorders = table.wrap(opt.linkorders) if #linkorders > 0 then sortlinks = true end - local linkgroups = table.wrap(target:get("linkgroups")) + local linkgroups = table.wrap(opt.linkgroups) local linkgroups_set = hashset.new() if #linkgroups > 0 then makegroups = true @@ -460,34 +534,54 @@ function builder:_sort_links_of_items(target, items) -- get all links local links = {} local linkgroups_map = {} + local extras_map = {} local link_mapper local framework_mapper local linkgroup_mapper if sortlinks or makegroups then + local linkitems = {} table.remove_if(items, function (_, item) local name = item.name local removed = false + if name == "links" or name == "syslinks" then + link_mapper = item.mapper + removed = true + table.insert(linkitems, item) + elseif name == "frameworks" then + framework_mapper = item.mapper + removed = true + table.insert(linkitems, item) + elseif name == "linkgroups" then + linkgroup_mapper = item.mapper + removed = true + table.insert(linkitems, item) + end + return removed + end) + + -- @note table.remove_if will traverse backwards, + -- we need to fix the initial link order first to make sure the syslinks are in the correct order + linkitems = table.reverse(linkitems) + for _, item in ipairs(linkitems) do + local name = item.name for _, value in ipairs(item.values) do if name == "links" or name == "syslinks" then if not linkgroups_set:has(value) then table.insert(links, value) end - link_mapper = item.mapper - removed = true elseif name == "frameworks" then table.insert(links, "framework::" .. value) - framework_mapper = item.mapper - removed = true elseif name == "linkgroups" then - local key = target:extraconf("linkgroups", value, "name") or tostring(value) + local extras = item.extras + local extra = self:_extraconf(extras, value) + local key = extra and extra.name or tostring(value) table.insert(links, "linkgroup::" .. key) linkgroups_map[key] = value - linkgroup_mapper = item.mapper - removed = true + extras_map[key] = extras end end - return removed - end) + end + links = table.reverse_unique(links) end @@ -505,12 +599,24 @@ function builder:_sort_links_of_items(target, items) end -- we need remove cycle in original links -- e.g. + -- + -- case1: -- original_deps: a -> b -> c -> d -> e -- new deps: e -> b - -- graph: a -> b -> c -> d e (remove d -> e) - -- /\ | - -- | | + -- graph: a -> b -> c -> d e (remove d -> e, add d -> nil) + -- /|\ | -- -------------- + -- + -- case2: + -- original_deps: a -> b -> c -> d -> e + -- new deps: b -> a + -- + -- --------- + -- | \|/ + -- graph: a b -> c -> d -> e (remove a -> b, add a -> c) + -- /|\ | + -- ---- + -- local function remove_cycle_in_original_deps(f, t) local k local v = t @@ -522,7 +628,11 @@ function builder:_sort_links_of_items(target, items) end end if v == f and k ~= nil then - original_deps[k] = nil + -- break the original from node, link to next node + -- e.g. + -- case1: d -x-> e, d -> nil, k: d, f: e + -- case2: a -x-> b, a -> c, k: a, f: b + original_deps[k] = original_deps[f] end end local links_set = hashset.from(links) @@ -559,8 +669,9 @@ function builder:_sort_links_of_items(target, items) table.insert(items, {name = "frameworks", values = table.wrap(link), check = false, multival = false, mapper = framework_mapper}) elseif link:startswith("linkgroup::") then local key = link:sub(12) - local value = linkgroups_map[key] - table.insert(items, {name = "linkgroups", values = table.wrap(value), check = false, multival = false, mapper = linkgroup_mapper}) + local values = linkgroups_map[key] + local extras = extras_map[key] + table.insert(items, {name = "linkgroups", values = table.wrap(values), extras = extras, check = false, multival = false, mapper = linkgroup_mapper}) else table.insert(items, {name = "links", values = table.wrap(link), check = false, multival = false, mapper = link_mapper}) end diff --git a/xmake/languages/c++/load.lua b/xmake/languages/c++/load.lua index b35938783..932769a7c 100644 --- a/xmake/languages/c++/load.lua +++ b/xmake/languages/c++/load.lua @@ -94,6 +94,9 @@ function _get_apis() -- target.add_xxx "target.add_linkorders" , "target.add_linkgroups" + -- package.add_xxx + , "package.add_linkorders" + , "package.add_linkgroups" } apis.paths = { -- target.set_xxx diff --git a/xmake/languages/c++/xmake.lua b/xmake/languages/c++/xmake.lua index a6e497cc4..22a69a417 100644 --- a/xmake/languages/c++/xmake.lua +++ b/xmake/languages/c++/xmake.lua @@ -74,8 +74,8 @@ language("c++") , "toolchain.rpathdirs" , "toolchain.frameworkdirs" , "config.links" + , "target.linkgroups" -- we must move it before target.links, because we need sort correct order for package and its deps , "target.links" - , "target.linkgroups" , "toolchain.links" , "config.frameworks" , "target.frameworks" diff --git a/xmake/modules/core/tools/cl.lua b/xmake/modules/core/tools/cl.lua index 607f0f4d9..34927d179 100644 --- a/xmake/modules/core/tools/cl.lua +++ b/xmake/modules/core/tools/cl.lua @@ -88,7 +88,7 @@ function init(self) end -- make the symbol flags -function nf_symbols(self, levels, target) +function nf_symbols(self, levels, opt) local flags = nil local values = hashset.from(levels) if values:has("debug") then @@ -103,6 +103,7 @@ function nf_symbols(self, levels, target) -- generate *.pdb file local symbolfile = nil + local target = opt.target if target and target.symbolfile and not values:has("embed") then symbolfile = target:symbolfile() end @@ -296,7 +297,8 @@ function nf_includedir(self, dir) end -- make the force include flag -function nf_forceinclude(self, headerfile, target) +function nf_forceinclude(self, headerfile, opt) + local target = opt.target local sourcekinds = target and target:extraconf("forceincludes", headerfile, "sourcekinds") if not sourcekinds or table.contains(table.wrap(sourcekinds), self:kind()) then return {"-FI", headerfile} @@ -377,8 +379,9 @@ function nf_encoding(self, encoding) end -- make the c precompiled header flag -function nf_pcheader(self, pcheaderfile, target) +function nf_pcheader(self, pcheaderfile, opt) if self:kind() == "cc" then + local target = opt.target local objectfiles = target:objectfiles() if objectfiles then table.insert(objectfiles, target:pcoutputfile("c") .. ".obj") @@ -388,8 +391,9 @@ function nf_pcheader(self, pcheaderfile, target) end -- make the c++ precompiled header flag -function nf_pcxxheader(self, pcheaderfile, target) +function nf_pcxxheader(self, pcheaderfile, opt) if self:kind() == "cxx" then + local target = opt.target local objectfiles = target:objectfiles() if objectfiles then table.insert(objectfiles, target:pcoutputfile("cxx") .. ".obj") diff --git a/xmake/modules/core/tools/clang_cl.lua b/xmake/modules/core/tools/clang_cl.lua index ee795e9d7..69459ad3e 100644 --- a/xmake/modules/core/tools/clang_cl.lua +++ b/xmake/modules/core/tools/clang_cl.lua @@ -105,7 +105,8 @@ function nf_optimize(self, level) end -- make the c precompiled header flag -function nf_pcheader(self, pcheaderfile, target) +function nf_pcheader(self, pcheaderfile, opt) + local target = opt.target if self:kind() == "cc" then local objectfiles = target:objectfiles() if objectfiles then @@ -119,7 +120,8 @@ function nf_pcheader(self, pcheaderfile, target) end -- make the c++ precompiled header flag -function nf_pcxxheader(self, pcheaderfile, target) +function nf_pcxxheader(self, pcheaderfile, opt) + local target = opt.target if self:kind() == "cxx" then local objectfiles = target:objectfiles() if objectfiles then diff --git a/xmake/modules/core/tools/dmd.lua b/xmake/modules/core/tools/dmd.lua index e02a1ed0a..8be22bf97 100644 --- a/xmake/modules/core/tools/dmd.lua +++ b/xmake/modules/core/tools/dmd.lua @@ -72,7 +72,7 @@ function nf_strip(self, level) end -- make the symbol flag -function nf_symbol(self, level, target) +function nf_symbol(self, level) local kind = self:kind() if language.sourcekinds()[kind] then local maps = _g.symbol_maps @@ -83,7 +83,7 @@ function nf_symbol(self, level, target) _g.symbol_maps = maps end return maps[level .. '_' .. kind] or maps[level] - elseif (kind == "dcld" or kind == "dcsh") and target:is_plat("windows") and level == "debug" then + elseif (kind == "dcld" or kind == "dcsh") and self:is_plat("windows") and level == "debug" then return "-g" end end diff --git a/xmake/modules/core/tools/gcc.lua b/xmake/modules/core/tools/gcc.lua index 6589b6f13..1d4a9e8f9 100644 --- a/xmake/modules/core/tools/gcc.lua +++ b/xmake/modules/core/tools/gcc.lua @@ -81,7 +81,7 @@ function load(self) end -- make the strip flag -function nf_strip(self, level, target) +function nf_strip(self, level) local maps = { debug = "-Wl,-S" , all = "-s" @@ -283,7 +283,8 @@ function nf_sysincludedir(self, dir) end -- make the force include flag -function nf_forceinclude(self, headerfile, target) +function nf_forceinclude(self, headerfile, opt) + local target = opt.target local sourcekinds = target and target:extraconf("forceincludes", headerfile, "sourcekinds") if not sourcekinds or table.contains(table.wrap(sourcekinds), self:kind()) then return {"-include", headerfile} @@ -307,15 +308,16 @@ function nf_syslink(self, lib) end -- make the link group flag -function nf_linkgroup(self, linkgroup, target) +function nf_linkgroup(self, linkgroup, opt) local linkflags = {} for _, lib in ipairs(linkgroup) do table.insert(linkflags, nf_link(self, lib)) end local flags = {} - if not self:is_plat("macosx", "windows", "mingw") then - local group = target:extraconf("linkgroups", linkgroup, "group") - local whole = target:extraconf("linkgroups", linkgroup, "whole") + local extra = opt.extra + if extra and not self:is_plat("macosx", "windows", "mingw") then + local group = extra.group + local whole = extra.whole if group and whole then -- https://github.com/xmake-io/xmake/issues/4308 table.join2(flags, "-Wl,--whole-archive", "-Wl,--start-group", linkflags, "-Wl,--end-group", "-Wl,--no-whole-archive") @@ -324,7 +326,7 @@ function nf_linkgroup(self, linkgroup, target) elseif whole then table.join2(flags, "-Wl,--whole-archive", linkflags, "-Wl,--no-whole-archive") end - local static = target:extraconf("linkgroups", linkgroup, "static") + local static = extra.static if static then table.join2(flags, "-Wl,-Bstatic", linkflags, "-Wl,-Bdynamic") end @@ -416,8 +418,9 @@ function nf_encoding(self, encoding) end -- make the c precompiled header flag -function nf_pcheader(self, pcheaderfile, target) +function nf_pcheader(self, pcheaderfile, opt) if self:kind() == "cc" then + local target = opt.target local pcoutputfile = target:pcoutputfile("c") if self:name() == "clang" then return {"-include", pcheaderfile, "-include-pch", pcoutputfile} @@ -428,8 +431,9 @@ function nf_pcheader(self, pcheaderfile, target) end -- make the c++ precompiled header flag -function nf_pcxxheader(self, pcheaderfile, target) +function nf_pcxxheader(self, pcheaderfile, opt) if self:kind() == "cxx" then + local target = opt.target local pcoutputfile = target:pcoutputfile("cxx") if self:name() == "clang" then return {"-include", pcheaderfile, "-include-pch", pcoutputfile} @@ -440,8 +444,9 @@ function nf_pcxxheader(self, pcheaderfile, target) end -- make the objc precompiled header flag -function nf_pmheader(self, pcheaderfile, target) +function nf_pmheader(self, pcheaderfile, opt) if self:kind() == "mm" then + local target = opt.target local pcoutputfile = target:pcoutputfile("m") if self:name() == "clang" then return {"-include", pcheaderfile, "-include-pch", pcoutputfile} @@ -452,8 +457,9 @@ function nf_pmheader(self, pcheaderfile, target) end -- make the objc++ precompiled header flag -function nf_pmxxheader(self, pcheaderfile, target) +function nf_pmxxheader(self, pcheaderfile, opt) if self:kind() == "mxx" then + local target = opt.target local pcoutputfile = target:pcoutputfile("mxx") if self:name() == "clang" then return {"-include", pcheaderfile, "-include-pch", pcoutputfile} diff --git a/xmake/modules/core/tools/go.lua b/xmake/modules/core/tools/go.lua index 51f5709af..e7d660d75 100644 --- a/xmake/modules/core/tools/go.lua +++ b/xmake/modules/core/tools/go.lua @@ -42,8 +42,9 @@ function nf_optimize(self, level) end -- make the symbol flag -function nf_symbol(self, level, target, mapkind) - if mapkind ~= "object" then +function nf_symbol(self, level, opt) + local targetkind = opt.targetkind + if targetkind ~= "object" then return end local maps = { diff --git a/xmake/modules/core/tools/ld.lua b/xmake/modules/core/tools/ld.lua index e933a847c..29917f70e 100644 --- a/xmake/modules/core/tools/ld.lua +++ b/xmake/modules/core/tools/ld.lua @@ -38,7 +38,7 @@ function init(self) end -- make the strip flag -function nf_strip(self, level, target) +function nf_strip(self, level) local maps = { debug = "-S" , all = "-s" diff --git a/xmake/modules/core/tools/ldc2.lua b/xmake/modules/core/tools/ldc2.lua index 5a9f77026..49a1c451e 100644 --- a/xmake/modules/core/tools/ldc2.lua +++ b/xmake/modules/core/tools/ldc2.lua @@ -49,7 +49,7 @@ function nf_optimize(self, level) end -- make the symbol flag -function nf_symbol(self, level, target) +function nf_symbol(self, level) local kind = self:kind() if language.sourcekinds()[kind] then local maps = _g.symbol_maps @@ -61,7 +61,7 @@ function nf_symbol(self, level, target) _g.symbol_maps = maps end return maps[level .. '_' .. kind] or maps[level] - elseif (kind == "dcld" or kind == "dcsh") and target:is_plat("windows") and level == "debug" then + elseif (kind == "dcld" or kind == "dcsh") and self:is_plat("windows") and level == "debug" then return "-g" end end diff --git a/xmake/modules/core/tools/link.lua b/xmake/modules/core/tools/link.lua index ee68cf01f..9c53c2e28 100644 --- a/xmake/modules/core/tools/link.lua +++ b/xmake/modules/core/tools/link.lua @@ -58,9 +58,10 @@ function get(self, name) end -- make the strip flag -function nf_strip(self, level, target) +function nf_strip(self, level, opt) -- link.exe/arm64 does not support /opt:ref, /opt:icf + local target = opt.target if target and target:is_arch("arm64") then return end @@ -80,10 +81,11 @@ function nf_strip(self, level, target) end -- make the symbol flag -function nf_symbol(self, level, target) +function nf_symbol(self, level, opt) -- debug? generate *.pdb file local flags = nil + local target = opt.target if target then if target:type() == "target" then if level == "debug" and (target:is_binary() or target:is_shared()) then diff --git a/xmake/modules/core/tools/ml.lua b/xmake/modules/core/tools/ml.lua index 042937ed7..5bb1fb884 100644 --- a/xmake/modules/core/tools/ml.lua +++ b/xmake/modules/core/tools/ml.lua @@ -55,7 +55,7 @@ function init(self) end -- make the symbol flags -function nf_symbols(self, levels, target) +function nf_symbols(self, levels) local flags = nil local values = hashset.from(levels) if values:has("debug") then diff --git a/xmake/modules/core/tools/nim.lua b/xmake/modules/core/tools/nim.lua index 13d8fb3d6..b765f01f6 100644 --- a/xmake/modules/core/tools/nim.lua +++ b/xmake/modules/core/tools/nim.lua @@ -89,7 +89,7 @@ function nf_symbol(self, level) end -- make the strip flag -function nf_strip(self, level, target) +function nf_strip(self, level) if self:is_plat("linux", "macosx", "bsd") then if level == "debug" or level == "all" then return "--passL:-s" @@ -103,7 +103,7 @@ function nf_includedir(self, dir) end -- make the link flag -function nf_link(self, lib, target) +function nf_link(self, lib) if self:is_plat("windows") then return "--passL:" .. lib .. ".lib" else @@ -112,7 +112,7 @@ function nf_link(self, lib, target) end -- make the linkdir flag -function nf_linkdir(self, dir, target) +function nf_linkdir(self, dir) if self:is_plat("windows") then return {"--passL:-libpath:" .. path.translate(dir)} else diff --git a/xmake/modules/core/tools/nvcc.lua b/xmake/modules/core/tools/nvcc.lua index 038e2e75a..7050f86af 100644 --- a/xmake/modules/core/tools/nvcc.lua +++ b/xmake/modules/core/tools/nvcc.lua @@ -53,7 +53,7 @@ function init(self) end -- make the symbol flag -function nf_symbol(self, level, target) +function nf_symbol(self, level, opt) -- debug? generate *.pdb file local flags = nil @@ -62,6 +62,7 @@ function nf_symbol(self, level, target) if self:is_plat("windows") then local host_flags = nil local symbolfile = nil + local target = opt.target if target and target.symbolfile then symbolfile = target:symbolfile() end @@ -259,12 +260,12 @@ function nf_rpathdir(self, dir) end -- make the c precompiled header flag -function nf_pcheader(self, pcheaderfile, target) +function nf_pcheader(self, pcheaderfile) return {"-include", pcheaderfile} end -- make the c++ precompiled header flag -function nf_pcxxheader(self, pcheaderfile, target) +function nf_pcxxheader(self, pcheaderfile) return {"-include", pcheaderfile} end diff --git a/xmake/modules/core/tools/zig_cc.lua b/xmake/modules/core/tools/zig_cc.lua index 8fefb0f97..205ecddfe 100644 --- a/xmake/modules/core/tools/zig_cc.lua +++ b/xmake/modules/core/tools/zig_cc.lua @@ -22,7 +22,7 @@ inherit("gcc") -- make the strip flag -function nf_strip(self, level, target) +function nf_strip(self, level) local maps = { debug = "-Wl,-S" , all = "-s" diff --git a/xmake/modules/package/manager/xmake/find_package.lua b/xmake/modules/package/manager/xmake/find_package.lua index a1f2dfac2..0d5336fe4 100644 --- a/xmake/modules/package/manager/xmake/find_package.lua +++ b/xmake/modules/package/manager/xmake/find_package.lua @@ -245,6 +245,7 @@ function _find_package_from_repo(name, opt) -- get version and license result.version = manifest.version or path.filename(path.directory(path.directory(manifest_file))) result.license = manifest.license + result.extras = manifest.extras return result end @@ -352,5 +353,6 @@ function main(name, opt) if not result and opt.packagedirs then result = _find_package_from_packagedirs(name, opt) end + return result end diff --git a/xmake/modules/private/action/require/impl/register_packages.lua b/xmake/modules/private/action/require/impl/register_packages.lua index ae66b6e75..621e383d1 100644 --- a/xmake/modules/private/action/require/impl/register_packages.lua +++ b/xmake/modules/private/action/require/impl/register_packages.lua @@ -51,7 +51,7 @@ function _register_required_package_libs(instance, required_package, is_deps) fetchinfo.static = nil fetchinfo.shared = nil fetchinfo.installdir = nil - fetchinfo.extra = nil + fetchinfo.extras = nil fetchinfo.components = nil end |
