Add clangd support for VS Code (#2207)

* Add clangd support for VS Code

* Revert accidental change
This commit is contained in:
Luke Street 2024-10-12 22:43:10 -06:00 committed by GitHub
parent 7ec2d80414
commit 0db466edfa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 436 additions and 119 deletions

38
.gitignore vendored
View File

@ -1,13 +1,35 @@
# IDE folders
.idea/
.vs/
# Caches
__pycache__ __pycache__
.idea
.vscode
.ninja_*
.mypy_cache .mypy_cache
*.exe .cache/
build
build.ninja # Original files
objdiff.json
orig/*/* orig/*/*
!orig/*/.gitkeep !orig/*/.gitkeep
*.dol
*.rel
*.elf
*.o
*.map
*.MAP
# Build files
build/
.ninja_*
build.ninja
# decompctx output
ctx.*
*.ctx
# Generated configs
objdiff.json
compile_commands.json
# Miscellaneous
/*.txt /*.txt
ctx.c *.exe

12
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,12 @@
{
"recommendations": [
"llvm-vs-code-extensions.vscode-clangd",
"ms-python.black-formatter",
"ms-python.flake8",
],
"unwantedRecommendations": [
"ms-vscode.cmake-tools",
"ms-vscode.cpptools-extension-pack",
"ms-vscode.cpptools",
]
}

23
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,23 @@
{
"[c]": {
"files.encoding": "utf8",
"editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd"
},
"[cpp]": {
"files.encoding": "utf8",
"editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd"
},
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
},
"editor.tabSize": 4,
"files.autoSave": "onFocusChange",
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"files.associations": {
"*.inc": "c",
".clangd": "yaml"
},
// Disable C/C++ IntelliSense, use clangd instead
"C_Cpp.intelliSenseEngine": "disabled",
}

16
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
// Use Ctrl+Shift+B to run build tasks.
// Or "Run Build Task" in the Command Palette.
"version": "2.0.0",
"tasks": [
{
"label": "ninja",
"type": "shell",
"command": "ninja",
"group": {
"kind": "build",
"isDefault": true
}
},
]
}

View File

@ -249,9 +249,9 @@ struct TLinkList_factory : public TLinkList<T, I> {
virtual T* Do_create() = 0; virtual T* Do_create() = 0;
virtual void Do_destroy(T*) = 0; virtual void Do_destroy(T*) = 0;
void Clear_destroy() { void Clear_destroy() {
while (!empty()) { while (!this->empty()) {
T* item = &front(); T* item = &this->front();
pop_front(); this->pop_front();
Do_destroy(item); Do_destroy(item);
} }
} }
@ -298,16 +298,16 @@ struct TEnumerator2 {
}; };
template <typename T, int I> template <typename T, int I>
struct TContainerEnumerator : public TEnumerator2<TLinkList<T, I>::iterator, T> { struct TContainerEnumerator : public TEnumerator2<typename TLinkList<T, I>::iterator, T> {
inline TContainerEnumerator(TLinkList<T, I>* param_0) inline TContainerEnumerator(TLinkList<T, I>* param_0)
: TEnumerator2<TLinkList<T, I>::iterator, T>(param_0->begin(), param_0->end()) {} : TEnumerator2<typename TLinkList<T, I>::iterator, T>(param_0->begin(), param_0->end()) {}
}; };
template <typename T, int I> template <typename T, int I>
struct TContainerEnumerator_const : public TEnumerator2<TLinkList<T, I>::const_iterator, const T> { struct TContainerEnumerator_const : public TEnumerator2<typename TLinkList<T, I>::const_iterator, const T> {
inline TContainerEnumerator_const(const TLinkList<T, I>* param_0) inline TContainerEnumerator_const(const TLinkList<T, I>* param_0)
: TEnumerator2<TLinkList<T, I>::const_iterator, const T>(param_0->begin(), param_0->end()) {} : TEnumerator2<typename TLinkList<T, I>::const_iterator, const T>(param_0->begin(), param_0->end()) {}
}; };
}; // namespace JGadget }; // namespace JGadget

View File

@ -2,7 +2,7 @@
#define DOL2ASM #define DOL2ASM
// this helps remove useless error from the linter when using vscode. // this helps remove useless error from the linter when using vscode.
#ifndef IN_VSCODE_EDITOR #ifdef __MWERKS__
#ifdef __cplusplus #ifdef __cplusplus
#define SECTION_INIT extern "C" __declspec(section ".init") #define SECTION_INIT extern "C" __declspec(section ".init")

View File

@ -8,8 +8,8 @@
extern "C" { extern "C" {
#endif #endif
volatile u16 __DSPRegs[32] : 0xCC005000; volatile u16 __DSPRegs[32] AT_ADDRESS(0xCC005000);
volatile u32 __AIRegs[8] : 0xCC006C00; volatile u32 __AIRegs[8] AT_ADDRESS(0xCC006C00);
#define DSP_TASK_FLAG_CLEARALL 0x00000000 #define DSP_TASK_FLAG_CLEARALL 0x00000000
#define DSP_TASK_FLAG_ATTACHED 0x00000001 #define DSP_TASK_FLAG_ATTACHED 0x00000001

View File

@ -4,6 +4,6 @@
#include "dolphin/types.h" #include "dolphin/types.h"
typedef void (*DVDLowCallback)(u32 intType); typedef void (*DVDLowCallback)(u32 intType);
vu32 __DIRegs[16] : 0xCC006000; vu32 __DIRegs[16] AT_ADDRESS(0xCC006000);
#endif /* DVDLOW_H */ #endif /* DVDLOW_H */

View File

@ -9,7 +9,7 @@ extern "C" {
typedef struct OSContext OSContext; typedef struct OSContext OSContext;
vu32 __EXIRegs[16] : 0xCC006800; vu32 __EXIRegs[16] AT_ADDRESS(0xCC006800);
#define EXI_MEMORY_CARD_59 0x00000004 #define EXI_MEMORY_CARD_59 0x00000004
#define EXI_MEMORY_CARD_123 0x00000008 #define EXI_MEMORY_CARD_123 0x00000008

View File

@ -58,7 +58,7 @@ typedef union {
} PPCWGPipe; } PPCWGPipe;
#define GXFIFO_ADDR 0xCC008000 #define GXFIFO_ADDR 0xCC008000
volatile PPCWGPipe GXWGFifo : GXFIFO_ADDR; volatile PPCWGPipe GXWGFifo AT_ADDRESS(GXFIFO_ADDR);
#define GX_WRITE_U8(data) GXWGFifo.u8 = data; #define GX_WRITE_U8(data) GXWGFifo.u8 = data;
#define GX_WRITE_U32(data) GXWGFifo.u32 = data; #define GX_WRITE_U32(data) GXWGFifo.u32 = data;

View File

@ -65,19 +65,19 @@ extern "C" {
#define OS_CONSOLE_PC_EMULATOR 0x10000001 #define OS_CONSOLE_PC_EMULATOR 0x10000001
#define OS_CONSOLE_EMULATOR 0x10000000 #define OS_CONSOLE_EMULATOR 0x10000000
volatile u16 __OSDeviceCode : 0x800030E6; volatile u16 __OSDeviceCode AT_ADDRESS(0x800030E6);
volatile u32 OS_PI_INTR_CAUSE : 0xCC003000; volatile u32 OS_PI_INTR_CAUSE AT_ADDRESS(0xCC003000);
volatile u32 OS_PI_INTR_MASK : 0xCC003004; volatile u32 OS_PI_INTR_MASK AT_ADDRESS(0xCC003004);
volatile u16 OS_MI_INTR_MASK : 0xCC00401C; volatile u16 OS_MI_INTR_MASK AT_ADDRESS(0xCC00401C);
volatile u16 OS_DSP_DMA_ADDR_HI : 0xCC005030; volatile u16 OS_DSP_DMA_ADDR_HI AT_ADDRESS(0xCC005030);
volatile u16 OS_DSP_DMA_ADDR_LO : 0xCC005032; volatile u16 OS_DSP_DMA_ADDR_LO AT_ADDRESS(0xCC005032);
volatile u16 OS_DSP_INTR_MASK : 0xCC00500A; volatile u16 OS_DSP_INTR_MASK AT_ADDRESS(0xCC00500A);
volatile u16 OS_ARAM_DMA_ADDR_HI : 0xCC005020; volatile u16 OS_ARAM_DMA_ADDR_HI AT_ADDRESS(0xCC005020);
volatile u16 OS_ARAM_DMA_ADDR_LO : 0xCC005022; volatile u16 OS_ARAM_DMA_ADDR_LO AT_ADDRESS(0xCC005022);
BOOL OSIsThreadSuspended(OSThread* thread); BOOL OSIsThreadSuspended(OSThread* thread);

View File

@ -151,8 +151,8 @@ typedef struct OSContext {
/* 0x1C4 */ f64 ps[32]; /* 0x1C4 */ f64 ps[32];
} OSContext; } OSContext;
OSContext* OS_CURRENT_CONTEXT : 0x800000D4; OSContext* OS_CURRENT_CONTEXT AT_ADDRESS(0x800000D4);
OSContext* OS_CURRENT_FPU_CONTEXT : 0x800000D8; OSContext* OS_CURRENT_FPU_CONTEXT AT_ADDRESS(0x800000D8);
void __OSLoadFPUContext(void); void __OSLoadFPUContext(void);
void __OSSaveFPUContext(s32 unused0, s32 unused1, OSContext* context); void __OSSaveFPUContext(s32 unused0, s32 unused1, OSContext* context);

View File

@ -1,6 +1,7 @@
#ifndef OSLINK_H #ifndef OSLINK_H
#define OSLINK_H #define OSLINK_H
#include "dolphin/types.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -18,14 +19,14 @@ typedef struct OSSectionInfo OSSectionInfo;
typedef struct OSImportInfo OSImportInfo; typedef struct OSImportInfo OSImportInfo;
typedef struct OSRel OSRel; typedef struct OSRel OSRel;
OSModuleQueue __OSModuleList : 0x800030C8;
void* __OSStringTable : 0x800030D0;
struct OSModuleQueue { struct OSModuleQueue {
OSModuleInfo* head; OSModuleInfo* head;
OSModuleInfo* tail; OSModuleInfo* tail;
}; };
OSModuleQueue __OSModuleList AT_ADDRESS(0x800030C8);
void* __OSStringTable AT_ADDRESS(0x800030D0);
struct OSModuleLink { struct OSModuleLink {
OSModuleInfo* next; OSModuleInfo* next;
OSModuleInfo* prev; OSModuleInfo* prev;

View File

@ -7,7 +7,7 @@
extern "C" { extern "C" {
#endif #endif
vu32 __PIRegs[12] : 0xCC003000; vu32 __PIRegs[12] AT_ADDRESS(0xCC003000);
#define OS_RESETCODE_RESTART 0x80000000 #define OS_RESETCODE_RESTART 0x80000000
#define OS_RESETCODE_SYSTEM 0x40000000 #define OS_RESETCODE_SYSTEM 0x40000000

View File

@ -75,8 +75,8 @@ struct OSThread {
typedef void (*OSSwitchThreadCallback)(OSThread* from, OSThread* to); typedef void (*OSSwitchThreadCallback)(OSThread* from, OSThread* to);
OSThreadQueue OS_THREAD_QUEUE : 0x800000DC; OSThreadQueue OS_THREAD_QUEUE AT_ADDRESS(0x800000DC);
OSThread* OS_CURRENT_THREAD : 0x800000E4; OSThread* OS_CURRENT_THREAD AT_ADDRESS(0x800000E4);
static void DefaultSwitchThreadCallback(OSThread* from, OSThread* to); static void DefaultSwitchThreadCallback(OSThread* from, OSThread* to);
OSSwitchThreadCallback OSSetSwitchThreadCallback(OSSwitchThreadCallback func); OSSwitchThreadCallback OSSetSwitchThreadCallback(OSSwitchThreadCallback func);

View File

@ -10,7 +10,7 @@ extern "C" {
typedef s64 OSTime; typedef s64 OSTime;
typedef u32 OSTick; typedef u32 OSTick;
OSTime OS_SYSTEM_TIME : 0x800030D8; OSTime OS_SYSTEM_TIME AT_ADDRESS(0x800030D8);
typedef struct OSCalendarTime { typedef struct OSCalendarTime {
/* 0x00 */ s32 seconds; /* 0x00 */ s32 seconds;
@ -32,7 +32,7 @@ OSTime __OSTimeToSystemTime(OSTime time);
void GetDates(s32 days, OSCalendarTime* ct); void GetDates(s32 days, OSCalendarTime* ct);
void OSTicksToCalendarTime(OSTime ticks, OSCalendarTime* ct); void OSTicksToCalendarTime(OSTime ticks, OSCalendarTime* ct);
extern u32 __OSBusClock : 0x800000F8; extern u32 __OSBusClock AT_ADDRESS(0x800000F8);
#define OS_BUS_CLOCK (__OSBusClock) #define OS_BUS_CLOCK (__OSBusClock)
#define OS_CORE_CLOCK (*(u32*)0x800000FC) #define OS_CORE_CLOCK (*(u32*)0x800000FC)

View File

@ -120,7 +120,7 @@ BOOL SITransfer(s32 chan, void* output, u32 outputBytes, void* input, u32 inputB
u32 SIGetType(s32 chan); u32 SIGetType(s32 chan);
u32 SIGetTypeAsync(s32 chan, SITypeAndStatusCallback callback); u32 SIGetTypeAsync(s32 chan, SITypeAndStatusCallback callback);
vu32 __SIRegs[64] : 0xCC006400; vu32 __SIRegs[64] AT_ADDRESS(0xCC006400);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -42,4 +42,10 @@ typedef int BOOL;
#define FLOAT_MIN (1.175494351e-38f) #define FLOAT_MIN (1.175494351e-38f)
#define FLOAT_MAX (3.40282346638528860e+38f) #define FLOAT_MAX (3.40282346638528860e+38f)
#endif #ifdef __MWERKS__
#define AT_ADDRESS(xyz) : (xyz)
#else
#define AT_ADDRESS(xyz)
#endif
#endif

View File

@ -138,7 +138,7 @@ u32 VIGetRetraceCount();
u32 VIGetDTVStatus(); u32 VIGetDTVStatus();
u32 VIGetTvFormat(void); u32 VIGetTvFormat(void);
vu16 __VIRegs[59] : 0xCC002000; vu16 __VIRegs[59] AT_ADDRESS(0xCC002000);
#ifdef __cplusplus #ifdef __cplusplus
}; };

View File

@ -24,7 +24,7 @@
#define _SDA_BASE_(dummy) 0 #define _SDA_BASE_(dummy) 0
#define _SDA2_BASE_(dummy) 0 #define _SDA2_BASE_(dummy) 0
#ifndef IN_VSCODE_EDITOR #ifdef __MWERKS__
#define GLUE(a, b) a##b #define GLUE(a, b) a##b
#define GLUE2(a, b) GLUE(a, b) #define GLUE2(a, b) GLUE(a, b)
#define STATIC_ASSERT(cond) typedef char GLUE2(static_assertion_failed, __LINE__)[(cond) ? 1 : -1] #define STATIC_ASSERT(cond) typedef char GLUE2(static_assertion_failed, __LINE__)[(cond) ? 1 : -1]

View File

@ -23,4 +23,4 @@ typedef long ptrdiff_t;
}; };
#endif #endif
#endif #endif

View File

@ -19,7 +19,7 @@ extern "C" void* __va_arg(_va_list_struct*, int);
void* __va_arg(_va_list_struct*, int); void* __va_arg(_va_list_struct*, int);
#endif #endif
#if IN_VSCODE_EDITOR #ifndef __MWERKS__
#define __builtin_va_info(...) #define __builtin_va_info(...)
#define _var_arg_typeof(...) #define _var_arg_typeof(...)
#endif #endif
@ -37,4 +37,4 @@ void* __va_arg(_va_list_struct*, int);
#define __va_copy(a, b) (*(a) = *(b)) #define __va_copy(a, b) (*(a) = *(b))
#endif /* __VA_ARG_H */ #endif /* __VA_ARG_H */

View File

@ -3,7 +3,7 @@
#include "dolphin/card/CARDPriv.h" #include "dolphin/card/CARDPriv.h"
#include "dolphin/os/OSRtc.h" #include "dolphin/os/OSRtc.h"
u8 GameChoice : 0x800030E3; u8 GameChoice AT_ADDRESS(0x800030E3);
static BOOL IsCard(u32 id); static BOOL IsCard(u32 id);
static s32 DoMount(s32 chan); static s32 DoMount(s32 chan);
@ -395,4 +395,4 @@ s32 CARDUnmount(s32 chan) {
} }
DoUnmount(chan, CARD_RESULT_NOCARD); DoUnmount(chan, CARD_RESULT_NOCARD);
return CARD_RESULT_READY; return CARD_RESULT_READY;
} }

View File

@ -678,7 +678,7 @@ asm void __OSPSInit(void){
// clang-format on // clang-format on
} }
vu32 __DIRegs[16] : 0xCC006000; vu32 __DIRegs[16] AT_ADDRESS(0xCC006000);
#define DI_CONFIG_IDX 0x9 #define DI_CONFIG_IDX 0x9
#define DI_CONFIG_CONFIG_MASK 0xFF #define DI_CONFIG_CONFIG_MASK 0xFF
@ -690,4 +690,4 @@ u32 __OSGetDIConfig(void) {
/* 8033A874-8033A8A0 3351B4 002C+00 1/1 11/11 0/0 .text OSRegisterVersion */ /* 8033A874-8033A8A0 3351B4 002C+00 1/1 11/11 0/0 .text OSRegisterVersion */
void OSRegisterVersion(const char* version) { void OSRegisterVersion(const char* version) {
OSReport("%s\n", version); OSReport("%s\n", version);
} }

View File

@ -104,7 +104,7 @@ static void Callback(s32, DVDCommandBlock*) {
Prepared = TRUE; Prepared = TRUE;
} }
OSExecParams* osExecParams : 0x800030f0; OSExecParams* osExecParams AT_ADDRESS(0x800030f0);
static int IsStreamEnabled() { static int IsStreamEnabled() {
if (DVDGetCurrentDiskID()->is_streaming) { if (DVDGetCurrentDiskID()->is_streaming) {
@ -148,7 +148,7 @@ static void StopStreaming() {
} }
/* 8033CCFC-8033CDC0 33763C 00C4+00 1/1 0/0 0/0 .text GetApploaderPosition */ /* 8033CCFC-8033CDC0 33763C 00C4+00 1/1 0/0 0/0 .text GetApploaderPosition */
s32 __OSAppLoaderOffset : 0x800030f4; s32 __OSAppLoaderOffset AT_ADDRESS(0x800030f4);
static int GetApploaderPosition(void) { static int GetApploaderPosition(void) {
static s32 apploaderPosition; static s32 apploaderPosition;
@ -220,9 +220,9 @@ static BOOL IsNewApploader(AppLoaderStruct* header) {
return strncmp(header->date, "2004/02/01", 10) > 0 ? TRUE : FALSE; return strncmp(header->date, "2004/02/01", 10) > 0 ? TRUE : FALSE;
} }
extern volatile u32 BOOT_REGION_START : 0x812FDFF0; extern volatile u32 BOOT_REGION_START AT_ADDRESS(0x812FDFF0);
extern volatile u32 BOOT_REGION_END : 0x812FDFEC; extern volatile u32 BOOT_REGION_END AT_ADDRESS(0x812FDFEC);
extern volatile u8 g_unk_800030E2 : 0x800030E2; extern volatile u8 g_unk_800030E2 AT_ADDRESS(0x800030E2);
/* 8033CDC0-8033D244 337700 0484+00 1/1 0/0 0/0 .text __OSBootDolSimple */ /* 8033CDC0-8033D244 337700 0484+00 1/1 0/0 0/0 .text __OSBootDolSimple */
void __OSBootDolSimple(u32 doloffset, u32 restartCode, void* regionStart, void* regionEnd, BOOL argsUseDefault, s32 argc, char** argv) { void __OSBootDolSimple(u32 doloffset, u32 restartCode, void* regionStart, void* regionEnd, BOOL argsUseDefault, s32 argc, char** argv) {
@ -312,4 +312,4 @@ void __OSBootDol(u32 doloffset, u32 restartCode, const char** argv) {
} }
__OSBootDolSimple(-1, restartCode, saveStart, saveEnd, FALSE, argvlen, argvToPass); __OSBootDolSimple(-1, restartCode, saveStart, saveEnd, FALSE, argvlen, argvToPass);
} }

View File

@ -3,8 +3,8 @@
#include "dolphin/exi/EXIBios.h" #include "dolphin/exi/EXIBios.h"
#include "dolphin/os.h" #include "dolphin/os.h"
vu32 __PIRegs[12] : 0xCC003000; vu32 __PIRegs[12] AT_ADDRESS(0xCC003000);
vu16 __MEMRegs[64] : 0xCC004000; vu16 __MEMRegs[64] AT_ADDRESS(0xCC004000);
/* 8033D6F4-8033D700 338034 000C+00 2/2 200/200 5/5 .text OSDisableInterrupts */ /* 8033D6F4-8033D700 338034 000C+00 2/2 200/200 5/5 .text OSDisableInterrupts */
asm BOOL OSDisableInterrupts(void) { asm BOOL OSDisableInterrupts(void) {
@ -445,4 +445,4 @@ static asm void ExternalInterruptHandler(register __OSInterrupt type, register O
stwu r1, -8(r1) stwu r1, -8(r1)
b __OSDispatchInterrupt b __OSDispatchInterrupt
// clang-format on // clang-format on
} }

View File

@ -4,7 +4,7 @@
#define TRUNC(n, a) (((u32)(n)) & ~((a)-1)) #define TRUNC(n, a) (((u32)(n)) & ~((a)-1))
#define ROUND(n, a) (((u32)(n) + (a)-1) & ~((a)-1)) #define ROUND(n, a) (((u32)(n) + (a)-1) & ~((a)-1))
vu16 __MEMRegs[64] : 0xCC004000; vu16 __MEMRegs[64] AT_ADDRESS(0xCC004000);
extern OSErrorHandlerEx __OSErrorTable[16]; extern OSErrorHandlerEx __OSErrorTable[16];
@ -216,4 +216,4 @@ void __OSInitMemoryProtection() {
__OSUnmaskInterrupts(OS_INTERRUPTMASK_MEM_ADDRESS); __OSUnmaskInterrupts(OS_INTERRUPTMASK_MEM_ADDRESS);
OSRestoreInterrupts(enabled); OSRestoreInterrupts(enabled);
} }

View File

@ -1,7 +1,7 @@
#include "dolphin/os/OSReset.h" #include "dolphin/os/OSReset.h"
#include "dolphin/os.h" #include "dolphin/os.h"
vu16 __VIRegs[59] : 0xCC002000; vu16 __VIRegs[59] AT_ADDRESS(0xCC002000);
OSThreadQueue __OSActiveThreadQueue : (OS_BASE_CACHED | 0x00DC); OSThreadQueue __OSActiveThreadQueue : (OS_BASE_CACHED | 0x00DC);
extern OSExecParams __OSRebootParams; extern OSExecParams __OSRebootParams;
@ -217,4 +217,4 @@ u32 OSGetResetCode(void) {
return 0x80000000 | __OSRebootParams.restartCode; return 0x80000000 | __OSRebootParams.restartCode;
return ((__PIRegs[9] & ~7) >> 3); return ((__PIRegs[9] & ~7) >> 3);
} }

View File

@ -17,7 +17,7 @@ import os
import platform import platform
import sys import sys
from pathlib import Path from pathlib import Path
from typing import IO, Any, Dict, List, Optional, Set, Tuple, Union, cast from typing import IO, Any, Dict, Iterable, List, Optional, Set, Tuple, cast
from . import ninja_syntax from . import ninja_syntax
from .ninja_syntax import serialize_path from .ninja_syntax import serialize_path
@ -155,6 +155,9 @@ class ProjectConfig:
self.custom_build_steps: Optional[Dict[str, List[Dict[str, Any]]]] = ( self.custom_build_steps: Optional[Dict[str, List[Dict[str, Any]]]] = (
None # Custom build steps, types are ["pre-compile", "post-compile", "post-link", "post-build"] None # Custom build steps, types are ["pre-compile", "post-compile", "post-link", "post-build"]
) )
self.generate_compile_commands: bool = (
True # Generate compile_commands.json for clangd
)
# Progress output, progress.json and report.json config # Progress output, progress.json and report.json config
self.progress = True # Enable progress output self.progress = True # Enable progress output
@ -200,9 +203,40 @@ class ProjectConfig:
out[obj.name] = obj.resolve(self, lib) out[obj.name] = obj.resolve(self, lib)
return out return out
# Gets the output path for build-related files.
def out_path(self) -> Path: def out_path(self) -> Path:
return self.build_dir / str(self.version) return self.build_dir / str(self.version)
# Gets the path to the compilers directory.
# Exits the program if neither `compilers_path` nor `compilers_tag` is provided.
def compilers(self) -> Path:
if self.compilers_path:
return self.compilers_path
elif self.compilers_tag:
return self.build_dir / "compilers"
else:
sys.exit("ProjectConfig.compilers_tag missing")
# Gets the wrapper to use for compiler commands, if set.
def compiler_wrapper(self) -> Optional[Path]:
wrapper = self.wrapper
if self.use_wibo():
wrapper = self.build_dir / "tools" / "wibo"
if not is_windows() and wrapper is None:
wrapper = Path("wine")
return wrapper
# Determines whether or not to use wibo as the compiler wrapper.
def use_wibo(self) -> bool:
return (
self.wibo_tag is not None
and sys.platform == "linux"
and platform.machine() in ("i386", "x86_64")
and self.wrapper is None
)
def is_windows() -> bool: def is_windows() -> bool:
return os.name == "nt" return os.name == "nt"
@ -214,13 +248,26 @@ CHAIN = "cmd /c " if is_windows() else ""
EXE = ".exe" if is_windows() else "" EXE = ".exe" if is_windows() else ""
def make_flags_str(flags: Optional[Union[str, List[str]]]) -> str: def file_is_asm(path: Path) -> bool:
return path.suffix.lower() == ".s"
def file_is_c(path: Path) -> bool:
return path.suffix.lower() == ".c"
def file_is_cpp(path: Path) -> bool:
return path.suffix.lower() in (".cc", ".cp", ".cpp", ".cxx")
def file_is_c_cpp(path: Path) -> bool:
return file_is_c(path) or file_is_cpp(path)
def make_flags_str(flags: Optional[List[str]]) -> str:
if flags is None: if flags is None:
return "" return ""
elif isinstance(flags, list): return " ".join(flags)
return " ".join(flags)
else:
return flags
# Load decomp-toolkit generated config.json # Load decomp-toolkit generated config.json
@ -253,13 +300,14 @@ def load_build_config(
return build_config return build_config
# Generate build.ninja and objdiff.json # Generate build.ninja, objdiff.json and compile_commands.json
def generate_build(config: ProjectConfig) -> None: def generate_build(config: ProjectConfig) -> None:
config.validate() config.validate()
objects = config.objects() objects = config.objects()
build_config = load_build_config(config, config.out_path() / "config.json") build_config = load_build_config(config, config.out_path() / "config.json")
generate_build_ninja(config, objects, build_config) generate_build_ninja(config, objects, build_config)
generate_objdiff_config(config, objects, build_config) generate_objdiff_config(config, objects, build_config)
generate_compile_commands(config, objects, build_config)
# Generate build.ninja # Generate build.ninja
@ -406,16 +454,10 @@ def generate_build_ninja(
else: else:
sys.exit("ProjectConfig.sjiswrap_tag missing") sys.exit("ProjectConfig.sjiswrap_tag missing")
wrapper = config.compiler_wrapper()
# Only add an implicit dependency on wibo if we download it # Only add an implicit dependency on wibo if we download it
wrapper = config.wrapper
wrapper_implicit: Optional[Path] = None wrapper_implicit: Optional[Path] = None
if ( if wrapper is not None and config.use_wibo():
config.wibo_tag is not None
and sys.platform == "linux"
and platform.machine() in ("i386", "x86_64")
and config.wrapper is None
):
wrapper = build_tools_path / "wibo"
wrapper_implicit = wrapper wrapper_implicit = wrapper
n.build( n.build(
outputs=wrapper, outputs=wrapper,
@ -426,15 +468,11 @@ def generate_build_ninja(
"tag": config.wibo_tag, "tag": config.wibo_tag,
}, },
) )
if not is_windows() and wrapper is None:
wrapper = Path("wine")
wrapper_cmd = f"{wrapper} " if wrapper else "" wrapper_cmd = f"{wrapper} " if wrapper else ""
compilers = config.compilers()
compilers_implicit: Optional[Path] = None compilers_implicit: Optional[Path] = None
if config.compilers_path: if config.compilers_path is None and config.compilers_tag is not None:
compilers = config.compilers_path
elif config.compilers_tag:
compilers = config.build_dir / "compilers"
compilers_implicit = compilers compilers_implicit = compilers
n.build( n.build(
outputs=compilers, outputs=compilers,
@ -445,8 +483,6 @@ def generate_build_ninja(
"tag": config.compilers_tag, "tag": config.compilers_tag,
}, },
) )
else:
sys.exit("ProjectConfig.compilers_tag missing")
binutils_implicit = None binutils_implicit = None
if config.binutils_path: if config.binutils_path:
@ -660,7 +696,6 @@ def generate_build_ninja(
n.comment(f"Link {self.name}") n.comment(f"Link {self.name}")
if self.module_id == 0: if self.module_id == 0:
elf_path = build_path / f"{self.name}.elf" elf_path = build_path / f"{self.name}.elf"
dol_path = build_path / f"{self.name}.dol"
elf_ldflags = f"$ldflags -lcf {serialize_path(self.ldscript)}" elf_ldflags = f"$ldflags -lcf {serialize_path(self.ldscript)}"
if config.generate_map: if config.generate_map:
elf_map = map_path(elf_path) elf_map = map_path(elf_path)
@ -725,17 +760,36 @@ def generate_build_ninja(
source_added: Set[Path] = set() source_added: Set[Path] = set()
def c_build(obj: Object, src_path: Path) -> Optional[Path]: def c_build(obj: Object, src_path: Path) -> Optional[Path]:
cflags_str = make_flags_str(obj.options["cflags"])
if obj.options["extra_cflags"] is not None:
extra_cflags_str = make_flags_str(obj.options["extra_cflags"])
cflags_str += " " + extra_cflags_str
used_compiler_versions.add(obj.options["mw_version"])
# Avoid creating duplicate build rules # Avoid creating duplicate build rules
if obj.src_obj_path is None or obj.src_obj_path in source_added: if obj.src_obj_path is None or obj.src_obj_path in source_added:
return obj.src_obj_path return obj.src_obj_path
source_added.add(obj.src_obj_path) source_added.add(obj.src_obj_path)
cflags = obj.options["cflags"]
extra_cflags = obj.options["extra_cflags"]
# Add appropriate language flag if it doesn't exist already
# Added directly to the source so it flows to other generation tasks
if not any(flag.startswith("-lang") for flag in cflags) and (
extra_cflags is None
or not any(flag.startswith("-lang") for flag in extra_cflags)
):
# Ensure extra_cflags is a unique instance,
# and insert into there to avoid modifying shared sets of flags
if extra_cflags is None:
extra_cflags = []
extra_cflags = obj.options["extra_cflags"] = list(extra_cflags)
if file_is_cpp(src_path):
extra_cflags.insert(0, "-lang=c++")
else:
extra_cflags.insert(0, "-lang=c")
cflags_str = make_flags_str(cflags)
if extra_cflags is not None:
extra_cflags_str = make_flags_str(extra_cflags)
cflags_str += " " + extra_cflags_str
used_compiler_versions.add(obj.options["mw_version"])
# Add MWCC build rule # Add MWCC build rule
lib_name = obj.options["lib"] lib_name = obj.options["lib"]
n.comment(f"{obj.name}: {lib_name} (linked {obj.completed})") n.comment(f"{obj.name}: {lib_name} (linked {obj.completed})")
@ -767,7 +821,7 @@ def generate_build_ninja(
if obj.options["host"] and obj.host_obj_path is not None: if obj.options["host"] and obj.host_obj_path is not None:
n.build( n.build(
outputs=obj.host_obj_path, outputs=obj.host_obj_path,
rule="host_cc" if src_path.suffix == ".c" else "host_cpp", rule="host_cc" if file_is_c(src_path) else "host_cpp",
inputs=src_path, inputs=src_path,
variables={ variables={
"basedir": os.path.dirname(obj.host_obj_path), "basedir": os.path.dirname(obj.host_obj_path),
@ -827,10 +881,10 @@ def generate_build_ninja(
link_built_obj = obj.completed link_built_obj = obj.completed
built_obj_path: Optional[Path] = None built_obj_path: Optional[Path] = None
if obj.src_path is not None and obj.src_path.exists(): if obj.src_path is not None and obj.src_path.exists():
if obj.src_path.suffix in (".c", ".cp", ".cpp"): if file_is_c_cpp(obj.src_path):
# Add MWCC & host build rules # Add MWCC & host build rules
built_obj_path = c_build(obj, obj.src_path) built_obj_path = c_build(obj, obj.src_path)
elif obj.src_path.suffix == ".s": elif file_is_asm(obj.src_path):
# Add assembler build rule # Add assembler build rule
built_obj_path = asm_build(obj, obj.src_path, obj.src_obj_path) built_obj_path = asm_build(obj, obj.src_path, obj.src_obj_path)
else: else:
@ -1274,30 +1328,20 @@ def generate_objdiff_config(
cflags = obj.options["cflags"] cflags = obj.options["cflags"]
reverse_fn_order = False reverse_fn_order = False
if type(cflags) is list: for flag in cflags:
for flag in cflags: if not flag.startswith("-inline "):
if not flag.startswith("-inline "): continue
continue for value in flag.split(" ")[1].split(","):
for value in flag.split(" ")[1].split(","): if value == "deferred":
if value == "deferred": reverse_fn_order = True
reverse_fn_order = True elif value == "nodeferred":
elif value == "nodeferred": reverse_fn_order = False
reverse_fn_order = False
# Filter out include directories # Filter out include directories
def keep_flag(flag): def keep_flag(flag):
return not flag.startswith("-i ") and not flag.startswith("-I ") return not flag.startswith("-i ") and not flag.startswith("-I ")
cflags = list(filter(keep_flag, cflags)) cflags = list(filter(keep_flag, cflags))
# Add appropriate lang flag
if obj.src_path is not None and not any(
flag.startswith("-lang") for flag in cflags
):
if obj.src_path.suffix in (".cp", ".cpp"):
cflags.insert(0, "-lang=c++")
else:
cflags.insert(0, "-lang=c")
compiler_version = COMPILER_MAP.get(obj.options["mw_version"]) compiler_version = COMPILER_MAP.get(obj.options["mw_version"])
if compiler_version is None: if compiler_version is None:
@ -1388,6 +1432,199 @@ def generate_objdiff_config(
json.dump(cleandict(objdiff_config), w, indent=2, default=unix_path) json.dump(cleandict(objdiff_config), w, indent=2, default=unix_path)
def generate_compile_commands(
config: ProjectConfig,
objects: Dict[str, Object],
build_config: Optional[Dict[str, Any]],
) -> None:
if build_config is None or not config.generate_compile_commands:
return
# The following code attempts to convert mwcc flags to clang flags
# for use with clangd.
# Flags to ignore explicitly
CFLAG_IGNORE: Set[str] = {
# Search order modifier
# Has a different meaning to Clang, and would otherwise
# be picked up by the include passthrough prefix
"-I-",
"-i-",
}
CFLAG_IGNORE_PREFIX: Tuple[str, ...] = tuple()
# Flags to replace
CFLAG_REPLACE: Dict[str, str] = {}
CFLAG_REPLACE_PREFIX: Tuple[Tuple[str, str], ...] = (
# Includes
("-i ", "-I"),
("-I ", "-I"),
("-I+", "-I"),
# Defines
("-d ", "-D"),
("-D ", "-D"),
("-D+", "-D"),
)
# Flags with a finite set of options
CFLAG_REPLACE_OPTIONS: Tuple[Tuple[str, Dict[str, Tuple[str, ...]]], ...] = (
# Exceptions
(
"-Cpp_exceptions",
{
"off": ("-fno-cxx-exceptions",),
"on": ("-fcxx-exceptions",),
},
),
# RTTI
(
"-RTTI",
{
"off": ("-fno-rtti",),
"on": ("-frtti",),
},
),
# Language configuration
(
"-lang",
{
"c": ("--language=c", "--std=c89"),
"c99": ("--language=c", "--std=c99"),
"c++": ("--language=c++", "--std=c++98"),
"cplus": ("--language=c++", "--std=c++98"),
},
),
)
# Flags to pass through
CFLAG_PASSTHROUGH: Set[str] = set()
CFLAG_PASSTHROUGH_PREFIX: Tuple[str, ...] = (
"-I", # includes
"-D", # defines
)
clangd_config = []
def add_unit(build_obj: Dict[str, Any]) -> None:
obj = objects.get(build_obj["name"])
if obj is None:
return
# Skip unresolved objects
if (
obj.src_path is None
or obj.src_obj_path is None
or not file_is_c_cpp(obj.src_path)
):
return
# Gather cflags for source file
cflags: list[str] = []
def append_cflags(flags: Iterable[str]) -> None:
# Match a flag against either a set of concrete flags, or a set of prefixes.
def flag_match(
flag: str, concrete: Set[str], prefixes: Tuple[str, ...]
) -> bool:
if flag in concrete:
return True
for prefix in prefixes:
if flag.startswith(prefix):
return True
return False
# Determine whether a flag should be ignored.
def should_ignore(flag: str) -> bool:
return flag_match(flag, CFLAG_IGNORE, CFLAG_IGNORE_PREFIX)
# Determine whether a flag should be passed through.
def should_passthrough(flag: str) -> bool:
return flag_match(flag, CFLAG_PASSTHROUGH, CFLAG_PASSTHROUGH_PREFIX)
# Attempts replacement for the given flag.
def try_replace(flag: str) -> bool:
replacement = CFLAG_REPLACE.get(flag)
if replacement is not None:
cflags.append(replacement)
return True
for prefix, replacement in CFLAG_REPLACE_PREFIX:
if flag.startswith(prefix):
cflags.append(flag.replace(prefix, replacement, 1))
return True
for prefix, options in CFLAG_REPLACE_OPTIONS:
if not flag.startswith(prefix):
continue
# "-lang c99" and "-lang=c99" are both generally valid option forms
option = flag.removeprefix(prefix).removeprefix("=").lstrip()
replacements = options.get(option)
if replacements is not None:
cflags.extend(replacements)
return True
return False
for flag in flags:
# Ignore flags first
if should_ignore(flag):
continue
# Then find replacements
if try_replace(flag):
continue
# Pass flags through last
if should_passthrough(flag):
cflags.append(flag)
continue
append_cflags(obj.options["cflags"])
if isinstance(obj.options["extra_cflags"], list):
append_cflags(obj.options["extra_cflags"])
unit_config = {
"directory": Path.cwd(),
"file": obj.src_path,
"output": obj.src_obj_path,
"arguments": [
"clang",
"-nostdinc",
"-fno-builtin",
"--target=powerpc-eabi",
*cflags,
"-c",
obj.src_path,
"-o",
obj.src_obj_path,
],
}
clangd_config.append(unit_config)
# Add DOL units
for unit in build_config["units"]:
add_unit(unit)
# Add REL units
for module in build_config["modules"]:
for unit in module["units"]:
add_unit(unit)
# Write compile_commands.json
with open("compile_commands.json", "w", encoding="utf-8") as w:
def default_format(o):
if isinstance(o, Path):
return o.resolve().as_posix()
return str(o)
json.dump(clangd_config, w, indent=2, default=default_format)
# Calculate, print and write progress to progress.json # Calculate, print and write progress to progress.json
def calculate_progress(config: ProjectConfig) -> None: def calculate_progress(config: ProjectConfig) -> None:
config.validate() config.validate()