From a22ab19a8c964839c228ead191b18ab550108fb5 Mon Sep 17 00:00:00 2001 From: kotofyt Date: Wed, 29 Oct 2025 01:30:21 +0200 Subject: [PATCH] driver cross-compilation --- fpc/.fpccfg | 5 -- fpc/build.cpp | 11 ++- fpc/library/clang/c.cpp | 1 + fpc/library/clang/ld.cpp | 3 + fpc/library/helper.cpp | 28 ++++++ fpc/library/runner.cpp | 3 + fpc/library/windows/c.cpp | 178 +++++++++++++++++++++++++++++++++++++ fpc/library/windows/ld.cpp | 44 ++++++--- fpc/library/winerunner.cpp | 44 +++++++++ fpc/main.cpp | 2 + fpc/public/helper.h | 2 + fpc/public/runner.h | 1 + fpc/public/winerunner.h | 28 ++++++ tier2/fileformats/ini.cpp | 4 +- tier2/filesystem_libc.cpp | 4 +- 15 files changed, 336 insertions(+), 22 deletions(-) delete mode 100644 fpc/.fpccfg create mode 100644 fpc/library/winerunner.cpp create mode 100644 fpc/public/winerunner.h diff --git a/fpc/.fpccfg b/fpc/.fpccfg deleted file mode 100644 index 46c20a6..0000000 --- a/fpc/.fpccfg +++ /dev/null @@ -1,5 +0,0 @@ -[MSVC_C_COMPILER_INTERFACE_NAME] -exe = /opt/msvc/bin/x64/cl - -[MSVC_LINKER_INTERFACE_NAME] -exe = /opt/msvc/bin/x64/link diff --git a/fpc/build.cpp b/fpc/build.cpp index ed48e3c..9281a41 100644 --- a/fpc/build.cpp +++ b/fpc/build.cpp @@ -22,14 +22,21 @@ CUtlVector g_CompiledFiles = { "main.cpp", "library/runner.cpp", "library/helper.cpp", - "library/c.cpp", - "library/ld.cpp", "library/target.cpp", + + "library/winerunner.cpp", + + "library/c.cpp", + "library/ld.cpp", "library/android/apktool.cpp", "library/clang/c.cpp", "library/clang/ld.cpp", + + "library/windows/c.cpp", + "library/windows/ld.cpp", + }; CUtlVector g_IncludeDirectories = { diff --git a/fpc/library/clang/c.cpp b/fpc/library/clang/c.cpp index 4668901..1240e0d 100644 --- a/fpc/library/clang/c.cpp +++ b/fpc/library/clang/c.cpp @@ -158,6 +158,7 @@ skipcompile: g_clangFiles.AppendTail(cfile); } + runner->Wait(); return proj; } diff --git a/fpc/library/clang/ld.cpp b/fpc/library/clang/ld.cpp index c76ebd0..4ababf3 100644 --- a/fpc/library/clang/ld.cpp +++ b/fpc/library/clang/ld.cpp @@ -44,6 +44,9 @@ CUtlString CClangLinker::Link( LinkProject_t *pProject ) if (pProject->m_target.kernel == TARGET_KERNEL_LINUX) szFileName = CUtlString("lib%s.so", pProject->m_szName.GetString()); break; + case ELINK_KERNEL_DRIVER: + Plat_FatalErrorFunc("TODO: not supported\n"); + break; } CUtlString szTarget = pProject->m_target.GetTriplet(); diff --git a/fpc/library/helper.cpp b/fpc/library/helper.cpp index 81fbe4b..f2fe73f 100644 --- a/fpc/library/helper.cpp +++ b/fpc/library/helper.cpp @@ -48,6 +48,34 @@ public: virtual bool ShouldRecompile( const char *szSource, const char *szOutput ) override; }; +char *GetWindowsPath( const char *szPath ) +{ + char *szNewPath = (char*)V_malloc(V_strlen(szPath)+1); + int i = 0; + V_strcpy(szNewPath, szPath); + while(szNewPath[i]) + { + if (szNewPath[i] == '/') + szNewPath[i] = '\\'; + i++; + } + return szNewPath; +} + +char *GetPOSIXPath( const char *szPath ) +{ + char *szNewPath = (char*)V_malloc(V_strlen(szPath)+1); + int i = 0; + V_strcpy(szNewPath, szPath); + while(szNewPath[i]) + { + if (szNewPath[i] == '\\') + szNewPath[i] = '/'; + i++; + } + return szNewPath; +} + EXPOSE_INTERFACE(CPOSIXFileSystem2, IFileSystem2, FILE_SYSTEM_2_INTERFACE_NAME); IFileSystem2 *filesystem2; diff --git a/fpc/library/runner.cpp b/fpc/library/runner.cpp index b10a89c..54b6fbe 100644 --- a/fpc/library/runner.cpp +++ b/fpc/library/runner.cpp @@ -7,6 +7,8 @@ #include "sys/wait.h" #include "tier1/commandline.h" +#include "winerunner.h" + CUtlVector g_processes; class CPOSIXRunner: public IRunner @@ -20,6 +22,7 @@ public: EXPOSE_INTERFACE(CPOSIXRunner, IRunner, RUNNER_INTERFACE_NAME); IRunner *runner; +IWineRunner *winerunner; int CPOSIXRunner::Run(CUtlString szName, CUtlVector& args) { diff --git a/fpc/library/windows/c.cpp b/fpc/library/windows/c.cpp index e69de29..04d3850 100644 --- a/fpc/library/windows/c.cpp +++ b/fpc/library/windows/c.cpp @@ -0,0 +1,178 @@ +#include "tier0/mem.h" +#include "winerunner.h" +#include "c.h" +#include "helper.h" +#include "obj.h" +#include "target.h" +#include "tier0/lib.h" +#include "tier0/mem.h" +#include "tier0/platform.h" +#include "tier1/commandline.h" +#include "tier1/interface.h" +#include "tier1/utlstring.h" +#include "tier1/utlvector.h" +#include "libgen.h" +#include "ctype.h" + +struct ClangFile_t +{ + CUtlString m_szName; + CUtlVector m_szArguments; +}; + +class CMSVCCompiler : public ICCompiler +{ +public: + virtual LinkProject_t Compile( CProject_t *pProject ) override; + virtual void GenerateLinterData( void ) override; +}; + +EXPOSE_INTERFACE(CMSVCCompiler, ICCompiler, MSVC_C_COMPILER_INTERFACE_NAME); +/* +CUtlVector g_msvcFiles; +*/ +LinkProject_t CMSVCCompiler::Compile( CProject_t *pProject ) +{ + if (pProject->m_szName == 0) + { + Plat_FatalErrorFunc("m_szName must be present\n"); + } + + if (pProject->m_target.kernel != TARGET_KERNEL_WINDOWS_MSVC) + { + Plat_FatalErrorFunc("target must be TARGET_KERNEL_WINDOWS_MSVC\n"); + } + + LinkProject_t proj = {}; + proj.m_szName = pProject->m_szName; + proj.m_target = pProject->m_target; + proj.m_androidmanifest = pProject->m_androidmanifest; + unsigned int hash = pProject->GenerateProjectHash(); + + if (!g_pConfig) + Plat_FatalErrorFunc(".fpccfg was not found\n"); + IINISection *pSection = g_pConfig->GetSection("MSVC_C_COMPILER_INTERFACE_NAME"); + if (!pSection) + Plat_FatalErrorFunc("MSVC_C_COMPILER_INTERFACE_NAME was not found in .fpccfg\n"); + CUtlString szExePath = pSection->GetStringValue("exe"); + if (!pSection) + Plat_FatalErrorFunc("exe was not found in MSVC_C_COMPILER_INTERFACE_NAME\n"); + + + + // Get output directories + for (auto &file: pProject->files) + { + CUtlString szTarget = pProject->m_target.GetTriplet(); + CUtlString szOutputFile = CUtlString("%s/%s/cc/%u_%s/%s/%s.obj",FPC_TEMPORAL_DIRNAME, szTarget.GetString(), hash, pProject->m_szName.GetString(), filesystem2->BuildDirectory(), file.GetString()); + CUtlString szOutputDir = szOutputFile; + szOutputDir = dirname(szOutputDir); + filesystem2->MakeDirectory(szOutputDir); + } + + // Run CC + for (auto &file: pProject->files) + { + V_printf(" CC %s\n", file.GetString()); + + CUtlVector args; + CUtlString szTarget = pProject->m_target.GetTriplet(); + CUtlString szCompiledTarget = szTarget; + bool bAreDependenciesUpdated = false; + if (pProject->m_target.kernel == TARGET_KERNEL_ANDROID) + { + szCompiledTarget = CUtlString("%s%u", szTarget.GetString(), pProject->m_androidmanifest.m_nTargetVersion); + } + CUtlString szOutputFile = CUtlString("%s/%s/cc/%u_%s/%s/%s.obj",FPC_TEMPORAL_DIRNAME, szTarget.GetString(), hash, pProject->m_szName.GetString(), filesystem2->BuildDirectory(), file.GetString()); + + args = { + "/nologo", + }; + /* + if (!strcmp(Plat_GetExtension(file),"cpp")) + args.AppendTail("-std=c++17"); + else if (!strcmp(Plat_GetExtension(file),"mm")) + ; + else + args.AppendTail("-std=c99"); + + if (pProject->bFPIC) + args.AppendTail("-fPIC"); + if (pProject->bFPIE) + args.AppendTail("-fPIE"); + */ + for (auto ¯o: pProject->macros) + { + args.AppendTail(CUtlString("/D%s=%s", (char*)macro.szName, (char*)macro.szValue)); + } + for (auto &include: pProject->includeDirectories) + { + const char *szWindowsPath = GetWindowsPath(include.GetString()); + args.AppendTail(CUtlString("/I%s", szWindowsPath)); + V_free((void*)szWindowsPath); + } + /* + for (auto &include: pProject->includeFiles) + { + args.AppendTail("-include"); + args.AppendTail(include); + } + */ + + + if (!filesystem2->ShouldRecompile(file, szOutputFile) && !bAreDependenciesUpdated) + goto skipcompile; + + args.AppendTail("/c"); + args.AppendTail(CUtlString("/Fo%s", szOutputFile.GetString())); + args.AppendTail(file); + + winerunner->Run(szExePath, args); +skipcompile: + proj.objects.AppendTail((Object_t){szOutputFile}); + + ClangFile_t cfile = {}; + cfile.m_szName = file; + cfile.m_szArguments = args; + if (pProject->m_target.kernel == TARGET_KERNEL_ANDROID) + { + if (!pProject->m_target.szSysroot) + Plat_FatalErrorFunc("sysroot must be specified for android\n"); + cfile.m_szArguments.AppendHead(CUtlString("%s/bin/clang", pProject->m_target.szSysroot)); + } + else + cfile.m_szArguments.AppendHead("clang"); + + /* + g_clangFiles.AppendTail(cfile); + */ + } + winerunner->Wait(); + return proj; +} + +void CMSVCCompiler::GenerateLinterData() +{ + /* + FILE* f = V_fopen("compile_commands.json", "wb"); + V_fprintf(f, "[\n"); + uint32_t i = 0; + for (auto &file: g_clangFiles) + { + V_fprintf(f, "\t{\n"); + V_fprintf(f, "\t\t\"arguments\": [\n"); + for (auto &arg: file.m_szArguments) + V_fprintf(f, "\t\t\t\"%s\",\n",arg.GetString()); + + V_fseek(f, -2, SEEK_CUR); + V_fprintf(f, "\n\t\t],\n"); + V_fprintf(f, "\t\t\"file\": \"%s\",\n", file.m_szName.GetString()); + V_fprintf(f, "\t\t\"directory\": \"%s\"\n", filesystem2->BuildDirectory()); + V_fprintf(f, "\t},\n"); + }; + V_fseek(f, -2, SEEK_CUR); + V_fprintf(f, "\n]\n"); + V_fclose(f); + */ +}; + diff --git a/fpc/library/windows/ld.cpp b/fpc/library/windows/ld.cpp index 785652f..3e26e2c 100644 --- a/fpc/library/windows/ld.cpp +++ b/fpc/library/windows/ld.cpp @@ -6,6 +6,7 @@ #include "tier1/interface.h" #include "tier1/utlstring.h" #include "tier2/fileformats/ini.h" +#include "winerunner.h" class CMSVCLinker : public ILinker { @@ -41,6 +42,9 @@ CUtlString CMSVCLinker::Link( LinkProject_t *pProject ) case ELINK_DYNAMIC_LIBRARY: szFileName = CUtlString("%s.dll", pProject->m_szName.GetString()); break; + case ELINK_KERNEL_DRIVER: + szFileName = CUtlString("%s.sys", pProject->m_szName.GetString()); + break; } CUtlString szTarget = pProject->m_target.GetTriplet(); @@ -50,13 +54,13 @@ CUtlString CMSVCLinker::Link( LinkProject_t *pProject ) filesystem2->MakeDirectory(szOutputDir); if (!g_pConfig) - Plat_FatalErrorFunc(".fpccfg was not found"); + Plat_FatalErrorFunc(".fpccfg was not found\n"); IINISection *pSection = g_pConfig->GetSection("MSVC_LINKER_INTERFACE_NAME"); if (!pSection) - Plat_FatalErrorFunc("MSVC_LINKER_INTERFACE_NAME was not found in .fpccfg"); + Plat_FatalErrorFunc("MSVC_LINKER_INTERFACE_NAME was not found in .fpccfg\n"); CUtlString szExePath = pSection->GetStringValue("exe"); if (!pSection) - Plat_FatalErrorFunc("exe was not found in MSVC_LINKER_INTERFACE_NAME"); + Plat_FatalErrorFunc("exe was not found in MSVC_LINKER_INTERFACE_NAME\n"); if (pProject->linkType == ELINK_STATIC_LIBRARY) { @@ -106,16 +110,26 @@ CUtlString CMSVCLinker::Link( LinkProject_t *pProject ) { szCompiledTarget = CUtlString("%s%u", szTarget.GetString(), pProject->m_androidmanifest.m_nTargetVersion); } + args = { - "-o", - szOutputFile, - "-target", - szCompiledTarget, + "/nologo" }; + + const char *szWindowsPath = GetWindowsPath(szOutputFile); + args.AppendTail(CUtlString("/out:%s", szWindowsPath)); + V_free((void*)szWindowsPath); + if (pProject->linkType == ELINK_KERNEL_DRIVER) { args.AppendTail("/driver"); - args.AppendTail(CUtlString("/entry:\"%s\"", pProject->szEntry)); + args.AppendTail(CUtlString("/entry:%s", pProject->szEntry)); + } + switch (pProject->m_eWindowsSubsystem) + { + case WINDOWS_SUBSYSTEM_NATIVE: + args.AppendTail("/subsystem:native"); + default: + break; } // Disable stdlib @@ -134,9 +148,19 @@ CUtlString CMSVCLinker::Link( LinkProject_t *pProject ) */ } - runner->Run(szExePath, args); - runner->Wait(); + winerunner->Run(szExePath, args); + winerunner->Wait(); } compiled: return szOutputFile; }; + +bool CMSVCLinker::IsLibraryExists( CUtlString szName ) +{ + szName = CUtlString("%s.dll", szName.GetString()); + void *pLib = Plat_LoadLibrary(szName.GetString()); + if (!pLib) + return false; + Plat_UnloadLibrary(pLib); + return true; +} diff --git a/fpc/library/winerunner.cpp b/fpc/library/winerunner.cpp new file mode 100644 index 0000000..808a5c2 --- /dev/null +++ b/fpc/library/winerunner.cpp @@ -0,0 +1,44 @@ +#include "runner.h" +#include "winerunner.h" +#include "tier0/platform.h" +#include "tier1/interface.h" +#include "tier1/utlstring.h" +#include "tier1/utlvector.h" +#include "unistd.h" +#include "sys/wait.h" +#include "tier1/commandline.h" + +class CWineRunner: public IWineRunner +{ +public: + virtual int Run( CUtlString szName, CUtlVector& args ) override; + virtual int Run( CUtlString szName, CUtlString szDirectory, CUtlVector& args ) override; + virtual int Run( CUtlString szName, CUtlString szDirectory, CUtlVector& args, CUtlVector& environment ) override; + virtual int Wait( void ) override; +}; + +EXPOSE_INTERFACE(CWineRunner, IWineRunner, WINE_RUNNER_INTERFACE_NAME); + +int CWineRunner::Run(CUtlString szName, CUtlVector& args) +{ + return runner->Run(szName, args); +} + +int CWineRunner::Run(CUtlString szName, CUtlString szDirectory, CUtlVector& args) +{ + CUtlVector args2 = args; + args2.AppendHead(szName); + return runner->Run("wine", szDirectory, args2); +} + +int CWineRunner::Run(CUtlString szName, CUtlString szDirectory, CUtlVector& args, CUtlVector& environment) +{ + CUtlVector args2 = args; + args2.AppendHead(szName); + return runner->Run("wine", szDirectory, args2, environment); +} + +int CWineRunner::Wait( void ) +{ + return runner->Wait(); +}; diff --git a/fpc/main.cpp b/fpc/main.cpp index ce9763b..dd63e7e 100644 --- a/fpc/main.cpp +++ b/fpc/main.cpp @@ -9,6 +9,7 @@ #include "public/ld.h" #include "public/target.h" #include "runner.h" +#include "winerunner.h" #include "c.h" #include "signal.h" #include "libgen.h" @@ -106,6 +107,7 @@ findbuild: #endif runner = (IRunner*)CreateInterface(RUNNER_INTERFACE_NAME, NULL); + winerunner = (IWineRunner*)CreateInterface(WINE_RUNNER_INTERFACE_NAME, NULL); filesystem2 = (IFileSystem2*)CreateInterface(FILE_SYSTEM_2_INTERFACE_NAME, NULL); ccompiler = (ICCompiler*)CreateInterface(CLANG_C_COMPILER_INTERFACE_NAME, NULL); linker = (ILinker*)CreateInterface(CLANG_LINKER_INTERFACE_NAME, NULL); diff --git a/fpc/public/helper.h b/fpc/public/helper.h index ff46ca3..807ecd8 100644 --- a/fpc/public/helper.h +++ b/fpc/public/helper.h @@ -70,6 +70,8 @@ public: }; extern IFileSystem2 *filesystem2; +char *GetWindowsPath( const char *szPath ); +char *GetPOSIXPath( const char *szPath ); //----------------------------------------------------------------------------- diff --git a/fpc/public/runner.h b/fpc/public/runner.h index b9508e9..ff079ed 100644 --- a/fpc/public/runner.h +++ b/fpc/public/runner.h @@ -21,6 +21,7 @@ public: virtual int Wait( void ) = 0; }; + extern IRunner *runner; #endif diff --git a/fpc/public/winerunner.h b/fpc/public/winerunner.h new file mode 100644 index 0000000..3c54002 --- /dev/null +++ b/fpc/public/winerunner.h @@ -0,0 +1,28 @@ +//================= Copyright kotofyt, All rights reserved ==================// +// Purpose: Run Windows applications using Wine. On native system this will use +// CWindowsRunner. +//===========================================================================// + +#ifndef WINE_RUNNER_H +#define WINE_RUNNER_H + +#include "runner.h" +#include "tier0/platform.h" +#include "tier1/utlvector.h" +#include "tier1/utlstring.h" + +#define WINE_RUNNER_INTERFACE_NAME "WineRunner001" + +abstract_class IWineRunner: public IRunner +{ +public: + virtual int Run( CUtlString szName, CUtlVector& args ) = 0; + virtual int Run( CUtlString szName, CUtlString szDirectory, CUtlVector& args ) = 0; + virtual int Run( CUtlString szName, CUtlString szDirectory, CUtlVector& args, CUtlVector& environment ) = 0; + virtual int Wait( void ) = 0; +}; + + +extern IWineRunner *winerunner; + +#endif diff --git a/tier2/fileformats/ini.cpp b/tier2/fileformats/ini.cpp index f66a111..b7278b1 100644 --- a/tier2/fileformats/ini.cpp +++ b/tier2/fileformats/ini.cpp @@ -98,7 +98,7 @@ IINIFile *CINIManager::ReadFile( const char *psz ) if (!pFile) return NULL; data = CUtlBuffer(pFile->Size()+1); - pFile->Read(data.GetMemory(), pFile->Size()); + uint32_t nSize = pFile->Read(data.GetMemory(), pFile->Size()); pFile->Close(); return ReadString(data.GetMemory()); @@ -259,8 +259,6 @@ IINIFile *CINIManager::ReadString( const char *psz ) pCurrentSectionData->m_eSectionType = SECTIONTYPE_STRING; pCurrentSectionData->m_szKey = tokens[i]; - - i++; if (i>=tokens.GetSize()) { diff --git a/tier2/filesystem_libc.cpp b/tier2/filesystem_libc.cpp index 821d7a8..80877e7 100644 --- a/tier2/filesystem_libc.cpp +++ b/tier2/filesystem_libc.cpp @@ -41,9 +41,9 @@ public: pHandle = new CLIBCFileHandle; pHandle->m_pFileSystem = this; pHandle->m_pFile = pFile; - pHandle->Seek(SEEKMODE_RELATIVE_START, 0); - pHandle->m_nSize = pHandle->Tell(); pHandle->Seek(SEEKMODE_RELATIVE_END, 0); + pHandle->m_nSize = pHandle->Tell(); + pHandle->Seek(SEEKMODE_RELATIVE_START, 0); return pHandle; } virtual size_t Write( IFileHandle *pFile, const void *pData, size_t nDataSize ) override