22 KiB
22 KiB
属性
scopeinfo
xmake.lua 生成属性信息,再根据类型生成
local _instance = scopeinfo
--[[
kind : scopetype {root , target , rule , package , task}
info : scopeconfig {come from xmake.lua}
opt : unknown
]]
-- new an instance
function _instance.new(kind, info, opt)
opt = opt or {}
local instance = table.inherit(_instance)
instance._KIND = kind or "root"
instance._INFO = info
instance._INTERPRETER = opt.interpreter
instance._DEDUPLICATE = opt.deduplicate
instance._ENABLE_FILTER = opt.enable_filter
--print("scope", kind , info.__scriptdir , info.kind)
return instance
end
Target
--[[
name : scopename
info : scopeinfo
]]
-- new a target instance
function _instance.new(name, info)
local instance = table.inherit(_instance)
instance._NAME = name
instance._INFO = info
instance._CACHEID = 1
return instance
end
Rule
-- new a rule instance
function rule.new(name, info, opt)
opt = opt or {}
local instance = table.inherit(_instance)
instance._NAME = name
instance._INFO = info
instance._PACKAGE = opt.package
if opt.package then
local deps = {}
for _, depname in ipairs(table.wrap(instance:get("deps"))) do
-- @xxx -> @package/xxx
if depname:startswith("@") and not depname:find("/", 1, true) then
depname = "@" .. opt.package:name() .. "/" .. depname:sub(2)
end
table.insert(deps, depname)
end
deps = table.unwrap(deps)
if deps and #deps > 0 then
instance:set("deps", deps)
end
for depname, extraconf in pairs(table.wrap(instance:extraconf("deps"))) do
if depname:startswith("@") and not depname:find("/", 1, true) then
depname = "@" .. opt.package:name() .. "/" .. depname:sub(2)
instance:extraconf_set("deps", depname, extraconf)
end
end
end
return instance
end
Package
-- new an instance
function _instance.new(name, info, opt)
opt = opt or {}
local instance = table.inherit(_instance)
instance._NAME = name
instance._INFO = info
instance._REPO = opt.repo
instance._SCRIPTDIR = opt.scriptdir and path.absolute(opt.scriptdir)
return instance
end
Task
-- new a task instance
function task.new(name, info)
local instance = table.inherit(task)
instance._NAME = name
instance._INFO = info
return instance
end
事件
只触发一次
Project
- targets
- _load_targets
- t:_load_before()
- r:_load_before()
- t:_load()
- r:_load()
- t:_load_before()
- t:_load_after()
- r:_load_after()
- _load_targets
local _instance = Project
function project.targets()
if project._memcache():get("targets", targets) then
return
end
local targets, errors = project._load_targets()
project._memcache():set("targets", targets)
for _, t in ipairs(project.ordertargets()) do
local ok, errors = t:_load_after()
end
end
-- load targets
function project._load_targets()
-- load all requires first and reload the project file to ensure has_package() works for targets
local requires = project.required_packages()
local ok, errors = project._load(true)
-- load targets
local results, errors = project._load_scope("target", true, true)
-- make targets
local targets = {}
for targetname, targetinfo in pairs(results) do
local t = target.new(targetname, targetinfo)
if t and (t:get("enabled") == nil or t:get("enabled") == true) then
targets[targetname] = t
end
end
-- load and attach target deps, rules and packages
for _, t in pairs(targets) do
-- load rules from target and language
t._RULES = t._RULES or {}
local rulenames = {}
for _, sourcefile in ipairs(table.wrap(t:get("files"))) do
local extension = path.extension((sourcefile:gsub("|.*$", "")))
local lang = language.load_ex(extension)
table.join2(rulenames, lang:rules())
end
rulenames = table.unique(rulenames)
for _, rulename in ipairs(rulenames) do
local r = project.rule(rulename) or rule.rule(rulename)
-- only add target rules
if r:kind() == "target" then
t._RULES[rulename] = r
for _, deprule in ipairs(r:orderdeps()) do
t._RULES[deprule:name()] = deprule
end
end
-- we need ignore `@package/rulename`, it will be loaded later
end
-- @note it's deprecated, please use on_load instead of before_load
ok, errors = t:_load_before()
-- 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()
end
return targets
end
Target
- _load_before
- _load_rules
- _load
- _load_rules
- on_load
- _load_after
- _load_rules
local _instance = Target
-- load rules suffix = {before nil after}
function _instance:_load_rules(suffix)
for k, r in ipairs(self:orderules()) do
local ok, errors = self:_load_rule(r, suffix)
end
return true
end
-- do before_load for rules
-- @note it's deprecated, please use on_load instead of before_load
function _instance:_load_before()
local ok, errors = self:_load_rules("before")
return true
end
-- do load target and rules
function _instance:_load()
-- do load with target rules
local ok, errors = self:_load_rules()
-- do load for target
local on_load = self:script("load")
ok, errors = sandbox.load(on_load, self)
-- mark as loaded
return true
end
-- do after_load target and rules
function _instance:_load_after()
-- enter the environments of the target packages
local oldenvs = os.addenvs(self:pkgenvs())
-- do load for target
local after_load = self:script("load_after")
if after_load then
local ok, errors = sandbox.load(after_load, self)
end
-- do after_load with target rules
local ok, errors = self:_load_rules("after")
-- leave the environments of the target packages
os.setenvs(oldenvs)
return true
end
Language
任务
Build
-
load
-
before_load
-
on_load
-
after_load
-
-
build
-
before_build
-
on_build
-
after_build
-
function main()
-- lock the whole project
project.lock()
-- config it first
task.run("config", {}, {disable_dump = true})
-- enter project directory
local oldir = os.cd(project.directory())
-- clean up temporary files once a day
cleaner.cleanup()
-- do rules before building
_do_project_rules("build_before")
-- do build
_do_build(targetname, group_pattern)
-- dump cache stats
if option.get("diagnosis") then
build_cache.dump_stats()
end
-- do rules after building
_do_project_rules("build_after")
-- unlock the whole project
project.unlock()
end
Project
-
install
-
before_install
-
on_install
-
after_install
-
-
load
- before_load
- on_load
- after_load
-- make target info
function getinfo._make_targetinfo(mode, arch, target)
-- init target info
local targetinfo =
{
mode = mode
, arch = arch
, plat = config.get("plat")
, vsarch = vsutils.vsarch(arch)
, sdkver = config.get("vs_sdkver")
}
-- write only if not default
-- use target:get("xxx") rather than target:xxx()
-- save target kind
targetinfo.kind = target:kind()
-- is default?
targetinfo.default = tostring(target:is_default())
-- save target file
targetinfo.basename = vsutils.escape(target:basename())
targetinfo.filename = vsutils.escape(target:filename())
-- save dirs
targetinfo.targetdir = _make_dirs(target:get("targetdir"))
targetinfo.buildir = _make_dirs(config.get("buildir"))
targetinfo.rundir = _make_dirs(target:get("rundir"))
targetinfo.configdir = _make_dirs(os.getenv("XMAKE_CONFIGDIR"))
targetinfo.configfiledir = _make_dirs(target:get("configdir"))
targetinfo.includedirs = _make_dirs(table.join(_get_values_from_target(target, "includedirs") or {}, _get_values_from_target(target, "sysincludedirs")))
targetinfo.linkdirs = _make_dirs(_get_values_from_target(target, "linkdirs"))
targetinfo.sourcedirs = _make_dirs(_get_values_from_target(target, "values.project.vsxmake.sourcedirs"))
targetinfo.pcheaderfile = target:pcheaderfile("cxx") or target:pcheaderfile("c")
-- save defines
targetinfo.defines = _make_arrs(_get_values_from_target(target, "defines"))
-- save languages
targetinfo.languages = _make_arrs(_get_values_from_target(target, "languages"))
if targetinfo.languages then
-- fix c++17 to cxx17 for Xmake.props
targetinfo.languages = targetinfo.languages:replace("c++", "cxx", {plain = true})
end
if target:is_phony() or target:is_headeronly() then
return targetinfo
end
-- save subsystem
local linkflags = linker.linkflags(target:kind(), target:sourcekinds(), {target = target})
for _, linkflag in ipairs(linkflags) do
if linkflag:lower():find("[%-/]subsystem:windows") then
targetinfo.subsystem = "windows"
end
end
if not targetinfo.subsystem then
targetinfo.subsystem = "console"
end
-- save runenvs
local runenvs = {}
local addrunenvs, setrunenvs = make_runenvs(target)
for k, v in table.orderpairs(target:pkgenvs()) do
addrunenvs = addrunenvs or {}
addrunenvs[k] = table.join(table.wrap(addrunenvs[k]), path.splitenv(v))
end
for _, dep in ipairs(target:orderdeps()) do
for k, v in table.orderpairs(dep:pkgenvs()) do
addrunenvs = addrunenvs or {}
addrunenvs[k] = table.join(table.wrap(addrunenvs[k]), path.splitenv(v))
end
end
for k, v in table.orderpairs(addrunenvs) do
if k:upper() == "PATH" then
runenvs[k] = _make_dirs(v) .. ";$([System.Environment]::GetEnvironmentVariable('" .. k .. "'))"
else
runenvs[k] = path.joinenv(v) .. ";$([System.Environment]::GetEnvironmentVariable('" .. k .."'))"
end
end
for k, v in table.orderpairs(setrunenvs) do
if #v == 1 then
v = v[1]
if path.is_absolute(v) and v:startswith(project.directory()) then
runenvs[k] = _make_dirs(v)
else
runenvs[k] = v[1]
end
else
runenvs[k] = path.joinenv(v)
end
end
local runenvstr = {}
for k, v in table.orderpairs(runenvs) do
table.insert(runenvstr, k .. "=" .. v)
end
targetinfo.runenvs = table.concat(runenvstr, "\n")
local runargs = target:get("runargs")
if runargs then
targetinfo.runargs = os.args(table.wrap(runargs))
end
-- use mfc? save the mfc runtime kind
if target:rule("win.sdk.mfc.shared_app") or target:rule("win.sdk.mfc.shared") then
targetinfo.mfckind = "Dynamic"
elseif target:rule("win.sdk.mfc.static_app") or target:rule("win.sdk.mfc.static") then
targetinfo.mfckind = "Static"
end
-- use cuda? save the cuda runtime version
if target:rule("cuda") then
local nvcc = find_tool("nvcc", { version = true })
local ver = semver.new(nvcc.version)
targetinfo.cudaver = ver:major() .. "." .. ver:minor()
end
return targetinfo
end
-- make vstudio project
function getinfo.main(outputdir, vsinfo)
-- enter project directory
local oldir = os.cd(project.directory())
-- init solution directory
vsinfo.solution_dir = path.absolute(path.join(outputdir, "vsxmake" .. vsinfo.vstudio_version))
vsinfo.programdir = _make_dirs(xmake.programdir())
vsinfo.projectdir = project.directory()
vsinfo.sln_projectfile = path.relative(project.rootfile(), vsinfo.solution_dir)
local projectfile = path.filename(project.rootfile())
vsinfo.slnfile = project.name() or path.filename(project.directory())
-- write only if not default
vsinfo.xmake_info = format("xmake version %s", xmake.version())
vsinfo.solution_id = hash.uuid4(project.directory() .. vsinfo.solution_dir)
vsinfo.vs_version = vsinfo.project_version .. ".0"
-- init modes
vsinfo.modes = _make_vsinfo_modes()
-- init archs
vsinfo.archs = _make_vsinfo_archs()
-- init groups
local groups, group_deps = _make_vsinfo_groups()
vsinfo.groups = table.orderkeys(groups)
vsinfo.group_deps = table.orderkeys(group_deps)
vsinfo._groups = groups
vsinfo._group_deps = group_deps
-- init config flags
local flags = {}
for k, v in table.orderpairs(localcache.get("config", "options")) do
if k ~= "plat" and k ~= "mode" and k ~= "arch" and k ~= "clean" and k ~= "buildir" then
table.insert(flags, "--" .. k .. "=" .. tostring(v))
end
end
vsinfo.configflags = os.args(flags)
-- load targets
local targets = {}
vsinfo._arch_modes = {}
for _, mode in ipairs(vsinfo.modes) do
vsinfo._arch_modes[mode] = {}
for _, arch in ipairs(vsinfo.archs) do
vsinfo._arch_modes[mode][arch] = { mode = mode, arch = arch }
-- trace
print("checking for %s.%s ...", mode, arch)
-- reload config, project and platform
-- modify config
config.set("as", nil, {force = true}) -- force to re-check as for ml/ml64
config.set("mode", mode, {readonly = true, force = true})
config.set("arch", arch, {readonly = true, force = true})
-- clear all options
for _, opt in ipairs(project.options()) do
opt:clear()
end
-- clear cache
memcache.clear()
-- check platform
platform.load(config.plat(), arch):check()
-- check project options
project.check()
-- install and update requires
install_requires()
-- load package rules for targets
_load_package_rules_for_targets()
-- config targets
_config_targets()
-- update config files
generate_configfiles()
generate_configheader()
-- ensure to enter project directory
os.cd(project.directory())
-- save targets
for targetname, target in table.orderpairs(project.targets()) do
-- https://github.com/xmake-io/xmake/issues/2337
target:data_set("plugin.project.kind", "vsxmake")
-- make target with the given mode and arch
targets[targetname] = targets[targetname] or {}
local _target = targets[targetname]
-- init target info
_target.target = targetname
_target.vcxprojdir = path.join(vsinfo.solution_dir, targetname)
_target.target_id = hash.uuid4(targetname)
_target.kind = target:kind()
_target.absscriptdir = target:scriptdir()
_target.scriptdir = path.relative(target:scriptdir(), _target.vcxprojdir)
_target.projectdir = path.relative(project.directory(), _target.vcxprojdir)
local targetdir = target:get("targetdir")
if targetdir then _target.targetdir = path.relative(targetdir, _target.vcxprojdir) end
_target._targets = _target._targets or {}
_target._targets[mode] = _target._targets[mode] or {}
local targetinfo = _make_targetinfo(mode, arch, target)
_target._targets[mode][arch] = targetinfo
_target.sdkver = targetinfo.sdkver
_target.default = targetinfo.default
-- save all sourcefiles and headerfiles
_target.sourcefiles = table.unique(table.join(_target.sourcefiles or {}, (target:sourcefiles())))
_target.headerfiles = table.unique(table.join(_target.headerfiles or {}, (target:headerfiles())))
-- sort them to stabilize generation
table.sort(_target.sourcefiles)
table.sort(_target.headerfiles)
-- save file groups
_target.filegroups = target:get("filegroups")
_target.filegroups_extraconf = target:extraconf("filegroups")
-- save deps
_target.deps = table.unique(table.join(_target.deps or {}, table.orderkeys(target:deps()), nil))
end
end
end
os.cd(oldir)
for _, target in table.orderpairs(targets) do
target._paths = {}
local dirs = {}
local projectdir = project.directory()
local root = target.absscriptdir or projectdir
target.sourcefiles = table.imap(target.sourcefiles, function(_, v) return path.relative(v, projectdir) end)
target.headerfiles = table.imap(target.headerfiles, function(_, v) return path.relative(v, projectdir) end)
for _, f in ipairs(table.join(target.sourcefiles, target.headerfiles)) do
local dir = _make_filter(f, target, root)
local escaped_f = vsutils.escape(f)
target._paths[f] =
{
-- @see https://github.com/xmake-io/xmake/issues/2077
path = path.is_absolute(escaped_f) and escaped_f or "$(XmakeProjectDir)\\" .. escaped_f,
dir = vsutils.escape(dir)
}
while dir and dir ~= "." do
if not dirs[dir] then
dirs[dir] =
{
dir = vsutils.escape(dir),
dir_id = hash.uuid4(dir)
}
end
dir = path.directory(dir) or "."
end
end
target._dirs = dirs
target.dirs = table.orderkeys(dirs)
target._deps = {}
for _, v in ipairs(target.deps) do
target._deps[v] = targets[v]
end
end
-- we need set startup project for default or binary target
-- @see https://github.com/xmake-io/xmake/issues/1249
local targetnames = {}
for targetname, target in table.orderpairs(project.targets()) do
if target:get("default") == true then
table.insert(targetnames, 1, targetname)
elseif target:is_binary() then
local first_target = targetnames[1] and project.target(targetnames[1])
if not first_target or first_target:is_default() then
table.insert(targetnames, 1, targetname)
else
table.insert(targetnames, targetname)
end
else
table.insert(targetnames, targetname)
end
end
vsinfo.targets = targetnames
vsinfo._targets = targets
return vsinfo
end
-- make
function make(version)
return function(outputdir)
-- trace
vprint("using project kind vs%d", version)
-- get info and params
local info = getinfo(outputdir, vsinfo(version))
local paramsprovidersln = _buildparams(info)
-- write solution file
local sln = path.join(info.solution_dir, info.slnfile .. ".sln")
_writefileifneeded(sln, render(template_sln, "#([A-Za-z0-9_,%.%*%(%)]+)#", "@([^@]+)@", paramsprovidersln))
-- add solution custom file
_trycp(template_props, info.solution_dir)
_trycp(template_targets, info.solution_dir)
for _, target in ipairs(info.targets) do
local paramsprovidertarget = _buildparams(info, target, "<!-- nil -->")
local proj_dir = info._targets[target].vcxprojdir
-- write project file
local proj = path.join(proj_dir, target .. ".vcxproj")
_writefileifneeded(proj, render(template_vcx, "#([A-Za-z0-9_,%.%*%(%)]+)#", "@([^@]+)@", paramsprovidertarget))
local projfil = path.join(proj_dir, target .. ".vcxproj.filters")
_writefileifneeded(projfil, render(template_fil, "#([A-Za-z0-9_,%.%*%(%)]+)#", "@([^@]+)@", paramsprovidertarget))
-- add project custom file
_trycp(template_props, proj_dir)
_trycp(template_targets, proj_dir)
_trycp(template_items, proj_dir)
_trycp(template_itemfil, proj_dir)
end
-- clear config and local cache
_clear_cache()
-- save plugin arguments for autoupdate
_save_plugin_arguments()
end
end
-- main
function main()
-- in project generator?
os.setenv("XMAKE_IN_PROJECT_GENERATOR", "true")
-- config it first
task.run("config")
-- make project
_make[option.get("kind")](option.get("outputdir"))
-- trace
cprint("${color.success}create ok!")
os.setenv("XMAKE_IN_PROJECT_GENERATOR", nil)
end
库依赖
Package
-- register the required local package
function _register_required_package(instance, required_package)
-- disable it if this package is missing
if not instance:exists() then
required_package:enable(false)
else
-- clear require info first
required_package:clear()
-- add packages info with all dependencies
local envs = {}
_register_required_package_base(instance, required_package)
_register_required_package_libs(instance, required_package)
_register_required_package_envs(instance, envs)
for _, dep in ipairs(instance:librarydeps()) do
if instance:is_library() then
_register_required_package_libs(dep, required_package, true)
end
end
for _, dep in ipairs(instance:orderdeps()) do
if not dep:is_private() then
_register_required_package_envs(dep, envs)
end
end
if #table.keys(envs) > 0 then
required_package:add({envs = envs})
end
-- enable this require info
required_package:enable(true)
end
-- save this require info and flush the whole cache file
required_package:save()
end