From 834edfbc9ccbe2dbe63b89a8d09c4199d000da38 Mon Sep 17 00:00:00 2001 From: Sridhar Periyasamy Date: Tue, 23 Feb 2016 18:04:49 -0800 Subject: [PATCH] Add support for building Windows x86 version of dotnet CLI - Changes to build scripts to produce Winx86 build artifacts like zip/installer. - Change to run Nuget-xplat in the same process as dotnet.exe instead of spinning up a new 'corerun' process. --- packaging/windows/generatemsi.ps1 | 8 ++--- packaging/windows/variables.wxi | 2 +- scripts/dotnet-cli-build/CompileTargets.cs | 36 ++++++++++++++++++-- scripts/dotnet-cli-build/PublishTargets.cs | 4 ++- scripts/dotnet-cli-build/Utils/DotNetCli.cs | 4 ++- scripts/obtain/install.ps1 | 7 ++-- scripts/package/package-zip.ps1 | 2 +- scripts/publish/publish.ps1 | 20 ++--------- scripts/run-build.ps1 | 6 ++-- src/dotnet/commands/dotnet-restore/NuGet3.cs | 20 +++++------ test/EndToEnd/EndToEndTest.cs | 24 +++++++++++++ 11 files changed, 89 insertions(+), 44 deletions(-) diff --git a/packaging/windows/generatemsi.ps1 b/packaging/windows/generatemsi.ps1 index 59d009679..fa6671e9f 100644 --- a/packaging/windows/generatemsi.ps1 +++ b/packaging/windows/generatemsi.ps1 @@ -71,7 +71,7 @@ function RunCandle -dBuildVersion="$env:DOTNET_MSI_VERSION" ` -dDisplayVersion="$env:DOTNET_CLI_VERSION" ` -dReleaseSuffix="$env:ReleaseSuffix" ` - -arch x64 ` + -arch "$env:ARCHITECTURE" ` -ext WixDependencyExtension.dll ` "$AuthWsxRoot\dotnet.wxs" ` "$AuthWsxRoot\provider.wxs" ` @@ -136,7 +136,7 @@ function RunCandleForBundle -dDisplayVersion="$env:DOTNET_CLI_VERSION" ` -dReleaseSuffix="$env:ReleaseSuffix" ` -dMsiSourcePath="$DotnetMSIOutput" ` - -arch x64 ` + -arch "$env:ARCHITECTURE" ` -ext WixBalExtension.dll ` -ext WixUtilExtension.dll ` -ext WixTagExtension.dll ` @@ -190,8 +190,8 @@ if(!(Test-Path $PackageDir)) mkdir $PackageDir | Out-Null } -$DotnetMSIOutput = Join-Path $PackageDir "dotnet-win-x64.$env:DOTNET_CLI_VERSION.msi" -$DotnetBundleOutput = Join-Path $PackageDir "dotnet-win-x64.$env:DOTNET_CLI_VERSION.exe" +$DotnetMSIOutput = Join-Path $PackageDir "dotnet-win-$env:ARCHITECTURE.$env:DOTNET_CLI_VERSION.msi" +$DotnetBundleOutput = Join-Path $PackageDir "dotnet-win-$env:ARCHITECTURE.$env:DOTNET_CLI_VERSION.exe" Write-Host "Creating dotnet MSI at $DotnetMSIOutput" Write-Host "Creating dotnet Bundle at $DotnetBundleOutput" diff --git a/packaging/windows/variables.wxi b/packaging/windows/variables.wxi index 9466f0206..d5affc10a 100644 --- a/packaging/windows/variables.wxi +++ b/packaging/windows/variables.wxi @@ -26,7 +26,7 @@ - + diff --git a/scripts/dotnet-cli-build/CompileTargets.cs b/scripts/dotnet-cli-build/CompileTargets.cs index 04244fd4d..dae340152 100644 --- a/scripts/dotnet-cli-build/CompileTargets.cs +++ b/scripts/dotnet-cli-build/CompileTargets.cs @@ -14,6 +14,8 @@ namespace Microsoft.DotNet.Cli.Build { public static readonly string CoreCLRVersion = "1.0.1-rc2-23811"; public static readonly string AppDepSdkVersion = "1.0.6-prerelease-00003"; + public static readonly bool IsWinx86 = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + RuntimeInformation.ProcessArchitecture == Architecture.X86; public static readonly List AssembliesToCrossGen = GetAssembliesToCrossGen(); @@ -60,16 +62,18 @@ namespace Microsoft.DotNet.Cli.Build Mkdirp(cmakeOut); var configuration = c.BuildContext.Get("Configuration"); + var architecture = PlatformServices.Default.Runtime.RuntimeArchitecture; // Run the build if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // Why does Windows directly call cmake but Linux/Mac calls "build.sh" in the corehost dir? // See the comment in "src/corehost/build.sh" for details. It doesn't work for some reason. + var visualStudio = IsWinx86 ? "Visual Studio 14 2015" : "Visual Studio 14 2015 Win64"; ExecIn(cmakeOut, "cmake", Path.Combine(c.BuildContext.BuildDirectory, "src", "corehost"), "-G", - "Visual Studio 14 2015 Win64"); + visualStudio); var pf32 = RuntimeInformation.OSArchitecture == Architecture.X64 ? Environment.GetEnvironmentVariable("ProgramFiles(x86)") : @@ -203,6 +207,25 @@ namespace Microsoft.DotNet.Cli.Build File.Delete(Path.Combine(binDir, $"dotnet{Constants.ExeSuffix}")); File.Copy(Path.Combine(binDir, $"corehost{Constants.ExeSuffix}"), Path.Combine(binDir, $"dotnet{Constants.ExeSuffix}")); + // HACK + // bootstrapping for Windows x86. Copy csc/vbc from stage0. + // This is a temporary hack for https://github.com/dotnet/roslyn/issues/8951 + if (IsWinx86) + { + List x86compilerBins = new List { + "csc.dll", + "Microsoft.CodeAnalysis.CSharp.dll", + "Microsoft.CodeAnalysis.dll", + "Microsoft.CodeAnalysis.VisualBasic.dll", + "vbc.dll" + }; + + foreach (var binary in x86compilerBins) + { + File.Copy(Path.Combine(DotNetCli.Stage0.BinPath, binary), Path.Combine(binDir, binary), true); + } + } + // Crossgen Roslyn var result = Crossgen(c, binDir); if (!result.Success) @@ -233,8 +256,16 @@ namespace Microsoft.DotNet.Cli.Build // Find toolchain package string packageId; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { + if (IsWinx86) + { + // https://github.com/dotnet/cli/issues/1550 + c.Warn("Native compilation is not yet working on Windows x86"); + return c.Success(); + } + packageId = "toolchain.win7-x64.Microsoft.DotNet.AppDep"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) @@ -282,10 +313,11 @@ namespace Microsoft.DotNet.Cli.Build } // Find crossgen + string arch = PlatformServices.Default.Runtime.RuntimeArchitecture; string packageId; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - packageId = "runtime.win7-x64.Microsoft.NETCore.Runtime.CoreCLR"; + packageId = $"runtime.win7-{arch}.Microsoft.NETCore.Runtime.CoreCLR"; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { diff --git a/scripts/dotnet-cli-build/PublishTargets.cs b/scripts/dotnet-cli-build/PublishTargets.cs index 5c25f813b..0255c2946 100644 --- a/scripts/dotnet-cli-build/PublishTargets.cs +++ b/scripts/dotnet-cli-build/PublishTargets.cs @@ -27,6 +27,7 @@ namespace Microsoft.DotNet.Cli.Build // This is overkill, but I want to cover all the variables used in all OSes (including where some have the same names) var buildVersion = c.BuildContext.Get("BuildVersion"); var configuration = c.BuildContext.Get("Configuration"); + var architecture = PlatformServices.Default.Runtime.RuntimeArchitecture; var env = new Dictionary() { { "RID", PlatformServices.Default.Runtime.GetRuntimeIdentifier() }, @@ -50,7 +51,8 @@ namespace Microsoft.DotNet.Cli.Build { "DOTNET_CLI_VERSION", buildVersion.SimpleVersion }, { "DOTNET_MSI_VERSION", buildVersion.GenerateMsiVersion() }, { "VersionSuffix", buildVersion.VersionSuffix }, - { "CONFIGURATION", configuration } + { "CONFIGURATION", configuration }, + { "ARCHITECTURE", architecture } }; if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) diff --git a/scripts/dotnet-cli-build/Utils/DotNetCli.cs b/scripts/dotnet-cli-build/Utils/DotNetCli.cs index 67d30fa8b..a84ec89dc 100644 --- a/scripts/dotnet-cli-build/Utils/DotNetCli.cs +++ b/scripts/dotnet-cli-build/Utils/DotNetCli.cs @@ -35,7 +35,9 @@ namespace Microsoft.DotNet.Cli.Build { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - return Path.Combine(Directory.GetCurrentDirectory(), ".dotnet_stage0", PlatformServices.Default.Runtime.OperatingSystemPlatform.ToString(), "cli", "bin"); + return Path.Combine(Directory.GetCurrentDirectory(), ".dotnet_stage0", + PlatformServices.Default.Runtime.OperatingSystemPlatform.ToString(), + PlatformServices.Default.Runtime.RuntimeArchitecture, "cli", "bin"); } else { diff --git a/scripts/obtain/install.ps1 b/scripts/obtain/install.ps1 index 218f06ba1..648aff8f6 100644 --- a/scripts/obtain/install.ps1 +++ b/scripts/obtain/install.ps1 @@ -5,7 +5,8 @@ param( [string]$Channel="dev", - [string]$version="Latest" + [string]$version="Latest", + [string]$Architecture="x64" ) $ErrorActionPreference="Stop" @@ -16,7 +17,7 @@ if ($fileVersion -eq "Latest") { $fileVersion = "latest" } $Feed="https://dotnetcli.blob.core.windows.net/dotnet" -$DotNetFileName="dotnet-win-x64.$fileVersion.zip" +$DotNetFileName="dotnet-win-$Architecture.$fileVersion.zip" $DotNetUrl="$Feed/$Channel/Binaries/$Version" function say($str) @@ -42,7 +43,7 @@ if (Test-Path $LocalFile) { if ($Version -eq "Latest") { - $RemoteResponse = Invoke-WebRequest -UseBasicParsing "$Feed/$Channel/dnvm/latest.win.version" + $RemoteResponse = Invoke-WebRequest -UseBasicParsing "$Feed/$Channel/dnvm/latest.win.$Architecture.version" $RemoteData = @([Text.Encoding]::UTF8.GetString($RemoteResponse.Content).Split([char[]]@(), [StringSplitOptions]::RemoveEmptyEntries)); $RemoteHash = $RemoteData[0].Trim() $RemoteVersion = $RemoteData[1].Trim() diff --git a/scripts/package/package-zip.ps1 b/scripts/package/package-zip.ps1 index 6c4b0d2f2..8b7000d65 100644 --- a/scripts/package/package-zip.ps1 +++ b/scripts/package/package-zip.ps1 @@ -26,7 +26,7 @@ $PackageVersion $VersionContent | Out-File -Encoding UTF8 "$Stage2Dir\.version" -$PackageName = Join-Path $PackageDir "dotnet-win-x64.$PackageVersion.zip" +$PackageName = Join-Path $PackageDir "dotnet-win-$env:ARCHITECTURE.$PackageVersion.zip" if (Test-Path $PackageName) { diff --git a/scripts/publish/publish.ps1 b/scripts/publish/publish.ps1 index f7c9a2453..7c106f0bc 100644 --- a/scripts/publish/publish.ps1 +++ b/scripts/publish/publish.ps1 @@ -98,30 +98,16 @@ function UploadBinaries($zipFile) } Write-Host "Updating the latest dotnet binaries for windows.." - $zipBlobLatest = "$env:CHANNEL/Binaries/Latest/dotnet-win-x64.latest.zip" + $zipBlobLatest = "$env:CHANNEL/Binaries/Latest/dotnet-win-$env:ARCHITECTURE.latest.zip" if(-Not (UploadFile $zipBlobLatest $zipFile $true)) { return -1 } - - # update the index file too - $indexContent = "Binaries/$env:DOTNET_CLI_VERSION/$fileName" - $indexFile = "$env:TEMP\latest.win.index" - $indexContent | Out-File -FilePath $indexFile - - # upload the index file - $indexBlob = "$env:CHANNEL/dnvm/latest.win.index" - - if(-Not (UploadFile $indexBlob $indexFile $true)) - { - return -1 - } - # update the version file $versionFile = Convert-Path $PSScriptRoot\..\..\artifacts\$env:RID\stage2\.version - $versionBlob = "$env:CHANNEL/dnvm/latest.win.version" + $versionBlob = "$env:CHANNEL/dnvm/latest.win.$env:ARCHITECTURE.version" if(-Not (UploadFile $versionBlob $versionFile $true)) { @@ -142,7 +128,7 @@ function UploadInstallers($installerFile) } Write-Host "Updating the latest dotnet installer for windows.." - $installerBlobLatest = "$env:CHANNEL/Installers/Latest/dotnet-win-x64.latest.exe" + $installerBlobLatest = "$env:CHANNEL/Installers/Latest/dotnet-win-$env:ARCHITECTURE.latest.exe" if(-Not (UploadFile $installerBlobLatest $installerFile $true)) { diff --git a/scripts/run-build.ps1 b/scripts/run-build.ps1 index f78681076..7206b3b0a 100644 --- a/scripts/run-build.ps1 +++ b/scripts/run-build.ps1 @@ -5,6 +5,7 @@ param( [string]$Configuration="Debug", + [string]$Architecture="x64", [switch]$NoPackage, [switch]$Help) @@ -14,6 +15,7 @@ if($Help) Write-Host "" Write-Host "Options:" Write-Host " -Configuration Build the specified Configuration (Debug or Release, default: Debug)" + Write-Host " -Architecture Build the specified architecture (x64 or x86 (supported only on Windows), default: x64)" Write-Host " -NoPackage Skip packaging targets" Write-Host " -Help Display this help message" Write-Host " The build targets to run (Init, Compile, Publish, etc.; Default is a full build and publish)" @@ -44,7 +46,7 @@ $env:CHANNEL=$env:RELEASE_SUFFIX # Use a repo-local install directory (but not the artifacts directory because that gets cleaned a lot if (!$env:DOTNET_INSTALL_DIR) { - $env:DOTNET_INSTALL_DIR="$PSScriptRoot\..\.dotnet_stage0\Windows" + $env:DOTNET_INSTALL_DIR="$PSScriptRoot\..\.dotnet_stage0\Windows\$Architecture" } if (!(Test-Path $env:DOTNET_INSTALL_DIR)) @@ -54,7 +56,7 @@ if (!(Test-Path $env:DOTNET_INSTALL_DIR)) # Install a stage 0 Write-Host "Installing .NET Core CLI Stage 0 from beta channel" -& "$PSScriptRoot\obtain\install.ps1" -Channel $env:CHANNEL +& "$PSScriptRoot\obtain\install.ps1" -Channel $env:CHANNEL -Architecture $Architecture # Put the stage0 on the path $env:PATH = "$env:DOTNET_INSTALL_DIR\cli\bin;$env:PATH" diff --git a/src/dotnet/commands/dotnet-restore/NuGet3.cs b/src/dotnet/commands/dotnet-restore/NuGet3.cs index 58f74a6b8..c7d866ca5 100644 --- a/src/dotnet/commands/dotnet-restore/NuGet3.cs +++ b/src/dotnet/commands/dotnet-restore/NuGet3.cs @@ -2,7 +2,9 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using Microsoft.DotNet.Cli.Utils; +using NugetProgram = NuGet.CommandLine.XPlat.Program; namespace Microsoft.DotNet.Tools.Restore { @@ -20,22 +22,16 @@ namespace Microsoft.DotNet.Tools.Restore var result = Run(Enumerable.Concat( prefixArgs, - args)) - .ForwardStdOut() - .ForwardStdErr() - .Execute(); + args).ToArray()); - return result.ExitCode; + return result; } - private static Command Run(IEnumerable nugetArgs) + private static int Run(string[] nugetArgs) { - var corerun = Path.Combine( - AppContext.BaseDirectory, - "corerun" + Constants.ExeSuffix); - return Command.Create(corerun, Enumerable.Concat( - new[] { Path.Combine(AppContext.BaseDirectory, "NuGet.CommandLine.XPlat.dll") }, - nugetArgs)); + var nugetAsm = typeof(NugetProgram).GetTypeInfo().Assembly; + var mainMethod = nugetAsm.EntryPoint; + return (int)mainMethod.Invoke(null, new object[] { nugetArgs }); } } } diff --git a/test/EndToEnd/EndToEndTest.cs b/test/EndToEnd/EndToEndTest.cs index d01fdde8b..1f734e3bb 100644 --- a/test/EndToEnd/EndToEndTest.cs +++ b/test/EndToEnd/EndToEndTest.cs @@ -89,6 +89,12 @@ namespace Microsoft.DotNet.Tests.EndToEnd return; } + if (IsWinX86()) + { + Console.WriteLine("Skipping native compilation tests on Windows x86 - https://github.com/dotnet/cli/issues/1550"); + return; + } + var buildCommand = new BuildCommand(TestProject, output: OutputDirectory, native: true, framework: DefaultFramework); buildCommand.Execute().Should().Pass(); @@ -105,6 +111,12 @@ namespace Microsoft.DotNet.Tests.EndToEnd return; } + if (IsWinX86()) + { + Console.WriteLine("Skipping native compilation tests on Windows x86 - https://github.com/dotnet/cli/issues/1550"); + return; + } + var buildCommand = new BuildCommand(TestProject, output: OutputDirectory, native: true, nativeCppMode: true, framework: DefaultFramework); buildCommand.Execute().Should().Pass(); @@ -121,6 +133,12 @@ namespace Microsoft.DotNet.Tests.EndToEnd return; } + if (IsWinX86()) + { + Console.WriteLine("Skipping native compilation tests on Windows x86 - https://github.com/dotnet/cli/issues/1550"); + return; + } + // first build var buildCommand = new BuildCommand(TestProject, output: OutputDirectory, native: true, nativeCppMode: true, framework: DefaultFramework); var binariesOutputDirectory = GetCompilationOutputPath(OutputDirectory, false); @@ -233,6 +251,12 @@ namespace Microsoft.DotNet.Tests.EndToEnd return false; } + private bool IsWinX86() + { + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && + RuntimeInformation.ProcessArchitecture == Architecture.X86; + } + private static DateTime GetLastWriteTimeUtcOfDirectoryFiles(string outputDirectory) { return Directory.EnumerateFiles(outputDirectory).Max(f => File.GetLastWriteTimeUtc(f));