44using System ;
55using System . Collections . Generic ;
66using System . IO ;
7+ using UnityEditor . PackageManager ;
78using UnityEngine ;
89
910namespace Microsoft . MixedReality . Toolkit . Utilities . Editor
@@ -17,14 +18,14 @@ internal static class PackageManifestUpdater
1718 private static string MSBuildRegistryName = "MS Build for Unity" ;
1819 private static string [ ] MSBuildRegistryScopes = new string [ ] { "com.microsoft" } ;
1920
20- private static string MSBuildPackageName = "com.microsoft.msbuildforunity" ;
21- private static string MSBuildPackageVersion = "0.9.1" ;
21+ internal const string MSBuildPackageName = "com.microsoft.msbuildforunity" ;
22+ internal const string MSBuildPackageVersion = "0.9.1" ;
2223
2324 /// <summary>
24- /// Ensures the required settings exist in the package manager to allow for
25- /// installing MSBuild for Unity.
25+ /// Finds and returns the fully qualified path to the Unity Package Manager manifest
2626 /// </summary>
27- internal static void EnsureMSBuildForUnity ( )
27+ /// <returns>The path to the manifest file, or null.</returns>
28+ private static string GetPackageManifestFilePath ( )
2829 {
2930 // Locate the full path to the package manifest.
3031 DirectoryInfo projectRoot = new DirectoryInfo ( Application . dataPath ) . Parent ;
@@ -35,11 +36,95 @@ internal static void EnsureMSBuildForUnity()
3536 if ( ! File . Exists ( manifestPath ) )
3637 {
3738 Debug . LogError ( $ "Package manifest file ({ manifestPath } ) could not be found.") ;
38- return ;
39+ return null ;
40+ }
41+
42+ return manifestPath ;
43+ }
44+
45+ /// <summary>
46+ /// Reports whether or not the appropriate version of MSBuild for Unity is specified
47+ /// in the Unity Package Manager manifest.
48+ /// </summary>
49+ /// <returns>
50+ /// True if an appropriate verson of MS Build for Unity is configured in the manifest, otherwise false.
51+ /// </returns>
52+ internal static bool IsMSBuildForUnityEnabled ( )
53+ {
54+ string manifestPath = GetPackageManifestFilePath ( ) ;
55+ if ( string . IsNullOrWhiteSpace ( manifestPath ) )
56+ {
57+ return false ;
58+ }
59+
60+ // Load the manifest file.
61+ string manifestFileContents = File . ReadAllText ( manifestPath ) ;
62+ if ( string . IsNullOrWhiteSpace ( manifestFileContents ) )
63+ {
64+ return false ;
65+ }
66+
67+ // Read the package manifest a line at a time.
68+ bool msBuildFound = false ;
69+ bool isAppropriateVersion = false ;
70+ Version minVersion = Version . Parse ( MSBuildPackageVersion ) ;
71+ using ( FileStream manifestStream = new FileStream ( manifestPath , FileMode . Open , FileAccess . Read ) )
72+ {
73+ using ( StreamReader reader = new StreamReader ( manifestStream ) )
74+ {
75+ // Read the manifest file a line at a time.
76+ while ( ! reader . EndOfStream )
77+ {
78+ string line = reader . ReadLine ( ) ;
79+ if ( line . Contains ( MSBuildPackageName ) )
80+ {
81+ msBuildFound = true ;
82+
83+ // Next, check the version.
84+ string [ ] splitLine = line . Split ( new char [ ] { ':' } ) ;
85+ if ( splitLine . Length == 2 )
86+ {
87+ // Ensure correct formatting of the version string, before we attempt to parse it.
88+ string versionString = splitLine [ 1 ] . Trim ( new char [ ] { ' ' , '\" ' , ',' } ) ;
89+ bool replaceOnEquals = false ;
90+ if ( versionString . Contains ( "-" ) )
91+ {
92+ // The string references a preview version. Truncate at the '-'.
93+ versionString = versionString . Substring ( 0 , versionString . IndexOf ( '-' ) ) ;
94+
95+ // We want to update preview versions to the final.
96+ replaceOnEquals = true ;
97+ }
98+
99+ Version version ;
100+ if ( Version . TryParse ( versionString , out version ) )
101+ {
102+ isAppropriateVersion = replaceOnEquals ? ( version > minVersion ) : ( version >= minVersion ) ;
103+ }
104+ }
105+
106+ break ;
107+ }
108+ }
109+ }
39110 }
40111
112+ return ( msBuildFound && isAppropriateVersion ) ;
113+ }
114+
115+ /// <summary>
116+ /// Ensures the required settings exist in the package manager to allow for using MSBuild for Unity.
117+ /// </summary>
118+ internal static void EnsureMSBuildForUnity ( )
119+ {
41120 PackageManifest manifest = null ;
42121
122+ string manifestPath = GetPackageManifestFilePath ( ) ;
123+ if ( string . IsNullOrWhiteSpace ( manifestPath ) )
124+ {
125+ return ;
126+ }
127+
43128 // Read the package manifest into a list of strings (for easy finding of entries)
44129 // and then deserialize.
45130 List < string > manifestFileLines = new List < string > ( ) ;
@@ -102,11 +187,12 @@ internal static void EnsureMSBuildForUnity()
102187 int dependenciesStartIndex = - 1 ;
103188 int scopedRegistriesStartIndex = - 1 ;
104189 int scopedRegistriesEndIndex = - 1 ;
190+ int packageLine = - 1 ;
105191
106192 // Attempt to find the MSBuild for Unity package entry in the dependencies collection
107193 // This loop also identifies the dependecies collection line and the start / end of a
108194 // pre-existing scoped registries collections
109- bool needToAddPackage = true ;
195+ bool addPackage = true ;
110196 for ( int i = 0 ; i < manifestFileLines . Count ; i ++ )
111197 {
112198 if ( manifestFileLines [ i ] . Contains ( "\" scopedRegistries\" :" ) )
@@ -123,55 +209,57 @@ internal static void EnsureMSBuildForUnity()
123209 }
124210 if ( manifestFileLines [ i ] . Contains ( MSBuildPackageName ) )
125211 {
126- needToAddPackage = false ;
212+ packageLine = i ;
213+ addPackage = false ;
127214 }
128215 }
129216
130217 // If no package was found add it to the dependencies collection.
131- if ( needToAddPackage )
218+ if ( addPackage )
132219 {
133220 // Add the package to the collection (pad the entry with four spaces)
134221 manifestFileLines . Insert ( dependenciesStartIndex + 1 , $ " \" { MSBuildPackageName } \" : \" { MSBuildPackageVersion } \" ,") ;
135222 }
136-
137- if ( needToAddRegistry || needToAddPackage )
223+ else
138224 {
139- // If we added a scoped registry or package, rewrite the manifest file.
225+ // Replace the line that currently exists
226+ manifestFileLines [ packageLine ] = $ " \" { MSBuildPackageName } \" : \" { MSBuildPackageVersion } \" ,";
227+ }
140228
141- // First, serialize the scoped registry collection.
142- string serializedRegistriesJson = JsonUtility . ToJson ( manifest , true ) ;
229+ // Update the manifest file.
230+ // First, serialize the scoped registry collection.
231+ string serializedRegistriesJson = JsonUtility . ToJson ( manifest , true ) ;
143232
144- // Ensure that the file is truncated to ensure it is always valid after writing.
145- using ( FileStream outFile = new FileStream ( manifestPath , FileMode . Truncate , FileAccess . Write ) )
233+ // Ensure that the file is truncated to ensure it is always valid after writing.
234+ using ( FileStream outFile = new FileStream ( manifestPath , FileMode . Truncate , FileAccess . Write ) )
235+ {
236+ using ( StreamWriter writer = new StreamWriter ( outFile ) )
146237 {
147- using ( StreamWriter writer = new StreamWriter ( outFile ) )
148- {
149- bool scopedRegistriesWritten = false ;
238+ bool scopedRegistriesWritten = false ;
150239
151- // Write each line of the manifest back to the file.
152- for ( int i = 0 ; i < manifestFileLines . Count ; i ++ )
240+ // Write each line of the manifest back to the file.
241+ for ( int i = 0 ; i < manifestFileLines . Count ; i ++ )
242+ {
243+ if ( ( i >= scopedRegistriesStartIndex ) && ( i <= scopedRegistriesEndIndex ) )
153244 {
154- if ( ( i >= scopedRegistriesStartIndex ) && ( i <= scopedRegistriesEndIndex ) )
155- {
156- // Skip these lines, they will be replaced.
157- continue ;
158- }
245+ // Skip these lines, they will be replaced.
246+ continue ;
247+ }
159248
160- if ( ! scopedRegistriesWritten && ( i > 0 ) )
161- {
162- // Trim the leading '{' and '\n' from the serialized scoped registries
163- serializedRegistriesJson = serializedRegistriesJson . Remove ( 0 , 2 ) ;
164- // Trim, the trailing '\n' and '}'
165- serializedRegistriesJson = serializedRegistriesJson . Remove ( serializedRegistriesJson . Length - 2 ) ;
166- // Append a trailing ',' to close the scopedRegistries node
167- serializedRegistriesJson = serializedRegistriesJson . Insert ( serializedRegistriesJson . Length , "," ) ;
168- writer . WriteLine ( serializedRegistriesJson ) ;
169-
170- scopedRegistriesWritten = true ;
171- }
249+ if ( ! scopedRegistriesWritten && ( i > 0 ) )
250+ {
251+ // Trim the leading '{' and '\n' from the serialized scoped registries
252+ serializedRegistriesJson = serializedRegistriesJson . Remove ( 0 , 2 ) ;
253+ // Trim, the trailing '\n' and '}'
254+ serializedRegistriesJson = serializedRegistriesJson . Remove ( serializedRegistriesJson . Length - 2 ) ;
255+ // Append a trailing ',' to close the scopedRegistries node
256+ serializedRegistriesJson = serializedRegistriesJson . Insert ( serializedRegistriesJson . Length , "," ) ;
257+ writer . WriteLine ( serializedRegistriesJson ) ;
172258
173- writer . WriteLine ( manifestFileLines [ i ] ) ;
259+ scopedRegistriesWritten = true ;
174260 }
261+
262+ writer . WriteLine ( manifestFileLines [ i ] ) ;
175263 }
176264 }
177265 }
0 commit comments