From 1d5de1533ad35b39f776701c4d22b68a3def2dd8 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Thu, 21 Nov 2019 23:13:26 +0100 Subject: [PATCH] Libraries testing (#178) * Add Libraries Testing framework package as inline The existing package Microsoft.DotNet.CoreFxTesting lived in Arcade because of no infrastructure being available to compile local tasks in the repository. As the runtime repository now offers that we can inline the testing framework. * Hardcode configuration for installer.tasks * Update ReportGenerator global tool version * Add vstest support * Update binary serialization blobs --- .config/dotnet-tools.json | 2 +- .gitignore | 1 + Directory.Build.props | 15 +- NuGet.config | 1 + docs/libraries/building/code-coverage.md | 2 +- docs/libraries/project-docs/developer-guide.md | 2 +- eng/Build.props | 3 +- eng/Version.Details.xml | 4 + eng/Versions.props | 6 +- eng/pipelines/libraries/corefx-base.yml | 2 +- eng/pipelines/libraries/windows.yml | 2 +- eng/references.targets | 30 +++- eng/testing/RunnerTemplate.Unix.txt | 200 +++++++++++++++++++++ eng/testing/RunnerTemplate.Windows.txt | 74 ++++++++ eng/testing/coverage.props | 14 ++ eng/testing/coverage.targets | 62 +++++++ eng/testing/launchSettings.json | 10 ++ eng/testing/launchSettings.targets | 22 +++ eng/testing/netfx.exe.config | 7 + eng/testing/runtimeConfiguration.targets | 45 +++++ eng/testing/tests.props | 17 ++ eng/testing/tests.targets | 127 +++++++++++++ eng/testing/xunit/vstest.props | 20 +++ eng/testing/xunit/vstest.targets | 32 ++++ eng/testing/xunit/xunit.console.props | 21 +++ eng/testing/xunit/xunit.console.targets | 75 ++++++++ eng/testing/xunit/xunit.props | 18 ++ eng/testing/xunit/xunit.runner.json | 5 + eng/testing/xunit/xunit.targets | 31 ++++ global.json | 1 - src/coreclr/tests/scripts/run-corefx-tests.py | 2 +- src/libraries/Directory.Build.props | 90 +++++----- src/libraries/Directory.Build.targets | 16 +- ...Configuration.ConfigurationManager.Tests.csproj | 5 +- .../tests/BinaryFormatterTests.cs | 4 +- src/libraries/pkg/test/testPackages.proj | 6 +- src/libraries/pretest.proj | 22 +++ src/libraries/restore/runtime/runtime.depproj | 1 - src/libraries/tests.proj | 22 ++- .../installer.tasks/GenerateFileVersionProps.cs | 35 ++-- .../tasks/installer.tasks/GenerateRunScript.cs | 114 ++++++++++++ .../GenerateTestSharedFrameworkDepsFile.cs | 132 ++++++++++++++ .../tasks/installer.tasks/installer.tasks.csproj | 53 +++--- 43 files changed, 1228 insertions(+), 125 deletions(-) create mode 100644 eng/testing/RunnerTemplate.Unix.txt create mode 100644 eng/testing/RunnerTemplate.Windows.txt create mode 100644 eng/testing/coverage.props create mode 100644 eng/testing/coverage.targets create mode 100644 eng/testing/launchSettings.json create mode 100644 eng/testing/launchSettings.targets create mode 100644 eng/testing/netfx.exe.config create mode 100644 eng/testing/runtimeConfiguration.targets create mode 100644 eng/testing/tests.props create mode 100644 eng/testing/tests.targets create mode 100644 eng/testing/xunit/vstest.props create mode 100644 eng/testing/xunit/vstest.targets create mode 100644 eng/testing/xunit/xunit.console.props create mode 100644 eng/testing/xunit/xunit.console.targets create mode 100644 eng/testing/xunit/xunit.props create mode 100644 eng/testing/xunit/xunit.runner.json create mode 100644 eng/testing/xunit/xunit.targets create mode 100644 tools-local/tasks/installer.tasks/GenerateRunScript.cs create mode 100644 tools-local/tasks/installer.tasks/GenerateTestSharedFrameworkDepsFile.cs diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 8d72881..d18a21c 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -9,7 +9,7 @@ ] }, "dotnet-reportgenerator-globaltool": { - "version": "4.3.0", + "version": "4.3.6", "commands": [ "reportgenerator" ] diff --git a/.gitignore b/.gitignore index f39c943..bf23e45 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,7 @@ cross/android-rootfs/ #NUNIT *.VisualState.xml TestResult.xml +testResults.xml # Build Results of an ATL Project [Dd]ebugPS/ diff --git a/Directory.Build.props b/Directory.Build.props index c902460..d7e46d2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -11,18 +11,23 @@ - - + + + $(MSBuildThisFileDirectory) $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'eng')) + $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'artifacts')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsDir)', 'bin')) + + $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'tools-local')) $([MSBuild]::NormalizeDirectory('$(RepoToolsLocalDir)', 'tasks')) - $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'installer.tasks', '$(Configuration)')) - $([MSBuild]::NormalizePath('$(InstallerTasksOutputPath)', 'netstandard2.0', 'installer.tasks.dll')) - $([MSBuild]::NormalizePath('$(InstallerTasksOutputPath)', 'net46', 'installer.tasks.dll')) + $([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'installer.tasks')) + $([MSBuild]::NormalizePath('$(InstallerTasksOutputPath)', 'Debug', 'netstandard2.0', 'installer.tasks.dll')) + $([MSBuild]::NormalizePath('$(InstallerTasksOutputPath)', 'Debug', 'net46', 'installer.tasks.dll')) $(ArtifactsObjDir)HostMachineInfo.props $([MSBuild]::NormalizeDirectory('$(RepoRoot)', 'src', 'libraries')) diff --git a/NuGet.config b/NuGet.config index 6b6e54b..b656e4d 100644 --- a/NuGet.config +++ b/NuGet.config @@ -12,6 +12,7 @@ --> + diff --git a/docs/libraries/building/code-coverage.md b/docs/libraries/building/code-coverage.md index f612695..76e7834 100644 --- a/docs/libraries/building/code-coverage.md +++ b/docs/libraries/building/code-coverage.md @@ -45,7 +45,7 @@ The results for this one library will then show up in the aforementioned index.h And then once the run completes: - $(TestPath)\report\index.htm + $(OutDir)\report\index.htm **Note:** If you only want to measure the coverage of your local changes (that haven't been pushed to git), run: diff --git a/docs/libraries/project-docs/developer-guide.md b/docs/libraries/project-docs/developer-guide.md index 4dd8b73..8865259 100644 --- a/docs/libraries/project-docs/developer-guide.md +++ b/docs/libraries/project-docs/developer-guide.md @@ -423,7 +423,7 @@ cd src\System.Collections.Immutable\tests dotnet msbuild /t:BuildAndTest /p:Coverage=true ``` -If coverage succeeds, the individual report can be found at `$(TestPath)\report\index.htm`. +If coverage succeeds, the individual report can be found at `$(OutDir)\report\index.htm`. Code coverage reports from the continuous integration system are available from the links on the front page of the corefx repo. diff --git a/eng/Build.props b/eng/Build.props index 3f36247..e6bf9aa 100644 --- a/eng/Build.props +++ b/eng/Build.props @@ -79,6 +79,7 @@ $(RepoTasksDir) - $(ArtifactsObjDir)runtime.tasks\$(Configuration)\build-semaphore.txt + $(ArtifactsObjDir)runtime.tasks\Debug\build-semaphore.txt diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index a0cc682..3e11596 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -130,5 +130,9 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-optimization d0bb63d2ec7060714e63ee4082fac48f2e57f3e2 + + https://github.com/Microsoft/vstest + ca987de449d17284f8c9806df36f42276f13962a + diff --git a/eng/Versions.props b/eng/Versions.props index c388700..25eac8a 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -80,12 +80,12 @@ 4.9.4 4.4.0 - 4.4.0 - 16.3.0 + 16.4.0 2.4.1 2.0.5 - 12.0.1 + 12.0.3 + 2.1.26 1.0.16 1.0.4 diff --git a/eng/pipelines/libraries/corefx-base.yml b/eng/pipelines/libraries/corefx-base.yml index a8ad96c..aac9dbf 100644 --- a/eng/pipelines/libraries/corefx-base.yml +++ b/eng/pipelines/libraries/corefx-base.yml @@ -84,7 +84,7 @@ jobs: - _msbuildCommonParameters: /p:OfficialBuildId=$(Build.BuildNumber) - ${{ if eq(job.submitToHelix, 'true') }}: - - _archiveTestsParameter: /p:ArchiveTests=Tests + - _archiveTestsParameter: /p:ArchiveTests=true - ${{ if eq(parameters.isOfficialBuild, 'true') }}: - group: DotNet-HelixApi-Access diff --git a/eng/pipelines/libraries/windows.yml b/eng/pipelines/libraries/windows.yml index 6fd05b3..c1825ea 100644 --- a/eng/pipelines/libraries/windows.yml +++ b/eng/pipelines/libraries/windows.yml @@ -134,7 +134,7 @@ stages: -allconfigurations -arch $(_architecture) /p:RuntimeOS=win10 - /p:ArchiveTests=Packages + /p:ArchiveTests=true $(_msbuildCommonParameters) displayName: Build Packages and Tests diff --git a/eng/references.targets b/eng/references.targets index 3f91056..0c104db 100644 --- a/eng/references.targets +++ b/eng/references.targets @@ -17,8 +17,9 @@ true true + - + @@ -35,5 +36,32 @@ + + + + + + + + + + %(DefaultReferenceDirs.Identity) + + <_defaultReferenceExclusionsFullPath Include="%(DefaultReferenceExclusions.RefDir)%(DefaultReferenceExclusions.Identity).dll" /> + + + + + + + + + + + + \ No newline at end of file diff --git a/eng/testing/RunnerTemplate.Unix.txt b/eng/testing/RunnerTemplate.Unix.txt new file mode 100644 index 0000000..80b496e --- /dev/null +++ b/eng/testing/RunnerTemplate.Unix.txt @@ -0,0 +1,200 @@ +#!/usr/bin/env bash + +usage() +{ + echo "Usage: RunTests.sh {-r|--runtime-path} [{--rsp-file} ]" + echo "" + echo "Parameters:" + echo "--runtime-path (Mandatory) Testhost containing the test runtime used during test execution (short: -r)" + echo "--rsp-file RSP file to pass in additional arguments" + echo "--help Print help and exit (short: -h)" +} + +EXECUTION_DIR=$(dirname "$0") +RUNTIME_PATH='' +RSP_FILE='' + +while [[ $# > 0 ]]; do + opt="$(echo "${1}" | awk '{print tolower($0)}')" + case "$opt" in + --help|-h) + usage + exit 0 + ;; + --runtime-path|-r) + RUNTIME_PATH=$2 + shift + ;; + --rsp-file) + RSP_FILE=\@$2 + shift + ;; + *) + echo "Invalid argument: $1" + usage + exit -1 + ;; + esac + + shift +done + +if [ "$RUNTIME_PATH" == "" ]; then + echo "error: -r|--runtime-path argument is required." + usage + exit -1 +fi + +# Don't use a globally installed SDK. +export DOTNET_MULTILEVEL_LOOKUP=0 + +exitcode_list[0]="Exited Successfully" +exitcode_list[130]="SIGINT Ctrl-C occurred. Likely tests timed out." +exitcode_list[131]="SIGQUIT Ctrl-\ occurred. Core dumped." +exitcode_list[132]="SIGILL Illegal Instruction. Core dumped. Likely codegen issue." +exitcode_list[133]="SIGTRAP Breakpoint hit. Core dumped." +exitcode_list[134]="SIGABRT Abort. Managed or native assert, or runtime check such as heap corruption, caused call to abort(). Core dumped." +exitcode_list[135]="IGBUS Unaligned memory access. Core dumped." +exitcode_list[136]="SIGFPE Bad floating point arguments. Core dumped." +exitcode_list[137]="SIGKILL Killed eg by kill" +exitcode_list[139]="SIGSEGV Illegal memory access. Deref invalid pointer, overrunning buffer, stack overflow etc. Core dumped." +exitcode_list[143]="SIGTERM Terminated. Usually before SIGKILL." +exitcode_list[159]="SIGSYS Bad System Call." + +function print_info_from_core_file_using_lldb { + local core_file_name=$1 + local executable_name=$2 + local plugin_path_name="$RUNTIME_PATH/shared/Microsoft.NETCore.App/9.9.9/libsosplugin.so" + + # check for existence of lldb on the path + hash lldb 2>/dev/null || { echo >&2 "lldb was not found. Unable to print core file."; return; } + + # pe, clrstack, and dumpasync are defined in libsosplugin.so + if [ ! -f $plugin_path_name ]; then + echo $plugin_path_name cannot be found. + return + fi + + echo ----- start =============== lldb Output ===================================================== + echo Printing managed exceptions, managed call stacks, and async state machines. + lldb -O "settings set target.exec-search-paths $RUNTIME_PATH" -o "plugin load $plugin_path_name" -o "clrthreads -managedexception" -o "pe -nested" -o "clrstack -all -a -f" -o "dumpasync -fields -stacks -roots" -o "quit" --core $core_file_name $executable_name + echo ----- end =============== lldb Output ======================================================= +} + +function print_info_from_core_file_using_gdb { + local core_file_name=$1 + local executable_name=$2 + + # Check for the existence of GDB on the path + hash gdb 2>/dev/null || { echo >&2 "GDB was not found. Unable to print core file."; return; } + + echo ----- start =============== GDB Output ===================================================== + # Open the dump in GDB and print the stack from each thread. We can add more + # commands here if desired. + echo printing native stack. + gdb --batch -ex "thread apply all bt full" -ex "quit" $executable_name $core_file_name + echo ----- end =============== GDB Output ======================================================= +} + +function print_info_from_core_file { + local core_file_name=$1 + local executable_name=$RUNTIME_PATH/$2 + + if ! [ -e $executable_name ]; then + echo "Unable to find executable $executable_name" + return + elif ! [ -e $core_file_name ]; then + echo "Unable to find core file $core_file_name" + return + fi + echo "Printing info from core file $core_file_name" + print_info_from_core_file_using_gdb $core_file_name $executable_name + print_info_from_core_file_using_lldb $core_file_name $executable_name +} + +function copy_core_file_to_temp_location { + local core_file_name=$1 + + local storage_location="/tmp/coredumps" + + # Create the directory (this shouldn't fail even if it already exists). + mkdir -p $storage_location + + local new_location=$storage_location/core.$RANDOM + + echo "Copying core file $core_file_name to $new_location in case you need it." + cp $core_file_name $new_location +} + +# ========================= BEGIN Core File Setup ============================ +if [ "$(uname -s)" == "Darwin" ]; then + # On OS X, we will enable core dump generation only if there are no core + # files already in /cores/ at this point. This is being done to prevent + # inadvertently flooding the CI machines with dumps. + if [[ ! -d "/cores" || ! "$(ls -A /cores)" ]]; then + ulimit -c unlimited + fi +elif [ "$(uname -s)" == "Linux" ]; then + # On Linux, we'll enable core file generation unconditionally, and if a dump + # is generated, we will print some useful information from it and delete the + # dump immediately. + + if [ -e /proc/self/coredump_filter ]; then + # Include memory in private and shared file-backed mappings in the dump. + # This ensures that we can see disassembly from our shared libraries when + # inspecting the contents of the dump. See 'man core' for details. + echo -n 0x3F > /proc/self/coredump_filter + fi + + ulimit -c unlimited +fi +# ========================= END Core File Setup ============================== + +# ========================= BEGIN Test Execution ============================= +echo ----- start $(date) =============== To repro directly: ===================================================== +echo pushd $EXECUTION_DIR +[[RunCommandsEcho]] +echo popd +echo =========================================================================================================== +pushd $EXECUTION_DIR +[[RunCommands]] +test_exitcode=$? +popd +echo ----- end $(date) ----- exit code $test_exitcode ---------------------------------------------------------- + +if [ "${exitcode_list[$test_exitcode]}" != "" ]; then + echo exit code $test_exitcode means ${exitcode_list[$test_exitcode]} +fi +# ========================= END Test Execution =============================== + +# ======================= BEGIN Core File Inspection ========================= +pushd $EXECUTION_DIR >/dev/null +if [[ "$(uname -s)" == "Linux" && $test_exitcode -ne 0 ]]; then + echo Looking around for any Linux dump... + # Depending on distro/configuration, the core files may either be named "core" + # or "core." by default. We read /proc/sys/kernel/core_uses_pid to + # determine which it is. + core_name_uses_pid=0 + if [ -e /proc/sys/kernel/core_uses_pid ] && [ "1" == $(cat /proc/sys/kernel/core_uses_pid) ]; then + core_name_uses_pid=1 + fi + + if [ $core_name_uses_pid == "1" ]; then + # We don't know what the PID of the process was, so let's look at all core + # files whose name matches core.NUMBER + echo Looking for files matching core.* ... + for f in core.*; do + [[ $f =~ core.[0-9]+ ]] && print_info_from_core_file "$f" "dotnet" && copy_core_file_to_temp_location "$f" && rm "$f" + done + elif [ -f core ]; then + echo found a dump named core in $EXECUTION_DIR ! + print_info_from_core_file "core" "dotnet" + copy_core_file_to_temp_location "core" + rm "core" + else + echo ... found no dump in $PWD + fi +fi +popd >/dev/null +# ======================== END Core File Inspection ========================== +exit $test_exitcode diff --git a/eng/testing/RunnerTemplate.Windows.txt b/eng/testing/RunnerTemplate.Windows.txt new file mode 100644 index 0000000..6d956f5 --- /dev/null +++ b/eng/testing/RunnerTemplate.Windows.txt @@ -0,0 +1,74 @@ +@echo off +setlocal enabledelayedexpansion + +set EXECUTION_DIR=%~dp0 + +:argparser_start + if "%~1" == "" goto argparser_end + set "argparser_currentarg=%~1" + shift + + set "argparser_help_specified_inloop=" + if /i "%argparser_currentarg%"=="-h" ( set "argparser_help_specified_inloop=1" ) + if /i "%argparser_currentarg%"=="--help" ( set "argparser_help_specified_inloop=1" ) + if defined argparser_help_specified_inloop ( + goto usage + ) + + set "argparser_runtime_path_specified_inloop=" + if /i "%argparser_currentarg%"=="-r" ( set "argparser_runtime_path_specified_inloop=1" ) + if /i "%argparser_currentarg%"=="--runtime-path" ( set "argparser_runtime_path_specified_inloop=1" ) + if defined argparser_runtime_path_specified_inloop ( + if "%~1" == "" ( goto argparser_invalid ) + set "RUNTIME_PATH=%~1" + goto argparser_break + ) + + if /i "%argparser_currentarg%"=="--rsp-file" ( + if "%~1" == "" ( goto argparser_invalid ) + set "RSP_FILE=@%~1" + goto argparser_break + ) + +:argparser_invalid + echo Invalid argument or value: %argparser_currentarg% + call :usage + exit /b -1 + +:argparser_break + shift + goto argparser_start + +:argparser_end + +if not defined RUNTIME_PATH ( + echo error: -r|--runtime-path argument is required. + call :usage + exit /b -1 +) + +:: Don't use a globally installed SDK. +set DOTNET_MULTILEVEL_LOOKUP=0 + +:: ========================= BEGIN Test Execution ============================= +echo ----- start %DATE% %TIME% =============== To repro directly: ===================================================== +echo pushd %EXECUTION_DIR% +[[RunCommandsEcho]] +echo popd +echo =========================================================================================================== +pushd %EXECUTION_DIR% +@echo on +[[RunCommands]] +@echo off +popd +echo ----- end %DATE% %TIME% ----- exit code %ERRORLEVEL% ---------------------------------------------------------- +exit /b %ERRORLEVEL% +:: ========================= END Test Execution ================================= + +:usage +echo Usage: RunTests.cmd {-r^|--runtime-path} ^ [{--rsp-file} ^] +echo. +echo Parameters: +echo --runtime-path (Mandatory) Testhost containing the test runtime used during test execution (short: -r)" +echo --rsp-file RSP file to pass in additional arguments +echo --help Print help and exit (short: -h) diff --git a/eng/testing/coverage.props b/eng/testing/coverage.props new file mode 100644 index 0000000..7af3026 --- /dev/null +++ b/eng/testing/coverage.props @@ -0,0 +1,14 @@ + + + coverage.xml + 0 + line,branch,method + opencover + true + normal + + $(CoverageOutputPath) + Html + Info + + diff --git a/eng/testing/coverage.targets b/eng/testing/coverage.targets new file mode 100644 index 0000000..c2d9059 --- /dev/null +++ b/eng/testing/coverage.targets @@ -0,0 +1,62 @@ + + + + "$(TargetFileName)" --target "$(RunCommand)" --targetargs "$(RunArguments)" --format "$(CoverageFormat)" --output "$(CoverageOutputPath)" --threshold "$(CoverageThreshold)" --threshold-type "$(CoverageThresholdType)" --verbosity "$(CoverageVerbosity)" + $(RunArguments) --use-source-link + "$(DotNetTool)" tool run coverlet + + + + + $([MSBuild]::NormalizeDirectory('$(OutDir)', 'report')) + $([MSBuild]::NormalizePath('$(CoverageReportDir)', 'index.htm')) + "$(DotNetTool)" tool run reportgenerator "-reports:$(CoverageReportInputPath)" "-targetdir:$(CoverageReportDir.TrimEnd('\/'))" "-reporttypes:$(CoverageReportTypes)" "-verbosity:$(CoverageReportVerbosity)" + + + + + + + + + + --exclude-by-file @(CoverageExcludeFile -> '"%(Identity)"', ' --exclude-by-file ') + $(RunArguments) $(CoverageExcludeByFileFilter) + + + + --include-directory @(CoverageProbePath -> '"$(RunScriptHostDir)%(Identity)"', ' --include-directory ') + $(RunArguments) $(IncludeDirectoriesFilter) + + + + --exclude @(CoverageExclude -> '"%(Identity)"', ' --exclude ') + $(RunArguments) $(CoverageExcludeFilter) + + + + + <_ProjectDirectoryUnderSourceDir>$(MSBuildProjectDirectory.SubString($(LibrariesProjectRoot.Length))) + $(_ProjectDirectoryUnderSourceDir.SubString(0, $(_ProjectDirectoryUnderSourceDir.IndexOfAny("\\/")))) + + + + + <_CoverageAssemblies Include="$(AssemblyBeingTested)" /> + <_CoverageAssemblies Include="System.Private.CoreLib" Condition="'$(TestRuntime)' == 'true'" /> + <_CoverageAssemblies Include="@(AssembliesBeingTested)" /> + <_CoverageAssemblies Include="$(CoverageAssemblies)" Condition="'$(CoverageAssemblies)' != ''" /> + + + + --include @(_CoverageAssemblies -> '"[%(Identity)]*"', ' --include ') + $(RunArguments) $(CoverageFilter) + + + diff --git a/eng/testing/launchSettings.json b/eng/testing/launchSettings.json new file mode 100644 index 0000000..c215e10 --- /dev/null +++ b/eng/testing/launchSettings.json @@ -0,0 +1,10 @@ +{ + "profiles": { + ".NET Core xUnit Console": { + "commandName": "Executable", + "executablePath": "$(TestHostRootPath)dotnet.exe", + "commandLineArgs": "$(RunArguments) -parallel none", + "workingDirectory": "$(RunWorkingDirectory)" + } + } +} diff --git a/eng/testing/launchSettings.targets b/eng/testing/launchSettings.targets new file mode 100644 index 0000000..6caf947 --- /dev/null +++ b/eng/testing/launchSettings.targets @@ -0,0 +1,22 @@ + + + + $(MSBuildThisFileDirectory)launchSettings.json + $([MSBuild]::NormalizePath('$(MSBuildProjectDirectory)', '$(AppDesignerFolder)', 'launchSettings.json')) + GenerateLaunchSettingsFile;$(PrepareForRunDependsOn); + + + + + + + \ No newline at end of file diff --git a/eng/testing/netfx.exe.config b/eng/testing/netfx.exe.config new file mode 100644 index 0000000..ed7d7d0 --- /dev/null +++ b/eng/testing/netfx.exe.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/eng/testing/runtimeConfiguration.targets b/eng/testing/runtimeConfiguration.targets new file mode 100644 index 0000000..6b6c952 --- /dev/null +++ b/eng/testing/runtimeConfiguration.targets @@ -0,0 +1,45 @@ + + + + $(MSBuildThisFileDirectory)netfx.exe.config + + + true + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/eng/testing/tests.props b/eng/testing/tests.props new file mode 100644 index 0000000..1dc35ce --- /dev/null +++ b/eng/testing/tests.props @@ -0,0 +1,17 @@ + + + $(MSBuildProjectName) + xunit + + + + + + + + + + diff --git a/eng/testing/tests.targets b/eng/testing/tests.targets new file mode 100644 index 0000000..527f418 --- /dev/null +++ b/eng/testing/tests.targets @@ -0,0 +1,127 @@ + + + $(DefaultOSGroup) + $(OutDir) + + RunnerTemplate.Windows.txt + RunnerTemplate.Unix.txt + $(MSBuildThisFileDirectory)$(RunScriptInputName) + + RunTests.cmd + RunTests.sh + $([MSBuild]::NormalizePath('$(OutDir)', '$(RunScriptOutputName)')) + + %RUNTIME_PATH%\ + $RUNTIME_PATH/ + + $(RunScriptHostDir)dotnet.exe + $(RunScriptHostDir)dotnet + + + + + + + + + + + + + + + $(RunArguments) %RSP_FILE% + $(RunArguments) $RSP_FILE + + + $([MSBuild]::Escape('$(RunArguments)')) + + $(RunCommand) $(RunArguments) + + + + + $(TestDebugger) $(RunScriptCommand) + $(TestDebugger) /debugexe $(RunScriptCommand) + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + "$(RunScriptOutputPath)" --runtime-path "$(TestHostRootPath.TrimEnd('\/'))" + $(RunTestsCommand) --rsp-file "$(TestRspFile)" + + + + + + + + + One or more tests failed while running tests from '$(TestProjectName)'. + $(TestRunErrorMessage) Please check $(TestResultsPath) for details! + + + + + + + + + + + + diff --git a/eng/testing/xunit/vstest.props b/eng/testing/xunit/vstest.props new file mode 100644 index 0000000..bcf5476 --- /dev/null +++ b/eng/testing/xunit/vstest.props @@ -0,0 +1,20 @@ + + + + en + + + + + + + + + + + + + + diff --git a/eng/testing/xunit/vstest.targets b/eng/testing/xunit/vstest.targets new file mode 100644 index 0000000..9e1517a2 --- /dev/null +++ b/eng/testing/xunit/vstest.targets @@ -0,0 +1,32 @@ + + + "$(RunScriptHost)" + test $(TargetFileName) + + + $(RunArguments) --logger "xunit;LogFilePath=$(TestResultsName)" + $(RunArguments) --framework $(TargetFramework) + $(RunArguments) --platform $(ArchGroup) + $(RunArguments) --parallel + $(RunArguments) --blame + + + <_testFilter Condition="'$(_withCategories)' != ''">$(_withCategories.Replace(';', '&category=')) + <_testFilter Condition="'$(_withoutCategories)' != ''">$(_testFilter)$(_withoutCategories.Replace(';', '&category!=')) + + <_testFilter Condition="'$(TargetOS)' == 'Windows_NT'">$(_testFilter.Replace('!=', '^!=')) + <_testFilter>$(_testFilter.Trim('&')) + + <_testFilter Condition="'$(TestFilter)' != ''">$(_testFilter)&$(TestFilter.Replace('!=', '^!=')) + + $(RunArguments) --filter "($(_testFilter))" + + + $(RunArguments) $(XUnitOptions) + + + $(RunSettingsOptions) RunConfiguration.DisableParallelization=true + $(RunSettingsOptions) RunConfiguration.DisableAppDomain=true + $(RunArguments) --(RunSettingsOptions) + + \ No newline at end of file diff --git a/eng/testing/xunit/xunit.console.props b/eng/testing/xunit/xunit.console.props new file mode 100644 index 0000000..48a3919 --- /dev/null +++ b/eng/testing/xunit/xunit.console.props @@ -0,0 +1,21 @@ + + + true + + + + + + + + + + + + + + diff --git a/eng/testing/xunit/xunit.console.targets b/eng/testing/xunit/xunit.console.targets new file mode 100644 index 0000000..c7f10a9 --- /dev/null +++ b/eng/testing/xunit/xunit.console.targets @@ -0,0 +1,75 @@ + + + $(TargetFileName) + $(RunArguments) -xml $(TestResultsName) + $(RunArguments) -nologo + $(RunArguments) -nocolor + + + $(RunArguments) -maxthreads 1 + $(RunArguments) -method $(XUnitMethodName) + $(RunArguments) -class $(XUnitClassName) + $(RunArguments) -verbose + $(RunArguments) -noappdomain + + + $(RunArguments)$(_withCategories.Replace(';', ' -trait category=')) + $(RunArguments)$(_withoutCategories.Replace(';', ' -notrait category=')) + + + $(RunArguments) $(XUnitOptions) + + + + + + + <_testRunnerConfigSourceFile Include="$(TargetDir)$(TargetName).exe.config" /> + <_testRunnerConfigDestFile Include="$(TargetDir)$(_testRunnerName).config" /> + + + + + + + + + "$(RunScriptHost)" + <_depsFileRunArgument Condition="'$(GenerateDependencyFile)' == 'true'">--depsfile $(AssemblyName).deps.json + exec --runtimeconfig $(AssemblyName).runtimeconfig.json $(_depsFileRunArgument) xunit.console.dll $(RunArguments) + + + + <_testRunnerName>xunit.console.exe + $(_testRunnerName) + + + + + + + + + + + + <_xunitConsoleNetCoreExclude Condition="'$(GenerateDependencyFile)' != 'true'" Include="$([System.IO.Path]::GetDirectoryName('$(XunitConsoleNetCore21AppPath)'))\xunit.console.deps.json" /> + + + + diff --git a/eng/testing/xunit/xunit.props b/eng/testing/xunit/xunit.props new file mode 100644 index 0000000..2282ba7 --- /dev/null +++ b/eng/testing/xunit/xunit.props @@ -0,0 +1,18 @@ + + + xunit.console + $(MSBuildThisFileDirectory)xunit.runner.json + testResults.xml + + + + + + + + + + + + + diff --git a/eng/testing/xunit/xunit.runner.json b/eng/testing/xunit/xunit.runner.json new file mode 100644 index 0000000..5d2ac04 --- /dev/null +++ b/eng/testing/xunit/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "diagnosticMessages": true, + "longRunningTestSeconds": 120, + "shadowCopy": false +} \ No newline at end of file diff --git a/eng/testing/xunit/xunit.targets b/eng/testing/xunit/xunit.targets new file mode 100644 index 0000000..4ddddcc --- /dev/null +++ b/eng/testing/xunit/xunit.targets @@ -0,0 +1,31 @@ + + + nonwindowstests + nonlinuxtests + nonosxtests + nonfreebsdtests + nonnetbsdtests + + + <_withCategories Condition="'$(WithCategories)' != ''">;$(WithCategories.Trim(';')) + <_withoutCategories Condition="'$(WithoutCategories)' != ''">;$(WithoutCategories.Trim(';')) + + all + <_withCategories Condition="'$(TestScope)' == 'outerloop'">$(_withCategories);OuterLoop + <_withoutCategories Condition="'$(ArchiveTest)' == 'true'">$(_withoutCategories);IgnoreForCI + <_withoutCategories Condition="'$(TestScope)' == '' or '$(TestScope)' == 'innerloop'">$(_withoutCategories);OuterLoop + <_withoutCategories Condition="!$(_withCategories.Contains('failing'))">$(_withoutCategories);failing + + <_withoutCategories>$(_withoutCategories);non$(_bc_TargetGroup)tests + <_withoutCategories Condition="'$(TargetOSCategory)' != ''">$(_withoutCategories);$(TargetOSCategory) + + + + + + + + + diff --git a/global.json b/global.json index 5cf141c..bf792d3 100644 --- a/global.json +++ b/global.json @@ -16,7 +16,6 @@ "Microsoft.DotNet.Build.Tasks.SharedFramework.Sdk": "5.0.0-beta.19567.2", "Microsoft.DotNet.Helix.Sdk": "5.0.0-beta.19567.2", "Microsoft.DotNet.Build.Tasks.Configuration": "5.0.0-beta.19567.2", - "Microsoft.DotNet.CoreFxTesting": "5.0.0-beta.19567.2", "FIX-85B6-MERGE-9C38-CONFLICT": "1.0.0", "Microsoft.NET.Sdk.IL": "5.0.0-alpha1.19563.3", "Microsoft.Build.NoTargets": "1.0.53", diff --git a/src/coreclr/tests/scripts/run-corefx-tests.py b/src/coreclr/tests/scripts/run-corefx-tests.py index b346097..1dad960 100644 --- a/src/coreclr/tests/scripts/run-corefx-tests.py +++ b/src/coreclr/tests/scripts/run-corefx-tests.py @@ -300,7 +300,7 @@ def main(args): common_config_args = '-configuration Release -framework netcoreapp -os %s -arch %s' % (clr_os, arch) build_args = '-build -restore' - build_test_args = '-buildtests /p:ArchiveTests=Tests' + build_test_args = '-buildtests /p:ArchiveTests=true' if not no_run_tests: build_test_args += ' -test' diff --git a/src/libraries/Directory.Build.props b/src/libraries/Directory.Build.props index 84d0266..dd2bcf6 100644 --- a/src/libraries/Directory.Build.props +++ b/src/libraries/Directory.Build.props @@ -86,7 +86,19 @@ - + + + + false + true + + false + true + true + + + false + true @@ -304,45 +316,25 @@ $(ArtifactsObjDir)version.txt - - - true - true - - $([MSBuild]::NormalizeDirectory('$(TestHostRootPath)', 'shared', 'Microsoft.NETCore.App', '$(ProductVersion)')) - - $(NETCoreAppTestSharedFrameworkPath) - $(TestHostRootPath) - - $(TestHostRuntimePath)PlatformManifest.txt - - - - - - - - - true - - - - - true - - - + + false + true + true + + + false + - $(OutputPath)$(MSBuildProjectName).xml - + $(OutputPath)$(MSBuildProjectName).xml + + $(LibrariesProjectRoot)CodeAnalysis.ruleset + false - - true @@ -356,22 +348,36 @@ false - - false - true - true + + + true + true - - false + $([MSBuild]::NormalizeDirectory('$(TestHostRootPath)', 'shared', 'Microsoft.NETCore.App', '$(ProductVersion)')) - $(LibrariesProjectRoot)CodeAnalysis.ruleset - false + $(NETCoreAppTestSharedFrameworkPath) + $(TestHostRootPath) + + $(TestHostRuntimePath)PlatformManifest.txt + + + + - + + + true + true + true + + + + + diff --git a/src/libraries/Directory.Build.targets b/src/libraries/Directory.Build.targets index 4178908..0842649 100644 --- a/src/libraries/Directory.Build.targets +++ b/src/libraries/Directory.Build.targets @@ -115,9 +115,12 @@ + + + + - @@ -178,17 +181,6 @@ - - - - - - - - true - 1 + true + true true netcoreapp-Debug;netcoreapp-Release;netfx-Debug;netfx-Release - Common\System\IO\TempDirectory.cs diff --git a/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs b/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs index 5232c1a..31ecb26 100644 --- a/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs +++ b/src/libraries/System.Runtime.Serialization.Formatters/tests/BinaryFormatterTests.cs @@ -159,7 +159,7 @@ namespace System.Runtime.Serialization.Formatters.Tests // To generate this properly, change AssemblyVersion to a value which is unlikely to happen in production and generate base64(serialized-data) // For this test 9.98.7.987 is being used var obj = new SomeType() { SomeField = 7 }; - string serializedObj = @"AAEAAAD/////AQAAAAAAAAAMAgAAAHNTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMsIFZlcnNpb249OS45OC43Ljk4NywgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj05ZDc3Y2M3YWQzOWI2OGViBQEAAAA2U3lzdGVtLlJ1bnRpbWUuU2VyaWFsaXphdGlvbi5Gb3JtYXR0ZXJzLlRlc3RzLlNvbWVUeXBlAQAAAAlTb21lRmllbGQACAIAAAAHAAAACw=="; + string serializedObj = @"AAEAAAD/////AQAAAAAAAAAMAgAAAHBTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMsIFZlcnNpb249NS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1jYzdiMTNmZmNkMmRkZDUxBQEAAAA2U3lzdGVtLlJ1bnRpbWUuU2VyaWFsaXphdGlvbi5Gb3JtYXR0ZXJzLlRlc3RzLlNvbWVUeXBlAQAAAAlTb21lRmllbGQACAIAAAAHAAAACw=="; var deserialized = (SomeType)BinaryFormatterHelpers.FromBase64String(serializedObj, FormatterAssemblyStyle.Simple); Assert.Equal(obj, deserialized); @@ -171,7 +171,7 @@ namespace System.Runtime.Serialization.Formatters.Tests // To generate this properly, change AssemblyVersion to a value which is unlikely to happen in production and generate base64(serialized-data) // For this test 9.98.7.987 is being used var obj = new GenericTypeWithArg() { Test = new SomeType() { SomeField = 9 } }; - string serializedObj = @"AAEAAAD/////AQAAAAAAAAAMAgAAAHNTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMsIFZlcnNpb249OS45OC43Ljk4NywgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj05ZDc3Y2M3YWQzOWI2OGViBQEAAADxAVN5c3RlbS5SdW50aW1lLlNlcmlhbGl6YXRpb24uRm9ybWF0dGVycy5UZXN0cy5HZW5lcmljVHlwZVdpdGhBcmdgMVtbU3lzdGVtLlJ1bnRpbWUuU2VyaWFsaXphdGlvbi5Gb3JtYXR0ZXJzLlRlc3RzLlNvbWVUeXBlLCBTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMsIFZlcnNpb249OS45OC43Ljk4NywgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj05ZDc3Y2M3YWQzOWI2OGViXV0BAAAABFRlc3QENlN5c3RlbS5SdW50aW1lLlNlcmlhbGl6YXRpb24uRm9ybWF0dGVycy5UZXN0cy5Tb21lVHlwZQIAAAACAAAACQMAAAAFAwAAADZTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMuU29tZVR5cGUBAAAACVNvbWVGaWVsZAAIAgAAAAkAAAAL"; + string serializedObj = @"AAEAAAD/////AQAAAAAAAAAMAgAAAHBTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMsIFZlcnNpb249NS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1jYzdiMTNmZmNkMmRkZDUxBQEAAADuAVN5c3RlbS5SdW50aW1lLlNlcmlhbGl6YXRpb24uRm9ybWF0dGVycy5UZXN0cy5HZW5lcmljVHlwZVdpdGhBcmdgMVtbU3lzdGVtLlJ1bnRpbWUuU2VyaWFsaXphdGlvbi5Gb3JtYXR0ZXJzLlRlc3RzLlNvbWVUeXBlLCBTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMsIFZlcnNpb249NS4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1jYzdiMTNmZmNkMmRkZDUxXV0BAAAABFRlc3QENlN5c3RlbS5SdW50aW1lLlNlcmlhbGl6YXRpb24uRm9ybWF0dGVycy5UZXN0cy5Tb21lVHlwZQIAAAACAAAACQMAAAAFAwAAADZTeXN0ZW0uUnVudGltZS5TZXJpYWxpemF0aW9uLkZvcm1hdHRlcnMuVGVzdHMuU29tZVR5cGUBAAAACVNvbWVGaWVsZAAIAgAAAAkAAAAL"; var deserialized = (GenericTypeWithArg)BinaryFormatterHelpers.FromBase64String(serializedObj, FormatterAssemblyStyle.Simple); Assert.Equal(obj, deserialized); diff --git a/src/libraries/pkg/test/testPackages.proj b/src/libraries/pkg/test/testPackages.proj index fd928d2..6db7b5f 100644 --- a/src/libraries/pkg/test/testPackages.proj +++ b/src/libraries/pkg/test/testPackages.proj @@ -155,7 +155,7 @@ + Condition="'$(ArchiveTests)' == 'true'"> + Condition="'$(ArchiveTests)' != 'true'"> $(TestDotNetPath) @@ -191,7 +191,7 @@ + Condition="'$(ArchiveTests)' != 'true'"> $(TestDotNetPath) diff --git a/src/libraries/pretest.proj b/src/libraries/pretest.proj index 25ed7f6..a86dfd0 100644 --- a/src/libraries/pretest.proj +++ b/src/libraries/pretest.proj @@ -28,6 +28,28 @@ + + + + + + + + + + + diff --git a/src/libraries/restore/runtime/runtime.depproj b/src/libraries/restore/runtime/runtime.depproj index 397f7de..bfb02bc 100644 --- a/src/libraries/restore/runtime/runtime.depproj +++ b/src/libraries/restore/runtime/runtime.depproj @@ -21,7 +21,6 @@ - - + @@ -57,7 +65,7 @@ ContinueOnError="ErrorAndContinue" /> - + diff --git a/tools-local/tasks/installer.tasks/GenerateFileVersionProps.cs b/tools-local/tasks/installer.tasks/GenerateFileVersionProps.cs index a8cfdb8..9ded179 100644 --- a/tools-local/tasks/installer.tasks/GenerateFileVersionProps.cs +++ b/tools-local/tasks/installer.tasks/GenerateFileVersionProps.cs @@ -17,7 +17,6 @@ namespace Microsoft.DotNet.Build.Tasks private const string PreferredPackagesProperty = "PackageConflictPreferredPackages"; private static readonly Version ZeroVersion = new Version(0, 0, 0, 0); - [Required] public ITaskItem[] Files { get; set; } @@ -30,7 +29,6 @@ namespace Microsoft.DotNet.Build.Tasks [Required] public string PlatformManifestFile { get; set; } - [Required] public string PropsFile { get; set; } [Required] @@ -121,13 +119,19 @@ namespace Microsoft.DotNet.Build.Tasks } } - var props = ProjectRootElement.Create(); - var itemGroup = props.AddItemGroup(); - // set the platform manifest when the platform is not being published as part of the app - itemGroup.Condition = "'$(RuntimeIdentifier)' == '' or '$(SelfContained)' != 'true'"; + bool generatePropsFile = !string.IsNullOrWhiteSpace(PropsFile); + ProjectRootElement props = null; - var manifestFileName = Path.GetFileName(PlatformManifestFile); - itemGroup.AddItem(PlatformManifestsItem, $"$(MSBuildThisFileDirectory){manifestFileName}"); + if (generatePropsFile) + { + props = ProjectRootElement.Create(); + var itemGroup = props.AddItemGroup(); + // set the platform manifest when the platform is not being published as part of the app + itemGroup.Condition = "'$(RuntimeIdentifier)' == '' or '$(SelfContained)' != 'true'"; + + var manifestFileName = Path.GetFileName(PlatformManifestFile); + itemGroup.AddItem(PlatformManifestsItem, $"$(MSBuildThisFileDirectory){manifestFileName}"); + } Directory.CreateDirectory(Path.GetDirectoryName(PlatformManifestFile)); using (var manifestWriter = File.CreateText(PlatformManifestFile)) @@ -143,13 +147,16 @@ namespace Microsoft.DotNet.Build.Tasks } } - var propertyGroup = props.AddPropertyGroup(); - propertyGroup.AddProperty(PreferredPackagesProperty, PreferredPackages); + if (!string.IsNullOrWhiteSpace(PropsFile)) + { + var propertyGroup = props.AddPropertyGroup(); + propertyGroup.AddProperty(PreferredPackagesProperty, PreferredPackages); - var versionPropertyName = $"_{PackageId.Replace(".", "_")}_Version"; - propertyGroup.AddProperty(versionPropertyName, PackageVersion); + var versionPropertyName = $"_{PackageId.Replace(".", "_")}_Version"; + propertyGroup.AddProperty(versionPropertyName, PackageVersion); - props.Save(PropsFile); + props.Save(PropsFile); + } return !Log.HasLoggedErrors; } @@ -198,4 +205,4 @@ namespace Microsoft.DotNet.Build.Tasks public ITaskItem File { get; set; } } } -} +} \ No newline at end of file diff --git a/tools-local/tasks/installer.tasks/GenerateRunScript.cs b/tools-local/tasks/installer.tasks/GenerateRunScript.cs new file mode 100644 index 0000000..0dd2408 --- /dev/null +++ b/tools-local/tasks/installer.tasks/GenerateRunScript.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using System; +using System.IO; +using System.Text; + +namespace Microsoft.DotNet.Build.Tasks +{ + public class GenerateRunScript : Task + { + [Required] + public string[] RunCommands { get; set; } + + [Required] + public string TemplatePath { get; set; } + + [Required] + public string OutputPath { get; set; } + + public override bool Execute() + { + if (RunCommands.Length == 0) + { + Log.LogError("Please provide at least one test command to execute via the RunCommands property."); + return false; + } + + if (!File.Exists(TemplatePath)) + { + Log.LogError($"Runner script template {TemplatePath} was not found."); + return false; + } + + string templateContent = File.ReadAllText(TemplatePath); + Directory.CreateDirectory(Path.GetDirectoryName(OutputPath)); + + Log.LogMessage($"Run commands = {string.Join(Environment.NewLine, RunCommands)}"); + + string extension = Path.GetExtension(Path.GetFileName(OutputPath)).ToLowerInvariant(); + switch (extension) + { + case ".sh": + case ".cmd": + case ".bat": + WriteRunScript(templateContent, extension); + break; + default: + Log.LogError($"Generating runner scripts with extension '{extension}' is not supported."); + return false; + } + + return true; + } + + private void WriteRunScript(string templateContent, string extension) + { + bool isUnix = extension == ".sh"; + string lineFeed = isUnix ? "\n" : "\r\n"; + + var runCommandsBuilder = new StringBuilder(); + for (int i = 0; i < RunCommands.Length; i++) + { + runCommandsBuilder.Append(RunCommands[i]); + if (i < RunCommands.Length - 1) + { + runCommandsBuilder.Append(lineFeed); + } + } + templateContent = templateContent.Replace("[[RunCommands]]", runCommandsBuilder.ToString()); + + var runCommandEchoesBuilder = new StringBuilder(); + foreach (string runCommand in RunCommands) + { + // Escape backtick and question mark characters to avoid running commands instead of echo'ing them. + string sanitizedRunCommand = runCommand.Replace("`", "\\`") + .Replace("?", "\\") + .Replace("\r","") + .Replace("\n"," ") + .Replace("&", "^&") + .Replace(">", "^>"); + + if (isUnix) + { + // Remove parentheses and quotes from echo command before wrapping it in quotes to avoid errors on Linux. + sanitizedRunCommand = "\"" + sanitizedRunCommand.Replace("\"", "") + .Replace("(", "") + .Replace(")", "") + "\""; + } + + runCommandEchoesBuilder.Append($"echo {sanitizedRunCommand}{lineFeed}"); + } + templateContent = templateContent.Replace("[[RunCommandsEcho]]", runCommandEchoesBuilder.ToString()); + + if (isUnix) + { + // Just in case any Windows EOLs have made it in by here, clean any up. + templateContent = templateContent.Replace("\r\n", "\n"); + } + + using (StreamWriter sw = new StreamWriter(new FileStream(OutputPath, FileMode.Create))) + { + sw.NewLine = lineFeed; + sw.Write(templateContent); + sw.WriteLine(); + } + + Log.LogMessage($"Wrote {extension} run script to {OutputPath}"); + } + } +} \ No newline at end of file diff --git a/tools-local/tasks/installer.tasks/GenerateTestSharedFrameworkDepsFile.cs b/tools-local/tasks/installer.tasks/GenerateTestSharedFrameworkDepsFile.cs new file mode 100644 index 0000000..0048a46 --- /dev/null +++ b/tools-local/tasks/installer.tasks/GenerateTestSharedFrameworkDepsFile.cs @@ -0,0 +1,132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.Build.Framework; +using System; +using System.IO; +using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using Microsoft.Extensions.DependencyModel; +using System.Collections.Generic; +using NuGet.RuntimeModel; + +namespace Microsoft.DotNet.Build.Tasks +{ + public partial class GenerateTestSharedFrameworkDepsFile : BuildTask + { + // we don't care about these values in the deps file + const string rid = "rid"; + const string tfm = "netcoreapp0.0"; + const string fullTfm = ".NETCoreApp,Version=v0.0"; + + [Required] + public string SharedFrameworkDirectory { get; set; } + + [Required] + public string[] RuntimeGraphFiles { get; set; } + + [Required] + public string TargetRuntimeIdentifier { get; set; } + + public override bool Execute() + { + var sharedFxDir = new DirectoryInfo(SharedFrameworkDirectory); + if (!sharedFxDir.Exists) + { + Log.LogError($"{nameof(SharedFrameworkDirectory)} '{SharedFrameworkDirectory}' does not exist."); + return false; + } + + // directory is the version folder, parent is the shared framework name. + string sharedFxVersion = sharedFxDir.Name; + string sharedFxName = sharedFxDir.Parent.Name; + + var ignoredExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) + { + ".pdb", + ".json", + ".config", + ".xml" + }; + + var isAssemblyTofileNames = Directory.EnumerateFiles(SharedFrameworkDirectory) + .Where(file => !ignoredExtensions.Contains(Path.GetExtension(file))) + .ToLookup(file => IsManagedAssembly(file), file => Path.GetFileName(file)); + + var managedFileNames = isAssemblyTofileNames[true]; + var nativeFileNames = isAssemblyTofileNames[false]; + + var runtimeLibraries = new[] + { + new RuntimeLibrary( + type:"package", + name: sharedFxName, + version: sharedFxVersion, + hash: "hash", + runtimeAssemblyGroups: new[] { new RuntimeAssetGroup(string.Empty, managedFileNames.Select(f => $"runtimes/{rid}/lib/{tfm}/{f}")) }, + nativeLibraryGroups: new[] { new RuntimeAssetGroup(string.Empty, nativeFileNames.Select(f => $"runtimes/{rid}/native/{f}")) }, + resourceAssemblies: Enumerable.Empty(), + dependencies: Enumerable.Empty(), + serviceable: true) + }; + + var targetInfo = new TargetInfo(fullTfm, rid, "runtimeSignature", isPortable: false); + + var runtimeFallbacks = GetRuntimeFallbacks(RuntimeGraphFiles, TargetRuntimeIdentifier); + + var dependencyContext = new DependencyContext( + targetInfo, + CompilationOptions.Default, + Enumerable.Empty(), + runtimeLibraries, + runtimeFallbacks); + + using (var depsFileStream = File.Create(Path.Combine(SharedFrameworkDirectory, $"{sharedFxName}.deps.json"))) + { + new DependencyContextWriter().Write(dependencyContext, depsFileStream); + } + + return !Log.HasLoggedErrors; + } + + private static bool IsManagedAssembly(string file) + { + bool result = false; + try + { + using (var peReader = new PEReader(File.OpenRead(file))) + { + result = peReader.HasMetadata && peReader.GetMetadataReader().IsAssembly; + } + } + catch (BadImageFormatException) + { } + + return result; + } + + private static IEnumerable GetRuntimeFallbacks(string[] runtimeGraphFiles, string runtime) + { + RuntimeGraph runtimeGraph = RuntimeGraph.Empty; + + foreach (string runtimeGraphFile in runtimeGraphFiles) + { + runtimeGraph = RuntimeGraph.Merge(runtimeGraph, JsonRuntimeFormat.ReadRuntimeGraph(runtimeGraphFile)); + } + + foreach (string rid in runtimeGraph.Runtimes.Select(p => p.Key)) + { + IEnumerable ridFallback = runtimeGraph.ExpandRuntime(rid); + + if (ridFallback.Contains(runtime)) + { + // ExpandRuntime return runtime itself as first item so we are skiping it + yield return new RuntimeFallbacks(rid, ridFallback.Skip(1)); + } + } + } + + } +} diff --git a/tools-local/tasks/installer.tasks/installer.tasks.csproj b/tools-local/tasks/installer.tasks/installer.tasks.csproj index a955c7d..0c73fdc 100644 --- a/tools-local/tasks/installer.tasks/installer.tasks.csproj +++ b/tools-local/tasks/installer.tasks/installer.tasks.csproj @@ -1,14 +1,12 @@ - + netstandard2.0 $(TargetFrameworks);net46 false - - $(IntermediateOutputPath)netstandard2.0\installer.tasks.dll - $(IntermediateOutputPath)net46\installer.tasks.dll AnyCPU + Debug @@ -17,30 +15,37 @@ - + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + -- 2.7.4