local jsonProcessor = auRequire("Core/JSON").projectBase ------------------------------------------------------- -- globals ------------------------------------------------------- _auProjects = {} _auProjectsBlocked = {} _auNamespacesEmitted = {} _auResolvedDep = {} _auCurrentProject = {} _auCurrentBaseProject = nil _auRefGuardStart = false _auFatalMsg = {} _auLinkGuard = {} _auRefGuard = {} ------------------------------------------------------- -- 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 -- 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 = {} _auRefGuard = {} _auLinkGuard[projs[idx].info.name] = {} _auRefGuard[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 local linkStart = false if (auGetCurrentProjectMeta()) then if (_auRefGuard[dep] ) then return end _auRefGuard[dep] = dep end if (not _auRefGuardStart) then _auRefGuardStart = true linkStart = true end pushProject(a, function() _auFatalMsg["processing project ref"] = a.info.name processor:handleReference(isWeakCircularReference(dep)) _auFatalMsg["processing project ref"] = nil end) if (linkStart) then _auRefGuard = {} _auRefGuardStart = false 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 }