From 66206868cdd09449d8129bbe15a4ce2e273924eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Thu, 28 Oct 2021 09:53:43 +0200 Subject: [PATCH] XMake: Add automatic header order fix --- .../OpenGLRenderer/Wrapper/CoreFunctions.hpp | 2 + .../Wrapper/WGL/WGLFunctions.hpp | 2 + .../Wrapper/Win32/Gdi32Functions.hpp | 2 + xmake/actions/checkfiles.lua | 217 ++++++++++++++++-- 4 files changed, 198 insertions(+), 25 deletions(-) diff --git a/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp b/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp index 0c23ac2f8..8ac084f20 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp @@ -7,6 +7,8 @@ #ifndef NAZARA_OPENGLRENDERER_WRAPPER_COREFUNCTIONS_HPP #define NAZARA_OPENGLRENDERER_WRAPPER_COREFUNCTIONS_HPP +// no include reordering + #define GL_GLES_PROTOTYPES 0 #include #include diff --git a/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLFunctions.hpp b/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLFunctions.hpp index 33ef418b0..0cf45a8ee 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLFunctions.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/WGL/WGLFunctions.hpp @@ -9,6 +9,8 @@ #undef WIN32_LEAN_AND_MEAN //< Redefined by wgl.h header (ty Khronos) +// no include reordering + #include #include #include diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Win32/Gdi32Functions.hpp b/include/Nazara/OpenGLRenderer/Wrapper/Win32/Gdi32Functions.hpp index 6d19c68cf..e56a95cf9 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Win32/Gdi32Functions.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/Win32/Gdi32Functions.hpp @@ -9,6 +9,8 @@ #undef WIN32_LEAN_AND_MEAN //< Redefined by wgl.h header (ty Khronos) +// no include reordering + #include #include #include diff --git a/xmake/actions/checkfiles.lua b/xmake/actions/checkfiles.lua index c574ef532..0280255d9 100644 --- a/xmake/actions/checkfiles.lua +++ b/xmake/actions/checkfiles.lua @@ -28,6 +28,42 @@ local function CompareLines(referenceLines, lines, firstLine, lineCount) return true end +local function ComputeHeaderFile(filePath) + local headerPath = filePath:gsub("[\\/]", "/") + headerPath = headerPath:sub(headerPath:find("Nazara/"), -1) + headerPath = headerPath:sub(1, headerPath:find("%..+$") - 1) .. ".hpp" + + return headerPath +end + +local systemHeaders = { + ["fcntl.h"] = true, + ["mstcpip"] = true, + ["netdb.h"] = true, + ["poll.h"] = true, + ["process.h"] = true, + ["pthread.h"] = true, + ["unistd.h"] = true, + ["windows.h"] = true, + ["winsock2"] = true, +} + +local function IsSystemHeader(header) + if systemHeaders[header] then + return true + end + + if header:match("netinet/.*") then + return true + end + + if header:match("sys/.*") then + return true + end + + return false +end + on_run(function () import("core.base.option") @@ -281,10 +317,7 @@ on_run(function () for _, filePath in pairs(files) do local lines = GetFile(filePath) - local headerPath = filePath:gsub("[\\/]", "/") - headerPath = headerPath:sub(headerPath:find("Nazara/"), -1) - headerPath = headerPath:sub(1, headerPath:find("%..+$") - 1) .. ".hpp" - + local headerPath = ComputeHeaderFile(filePath) if os.isfile("include/" .. headerPath) or os.isfile("src/" .. headerPath) then local inclusions = {} @@ -296,7 +329,7 @@ on_run(function () break end - local includeMode, includePath = lines[i]:match("^%s*#%s*include%s*([<\"])(.+)[>\"]") + local includeMode, includePath = lines[i]:match("^#include%s*([<\"])(.+)[>\"]") if includeMode then table.insert(inclusions, { line = i, @@ -307,36 +340,24 @@ on_run(function () end if #inclusions > 0 then + local increment = 0 + + -- Add corresponding header local headerInclude for i = 1, #inclusions do if inclusions[i].path == headerPath then headerInclude = i - if i ~= 1 then - print(filePath .. " doesn't include corresponding header first") - - local currentInclusion = inclusions[i] - table.insert(fixes, { - File = filePath, - Func = function (lines) - table.remove(lines, currentInclusion.line) - local firstHeaderLine = inclusions[1].line - table.insert(lines, firstHeaderLine, "#include <" .. headerPath .. ">") - - return lines - end - }) - end - break end end + -- Check debug headers local debugIncludeModule = moduleName ~= "Math" and moduleName or "Core" local debugInclude local debugIncludeOff for i = 1, #inclusions do - local module, off = inclusions[i].path:match("Nazara/(.+)/Debug(O?f?f?).hpp") + local module, off = inclusions[i].path:match("^Nazara/(.+)/Debug(O?f?f?).hpp$") if module then if off == "Off" then debugIncludeOff = i @@ -361,8 +382,7 @@ on_run(function () end end - local increment = 0 - + -- Add header inclusion if it's missing if not headerInclude then print(filePath .. " is missing corresponding header inclusion") @@ -392,7 +412,7 @@ on_run(function () end if path.extension(filePath) == ".inl" then - if not debugInclude then + if not debugIncludeOff then print(filePath .. ": has missing DebugOff include") table.insert(fixes, { File = filePath, @@ -413,6 +433,153 @@ on_run(function () end }) + -- Reorder includes and remove duplicates + table.insert(checks, { + Name = "inclusion order", + 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) + + local headerPath = ComputeHeaderFile(filePath) + + local inclusions = {} + + -- Retrieve all inclusions from the first inclusion block + for i = 1, #lines do + if lines[i] == "// no include reordering" then + -- ignore file + inclusions = {} + break + end + + local includeMode, includePath = lines[i]:match("^#include%s*([<\"])(.+)[>\"]") + if includeMode then + table.insert(inclusions, { + line = i, + mode = includeMode, + path = includePath + }) + elseif #inclusions > 0 then + -- Stop when outside the inclusion block + break + end + end + + local debugIncludeModule = moduleName ~= "Math" and moduleName or "Core" + + local includeList = {} + local shouldReorder = false + for i = 1, #inclusions do + local order + if inclusions[i].path == headerPath or inclusions[i].path == "Nazara/Prerequisites.hpp" then + order = 0 -- own include comes first + elseif inclusions[i].path == "Nazara/" .. debugIncludeModule .. "/Debug.hpp" then + order = 5 -- debug include + elseif inclusions[i].path:match("^Nazara/") then + order = 1 -- engine includes + elseif IsSystemHeader(inclusions[i].path) then + order = 4 -- system includes + elseif inclusions[i].path:match(".+%.hp?p?") then + order = 2 -- thirdparty includes + else + order = 3 -- standard includes + end + + table.insert(includeList, { + order = order, + path = inclusions[i].path, + content = lines[inclusions[i].line] + }) + end + + local function compareFunc(a, b) + if a.order == b.order then + local moduleA = a.path:match("^Nazara/(.-)/") + local moduleB = b.path:match("^Nazara/(.-)/") + if moduleA ~= moduleB then + return moduleA < moduleB + end + + local _, folderCountA = a.path:gsub("/", "") + local _, folderCountB = b.path:gsub("/", "") + if folderCountA == folderCountB then + return a.path < b.path + else + return folderCountA < folderCountB + end + else + return a.order < b.order + end + end + + local isOrdered = true + for i = 2, #includeList do + if includeList[i - 1].path == includeList[i].path then + -- duplicate found + print(filePath .. ": include list has duplicates") + isOrdered = false + break + end + + if not compareFunc(includeList[i - 1], includeList[i]) then + print(filePath .. ": include list is not ordered") + isOrdered = false + break + end + end + + if not isOrdered then + table.sort(includeList, compareFunc) + + table.insert(fixes, { + File = filePath, + Func = function (lines) + -- Reorder includes + local newInclusions = {} + for i = 1, #inclusions do + lines[inclusions[i].line] = includeList[i].content + table.insert(newInclusions, { + content = includeList[i].content, + path = includeList[i].path, + line = inclusions[i].line + }) + end + + -- Remove duplicates + table.sort(newInclusions, function (a, b) return a.line > b.line end) + + for i = 2, #newInclusions do + local a = newInclusions[i - 1] + local b = newInclusions[i] + + if a.path == b.path then + if #a.content > #b.content then -- keep longest line (for comments) + table.remove(lines, b.line) + else + table.remove(lines, a.line) + end + end + end + + return lines + end + }) + end + end + + return fixes + end + }) + -- Check copyright date and format table.insert(checks, { Name = "copyright",