XMake: add auto-fixer script

This commit is contained in:
Jérôme Leclercq 2021-10-26 14:31:19 +02:00
parent 8d5276ddac
commit b8309fd511
4 changed files with 379 additions and 3 deletions

View File

@ -0,0 +1,376 @@
task("check-files")
set_menu({
-- Settings menu usage
usage = "xmake check-files [options]",
description = "Check every file for consistency (can fix some errors)",
options =
{
-- Set k mode as key-only bool parameter
{'f', "fix", "k", nil, "Attempt to automatically fix files." }
}
})
local function CompareLines(referenceLines, lines, firstLine, lineCount)
firstLine = firstLine or 1
lineCount = lineCount or (#lines - firstLine + 1)
if lineCount ~= #referenceLines then
return false
end
for i = 1, lineCount do
if lines[firstLine + i - 1] ~= referenceLines[i] then
print(lines[firstLine + i])
print(referenceLines[i])
return false
end
end
return true
end
on_run(function ()
import("core.base.option")
local modules = os.dirs("include/Nazara/*")
local fileLines = {}
local updatedFiles = {}
local function GetFile(filePath)
filePath = path.translate(filePath)
local lines = fileLines[filePath]
if not lines then
lines = table.to_array(io.lines(filePath))
if not lines then
os.raise("failed to open " .. filePath)
end
fileLines[filePath] = lines
end
return lines
end
local function UpdateFile(filePath, lines)
filePath = path.translate(filePath)
if lines then
fileLines[filePath] = lines
end
updatedFiles[filePath] = true
end
local checks = {}
table.insert(checks, {
Name = "remove empty lines at the beginning",
Check = function (moduleName)
local files = table.join(
os.files("include/Nazara/" .. moduleName .. "/**.hpp"),
os.files("include/Nazara/" .. moduleName .. "/**.inl"),
os.files("src/Nazara/" .. moduleName .. "/**.hpp"),
os.files("src/Nazara/" .. moduleName .. "/**.inl"),
os.files("src/Nazara/" .. moduleName .. "/**.cpp")
)
local fixes = {}
for _, filePath in pairs(files) do
local lines = GetFile(filePath)
for i = 1, #lines do
if not lines[i]:match("^%s*$") then
if i ~= 1 then
print(filePath .. " starts with empty lines")
table.insert(fixes, {
File = filePath,
Func = function (lines)
for j = 1, i - 1 do
table.remove(lines, 1)
end
UpdateFile(filePath, lines)
end
})
end
break
end
end
end
return fixes
end
})
table.insert(checks, {
Name = "copyright",
Check = function (moduleName)
local files = table.join(
os.files("include/Nazara/" .. moduleName .. "/**.hpp|Config.hpp"),
os.files("include/Nazara/" .. moduleName .. "/**.inl"),
os.files("src/Nazara/" .. moduleName .. "/**.hpp"),
os.files("src/Nazara/" .. moduleName .. "/**.inl"),
os.files("src/Nazara/" .. moduleName .. "/**.cpp")
)
local currentYear = os.date("%Y")
local engineAuthor = "Jérôme \"Lynix\" Leclercq (lynix680@gmail.com)"
local prevAuthor = "Jérôme Leclercq"
local fixes = {}
local moduleDesc
if #moduleName > 8 and moduleName:endswith("Renderer") then
moduleDesc = moduleName:sub(1, -9) .. " renderer"
else
moduleDesc = moduleName .. " module"
end
-- Config
do
local configFilePath = path.translate("include/Nazara/" .. moduleName .. "/Config.hpp")
local lines = GetFile(configFilePath)
local pragmaLine
for i = 1, #lines do
if lines[i] == "#pragma once" then
pragmaLine = i
break
end
end
local canFix = true
repeat
if not pragmaLine then
print(configFilePath .. ": pragma once not found")
canFix = false
break
end
local licenseText = [[
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once]]
local licenseLines = licenseText:split("\r?\n", { strict = true })
local shouldFix = false
-- Try to retrieve year and authors
if lines[1] ~= "/*" then
print(configFilePath .. ": file doesn't begin with block comment")
break
end
if lines[2]:match("\tNazara Engine - " .. moduleDesc) then
print(configFilePath .. ": module description doesn't match")
shouldFix = true
end
if lines[3] ~= "" then
-- Not really empty because of space characters? Not a big deal
if lines[3]:match("^%s*$") then
shouldFix = true
else
print(configFilePath .. ": expected space after project name")
break
end
end
local year, moduleAuthor = lines[4]:match("^\tCopyright %(C%) (Y?E?A?R?%d*) (.+)$")
if not year then
print(configFilePath .. ": couldn't parse copyright date and author")
break
end
if year ~= currentYear then
print(configFilePath .. ": incorrect copyright year")
shouldFix = true
end
local additionalAuthors = {}
for i = 5, #lines do
if lines[i]:match("^%s*$") then
-- Empty line
if not CompareLines(licenseLines, lines, i, pragmaLine - i + 1) then
shouldFix = true
end
break
end
if lines[i]:match("%s*Permission is hereby granted") then
print(configFilePath .. ": missing empty line before licence text")
shouldFix = true
break
end
table.insert(additionalAuthors, lines[i]:match("%s*(.+)"))
end
if shouldFix then
table.insert(fixes, {
File = configFilePath,
Func = function (lines)
local newLines = {
"/*",
"\tNazara Engine - " .. moduleDesc,
"",
}
local copyrightNotice = "Copyright (C) " .. currentYear
table.insert(newLines, "\t" .. copyrightNotice .. " " .. moduleAuthor)
for _, author in ipairs(additionalAuthors) do
table.insert(newLines, "\t" .. string.rep(" ", #copyrightNotice) .. " " .. author)
end
for _, line in ipairs(licenseLines) do
table.insert(newLines, line)
end
for i = pragmaLine + 1, #lines do
table.insert(newLines, lines[i])
end
UpdateFile(configFilePath, newLines)
end
})
end
until true
if not canFix then
print(configFilePath .. ": header couldn't be parsed, no automated fix will be done")
end
end
-- Headers
for _, filePath in pairs(files) do
local lines = GetFile(filePath)
local hasCopyright
local shouldFix = false
if lines[1]:lower():match("^// this file was automatically generated") then
goto skip
end
local year, authors = lines[1]:match("^// Copyright %(C%) (Y?E?A?R?%d*) (.+)$")
hasCopyright = year ~= nil
if authors == "AUTHORS" then
authors = engineAuthor
else
local fixedAuthors = authors:gsub(prevAuthor, engineAuthor)
if fixedAuthors ~= authors then
authors = fixedAuthors
shouldFix = true
end
end
if hasCopyright then
if year ~= currentYear then
print(filePath .. ": copyright year error")
shouldFix = true
end
local copyrightModule = lines[2]:match("^// This file is part of the \"Nazara Engine %- (.+)\"$")
if copyrightModule ~= moduleDesc then
print(filePath .. ": copyright module error")
shouldFix = true
end
if lines[3] ~= "// For conditions of distribution and use, see copyright notice in Config.hpp" then
print(filePath .. ": copyright file reference error")
shouldFix = true
end
else
print(filePath .. ": copyright not found")
shouldFix = true
end
if shouldFix then
table.insert(fixes, {
File = filePath,
Func = function(lines)
local copyrightLines = {
"// Copyright (C) " .. currentYear .. " " .. (authors or engineAuthor),
"// This file is part of the \"Nazara Engine - " .. moduleDesc .. "\"",
"// For conditions of distribution and use, see copyright notice in Config.hpp"
}
if hasCopyright then
for i, line in ipairs(copyrightLines) do
lines[i] = line
end
else
for i, line in ipairs(copyrightLines) do
table.insert(lines, i, line)
end
table.insert(lines, #copyrightLines + 1, "")
end
UpdateFile(filePath, lines)
end
})
end
::skip::
end
return fixes
end
})
local shouldFix = option.get("fix") or false
for _, check in pairs(checks) do
print("Running " .. check.Name .. " check...")
local fixes = {}
for _, modulePath in pairs(modules) do
local moduleName = modulePath:match(".*[\\/](.*)")
table.join2(fixes, check.Check(moduleName))
end
if shouldFix then
for _, fix in pairs(fixes) do
print("Fixing " .. fix.File)
fix.Func(assert(fileLines[fix.File]))
end
for filePath, _ in pairs(updatedFiles) do
local lines = assert(fileLines[filePath])
if lines[#lines] ~= "" then
table.insert(lines, "")
end
print("Saving changes to " .. filePath)
io.writefile(filePath, table.concat(lines, "\n"))
end
end
end
end)

View File

@ -118,7 +118,7 @@ on_run(function ()
error("Failed to create header file (" .. v.Target .. "): " .. err)
end
header:write("// This file was automatically generated\n\n")
header:write("// this file was automatically generated and should not be edited\n\n")
if (v.Header) then
header:write(v.Header)
end

View File

@ -215,7 +215,7 @@ headerFile:write([[
// Copyright (C) ]] .. os.date("%Y") .. [[ Jérôme Leclercq
// This file is part of the "Nazara Engine - Shader module"
// For conditions of distribution and use, see copyright notice in Config.hpp"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Shader/SpirvData.hpp>
#include <algorithm>

View File

@ -182,7 +182,7 @@ local DirectionToString = {}
// Copyright (C) ]] .. os.date("%Y") .. [[ Jérôme Leclercq
// This file is part of the "Nazara Engine - Core module"
// For conditions of distribution and use, see copyright notice in Config.hpp"
// For conditions of distribution and use, see copyright notice in Config.hpp
]])