Files
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

356 lines
14 KiB
C++

// 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<CUTF8String> 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<TSRef<Verse::Vst::Snippet>> GetVst() const = 0;
virtual void SetVst(TSRef<Verse::Vst::Snippet> 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<ISourceSnippet> _SourceSnippets;
/// Submodules of this module
TSRefArray<CSourceModule> _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<TSRef<CSourceModule>> FindSubmodule(const CUTF8StringView& ModuleName) const;
VERSECOMPILER_API void AddSnippet(const uLang::TSRef<ISourceSnippet>& Snippet);
VERSECOMPILER_API bool RemoveSnippet(const uLang::TSRef<ISourceSnippet>& 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<typename FunctionType>
bool VisitAll(FunctionType&& Lambda) const;
template<typename FunctionType>
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<uint32_t> _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<uint32_t> _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<CUTF8String> _DependencyPackages; // Names of packages this package is dependent on
TOptional<CUTF8String> _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<ISolFingerprintGenerator>& 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<const uLang::CUTF8String*> 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<ISourceSnippet> _Snippet;
uint32_t _EffectiveVerseVersion;
TArray<CUTF8String> _DependencyPackages;
void GenerateFingerprint(TSRef<ISolFingerprintGenerator>& Generator) const
{
if (_Snippet.IsValid())
{
uLang::TOptional<uLang::CUTF8String> 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<const uLang::CUTF8String*> 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<CSourceModule> _RootModule;
/// Optional digest to be used instead of source if desired
TOptional<SVersionedDigest> _Digest;
/// The public-only digest, if it exists.
TOptional<SVersionedDigest> _PublicDigest;
/// Construct from name
CSourcePackage(const CUTF8StringView& Name, const TSRef<CSourceModule>& 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<ISolFingerprintGenerator>& 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<uint32_t> 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<CUTF8String>&& PackageNames);
VERSECOMPILER_API void AddDependencyPackage(const CUTF8StringView& PackageName);
VERSECOMPILER_API void TruncateVniDestDir(); // Strip parent path from VniDestDir
VERSECOMPILER_API bool RemoveSnippet(const uLang::TSRef<ISourceSnippet>& 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<CSourcePackage> _Package;
bool _bReadonly = false;
bool _bValkyrieSource = false;
};
/// The packages contained in this project
TArray<SPackage> _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<ISourceSnippet>& Snippet, const CUTF8StringView& PackageName, const CUTF8StringView& PackageVersePath);
VERSECOMPILER_API bool RemoveSnippet(const uLang::TSRef<ISourceSnippet>& Snippet);
VERSECOMPILER_API void TruncateVniDestDirs(); // Strip parent path from all VniDestDirs
void PopulateTransitiveDependencyMap(uLang::TMap<const CSourcePackage*, uLang::TArray<const CSourcePackage*>>& OutMap); // Only exposed for testing purposes
VERSECOMPILER_API void GeneratePackageFingerprints(TSRef<ISolFingerprintGenerator>& Generator);
private:
/// Name of this project
CUTF8String _Name;
};
/* Implementations
*****************************************************************************/
template<typename FunctionType>
bool CSourceModule::VisitAll(FunctionType&& Lambda) const
{
if (!Lambda(*this))
{
return false;
}
for (const TSRef<CSourceModule>& Submodule : _Submodules)
{
if (!Submodule->VisitAll(Lambda))
{
return false;
}
}
return true;
}
template<typename FunctionType>
bool CSourceModule::VisitAll(FunctionType&& Lambda) // non-const version
{
if (!Lambda(*this))
{
return false;
}
for (const TSRef<CSourceModule>& Submodule : _Submodules)
{
if (!Submodule->VisitAll(Lambda))
{
return false;
}
}
return true;
}
}