--!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, Xmake Open Source Community. -- -- @author ruki -- @file target.lua -- -- imports import("core.base.option") import("core.base.hashset") import("core.project.rule") import("core.project.config") import("core.project.project") import("async.runjobs", {alias = "async_runjobs"}) import("async.jobgraph", {alias = "async_jobgraph"}) import("private.utils.batchcmds") import("private.utils.rule", {alias = "rule_utils"}) import("utils.progress", {alias = "progress_utils"}) -- clean target for rebuilding function _clean_target(target) if target:targetfile() then os.tryrm(target:symbolfile()) os.tryrm(target:targetfile()) end end -- match source files function _match_sourcefiles(sourcefile, filepatterns) for _, filepattern in ipairs(filepatterns) do if sourcefile:match(filepattern.pattern) == sourcefile then if filepattern.excludes then if filepattern.rootdir and sourcefile:startswith(filepattern.rootdir) then sourcefile = sourcefile:sub(#filepattern.rootdir + 2) end for _, exclude in ipairs(filepattern.excludes) do if sourcefile:match(exclude) == sourcefile then return false end end end return true end end end -- match sourcebatches function _match_sourcebatches(target, filepatterns) local newbatches = {} local sourcecount = 0 for rulename, sourcebatch in pairs(target:sourcebatches()) do local objectfiles = sourcebatch.objectfiles local dependfiles = sourcebatch.dependfiles local sourcekind = sourcebatch.sourcekind for idx, sourcefile in ipairs(sourcebatch.sourcefiles) do if _match_sourcefiles(sourcefile, filepatterns) then local newbatch = newbatches[rulename] if not newbatch then newbatch = {} newbatch.sourcekind = sourcekind newbatch.rulename = rulename newbatch.sourcefiles = {} end table.insert(newbatch.sourcefiles, sourcefile) if objectfiles then newbatch.objectfiles = newbatch.objectfiles or {} table.insert(newbatch.objectfiles, objectfiles[idx]) end if dependfiles then newbatch.dependfiles = newbatch.dependfiles or {} table.insert(newbatch.dependfiles, dependfiles[idx]) end newbatches[rulename] = newbatch sourcecount = sourcecount + 1 end end end if sourcecount > 0 then return newbatches end end -- add plain orders for targetjobs and deps function _add_targetjobs_plain_orders(jobgraph, target, dep, opt) local jobname, jobname_dep local job_kind = opt.job_kind -- Configure the build order only if dependency linking inheritance is not disabled. -- e.g. add_deps("foo", {links = false}) -- @see https://github.com/xmake-io/xmake/issues/6925 if job_kind == "build" and target:extraconf("deps", dep:name(), "links") ~= false then jobname = target:fullname() .. "/link" jobname_dep = dep:fullname() .. "/link" if not jobgraph:has(jobname) then jobname = string.format("%s/begin_%s", target:fullname(), job_kind) end if not jobgraph:has(jobname_dep) then jobname_dep = string.format("%s/end_%s", dep:fullname(), job_kind) end end if jobname and jobname_dep and jobgraph:has(jobname) and jobgraph:has(jobname_dep) then jobgraph:add_orders(jobname_dep, jobname) end end -- add deep orders for targetjobs and deps function _add_targetjobs_deep_orders(jobgraph, target, dep, opt) local jobname, jobname_dep local job_kind = opt.job_kind local target_fence = opt.target_fence if target_fence == nil and (job_kind == "prepare" or job_kind == "build") then target_fence = dep:policy("build.fence") or dep:policy("build.across_targets_in_parallel") == false end if target_fence then jobname = string.format("%s/begin_%s", target:fullname(), job_kind) jobname_dep = string.format("%s/end_%s", dep:fullname(), job_kind) -- build.across_targets_in_parallel is deprecated if dep:policy("build.across_targets_in_parallel") == false then wprint("policy(\"build.across_targets_in_parallel\") has been deprecated, please use policy(\"build.fence\") instead of it.") end end if jobname and jobname_dep and jobgraph:has(jobname) and jobgraph:has(jobname_dep) then jobgraph:add_orders(jobname_dep, jobname) end end -- add target jobs for the builtin script function add_targetjobs_for_builtin_script(jobgraph, target, opt) opt = opt or {} local job_kind = opt.job_kind or "build" if target:is_static() or target:is_binary() or target:is_shared() or target:is_object() or target:is_moduleonly() then if job_kind == "prepare" then import("private.action.build.prepare_files", {anonymous = true})(jobgraph, target, opt) elseif job_kind == "link" then import("private.action.build.link_objects", {anonymous = true})(jobgraph, target, opt) elseif job_kind == "build" then import("private.action.build.build_" .. target:kind(), {anonymous = true})(jobgraph, target, opt) end end end -- add target jobs for the given script function add_targetjobs_for_script(jobgraph, target, instance, opt) opt = opt or {} local has_script = false local buildcmds = opt.buildcmds local job_opt = opt.job_opt local job_prefix = target:fullname() if target == instance then job_prefix = job_prefix .. "/target" else job_prefix = job_prefix .. "/rule/" .. instance:fullname() end -- call script if not has_script and not buildcmds then local script_name = opt.script_name local script = instance:script(script_name) if script then -- call custom script with jobgraph -- e.g. -- -- target("test") -- on_build(function (target, jobgraph, opt) -- end, {jobgraph = true}) if instance:extraconf(script_name, "jobgraph") then script(target, jobgraph) elseif instance:extraconf(script_name, "batch") then wprint("%s.%s: the batch mode is deprecated, please use jobgraph mode instead of it, or disable `build.jobgraph` policy to use it.", instance:fullname(), script_name) else -- call custom script directly -- e.g. -- -- target("test") -- on_build(function (target, opt) -- end) local jobname = string.format("%s/%s", job_prefix, script_name) jobgraph:add(jobname, function (index, total, opt) script(target, table.join({progress = opt.progress}, job_opt)) end) end has_script = true end end -- call command script -- e.g. -- -- target("test") -- on_buildcmd(function (target, batchcmds, opt) -- end) if not has_script then local scriptcmd_name = opt.scriptcmd_name local scriptcmd = instance:script(scriptcmd_name) if scriptcmd then local jobname = string.format("%s/%s", job_prefix, scriptcmd_name) jobgraph:add(jobname, function (index, total, opt) if buildcmds then -- only generate cmds and do not run them, use cases: e.g. project generator scriptcmd(target, buildcmds, {progress = opt.progress}) else local batchcmds_ = batchcmds.new({target = target}) scriptcmd(target, batchcmds_, table.join({progress = opt.progress}, job_opt)) batchcmds_:runcmds({changed = target:is_rebuilt(), dryrun = option.get("dry-run")}) end end) has_script = true end end return has_script end -- add target jobs with the given stage -- stage: before, after or "" function add_targetjobs_with_stage(jobgraph, target, stage, opt) opt = opt or {} local job_kind = opt.job_kind local ignored_rules = opt.ignored_rules -- the group name, e.g. foo/after_prepare, bar/before_build local group_name = string.format("%s/%s_%s", target:fullname(), stage ~= "" and stage or "on", job_kind) -- the script name, e.g. before/after_prepare, before/after_build local script_name = stage ~= "" and (job_kind .. "_" .. stage) or job_kind -- the command script name, e.g. before/after_preparecmd, before/after_buildcmd local scriptcmd_name = stage ~= "" and (job_kind .. "cmd_" .. stage) or (job_kind .. "cmd") -- call target and rules script local instances = {target} for _, ruleinst in ipairs(target:orderules()) do -- we only ignore some builtin rules, so we need not to use fullname. if target:rule_is_enabled(ruleinst:fullname()) and (not ignored_rules or not ignored_rules:has(ruleinst:name())) then table.insert(instances, ruleinst) end end -- on_config is different from on_build/on_prepare, -- it does not rewrite all rules, and target.on_config needs to be called last. if job_kind == "config" then instances = table.slice(instances, 2) table.insert(instances, target) end local jobsize = jobgraph:size() jobgraph:group(group_name, function () local has_script = false local script_opt = { script_name = script_name, scriptcmd_name = scriptcmd_name, buildcmds = opt.buildcmds, job_opt = opt.job_opt } for _, instance in ipairs(instances) do -- we need to use this group to sort rule scripts with add_orders local script_group = group_name .. "/" .. instance:fullname() jobgraph:group(script_group, function () if add_targetjobs_for_script(jobgraph, target, instance, script_opt) then has_script = true end end) -- if custom target.on_build/prepare exists, we need to ignore all scripts in rules if has_script and instance == target and stage == "" then break end end -- call builtin script, e.g. on_prepare, on_build, ... if not has_script and stage == "" then add_targetjobs_for_builtin_script(jobgraph, target, opt) end end) -- no any new jobs if jobgraph:size() == jobsize then return end -- sort build rules rule_utils.build_orders_in_jobgraph(jobgraph, target, instances, {root_group = group_name}) return group_name end -- add target jobs for the given target function add_targetjobs(jobgraph, target, opt) opt = opt or {} if not target:is_enabled() then return end local pkgenvs = _g.pkgenvs if pkgenvs == nil then pkgenvs = {} _g.pkgenvs = pkgenvs end local buildcmds = opt.buildcmds local job_kind = opt.job_kind local for_generator = opt.for_generator local job_begin = string.format("%s/begin_%s", target:fullname(), job_kind) local job_end = string.format("%s/end_%s", target:fullname(), job_kind) jobgraph:add(job_begin, function (index, total, opt) if buildcmds then return end -- enter package environments -- https://github.com/xmake-io/xmake/issues/4033 -- -- maybe mixing envs isn't a great solution, -- but it's the most efficient compromise compared to setting envs in every on_build_file. -- if target:pkgenvs() then pkgenvs.oldenvs = pkgenvs.oldenvs or os.getenvs() pkgenvs.newenvs = pkgenvs.newenvs or {} pkgenvs.newenvs[target] = target:pkgenvs() local newenvs = pkgenvs.oldenvs for _, envs in pairs(pkgenvs.newenvs) do newenvs = os.joinenvs(envs, newenvs) end os.setenvs(newenvs) end -- clean target first if rebuild if job_kind == "prepare" and target:is_rebuilt() and not for_generator and not option.get("dry-run") then _clean_target(target) end end) jobgraph:add(job_end, function (index, total, opt) if buildcmds then return end -- restore environments if target:pkgenvs() then pkgenvs.oldenvs = pkgenvs.oldenvs or os.getenvs() pkgenvs.newenvs = pkgenvs.newenvs or {} pkgenvs.newenvs[target] = nil local newenvs = pkgenvs.oldenvs for _, envs in pairs(pkgenvs.newenvs) do newenvs = os.joinenvs(envs, newenvs) end os.setenvs(newenvs) end end) -- add jobs with target stage, e.g. begin -> before_xxx -> on_xxx -> after_xxx local with_stages = opt.with_stages local group, group_before, group_after if not with_stages or with_stages:has("on") then group = add_targetjobs_with_stage(jobgraph, target, "", opt) end if not with_stages or with_stages:has("before") then group_before = add_targetjobs_with_stage(jobgraph, target, "before", opt) end if not with_stages or with_stages:has("after") then group_after = add_targetjobs_with_stage(jobgraph, target, "after", opt) end jobgraph:add_orders(job_begin, group_before, group, group_after, job_end) end -- add target jobs for the given target and deps function add_targetjobs_and_deps(jobgraph, target, targetrefs, opt) local targetname = target:fullname() if not targetrefs[targetname] then targetrefs[targetname] = target add_targetjobs(jobgraph, target, opt) for _, depname in ipairs(target:get("deps")) do local dep = project.target(depname, {namespace = target:namespace()}) add_targetjobs_and_deps(jobgraph, dep, targetrefs, opt) _add_targetjobs_plain_orders(jobgraph, target, dep, opt) end -- we need to pass to the whole dependency chain -- @see https://github.com/xmake-io/xmake/issues/6586 for _, dep in ipairs(target:orderdeps()) do _add_targetjobs_deep_orders(jobgraph, target, dep, opt) end end end -- get target jobs function get_targetjobs(targets_root, opt) local jobgraph = async_jobgraph.new(opt.job_kind) local targetrefs = {} for _, target in ipairs(targets_root) do add_targetjobs_and_deps(jobgraph, target, targetrefs, opt) end return jobgraph end -- add file jobs for the given script function add_filejobs_for_script(jobgraph, target, instance, sourcebatch, opt) opt = opt or {} local has_script = false local buildcmds = opt.buildcmds local job_prefix = target:fullname() local file_group = sourcebatch.rulename if target == instance then job_prefix = job_prefix .. "/target/" .. file_group else job_prefix = job_prefix .. "/rule/" .. file_group end -- call script files if not has_script and not buildcmds then local script_files_name = opt.script_files_name local script_files = instance:script(script_files_name) if script_files then -- call custom script with jobgraph -- e.g. -- -- target("test") -- on_build_files(function (target, jobgraph, sourcebatch, opt) -- end, {jobgraph = true}) local distcc = instance:extraconf(script_files_name, "distcc") if instance:extraconf(script_files_name, "jobgraph") then script_files(target, jobgraph, sourcebatch, {distcc = distcc}) elseif instance:extraconf(script_files_name, "batch") then wprint("%s.%s: the batch mode is deprecated, please use jobgraph mode instead of it, or disable `build.jobgraph` policy to use it.", instance:fullname(), script_files_name) else -- call custom script directly -- e.g. -- -- target("test") -- on_build_files(function (target, sourcebatch, opt) -- end) local jobname = string.format("%s/%s", job_prefix, script_files_name) jobgraph:add(jobname, function (index, total, opt) script_files(target, sourcebatch, {progress = opt.progress, distcc = distcc}) end) end has_script = true end end -- call script file if not has_script and not buildcmds then local script_file_name = opt.script_file_name local script_file = instance:script(script_file_name) if script_file then -- call custom script with jobgraph -- e.g. -- -- target("test") -- on_build_file(function (target, jobgraph, sourcefile, opt) -- end, {jobgraph = true}) local distcc = instance:extraconf(script_file_name, "distcc") local sourcekind = sourcebatch.sourcekind if instance:extraconf(script_file_name, "jobgraph") then for _, sourcefile in ipairs(sourcebatch.sourcefiles) do script_file(target, jobgraph, sourcefile, {sourcekind = sourcekind, distcc = distcc}) end elseif instance:extraconf(script_file_name, "batch") then wprint("%s.%s: the batch mode is deprecated, please use jobgraph mode instead of it, or disable `build.jobgraph` policy to use it.", instance:fullname(), script_file_name) else -- call custom script to process sourcefiles in parallel directly -- e.g. -- -- target("test") -- on_build_file(function (target, sourcefile, opt) -- end) for _, sourcefile in ipairs(sourcebatch.sourcefiles) do local jobname = string.format("%s/%s/%s", job_prefix, script_file_name, sourcefile) jobgraph:add(jobname, function (index, total, opt) script_file(target, sourcefile, {progress = opt.progress, sourcekind = sourcekind, distcc = distcc}) end) end end has_script = true end end -- call command script files -- e.g. -- -- target("test") -- on_buildcmd_files(function (target, batchcmds, sourcebatch, opt) -- end) if not has_script then local scriptcmd_files_name = opt.scriptcmd_files_name local scriptcmd_files = instance:script(scriptcmd_files_name) if scriptcmd_files then local distcc = instance:extraconf(scriptcmd_files_name, "distcc") local jobname = string.format("%s/%s", job_prefix, scriptcmd_files_name) jobgraph:add(jobname, function (index, total, opt) -- only generate cmds and do not run them, use cases: e.g. project generator if buildcmds then scriptcmd_files(target, buildcmds, sourcebatch, {progress = opt.progress}) else local batchcmds_ = batchcmds.new({target = target}) scriptcmd_files(target, batchcmds_, sourcebatch, {progress = opt.progress, distcc = distcc}) batchcmds_:runcmds({changed = target:is_rebuilt(), dryrun = option.get("dry-run")}) end end) has_script = true end end -- call command script file -- e.g. -- -- target("test") -- on_buildcmd_file(function (target, batchcmds, sourcefile, opt) -- end) if not has_script then local scriptcmd_file_name = opt.scriptcmd_file_name local scriptcmd_file = instance:script(scriptcmd_file_name) if scriptcmd_file then local sourcekind = sourcebatch.sourcekind local distcc = instance:extraconf(scriptcmd_file_name, "distcc") if buildcmds then -- only generate cmds and do not run them, use cases: e.g. project generator local jobname = string.format("%s/%s", job_prefix, scriptcmd_file_name) jobgraph:add(jobname, function (index, total, opt) for _, sourcefile in ipairs(sourcebatch.sourcefiles) do scriptcmd_file(target, buildcmds, sourcefile, {progress = opt.progress, sourcekind = sourcekind}) end end) else -- fall back to using batchcmd to process sourcefiles in parallel for _, sourcefile in ipairs(sourcebatch.sourcefiles) do local jobname = string.format("%s/%s/%s", job_prefix, scriptcmd_file_name, sourcefile) jobgraph:add(jobname, function (index, total, opt) local batchcmds_ = batchcmds.new({target = target}) scriptcmd_file(target, batchcmds_, sourcefile, {progress = opt.progress, sourcekind = sourcekind, distcc = distcc}) batchcmds_:runcmds({changed = target:is_rebuilt(), dryrun = option.get("dry-run")}) end) end end has_script = true end end return has_script end -- add file jobs with the given stage -- stage: before, after or "" -- function add_filejobs_with_stage(jobgraph, target, sourcebatches, stage, opt) opt = opt or {} local buildcmds = opt.buildcmds local ignored_rules = opt.ignored_rules local job_kind = opt.job_kind local job_kind_file = job_kind .. "_file" local job_kind_files = job_kind .. "_files" local job_kindcmd_file = job_kind .. "cmd_file" local job_kindcmd_files = job_kind .. "cmd_files" -- the group name, e.g. foo/after_prepare_files, bar/before_build_files local group_name = string.format("%s/%s_%s_files", target:fullname(), stage ~= "" and stage or "on", job_kind) -- the script name, e.g. before/after_prepare_files, before/after_build_files local script_file_name = stage ~= "" and (job_kind_file .. "_" .. stage) or job_kind_file local script_files_name = stage ~= "" and (job_kind_files .. "_" .. stage) or job_kind_files -- the command script name, e.g. before/after_preparecmd_files, before/after_buildcmd_files local scriptcmd_file_name = stage ~= "" and (job_kindcmd_file .. "_" .. stage) or job_kindcmd_file local scriptcmd_files_name = stage ~= "" and (job_kindcmd_files .. "_" .. stage) or job_kindcmd_files -- build sourcebatches map local instances = {target} local sourcebatches_map = {} local sourcebatches_for_target = {} for _, sourcebatch in pairs(sourcebatches) do local rulename = sourcebatch.rulename if rulename then -- we only ignore some builtin rules, so we need not to use fullname. local ruleinst = rule_utils.get_rule(target, rulename) if target:rule_is_enabled(ruleinst:fullname()) and (not ignored_rules or not ignored_rules:has(ruleinst:name())) then sourcebatches_map[ruleinst] = sourcebatch -- avoid duplicate scripts being called twice in the target, -- we just build sourcebatch with on_build_files scripts -- -- for example, c++.build and c++.build.modules.builder rules have same sourcefiles, -- but we just build it for c++.build -- -- @see https://github.com/xmake-io/xmake/issues/3171 -- if ruleinst:script("build_file") or ruleinst:script("build_files") then table.insert(sourcebatches_for_target, sourcebatch) end table.insert(instances, ruleinst) end else table.insert(sourcebatches_for_target, sourcebatch) end end -- call target and rules script local jobsize = jobgraph:size() jobgraph:group(group_name, function () local script_opt = { script_file_name = script_file_name, script_files_name = script_files_name, scriptcmd_file_name = scriptcmd_file_name, scriptcmd_files_name = scriptcmd_files_name, buildcmds = buildcmds } local has_target_script = false for _, instance in ipairs(instances) do -- we need to use this group to sort rule scripts with add_orders local script_group = group_name .. "/" .. instance:fullname() jobgraph:group(script_group, function () if instance == target then for _, sourcebatch in ipairs(sourcebatches_for_target) do local has_script = add_filejobs_for_script(jobgraph, target, instance, sourcebatch, script_opt) -- if custom target.on_build_file[s] exists, we need to ignore all scripts in rules if has_script and stage == "" then has_target_script = true end end elseif not has_target_script then -- rule local sourcebatch = sourcebatches_map[instance] if sourcebatch then add_filejobs_for_script(jobgraph, target, instance, sourcebatch, script_opt) end end end) end end) -- no any new jobs if jobgraph:size() == jobsize then return end -- sort build rules rule_utils.build_orders_in_jobgraph(jobgraph, target, instances, {root_group = group_name}) return group_name end -- add file jobs for the given target function add_filejobs(jobgraph, target, opt) opt = opt or {} if not target:is_enabled() then return end -- get sourcebatches local sourcebatches local filepatterns = opt.filepatterns if filepatterns then sourcebatches = _match_sourcebatches(target, filepatterns) else sourcebatches = target:sourcebatches() end -- add file jobs with target stage, e.g. before_xxx_files -> on_xxx_files -> after_xxx_files local with_stages = opt.with_stages local group, group_before, group_after if not with_stages or with_stages:has("on") then group = add_filejobs_with_stage(jobgraph, target, sourcebatches, "", opt) end if not with_stages or with_stages:has("before") then group_before = add_filejobs_with_stage(jobgraph, target, sourcebatches, "before", opt) end if not with_stages or with_stages:has("after") then group_after = add_filejobs_with_stage(jobgraph, target, sourcebatches, "after", opt) end jobgraph:add_orders(group_before, group, group_after) end -- add file jobs for the given target and deps function add_filejobs_and_deps(jobgraph, target, targetrefs, opt) local targetname = target:fullname() if not targetrefs[targetname] then targetrefs[targetname] = target add_filejobs(jobgraph, target, opt) for _, depname in ipairs(target:get("deps")) do local dep = project.target(depname, {namespace = target:namespace()}) add_filejobs_and_deps(jobgraph, dep, targetrefs, opt) end end end -- get files jobs function get_filejobs(targets_root, opt) local jobgraph = async_jobgraph.new(opt.job_kind) local targetrefs = {} for _, target in ipairs(targets_root) do add_filejobs_and_deps(jobgraph, target, targetrefs, opt) end return jobgraph end -- add link jobs for the given target function add_linkjobs(jobgraph, target, opt) opt = table.clone(opt or {}) opt.job_kind = "link" local with_stages = opt.with_stages local group, group_before, group_after if not with_stages or with_stages:has("on") then group = add_targetjobs_with_stage(jobgraph, target, "", opt) end if not with_stages or with_stages:has("before") then group_before = add_targetjobs_with_stage(jobgraph, target, "before", opt) end if not with_stages or with_stages:has("after") then group_after = add_targetjobs_with_stage(jobgraph, target, "after", opt) end jobgraph:add_orders(group_before, group, group_after) end -- get link depfiles function get_linkdepfiles(target) local extrafiles = {} for _, dep in ipairs(target:orderdeps()) do if dep:kind() == "static" then table.insert(extrafiles, dep:targetfile()) end end local linkdepfiles = target:data("linkdepfiles") if linkdepfiles then table.join2(extrafiles, linkdepfiles) end local objectfiles = target:objectfiles() local depfiles = objectfiles if #extrafiles > 0 then depfiles = table.join(objectfiles, extrafiles) end return depfiles end -- get all root targets function get_root_targets(targetnames, opt) opt = opt or {} -- get root targets local targets_root = {} if targetnames then for _, targetname in ipairs(table.wrap(targetnames)) do local target = project.target(targetname) if target then table.insert(targets_root, target) if option.get("rebuild") then target:data_set("rebuilt", true) if not option.get("shallow") then for _, dep in ipairs(target:orderdeps()) do dep:data_set("rebuilt", true) end end end end end else local all = opt.all local group_pattern = opt.group_pattern local depset = hashset.new() local targets = {} for _, target in ipairs(project.ordertargets()) do if target:is_enabled() then local group = target:get("group") if (target:is_default() and not group_pattern) or all or option.get("all") or (group_pattern and group and group:match(group_pattern)) then for _, depname in ipairs(target:get("deps")) do depset:insert(depname) end table.insert(targets, target) end end end for _, target in ipairs(targets) do if not depset:has(target:name()) then table.insert(targets_root, target) end if option.get("rebuild") then target:data_set("rebuilt", true) end end end return targets_root end -- run target-level jobs, e.g. on_prepare, on_build, ... function run_targetjobs(targets_root, opt) opt = opt or {} local job_kind = opt.job_kind local jobgraph = get_targetjobs(targets_root, opt) if jobgraph and not jobgraph:empty() then local curdir = os.curdir() local runjobs_opt = { comax = opt.jobs or option.get("jobs") or 1, curdir = curdir, distcc = opt.distcc, remote_only = opt.remote_only, progress_factor = opt.progress_factor, progress_refresh = true } async_runjobs(job_kind, jobgraph, runjobs_opt) os.cd(curdir) return true end end -- run files-level jobs, e.g. on_prepare_files, on_build_files, ... function run_filejobs(targets_root, opt) opt = opt or {} local job_kind = opt.job_kind local jobgraph = get_filejobs(targets_root, opt) if jobgraph and not jobgraph:empty() then local curdir = os.curdir() local runjobs_opt = { comax = opt.jobs or option.get("jobs") or 1, curdir = curdir, distcc = opt.distcc, remote_only = opt.remote_only, progress_factor = opt.progress_factor, progress_refresh = true } async_runjobs(job_kind, jobgraph, runjobs_opt) os.cd(curdir) return true end end