From cd753db22872b2bde2dcfb60cb388c67cf9c2a4b Mon Sep 17 00:00:00 2001 From: John Beisner Date: Fri, 9 Jun 2017 12:00:29 -0700 Subject: [PATCH 1/7] Default channel=LTS Clarify nomenclature from "alternate" to "legacy" Skip construction of legacy URL if it's flawed. --- scripts/obtain/dotnet-install.ps1 | 14 +++++------ scripts/obtain/dotnet-install.sh | 40 ++++++++++++++++++------------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/scripts/obtain/dotnet-install.ps1 b/scripts/obtain/dotnet-install.ps1 index f4869d690..124db144e 100644 --- a/scripts/obtain/dotnet-install.ps1 +++ b/scripts/obtain/dotnet-install.ps1 @@ -10,7 +10,7 @@ Installs dotnet cli. If dotnet installation already exists in the given directory it will update it only if the requested version differs from the one already installed. .PARAMETER Channel - Default: release/1.0.0 + Default: LTS Download from the Channel specified .PARAMETER Version Default: latest @@ -55,7 +55,7 @@ #> [cmdletbinding()] param( - [string]$Channel="release/1.0.0", + [string]$Channel="LTS", [string]$Version="Latest", [string]$InstallDir="", [string]$Architecture="", @@ -261,7 +261,7 @@ function Get-Download-Link([string]$AzureFeed, [string]$Channel, [string]$Specif return $PayloadURL } -function Get-AltDownload-Link([string]$AzureFeed, [string]$Channel, [string]$SpecificVersion, [string]$CLIArchitecture) { +function Get-LegacytDownload-Link([string]$AzureFeed, [string]$Channel, [string]$SpecificVersion, [string]$CLIArchitecture) { Say-Invocation $MyInvocation if ($SharedRuntime) { @@ -271,7 +271,7 @@ function Get-AltDownload-Link([string]$AzureFeed, [string]$Channel, [string]$Spe $PayloadURL = "$AzureFeed/Sdk/$SpecificVersion/dotnet-dev-win-$CLIArchitecture.$SpecificVersion.zip" } - Say-Verbose "Constructed alternate payload URL: $PayloadURL" + Say-Verbose "Constructed legacy payload URL: $PayloadURL" return $PayloadURL } @@ -432,12 +432,12 @@ function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot, [string]$BinFolde $CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture $SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $AzureFeed -Channel $Channel -Version $Version $DownloadLink = Get-Download-Link -AzureFeed $AzureFeed -Channel $Channel -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture -$AltDownloadLink = Get-AltDownload-Link -AzureFeed $AzureFeed -Channel $Channel -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture +$LegacyDownloadLink = Get-LegacyDownload-Link -AzureFeed $AzureFeed -Channel $Channel -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture if ($DryRun) { Say "Payload URLs:" Say "Primary - $DownloadLink" - Say "Alternate - $AltDownloadLink" + Say "Legacy - $LegacyDownloadLink" Say "Repeatable invocation: .\$($MyInvocation.MyCommand) -Version $SpecificVersion -Channel $Channel -Architecture $CLIArchitecture -InstallDir $InstallDir" exit 0 } @@ -469,7 +469,7 @@ try { DownloadFile -Uri $DownloadLink -OutPath $ZipPath } catch { - $DownloadLink = $AltDownloadLink + $DownloadLink = $LegacyDownloadLink $ZipPath = [System.IO.Path]::GetTempFileName() Say "Downloading $DownloadLink" DownloadFile -Uri $DownloadLink -OutPath $ZipPath diff --git a/scripts/obtain/dotnet-install.sh b/scripts/obtain/dotnet-install.sh index c4d0c5850..eaea0dca6 100755 --- a/scripts/obtain/dotnet-install.sh +++ b/scripts/obtain/dotnet-install.sh @@ -147,7 +147,7 @@ get_distro_specific_os_name() { fi fi - say_err "OS name could not be detected: $ID.$VERSION_ID" + say_verbose "Distribution specific OS name + version could not be detected: $ID.$VERSION_ID" return 1 } @@ -412,7 +412,7 @@ construct_download_link() { # channel - $2 # normalized_architecture - $3 # specific_version - $4 -construct_alt_download_link() { +construct_legacy_download_link() { eval $invocation local azure_feed=$1 @@ -423,14 +423,14 @@ construct_alt_download_link() { local distro_specific_osname distro_specific_osname=$(get_distro_specific_os_name) || return 1 - local alt_download_link=null + local legacy_download_link=null if [ "$shared_runtime" = true ]; then - alt_download_link="$azure_feed/Runtime/$specific_version/dotnet-$distro_specific_osname-$normalized_architecture.$specific_version.tar.gz" + legacy_download_link="$azure_feed/Runtime/$specific_version/dotnet-$distro_specific_osname-$normalized_architecture.$specific_version.tar.gz" else - alt_download_link="$azure_feed/Sdk/$specific_version/dotnet-dev-$distro_specific_osname-$normalized_architecture.$specific_version.tar.gz" + legacy_download_link="$azure_feed/Sdk/$specific_version/dotnet-dev-$distro_specific_osname-$normalized_architecture.$specific_version.tar.gz" fi - echo "$alt_download_link" + echo "$legacy_download_link" return 0 } @@ -601,7 +601,8 @@ downloadwget() { calculate_vars() { eval $invocation - + valid_legacy_download_link=false + normalized_architecture=$(get_normalized_architecture_from_architecture "$architecture") say_verbose "normalized_architecture=$normalized_architecture" @@ -615,8 +616,12 @@ calculate_vars() { download_link=$(construct_download_link $azure_feed $channel $normalized_architecture $specific_version) say_verbose "download_link=$download_link" - alt_download_link=$(construct_alt_download_link $azure_feed $channel $normalized_architecture $specific_version) - say_verbose "alt_download_link=$alt_download_link" + if [ legacy_download_link=$(construct_legacy_download_link $azure_feed $channel $normalized_architecture $specific_version) ]; then + say_verbose "legacy_download_link=$legacy_download_link" + valid_legacy_download_link=true + else + say_verbose "Cound not construct a legacy_download_link; omitting..." + fi install_root=$(resolve_installation_path $install_dir) say_verbose "install_root=$install_root" @@ -638,13 +643,13 @@ install_dotnet() { say "Downloading link: $download_link" download "$download_link" $zip_path || download_failed=true - # if the download fails, download the alt_download_link - if [ "$download_failed" = true ]; then + # if the download fails, download the legacy_download_link + if [ "$download_failed" = true && "$valid_legacy_download_link" = true ]; then say "Cannot download: $download_link" zip_path=$(mktemp $temporary_file_template) - say_verbose "Alternate zip path: $zip_path" - say "Downloading alternate link: $alt_download_link" - download "$alt_download_link" $zip_path + say_verbose "Legacy zip path: $zip_path" + say "Downloading legacy link: $legacy_download_link" + download "$legacy_download_link" $zip_path fi say "Extracting zip" @@ -657,7 +662,7 @@ local_version_file_relative_path="/.version" bin_folder_relative_path="" temporary_file_template="${TMPDIR:-/tmp}/dotnet.XXXXXXXXX" -channel="release/1.0.0" +channel="LTS" version="Latest" install_dir="" architecture="" @@ -764,9 +769,12 @@ done check_min_reqs calculate_vars + if [ "$dry_run" = true ]; then say "Payload URL: $download_link" - say "Alternate payload URL: $alt_download_link" + if [ "$valid_legacy_download_link" = true ]; then + say "Legacy payload URL: $legacy_download_link" + fi say "Repeatable invocation: ./$(basename $0) --version $specific_version --channel $channel --install-dir $install_dir" exit 0 fi From 5eae1a3a311c8d897209ea48d35f7cd5a950deba Mon Sep 17 00:00:00 2001 From: John Beisner Date: Mon, 12 Jun 2017 09:31:09 -0700 Subject: [PATCH 2/7] Fixing logic; variable name. --- scripts/obtain/dotnet-install.ps1 | 2 +- scripts/obtain/dotnet-install.sh | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/obtain/dotnet-install.ps1 b/scripts/obtain/dotnet-install.ps1 index 124db144e..8db843a58 100644 --- a/scripts/obtain/dotnet-install.ps1 +++ b/scripts/obtain/dotnet-install.ps1 @@ -261,7 +261,7 @@ function Get-Download-Link([string]$AzureFeed, [string]$Channel, [string]$Specif return $PayloadURL } -function Get-LegacytDownload-Link([string]$AzureFeed, [string]$Channel, [string]$SpecificVersion, [string]$CLIArchitecture) { +function Get-LegacyDownload-Link([string]$AzureFeed, [string]$Channel, [string]$SpecificVersion, [string]$CLIArchitecture) { Say-Invocation $MyInvocation if ($SharedRuntime) { diff --git a/scripts/obtain/dotnet-install.sh b/scripts/obtain/dotnet-install.sh index eaea0dca6..8697816e7 100755 --- a/scripts/obtain/dotnet-install.sh +++ b/scripts/obtain/dotnet-install.sh @@ -601,7 +601,7 @@ downloadwget() { calculate_vars() { eval $invocation - valid_legacy_download_link=false + valid_legacy_download_link=true normalized_architecture=$(get_normalized_architecture_from_architecture "$architecture") say_verbose "normalized_architecture=$normalized_architecture" @@ -616,9 +616,10 @@ calculate_vars() { download_link=$(construct_download_link $azure_feed $channel $normalized_architecture $specific_version) say_verbose "download_link=$download_link" - if [ legacy_download_link=$(construct_legacy_download_link $azure_feed $channel $normalized_architecture $specific_version) ]; then + legacy_download_link=$(construct_legacy_download_link $azure_feed $channel $normalized_architecture $specific_version) || valid_legacy_download_link=false + + if [ "$valid_legacy_download_link" = true ]; then say_verbose "legacy_download_link=$legacy_download_link" - valid_legacy_download_link=true else say_verbose "Cound not construct a legacy_download_link; omitting..." fi From b92c63c4d38e410145b954d1a3831e9d1946392b Mon Sep 17 00:00:00 2001 From: John Beisner Date: Mon, 12 Jun 2017 09:38:02 -0700 Subject: [PATCH 3/7] Fixing a conditional. --- scripts/obtain/dotnet-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/obtain/dotnet-install.sh b/scripts/obtain/dotnet-install.sh index 8697816e7..30024e328 100755 --- a/scripts/obtain/dotnet-install.sh +++ b/scripts/obtain/dotnet-install.sh @@ -645,7 +645,7 @@ install_dotnet() { download "$download_link" $zip_path || download_failed=true # if the download fails, download the legacy_download_link - if [ "$download_failed" = true && "$valid_legacy_download_link" = true ]; then + if [ "$download_failed" = true ] && [ "$valid_legacy_download_link" = true ]; then say "Cannot download: $download_link" zip_path=$(mktemp $temporary_file_template) say_verbose "Legacy zip path: $zip_path" From cda914a365b51d0903ea92aefd633879cdd0815c Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Tue, 13 Jun 2017 09:51:59 +0100 Subject: [PATCH 4/7] Update SpaTemplateVersion to 1.0.0-preview-000297 --- build/DependencyVersions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/DependencyVersions.props b/build/DependencyVersions.props index 2b60d26af..2436c07c4 100644 --- a/build/DependencyVersions.props +++ b/build/DependencyVersions.props @@ -28,7 +28,7 @@ 0.1.0-alpha-142 1.2.1-alpha-002133 0.2.0 - 1.0.0-preview-000286 + 1.0.0-preview-000297 timestamped From 3419a87d6fb620a2ea7981cdd6cd43db3b17cb5f Mon Sep 17 00:00:00 2001 From: William Li Date: Sun, 11 Jun 2017 15:27:48 -0700 Subject: [PATCH 5/7] Produce RPM packages Reference from https://github.com/dotnet/core-setup/tree/master/src/pkg/packaging/rpm The goal is to have parity with Debian, but does not publish to the feed When run the script with rhel, it will produce rpm file in the package step and publish rpm to the blob storage --- Microsoft.DotNet.Cli.sln | 2 + build/FileExtensions.props | 4 + build/Package.targets | 3 +- build/package/Installer.RPM.props | 8 + build/package/Installer.RPM.targets | 213 ++++++++++++ .../dotnet-cli-build/BuildFPMToolPreReqs.cs | 309 ++++++++++++++++++ packaging/rpm/dotnet-config.json | 38 +++ packaging/rpm/scripts/after_install_host.sh | 22 ++ packaging/rpm/scripts/after_remove_host.sh | 5 + packaging/rpm/templates/changelog | 2 + packaging/rpm/templates/copyright | 8 + scripts/docker/rhel/Dockerfile | 27 ++ scripts/dockerrun.sh | 3 + .../dotnet-uninstall-rpm-packages.sh | 39 +++ 14 files changed, 682 insertions(+), 1 deletion(-) create mode 100644 build/package/Installer.RPM.props create mode 100644 build/package/Installer.RPM.targets create mode 100644 build_projects/dotnet-cli-build/BuildFPMToolPreReqs.cs create mode 100644 packaging/rpm/dotnet-config.json create mode 100644 packaging/rpm/scripts/after_install_host.sh create mode 100644 packaging/rpm/scripts/after_remove_host.sh create mode 100644 packaging/rpm/templates/changelog create mode 100644 packaging/rpm/templates/copyright create mode 100644 scripts/docker/rhel/Dockerfile create mode 100755 scripts/obtain/uninstall/dotnet-uninstall-rpm-packages.sh diff --git a/Microsoft.DotNet.Cli.sln b/Microsoft.DotNet.Cli.sln index 26d5ac266..0f680db2f 100644 --- a/Microsoft.DotNet.Cli.sln +++ b/Microsoft.DotNet.Cli.sln @@ -68,6 +68,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "package", "package", "{FD7D build\package\Installer.DEB.targets = build\package\Installer.DEB.targets build\package\Installer.MSI.targets = build\package\Installer.MSI.targets build\package\Installer.PKG.targets = build\package\Installer.PKG.targets + build\package\Installer.RPM.props = build\package\Installer.RPM.props + build\package\Installer.RPM.targets = build\package\Installer.RPM.targets build\package\Layout.targets = build\package\Layout.targets build\package\Nupkg.targets = build\package\Nupkg.targets EndProjectSection diff --git a/build/FileExtensions.props b/build/FileExtensions.props index a6c6fca35..433a6dd6b 100644 --- a/build/FileExtensions.props +++ b/build/FileExtensions.props @@ -1,16 +1,20 @@ + True + .zip .tar.gz .msi .pkg .deb + .rpm .exe $(InstallerExtension) $(InstallerExtension) + .rpm lib diff --git a/build/Package.targets b/build/Package.targets index 17626cd45..8896bdb1f 100644 --- a/build/Package.targets +++ b/build/Package.targets @@ -6,10 +6,11 @@ + + DependsOnTargets="Init;Layout;GeneratePkgs;GenerateDebs;GenerateMsis;GenerateRpms" /> + + + rpm_config.json + $(RepoRoot)/packaging/rpm/templates + $(RepoRoot)/packaging/rpm/scripts + + \ No newline at end of file diff --git a/build/package/Installer.RPM.targets b/build/package/Installer.RPM.targets new file mode 100644 index 000000000..dcfd68c3d --- /dev/null +++ b/build/package/Installer.RPM.targets @@ -0,0 +1,213 @@ + + + + + + + + + True + + + + + + + + + $(SdkVersion) + $(OutputDirectory)/sdk + $(InstallerOutputDirectory)/$(DistroSpecificArtifactNameWithVersionCombinedHostHostFxrFrameworkSdk)$(InstallerExtension) + $(SdkInstallerFile) + $(RepoRoot)/Documentation/manpages + $(RepoRoot)/packaging/rpm/dotnet-config.json + $(IntermediateDirectory)/$(RpmPackageName)/$(RpmPackageVersion) + $(RpmIntermediatesDir)/debian-testResults.xml + /usr/share/dotnet + + + + $(RpmIntermediatesDir)/RpmLayoutDirectory/ + $(RpmLayoutDirectory)package_root + $(RpmLayoutPackageRoot)/sdk + $(RpmLayoutDirectory)docs + $(RpmLayoutDirectory)templates + $(RpmLayoutDirectory)scripts + + + + $(SdkVersion) + $(ArtifactNameCombinedHostHostFxrFrameworkSdk)-$(SdkRpmPackageVersion) + $(SharedFrameworkVersion) + dotnet-runtime-$(SharedFxRpmPackageVersion) + $(SharedFxRpmPackageName.ToLower()) + $(HostFxrVersion) + dotnet-hostfxr-$(HostFxrRpmPackageVersion) + $(HostFxrRpmPackageName.ToLower()) + dotnet-host + + + + $(RepoRoot)/test/EndToEnd/EndToEnd.csproj + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SharedFrameworkVersion) + + + $(SharedFxRpmPackageName) + + + $(SdkVersion) + + + $(SdkBrandName) + + + $(SdkRpmPackageName) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + True + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build_projects/dotnet-cli-build/BuildFPMToolPreReqs.cs b/build_projects/dotnet-cli-build/BuildFPMToolPreReqs.cs new file mode 100644 index 000000000..8aaf19dfa --- /dev/null +++ b/build_projects/dotnet-cli-build/BuildFPMToolPreReqs.cs @@ -0,0 +1,309 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using Microsoft.Build.Utilities; +using Microsoft.Build.Framework; +using System.IO; +using System.Linq; +using System.Text; +using Newtonsoft.Json; + +namespace Microsoft.DotNet.Build.Tasks +{ + /// + /// This task prepares the command line parameters for running a RPM build using FPM tool and also updates the copyright and changelog file tokens. + /// If parses various values from the config json by first reading it into a model and then builds the required string for parameters and passes it back. + /// + /// + public class BuildFPMToolPreReqs : Task + { + [Required] + public string InputDir { get; set; } + + [Required] + public string OutputDir { get; set; } + + [Required] + public string PackageVersion { get; set; } + + [Required] + public string ConfigJsonFile { get; set; } + + [Output] + public string FPMParameters { get; set; } + + public override bool Execute() + { + if (!File.Exists(ConfigJsonFile)) + { + throw new FileNotFoundException($"Expected file {ConfigJsonFile} was not found."); + } + + // Open the Config Json and read the values into the model + TextReader projectFileReader = File.OpenText(ConfigJsonFile); + string jsonFileText = projectFileReader.ReadToEnd(); + ConfigJson configJson = JsonConvert.DeserializeObject(jsonFileText); + + // Update the Changelog and Copyright files by replacing tokens with values from config json + UpdateChangelog(configJson, PackageVersion); + UpdateCopyright(configJson); + + // Build the full list of parameters + FPMParameters = BuildCmdParameters(configJson, PackageVersion); + Log.LogMessage(MessageImportance.Normal, "Generated RPM paramters: " + FPMParameters); + + return !Log.HasLoggedErrors; + } + + // Update the tokens in the changelog file from the config Json + private void UpdateChangelog(ConfigJson configJson, string package_version) + { + string changelogFile = Path.Combine(InputDir, "templates", "changelog"); + if (!File.Exists(changelogFile)) + { + throw new FileNotFoundException($"Expected file {changelogFile} was not found."); + } + string str = File.ReadAllText(changelogFile); + str = str.Replace("{PACKAGE_NAME}", configJson.Package_Name); + str = str.Replace("{PACKAGE_VERSION}", package_version); + str = str.Replace("{PACKAGE_REVISION}", configJson.Release.Package_Revision); + str = str.Replace("{CHANGELOG_MESSAGE}", configJson.Release.Changelog_Message); + str = str.Replace("{MAINTAINER_NAME}", configJson.Maintainer_Name); + str = str.Replace("{MAINTAINER_EMAIL}", configJson.Maintainer_Email); + // The date format needs to be like Wed May 17 2017 + str = str.Replace("{DATE}", DateTime.UtcNow.ToString("ddd MMM dd yyyy")); + File.WriteAllText(changelogFile, str); + } + + public void UpdateCopyright(ConfigJson configJson) + { + string copyrightFile = Path.Combine(InputDir, "templates", "copyright"); + if (!File.Exists(copyrightFile)) + { + throw new FileNotFoundException($"Expected file {copyrightFile} was not found."); + } + string str = File.ReadAllText(copyrightFile); + str = str.Replace("{COPYRIGHT_TEXT}", configJson.CopyRight); + str = str.Replace("{LICENSE_NAME}", configJson.License.Type); + str = str.Replace("{LICENSE_TEXT}", configJson.License.Full_Text); + File.WriteAllText(copyrightFile, str); + } + + private string BuildCmdParameters(ConfigJson configJson, string package_version) + { + // Parameter list that needs to be passed to FPM tool: + // -s : is the input source type(dir) --Static + // -t : is the type of package(rpm) --Static + // -n : is for the name of the package --JSON + // -v : is the version to give to the package --ARG + // -a : architecture --JSON + // -d : is for all dependent packages. This can be used multiple times to specify the dependencies of the package. --JSON + // --rpm-os : the operating system to target this rpm --Static + // --rpm-changelog : the changelog from FILEPATH contents --ARG + // --rpm-summary : it is the RPM summary that shows in the Title --JSON + // --description : it is the description for the package --JSON + // -p : The actual package name (with path) for your package. --ARG+JSON + // --conflicts : Other packages/versions this package conflicts with provided as CSV --JSON + // --directories : Recursively add directories as being owned by the package. --JSON + // --after-install : FILEPATH to the script to be run after install of the package --JSON + // --after-remove : FILEPATH to the script to be run after package removal --JSON + // --license : the licensing name for the package. This will include the license type in the meta-data for the package, but will not include the associated license file within the package itself. --JSON + // --iteration : the iteration to give to the package. This comes from the package_revision --JSON + // --url : url for this package. --JSON + // --verbose : Set verbose output for FPM tool --Static + // : Add all the folder mappings for packge_root, docs, man pages --Static + + var parameters = new List(); + parameters.Add("-s dir"); + parameters.Add("-t rpm"); + parameters.Add(string.Concat("-n ", configJson.Package_Name)); + parameters.Add(string.Concat("-v ", package_version)); + parameters.Add(string.Concat("-a ", configJson.Control.Architecture)); + + // Build the list of dependencies as -d -d + if (configJson.Rpm_Dependencies != null) + { + foreach (RpmDependency rpmdep in configJson.Rpm_Dependencies) + { + string dependency = ""; + if (rpmdep.Package_Name != "") + { + // If no version is specified then the dependency is just the package without >= check + if (rpmdep.Package_Version == "") + { + dependency = rpmdep.Package_Name; + } + else + { + dependency = string.Concat(rpmdep.Package_Name, " >= ", rpmdep.Package_Version); + } + } + if (dependency != "") parameters.Add(string.Concat("-d ", EscapeArg(dependency))); + } + } + + // Build the list of owned directories + if (configJson.Directories != null) + { + foreach (string dir in configJson.Directories) + { + if (dir != "") + { + parameters.Add(string.Concat("--directories ", EscapeArg(dir))); + } + } + } + + parameters.Add("--rpm-os linux"); + parameters.Add(string.Concat("--rpm-changelog ", + EscapeArg(Path.Combine(InputDir, "templates", "changelog")))); // Changelog File + parameters.Add(string.Concat("--rpm-summary ", EscapeArg(configJson.Short_Description))); + parameters.Add(string.Concat("--description ", EscapeArg(configJson.Long_Description))); + parameters.Add(string.Concat("--maintainer ", + EscapeArg(configJson.Maintainer_Name + " <" + configJson.Maintainer_Email + ">"))); + parameters.Add(string.Concat("--vendor ", EscapeArg(configJson.Vendor))); + parameters.Add(string.Concat("-p ", Path.Combine(OutputDir, configJson.Package_Name + ".rpm"))); + if (configJson.Package_Conflicts != null) + parameters.Add(string.Concat("--conflicts ", + EscapeArg(string.Join(",", configJson.Package_Conflicts)))); + if (configJson.After_Install_Source != null) + parameters.Add(string.Concat("--after-install ", + Path.Combine(InputDir, EscapeArg(configJson.After_Install_Source)))); + if (configJson.After_Remove_Source != null) + parameters.Add(string.Concat("--after-remove ", + Path.Combine(InputDir, EscapeArg(configJson.After_Remove_Source)))); + parameters.Add(string.Concat("--license ", EscapeArg(configJson.License.Type))); + parameters.Add(string.Concat("--iteration ", configJson.Release.Package_Revision)); + parameters.Add(string.Concat("--url ", "\"", EscapeArg(configJson.Homepage), "\"")); + parameters.Add("--verbose"); + + // Map all the payload directories as they need to install on the system + if (configJson.Install_Root != null) + parameters.Add(string.Concat(Path.Combine(InputDir, "package_root/="), + configJson.Install_Root)); // Package Files + if (configJson.Install_Man != null) + parameters.Add(string.Concat(Path.Combine(InputDir, "docs", "host/="), + configJson.Install_Man)); // Man Pages + if (configJson.Install_Doc != null) + parameters.Add(string.Concat(Path.Combine(InputDir, "templates", "copyright="), + configJson.Install_Doc)); // CopyRight File + + return string.Join(" ", parameters); + } + + private string EscapeArg(string arg) + { + var sb = new StringBuilder(); + + bool quoted = ShouldSurroundWithQuotes(arg); + if (quoted) sb.Append("\""); + + for (int i = 0; i < arg.Length; ++i) + { + var backslashCount = 0; + + // Consume All Backslashes + while (i < arg.Length && arg[i] == '\\') + { + backslashCount++; + i++; + } + + // Escape any backslashes at the end of the arg + // This ensures the outside quote is interpreted as + // an argument delimiter + if (i == arg.Length) + { + sb.Append('\\', 2 * backslashCount); + } + + // Escape any preceding backslashes and the quote + else if (arg[i] == '"') + { + sb.Append('\\', (2 * backslashCount) + 1); + sb.Append('"'); + } + + // Output any consumed backslashes and the character + else + { + sb.Append('\\', backslashCount); + sb.Append(arg[i]); + } + } + + if (quoted) sb.Append("\""); + + return sb.ToString(); + } + + private bool ShouldSurroundWithQuotes(string argument) + { + // Don't quote already quoted strings + if (argument.StartsWith("\"", StringComparison.Ordinal) && + argument.EndsWith("\"", StringComparison.Ordinal)) + { + return false; + } + + // Only quote if whitespace exists in the string + if (argument.Contains(" ") || argument.Contains("\t") || argument.Contains("\n")) + { + return true; + } + return false; + } + } + + /// + /// Model classes for reading and storing the JSON. + /// + public class ConfigJson + { + public string Maintainer_Name { get; set; } + public string Maintainer_Email { get; set; } + public string Vendor { get; set; } + public string Package_Name { get; set; } + public string Install_Root { get; set; } + public string Install_Doc { get; set; } + public string Install_Man { get; set; } + public string Short_Description { get; set; } + public string Long_Description { get; set; } + public string Homepage { get; set; } + public string CopyRight { get; set; } + public Release Release { get; set; } + public Control Control { get; set; } + public License License { get; set; } + public List Rpm_Dependencies { get; set; } + public List Package_Conflicts { get; set; } + public List Directories { get; set; } + public string After_Install_Source { get; set; } + public string After_Remove_Source { get; set; } + } + + public class Release + { + public string Package_Version { get; set; } + public string Package_Revision { get; set; } + public string Changelog_Message { get; set; } + } + + public class Control + { + public string Architecture { get; set; } + } + + public class License + { + public string Type { get; set; } + public string Full_Text { get; set; } + } + + public class RpmDependency + { + public string Package_Name { get; set; } + public string Package_Version { get; set; } + } +} \ No newline at end of file diff --git a/packaging/rpm/dotnet-config.json b/packaging/rpm/dotnet-config.json new file mode 100644 index 000000000..6ed9d53ad --- /dev/null +++ b/packaging/rpm/dotnet-config.json @@ -0,0 +1,38 @@ +{ + "maintainer_name": "Microsoft", + "maintainer_email": "dotnetcore@microsoft.com", + "vendor": ".NET Foundation", + "package_name": "%SDK_RPM_PACKAGE_NAME%", + "install_root": "/usr/share/dotnet", + "install_doc": "/usr/share/doc/%SDK_RPM_PACKAGE_NAME%/", + "short_description": "%CLI_SDK_BRAND_NAME% %SDK_NUGET_VERSION%", + "long_description": ".NET Core is a development platform that you can use to build command-line applications, microservices and modern websites. It is open source, cross-platform and is supported by Microsoft. We hope you enjoy using it! If you do, please consider joining the active community of developers that are contributing to the project on GitHub (https://github.com/dotnet/core). We happily accept issues and PRs.", + "homepage": "https://github.com/dotnet/core", + "release": { + "package_version": "1.0.0.0", + "package_revision": "1", + "changelog_message": "Bootstrap loop package" + }, + "control": { + "architecture": "amd64" + }, + "copyright": "2015 Microsoft", + "license": { + "type": "MIT", + "full_text": "Copyright (c) 2015 Microsoft\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE." + }, + "rpm_dependencies": [ + { + "package_name": "%SHARED_HOST_RPM_NAME%", + "package_version": "%SHARED_HOST_RPM_VERSION%" + } + ], + "directories": [ + "/usr/share/dotnet/sdk", + "/usr/share/dotnet/store", + "/usr/share/dotnet/additionalDeps", + "/usr/share/doc/%SDK_RPM_PACKAGE_NAME%" + ], + "after_install_source": "scripts/after_install_host.sh", + "after_remove_source": "scripts/after_remove_host.sh" +} \ No newline at end of file diff --git a/packaging/rpm/scripts/after_install_host.sh b/packaging/rpm/scripts/after_install_host.sh new file mode 100644 index 000000000..dc9f6c0d9 --- /dev/null +++ b/packaging/rpm/scripts/after_install_host.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# +echo "This software may collect information about you and your use of the software, and send that to Microsoft." +echo "Please visit http://aka.ms/dotnet-cli-eula for more information." + +# Run 'dotnet new' as the user to trigger the first time experience to initialize the cache +echo "Welcome to .NET Core! +--------------------- +Learn more about .NET Core @ https://aka.ms/dotnet-docs. Use dotnet --help to see available commands or go to https://aka.ms/dotnet-cli-docs. + +.NET Core Tools Telemetry +-------------- +The .NET Core Tools include a telemetry feature that collects usage information. It is important that the .NET Team understands how the tools are being used so that we can improve them. + +The data collected is anonymous and will be published in an aggregated form for use by both Microsoft and community engineers under the Creative Commons Attribution License. + +The .NET Core Tools telemetry feature is enabled by default. You can opt-out of the telemetry feature by setting an environment variable DOTNET_CLI_TELEMETRY_OPTOUT (for example, 'export' on macOS/Linux, 'set' on Windows) to true (for example, 'true', 1). You can read more about .NET Core tools telemetry at https://aka.ms/dotnet-cli-telemetry." + +su - $SUDO_USER -c "dotnet new > /dev/null 2>&1 || true" diff --git a/packaging/rpm/scripts/after_remove_host.sh b/packaging/rpm/scripts/after_remove_host.sh new file mode 100644 index 000000000..bb0cdef0a --- /dev/null +++ b/packaging/rpm/scripts/after_remove_host.sh @@ -0,0 +1,5 @@ +#!/bin/sh +# +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# diff --git a/packaging/rpm/templates/changelog b/packaging/rpm/templates/changelog new file mode 100644 index 000000000..e61dbc607 --- /dev/null +++ b/packaging/rpm/templates/changelog @@ -0,0 +1,2 @@ +* {DATE} {MAINTAINER_NAME} <{MAINTAINER_EMAIL}> - {PACKAGE_VERSION}-{PACKAGE_REVISION} +- {CHANGELOG_MESSAGE} diff --git a/packaging/rpm/templates/copyright b/packaging/rpm/templates/copyright new file mode 100644 index 000000000..a336435d6 --- /dev/null +++ b/packaging/rpm/templates/copyright @@ -0,0 +1,8 @@ +Comment: Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information. + +Files: * +Copyright: {COPYRIGHT_TEXT} +License: {LICENSE_NAME} + +License: {LICENSE_NAME} + {LICENSE_TEXT} \ No newline at end of file diff --git a/scripts/docker/rhel/Dockerfile b/scripts/docker/rhel/Dockerfile new file mode 100644 index 000000000..17ad16cf5 --- /dev/null +++ b/scripts/docker/rhel/Dockerfile @@ -0,0 +1,27 @@ +# +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# + +# Dockerfile that creates a container suitable to build dotnet-cli +FROM microsoft/dotnet-buildtools-prereqs:rhel-7-rpmpkg-c982313-20174116044113 + +# Install from sudo main package TODO This package needs to be mirrored +RUN yum install -y https://www.sudo.ws/sudo/dist/packages/RHEL/7/sudo-1.8.20-3.el7.x86_64.rpm \ + && yum clean all + +# Setup User to match Host User, and give superuser permissions +ARG USER_ID=0 +RUN useradd -m code_executor -u ${USER_ID} -g root +RUN echo 'code_executor ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers + +# With the User Change, we need to change permssions on these directories +RUN chmod -R a+rwx /usr/local +RUN chmod -R a+rwx /home +RUN chown root:root /usr/bin/sudo && chmod 4755 /usr/bin/sudo + +# Set user to the one we just created +USER ${USER_ID} + +# Set working directory +WORKDIR /opt/code diff --git a/scripts/dockerrun.sh b/scripts/dockerrun.sh index 897aeb907..abdaec636 100755 --- a/scripts/dockerrun.sh +++ b/scripts/dockerrun.sh @@ -67,6 +67,9 @@ if [ -z "$DOCKERFILE" ]; then elif [ "$(cat /etc/*-release | grep -cim1 centos)" -eq 1 ]; then echo "Detected current OS as CentOS, using 'centos' image" export DOCKERFILE=scripts/docker/centos + elif [ "$(cat /etc/*-release | grep -cim1 rhel)" -eq 1 ]; then + echo "Detected current OS as rhel, using 'rhel' image" + export DOCKERFILE=scripts/docker/rhel elif [ "$(cat /etc/*-release | grep -cim1 debian)" -eq 1 ]; then echo "Detected current OS as Debian, using 'debian' image" export DOCKERFILE=scripts/docker/debian diff --git a/scripts/obtain/uninstall/dotnet-uninstall-rpm-packages.sh b/scripts/obtain/uninstall/dotnet-uninstall-rpm-packages.sh new file mode 100755 index 000000000..d3572ec38 --- /dev/null +++ b/scripts/obtain/uninstall/dotnet-uninstall-rpm-packages.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# +# Copyright (c) .NET Foundation and contributors. All rights reserved. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +current_userid=$(id -u) +if [ $current_userid -ne 0 ]; then + echo "$(basename "$0") uninstallation script requires superuser privileges to run" + exit 1 +fi + +host_package_name="dotnet-host" + +remove_all(){ + yum remove -y $host_package_name +} + +is_dotnet_host_installed(){ + local out="$(yum list installed | grep $host_package_name)" + [ -z "$out" ] +} + +is_dotnet_host_installed +[ "$?" -eq 0 ] && echo "Unable to find dotnet installation to remove." >&2 \ + && exit 0 + +remove_all +[ "$?" -ne 0 ] && echo "Failed to remove dotnet packages." >&2 && exit 1 + +is_dotnet_host_installed +[ "$?" -ne 0 ] && \ + echo "dotnet package removal succeeded but appear to still be installed. Please file an issue at https://github.com/dotnet/cli" >&2 && \ + exit 1 + +echo "dotnet package removal succeeded." >&2 +exit 0 From a01ab8f362b6656c0e1708c0a4d4c6abcb75ad62 Mon Sep 17 00:00:00 2001 From: William Li Date: Tue, 13 Jun 2017 12:42:51 -0700 Subject: [PATCH 6/7] Fix Debian VSTS/packing failure One place missing replacement of OsName => HostOsName. But the root cause is $(HostOSName)' == 'ubuntu' OR '$(HostOSName)' == 'debian' is duplicated too much. Extract IsDebianBaseDistro for it --- build/FileExtensions.props | 8 ++++---- build/package/Installer.DEB.proj | 6 +++--- build/publish/PublishDebian.targets | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/build/FileExtensions.props b/build/FileExtensions.props index 433a6dd6b..fbbca63a7 100644 --- a/build/FileExtensions.props +++ b/build/FileExtensions.props @@ -1,6 +1,6 @@ - + True True .zip @@ -8,13 +8,13 @@ .msi .pkg - .deb + .deb .rpm .exe $(InstallerExtension) - $(InstallerExtension) - .rpm + $(InstallerExtension) + $(InstallerExtension) lib diff --git a/build/package/Installer.DEB.proj b/build/package/Installer.DEB.proj index 32721227c..34e5e5978 100644 --- a/build/package/Installer.DEB.proj +++ b/build/package/Installer.DEB.proj @@ -10,11 +10,11 @@ TestDebuild; BuildSdkDeb; TestSdkDeb;" - Condition=" '$(HostOSName)' == 'ubuntu' OR '$(HostOSName)' == 'debian' " + Condition=" '$(IsDebianBaseDistro)' == 'True' " Outputs="@(GeneratedInstallers)"/> + From 5e5b4de0b968ac02c1a212427682a04cd6010e2b Mon Sep 17 00:00:00 2001 From: Livar Cunha Date: Tue, 13 Jun 2017 00:04:25 -0700 Subject: [PATCH 7/7] Do no pass down to implicit restore the framework option. --- .../TestLibrary/Helper.cs | 24 +++++++++++++++++ .../TestLibrary/TestLibrary.csproj | 7 +++++ .../XUnitProject/UnitTest1.cs | 24 +++++++++++++++++ .../XUnitProject/XUnitProject.csproj | 17 ++++++++++++ .../MultiTFMTestApp/MultiTFMTestApp.csproj | 12 +++++++++ .../MultiTFMTestApp/Program.cs | 15 +++++++++++ src/dotnet/commands/RestoringCommand.cs | 27 +++++++++++-------- .../commands/dotnet-build/BuildCommand.cs | 16 ++++++++--- .../commands/dotnet-pack/PackCommand.cs | 16 ++++++++--- src/dotnet/commands/dotnet-publish/Program.cs | 16 ++++++++--- src/dotnet/commands/dotnet-run/RunCommand.cs | 3 ++- src/dotnet/commands/dotnet-test/Program.cs | 16 ++++++++--- .../GivenDotnetBuildBuildsCsproj.cs | 18 +++++++++++++ .../GivenDotnetPublishPublishesProjects.cs | 18 +++++++++++++ .../GivenDotnetRunRunsCsProj.cs | 19 +++++++++++++ ...ildsAndRunsTestFromCsprojForMultipleTFM.cs | 18 +++++++++++++ 16 files changed, 242 insertions(+), 24 deletions(-) create mode 100644 TestAssets/DesktopTestProjects/MultiTFMXunitProject/TestLibrary/Helper.cs create mode 100644 TestAssets/DesktopTestProjects/MultiTFMXunitProject/TestLibrary/TestLibrary.csproj create mode 100644 TestAssets/DesktopTestProjects/MultiTFMXunitProject/XUnitProject/UnitTest1.cs create mode 100644 TestAssets/DesktopTestProjects/MultiTFMXunitProject/XUnitProject/XUnitProject.csproj create mode 100644 TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/MultiTFMTestApp/MultiTFMTestApp.csproj create mode 100644 TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/MultiTFMTestApp/Program.cs diff --git a/TestAssets/DesktopTestProjects/MultiTFMXunitProject/TestLibrary/Helper.cs b/TestAssets/DesktopTestProjects/MultiTFMXunitProject/TestLibrary/Helper.cs new file mode 100644 index 000000000..67b32cfdb --- /dev/null +++ b/TestAssets/DesktopTestProjects/MultiTFMXunitProject/TestLibrary/Helper.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace TestLibrary +{ + public static class Helper + { + /// + /// Gets the message from the helper. This comment is here to help test XML documentation file generation, please do not remove it. + /// + /// A message + public static string GetMessage() + { + return "This string came from the test library!"; + } + + public static void SayHi() + { + Console.WriteLine("Hello there!"); + } + } +} \ No newline at end of file diff --git a/TestAssets/DesktopTestProjects/MultiTFMXunitProject/TestLibrary/TestLibrary.csproj b/TestAssets/DesktopTestProjects/MultiTFMXunitProject/TestLibrary/TestLibrary.csproj new file mode 100644 index 000000000..9f5c4f4ab --- /dev/null +++ b/TestAssets/DesktopTestProjects/MultiTFMXunitProject/TestLibrary/TestLibrary.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/TestAssets/DesktopTestProjects/MultiTFMXunitProject/XUnitProject/UnitTest1.cs b/TestAssets/DesktopTestProjects/MultiTFMXunitProject/XUnitProject/UnitTest1.cs new file mode 100644 index 000000000..f86168c43 --- /dev/null +++ b/TestAssets/DesktopTestProjects/MultiTFMXunitProject/XUnitProject/UnitTest1.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using TestLibrary; +using Xunit; + +namespace TestNamespace +{ + public class VSTestXunitTests + { + [Fact] + public void VSTestXunitPassTest() + { + Assert.Equal("This string came from the test library!", Helper.GetMessage()); + } + + [Fact] + public void VSTestXunitFailTest() + { + Assert.Equal(2, 2); + } + } +} diff --git a/TestAssets/DesktopTestProjects/MultiTFMXunitProject/XUnitProject/XUnitProject.csproj b/TestAssets/DesktopTestProjects/MultiTFMXunitProject/XUnitProject/XUnitProject.csproj new file mode 100644 index 000000000..2ae7ce025 --- /dev/null +++ b/TestAssets/DesktopTestProjects/MultiTFMXunitProject/XUnitProject/XUnitProject.csproj @@ -0,0 +1,17 @@ + + + + + net461;netcoreapp2.0 + + + + + + + + + + + + diff --git a/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/MultiTFMTestApp/MultiTFMTestApp.csproj b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/MultiTFMTestApp/MultiTFMTestApp.csproj new file mode 100644 index 000000000..cc54ec8ae --- /dev/null +++ b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/MultiTFMTestApp/MultiTFMTestApp.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp2.0;net461 + + + + + + + diff --git a/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/MultiTFMTestApp/Program.cs b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/MultiTFMTestApp/Program.cs new file mode 100644 index 000000000..a168da37c --- /dev/null +++ b/TestAssets/DesktopTestProjects/NETFrameworkReferenceNETStandard20/MultiTFMTestApp/Program.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace TestApp +{ + class Program + { + public static void Main(string[] args) + { + Console.WriteLine(TestLibrary.Helper.GetMessage()); + } + } +} diff --git a/src/dotnet/commands/RestoringCommand.cs b/src/dotnet/commands/RestoringCommand.cs index 67c114577..0239fd69b 100644 --- a/src/dotnet/commands/RestoringCommand.cs +++ b/src/dotnet/commands/RestoringCommand.cs @@ -12,31 +12,36 @@ namespace Microsoft.DotNet.Tools { private bool NoRestore { get; } - private IEnumerable ArgsToForward { get; } + private IEnumerable ParsedArguments { get; } + + private IEnumerable TrailingArguments { get; } private IEnumerable ArgsToForwardToRestore() { - var restoreArguments = ArgsToForward.Where(a => - !a.StartsWith("/t:") && - !a.StartsWith("/target:") && - !a.StartsWith("/ConsoleLoggerParameters:") && - !a.StartsWith("/clp:")); + var restoreArguments = ParsedArguments.Where(a => + !a.StartsWith("/p:TargetFramework")); - if (!restoreArguments.Any(a => a.StartsWith("/v:") || a.StartsWith("/verbosity:"))) + if (!restoreArguments.Any(a => a.StartsWith("/verbosity:"))) { - restoreArguments = restoreArguments.Concat(new string[] { "/v:q" }); + restoreArguments = restoreArguments.Concat(new string[] { "/verbosity:q" }); } - return restoreArguments; + return restoreArguments.Concat(TrailingArguments); } private bool ShouldRunImplicitRestore => !NoRestore; - public RestoringCommand(IEnumerable msbuildArgs, bool noRestore, string msbuildPath = null) + public RestoringCommand( + IEnumerable msbuildArgs, + IEnumerable parsedArguments, + IEnumerable trailingArguments, + bool noRestore, + string msbuildPath = null) : base(msbuildArgs, msbuildPath) { NoRestore = noRestore; - ArgsToForward = msbuildArgs; + ParsedArguments = parsedArguments; + TrailingArguments = trailingArguments; } public override int Execute() diff --git a/src/dotnet/commands/dotnet-build/BuildCommand.cs b/src/dotnet/commands/dotnet-build/BuildCommand.cs index 95539c662..c7127353b 100644 --- a/src/dotnet/commands/dotnet-build/BuildCommand.cs +++ b/src/dotnet/commands/dotnet-build/BuildCommand.cs @@ -15,8 +15,13 @@ namespace Microsoft.DotNet.Tools.Build { public class BuildCommand : RestoringCommand { - public BuildCommand(IEnumerable msbuildArgs, bool noRestore, string msbuildPath = null) - : base(msbuildArgs, noRestore, msbuildPath) + public BuildCommand( + IEnumerable msbuildArgs, + IEnumerable userDefinedArguments, + IEnumerable trailingArguments, + bool noRestore, + string msbuildPath = null) + : base(msbuildArgs, userDefinedArguments, trailingArguments, noRestore, msbuildPath) { } @@ -49,7 +54,12 @@ namespace Microsoft.DotNet.Tools.Build bool noRestore = appliedBuildOptions.HasOption("--no-restore"); - return new BuildCommand(msbuildArgs, noRestore, msbuildPath); + return new BuildCommand( + msbuildArgs, + appliedBuildOptions.OptionValuesToBeForwarded(), + appliedBuildOptions.Arguments, + noRestore, + msbuildPath); } public static int Run(string[] args) diff --git a/src/dotnet/commands/dotnet-pack/PackCommand.cs b/src/dotnet/commands/dotnet-pack/PackCommand.cs index 0c7146356..a781fe254 100644 --- a/src/dotnet/commands/dotnet-pack/PackCommand.cs +++ b/src/dotnet/commands/dotnet-pack/PackCommand.cs @@ -14,8 +14,13 @@ namespace Microsoft.DotNet.Tools.Pack { public class PackCommand : RestoringCommand { - public PackCommand(IEnumerable msbuildArgs, bool noRestore, string msbuildPath = null) - : base(msbuildArgs, noRestore, msbuildPath) + public PackCommand( + IEnumerable msbuildArgs, + IEnumerable userDefinedArguments, + IEnumerable trailingArguments, + bool noRestore, + string msbuildPath = null) + : base(msbuildArgs, userDefinedArguments, trailingArguments, noRestore, msbuildPath) { } @@ -40,7 +45,12 @@ namespace Microsoft.DotNet.Tools.Pack bool noRestore = parsedPack.HasOption("--no-restore"); - return new PackCommand(msbuildArgs, noRestore, msbuildPath); + return new PackCommand( + msbuildArgs, + parsedPack.OptionValuesToBeForwarded(), + parsedPack.Arguments, + noRestore, + msbuildPath); } public static int Run(string[] args) diff --git a/src/dotnet/commands/dotnet-publish/Program.cs b/src/dotnet/commands/dotnet-publish/Program.cs index d4337e107..f44ecb2ca 100644 --- a/src/dotnet/commands/dotnet-publish/Program.cs +++ b/src/dotnet/commands/dotnet-publish/Program.cs @@ -13,8 +13,13 @@ namespace Microsoft.DotNet.Tools.Publish { public class PublishCommand : RestoringCommand { - private PublishCommand(IEnumerable msbuildArgs, bool noRestore, string msbuildPath = null) - : base(msbuildArgs, noRestore, msbuildPath) + private PublishCommand( + IEnumerable msbuildArgs, + IEnumerable userDefinedArguments, + IEnumerable trailingArguments, + bool noRestore, + string msbuildPath = null) + : base(msbuildArgs, userDefinedArguments, trailingArguments, noRestore, msbuildPath) { } @@ -40,7 +45,12 @@ namespace Microsoft.DotNet.Tools.Publish bool noRestore = appliedPublishOption.HasOption("--no-restore"); - return new PublishCommand(msbuildArgs, noRestore, msbuildPath); + return new PublishCommand( + msbuildArgs, + appliedPublishOption.OptionValuesToBeForwarded(), + appliedPublishOption.Arguments, + noRestore, + msbuildPath); } public static int Run(string[] args) diff --git a/src/dotnet/commands/dotnet-run/RunCommand.cs b/src/dotnet/commands/dotnet-run/RunCommand.cs index 38ef571c5..d760a8690 100644 --- a/src/dotnet/commands/dotnet-run/RunCommand.cs +++ b/src/dotnet/commands/dotnet-run/RunCommand.cs @@ -145,7 +145,8 @@ namespace Microsoft.DotNet.Tools.Run buildArgs.AddRange(RestoreArgs); - var buildResult = new RestoringCommand(buildArgs, NoRestore).Execute(); + var buildResult = + new RestoringCommand(buildArgs, RestoreArgs, new [] { Project }, NoRestore).Execute(); if (buildResult != 0) { Reporter.Error.WriteLine(); diff --git a/src/dotnet/commands/dotnet-test/Program.cs b/src/dotnet/commands/dotnet-test/Program.cs index 805639d9e..d714d2f9e 100644 --- a/src/dotnet/commands/dotnet-test/Program.cs +++ b/src/dotnet/commands/dotnet-test/Program.cs @@ -16,8 +16,13 @@ namespace Microsoft.DotNet.Tools.Test { public class TestCommand : RestoringCommand { - public TestCommand(IEnumerable msbuildArgs, bool noRestore, string msbuildPath = null) - : base(msbuildArgs, noRestore, msbuildPath) + public TestCommand( + IEnumerable msbuildArgs, + IEnumerable userDefinedArguments, + IEnumerable trailingArguments, + bool noRestore, + string msbuildPath = null) + : base(msbuildArgs, userDefinedArguments, trailingArguments, noRestore, msbuildPath) { } @@ -67,7 +72,12 @@ namespace Microsoft.DotNet.Tools.Test bool noRestore = parsedTest.HasOption("--no-restore"); - return new TestCommand(msbuildArgs, noRestore, msbuildPath); + return new TestCommand( + msbuildArgs, + parsedTest.OptionValuesToBeForwarded(), + parsedTest.Arguments, + noRestore, + msbuildPath); } public static int Run(string[] args) diff --git a/test/dotnet-build.Tests/GivenDotnetBuildBuildsCsproj.cs b/test/dotnet-build.Tests/GivenDotnetBuildBuildsCsproj.cs index 29049f238..30b16d53d 100644 --- a/test/dotnet-build.Tests/GivenDotnetBuildBuildsCsproj.cs +++ b/test/dotnet-build.Tests/GivenDotnetBuildBuildsCsproj.cs @@ -4,6 +4,7 @@ using System; using System.IO; using FluentAssertions; +using Microsoft.DotNet.TestFramework; using Microsoft.DotNet.Tools.Test.Utilities; using Xunit; using System.Linq; @@ -52,6 +53,23 @@ namespace Microsoft.DotNet.Cli.Build.Tests .Should().Pass(); } + [Fact] + public void ItCanBuildAMultiTFMProjectWithImplicitRestore() + { + var testInstance = TestAssets.Get( + TestAssetKinds.DesktopTestProjects, + "NETFrameworkReferenceNETStandard20") + .CreateInstance() + .WithSourceFiles(); + + string projectDirectory = Path.Combine(testInstance.Root.FullName, "MultiTFMTestApp"); + + new BuildCommand() + .WithWorkingDirectory(projectDirectory) + .Execute("--framework netcoreapp2.0") + .Should().Pass(); + } + [Fact] public void ItDoesNotImplicitlyRestoreAProjectWhenBuildingWithTheNoRestoreOption() { diff --git a/test/dotnet-publish.Tests/GivenDotnetPublishPublishesProjects.cs b/test/dotnet-publish.Tests/GivenDotnetPublishPublishesProjects.cs index 80985a5a4..ddad9808c 100644 --- a/test/dotnet-publish.Tests/GivenDotnetPublishPublishesProjects.cs +++ b/test/dotnet-publish.Tests/GivenDotnetPublishPublishesProjects.cs @@ -8,6 +8,7 @@ using System.Runtime.InteropServices; using FluentAssertions; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.PlatformAbstractions; +using Microsoft.DotNet.TestFramework; using Microsoft.DotNet.Tools.Test.Utilities; using Xunit; @@ -60,6 +61,23 @@ namespace Microsoft.DotNet.Cli.Publish.Tests .Should().Pass(); } + [Fact] + public void ItCanPublishAMultiTFMProjectWithImplicitRestore() + { + var testInstance = TestAssets.Get( + TestAssetKinds.DesktopTestProjects, + "NETFrameworkReferenceNETStandard20") + .CreateInstance() + .WithSourceFiles(); + + string projectDirectory = Path.Combine(testInstance.Root.FullName, "MultiTFMTestApp"); + + new PublishCommand() + .WithWorkingDirectory(projectDirectory) + .Execute("--framework netcoreapp2.0") + .Should().Pass(); + } + [Fact] public void ItDoesNotImplicitlyRestoreAProjectWhenPublishingWithTheNoRestoreOption() { diff --git a/test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs b/test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs index 21d1dee1a..88cc49dcf 100644 --- a/test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs +++ b/test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs @@ -3,6 +3,7 @@ using System.IO; using FluentAssertions; +using Microsoft.DotNet.TestFramework; using Microsoft.DotNet.Tools.Test.Utilities; using Xunit; @@ -54,6 +55,24 @@ namespace Microsoft.DotNet.Cli.Run.Tests .And.HaveStdOutContaining("Hello World!"); } + [Fact] + public void ItCanRunAMultiTFMProjectWithImplicitRestore() + { + var testInstance = TestAssets.Get( + TestAssetKinds.DesktopTestProjects, + "NETFrameworkReferenceNETStandard20") + .CreateInstance() + .WithSourceFiles(); + + string projectDirectory = Path.Combine(testInstance.Root.FullName, "MultiTFMTestApp"); + + new RunCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput("--framework netcoreapp2.0") + .Should().Pass() + .And.HaveStdOutContaining("This string came from the test library!"); + } + [Fact] public void ItDoesNotImplicitlyRestoreAProjectWhenRunningWithTheNoRestoreOption() { diff --git a/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestFromCsprojForMultipleTFM.cs b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestFromCsprojForMultipleTFM.cs index a111db3c3..30f1ee12f 100644 --- a/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestFromCsprojForMultipleTFM.cs +++ b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestFromCsprojForMultipleTFM.cs @@ -7,6 +7,7 @@ using Microsoft.DotNet.TestFramework; using Microsoft.DotNet.Cli.Utils; using System.IO; using System; +using Xunit; namespace Microsoft.DotNet.Cli.Test.Tests { @@ -75,5 +76,22 @@ namespace Microsoft.DotNet.Cli.Test.Tests result.StdOut.Should().Contain("Failed TestNamespace.VSTestXunitTests.VSTestXunitFailTestNetCoreApp"); result.ExitCode.Should().Be(1); } + + [Fact] + public void ItCanTestAMultiTFMProjectWithImplicitRestore() + { + var testInstance = TestAssets.Get( + TestAssetKinds.DesktopTestProjects, + "MultiTFMXunitProject") + .CreateInstance() + .WithSourceFiles(); + + string projectDirectory = Path.Combine(testInstance.Root.FullName, "XUnitProject"); + + new DotnetTestCommand() + .WithWorkingDirectory(projectDirectory) + .ExecuteWithCapturedOutput($"{TestBase.ConsoleLoggerOutputNormal} --framework netcoreapp2.0") + .Should().Pass(); + } } }