From 95d7489aa3604403f3ca8a5cb8057295e53fad4a Mon Sep 17 00:00:00 2001 From: kotofyt Date: Thu, 1 Jan 2026 16:13:08 +0200 Subject: [PATCH] android signing --- fpc/library/android/apktool.cpp | 70 ++++++++++++++- fpc/library/clang/ld.cpp | 100 +++++++++++++++++++-- fpc/public/apktool.h | 2 + fpc/public/ld.h | 3 - fpc/public/legal.h | 16 ++++ fpc/tests/android_build/build.cpp | 3 +- fpc/tests/android_build/main.c | 1 + fpc/tests/android_build/my-release-key.jks | Bin 2742 -> 2550 bytes 8 files changed, 180 insertions(+), 15 deletions(-) create mode 100644 fpc/public/legal.h diff --git a/fpc/library/android/apktool.cpp b/fpc/library/android/apktool.cpp index 7b413d6..c961e88 100644 --- a/fpc/library/android/apktool.cpp +++ b/fpc/library/android/apktool.cpp @@ -53,7 +53,7 @@ CUtlString AndroidManifest_t::BuildManifest() CPUProject_t project = {}; project.m_szName = m_szPackageName; unsigned int hash = project.GenerateProjectHash(); - CUtlString szOutputDir = CUtlString("%s/%s/android/%u_%s/",FPC_TEMPORAL_DIRNAME, project.m_target.GetTriplet().GetString(), hash, m_szPackageID.GetString()); + CUtlString szOutputDir = CUtlString("%s/android/%u_%s/",FPC_TEMPORAL_DIRNAME, hash, m_szPackageID.GetString()); filesystem2->MakeDirectory(szOutputDir); filesystem2->MakeDirectory(CUtlString("%s/res", szOutputDir.GetString())); @@ -84,6 +84,7 @@ class CAPKTool : public IAPKTool { public: virtual CUtlString BuildPackage( AndroidManifest_t manifest, CUtlString szManifestDir ) override; + virtual CUtlString SignPackage( const char *szApk, LegalInfo_t *pLegalInfo, const char *szAlias, const char *szStorePassword, const char *szKeyPassword ) override; private: }; @@ -131,9 +132,74 @@ CUtlString CAPKTool::BuildPackage( AndroidManifest_t manifest, CUtlString szMani CUtlString("%s.apk", manifest.m_szPackageID.GetString()), }; runner->Run(CUtlString("%s/zipalign",szBuildTools),szManifestDir,args); - return manifest.m_szPackageID; + runner->Wait(); + + return CUtlString("%s/%s.apk",szManifestDir.GetString(), manifest.m_szPackageID.GetString()); } +CUtlString CAPKTool::SignPackage( const char *szApk, LegalInfo_t *pLegalInfo, const char *szAlias, const char *szStorePassword, const char *szKeyPassword ) +{ + V_printf(" APKSIGN %s\n", szApk ); + CUtlString szOutput = CUtlString("%s.signed", szApk); + LegalInfo_t stLegalInfo = {}; + CUtlVector args = {}; + + CPUProject_t project = {}; + project.m_szName = szAlias; + unsigned int hash = project.GenerateProjectHash(); + CUtlString szOutputDir = CUtlString("%s/android/%u_%s/",FPC_TEMPORAL_DIRNAME, hash, szAlias); + filesystem2->MakeDirectory(szOutputDir); + CUtlString szKeystoreDir = CUtlString("%s/%s.jks",szOutputDir.GetString(),szAlias); + CUtlString szDname = ""; + + IINISection *pSection = NULL; + const char *szBuildTools; + pSection = g_pConfig->GetSection("Android_Build_Tools"); + if (!pSection) + Plat_FatalErrorFunc("build_tools are required for this target"); + szBuildTools = pSection->GetStringValue("path"); + + // We need to have a name at least + stLegalInfo.FirstName = "Gordon"; + stLegalInfo.LastName = "Freeman"; + + if (pLegalInfo != NULL) + stLegalInfo = *pLegalInfo; + + if (!stLegalInfo.FirstName) + Plat_FatalErrorFunc("First name wasn't provided\n"); + if (!stLegalInfo.LastName) + Plat_FatalErrorFunc("Last name wasn't provided\n"); + szDname.AppendTail(CUtlString("CN=%s %s", stLegalInfo.FirstName, stLegalInfo.LastName)); + + args = { + "-genkeypair", + "-keystore", szKeystoreDir, + "-alias", szAlias, + "-keyalg", "RSA", + "-keysize", "2048", + "-validity", "10000", + "-storepass", szStorePassword, + "-keypass", szKeyPassword, + "-dname", szDname, + }; + runner->Run("keytool", args); + runner->Wait(); + + args = { + "sign", + "--ks", szKeystoreDir, + "--ks-key-alias", szAlias, + "--ks-pass", CUtlString("pass:%s",szStorePassword), + "--key-pass", CUtlString("pass:%s",szKeyPassword), + szApk, + }; + runner->Run(CUtlString("%s/apksigner",szBuildTools), args); + runner->Wait(); + + return szApk; +}; + IAPKTool *APKTool() { static CAPKTool s_apktool = {}; diff --git a/fpc/library/clang/ld.cpp b/fpc/library/clang/ld.cpp index 0d1c789..0d8828b 100644 --- a/fpc/library/clang/ld.cpp +++ b/fpc/library/clang/ld.cpp @@ -12,21 +12,15 @@ public: virtual CUtlString Link( LinkProject_t *pProject ) override; virtual bool IsLibraryExists( CUtlString szName ) override; protected: - // Returns file name of the - CUtlString GetOutputObjectName( LinkProject_t *pProject, unsigned int hash, CUtlString szFileName ); - - virtual CUtlVector BuildLinkCommandLine( LinkProject_t *pProject, const char *szFileName, const char *szOutputFileName ); - virtual CUtlVector BuildArchiveCommandLine( LinkProject_t *pProject, const char *szFileName, const char *szOutputFileName ); + //virtual CUtlVector BuildLinkCommandLine( LinkProject_t *pProject, const char *szFileName, const char *szOutputFileName ); + //virtual CUtlVector BuildArchiveCommandLine( LinkProject_t *pProject, const char *szFileName, const char *szOutputFileName ); // Returns executable which should the OS run virtual const char *GetCompilerExecutable( LinkProject_t *pProject ) override; - - // returns object file format, eg .obj or .o - virtual const char *GetOutputObjectFormat() override; virtual void SetTarget( CUtlVector &cmd, LinkProject_t *pProject ) override; virtual void SetSysroot( CUtlVector &cmd, LinkProject_t *pProject , const char *szSysroot ) override; - virtual void SetOutputFile( CUtlVector &cmd, const char *szName ) override; + virtual void SetOutputFile( CUtlVector &cmd) override; // sets rpath @@ -50,6 +44,94 @@ protected: virtual void LinkLibraryPath( CUtlVector &cmd, const char *szName ) override; }; + + +const char *CClangLinker::GetCompilerExecutable( LinkProject_t *pProject ) +{ + IINISection *pSection = NULL; + const char *szLinker = "clang++"; + if (!g_pConfig) + return szLinker; + + + pSection = g_pConfig->GetSection(pProject->m_target.GetTriplet()); + if (!pSection) + return szLinker; + + + szLinker = pSection->GetStringValue("CLANG_LINKER_INTERFACE_NAME"); + if (szLinker == NULL) + return "clang++"; + return szLinker; +} + + + +void CClangLinker::SetTarget( CUtlVector &cmd, LinkProject_t *pProject ) +{ + +} + +void CClangLinker::SetSysroot( CUtlVector &cmd, LinkProject_t *pProject , const char *szSysroot ) +{ + +} + +void CClangLinker::SetOutputFile( CUtlVector &cmd) +{ + +} + +void CClangLinker::SetDefaultLibraryPaths( CUtlVector &cmd ) +{ + +} + + +void CClangLinker::UseStdLib( CUtlVector &cmd, bool bUse ) +{ + +} + + +void CClangLinker::UseDynamicLookup( CUtlVector &cmd, bool bUse ) +{ + +} + + +void CClangLinker::UseFullFile( CUtlVector &cmd ) +{ + +} + + +void CClangLinker::UsePartialFile( CUtlVector &cmd ) +{ + +} + + +void CClangLinker::LinkFile( CUtlVector &cmd, const char *szName ) +{ + +} + +void CClangLinker::LinkLibraryObject( CUtlVector &cmd, const char *szName ) +{ + +} + +void CClangLinker::LinkLibrary( CUtlVector &cmd, const char *szName ) +{ + +} + +void CClangLinker::LinkLibraryPath( CUtlVector &cmd, const char *szName ) +{ + +} + EXPOSE_INTERFACE(CClangLinker, ILinker, CLANG_LINKER_INTERFACE_NAME); CUtlString CClangLinker::Link( LinkProject_t *pProject ) diff --git a/fpc/public/apktool.h b/fpc/public/apktool.h index 12a1f29..8a64624 100644 --- a/fpc/public/apktool.h +++ b/fpc/public/apktool.h @@ -7,6 +7,7 @@ #include "tier0/platform.h" #include "tier1/utlstring.h" +#include "legal.h" struct AndroidManifest_t { @@ -36,6 +37,7 @@ abstract_class IAPKTool { public: virtual CUtlString BuildPackage( AndroidManifest_t manifest, CUtlString szManifestDir ) = 0; + virtual CUtlString SignPackage( const char *szApk, LegalInfo_t *pLegalInfo, const char *szAlias, const char *szStorePassword, const char *szKeyPassword ) = 0; }; IAPKTool *APKTool(); diff --git a/fpc/public/ld.h b/fpc/public/ld.h index 710cfc1..b276e87 100644 --- a/fpc/public/ld.h +++ b/fpc/public/ld.h @@ -105,9 +105,6 @@ protected: // Returns executable which should the OS run virtual const char *GetCompilerExecutable( LinkProject_t *pProject ) = 0; - - // returns object file format, eg .obj or .o - virtual const char *GetOutputObjectFormat() = 0; virtual void SetTarget( CUtlVector &cmd, LinkProject_t *pProject ) = 0; virtual void SetSysroot( CUtlVector &cmd, LinkProject_t *pProject , const char *szSysroot ) = 0; diff --git a/fpc/public/legal.h b/fpc/public/legal.h new file mode 100644 index 0000000..9097751 --- /dev/null +++ b/fpc/public/legal.h @@ -0,0 +1,16 @@ +#ifndef LEGAL_H +#define LEGAL_H + +struct LegalInfo_t +{ + const char *FirstName; + const char *LastName; + const char *OrganizationalUnitName; + const char *OrganizationName; + const char *City; + const char *State; + const char *CountryCode; +}; + + +#endif diff --git a/fpc/tests/android_build/build.cpp b/fpc/tests/android_build/build.cpp index 42ec776..5318a80 100644 --- a/fpc/tests/android_build/build.cpp +++ b/fpc/tests/android_build/build.cpp @@ -31,7 +31,8 @@ DECLARE_BUILD_STAGE(android_build) filesystem2->MakeDirectory(CUtlString("%s/lib/x86_64",szManifestDir.GetString())); filesystem2->CopyFile(CUtlString("%s/lib/x86_64/libnative-app.so",szManifestDir.GetString()), szOutputDir); - APKTool()->BuildPackage(manifest, szManifestDir); + CUtlString szApk = APKTool()->BuildPackage(manifest, szManifestDir); + APKTool()->SignPackage(szApk, NULL, "mykey", "storepass", "storepass"); return 0; } diff --git a/fpc/tests/android_build/main.c b/fpc/tests/android_build/main.c index 118e3ff..1c77754 100644 --- a/fpc/tests/android_build/main.c +++ b/fpc/tests/android_build/main.c @@ -26,6 +26,7 @@ void android_main(struct android_app* app) { struct android_poll_source* source; while (1) { + LOGI("what"); int ident; while ((ident = ALooper_pollOnce(0, NULL, &events, (void**)&source)) >= 0) { if (source) source->process(app, source); diff --git a/fpc/tests/android_build/my-release-key.jks b/fpc/tests/android_build/my-release-key.jks index 091a293ead3fd4d05e3f4dfe2f6c74d7fd64f88f..227d6f777fdae5d9c924edb1345bfa2c8b94e856 100644 GIT binary patch delta 2358 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 delta 2551 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 N1`;H0JvjmbClC-RwRQji