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
|
-- project script processing
|
||||||
"base/oven.lua",
|
"base/oven.lua",
|
||||||
|
"base/validation.lua",
|
||||||
"base/premake.lua",
|
"base/premake.lua",
|
||||||
"base/help.lua",
|
"base/help.lua",
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@
|
|||||||
|
|
||||||
-- Sanity check the current project setup
|
-- Sanity check the current project setup
|
||||||
|
|
||||||
premake.validate()
|
p.container.validate(p.api.rootContainer())
|
||||||
|
|
||||||
-- Hand over control to the action
|
-- Hand over control to the action
|
||||||
|
|
||||||
|
@ -232,3 +232,25 @@
|
|||||||
return container.classes[name]
|
return container.classes[name]
|
||||||
end
|
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
|
-- Write a formatted string to the exported file, at the current
|
||||||
-- level of indentation, and appends an end of line sequence.
|
-- 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 suite = test.declare("premake_validation")
|
||||||
|
|
||||||
|
local p = premake
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Setup
|
-- Setup
|
||||||
--
|
--
|
||||||
|
|
||||||
local function verify()
|
local function validate()
|
||||||
ok, err = pcall(premake.validate)
|
return pcall(function() p.container.validate(p.api.rootContainer()) end)
|
||||||
return ok and not test.stderr()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -27,9 +28,7 @@
|
|||||||
project "MyProject"
|
project "MyProject"
|
||||||
kind "ConsoleApp"
|
kind "ConsoleApp"
|
||||||
language "C++"
|
language "C++"
|
||||||
|
test.istrue(validate())
|
||||||
test.istrue(pcall(premake.validate))
|
|
||||||
test.stderr()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -42,8 +41,7 @@
|
|||||||
project "MyProject"
|
project "MyProject"
|
||||||
kind "ConsoleApp"
|
kind "ConsoleApp"
|
||||||
language "C++"
|
language "C++"
|
||||||
|
test.isfalse(validate())
|
||||||
test.isfalse(pcall(premake.validate))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -60,8 +58,7 @@
|
|||||||
uuid "D4110D7D-FB18-4A1C-A75B-CA432F4FE770"
|
uuid "D4110D7D-FB18-4A1C-A75B-CA432F4FE770"
|
||||||
project "MyProject2"
|
project "MyProject2"
|
||||||
uuid "D4110D7D-FB18-4A1C-A75B-CA432F4FE770"
|
uuid "D4110D7D-FB18-4A1C-A75B-CA432F4FE770"
|
||||||
|
test.isfalse(validate())
|
||||||
test.isfalse(pcall(premake.validate))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -74,8 +71,7 @@
|
|||||||
configurations { "Debug", "Release" }
|
configurations { "Debug", "Release" }
|
||||||
project "MyProject"
|
project "MyProject"
|
||||||
language "C++"
|
language "C++"
|
||||||
|
test.isfalse(validate())
|
||||||
test.isfalse(pcall(premake.validate))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -91,7 +87,7 @@
|
|||||||
project "MyProject"
|
project "MyProject"
|
||||||
kind "ConsoleApp"
|
kind "ConsoleApp"
|
||||||
language "C++"
|
language "C++"
|
||||||
premake.validate()
|
validate()
|
||||||
test.stderr("'startproject' on config")
|
test.stderr("'startproject' on config")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -103,7 +99,7 @@
|
|||||||
language "C++"
|
language "C++"
|
||||||
filter "Debug"
|
filter "Debug"
|
||||||
location "MyProject"
|
location "MyProject"
|
||||||
premake.validate()
|
validate()
|
||||||
test.stderr("'location' on config")
|
test.stderr("'location' on config")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -115,7 +111,7 @@
|
|||||||
language "C++"
|
language "C++"
|
||||||
filter "Debug"
|
filter "Debug"
|
||||||
configurations "Deployment"
|
configurations "Deployment"
|
||||||
premake.validate()
|
validate()
|
||||||
test.stderr("'configurations' on config")
|
test.stderr("'configurations' on config")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -129,6 +125,6 @@
|
|||||||
configurations { "Debug", "Release" }
|
configurations { "Debug", "Release" }
|
||||||
project "MyProject"
|
project "MyProject"
|
||||||
rules { "NoSuchRule" }
|
rules { "NoSuchRule" }
|
||||||
test.isfalse(pcall(premake.validate))
|
test.isfalse(validate())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user