Build/Core/main.lua
2023-12-12 20:56:05 +00:00

510 lines
12 KiB
Lua

local jsonProcessor = auRequire("Core/JSON").projectBase
-------------------------------------------------------
-- globals
-------------------------------------------------------
_auProjects = {}
_auProjectsBlocked = {}
_auNamespacesEmitted = {}
_auResolvedDep = {}
_auCurrentProject = {}
_auCurrentBaseProject = nil
_auFatalMsg = {}
-------------------------------------------------------
-- utils
-------------------------------------------------------
local function normalizeSourceRoot(path)
local backup = path
path = os.realpath(_G.path.join(os.getcwd(), path));
if (not path) then
print("path error, not found?", backup)
return
end
return path
end
local function extendInfo(this)
if (not this.projectType) then return end
local type = this.projectType:lower()
this.isShared = type == "sharedlib"
this.isStatic = type == "staticlib"
this.isExec = type == "consoleapp" or type == 'windowedapp'
this.isConsole = type == "consoleapp"
this.isWindowed = type == "windowedapp"
end
-------------------------------------------------------
-- project loading
-------------------------------------------------------
local function addVisit(ina)
local args = {
namespace = ina.namespace,
name = ina.name, -- OPT: recommended
path = ina.path,
type = ina.type,
out = ina.out,
options = ina.options,
translations = ina.translations -- OPT: dictionary of dependency maps
}
local path = normalizeSourceRoot(args.path)
if (not path) then
return
end
local info = {
namespace = args.namespace,
path = path,
projectType = args.type,
out = args.out,
name = args.name,
translations = args.translations,
options = args.options
}
extendInfo(info)
local project = {
info = info,
processor = nil,
deps = {}
}
local cwd = auGetRoot()
local remoteLua = path .. "/Aurora.lua"
local remoteJson = path .. "/Aurora.json"
local localJson = Aurora.Settings.sAbsRepoScripts .. "/" .. args.name .. ".aurora.json"
local localLua = Aurora.Settings.sAbsRepoScripts .. "/" .. args.name .. ".aurora.lua"
if (os.isfile(localLua)) then
project.processor = auRequireAbs(localLua)(info)
elseif (os.isfile(localJson)) then
info.jpath = localJson
project.processor = jsonProcessor(info)
elseif (os.isfile(remoteLua)) then
project.processor = auRequireAbs(remoteLua)(info)
elseif (os.isfile(remoteJson)) then
info.jpath = remoteJson
project.processor = jsonProcessor(info)
else
print("Couldnt find Aurora build script for: ", path)
return
end
auRequire("Core").project.expendBaseProcessor(project)
_auProjects[info.name] = project
end
local function addScript(ina)
local args = {
namespace = ina.namespace,
script = ina.script,
path = ina.path,
type = ina.type,
out = ina.out,
options = ina.options,
translations = ina.translations
}
local path = normalizeSourceRoot(args.path)
if (not path) then
return
end
local info = {
namespace = args.namespace,
path = path,
projectType = args.type,
out = args.out,
translations = args.translations,
options = args.options
}
extendInfo(info)
local project = {
info = info,
processor = nil,
deps = {}
}
local procesor = userRequire(args.script)
if (not procesor) then
processor = auRequireAbs(args.script)
if (not procesor) then
print("missing project script:", args.script, path)
return
end
end
project.processor = procesor(info)
if (not project.processor) then
print("script error")
return
end
_auProjects[info.name] = project
end
local function pushProjectState(name, path, callback)
local cwd = os.getcwd()
local old = _auCurrentProject
_auCurrentProject = name
os.chdir(path)
callback()
os.chdir(cwd)
_auCurrentProject = old
end
local function pushProject(project, callback)
pushProjectState(project.info.name, project.info.path, callback)
end
_auLinkGuard = {}
-- private
local function processInit(project)
if (project.isInitialized) then
return
end
project.isInitialized = true
if (project.processor.init) then
pushProject(project, function()
project.processor:init()
end)
end
end
-- private
local function initializeDependencyTree(proj, resolveProject)
local name = proj.info.name
_auResolvedDep[name] = name
if (not proj.resolvedDeps) then
if (proj.processor.resolveDependencies) then
pushProject(proj, function()
local depRecursive = _auFatalMsg.deps or {}
_auFatalMsg.deps = depRecursive
local strA = "starting resolve dependencies" .. name
_auFatalMsg[strA] = nil
proj.processor:resolveDependencies(function(name, soft)
_auFatalMsg[strA] = nil
table.insert(proj.deps, name)
if (_auResolvedDep[name]) then
return
end
local projRootName = (auGetBaseProjectName() or auGetCurrentProject() or "")
local strB = "starting resolve specific " .. name .. " for " .. projRootName
local i = #depRecursive + 1
depRecursive[i] = strB
local depProj = _auProjects[name]
if (not depProj) then
if (not soft) then
auFatal("missing dependency: ", name)
else
return false
end
end
initializeDependencyTree(depProj, true)
depRecursive[i] = nil
end)
end)
end
proj.resolvedDeps = true
end
processInit(proj)
end
local processProject = {}
-- private
local function processNS(namespace)
local projs = {}
local projsIdxs = {}
auForEach(_auProjects, function(proj)
if (proj.info.namespace ~= namespace) then
return
end
local name = proj.info.name
projs[name] = proj
table.insert(projsIdxs, name)
end)
table.sort(projsIdxs, function(a, b)
return a:upper() < b:upper()
end)
_auFatalMsg = {}
_auFatalMsg["starting dependency tree"] = namespace
auForEach(projsIdxs, function(idx)
initializeDependencyTree(projs[idx], true)
end)
_auFatalMsg = {}
_auFatalMsg["starting projects"] = namespace
auForEach(projsIdxs, function(idx)
_auLinkGuard = {}
_auLinkGuard[projs[idx].info.name]= {}
processProject(projs[idx].info.name)
end)
end
local function processSolution()
local hack = {}
local hackIdx = {}
auForEach(_auProjects, function(proj)
table.insert(hackIdx, proj.info.namespace)
end)
table.sort(hackIdx, function(a, b)
return a:upper() < b:upper()
end)
auForEach(hackIdx, processNS)
end
local function attemptNS(ns)
local attemptLoad = false
if (not _auNamespacesEmitted[ns]) then
auStartGroup(ns) -- only print the group once
attemptLoad = true
end
group(ns)
return attemptLoad
end
processProject = function(name, required, noNs)
local a = _auProjects[name]
if (not a) then
if (required) then
auFatal("missing project: ", name)
else
return false
end
end
local oldBaeProjName = _auCurrentBaseProject
_auCurrentBaseProject = name;
-- ensure the project is initializd
processInit(a)
-- recursion protection
if (_auProjectsBlocked[name]) then
_auCurrentBaseProject = oldBaeProjName
return true
end
_auProjectsBlocked[name] = name
-- process all within the namespace before processing the requested project
local ns = a.info.namespace
local loadOthers = attemptNS(ns)
pushProject(a, function()
_auFatalMsg = {}
_auFatalMsg["processing project"] = a.info.name
a.processor:process()
_auFatalMsg = {}
end)
-- cont
if (loadOthers) then
_auNamespacesEmitted[ns] = "";
processNS(ns)
end
_auCurrentBaseProject = oldBaeProjName
return true
end
function isWeakCircularReference(depName)
local dep = _auProjects[depName]
if (not dep) then
return
end
-- TODO: recursion
for index, value in ipairs(dep.deps) do
if (value == _auCurrentProject or value == _auCurrentBaseProject) then
return true
end
end
return false
end
local function isProjectLoaded(name)
local a = _auProjects[name]
if (not a) then
return false
end
return a.isInitialized
end
local function getProjectProcessor(name)
if (not name) then
return
end
local scre = _auProjects[name]
if (not scre) then
return
end
return scre.processor
end
local function getCurrentProjectName()
return _auCurrentProject
end
local function getBaseProjectName()
return _auCurrentBaseProject
end
local function includeAuProject(dep, soft)
local processor = getProjectProcessor(dep)
if (not processor) then
return false
end
local processor = getProjectProcessor(dep)
if (not processor) then
return false
end
local a = _auProjects[dep]
if (not a) then
if (not soft) then
auFatal("missing project: ", dep)
else
return false
end
end
pushProject(a, function()
_auFatalMsg["processing project ref"] = a.info.name
processor:handleReference(isWeakCircularReference(dep))
_auFatalMsg["processing project ref"] = nil
end)
return true
end
local function linkAuProject(dep, soft)
local processor = getProjectProcessor(dep)
if (not processor) then
return false
end
local a = _auProjects[dep]
if (not a) then
if (not soft) then
auFatal("missing project: ", dep)
else
return false
end
end
local linkStart = false
if (auGetCurrentProjectMeta()) then
if (_auLinkGuard[dep] ) then
return
end
_auLinkGuard[dep] = dep
end
if (not _auLinkGuardStart) then
_auLinkGuardStart = true
linkStart = true
end
if (auGetBaseProjectName() ~= dep) then
dependson(dep)
--if (not processor:getMeta().isStatic) then
pushProject(a, function()
_auFatalMsg["processing project link"] = a.info.name
processor:handleLink()
_auFatalMsg["processing project link"] = nil
end)
--end
end
if (linkStart) then
_auLinkGuard = {}
_auLinkGuardStart = false
end
return true
end
local function importAndLinkProject(dep, soft)
includeAuProject(dep, soft)
local this = auGetCurrentProjectMeta()
if (this) then
if (this.isStatic) then
return true
end
end
linkAuProject(dep, soft)
return true
end
local function addFeature(feature)
--print("adding feature ", feature)
local script = Aurora.Settings.sAbsScripts .. "/Features/" .. feature:lower() .. ".lua"
if (not os.isfile(script)) then
auFatal("missing feature", feature, script)
return
end
auRequireAbs(script)()
end
return {
addVisit = addVisit,
addScript = addScript,
addFeature = addFeature,
linkAuProject = linkAuProject,
includeAuProject = includeAuProject,
importAndLinkProject = importAndLinkProject,
getProjectProcessor = getProjectProcessor,
isProjectLoaded = isProjectLoaded,
getCurrentProjectName = getCurrentProjectName,
processSolution = processSolution,
getBaseProjectName = getBaseProjectName
}