Port validation logic to walk the new container hierarchy
This commit is contained in:
parent
a956ea740e
commit
011b0d9fd8
@ -45,6 +45,7 @@
|
||||
|
||||
-- project script processing
|
||||
"base/oven.lua",
|
||||
"base/validation.lua",
|
||||
"base/premake.lua",
|
||||
"base/help.lua",
|
||||
|
||||
|
@ -142,7 +142,7 @@
|
||||
|
||||
-- Sanity check the current project setup
|
||||
|
||||
premake.validate()
|
||||
p.container.validate(p.api.rootContainer())
|
||||
|
||||
-- Hand over control to the action
|
||||
|
||||
|
@ -232,3 +232,25 @@
|
||||
return container.classes[name]
|
||||
end
|
||||
|
||||
|
||||
|
||||
---
|
||||
-- Call out to the container validation to make sure everything
|
||||
-- is as it should be before handing off to the actions.
|
||||
---
|
||||
|
||||
function container.validate(self)
|
||||
if type(self.class.validate) == "function" then
|
||||
self.class.validate(self)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function container.validateChildren(self)
|
||||
for class in container.eachChildClass(self.class) do
|
||||
local children = self[class.pluralName]
|
||||
for i = 1, #children do
|
||||
container.validate(children[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -303,158 +303,6 @@ end
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Sanity check the project information loaded from the scripts, to
|
||||
-- make sure it all meets some minimum requirements. Raises an error if
|
||||
-- an insane state is detected.
|
||||
--
|
||||
|
||||
function premake.validate()
|
||||
local ctx = {}
|
||||
|
||||
for sln in premake.global.eachSolution() do
|
||||
premake.validateSolution(sln, ctx)
|
||||
|
||||
for prj in solution.eachproject(sln) do
|
||||
premake.validateProject(prj, ctx)
|
||||
|
||||
for cfg in project.eachconfig(prj) do
|
||||
premake.validateConfig(cfg, ctx)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Sanity check the settings of a specific solution. Raises an error if
|
||||
-- an insane state is detected.
|
||||
--
|
||||
-- @param sln
|
||||
-- The solution to be checked.
|
||||
-- @param ctx
|
||||
-- The validation context; keeps track of what has already been checked.
|
||||
--
|
||||
|
||||
function premake.validateSolution(sln, ctx)
|
||||
-- there must be at least one build configuration
|
||||
if not sln.configurations or #sln.configurations == 0 then
|
||||
premake.error("solution '%s' does not contain any configurations", sln.name)
|
||||
end
|
||||
|
||||
-- all project UUIDs must be unique
|
||||
local uuids = {}
|
||||
for prj in solution.eachproject(sln) do
|
||||
if uuids[prj.uuid] then
|
||||
premake.error("projects '%s' and '%s' have the same UUID", uuids[prj.uuid], prj.name)
|
||||
end
|
||||
uuids[prj.uuid] = prj.name
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
-- Sanity check the settings of a specific project. Raises an error if
|
||||
-- an insane state is detected.
|
||||
--
|
||||
-- @param prj
|
||||
-- The project to be checked.
|
||||
-- @param ctx
|
||||
-- The validation context; keeps track of what has already been checked.
|
||||
--
|
||||
|
||||
function premake.validateProject(prj, ctx)
|
||||
-- must have a language
|
||||
if not prj.language then
|
||||
premake.error("project '%s' does not have a language", prj.name)
|
||||
end
|
||||
|
||||
-- all rules must exist
|
||||
for i = 1, #prj.rules do
|
||||
local rule = prj.rules[i]
|
||||
if not p.global.getRule(rule) then
|
||||
premake.error("project '%s' uses missing rule '%s'", prj.name, rule)
|
||||
end
|
||||
end
|
||||
|
||||
-- check for out of scope fields
|
||||
premake.validateScopes(prj, "project", ctx)
|
||||
end
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Sanity check the settings of a specific configuration. Raises an error
|
||||
-- if an insane state is detected.
|
||||
--
|
||||
-- @param cfg
|
||||
-- The configuration to be checked.
|
||||
-- @param ctx
|
||||
-- The validation context; keeps track of what has already been checked.
|
||||
--
|
||||
|
||||
function premake.validateConfig(cfg, ctx)
|
||||
-- must have a kind
|
||||
if not cfg.kind then
|
||||
premake.error("project '%s' needs a kind in configuration '%s'", cfg.project.name, cfg.name)
|
||||
end
|
||||
|
||||
-- makefile configuration can only appear in C++ projects; this is the
|
||||
-- default now, so should only be a problem if overridden.
|
||||
if (cfg.kind == premake.MAKEFILE or cfg.kind == premake.NONE) and not project.iscpp(cfg.project) then
|
||||
premake.error("project '%s' uses %s kind in configuration '%s'; language must be C++", cfg.project.name, cfg.kind, cfg.name)
|
||||
end
|
||||
|
||||
-- check for out of scope fields
|
||||
premake.validateScopes(cfg, "config", ctx)
|
||||
end
|
||||
|
||||
|
||||
|
||||
--
|
||||
-- Check the values stored in a configuration object (solution, project, or
|
||||
-- configuration) for values that might have been set out of scope.
|
||||
--
|
||||
-- @param cfg
|
||||
-- The configuration object to validate.
|
||||
-- @param expected
|
||||
-- The expected scope of values in this object; one of "project" or "config".
|
||||
-- @param ctx
|
||||
-- The validation context; used to prevent multiple warnings on same field.
|
||||
--
|
||||
|
||||
function premake.validateScopes(cfg, expected, ctx)
|
||||
for f in field.each() do
|
||||
-- Get the field's scope
|
||||
-- TODO: This whole scope validation needs to be generalized
|
||||
-- now that containers are in place. For now, ignore rule
|
||||
-- containers until I can make things work properly.
|
||||
local scope
|
||||
if f.scopes[1] ~= "rule" then
|
||||
scope = f.scopes[1]
|
||||
end
|
||||
|
||||
-- Skip fields that are at or below the expected scope. Config-
|
||||
-- level fields are the most general (can be applied to projects
|
||||
-- or solutions) and so can never be out of scope.
|
||||
local okay = (not scope or scope == "config" or scope == expected or p.oven.bubbledFields[f.name])
|
||||
|
||||
-- this one needs to checked
|
||||
if not okay then
|
||||
okay = field.compare(f, cfg[scope][f.name], cfg[f.name])
|
||||
end
|
||||
|
||||
-- found a problem?
|
||||
if not okay then
|
||||
local key = "validate." .. f.name
|
||||
premake.warnOnce(key, "'%s' on %s '%s' differs from %s '%s'; may be set out of scope", f.name, expected, cfg.name, scope, cfg[scope].name)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
---
|
||||
-- Write a formatted string to the exported file, at the current
|
||||
-- level of indentation, and appends an end of line sequence.
|
||||
|
145
src/base/validation.lua
Normal file
145
src/base/validation.lua
Normal file
@ -0,0 +1,145 @@
|
||||
---
|
||||
-- base/validation.lua
|
||||
--
|
||||
-- Verify the contents of the project object before handing them off to
|
||||
-- the action/exporter.
|
||||
--
|
||||
-- Copyright (c) 2002-2014 Jason Perkins and the Premake project
|
||||
---
|
||||
|
||||
local p = premake
|
||||
|
||||
|
||||
|
||||
---
|
||||
-- Validate the global container and all of its contents.
|
||||
---
|
||||
|
||||
function p.global.validate(self)
|
||||
p.container.validateChildren(self)
|
||||
end
|
||||
|
||||
|
||||
|
||||
---
|
||||
-- Validate a solution and its projects.
|
||||
---
|
||||
|
||||
function p.solution.validate(self)
|
||||
-- there must be at least one build configuration
|
||||
if not self.configurations or #self.configurations == 0 then
|
||||
p.error("solution '%s' does not contain any configurations", self.name)
|
||||
end
|
||||
|
||||
-- all project UUIDs must be unique
|
||||
local uuids = {}
|
||||
for prj in p.solution.eachproject(self) do
|
||||
if uuids[prj.uuid] then
|
||||
p.error("projects '%s' and '%s' have the same UUID", uuids[prj.uuid], prj.name)
|
||||
end
|
||||
uuids[prj.uuid] = prj.name
|
||||
end
|
||||
|
||||
p.container.validateChildren(self)
|
||||
end
|
||||
|
||||
|
||||
|
||||
---
|
||||
-- Validate a project and its configurations.
|
||||
---
|
||||
|
||||
function p.project.validate(self)
|
||||
-- must have a language
|
||||
if not self.language then
|
||||
p.error("project '%s' does not have a language", self.name)
|
||||
end
|
||||
|
||||
-- all rules must exist
|
||||
for i = 1, #self.rules do
|
||||
local rule = self.rules[i]
|
||||
if not p.global.getRule(rule) then
|
||||
p.error("project '%s' uses missing rule '%s'", self.name, rule)
|
||||
end
|
||||
end
|
||||
|
||||
-- check for out of scope fields
|
||||
p.config.validateScopes(self, "project")
|
||||
|
||||
for cfg in p.project.eachconfig(self) do
|
||||
p.config.validate(cfg)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
---
|
||||
-- Validate a project configuration.
|
||||
---
|
||||
|
||||
function p.config.validate(self)
|
||||
-- must have a kind
|
||||
if not self.kind then
|
||||
p.error("project '%s' needs a kind in configuration '%s'", self.project.name, self.name)
|
||||
end
|
||||
|
||||
-- makefile configuration can only appear in C++ projects; this is the
|
||||
-- default now, so should only be a problem if overridden.
|
||||
if (self.kind == p.MAKEFILE or self.kind == p.NONE) and not p.project.iscpp(self.project) then
|
||||
p.error("project '%s' uses %s kind in configuration '%s'; language must be C++", self.project.name, self.kind, self.name)
|
||||
end
|
||||
|
||||
-- check for out of scope fields
|
||||
p.config.validateScopes(self, "config")
|
||||
end
|
||||
|
||||
|
||||
|
||||
---
|
||||
-- Check the values stored in a configuration for values that might have
|
||||
-- been set out of scope.
|
||||
--
|
||||
-- @param expected
|
||||
-- The expected scope of values in this object; one of "project" or "config".
|
||||
---
|
||||
|
||||
function p.config.validateScopes(self, expected)
|
||||
for f in p.field.each() do
|
||||
-- Get the field's scope
|
||||
-- TODO: This whole scope validation needs to be generalized
|
||||
-- now that containers are in place. For now, ignore rule
|
||||
-- containers until I can make things work properly.
|
||||
local scope
|
||||
if f.scopes[1] ~= "rule" then
|
||||
scope = f.scopes[1]
|
||||
end
|
||||
|
||||
-- Skip fields that are at or below the expected scope. Config-
|
||||
-- level fields are the most general (can be applied to projects
|
||||
-- or solutions) and so can never be out of scope.
|
||||
local okay = (not scope or scope == "config" or scope == expected or p.oven.bubbledFields[f.name])
|
||||
|
||||
-- this one needs to checked
|
||||
if not okay then
|
||||
okay = p.field.compare(f, self[scope][f.name], self[f.name])
|
||||
end
|
||||
|
||||
-- found a problem?
|
||||
if not okay then
|
||||
local key = "validate." .. f.name
|
||||
p.warnOnce(key, "'%s' on %s '%s' differs from %s '%s'; may be set out of scope", f.name, expected, self.name, scope, self[scope].name)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
---
|
||||
-- Validate a rule.
|
||||
---
|
||||
|
||||
function p.rule.validate(self)
|
||||
-- TODO: fill this in
|
||||
end
|
||||
|
@ -6,14 +6,15 @@
|
||||
|
||||
local suite = test.declare("premake_validation")
|
||||
|
||||
local p = premake
|
||||
|
||||
|
||||
--
|
||||
-- Setup
|
||||
--
|
||||
|
||||
local function verify()
|
||||
ok, err = pcall(premake.validate)
|
||||
return ok and not test.stderr()
|
||||
local function validate()
|
||||
return pcall(function() p.container.validate(p.api.rootContainer()) end)
|
||||
end
|
||||
|
||||
|
||||
@ -27,9 +28,7 @@
|
||||
project "MyProject"
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
|
||||
test.istrue(pcall(premake.validate))
|
||||
test.stderr()
|
||||
test.istrue(validate())
|
||||
end
|
||||
|
||||
|
||||
@ -42,8 +41,7 @@
|
||||
project "MyProject"
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
|
||||
test.isfalse(pcall(premake.validate))
|
||||
test.isfalse(validate())
|
||||
end
|
||||
|
||||
|
||||
@ -60,8 +58,7 @@
|
||||
uuid "D4110D7D-FB18-4A1C-A75B-CA432F4FE770"
|
||||
project "MyProject2"
|
||||
uuid "D4110D7D-FB18-4A1C-A75B-CA432F4FE770"
|
||||
|
||||
test.isfalse(pcall(premake.validate))
|
||||
test.isfalse(validate())
|
||||
end
|
||||
|
||||
|
||||
@ -74,8 +71,7 @@
|
||||
configurations { "Debug", "Release" }
|
||||
project "MyProject"
|
||||
language "C++"
|
||||
|
||||
test.isfalse(pcall(premake.validate))
|
||||
test.isfalse(validate())
|
||||
end
|
||||
|
||||
|
||||
@ -91,7 +87,7 @@
|
||||
project "MyProject"
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
premake.validate()
|
||||
validate()
|
||||
test.stderr("'startproject' on config")
|
||||
end
|
||||
|
||||
@ -103,7 +99,7 @@
|
||||
language "C++"
|
||||
filter "Debug"
|
||||
location "MyProject"
|
||||
premake.validate()
|
||||
validate()
|
||||
test.stderr("'location' on config")
|
||||
end
|
||||
|
||||
@ -115,7 +111,7 @@
|
||||
language "C++"
|
||||
filter "Debug"
|
||||
configurations "Deployment"
|
||||
premake.validate()
|
||||
validate()
|
||||
test.stderr("'configurations' on config")
|
||||
end
|
||||
|
||||
@ -129,6 +125,6 @@
|
||||
configurations { "Debug", "Release" }
|
||||
project "MyProject"
|
||||
rules { "NoSuchRule" }
|
||||
test.isfalse(pcall(premake.validate))
|
||||
test.isfalse(validate())
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user