// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "uLang/Common/Containers/SharedPointerArray.h" #include "uLang/Common/Misc/Optional.h" #include "uLang/Common/Text/UTF8String.h" #include "uLang/CompilerPasses/CompilerTypes.h" namespace uLang { class CArchive; class CSourcePackage; class CSourceProject; /** * Abstraction of a source file **/ class ISourceSnippet : public CSharedMix { public: virtual ~ISourceSnippet() {} /** * Globally unique path of this snippet, e.g. a fully qualified file path **/ virtual CUTF8String GetPath() const = 0; virtual void SetPath(const CUTF8String& Path) = 0; /** * Get text representation of this snippet * When the result is EResult::Unspecified, no text representation exists * When the result is EResult::Error, text representation exists, but an error occurred retrieving it **/ virtual TOptional GetText() const = 0; /** * Get Vst representation of this snippet * When the result is EResult::Unspecified, no Vst representation exists * When the result is EResult::Error, Vst representation exists, but an error occurred retrieving it **/ virtual TOptional> GetVst() const = 0; virtual void SetVst(TSRef Snippet) = 0; /** * Source version tracking to facilitate some VST reuse across compiles * By default, all cached snippet VSTs are invalid so we won't reuse them. */ virtual bool IsSnippetValid(Verse::Vst::Snippet& Snippet) const { return false; } virtual uint64_t GetSourceVersion() const { return 0; } }; /** * A module **/ class CSourceModule : public CSharedMix { public: /// The source files contained in the module TSRefArray _SourceSnippets; /// Submodules of this module TSRefArray _Submodules; /// Construct from name CSourceModule(const CUTF8StringView& Name) : _Name(Name) {} virtual ~CSourceModule() = default; const CUTF8String& GetName() const { return _Name; } virtual const CUTF8String& GetFilePath() const { return CUTF8String::GetEmpty(); } VERSECOMPILER_API CUTF8StringView GetNameFromFile() const; VERSECOMPILER_API static CUTF8StringView GetNameFromFile(const CUTF8StringView& ModuleFilePath); VERSECOMPILER_API TOptional> FindSubmodule(const CUTF8StringView& ModuleName) const; VERSECOMPILER_API void AddSnippet(const uLang::TSRef& Snippet); VERSECOMPILER_API bool RemoveSnippet(const uLang::TSRef& Snippet, bool bRecursive); /// Visit this module and all its submodules /// Lambda returns true to continue visiting, false to terminate search /// Returns true if all modules have been visited, false if search was terminated early template bool VisitAll(FunctionType&& Lambda) const; template bool VisitAll(FunctionType&& Lambda); // non-const version /// For lookup by name ULANG_FORCEINLINE bool operator==(const CUTF8StringView& Name) const { return _Name == Name; } protected: /// Name of this module CUTF8String _Name; }; /** * A package of modules **/ class CSourcePackage : public CSharedMix { public: /// Settings of a package /// This mirrors FVersePackageSettings in the runtime struct SSettings { CUTF8String _VersePath; // Verse path of the root module of this package EVerseScope _VerseScope = EVerseScope::PublicUser; // Origin/visibility of Verse code in this package EPackageRole _Role = EPackageRole::Source; // The role this package plays in the project. TOptional _VerseVersion; // The language version the package targets. Note that this value is ignored for digests, which are assumed to target the latest unstable version. TOptional _UploadedAtFNVersion; // Only set for constraint `_Role`. bool _bTreatModulesAsImplicit = false; // If true, module macros in this package's source and digest will be treated as implicit TArray _DependencyPackages; // Names of packages this package is dependent on TOptional _VniDestDir; // Destination directory for VNI generated C++ code (fully qualified) bool _bAllowExperimental = false; // If true, this package can use experimental definitions (but cannot publish in UEFN). bool _bEnableSceneGraph = false; // If true, Scene Graph is enabled. This impacts the asset digest that is generated. bool _bDefsInClassesInAssetManifests = true; // See FVersePackageSettings::_bDefsInClassesInAssetManifests uint32_t GetUploadedAtFNVersion(uint32_t Default) const { return _UploadedAtFNVersion.Get(Default); } void GenerateFingerprint(TSRef& Generator) const { Generator->Update(_VersePath.AsCString(), _VersePath.ByteLen(), "SSettings::_VersePath"); Generator->Update(&_VerseScope, sizeof(_VerseScope), "SSettings::_VerseScope"); // Do not hash the Role as this changes before/after we compile when we mark packages as External //Generator->Update(&_Role, sizeof(_Role), "SSettings::_Role"); if (_VerseVersion.IsSet()) { uint32_t VerseVersion = *_VerseVersion; Generator->Update(&VerseVersion, sizeof(VerseVersion), "SSettings::_VerseVersion"); } if (_UploadedAtFNVersion.IsSet()) { uint32_t UploadVersion = *_UploadedAtFNVersion; Generator->Update(&UploadVersion, sizeof(UploadVersion), "SSettings::_UploadedAtFNVersion"); } Generator->Update(&_bTreatModulesAsImplicit, sizeof(_bTreatModulesAsImplicit), "SSettings::_bTreatModulesAsImplicit"); TArray Dependencies; for (const uLang::CUTF8String& Dependency : _DependencyPackages) { Dependencies.Add(&Dependency); } Algo::Sort(Dependencies, [](const uLang::CUTF8String* LHS, const uLang::CUTF8String* RHS) -> bool { return strcmp(LHS->AsCString(), RHS->AsCString()) < 0; }); for (const uLang::CUTF8String* Dependency : Dependencies) { Generator->Update(Dependency->AsCString(), Dependency->ByteLen(), "SSettings::_DependencyPackages[]"); } if (_VniDestDir.IsSet()) { const CUTF8String& VniDestDir = *_VniDestDir; Generator->Update(VniDestDir.AsCString(), VniDestDir.ByteLen(), "SSettings::_VniDestDir"); } Generator->Update(&_bAllowExperimental, sizeof(_bAllowExperimental), "SSettings::_bAllowExperimental"); Generator->Update(&_bEnableSceneGraph, sizeof(_bEnableSceneGraph), "SSettings::_bEnableSceneGraph"); Generator->Update(&_bDefsInClassesInAssetManifests, sizeof(_bDefsInClassesInAssetManifests), "SSettings::_bDefsInClassesInAssetManifests"); } }; struct SVersionedDigest { TSRef _Snippet; uint32_t _EffectiveVerseVersion; TArray _DependencyPackages; void GenerateFingerprint(TSRef& Generator) const { if (_Snippet.IsValid()) { uLang::TOptional Text = _Snippet->GetText(); if (Text.IsSet()) { Generator->Update(Text->AsCString(), Text->ByteLen(), uLang::CUTF8String("SVersionedDigest::_Snippet - %s", _Snippet->GetPath().AsCString()).AsCString()); } } Generator->Update(&_EffectiveVerseVersion, sizeof(_EffectiveVerseVersion), "SVersionedDigest::_EffectiveVerseVersion"); TArray Dependencies; for (const uLang::CUTF8String& Dependency : _DependencyPackages) { Dependencies.Add(&Dependency); } Algo::Sort(Dependencies, [](const uLang::CUTF8String* LHS, const uLang::CUTF8String* RHS) -> bool { return strcmp(LHS->AsCString(), RHS->AsCString()) < 0; }); for (const uLang::CUTF8String* Dependency : Dependencies) { Generator->Update(Dependency->AsCString(), Dependency->ByteLen(), "SVersionedDigest::_DependencyPackages[]"); } } }; /// Where the source code of this package originates enum EOrigin : uint8_t { Unknown, Memory, FileSystem }; /// The root module of this package, equivalent to the _VersePath specified in _Settings TSRef _RootModule; /// Optional digest to be used instead of source if desired TOptional _Digest; /// The public-only digest, if it exists. TOptional _PublicDigest; /// Construct from name CSourcePackage(const CUTF8StringView& Name, const TSRef& RootModule) : _RootModule(RootModule), _Name(Name) {} virtual ~CSourcePackage() = default; const CUTF8String& GetName() const { return _Name; } const SSettings& GetSettings() const { return _Settings; } virtual EOrigin GetOrigin() const { return EOrigin::Unknown; } virtual const CUTF8String& GetDirPath() const { return CUTF8String::GetEmpty(); } virtual const CUTF8String& GetFilePath() const { return CUTF8String::GetEmpty(); } VERSECOMPILER_API int32_t GetNumSnippets() const; const FSolFingerprint& GetFingerprint() const { return _Fingerprint; } VERSECOMPILER_API void GenerateFingerprint(TSRef& Generator) const; void SetName(const CUTF8StringView& NewName) { _Name = NewName; } void SetVersePath(const CUTF8StringView& VersePath) { _Settings._VersePath = VersePath; } void SetVerseScope(const EVerseScope VerseScope) { _Settings._VerseScope = VerseScope; } void SetVerseVersion(const TOptional VerseVersion) { _Settings._VerseVersion = VerseVersion; } void SetAllowExperimental(const bool bAllowExperimental) { _Settings._bAllowExperimental = bAllowExperimental; } void SetRole(const EPackageRole Role) { _Settings._Role = Role; } void SetTreatDefinitionsAsImplicit(bool bTreatAsImplicit) { _Settings._bTreatModulesAsImplicit = bTreatAsImplicit; } void SetFingerprint(FSolFingerprint Fingerprint) { _Fingerprint = Fingerprint; } VERSECOMPILER_API void SetDependencyPackages(TArray&& PackageNames); VERSECOMPILER_API void AddDependencyPackage(const CUTF8StringView& PackageName); VERSECOMPILER_API void TruncateVniDestDir(); // Strip parent path from VniDestDir VERSECOMPILER_API bool RemoveSnippet(const uLang::TSRef& Snippet); private: /// Name of this package CUTF8String _Name; /// Fingerprint of this package FSolFingerprint _Fingerprint; protected: /// Settings, e.g. parsed from .vpackage file SSettings _Settings; }; /** * A project, holding packages and other information **/ class CSourceProject : public CSharedMix { public: /// Entry for a package contained in this project struct SPackage { TSPtr _Package; bool _bReadonly = false; bool _bValkyrieSource = false; }; /// The packages contained in this project TArray _Packages; /// Construct from name CSourceProject(const CUTF8StringView& Name) : _Name(Name) {} /// Construct from other project by making a shallow copy VERSECOMPILER_API CSourceProject(const CSourceProject& Other); virtual ~CSourceProject() = default; const CUTF8String& GetName() const { return _Name; } virtual const CUTF8String& GetFilePath() const { return CUTF8String::GetEmpty(); } VERSECOMPILER_API int32_t GetNumSnippets() const; VERSECOMPILER_API const SPackage* FindPackage(const CUTF8StringView& PackageName, const CUTF8StringView& PackageVersePath) const; VERSECOMPILER_API const SPackage& FindOrAddPackage(const CUTF8StringView& PackageName, const CUTF8StringView& PackageVersePath); VERSECOMPILER_API void AddSnippet(const uLang::TSRef& Snippet, const CUTF8StringView& PackageName, const CUTF8StringView& PackageVersePath); VERSECOMPILER_API bool RemoveSnippet(const uLang::TSRef& Snippet); VERSECOMPILER_API void TruncateVniDestDirs(); // Strip parent path from all VniDestDirs void PopulateTransitiveDependencyMap(uLang::TMap>& OutMap); // Only exposed for testing purposes VERSECOMPILER_API void GeneratePackageFingerprints(TSRef& Generator); private: /// Name of this project CUTF8String _Name; }; /* Implementations *****************************************************************************/ template bool CSourceModule::VisitAll(FunctionType&& Lambda) const { if (!Lambda(*this)) { return false; } for (const TSRef& Submodule : _Submodules) { if (!Submodule->VisitAll(Lambda)) { return false; } } return true; } template bool CSourceModule::VisitAll(FunctionType&& Lambda) // non-const version { if (!Lambda(*this)) { return false; } for (const TSRef& Submodule : _Submodules) { if (!Submodule->VisitAll(Lambda)) { return false; } } return true; } }