initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -0,0 +1,8 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.ScriptableBuildPipeline.Editor.Tests")]
[assembly: InternalsVisibleTo("Unity.ScriptableBuildPipeline.Test.Fixtures")]
[assembly: InternalsVisibleTo("PerformanceTests.Editor")]
[assembly: InternalsVisibleTo("SBPDebug.Editor")]
[assembly: InternalsVisibleTo("SBPDebug.Editor.Tests")]
[assembly: InternalsVisibleTo("Unity.Addressables.Editor")]

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f13538c0e661bcc4c979c0f336830337
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: af24b50f4a1bf85459001d38afa5850e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
namespace UnityEditor.Build.CacheServer
{
/// <summary>
/// The CacheServerUploader is responsible for uploading assets to a given Cache Server.
/// </summary>
public static class CacheServerUploader
{
private struct Transaction
{
public struct FileInfo
{
public readonly FileType type;
public readonly string path;
public FileInfo(FileType type, string path)
{
this.type = type;
this.path = path;
}
}
public readonly FileId fileId;
public readonly FileInfo[] files;
private Transaction(FileId fileId, FileInfo[] files)
{
this.fileId = fileId;
this.files = files;
}
public static Transaction CreateForAssetPath(string assetPath)
{
var projectRoot = Directory.GetParent(Application.dataPath).FullName;
var guid = AssetDatabase.AssetPathToGUID(assetPath);
var hash = AssetDatabase.GetAssetDependencyHash(assetPath);
var libPath =
new[] { projectRoot, "Library", "metadata", guid.Substring(0, 2), guid }
.Aggregate(string.Empty, Path.Combine);
if (!File.Exists(libPath))
{
throw new Exception("Cannot find Library representation for GUID " + guid);
}
var files = new List<FileInfo>
{
new FileInfo(FileType.Asset, libPath)
};
var infoLibPath = libPath + ".info";
if (File.Exists(infoLibPath))
{
files.Add(new FileInfo(FileType.Info, infoLibPath));
}
var resLibPath = libPath + ".resource";
if (File.Exists(resLibPath))
{
files.Add(new FileInfo(FileType.Resource, resLibPath));
}
return new Transaction(FileId.From(guid, hash.ToString()), files.ToArray());
}
}
/// <summary>
/// Synchronize project library with the configured Cache Server.
/// </summary>
public static void UploadAllFilesToCacheServer()
{
string host;
int port;
Util.ParseCacheServerIpAddress(Util.ConfigCacheServerAddress, out host, out port);
UploadAllFilesToCacheServer(host, port);
}
/// <summary>
/// Synchronize project library folder with a remote Cache Server.
/// </summary>
/// <param name="host">Host name or IP or remote Cache Server</param>
/// <param name="port">Port number for remote Cache Server</param>
public static void UploadAllFilesToCacheServer(string host, int port)
{
var client = new Client(host, port);
client.Connect();
var assetPaths = AssetDatabase.GetAllAssetPaths();
var len = assetPaths.Length;
for (var i = 0; i < len; i++)
{
var path = assetPaths[i];
if (!File.Exists(path))
continue;
var progress = (float)(i + 1) / (len + 1);
if (EditorUtility.DisplayCancelableProgressBar("Uploading to Cache Server", path, progress)) break;
try
{
var trx = Transaction.CreateForAssetPath(path);
client.BeginTransaction(trx.fileId);
foreach (var file in trx.files)
using (var stream = new FileStream(file.path, FileMode.Open, FileAccess.Read))
client.Upload(file.type, stream);
client.EndTransaction();
}
catch (Exception e)
{
Debug.LogError(e);
break;
}
}
EditorUtility.ClearProgressBar();
client.Close();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e766efa9ccaf441728f810262e5ca359
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,90 @@
using System;
using UnityEngine;
namespace UnityEditor.Build.CacheServer
{
/// <summary>
/// The Cache Server Uploader window. This interface will upload your assets to a the given address of a Cache Server.
/// </summary>
public class CacheServerUploaderWindow : EditorWindow
{
private string m_address;
private void Awake()
{
m_address = Util.ConfigCacheServerAddress;
titleContent = new GUIContent("CS Upload");
}
private bool ValidateAddress()
{
string host;
int port;
Util.ParseCacheServerIpAddress(m_address, out host, out port);
var c = new Client(host, port);
try
{
c.Connect();
c.Close();
}
catch (Exception e)
{
Debug.LogError(e);
return false;
}
return true;
}
private void OnGUI()
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Cache Server Address: ");
m_address = GUILayout.TextField(m_address);
EditorGUILayout.EndHorizontal();
GUILayout.FlexibleSpace();
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Upload") && EditorUtility.DisplayDialog("Upload to Cache Server",
"This will upload all assets in your Library folder to the specified Cache Server.", "Continue", "Cancel"))
{
GetWindow<CacheServerUploaderWindow>().Close();
if (!ValidateAddress())
{
Debug.LogError("Could not connect to Cache Server");
return;
}
string host;
int port;
Util.ParseCacheServerIpAddress(m_address, out host, out port);
CacheServerUploader.UploadAllFilesToCacheServer(host, port);
}
if (GUILayout.Button("Cancel"))
{
GetWindow<CacheServerUploaderWindow>().Close();
}
EditorGUILayout.EndHorizontal();
}
private void OnInspectorUpdate()
{
Repaint();
}
/// <summary>
/// Uploads all assets to the cache server.
/// </summary>
[MenuItem("Assets/Cache Server/Upload All Assets")]
public static void UploadAllFilesToCacheServerMenuItem()
{
var window = GetWindow<CacheServerUploaderWindow>();
window.ShowUtility();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c18f0df0496b5418bbb5a34c4c30d6e8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,541 @@
using System;
using System.Collections;
using System.Text;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using UnityEngine;
using System.Threading;
namespace UnityEditor.Build.CacheServer
{
/// <summary>
/// Options for the type of a particular file.
/// </summary>
public enum FileType
{
/// <summary>
/// Use to indicate that the file is an asset.
/// </summary>
Asset = 'a',
/// <summary>
/// Use to indicate that the file holds information for an asset/resource.
/// </summary>
Info = 'i',
/// <summary>
/// Use to indicate that the file is a resource.
/// </summary>
Resource = 'r'
}
/// <summary>
/// Options for the result returned by a download operation.
/// </summary>
public enum DownloadResult
{
/// <summary>
/// Use to indicate that the operation failed.
/// </summary>
Failure = 0,
/// <summary>
/// Use to indicate that the operation failed because it could not locate the specified file.
/// </summary>
FileNotFound = 1,
/// <summary>
/// Use to indicate that the operation succedeed.
/// </summary>
Success = 2
}
/// <summary>
/// A GUID/Hash pair that uniquely identifies a particular file. For each FileId, the Cache Server can store a separate
/// binary stream for each FileType.
/// </summary>
public struct FileId : IEqualityComparer
{
/// <summary>
/// The guid byte array.
/// </summary>
public readonly byte[] guid;
/// <summary>
/// The hash code byte array.
/// </summary>
public readonly byte[] hash;
/// <summary>
/// A structure used to identify a file by guid and hash code.
/// </summary>
/// <param name="guid">File GUID.</param>
/// <param name="hash">File hash code.</param>
private FileId(byte[] guid, byte[] hash)
{
this.guid = guid;
this.hash = hash;
}
/// <summary>
/// Create a FileId given a string guid and string hash code representation.
/// </summary>
/// <param name="guidStr">GUID string representation.</param>
/// <param name="hashStr">Hash code string representation.</param>
/// <returns></returns>
public static FileId From(string guidStr, string hashStr)
{
if (guidStr.Length != 32)
throw new ArgumentException("Length != 32", "guidStr");
if (hashStr.Length != 32)
throw new ArgumentException("Length != 32", "hashStr");
return new FileId(Util.StringToGuid(guidStr), Util.StringToHash(hashStr));
}
/// <summary>
/// Create a FileId given a byte array guid and byte array hash code.
/// </summary>
/// <param name="guid">GUID byte array.</param>
/// <param name="hash">Hash code byte array.</param>
/// <returns></returns>
public static FileId From(byte[] guid, byte[] hash)
{
if (guid.Length != 16)
throw new ArgumentException("Length != 32", "guid");
if (hash.Length != 16)
throw new ArgumentException("Length != 32", "hash");
return new FileId(guid, hash);
}
/// <summary>
/// Check equality of two objects given their guid and hash code.
/// </summary>
/// <param name="x">lhs object.</param>
/// <param name="y">rhs object.</param>
/// <returns></returns>
public new bool Equals(object x, object y)
{
var hash1 = (byte[])x;
var hash2 = (byte[])y;
if (hash1.Length != hash2.Length)
return false;
for (var i = 0; i < hash1.Length; i++)
if (hash1[i] != hash2[i])
return false;
return true;
}
/// <summary>
/// Get the hash code for a specific object.
/// </summary>
/// <param name="obj">The object you want the hash code for.</param>
/// <returns></returns>
public int GetHashCode(object obj)
{
var hc = 17;
hc = hc * 23 + guid.GetHashCode();
hc = hc * 23 + hash.GetHashCode();
return hc;
}
}
/// <summary>
/// Exception thrown when an upload operation is not properly isolated within a begin/end transaction
/// </summary>
public class TransactionIsolationException : Exception
{
/// <summary>
/// Creates a new exception for when an upload operation is not properly isolated within a begin/end transaction.
/// </summary>
/// <param name="msg">The text containing information to display.</param>
public TransactionIsolationException(string msg) : base(msg) {}
}
/// <summary>
/// EventArgs passed to the DownloadFinished event handler
/// </summary>
public class DownloadFinishedEventArgs : EventArgs
{
/// <summary>
/// EventArgs download result code.
/// </summary>
public DownloadResult Result { get; set; }
/// <summary>
/// The downloaded item.
/// </summary>
public IDownloadItem DownloadItem { get; set; }
/// <summary>
/// The size of the downloaded item.
/// </summary>
public long Size { get; set; }
/// <summary>
/// The length of the download queue.
/// </summary>
public long DownloadQueueLength { get; set; }
}
/// <summary>
/// A client API for uploading and downloading files from a Cache Server
/// </summary>
public class Client
{
private enum StreamReadState
{
Response,
Size,
Id
}
private const int ProtocolVersion = 254;
private const string CmdTrxBegin = "ts";
private const string CmdTrxEnd = "te";
private const string CmdGet = "g";
private const string CmdPut = "p";
private const string CmdQuit = "q";
private const int ResponseLen = 2;
private const int SizeLen = 16;
private const int GuidLen = 16;
private const int HashLen = 16;
private const int IdLen = GuidLen + HashLen;
private const int ReadBufferLen = 64 * 1024;
private readonly Queue<IDownloadItem> m_downloadQueue;
private readonly TcpClient m_tcpClient;
private readonly string m_host;
private readonly int m_port;
internal Stream m_stream;
private Mutex m_mutex;
private readonly byte[] m_streamReadBuffer;
private int m_streamBytesRead;
private int m_streamBytesNeeded;
private StreamReadState m_streamReadState = StreamReadState.Response;
private DownloadFinishedEventArgs m_nextFileCompleteEventArgs;
private Stream m_nextWriteStream;
private bool m_inTrx;
/// <summary>
/// Returns the number of items in the download queue
/// </summary>
public int DownloadQueueLength
{
get { return m_downloadQueue.Count; }
}
/// <summary>
/// Event fired when a queued download request finishes.
/// </summary>
public event EventHandler<DownloadFinishedEventArgs> DownloadFinished;
/// <summary>
/// Remove all listeners from the DownloadFinished event
/// </summary>
public void ResetDownloadFinishedEventHandler()
{
DownloadFinished = null;
}
/// <summary>
/// Create a new Cache Server client
/// </summary>
/// <param name="host">The host name or IP of the Cache Server.</param>
/// <param name="port">The port number of the Cache Server. Default port is 8126.</param>
public Client(string host, int port = 8126)
{
m_streamReadBuffer = new byte[ReadBufferLen];
m_downloadQueue = new Queue<IDownloadItem>();
m_tcpClient = new TcpClient();
m_host = host;
m_port = port;
}
/// <summary>
/// Connects to the Cache Server and sends a protocol version handshake.
/// </summary>
/// <exception cref="Exception"></exception>
public void Connect()
{
var client = m_tcpClient;
client.Connect(m_host, m_port);
m_stream = client.GetStream();
m_stream.ReadTimeout = 10000;
m_stream.WriteTimeout = 10000;
SendVersion();
m_mutex = new Mutex();
}
/// <summary>
/// Connects to the Cache Server and sends a protocol version handshake. A TimeoutException is thrown if the connection cannot
/// be established within <paramref name="timeoutMs"/> milliseconds.
/// </summary>
/// <param name="timeoutMs"></param>
/// <exception cref="TimeoutException"></exception>
/// <exception cref="Exception"></exception>
public void Connect(int timeoutMs)
{
var client = m_tcpClient;
var op = client.BeginConnect(m_host, m_port, null, null);
var connected = op.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(timeoutMs));
if (!connected)
throw new TimeoutException();
m_stream = client.GetStream();
SendVersion();
m_mutex = new Mutex();
}
/// <summary>
/// Begin an upload transaction for an asset. Transactions in process can be interupted by calling BeginTransaction
/// again before calling EndTransaction.
/// </summary>
/// <param name="fileId"></param>
public void BeginTransaction(FileId fileId)
{
m_inTrx = true;
m_stream.Write(Encoding.ASCII.GetBytes(CmdTrxBegin), 0, 2);
m_stream.Write(fileId.guid, 0, GuidLen);
m_stream.Write(fileId.hash, 0, HashLen);
}
/// <summary>
/// Upload from the given stream for the given FileType. Will throw an exception if not preceeded by BeginTransaction.
/// </summary>
/// <param name="type"></param>
/// <param name="readStream"></param>
/// <exception cref="TransactionIsolationException"></exception>
/// <exception cref="ArgumentException"></exception>
public void Upload(FileType type, Stream readStream)
{
if (!m_inTrx)
throw new TransactionIsolationException("Upload without BeginTransaction");
if (!readStream.CanRead || !readStream.CanSeek)
throw new ArgumentException();
m_stream.Write(Encoding.ASCII.GetBytes(CmdPut + (char)type), 0, 2);
m_stream.Write(Util.EncodeInt64(readStream.Length), 0, SizeLen);
var buf = new byte[ReadBufferLen];
while (readStream.Position < readStream.Length - 1)
{
var len = readStream.Read(buf, 0, ReadBufferLen);
m_stream.Write(buf, 0, len);
}
}
/// <summary>
/// Commit the uploaded files to the Cache Server. Will throw an exception if not preceeded by BeginTransaction.
/// </summary>
/// <exception cref="TransactionIsolationException"></exception>
public void EndTransaction()
{
if (!m_inTrx)
throw new TransactionIsolationException("EndTransaction without BeginTransaction");
m_inTrx = false;
m_stream.Write(Encoding.ASCII.GetBytes(CmdTrxEnd), 0, 2);
}
/// <summary>
/// Send a download request to the Cache Server. Listen to the DownloadComplete event to read the results.
/// </summary>
/// <param name="downloadItem">The IDownloadItem that specifies which file to download</param>
public void QueueDownload(IDownloadItem downloadItem)
{
m_stream.Write(Encoding.ASCII.GetBytes(CmdGet + (char)downloadItem.Type), 0, 2);
m_stream.Write(downloadItem.Id.guid, 0, GuidLen);
m_stream.Write(downloadItem.Id.hash, 0, HashLen);
m_mutex.WaitOne();
m_downloadQueue.Enqueue(downloadItem);
int count = m_downloadQueue.Count;
m_mutex.ReleaseMutex();
if (count == 1)
ReadNextDownloadResult();
}
/// <summary>
/// Close the connection to the Cache Server. Sends the 'quit' command and closes the network stream.
/// </summary>
public void Close()
{
if (m_stream != null)
m_stream.Write(Encoding.ASCII.GetBytes(CmdQuit), 0, 1);
if (m_tcpClient != null)
m_tcpClient.Close();
if (m_mutex != null)
{
m_mutex.Dispose();
m_mutex = null;
}
}
private void SendVersion()
{
var encodedVersion = Util.EncodeInt32(ProtocolVersion, true);
m_stream.Write(encodedVersion, 0, encodedVersion.Length);
var versionBuf = new byte[8];
var pos = 0;
while (pos < versionBuf.Length - 1)
{
pos += m_stream.Read(versionBuf, 0, versionBuf.Length);
}
if (Util.ReadUInt32(versionBuf, 0) != ProtocolVersion)
throw new Exception("Server version mismatch");
}
private void OnDownloadFinished(DownloadFinishedEventArgs e)
{
m_mutex.WaitOne();
m_downloadQueue.Dequeue();
int count = m_downloadQueue.Count;
m_mutex.ReleaseMutex();
e.DownloadQueueLength = count;
if (DownloadFinished != null)
DownloadFinished(this, e);
if (count > 0)
ReadNextDownloadResult();
}
internal void ReadNextDownloadResult()
{
m_streamReadState = StreamReadState.Response;
m_streamBytesNeeded = ResponseLen;
m_streamBytesRead = 0;
m_nextFileCompleteEventArgs = new DownloadFinishedEventArgs { Result = DownloadResult.Failure };
BeginReadHeader();
}
private void BeginReadHeader()
{
m_stream.BeginRead(m_streamReadBuffer,
m_streamBytesRead,
m_streamBytesNeeded - m_streamBytesRead,
EndReadHeader,
m_stream);
}
internal Action<int, byte[]> OnReadHeader;
private void EndReadHeader(IAsyncResult r)
{
var bytesRead = m_stream.EndRead(r);
if (bytesRead <= 0) return;
m_streamBytesRead += bytesRead;
if (OnReadHeader != null)
OnReadHeader(m_streamBytesRead, m_streamReadBuffer);
if (m_streamBytesRead < m_streamBytesNeeded)
{
BeginReadHeader();
return;
}
switch (m_streamReadState)
{
case StreamReadState.Response:
if (Convert.ToChar(m_streamReadBuffer[0]) == '+')
{
m_streamReadState = StreamReadState.Size;
m_streamBytesNeeded = SizeLen;
}
else
{
m_nextFileCompleteEventArgs.Result = DownloadResult.FileNotFound;
m_streamReadState = StreamReadState.Id;
m_streamBytesNeeded = IdLen;
}
break;
case StreamReadState.Size:
m_nextFileCompleteEventArgs.Size = Util.ReadUInt64(m_streamReadBuffer, 0);
m_streamReadState = StreamReadState.Id;
m_streamBytesNeeded = IdLen;
break;
case StreamReadState.Id:
m_mutex.WaitOne();
var next = m_downloadQueue.Peek();
m_mutex.ReleaseMutex();
m_nextFileCompleteEventArgs.DownloadItem = next;
var match =
Util.ByteArraysAreEqual(next.Id.guid, 0, m_streamReadBuffer, 0, GuidLen) &&
Util.ByteArraysAreEqual(next.Id.hash, 0, m_streamReadBuffer, GuidLen, HashLen);
if (!match)
{
Close();
throw new InvalidDataException();
}
if (m_nextFileCompleteEventArgs.Result == DownloadResult.FileNotFound)
{
OnDownloadFinished(m_nextFileCompleteEventArgs);
}
else
{
var size = m_nextFileCompleteEventArgs.Size;
m_nextWriteStream = next.GetWriteStream(size);
m_streamBytesNeeded = (int)size;
m_streamBytesRead = 0;
BeginReadData();
}
return;
default:
throw new ArgumentOutOfRangeException();
}
m_streamBytesRead = 0;
BeginReadHeader();
}
private void BeginReadData()
{
var len = Math.Min(ReadBufferLen, m_streamBytesNeeded - m_streamBytesRead);
m_stream.BeginRead(m_streamReadBuffer, 0, len, EndReadData, null);
}
private void EndReadData(IAsyncResult readResult)
{
var bytesRead = m_stream.EndRead(readResult);
Debug.Assert(bytesRead > 0);
m_streamBytesRead += bytesRead;
var writeResult = m_nextWriteStream.BeginWrite(m_streamReadBuffer, 0, bytesRead, null, null);
m_nextWriteStream.EndWrite(writeResult);
if (m_streamBytesRead < m_streamBytesNeeded)
{
BeginReadData();
}
else
{
m_nextFileCompleteEventArgs.DownloadItem.Finish();
m_nextFileCompleteEventArgs.Result = DownloadResult.Success;
OnDownloadFinished(m_nextFileCompleteEventArgs);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 02aa99c7f5d8f4c77810419ebddad96d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,82 @@
using System;
using System.IO;
using UnityEngine;
namespace UnityEditor.Build.CacheServer
{
/// <summary>
/// IDownloadItem implementation for downloading to a file specified by path.
/// </summary>
public class FileDownloadItem : IDownloadItem
{
private Stream m_writeStream;
private string m_tmpPath;
/// <summary>
/// The FileId for the FileDownloadItem. FileId consists of an assets guid and hash code.
/// </summary>
public FileId Id { get; private set; }
/// <summary>
/// The type of the FileDownloadItems desired item.
/// </summary>
public FileType Type { get; private set; }
/// <summary>
/// File path where downloaded file data is saved. Data is first written to a temporary file location, and moved
/// into place when the Finish() method is called by the Cache Server Client.
/// </summary>
public string FilePath { get; private set; }
/// <summary>
/// Gets the write stream for a given FileDownloadItem. If one does not exist, it will be created.
/// </summary>
/// <param name="size"></param>
/// <returns></returns>
public Stream GetWriteStream(long size)
{
if (m_writeStream == null)
{
m_tmpPath = Path.GetTempFileName();
m_writeStream = new FileStream(m_tmpPath, FileMode.Create, FileAccess.Write);
}
return m_writeStream;
}
/// <summary>
/// Create a new FileDownloadItem
/// </summary>
/// <param name="fileId">The FileId of the desired item.</param>
/// <param name="fileType">The FileType of the desired item.</param>
/// <param name="path">The path of the desired item.</param>
public FileDownloadItem(FileId fileId, FileType fileType, string path)
{
Id = fileId;
Type = fileType;
FilePath = path;
}
/// <summary>
/// Dispose the FileDownloadItems write stream and move the data from the temporary path to its final destination.
/// </summary>
public void Finish()
{
if (m_writeStream == null)
return;
m_writeStream.Dispose();
try
{
if (File.Exists(FilePath))
File.Delete(FilePath);
File.Move(m_tmpPath, FilePath);
}
catch (Exception e)
{
Debug.LogError(e);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e253c6be7d9134114a9b533965c2b45f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,33 @@
using System.IO;
namespace UnityEditor.Build.CacheServer
{
/// <summary>
/// Represents a single file download request from a Cache Server.
/// </summary>
public interface IDownloadItem
{
/// <summary>
/// the FileId (guid/hash pair) of the file to download
/// </summary>
FileId Id { get; }
/// <summary>
/// the FileType for the given FileId to download
/// </summary>
FileType Type { get; }
/// <summary>
/// Provides a writable stream for saving downloaded file bytes
/// </summary>
/// <param name="size">Size of file to download</param>
/// <returns>A writable stream</returns>
Stream GetWriteStream(long size);
/// <summary>
/// Method called when a download is finished. Used to finalize and cleanup a single file download. e.g. to move
/// a temporary file into place.
/// </summary>
void Finish();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 31314932afa7e4ce3ab642bfbc2dd8ff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,14 @@
{
"name": "UnityEditor.CacheServer",
"references": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": []
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 27fb11404301449859aa664f4cb020e0
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,163 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Text;
[assembly: InternalsVisibleTo("UnityEditor.CacheServerTests")]
namespace UnityEditor.Build.CacheServer
{
internal static class Util
{
private const string IpAddressKey = "CacheServerIPAddress";
private static int ReverseByte(int b)
{
return ((b & 0x0F) << 4) | ((b >> 4) & 0x0F);
}
private static byte[] StringToByteArray(string input, bool asGuid)
{
var bytes = new byte[input.Length / 2];
for (var i = 0; i < input.Length; i += 2)
{
var b = Convert.ToByte(input.Substring(i, 2), 16);
bytes[i / 2] = asGuid ? (byte)ReverseByte(b) : b;
}
return bytes;
}
/// <summary>
/// Convert a hex string to a byte array that represents an Asset Hash
/// </summary>
/// <param name="hashStr">32 character hex string</param>
/// <returns>byte array</returns>
public static byte[] StringToHash(string hashStr)
{
Debug.Assert(hashStr.Length == 32);
return StringToByteArray(hashStr, false);
}
/// <summary>
/// Convert a hex string to a byte array that represents an Asset GUID
/// </summary>
/// <param name="guidStr">32 character hex string</param>
/// <returns>byte array</returns>
public static byte[] StringToGuid(string guidStr)
{
Debug.Assert(guidStr.Length == 32);
return StringToByteArray(guidStr, true);
}
/// <summary>
/// Parse an ascii byte array at <paramref name="index"/>start as an int value
/// </summary>
/// <param name="bytes">byte array</param>
/// <param name="index">offset</param>
/// <returns></returns>
public static int ReadUInt32(byte[] bytes, int index)
{
Debug.Assert(bytes.Length + index >= 8);
return Int32.Parse(Encoding.ASCII.GetString(bytes, index, 8), NumberStyles.HexNumber);
}
/// <summary>
/// Encode an integer as an ascii byte array
/// </summary>
/// <param name="input">integer</param>
/// <param name="minLength">true ensure the byte array is as short as possible; false to pad to 8 bytes</param>
/// <returns></returns>
public static byte[] EncodeInt32(int input, bool minLength = false)
{
return Encoding.ASCII.GetBytes(input.ToString(minLength ? "X" : "X8"));
}
/// <summary>
/// Parse a subset of an ascii byte array as a long value
/// </summary>
/// <param name="bytes">byte array</param>
/// <param name="index">offset within <paramref name="bytes"/> to read from</param>
/// <returns></returns>
public static long ReadUInt64(byte[] bytes, int index)
{
Debug.Assert(bytes.Length + index >= 16);
return Int64.Parse(Encoding.ASCII.GetString(bytes, index, 16), NumberStyles.HexNumber);
}
/// <summary>
/// Encode a long value into an ascii byte array
/// </summary>
/// <param name="input">long value</param>
/// <returns></returns>
public static byte[] EncodeInt64(long input)
{
return Encoding.ASCII.GetBytes(input.ToString("X16"));
}
/// <summary>
/// Compare two byte arrays for value equality
/// </summary>
/// <param name="ar1">first array</param>
/// <param name="ar2">second array</param>
/// <returns></returns>
public static bool ByteArraysAreEqual(byte[] ar1, byte[] ar2)
{
return ar1.Length == ar2.Length && ByteArraysAreEqual(ar1, 0, ar2, 0, ar1.Length);
}
/// <summary>
/// Compare two byte arrays for value equality at specific offsets and length
/// </summary>
/// <param name="ar1">first array</param>
/// <param name="start1">offset within first array</param>
/// <param name="ar2">second array</param>
/// <param name="start2">offset within second array</param>
/// <param name="count">number of bytes to compare</param>
/// <returns></returns>
public static bool ByteArraysAreEqual(byte[] ar1, int start1, byte[] ar2, int start2, int count)
{
Debug.Assert(start1 >= 0 && start2 >= 0 && count >= 0);
if (start1 + count > ar1.Length)
return false;
if (start2 + count > ar2.Length)
return false;
for (var i = 0; i < count; i++)
if (ar1[start1 + i] != ar2[start2 + i])
return false;
return true;
}
/// <summary>
/// Retrieve the configured cache server address for the Unity Editor
/// </summary>
public static string ConfigCacheServerAddress
{
get { return EditorPrefs.GetString(IpAddressKey); }
}
/// <summary>
/// Parse an address string in the format of 'address:port' to a string address and integer port number
/// </summary>
/// <param name="address">combined address string</param>
/// <param name="host">address part</param>
/// <param name="port">port part</param>
public static void ParseCacheServerIpAddress(string address, out string host, out int port)
{
host = null;
port = 8126;
var parts = address.Split(':');
if (parts.Length > 0)
host = parts[0];
if (parts.Length > 1)
port = int.Parse(parts[1]);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a0c7d049419ae46f19ead59b45d622fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,101 @@
using System.IO;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEngine;
using UnityEngine.Build.Pipeline;
namespace UnityEditor.Build.Pipeline
{
#if UNITY_2018_3_OR_NEWER
using BuildCompression = UnityEngine.BuildCompression;
#else
using BuildCompression = UnityEditor.Build.Content.BuildCompression;
#endif
/// <summary>
/// Static class exposing convenient methods that match the BuildPipeline <seealso cref="BuildPipeline.BuildAssetBundles"/> method, suitable
/// for porting existing projects to the Scriptable Build Pipeline quickly.
/// New projects could consider calling <see cref="ContentPipeline.BuildAssetBundles"/> directly.
/// </summary>
public static class CompatibilityBuildPipeline
{
/// <summary>
/// Wrapper API to match BuildPipeline API but use the Scriptable Build Pipeline to build Asset Bundles.
/// <seealso cref="BuildPipeline.BuildAssetBundles(string, BuildAssetBundleOptions, BuildTarget)"/>
/// </summary>
/// <remarks>
/// Not all BuildAssetBundleOptions are supported in the Scriptable Build Pipeline.
/// Supported options are: ForceRebuildAssetBundle, AppendHashToAssetBundleName, ChunkBasedCompression, UncompressedAssetBundle, and DisableWriteTypeTree.
/// In addition, existing BuildPipeline callbacks are not yet supported.
/// </remarks>
/// <param name="outputPath">Output path for the AssetBundles.</param>
/// <param name="assetBundleOptions">AssetBundle building options.</param>
/// <param name="targetPlatform">Chosen target build platform.</param>
/// <returns>CompatibilityAssetBundleManifest object exposing information about the generated asset bundles.</returns>
public static CompatibilityAssetBundleManifest BuildAssetBundles(string outputPath, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform)
{
// Retrieve the AssetBundle definitions as defined through the Inspector and saved in the AssetDatabase
var buildInput = ContentBuildInterface.GenerateAssetBundleBuilds();
return BuildAssetBundles_Internal(outputPath, new BundleBuildContent(buildInput), assetBundleOptions, targetPlatform);
}
/// <summary>
/// Wrapper API to match BuildPipeline API but use the Scriptable Build Pipeline to build Asset Bundles.
/// <seealso cref="BuildPipeline.BuildAssetBundles(string, AssetBundleBuild[], BuildAssetBundleOptions, BuildTarget)"/>
/// </summary>
/// <remarks>
/// Not all BuildAssetBundleOptions are supported in the Scriptable Build Pipeline.
/// Supported options are: ForceRebuildAssetBundle, AppendHashToAssetBundleName, ChunkBasedCompression, UncompressedAssetBundle, and DisableWriteTypeTree.
/// In addition, existing BuildPipeline callbacks are not yet supported.
/// </remarks>
/// <param name="outputPath">Output path for the AssetBundles.</param>
/// <param name="builds">AssetBundle building map.</param>
/// <param name="assetBundleOptions">AssetBundle building options.</param>
/// <param name="targetPlatform">Chosen target build platform.</param>
/// <returns>CompatibilityAssetBundleManifest object exposing information about the generated asset bundles.</returns>
public static CompatibilityAssetBundleManifest BuildAssetBundles(string outputPath, AssetBundleBuild[] builds, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform)
{
return BuildAssetBundles_Internal(outputPath, new BundleBuildContent(builds), assetBundleOptions, targetPlatform);
}
internal static CompatibilityAssetBundleManifest BuildAssetBundles_Internal(string outputPath, IBundleBuildContent content, BuildAssetBundleOptions options, BuildTarget targetPlatform)
{
var group = BuildPipeline.GetBuildTargetGroup(targetPlatform);
var parameters = new BundleBuildParameters(targetPlatform, group, outputPath);
if ((options & BuildAssetBundleOptions.ForceRebuildAssetBundle) != 0)
parameters.UseCache = false;
if ((options & BuildAssetBundleOptions.AppendHashToAssetBundleName) != 0)
parameters.AppendHash = true;
#if UNITY_2018_3_OR_NEWER
if ((options & BuildAssetBundleOptions.ChunkBasedCompression) != 0)
parameters.BundleCompression = BuildCompression.LZ4;
else if ((options & BuildAssetBundleOptions.UncompressedAssetBundle) != 0)
parameters.BundleCompression = BuildCompression.Uncompressed;
else
parameters.BundleCompression = BuildCompression.LZMA;
#else
if ((options & BuildAssetBundleOptions.ChunkBasedCompression) != 0)
parameters.BundleCompression = BuildCompression.DefaultLZ4;
else if ((options & BuildAssetBundleOptions.UncompressedAssetBundle) != 0)
parameters.BundleCompression = BuildCompression.DefaultUncompressed;
else
parameters.BundleCompression = BuildCompression.DefaultLZMA;
#endif
if ((options & BuildAssetBundleOptions.DisableWriteTypeTree) != 0)
parameters.ContentBuildFlags |= ContentBuildFlags.DisableWriteTypeTree;
IBundleBuildResults results;
ReturnCode exitCode = ContentPipeline.BuildAssetBundles(parameters, content, out results);
if (exitCode < ReturnCode.Success)
return null;
var manifest = ScriptableObject.CreateInstance<CompatibilityAssetBundleManifest>();
manifest.SetResults(results.BundleInfos);
File.WriteAllText(parameters.GetOutputFilePathForIdentifier(Path.GetFileName(outputPath) + ".manifest"), manifest.ToString());
return manifest;
}
}
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: e4a7ddc0ee0cb5e47b5c53fb53a97808
timeCreated: 1504709534
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,189 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEditor.Build.Utilities;
using UnityEditor.Modules;
namespace UnityEditor.Build.Pipeline
{
/// <summary>
/// Static class containing the main content building entry points into the Scriptable Build Pipeline.
/// </summary>
public static class ContentPipeline
{
/// <summary>
/// Default temporary path used for building content data.
/// </summary>
public const string kTempBuildPath = "Temp/ContentBuildData";
/// <summary>
/// Default temporary path used for building script data.
/// </summary>
public const string kScriptBuildPath = "Library/PlayerScriptAssemblies";
/// <summary>
/// Default callback implementation.
/// </summary>
public static BuildCallbacks BuildCallbacks = new BuildCallbacks();
/// <summary>
/// Default implementation of generating Asset Bundles using the Scriptable Build Pipeline.
/// </summary>
/// <param name="parameters">Set of parameters used for building asset bundles.</param>
/// <param name="content">Set of content and explicit asset bundle layout to build.</param>
/// <param name="result">Results from building the content and explicit asset bundle layout.</param>
/// <returns>Return code with status information about success or failure causes.</returns>
public static ReturnCode BuildAssetBundles(IBundleBuildParameters parameters, IBundleBuildContent content, out IBundleBuildResults result)
{
var taskList = DefaultBuildTasks.Create(DefaultBuildTasks.Preset.AssetBundleCompatible);
return BuildAssetBundles(parameters, content, out result, taskList);
}
/// <summary>
/// Default implementation of generating Asset Bundles using the Scriptable Build Pipeline.
/// </summary>
/// <param name="parameters">Set of parameters used for building asset bundles.</param>
/// <param name="content">Set of content and explicit asset bundle layout to build.</param>
/// <param name="result">Results from building the content and explicit asset bundle layout.</param>
/// <param name="taskList">Custom task list for building asset bundles.</param>
/// <param name="contextObjects">Additional context objects to make available to the build.</param>
/// <returns>Return code with status information about success or failure causes.</returns>
public static ReturnCode BuildAssetBundles(IBundleBuildParameters parameters, IBundleBuildContent content, out IBundleBuildResults result, IList<IBuildTask> taskList, params IContextObject[] contextObjects)
{
if (BuildPipeline.isBuildingPlayer)
{
result = null;
BuildLogger.LogException(new InvalidOperationException("Cannot build asset bundles while a build is in progress"));
return ReturnCode.Exception;
}
// Avoid throwing exceptions in here as we don't want them bubbling up to calling user code
if (parameters == null)
{
result = null;
BuildLogger.LogException(new ArgumentNullException("parameters"));
return ReturnCode.Exception;
}
// Avoid throwing exceptions in here as we don't want them bubbling up to calling user code
if (taskList.IsNullOrEmpty())
{
result = null;
BuildLogger.LogException(new ArgumentException("Argument cannot be null or empty.", "taskList"));
return ReturnCode.Exception;
}
var contentBuildSettings = parameters.GetContentBuildSettings();
if (!CanBuildPlayer(contentBuildSettings.target, contentBuildSettings.group))
{
result = null;
BuildLogger.LogException(new InvalidOperationException("Unable to build with the current configuration, please check the Build Settings."));
return ReturnCode.Exception;
}
// Don't run if there are unsaved changes
if (ValidationMethods.HasDirtyScenes())
{
result = null;
return ReturnCode.UnsavedChanges;
}
ThreadingManager.WaitForOutstandingTasks();
BuildContext buildContext = new BuildContext(contextObjects);
BuildLog buildLog = null;
IBuildLogger logger;
if (!buildContext.TryGetContextObject<IBuildLogger>(out logger))
{
logger = buildLog = new BuildLog();
buildContext.SetContextObject(buildLog);
}
using (logger.ScopedStep(LogLevel.Info, "AssetDatabase.SaveAssets"))
AssetDatabase.SaveAssets();
ReturnCode exitCode;
result = new BundleBuildResults();
#if !CI_TESTRUNNER_PROJECT
using (new SceneStateCleanup())
using (var progressTracker = new ProgressTracker())
#else
using (var progressTracker = new ProgressLoggingTracker())
#endif
{
using (new AutoBuildCacheUtility())
using (var interfacesWrapper = new BuildInterfacesWrapper())
using (var buildCache = new BuildCache(parameters.CacheServerHost, parameters.CacheServerPort))
{
BuildCacheUtility.SetCurrentBuildContent(content);
Directory.CreateDirectory(parameters.TempOutputFolder);
Directory.CreateDirectory(parameters.ScriptOutputFolder);
try
{
buildContext.SetContextObject(parameters);
buildContext.SetContextObject(content);
buildContext.SetContextObject(result);
buildContext.SetContextObject(interfacesWrapper);
buildContext.SetContextObject(progressTracker);
buildContext.SetContextObject(buildCache);
// If IDeterministicIdentifiers was passed in with contextObjects, don't add the default
if (!buildContext.ContainsContextObject(typeof(IDeterministicIdentifiers)))
buildContext.SetContextObject(parameters.ContiguousBundles ? new PrefabPackedIdentifiers() : (IDeterministicIdentifiers)new Unity5PackedIdentifiers());
buildContext.SetContextObject(new BuildDependencyData());
buildContext.SetContextObject(new ObjectDependencyData());
buildContext.SetContextObject(new BundleWriteData());
buildContext.SetContextObject(BuildCallbacks);
buildCache.SetBuildLogger(logger);
}
catch (Exception e)
{
// Avoid throwing exceptions in here as we don't want them bubbling up to calling user code
result = null;
BuildLogger.LogException(e);
return ReturnCode.Exception;
}
exitCode = BuildTasksRunner.Validate(taskList, buildContext);
if (exitCode >= ReturnCode.Success)
#if SBP_PROFILER_ENABLE
exitCode = BuildTasksRunner.RunProfiled(taskList, buildContext);
#else
exitCode = BuildTasksRunner.Run(taskList, buildContext);
#endif
if (Directory.Exists(parameters.TempOutputFolder))
Directory.Delete(parameters.TempOutputFolder, true);
if (buildLog != null)
{
string buildLogPath = parameters.GetOutputFilePathForIdentifier("buildlogtep.json");
Directory.CreateDirectory(Path.GetDirectoryName(buildLogPath));
File.WriteAllText(parameters.GetOutputFilePathForIdentifier("buildlogtep.json"), buildLog.FormatForTraceEventProfiler());
}
}
}
long maximumCacheSize = ScriptableBuildPipeline.maximumCacheSize * BuildCache.k_BytesToGigaBytes;
BuildCache.PruneCache_Background(maximumCacheSize);
return exitCode;
}
//Functionality has been removed due to issues with APV in yamato for package release (https://jira.unity3d.com/browse/BPSBP-735)
private static bool CanBuildPlayer(BuildTarget target, BuildTargetGroup targetGroup)
{
// The Editor APIs we need only exist in 2021.3 and later. For earlier versions, assume we can build.
//#if UNITY_2021_3_OR_NEWER
// var module = ModuleManager.GetTargetStringFrom(targetGroup, target);
// var buildWindowExtension = ModuleManager.GetBuildWindowExtension(module);
// return buildWindowExtension != null ? buildWindowExtension.EnabledBuildButton() : false;
//#else
return true;
//#endif
}
}
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: 3ba8763d94231c9459f5a2d2aae30cfa
timeCreated: 1504709533
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 83be1c14cfbe90c4288847f2b01c1884
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,109 @@
using System;
using System.Reflection;
using UnityEditor.Build.Pipeline.Interfaces;
namespace UnityEditor.Build.Pipeline.Injector
{
/// <summary>
/// Use to pass around information between build tasks.
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public class InjectContextAttribute : Attribute
{
//public string Identifier { get; set; }
/// <summary>
/// Stores the how the attribute is used among build tasks.
/// </summary>
public ContextUsage Usage { get; set; }
/// <summary>
/// Stores whether using the context attribute is optional.
/// </summary>
public bool Optional { get; set; }
/// <summary>
/// Creates a new context attribute that stores information that can be passed between build tasks.
/// </summary>
/// <param name="usage">The usage behavior for the attribute. By default it is set to <see cref="ContextUsage.InOut"/>.</param>
/// <param name="optional">Set to true if using the attribute is optional. Set to false otherwise.</param>
public InjectContextAttribute(ContextUsage usage = ContextUsage.InOut, bool optional = false)
{
this.Usage = usage;
Optional = optional;
}
}
/// <summary>
/// Options for how the attribute is used among build tasks. It can be either injected to and or extracted from a build task.
/// </summary>
public enum ContextUsage
{
/// <summary>
/// Use to indicate that the attribute can be injected to and extracted from a build task.
/// </summary>
InOut,
/// <summary>
/// Use to indicate that the attribute can only be injected to a build task.
/// </summary>
In,
/// <summary>
/// Use to indicate that the attribute can only be extracted from a build task.
/// </summary>
Out
}
class ContextInjector
{
public static void Inject(IBuildContext context, object obj)
{
FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (FieldInfo field in fields)
{
object[] attrs = field.GetCustomAttributes(typeof(InjectContextAttribute), true);
if (attrs.Length == 0)
continue;
InjectContextAttribute attr = attrs[0] as InjectContextAttribute;
if (attr == null || attr.Usage == ContextUsage.Out)
continue;
object injectionObject;
if (field.FieldType == typeof(IBuildContext))
injectionObject = context;
else if (!attr.Optional)
injectionObject = context.GetContextObject(field.FieldType);
else
{
IContextObject contextObject;
context.TryGetContextObject(field.FieldType, out contextObject);
injectionObject = contextObject;
}
field.SetValue(obj, injectionObject);
}
}
public static void Extract(IBuildContext context, object obj)
{
FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (FieldInfo field in fields)
{
object[] attrs = field.GetCustomAttributes(typeof(InjectContextAttribute), true);
if (attrs.Length == 0)
continue;
InjectContextAttribute attr = attrs[0] as InjectContextAttribute;
if (attr == null || attr.Usage == ContextUsage.In)
continue;
if (field.FieldType == typeof(IBuildContext))
throw new InvalidOperationException("IBuildContext can only be used with the ContextUsage.In option.");
IContextObject contextObject = field.GetValue(obj) as IContextObject;
if (!attr.Optional)
context.SetContextObject(field.FieldType, contextObject);
else if (contextObject != null)
context.SetContextObject(field.FieldType, contextObject);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 29402082393e3a1439cfaa6cc77aa31b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e501e91b337adda41bd6c22cf8cae83e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using UnityEditor.Build.Content;
namespace UnityEditor.Build.Pipeline.Interfaces
{
/// <summary>
/// The extended data about an asset.
/// </summary>
[Serializable]
public class ExtendedAssetData
{
/// <summary>
/// List of object identifiers that are classified as asset representations (sub assets).
/// </summary>
public List<ObjectIdentifier> Representations { get; set; }
/// <summary>
/// Default constructor, initializes properties to defaults
/// </summary>
public ExtendedAssetData()
{
Representations = new List<ObjectIdentifier>();
}
}
/// <summary>
/// Base interface for the storing extended data about an asset.
/// </summary>
public interface IBuildExtendedAssetData : IContextObject
{
/// <summary>
/// Map of asset to extended data about an asset.
/// </summary>
Dictionary<GUID, ExtendedAssetData> ExtendedData { get; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 892bac4397edc914d9ffa4cde040eb0b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Utilities;
namespace UnityEditor.Build.Pipeline.Interfaces
{
/// <summary>
/// Base interface for the Build Caching
/// </summary>
public interface IBuildCache : IContextObject
{
/// <summary>
/// Gets a CacheEntry for an asset identified by its GUID.
/// </summary>
/// <param name="asset">GUID identifier for an asset from the Asset Database</param>
/// <param name="version">Version number of the system asking for an entry to distinguish it from previous incompatible entries. (Optional)</param>
/// <returns>CacheEntry representing current asset.</returns>
CacheEntry GetCacheEntry(GUID asset, int version = 1);
/// <summary>
/// Gets a CacheEntry for a file identified by its relative path.
/// </summary>
/// <param name="path">Relative path of a file on disk</param>
/// <param name="version">Version number of the system asking for an entry to distinguish it from previous incompatible entries. (Optional)</param>
/// <returns>CacheEntry representing a file on disk.</returns>
CacheEntry GetCacheEntry(string path, int version = 1);
/// <summary>
/// Gets a CacheEntry for an object identified by an Object Identifier.
/// </summary>
/// <param name="objectID">Object identifier for an object</param>
/// <param name="version">Version number of the system asking for an entry to distinguish it from previous incompatible entries. (Optional)</param>
/// <returns>CacheEntry representing an object identifier.</returns>
CacheEntry GetCacheEntry(ObjectIdentifier objectID, int version = 1);
/// <summary>
/// Gets a CacheEntry for a scripting type by a System.Type.
/// </summary>
/// <param name="type">System.Type for a scripting type</param>
/// <param name="version">Version number of the system asking for an entry to distinguish it from previous incompatible entries. (Optional)</param>
/// <returns>CacheEntry representing an object identifier.</returns>
CacheEntry GetCacheEntry(Type type, int version = 1);
/// <summary>
/// Checks if the CachedInfo passed in needs to be rebuilt
/// </summary>
/// <param name="info">Cached Info to check</param>
/// <returns><c>true</c> if the cached info needs to be rebuilt; otherwise, <c>false</c>.</returns>
bool HasAssetOrDependencyChanged(CachedInfo info);
/// <summary>
/// Returns the path where info data can be saved in the cache
/// </summary>
/// <param name="entry">Cache entry to get the path</param>
/// <returns>Path on disk where to save cached info</returns>
string GetCachedInfoFile(CacheEntry entry);
/// <summary>
/// Returns the path where artifact data can be saved in the cache
/// </summary>
/// <param name="entry">Cache entry to get the path</param>
/// <returns>Path on disk where to save cached artifacts</returns>
string GetCachedArtifactsDirectory(CacheEntry entry);
/// <summary>
/// Loads a set of CachedInfos from the cache
/// </summary>
/// <param name="entries">List of cache entries to load</param>
/// <param name="cachedInfos">Out list of cached infos loaded</param>
void LoadCachedData(IList<CacheEntry> entries, out IList<CachedInfo> cachedInfos);
/// <summary>
/// Saves a set of CachedInfos to the cache
/// </summary>
/// <param name="infos">List of cached infos to save</param>
void SaveCachedData(IList<CachedInfo> infos);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 717b55fbf36106b449169defda0705c7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Tasks;
namespace UnityEditor.Build.Pipeline.Interfaces
{
#if UNITY_2019_3_OR_NEWER
/// <summary>
/// Custom Content struct mapping a source asset to a processor to generate custom data for that asset.
/// </summary>
[Serializable]
public struct CustomContent : IEquatable<CustomContent>
{
/// <summary>
/// Input Asset for custom content
/// </summary>
public GUID Asset { get; set; }
/// <summary>
/// Processor function to run to convert the input asset to the custom content
/// </summary>
public Action<GUID, CalculateCustomDependencyData> Processor;
/// <summary>
/// IEquatable<CustomContent> Equals operator to handle generic collections
/// </summary>
/// <param name="other">Other CustomContent object to compare against.</param>
/// <returns></returns>
public bool Equals(CustomContent other)
{
return Asset == other.Asset && Processor == other.Processor;
}
}
/// <summary>
/// Base interface for storing the list of Custom Assets generated during the Scriptable Build Pipeline.
/// </summary>
public interface ICustomAssets : IContextObject
{
/// <summary>
/// List of Custom Assets to include.
/// </summary>
List<GUID> Assets { get; }
}
#endif
/// <summary>
/// Base interface for feeding Assets to the Scriptable Build Pipeline.
/// </summary>
public interface IBuildContent : IContextObject
{
/// <summary>
/// List of Assets to include.
/// </summary>
List<GUID> Assets { get; }
/// <summary>
/// List of Scenes to include.
/// </summary>
List<GUID> Scenes { get; }
#if UNITY_2019_3_OR_NEWER
/// <summary>
/// List of custom content to be included in asset bundles.
/// </summary>
List<CustomContent> CustomAssets { get; }
#endif
}
/// <summary>
/// Base interface for feeding Assets with explicit Asset Bundle layout to the Scriptable Build Pipeline.
/// </summary>
public interface IBundleBuildContent : IBuildContent
{
/// <summary>
/// Specific layout of asset bundles to assets or scenes.
/// </summary>
Dictionary<string, List<GUID>> BundleLayout { get; }
#if UNITY_2019_3_OR_NEWER
/// <summary>
/// Additional list of raw files to add to an asset bundle
/// </summary>
Dictionary<string, List<ResourceFile>> AdditionalFiles { get; }
#endif
/// <summary>
/// Custom loading identifiers to use for Assets or Scenes.
/// </summary>
Dictionary<GUID, string> Addresses { get; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 431079301da72b74cba77e88c2af37ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,138 @@
using System;
namespace UnityEditor.Build.Pipeline.Interfaces
{
/// <summary>
/// Base interface for all objects that can be stored in <see cref="IBuildContext"/>.
/// </summary>
public interface IContextObject {}
/// <summary>
/// Base interface that handles processing the callbacks after script building step.
/// </summary>
public interface IScriptsCallback : IContextObject
{
/// <summary>
/// Processes all the callbacks after script building step.
/// </summary>
/// <param name="parameters">Parameters passed into the build pipeline.</param>
/// <param name="results">Results from the script building step.</param>
/// <returns>Return code from processing the callbacks.</returns>
ReturnCode PostScripts(IBuildParameters parameters, IBuildResults results);
}
/// <summary>
/// Base interface for handling running the callbacks after dependency calculation step.
/// </summary>
public interface IDependencyCallback : IContextObject
{
/// <summary>
/// Processes all the callbacks after dependency calculation step.
/// </summary>
/// <param name="parameters">Parameters passed into the build pipeline.</param>
/// <param name="dependencyData">Results from the dependency calculation step.</param>
/// <returns>Return code from processing the callbacks.</returns>
ReturnCode PostDependency(IBuildParameters parameters, IDependencyData dependencyData);
}
/// <summary>
/// Base interface for handling running the callbacks after packing step.
/// </summary>
public interface IPackingCallback : IContextObject
{
/// <summary>
/// Processes all the callbacks after packing step.
/// </summary>
/// <param name="parameters">Parameters passed into the build pipeline.</param>
/// <param name="dependencyData">Results from the dependency calculation step.</param>
/// <param name="writeData">Results from the packing step.</param>
/// <returns>Return code from processing the callbacks.</returns>
ReturnCode PostPacking(IBuildParameters parameters, IDependencyData dependencyData, IWriteData writeData);
}
/// <summary>
/// Base interface for handling running the callbacks after writing step.
/// </summary>
public interface IWritingCallback : IContextObject
{
/// <summary>
/// Processes all the callbacks after writing step.
/// </summary>
/// <param name="parameters">Parameters passed into the build pipeline.</param>
/// <param name="dependencyData">Results from the dependency calculation step.</param>
/// <param name="writeData">Results from the packing step.</param>
/// <param name="results">Results from the writing step.</param>
/// <returns>Return code from processing the callbacks.</returns>
ReturnCode PostWriting(IBuildParameters parameters, IDependencyData dependencyData, IWriteData writeData, IBuildResults results);
}
/// <summary>
/// Base interface for build data container system
/// </summary>
public interface IBuildContext
{
/// <summary>
/// Checks the build context for existence of a data that is of the specified type.
/// </summary>
/// <typeparam name="T">Type of data to check for existence.</typeparam>
/// <returns><c>true</c> if the context contains specified type of data; otherwise, <c>false</c>.</returns>
bool ContainsContextObject<T>() where T : IContextObject;
/// <summary>
/// Checks the build context for existence of a data that is of the specified type.
/// </summary>
/// <param name="type">Type of data to check for existence.</param>
/// <returns><c>true</c> if the context contains specified type of data; otherwise, <c>false</c>.</returns>
bool ContainsContextObject(Type type);
/// <summary>
/// Gets the data of the specified type contained in the build context.
/// </summary>
/// <typeparam name="T">Type of data to return.</typeparam>
/// <returns>The type of data specified.</returns>
T GetContextObject<T>() where T : IContextObject;
/// <summary>
/// Gets the data of the specified type contained in the build context.
/// </summary>
/// <param name="type">Type of data to return.</param>
/// <returns>The type of data specified.</returns>
IContextObject GetContextObject(Type type);
/// <summary>
/// Adds the data of the specified type to the build context.
/// </summary>
/// <typeparam name="T">Type of data to add.</typeparam>
/// <param name="contextObject">Object holding the data to add.</param>
void SetContextObject<T>(IContextObject contextObject) where T : IContextObject;
/// <summary>
/// Adds the data of the specified type to the build context.
/// </summary>
/// <param name="type">Type of data to add.</param>
/// <param name="contextObject">Object holding the data to add.</param>
void SetContextObject(Type type, IContextObject contextObject);
/// <summary>
/// Adds the data to the build context. Type will be inferred using Reflection.
/// </summary>
/// <param name="contextObject">Object holding the data to add.</param>
void SetContextObject(IContextObject contextObject);
/// <summary>
/// Tries to get the data of the specified type contained in the build context.
/// </summary>
/// <typeparam name="T">Type of data to return.</typeparam>
/// <param name="contextObject">The object holding the data to be returned if found.</param>
/// <returns><c>true</c> if the context was able to returned the specified data; otherwise, <c>false</c>.</returns>
bool TryGetContextObject<T>(out T contextObject) where T : IContextObject;
/// <summary>
/// Tries to get the data of the specified type contained in the build context.
/// </summary>
/// <param name="type">Type of data to return.</param>
/// <param name="contextObject">The object holding the data to be returned if found.</param>
/// <returns><c>true</c> if the context was able to returned the specified data; otherwise, <c>false</c>.</returns>
bool TryGetContextObject(Type type, out IContextObject contextObject);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ef728e637accb8d46ad52f863653cf75
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,211 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Utilities;
[assembly: InternalsVisibleTo("Unity.Addressables.Editor.Tests")]
namespace UnityEditor.Build.Pipeline.Interfaces
{
/// <summary>
/// Describes the level of a log entry
/// </summary>
public enum LogLevel
{
/// <summary>
/// The entry is reporting an error.
/// </summary>
Error,
/// <summary>
/// The entry is reporting an warning.
/// </summary>
Warning,
/// <summary>
/// The entry is reporting general information.
/// </summary>
Info,
/// <summary>
/// The entry is reporting verbose information.
/// </summary>
Verbose
}
/// <summary>
/// Interface for monitoring the build process. Several tasks will log details of their progress through this interface.
/// See the [Build Logging](https://docs.unity3d.com/Packages/com.unity.scriptablebuildpipeline@latest/index.html?subfolder=/manual/BuildLogger.html) documentation for more details.
/// </summary>
public interface IBuildLogger : IContextObject
{
/// <summary>
/// Adds details to the active build step
/// </summary>
/// <param name="level">The log level of this entry.</param>
/// <param name="msg">The message to add.</param>
void AddEntry(LogLevel level, string msg);
/// <summary>
/// Should be called when beginning a build step.
/// </summary>
/// <param name="level">The log level of this step.</param>
/// <param name="stepName">A name associated with the step. It is recommended that this name does not include specific context about the step; dynamic context should be added under the step as an entry.</param>
/// <param name="subStepsCanBeThreaded">True if within this build step the IBuildLogger will be used on multiple threads.</param>
void BeginBuildStep(LogLevel level, string stepName, bool subStepsCanBeThreaded);
/// <summary>
/// Ends the build step.
/// </summary>
void EndBuildStep();
}
internal enum DeferredEventType
{
Begin,
End,
Info
}
internal struct DeferredEvent
{
public LogLevel Level;
public DeferredEventType Type;
public double Time;
public string Name;
public string Context;
}
internal interface IDeferredBuildLogger
{
void HandleDeferredEventStream(IEnumerable<DeferredEvent> events);
}
/// <summary>
/// Helper class to define a scope with a using statement
/// </summary>
public struct ScopedBuildStep : IDisposable
{
IBuildLogger m_Logger;
internal ScopedBuildStep(LogLevel level, string stepName, IBuildLogger logger, bool multiThreaded, string context)
{
m_Logger = logger;
m_Logger?.BeginBuildStep(level, stepName, multiThreaded);
if (!string.IsNullOrEmpty(context))
m_Logger?.AddEntrySafe(level, context);
}
/// <inheritdoc/>
void IDisposable.Dispose()
{
m_Logger?.EndBuildStep();
}
}
#if UNITY_2020_2_OR_NEWER || ENABLE_DETAILED_PROFILE_CAPTURING
internal struct ProfileCaptureScope : IDisposable
{
IBuildLogger m_Logger;
public ProfileCaptureScope(IBuildLogger logger, ProfileCaptureOptions options)
{
m_Logger = ScriptableBuildPipeline.useDetailedBuildLog ? logger : null;
ContentBuildInterface.StartProfileCapture(options);
}
public void Dispose()
{
ContentBuildProfileEvent[] events = ContentBuildInterface.StopProfileCapture();
if (m_Logger == null)
return;
IDeferredBuildLogger dLog = (IDeferredBuildLogger)m_Logger;
IEnumerable<DeferredEvent> dEvents = events.Select(i =>
{
var e = new DeferredEvent();
e.Level = LogLevel.Verbose;
BuildLoggerExternsions.ConvertNativeEventName(i.Name, out e.Name, out e.Context);
e.Time = (double)i.TimeMicroseconds / (double)1000;
e.Type = BuildLoggerExternsions.ConvertToDeferredType(i.Type);
return e;
});
dLog.HandleDeferredEventStream(dEvents);
}
}
#endif
/// <summary>
/// Contains extension methods for the IBuildLogger interface
/// </summary>
public static class BuildLoggerExternsions
{
/// <summary>
/// Adds details to the active build step
/// </summary>
/// <param name="log">The build log.</param>
/// <param name="level">The log level of this entry.</param>
/// <param name="msg">The message to add.</param>
public static void AddEntrySafe(this IBuildLogger log, LogLevel level, string msg)
{
if (log != null)
{
log.AddEntry(level, msg);
}
}
/// <summary>
/// Begins a new build step and returns an ScopedBuildStep which will end the build step when disposed. It is recommended to use this in conjunction with the using statement.
/// </summary>
/// <param name="log">The build log.</param>
/// <param name="level">The log level of this step.</param>
/// <param name="stepName">A name associated with the step.</param>
/// <param name="multiThreaded">True if within this build step the IBuildLogger will be used on multiple threads.</param>
/// <returns>Returns a ScopedBuildStep that will end the build step when it is disposed.</returns>
public static ScopedBuildStep ScopedStep(this IBuildLogger log, LogLevel level, string stepName, bool multiThreaded = false)
{
return new ScopedBuildStep(level, stepName, log, multiThreaded, null);
}
/// <summary>
/// Begins a new build step and returns an ScopedBuildStep which will end the build step when disposed. It is recommended to use this in conjunction with the using statement.
/// </summary>
/// <param name="log">The build log.</param>
/// <param name="level">The log level of this step.</param>
/// <param name="stepName">A name associated with the step.</param>
/// <param name="context">Adds an entry message the build step. This allows attaching specific context data without changing the stepName.</param>
/// <returns>Returns a ScopedBuildStep that will end the build step when it is disposed.</returns>
public static ScopedBuildStep ScopedStep(this IBuildLogger log, LogLevel level, string stepName, string context)
{
return new ScopedBuildStep(level, stepName, log, false, context);
}
#if UNITY_2020_2_OR_NEWER || ENABLE_DETAILED_PROFILE_CAPTURING
internal static DeferredEventType ConvertToDeferredType(ProfileEventType type)
{
if (type == ProfileEventType.Begin) return DeferredEventType.Begin;
if (type == ProfileEventType.End) return DeferredEventType.End;
if (type == ProfileEventType.Info) return DeferredEventType.Info;
throw new Exception("Unknown type");
}
const string k_WriteFile = "Write file:";
const string k_WriteObject = "Write object - ";
internal static void ConvertNativeEventName(string nativeName, out string eventName, out string eventContext)
{
eventName = nativeName;
eventContext = "";
if (nativeName.StartsWith(k_WriteFile, StringComparison.Ordinal))
{
eventName = "Write File";
eventContext = nativeName.Substring(k_WriteFile.Length);
}
else if (nativeName.StartsWith(k_WriteObject, StringComparison.Ordinal))
{
eventName = "Write Object";
eventContext = nativeName.Substring(k_WriteObject.Length);
}
if (eventContext.Any(c => c == '"'))
eventContext = eventContext.Replace("\"", "\\\"");
}
#endif
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4f04442bb071fe8438a7e40408a9bada
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,130 @@
using UnityEditor.Build.Content;
using UnityEditor.Build.Player;
using UnityEngine;
namespace UnityEditor.Build.Pipeline.Interfaces
{
#if UNITY_2018_3_OR_NEWER
using BuildCompression = UnityEngine.BuildCompression;
#else
using BuildCompression = UnityEditor.Build.Content.BuildCompression;
#endif
/// <summary>
/// Base interface for the parameters container
/// </summary>
public interface IBuildParameters : IContextObject
{
/// <summary>
/// Target build platform. <seealso cref="BuildTarget"/>
/// </summary>
BuildTarget Target { get; set; }
/// <summary>
/// Target build platform group. <seealso cref="BuildTargetGroup"/>
/// </summary>
BuildTargetGroup Group { get; set; }
/// <summary>
/// The set of build flags to use for building content.
/// </summary>
ContentBuildFlags ContentBuildFlags { get; set; }
/// <summary>
/// Scripting type information to use when building content.
/// Setting this to a previously cached value will prevent the default script compiling step.
/// </summary>
TypeDB ScriptInfo { get; set; }
/// <summary>
/// Script compilation options to use for the script compiling step.
/// </summary>
ScriptCompilationOptions ScriptOptions { get; set; }
/// <summary>
/// Temporary location to be used for artifacts generated during the build but are not part of the final output.
/// </summary>
string TempOutputFolder { get; set; }
/// <summary>
/// Location to be used for compiled scripts generated during the build.
/// </summary>
string ScriptOutputFolder { get; set; }
/// <summary>
/// Enables the use of the build cache if set to true.
/// </summary>
bool UseCache { get; set; }
/// <summary>
/// Enables and specifies the cache server to use.
/// </summary>
string CacheServerHost { get; set; }
/// <summary>
/// The port for the cache server to use
/// </summary>
int CacheServerPort { get; set; }
/// <summary>
/// Writes out a link.xml file to the output folder to use with Unity managed code stripping.
/// </summary>
bool WriteLinkXML { get; set; }
#if NONRECURSIVE_DEPENDENCY_DATA
/// <summary>
/// Calculates and build asset bundles using Non-Recursive Dependency calculation methods.
/// This approach helps reduce asset bundle rebuilds and runtime memory consumption.
/// </summary>
bool NonRecursiveDependencies { get; set; }
#endif
/// <summary>
/// Constructs and returns the BuildSettings struct to use for content building.
/// </summary>
/// <returns>Returns the BuildSettings struct to use for content building.</returns>
BuildSettings GetContentBuildSettings();
/// <summary>
/// Returns the output folder to use for the specified identifier.
/// </summary>
/// <param name="identifier">Identifier used to identify which output folder to use.</param>
/// <returns>Returns the output folder to use for the specified identifier.</returns>
string GetOutputFilePathForIdentifier(string identifier);
/// <summary>
/// Constructs and returns the BuildCompression struct to use for the specified identifier.
/// </summary>
/// <param name="identifier">Identifier used to construct the BuildCompression struct.</param>
/// <returns>Returns the BuildCompression struct to use for a specific identifier.</returns>
BuildCompression GetCompressionForIdentifier(string identifier);
/// <summary>
/// Constructs and returns the ScriptCompilationSettings struct to use for script compiling.
/// </summary>
/// <returns>Returns the ScriptCompilationSettings struct to use for script compiling.</returns>
ScriptCompilationSettings GetScriptCompilationSettings();
}
/// <summary>
/// Base interface for the parameters container for building bundles.
/// </summary>
public interface IBundleBuildParameters : IBuildParameters
{
/// <summary>
/// Append the hash of the AssetBundle content to the file name.
/// </summary>
bool AppendHash { get; set; }
/// <summary>
/// Packs assets in bundles contiguously based on the ordering of the source asset which results in improved asset loading times.
/// </summary>
bool ContiguousBundles { get; set; }
/// <summary>
/// Assume sub Assets have no visible asset representations (are not visible in the Project view) which results in improved build times.
/// Sub Assets in the built bundles cannot be accessed by AssetBundle.LoadAsset&lt;T&gt; or AssetBundle.LoadAllAssets&lt;T&gt;.
/// </summary>
bool DisableVisibleSubAssetRepresentations { get; set; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e18580a7226c3b34c9fa7fdb324edfe6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using UnityEditor.Build.Content;
using UnityEditor.Build.Player;
using UnityEngine;
using UnityEngine.Build.Pipeline;
namespace UnityEditor.Build.Pipeline.Interfaces
{
public struct AssetResultData
{
public GUID Guid;
public Hash128 Hash;
public List<ObjectIdentifier> IncludedObjects;
public List<ObjectIdentifier> ReferencedObjects;
public Dictionary<ObjectIdentifier, Type[]> ObjectTypes;
}
/// <summary>
/// Base interface for the build results container
/// </summary>
public interface IBuildResults : IContextObject
{
/// <summary>
/// Results from the script compiling step.
/// </summary>
ScriptCompilationResult ScriptResults { get; set; }
/// <summary>
/// Map of serialized file name to results for built content.
/// </summary>
Dictionary<string, WriteResult> WriteResults { get; }
/// <summary>
/// Map of serialized file name to additional metadata associated with the write result.
/// </summary>
Dictionary<string, SerializedFileMetaData> WriteResultsMetaData { get; }
/// <summary>
/// Map of Asset data included in this build
/// </summary>
Dictionary<GUID, AssetResultData> AssetResults { get; }
}
/// <summary>
/// Extended interface for Asset Bundle build results container.
/// <seealso cref="IBuildResults"/>
/// </summary>
public interface IBundleBuildResults : IBuildResults
{
/// <summary>
/// Map of Asset Bundle name to details about the built bundle.
/// </summary>
Dictionary<string, BundleDetails> BundleInfos { get; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 87c4ff2c85dcb524685caba3bb22110c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using UnityEditor.Build.Content;
namespace UnityEditor.Build.Pipeline.Interfaces
{
/// <summary>
/// The importer data about a sprite asset.
/// </summary>
[Serializable]
public class SpriteImporterData
{
/// <summary>
/// Property if this sprite asset is packed by the sprite packer.
/// </summary>
public bool PackedSprite { get; set; }
/// <summary>
/// Object identifier of the source texture for the sprite.
/// </summary>
public ObjectIdentifier SourceTexture { get; set; }
}
/// <summary>
/// Base interface for the storing sprite importer data for sprite assets.
/// </summary>
public interface IBuildSpriteData : IContextObject
{
/// <summary>
/// Map of sprite asset to importer data.
/// </summary>
Dictionary<GUID, SpriteImporterData> ImporterData { get; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fb4c2e00689dfb34985500f260df63cb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,20 @@
namespace UnityEditor.Build.Pipeline.Interfaces
{
/// <summary>
/// Base interface of all build tasks.
/// </summary>
public interface IBuildTask
{
/// <summary>
/// Version identifier for the build task.
/// Primarily for caching.
/// </summary>
int Version { get; }
/// <summary>
/// Run task method
/// </summary>
/// <returns>Return code with status information about success or failure causes.</returns>
ReturnCode Run();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8f2c2ad6904ea9147a43a79fccd94ca1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,16 @@
using System.Collections.Generic;
using UnityEditor.Build.Content;
namespace UnityEditor.Build.Pipeline.Interfaces
{
/// <summary>
/// Optional interface used for overriding the location where specific objects will be serialized
/// </summary>
public interface IBundleExplictObjectLayout : IContextObject
{
/// <summary>
/// Map listing object identifiers and their new bundle location
/// </summary>
Dictionary<ObjectIdentifier, string> ExplicitObjectLocation { get; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a78160a9be3bdf4d9397ca7b0466850
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,47 @@
using System.Collections.Generic;
using UnityEditor.Build.Content;
using UnityEngine;
namespace UnityEditor.Build.Pipeline.Interfaces
{
/// <summary>
/// Base interface for the dependency data container
/// </summary>
public interface IDependencyData : IContextObject
{
/// <summary>
/// Map of Asset to dependency data.
/// </summary>
Dictionary<GUID, AssetLoadInfo> AssetInfo { get; }
/// <summary>
/// Map of Asset to usage data.
/// </summary>
Dictionary<GUID, BuildUsageTagSet> AssetUsage { get; }
/// <summary>
/// Map of Scene to dependency data.
/// </summary>
Dictionary<GUID, SceneDependencyInfo> SceneInfo { get; }
/// <summary>
/// Map of Scene to usage data.
/// </summary>
Dictionary<GUID, BuildUsageTagSet> SceneUsage { get; }
/// <summary>
/// Map of Asset or Scene to pre-calculated dependency hash for caching.
/// </summary>
Dictionary<GUID, Hash128> DependencyHash { get; }
/// <summary>
/// Reusable cache for calculating usage tags
/// </summary>
BuildUsageCache DependencyUsageCache { get; }
/// <summary>
/// BuildUsageTagGlobal value from GraphicsSettings
/// </summary>
BuildUsageTagGlobal GlobalUsage { get; set; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a99d07c2f0fab1e4f8623f5913408df5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,24 @@
using UnityEditor.Build.Content;
namespace UnityEditor.Build.Pipeline.Interfaces
{
/// <summary>
/// Base interface for the generating deterministic identifiers for different parts of the build pipeline.
/// </summary>
public interface IDeterministicIdentifiers : IContextObject
{
/// <summary>
/// Generates a deterministic internal file name from the passed in name.
/// </summary>
/// <param name="name">Name identifier for internal file name generation</param>
/// <returns>Deterministic file name.</returns>
string GenerateInternalFileName(string name);
/// <summary>
/// Generates a deterministic id for a given object in the build.
/// </summary>
/// <param name="objectID">Object identifier to for id generation.</param>
/// <returns><c>long</c> representing the id of the objectID.</returns>
long SerializationIndexFromObjectIdentifier(ObjectIdentifier objectID);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f2f62872d2484231aa95c2eb9feb8326
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEditor.Build.Content;
//TODO: Remove this when we make this interface public
[assembly: InternalsVisibleTo("Unity.Addressables.Editor", AllInternalsVisible = true)]
namespace UnityEditor.Build.Pipeline.Interfaces
{
/// <summary>
/// Base interface for the dependency data container
/// </summary>
internal interface IObjectDependencyData : IContextObject
{
/// <summary>
/// Dependencies of a given object
/// </summary>
Dictionary<ObjectIdentifier, List<ObjectIdentifier>> ObjectDependencyMap { get; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c07ce938101b77544a5df7d58f37aa97
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,32 @@
namespace UnityEditor.Build.Pipeline.Interfaces
{
/// <summary>
/// Base interface for the build progress tracker
/// </summary>
public interface IProgressTracker : IContextObject
{
/// <summary>
/// Number of build tasks to run
/// </summary>
int TaskCount { get; set; }
/// <summary>
/// Current 0.0f to 1.0f progress though the TaskCount
/// </summary>
float Progress { get; }
/// <summary>
/// Increments and updated the title of the progress bar.
/// </summary>
/// <param name="taskTitle">The title to display on the progress bar.</param>
/// <returns><c>false</c> if the build should not continue due to user interaction with the progress bar; otherwise, <c>true</c>.</returns>
bool UpdateTask(string taskTitle);
/// <summary>
/// Updates the secondary information display of the progress bar.
/// </summary>
/// <param name="taskInfo">The secondary information to display on the progress bar.</param>
/// <returns><c>false</c> if the build should not continue due to user interaction with the progress bar; otherwise, <c>true</c>.</returns>
bool UpdateInfo(string taskInfo);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e25b76d1a8a87c641894e4694a750cd8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,48 @@
using System.Collections.Generic;
using UnityEditor.Build.Content;
namespace UnityEditor.Build.Pipeline.Interfaces
{
/// <summary>
/// Base interface for the write data container.
/// </summary>
public interface IWriteData : IContextObject
{
/// <summary>
/// Map of asset to file dependencies.
/// First dependency in the list is the main file for an asset.
/// </summary>
Dictionary<GUID, List<string>> AssetToFiles { get; }
/// <summary>
/// Map of file to list of objects in that file
/// </summary>
Dictionary<string, List<ObjectIdentifier>> FileToObjects { get; }
/// <summary>
/// List of all write operations to serialize data to disk
/// </summary>
List<IWriteOperation> WriteOperations { get; }
}
/// <summary>
/// Extended interface for Asset Bundle write data container.
/// </summary>
public interface IBundleWriteData : IWriteData
{
/// <summary>
/// Map of file name to bundle name
/// </summary>
Dictionary<string, string> FileToBundle { get; }
/// <summary>
/// Map of file name to calculated usage set
/// </summary>
Dictionary<string, BuildUsageTagSet> FileToUsageSet { get; }
/// <summary>
/// Map of file name to calculated object references
/// </summary>
Dictionary<string, BuildReferenceMap> FileToReferenceMap { get; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a8092de151d449b0962952fe68a54dd9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,57 @@
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEngine;
namespace UnityEditor.Build.Pipeline.Interfaces
{
/// <summary>
/// Base interface for wrapping the different low level WriteSerializeFile API around a common high level Write function
/// </summary>
public interface IWriteOperation
{
/// <summary>
/// The specific write command containing the details about what to write to disk.
/// <seealso cref="WriteCommand"/>
/// </summary>
WriteCommand Command { get; set; }
/// <summary>
/// The specific usage data for objects in the write command.
/// <seealso cref="BuildUsageTagSet"/>
/// </summary>
BuildUsageTagSet UsageSet { get; set; }
/// <summary>
/// The specific reference data for objects in the write command.
/// <seealso cref="BuildReferenceMap"/>
/// </summary>
BuildReferenceMap ReferenceMap { get; set; }
/// <summary>
/// The hash that represents the unique set of input dependencies for caching this write command.
/// </summary>
Hash128 DependencyHash { get; set; }
/// <summary>
/// Write function that wraps the low level WriteSerializeFile APIs that takes the common minimum set of parameters.
/// </summary>
/// <param name="outputFolder">The location to write data to disk.</param>
/// <param name="settings">The build settings to use for writing data.</param>
/// <param name="globalUsage">The global usage to use for writing data.</param>
/// <returns>The write results struct containing details about what was written to disk</returns>
WriteResult Write(string outputFolder, BuildSettings settings, BuildUsageTagGlobal globalUsage);
/// <summary>
/// Optimized hash function for use with the Build Cache system.
/// </summary>
/// <returns>Unique hash for the contents of this write operation.</returns>
Hash128 GetHash128();
/// <summary>
/// Optimized hash function for use with the Build Cache system.
/// </summary>
/// <param name="log">The build log.</param>
/// <returns>Unique hash for the contents of this write operation.</returns>
Hash128 GetHash128(IBuildLogger log);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7df65588254d9194cb3fb45879168954
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 24de8307d973e7541803ec7b592a5361
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,57 @@
using System.Diagnostics;
namespace UnityEditor.Build.Profiler
{
internal class BuildProfiler
{
Stopwatch[] m_Trackers;
long[] m_CallCount;
string[] m_Names;
public BuildProfiler(int count)
{
m_Trackers = new Stopwatch[count];
m_CallCount = new long[count];
m_Names = new string[count];
for (int i = 0; i < count; i++)
{
m_Trackers[i] = new Stopwatch();
m_CallCount[i] = 0;
}
}
public void Start(int index, string name)
{
if (m_Trackers == null)
return;
Debug.Assert(!m_Trackers[index].IsRunning);
m_Trackers[index].Start();
m_CallCount[index]++;
m_Names[index] = name;
}
public void Stop(int index)
{
if (m_Trackers == null)
return;
Debug.Assert(m_Trackers[index].IsRunning);
m_Trackers[index].Stop();
}
public void Print()
{
if (m_Trackers == null)
return;
string msg = "";
for (int i = 0; i < m_Trackers.Length; i++)
{
Debug.Assert(!m_Trackers[i].IsRunning);
msg += string.Format("Counter[{0}]\t{1}\t{2}\n", m_Names[i], m_CallCount[i], m_Trackers[i].ElapsedMilliseconds);
}
UnityEngine.Debug.Log(msg);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ba1f63c8c87d5ab4cad407b7245879e4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,43 @@
namespace UnityEditor.Build.Pipeline
{
/// <summary>
/// Options for scriptable build pipeline return codes. Int values of these return codes are standardized to 0 or greater for Success and -1 or less for Error.
/// </summary>
public enum ReturnCode
{
// Success Codes are Positive!
/// <summary>
/// Use to indicate that the operation suceeded.
/// </summary>
Success = 0,
/// <summary>
/// Use to indicate that the operation suceeded.
/// </summary>
SuccessCached = 1,
/// <summary>
/// Use to indicate that the operation suceeded but did not actually execute.
/// </summary>
SuccessNotRun = 2,
// Error Codes are Negative!
/// <summary>
/// Use to indicate that the operation encountered an error.
/// </summary>
Error = -1,
/// <summary>
/// Use to indicate that the operation encountered an exception.
/// </summary>
Exception = -2,
/// <summary>
/// Use to indicate that the operation was cancelled.
/// </summary>
Canceled = -3,
/// <summary>
/// Use to indicate that the operation failed because there are unsaved scene changes.
/// </summary>
UnsavedChanges = -4,
/// <summary>
/// Use to indicate that the operation failed because it was missing the required objects.
/// </summary>
MissingRequiredObjects = -5
}
}

View file

@ -0,0 +1,13 @@
fileFormatVersion: 2
guid: bef0b83030006f0409c3a3cfa181ee64
timeCreated: 1504709534
licenseType: Pro
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: d2753eab703276041afb1d58301a291c
folderAsset: yes
timeCreated: 1505167493
licenseType: Pro
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,65 @@
using System;
using UnityEditor.Build.Pipeline.Interfaces;
namespace UnityEditor.Build.Pipeline
{
/// <summary>
/// Basic implementation of IDependencyCallback, IPackingCallback, IWritingCallback, and IScriptsCallback.
/// Uses Func implementation for callbacks. <seealso cref="IDependencyCallback"/>, <seealso cref="IPackingCallback"/>
/// <seealso cref="IWritingCallback"/>, and <seealso cref="IScriptsCallback"/>
/// </summary>
public class BuildCallbacks : IDependencyCallback, IPackingCallback, IWritingCallback, IScriptsCallback
{
/// <summary>
/// Func delegate for the callback after scripts have been compiled.
/// </summary>
public Func<IBuildParameters, IBuildResults, ReturnCode> PostScriptsCallbacks { get; set; }
/// <summary>
/// Func delegate for the callback after dependency calculation has occurred.
/// </summary>
public Func<IBuildParameters, IDependencyData, ReturnCode> PostDependencyCallback { get; set; }
/// <summary>
/// Func delegate for the callback after packing has occurred.
/// </summary>
public Func<IBuildParameters, IDependencyData, IWriteData, ReturnCode> PostPackingCallback { get; set; }
/// <summary>
/// Func delegate for the callback after writing content has occurred.
/// </summary>
public Func<IBuildParameters, IDependencyData, IWriteData, IBuildResults, ReturnCode> PostWritingCallback { get; set; }
/// <inheritdoc />
public ReturnCode PostScripts(IBuildParameters parameters, IBuildResults results)
{
if (PostScriptsCallbacks != null)
return PostScriptsCallbacks(parameters, results);
return ReturnCode.Success;
}
/// <inheritdoc />
public ReturnCode PostDependency(IBuildParameters buildParameters, IDependencyData dependencyData)
{
if (PostDependencyCallback != null)
return PostDependencyCallback(buildParameters, dependencyData);
return ReturnCode.Success;
}
/// <inheritdoc />
public ReturnCode PostPacking(IBuildParameters buildParameters, IDependencyData dependencyData, IWriteData writeData)
{
if (PostPackingCallback != null)
return PostPackingCallback(buildParameters, dependencyData, writeData);
return ReturnCode.Success;
}
/// <inheritdoc />
public ReturnCode PostWriting(IBuildParameters parameters, IDependencyData dependencyData, IWriteData writeData, IBuildResults results)
{
if (PostWritingCallback != null)
return PostWritingCallback(parameters, dependencyData, writeData, results);
return ReturnCode.Success;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 98f1571a4d4b4a3f8a65343e6ca1966d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
#if UNITY_2019_3_OR_NEWER
using UnityEditor.Build.Content;
#endif
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
namespace UnityEditor.Build.Pipeline
{
#if UNITY_2019_3_OR_NEWER
/// <summary>
/// Basic implementation of ICustomAssets. Stores the list of Custom Assets generated during the Scriptable Build Pipeline.
/// <seealso cref="ICustomAssets"/>
/// </summary>
[Serializable]
public class CustomAssets : ICustomAssets
{
/// <inheritdoc />
public List<GUID> Assets { get; private set; }
/// <summary>
/// Default constructor, creates an empty CustomAssets.
/// </summary>
public CustomAssets()
{
Assets = new List<GUID>();
}
}
#endif
/// <summary>
/// Basic implementation of IBuildContent. Stores the list of Assets to feed the Scriptable Build Pipeline.
/// <seealso cref="IBuildContent"/>
/// </summary>
[Serializable]
public class BuildContent : IBuildContent
{
/// <inheritdoc />
public List<GUID> Assets { get; private set; }
/// <inheritdoc />
public List<GUID> Scenes { get; private set; }
#if UNITY_2019_3_OR_NEWER
/// <inheritdoc />
public List<CustomContent> CustomAssets { get; private set; }
#endif
/// <summary>
/// Default constructor, creates an empty BuildContent.
/// </summary>
public BuildContent() {}
/// <summary>
/// Default constructor, takes a set of Assets and converts them to the appropriate properties.
/// </summary>
/// <param name="assets">The set of Assets identified by GUID to ensure are packaged with the build</param>
public BuildContent(IEnumerable<GUID> assets)
{
if (assets == null)
throw new ArgumentNullException("assets");
Assets = new List<GUID>();
Scenes = new List<GUID>();
#if UNITY_2019_3_OR_NEWER
CustomAssets = new List<CustomContent>();
#endif
foreach (var asset in assets)
{
ValidationMethods.Status assetType = ValidationMethods.ValidAsset(asset);
if (assetType == ValidationMethods.Status.Asset)
Assets.Add(asset);
else if (assetType == ValidationMethods.Status.Scene)
Scenes.Add(asset);
else
throw new ArgumentException(string.Format("Asset '{0}' is not a valid Asset or Scene.", asset.ToString()));
}
}
}
/// <summary>
/// Basic implementation of IBundleBuildContent. Stores the list of Assets with explicit Asset Bundle layout to feed the Scriptable Build Pipeline.
/// <seealso cref="IBundleBuildContent"/>
/// </summary>
[Serializable]
public class BundleBuildContent : IBundleBuildContent
{
/// <inheritdoc />
public List<GUID> Assets { get; private set; }
/// <inheritdoc />
public List<GUID> Scenes { get; private set; }
#if UNITY_2019_3_OR_NEWER
/// <inheritdoc />
public List<CustomContent> CustomAssets { get; private set; }
/// <inheritdoc />
public Dictionary<string, List<ResourceFile>> AdditionalFiles { get; private set; }
#endif
/// <inheritdoc />
public Dictionary<GUID, string> Addresses { get; private set; }
/// <inheritdoc />
public Dictionary<string, List<GUID>> BundleLayout { get; private set; }
/// <summary>
/// Default constructor, creates an empty BundleBuildContent.
/// </summary>
public BundleBuildContent() {}
/// <summary>
/// Default constructor, takes a set of AssetBundleBuild and converts them to the appropriate properties.
/// </summary>
/// <param name="bundleBuilds">The set of AssetbundleBuild to be built.</param>
public BundleBuildContent(IEnumerable<AssetBundleBuild> bundleBuilds)
{
if (bundleBuilds == null)
throw new ArgumentNullException("bundleBuilds");
Assets = new List<GUID>();
Scenes = new List<GUID>();
Addresses = new Dictionary<GUID, string>();
BundleLayout = new Dictionary<string, List<GUID>>();
#if UNITY_2019_3_OR_NEWER
CustomAssets = new List<CustomContent>();
AdditionalFiles = new Dictionary<string, List<ResourceFile>>();
#endif
foreach (var bundleBuild in bundleBuilds)
{
List<GUID> guids;
BundleLayout.GetOrAdd(bundleBuild.assetBundleName, out guids);
ValidationMethods.Status bundleType = ValidationMethods.Status.Invalid;
for (int i = 0; i < bundleBuild.assetNames.Length; i++)
{
string assetPath = bundleBuild.assetNames[i];
GUID asset = new GUID(AssetDatabase.AssetPathToGUID(assetPath));
// Ensure the path is valid
ValidationMethods.Status status = ValidationMethods.ValidAsset(asset);
if (status == ValidationMethods.Status.Invalid)
throw new ArgumentException(string.Format("Asset '{0}' is not a valid Asset or Scene.", assetPath));
// Ensure we do not have a mixed bundle
if (bundleType == ValidationMethods.Status.Invalid)
bundleType = status;
else if (bundleType != status)
throw new ArgumentException(string.Format("Asset Bundle '{0}' is invalid because it contains mixed Asset and Scene types.", bundleBuild.assetBundleName));
string address = bundleBuild.addressableNames != null && i < bundleBuild.addressableNames.Length && !string.IsNullOrEmpty(bundleBuild.addressableNames[i]) ?
bundleBuild.addressableNames[i] : AssetDatabase.GUIDToAssetPath(asset.ToString());
// Add the guid to the bundle map
guids.Add(asset);
// Add the guid & address
Addresses.Add(asset, address);
// Add the asset to the correct collection
if (status == ValidationMethods.Status.Asset)
Assets.Add(asset);
else if (status == ValidationMethods.Status.Scene)
Scenes.Add(asset);
}
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6616141fe64e4fc2b16c015f32d0a64d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEngine;
namespace UnityEditor.Build.Pipeline
{
/// <summary>
/// Basic implementation of IBuildContext. Stores data generated during a build.
/// <seealso cref="IBuildContext"/>
/// </summary>
public class BuildContext : IBuildContext
{
internal Dictionary<Type, IContextObject> m_ContextObjects;
/// <summary>
/// Default constructor
/// </summary>
public BuildContext()
{
m_ContextObjects = new Dictionary<Type, IContextObject>();
}
/// <summary>
/// Default constructor, adds the passed in parameters to the context.
/// </summary>
/// <param name="buildParams">The set of initial parameters to add to the context.</param>
public BuildContext(params IContextObject[] buildParams)
{
m_ContextObjects = new Dictionary<Type, IContextObject>();
if (buildParams == null)
return;
foreach (var buildParam in buildParams)
{
if (buildParam != null)
SetContextObject(buildParam);
}
}
/// <inheritdoc />
public void SetContextObject<T>(IContextObject contextObject) where T : IContextObject
{
if (contextObject == null)
throw new ArgumentNullException("contextObject");
var type = typeof(T);
if (!type.IsInterface)
throw new InvalidOperationException(string.Format("Passed in type '{0}' is not an interface.", type));
if (!(contextObject is T))
throw new InvalidOperationException(string.Format("'{0}' is not of passed in type '{1}'.", contextObject.GetType(), type));
m_ContextObjects[typeof(T)] = contextObject;
}
/// <inheritdoc />
public void SetContextObject(Type type, IContextObject contextObject)
{
if (contextObject == null)
throw new ArgumentNullException("contextObject");
if (!type.IsInterface)
throw new InvalidOperationException(string.Format("Passed in type '{0}' is not an interface.", type));
if (!type.IsInstanceOfType(contextObject))
throw new InvalidOperationException(string.Format("'{0}' is not of passed in type '{1}'.", contextObject.GetType(), type));
m_ContextObjects[type] = contextObject;
}
private IEnumerable<Type> WalkAssignableTypes(IContextObject contextObject)
{
var iCType = typeof(IContextObject);
foreach (Type t in contextObject.GetType().GetInterfaces())
{
if (iCType.IsAssignableFrom(t) && t != iCType)
yield return t;
}
for (var current = contextObject.GetType(); current != null; current = current.BaseType)
if (iCType.IsAssignableFrom(current) && current != iCType)
yield return current;
}
/// <inheritdoc />
public void SetContextObject(IContextObject contextObject)
{
if (contextObject == null)
throw new ArgumentNullException("contextObject");
List<Type> types = new List<Type>(WalkAssignableTypes(contextObject));
if (types.Count == 0)
throw new Exception($"Could not determine context object type for object of type {contextObject.GetType().FullName}");
types.ForEach(x => m_ContextObjects[x] = contextObject);
}
/// <inheritdoc />
public bool ContainsContextObject<T>() where T : IContextObject
{
return ContainsContextObject(typeof(T));
}
/// <inheritdoc />
public bool ContainsContextObject(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
return m_ContextObjects.ContainsKey(type);
}
/// <inheritdoc />
public T GetContextObject<T>() where T : IContextObject
{
return (T)GetContextObject(typeof(T));
}
/// <inheritdoc />
public IContextObject GetContextObject(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
if (!m_ContextObjects.ContainsKey(type))
throw new Exception($"Object of Type {type} was not available within the BuildContext");
return m_ContextObjects[type];
}
/// <inheritdoc />
public bool TryGetContextObject<T>(out T contextObject) where T : IContextObject
{
IContextObject cachedContextObject;
if (m_ContextObjects.TryGetValue(typeof(T), out cachedContextObject) && cachedContextObject is T)
{
contextObject = (T)cachedContextObject;
return true;
}
contextObject = default(T);
return false;
}
/// <inheritdoc />
public bool TryGetContextObject(Type type, out IContextObject contextObject)
{
IContextObject cachedContextObject;
if (m_ContextObjects.TryGetValue(type, out cachedContextObject) && type.IsInstanceOfType(cachedContextObject))
{
contextObject = cachedContextObject;
return true;
}
contextObject = null;
return false;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fdd470c95bb040bdb705dc362b132291
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEngine;
namespace UnityEditor.Build.Pipeline
{
/// <summary>
/// Basic implementation of IDependencyData. Stores the dependency and usage data calculated during a build.
/// <seealso cref="IDependencyData"/>
/// </summary>
[Serializable]
public class BuildDependencyData : IDependencyData
{
/// <inheritdoc />
public Dictionary<GUID, AssetLoadInfo> AssetInfo { get; private set; }
/// <inheritdoc />
public Dictionary<GUID, BuildUsageTagSet> AssetUsage { get; private set; }
/// <inheritdoc />
public Dictionary<GUID, SceneDependencyInfo> SceneInfo { get; private set; }
/// <inheritdoc />
public Dictionary<GUID, BuildUsageTagSet> SceneUsage { get; private set; }
/// <inheritdoc />
public Dictionary<GUID, Hash128> DependencyHash { get; private set; }
/// <summary>
/// Stores how lighting information is being used during a build.
/// </summary>
public BuildUsageTagGlobal GlobalUsage { get; set; }
[NonSerialized]
BuildUsageCache m_BuildUsageCache;
/// <summary>
/// Stores the dependency caching object.
/// </summary>
public BuildUsageCache DependencyUsageCache
{
get
{
if (m_BuildUsageCache == null)
m_BuildUsageCache = new BuildUsageCache();
return m_BuildUsageCache;
}
}
/// <summary>
/// Default constructor, initializes properties to defaults
/// </summary>
public BuildDependencyData()
{
AssetInfo = new Dictionary<GUID, AssetLoadInfo>();
AssetUsage = new Dictionary<GUID, BuildUsageTagSet>();
SceneInfo = new Dictionary<GUID, SceneDependencyInfo>();
SceneUsage = new Dictionary<GUID, BuildUsageTagSet>();
DependencyHash = new Dictionary<GUID, Hash128>();
m_BuildUsageCache = new BuildUsageCache();
GlobalUsage = GraphicsSettingsApi.GetGlobalUsage();
}
}
/// <summary>
/// Basic implementation of IObjectDependencyData. Stores the dependencies between objects calculated during a build.
/// <seealso cref="IObjectDependencyData"/>
/// </summary>
[Serializable]
internal class ObjectDependencyData : IObjectDependencyData
{
/// <inheritdoc />
public Dictionary<ObjectIdentifier, List<ObjectIdentifier>> ObjectDependencyMap { get; }
/// <summary>
/// Default constructor, initializes properties to defaults
/// </summary>
public ObjectDependencyData()
{
ObjectDependencyMap = new Dictionary<ObjectIdentifier, List<ObjectIdentifier>>();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 48594b0afbd3e84448546c74bf35a6b3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using UnityEditor.Build.Pipeline.Interfaces;
namespace UnityEditor.Build.Pipeline
{
/// <summary>
/// Basic implementation of IBuildExtendedAssetData. Stores the extended data about an asset in the build.
/// <seealso cref="IBuildExtendedAssetData"/>
/// </summary>
[Serializable]
public class BuildExtendedAssetData : IBuildExtendedAssetData
{
/// <inheritdoc />
public Dictionary<GUID, ExtendedAssetData> ExtendedData { get; private set; }
/// <summary>
/// Default constructor, initializes properties to defaults
/// </summary>
public BuildExtendedAssetData()
{
ExtendedData = new Dictionary<GUID, ExtendedAssetData>();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b0e503817aafa447ac3b7d7db84ccc8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,189 @@
using System;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEditor.Build.Player;
using UnityEngine;
namespace UnityEditor.Build.Pipeline
{
#if UNITY_2018_3_OR_NEWER
using BuildCompression = UnityEngine.BuildCompression;
#else
using BuildCompression = UnityEditor.Build.Content.BuildCompression;
#endif
/// <summary>
/// Basic implementation of IBuildParameters. Stores the set of parameters passed into the Scriptable Build Pipeline.
/// <seealso cref="IBuildParameters"/>
/// </summary>
[Serializable]
public class BuildParameters : IBuildParameters
{
/// <inheritdoc />
public BuildTarget Target { get; set; }
/// <inheritdoc />
public BuildTargetGroup Group { get; set; }
/// <inheritdoc />
public ContentBuildFlags ContentBuildFlags { get; set; }
/// <inheritdoc />
public TypeDB ScriptInfo { get; set; }
/// <inheritdoc />
public ScriptCompilationOptions ScriptOptions { get; set; }
/// <summary>
/// Default compression option to use for all built content files
/// </summary>
public BuildCompression BundleCompression { get; set; }
/// <summary>
/// Final output location where built content will be written.
/// </summary>
public string OutputFolder { get; set; }
string m_TempOutputFolder;
/// <inheritdoc />
public string TempOutputFolder
{
get { return m_TempOutputFolder; }
set
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException("Argument cannot be null or empty.", "value");
m_TempOutputFolder = value;
}
}
string m_ScriptOutputFolder;
/// <inheritdoc />
public string ScriptOutputFolder
{
get { return m_ScriptOutputFolder; }
set
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException("Argument cannot be null or empty.", "value");
m_ScriptOutputFolder = value;
}
}
/// <inheritdoc />
public bool UseCache { get; set; }
/// <inheritdoc />
public string CacheServerHost { get; set; }
/// <inheritdoc />
public int CacheServerPort { get; set; }
/// <inheritdoc />
public bool WriteLinkXML { get; set; }
#if NONRECURSIVE_DEPENDENCY_DATA
/// <inheritdoc />
public bool NonRecursiveDependencies { get; set; }
#endif
internal BuildParameters() {}
/// <summary>
/// Default constructor, requires the target, group and output parameters at minimum for a successful build.
/// </summary>
/// <param name="target">The target for building content.</param>
/// <param name="group">The group for building content.</param>
/// <param name="outputFolder">The final output location for built content.</param>
public BuildParameters(BuildTarget target, BuildTargetGroup group, string outputFolder)
{
if (string.IsNullOrEmpty(outputFolder))
throw new ArgumentException("Argument cannot be null or empty.", "outputFolder");
Target = target;
Group = group;
// TODO: Validate target & group
ScriptInfo = null;
ScriptOptions = ScriptCompilationOptions.None;
#if UNITY_2018_3_OR_NEWER
BundleCompression = BuildCompression.LZMA;
#else
BundleCompression = BuildCompression.DefaultLZMA;
#endif
OutputFolder = outputFolder;
TempOutputFolder = ContentPipeline.kTempBuildPath;
ScriptOutputFolder = ContentPipeline.kScriptBuildPath;
UseCache = true;
CacheServerPort = 8126;
if (ScriptableBuildPipeline.UseBuildCacheServer)
{
CacheServerHost = ScriptableBuildPipeline.CacheServerHost;
CacheServerPort = ScriptableBuildPipeline.CacheServerPort;
}
WriteLinkXML = false;
#if NONRECURSIVE_DEPENDENCY_DATA && UNITY_2021_1_OR_NEWER
NonRecursiveDependencies = true;
#endif
}
/// <inheritdoc />
public virtual BuildSettings GetContentBuildSettings()
{
return new BuildSettings
{
group = Group,
target = Target,
typeDB = ScriptInfo,
buildFlags = ContentBuildFlags
};
}
/// <inheritdoc />
public virtual ScriptCompilationSettings GetScriptCompilationSettings()
{
return new ScriptCompilationSettings
{
group = Group,
target = Target,
options = ScriptOptions
};
}
/// <inheritdoc />
public virtual string GetOutputFilePathForIdentifier(string identifier)
{
return string.Format("{0}/{1}", OutputFolder, identifier);
}
/// <inheritdoc />
public virtual BuildCompression GetCompressionForIdentifier(string identifier)
{
return BundleCompression;
}
}
/// <summary>
/// Stores the set of parameters passed into Scriptable Build Pipeline when building bundles.
/// </summary>
[Serializable]
public class BundleBuildParameters : BuildParameters, IBundleBuildParameters
{
internal BundleBuildParameters() {}
/// <inheritdoc />
public BundleBuildParameters(BuildTarget target, BuildTargetGroup group, string outputFolder)
: base(target, group, outputFolder)
{
#if UNITY_2021_1_OR_NEWER
ContiguousBundles = true;
#endif
}
/// <inheritdoc />
public bool AppendHash { get; set; }
/// <inheritdoc />
public bool ContiguousBundles { get; set; }
/// <inheritdoc />
public bool DisableVisibleSubAssetRepresentations { get; set; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 33d6ef15c1984642bd83826064bee25d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Player;
using UnityEngine;
using UnityEngine.Build.Pipeline;
namespace UnityEditor.Build.Pipeline
{
/// <summary>
/// Additional MetaData that is associated with a serialized file write result
/// <seealso cref="IBuildResults"/>
/// </summary>
[Serializable]
public class SerializedFileMetaData
{
/// <summary>
/// A hash of all the serialized files
/// </summary>
public Hash128 RawFileHash;
/// <summary>
/// Hash of file contents. Some resource files may choose to exclude sections of their content from this hash. For example,
/// serialized files exclude the header of their content which allows this hash not to change with new Unity versions.
/// </summary>
public Hash128 ContentHash;
}
/// <summary>
/// Basic implementation of IBuildResults. Stores the results for script compilation and content building.
/// <seealso cref="IBuildResults"/>
/// </summary>
[Serializable]
public class BuildResults : IBuildResults
{
/// <inheritdoc />
public ScriptCompilationResult ScriptResults { get; set; }
/// <inheritdoc />
public Dictionary<string, WriteResult> WriteResults { get; private set; }
/// <inheritdoc />
public Dictionary<string, SerializedFileMetaData> WriteResultsMetaData { get; private set; }
/// <inheritdoc />
public Dictionary<GUID, AssetResultData> AssetResults { get; private set; }
/// <summary>
/// Default constructor, initializes properties to defaults
/// </summary>
public BuildResults()
{
WriteResults = new Dictionary<string, WriteResult>();
}
}
/// <summary>
/// Basic implementation of IBundleBuildResults. Stores the results for script compilation and asset bundle building.
/// <seealso cref="IBundleBuildResults"/>
/// </summary>
[Serializable]
public class BundleBuildResults : IBundleBuildResults
{
/// <inheritdoc />
public ScriptCompilationResult ScriptResults { get; set; }
/// <inheritdoc />
public Dictionary<string, BundleDetails> BundleInfos { get; private set; }
/// <inheritdoc />
public Dictionary<string, WriteResult> WriteResults { get; private set; }
/// <inheritdoc />
public Dictionary<string, SerializedFileMetaData> WriteResultsMetaData { get; private set; }
/// <inheritdoc />
public Dictionary<GUID, AssetResultData> AssetResults { get; private set; }
/// <summary>
/// Default constructor, initializes properties to defaults
/// </summary>
public BundleBuildResults()
{
BundleInfos = new Dictionary<string, BundleDetails>();
WriteResults = new Dictionary<string, WriteResult>();
WriteResultsMetaData = new Dictionary<string, SerializedFileMetaData>();
AssetResults = new Dictionary<GUID, AssetResultData>();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 63ad63bf47749b944a87fce982b8b54b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using UnityEditor.Build.Pipeline.Interfaces;
namespace UnityEditor.Build.Pipeline
{
/// <summary>
/// Basic implementation of IBuildSpriteData. Stores the sprite importer data for a sprite asset in the build.
/// <seealso cref="IBuildSpriteData"/>
/// </summary>
[Serializable]
public class BuildSpriteData : IBuildSpriteData
{
/// <inheritdoc />
public Dictionary<GUID, SpriteImporterData> ImporterData { get; set; }
/// <summary>
/// Default constructor, initializes properties to defaults
/// </summary>
public BuildSpriteData()
{
ImporterData = new Dictionary<GUID, SpriteImporterData>();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 54aaf7e8e1f02d34182d881089f11f33
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,186 @@
using System;
using System.Collections.Generic;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEditor.Build.Profiler;
using UnityEditor.Build.Reporting;
namespace UnityEditor.Build.Pipeline
{
/// <summary>
/// Basic static class containing default implementations for BuildTask validation and running.
/// </summary>
public static class BuildTasksRunner
{
/// <summary>
/// Basic run implementation that takes a set of tasks, a context, and runs returning the build results.
/// <seealso cref="IBuildTask"/>, <seealso cref="IBuildContext"/>, and <seealso cref="ReturnCode"/>
/// </summary>
/// <param name="pipeline">The set of build tasks to run.</param>
/// <param name="context">The build context to use for this run.</param>
/// <returns>Return code with status information about success or failure causes.</returns>
public static ReturnCode Run(IList<IBuildTask> pipeline, IBuildContext context)
{
// Avoid throwing exceptions in here as we don't want them bubbling up to calling user code
if (pipeline == null)
{
BuildLogger.LogException(new ArgumentNullException("pipeline"));
return ReturnCode.Exception;
}
// Avoid throwing exceptions in here as we don't want them bubbling up to calling user code
if (context == null)
{
BuildLogger.LogException(new ArgumentNullException("context"));
return ReturnCode.Exception;
}
IProgressTracker tracker;
if (context.TryGetContextObject(out tracker))
tracker.TaskCount = pipeline.Count;
context.TryGetContextObject(out IBuildLogger logger);
foreach (IBuildTask task in pipeline)
{
{
try
{
if (!tracker.UpdateTaskUnchecked(task.GetType().Name.HumanReadable()))
return ReturnCode.Canceled;
ContextInjector.Inject(context, task);
ReturnCode result;
using (logger.ScopedStep(LogLevel.Info, task.GetType().Name))
result = task.Run();
if (result < ReturnCode.Success)
return result;
ContextInjector.Extract(context, task);
}
catch (Exception e)
{
BuildLogger.LogError("Build Task {0} failed with exception:\n{1}\n{2}", task.GetType().Name, e.Message, e.StackTrace);
return ReturnCode.Exception;
}
}
}
if (tracker is IDisposable disposable)
disposable.Dispose();
return ReturnCode.Success;
}
/// <summary>
/// Run implementation with task profiler that takes a set of tasks, a context, runs returning the build results and prints out the profiler details.
/// <seealso cref="IBuildTask"/>, <seealso cref="IBuildContext"/>, and <seealso cref="ReturnCode"/>
/// </summary>
/// <param name="pipeline">The set of build tasks to run.</param>
/// <param name="context">The build context to use for this run.</param>
/// <returns>Return code with status information about success or failure causes.</returns>
internal static ReturnCode RunProfiled(IList<IBuildTask> pipeline, IBuildContext context)
{
// Avoid throwing exceptions in here as we don't want them bubbling up to calling user code
if (pipeline == null)
{
BuildLogger.LogException(new ArgumentNullException("pipeline"));
return ReturnCode.Exception;
}
// Avoid throwing exceptions in here as we don't want them bubbling up to calling user code
if (context == null)
{
BuildLogger.LogException(new ArgumentNullException("context"));
return ReturnCode.Exception;
}
var profiler = new BuildProfiler(pipeline.Count + 1);
profiler.Start(pipeline.Count, "TotalTime");
int count = 0;
IProgressTracker tracker;
if (context.TryGetContextObject(out tracker))
tracker.TaskCount = pipeline.Count;
foreach (IBuildTask task in pipeline)
{
try
{
if (!tracker.UpdateTaskUnchecked(task.GetType().Name.HumanReadable()))
{
profiler.Stop(pipeline.Count);
profiler.Print();
return ReturnCode.Canceled;
}
ContextInjector.Inject(context, task);
profiler.Start(count, task.GetType().Name);
var result = task.Run();
profiler.Stop(count++);
if (result < ReturnCode.Success)
{
profiler.Stop(pipeline.Count);
profiler.Print();
return result;
}
ContextInjector.Extract(context, task);
}
catch (Exception e)
{
BuildLogger.LogException(e);
profiler.Stop(count);
profiler.Print();
return ReturnCode.Exception;
}
}
profiler.Stop(pipeline.Count);
profiler.Print();
return ReturnCode.Success;
}
/// <summary>
/// Basic validate implementation that takes a set of tasks, a context, and does checks to ensure the task requirements are all satisfied.
/// <seealso cref="IBuildTask"/>, <seealso cref="IBuildContext"/>, and <seealso cref="ReturnCode"/>
/// </summary>
/// <param name="pipeline">The set of build tasks to run.</param>
/// <param name="context">The build context to use for this run.</param>
/// <returns>Return code with status information about success or failure causes.</returns>
public static ReturnCode Validate(IList<IBuildTask> pipeline, IBuildContext context)
{
//// Avoid throwing exceptions in here as we don't want them bubbling up to calling user code
//if (pipeline == null)
//{
// BuildLogger.LogException(new ArgumentNullException("pipeline"));
// return ReturnCode.Exception;
//}
//// Avoid throwing exceptions in here as we don't want them bubbling up to calling user code
//if (context == null)
//{
// BuildLogger.LogException(new ArgumentNullException("context"));
// return ReturnCode.Exception;
//}
//var requiredTypes = new HashSet<Type>();
//foreach (IBuildTask task in pipeline)
// requiredTypes.UnionWith(task.RequiredContextTypes);
//var missingTypes = new List<string>();
//foreach (Type requiredType in requiredTypes)
//{
// if (!context.ContainsContextObject(requiredType))
// missingTypes.Add(requiredType.Name);
//}
//if (missingTypes.Count > 0)
//{
// BuildLogger.LogError("Missing required object types to run build pipeline:\n{0}", string.Join(", ", missingTypes.ToArray()));
// return ReturnCode.MissingRequiredObjects;
//}
return ReturnCode.Success;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cba3bf5fbe905c841b9ace7d603fb1d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Interfaces;
namespace UnityEditor.Build.Pipeline
{
/// <summary>
/// Basic implementation of IWriteData. Stores the write information calculated during a build.
/// <seealso cref="IWriteData"/>
/// </summary>
[Serializable]
public class BuildWriteData : IWriteData
{
/// <inheritdoc />
public Dictionary<GUID, List<string>> AssetToFiles { get; private set; }
/// <inheritdoc />
public Dictionary<string, List<ObjectIdentifier>> FileToObjects { get; private set; }
/// <inheritdoc />
public List<IWriteOperation> WriteOperations { get; private set; }
/// <summary>
/// Default constructor, initializes properties to defaults
/// </summary>
public BuildWriteData()
{
AssetToFiles = new Dictionary<GUID, List<string>>();
FileToObjects = new Dictionary<string, List<ObjectIdentifier>>();
WriteOperations = new List<IWriteOperation>();
}
}
/// <summary>
/// Basic implementation of IBundleWriteData. Stores the asset bundle write information calculated during a build.
/// <seealso cref="IBundleWriteData"/>
/// </summary>
[Serializable]
public class BundleWriteData : IBundleWriteData
{
/// <inheritdoc />
public Dictionary<GUID, List<string>> AssetToFiles { get; private set; }
/// <inheritdoc />
public Dictionary<string, List<ObjectIdentifier>> FileToObjects { get; private set; }
/// <inheritdoc />
public Dictionary<string, string> FileToBundle { get; private set; }
/// <inheritdoc />
public Dictionary<string, BuildUsageTagSet> FileToUsageSet { get; private set; }
/// <inheritdoc />
public Dictionary<string, BuildReferenceMap> FileToReferenceMap { get; private set; }
/// <inheritdoc />
public List<IWriteOperation> WriteOperations { get; private set; }
/// <summary>
/// Default constructor, initializes properties to defaults
/// </summary>
public BundleWriteData()
{
AssetToFiles = new Dictionary<GUID, List<string>>();
FileToObjects = new Dictionary<string, List<ObjectIdentifier>>();
FileToBundle = new Dictionary<string, string>();
FileToUsageSet = new Dictionary<string, BuildUsageTagSet>();
FileToReferenceMap = new Dictionary<string, BuildReferenceMap>();
WriteOperations = new List<IWriteOperation>();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2b783d170d8ed6b468a9500bbe040087
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Interfaces;
namespace UnityEditor.Build.Pipeline
{
/// <summary>
/// Optional context object used for overriding the location where specific objects will be serialized
/// </summary>
[Serializable]
public class BundleExplictObjectLayout : IBundleExplictObjectLayout
{
/// <inheritdoc />
public Dictionary<ObjectIdentifier, string> ExplicitObjectLocation { get; set; }
/// <summary>
/// Default constructor, initializes properties to defaults
/// </summary>
public BundleExplictObjectLayout()
{
ExplicitObjectLocation = new Dictionary<ObjectIdentifier, string>();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d723a770823401649b007044d9a978ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Tasks;
namespace UnityEditor.Build.Pipeline
{
/// <summary>
/// Basic static class containing preset build pipeline task collections.
/// </summary>
public static class DefaultBuildTasks
{
/// <summary>
/// Options for different preset build pipelines
/// </summary>
public enum Preset
{
/// <summary>
/// Use to indicate that the pipeline only executes player scripts.
/// </summary>
PlayerScriptsOnly,
/// <summary>
/// Use to indicate that the pipeline should create asset bundles.
/// </summary>
AssetBundleCompatible,
/// <summary>
/// Use to indicate that the pipeline should create asset bundles and the built-in shader bundle.
/// </summary>
AssetBundleBuiltInShaderExtraction,
/// <summary>
/// Use to indicate that the pipeline should create asset bundles, the built-in shader bundle, and MonoScript bundle.
/// </summary>
AssetBundleShaderAndScriptExtraction,
}
/// <summary>
/// Constructs and returns an IList containing the build tasks in the correct order for the preset build pipeline.
/// </summary>
/// <param name="preset">The preset build pipeline to construct and return.</param>
/// <returns>IList containing the build tasks in the correct order for the preset build pipeline.</returns>
public static IList<IBuildTask> Create(Preset preset)
{
switch (preset)
{
case Preset.PlayerScriptsOnly:
return PlayerScriptsOnly();
case Preset.AssetBundleCompatible:
return AssetBundleCompatible(false, false);
case Preset.AssetBundleBuiltInShaderExtraction:
return AssetBundleCompatible(true, false);
case Preset.AssetBundleShaderAndScriptExtraction:
return AssetBundleCompatible(true, true);
default:
throw new NotImplementedException(string.Format("Preset for '{0}' not yet implemented.", preset));
}
}
static IList<IBuildTask> PlayerScriptsOnly()
{
var buildTasks = new List<IBuildTask>();
// Setup
buildTasks.Add(new SwitchToBuildPlatform());
// Player Scripts
buildTasks.Add(new BuildPlayerScripts());
buildTasks.Add(new PostScriptsCallback());
// Dependency
// - Empty
// Packing
// - Empty
// Writing
// - Empty
return buildTasks;
}
static IList<IBuildTask> AssetBundleCompatible(bool shaderTask, bool monoscriptTask)
{
var buildTasks = new List<IBuildTask>();
// Setup
buildTasks.Add(new SwitchToBuildPlatform());
buildTasks.Add(new RebuildSpriteAtlasCache());
// Player Scripts
buildTasks.Add(new BuildPlayerScripts());
buildTasks.Add(new PostScriptsCallback());
// Dependency
buildTasks.Add(new CalculateSceneDependencyData());
#if UNITY_2019_3_OR_NEWER
buildTasks.Add(new CalculateCustomDependencyData());
#endif
buildTasks.Add(new CalculateAssetDependencyData());
buildTasks.Add(new StripUnusedSpriteSources());
if (shaderTask)
buildTasks.Add(new CreateBuiltInShadersBundle("UnityBuiltInShaders.bundle"));
if (monoscriptTask)
buildTasks.Add(new CreateMonoScriptBundle("UnityMonoScripts.bundle"));
buildTasks.Add(new PostDependencyCallback());
// Packing
buildTasks.Add(new GenerateBundlePacking());
if (shaderTask || monoscriptTask)
buildTasks.Add(new UpdateBundleObjectLayout());
buildTasks.Add(new GenerateBundleCommands());
buildTasks.Add(new GenerateSubAssetPathMaps());
buildTasks.Add(new GenerateBundleMaps());
buildTasks.Add(new PostPackingCallback());
// Writing
buildTasks.Add(new WriteSerializedFiles());
buildTasks.Add(new ArchiveAndCompressBundles());
buildTasks.Add(new AppendBundleHash());
buildTasks.Add(new GenerateLinkXml());
buildTasks.Add(new PostWritingCallback());
return buildTasks;
}
#if UNITY_2022_2_OR_NEWER
public static IList<IBuildTask> ContentFileCompatible()
{
var buildTasks = new List<IBuildTask>();
// Setup
buildTasks.Add(new SwitchToBuildPlatform());
buildTasks.Add(new RebuildSpriteAtlasCache());
// Player Scripts
buildTasks.Add(new BuildPlayerScripts());
buildTasks.Add(new PostScriptsCallback());
// Dependency
buildTasks.Add(new CalculateSceneDependencyData());
buildTasks.Add(new CalculateCustomDependencyData());
buildTasks.Add(new CalculateAssetDependencyData());
buildTasks.Add(new StripUnusedSpriteSources());
buildTasks.Add(new PostDependencyCallback());
// Packing
buildTasks.Add(new ClusterBuildLayout());
buildTasks.Add(new PostPackingCallback());
// Writing
buildTasks.Add(new WriteSerializedFiles());
buildTasks.Add(new ArchiveAndCompressBundles());
buildTasks.Add(new GenerateLinkXml());
buildTasks.Add(new PostWritingCallback());
return buildTasks;
}
#endif
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 552d6f72e940a1c4a809b635a8f40d8b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,39 @@
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
namespace UnityEditor.Build.Pipeline
{
/// <summary>
/// Generates identifiers linearly for built content. Only deterministic if object order and initial Index is deterministic.
/// </summary>
public class LinearPackedIdentifiers : IDeterministicIdentifiers
{
/// <summary>
/// The index at which to start linear id assignment.
/// </summary>
public long Index { get; set; }
/// <summary>
/// Default constructor, takes an initial index at which to start linear id assignment.
/// </summary>
/// <param name="index">Initial index at which to start linear id assignment.</param>
public LinearPackedIdentifiers(long index)
{
Index = index;
}
/// <inheritdoc />
public virtual string GenerateInternalFileName(string name)
{
var hash = HashingMethods.Calculate(name).ToString();
return string.Format("CAB-{0}", hash);
}
/// <inheritdoc />
public virtual long SerializationIndexFromObjectIdentifier(ObjectIdentifier objectID)
{
return Index++;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f004933c56f4b2f4792fdcc1aca8f9b7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,65 @@
using System;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
namespace UnityEditor.Build.Pipeline
{
/// <summary>
/// Generates a deterministic identifier using a MD5 hash algorithm and does not require object ordering to be deterministic.
/// This algorithm ensures objects coming from the same asset are packed closer together and can improve loading performance under certain situations.
/// </summary>
public class PrefabPackedIdentifiers : IDeterministicIdentifiers
{
/// <inheritdoc />
public virtual string GenerateInternalFileName(string name)
{
return "CAB-" + HashingMethods.Calculate(name);
}
/// <inheritdoc />
public virtual long SerializationIndexFromObjectIdentifier(ObjectIdentifier objectID)
{
byte[] assetHash;
byte[] objectHash;
bool extraArtifact = objectID.filePath.StartsWith("VirtualArtifacts/Extra/", StringComparison.Ordinal);
int hashSeed = ScriptableBuildPipeline.fileIDHashSeed;
if (extraArtifact && hashSeed != 0)
{
RawHash fileHash = HashingMethods.CalculateFile(objectID.filePath);
assetHash = HashingMethods.Calculate(hashSeed, fileHash).ToBytes();
objectHash = HashingMethods.Calculate(hashSeed, fileHash, objectID.localIdentifierInFile).ToBytes();
}
else if (extraArtifact)
{
RawHash fileHash = HashingMethods.CalculateFile(objectID.filePath);
assetHash = fileHash.ToBytes();
objectHash = HashingMethods.Calculate(fileHash, objectID.localIdentifierInFile).ToBytes();
}
else if (hashSeed != 0)
{
assetHash = HashingMethods.Calculate(hashSeed, objectID.guid, objectID.filePath).ToBytes();
objectHash = HashingMethods.Calculate(hashSeed, objectID).ToBytes();
}
else
{
assetHash = HashingMethods.Calculate(objectID.guid, objectID.filePath).ToBytes();
objectHash = HashingMethods.Calculate(objectID).ToBytes();
}
int headerSize = ScriptableBuildPipeline.prefabPackedHeaderSize;
if (headerSize < 4)
{
for (int i = 0; i < headerSize; i++)
objectHash[i] = assetHash[i];
return BitConverter.ToInt64(objectHash, 0);
}
else
{
var assetVal = BitConverter.ToUInt64(assetHash, 0);
var objectVal = BitConverter.ToUInt64(objectHash, 0);
return (long)((0xFFFFFFFF00000000 & assetVal) | (0x00000000FFFFFFFF & (objectVal ^ assetVal)));
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5046b687c98667141a9a4f0add3345be
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,54 @@
using System;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
namespace UnityEditor.Build.Pipeline
{
/// <summary>
/// Generates a deterministic identifier using a MD4 hash algorithm and does not require object ordering to be deterministic.
/// This algorithm generates identical results to what is used internally in <c>BuildPipeline.BuildAssetbundles</c>.
/// </summary>
public class Unity5PackedIdentifiers : IDeterministicIdentifiers
{
/// <inheritdoc />
public virtual string GenerateInternalFileName(string name)
{
return "CAB-" + HashingMethods.Calculate<MD4>(name);
}
/// <inheritdoc />
public virtual long SerializationIndexFromObjectIdentifier(ObjectIdentifier objectID)
{
RawHash hash;
bool extraArtifact = objectID.filePath.StartsWith("VirtualArtifacts/Extra/", StringComparison.Ordinal);
int hashSeed = ScriptableBuildPipeline.fileIDHashSeed;
if (extraArtifact && hashSeed != 0)
{
RawHash fileHash = HashingMethods.CalculateFile(objectID.filePath);
hash = HashingMethods.Calculate(hashSeed, fileHash, objectID.localIdentifierInFile);
}
else if (extraArtifact)
{
RawHash fileHash = HashingMethods.CalculateFile(objectID.filePath);
hash = HashingMethods.Calculate(fileHash, objectID.localIdentifierInFile);
}
else if (hashSeed != 0)
{
if (objectID.fileType == FileType.MetaAssetType || objectID.fileType == FileType.SerializedAssetType)
hash = HashingMethods.Calculate<MD4>(hashSeed, objectID.guid.ToString(), objectID.fileType, objectID.localIdentifierInFile);
else
hash = HashingMethods.Calculate<MD4>(hashSeed, objectID.filePath, objectID.localIdentifierInFile);
}
else
{
if (objectID.fileType == FileType.MetaAssetType || objectID.fileType == FileType.SerializedAssetType)
hash = HashingMethods.Calculate<MD4>(objectID.guid.ToString(), objectID.fileType, objectID.localIdentifierInFile);
else
hash = HashingMethods.Calculate<MD4>(objectID.filePath, objectID.localIdentifierInFile);
}
return BitConverter.ToInt64(hash.ToBytes(), 0);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b8ee6f8e7df445bfb88fb0372dcf691a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d3f1cef9a16d4a21826a13742ee5ea01
timeCreated: 1513096288

View file

@ -0,0 +1,46 @@
using System.IO;
using System.Linq;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Append a hash to each bundle name.
/// </summary>
public class AppendBundleHash : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IBundleBuildParameters m_Parameters;
[InjectContext]
IBundleBuildResults m_Results;
#pragma warning restore 649
/// <inheritdoc />
public ReturnCode Run()
{
if (!m_Parameters.AppendHash)
return ReturnCode.SuccessNotRun;
string[] bundles = m_Results.BundleInfos.Keys.ToArray();
foreach (string bundle in bundles)
{
var details = m_Results.BundleInfos[bundle];
var oldFileName = details.FileName;
var newFileName = string.Format("{0}_{1}", details.FileName, details.Hash.ToString());
details.FileName = newFileName;
m_Results.BundleInfos[bundle] = details;
File.Delete(newFileName);
File.Move(oldFileName, newFileName);
}
return ReturnCode.Success;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f4af5ab96cd890342912b92e54ac2c3e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,442 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using UnityEditor.Build.Content;
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEngine;
using UnityEngine.Build.Pipeline;
namespace UnityEditor.Build.Pipeline.Tasks
{
#if UNITY_2018_3_OR_NEWER
using BuildCompression = UnityEngine.BuildCompression;
#else
using BuildCompression = UnityEditor.Build.Content.BuildCompression;
#endif
/// <summary>
/// Archives and compresses all asset bundles.
/// </summary>
public class ArchiveAndCompressBundles : IBuildTask
{
private const int kVersion = 2;
/// <inheritdoc />
public int Version { get { return kVersion; } }
#pragma warning disable 649
[InjectContext(ContextUsage.In)]
IBuildParameters m_Parameters;
[InjectContext(ContextUsage.In)]
IBundleWriteData m_WriteData;
#if UNITY_2019_3_OR_NEWER
[InjectContext(ContextUsage.In)]
IBundleBuildContent m_Content;
#endif
[InjectContext]
IBundleBuildResults m_Results;
[InjectContext(ContextUsage.In, true)]
IProgressTracker m_Tracker;
[InjectContext(ContextUsage.In, true)]
IBuildCache m_Cache;
[InjectContext(ContextUsage.In, true)]
IBuildLogger m_Log;
#pragma warning restore 649
internal static void CopyFileWithTimestampIfDifferent(string srcPath, string destPath, IBuildLogger log)
{
if (srcPath == destPath)
return;
srcPath = Path.GetFullPath(srcPath);
destPath = Path.GetFullPath(destPath);
#if UNITY_EDITOR_WIN
// Max path length per MS Path code.
const int MaxPath = 260;
if (srcPath.Length > MaxPath)
throw new PathTooLongException(srcPath);
if (destPath.Length > MaxPath)
throw new PathTooLongException(destPath);
#endif
DateTime time = File.GetLastWriteTime(srcPath);
DateTime destTime = File.Exists(destPath) ? File.GetLastWriteTime(destPath) : new DateTime();
if (destTime == time)
return;
using (log.ScopedStep(LogLevel.Verbose, "Copying From Cache", $"{srcPath} -> {destPath}"))
{
var directory = Path.GetDirectoryName(destPath);
Directory.CreateDirectory(directory);
File.Copy(srcPath, destPath, true);
}
}
static CacheEntry GetCacheEntry(IBuildCache cache, string bundleName, IEnumerable<ResourceFile> resources, BuildCompression compression, List<SerializedFileMetaData> hashes)
{
var entry = new CacheEntry();
entry.Type = CacheEntry.EntryType.Data;
entry.Guid = HashingMethods.Calculate("ArchiveAndCompressBundles", bundleName).ToGUID();
List<object> toHash = new List<object> { kVersion, compression };
foreach (var resource in resources)
{
toHash.Add(resource.serializedFile);
toHash.Add(resource.fileAlias);
}
toHash.AddRange(hashes.Select(x => (object)x.RawFileHash));
entry.Hash = HashingMethods.Calculate(toHash).ToHash128();
entry.Version = kVersion;
return entry;
}
static CachedInfo GetCachedInfo(IBuildCache cache, CacheEntry entry, IEnumerable<ResourceFile> resources, BundleDetails details)
{
var info = new CachedInfo();
info.Asset = entry;
info.Dependencies = new CacheEntry[0];
info.Data = new object[] { details };
return info;
}
internal static Hash128 CalculateHashVersion(ArchiveWorkItem item, string[] dependencies)
{
List<Hash128> hashes = new List<Hash128>();
hashes.AddRange(item.SeriliazedFileMetaDatas.Select(x => x.ContentHash));
return HashingMethods.Calculate(hashes, dependencies).ToHash128();
}
internal class ArchiveWorkItem
{
public int Index;
public string BundleName;
public string OutputFilePath;
public string CachedArtifactPath;
public List<ResourceFile> ResourceFiles;
public BuildCompression Compression;
public BundleDetails ResultDetails;
public List<SerializedFileMetaData> SeriliazedFileMetaDatas = new List<SerializedFileMetaData>();
}
internal struct TaskInput
{
public Dictionary<string, WriteResult> InternalFilenameToWriteResults;
public Dictionary<string, SerializedFileMetaData> InternalFilenameToWriteMetaData;
#if UNITY_2019_3_OR_NEWER
public Dictionary<string, List<ResourceFile>> BundleNameToAdditionalFiles;
#endif
public Dictionary<string, string> InternalFilenameToBundleName;
public Func<string, BuildCompression> GetCompressionForIdentifier;
public Func<string, string> GetOutputFilePathForIdentifier;
public IBuildCache BuildCache;
public Dictionary<GUID, List<string>> AssetToFilesDependencies;
public IProgressTracker ProgressTracker;
public string TempOutputFolder;
public bool Threaded;
public List<string> OutCachedBundles;
public IBuildLogger Log;
public bool StripUnityVersion;
}
internal struct TaskOutput
{
public Dictionary<string, BundleDetails> BundleDetails;
}
/// <inheritdoc />
public ReturnCode Run()
{
TaskInput input = new TaskInput();
input.InternalFilenameToWriteResults = m_Results.WriteResults;
#if UNITY_2019_3_OR_NEWER
input.BundleNameToAdditionalFiles = m_Content.AdditionalFiles;
#endif
input.InternalFilenameToBundleName = m_WriteData.FileToBundle;
input.GetCompressionForIdentifier = (x) => m_Parameters.GetCompressionForIdentifier(x);
input.GetOutputFilePathForIdentifier = (x) => m_Parameters.GetOutputFilePathForIdentifier(x);
input.BuildCache = m_Parameters.UseCache ? m_Cache : null;
input.ProgressTracker = m_Tracker;
input.TempOutputFolder = m_Parameters.TempOutputFolder;
input.AssetToFilesDependencies = m_WriteData.AssetToFiles;
input.InternalFilenameToWriteMetaData = m_Results.WriteResultsMetaData;
input.Log = m_Log;
input.StripUnityVersion = (m_Parameters.GetContentBuildSettings().buildFlags & ContentBuildFlags.StripUnityVersion) != 0;
input.Threaded = ReflectionExtensions.SupportsMultiThreadedArchiving && ScriptableBuildPipeline.threadedArchiving;
TaskOutput output;
ReturnCode code = Run(input, out output);
if (code == ReturnCode.Success)
{
foreach (var item in output.BundleDetails)
m_Results.BundleInfos.Add(item.Key, item.Value);
}
return code;
}
internal static Dictionary<string, string[]> CalculateBundleDependencies(List<List<string>> assetFileList, Dictionary<string, string> filenameToBundleName)
{
var bundleDependencies = new Dictionary<string, string[]>();
Dictionary<string, HashSet<string>> bundleDependenciesHash = new Dictionary<string, HashSet<string>>();
foreach (var files in assetFileList)
{
if (files.IsNullOrEmpty())
continue;
string bundle = filenameToBundleName[files.First()];
HashSet<string> dependencies;
bundleDependenciesHash.GetOrAdd(bundle, out dependencies);
dependencies.UnionWith(files.Select(x => filenameToBundleName[x]));
dependencies.Remove(bundle);
// Ensure we create mappings for all encountered files
foreach (var file in files)
bundleDependenciesHash.GetOrAdd(filenameToBundleName[file], out dependencies);
}
// Recursively combine dependencies
foreach (var dependencyPair in bundleDependenciesHash)
{
List<string> dependencies = dependencyPair.Value.ToList();
for (int i = 0; i < dependencies.Count; i++)
{
if (!bundleDependenciesHash.TryGetValue(dependencies[i], out var recursiveDependencies))
continue;
foreach (var recursiveDependency in recursiveDependencies)
{
if (dependencyPair.Value.Add(recursiveDependency))
dependencies.Add(recursiveDependency);
}
}
}
foreach (var dep in bundleDependenciesHash)
{
string[] ret = dep.Value.ToArray();
Array.Sort(ret);
bundleDependencies.Add(dep.Key, ret);
}
return bundleDependencies;
}
static void PostArchiveProcessing(List<ArchiveWorkItem> items, List<List<string>> assetFileList, Dictionary<string, string> filenameToBundleName, IBuildLogger log)
{
using (log.ScopedStep(LogLevel.Info, "PostArchiveProcessing"))
{
Dictionary<string, string[]> bundleDependencies = CalculateBundleDependencies(assetFileList, filenameToBundleName);
foreach (ArchiveWorkItem item in items)
{
// apply bundle dependencies
item.ResultDetails.Dependencies = bundleDependencies.ContainsKey(item.BundleName) ? bundleDependencies[item.BundleName] : new string[0];
item.ResultDetails.Hash = CalculateHashVersion(item, item.ResultDetails.Dependencies);
}
}
}
static ArchiveWorkItem GetOrCreateWorkItem(TaskInput input, string bundleName, Dictionary<string, ArchiveWorkItem> bundleToWorkItem)
{
if (!bundleToWorkItem.TryGetValue(bundleName, out ArchiveWorkItem item))
{
item = new ArchiveWorkItem();
item.BundleName = bundleName;
item.Compression = input.GetCompressionForIdentifier(bundleName);
item.OutputFilePath = input.GetOutputFilePathForIdentifier(bundleName);
item.ResourceFiles = new List<ResourceFile>();
bundleToWorkItem[bundleName] = item;
}
return item;
}
static RawHash HashResourceFiles(List<ResourceFile> files)
{
return HashingMethods.Calculate(files.Select((x) => HashingMethods.CalculateFile(x.fileName)));
}
static List<ArchiveWorkItem> CreateWorkItems(TaskInput input)
{
using (input.Log.ScopedStep(LogLevel.Info, "CreateWorkItems"))
{
Dictionary<string, ArchiveWorkItem> bundleNameToWorkItem = new Dictionary<string, ArchiveWorkItem>();
foreach (var pair in input.InternalFilenameToWriteResults)
{
string internalName = pair.Key;
string bundleName = input.InternalFilenameToBundleName[internalName];
ArchiveWorkItem item = GetOrCreateWorkItem(input, bundleName, bundleNameToWorkItem);
if (input.InternalFilenameToWriteMetaData.TryGetValue(pair.Key, out SerializedFileMetaData md))
item.SeriliazedFileMetaDatas.Add(md);
else
throw new Exception($"Archive {bundleName} with internal name {internalName} does not have associated SerializedFileMetaData");
item.ResourceFiles.AddRange(pair.Value.resourceFiles);
#if UNITY_2019_3_OR_NEWER
if (input.BundleNameToAdditionalFiles.TryGetValue(bundleName, out List<ResourceFile> additionalFiles))
{
RawHash hash = HashResourceFiles(additionalFiles);
item.SeriliazedFileMetaDatas.Add(new SerializedFileMetaData() { ContentHash = hash.ToHash128(), RawFileHash = hash.ToHash128() });
item.ResourceFiles.AddRange(additionalFiles);
}
#endif
}
List<ArchiveWorkItem> allItems = bundleNameToWorkItem.Select((x, index) => { x.Value.Index = index; return x.Value; }).ToList();
return allItems;
}
}
static internal ReturnCode Run(TaskInput input, out TaskOutput output)
{
output = new TaskOutput();
output.BundleDetails = new Dictionary<string, BundleDetails>();
List<ArchiveWorkItem> allItems = CreateWorkItems(input);
IList<CacheEntry> cacheEntries = null;
IList<CachedInfo> cachedInfo = null;
List<ArchiveWorkItem> cachedItems = new List<ArchiveWorkItem>();
List<ArchiveWorkItem> nonCachedItems = allItems;
if (input.BuildCache != null)
{
using (input.Log.ScopedStep(LogLevel.Info, "Creating Cache Entries"))
cacheEntries = allItems.Select(x => GetCacheEntry(input.BuildCache, x.BundleName, x.ResourceFiles, x.Compression, x.SeriliazedFileMetaDatas)).ToList();
using (input.Log.ScopedStep(LogLevel.Info, "Load Cached Data"))
input.BuildCache.LoadCachedData(cacheEntries, out cachedInfo);
cachedItems = allItems.Where(x => cachedInfo[x.Index] != null).ToList();
nonCachedItems = allItems.Where(x => cachedInfo[x.Index] == null).ToList();
foreach (ArchiveWorkItem i in allItems)
i.CachedArtifactPath = string.Format("{0}/{1}", input.BuildCache.GetCachedArtifactsDirectory(cacheEntries[i.Index]), HashingMethods.Calculate(i.BundleName));
}
using (input.Log.ScopedStep(LogLevel.Info, "CopyingCachedFiles"))
{
foreach (ArchiveWorkItem item in cachedItems)
{
if (!input.ProgressTracker.UpdateInfoUnchecked(string.Format("{0} (Cached)", item.BundleName)))
return ReturnCode.Canceled;
item.ResultDetails = (BundleDetails)cachedInfo[item.Index].Data[0];
item.ResultDetails.FileName = item.OutputFilePath;
CopyFileWithTimestampIfDifferent(item.CachedArtifactPath, item.ResultDetails.FileName, input.Log);
}
}
// Write all the files that aren't cached
if (!ArchiveItems(nonCachedItems, input.TempOutputFolder, input.ProgressTracker, input.Threaded, input.Log, input.StripUnityVersion))
return ReturnCode.Canceled;
PostArchiveProcessing(allItems, input.AssetToFilesDependencies.Values.ToList(), input.InternalFilenameToBundleName, input.Log);
// Put everything into the cache
if (input.BuildCache != null)
{
using (input.Log.ScopedStep(LogLevel.Info, "Copying To Cache"))
{
List<CachedInfo> uncachedInfo = nonCachedItems.Select(x => GetCachedInfo(input.BuildCache, cacheEntries[x.Index], x.ResourceFiles, x.ResultDetails)).ToList();
input.BuildCache.SaveCachedData(uncachedInfo);
}
}
output.BundleDetails = allItems.ToDictionary((x) => x.BundleName, (x) => x.ResultDetails);
if (input.OutCachedBundles != null)
input.OutCachedBundles.AddRange(cachedItems.Select(x => x.BundleName));
return ReturnCode.Success;
}
static private void ArchiveSingleItem(ArchiveWorkItem item, string tempOutputFolder, IBuildLogger log, bool stripUnityVersion)
{
using (log.ScopedStep(LogLevel.Info, "ArchiveSingleItem", item.BundleName))
{
item.ResultDetails = new BundleDetails();
string writePath = string.Format("{0}/{1}", tempOutputFolder, item.BundleName);
if (!string.IsNullOrEmpty(item.CachedArtifactPath))
writePath = item.CachedArtifactPath;
Directory.CreateDirectory(Path.GetDirectoryName(writePath));
item.ResultDetails.FileName = item.OutputFilePath;
item.ResultDetails.Crc = ContentBuildInterface.ArchiveAndCompress(item.ResourceFiles.ToArray(), writePath, item.Compression, stripUnityVersion);
CopyFileWithTimestampIfDifferent(writePath, item.ResultDetails.FileName, log);
}
}
static private bool ArchiveItems(List<ArchiveWorkItem> items, string tempOutputFolder, IProgressTracker tracker, bool threaded, IBuildLogger log, bool stripUnityVersion)
{
using (log.ScopedStep(LogLevel.Info, "ArchiveItems", threaded))
{
log?.AddEntry(LogLevel.Info, $"Archiving {items.Count} Bundles");
if (threaded)
return ArchiveItemsThreaded(items, tempOutputFolder, tracker, log, stripUnityVersion);
foreach (ArchiveWorkItem item in items)
{
if (tracker != null && !tracker.UpdateInfoUnchecked(item.BundleName))
return false;
ArchiveSingleItem(item, tempOutputFolder, log, stripUnityVersion);
}
return true;
}
}
static private bool ArchiveItemsThreaded(List<ArchiveWorkItem> items, string tempOutputFolder, IProgressTracker tracker, IBuildLogger log, bool stripUnityVersion)
{
CancellationTokenSource srcToken = new CancellationTokenSource();
SemaphoreSlim semaphore = new SemaphoreSlim(0);
List<Task> tasks = new List<Task>(items.Count);
foreach (ArchiveWorkItem item in items)
{
tasks.Add(Task.Run(() =>
{
try { ArchiveSingleItem(item, tempOutputFolder, log, stripUnityVersion); }
finally { semaphore.Release(); }
}, srcToken.Token));
}
for (int i = 0; i < items.Count; i++)
{
semaphore.Wait(srcToken.Token);
if (tracker != null && !tracker.UpdateInfoUnchecked($"Archive {i + 1}/{items.Count}"))
{
srcToken.Cancel();
break;
}
}
Task.WaitAny(Task.WhenAll(tasks));
int count = 0;
foreach (var task in tasks)
{
if (task.Exception == null)
continue;
Debug.LogException(task.Exception);
count++;
}
if (count > 0)
throw new BuildFailedException($"ArchiveAndCompressBundles encountered {count} exception(s). See console for logged exceptions.");
return !srcToken.Token.IsCancellationRequested;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b7bdc0bcfab4e714b88a12d6b22bac9b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,50 @@
using UnityEditor.Build.Pipeline.Injector;
using UnityEditor.Build.Pipeline.Utilities;
using UnityEditor.Build.Pipeline.Interfaces;
using UnityEditor.Build.Player;
using System.IO;
namespace UnityEditor.Build.Pipeline.Tasks
{
/// <summary>
/// Compiles all player scripts.
/// </summary>
public class BuildPlayerScripts : IBuildTask
{
/// <inheritdoc />
public int Version { get { return 1; } }
#pragma warning disable 649
[InjectContext]
IBuildParameters m_Parameters;
[InjectContext]
IBuildResults m_Results;
#pragma warning restore 649
/// <inheritdoc />
public ReturnCode Run()
{
if (m_Parameters.ScriptInfo != null)
{
BuildCacheUtility.SetTypeDB(m_Parameters.ScriptInfo);
return ReturnCode.SuccessNotRun;
}
// We need to ensure the directory is empty so prior results or other artifacts in this directory do not influence the build result
if (Directory.Exists(m_Parameters.ScriptOutputFolder))
{
Directory.Delete(m_Parameters.ScriptOutputFolder, true);
Directory.CreateDirectory(m_Parameters.ScriptOutputFolder);
}
m_Results.ScriptResults = PlayerBuildInterface.CompilePlayerScripts(m_Parameters.GetScriptCompilationSettings(), m_Parameters.ScriptOutputFolder);
m_Parameters.ScriptInfo = m_Results.ScriptResults.typeDB;
BuildCacheUtility.SetTypeDB(m_Parameters.ScriptInfo);
if (m_Results.ScriptResults.assemblies.IsNullOrEmpty() && m_Results.ScriptResults.typeDB == null)
return ReturnCode.Error;
return ReturnCode.Success;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c42a8e1e1d1b874e95761dab5bdfbc8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Some files were not shown because too many files have changed in this diff Show more