# 属性 ## scopeinfo xmake.lua 生成属性信息,再根据类型生成 ```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 ```lua --[[ 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 ```lua -- 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 ```lua -- 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 ```lua -- 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_after() - r:_load_after() ```lua 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 - ```lua 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 ```lua 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 ```lua -- 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 ``` ```lua -- 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, "") 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 ```lua -- 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 ```