- update premake usage module

This commit is contained in:
Ilya Shurumov 2021-06-10 13:43:39 +06:00 committed by InspirationByte
parent be0b8da453
commit ddfc77db15
2 changed files with 502 additions and 353 deletions

View File

@ -0,0 +1,79 @@
-- Stack Table
-- Uses a table as stack, use <table>:push(value) and <table>:pop()
-- Lua 5.1 compatible
unpack = table.unpack or unpack
-- GLOBAL
Stack = {}
-- Create a Table with stack functions
function Stack:Create()
-- stack table
local t = {}
-- entry table
t._et = {}
-- push a value on to the stack
function t:push(...)
if ... then
local targs = {...}
-- add values
for _,v in ipairs(targs) do
table.insert(self._et, v)
end
end
end
-- pop a value from the stack
function t:pop(num)
-- get num values from stack
local num = num or 1
-- return table
local entries = {}
-- get values into entries
for i = 1, num do
-- get last entry
if #self._et ~= 0 then
table.insert(entries, self._et[#self._et])
-- remove last value
table.remove(self._et)
else
break
end
end
-- return unpacked entries
return unpack(entries)
end
-- get entries
function t:getn()
return #self._et
end
-- get entry form index
function t:get(index)
return self._et[index]
end
-- list values
function t:list()
for i,v in pairs(self._et) do
print(i, v)
end
end
-- list values
function t:find_value(value)
for i,v in pairs(self._et) do
if v == value then
return i
end
end
return nil
end
return t
end

View File

@ -21,7 +21,7 @@
--
-- project "app"
-- kind "ConsoleApp"
-- uses "core_lib"
-- uses "core"
--
-- "core" will link vendor and openssh and define 'USING_VENDOR'
-- "app" will link 'core' and define 'USING_VENDOR'
@ -44,49 +44,33 @@
-- Using a project will link the project and resolves nested uses,
-- in addition to any declared usage.
--
-- TO CONSIDER: not sure this is needed anymore.
-- 'uses' can filter down to 'linkoptions' or 'buildoptions'
-- uses "linkoptions:opengl"
-- uses "buildoptions:opengl"
-- link options are defined as: links, linkoptions, libdirs.
-- buildoptions are defined as anything that isn't a link option.
-- TO CONSIDER: separate api, uses_linkoptions/uses_buildoptions?
--
-- The context of a 'usage' is the ultimate target context it is being
-- used in.
--
-- usage "external_lib"
-- links "external_lib-%{cfg.buildcfg}"
--
-- Filters work as you'd expect:
--
-- usage "debug_stuff"
-- defines "DEBUG_STUFF"
--
-- project "shared"
-- kind "SharedLib"
-- filter "configurations:debug"
-- uses "debug_stuff"
--
-- project "app"
-- kind "ConsoleApp"
-- uses "shared" -- DEBUG_STUFF will be defined in the debug configuration.
--
-- Todo
-- ----
--
-- Cyclic reference detection. We are allowed to use a project more than
-- once, (eg. from different configurations) but not along the same call path.
-- Decide what to do with diamond shaped uses graphs, include twice, or
-- first use?
--
-- Handle block insertion ordering and 'removes' correctly
--
-- "uses" do not respect configurations/filters due to havy optimizations
-- "usage" can contain configuration filters
require 'stack'
local p = premake
local oven = p.oven
local context = p.context
local keyLinks = "links"
local field = p.field
local fieldLinks = field.get(keyLinks)
newoption {
trigger = "usage-optimization-off",
description = "Turn off optimization for usage module. Useful for debuging dependencies (50% slower generation)."
}
local optimization = (_OPTIONS["usage-optimization-off"] == nil)
---
-- Two stacks which stores actual order of projects and usages
-- Projects stack and uses stack are aligned
--
local usesStack = Stack:Create()
local sourcesStack = Stack:Create()
--
-- 'uses' api
--
@ -122,383 +106,469 @@ p.api.register {
return nil
end
--
-- Given the current usage filter, does the new filter fit into it?
---
-- Push usage and source project name on stack
-- * if "usage" is already on stack it mean that "usage" in this configuration was in use,
--
local function compatibleUsage(currentUsageFilter, newUsageFilter)
if newUsageFilter and currentUsageFilter then
if currentUsageFilter ~= newUsageFilter then
return false
end
local printUsesStack
local function pushUsage( usageName, sourceName )
-- search for uses
local usesStackIndex = usesStack:find_value(usageName)
if usesStackIndex == nil then
verbosef("{PUSH} USES_STACK [%s]: %s", sourceName, usageName)
-- push on stack
usesStack:push(usageName)
sourcesStack:push(sourceName)
else
verbosef("{EXIST} USES_STACK [%s]: %s", sourceName, usageName)
end
return true
end
--
-- resolveUsage
---
-- Pop usage and source name form stack
--
local resolveAllUsesInBlock
local function popUsage( count )
local count = count or 1
for i = 1, count do
local function resolveUsage( targetProject, targetBlock,
usage, usageName, usageFilter,
inheritedCriteria )
-- pop form stack
local sourceName = sourcesStack:pop()
local usageName = usesStack:pop()
verbosef("\nProject %s is using usage %s %s\n",
targetProject.name, usage.name, usageFilter or "" )
verbosef("{POP} USES_STACK [%s]: %s", sourceName, usageName)
end
end
-- A map for how to copy the usage blocks into the target project.
-- Each entry in the map creates a new block.
--
-- During baking we can start new bake so we have to stash current stack
--
local stackStash = Stack:Create()
local propagateMap = {}
---
-- During "bake" process it is posible to call another "bake",
-- to starts new bake we have to stash current uses stack and create new empty
---
local commonFields = { "_criteria", "_basedir", "uses" }
local linkFields = { "links", "linkoptions", "libdirs" }
if usageFilter == nil then
-- We don't have a filter.
-- Create one block with the build options,
-- and another with only the links options that is filtered
-- for just link targets.
table.insert( propagateMap, {
usageFilter = "buildoptions",
excludeFields = linkFields
} )
table.insert( propagateMap, {
terms = {'kind:not StaticLib'},
usageFilter = "linkoptions",
includeFields = table.join( commonFields, linkFields )
} )
else
-- Respect the filter we have, and create one block with
-- either build or linkoptions
if usageFilter == "buildoptions" then
table.insert( propagateMap, {
usageFilter = "buildoptions",
excludeFields = linkFields
} )
local function stashStack()
if _OPTIONS["verbose"] then
for i , sourceName in ipairs(sourcesStack._et) do
verbosef("{STASH} USES_STACK [%s]: %s", sourceName, usesStack:get(i))
end
end
stackStash:push( {["sourcesStack"] = sourcesStack, ["usesStack"] = usesStack} )
sourcesStack = Stack:Create()
usesStack = Stack:Create()
end
if usageFilter == "linkoptions" then
table.insert( propagateMap, {
usageFilter = "linkoptions",
includeFields = table.join( commonFields, linkFields )
} )
---
-- After "bake" previous uses stack have to be recreated
---
local function unstashStack()
local stash = stackStash:pop()
sourcesStack = stash.sourcesStack
usesStack = stash.usesStack
if _OPTIONS["verbose"] then
for i , sourceName in ipairs(sourcesStack._et) do
verbosef("{UNSTASH} USES_STACK [%s]: %s", sourceName, usesStack:get(i))
end
end
end
--
-- Print stack
--
printUsesStack = function()
-- reconstructed full stack from stashed and current stacks
local fullStack = {}
-- for formating we need max length of sourceName, it will be first column
local maxSourceNameLen = 1
-- get full stack form stashed stacks. form oldest to newest
for _, stacks in ipairs(stackStash._et) do
if stacks.sourcesStack:getn() > 0 then
for i, sourceName in ipairs(stacks.sourcesStack._et) do
local len = sourceName:len()
if len > maxSourceNameLen then
maxSourceNameLen = len
end
table.insert(fullStack, { ["sourceName"] = sourceName, ["usageName"] = stacks.usesStack:get(i) } )
end
end
end
-- get stack form currect "bake"
for i, sourceName in ipairs(sourcesStack._et) do
local len = sourceName:len()
if len > maxSourceNameLen then
maxSourceNameLen = len
end
table.insert(fullStack, { ["sourceName"] = sourceName, ["usageName"] = usesStack:get(i) } )
end
-- print full stack in three columns
-- SOURCE: ACRION: USAGE:
--
-- Actions:
-- BAKES - mean that usage requred "bake" project
-- USES - use project or usage
print("\nUses stack:\n")
local line = string.format("%-" .. maxSourceNameLen .. "s ACRION: USAGE: \n", "SOURCE:")
print(line)
for _, entry in ipairs(fullStack) do
if not entry.sourceName:find('"bake"') then
if entry.usageName:find('"bake"') then
line = string.format("%-" .. maxSourceNameLen .. "s BAKES %s", entry.sourceName, entry.usageName:gsub('"bake" ', ""))
else
line = string.format("%-" .. maxSourceNameLen .. "s USES %s", entry.sourceName, entry.usageName)
end
print(line)
end
end
end
---
-- Resolve usage of targetProject.targetBlock
--
local function resolveUsage( targetProject, targetBlock, usage )
verbosef("\nProject %s is using usage %s\n",
targetProject.name, usage.name)
-- Clone each block in the usage and insert it into the target project
for _,usageBlock in ipairs(usage.blocks) do
-- Merge the criteria for the usage and target
for _,propagate in ipairs(propagateMap) do
-- detach fat references before deepcopy
usageOrigin = usageBlock._origin
usageBlock._origin = nil
local newBlock = table.deepcopy(usageBlock)
verbosef("\nApplying %s to project %s from using usage %s\n",
propagate.usageFilter, targetProject.name, usage.name )
-- attach fat references after deepcopy
newBlock._origin = usageOrigin
usageBlock._origin = usageOrigin
for _,usageBlock in ipairs(usage.blocks) do
newBlock._criteria.patterns = table.join(
newBlock._criteria.patterns,
targetBlock._criteria.patterns )
-- Merge the criteria for the usage, target, propagate
-- with any inherited criteria
newBlock._criteria.data =
p.criteria._compile(newBlock._criteria.patterns)
local newBlock = table.deepcopy(usageBlock)
newBlock._criteria.patterns = table.join(
newBlock._criteria.patterns,
targetBlock._criteria.patterns )
if inheritedCriteria ~= nil then
newBlock._criteria.patterns = table.join(
newBlock._criteria.patterns,
inheritedCriteria.patterns )
end
if propagate.terms ~= nil then
local propagateCriteria =
p.criteria.new( propagate.terms )
newBlock._criteria.patterns = table.join(
newBlock._criteria.patterns,
propagateCriteria.patterns )
end
newBlock._criteria.data =
p.criteria._compile(newBlock._criteria.patterns)
-- todo: would be nice not to do this.
-- needs to be in sync with internal block logic.
if newBlock.filename then
newBlock.filename = nil
newBlock._basedir = newBlock.basedir
newBlock.basedir = nil
end
-- Filter the field set of the new block to the ones we
-- are propagating
for field,v in pairs(usageBlock) do
local reject = false
if propagate.excludeFields ~= nil then
reject = table.contains(
propagate.excludeFields, field )
end
if propagate.includeFields ~= nil then
reject = reject or not table.contains(
propagate.includeFields, field )
end
if reject then
verbosef("ignoring %s from %s %s",
field, usage.name, propagate.usageFilter)
newBlock[field] = nil
else
verbosef("keeping %s from %s %s",
field, usage.name, propagate.usageFilter)
end
end
-- Insert the new block into the target project.
-- TODO: We need to insert as if at the call site,
-- and it need to deal with with 'removes'
-- merging between the two blocks.
table.insert( targetProject.blocks, newBlock )
-- Recurse into the new block and resolve any 'uses' there
resolveAllUsesInBlock(
targetProject,
newBlock,
propagate.usageFilter )
-- todo: would be nice not to do this.
-- needs to be in sync with internal block logic.
if newBlock.filename then
newBlock.filename = nil
newBlock._basedir = newBlock.basedir
newBlock.basedir = nil
end
-- Insert the new block into the target project.
-- TODO: We need to insert as if at the call site,
-- and it need to deal with with 'removes'
-- merging between the two blocks.
table.insert( targetProject.blocks, newBlock )
-- Recursion in usage is to fuzzy
if newBlock.uses then
error("Usage '" .. usage.name .. "'. Uses in usage is forbidden, move 'uses' to project.")
end
end
end
---
-- Insert usageName to outTable and push usageName on stack,
-- if srcProjectName and usageName are equal push is not performed.
-- This is common case and it should be in separeted function
--
local function insertUsage( usesTable, usageName, sourceName )
local usagePushed = 0
-- project can use own usage and it is valid use
-- without this check we will get error about double push on stack
if usageName ~= sourceName then
pushUsage( usageName, sourceName )
usagePushed = 1
end
table.insert( usesTable, usageName )
return usagePushed
end
---
-- Manually run "bake" process on source project if need,
-- if "bake" on sourceProject is under way this mean that we have circular dependence
--
-- Resolve a single 'use', with a given filter and criteria,
-- into a target block of a target project
--
local function resolveUse( targetProject, targetBlock,
usageName, usageFilter, inheritedCriteria )
local targetWorkspace = targetProject.solution
local sourceProject = p.workspace.findproject(
targetWorkspace, usageName )
local usage = p.workspace.findusage( targetWorkspace, usageName)
if sourceProject == nil and usage == nil then
local function bakeProject( sourceProject, targetProject )
-- _isUsageBaking - is flag on project which informs that premake alredy starts "bake" process
-- such situation occure with circular dependence
if sourceProject._isUsageBaking then
printUsesStack()
error("Use of "
.. "'" .. usageName.. "'"
.. ", is not defined as a usage or project in workspace "
.. "'" .. targetWorkspace.name .. "'")
.. "'" .. sourceProject.name .. "' in '" .. targetProject.name .. "'"
.." circular dependence!!!" )
else
if not sourceProject._usageBaked then
verbosef("\nUsage starts baking %s\n", sourceProject.name )
sourceProject._isUsageBaking = true
pushUsage( '"bake" ' .. sourceProject.name, targetProject.name ) -- source mark "bake" process on stack
if optimization then
bakeProjectUsage( sourceProject )
else
sourceProject.ctx = p.container.bake(sourceProject) -- bake context is temporary in current version of premake5, we have to store it
sourceProject._isBaked = false -- premake5 thinks that object was not baked
end
popUsage()
sourceProject._isUsageBaking = false
sourceProject._usageBaked = true
verbosef("\nUsage ends baking %s\n", sourceProject.name )
end
end
end
if sourceProject ~= nil and sourceProject.name ~= targetProject.name then
---
-- Resolve a sourceProject 'uses' into a target block of a target project
--
verbosef("\nProject %s is using project %s\n",
targetProject.name, sourceProject.name )
local resolveUse
-- The source project might not be baked yet, bake it now.
-- We need this for the configuration list.
-- TODO: not sure if we need to do some project configuration
-- mapping between source and target too? project.getconfig?
local function resolveUses( sourceProject, targetProject, targetBlock )
if optimization then
local targetProjectResolvedUses = targetProject.resolvedUses -- get table of already resolved uses for this projects
local sourceProjectResolvedUses = sourceProject.resolvedUses
local sourceProjectName = sourceProject.name
local targetBlockResolvedUsesLinks = targetBlock.resolvedUsesLinks
sourceProject = p.container.bake(sourceProject)
-- Iterate over the source project uses
for usageName,usageInfo in pairs(sourceProjectResolvedUses) do
-- project can use own usage and it is valid use
-- without this check we will get error about double push on stack
if usageName ~= usageInfo.parent then
pushUsage( '"bake" ' .. usageInfo.parent, '???' )
pushUsage( usageName, usageInfo.parent )
popUsage(2)
end
if not targetProjectResolvedUses[usageName] then
-- Iterate over the source project configurations and propagate
-- any 'uses' in each configuration from the source project to
-- the target project.
-- We do this per configuration so we know the 'kind' of that
-- configuration to influence what options are propagated
-- through the project.
usage = usageInfo.block
-- sourceProject include own usage
if usage then
resolveUsage( targetProject, targetBlock, usage )
end
for configName,configCtx in pairs(sourceProject.configs) do
if usageInfo.type == "project" then
-- When referring to a project, 'uses' acts like 'links' too.
table.insert(targetBlockResolvedUsesLinks, usageName )
end
-- If this source configuration is link target, we only want to
-- propagate buildoptions.
-- For example, if the source project is a shared library,
-- that linked with a static library, we don't also want the
-- target to link the same static library since it's already
-- linked in the source project.
targetProjectResolvedUses[usageName] = usageInfo
end
end
else
local pushCount = 0
local configKind = context.fetchvalue(configCtx, 'kind')
-- Store feched uses for future usage
if sourceProject.ctx.usesValues == nil then
sourceProject.ctx.usesValues = context.fetchvalue( sourceProject.ctx, 'uses' )
end
local configUsageFilter
if configKind ~= 'StaticLib' then
configUsageFilter = 'buildoptions'
local usesToResolve = {}
-- push all uses on stack before resolve, it guranty that all uses on current level are before uses are resolved
-- this is essential for duplicated uses detection
for k,v in ipairs(sourceProject.ctx.usesValues ) do
if type(v) == "table" then
for _, vName in ipairs(v) do
pushCount = pushCount + insertUsage( usesToResolve, vName, sourceProject.name )
end
else
pushCount = pushCount + insertUsage( usesToResolve, v, sourceProject.name )
end
end
-- Iterate over the source project uses'
for _,v in ipairs(usesToResolve) do
if v == sourceProject.name then
if not usage then
error( "Project " .. sourceProject.name .. " used itself but declares no usage")
end
else
resolveUse( targetProject,
targetBlock,
v )
end
end
if pushCount > 0 then
popUsage(pushCount)
end
end
end
---
-- Resolve a single 'use' into a target block of a target project
--
resolveUse = function( targetProject, targetBlock, usageName )
-- get table of already resolved uses for this projects
local resolvedUses = targetProject.resolvedUses
local usageType = "project"
local usageScriptPath = ""
-- if use already resolved SKIP it
if not resolvedUses[usageName] then
local targetWorkspace = targetProject.solution
local sourceProject = p.workspace.findproject( targetWorkspace, usageName )
local usage = p.workspace.findusage( targetWorkspace, usageName )
if sourceProject ~= nil and sourceProject.name ~= targetProject.name then
verbosef("\nProject %s is using project %s\n",
targetProject.name, sourceProject.name )
-- The source project might not be baked yet, bake it now.
-- We need this for the context.
bakeProject( sourceProject, targetProject )
resolveUses( sourceProject, targetProject, targetBlock )
-- sourceProject include own usage
if usage then
resolveUsage( targetProject, targetBlock, usage )
end
if compatibleUsage( usageFilter, configUsageFilter ) then
verbosef("== %s %s", configName,configKind)
-- Resolve the 'uses' in this configuration to the target.
-- Pass on this configuration as a criteria for the resolve.
-- todo: already exists in configCtx._criteria
-- todo: should be merging with inheritedCriteria?
local configCriteria =
p.criteria.new( {'configurations:' .. configName } )
local configUses = context.fetchvalue( configCtx, 'uses' )
local usedSelf = false
for k,v in ipairs(configUses) do
if v == sourceProject.name then
usedSelf = true
if usage then
resolveUsage( targetProject, targetBlock,
usage, v, configUsageFilter or usageFilter,
configCriteria )
else
error( "Project " .. sourceProject.name
.. " used itself but declares no usage")
end
else
resolveUse( targetProject,
targetBlock,
v,
configUsageFilter or usageFilter,
configCriteria )
end
end
-- The project did not use it's own usage
-- It is only exposing it only visible to other project.
-- Don't apply configUsageFilter
-- TO CONSIDER: not sure about that, but makes sense if we've not
-- linked internally what's specified in our usage.
if usage and not usedSelf then
verbosef("= Project %s config %s, did not resolve own usage to %s. Resolving now\n",
sourceProject.name, configName, targetProject.name )
resolveUsage( targetProject, targetBlock,
usage, usageName, usageFilter,
configCriteria )
end
-- When referring to a project, 'uses' acts like 'links' too.
table.insert(targetBlock.resolvedUsesLinks, usageName )
usageType = "project"
usageScriptPath = sourceProject.script
elseif usage ~= nil then
resolveUsage( targetProject, targetBlock, usage )
usageType = "usage"
usageScriptPath = usage.script
elseif sourceProject ~= nil then
error( "Project " .. sourceProject.name
.. " used itself but declares no usage")
else
-- throw an error on Windows and keep going on Linux
local isLinux = ( package.config:sub(1,1) == '/' )
local messageText = "Use of "
.. "'" .. usageName.. "'"
.. ", is not defined as a usage or project in workspace "
.. "'" .. targetWorkspace.name .. "'" .. " for " .. targetProject.name
if isLinux then
print( messageText )
return
else
error( messageText )
end
end
-- When referring to a project, 'uses' acts like 'links' too.
if usageFilter == nil or usageFilter == "linkoptions" then
local linkBlock = targetBlock
-- If we don't have a usageFilter, make sure this link only
-- occurs on linkable targets.
-- TODO: Functionally similar to block insertion for usages.
-- same requirements/todos/bugs, should dedupe.
if usageFilter == nil then
linkBlock = {}
-- todo: would be nice not to do this.
-- needs to be in sync with internal block logic.
if targetBlock.filename then
linkBlock._basedir = targetBlock.basedir
else
linkBlock._basedir = targetBlock._basedir
end
local linkCriteria =
p.criteria.new( {"kind:not StaticLib"} )
linkBlock._criteria = {}
linkBlock._criteria.patterns = table.join(
targetBlock._criteria.patterns,
linkCriteria.patterns)
linkBlock._criteria.data =
p.criteria._compile(linkBlock._criteria.patterns)
table.insert( targetProject.blocks, linkBlock )
end
local key = "links"
local field = p.field.get(key)
linkBlock[key] = p.field.store( field,
linkBlock[key],
sourceProject.name )
end
elseif usage ~= nil then
resolveUsage( targetProject, targetBlock,
usage, usageName, usageFilter,
inheritedCriteria )
resolvedUses[usageName] = { ["type"] = usageType, ["script"] = usageScriptPath, ["block"] = usage, ["parent"] = targetProject.name }
end
end
--
---
-- Resolve all uses from a target block in a target project
--
function resolveAllUsesInBlock( targetProject,
targetBlock,
inheritedUsageFilter )
local function resolveAllUsesInBlock( targetProject, targetBlock )
if targetBlock.uses then
for _, usageKey in ipairs(targetBlock.uses) do
local usageName = usageKey
local usageFilter
local separatorPos = usageKey:find(':')
if separatorPos then
usageName = usageKey:sub(separatorPos + 1)
usageFilter = usageKey:sub(0, separatorPos - 1)
local pushCount = 0
usesToResolve = {}
for _, usageKey in ipairs(targetBlock.uses) do
if type(usageKey) == "table" then
for _, usageName in ipairs(usageKey) do
pushCount = pushCount + insertUsage( usesToResolve, usageName, targetProject.name )
end
if compatibleUsage(inheritedUsageFilter, usageFilter) then
resolveUse( targetProject, targetBlock, usageName,
inheritedUsageFilter or usageFilter )
end
end
else
pushCount = pushCount + insertUsage( usesToResolve, usageKey, targetProject.name )
end
end
for _, usageName in ipairs(usesToResolve) do
resolveUse( targetProject, targetBlock, usageName )
end
if pushCount > 0 then
popUsage(pushCount)
end
end
--
-- Before baking a project, resolve all the 'uses'
--
function bakeProjectUsage( prj )
-- do not resolve "uses" twice
if prj.resolvedUses == nil then
-- create table of already resolved uses for this projects
prj.resolvedUses = {}
stashStack()
local blocks = {}
for k, v in pairs(prj.blocks) do
blocks[k] = v
end
pushUsage( prj.name, '"bake"' )
for _, block in pairs(blocks) do
if block.uses then
block.resolvedUsesLinks = {}
resolveAllUsesInBlock(prj, block)
-- When referring to a project, 'uses' acts like 'links' too.
block[keyLinks] = field.store( fieldLinks, block[keyLinks], fixOrder( block.resolvedUsesLinks ) )
end
end
popUsage()
unstashStack()
end
end
--
-- Put the linked projects in the right order
--
function fixOrder( resolvedUsesLinks )
if next(resolvedUsesLinks) ~= nil then
local fixedResolvedUsesLinks = {}
for i = #resolvedUsesLinks, 1, -1 do
table.insert( fixedResolvedUsesLinks, resolvedUsesLinks[ i ] )
end
return fixedResolvedUsesLinks
end
return resolvedUsesLinks
end
--
-- Register override
--
premake.override(p.project, "bake", function(base, self)
-- Keep the list stable while we iterate and modify it
local blocks = {}
for k, v in pairs(self.blocks) do
blocks[k] = v
end
for _, block in pairs(blocks) do
resolveAllUsesInBlock(self, block)
end
bakeProjectUsage(self)
return base(self)