Merge pull request #11158 from boykopovar/refactor/no-3rdparty-in-public-headers

refactor(libs): remove 3rdparty from public headers
This commit is contained in:
boykopovar authored and GitHub committed 2026-10-25 23:25:14 +03:00
commit 5a2744c55e
4 files changed
+115 -66

No files matched your search

@@ -3,16 +3,21 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include <ft2build.h>
#include FT_LIST_H
#include <cstddef>
#include <cstdint>
namespace FreeType {
using GuestListRec = FT_ListRec;
using GuestListNodeRec = FT_ListNodeRec;
struct GuestListNodeRec {
GuestListNodeRec* prev;
GuestListNodeRec* next;
void* data;
};
struct GuestListRec {
GuestListNodeRec* head;
GuestListNodeRec* tail;
};
struct GuestMemoryRec {
void* user;
+52 -38
View File
@@ -4,6 +4,8 @@
#include <array>
#include <stdexcept>
#include <ft2build.h>
#include FT_LIST_H
using namespace FreeType;
@@ -26,77 +28,89 @@ private:
std::array<GuestArena::HostWrite, 5> writes;
};
struct Iteration {
GuestListIterator iterator;
void* user;
};
int Iterate(FT_ListNode node, void* user) {
auto& call = *static_cast<Iteration*>(user);
return call.iterator(node, call.user);
}
struct Finalization {
GuestMemoryRec* memory;
GuestListDestructor destroy;
void* user;
};
void Destroy(FT_Memory memory, void* data, void*) {
auto& call = *static_cast<Finalization*>(memory->user);
call.destroy(call.memory, data, call.user);
}
void FreeNode(FT_Memory memory, void* block) {
auto& call = *static_cast<Finalization*>(memory->user);
call.memory->free(call.memory, block);
}
}
extern "C" {
GuestListNodeRec* APS5_VABI FT_List_Find_nid_postfix(GuestListRec* list, void* data) {
return FT_List_Find(list, data);
if (!list) return nullptr;
for (auto* node = list->head; node; node = node->next) {
if (node->data == data) return node;
}
return nullptr;
}
void APS5_VABI FT_List_Add_nid_postfix(GuestListRec* list, GuestListNodeRec* node) {
if (!list || !node) return;
const ListWrite write(list, {node, list->tail});
FT_List_Add(list, node);
auto* tail = list->tail;
node->next = nullptr;
node->prev = tail;
if (tail) tail->next = node;
else list->head = node;
list->tail = node;
}
void APS5_VABI FT_List_Insert_nid_postfix(GuestListRec* list, GuestListNodeRec* node) {
if (!list || !node) return;
const ListWrite write(list, {node, list->head});
FT_List_Insert(list, node);
auto* head = list->head;
node->next = head;
node->prev = nullptr;
if (head) head->prev = node;
else list->tail = node;
list->head = node;
}
void APS5_VABI FT_List_Remove_nid_postfix(GuestListRec* list, GuestListNodeRec* node) {
if (!list || !node) return;
const ListWrite write(list, {node->prev, node->next});
FT_List_Remove(list, node);
auto* previous = node->prev;
auto* next = node->next;
if (previous) previous->next = next;
else list->head = next;
if (next) next->prev = previous;
else list->tail = previous;
}
void APS5_VABI FT_List_Up_nid_postfix(GuestListRec* list, GuestListNodeRec* node) {
if (!list || !node || !node->prev) return;
const ListWrite write(list, {node, node->prev, node->next, list->head});
FT_List_Up(list, node);
auto* previous = node->prev;
auto* next = node->next;
previous->next = next;
if (next) next->prev = previous;
else list->tail = previous;
node->prev = nullptr;
node->next = list->head;
list->head->prev = node;
list->head = node;
}
int APS5_VABI FT_List_Iterate_nid_postfix(GuestListRec* list, GuestListIterator iterator, void* user) {
Iteration call{iterator, user};
return FT_List_Iterate(list, iterator ? Iterate : nullptr, &call);
if (!list || !iterator) return FT_Err_Invalid_Argument;
auto* node = list->head;
while (node) {
auto* next = node->next;
const int error = iterator(node, user);
if (error) return error;
node = next;
}
return 0;
}
void APS5_VABI FT_List_Finalize_nid_postfix(GuestListRec* list, GuestListDestructor destroy, GuestMemoryRec* memory, void* user) {
if (!list || !memory) return;
const ListWrite write(list);
Finalization call{memory, destroy, user};
FT_MemoryRec_ native{};
native.user = &call;
native.free = FreeNode;
FT_List_Finalize(list, destroy ? Destroy : nullptr, &native, user);
auto* node = list->head;
while (node) {
auto* next = node->next;
if (destroy) destroy(memory, node->data, user);
memory->free(memory, node);
node = next;
}
list->head = nullptr;
list->tail = nullptr;
}
}
+30 -19
View File
@@ -7,6 +7,9 @@
#include <map>
#include <random>
#include <vector>
#include <type_traits>
#include <ft2build.h>
#include FT_LIST_H
using namespace FreeType;
@@ -20,9 +23,11 @@ static void Check(bool value, int line) {
#define Require(value) Check((value), __LINE__)
constexpr std::size_t Count = 16;
using Nodes = std::array<FT_ListNodeRec, Count>;
template<typename TNode>
using Nodes = std::array<TNode, Count>;
static int Index(FT_ListNode node, const Nodes& nodes) {
template<typename TNode>
static int Index(TNode* node, const Nodes<TNode>& nodes) {
if (!node) return -1;
for (std::size_t i = 0; i < nodes.size(); ++i) if (node == &nodes[i]) return static_cast<int>(i);
Require(false);
@@ -30,10 +35,10 @@ static int Index(FT_ListNode node, const Nodes& nodes) {
}
struct Lists {
FT_ListRec guest{};
GuestListRec guest{};
FT_ListRec native{};
Nodes actual{};
Nodes expected{};
Nodes<GuestListNodeRec> actual{};
Nodes<FT_ListNodeRec> expected{};
std::array<int, 4> values{11, 22, 33, 44};
Lists() {
@@ -114,9 +119,10 @@ static void Mutations() {
std::printf("list mutations: 2048 states compared with native FreeType\n");
}
template<typename TNode>
struct Walk {
FT_List list;
Nodes* nodes;
std::conditional_t<std::is_same_v<TNode, GuestListNodeRec>, GuestListRec, FT_ListRec>* list;
Nodes<TNode>* nodes;
bool guest;
bool remove;
std::size_t stop;
@@ -124,12 +130,13 @@ struct Walk {
std::vector<int> visits;
};
static int Visit(FT_ListNode node, void* user, bool guest) {
auto& state = *static_cast<Walk*>(user);
template<typename TNode>
static int Visit(TNode* node, void* user, bool guest) {
auto& state = *static_cast<Walk<TNode>*>(user);
Require(state.guest == guest);
state.visits.push_back(Index(node, *state.nodes));
if (state.remove) {
if (guest) FT_List_Remove_nid_postfix(state.list, node);
if constexpr (std::is_same_v<TNode, GuestListNodeRec>) FT_List_Remove_nid_postfix(state.list, node);
else FT_List_Remove(state.list, node);
node->prev = nullptr;
node->next = nullptr;
@@ -172,8 +179,9 @@ struct Event {
bool operator==(const Event&) const = default;
};
template<typename TList>
struct Disposal {
FT_List list;
TList* list;
FT_Memory native;
GuestMemoryRec* guest;
void* user;
@@ -181,34 +189,36 @@ struct Disposal {
std::vector<Event> events;
};
static void Release(Disposal& state, void* block) {
template<typename TList>
static void Release(Disposal<TList>& state, void* block) {
const auto found = state.live.find(block);
Require(found != state.live.end());
state.events.push_back({2, found->second, state.list->head == nullptr || state.list->tail == nullptr});
state.live.erase(found);
delete static_cast<FT_ListNode>(block);
using Node = std::conditional_t<std::is_same_v<TList, GuestListRec>, GuestListNodeRec, FT_ListNodeRec>;
delete static_cast<Node*>(block);
}
static void NativeFree(FT_Memory memory, void* block) {
auto& state = *static_cast<Disposal*>(memory->user);
auto& state = *static_cast<Disposal<FT_ListRec>*>(memory->user);
Require(state.native == memory);
Release(state, block);
}
static void APS5_VABI GuestFree(GuestMemoryRec* memory, void* block) {
auto& state = *static_cast<Disposal*>(memory->user);
auto& state = *static_cast<Disposal<GuestListRec>*>(memory->user);
Require(state.guest == memory);
Release(state, block);
}
static void NativeDestroy(FT_Memory memory, void* data, void* user) {
auto& state = *static_cast<Disposal*>(memory->user);
auto& state = *static_cast<Disposal<FT_ListRec>*>(memory->user);
Require(state.native == memory && state.user == user);
state.events.push_back({1, data ? *static_cast<int*>(data) : -1, state.list->head == nullptr || state.list->tail == nullptr});
}
static void APS5_VABI GuestDestroy(GuestMemoryRec* memory, void* data, void* user) {
auto& state = *static_cast<Disposal*>(memory->user);
auto& state = *static_cast<Disposal<GuestListRec>*>(memory->user);
Require(state.guest == memory && state.user == user);
state.events.push_back({1, data ? *static_cast<int*>(data) : -1, state.list->head == nullptr || state.list->tail == nullptr});
}
@@ -216,7 +226,8 @@ static void APS5_VABI GuestDestroy(GuestMemoryRec* memory, void* data, void* use
static void FinalizationCases() {
for (const bool destroy : {false, true}) {
for (const unsigned count : {0u, 1u, 8u}) {
FT_ListRec guestList{}, nativeList{};
GuestListRec guestList{};
FT_ListRec nativeList{};
std::array<int, 8> ids{0, 1, 2, 3, 4, 5, 6, 7};
int token = 9;
Disposal guest{&guestList, nullptr, nullptr, &token, {}, {}};
@@ -226,7 +237,7 @@ static void FinalizationCases() {
guest.guest = &guestMemory;
native.native = &nativeMemory;
for (unsigned i = 0; i < count; ++i) {
auto* a = new FT_ListNodeRec{};
auto* a = new GuestListNodeRec{};
auto* b = new FT_ListNodeRec{};
a->data = b->data = i == 0 ? nullptr : &ids[i];
guest.live.emplace(a, i);
+23 -4
View File
@@ -5,6 +5,7 @@ import json
import re
import subprocess
import sys
import tempfile
from functools import cache
from pathlib import Path
@@ -171,12 +172,30 @@ def compile_headers(compiler, compiler_args, groups, workers=4):
def compile_one(group):
includes = "".join(f'#include "{header.relative_to(LIBS).as_posix()}"\n' for header in group)
result = subprocess.run([compiler, *compiler_args, "-std=c++20", *warnings, "-fsyntax-only", "-x", "c++", f"-I{LIBS}", f"-I{ROOT / '3rdparty/freetype/include'}", "-"], input=includes * 2, text=True, capture_output=True)
if result.returncode:
raise RuntimeError(f"Header compilation failed: {group}\n{result.stdout}{result.stderr}")
with tempfile.TemporaryDirectory() as directory:
dependencies = Path(directory) / "headers.d"
result = subprocess.run([compiler, *compiler_args, "-std=c++20", *warnings, "-fsyntax-only", "-MD", "-MF", str(dependencies), "-MT", "public_headers", "-x", "c++", f"-I{LIBS}", f"-I{ROOT / '3rdparty/freetype/include'}", "-"], input=includes * 2, text=True, capture_output=True)
if result.returncode:
raise RuntimeError(f"Header compilation failed: {group}\n{result.stdout}{result.stderr}")
text = dependencies.read_text(encoding="utf-8").replace("\\\n", "")
if not text.startswith("public_headers:"):
raise RuntimeError(f"Invalid compiler dependency output: {dependencies}")
names = re.findall(r"(?:\\[ \t]|[^\s])+", text.removeprefix("public_headers:"))
paths = [Path(re.sub(r"\\([ \t#\\])", r"\1", name).replace("$$", "$")).resolve() for name in names]
forbidden = [path for path in paths if path.is_relative_to((ROOT / "3rdparty").resolve())]
if forbidden:
raise RuntimeError(f"Public headers depend on 3rdparty: {group}\n" + "\n".join(map(str, forbidden)))
errors = []
with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as pool:
list(pool.map(compile_one, groups))
futures = [(group, pool.submit(compile_one, group)) for group in groups]
for group, future in futures:
try:
future.result()
except Exception as error:
errors.append(f"{group}: {error}")
if errors:
raise RuntimeError(f"{len(errors)} header compilation units failed:\n\n" + "\n\n".join(errors))
def main():