@@ -4,6 +4,7 @@ import com.ullink.util.GradleHelper
44import org.gradle.api.tasks.Input
55import org.gradle.api.tasks.InputDirectory
66import org.gradle.api.tasks.InputFile
7+ import org.gradle.api.tasks.Internal
78import org.gradle.api.tasks.Optional
89import org.gradle.api.tasks.OutputDirectory
910
@@ -37,6 +38,9 @@ class NuGetRestore extends BaseNuGet {
3738 def msBuildVersion
3839 @Optional
3940 @Input
41+ def msBuildPath
42+ @Optional
43+ @Input
4044 def packagesDirectory
4145 @Input
4246 def ignoreFailuresOnNonWindows = false
@@ -86,14 +90,39 @@ class NuGetRestore extends BaseNuGet {
8690 if (solutionDirectory) args ' -SolutionDirectory' , solutionDirectory
8791 if (disableParallelProcessing) args ' -DisableParallelProcessing'
8892
89- // Skip MSBuildVersion on non-Windows platforms (macOS/Linux) because Mono's xbuild/MSBuild
90- // doesn't work properly with NuGet restore and causes "Too many project files specified" errors
93+ // On non-Windows platforms, try to use modern .NET SDK's MSBuild if available
9194 if (! isFamily(FAMILY_WINDOWS )) {
92- project. logger. debug(" Skipping MSBuildVersion on non-Windows platform to avoid Mono xbuild issues" )
95+ // If msBuildPath is not explicitly set, try to auto-detect dotnet SDK MSBuild
96+ if (! msBuildPath) {
97+ def dotnetPath = findDotnetPath()
98+ if (dotnetPath) {
99+ def dotnetMsbuildPath = findDotnetMsbuildPath(dotnetPath)
100+ if (dotnetMsbuildPath) {
101+ msBuildPath = dotnetMsbuildPath
102+ project. logger. debug(" Auto-detected dotnet MSBuild at: ${ msBuildPath} " )
103+ }
104+ }
105+ }
106+
107+ // Use MSBuildPath if available (takes precedence over MSBuildVersion)
108+ if (msBuildPath) {
109+ args ' -MSBuildPath' , msBuildPath
110+ project. logger. debug(" Using MSBuildPath: ${ msBuildPath} " )
111+ } else {
112+ // Skip MSBuildVersion on non-Windows if no MSBuildPath found
113+ // because Mono's xbuild/MSBuild doesn't work properly with NuGet restore
114+ project. logger. debug(" Skipping MSBuildVersion on non-Windows platform (no dotnet MSBuild found)" )
115+ }
93116 } else {
117+ // On Windows, use MSBuildVersion as before
94118 if (! msBuildVersion) msBuildVersion = GradleHelper . getPropertyFromTask(project, ' version' , ' msbuild' )
95119 if (msBuildVersion) args ' -MsBuildVersion' , msBuildVersion
96120 }
121+
122+ // MSBuildPath can also be explicitly set on Windows
123+ if (msBuildPath) {
124+ args ' -MSBuildPath' , msBuildPath
125+ }
97126
98127 project. logger. info " Restoring NuGet packages " +
99128 (sources ? " from $sources " : ' ' ) +
@@ -116,4 +145,88 @@ class NuGetRestore extends BaseNuGet {
116145 def solutionDir = solutionFile ? project. file(solutionFile. getParent()) : solutionDirectory
117146 return new File (solutionDir ? solutionDir. toString() : ' .' , ' packages' )
118147 }
148+
149+ /**
150+ * Find the dotnet executable path
151+ */
152+ private String findDotnetPath () {
153+ try {
154+ def process = [' which' , ' dotnet' ]. execute()
155+ process. waitFor()
156+ if (process. exitValue() == 0 ) {
157+ return process. text. trim()
158+ }
159+ } catch (Exception e) {
160+ // dotnet not found
161+ }
162+ return null
163+ }
164+
165+ /**
166+ * Find the MSBuild.dll path in the dotnet SDK
167+ */
168+ private String findDotnetMsbuildPath (String dotnetPath ) {
169+ try {
170+ // Get dotnet SDK path
171+ def process = [dotnetPath, ' --info' ]. execute()
172+ process. waitFor()
173+ def output = process. text
174+
175+ // Look for SDK base path
176+ def sdkBasePath = null
177+ output. eachLine { line ->
178+ if (line. contains(' Base Path:' ) || line. contains(' SDK Base Path:' )) {
179+ def path = line. split(' :' )[1 ]?. trim()
180+ if (path) {
181+ sdkBasePath = path
182+ }
183+ }
184+ }
185+
186+ if (sdkBasePath) {
187+ // Try to find MSBuild.dll in the SDK
188+ def msbuildDll = new File (sdkBasePath, ' MSBuild.dll' )
189+ if (msbuildDll. exists()) {
190+ return msbuildDll. parentFile. absolutePath
191+ }
192+
193+ // Alternative: look in Current/MSBuild directory
194+ def currentMsbuild = new File (sdkBasePath, ' Current/MSBuild.dll' )
195+ if (currentMsbuild. exists()) {
196+ return currentMsbuild. parentFile. absolutePath
197+ }
198+ }
199+
200+ // Fallback: try common SDK locations
201+ def commonPaths = [
202+ ' /usr/local/share/dotnet/sdk' ,
203+ ' /usr/share/dotnet/sdk' ,
204+ System . getProperty(' user.home' ) + ' /.dotnet/sdk'
205+ ]
206+
207+ for (def basePath : commonPaths) {
208+ def sdkDir = new File (basePath)
209+ if (sdkDir. exists() && sdkDir. isDirectory()) {
210+ // Find the highest version SDK
211+ def sdkVersions = sdkDir. listFiles(). findAll { it. isDirectory() && it. name. matches(/ ^\d +\.\d +\.\d +.*/ ) }
212+ if (sdkVersions) {
213+ sdkVersions. sort { a , b ->
214+ // Simple version comparison
215+ def aVer = a. name. split(/ [.-]/ ). collect { it. toInteger() }
216+ def bVer = b. name. split(/ [.-]/ ). collect { it. toInteger() }
217+ return bVer <=> aVer
218+ }
219+ def latestSdk = sdkVersions[0 ]
220+ def msbuildDll = new File (latestSdk, ' MSBuild.dll' )
221+ if (msbuildDll. exists()) {
222+ return msbuildDll. parentFile. absolutePath
223+ }
224+ }
225+ }
226+ }
227+ } catch (Exception e) {
228+ project. logger. debug(" Could not find dotnet MSBuild: ${ e.message} " )
229+ }
230+ return null
231+ }
119232}
0 commit comments